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
1 change: 0 additions & 1 deletion contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ pragma solidity ^0.8.0;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { DonationBox } from "../../chain-adapters/DonationBox.sol";

// Import MulticallHandler
import { MulticallHandler } from "../../handlers/MulticallHandler.sol";
Expand Down
104 changes: 59 additions & 45 deletions contracts/periphery/mintburn/HyperCoreFlowExecutor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,23 @@
pragma solidity ^0.8.0;

import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol";
import { IERC20Metadata, IERC20 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { DonationBox } from "../../chain-adapters/DonationBox.sol";
import { HyperCoreLib } from "../../libraries/HyperCoreLib.sol";
import { CoreTokenInfo } from "./Structs.sol";
import { FinalTokenInfo } from "./Structs.sol";
import { SwapHandler } from "./SwapHandler.sol";
import { BPS_DECIMALS, BPS_SCALAR } from "./Constants.sol";
import { BPS_SCALAR } from "./Constants.sol";
import { Lockable } from "../../Lockable.sol";

/**
* @title HyperCoreFlowExecutor
* @notice Contract handling HyperCore interactions for transfer-to-core or swap-with-core actions after stablecoin bridge transactions
* @dev This contract is designed to work with stablecoins. baseToken and every finalToken should all be stablecoins.
* @custom:security-contact bugs@across.to
*/
contract HyperCoreFlowExecutor is AccessControl {
contract HyperCoreFlowExecutor is AccessControl, Lockable {
using SafeERC20 for IERC20;

// Common decimals scalars
Expand All @@ -40,11 +41,11 @@ contract HyperCoreFlowExecutor is AccessControl {
mapping(address => FinalTokenInfo) public finalTokenInfos;

/// @notice All operations performed in this contract are relative to this baseToken
address immutable baseToken;
address public immutable baseToken;

/// @notice The block number of the last funds pull action per final token: either as a part of finalizing pending swaps,
/// or an admin funds pull
mapping(address finalToken => uint256 lastPullFundsBlock) lastPullFundsBlock;
mapping(address finalToken => uint256 lastPullFundsBlock) public lastPullFundsBlock;

/// @notice A struct used for storing state of a swap flow that has been initialized, but not yet finished
struct PendingSwap {
Expand Down Expand Up @@ -274,7 +275,7 @@ contract HyperCoreFlowExecutor is AccessControl {
bool canBeUsedForAccountActivation,
uint64 accountActivationFeeCore,
uint64 bridgeSafetyBufferCore
) external onlyDefaultAdmin {
) external nonReentrant onlyDefaultAdmin {
_setCoreTokenInfo(
token,
coreIndex,
Expand Down Expand Up @@ -302,7 +303,13 @@ contract HyperCoreFlowExecutor is AccessControl {
uint32 feePpm,
uint32 suggestedDiscountBps,
address accountActivationFeeToken
) external onlyExistingCoreToken(finalToken) onlyExistingCoreToken(accountActivationFeeToken) onlyDefaultAdmin {
)
external
nonReentrant
onlyExistingCoreToken(finalToken)
onlyExistingCoreToken(accountActivationFeeToken)
onlyDefaultAdmin
{
SwapHandler swapHandler = finalTokenInfos[finalToken].swapHandler;
if (address(swapHandler) == address(0)) {
bytes32 salt = _swapHandlerSalt(finalToken);
Expand Down Expand Up @@ -622,15 +629,39 @@ contract HyperCoreFlowExecutor is AccessControl {
return;
}

// Transfer funds to SwapHandler @ core
SwapHandler swapHandler = finalTokenInfo.swapHandler;
// State changes
uint128 cloid = ++nextCloid;
pendingSwaps[quoteNonce] = PendingSwap({
finalRecipient: finalRecipient,
finalToken: finalToken,
minCoreAmountFromLO: guaranteedLOOut,
sponsoredCoreAmountPreFunded: totalCoreAmountToSponsor,
limitOrderCloid: cloid
});
pendingQueue[finalToken].push(quoteNonce);
cloidToQuoteNonce[cloid] = quoteNonce;
cumulativeSponsoredAmount[finalToken] += totalEVMAmountToSponsor;

emit SwapFlowInitialized(
quoteNonce,
finalRecipient,
finalToken,
amountInEVM,
totalEVMAmountToSponsor,
finalCoreSendAmount,
finalTokenInfo.assetIndex,
cloid
);

// 1. Fund SwapHandler @ core with `initialToken`: use it for the trade
(uint256 evmToSendForTrade, ) = HyperCoreLib.minimumCoreReceiveAmountToAmounts(
tokensToSendCore,
initialCoreTokenInfo.tokenInfo.evmExtraWeiDecimals
);
// Here, we're sending amount that is <= amountInEVM (came from user's bridge transaction)

SwapHandler swapHandler = finalTokenInfo.swapHandler;
// Interactions with external contracts
// 1. Fund SwapHandler @ core with `initialToken`: use it for the trade
// Always: evmToSendForTrade <= amountInEVM because of how it's calculated
IERC20(initialToken).safeTransfer(address(swapHandler), evmToSendForTrade);
swapHandler.transferFundsToSelfOnCore(
initialToken,
Expand All @@ -651,39 +682,20 @@ contract HyperCoreFlowExecutor is AccessControl {
totalEVMAmountToSponsor,
finalCoreTokenInfo.tokenInfo.evmExtraWeiDecimals
);
cumulativeSponsoredAmount[finalToken] += totalEVMAmountToSponsor;
}

uint128 cloid = ++nextCloid;
swapHandler.submitLimitOrder(finalTokenInfo, limitPriceX1e8, sizeX1e8, cloid);

pendingSwaps[quoteNonce] = PendingSwap({
finalRecipient: finalRecipient,
finalToken: finalToken,
minCoreAmountFromLO: guaranteedLOOut,
sponsoredCoreAmountPreFunded: totalCoreAmountToSponsor,
limitOrderCloid: cloid
});
pendingQueue[finalToken].push(quoteNonce);
cloidToQuoteNonce[cloid] = quoteNonce;

emit SwapFlowInitialized(
quoteNonce,
finalRecipient,
finalToken,
amountInEVM,
totalEVMAmountToSponsor,
finalCoreSendAmount,
finalTokenInfo.assetIndex,
cloid
);
}

/// @notice Finalizes pending queue of swaps for `finalToken` if a corresponding SwapHandler has enough balance
function finalizePendingSwaps(
address finalToken,
uint256 maxSwapCountToFinalize
) external returns (uint256 finalizedSwapsCount, uint256 finalizedSwapsAmount, uint256 totalPendingSwapsRemaining) {
)
external
nonReentrant
returns (uint256 finalizedSwapsCount, uint256 finalizedSwapsAmount, uint256 totalPendingSwapsRemaining)
{
FinalTokenInfo memory finalTokenInfo = _getExistingFinalTokenInfo(finalToken);
CoreTokenInfo memory coreTokenInfo = coreTokenInfos[finalToken];

Expand Down Expand Up @@ -749,7 +761,7 @@ contract HyperCoreFlowExecutor is AccessControl {
bytes32 quoteNonce,
address finalRecipient,
address fundingToken
) external onlyPermissionedBot {
) external nonReentrant onlyPermissionedBot {
CoreTokenInfo memory coreTokenInfo = _getExistingCoreTokenInfo(fundingToken);
bool coreUserExists = HyperCoreLib.coreUserExists(finalRecipient);
require(coreUserExists == false, "Can't fund account activation for existing user");
Expand All @@ -761,6 +773,8 @@ contract HyperCoreFlowExecutor is AccessControl {
);
require(safeToBridge, "Not safe to bridge");
uint256 activationFeeEvm = coreTokenInfo.accountActivationFeeEVM;
cumulativeSponsoredActivationFee[fundingToken] += activationFeeEvm;

// donationBox @ evm -> Handler @ evm
_getFromDonationBox(fundingToken, activationFeeEvm);
// Handler @ evm -> Handler @ core -> finalRecipient @ core
Expand All @@ -772,14 +786,14 @@ contract HyperCoreFlowExecutor is AccessControl {
coreTokenInfo.tokenInfo.evmExtraWeiDecimals
);

cumulativeSponsoredActivationFee[fundingToken] += activationFeeEvm;

emit SponsoredAccountActivation(quoteNonce, finalRecipient, fundingToken, activationFeeEvm);
}

/// @notice Cancells a pending limit order by `cloid` with an intention to submit a new limit order in its place. To
/// be used for stale limit orders to speed up executing user transactions
function cancelLimitOrderByCloid(uint128 cloid) external onlyPermissionedBot returns (bytes32 quoteNonce) {
function cancelLimitOrderByCloid(
uint128 cloid
) external nonReentrant onlyPermissionedBot returns (bytes32 quoteNonce) {
quoteNonce = cloidToQuoteNonce[cloid];
PendingSwap storage pendingSwap = pendingSwaps[quoteNonce];
// A pending swap was enqueued with this Final token, so it had to be set. Unsetting the final token config is not
Expand Down Expand Up @@ -821,7 +835,7 @@ contract HyperCoreFlowExecutor is AccessControl {
uint64 priceX1e8,
uint64 oldPriceX1e8,
uint64 oldSizeX1e8Left
) external onlyPermissionedBot {
) external nonReentrant onlyPermissionedBot {
PendingSwap storage pendingSwap = pendingSwaps[quoteNonce];
require(pendingSwap.limitOrderCloid == 0, "Cannot resubmit LO for non-empty cloid");

Expand Down Expand Up @@ -1026,27 +1040,27 @@ contract HyperCoreFlowExecutor is AccessControl {
* SWEEP FUNCTIONS *
**************************************/

function sweepErc20(address token, uint256 amount) external onlyFundsSweeper {
function sweepErc20(address token, uint256 amount) external nonReentrant onlyFundsSweeper {
IERC20(token).safeTransfer(msg.sender, amount);
}

function sweepErc20FromDonationBox(address token, uint256 amount) external onlyFundsSweeper {
function sweepErc20FromDonationBox(address token, uint256 amount) external nonReentrant onlyFundsSweeper {
_getFromDonationBox(token, amount);
IERC20(token).safeTransfer(msg.sender, amount);
}

function sweepERC20FromSwapHandler(address token, uint256 amount) external onlyFundsSweeper {
function sweepERC20FromSwapHandler(address token, uint256 amount) external nonReentrant onlyFundsSweeper {
SwapHandler swapHandler = finalTokenInfos[token].swapHandler;
swapHandler.sweepErc20(token, amount);
IERC20(token).safeTransfer(msg.sender, amount);
}

function sweepOnCore(address token, uint64 amount) external onlyFundsSweeper {
function sweepOnCore(address token, uint64 amount) external nonReentrant onlyFundsSweeper {
HyperCoreLib.transferERC20CoreToCore(coreTokenInfos[token].coreIndex, msg.sender, amount);
}

// TODO? Alternative flow: make this permissionless, send money SwapHandler @ core -> DonationBox @ core => DonationBox pulls money from Core to Self (needs DonationBox code change)
function sweepOnCoreFromSwapHandler(address token, uint64 amount) external onlyPermissionedBot {
function sweepOnCoreFromSwapHandler(address token, uint64 amount) external nonReentrant onlyPermissionedBot {
// We first want to make sure there are not pending limit orders for this token
uint256 head = pendingQueueHead[token];
if (head < pendingQueue[token].length) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { IMessageTransmitterV2 } from "../../../external/interfaces/CCTPInterfac
import { SponsoredCCTPQuoteLib } from "../../../libraries/SponsoredCCTPQuoteLib.sol";
import { SponsoredCCTPInterface } from "../../../interfaces/SponsoredCCTPInterface.sol";
import { Bytes32ToAddress } from "../../../libraries/AddressConverters.sol";
import { DonationBox } from "../../../chain-adapters/DonationBox.sol";
import { HyperCoreFlowExecutor } from "../HyperCoreFlowExecutor.sol";
import { ArbitraryEVMFlowExecutor } from "../ArbitraryEVMFlowExecutor.sol";

Expand Down Expand Up @@ -38,10 +37,6 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu
* @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(
Expand All @@ -59,15 +54,15 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu
* @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 {
function setSigner(address _signer) external nonReentrant 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 {
function setQuoteDeadlineBuffer(uint256 _quoteDeadlineBuffer) external nonReentrant onlyDefaultAdmin {
quoteDeadlineBuffer = _quoteDeadlineBuffer;
}

Expand All @@ -78,7 +73,11 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu
* @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 {
function receiveMessage(
bytes memory message,
bytes memory attestation,
bytes memory signature
) external nonReentrant {
cctpMessageTransmitter.receiveMessage(message, attestation);

// If the hook data is invalid or the mint recipient is not this contract we cannot process the message
Expand Down
4 changes: 2 additions & 2 deletions contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryEV
}
}

function setAuthorizedPeriphery(uint32 srcEid, bytes32 srcPeriphery) external onlyDefaultAdmin {
function setAuthorizedPeriphery(uint32 srcEid, bytes32 srcPeriphery) external nonReentrant onlyDefaultAdmin {
authorizedSrcPeripheryContracts[srcEid] = srcPeriphery;
emit SetAuthorizedPeriphery(srcEid, srcPeriphery);
}
Expand All @@ -92,7 +92,7 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryEV
bytes calldata _message,
address /* _executor */,
bytes calldata /* _extraData */
) external payable override {
) external payable override nonReentrant {
_requireAuthorizedMessage(_oApp, _message);

// Decode the actual `composeMsg` payload to extract the recipient address
Expand Down
Loading