diff --git a/contracts/libraries/SponsoredCCTPQuoteLib.sol b/contracts/libraries/SponsoredCCTPQuoteLib.sol index c10a063ff..251060312 100644 --- a/contracts/libraries/SponsoredCCTPQuoteLib.sol +++ b/contracts/libraries/SponsoredCCTPQuoteLib.sol @@ -7,11 +7,16 @@ import { SponsoredCCTPInterface } from "../interfaces/SponsoredCCTPInterface.sol import { BytesLib } from "./BytesLib.sol"; import { Bytes32ToAddress } from "./AddressConverters.sol"; +/** + * @title SponsoredCCTPQuoteLib + * @notice Library that contains the functions to get the data from the quotes and validate the signatures. + */ library SponsoredCCTPQuoteLib { using BytesLib for bytes; using Bytes32ToAddress for bytes32; - // Indices of each field in message + /// @dev Indices of each field in message that we get from CCTP + /// Source: https://github.com/circlefin/evm-cctp-contracts/blob/4061786a5726bc05f99fcdb53b0985599f0dbaf7/src/messages/v2/MessageV2.sol#L52-L61 uint256 private constant VERSION_INDEX = 0; uint256 private constant SOURCE_DOMAIN_INDEX = 4; uint256 private constant DESTINATION_DOMAIN_INDEX = 8; @@ -23,7 +28,8 @@ library SponsoredCCTPQuoteLib { uint256 private constant FINALITY_THRESHOLD_EXECUTED_INDEX = 144; uint256 private constant MESSAGE_BODY_INDEX = 148; - // Field indices in message body + /// @dev Indices of each field in message body that is extracted from message + /// Source: https://github.com/circlefin/evm-cctp-contracts/blob/4061786a5726bc05f99fcdb53b0985599f0dbaf7/src/messages/v2/BurnMessageV2.sol#L48-L52 uint256 private constant BURN_TOKEN_INDEX = 4; uint256 private constant MINT_RECIPIENT_INDEX = 36; uint256 private constant AMOUNT_INDEX = 68; @@ -34,6 +40,18 @@ library SponsoredCCTPQuoteLib { // Minimum length of the message body (can be longer due to variable actionData) uint256 private constant MIN_MSG_BYTES_LENGTH = 568; + /** + * @notice Gets the data for the deposit for burn. + * @param quote The quote that contains the data for the deposit. + * @return amount The amount of tokens to deposit for burn. + * @return destinationDomain The destination domain ID for the chain that the tokens are being deposited to. + * @return mintRecipient The recipent of the minted tokens. This would be the destination periphery contract. + * @return burnToken The address of the token to burn. + * @return destinationCaller The address that will call the CCTP receiveMessage function. This would be the destination periphery contract. + * @return maxFee The maximum fee that can be paid for the deposit. + * @return minFinalityThreshold The minimum finality threshold for the deposit. + * @return hookData The hook data for the deposit. Contrains additional data to be used by the destination periphery contract. + */ function getDepositForBurnData( SponsoredCCTPInterface.SponsoredCCTPQuote memory quote ) @@ -69,6 +87,12 @@ library SponsoredCCTPQuoteLib { ); } + /** + * @notice Validates the message that is received from CCTP. If this checks fails, then the quote on source chain was invalid + * and we are unable to retrieve user's address to send the funds to. In that case the funds will stay in this contract. + * @param message The message that is received from CCTP. + * @return isValid True if the message is valid, false otherwise. + */ function validateMessage(bytes memory message) internal view returns (bool) { // Message must be at least the minimum length (can be longer due to variable actionData) if (message.length < MIN_MSG_BYTES_LENGTH) { @@ -93,6 +117,12 @@ library SponsoredCCTPQuoteLib { return finalRecipient.isValidAddress() && finalToken.isValidAddress(); } + /** + * @notice Returns the quote and the fee that was executed from the CCTP message. + * @param message The message that is received from CCTP. + * @return quote The quote that contains the data of the deposit. + * @return feeExecuted The fee that was executed for the deposit. This is the fee that was paid to the CCTP message transmitter. + */ function getSponsoredCCTPQuoteData( bytes memory message ) internal pure returns (SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, uint256 feeExecuted) { @@ -101,6 +131,7 @@ library SponsoredCCTPQuoteLib { quote.destinationCaller = message.toBytes32(DESTINATION_CALLER_INDEX); quote.minFinalityThreshold = message.toUint32(MIN_FINALITY_THRESHOLD_INDEX); + // first need to extract the message body from the message bytes memory messageBody = message.slice(MESSAGE_BODY_INDEX, message.length); quote.mintRecipient = messageBody.toBytes32(MINT_RECIPIENT_INDEX); quote.amount = messageBody.toUint256(AMOUNT_INDEX); @@ -121,6 +152,13 @@ library SponsoredCCTPQuoteLib { ) = abi.decode(hookData, (bytes32, uint256, uint256, uint256, bytes32, bytes32, uint8, bytes)); } + /** + * @notice Validates the signature against the quote. + * @param signer The signer address that was used to sign the quote. + * @param quote The quote that contains the data of the deposit. + * @param signature The signature of the quote. + * @return isValid True if the signature is valid, false otherwise. + */ function validateSignature( address signer, SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, diff --git a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol index 23c543e09..9eecf302c 100644 --- a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol +++ b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol @@ -739,6 +739,12 @@ contract HyperCoreFlowExecutor is AccessControl { return (finalizedSwapsCount, finalizedSwapsAmount, queue.length - head); } + /** + * @notice Activates a user account on Core by funding the account activation fee. + * @param quoteNonce The nonce of the quote that is used to identify the user. + * @param finalRecipient The address of the recipient of the funds. + * @param fundingToken The address of the token that is used to fund the account activation fee. + */ function activateUserAccount( bytes32 quoteNonce, address finalRecipient, @@ -1139,6 +1145,21 @@ contract HyperCoreFlowExecutor is AccessControl { } } + /** + * @notice Given the quote budget and the price, this function calculates the size of the buy limit order to set + * as well as the minimum amount of out token to expect. This calculation is based on the HIP-1 spot trading formula. + * Source: https://hyperliquid.gitbook.io/hyperliquid-docs/hyperliquid-improvement-proposals-hips/hip-1-native-token-standard#spot-trading + * @param quoteBudget The budget of the quote in base token. + * @param pxX1e8 The price of the quote token in base token. + * @param quoteD The decimals of the quote token. + * @param quoteSz The size decimals of the quote token. + * @param baseD The decimals of the base token. + * @param baseSz The size decimals of the base token. + * @param feePpm The fee in ppm that is applied to the quote. + * @return szX1e8 The size of the limit order to set. + * @return tokensToSendCore The number of tokens to send for this trade to suceed. + * @return minAmountOutCore The minimum amount of out token to expect. + */ function _calcLOAmountsBuy( uint64 quoteBudget, uint64 pxX1e8, @@ -1158,6 +1179,21 @@ contract HyperCoreFlowExecutor is AccessControl { minAmountOutCore = outBaseNet; } + /** + * @notice Given the quote budget and the price, this function calculates the size of the sell limit order to set + * as well as the minimum amount of out token to expect. This calculation is based on the HIP-1 spot trading formula. + * Source: https://hyperliquid.gitbook.io/hyperliquid-docs/hyperliquid-improvement-proposals-hips/hip-1-native-token-standard#spot-trading + * @param baseBudget The budget of the quote in base token. + * @param pxX1e8 The price of the quote token in base token. + * @param quoteD The decimals of the quote token. + * @param quoteSz The size decimals of the quote token. + * @param baseD The decimals of the base token. + * @param baseSz The size decimals of the base token. + * @param feePpm The fee in ppm that is applied to the quote. + * @return szX1e8 The size of the limit order to set. + * @return tokensToSendCore The number of tokens to send for this trade to suceed. + * @return minAmountOutCore The minimum amount of out token to expect. + */ function _calcLOAmountsSell( uint64 baseBudget, uint64 pxX1e8, diff --git a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol index add6d243f..ec05d1a8c 100644 --- a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol +++ b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol @@ -11,6 +11,11 @@ import { DonationBox } from "../../../chain-adapters/DonationBox.sol"; import { HyperCoreFlowExecutor } from "../HyperCoreFlowExecutor.sol"; import { ArbitraryEVMFlowExecutor } from "../ArbitraryEVMFlowExecutor.sol"; +/** + * @title SponsoredCCTPDstPeriphery + * @notice Destination chain periphery contract that supports sponsored/non-sponsored CCTP deposits. + * @dev This contract is used to receive tokens via CCTP and execute the flow accordingly. + */ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecutor, ArbitraryEVMFlowExecutor { using SafeERC20 for IERC20Metadata; using Bytes32ToAddress for bytes32; @@ -27,6 +32,18 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu /// @notice A mapping of used nonces to prevent replay attacks. mapping(bytes32 => bool) public usedNonces; + /** + * @notice Constructor for the SponsoredCCTPDstPeriphery contract. + * @param _cctpMessageTransmitter The address of the CCTP message transmitter contract. + * @param _signer The address of the signer that was used to sign the quotes. + * @param _donationBox The address of the donation box contract. This is used to store funds that are used for sponsored flows. + * @param _baseToken The address of the base token which would be the USDC on HyperEVM. + * @param _coreIndex The index of the base token on HyperCore. + * @param _canBeUsedForAccountActivation Whether the token can be used for account activation. + * @param _accountActivationFeeCore If the token can be used for account activation, this is the fee that is needed for account activation. + * @param _bridgeSafetyBufferCore This buffers is used to check if the bridging to Core is safe. + * @param _multicallHandler The address of the multicall handler contract. + */ constructor( address _cctpMessageTransmitter, address _signer, @@ -38,26 +55,43 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu signer = _signer; } + /** + * @notice Sets the signer address that is used to validate the signatures of the quotes. + * @param _signer The new signer address. + */ function setSigner(address _signer) external onlyDefaultAdmin { signer = _signer; } + /** + * @notice Sets the quote deadline buffer. This is used to prevent the quote from being used after it has expired. + * @param _quoteDeadlineBuffer The new quote deadline buffer. + */ function setQuoteDeadlineBuffer(uint256 _quoteDeadlineBuffer) external onlyDefaultAdmin { quoteDeadlineBuffer = _quoteDeadlineBuffer; } + /** + * @notice Receives a message from CCTP and executes the flow accordingly. This function first calls the + * CCTP message transmitter to receive the funds before validating the quote and executing the flow. + * @param message The message that is received from CCTP. + * @param attestation The attestation that is received from CCTP. + * @param signature The signature of the quote. + */ function receiveMessage(bytes memory message, bytes memory attestation, bytes memory signature) external { cctpMessageTransmitter.receiveMessage(message, attestation); - // If the hook data is invalid or the mint recipient is not this contract we cannot process the message and therefore we return. - // In this case the funds will be kept in this contract + // If the hook data is invalid or the mint recipient is not this contract we cannot process the message + // and therefore we exit. In this case the funds will be kept in this contract. if (!SponsoredCCTPQuoteLib.validateMessage(message)) { return; } + // Extract the quote and the fee that was executed from the message. (SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, uint256 feeExecuted) = SponsoredCCTPQuoteLib .getSponsoredCCTPQuoteData(message); + // Validate the quote and the signature. bool isQuoteValid = _isQuoteValid(quote, signature); if (isQuoteValid) { usedNonces[quote.nonce] = true; diff --git a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol index af18f3da7..d7a70ac76 100644 --- a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol +++ b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol @@ -9,25 +9,43 @@ import { ITokenMessengerV2 } from "../../../external/interfaces/CCTPInterfaces.s import { SponsoredCCTPQuoteLib } from "../../../libraries/SponsoredCCTPQuoteLib.sol"; import { SponsoredCCTPInterface } from "../../../interfaces/SponsoredCCTPInterface.sol"; +/** + * @title SponsoredCCTPSrcPeriphery + * @notice Source chain periphery contract that supports sponsored/non-sponsored CCTP deposits. + * @dev This contract is used to deposit tokens for burn via CCTP. + */ contract SponsoredCCTPSrcPeriphery is SponsoredCCTPInterface, Ownable { using SafeERC20 for IERC20; + /// @notice The CCTP token messenger contract. ITokenMessengerV2 public immutable cctpTokenMessenger; + /// @notice The source domain ID for the chain that this contract is deployed on. uint32 public immutable sourceDomain; + /// @notice The signer address that is used to validate the signatures of the quotes. address public signer; + /// @notice A mapping of used nonces to prevent replay attacks. mapping(bytes32 => bool) public usedNonces; + /** + * @notice Constructor for the SponsoredCCTPSrcPeriphery contract. + * @param _cctpTokenMessenger The address of the CCTP token messenger contract. + * @param _sourceDomain The source domain ID for the chain that this contract is deployed on. + * @param _signer The signer address that is used to validate the signatures of the quotes. + */ constructor(address _cctpTokenMessenger, uint32 _sourceDomain, address _signer) { cctpTokenMessenger = ITokenMessengerV2(_cctpTokenMessenger); sourceDomain = _sourceDomain; signer = _signer; } - using SponsoredCCTPQuoteLib for bytes; - + /** + * @notice Deposits tokens for burn via CCTP. + * @param quote The quote that contains the data for the deposit. + * @param signature The signature of the quote. + */ function depositForBurn(SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, bytes memory signature) external { if (!SponsoredCCTPQuoteLib.validateSignature(signer, quote, signature)) revert InvalidSignature(); if (usedNonces[quote.nonce]) revert InvalidNonce(); @@ -73,6 +91,10 @@ contract SponsoredCCTPSrcPeriphery is SponsoredCCTPInterface, Ownable { ); } + /** + * @notice Sets the signer address that is used to validate the signatures of the quotes. + * @param _signer The new signer address. + */ function setSigner(address _signer) external onlyOwner { signer = _signer; }