Contract Source Code:
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
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 "../libs/BytesLib.sol";
import "../libs/SignatureVerifier.sol";
import "./SwiftStructs.sol";
import "./SwiftErrors.sol";
contract SwiftDest is ReentrancyGuard {
event OrderCreated(bytes32 key);
event OrderFulfilled(bytes32 key, uint64 sequence, uint256 fulfilledAmount);
event OrderUnlocked(bytes32 key);
event OrderCanceled(bytes32 key, uint64 sequence);
event OrderRefunded(bytes32 key, uint256 refundedAmount);
using SafeERC20 for IERC20;
using BytesLib for bytes;
using SignatureVerifier for bytes;
uint8 constant NATIVE_DECIMALS = 18;
IWormhole public immutable wormhole;
address public auctionVerifier;
uint16 public auctionChainId;
bytes32 public auctionAddr;
uint8 public consistencyLevel;
address public immutable rescueVault;
address public guardian;
address public nextGuardian;
bool public paused;
mapping(bytes32 => Order) public orders;
mapping(bytes32 => bytes) public unlockMsgs;
mapping(bytes32 => uint256) public pendingAmounts;
mapping(uint16 => bytes32) public emitters;
mapping(uint64 => bool) public usedSequences;
constructor(
address _wormhole,
address _auctionVerifier,
uint16 _auctionChainId,
bytes32 _auctionAddr,
uint8 _consistencyLevel,
address _rescueVault
) {
guardian = msg.sender;
wormhole = IWormhole(_wormhole);
auctionVerifier = _auctionVerifier;
auctionChainId = _auctionChainId;
auctionAddr = _auctionAddr;
consistencyLevel = _consistencyLevel;
rescueVault = _rescueVault;
}
function fulfillOrder(
uint256 fulfillAmount,
bytes memory encodedVm,
OrderParams memory params,
ExtraParams memory extraParams,
UnlockParams memory unlockParams,
PermitParams calldata permit
) nonReentrant public payable returns (bytes memory) {
(IWormhole.VM memory vm, bool valid, string memory reason) = IWormhole(auctionVerifier).parseAndVerifyVM(encodedVm);
require(valid, reason);
if (vm.emitterChainId != auctionChainId) {
revert InvalidEmitterChain();
}
if (vm.emitterAddress != auctionAddr) {
revert InvalidEmitterAddress();
}
FulfillMsg memory fulfillMsg = parseFulfillPayload(vm.payload);
params.destChainId = wormhole.chainId();
if (keccak256(encodeKey(buildKey(params, extraParams))) != fulfillMsg.orderHash) {
revert InvalidOrderHash();
}
address tokenOut = truncateAddress(params.tokenOut);
if (tokenOut != address(0)) {
fulfillAmount = pullTokenIn(tokenOut, fulfillAmount, permit);
}
if (truncateAddress(fulfillMsg.driver) != tx.origin && block.timestamp <= params.deadline - fulfillMsg.penaltyPeriod) {
revert Unauthorized();
}
if (block.timestamp >= params.deadline) {
revert DeadlineViolation();
}
if (orders[fulfillMsg.orderHash].status != Status.CREATED) {
revert InvalidOrderStatus();
}
if (params.payloadType == 2) {
orders[fulfillMsg.orderHash].status = Status.FULFILLED;
} else {
orders[fulfillMsg.orderHash].status = Status.SETTLED;
}
makePayments(fulfillAmount, PaymentParams({
payloadType: params.payloadType,
orderHash: fulfillMsg.orderHash,
promisedAmount: fulfillMsg.promisedAmount,
minAmountOut: params.minAmountOut,
destAddr: truncateAddress(params.destAddr),
tokenOut: tokenOut,
gasDrop: params.gasDrop,
batch: unlockParams.batch
}));
uint64 sequence = batchOrSendUnlockMsg(buildUnlockMsg(fulfillMsg.orderHash, params, extraParams, unlockParams), unlockParams.batch);
emit OrderFulfilled(fulfillMsg.orderHash, sequence, fulfillAmount);
return vm.payload;
}
function fulfillSimple(
uint256 fulfillAmount,
bytes32 orderHash,
OrderParams memory params,
ExtraParams memory extraParams,
UnlockParams memory unlockParams,
PermitParams calldata permit
) public nonReentrant payable {
if (params.auctionMode != uint8(AuctionMode.LIMIT_ORDER)) {
revert InvalidAuctionMode();
}
address tokenOut = truncateAddress(params.tokenOut);
if (tokenOut != address(0)) {
fulfillAmount = pullTokenIn(tokenOut, fulfillAmount, permit);
}
params.destChainId = wormhole.chainId();
if (keccak256(encodeKey(buildKey(params, extraParams))) != orderHash) {
revert InvalidOrderHash();
}
if (block.timestamp > params.deadline) {
revert DeadlineViolation();
}
if (orders[orderHash].status != Status.CREATED) {
revert InvalidOrderStatus();
}
if (params.payloadType == 2) {
orders[orderHash].status = Status.FULFILLED;
} else {
orders[orderHash].status = Status.SETTLED;
}
makePayments(fulfillAmount, PaymentParams({
payloadType: params.payloadType,
orderHash: orderHash,
promisedAmount: params.minAmountOut,
minAmountOut: params.minAmountOut,
destAddr: truncateAddress(params.destAddr),
tokenOut: tokenOut,
gasDrop: params.gasDrop,
batch: unlockParams.batch
}));
uint64 sequence = batchOrSendUnlockMsg(buildUnlockMsg(orderHash, params, extraParams, unlockParams), unlockParams.batch);
emit OrderFulfilled(orderHash, sequence, fulfillAmount);
}
function cancelOrder(
bytes32 orderHash,
OrderParams memory params,
ExtraParams memory extraParams,
bytes32 canceler
) public nonReentrant payable returns (uint64 sequence) {
params.destChainId = wormhole.chainId();
bytes32 computedOrderHash = keccak256(encodeKey(buildKey(params, extraParams)));
if (computedOrderHash != orderHash) {
revert InvalidOrderHash();
}
Order memory order = orders[orderHash];
if (block.timestamp <= params.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: extraParams.srcChainId,
tokenIn: extraParams.tokenIn,
trader: params.trader,
canceler: canceler,
cancelFee: params.cancelFee,
refundFee: params.refundFee
});
bytes memory encoded = encodeRefundMsg(refundMsg);
sequence = wormhole.publishMessage{
value : msg.value
}(0, encoded, consistencyLevel);
emit OrderCanceled(orderHash, sequence);
}
function postBatch(bytes32[] memory orderHashes) public payable returns (uint64 sequence) {
bytes memory encoded;
for(uint i=0; i<orderHashes.length; i++) {
bytes memory unlockMsg = unlockMsgs[orderHashes[i]];
if (unlockMsg.length != UNLOCK_MSG_SIZE) {
revert OrderNotExists(orderHashes[i]);
}
encoded = abi.encodePacked(encoded, unlockMsg);
delete unlockMsgs[orderHashes[i]];
}
bytes memory payload = abi.encodePacked(uint8(Action.COMPRESSED_UNLOCK), uint16(orderHashes.length), keccak256(encoded));
sequence = wormhole.publishMessage{
value : msg.value
}(0, payload, consistencyLevel);
}
function settleWithPayload(
OrderParams memory params,
ExtraParams memory extraParams
) nonReentrant public returns (uint256 netAmount) {
if (params.payloadType != 2) {
revert InvalidAction();
}
if (truncateAddress(params.destAddr) != msg.sender) {
revert Unauthorized();
}
params.destChainId = wormhole.chainId();
bytes32 orderHash = keccak256(encodeKey(buildKey(params, extraParams)));
if (orders[orderHash].status != Status.FULFILLED) {
revert InvalidOrderStatus();
}
orders[orderHash].status = Status.SETTLED;
netAmount = pendingAmounts[orderHash];
address tokenOut = truncateAddress(params.tokenOut);
if (tokenOut == address(0)) {
payEth(msg.sender, netAmount, true);
} else {
IERC20(tokenOut).safeTransfer(msg.sender, netAmount);
}
}
function rescue(bytes memory encodedVm) public {
if (msg.sender != guardian) {
revert Unauthorized();
}
(IWormhole.VM memory vm, bool valid, string memory reason) = IWormhole(wormhole).parseAndVerifyVM(encodedVm);
require(valid, reason);
if (usedSequences[vm.sequence]) {
revert SequenceAlreadyUsed();
}
usedSequences[vm.sequence] = true;
if (vm.emitterChainId != 1) {
revert InvalidEmitterChain();
}
if (vm.emitterAddress != emitters[1]) {
revert InvalidEmitterAddress();
}
RescueMsg memory rescueMsg = parseRescuePayload(vm.payload);
if (rescueMsg.chainId != wormhole.chainId()) {
revert InvalidSrcChain();
}
if (rescueMsg.targetContract != address(this)) {
revert InvalidTargetContract();
}
if (rescueMsg.orderHash != bytes32(0)) {
orders[rescueMsg.orderHash].status = Status(rescueMsg.orderStatus);
}
if (rescueMsg.amount > 0) {
uint256 amount = deNormalizeAmount(rescueMsg.amount, decimalsOf(rescueMsg.token));
if (rescueMsg.token == address(0)) {
payEth(rescueVault, amount, true);
} else {
IERC20(rescueMsg.token).safeTransfer(rescueVault, amount);
}
}
}
function makePayments(
uint256 fulfillAmount,
PaymentParams memory params
) internal {
uint8 decimals;
if (params.tokenOut == address(0)) {
decimals = NATIVE_DECIMALS;
} else {
decimals = decimalsOf(params.tokenOut);
}
if (fulfillAmount < deNormalizeAmount(params.promisedAmount, decimals)) {
revert InsufficientAmount();
}
if (normalizeAmount(fulfillAmount, decimals) < params.minAmountOut) {
revert InvalidAmount();
}
if (params.tokenOut == address(0)) {
if (
(params.batch && msg.value != fulfillAmount) ||
(!params.batch && msg.value != fulfillAmount + wormhole.messageFee())
) {
revert InvalidWormholeFee();
}
if (params.payloadType == 2) {
pendingAmounts[params.orderHash] = fulfillAmount;
} else {
payEth(params.destAddr, fulfillAmount, true);
}
} 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();
}
payEth(params.destAddr, gasDrop, false);
} else if (
(params.batch && msg.value != 0) ||
(!params.batch && msg.value != wormhole.messageFee())
) {
revert InvalidWormholeFee();
}
if (params.payloadType == 2) {
pendingAmounts[params.orderHash] = fulfillAmount;
} else {
IERC20(params.tokenOut).safeTransfer(params.destAddr, fulfillAmount);
}
}
}
function buildUnlockMsg(
bytes32 orderHash,
OrderParams memory params,
ExtraParams memory extraParams,
UnlockParams memory unlockParams
) internal view returns (UnlockMsg memory) {
return UnlockMsg({
action: uint8(Action.UNLOCK),
orderHash: orderHash,
srcChainId: extraParams.srcChainId,
tokenIn: extraParams.tokenIn,
referrerAddr: params.referrerAddr,
referrerBps: params.referrerBps,
protocolBps: extraParams.protocolBps,
unlockReceiver: unlockParams.recipient,
driver: unlockParams.driver,
fulfillTime: uint64(block.timestamp)
});
}
function batchOrSendUnlockMsg(UnlockMsg memory unlockMsg, bool batch) internal returns (uint64 sequence) {
bytes memory encodedUnlockMsg = encodeUnlockMsg(unlockMsg);
if (batch) {
unlockMsgs[unlockMsg.orderHash] = encodedUnlockMsg;
} else {
return wormhole.publishMessage{
value : wormhole.messageFee()
}(0, abi.encodePacked(Action.UNLOCK, encodedUnlockMsg), consistencyLevel);
}
}
function buildKey(
OrderParams memory params,
ExtraParams memory extraParams
) internal pure returns (Key memory) {
return Key({
payloadType: params.payloadType,
trader: params.trader,
srcChainId: extraParams.srcChainId,
tokenIn: extraParams.tokenIn,
destAddr: params.destAddr,
destChainId: params.destChainId,
tokenOut: params.tokenOut,
minAmountOut: params.minAmountOut,
gasDrop: params.gasDrop,
cancelFee: params.cancelFee,
refundFee: params.refundFee,
deadline: params.deadline,
referrerAddr: params.referrerAddr,
referrerBps: params.referrerBps,
protocolBps: extraParams.protocolBps,
auctionMode: params.auctionMode,
random: params.random,
customPayloadHash: extraParams.customPayloadHash
});
}
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.promisedAmount = encoded.toUint64(index);
index += 8;
fulfillMsg.driver = encoded.toBytes32(index);
index += 32;
fulfillMsg.penaltyPeriod = encoded.toUint16(index);
index += 2;
}
function parseRescuePayload(bytes memory encoded) public pure returns (RescueMsg memory rescueMsg) {
uint index = 0;
rescueMsg.action = encoded.toUint8(index);
index += 1;
if (rescueMsg.action != uint8(Action.RESCUE)) {
revert InvalidAction();
}
rescueMsg.orderHash = encoded.toBytes32(index);
index += 32;
rescueMsg.orderStatus = encoded.toUint8(index);
index += 1;
rescueMsg.token = address(uint160(encoded.toUint256(index)));
index += 32;
rescueMsg.amount = encoded.toUint64(index);
index += 8;
}
function encodeKey(Key memory key) internal pure returns (bytes memory encoded) {
encoded = abi.encodePacked(
key.payloadType,
key.trader,
key.srcChainId,
key.tokenIn,
key.destAddr,
key.destChainId,
key.tokenOut,
key.minAmountOut,
key.gasDrop,
key.cancelFee,
key.refundFee,
key.deadline
);
encoded = encoded.concat(abi.encodePacked(
key.referrerAddr,
key.referrerBps,
key.protocolBps,
key.auctionMode,
key.random,
key.customPayloadHash
));
}
function encodeUnlockMsg(UnlockMsg memory unlockMsg) internal pure returns (bytes memory encoded) {
encoded = abi.encodePacked(
//unlockMsg.action,
unlockMsg.orderHash,
unlockMsg.srcChainId,
unlockMsg.tokenIn,
unlockMsg.referrerAddr,
unlockMsg.referrerBps,
unlockMsg.protocolBps,
unlockMsg.unlockReceiver,
unlockMsg.driver,
unlockMsg.fulfillTime
);
}
function encodeRefundMsg(RefundMsg memory refundMsg) internal pure returns (bytes memory encoded) {
encoded = abi.encodePacked(
refundMsg.action,
refundMsg.orderHash,
refundMsg.srcChainId,
refundMsg.tokenIn,
refundMsg.trader,
refundMsg.canceler,
refundMsg.cancelFee,
refundMsg.refundFee
);
}
function payEth(address to, uint256 amount, bool revertOnFailure) internal {
(bool success, ) = payable(to).call{value: amount}('');
if (revertOnFailure) {
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 pullTokenIn(
address tokenIn,
uint256 amountIn,
PermitParams calldata permitParams
) internal returns (uint256) {
uint256 allowance = IERC20(tokenIn).allowance(msg.sender, address(this));
if (allowance < amountIn) {
execPermit(tokenIn, msg.sender, permitParams);
}
uint256 balance = IERC20(tokenIn).balanceOf(address(this));
IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
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 setAuctionConfig(address _auctionVerifier, uint16 _auctionChainId, bytes32 _auctionAddr) public {
if (msg.sender != guardian) {
revert Unauthorized();
}
if (_auctionChainId == 0 || _auctionAddr == bytes32(0)) {
revert InvalidAuctionConfig();
}
auctionVerifier = _auctionVerifier;
auctionChainId = _auctionChainId;
auctionAddr = _auctionAddr;
}
function setConsistencyLevel(uint8 _consistencyLevel) public {
if (msg.sender != guardian) {
revert Unauthorized();
}
consistencyLevel = _consistencyLevel;
}
function setEmitters(uint16[] memory chainIds, bytes32[] memory addresses) public {
if (msg.sender != guardian) {
revert Unauthorized();
}
require(chainIds.length == addresses.length, 'invalid array length');
for (uint i=0; i<chainIds.length; i++) {
if (emitters[chainIds[i]] != bytes32(0)) {
revert EmitterAddressExists();
}
emitters[chainIds[i]] = addresses[i];
}
}
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 {}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
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));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
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));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
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));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
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);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
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");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
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");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: Apache 2
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;
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: Unlicense
/*
* @title Solidity Bytes Arrays Utils
* @author Gonçalo Sá <goncalo.sa@consensys.net>
*
* @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
* The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
*/
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 {
// Get a location of some free memory and store it in tempBytes as
// Solidity does for memory variables.
tempBytes := mload(0x40)
// Store the length of the first bytes array at the beginning of
// the memory for tempBytes.
let length := mload(_preBytes)
mstore(tempBytes, length)
// Maintain a memory counter for the current write location in the
// temp bytes array by adding the 32 bytes for the array length to
// the starting location.
let mc := add(tempBytes, 0x20)
// Stop copying when the memory counter reaches the length of the
// first bytes array.
let end := add(mc, length)
for {
// Initialize a copy counter to the start of the _preBytes data,
// 32 bytes into its memory.
let cc := add(_preBytes, 0x20)
} lt(mc, end) {
// Increase both counters by 32 bytes each iteration.
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
// Write the _preBytes data into the tempBytes memory 32 bytes
// at a time.
mstore(mc, mload(cc))
}
// Add the length of _postBytes to the current length of tempBytes
// and store it as the new length in the first 32 bytes of the
// tempBytes memory.
length := mload(_postBytes)
mstore(tempBytes, add(length, mload(tempBytes)))
// Move the memory counter back from a multiple of 0x20 to the
// actual end of the _preBytes data.
mc := end
// Stop copying when the memory counter reaches the new combined
// length of the arrays.
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))
}
// Update the free-memory pointer by padding our last write location
// to 32 bytes: add 31 bytes to the end of tempBytes to move to the
// next 32 byte block, then round down to the nearest multiple of
// 32. If the sum of the length of the two arrays is zero then add
// one before rounding down to leave a blank 32 bytes (the length block with 0).
mstore(0x40, and(
add(add(end, iszero(add(length, mload(_preBytes)))), 31),
not(31) // Round down to the nearest 32 bytes.
))
}
return tempBytes;
}
function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
assembly {
// Read the first 32 bytes of _preBytes storage, which is the length
// of the array. (We don't need to use the offset into the slot
// because arrays use the entire slot.)
let fslot := sload(_preBytes.slot)
// Arrays of 31 bytes or less have an even value in their slot,
// while longer arrays have an odd value. The actual length is
// the slot divided by two for odd values, and the lowest order
// byte divided by two for even values.
// If the slot is even, bitwise and the slot with 255 and divide by
// two to get the length. If the slot is odd, bitwise and the slot
// with -1 and divide by two.
let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
let mlength := mload(_postBytes)
let newlength := add(slength, mlength)
// slength can contain both the length and contents of the array
// if length < 32 bytes so let's prepare for that
// v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
switch add(lt(slength, 32), lt(newlength, 32))
case 2 {
// Since the new array still fits in the slot, we just need to
// update the contents of the slot.
// uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
sstore(
_preBytes.slot,
// all the modifications to the slot are inside this
// next block
add(
// we can just add to the slot contents because the
// bytes we want to change are the LSBs
fslot,
add(
mul(
div(
// load the bytes from memory
mload(add(_postBytes, 0x20)),
// zero all bytes to the right
exp(0x100, sub(32, mlength))
),
// and now shift left the number of bytes to
// leave space for the length in the slot
exp(0x100, sub(32, newlength))
),
// increase length by the double of the memory
// bytes length
mul(mlength, 2)
)
)
)
}
case 1 {
// The stored value fits in the slot, but the combined value
// will exceed it.
// get the keccak hash to get the contents of the array
mstore(0x0, _preBytes.slot)
let sc := add(keccak256(0x0, 0x20), div(slength, 32))
// save new length
sstore(_preBytes.slot, add(mul(newlength, 2), 1))
// The contents of the _postBytes array start 32 bytes into
// the structure. Our first read should obtain the `submod`
// bytes that can fit into the unused space in the last word
// of the stored array. To get this, we read 32 bytes starting
// from `submod`, so the data we read overlaps with the array
// contents by `submod` bytes. Masking the lowest-order
// `submod` bytes allows us to add that value directly to the
// stored value.
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 {
// get the keccak hash to get the contents of the array
mstore(0x0, _preBytes.slot)
// Start copying to the last used word of the stored array.
let sc := add(keccak256(0x0, 0x20), div(slength, 32))
// save new length
sstore(_preBytes.slot, add(mul(newlength, 2), 1))
// Copy over the first `submod` bytes of the new data as in
// case 1 above.
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 {
// Get a location of some free memory and store it in tempBytes as
// Solidity does for memory variables.
tempBytes := mload(0x40)
// The first word of the slice result is potentially a partial
// word read from the original array. To read it, we calculate
// the length of that partial word and start copying that many
// bytes into the array. The first word we copy will start with
// data we don't care about, but the last `lengthmod` bytes will
// land at the beginning of the contents of the new array. When
// we're done copying, we overwrite the full first word with
// the actual length of the slice.
let lengthmod := and(_length, 31)
// The multiplication in the next line is necessary
// because when slicing multiples of 32 bytes (lengthmod == 0)
// the following copy loop was copying the origin's length
// and then ending prematurely not copying everything it should.
let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
let end := add(mc, _length)
for {
// The multiplication in the next line has the same exact purpose
// as the one above.
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)
//update free-memory pointer
//allocating the array padded to 32 bytes like the compiler does now
mstore(0x40, and(add(mc, 31), not(31)))
}
//if we want a zero-length slice let's just return a zero-length array
default {
tempBytes := mload(0x40)
//zero out the 32 bytes slice we are about to return
//we need to do it because Solidity does not garbage collect
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)
// if lengths don't match the arrays are not equal
switch eq(length, mload(_postBytes))
case 1 {
// cb is a circuit breaker in the for loop since there's
// no said feature for inline assembly loops
// cb = 1 - don't breaker
// cb = 0 - break
let cb := 1
let mc := add(_preBytes, 0x20)
let end := add(mc, length)
for {
let cc := add(_postBytes, 0x20)
// the next line is the loop condition:
// while(uint256(mc < end) + cb == 2)
} eq(add(lt(mc, end), cb), 2) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
// if any of these checks fails then arrays are not equal
if iszero(eq(mload(mc), mload(cc))) {
// unsuccess:
success := 0
cb := 0
}
}
}
default {
// unsuccess:
success := 0
}
}
return success;
}
function equalStorage(
bytes storage _preBytes,
bytes memory _postBytes
)
internal
view
returns (bool)
{
bool success = true;
assembly {
// we know _preBytes_offset is 0
let fslot := sload(_preBytes.slot)
// Decode the length of the stored array like in concatStorage().
let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
let mlength := mload(_postBytes)
// if lengths don't match the arrays are not equal
switch eq(slength, mlength)
case 1 {
// slength can contain both the length and contents of the array
// if length < 32 bytes so let's prepare for that
// v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
if iszero(iszero(slength)) {
switch lt(slength, 32)
case 1 {
// blank the last byte which is the length
fslot := mul(div(fslot, 0x100), 0x100)
if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
// unsuccess:
success := 0
}
}
default {
// cb is a circuit breaker in the for loop since there's
// no said feature for inline assembly loops
// cb = 1 - don't breaker
// cb = 0 - break
let cb := 1
// get the keccak hash to get the contents of the array
mstore(0x0, _preBytes.slot)
let sc := keccak256(0x0, 0x20)
let mc := add(_postBytes, 0x20)
let end := add(mc, mlength)
// the next line is the loop condition:
// while(uint256(mc < end) + cb == 2)
for {} eq(add(lt(mc, end), cb), 2) {
sc := add(sc, 1)
mc := add(mc, 0x20)
} {
if iszero(eq(sload(sc), mload(mc))) {
// unsuccess:
success := 0
cb := 0
}
}
}
}
}
default {
// unsuccess:
success := 0
}
}
return success;
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IERC1271} from "../interfaces/IERC1271.sol";
library SignatureVerifier {
/// @notice Thrown when the passed in signature is not a valid length
error InvalidSignatureLength();
/// @notice Thrown when the recovered signer is equal to the zero address
error InvalidSignature();
/// @notice Thrown when the recovered signer does not equal the claimedSigner
error InvalidSigner();
/// @notice Thrown when the recovered contract signature is incorrect
error InvalidContractSignature();
bytes32 constant UPPER_BIT_MASK = (0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
function verify(bytes calldata signature, bytes32 hash, address claimedSigner) internal 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) {
// EIP-2098
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();
}
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
struct Order {
Status status;
uint64 amountIn;
uint16 destChainId;
}
struct OrderParams {
uint8 payloadType;
bytes32 trader;
bytes32 destAddr;
uint16 destChainId;
bytes32 referrerAddr;
bytes32 tokenOut;
uint64 minAmountOut;
uint64 gasDrop;
uint64 cancelFee;
uint64 refundFee;
uint64 deadline;
uint8 referrerBps;
uint8 auctionMode;
bytes32 random;
}
struct ExtraParams {
uint16 srcChainId;
bytes32 tokenIn;
uint8 protocolBps;
bytes32 customPayloadHash;
}
struct PermitParams {
uint256 value;
uint256 deadline;
uint8 v;
bytes32 r;
bytes32 s;
}
struct Key {
uint8 payloadType;
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;
bytes32 customPayloadHash;
}
struct PaymentParams {
uint8 payloadType;
bytes32 orderHash;
uint64 promisedAmount;
uint64 minAmountOut;
address destAddr;
address tokenOut;
uint64 gasDrop;
bool batch;
}
enum Status {
CREATED,
FULFILLED,
SETTLED,
UNLOCKED,
CANCELED,
REFUNDED
}
enum Action {
INVALID,
FULFILL,
UNLOCK,
REFUND,
BATCH_UNLOCK,
COMPRESSED_UNLOCK,
SET_REFUND_VERIFIER,
RESCUE
}
enum AuctionMode {
INVALID,
LIMIT_ORDER,
ENGLISH
}
struct UnlockMsg {
uint8 action;
bytes32 orderHash;
uint16 srcChainId;
bytes32 tokenIn;
bytes32 referrerAddr;
uint8 referrerBps;
uint8 protocolBps;
bytes32 unlockReceiver;
bytes32 driver;
uint64 fulfillTime;
}
uint constant UNLOCK_MSG_SIZE = 172; // excluding the action field
struct RefundMsg {
uint8 action;
bytes32 orderHash;
uint16 srcChainId;
bytes32 tokenIn;
bytes32 trader;
bytes32 canceler;
uint64 cancelFee;
uint64 refundFee;
}
struct FulfillMsg {
uint8 action;
bytes32 orderHash;
bytes32 driver;
uint64 promisedAmount;
uint16 penaltyPeriod;
}
struct TransferParams {
address from;
uint256 validAfter;
uint256 validBefore;
}
struct UnlockParams {
bytes32 recipient;
bytes32 driver;
bool batch;
}
struct RescueMsg {
uint8 action;
uint16 chainId;
address targetContract;
bytes32 orderHash;
uint8 orderStatus;
address token;
uint64 amount;
}
struct RefundVerifier {
uint8 action;
uint16 chainId;
address targetContract;
address verifier;
uint16 emitterChainId;
bytes32 emitterAddr;
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
error Paused();
error Unauthorized();
error InvalidAction();
error InvalidBpsFee();
error InvalidOrderStatus();
error InvalidOrderHash();
error InvalidEmitterChain();
error InvalidEmitterAddress();
error InvalidSrcChain();
error OrderNotExists(bytes32 orderHash);
error SmallAmountIn();
error FeesTooHigh();
error InvalidGasDrop();
error InvalidDestChain();
error DuplicateOrder();
error InsufficientAmount();
error InvalidAmount();
error DeadlineViolation();
error InvalidWormholeFee();
error InvalidAuctionMode();
error InvalidEvmAddr();
error InvalidPayload();
error InvalidPayloadLength();
error EmitterAddressExists();
error InvalidBatchIndex();
error InvalidAuctionConfig();
error SequenceAlreadyUsed();
error InvalidTargetContract();
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
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");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
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");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
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);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
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);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
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);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
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 {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IERC1271 {
/// @dev Should return whether the signature provided is valid for the provided data
/// @param hash Hash of the data to be signed
/// @param signature Signature byte array associated with _data
/// @return magicValue The bytes4 magic value 0x1626ba7e
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}