文件 1 的 11: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 的 11: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 的 11:IERC1271.sol
pragma solidity ^0.8.0;
interface IERC1271 {
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}
文件 4 的 11: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);
}
文件 5 的 11: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);
}
文件 6 的 11: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);
}
文件 7 的 11: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;
}
文件 8 的 11:MayanSwift.sol
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "./interfaces/IWormhole.sol";
import "./interfaces/IFeeManager.sol";
import "./libs/BytesLib.sol";
import "./libs/SignatureVerifier.sol";
contract MayanSwift is ReentrancyGuard {
event OrderCreated(bytes32 key);
event OrderFulfilled(bytes32 key, uint64 sequence, uint256 netAmount);
event OrderUnlocked(bytes32 key);
event OrderCanceled(bytes32 key, uint64 sequence);
event OrderRefunded(bytes32 key, uint256 netAmount);
using SafeERC20 for IERC20;
using BytesLib for bytes;
using SignatureVerifier for bytes;
uint16 constant SOLANA_CHAIN_ID = 1;
uint8 constant BPS_FEE_LIMIT = 50;
uint8 constant NATIVE_DECIMALS = 18;
IWormhole public immutable wormhole;
uint16 public immutable auctionChainId;
bytes32 public immutable auctionAddr;
bytes32 public immutable solanaEmitter;
IFeeManager public feeManager;
uint8 public consistencyLevel;
address public guardian;
address public nextGuardian;
bool public paused;
bytes32 private domainSeparator;
mapping(bytes32 => Order) public orders;
mapping(bytes32 => UnlockMsg) public unlockMsgs;
error Paused();
error Unauthorized();
error InvalidAction();
error InvalidBpsFee();
error InvalidOrderStatus();
error InvalidOrderHash();
error InvalidEmitterChain();
error InvalidEmitterAddress();
error InvalidSrcChain();
error OrderNotExists();
error SmallAmountIn();
error FeesTooHigh();
error InvalidGasDrop();
error InvalidDestChain();
error DuplicateOrder();
error InvalidAmount();
error DeadlineViolation();
error InvalidWormholeFee();
error InvalidAuctionMode();
error InvalidEvmAddr();
struct Order {
Status status;
uint64 amountIn;
uint16 destChainId;
}
struct OrderParams {
bytes32 trader;
bytes32 tokenOut;
uint64 minAmountOut;
uint64 gasDrop;
uint64 cancelFee;
uint64 refundFee;
uint64 deadline;
bytes32 destAddr;
uint16 destChainId;
bytes32 referrerAddr;
uint8 referrerBps;
uint8 auctionMode;
bytes32 random;
}
struct PermitParams {
uint256 value;
uint256 deadline;
uint8 v;
bytes32 r;
bytes32 s;
}
struct Key {
bytes32 trader;
uint16 srcChainId;
bytes32 tokenIn;
bytes32 destAddr;
uint16 destChainId;
bytes32 tokenOut;
uint64 minAmountOut;
uint64 gasDrop;
uint64 cancelFee;
uint64 refundFee;
uint64 deadline;
bytes32 referrerAddr;
uint8 referrerBps;
uint8 protocolBps;
uint8 auctionMode;
bytes32 random;
}
struct PaymentParams {
address destAddr;
address tokenOut;
uint64 promisedAmount;
uint64 gasDrop;
address referrerAddr;
uint8 referrerBps;
uint8 protocolBps;
bool batch;
}
enum Status {
CREATED,
FULFILLED,
UNLOCKED,
CANCELED,
REFUNDED
}
enum Action {
NONE,
FULFILL,
UNLOCK,
REFUND,
BATCH_UNLOCK
}
enum AuctionMode {
NONE,
BYPASS,
ENGLISH
}
struct UnlockMsg {
uint8 action;
bytes32 orderHash;
uint16 srcChainId;
bytes32 tokenIn;
bytes32 recipient;
}
struct RefundMsg {
uint8 action;
bytes32 orderHash;
uint16 srcChainId;
bytes32 tokenIn;
bytes32 recipient;
bytes32 canceler;
uint64 cancelFee;
uint64 refundFee;
}
struct FulfillMsg {
uint8 action;
bytes32 orderHash;
uint16 destChainId;
bytes32 destAddr;
bytes32 driver;
bytes32 tokenOut;
uint64 promisedAmount;
uint64 gasDrop;
uint64 deadline;
bytes32 referrerAddr;
uint8 referrerBps;
uint8 protocolBps;
uint16 srcChainId;
bytes32 tokenIn;
}
struct TransferParams {
address from;
uint256 validAfter;
uint256 validBefore;
}
constructor(
address _wormhole,
address _feeManager,
uint16 _auctionChainId,
bytes32 _auctionAddr,
bytes32 _solanaEmitter,
uint8 _consistencyLevel
) {
guardian = msg.sender;
wormhole = IWormhole(_wormhole);
feeManager = IFeeManager(_feeManager);
auctionChainId = _auctionChainId;
auctionAddr = _auctionAddr;
solanaEmitter = _solanaEmitter;
consistencyLevel = _consistencyLevel;
domainSeparator = keccak256(abi.encode(
keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"),
keccak256("Mayan Swift"),
uint256(block.chainid),
address(this)
));
}
function createOrderWithEth(OrderParams memory params) nonReentrant external payable returns (bytes32 orderHash) {
if (paused) {
revert Paused();
}
uint64 normlizedAmountIn = uint64(normalizeAmount(msg.value, NATIVE_DECIMALS));
if (normlizedAmountIn == 0) {
revert SmallAmountIn();
}
if (params.cancelFee + params.refundFee >= normlizedAmountIn) {
revert FeesTooHigh();
}
if (params.tokenOut == bytes32(0) && params.gasDrop != 0) {
revert InvalidGasDrop();
}
uint8 protocolBps = feeManager.calcProtocolBps(normlizedAmountIn, address(0), params.tokenOut, params.destChainId, params.referrerBps);
if (params.referrerBps > BPS_FEE_LIMIT || protocolBps > BPS_FEE_LIMIT) {
revert InvalidBpsFee();
}
Key memory key = buildKey(params, bytes32(0), wormhole.chainId(), protocolBps);
orderHash = keccak256(encodeKey(key));
if (params.destChainId == 0 || params.destChainId == wormhole.chainId()) {
revert InvalidDestChain();
}
if (orders[orderHash].destChainId != 0) {
revert DuplicateOrder();
}
orders[orderHash] = Order({
status: Status.CREATED,
amountIn: normlizedAmountIn,
destChainId: params.destChainId
});
emit OrderCreated(orderHash);
}
function createOrderWithToken(
address tokenIn,
uint256 amountIn,
OrderParams memory params
) nonReentrant external returns (bytes32 orderHash) {
if (paused) {
revert Paused();
}
amountIn = pullTokensFrom(tokenIn, amountIn, msg.sender);
uint64 normlizedAmountIn = uint64(normalizeAmount(amountIn, decimalsOf(tokenIn)));
if (normlizedAmountIn == 0) {
revert SmallAmountIn();
}
if (params.cancelFee + params.refundFee >= normlizedAmountIn) {
revert FeesTooHigh();
}
if (params.tokenOut == bytes32(0) && params.gasDrop != 0) {
revert InvalidGasDrop();
}
uint8 protocolBps = feeManager.calcProtocolBps(normlizedAmountIn, tokenIn, params.tokenOut, params.destChainId, params.referrerBps);
if (params.referrerBps > BPS_FEE_LIMIT || protocolBps > BPS_FEE_LIMIT) {
revert InvalidBpsFee();
}
Key memory key = buildKey(params, bytes32(uint256(uint160(tokenIn))), wormhole.chainId(), protocolBps);
orderHash = keccak256(encodeKey(key));
if (params.destChainId == 0 || params.destChainId == wormhole.chainId()) {
revert InvalidDestChain();
}
if (orders[orderHash].destChainId != 0) {
revert DuplicateOrder();
}
orders[orderHash] = Order({
status: Status.CREATED,
amountIn: normlizedAmountIn,
destChainId: params.destChainId
});
emit OrderCreated(orderHash);
}
function createOrderWithSig(
address tokenIn,
uint256 amountIn,
OrderParams memory params,
uint256 submissionFee,
bytes calldata signedOrderHash,
PermitParams calldata permitParams
) nonReentrant external returns (bytes32 orderHash) {
if (paused) {
revert Paused();
}
address trader = truncateAddress(params.trader);
uint256 allowance = IERC20(tokenIn).allowance(trader, address(this));
if (allowance < amountIn + submissionFee) {
execPermit(tokenIn, trader, permitParams);
}
amountIn = pullTokensFrom(tokenIn, amountIn, trader);
if (submissionFee > 0) {
IERC20(tokenIn).safeTransferFrom(trader, msg.sender, submissionFee);
}
uint64 normlizedAmountIn = uint64(normalizeAmount(amountIn, decimalsOf(tokenIn)));
if (normlizedAmountIn == 0) {
revert SmallAmountIn();
}
if (params.cancelFee + params.refundFee >= normlizedAmountIn) {
revert FeesTooHigh();
}
if (params.tokenOut == bytes32(0) && params.gasDrop != 0) {
revert InvalidGasDrop();
}
uint8 protocolBps = feeManager.calcProtocolBps(normlizedAmountIn, tokenIn, params.tokenOut, params.destChainId, params.referrerBps);
if (params.referrerBps > BPS_FEE_LIMIT || protocolBps > BPS_FEE_LIMIT) {
revert InvalidBpsFee();
}
orderHash = keccak256(encodeKey(buildKey(params, bytes32(uint256(uint160(tokenIn))), wormhole.chainId(), protocolBps)));
signedOrderHash.verify(hashTypedData(orderHash, amountIn, submissionFee), trader);
if (params.destChainId == 0 || params.destChainId == wormhole.chainId()) {
revert InvalidDestChain();
}
if (orders[orderHash].destChainId != 0) {
revert DuplicateOrder();
}
orders[orderHash] = Order({
status: Status.CREATED,
amountIn: normlizedAmountIn,
destChainId: params.destChainId
});
emit OrderCreated(orderHash);
}
function fulfillOrder(
uint256 fulfillAmount,
bytes memory encodedVm,
bytes32 recepient,
bool batch
) nonReentrant public payable returns (uint64 sequence) {
(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole.parseAndVerifyVM(encodedVm);
require(valid, reason);
if (vm.emitterChainId != auctionChainId) {
revert InvalidEmitterChain();
}
if (vm.emitterAddress != auctionAddr) {
revert InvalidEmitterAddress();
}
FulfillMsg memory fulfillMsg = parseFulfillPayload(vm.payload);
address tokenOut = truncateAddress(fulfillMsg.tokenOut);
if (tokenOut != address(0)) {
fulfillAmount = pullTokensFrom(tokenOut, fulfillAmount, msg.sender);
}
if (fulfillMsg.destChainId != wormhole.chainId()) {
revert InvalidDestChain();
}
if (truncateAddress(fulfillMsg.driver) != tx.origin) {
revert Unauthorized();
}
if (block.timestamp > fulfillMsg.deadline) {
revert DeadlineViolation();
}
if (orders[fulfillMsg.orderHash].status != Status.CREATED) {
revert InvalidOrderStatus();
}
orders[fulfillMsg.orderHash].status = Status.FULFILLED;
PaymentParams memory paymentParams = PaymentParams({
destAddr: truncateAddress(fulfillMsg.destAddr),
tokenOut: tokenOut,
promisedAmount: fulfillMsg.promisedAmount,
gasDrop: fulfillMsg.gasDrop,
referrerAddr: truncateAddress(fulfillMsg.referrerAddr),
referrerBps: fulfillMsg.referrerBps,
protocolBps: fulfillMsg.protocolBps,
batch: batch
});
uint256 netAmount = makePayments(fulfillAmount, paymentParams);
UnlockMsg memory unlockMsg = UnlockMsg({
action: uint8(Action.UNLOCK),
orderHash: fulfillMsg.orderHash,
srcChainId: fulfillMsg.srcChainId,
tokenIn: fulfillMsg.tokenIn,
recipient: recepient
});
if (batch) {
unlockMsgs[fulfillMsg.orderHash] = unlockMsg;
} else {
bytes memory encoded = encodeUnlockMsg(unlockMsg);
sequence = wormhole.publishMessage{
value : wormhole.messageFee()
}(0, encoded, consistencyLevel);
}
emit OrderFulfilled(fulfillMsg.orderHash, sequence, netAmount);
}
function fulfillSimple(
uint256 fulfillAmount,
bytes32 orderHash,
uint16 srcChainId,
bytes32 tokenIn,
uint8 protocolBps,
OrderParams memory params,
bytes32 recepient,
bool batch
) public nonReentrant payable returns (uint64 sequence) {
if (params.auctionMode != uint8(AuctionMode.BYPASS)) {
revert InvalidAuctionMode();
}
address tokenOut = truncateAddress(params.tokenOut);
if (tokenOut != address(0)) {
fulfillAmount = pullTokensFrom(tokenOut, fulfillAmount, msg.sender);
}
params.destChainId = wormhole.chainId();
Key memory key = buildKey(params, tokenIn, srcChainId, protocolBps);
bytes32 computedOrderHash = keccak256(encodeKey(key));
if (computedOrderHash != orderHash) {
revert InvalidOrderHash();
}
if (block.timestamp > key.deadline) {
revert DeadlineViolation();
}
if (orders[computedOrderHash].status != Status.CREATED) {
revert InvalidOrderStatus();
}
orders[computedOrderHash].status = Status.FULFILLED;
PaymentParams memory paymentParams = PaymentParams({
destAddr: truncateAddress(key.destAddr),
tokenOut: tokenOut,
promisedAmount: key.minAmountOut,
gasDrop: key.gasDrop,
referrerAddr: truncateAddress(key.referrerAddr),
referrerBps: key.referrerBps,
protocolBps: protocolBps,
batch: batch
});
uint256 netAmount = makePayments(fulfillAmount, paymentParams);
UnlockMsg memory unlockMsg = UnlockMsg({
action: uint8(Action.UNLOCK),
orderHash: computedOrderHash,
srcChainId: key.srcChainId,
tokenIn: key.tokenIn,
recipient: recepient
});
if (batch) {
unlockMsgs[computedOrderHash] = unlockMsg;
} else {
bytes memory encoded = encodeUnlockMsg(unlockMsg);
sequence = wormhole.publishMessage{
value : wormhole.messageFee()
}(0, encoded, consistencyLevel);
}
emit OrderFulfilled(computedOrderHash, sequence, netAmount);
}
function unlockOrder(UnlockMsg memory unlockMsg, Order memory order) internal {
if (unlockMsg.srcChainId != wormhole.chainId()) {
revert InvalidSrcChain();
}
if (order.destChainId == 0) {
revert OrderNotExists();
}
if (order.status != Status.CREATED) {
revert InvalidOrderStatus();
}
orders[unlockMsg.orderHash].status = Status.UNLOCKED;
address recipient = truncateAddress(unlockMsg.recipient);
address tokenIn = truncateAddress(unlockMsg.tokenIn);
uint8 decimals;
if (tokenIn == address(0)) {
decimals = NATIVE_DECIMALS;
} else {
decimals = decimalsOf(tokenIn);
}
uint256 amountIn = deNormalizeAmount(order.amountIn, decimals);
if (tokenIn == address(0)) {
payViaCall(recipient, amountIn);
} else {
IERC20(tokenIn).safeTransfer(recipient, amountIn);
}
emit OrderUnlocked(unlockMsg.orderHash);
}
function cancelOrder(
bytes32 tokenIn,
OrderParams memory params,
uint16 srcChainId,
uint8 protocolBps,
bytes32 canceler
) public nonReentrant payable returns (uint64 sequence) {
params.destChainId = wormhole.chainId();
Key memory key = buildKey(params, tokenIn, srcChainId, protocolBps);
bytes32 orderHash = keccak256(encodeKey(key));
Order memory order = orders[orderHash];
if (block.timestamp <= key.deadline) {
revert DeadlineViolation();
}
if (order.status != Status.CREATED) {
revert InvalidOrderStatus();
}
orders[orderHash].status = Status.CANCELED;
RefundMsg memory refundMsg = RefundMsg({
action: uint8(Action.REFUND),
orderHash: orderHash,
srcChainId: key.srcChainId,
tokenIn: key.tokenIn,
recipient: key.trader,
canceler: canceler,
cancelFee: key.cancelFee,
refundFee: key.refundFee
});
bytes memory encoded = encodeRefundMsg(refundMsg);
sequence = wormhole.publishMessage{
value : msg.value
}(0, encoded, consistencyLevel);
emit OrderCanceled(orderHash, sequence);
}
function refundOrder(bytes memory encodedVm) nonReentrant() public {
(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole.parseAndVerifyVM(encodedVm);
require(valid, reason);
RefundMsg memory refundMsg = parseRefundPayload(vm.payload);
Order memory order = orders[refundMsg.orderHash];
if (refundMsg.srcChainId != wormhole.chainId()) {
revert InvalidSrcChain();
}
if (order.destChainId == 0) {
revert OrderNotExists();
}
if (order.status != Status.CREATED) {
revert InvalidOrderStatus();
}
orders[refundMsg.orderHash].status = Status.REFUNDED;
if (vm.emitterChainId != order.destChainId) {
revert InvalidEmitterChain();
}
if (vm.emitterAddress != solanaEmitter && truncateAddress(vm.emitterAddress) != address(this)) {
revert InvalidEmitterAddress();
}
address recipient = truncateAddress(refundMsg.recipient);
address canceler = address(uint160(uint256(refundMsg.canceler)));
address tokenIn = truncateAddress(refundMsg.tokenIn);
uint8 decimals;
if (tokenIn == address(0)) {
decimals = NATIVE_DECIMALS;
} else {
decimals = decimalsOf(tokenIn);
}
uint256 cancelFee = deNormalizeAmount(refundMsg.cancelFee, decimals);
uint256 refundFee = deNormalizeAmount(refundMsg.refundFee, decimals);
uint256 amountIn = deNormalizeAmount(order.amountIn, decimals);
uint256 netAmount = amountIn - cancelFee - refundFee;
if (tokenIn == address(0)) {
payViaCall(canceler, cancelFee);
payViaCall(msg.sender, refundFee);
payViaCall(recipient, netAmount);
} else {
IERC20(tokenIn).safeTransfer(canceler, cancelFee);
IERC20(tokenIn).safeTransfer(msg.sender, refundFee);
IERC20(tokenIn).safeTransfer(recipient, netAmount);
}
emit OrderRefunded(refundMsg.orderHash, netAmount);
}
function unlockSingle(bytes memory encodedVm) nonReentrant public {
(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole.parseAndVerifyVM(encodedVm);
require(valid, reason);
UnlockMsg memory unlockMsg = parseUnlockPayload(vm.payload);
Order memory order = orders[unlockMsg.orderHash];
if (vm.emitterChainId != order.destChainId) {
revert InvalidEmitterChain();
}
if (vm.emitterAddress != solanaEmitter && truncateAddress(vm.emitterAddress) != address(this)) {
revert InvalidEmitterAddress();
}
unlockOrder(unlockMsg, order);
}
function unlockBatch(bytes memory encodedVm) nonReentrant public {
(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole.parseAndVerifyVM(encodedVm);
require(valid, reason);
uint8 action = vm.payload.toUint8(0);
uint index = 1;
if (action != uint8(Action.BATCH_UNLOCK)) {
revert InvalidAction();
}
uint16 count = vm.payload.toUint16(index);
index += 2;
for (uint i=0; i<count; i++) {
UnlockMsg memory unlockMsg = UnlockMsg({
action: uint8(Action.UNLOCK),
orderHash: vm.payload.toBytes32(index),
srcChainId: vm.payload.toUint16(index + 32),
tokenIn: vm.payload.toBytes32(index + 34),
recipient: vm.payload.toBytes32(index + 66)
});
index += 98;
Order memory order = orders[unlockMsg.orderHash];
if (order.status != Status.CREATED) {
continue;
}
if (vm.emitterChainId != order.destChainId) {
revert InvalidEmitterChain();
}
if (vm.emitterAddress != solanaEmitter && truncateAddress(vm.emitterAddress) != address(this)) {
revert InvalidEmitterAddress();
}
unlockOrder(unlockMsg, order);
}
}
function postBatch(bytes32[] memory orderHashes) public payable returns (uint64 sequence) {
bytes memory encoded = abi.encodePacked(uint8(Action.BATCH_UNLOCK), uint16(orderHashes.length));
for(uint i=0; i<orderHashes.length; i++) {
UnlockMsg memory unlockMsg = unlockMsgs[orderHashes[i]];
if (unlockMsg.action != uint8(Action.UNLOCK)) {
revert InvalidAction();
}
bytes memory encodedUnlock = abi.encodePacked(
unlockMsg.orderHash,
unlockMsg.srcChainId,
unlockMsg.tokenIn,
unlockMsg.recipient
);
encoded = abi.encodePacked(encoded, encodedUnlock);
}
sequence = wormhole.publishMessage{
value : msg.value
}(0, encoded, consistencyLevel);
}
function makePayments(
uint256 fulfillAmount,
PaymentParams memory params
) internal returns (uint256 netAmount) {
uint8 decimals;
if (params.tokenOut == address(0)) {
decimals = NATIVE_DECIMALS;
} else {
decimals = decimalsOf(params.tokenOut);
}
uint256 referrerAmount = 0;
if (params.referrerAddr != address(0) && params.referrerBps != 0) {
referrerAmount = fulfillAmount * params.referrerBps / 10000;
}
uint256 protocolAmount = 0;
if (params.protocolBps != 0) {
protocolAmount = fulfillAmount * params.protocolBps / 10000;
}
netAmount = fulfillAmount - referrerAmount - protocolAmount;
uint256 promisedAmount = deNormalizeAmount(params.promisedAmount, decimals);
if (netAmount < promisedAmount) {
revert InvalidAmount();
}
if (params.tokenOut == address(0)) {
if (
(params.batch && msg.value != fulfillAmount) ||
(!params.batch && msg.value != fulfillAmount + wormhole.messageFee())
) {
revert InvalidWormholeFee();
}
if (referrerAmount > 0) {
payViaCall(params.referrerAddr, referrerAmount);
}
if (protocolAmount > 0) {
payViaCall(feeManager.feeCollector(), protocolAmount);
}
payViaCall(params.destAddr, netAmount);
} else {
if (params.gasDrop > 0) {
uint256 gasDrop = deNormalizeAmount(params.gasDrop, NATIVE_DECIMALS);
if (
(params.batch && msg.value != gasDrop) ||
(!params.batch && msg.value != gasDrop + wormhole.messageFee())
) {
revert InvalidGasDrop();
}
payViaCall(params.destAddr, gasDrop);
} else if (
(params.batch && msg.value != 0) ||
(!params.batch && msg.value != wormhole.messageFee())
) {
revert InvalidWormholeFee();
}
if (referrerAmount > 0) {
IERC20(params.tokenOut).safeTransfer(params.referrerAddr, referrerAmount);
}
if (protocolAmount > 0) {
IERC20(params.tokenOut).safeTransfer(feeManager.feeCollector(), protocolAmount);
}
IERC20(params.tokenOut).safeTransfer(params.destAddr, netAmount);
}
}
function buildKey(OrderParams memory params, bytes32 tokenIn, uint16 srcChainId, uint8 protocolBps) internal pure returns (Key memory) {
return Key({
trader: params.trader,
srcChainId: srcChainId,
tokenIn: tokenIn,
tokenOut: params.tokenOut,
minAmountOut: params.minAmountOut,
gasDrop: params.gasDrop,
cancelFee: params.cancelFee,
refundFee: params.refundFee,
deadline: params.deadline,
destAddr: params.destAddr,
destChainId: params.destChainId,
referrerAddr: params.referrerAddr,
referrerBps: params.referrerBps,
protocolBps: protocolBps,
auctionMode: params.auctionMode,
random: params.random
});
}
function parseFulfillPayload(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.orderHash = encoded.toBytes32(index);
index += 32;
fulfillMsg.srcChainId = encoded.toUint16(index);
index += 2;
fulfillMsg.tokenIn = encoded.toBytes32(index);
index += 32;
fulfillMsg.destAddr = encoded.toBytes32(index);
index += 32;
fulfillMsg.destChainId = encoded.toUint16(index);
index += 2;
fulfillMsg.tokenOut = encoded.toBytes32(index);
index += 32;
fulfillMsg.promisedAmount = encoded.toUint64(index);
index += 8;
fulfillMsg.gasDrop = encoded.toUint64(index);
index += 8;
fulfillMsg.deadline = 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.driver = encoded.toBytes32(index);
index += 32;
}
function parseUnlockPayload(bytes memory encoded) public pure returns (UnlockMsg memory unlockMsg) {
uint index = 0;
unlockMsg.action = encoded.toUint8(index);
index += 1;
if (unlockMsg.action != uint8(Action.UNLOCK)) {
revert InvalidAction();
}
unlockMsg.orderHash = encoded.toBytes32(index);
index += 32;
unlockMsg.srcChainId = encoded.toUint16(index);
index += 2;
unlockMsg.tokenIn = encoded.toBytes32(index);
index += 32;
unlockMsg.recipient = encoded.toBytes32(index);
index += 32;
}
function parseRefundPayload(bytes memory encoded) public pure returns (RefundMsg memory refundMsg) {
uint index = 0;
refundMsg.action = encoded.toUint8(index);
index += 1;
if (refundMsg.action != uint8(Action.REFUND)) {
revert InvalidAction();
}
refundMsg.orderHash = encoded.toBytes32(index);
index += 32;
refundMsg.srcChainId = encoded.toUint16(index);
index += 2;
refundMsg.tokenIn = encoded.toBytes32(index);
index += 32;
refundMsg.recipient = encoded.toBytes32(index);
index += 32;
refundMsg.canceler = encoded.toBytes32(index);
index += 32;
refundMsg.cancelFee = encoded.toUint64(index);
index += 8;
refundMsg.refundFee = encoded.toUint64(index);
index += 8;
}
function encodeKey(Key memory key) internal pure returns (bytes memory encoded) {
encoded = abi.encodePacked(
key.trader,
key.srcChainId,
key.tokenIn,
key.destAddr,
key.destChainId,
key.tokenOut,
key.minAmountOut,
key.gasDrop,
key.cancelFee,
key.refundFee,
key.deadline,
key.referrerAddr,
key.referrerBps
);
encoded = encoded.concat(abi.encodePacked(key.protocolBps, key.auctionMode, key.random));
}
function encodeUnlockMsg(UnlockMsg memory unlockMsg) internal pure returns (bytes memory encoded) {
encoded = abi.encodePacked(
unlockMsg.action,
unlockMsg.orderHash,
unlockMsg.srcChainId,
unlockMsg.tokenIn,
unlockMsg.recipient
);
}
function encodeRefundMsg(RefundMsg memory refundMsg) internal pure returns (bytes memory encoded) {
encoded = abi.encodePacked(
refundMsg.action,
refundMsg.orderHash,
refundMsg.srcChainId,
refundMsg.tokenIn,
refundMsg.recipient,
refundMsg.canceler,
refundMsg.cancelFee,
refundMsg.refundFee
);
}
function payViaCall(address to, uint256 amount) internal {
(bool success, ) = payable(to).call{value: amount}('');
require(success, 'payment failed');
}
function truncateAddress(bytes32 b) internal pure returns (address) {
if (bytes12(b) != 0) {
revert InvalidEvmAddr();
}
return address(uint160(uint256(b)));
}
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 hashTypedData(bytes32 orderHash, uint256 amountIn, uint256 submissionFee) internal view returns (bytes32) {
bytes memory encoded = abi.encode(keccak256("CreateOrder(bytes32 OrderId,uint256 InputAmount,uint256 SubmissionFee)"), orderHash, amountIn, submissionFee);
return toTypedDataHash(domainSeparator, keccak256(encoded));
}
function toTypedDataHash(bytes32 _domainSeparator, bytes32 _structHash) internal pure returns (bytes32 digest) {
assembly {
let ptr := mload(0x40)
mstore(ptr, "\x19\x01")
mstore(add(ptr, 0x02), _domainSeparator)
mstore(add(ptr, 0x22), _structHash)
digest := keccak256(ptr, 0x42)
}
}
function pullTokensFrom(address tokenIn, uint256 amount, address from) internal returns (uint256) {
uint256 balance = IERC20(tokenIn).balanceOf(address(this));
IERC20(tokenIn).safeTransferFrom(from, address(this), amount);
return IERC20(tokenIn).balanceOf(address(this)) - balance;
}
function execPermit(
address token,
address owner,
PermitParams calldata permitParams
) internal {
IERC20Permit(token).permit(
owner,
address(this),
permitParams.value,
permitParams.deadline,
permitParams.v,
permitParams.r,
permitParams.s
);
}
function setPause(bool _pause) public {
if (msg.sender != guardian) {
revert Unauthorized();
}
paused = _pause;
}
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 changeGuardian(address newGuardian) public {
if (msg.sender != guardian) {
revert Unauthorized();
}
nextGuardian = newGuardian;
}
function claimGuardian() public {
if (msg.sender != nextGuardian) {
revert Unauthorized();
}
guardian = nextGuardian;
}
function getOrders(bytes32[] memory orderHashes) public view returns (Order[] memory) {
Order[] memory result = new Order[](orderHashes.length);
for (uint i=0; i<orderHashes.length; i++) {
result[i] = orders[orderHashes[i]];
}
return result;
}
receive() external payable {}
}
文件 9 的 11: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;
}
}
文件 10 的 11: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));
}
}
文件 11 的 11:SignatureVerifier.sol
pragma solidity ^0.8.0;
import {IERC1271} from "../interfaces/IERC1271.sol";
library SignatureVerifier {
error InvalidSignatureLength();
error InvalidSignature();
error InvalidSigner();
error InvalidContractSignature();
bytes32 constant UPPER_BIT_MASK = (0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
function verify(bytes calldata signature, bytes32 hash, address claimedSigner) external view {
bytes32 r;
bytes32 s;
uint8 v;
if (claimedSigner.code.length == 0) {
if (signature.length == 65) {
(r, s) = abi.decode(signature, (bytes32, bytes32));
v = uint8(signature[64]);
} else if (signature.length == 64) {
bytes32 vs;
(r, vs) = abi.decode(signature, (bytes32, bytes32));
s = vs & UPPER_BIT_MASK;
v = uint8(uint256(vs >> 255)) + 27;
} else {
revert InvalidSignatureLength();
}
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) revert InvalidSignature();
if (signer != claimedSigner) revert InvalidSigner();
} else {
bytes4 magicValue = IERC1271(claimedSigner).isValidSignature(hash, signature);
if (magicValue != IERC1271.isValidSignature.selector) revert InvalidContractSignature();
}
}
}
{
"compilationTarget": {
"src/MayanSwift.sol": "MayanSwift"
},
"evmVersion": "istanbul",
"libraries": {
"src/libs/SignatureVerifier.sol:SignatureVerifier": "0x3ca829b74971035fe0b733cd6297ca7a8a39e7c0"
},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"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":"DeadlineViolation","type":"error"},{"inputs":[],"name":"DuplicateOrder","type":"error"},{"inputs":[],"name":"FeesTooHigh","type":"error"},{"inputs":[],"name":"InvalidAction","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"InvalidAuctionMode","type":"error"},{"inputs":[],"name":"InvalidBpsFee","type":"error"},{"inputs":[],"name":"InvalidDestChain","type":"error"},{"inputs":[],"name":"InvalidEmitterAddress","type":"error"},{"inputs":[],"name":"InvalidEmitterChain","type":"error"},{"inputs":[],"name":"InvalidEvmAddr","type":"error"},{"inputs":[],"name":"InvalidGasDrop","type":"error"},{"inputs":[],"name":"InvalidOrderHash","type":"error"},{"inputs":[],"name":"InvalidOrderStatus","type":"error"},{"inputs":[],"name":"InvalidSrcChain","type":"error"},{"inputs":[],"name":"InvalidWormholeFee","type":"error"},{"inputs":[],"name":"OrderNotExists","type":"error"},{"inputs":[],"name":"Paused","type":"error"},{"inputs":[],"name":"SmallAmountIn","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"key","type":"bytes32"},{"indexed":false,"internalType":"uint64","name":"sequence","type":"uint64"}],"name":"OrderCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"key","type":"bytes32"}],"name":"OrderCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"key","type":"bytes32"},{"indexed":false,"internalType":"uint64","name":"sequence","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"netAmount","type":"uint256"}],"name":"OrderFulfilled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"key","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"netAmount","type":"uint256"}],"name":"OrderRefunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"key","type":"bytes32"}],"name":"OrderUnlocked","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":"bytes32","name":"tokenIn","type":"bytes32"},{"components":[{"internalType":"bytes32","name":"trader","type":"bytes32"},{"internalType":"bytes32","name":"tokenOut","type":"bytes32"},{"internalType":"uint64","name":"minAmountOut","type":"uint64"},{"internalType":"uint64","name":"gasDrop","type":"uint64"},{"internalType":"uint64","name":"cancelFee","type":"uint64"},{"internalType":"uint64","name":"refundFee","type":"uint64"},{"internalType":"uint64","name":"deadline","type":"uint64"},{"internalType":"bytes32","name":"destAddr","type":"bytes32"},{"internalType":"uint16","name":"destChainId","type":"uint16"},{"internalType":"bytes32","name":"referrerAddr","type":"bytes32"},{"internalType":"uint8","name":"referrerBps","type":"uint8"},{"internalType":"uint8","name":"auctionMode","type":"uint8"},{"internalType":"bytes32","name":"random","type":"bytes32"}],"internalType":"struct MayanSwift.OrderParams","name":"params","type":"tuple"},{"internalType":"uint16","name":"srcChainId","type":"uint16"},{"internalType":"uint8","name":"protocolBps","type":"uint8"},{"internalType":"bytes32","name":"canceler","type":"bytes32"}],"name":"cancelOrder","outputs":[{"internalType":"uint64","name":"sequence","type":"uint64"}],"stateMutability":"payable","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":"bytes32","name":"trader","type":"bytes32"},{"internalType":"bytes32","name":"tokenOut","type":"bytes32"},{"internalType":"uint64","name":"minAmountOut","type":"uint64"},{"internalType":"uint64","name":"gasDrop","type":"uint64"},{"internalType":"uint64","name":"cancelFee","type":"uint64"},{"internalType":"uint64","name":"refundFee","type":"uint64"},{"internalType":"uint64","name":"deadline","type":"uint64"},{"internalType":"bytes32","name":"destAddr","type":"bytes32"},{"internalType":"uint16","name":"destChainId","type":"uint16"},{"internalType":"bytes32","name":"referrerAddr","type":"bytes32"},{"internalType":"uint8","name":"referrerBps","type":"uint8"},{"internalType":"uint8","name":"auctionMode","type":"uint8"},{"internalType":"bytes32","name":"random","type":"bytes32"}],"internalType":"struct MayanSwift.OrderParams","name":"params","type":"tuple"}],"name":"createOrderWithEth","outputs":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"components":[{"internalType":"bytes32","name":"trader","type":"bytes32"},{"internalType":"bytes32","name":"tokenOut","type":"bytes32"},{"internalType":"uint64","name":"minAmountOut","type":"uint64"},{"internalType":"uint64","name":"gasDrop","type":"uint64"},{"internalType":"uint64","name":"cancelFee","type":"uint64"},{"internalType":"uint64","name":"refundFee","type":"uint64"},{"internalType":"uint64","name":"deadline","type":"uint64"},{"internalType":"bytes32","name":"destAddr","type":"bytes32"},{"internalType":"uint16","name":"destChainId","type":"uint16"},{"internalType":"bytes32","name":"referrerAddr","type":"bytes32"},{"internalType":"uint8","name":"referrerBps","type":"uint8"},{"internalType":"uint8","name":"auctionMode","type":"uint8"},{"internalType":"bytes32","name":"random","type":"bytes32"}],"internalType":"struct MayanSwift.OrderParams","name":"params","type":"tuple"},{"internalType":"uint256","name":"submissionFee","type":"uint256"},{"internalType":"bytes","name":"signedOrderHash","type":"bytes"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct MayanSwift.PermitParams","name":"permitParams","type":"tuple"}],"name":"createOrderWithSig","outputs":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"components":[{"internalType":"bytes32","name":"trader","type":"bytes32"},{"internalType":"bytes32","name":"tokenOut","type":"bytes32"},{"internalType":"uint64","name":"minAmountOut","type":"uint64"},{"internalType":"uint64","name":"gasDrop","type":"uint64"},{"internalType":"uint64","name":"cancelFee","type":"uint64"},{"internalType":"uint64","name":"refundFee","type":"uint64"},{"internalType":"uint64","name":"deadline","type":"uint64"},{"internalType":"bytes32","name":"destAddr","type":"bytes32"},{"internalType":"uint16","name":"destChainId","type":"uint16"},{"internalType":"bytes32","name":"referrerAddr","type":"bytes32"},{"internalType":"uint8","name":"referrerBps","type":"uint8"},{"internalType":"uint8","name":"auctionMode","type":"uint8"},{"internalType":"bytes32","name":"random","type":"bytes32"}],"internalType":"struct MayanSwift.OrderParams","name":"params","type":"tuple"}],"name":"createOrderWithToken","outputs":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeManager","outputs":[{"internalType":"contract IFeeManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"fulfillAmount","type":"uint256"},{"internalType":"bytes","name":"encodedVm","type":"bytes"},{"internalType":"bytes32","name":"recepient","type":"bytes32"},{"internalType":"bool","name":"batch","type":"bool"}],"name":"fulfillOrder","outputs":[{"internalType":"uint64","name":"sequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"fulfillAmount","type":"uint256"},{"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"internalType":"uint16","name":"srcChainId","type":"uint16"},{"internalType":"bytes32","name":"tokenIn","type":"bytes32"},{"internalType":"uint8","name":"protocolBps","type":"uint8"},{"components":[{"internalType":"bytes32","name":"trader","type":"bytes32"},{"internalType":"bytes32","name":"tokenOut","type":"bytes32"},{"internalType":"uint64","name":"minAmountOut","type":"uint64"},{"internalType":"uint64","name":"gasDrop","type":"uint64"},{"internalType":"uint64","name":"cancelFee","type":"uint64"},{"internalType":"uint64","name":"refundFee","type":"uint64"},{"internalType":"uint64","name":"deadline","type":"uint64"},{"internalType":"bytes32","name":"destAddr","type":"bytes32"},{"internalType":"uint16","name":"destChainId","type":"uint16"},{"internalType":"bytes32","name":"referrerAddr","type":"bytes32"},{"internalType":"uint8","name":"referrerBps","type":"uint8"},{"internalType":"uint8","name":"auctionMode","type":"uint8"},{"internalType":"bytes32","name":"random","type":"bytes32"}],"internalType":"struct MayanSwift.OrderParams","name":"params","type":"tuple"},{"internalType":"bytes32","name":"recepient","type":"bytes32"},{"internalType":"bool","name":"batch","type":"bool"}],"name":"fulfillSimple","outputs":[{"internalType":"uint64","name":"sequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"orderHashes","type":"bytes32[]"}],"name":"getOrders","outputs":[{"components":[{"internalType":"enum MayanSwift.Status","name":"status","type":"uint8"},{"internalType":"uint64","name":"amountIn","type":"uint64"},{"internalType":"uint16","name":"destChainId","type":"uint16"}],"internalType":"struct MayanSwift.Order[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"guardian","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextGuardian","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"orders","outputs":[{"internalType":"enum MayanSwift.Status","name":"status","type":"uint8"},{"internalType":"uint64","name":"amountIn","type":"uint64"},{"internalType":"uint16","name":"destChainId","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"encoded","type":"bytes"}],"name":"parseFulfillPayload","outputs":[{"components":[{"internalType":"uint8","name":"action","type":"uint8"},{"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"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":"uint64","name":"deadline","type":"uint64"},{"internalType":"bytes32","name":"referrerAddr","type":"bytes32"},{"internalType":"uint8","name":"referrerBps","type":"uint8"},{"internalType":"uint8","name":"protocolBps","type":"uint8"},{"internalType":"uint16","name":"srcChainId","type":"uint16"},{"internalType":"bytes32","name":"tokenIn","type":"bytes32"}],"internalType":"struct MayanSwift.FulfillMsg","name":"fulfillMsg","type":"tuple"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes","name":"encoded","type":"bytes"}],"name":"parseRefundPayload","outputs":[{"components":[{"internalType":"uint8","name":"action","type":"uint8"},{"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"internalType":"uint16","name":"srcChainId","type":"uint16"},{"internalType":"bytes32","name":"tokenIn","type":"bytes32"},{"internalType":"bytes32","name":"recipient","type":"bytes32"},{"internalType":"bytes32","name":"canceler","type":"bytes32"},{"internalType":"uint64","name":"cancelFee","type":"uint64"},{"internalType":"uint64","name":"refundFee","type":"uint64"}],"internalType":"struct MayanSwift.RefundMsg","name":"refundMsg","type":"tuple"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes","name":"encoded","type":"bytes"}],"name":"parseUnlockPayload","outputs":[{"components":[{"internalType":"uint8","name":"action","type":"uint8"},{"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"internalType":"uint16","name":"srcChainId","type":"uint16"},{"internalType":"bytes32","name":"tokenIn","type":"bytes32"},{"internalType":"bytes32","name":"recipient","type":"bytes32"}],"internalType":"struct MayanSwift.UnlockMsg","name":"unlockMsg","type":"tuple"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"orderHashes","type":"bytes32[]"}],"name":"postBatch","outputs":[{"internalType":"uint64","name":"sequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"encodedVm","type":"bytes"}],"name":"refundOrder","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":"unlockBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"unlockMsgs","outputs":[{"internalType":"uint8","name":"action","type":"uint8"},{"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"internalType":"uint16","name":"srcChainId","type":"uint16"},{"internalType":"bytes32","name":"tokenIn","type":"bytes32"},{"internalType":"bytes32","name":"recipient","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"encodedVm","type":"bytes"}],"name":"unlockSingle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wormhole","outputs":[{"internalType":"contract IWormhole","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]