文件 1 的 16:Address.sol
pragma solidity ^0.8.1;
library Address {
function isContract(address account) internal view returns (bool) {
return account.code.length > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
文件 2 的 16:BytesLib.sol
pragma solidity >=0.8.0 <0.9.0;
library BytesLib {
function concat(
bytes memory _preBytes,
bytes memory _postBytes
)
internal
pure
returns (bytes memory)
{
bytes memory tempBytes;
assembly {
tempBytes := mload(0x40)
let length := mload(_preBytes)
mstore(tempBytes, length)
let mc := add(tempBytes, 0x20)
let end := add(mc, length)
for {
let cc := add(_preBytes, 0x20)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
length := mload(_postBytes)
mstore(tempBytes, add(length, mload(tempBytes)))
mc := end
end := add(mc, length)
for {
let cc := add(_postBytes, 0x20)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
mstore(0x40, and(
add(add(end, iszero(add(length, mload(_preBytes)))), 31),
not(31)
))
}
return tempBytes;
}
function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
assembly {
let fslot := sload(_preBytes.slot)
let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
let mlength := mload(_postBytes)
let newlength := add(slength, mlength)
switch add(lt(slength, 32), lt(newlength, 32))
case 2 {
sstore(
_preBytes.slot,
add(
fslot,
add(
mul(
div(
mload(add(_postBytes, 0x20)),
exp(0x100, sub(32, mlength))
),
exp(0x100, sub(32, newlength))
),
mul(mlength, 2)
)
)
)
}
case 1 {
mstore(0x0, _preBytes.slot)
let sc := add(keccak256(0x0, 0x20), div(slength, 32))
sstore(_preBytes.slot, add(mul(newlength, 2), 1))
let submod := sub(32, slength)
let mc := add(_postBytes, submod)
let end := add(_postBytes, mlength)
let mask := sub(exp(0x100, submod), 1)
sstore(
sc,
add(
and(
fslot,
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
),
and(mload(mc), mask)
)
)
for {
mc := add(mc, 0x20)
sc := add(sc, 1)
} lt(mc, end) {
sc := add(sc, 1)
mc := add(mc, 0x20)
} {
sstore(sc, mload(mc))
}
mask := exp(0x100, sub(mc, end))
sstore(sc, mul(div(mload(mc), mask), mask))
}
default {
mstore(0x0, _preBytes.slot)
let sc := add(keccak256(0x0, 0x20), div(slength, 32))
sstore(_preBytes.slot, add(mul(newlength, 2), 1))
let slengthmod := mod(slength, 32)
let mlengthmod := mod(mlength, 32)
let submod := sub(32, slengthmod)
let mc := add(_postBytes, submod)
let end := add(_postBytes, mlength)
let mask := sub(exp(0x100, submod), 1)
sstore(sc, add(sload(sc), and(mload(mc), mask)))
for {
sc := add(sc, 1)
mc := add(mc, 0x20)
} lt(mc, end) {
sc := add(sc, 1)
mc := add(mc, 0x20)
} {
sstore(sc, mload(mc))
}
mask := exp(0x100, sub(mc, end))
sstore(sc, mul(div(mload(mc), mask), mask))
}
}
}
function slice(
bytes memory _bytes,
uint256 _start,
uint256 _length
)
internal
pure
returns (bytes memory)
{
require(_length + 31 >= _length, "slice_overflow");
require(_bytes.length >= _start + _length, "slice_outOfBounds");
bytes memory tempBytes;
assembly {
switch iszero(_length)
case 0 {
tempBytes := mload(0x40)
let lengthmod := and(_length, 31)
let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
let end := add(mc, _length)
for {
let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
mstore(tempBytes, _length)
mstore(0x40, and(add(mc, 31), not(31)))
}
default {
tempBytes := mload(0x40)
mstore(tempBytes, 0)
mstore(0x40, add(tempBytes, 0x20))
}
}
return tempBytes;
}
function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
address tempAddress;
assembly {
tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
}
return tempAddress;
}
function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
require(_bytes.length >= _start + 1 , "toUint8_outOfBounds");
uint8 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x1), _start))
}
return tempUint;
}
function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
uint16 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x2), _start))
}
return tempUint;
}
function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
uint32 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x4), _start))
}
return tempUint;
}
function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
uint64 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x8), _start))
}
return tempUint;
}
function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
uint96 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0xc), _start))
}
return tempUint;
}
function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
uint128 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x10), _start))
}
return tempUint;
}
function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
uint256 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x20), _start))
}
return tempUint;
}
function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
bytes32 tempBytes32;
assembly {
tempBytes32 := mload(add(add(_bytes, 0x20), _start))
}
return tempBytes32;
}
function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
bool success = true;
assembly {
let length := mload(_preBytes)
switch eq(length, mload(_postBytes))
case 1 {
let cb := 1
let mc := add(_preBytes, 0x20)
let end := add(mc, length)
for {
let cc := add(_postBytes, 0x20)
} eq(add(lt(mc, end), cb), 2) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
if iszero(eq(mload(mc), mload(cc))) {
success := 0
cb := 0
}
}
}
default {
success := 0
}
}
return success;
}
function equalStorage(
bytes storage _preBytes,
bytes memory _postBytes
)
internal
view
returns (bool)
{
bool success = true;
assembly {
let fslot := sload(_preBytes.slot)
let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
let mlength := mload(_postBytes)
switch eq(slength, mlength)
case 1 {
if iszero(iszero(slength)) {
switch lt(slength, 32)
case 1 {
fslot := mul(div(fslot, 0x100), 0x100)
if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
success := 0
}
}
default {
let cb := 1
mstore(0x0, _preBytes.slot)
let sc := keccak256(0x0, 0x20)
let mc := add(_postBytes, 0x20)
let end := add(mc, mlength)
for {} eq(add(lt(mc, end), cb), 2) {
sc := add(sc, 1)
mc := add(mc, 0x20)
} {
if iszero(eq(sload(sc), mload(mc))) {
success := 0
cb := 0
}
}
}
}
}
default {
success := 0
}
}
return success;
}
}
文件 3 的 16:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
文件 4 的 16:IERC20Permit.sol
pragma solidity ^0.8.0;
interface IERC20Permit {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
文件 5 的 16:IFeeManager.sol
pragma solidity ^0.8.0;
interface IFeeManager {
function calcProtocolBps(
uint64 amountIn,
address tokenIn,
bytes32 tokenOut,
uint16 destChain,
uint8 referrerBps
) external view returns (uint8);
function feeCollector() external view returns (address);
}
文件 6 的 16:IMessageTransmitter.sol
pragma solidity ^0.8.0;
import "./IRelayer.sol";
import "./IReceiver.sol";
interface IMessageTransmitter is IRelayer, IReceiver {
function localDomain() external view returns (uint32);
}
文件 7 的 16:IReceiver.sol
pragma solidity ^0.8.0;
interface IReceiver {
function receiveMessage(bytes calldata message, bytes calldata signature)
external
returns (bool success);
}
文件 8 的 16:IRelayer.sol
pragma solidity ^0.8.0;
interface IRelayer {
function sendMessage(
uint32 destinationDomain,
bytes32 recipient,
bytes calldata messageBody
) external returns (uint64);
function sendMessageWithCaller(
uint32 destinationDomain,
bytes32 recipient,
bytes32 destinationCaller,
bytes calldata messageBody
) external returns (uint64);
function replaceMessage(
bytes calldata originalMessage,
bytes calldata originalAttestation,
bytes calldata newMessageBody,
bytes32 newDestinationCaller
) external;
}
文件 9 的 16:ITokenBridge.sol
pragma solidity ^0.8.0;
import "./IWETH.sol";
import "./IWormhole.sol";
interface ITokenBridge {
struct Transfer {
uint8 payloadID;
uint256 amount;
bytes32 tokenAddress;
uint16 tokenChain;
bytes32 to;
uint16 toChain;
uint256 fee;
}
struct TransferWithPayload {
uint8 payloadID;
uint256 amount;
bytes32 tokenAddress;
uint16 tokenChain;
bytes32 to;
uint16 toChain;
bytes32 fromAddress;
bytes payload;
}
struct AssetMeta {
uint8 payloadID;
bytes32 tokenAddress;
uint16 tokenChain;
uint8 decimals;
bytes32 symbol;
bytes32 name;
}
struct RegisterChain {
bytes32 module;
uint8 action;
uint16 chainId;
uint16 emitterChainID;
bytes32 emitterAddress;
}
struct UpgradeContract {
bytes32 module;
uint8 action;
uint16 chainId;
bytes32 newContract;
}
struct RecoverChainId {
bytes32 module;
uint8 action;
uint256 evmChainId;
uint16 newChainId;
}
event ContractUpgraded(address indexed oldContract, address indexed newContract);
function _parseTransferCommon(bytes memory encoded) external pure returns (Transfer memory transfer);
function attestToken(address tokenAddress, uint32 nonce) external payable returns (uint64 sequence);
function wrapAndTransferETH(uint16 recipientChain, bytes32 recipient, uint256 arbiterFee, uint32 nonce) external payable returns (uint64 sequence);
function wrapAndTransferETHWithPayload(uint16 recipientChain, bytes32 recipient, uint32 nonce, bytes memory payload) external payable returns (uint64 sequence);
function transferTokens(address token, uint256 amount, uint16 recipientChain, bytes32 recipient, uint256 arbiterFee, uint32 nonce) external payable returns (uint64 sequence);
function transferTokensWithPayload(address token, uint256 amount, uint16 recipientChain, bytes32 recipient, uint32 nonce, bytes memory payload) external payable returns (uint64 sequence);
function updateWrapped(bytes memory encodedVm) external returns (address token);
function createWrapped(bytes memory encodedVm) external returns (address token);
function completeTransferWithPayload(bytes memory encodedVm) external returns (bytes memory);
function completeTransferAndUnwrapETHWithPayload(bytes memory encodedVm) external returns (bytes memory);
function completeTransfer(bytes memory encodedVm) external;
function completeTransferAndUnwrapETH(bytes memory encodedVm) external;
function encodeAssetMeta(AssetMeta memory meta) external pure returns (bytes memory encoded);
function encodeTransfer(Transfer memory transfer) external pure returns (bytes memory encoded);
function encodeTransferWithPayload(TransferWithPayload memory transfer) external pure returns (bytes memory encoded);
function parsePayloadID(bytes memory encoded) external pure returns (uint8 payloadID);
function parseAssetMeta(bytes memory encoded) external pure returns (AssetMeta memory meta);
function parseTransfer(bytes memory encoded) external pure returns (Transfer memory transfer);
function parseTransferWithPayload(bytes memory encoded) external pure returns (TransferWithPayload memory transfer);
function governanceActionIsConsumed(bytes32 hash) external view returns (bool);
function isInitialized(address impl) external view returns (bool);
function isTransferCompleted(bytes32 hash) external view returns (bool);
function wormhole() external view returns (IWormhole);
function chainId() external view returns (uint16);
function evmChainId() external view returns (uint256);
function isFork() external view returns (bool);
function governanceChainId() external view returns (uint16);
function governanceContract() external view returns (bytes32);
function wrappedAsset(uint16 tokenChainId, bytes32 tokenAddress) external view returns (address);
function bridgeContracts(uint16 chainId_) external view returns (bytes32);
function tokenImplementation() external view returns (address);
function WETH() external view returns (IWETH);
function outstandingBridged(address token) external view returns (uint256);
function isWrappedAsset(address token) external view returns (bool);
function finality() external view returns (uint8);
function implementation() external view returns (address);
function initialize() external;
function registerChain(bytes memory encodedVM) external;
function upgrade(bytes memory encodedVM) external;
function submitRecoverChainId(bytes memory encodedVM) external;
function parseRegisterChain(bytes memory encoded) external pure returns (RegisterChain memory chain);
function parseUpgrade(bytes memory encoded) external pure returns (UpgradeContract memory chain);
function parseRecoverChainId(bytes memory encodedRecoverChainId) external pure returns (RecoverChainId memory rci);
}
文件 10 的 16:ITokenMessenger.sol
pragma solidity ^0.8.0;
import "./IMessageTransmitter.sol";
import "./ITokenMinter.sol";
interface ITokenMessenger {
function localMessageTransmitter() external view returns (IMessageTransmitter);
function localMinter() external view returns (ITokenMinter);
function depositForBurnWithCaller(
uint256 amount,
uint32 destinationDomain,
bytes32 mintRecipient,
address burnToken,
bytes32 destinationCaller
) external returns (uint64 nonce);
}
文件 11 的 16:ITokenMinter.sol
pragma solidity ^0.8.0;
interface ITokenMinter {
function mint(
uint32 sourceDomain,
bytes32 burnToken,
address to,
uint256 amount
) external returns (address mintToken);
function burn(address burnToken, uint256 amount) external;
function getLocalToken(uint32 remoteDomain, bytes32 remoteToken)
external
view
returns (address);
function setTokenController(address newTokenController) external;
}
文件 12 的 16:IWETH.sol
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IWETH is IERC20 {
function deposit() external payable;
function withdraw(uint amount) external;
}
文件 13 的 16:IWormhole.sol
pragma solidity ^0.8.0;
interface IWormhole {
struct GuardianSet {
address[] keys;
uint32 expirationTime;
}
struct Signature {
bytes32 r;
bytes32 s;
uint8 v;
uint8 guardianIndex;
}
struct VM {
uint8 version;
uint32 timestamp;
uint32 nonce;
uint16 emitterChainId;
bytes32 emitterAddress;
uint64 sequence;
uint8 consistencyLevel;
bytes payload;
uint32 guardianSetIndex;
Signature[] signatures;
bytes32 hash;
}
struct ContractUpgrade {
bytes32 module;
uint8 action;
uint16 chain;
address newContract;
}
struct GuardianSetUpgrade {
bytes32 module;
uint8 action;
uint16 chain;
GuardianSet newGuardianSet;
uint32 newGuardianSetIndex;
}
struct SetMessageFee {
bytes32 module;
uint8 action;
uint16 chain;
uint256 messageFee;
}
struct TransferFees {
bytes32 module;
uint8 action;
uint16 chain;
uint256 amount;
bytes32 recipient;
}
struct RecoverChainId {
bytes32 module;
uint8 action;
uint256 evmChainId;
uint16 newChainId;
}
event LogMessagePublished(address indexed sender, uint64 sequence, uint32 nonce, bytes payload, uint8 consistencyLevel);
event ContractUpgraded(address indexed oldContract, address indexed newContract);
event GuardianSetAdded(uint32 indexed index);
function publishMessage(
uint32 nonce,
bytes memory payload,
uint8 consistencyLevel
) external payable returns (uint64 sequence);
function initialize() external;
function parseAndVerifyVM(bytes calldata encodedVM) external view returns (VM memory vm, bool valid, string memory reason);
function verifyVM(VM memory vm) external view returns (bool valid, string memory reason);
function verifySignatures(bytes32 hash, Signature[] memory signatures, GuardianSet memory guardianSet) external pure returns (bool valid, string memory reason);
function parseVM(bytes memory encodedVM) external pure returns (VM memory vm);
function quorum(uint numGuardians) external pure returns (uint numSignaturesRequiredForQuorum);
function getGuardianSet(uint32 index) external view returns (GuardianSet memory);
function getCurrentGuardianSetIndex() external view returns (uint32);
function getGuardianSetExpiry() external view returns (uint32);
function governanceActionIsConsumed(bytes32 hash) external view returns (bool);
function isInitialized(address impl) external view returns (bool);
function chainId() external view returns (uint16);
function isFork() external view returns (bool);
function governanceChainId() external view returns (uint16);
function governanceContract() external view returns (bytes32);
function messageFee() external view returns (uint256);
function evmChainId() external view returns (uint256);
function nextSequence(address emitter) external view returns (uint64);
function parseContractUpgrade(bytes memory encodedUpgrade) external pure returns (ContractUpgrade memory cu);
function parseGuardianSetUpgrade(bytes memory encodedUpgrade) external pure returns (GuardianSetUpgrade memory gsu);
function parseSetMessageFee(bytes memory encodedSetMessageFee) external pure returns (SetMessageFee memory smf);
function parseTransferFees(bytes memory encodedTransferFees) external pure returns (TransferFees memory tf);
function parseRecoverChainId(bytes memory encodedRecoverChainId) external pure returns (RecoverChainId memory rci);
function submitContractUpgrade(bytes memory _vm) external;
function submitSetMessageFee(bytes memory _vm) external;
function submitNewGuardianSet(bytes memory _vm) external;
function submitTransferFees(bytes memory _vm) external;
function submitRecoverChainId(bytes memory _vm) external;
}
文件 14 的 16:MayanCircle.sol
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./libs/BytesLib.sol";
import "./interfaces/CCTP/IReceiver.sol";
import "./interfaces/CCTP/ITokenMessenger.sol";
import "./interfaces/IWormhole.sol";
import "./interfaces/ITokenBridge.sol";
import "./interfaces/IFeeManager.sol";
contract MayanCircle is ReentrancyGuard {
using SafeERC20 for IERC20;
using BytesLib for bytes;
IWormhole public immutable wormhole;
ITokenMessenger public immutable cctpTokenMessenger;
IFeeManager public feeManager;
uint32 public immutable localDomain;
uint16 public immutable auctionChainId;
bytes32 public immutable auctionAddr;
bytes32 public immutable solanaEmitter;
uint8 public consistencyLevel;
address public guardian;
address nextGuardian;
bool public paused;
mapping(uint64 => FeeLock) public feeStorage;
uint8 constant ETH_DECIMALS = 18;
uint32 constant SOLANA_DOMAIN = 5;
uint16 constant SOLANA_CHAIN_ID = 1;
event OrderFulfilled(uint32 sourceDomain, uint64 sourceNonce, uint256 amount);
event OrderRefunded(uint32 sourceDomain, uint64 sourceNonce, uint256 amount);
error Paused();
error Unauthorized();
error InvalidDomain();
error InvalidNonce();
error InvalidOrder();
error CctpReceiveFailed();
error InvalidGasDrop();
error InvalidAction();
error InvalidEmitter();
enum Action {
NONE,
SWAP,
FULFILL,
BRIDGE_WITH_FEE,
UNLOCK_FEE,
UNLOCK_FEE_REFINE
}
struct Order {
bytes32 trader;
uint16 sourceChain;
bytes32 tokenIn;
uint64 amountIn;
bytes32 destAddr;
uint16 destChain;
bytes32 tokenOut;
uint64 minAmountOut;
uint64 gasDrop;
uint64 redeemFee;
uint64 deadline;
bytes32 referrerAddr;
uint8 referrerBps;
uint8 protocolBps;
}
struct OrderParams {
address tokenIn;
uint256 amountIn;
uint64 gasDrop;
bytes32 destAddr;
uint16 destChain;
bytes32 tokenOut;
uint64 minAmountOut;
uint64 deadline;
uint64 redeemFee;
bytes32 referrerAddr;
uint8 referrerBps;
}
struct ExtraParams {
bytes32 trader;
uint16 sourceChainId;
uint8 protocolBps;
}
struct OrderMsg {
uint8 action;
uint8 payloadId;
bytes32 orderHash;
}
struct FeeLock {
bytes32 destAddr;
uint64 gasDrop;
address token;
uint256 redeemFee;
}
struct CctpRecipient {
uint32 destDomain;
bytes32 mintRecipient;
bytes32 callerAddr;
}
struct BridgeWithFeeMsg {
uint8 action;
uint8 payloadId;
uint64 cctpNonce;
uint32 cctpDomain;
bytes32 destAddr;
uint64 gasDrop;
uint64 redeemFee;
}
struct UnlockFeeMsg {
uint8 action;
uint8 payloadId;
uint64 cctpNonce;
uint32 cctpDomain;
bytes32 unlockerAddr;
uint64 gasDrop;
}
struct UnlockRefinedFeeMsg {
uint8 action;
uint8 payloadId;
uint64 cctpNonce;
uint32 cctpDomain;
bytes32 unlockerAddr;
uint64 gasDrop;
bytes32 destAddr;
}
struct FulfillMsg {
uint8 action;
uint8 payloadId;
uint16 destChainId;
bytes32 destAddr;
bytes32 driver;
bytes32 tokenOut;
uint64 promisedAmount;
uint64 gasDrop;
bytes32 referrerAddr;
uint8 referrerBps;
uint8 protocolBps;
uint64 deadline;
uint64 redeemFee;
uint32 cctpDomain;
uint64 cctpNonce;
}
constructor(
address _cctpTokenMessenger,
address _wormhole,
address _feeManager,
uint16 _auctionChainId,
bytes32 _auctionAddr,
bytes32 _solanaEmitter,
uint8 _consistencyLevel
) {
cctpTokenMessenger = ITokenMessenger(_cctpTokenMessenger);
wormhole = IWormhole(_wormhole);
feeManager = IFeeManager(_feeManager);
auctionChainId = _auctionChainId;
auctionAddr = _auctionAddr;
solanaEmitter = _solanaEmitter;
consistencyLevel = _consistencyLevel;
localDomain = ITokenMessenger(_cctpTokenMessenger).localMessageTransmitter().localDomain();
guardian = msg.sender;
}
function bridgeWithFee(
address tokenIn,
uint256 amountIn,
uint64 redeemFee,
uint64 gasDrop,
bytes32 destAddr,
CctpRecipient memory recipient
) external payable nonReentrant returns (uint64 sequence) {
if (paused) {
revert Paused();
}
uint256 burnAmount = IERC20(tokenIn).balanceOf(address(this));
IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
burnAmount = IERC20(tokenIn).balanceOf(address(this)) - burnAmount;
maxApproveIfNeeded(tokenIn, address(cctpTokenMessenger), burnAmount);
uint64 ccptNonce = cctpTokenMessenger.depositForBurnWithCaller(burnAmount, recipient.destDomain, recipient.mintRecipient, tokenIn, recipient.callerAddr);
BridgeWithFeeMsg memory bridgeMsg = BridgeWithFeeMsg({
action: uint8(Action.BRIDGE_WITH_FEE),
payloadId: 1,
cctpNonce: ccptNonce,
cctpDomain: localDomain,
destAddr: destAddr,
gasDrop: gasDrop,
redeemFee: redeemFee
});
bytes memory encoded = encodeBridgeWithFee(bridgeMsg);
sequence = wormhole.publishMessage{
value : msg.value
}(0, encoded, consistencyLevel);
}
function bridgeWithLockedFee(
address tokenIn,
uint256 amountIn,
uint64 gasDrop,
uint256 redeemFee,
CctpRecipient memory recipient
) external nonReentrant returns (uint64 cctpNonce) {
if (paused) {
revert Paused();
}
if (recipient.destDomain == SOLANA_DOMAIN) {
revert InvalidDomain();
}
require(redeemFee > 0, 'zero redeem fee');
uint256 burnAmount = IERC20(tokenIn).balanceOf(address(this));
IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
burnAmount = IERC20(tokenIn).balanceOf(address(this)) - burnAmount;
maxApproveIfNeeded(tokenIn, address(cctpTokenMessenger), burnAmount - redeemFee);
cctpNonce = cctpTokenMessenger.depositForBurnWithCaller(burnAmount - redeemFee, recipient.destDomain, recipient.mintRecipient, tokenIn, recipient.callerAddr);
feeStorage[cctpNonce] = FeeLock({
destAddr: recipient.mintRecipient,
gasDrop: gasDrop,
token: tokenIn,
redeemFee: redeemFee
});
}
function createOrder(
OrderParams memory params,
CctpRecipient memory recipient
) external payable nonReentrant {
if (paused) {
revert Paused();
}
if (params.tokenOut == bytes32(0) && params.gasDrop > 0) {
revert InvalidGasDrop();
}
IERC20(params.tokenIn).safeTransferFrom(msg.sender, address(this), params.amountIn);
maxApproveIfNeeded(params.tokenIn, address(cctpTokenMessenger), params.amountIn);
uint64 ccptNonce = cctpTokenMessenger.depositForBurnWithCaller(params.amountIn, recipient.destDomain, recipient.mintRecipient, params.tokenIn, recipient.callerAddr);
require(params.referrerBps <= 50, 'invalid referrer bps');
uint8 protocolBps = feeManager.calcProtocolBps(uint64(params.amountIn), params.tokenIn, params.tokenOut, params.destChain, params.referrerBps);
require(protocolBps <= 50, 'invalid protocol bps');
Order memory order = Order({
trader: bytes32(uint256(uint160(msg.sender))),
sourceChain: wormhole.chainId(),
tokenIn: bytes32(uint256(uint160(params.tokenIn))),
amountIn: uint64(params.amountIn),
destAddr: params.destAddr,
destChain: params.destChain,
tokenOut: params.tokenOut,
minAmountOut: params.minAmountOut,
gasDrop: params.gasDrop,
redeemFee: params.redeemFee,
deadline: params.deadline,
referrerAddr: params.referrerAddr,
referrerBps: params.referrerBps,
protocolBps: protocolBps
});
bytes memory encodedOrder = encodeOrder(order);
encodedOrder = encodedOrder.concat(abi.encodePacked(ccptNonce, cctpTokenMessenger.localMessageTransmitter().localDomain()));
bytes32 orderHash = keccak256(encodedOrder);
OrderMsg memory orderMsg = OrderMsg({
action: uint8(Action.SWAP),
payloadId: 1,
orderHash: orderHash
});
bytes memory encodedMsg = encodeOrderMsg(orderMsg);
wormhole.publishMessage{
value : msg.value
}(0, encodedMsg, consistencyLevel);
}
function redeemWithFee(bytes memory cctpMsg, bytes memory cctpSigs, bytes memory encodedVm) external nonReentrant payable {
(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole.parseAndVerifyVM(encodedVm);
require(valid, reason);
if (vm.emitterAddress != solanaEmitter && truncateAddress(vm.emitterAddress) != address(this)) {
revert InvalidEmitter();
}
BridgeWithFeeMsg memory redeemMsg = parseBridgeWithFee(vm.payload);
if (redeemMsg.action != uint8(Action.BRIDGE_WITH_FEE)) {
revert InvalidAction();
}
uint256 denormalizedGasDrop = deNormalizeAmount(redeemMsg.gasDrop, ETH_DECIMALS);
if (msg.value != denormalizedGasDrop) {
revert InvalidGasDrop();
}
uint32 cctpSourceDomain = cctpMsg.toUint32(4);
uint64 cctpNonce = cctpMsg.toUint64(12);
bytes32 cctpSourceToken = cctpMsg.toBytes32(120);
if (cctpSourceDomain != redeemMsg.cctpDomain) {
revert InvalidDomain();
}
if (cctpNonce != redeemMsg.cctpNonce) {
revert InvalidNonce();
}
address localToken = cctpTokenMessenger.localMinter().getLocalToken(cctpSourceDomain, cctpSourceToken);
uint256 amount = IERC20(localToken).balanceOf(address(this));
bool success = cctpTokenMessenger.localMessageTransmitter().receiveMessage(cctpMsg, cctpSigs);
if (!success) {
revert CctpReceiveFailed();
}
amount = IERC20(localToken).balanceOf(address(this)) - amount;
IERC20(localToken).safeTransfer(msg.sender, uint256(redeemMsg.redeemFee));
address recipient = truncateAddress(redeemMsg.destAddr);
IERC20(localToken).safeTransfer(recipient, amount - uint256(redeemMsg.redeemFee));
payable(recipient).transfer(denormalizedGasDrop);
}
function redeemWithLockedFee(bytes memory cctpMsg, bytes memory cctpSigs, bytes32 unlockerAddr) external nonReentrant payable returns (uint64 sequence) {
uint32 cctpSourceDomain = cctpMsg.toUint32(4);
uint64 cctpNonce = cctpMsg.toUint64(12);
address caller = truncateAddress(cctpMsg.toBytes32(84));
require(caller == address(this), 'invalid caller');
address mintRecipient = truncateAddress(cctpMsg.toBytes32(152));
require(mintRecipient != address(this), 'invalid mint recipient');
bool success = cctpTokenMessenger.localMessageTransmitter().receiveMessage(cctpMsg, cctpSigs);
if (!success) {
revert CctpReceiveFailed();
}
uint256 wormholeFee = wormhole.messageFee();
payable(mintRecipient).transfer(msg.value - wormholeFee);
UnlockFeeMsg memory unlockMsg = UnlockFeeMsg({
action: uint8(Action.UNLOCK_FEE),
payloadId: 1,
cctpDomain: cctpSourceDomain,
cctpNonce: cctpNonce,
unlockerAddr: unlockerAddr,
gasDrop: uint64(normalizeAmount(msg.value - wormholeFee, ETH_DECIMALS))
});
bytes memory encodedMsg = encodeUnlockFeeMsg(unlockMsg);
sequence = wormhole.publishMessage{
value : wormholeFee
}(0, encodedMsg, consistencyLevel);
}
function refineFee(uint32 cctpNonce, uint32 cctpDomain, bytes32 destAddr, bytes32 unlockerAddr) external nonReentrant payable returns (uint64 sequence) {
uint256 wormholeFee = wormhole.messageFee();
payable(truncateAddress(destAddr)).transfer(msg.value - wormholeFee);
UnlockRefinedFeeMsg memory unlockMsg = UnlockRefinedFeeMsg({
action: uint8(Action.UNLOCK_FEE_REFINE),
payloadId: 1,
cctpDomain: cctpDomain,
cctpNonce: cctpNonce,
unlockerAddr: unlockerAddr,
gasDrop: uint64(normalizeAmount(msg.value - wormholeFee, ETH_DECIMALS)),
destAddr: destAddr
});
bytes memory encodedMsg = encodeUnlockRefinedFeeMsg(unlockMsg);
sequence = wormhole.publishMessage{
value : wormholeFee
}(0, encodedMsg, consistencyLevel);
}
function unlockFee(bytes memory encodedVm) public nonReentrant {
(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole.parseAndVerifyVM(encodedVm);
require(valid, reason);
if (vm.emitterChainId == SOLANA_CHAIN_ID) {
require(vm.emitterAddress == solanaEmitter, 'invalid solana emitter');
} else {
require(truncateAddress(vm.emitterAddress) == address(this), 'invalid evm emitter');
}
UnlockFeeMsg memory unlockMsg = parseUnlockFeeMsg(vm.payload);
if (unlockMsg.action != uint8(Action.UNLOCK_FEE)) {
revert InvalidAction();
}
if (unlockMsg.cctpDomain != localDomain) {
revert InvalidDomain();
}
FeeLock memory feeLock = feeStorage[unlockMsg.cctpNonce];
require(feeLock.redeemFee > 0, 'fee not locked');
if (unlockMsg.gasDrop < feeLock.gasDrop) {
revert InvalidGasDrop();
}
IERC20(feeLock.token).safeTransfer(truncateAddress(unlockMsg.unlockerAddr), feeLock.redeemFee);
delete feeStorage[unlockMsg.cctpNonce];
}
function unlockFeeRefined(bytes memory encodedVm1, bytes memory encodedVm2) public nonReentrant {
(IWormhole.VM memory vm1, bool valid1, string memory reason1) = wormhole.parseAndVerifyVM(encodedVm1);
require(valid1, reason1);
if (vm1.emitterAddress != solanaEmitter && truncateAddress(vm1.emitterAddress) != address(this)) {
revert InvalidEmitter();
}
UnlockFeeMsg memory unlockMsg = parseUnlockFeeMsg(vm1.payload);
if (unlockMsg.action != uint8(Action.UNLOCK_FEE_REFINE)) {
revert InvalidAction();
}
if (unlockMsg.cctpDomain != localDomain) {
revert InvalidDomain();
}
FeeLock memory feeLock = feeStorage[unlockMsg.cctpNonce];
require(feeLock.redeemFee > 0, 'fee not locked');
require(unlockMsg.gasDrop < feeLock.gasDrop, 'gas was sufficient');
(IWormhole.VM memory vm2, bool valid2, string memory reason2) = wormhole.parseAndVerifyVM(encodedVm2);
require(valid2, reason2);
if (vm2.emitterAddress != solanaEmitter && truncateAddress(vm2.emitterAddress) != address(this)) {
revert InvalidEmitter();
}
UnlockRefinedFeeMsg memory refinedMsg = parseUnlockRefinedFee(vm1.payload);
require(refinedMsg.destAddr == feeLock.destAddr, 'invalid dest addr');
if (refinedMsg.cctpNonce != unlockMsg.cctpNonce) {
revert InvalidNonce();
}
if (refinedMsg.cctpDomain != unlockMsg.cctpDomain) {
revert InvalidDomain();
}
if (refinedMsg.gasDrop + unlockMsg.gasDrop < feeLock.gasDrop) {
revert InvalidGasDrop();
}
IERC20(feeLock.token).safeTransfer(truncateAddress(refinedMsg.unlockerAddr), feeLock.redeemFee);
delete feeStorage[unlockMsg.cctpNonce];
}
function fulfillOrder(
bytes memory cctpMsg,
bytes memory cctpSigs,
bytes memory encodedVm,
address swapProtocol,
bytes memory swapData
) public nonReentrant payable {
(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole.parseAndVerifyVM(encodedVm);
require(valid, reason);
require(vm.emitterChainId == SOLANA_CHAIN_ID, 'invalid emitter chain');
require(vm.emitterAddress == auctionAddr, 'invalid solana emitter');
FulfillMsg memory fulfillMsg = parseFulfillMsg(vm.payload);
require(fulfillMsg.deadline >= block.timestamp, 'deadline passed');
require(msg.sender == truncateAddress(fulfillMsg.driver), 'invalid driver');
uint32 cctpSourceDomain = cctpMsg.toUint32(4);
uint64 cctpSourceNonce = cctpMsg.toUint64(12);
bytes32 cctpSourceToken = cctpMsg.toBytes32(120);
require(cctpSourceDomain == fulfillMsg.cctpDomain, 'invalid cctp domain');
require(cctpSourceNonce == fulfillMsg.cctpNonce, 'invalid cctp nonce');
(address localToken, uint256 cctpAmount) = receiveCctp(cctpMsg, cctpSigs, cctpSourceDomain, cctpSourceToken);
if (fulfillMsg.redeemFee > 0) {
IERC20(localToken).transfer(msg.sender, fulfillMsg.redeemFee);
}
address tokenOut = truncateAddress(fulfillMsg.tokenOut);
maxApproveIfNeeded(localToken, swapProtocol, cctpAmount - uint256(fulfillMsg.redeemFee));
uint256 amountOut;
if (tokenOut == address(0)) {
amountOut = address(this).balance;
} else {
amountOut = IERC20(tokenOut).balanceOf(address(this));
}
(bool swapSuccess, bytes memory swapReturn) = swapProtocol.call{value: 0}(swapData);
require(swapSuccess, string(swapReturn));
if (tokenOut == address(0)) {
amountOut = address(this).balance - amountOut;
} else {
amountOut = IERC20(tokenOut).balanceOf(address(this)) - amountOut;
}
uint8 decimals;
if (tokenOut == address(0)) {
decimals = ETH_DECIMALS;
} else {
decimals = decimalsOf(tokenOut);
}
uint256 promisedAmount = deNormalizeAmount(fulfillMsg.promisedAmount, decimals);
require(amountOut >= promisedAmount, 'insufficient amount out');
makePayments(
fulfillMsg,
tokenOut,
amountOut
);
emit OrderFulfilled(cctpSourceDomain, cctpSourceNonce, amountOut);
}
function refund(
bytes memory encodedVm,
bytes memory cctpMsg,
bytes memory cctpSigs,
OrderParams memory orderParams,
ExtraParams memory extraParams
) public nonReentrant payable {
(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole.parseAndVerifyVM(encodedVm);
require(valid, reason);
if (vm.emitterAddress != solanaEmitter && truncateAddress(vm.emitterAddress) != address(this)) {
revert InvalidEmitter();
}
uint32 cctpSourceDomain = cctpMsg.toUint32(4);
uint64 cctpSourceNonce = cctpMsg.toUint64(12);
bytes32 cctpSourceToken = cctpMsg.toBytes32(120);
(address localToken, uint256 amount) = receiveCctp(cctpMsg, cctpSigs, cctpSourceDomain, cctpSourceToken);
Order memory order = recreateOrder(cctpSourceToken, uint64(amount), orderParams, extraParams);
bytes memory encodedOrder = encodeOrder(order);
encodedOrder = encodedOrder.concat(abi.encodePacked(cctpSourceNonce, cctpSourceDomain));
bytes32 calculatedHash = keccak256(encodedOrder);
OrderMsg memory orderMsg = parseOrderMsg(vm.payload);
if (orderMsg.action != uint8(Action.SWAP)) {
revert InvalidAction();
}
if (calculatedHash != orderMsg.orderHash) {
revert InvalidOrder();
}
require(order.deadline < block.timestamp, 'deadline not passed');
uint256 gasDrop = deNormalizeAmount(order.gasDrop, ETH_DECIMALS);
if (msg.value != gasDrop) {
revert InvalidGasDrop();
}
address destAddr = truncateAddress(order.destAddr);
if (gasDrop > 0) {
payable(destAddr).transfer(gasDrop);
}
IERC20(localToken).safeTransfer(msg.sender, order.redeemFee);
IERC20(localToken).safeTransfer(destAddr, amount - order.redeemFee);
emit OrderRefunded(cctpSourceDomain, cctpSourceNonce, amount);
}
function receiveCctp(bytes memory cctpMsg, bytes memory cctpSigs, uint32 cctpSourceDomain, bytes32 cctpSourceToken) internal returns (address, uint256) {
address localToken = cctpTokenMessenger.localMinter().getLocalToken(cctpSourceDomain, cctpSourceToken);
uint256 amount = IERC20(localToken).balanceOf(address(this));
bool success = cctpTokenMessenger.localMessageTransmitter().receiveMessage(cctpMsg, cctpSigs);
if (!success) {
revert CctpReceiveFailed();
}
amount = IERC20(localToken).balanceOf(address(this)) - amount;
return (localToken, amount);
}
function makePayments(
FulfillMsg memory fulfillMsg,
address tokenOut,
uint256 amount
) internal {
address referrerAddr = truncateAddress(fulfillMsg.referrerAddr);
uint256 referrerAmount = 0;
if (referrerAddr != address(0) && fulfillMsg.referrerBps != 0) {
referrerAmount = amount * fulfillMsg.referrerBps / 10000;
}
uint256 protocolAmount = 0;
if (fulfillMsg.protocolBps != 0) {
protocolAmount = amount * fulfillMsg.protocolBps / 10000;
}
address destAddr = truncateAddress(fulfillMsg.destAddr);
if (tokenOut == address(0)) {
if (referrerAmount > 0) {
payable(referrerAddr).transfer(referrerAmount);
}
if (protocolAmount > 0) {
payable(feeManager.feeCollector()).transfer(protocolAmount);
}
payable(destAddr).transfer(amount - referrerAmount - protocolAmount);
} else {
if (fulfillMsg.gasDrop > 0) {
uint256 gasDrop = deNormalizeAmount(fulfillMsg.gasDrop, ETH_DECIMALS);
if (msg.value != gasDrop) {
revert InvalidGasDrop();
}
payable(destAddr).transfer(gasDrop);
}
if (referrerAmount > 0) {
IERC20(tokenOut).safeTransfer(referrerAddr, referrerAmount);
}
if (protocolAmount > 0) {
IERC20(tokenOut).safeTransfer(feeManager.feeCollector(), protocolAmount);
}
IERC20(tokenOut).safeTransfer(destAddr, amount - referrerAmount - protocolAmount);
}
}
function recreateOrder(
bytes32 cctpSourceToken,
uint64 amountIn,
OrderParams memory params,
ExtraParams memory extraParams
) internal pure returns (Order memory) {
return Order({
trader: extraParams.trader,
sourceChain: extraParams.sourceChainId,
tokenIn: cctpSourceToken,
amountIn: amountIn,
destAddr: params.destAddr,
destChain: params.destChain,
tokenOut: params.tokenOut,
minAmountOut: params.minAmountOut,
gasDrop: params.gasDrop,
redeemFee: params.redeemFee,
deadline: params.deadline,
referrerAddr: params.referrerAddr,
referrerBps: params.referrerBps,
protocolBps: extraParams.protocolBps
});
}
function encodeBridgeWithFee(BridgeWithFeeMsg memory bridgeMsg) internal pure returns (bytes memory) {
return abi.encodePacked(
bridgeMsg.action,
bridgeMsg.payloadId,
bridgeMsg.cctpNonce,
bridgeMsg.cctpDomain,
bridgeMsg.destAddr,
bridgeMsg.gasDrop,
bridgeMsg.redeemFee
);
}
function parseBridgeWithFee(bytes memory payload) internal pure returns (BridgeWithFeeMsg memory) {
return BridgeWithFeeMsg({
action: payload.toUint8(0),
payloadId: payload.toUint8(1),
cctpNonce: payload.toUint64(2),
cctpDomain: payload.toUint32(10),
destAddr: payload.toBytes32(14),
gasDrop: payload.toUint64(46),
redeemFee: payload.toUint64(54)
});
}
function encodeUnlockFeeMsg(UnlockFeeMsg memory unlockMsg) internal pure returns (bytes memory) {
return abi.encodePacked(
unlockMsg.action,
unlockMsg.payloadId,
unlockMsg.cctpNonce,
unlockMsg.cctpDomain,
unlockMsg.unlockerAddr,
unlockMsg.gasDrop
);
}
function encodeUnlockRefinedFeeMsg(UnlockRefinedFeeMsg memory unlockMsg) internal pure returns (bytes memory) {
return abi.encodePacked(
unlockMsg.action,
unlockMsg.payloadId,
unlockMsg.cctpNonce,
unlockMsg.cctpDomain,
unlockMsg.unlockerAddr,
unlockMsg.gasDrop,
unlockMsg.destAddr
);
}
function parseFulfillMsg(bytes memory encoded) public pure returns (FulfillMsg memory fulfillMsg) {
uint index = 0;
fulfillMsg.action = encoded.toUint8(index);
index += 1;
if (fulfillMsg.action != uint8(Action.FULFILL)) {
revert InvalidAction();
}
fulfillMsg.payloadId = encoded.toUint8(index);
index += 1;
fulfillMsg.destChainId = encoded.toUint16(index);
index += 2;
fulfillMsg.destAddr = encoded.toBytes32(index);
index += 32;
fulfillMsg.driver = encoded.toBytes32(index);
index += 32;
fulfillMsg.tokenOut = encoded.toBytes32(index);
index += 32;
fulfillMsg.promisedAmount = encoded.toUint64(index);
index += 8;
fulfillMsg.gasDrop = encoded.toUint64(index);
index += 8;
fulfillMsg.referrerAddr = encoded.toBytes32(index);
index += 32;
fulfillMsg.referrerBps = encoded.toUint8(index);
index += 1;
fulfillMsg.protocolBps = encoded.toUint8(index);
index += 1;
fulfillMsg.deadline = encoded.toUint64(index);
index += 8;
fulfillMsg.redeemFee = encoded.toUint64(index);
index += 8;
fulfillMsg.cctpDomain = encoded.toUint32(index);
index += 4;
fulfillMsg.cctpNonce = encoded.toUint64(index);
index += 8;
}
function parseOrderMsg(bytes memory payload) internal pure returns (OrderMsg memory) {
return OrderMsg({
action: payload.toUint8(0),
payloadId: payload.toUint8(1),
orderHash: payload.toBytes32(2)
});
}
function parseUnlockFeeMsg(bytes memory payload) internal pure returns (UnlockFeeMsg memory) {
return UnlockFeeMsg({
action: payload.toUint8(0),
payloadId: payload.toUint8(1),
cctpNonce: payload.toUint64(2),
cctpDomain: payload.toUint32(10),
unlockerAddr: payload.toBytes32(14),
gasDrop: payload.toUint64(46)
});
}
function parseUnlockRefinedFee(bytes memory payload) internal pure returns (UnlockRefinedFeeMsg memory) {
return UnlockRefinedFeeMsg({
action: payload.toUint8(0),
payloadId: payload.toUint8(1),
cctpNonce: payload.toUint64(2),
cctpDomain: payload.toUint32(10),
unlockerAddr: payload.toBytes32(14),
gasDrop: payload.toUint64(46),
destAddr: payload.toBytes32(54)
});
}
function encodeOrder(Order memory order) internal pure returns (bytes memory) {
return abi.encodePacked(
order.trader,
order.sourceChain,
order.tokenIn,
order.amountIn,
order.destAddr,
order.destChain,
order.tokenOut,
order.minAmountOut,
order.gasDrop,
order.redeemFee,
order.deadline,
order.referrerAddr,
order.referrerBps,
order.protocolBps
);
}
function encodeOrderMsg(OrderMsg memory orderMsg) internal pure returns (bytes memory) {
return abi.encodePacked(
orderMsg.action,
orderMsg.payloadId,
orderMsg.orderHash
);
}
function maxApproveIfNeeded(address tokenAddr, address spender, uint256 amount) internal {
IERC20 token = IERC20(tokenAddr);
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < amount) {
token.safeApprove(spender, 0);
token.safeApprove(spender, type(uint256).max);
}
}
function decimalsOf(address token) internal view returns(uint8) {
(,bytes memory queriedDecimals) = token.staticcall(abi.encodeWithSignature('decimals()'));
return abi.decode(queriedDecimals, (uint8));
}
function normalizeAmount(uint256 amount, uint8 decimals) internal pure returns(uint256) {
if (decimals > 8) {
amount /= 10 ** (decimals - 8);
}
return amount;
}
function deNormalizeAmount(uint256 amount, uint8 decimals) internal pure returns(uint256) {
if (decimals > 8) {
amount *= 10 ** (decimals - 8);
}
return amount;
}
function truncateAddress(bytes32 b) internal pure returns (address) {
require(bytes12(b) == 0, 'invalid EVM address');
return address(uint160(uint256(b)));
}
function setFeeManager(address _feeManager) public {
if (msg.sender != guardian) {
revert Unauthorized();
}
feeManager = IFeeManager(_feeManager);
}
function setConsistencyLevel(uint8 _consistencyLevel) public {
if (msg.sender != guardian) {
revert Unauthorized();
}
consistencyLevel = _consistencyLevel;
}
function setPause(bool _pause) public {
if (msg.sender != guardian) {
revert Unauthorized();
}
paused = _pause;
}
function isPaused() public view returns(bool) {
return paused;
}
function rescueToken(address token, uint256 amount, address to) public {
if (msg.sender != guardian) {
revert Unauthorized();
}
IERC20(token).safeTransfer(to, amount);
}
function rescueEth(uint256 amount, address payable to) public {
if (msg.sender != guardian) {
revert Unauthorized();
}
require(to != address(0), 'transfer to the zero address');
to.transfer(amount);
}
function changeGuardian(address newGuardian) public {
if (msg.sender != guardian) {
revert Unauthorized();
}
nextGuardian = newGuardian;
}
function claimGuardian() public {
if (msg.sender != nextGuardian) {
revert Unauthorized();
}
guardian = nextGuardian;
}
receive() external payable {}
}
文件 15 的 16:ReentrancyGuard.sol
pragma solidity ^0.8.0;
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
}
function _nonReentrantAfter() private {
_status = _NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}
文件 16 的 16:SafeERC20.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
library SafeERC20 {
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(IERC20 token, address spender, uint256 value) internal {
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}
{
"compilationTarget": {
"src/MayanCircle.sol": "MayanCircle"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_cctpTokenMessenger","type":"address"},{"internalType":"address","name":"_wormhole","type":"address"},{"internalType":"address","name":"_feeManager","type":"address"},{"internalType":"uint16","name":"_auctionChainId","type":"uint16"},{"internalType":"bytes32","name":"_auctionAddr","type":"bytes32"},{"internalType":"bytes32","name":"_solanaEmitter","type":"bytes32"},{"internalType":"uint8","name":"_consistencyLevel","type":"uint8"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CctpReceiveFailed","type":"error"},{"inputs":[],"name":"InvalidAction","type":"error"},{"inputs":[],"name":"InvalidDomain","type":"error"},{"inputs":[],"name":"InvalidEmitter","type":"error"},{"inputs":[],"name":"InvalidGasDrop","type":"error"},{"inputs":[],"name":"InvalidNonce","type":"error"},{"inputs":[],"name":"InvalidOrder","type":"error"},{"inputs":[],"name":"Paused","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"sourceDomain","type":"uint32"},{"indexed":false,"internalType":"uint64","name":"sourceNonce","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"OrderFulfilled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"sourceDomain","type":"uint32"},{"indexed":false,"internalType":"uint64","name":"sourceNonce","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"OrderRefunded","type":"event"},{"inputs":[],"name":"auctionAddr","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"auctionChainId","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint64","name":"redeemFee","type":"uint64"},{"internalType":"uint64","name":"gasDrop","type":"uint64"},{"internalType":"bytes32","name":"destAddr","type":"bytes32"},{"components":[{"internalType":"uint32","name":"destDomain","type":"uint32"},{"internalType":"bytes32","name":"mintRecipient","type":"bytes32"},{"internalType":"bytes32","name":"callerAddr","type":"bytes32"}],"internalType":"struct MayanCircle.CctpRecipient","name":"recipient","type":"tuple"}],"name":"bridgeWithFee","outputs":[{"internalType":"uint64","name":"sequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint64","name":"gasDrop","type":"uint64"},{"internalType":"uint256","name":"redeemFee","type":"uint256"},{"components":[{"internalType":"uint32","name":"destDomain","type":"uint32"},{"internalType":"bytes32","name":"mintRecipient","type":"bytes32"},{"internalType":"bytes32","name":"callerAddr","type":"bytes32"}],"internalType":"struct MayanCircle.CctpRecipient","name":"recipient","type":"tuple"}],"name":"bridgeWithLockedFee","outputs":[{"internalType":"uint64","name":"cctpNonce","type":"uint64"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cctpTokenMessenger","outputs":[{"internalType":"contract ITokenMessenger","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newGuardian","type":"address"}],"name":"changeGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"consistencyLevel","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint64","name":"gasDrop","type":"uint64"},{"internalType":"bytes32","name":"destAddr","type":"bytes32"},{"internalType":"uint16","name":"destChain","type":"uint16"},{"internalType":"bytes32","name":"tokenOut","type":"bytes32"},{"internalType":"uint64","name":"minAmountOut","type":"uint64"},{"internalType":"uint64","name":"deadline","type":"uint64"},{"internalType":"uint64","name":"redeemFee","type":"uint64"},{"internalType":"bytes32","name":"referrerAddr","type":"bytes32"},{"internalType":"uint8","name":"referrerBps","type":"uint8"}],"internalType":"struct MayanCircle.OrderParams","name":"params","type":"tuple"},{"components":[{"internalType":"uint32","name":"destDomain","type":"uint32"},{"internalType":"bytes32","name":"mintRecipient","type":"bytes32"},{"internalType":"bytes32","name":"callerAddr","type":"bytes32"}],"internalType":"struct MayanCircle.CctpRecipient","name":"recipient","type":"tuple"}],"name":"createOrder","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"feeManager","outputs":[{"internalType":"contract IFeeManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"}],"name":"feeStorage","outputs":[{"internalType":"bytes32","name":"destAddr","type":"bytes32"},{"internalType":"uint64","name":"gasDrop","type":"uint64"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"redeemFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"cctpMsg","type":"bytes"},{"internalType":"bytes","name":"cctpSigs","type":"bytes"},{"internalType":"bytes","name":"encodedVm","type":"bytes"},{"internalType":"address","name":"swapProtocol","type":"address"},{"internalType":"bytes","name":"swapData","type":"bytes"}],"name":"fulfillOrder","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"guardian","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"localDomain","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"encoded","type":"bytes"}],"name":"parseFulfillMsg","outputs":[{"components":[{"internalType":"uint8","name":"action","type":"uint8"},{"internalType":"uint8","name":"payloadId","type":"uint8"},{"internalType":"uint16","name":"destChainId","type":"uint16"},{"internalType":"bytes32","name":"destAddr","type":"bytes32"},{"internalType":"bytes32","name":"driver","type":"bytes32"},{"internalType":"bytes32","name":"tokenOut","type":"bytes32"},{"internalType":"uint64","name":"promisedAmount","type":"uint64"},{"internalType":"uint64","name":"gasDrop","type":"uint64"},{"internalType":"bytes32","name":"referrerAddr","type":"bytes32"},{"internalType":"uint8","name":"referrerBps","type":"uint8"},{"internalType":"uint8","name":"protocolBps","type":"uint8"},{"internalType":"uint64","name":"deadline","type":"uint64"},{"internalType":"uint64","name":"redeemFee","type":"uint64"},{"internalType":"uint32","name":"cctpDomain","type":"uint32"},{"internalType":"uint64","name":"cctpNonce","type":"uint64"}],"internalType":"struct MayanCircle.FulfillMsg","name":"fulfillMsg","type":"tuple"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"cctpMsg","type":"bytes"},{"internalType":"bytes","name":"cctpSigs","type":"bytes"},{"internalType":"bytes","name":"encodedVm","type":"bytes"}],"name":"redeemWithFee","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"cctpMsg","type":"bytes"},{"internalType":"bytes","name":"cctpSigs","type":"bytes"},{"internalType":"bytes32","name":"unlockerAddr","type":"bytes32"}],"name":"redeemWithLockedFee","outputs":[{"internalType":"uint64","name":"sequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint32","name":"cctpNonce","type":"uint32"},{"internalType":"uint32","name":"cctpDomain","type":"uint32"},{"internalType":"bytes32","name":"destAddr","type":"bytes32"},{"internalType":"bytes32","name":"unlockerAddr","type":"bytes32"}],"name":"refineFee","outputs":[{"internalType":"uint64","name":"sequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"encodedVm","type":"bytes"},{"internalType":"bytes","name":"cctpMsg","type":"bytes"},{"internalType":"bytes","name":"cctpSigs","type":"bytes"},{"components":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint64","name":"gasDrop","type":"uint64"},{"internalType":"bytes32","name":"destAddr","type":"bytes32"},{"internalType":"uint16","name":"destChain","type":"uint16"},{"internalType":"bytes32","name":"tokenOut","type":"bytes32"},{"internalType":"uint64","name":"minAmountOut","type":"uint64"},{"internalType":"uint64","name":"deadline","type":"uint64"},{"internalType":"uint64","name":"redeemFee","type":"uint64"},{"internalType":"bytes32","name":"referrerAddr","type":"bytes32"},{"internalType":"uint8","name":"referrerBps","type":"uint8"}],"internalType":"struct MayanCircle.OrderParams","name":"orderParams","type":"tuple"},{"components":[{"internalType":"bytes32","name":"trader","type":"bytes32"},{"internalType":"uint16","name":"sourceChainId","type":"uint16"},{"internalType":"uint8","name":"protocolBps","type":"uint8"}],"internalType":"struct MayanCircle.ExtraParams","name":"extraParams","type":"tuple"}],"name":"refund","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address payable","name":"to","type":"address"}],"name":"rescueEth","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"rescueToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"_consistencyLevel","type":"uint8"}],"name":"setConsistencyLevel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_feeManager","type":"address"}],"name":"setFeeManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_pause","type":"bool"}],"name":"setPause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"solanaEmitter","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"encodedVm","type":"bytes"}],"name":"unlockFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"encodedVm1","type":"bytes"},{"internalType":"bytes","name":"encodedVm2","type":"bytes"}],"name":"unlockFeeRefined","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wormhole","outputs":[{"internalType":"contract IWormhole","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]