Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 40 additions & 2 deletions contracts/libraries/SponsoredCCTPQuoteLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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
)
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand All @@ -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);
Expand All @@ -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,
Expand Down
36 changes: 36 additions & 0 deletions contracts/periphery/mintburn/HyperCoreFlowExecutor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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;
}
Expand Down
Loading