Transaction Hash:
Block:
23123508 at Aug-12-2025 07:35:11 AM +UTC
Transaction Fee:
0.000107300982264504 ETH
$0.21
Gas Used:
263,928 Gas / 0.406553993 Gwei
Emitted Events:
| 124 |
0xe7ae30c03395d66f30a26c49c91edae151747911.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000d493066498ace409059fda4c1bcd2e73d8cffe01, 0x000000000000000000000000d2d9c936165a85f27a5a7e07afb974d022b89463, 0000000000000000000000000000000000000000000000000000002e90edd000 )
|
| 125 |
0xe7ae30c03395d66f30a26c49c91edae151747911.0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925( 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925, 0x000000000000000000000000d2d9c936165a85f27a5a7e07afb974d022b89463, 0x00000000000000000000000064e4b81023621cb08f5ac305c2dd6edaad717834, 0000000000000000000000000000000000000000000000000000002e90edd000 )
|
| 126 |
0xe7ae30c03395d66f30a26c49c91edae151747911.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000d2d9c936165a85f27a5a7e07afb974d022b89463, 0x00000000000000000000000064e4b81023621cb08f5ac305c2dd6edaad717834, 0000000000000000000000000000000000000000000000000000002e90edd000 )
|
| 127 |
0xe7ae30c03395d66f30a26c49c91edae151747911.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000064e4b81023621cb08f5ac305c2dd6edaad717834, 0x0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000002e90edd000 )
|
| 128 |
Wormhole.0x6eb224fb001ed210e379b335e35efe88672a8ce935d981a6896b27ffdf52a3b2( 0x6eb224fb001ed210e379b335e35efe88672a8ce935d981a6896b27ffdf52a3b2, 0x000000000000000000000000454f5cf9143cda1291eb80dcbbcdfab4544e0920, 0000000000000000000000000000000000000000000000000000000000000004, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000080, 00000000000000000000000000000000000000000000000000000000000000ca, 00000000000000000000000000000000000000000000000000000000000000d9, 9945ff1000000000000000000000000064e4b81023621cb08f5ac305c2dd6eda, ad717834000000000000000000000000429410525068b694160d686eacf72ce1, 2b66599100910000000000000000000000000000000000000000000000000000, 000000000000000000000000000000000000d2d9c936165a85f27a5a7e07afb9, 74d022b89463004f994e54540800000000000000140000000000000000000000, 00e7ae30c03395d66f30a26c49c91edae1517479110000000000000000000000, 00d493066498ace409059fda4c1bcd2e73d8cffe01001e000000000000000000 )
|
| 129 |
0x454f5cf9143cda1291eb80dcbbcdfab4544e0920.0xc3192e083c87c556db539f071d8a298869f487e951327b5616a6f85ae3da958e( 0xc3192e083c87c556db539f071d8a298869f487e951327b5616a6f85ae3da958e, 0000000000000000000000000000000000000000000000000000000000000002, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )
|
| 130 |
0x454f5cf9143cda1291eb80dcbbcdfab4544e0920.0x79376a0dc6cbfe6f6f8f89ad24c262a8c6233f8df181d3fe5abb2e2442e8c738( 0x79376a0dc6cbfe6f6f8f89ad24c262a8c6233f8df181d3fe5abb2e2442e8c738, 000000000000000000000000000000000000000000000000000000000000001e, 0000000000000000000000000000000000000000000000000000000000000040, 00000000000000000000000064e4b81023621cb08f5ac305c2dd6edaad717834, 000000000000000000000000429410525068b694160d686eacf72ce12b665991, 0000000000000000000000000000000000000000000000000000000000000080, 0000000000000000000000000000000000000000000000000000000000000140, 0000000000000000000000000000000000000000000000000000000000000091, 0000000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000d2d9c936165a85f27a5a7e07afb974d022b89463, 004f994e5454080000000000000014000000000000000000000000e7ae30c033, 95d66f30a26c49c91edae151747911000000000000000000000000d493066498, ace409059fda4c1bcd2e73d8cffe01001e000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )
|
| 131 |
0x64e4b81023621cb08f5ac305c2dd6edaad717834.0xe54e51e42099622516fa3b48e9733581c9dbdcb771cafb093f745a0532a35982( 0xe54e51e42099622516fa3b48e9733581c9dbdcb771cafb093f745a0532a35982, 0x000000000000000000000000d493066498ace409059fda4c1bcd2e73d8cffe01, 0x000000000000000000000000d493066498ace409059fda4c1bcd2e73d8cffe01, 0000000000000000000000000000000000000000000000000000002e90edd000, 0000000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000000000000000000000000000000000000000001e, 0000000000000000000000000000000000000000000000000000000000000000 )
|
| 132 |
0x64e4b81023621cb08f5ac305c2dd6edaad717834.0x3e6ae56314c6da8b461d872f41c6d0bb69317b9d0232805aaccfa45df1a16fa0( 0x3e6ae56314c6da8b461d872f41c6d0bb69317b9d0232805aaccfa45df1a16fa0, 0x20ddcbd022cb4af30c31cb1d454c42504d9a67eefa47b85df92e42315da44a77 )
|
| 133 |
Executor.RequestForExecution( quoterAddress=0xa5400801...E2b133596, amtPaid=24858336000000, dstChain=30, dstAddr=000000000000000000000000429410525068B694160D686EACF72CE12B665991, refundAddr=[Sender] 0xd493066498ace409059fda4c1bcd2e73d8cffe01, signedQuote=0x45513031A54008017941ECE968623A0DD8EE907E2B1335960000000000000000000000006A8BFC410A3CC7306D52872F116AFB12F1CEC6C60002001E00000000689AFCA600000000000389B700000000003306C800002739B4CDB80000002739B4CDB800BB9F0EDBB011387C1F0C9EC019461839C651191AE129B95262A22C30B16F258172E75B1A9A34B71AB376E14267816338E67B64C01B5068BCB539D3CB4A527F6B1C, requestBytes=0x45524E31000200000000000000000000000064E4B81023621CB08F5AC305C2DD6EDAAD7178340000000000000000000000000000000000000000000000000000000000000000, relayInstructions=0x010000000000000000000000000007A12000000000000000000000000000000000 )
|
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
|
0x388C818C...7ccB19297
Miner
| (Lido: Execution Layer Rewards Vault) | 45.582088061193801806 Eth | 45.582108911505801806 Eth | 0.000020850312 | |
| 0x64E4b810...aaD717834 | |||||
| 0x6a8BFC41...2F1cec6C6 | 0.899402461032826408 Eth | 0.899427319368826408 Eth | 0.000024858336 | ||
| 0x98f3c9e6...f1464288B | (Wormhole: Ethereum Core Bridge) | ||||
| 0xd4930664...3D8cffE01 |
0.009515396157492247 Eth
Nonce: 764
|
0.009383236839227743 Eth
Nonce: 765
| 0.000132159318264504 | ||
| 0xE7ae30C0...151747911 |
Execution Trace
ETH 0.000024858336
NttManagerWithExecutor.transfer( nttManager=0x64E4b81023621CB08f5aC305c2dD6eDaaD717834, amount=200000000000, recipientChain=30, recipientAddress=000000000000000000000000D493066498ACE409059FDA4C1BCD2E73D8CFFE01, refundAddress=000000000000000000000000D493066498ACE409059FDA4C1BCD2E73D8CFFE01, encodedInstructions=0x01000101, executorArgs=[{name:value, type:uint256, order:1, indexed:false, value:24858336000000, valueString:24858336000000}, {name:refundAddress, type:address, order:2, indexed:false, value:0xd493066498aCe409059fDA4c1bCD2E73D8cffE01, valueString:0xd493066498aCe409059fDA4c1bCD2E73D8cffE01}, {name:signedQuote, type:bytes, order:3, indexed:false, value:0x45513031A54008017941ECE968623A0DD8EE907E2B1335960000000000000000000000006A8BFC410A3CC7306D52872F116AFB12F1CEC6C60002001E00000000689AFCA600000000000389B700000000003306C800002739B4CDB80000002739B4CDB800BB9F0EDBB011387C1F0C9EC019461839C651191AE129B95262A22C30B16F258172E75B1A9A34B71AB376E14267816338E67B64C01B5068BCB539D3CB4A527F6B1C, valueString:0x45513031A54008017941ECE968623A0DD8EE907E2B1335960000000000000000000000006A8BFC410A3CC7306D52872F116AFB12F1CEC6C60002001E00000000689AFCA600000000000389B700000000003306C800002739B4CDB80000002739B4CDB800BB9F0EDBB011387C1F0C9EC019461839C651191AE129B95262A22C30B16F258172E75B1A9A34B71AB376E14267816338E67B64C01B5068BCB539D3CB4A527F6B1C}, {name:instructions, type:bytes, order:4, indexed:false, value:0x010000000000000000000000000007A12000000000000000000000000000000000, valueString:0x010000000000000000000000000007A12000000000000000000000000000000000}], feeArgs=[{name:dbps, type:uint16, order:1, indexed:false, value:50, valueString:50}, {name:payee, type:address, order:2, indexed:false, value:0x9b2A3B92b1D86938D3Ed37B0519952C227bA6D09, valueString:0x9b2A3B92b1D86938D3Ed37B0519952C227bA6D09}] ) => ( msgId=0 )
0x64e4b81023621cb08f5ac305c2dd6edaad717834.STATICCALL( )-
0xad3d2d108532d663123e9184f1dd87f46e488a55.DELEGATECALL( )
-
-
Cygnus: clBTC Token.70a08231( )
-
Cygnus: clBTC Token.23b872dd( )
-
Cygnus: clBTC Token.70a08231( )
0x64e4b81023621cb08f5ac305c2dd6edaad717834.c128d170( )-
0xad3d2d108532d663123e9184f1dd87f46e488a55.c128d170( )
-
0x64e4b81023621cb08f5ac305c2dd6edaad717834.STATICCALL( )0xad3d2d108532d663123e9184f1dd87f46e488a55.DELEGATECALL( )-
Cygnus: clBTC Token.STATICCALL( )
-
-
Cygnus: clBTC Token.dd62ed3e( )
-
Cygnus: clBTC Token.095ea7b3( )
0x64e4b81023621cb08f5ac305c2dd6edaad717834.b293f97f( )0xad3d2d108532d663123e9184f1dd87f46e488a55.b293f97f( )-
Cygnus: clBTC Token.70a08231( )
-
Cygnus: clBTC Token.23b872dd( )
-
Cygnus: clBTC Token.70a08231( )
-
Cygnus: clBTC Token.42966c68( )
-
Cygnus: clBTC Token.70a08231( )
-
Cygnus: clBTC Token.STATICCALL( )
-
Cygnus: clBTC Token.STATICCALL( )
-
0x8f0dea1fb3ffce6930268861fcc11efd60aea581.b620e872( ) 0x454f5cf9143cda1291eb80dcbbcdfab4544e0920.b5634c73( )0x0092aa9918bf93ebdbea816645816c1faee7f321.b5634c73( )Wormhole.STATICCALL( )-
0x3c3d457f1522d3540ab3325aa5f1864e34cba9d0.DELEGATECALL( )
-
-
0x8f0dea1fb3ffce6930268861fcc11efd60aea581.2b9f4796( ) -
0x8f0dea1fb3ffce6930268861fcc11efd60aea581.8b4979b8( ) 0x454f5cf9143cda1291eb80dcbbcdfab4544e0920.4b5b0505( )0x0092aa9918bf93ebdbea816645816c1faee7f321.4b5b0505( )-
0x8f0dea1fb3ffce6930268861fcc11efd60aea581.1185b23c( ) Wormhole.b19a437e( )-
0x3c3d457f1522d3540ab3325aa5f1864e34cba9d0.b19a437e( )
-
-
-
Cygnus: clBTC Token.STATICCALL( )
-
0x64e4b81023621cb08f5ac305c2dd6edaad717834.c128d170( )-
0xad3d2d108532d663123e9184f1dd87f46e488a55.c128d170( )
-
ETH 0.000024858336
Executor.requestExecution( dstChain=30, dstAddr=000000000000000000000000429410525068B694160D686EACF72CE12B665991, refundAddr=0xd493066498aCe409059fDA4c1bCD2E73D8cffE01, signedQuoteBytes=0x45513031A54008017941ECE968623A0DD8EE907E2B1335960000000000000000000000006A8BFC410A3CC7306D52872F116AFB12F1CEC6C60002001E00000000689AFCA600000000000389B700000000003306C800002739B4CDB80000002739B4CDB800BB9F0EDBB011387C1F0C9EC019461839C651191AE129B95262A22C30B16F258172E75B1A9A34B71AB376E14267816338E67B64C01B5068BCB539D3CB4A527F6B1C, requestBytes=0x45524E31000200000000000000000000000064E4B81023621CB08F5AC305C2DD6EDAAD7178340000000000000000000000000000000000000000000000000000000000000000, relayInstructions=0x010000000000000000000000000007A12000000000000000000000000000000000 )- ETH 0.000024858336
0x6a8bfc410a3cc7306d52872f116afb12f1cec6c6.CALL( )
- ETH 0.000024858336
File 1 of 3: NttManagerWithExecutor
File 2 of 3: Wormhole
File 3 of 3: Executor
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.19;
import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
import "example-messaging-executor/evm/src/interfaces/IExecutor.sol";
import "example-messaging-executor/evm/src/libraries/ExecutorMessages.sol";
import "native-token-transfers/evm/src/interfaces/INttManager.sol";
import "./interfaces/INttManagerWithExecutor.sol";
string constant nttManagerWithExecutorVersion = "NttManagerWithExecutor-0.0.1";
/// @title NttManagerWithExecutor
/// @author Wormhole Project Contributors.
/// @notice The NttManagerWithExecutor contract is a shim contract that initiates
/// an NTT transfer using the executor for relaying.
contract NttManagerWithExecutor is INttManagerWithExecutor {
using TrimmedAmountLib for uint256;
using TrimmedAmountLib for TrimmedAmount;
uint16 public immutable chainId;
IExecutor public immutable executor;
string public constant VERSION = nttManagerWithExecutorVersion;
constructor(uint16 _chainId, address _executor) {
assert(_chainId != 0);
assert(_executor != address(0));
chainId = _chainId;
executor = IExecutor(_executor);
}
// ==================== External Interface ===============================================
/// @inheritdoc INttManagerWithExecutor
function transfer(
address nttManager,
uint256 amount,
uint16 recipientChain,
bytes32 recipientAddress,
bytes32 refundAddress,
bytes memory encodedInstructions,
ExecutorArgs calldata executorArgs,
FeeArgs calldata feeArgs
) external payable returns (uint64 msgId) {
INttManager nttm = INttManager(nttManager);
// Custody the tokens in this contract and approve NTT to spend them.
// Not worrying about dust here since the `NttManager` will revert in that case.
address token = nttm.token();
amount = custodyTokens(token, amount);
// Transfer the fee to the referrer.
amount = payFee(token, amount, feeArgs, nttm, recipientChain);
// Initiate the transfer.
SafeERC20.safeApprove(IERC20(token), nttManager, amount);
msgId = nttm.transfer{value: msg.value - executorArgs.value}(
amount, recipientChain, recipientAddress, refundAddress, false, encodedInstructions
);
// Generate the executor event.
executor.requestExecution{value: executorArgs.value}(
recipientChain,
nttm.getPeer(recipientChain).peerAddress,
executorArgs.refundAddress,
executorArgs.signedQuote,
ExecutorMessages.makeNTTv1Request(
chainId, bytes32(uint256(uint160(address(nttm)))), bytes32(uint256(msgId))
),
executorArgs.instructions
);
// Refund any excess value.
uint256 currentBalance = address(this).balance;
if (currentBalance > 0) {
(bool refundSuccessful,) = payable(executorArgs.refundAddress).call{value: currentBalance}("");
if (!refundSuccessful) {
revert RefundFailed(currentBalance);
}
}
}
// necessary for receiving native assets
receive() external payable {}
// ==================== Internal Functions ==============================================
function custodyTokens(address token, uint256 amount) internal returns (uint256) {
// query own token balance before transfer
uint256 balanceBefore = getBalance(token);
// deposit tokens
SafeERC20.safeTransferFrom(IERC20(token), msg.sender, address(this), amount);
// return the balance difference
return getBalance(token) - balanceBefore;
}
function getBalance(address token) internal view returns (uint256 balance) {
// fetch the specified token balance for this contract
(, bytes memory queriedBalance) =
token.staticcall(abi.encodeWithSelector(IERC20.balanceOf.selector, address(this)));
balance = abi.decode(queriedBalance, (uint256));
}
// @dev The fee is calculated as a percentage of the amount being transferred.
function payFee(
address token,
uint256 amount,
FeeArgs calldata feeArgs,
INttManager nttManager,
uint16 recipientChain
) internal returns (uint256) {
uint256 fee = calculateFee(amount, feeArgs.dbps);
fee = trimFee(nttManager, fee, recipientChain);
if (fee > 0) {
// Don't need to check for fee greater than or equal to amount because it can never be (since dbps is a uint16).
amount -= fee;
SafeERC20.safeTransfer(IERC20(token), feeArgs.payee, fee);
}
return amount;
}
function calculateFee(uint256 amount, uint16 dbps) public pure returns (uint256 fee) {
unchecked {
uint256 q = amount / 100000;
uint256 r = amount % 100000;
fee = q * dbps + (r * dbps) / 100000;
}
}
function trimFee(INttManager nttManager, uint256 amount, uint16 toChain) internal view returns (uint256 newFee) {
uint8 toDecimals = nttManager.getPeer(toChain).tokenDecimals;
if (toDecimals == 0) {
revert InvalidPeerDecimals();
}
uint8 fromDecimals = nttManager.tokenDecimals();
TrimmedAmount trimmedAmount = amount.trim(fromDecimals, toDecimals);
newFee = trimmedAmount.untrim(fromDecimals);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.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);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/draft-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;
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));
}
/**
* @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));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
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");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
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");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.19;
interface IExecutor {
struct SignedQuoteHeader {
bytes4 prefix;
address quoterAddress;
bytes32 payeeAddress;
uint16 srcChain;
uint16 dstChain;
uint64 expiryTime;
}
event RequestForExecution(
address indexed quoterAddress,
uint256 amtPaid,
uint16 dstChain,
bytes32 dstAddr,
address refundAddr,
bytes signedQuote,
bytes requestBytes,
bytes relayInstructions
);
function requestExecution(
uint16 dstChain,
bytes32 dstAddr,
address refundAddr,
bytes calldata signedQuote,
bytes calldata requestBytes,
bytes calldata relayInstructions
) external payable;
}
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.19;
library ExecutorMessages {
bytes4 private constant REQ_MM = "ERM1";
bytes4 private constant REQ_VAA_V1 = "ERV1";
bytes4 private constant REQ_NTT_V1 = "ERN1";
bytes4 private constant REQ_CCTP_V1 = "ERC1";
/// @notice Payload length will not fit in a uint32.
/// @dev Selector: 492f620d.
error PayloadTooLarge();
/// @notice Encodes a modular messaging request payload.
/// @param srcChain The source chain for the message (usually this chain).
/// @param srcAddr The source address for the message.
/// @param sequence The sequence number returned by `endpoint.sendMessage`.
/// @param payload The full payload, the keccak of which was sent to `endpoint.sendMessage`.
/// @return bytes The encoded request.
function makeMMRequest(uint16 srcChain, address srcAddr, uint64 sequence, bytes memory payload)
internal
pure
returns (bytes memory)
{
if (payload.length > type(uint32).max) {
revert PayloadTooLarge();
}
return abi.encodePacked(
REQ_MM, srcChain, bytes32(uint256(uint160(srcAddr))), sequence, uint32(payload.length), payload
);
}
/// @notice Encodes a version 1 VAA request payload.
/// @param emitterChain The emitter chain from the VAA.
/// @param emitterAddress The emitter address from the VAA.
/// @param sequence The sequence number from the VAA.
/// @return bytes The encoded request.
function makeVAAv1Request(uint16 emitterChain, bytes32 emitterAddress, uint64 sequence)
internal
pure
returns (bytes memory)
{
return abi.encodePacked(REQ_VAA_V1, emitterChain, emitterAddress, sequence);
}
/// @notice Encodes a version 1 NTT request payload.
/// @param srcChain The source chain for the NTT transfer.
/// @param srcManager The source manager for the NTT transfer.
/// @param messageId The manager message id for the NTT transfer.
/// @return bytes The encoded request.
function makeNTTv1Request(uint16 srcChain, bytes32 srcManager, bytes32 messageId)
internal
pure
returns (bytes memory)
{
return abi.encodePacked(REQ_NTT_V1, srcChain, srcManager, messageId);
}
/// @notice Encodes a version 1 CCTP request payload.
/// @param sourceDomain The source chain for the CCTP transfer.
/// @param nonce The nonce of the CCTP transfer.
/// @return bytes The encoded request.
function makeCCTPv1Request(uint32 sourceDomain, uint64 nonce) internal pure returns (bytes memory) {
return abi.encodePacked(REQ_CCTP_V1, sourceDomain, nonce);
}
}
// SPDX-License-Identifier: Apache 2
pragma solidity >=0.8.8 <0.9.0;
import "../libraries/TrimmedAmount.sol";
import "../libraries/TransceiverStructs.sol";
import "./IManagerBase.sol";
interface INttManager is IManagerBase {
/// @dev The peer on another chain.
struct NttManagerPeer {
bytes32 peerAddress;
uint8 tokenDecimals;
}
/// @notice Emitted when a message is sent from the nttManager.
/// @dev Topic0
/// 0xe54e51e42099622516fa3b48e9733581c9dbdcb771cafb093f745a0532a35982.
/// @param recipient The recipient of the message.
/// @param refundAddress The address on the destination chain to which the
/// refund of unused gas will be paid
/// @param amount The amount transferred.
/// @param fee The amount of ether sent along with the tx to cover the delivery fee.
/// @param recipientChain The chain ID of the recipient.
/// @param msgSequence The unique sequence ID of the message.
event TransferSent(
bytes32 indexed recipient,
bytes32 indexed refundAddress,
uint256 amount,
uint256 fee,
uint16 recipientChain,
uint64 msgSequence
);
/// @notice Emitted when a message is sent from the nttManager.
/// @dev Topic0
/// 0x3e6ae56314c6da8b461d872f41c6d0bb69317b9d0232805aaccfa45df1a16fa0.
/// @param digest The digest of the message.
event TransferSent(bytes32 indexed digest);
/// @notice Emitted when the peer contract is updated.
/// @dev Topic0
/// 0x1456404e7f41f35c3daac941bb50bad417a66275c3040061b4287d787719599d.
/// @param chainId_ The chain ID of the peer contract.
/// @param oldPeerContract The old peer contract address.
/// @param oldPeerDecimals The old peer contract decimals.
/// @param peerContract The new peer contract address.
/// @param peerDecimals The new peer contract decimals.
event PeerUpdated(
uint16 indexed chainId_,
bytes32 oldPeerContract,
uint8 oldPeerDecimals,
bytes32 peerContract,
uint8 peerDecimals
);
/// @notice Emitted when a transfer has been redeemed
/// (either minted or unlocked on the recipient chain).
/// @dev Topic0
/// 0x504e6efe18ab9eed10dc6501a417f5b12a2f7f2b1593aed9b89f9bce3cf29a91.
/// @param digest The digest of the message.
event TransferRedeemed(bytes32 indexed digest);
/// @notice Emitted when an outbound transfer has been cancelled
/// @dev Topic0
/// 0xf80e572ae1b63e2449629b6c7d783add85c36473926f216077f17ee002bcfd07.
/// @param sequence The sequence number being cancelled
/// @param recipient The canceller and recipient of the funds
/// @param amount The amount of the transfer being cancelled
event OutboundTransferCancelled(uint256 sequence, address recipient, uint256 amount);
/// @notice The transfer has some dust.
/// @dev Selector 0x71f0634a
/// @dev This is a security measure to prevent users from losing funds.
/// This is the result of trimming the amount and then untrimming it.
/// @param amount The amount to transfer.
error TransferAmountHasDust(uint256 amount, uint256 dust);
/// @notice The mode is invalid. It is neither in LOCKING or BURNING mode.
/// @dev Selector 0x66001a89
/// @param mode The mode.
error InvalidMode(uint8 mode);
/// @notice Error when trying to execute a message on an unintended target chain.
/// @dev Selector 0x3dcb204a.
/// @param targetChain The target chain.
/// @param thisChain The current chain.
error InvalidTargetChain(uint16 targetChain, uint16 thisChain);
/// @notice Error when the transfer amount is zero.
/// @dev Selector 0x9993626a.
error ZeroAmount();
/// @notice Error when the recipient is invalid.
/// @dev Selector 0x9c8d2cd2.
error InvalidRecipient();
/// @notice Error when the recipient is invalid.
/// @dev Selector 0xe2fe2726.
error InvalidRefundAddress();
/// @notice Error when the amount burned is different than the balance difference,
/// since NTT does not support burn fees.
/// @dev Selector 0x02156a8f.
/// @param burnAmount The amount burned.
/// @param balanceDiff The balance after burning.
error BurnAmountDifferentThanBalanceDiff(uint256 burnAmount, uint256 balanceDiff);
/// @notice The caller is not the deployer.
error UnexpectedDeployer(address expectedOwner, address owner);
/// @notice Peer for the chain does not match the configuration.
/// @param chainId ChainId of the source chain.
/// @param peerAddress Address of the peer nttManager contract.
error InvalidPeer(uint16 chainId, bytes32 peerAddress);
/// @notice Peer chain ID cannot be zero.
error InvalidPeerChainIdZero();
/// @notice Peer cannot be the zero address.
error InvalidPeerZeroAddress();
/// @notice Peer cannot have zero decimals.
error InvalidPeerDecimals();
/// @notice Staticcall reverted
/// @dev Selector 0x1222cd83
error StaticcallFailed();
/// @notice Error when someone other than the original sender tries to cancel a queued outbound transfer.
/// @dev Selector 0xceb40a85.
/// @param canceller The address trying to cancel the transfer.
/// @param sender The original sender that initiated the transfer that was queued.
error CancellerNotSender(address canceller, address sender);
/// @notice An unexpected msg.value was passed with the call
/// @dev Selector 0xbd28e889.
error UnexpectedMsgValue();
/// @notice Peer cannot be on the same chain
/// @dev Selector 0x20371f2a.
error InvalidPeerSameChainId();
/// @notice Feature is not implemented.
error NotImplemented();
/// @notice Transfer a given amount to a recipient on a given chain. This function is called
/// by the user to send the token cross-chain. This function will either lock or burn the
/// sender's tokens. Finally, this function will call into registered `Endpoint` contracts
/// to send a message with the incrementing sequence number and the token transfer payload.
/// @param amount The amount to transfer.
/// @param recipientChain The Wormhole chain ID for the destination.
/// @param recipient The recipient address.
/// @return msgId The resulting message ID of the transfer
function transfer(
uint256 amount,
uint16 recipientChain,
bytes32 recipient
) external payable returns (uint64 msgId);
/// @notice Transfer a given amount to a recipient on a given chain. This function is called
/// by the user to send the token cross-chain. This function will either lock or burn the
/// sender's tokens. Finally, this function will call into registered `Endpoint` contracts
/// to send a message with the incrementing sequence number and the token transfer payload.
/// @dev Transfers are queued if the outbound limit is hit and must be completed by the client.
/// @param amount The amount to transfer.
/// @param recipientChain The Wormhole chain ID for the destination.
/// @param recipient The recipient address.
/// @param refundAddress The address to which a refund for unussed gas is issued on the recipient chain.
/// @param shouldQueue Whether the transfer should be queued if the outbound limit is hit.
/// @param encodedInstructions Additional instructions to be forwarded to the recipient chain.
/// @return msgId The resulting message ID of the transfer
function transfer(
uint256 amount,
uint16 recipientChain,
bytes32 recipient,
bytes32 refundAddress,
bool shouldQueue,
bytes memory encodedInstructions
) external payable returns (uint64 msgId);
/// @notice Complete an outbound transfer that's been queued.
/// @dev This method is called by the client to complete an outbound transfer that's been queued.
/// @param queueSequence The sequence of the message in the queue.
/// @return msgSequence The sequence of the message.
function completeOutboundQueuedTransfer(
uint64 queueSequence
) external payable returns (uint64 msgSequence);
/// @notice Cancels an outbound transfer that's been queued.
/// @dev This method is called by the client to cancel an outbound transfer that's been queued.
/// @param queueSequence The sequence of the message in the queue.
function cancelOutboundQueuedTransfer(
uint64 queueSequence
) external;
/// @notice Complete an inbound queued transfer.
/// @param digest The digest of the message to complete.
function completeInboundQueuedTransfer(
bytes32 digest
) external;
/// @notice Called by an Endpoint contract to deliver a verified attestation.
/// @dev This function enforces attestation threshold and replay logic for messages. Once all
/// validations are complete, this function calls `executeMsg` to execute the command specified
/// by the message.
/// @param sourceChainId The Wormhole chain id of the sender.
/// @param sourceNttManagerAddress The address of the sender's NTT Manager contract.
/// @param payload The VAA payload.
function attestationReceived(
uint16 sourceChainId,
bytes32 sourceNttManagerAddress,
TransceiverStructs.NttManagerMessage memory payload
) external;
/// @notice Called after a message has been sufficiently verified to execute
/// the command in the message. This function will decode the payload
/// as an NttManagerMessage to extract the sequence, msgType, and other parameters.
/// @dev This function is exposed as a fallback for when an `Transceiver` is deregistered
/// when a message is in flight.
/// @param sourceChainId The Wormhole chain id of the sender.
/// @param sourceNttManagerAddress The address of the sender's nttManager contract.
/// @param message The message to execute.
function executeMsg(
uint16 sourceChainId,
bytes32 sourceNttManagerAddress,
TransceiverStructs.NttManagerMessage memory message
) external;
/// @notice Returns the number of decimals of the token managed by the NttManager.
/// @return decimals The number of decimals of the token.
function tokenDecimals() external view returns (uint8);
/// @notice Returns registered peer contract for a given chain.
/// @param chainId_ Wormhole chain ID.
function getPeer(
uint16 chainId_
) external view returns (NttManagerPeer memory);
/// @notice Sets the corresponding peer.
/// @dev The nttManager that executes the message sets the source nttManager as the peer.
/// @param peerChainId The Wormhole chain ID of the peer.
/// @param peerContract The address of the peer nttManager contract.
/// @param decimals The number of decimals of the token on the peer chain.
/// @param inboundLimit The inbound rate limit for the peer chain id. This is formatted in the normal
/// token representation. e.g. a limit of 100 for a token with 6 decimals = 100_000_000
function setPeer(
uint16 peerChainId,
bytes32 peerContract,
uint8 decimals,
uint256 inboundLimit
) external;
/// @notice Sets the outbound transfer limit for a given chain.
/// @dev This method can only be executed by the `owner`.
/// @param limit The new outbound limit. This is formatted in the normal
/// token representation. e.g. a limit of 100 for a token with 6 decimals = 100_000_000
function setOutboundLimit(
uint256 limit
) external;
/// @notice Sets the inbound transfer limit for a given chain.
/// @dev This method can only be executed by the `owner`.
/// @param limit The new limit. This is formatted in the normal
/// token representation. e.g. a limit of 100 for a token with 6 decimals = 100_000_000
/// @param chainId The Wormhole chain ID to set the limit for.
function setInboundLimit(uint256 limit, uint16 chainId) external;
}
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.19;
struct ExecutorArgs {
// The msg value to be passed into the Executor.
uint256 value;
// The refund address used by the Executor.
address refundAddress;
// The signed quote to be passed into the Executor.
bytes signedQuote;
// The relay instructions to be passed into the Executor.
bytes instructions;
}
struct FeeArgs {
// The fee in tenths of basis points.
uint16 dbps;
// To whom the fee should be paid (the "referrer").
address payee;
}
interface INttManagerWithExecutor {
/// @notice Error when the refund to the sender fails.
/// @dev Selector 0x2ca23714.
/// @param refundAmount The refund amount.
error RefundFailed(uint256 refundAmount);
/// @notice Peer cannot have zero decimals.
error InvalidPeerDecimals();
/// @notice Transfer a given amount to a recipient on a given chain using the Executor for relaying.
/// @param nttManager The NTT manager used for the transfer.
/// @param amount The amount to transfer.
/// @param recipientChain The Wormhole chain ID for the destination.
/// @param recipientAddress The recipient address.
/// @param refundAddress The address to which a refund for unussed gas is issued on the recipient chain.
/// @param encodedInstructions Additional instructions to be forwarded to the recipient chain.
/// @param executorArgs The arguments to be passed into the Executor.
/// @param feeArgs The arguments used to compute and pay the referrer fee.
/// @return msgId The resulting message ID of the transfer
function transfer(
address nttManager,
uint256 amount,
uint16 recipientChain,
bytes32 recipientAddress,
bytes32 refundAddress,
bytes memory encodedInstructions,
ExecutorArgs calldata executorArgs,
FeeArgs calldata feeArgs
) external payable returns (uint64 msgId);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-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);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.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
* ====
*
* [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://diligence.consensys.net/posts/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.5.11/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);
}
}
}
// SPDX-License-Identifier: Apache 2
/// @dev TrimmedAmount is a utility library to handle token amounts with different decimals
pragma solidity >=0.8.8 <0.9.0;
import "openzeppelin-contracts/contracts/utils/math/SafeCast.sol";
/// @dev TrimmedAmount is a bit-packed representation of a token amount and its decimals.
/// @dev 64 bits: [0 - 64] amount
/// @dev 8 bits: [64 - 72] decimals
type TrimmedAmount is uint72;
using {gt as >, lt as <, sub as -, add as +, eq as ==, min, unwrap} for TrimmedAmount global;
function minUint8(uint8 a, uint8 b) pure returns (uint8) {
return a < b ? a : b;
}
/// @notice Error when the decimals of two TrimmedAmounts are not equal
/// @dev Selector. b9cdb6c2
/// @param decimals the decimals of the first TrimmedAmount
/// @param decimalsOther the decimals of the second TrimmedAmount
error NumberOfDecimalsNotEqual(uint8 decimals, uint8 decimalsOther);
uint8 constant TRIMMED_DECIMALS = 8;
function unwrap(
TrimmedAmount a
) pure returns (uint72) {
return TrimmedAmount.unwrap(a);
}
function packTrimmedAmount(uint64 amt, uint8 decimals) pure returns (TrimmedAmount) {
// cast to u72 first to prevent overflow
uint72 amount = uint72(amt);
uint72 dec = uint72(decimals);
// shift the amount to the left 8 bits
amount <<= 8;
return TrimmedAmount.wrap(amount | dec);
}
function eq(TrimmedAmount a, TrimmedAmount b) pure returns (bool) {
return TrimmedAmountLib.getAmount(a) == TrimmedAmountLib.getAmount(b)
&& TrimmedAmountLib.getDecimals(a) == TrimmedAmountLib.getDecimals(b);
}
function checkDecimals(TrimmedAmount a, TrimmedAmount b) pure {
uint8 aDecimals = TrimmedAmountLib.getDecimals(a);
uint8 bDecimals = TrimmedAmountLib.getDecimals(b);
if (aDecimals != bDecimals) {
revert NumberOfDecimalsNotEqual(aDecimals, bDecimals);
}
}
function gt(TrimmedAmount a, TrimmedAmount b) pure returns (bool) {
checkDecimals(a, b);
return TrimmedAmountLib.getAmount(a) > TrimmedAmountLib.getAmount(b);
}
function lt(TrimmedAmount a, TrimmedAmount b) pure returns (bool) {
checkDecimals(a, b);
return TrimmedAmountLib.getAmount(a) < TrimmedAmountLib.getAmount(b);
}
function sub(TrimmedAmount a, TrimmedAmount b) pure returns (TrimmedAmount) {
checkDecimals(a, b);
return packTrimmedAmount(
TrimmedAmountLib.getAmount(a) - TrimmedAmountLib.getAmount(b),
TrimmedAmountLib.getDecimals(a)
);
}
function add(TrimmedAmount a, TrimmedAmount b) pure returns (TrimmedAmount) {
checkDecimals(a, b);
return packTrimmedAmount(
TrimmedAmountLib.getAmount(a) + TrimmedAmountLib.getAmount(b),
TrimmedAmountLib.getDecimals(b)
);
}
function min(TrimmedAmount a, TrimmedAmount b) pure returns (TrimmedAmount) {
checkDecimals(a, b);
return TrimmedAmountLib.getAmount(a) < TrimmedAmountLib.getAmount(b) ? a : b;
}
library TrimmedAmountLib {
/// @notice Error when the amount to be trimmed is greater than u64MAX.
/// @dev Selector 0x08083b2a.
/// @param amount The amount to be trimmed.
error AmountTooLarge(uint256 amount);
function getAmount(
TrimmedAmount a
) internal pure returns (uint64) {
// Extract the raw integer value from TrimmedAmount
uint72 rawValue = TrimmedAmount.unwrap(a);
// Right shift to keep only the higher 64 bits
uint64 result = uint64(rawValue >> 8);
return result;
}
function getDecimals(
TrimmedAmount a
) internal pure returns (uint8) {
return uint8(TrimmedAmount.unwrap(a) & 0xFF);
}
/// @dev Set the decimals of the TrimmedAmount.
/// This function should only be used for testing purposes, as it
/// should not be necessary to change the decimals of a TrimmedAmount
/// under normal circumstances.
function setDecimals(TrimmedAmount a, uint8 decimals) internal pure returns (TrimmedAmount) {
return TrimmedAmount.wrap((TrimmedAmount.unwrap(a) & ~uint72(0xFF)) | decimals);
}
function isNull(
TrimmedAmount a
) internal pure returns (bool) {
return (getAmount(a) == 0 && getDecimals(a) == 0);
}
function saturatingAdd(
TrimmedAmount a,
TrimmedAmount b
) internal pure returns (TrimmedAmount) {
checkDecimals(a, b);
uint256 saturatedSum;
uint64 aAmount = getAmount(a);
uint64 bAmount = getAmount(b);
unchecked {
saturatedSum = uint256(aAmount) + uint256(bAmount);
saturatedSum = saturatedSum > type(uint64).max ? type(uint64).max : saturatedSum;
}
return packTrimmedAmount(SafeCast.toUint64(saturatedSum), getDecimals(a));
}
/// @dev scale the amount from original decimals to target decimals (base 10)
function scale(
uint256 amount,
uint8 fromDecimals,
uint8 toDecimals
) internal pure returns (uint256) {
if (fromDecimals == toDecimals) {
return amount;
}
if (fromDecimals > toDecimals) {
return amount / (10 ** (fromDecimals - toDecimals));
} else {
return amount * (10 ** (toDecimals - fromDecimals));
}
}
function shift(TrimmedAmount amount, uint8 toDecimals) internal pure returns (TrimmedAmount) {
uint8 actualToDecimals = minUint8(TRIMMED_DECIMALS, toDecimals);
return packTrimmedAmount(
SafeCast.toUint64(scale(getAmount(amount), getDecimals(amount), actualToDecimals)),
actualToDecimals
);
}
function max(
uint8 decimals
) internal pure returns (TrimmedAmount) {
uint8 actualDecimals = minUint8(TRIMMED_DECIMALS, decimals);
return packTrimmedAmount(type(uint64).max, actualDecimals);
}
/// @dev trim the amount to target decimals.
/// The actual resulting decimals is the minimum of TRIMMED_DECIMALS,
/// fromDecimals, and toDecimals. This ensures that no dust is
/// destroyed on either side of the transfer.
/// @param amt the amount to be trimmed
/// @param fromDecimals the original decimals of the amount
/// @param toDecimals the target decimals of the amount
/// @return TrimmedAmount uint72 value type bit-packed with decimals
function trim(
uint256 amt,
uint8 fromDecimals,
uint8 toDecimals
) internal pure returns (TrimmedAmount) {
uint8 actualToDecimals = minUint8(minUint8(TRIMMED_DECIMALS, fromDecimals), toDecimals);
uint256 amountScaled = scale(amt, fromDecimals, actualToDecimals);
// NOTE: amt after trimming must fit into uint64 (that's the point of
// trimming, as Solana only supports uint64 for token amts)
return packTrimmedAmount(SafeCast.toUint64(amountScaled), actualToDecimals);
}
function untrim(TrimmedAmount amt, uint8 toDecimals) internal pure returns (uint256) {
uint256 deNorm = uint256(getAmount(amt));
uint8 fromDecimals = getDecimals(amt);
uint256 amountScaled = scale(deNorm, fromDecimals, toDecimals);
return amountScaled;
}
}
// SPDX-License-Identifier: Apache 2
pragma solidity >=0.8.8 <0.9.0;
import "wormhole-solidity-sdk/libraries/BytesParsing.sol";
import "./TrimmedAmount.sol";
library TransceiverStructs {
using BytesParsing for bytes;
using TrimmedAmountLib for TrimmedAmount;
/// @notice Error thrown when the payload length exceeds the allowed maximum.
/// @dev Selector 0xa3419691.
/// @param size The size of the payload.
error PayloadTooLong(uint256 size);
/// @notice Error thrown when the prefix of an encoded message
/// does not match the expected value.
/// @dev Selector 0x56d2569d.
/// @param prefix The prefix that was found in the encoded message.
error IncorrectPrefix(bytes4 prefix);
/// @notice Error thrown when the transceiver instructions aren't
/// encoded with strictly increasing indices
/// @dev Selector 0x0555a4b9.
/// @param lastIndex Last parsed instruction index
/// @param instructionIndex The instruction index that was unordered
error UnorderedInstructions(uint256 lastIndex, uint256 instructionIndex);
/// @notice Error thrown when a transceiver instruction index
/// is greater than the number of registered transceivers
/// @dev We index from 0 so if providedIndex == numTransceivers then we're out-of-bounds too
/// @dev Selector 0x689f5016.
/// @param providedIndex The index specified in the instruction
/// @param numTransceivers The number of registered transceivers
error InvalidInstructionIndex(uint256 providedIndex, uint256 numTransceivers);
/// @dev Prefix for all NativeTokenTransfer payloads
/// This is 0x99'N''T''T'
bytes4 constant NTT_PREFIX = 0x994E5454;
/// @dev Message emitted and received by the nttManager contract.
/// The wire format is as follows:
/// - id - 32 bytes
/// - sender - 32 bytes
/// - payloadLength - 2 bytes
/// - payload - `payloadLength` bytes
struct NttManagerMessage {
/// @notice unique message identifier
/// @dev This is incrementally assigned on EVM chains, but this is not
/// guaranteed on other runtimes.
bytes32 id;
/// @notice original message sender address.
bytes32 sender;
/// @notice payload that corresponds to the type.
bytes payload;
}
function nttManagerMessageDigest(
uint16 sourceChainId,
NttManagerMessage memory m
) public pure returns (bytes32) {
return _nttManagerMessageDigest(sourceChainId, encodeNttManagerMessage(m));
}
function _nttManagerMessageDigest(
uint16 sourceChainId,
bytes memory encodedNttManagerMessage
) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(sourceChainId, encodedNttManagerMessage));
}
function encodeNttManagerMessage(
NttManagerMessage memory m
) public pure returns (bytes memory encoded) {
if (m.payload.length > type(uint16).max) {
revert PayloadTooLong(m.payload.length);
}
uint16 payloadLength = uint16(m.payload.length);
return abi.encodePacked(m.id, m.sender, payloadLength, m.payload);
}
/// @notice Parse a NttManagerMessage.
/// @param encoded The byte array corresponding to the encoded message
/// @return nttManagerMessage The parsed NttManagerMessage struct.
function parseNttManagerMessage(
bytes memory encoded
) public pure returns (NttManagerMessage memory nttManagerMessage) {
uint256 offset = 0;
(nttManagerMessage.id, offset) = encoded.asBytes32Unchecked(offset);
(nttManagerMessage.sender, offset) = encoded.asBytes32Unchecked(offset);
uint256 payloadLength;
(payloadLength, offset) = encoded.asUint16Unchecked(offset);
(nttManagerMessage.payload, offset) = encoded.sliceUnchecked(offset, payloadLength);
encoded.checkLength(offset);
}
/// @dev Native Token Transfer payload.
/// The wire format is as follows:
/// - NTT_PREFIX - 4 bytes
/// - numDecimals - 1 byte
/// - amount - 8 bytes
/// - sourceToken - 32 bytes
/// - to - 32 bytes
/// - toChain - 2 bytes
/// - additionalPayloadLength - 2 bytes, optional
/// - additionalPayload - `additionalPayloadLength` bytes
struct NativeTokenTransfer {
/// @notice Amount being transferred (big-endian u64 and u8 for decimals)
TrimmedAmount amount;
/// @notice Source chain token address.
bytes32 sourceToken;
/// @notice Address of the recipient.
bytes32 to;
/// @notice Chain ID of the recipient
uint16 toChain;
/// @notice Custom payload
/// @dev Recommended that the first 4 bytes are a unique prefix
bytes additionalPayload;
}
function encodeNativeTokenTransfer(
NativeTokenTransfer memory m
) public pure returns (bytes memory encoded) {
// The `amount` and `decimals` fields are encoded in reverse order compared to how they are declared in the
// `TrimmedAmount` type. This is consistent with the Rust NTT implementation.
TrimmedAmount transferAmount = m.amount;
if (m.additionalPayload.length > 0) {
if (m.additionalPayload.length > type(uint16).max) {
revert PayloadTooLong(m.additionalPayload.length);
}
uint16 additionalPayloadLength = uint16(m.additionalPayload.length);
return abi.encodePacked(
NTT_PREFIX,
transferAmount.getDecimals(),
transferAmount.getAmount(),
m.sourceToken,
m.to,
m.toChain,
additionalPayloadLength,
m.additionalPayload
);
}
return abi.encodePacked(
NTT_PREFIX,
transferAmount.getDecimals(),
transferAmount.getAmount(),
m.sourceToken,
m.to,
m.toChain
);
}
/// @dev Parse a NativeTokenTransfer.
/// @param encoded The byte array corresponding to the encoded message
/// @return nativeTokenTransfer The parsed NativeTokenTransfer struct.
function parseNativeTokenTransfer(
bytes memory encoded
) public pure returns (NativeTokenTransfer memory nativeTokenTransfer) {
uint256 offset = 0;
bytes4 prefix;
(prefix, offset) = encoded.asBytes4Unchecked(offset);
if (prefix != NTT_PREFIX) {
revert IncorrectPrefix(prefix);
}
// The `amount` and `decimals` fields are parsed in reverse order compared to how they are declared in the
// `TrimmedAmount` struct. This is consistent with the Rust NTT implementation.
uint8 numDecimals;
(numDecimals, offset) = encoded.asUint8Unchecked(offset);
uint64 amount;
(amount, offset) = encoded.asUint64Unchecked(offset);
nativeTokenTransfer.amount = packTrimmedAmount(amount, numDecimals);
(nativeTokenTransfer.sourceToken, offset) = encoded.asBytes32Unchecked(offset);
(nativeTokenTransfer.to, offset) = encoded.asBytes32Unchecked(offset);
(nativeTokenTransfer.toChain, offset) = encoded.asUint16Unchecked(offset);
// The additional payload may be omitted, but if it is included, it is prefixed by a u16 for its length.
// If there are at least 2 bytes remaining, attempt to parse the additional payload.
if (encoded.length >= offset + 2) {
uint256 payloadLength;
(payloadLength, offset) = encoded.asUint16Unchecked(offset);
(nativeTokenTransfer.additionalPayload, offset) =
encoded.sliceUnchecked(offset, payloadLength);
}
encoded.checkLength(offset);
}
/// @dev Message emitted by Transceiver implementations.
/// Each message includes an Transceiver-specified 4-byte prefix.
/// The wire format is as follows:
/// - prefix - 4 bytes
/// - sourceNttManagerAddress - 32 bytes
/// - recipientNttManagerAddress - 32 bytes
/// - nttManagerPayloadLength - 2 bytes
/// - nttManagerPayload - `nttManagerPayloadLength` bytes
/// - transceiverPayloadLength - 2 bytes
/// - transceiverPayload - `transceiverPayloadLength` bytes
struct TransceiverMessage {
/// @notice Address of the NttManager contract that emitted this message.
bytes32 sourceNttManagerAddress;
/// @notice Address of the NttManager contract that receives this message.
bytes32 recipientNttManagerAddress;
/// @notice Payload provided to the Transceiver contract by the NttManager contract.
bytes nttManagerPayload;
/// @notice Optional payload that the transceiver can encode and use for its own message passing purposes.
bytes transceiverPayload;
}
// @notice Encodes an Transceiver message for communication between the
// NttManager and the Transceiver.
// @param m The TransceiverMessage struct containing the message details.
// @return encoded The byte array corresponding to the encoded message.
// @custom:throw PayloadTooLong if the length of transceiverId, nttManagerPayload,
// or transceiverPayload exceeds the allowed maximum.
function encodeTransceiverMessage(
bytes4 prefix,
TransceiverMessage memory m
) public pure returns (bytes memory encoded) {
if (m.nttManagerPayload.length > type(uint16).max) {
revert PayloadTooLong(m.nttManagerPayload.length);
}
uint16 nttManagerPayloadLength = uint16(m.nttManagerPayload.length);
if (m.transceiverPayload.length > type(uint16).max) {
revert PayloadTooLong(m.transceiverPayload.length);
}
uint16 transceiverPayloadLength = uint16(m.transceiverPayload.length);
return abi.encodePacked(
prefix,
m.sourceNttManagerAddress,
m.recipientNttManagerAddress,
nttManagerPayloadLength,
m.nttManagerPayload,
transceiverPayloadLength,
m.transceiverPayload
);
}
function buildAndEncodeTransceiverMessage(
bytes4 prefix,
bytes32 sourceNttManagerAddress,
bytes32 recipientNttManagerAddress,
bytes memory nttManagerMessage,
bytes memory transceiverPayload
) public pure returns (TransceiverMessage memory, bytes memory) {
TransceiverMessage memory transceiverMessage = TransceiverMessage({
sourceNttManagerAddress: sourceNttManagerAddress,
recipientNttManagerAddress: recipientNttManagerAddress,
nttManagerPayload: nttManagerMessage,
transceiverPayload: transceiverPayload
});
bytes memory encoded = encodeTransceiverMessage(prefix, transceiverMessage);
return (transceiverMessage, encoded);
}
/// @dev Parses an encoded message and extracts information into an TransceiverMessage struct.
/// @param encoded The encoded bytes containing information about the TransceiverMessage.
/// @return transceiverMessage The parsed TransceiverMessage struct.
/// @custom:throw IncorrectPrefix if the prefix of the encoded message does not
/// match the expected prefix.
function parseTransceiverMessage(
bytes4 expectedPrefix,
bytes memory encoded
) internal pure returns (TransceiverMessage memory transceiverMessage) {
uint256 offset = 0;
bytes4 prefix;
(prefix, offset) = encoded.asBytes4Unchecked(offset);
if (prefix != expectedPrefix) {
revert IncorrectPrefix(prefix);
}
(transceiverMessage.sourceNttManagerAddress, offset) = encoded.asBytes32Unchecked(offset);
(transceiverMessage.recipientNttManagerAddress, offset) = encoded.asBytes32Unchecked(offset);
uint16 nttManagerPayloadLength;
(nttManagerPayloadLength, offset) = encoded.asUint16Unchecked(offset);
(transceiverMessage.nttManagerPayload, offset) =
encoded.sliceUnchecked(offset, nttManagerPayloadLength);
uint16 transceiverPayloadLength;
(transceiverPayloadLength, offset) = encoded.asUint16Unchecked(offset);
(transceiverMessage.transceiverPayload, offset) =
encoded.sliceUnchecked(offset, transceiverPayloadLength);
// Check if the entire byte array has been processed
encoded.checkLength(offset);
}
/// @dev Parses the payload of an Transceiver message and returns
/// the parsed NttManagerMessage struct.
/// @param expectedPrefix The prefix that should be encoded in the nttManager message.
/// @param payload The payload sent across the wire.
function parseTransceiverAndNttManagerMessage(
bytes4 expectedPrefix,
bytes memory payload
) public pure returns (TransceiverMessage memory, NttManagerMessage memory) {
// parse the encoded message payload from the Transceiver
TransceiverMessage memory parsedTransceiverMessage =
parseTransceiverMessage(expectedPrefix, payload);
// parse the encoded message payload from the NttManager
NttManagerMessage memory parsedNttManagerMessage =
parseNttManagerMessage(parsedTransceiverMessage.nttManagerPayload);
return (parsedTransceiverMessage, parsedNttManagerMessage);
}
/// @dev Variable-length transceiver-specific instruction that can be passed by the caller to the nttManager.
/// The index field refers to the index of the registeredTransceiver that this instruction should be passed to.
/// The serialization format is:
/// - index - 1 byte
/// - payloadLength - 1 byte
/// - payload - `payloadLength` bytes
struct TransceiverInstruction {
uint8 index;
bytes payload;
}
function encodeTransceiverInstruction(
TransceiverInstruction memory instruction
) public pure returns (bytes memory) {
if (instruction.payload.length > type(uint8).max) {
revert PayloadTooLong(instruction.payload.length);
}
uint8 payloadLength = uint8(instruction.payload.length);
return abi.encodePacked(instruction.index, payloadLength, instruction.payload);
}
function parseTransceiverInstructionUnchecked(
bytes memory encoded,
uint256 offset
) public pure returns (TransceiverInstruction memory instruction, uint256 nextOffset) {
(instruction.index, nextOffset) = encoded.asUint8Unchecked(offset);
uint8 instructionLength;
(instructionLength, nextOffset) = encoded.asUint8Unchecked(nextOffset);
(instruction.payload, nextOffset) = encoded.sliceUnchecked(nextOffset, instructionLength);
}
function parseTransceiverInstructionChecked(
bytes memory encoded
) public pure returns (TransceiverInstruction memory instruction) {
uint256 offset = 0;
(instruction, offset) = parseTransceiverInstructionUnchecked(encoded, offset);
encoded.checkLength(offset);
}
/// @dev Encode an array of multiple variable-length transceiver-specific instructions.
/// The serialization format is:
/// - instructionsLength - 1 byte
/// - `instructionsLength` number of serialized `TransceiverInstruction` types.
function encodeTransceiverInstructions(
TransceiverInstruction[] memory instructions
) public pure returns (bytes memory) {
if (instructions.length > type(uint8).max) {
revert PayloadTooLong(instructions.length);
}
uint256 instructionsLength = instructions.length;
bytes memory encoded;
for (uint256 i = 0; i < instructionsLength; i++) {
bytes memory innerEncoded = encodeTransceiverInstruction(instructions[i]);
encoded = bytes.concat(encoded, innerEncoded);
}
return abi.encodePacked(uint8(instructionsLength), encoded);
}
function parseTransceiverInstructions(
bytes memory encoded,
uint256 numRegisteredTransceivers
) public pure returns (TransceiverInstruction[] memory) {
uint256 offset = 0;
uint256 instructionsLength;
(instructionsLength, offset) = encoded.asUint8Unchecked(offset);
// We allocate an array with the length of the number of registered transceivers
// This gives us the flexibility to not have to pass instructions for transceivers that
// don't need them
TransceiverInstruction[] memory instructions =
new TransceiverInstruction[](numRegisteredTransceivers);
uint256 lastIndex = 0;
for (uint256 i = 0; i < instructionsLength; i++) {
TransceiverInstruction memory instruction;
(instruction, offset) = parseTransceiverInstructionUnchecked(encoded, offset);
uint8 instructionIndex = instruction.index;
// The instructions passed in have to be strictly increasing in terms of transceiver index
if (i != 0 && instructionIndex <= lastIndex) {
revert UnorderedInstructions(lastIndex, instructionIndex);
}
// Instruction index is out of bounds
if (instructionIndex >= numRegisteredTransceivers) {
revert InvalidInstructionIndex(instructionIndex, numRegisteredTransceivers);
}
lastIndex = instructionIndex;
instructions[instructionIndex] = instruction;
}
encoded.checkLength(offset);
return instructions;
}
struct TransceiverInit {
bytes4 transceiverIdentifier;
bytes32 nttManagerAddress;
uint8 nttManagerMode;
bytes32 tokenAddress;
uint8 tokenDecimals;
}
function encodeTransceiverInit(
TransceiverInit memory init
) public pure returns (bytes memory) {
return abi.encodePacked(
init.transceiverIdentifier,
init.nttManagerAddress,
init.nttManagerMode,
init.tokenAddress,
init.tokenDecimals
);
}
function decodeTransceiverInit(
bytes memory encoded
) public pure returns (TransceiverInit memory init) {
uint256 offset = 0;
(init.transceiverIdentifier, offset) = encoded.asBytes4Unchecked(offset);
(init.nttManagerAddress, offset) = encoded.asBytes32Unchecked(offset);
(init.nttManagerMode, offset) = encoded.asUint8Unchecked(offset);
(init.tokenAddress, offset) = encoded.asBytes32Unchecked(offset);
(init.tokenDecimals, offset) = encoded.asUint8Unchecked(offset);
encoded.checkLength(offset);
}
struct TransceiverRegistration {
bytes4 transceiverIdentifier;
uint16 transceiverChainId;
bytes32 transceiverAddress;
}
function encodeTransceiverRegistration(
TransceiverRegistration memory registration
) public pure returns (bytes memory) {
return abi.encodePacked(
registration.transceiverIdentifier,
registration.transceiverChainId,
registration.transceiverAddress
);
}
function decodeTransceiverRegistration(
bytes memory encoded
) public pure returns (TransceiverRegistration memory registration) {
uint256 offset = 0;
(registration.transceiverIdentifier, offset) = encoded.asBytes4Unchecked(offset);
(registration.transceiverChainId, offset) = encoded.asUint16Unchecked(offset);
(registration.transceiverAddress, offset) = encoded.asBytes32Unchecked(offset);
encoded.checkLength(offset);
}
}
// SPDX-License-Identifier: Apache 2
pragma solidity >=0.8.8 <0.9.0;
import "../libraries/TransceiverStructs.sol";
interface IManagerBase {
/// @notice The mode is either LOCKING or BURNING. In LOCKING mode, the NttManager locks the
/// tokens of the sender and mints an equivalent amount on the target chain. In BURNING
/// mode, the NttManager burns the tokens of the sender and mints an equivalent amount
/// on the target chain.LOCKING mode preserves the total supply of the tokens.
enum Mode {
LOCKING,
BURNING
}
/// @notice Information about attestations for a given message.
/// @dev The fields are as follows:
/// - executed: whether the message has been executed.
/// - attested: bitmap of transceivers that have attested to this message.
/// (NOTE: might contain disabled transceivers)
struct AttestationInfo {
bool executed;
uint64 attestedTransceivers;
}
struct _Sequence {
uint64 num;
}
struct _Threshold {
uint8 num;
}
/// @notice Emitted when a message has been attested to.
/// @dev Topic0
/// 0x35a2101eaac94b493e0dfca061f9a7f087913fde8678e7cde0aca9897edba0e5.
/// @param digest The digest of the message.
/// @param transceiver The address of the transceiver.
/// @param index The index of the transceiver in the bitmap.
event MessageAttestedTo(bytes32 digest, address transceiver, uint8 index);
/// @notice Emmitted when the threshold required transceivers is changed.
/// @dev Topic0
/// 0x2a855b929b9a53c6fb5b5ed248b27e502b709c088e036a5aa17620c8fc5085a9.
/// @param oldThreshold The old threshold.
/// @param threshold The new threshold.
event ThresholdChanged(uint8 oldThreshold, uint8 threshold);
/// @notice Emitted when an transceiver is removed from the nttManager.
/// @dev Topic0
/// 0xf05962b5774c658e85ed80c91a75af9d66d2af2253dda480f90bce78aff5eda5.
/// @param transceiver The address of the transceiver.
/// @param transceiversNum The current number of transceivers.
/// @param threshold The current threshold of transceivers.
event TransceiverAdded(address transceiver, uint256 transceiversNum, uint8 threshold);
/// @notice Emitted when an transceiver is removed from the nttManager.
/// @dev Topic0
/// 0x697a3853515b88013ad432f29f53d406debc9509ed6d9313dcfe115250fcd18f.
/// @param transceiver The address of the transceiver.
/// @param threshold The current threshold of transceivers.
event TransceiverRemoved(address transceiver, uint8 threshold);
/// @notice payment for a transfer is too low.
/// @param requiredPayment The required payment.
/// @param providedPayment The provided payment.
error DeliveryPaymentTooLow(uint256 requiredPayment, uint256 providedPayment);
/// @notice Error when the refund to the sender fails.
/// @dev Selector 0x2ca23714.
/// @param refundAmount The refund amount.
error RefundFailed(uint256 refundAmount);
/// @notice The number of thresholds should not be zero.
error ZeroThreshold();
error RetrievedIncorrectRegisteredTransceivers(uint256 retrieved, uint256 registered);
/// @notice The threshold for transceiver attestations is too high.
/// @param threshold The threshold.
/// @param transceivers The number of transceivers.
error ThresholdTooHigh(uint256 threshold, uint256 transceivers);
/// @notice Error when the tranceiver already attested to the message.
/// To ensure the client does not continue to initiate calls to the attestationReceived function.
/// @dev Selector 0x2113894.
/// @param nttManagerMessageHash The hash of the message.
error TransceiverAlreadyAttestedToMessage(bytes32 nttManagerMessageHash);
/// @notice Error when the message is not approved.
/// @dev Selector 0x451c4fb0.
/// @param msgHash The hash of the message.
error MessageNotApproved(bytes32 msgHash);
/// @notice Emitted when a message has already been executed to
/// notify client of against retries.
/// @dev Topic0
/// 0x4069dff8c9df7e38d2867c0910bd96fd61787695e5380281148c04932d02bef2.
/// @param sourceNttManager The address of the source nttManager.
/// @param msgHash The keccak-256 hash of the message.
event MessageAlreadyExecuted(bytes32 indexed sourceNttManager, bytes32 indexed msgHash);
/// @notice There are no transceivers enabled with the Manager
/// @dev Selector 0x69cf632a
error NoEnabledTransceivers();
/// @notice Error when the manager doesn't have a peer registered for the destination chain
/// @dev Selector 0x3af256bc.
/// @param chainId The target Wormhole chain id
error PeerNotRegistered(uint16 chainId);
/// @notice Fetch the delivery price for a given recipient chain transfer.
/// @param recipientChain The Wormhole chain ID of the transfer destination.
/// @param transceiverInstructions The transceiver specific instructions for quoting and sending
/// @return - The delivery prices associated with each enabled endpoint and the total price.
function quoteDeliveryPrice(
uint16 recipientChain,
bytes memory transceiverInstructions
) external view returns (uint256[] memory, uint256);
/// @notice Sets the threshold for the number of attestations required for a message
/// to be considered valid.
/// @param threshold The new threshold (number of attestations).
/// @dev This method can only be executed by the `owner`.
function setThreshold(
uint8 threshold
) external;
/// @notice Sets the transceiver for the given chain.
/// @param transceiver The address of the transceiver.
/// @dev This method can only be executed by the `owner`.
function setTransceiver(
address transceiver
) external;
/// @notice Removes the transceiver for the given chain.
/// @param transceiver The address of the transceiver.
/// @dev This method can only be executed by the `owner`.
function removeTransceiver(
address transceiver
) external;
/// @notice Checks if a message has been approved. The message should have at least
/// the minimum threshold of attestations from distinct endpoints.
/// @param digest The digest of the message.
/// @return - Boolean indicating if message has been approved.
function isMessageApproved(
bytes32 digest
) external view returns (bool);
/// @notice Checks if a message has been executed.
/// @param digest The digest of the message.
/// @return - Boolean indicating if message has been executed.
function isMessageExecuted(
bytes32 digest
) external view returns (bool);
/// @notice Returns the next message sequence.
function nextMessageSequence() external view returns (uint64);
/// @notice Upgrades to a new manager implementation.
/// @dev This is upgraded via a proxy, and can only be executed
/// by the `owner`.
/// @param newImplementation The address of the new implementation.
function upgrade(
address newImplementation
) external;
/// @notice Pauses the manager.
function pause() external;
/// @notice Returns the mode (locking or burning) of the NttManager.
/// @return mode A uint8 corresponding to the mode
function getMode() external view returns (uint8);
/// @notice Returns the number of Transceivers that must attest to a msgId for
/// it to be considered valid and acted upon.
function getThreshold() external view returns (uint8);
/// @notice Returns a boolean indicating if the transceiver has attested to the message.
/// @param digest The digest of the message.
/// @param index The index of the transceiver
/// @return - Boolean indicating whether the transceiver at index `index` attested to a message digest
function transceiverAttestedToMessage(
bytes32 digest,
uint8 index
) external view returns (bool);
/// @notice Returns the number of attestations for a given message.
/// @param digest The digest of the message.
/// @return count The number of attestations received for the given message digest
function messageAttestations(
bytes32 digest
) external view returns (uint8 count);
/// @notice Returns of the address of the token managed by this contract.
function token() external view returns (address);
/// @notice Returns the chain ID.
function chainId() external view returns (uint16);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.0;
/**
* @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*
* Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
* all math on `uint256` and `int256` and then downcasting.
*/
library SafeCast {
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*
* _Available since v4.7._
*/
function toUint248(uint256 value) internal pure returns (uint248) {
require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*
* _Available since v4.7._
*/
function toUint240(uint256 value) internal pure returns (uint240) {
require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*
* _Available since v4.7._
*/
function toUint232(uint256 value) internal pure returns (uint232) {
require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*
* _Available since v4.2._
*/
function toUint224(uint256 value) internal pure returns (uint224) {
require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*
* _Available since v4.7._
*/
function toUint216(uint256 value) internal pure returns (uint216) {
require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*
* _Available since v4.7._
*/
function toUint208(uint256 value) internal pure returns (uint208) {
require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*
* _Available since v4.7._
*/
function toUint200(uint256 value) internal pure returns (uint200) {
require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*
* _Available since v4.7._
*/
function toUint192(uint256 value) internal pure returns (uint192) {
require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*
* _Available since v4.7._
*/
function toUint184(uint256 value) internal pure returns (uint184) {
require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*
* _Available since v4.7._
*/
function toUint176(uint256 value) internal pure returns (uint176) {
require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*
* _Available since v4.7._
*/
function toUint168(uint256 value) internal pure returns (uint168) {
require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*
* _Available since v4.7._
*/
function toUint160(uint256 value) internal pure returns (uint160) {
require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*
* _Available since v4.7._
*/
function toUint152(uint256 value) internal pure returns (uint152) {
require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*
* _Available since v4.7._
*/
function toUint144(uint256 value) internal pure returns (uint144) {
require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*
* _Available since v4.7._
*/
function toUint136(uint256 value) internal pure returns (uint136) {
require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*
* _Available since v2.5._
*/
function toUint128(uint256 value) internal pure returns (uint128) {
require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*
* _Available since v4.7._
*/
function toUint120(uint256 value) internal pure returns (uint120) {
require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*
* _Available since v4.7._
*/
function toUint112(uint256 value) internal pure returns (uint112) {
require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*
* _Available since v4.7._
*/
function toUint104(uint256 value) internal pure returns (uint104) {
require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*
* _Available since v4.2._
*/
function toUint96(uint256 value) internal pure returns (uint96) {
require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*
* _Available since v4.7._
*/
function toUint88(uint256 value) internal pure returns (uint88) {
require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*
* _Available since v4.7._
*/
function toUint80(uint256 value) internal pure returns (uint80) {
require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*
* _Available since v4.7._
*/
function toUint72(uint256 value) internal pure returns (uint72) {
require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*
* _Available since v2.5._
*/
function toUint64(uint256 value) internal pure returns (uint64) {
require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*
* _Available since v4.7._
*/
function toUint56(uint256 value) internal pure returns (uint56) {
require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*
* _Available since v4.7._
*/
function toUint48(uint256 value) internal pure returns (uint48) {
require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*
* _Available since v4.7._
*/
function toUint40(uint256 value) internal pure returns (uint40) {
require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*
* _Available since v2.5._
*/
function toUint32(uint256 value) internal pure returns (uint32) {
require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*
* _Available since v4.7._
*/
function toUint24(uint256 value) internal pure returns (uint24) {
require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*
* _Available since v2.5._
*/
function toUint16(uint256 value) internal pure returns (uint16) {
require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*
* _Available since v2.5._
*/
function toUint8(uint256 value) internal pure returns (uint8) {
require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*
* _Available since v3.0._
*/
function toUint256(int256 value) internal pure returns (uint256) {
require(value >= 0, "SafeCast: value must be positive");
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*
* _Available since v4.7._
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*
* _Available since v4.7._
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*
* _Available since v4.7._
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*
* _Available since v4.7._
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*
* _Available since v4.7._
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*
* _Available since v4.7._
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*
* _Available since v4.7._
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*
* _Available since v4.7._
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*
* _Available since v4.7._
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*
* _Available since v4.7._
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*
* _Available since v4.7._
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*
* _Available since v4.7._
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*
* _Available since v4.7._
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*
* _Available since v4.7._
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*
* _Available since v4.7._
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*
* _Available since v3.1._
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*
* _Available since v4.7._
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*
* _Available since v4.7._
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*
* _Available since v4.7._
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*
* _Available since v4.7._
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*
* _Available since v4.7._
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*
* _Available since v4.7._
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*
* _Available since v4.7._
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*
* _Available since v3.1._
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*
* _Available since v4.7._
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*
* _Available since v4.7._
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*
* _Available since v4.7._
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*
* _Available since v3.1._
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*
* _Available since v4.7._
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*
* _Available since v3.1._
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*
* _Available since v3.1._
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*
* _Available since v3.0._
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
return int256(value);
}
}
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.13;
library BytesParsing {
uint256 private constant freeMemoryPtr = 0x40;
uint256 private constant wordSize = 32;
error OutOfBounds(uint256 offset, uint256 length);
error LengthMismatch(uint256 encodedLength, uint256 expectedLength);
error InvalidBoolVal(uint8 val);
function checkBound(uint offset, uint length) internal pure {
if (offset > length)
revert OutOfBounds(offset, length);
}
function checkLength(bytes memory encoded, uint256 expected) internal pure {
if (encoded.length != expected)
revert LengthMismatch(encoded.length, expected);
}
function sliceUnchecked(
bytes memory encoded,
uint offset,
uint length
) internal pure returns (bytes memory ret, uint nextOffset) {
//bail early for degenerate case
if (length == 0)
return (new bytes(0), offset);
assembly ("memory-safe") {
nextOffset := add(offset, length)
ret := mload(freeMemoryPtr)
//Explanation on how we copy data here:
// The bytes type has the following layout in memory:
// [length: 32 bytes, data: length bytes]
// So if we allocate `bytes memory foo = new bytes(1);` then `foo` will be a pointer to 33
// bytes where the first 32 bytes contain the length and the last byte is the actual data.
// Since mload always loads 32 bytes of memory at once, we use our shift variable to align
// our reads so that our last read lines up exactly with the last 32 bytes of `encoded`.
// However this also means that if the length of `encoded` is not a multiple of 32 bytes, our
// first read will necessarily partly contain bytes from `encoded`'s 32 length bytes that
// will be written into the length part of our `ret` slice.
// We remedy this issue by writing the length of our `ret` slice at the end, thus
// overwritting those garbage bytes.
let shift := and(length, 31) //equivalent to `mod(length, 32)` but 2 gas cheaper
if iszero(shift) {
shift := wordSize
}
let dest := add(ret, shift)
let end := add(dest, length)
for {
let src := add(add(encoded, shift), offset)
} lt(dest, end) {
src := add(src, wordSize)
dest := add(dest, wordSize)
} {
mstore(dest, mload(src))
}
mstore(ret, length)
//When compiling with --via-ir then normally allocated memory (i.e. via new) will have 32 byte
// memory alignment and so we enforce the same memory alignment here.
mstore(freeMemoryPtr, and(add(dest, 31), not(31)))
}
}
function slice(
bytes memory encoded,
uint offset,
uint length
) internal pure returns (bytes memory ret, uint nextOffset) {
(ret, nextOffset) = sliceUnchecked(encoded, offset, length);
checkBound(nextOffset, encoded.length);
}
function asAddressUnchecked(
bytes memory encoded,
uint offset
) internal pure returns (address, uint) {
(uint160 ret, uint nextOffset) = asUint160Unchecked(encoded, offset);
return (address(ret), nextOffset);
}
function asAddress(
bytes memory encoded,
uint offset
) internal pure returns (address ret, uint nextOffset) {
(ret, nextOffset) = asAddressUnchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBoolUnchecked(
bytes memory encoded,
uint offset
) internal pure returns (bool, uint) {
(uint8 val, uint nextOffset) = asUint8Unchecked(encoded, offset);
if (val & 0xfe != 0)
revert InvalidBoolVal(val);
uint cleanedVal = uint(val);
bool ret;
//skip 2x iszero opcode
assembly ("memory-safe") {
ret := cleanedVal
}
return (ret, nextOffset);
}
function asBool(
bytes memory encoded,
uint offset
) internal pure returns (bool ret, uint nextOffset) {
(ret, nextOffset) = asBoolUnchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
/* -------------------------------------------------------------------------------------------------
Remaining library code below was auto-generated by via the following js/node code:
for (let bytes = 1; bytes <= 32; ++bytes) {
const bits = bytes*8;
console.log(
`function asUint${bits}Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint${bits} ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, ${bytes})
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint${bits}(
bytes memory encoded,
uint offset
) internal pure returns (uint${bits} ret, uint nextOffset) {
(ret, nextOffset) = asUint${bits}Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes${bytes}Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes${bytes}, uint) {
(uint${bits} ret, uint nextOffset) = asUint${bits}Unchecked(encoded, offset);
return (bytes${bytes}(ret), nextOffset);
}
function asBytes${bytes}(
bytes memory encoded,
uint offset
) internal pure returns (bytes${bytes}, uint) {
(uint${bits} ret, uint nextOffset) = asUint${bits}(encoded, offset);
return (bytes${bytes}(ret), nextOffset);
}
`
);
}
------------------------------------------------------------------------------------------------- */
function asUint8Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint8 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 1)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint8(
bytes memory encoded,
uint offset
) internal pure returns (uint8 ret, uint nextOffset) {
(ret, nextOffset) = asUint8Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes1Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes1, uint) {
(uint8 ret, uint nextOffset) = asUint8Unchecked(encoded, offset);
return (bytes1(ret), nextOffset);
}
function asBytes1(
bytes memory encoded,
uint offset
) internal pure returns (bytes1, uint) {
(uint8 ret, uint nextOffset) = asUint8(encoded, offset);
return (bytes1(ret), nextOffset);
}
function asUint16Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint16 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 2)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint16(
bytes memory encoded,
uint offset
) internal pure returns (uint16 ret, uint nextOffset) {
(ret, nextOffset) = asUint16Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes2Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes2, uint) {
(uint16 ret, uint nextOffset) = asUint16Unchecked(encoded, offset);
return (bytes2(ret), nextOffset);
}
function asBytes2(
bytes memory encoded,
uint offset
) internal pure returns (bytes2, uint) {
(uint16 ret, uint nextOffset) = asUint16(encoded, offset);
return (bytes2(ret), nextOffset);
}
function asUint24Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint24 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 3)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint24(
bytes memory encoded,
uint offset
) internal pure returns (uint24 ret, uint nextOffset) {
(ret, nextOffset) = asUint24Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes3Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes3, uint) {
(uint24 ret, uint nextOffset) = asUint24Unchecked(encoded, offset);
return (bytes3(ret), nextOffset);
}
function asBytes3(
bytes memory encoded,
uint offset
) internal pure returns (bytes3, uint) {
(uint24 ret, uint nextOffset) = asUint24(encoded, offset);
return (bytes3(ret), nextOffset);
}
function asUint32Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint32 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 4)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint32(
bytes memory encoded,
uint offset
) internal pure returns (uint32 ret, uint nextOffset) {
(ret, nextOffset) = asUint32Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes4Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes4, uint) {
(uint32 ret, uint nextOffset) = asUint32Unchecked(encoded, offset);
return (bytes4(ret), nextOffset);
}
function asBytes4(
bytes memory encoded,
uint offset
) internal pure returns (bytes4, uint) {
(uint32 ret, uint nextOffset) = asUint32(encoded, offset);
return (bytes4(ret), nextOffset);
}
function asUint40Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint40 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 5)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint40(
bytes memory encoded,
uint offset
) internal pure returns (uint40 ret, uint nextOffset) {
(ret, nextOffset) = asUint40Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes5Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes5, uint) {
(uint40 ret, uint nextOffset) = asUint40Unchecked(encoded, offset);
return (bytes5(ret), nextOffset);
}
function asBytes5(
bytes memory encoded,
uint offset
) internal pure returns (bytes5, uint) {
(uint40 ret, uint nextOffset) = asUint40(encoded, offset);
return (bytes5(ret), nextOffset);
}
function asUint48Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint48 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 6)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint48(
bytes memory encoded,
uint offset
) internal pure returns (uint48 ret, uint nextOffset) {
(ret, nextOffset) = asUint48Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes6Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes6, uint) {
(uint48 ret, uint nextOffset) = asUint48Unchecked(encoded, offset);
return (bytes6(ret), nextOffset);
}
function asBytes6(
bytes memory encoded,
uint offset
) internal pure returns (bytes6, uint) {
(uint48 ret, uint nextOffset) = asUint48(encoded, offset);
return (bytes6(ret), nextOffset);
}
function asUint56Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint56 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 7)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint56(
bytes memory encoded,
uint offset
) internal pure returns (uint56 ret, uint nextOffset) {
(ret, nextOffset) = asUint56Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes7Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes7, uint) {
(uint56 ret, uint nextOffset) = asUint56Unchecked(encoded, offset);
return (bytes7(ret), nextOffset);
}
function asBytes7(
bytes memory encoded,
uint offset
) internal pure returns (bytes7, uint) {
(uint56 ret, uint nextOffset) = asUint56(encoded, offset);
return (bytes7(ret), nextOffset);
}
function asUint64Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint64 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 8)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint64(
bytes memory encoded,
uint offset
) internal pure returns (uint64 ret, uint nextOffset) {
(ret, nextOffset) = asUint64Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes8Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes8, uint) {
(uint64 ret, uint nextOffset) = asUint64Unchecked(encoded, offset);
return (bytes8(ret), nextOffset);
}
function asBytes8(
bytes memory encoded,
uint offset
) internal pure returns (bytes8, uint) {
(uint64 ret, uint nextOffset) = asUint64(encoded, offset);
return (bytes8(ret), nextOffset);
}
function asUint72Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint72 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 9)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint72(
bytes memory encoded,
uint offset
) internal pure returns (uint72 ret, uint nextOffset) {
(ret, nextOffset) = asUint72Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes9Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes9, uint) {
(uint72 ret, uint nextOffset) = asUint72Unchecked(encoded, offset);
return (bytes9(ret), nextOffset);
}
function asBytes9(
bytes memory encoded,
uint offset
) internal pure returns (bytes9, uint) {
(uint72 ret, uint nextOffset) = asUint72(encoded, offset);
return (bytes9(ret), nextOffset);
}
function asUint80Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint80 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 10)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint80(
bytes memory encoded,
uint offset
) internal pure returns (uint80 ret, uint nextOffset) {
(ret, nextOffset) = asUint80Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes10Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes10, uint) {
(uint80 ret, uint nextOffset) = asUint80Unchecked(encoded, offset);
return (bytes10(ret), nextOffset);
}
function asBytes10(
bytes memory encoded,
uint offset
) internal pure returns (bytes10, uint) {
(uint80 ret, uint nextOffset) = asUint80(encoded, offset);
return (bytes10(ret), nextOffset);
}
function asUint88Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint88 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 11)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint88(
bytes memory encoded,
uint offset
) internal pure returns (uint88 ret, uint nextOffset) {
(ret, nextOffset) = asUint88Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes11Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes11, uint) {
(uint88 ret, uint nextOffset) = asUint88Unchecked(encoded, offset);
return (bytes11(ret), nextOffset);
}
function asBytes11(
bytes memory encoded,
uint offset
) internal pure returns (bytes11, uint) {
(uint88 ret, uint nextOffset) = asUint88(encoded, offset);
return (bytes11(ret), nextOffset);
}
function asUint96Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint96 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 12)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint96(
bytes memory encoded,
uint offset
) internal pure returns (uint96 ret, uint nextOffset) {
(ret, nextOffset) = asUint96Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes12Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes12, uint) {
(uint96 ret, uint nextOffset) = asUint96Unchecked(encoded, offset);
return (bytes12(ret), nextOffset);
}
function asBytes12(
bytes memory encoded,
uint offset
) internal pure returns (bytes12, uint) {
(uint96 ret, uint nextOffset) = asUint96(encoded, offset);
return (bytes12(ret), nextOffset);
}
function asUint104Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint104 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 13)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint104(
bytes memory encoded,
uint offset
) internal pure returns (uint104 ret, uint nextOffset) {
(ret, nextOffset) = asUint104Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes13Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes13, uint) {
(uint104 ret, uint nextOffset) = asUint104Unchecked(encoded, offset);
return (bytes13(ret), nextOffset);
}
function asBytes13(
bytes memory encoded,
uint offset
) internal pure returns (bytes13, uint) {
(uint104 ret, uint nextOffset) = asUint104(encoded, offset);
return (bytes13(ret), nextOffset);
}
function asUint112Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint112 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 14)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint112(
bytes memory encoded,
uint offset
) internal pure returns (uint112 ret, uint nextOffset) {
(ret, nextOffset) = asUint112Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes14Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes14, uint) {
(uint112 ret, uint nextOffset) = asUint112Unchecked(encoded, offset);
return (bytes14(ret), nextOffset);
}
function asBytes14(
bytes memory encoded,
uint offset
) internal pure returns (bytes14, uint) {
(uint112 ret, uint nextOffset) = asUint112(encoded, offset);
return (bytes14(ret), nextOffset);
}
function asUint120Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint120 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 15)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint120(
bytes memory encoded,
uint offset
) internal pure returns (uint120 ret, uint nextOffset) {
(ret, nextOffset) = asUint120Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes15Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes15, uint) {
(uint120 ret, uint nextOffset) = asUint120Unchecked(encoded, offset);
return (bytes15(ret), nextOffset);
}
function asBytes15(
bytes memory encoded,
uint offset
) internal pure returns (bytes15, uint) {
(uint120 ret, uint nextOffset) = asUint120(encoded, offset);
return (bytes15(ret), nextOffset);
}
function asUint128Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint128 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 16)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint128(
bytes memory encoded,
uint offset
) internal pure returns (uint128 ret, uint nextOffset) {
(ret, nextOffset) = asUint128Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes16Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes16, uint) {
(uint128 ret, uint nextOffset) = asUint128Unchecked(encoded, offset);
return (bytes16(ret), nextOffset);
}
function asBytes16(
bytes memory encoded,
uint offset
) internal pure returns (bytes16, uint) {
(uint128 ret, uint nextOffset) = asUint128(encoded, offset);
return (bytes16(ret), nextOffset);
}
function asUint136Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint136 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 17)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint136(
bytes memory encoded,
uint offset
) internal pure returns (uint136 ret, uint nextOffset) {
(ret, nextOffset) = asUint136Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes17Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes17, uint) {
(uint136 ret, uint nextOffset) = asUint136Unchecked(encoded, offset);
return (bytes17(ret), nextOffset);
}
function asBytes17(
bytes memory encoded,
uint offset
) internal pure returns (bytes17, uint) {
(uint136 ret, uint nextOffset) = asUint136(encoded, offset);
return (bytes17(ret), nextOffset);
}
function asUint144Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint144 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 18)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint144(
bytes memory encoded,
uint offset
) internal pure returns (uint144 ret, uint nextOffset) {
(ret, nextOffset) = asUint144Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes18Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes18, uint) {
(uint144 ret, uint nextOffset) = asUint144Unchecked(encoded, offset);
return (bytes18(ret), nextOffset);
}
function asBytes18(
bytes memory encoded,
uint offset
) internal pure returns (bytes18, uint) {
(uint144 ret, uint nextOffset) = asUint144(encoded, offset);
return (bytes18(ret), nextOffset);
}
function asUint152Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint152 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 19)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint152(
bytes memory encoded,
uint offset
) internal pure returns (uint152 ret, uint nextOffset) {
(ret, nextOffset) = asUint152Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes19Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes19, uint) {
(uint152 ret, uint nextOffset) = asUint152Unchecked(encoded, offset);
return (bytes19(ret), nextOffset);
}
function asBytes19(
bytes memory encoded,
uint offset
) internal pure returns (bytes19, uint) {
(uint152 ret, uint nextOffset) = asUint152(encoded, offset);
return (bytes19(ret), nextOffset);
}
function asUint160Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint160 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 20)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint160(
bytes memory encoded,
uint offset
) internal pure returns (uint160 ret, uint nextOffset) {
(ret, nextOffset) = asUint160Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes20Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes20, uint) {
(uint160 ret, uint nextOffset) = asUint160Unchecked(encoded, offset);
return (bytes20(ret), nextOffset);
}
function asBytes20(
bytes memory encoded,
uint offset
) internal pure returns (bytes20, uint) {
(uint160 ret, uint nextOffset) = asUint160(encoded, offset);
return (bytes20(ret), nextOffset);
}
function asUint168Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint168 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 21)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint168(
bytes memory encoded,
uint offset
) internal pure returns (uint168 ret, uint nextOffset) {
(ret, nextOffset) = asUint168Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes21Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes21, uint) {
(uint168 ret, uint nextOffset) = asUint168Unchecked(encoded, offset);
return (bytes21(ret), nextOffset);
}
function asBytes21(
bytes memory encoded,
uint offset
) internal pure returns (bytes21, uint) {
(uint168 ret, uint nextOffset) = asUint168(encoded, offset);
return (bytes21(ret), nextOffset);
}
function asUint176Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint176 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 22)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint176(
bytes memory encoded,
uint offset
) internal pure returns (uint176 ret, uint nextOffset) {
(ret, nextOffset) = asUint176Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes22Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes22, uint) {
(uint176 ret, uint nextOffset) = asUint176Unchecked(encoded, offset);
return (bytes22(ret), nextOffset);
}
function asBytes22(
bytes memory encoded,
uint offset
) internal pure returns (bytes22, uint) {
(uint176 ret, uint nextOffset) = asUint176(encoded, offset);
return (bytes22(ret), nextOffset);
}
function asUint184Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint184 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 23)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint184(
bytes memory encoded,
uint offset
) internal pure returns (uint184 ret, uint nextOffset) {
(ret, nextOffset) = asUint184Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes23Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes23, uint) {
(uint184 ret, uint nextOffset) = asUint184Unchecked(encoded, offset);
return (bytes23(ret), nextOffset);
}
function asBytes23(
bytes memory encoded,
uint offset
) internal pure returns (bytes23, uint) {
(uint184 ret, uint nextOffset) = asUint184(encoded, offset);
return (bytes23(ret), nextOffset);
}
function asUint192Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint192 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 24)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint192(
bytes memory encoded,
uint offset
) internal pure returns (uint192 ret, uint nextOffset) {
(ret, nextOffset) = asUint192Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes24Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes24, uint) {
(uint192 ret, uint nextOffset) = asUint192Unchecked(encoded, offset);
return (bytes24(ret), nextOffset);
}
function asBytes24(
bytes memory encoded,
uint offset
) internal pure returns (bytes24, uint) {
(uint192 ret, uint nextOffset) = asUint192(encoded, offset);
return (bytes24(ret), nextOffset);
}
function asUint200Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint200 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 25)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint200(
bytes memory encoded,
uint offset
) internal pure returns (uint200 ret, uint nextOffset) {
(ret, nextOffset) = asUint200Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes25Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes25, uint) {
(uint200 ret, uint nextOffset) = asUint200Unchecked(encoded, offset);
return (bytes25(ret), nextOffset);
}
function asBytes25(
bytes memory encoded,
uint offset
) internal pure returns (bytes25, uint) {
(uint200 ret, uint nextOffset) = asUint200(encoded, offset);
return (bytes25(ret), nextOffset);
}
function asUint208Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint208 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 26)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint208(
bytes memory encoded,
uint offset
) internal pure returns (uint208 ret, uint nextOffset) {
(ret, nextOffset) = asUint208Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes26Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes26, uint) {
(uint208 ret, uint nextOffset) = asUint208Unchecked(encoded, offset);
return (bytes26(ret), nextOffset);
}
function asBytes26(
bytes memory encoded,
uint offset
) internal pure returns (bytes26, uint) {
(uint208 ret, uint nextOffset) = asUint208(encoded, offset);
return (bytes26(ret), nextOffset);
}
function asUint216Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint216 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 27)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint216(
bytes memory encoded,
uint offset
) internal pure returns (uint216 ret, uint nextOffset) {
(ret, nextOffset) = asUint216Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes27Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes27, uint) {
(uint216 ret, uint nextOffset) = asUint216Unchecked(encoded, offset);
return (bytes27(ret), nextOffset);
}
function asBytes27(
bytes memory encoded,
uint offset
) internal pure returns (bytes27, uint) {
(uint216 ret, uint nextOffset) = asUint216(encoded, offset);
return (bytes27(ret), nextOffset);
}
function asUint224Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint224 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 28)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint224(
bytes memory encoded,
uint offset
) internal pure returns (uint224 ret, uint nextOffset) {
(ret, nextOffset) = asUint224Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes28Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes28, uint) {
(uint224 ret, uint nextOffset) = asUint224Unchecked(encoded, offset);
return (bytes28(ret), nextOffset);
}
function asBytes28(
bytes memory encoded,
uint offset
) internal pure returns (bytes28, uint) {
(uint224 ret, uint nextOffset) = asUint224(encoded, offset);
return (bytes28(ret), nextOffset);
}
function asUint232Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint232 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 29)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint232(
bytes memory encoded,
uint offset
) internal pure returns (uint232 ret, uint nextOffset) {
(ret, nextOffset) = asUint232Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes29Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes29, uint) {
(uint232 ret, uint nextOffset) = asUint232Unchecked(encoded, offset);
return (bytes29(ret), nextOffset);
}
function asBytes29(
bytes memory encoded,
uint offset
) internal pure returns (bytes29, uint) {
(uint232 ret, uint nextOffset) = asUint232(encoded, offset);
return (bytes29(ret), nextOffset);
}
function asUint240Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint240 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 30)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint240(
bytes memory encoded,
uint offset
) internal pure returns (uint240 ret, uint nextOffset) {
(ret, nextOffset) = asUint240Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes30Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes30, uint) {
(uint240 ret, uint nextOffset) = asUint240Unchecked(encoded, offset);
return (bytes30(ret), nextOffset);
}
function asBytes30(
bytes memory encoded,
uint offset
) internal pure returns (bytes30, uint) {
(uint240 ret, uint nextOffset) = asUint240(encoded, offset);
return (bytes30(ret), nextOffset);
}
function asUint248Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint248 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 31)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint248(
bytes memory encoded,
uint offset
) internal pure returns (uint248 ret, uint nextOffset) {
(ret, nextOffset) = asUint248Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes31Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes31, uint) {
(uint248 ret, uint nextOffset) = asUint248Unchecked(encoded, offset);
return (bytes31(ret), nextOffset);
}
function asBytes31(
bytes memory encoded,
uint offset
) internal pure returns (bytes31, uint) {
(uint248 ret, uint nextOffset) = asUint248(encoded, offset);
return (bytes31(ret), nextOffset);
}
function asUint256Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (uint256 ret, uint nextOffset) {
assembly ("memory-safe") {
nextOffset := add(offset, 32)
ret := mload(add(encoded, nextOffset))
}
return (ret, nextOffset);
}
function asUint256(
bytes memory encoded,
uint offset
) internal pure returns (uint256 ret, uint nextOffset) {
(ret, nextOffset) = asUint256Unchecked(encoded, offset);
checkBound(nextOffset, encoded.length);
}
function asBytes32Unchecked(
bytes memory encoded,
uint offset
) internal pure returns (bytes32, uint) {
(uint256 ret, uint nextOffset) = asUint256Unchecked(encoded, offset);
return (bytes32(ret), nextOffset);
}
function asBytes32(
bytes memory encoded,
uint offset
) internal pure returns (bytes32, uint) {
(uint256 ret, uint nextOffset) = asUint256(encoded, offset);
return (bytes32(ret), nextOffset);
}
}
File 2 of 3: Wormhole
// contracts/Wormhole.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
contract Wormhole is ERC1967Proxy {
constructor (address implementation, bytes memory initData) ERC1967Proxy(
implementation,
initData
) { }
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../Proxy.sol";
import "./ERC1967Upgrade.sol";
/**
* @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
* implementation address that can be changed. This address is stored in storage in the location specified by
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
* implementation behind the proxy.
*/
contract ERC1967Proxy is Proxy, ERC1967Upgrade {
/**
* @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
*
* If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
* function call, and allows initializating the storage of the proxy like a Solidity constructor.
*/
constructor(address _logic, bytes memory _data) payable {
assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
_upgradeToAndCall(_logic, _data, false);
}
/**
* @dev Returns the current implementation address.
*/
function _implementation() internal view virtual override returns (address impl) {
return ERC1967Upgrade._getImplementation();
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
import "../beacon/IBeacon.sol";
import "../../utils/Address.sol";
import "../../utils/StorageSlot.sol";
/**
* @dev This abstract contract provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
*
* _Available since v4.1._
*
* @custom:oz-upgrades-unsafe-allow delegatecall
*/
abstract contract ERC1967Upgrade {
// This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
* validated in the constructor.
*/
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);
/**
* @dev Returns the current implementation address.
*/
function _getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @dev Perform implementation upgrade
*
* Emits an {Upgraded} event.
*/
function _upgradeTo(address newImplementation) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
}
/**
* @dev Perform implementation upgrade with additional setup call.
*
* Emits an {Upgraded} event.
*/
function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
if (data.length > 0 || forceCall) {
Address.functionDelegateCall(newImplementation, data);
}
}
/**
* @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
*
* Emits an {Upgraded} event.
*/
function _upgradeToAndCallSecure(address newImplementation, bytes memory data, bool forceCall) internal {
address oldImplementation = _getImplementation();
// Initial upgrade and setup call
_setImplementation(newImplementation);
if (data.length > 0 || forceCall) {
Address.functionDelegateCall(newImplementation, data);
}
// Perform rollback test if not already in progress
StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);
if (!rollbackTesting.value) {
// Trigger rollback using upgradeTo from the new implementation
rollbackTesting.value = true;
Address.functionDelegateCall(
newImplementation,
abi.encodeWithSignature(
"upgradeTo(address)",
oldImplementation
)
);
rollbackTesting.value = false;
// Check rollback was effective
require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
// Finally reset to the new implementation and log the upgrade
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
}
}
/**
* @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
* not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
*
* Emits a {BeaconUpgraded} event.
*/
function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
if (data.length > 0 || forceCall) {
Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
* validated in the constructor.
*/
bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Returns the current admin.
*/
function _getAdmin() internal view returns (address) {
return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
require(newAdmin != address(0), "ERC1967: new admin is the zero address");
StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {AdminChanged} event.
*/
function _changeAdmin(address newAdmin) internal {
emit AdminChanged(_getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
*/
bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Emitted when the beacon is upgraded.
*/
event BeaconUpgraded(address indexed beacon);
/**
* @dev Returns the current beacon.
*/
function _getBeacon() internal view returns (address) {
return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the EIP1967 beacon slot.
*/
function _setBeacon(address newBeacon) private {
require(
Address.isContract(newBeacon),
"ERC1967: new beacon is not a contract"
);
require(
Address.isContract(IBeacon(newBeacon).implementation()),
"ERC1967: beacon implementation is not a contract"
);
StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
* instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
* be specified by overriding the virtual {_implementation} function.
*
* Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
* different contract through the {_delegate} function.
*
* The success and return data of the delegated call will be returned back to the caller of the proxy.
*/
abstract contract Proxy {
/**
* @dev Delegates the current call to `implementation`.
*
* This function does not return to its internall call site, it will return directly to the external caller.
*/
function _delegate(address implementation) internal virtual {
// solhint-disable-next-line no-inline-assembly
assembly {
// Copy msg.data. We take full control of memory in this inline assembly
// block because it will not return to Solidity code. We overwrite the
// Solidity scratch pad at memory position 0.
calldatacopy(0, 0, calldatasize())
// Call the implementation.
// out and outsize are 0 because we don't know the size yet.
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
// Copy the returned data.
returndatacopy(0, 0, returndatasize())
switch result
// delegatecall returns 0 on error.
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
/**
* @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
* and {_fallback} should delegate.
*/
function _implementation() internal view virtual returns (address);
/**
* @dev Delegates the current call to the address returned by `_implementation()`.
*
* This function does not return to its internall call site, it will return directly to the external caller.
*/
function _fallback() internal virtual {
_beforeFallback();
_delegate(_implementation());
}
/**
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
* function in the contract matches the call data.
*/
fallback () external payable virtual {
_fallback();
}
/**
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
* is empty.
*/
receive () external payable virtual {
_fallback();
}
/**
* @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
* call, or as part of the Solidity `fallback` or `receive` functions.
*
* If overriden should call `super._beforeFallback()`.
*/
function _beforeFallback() internal virtual {
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {BeaconProxy} will check that this address is a contract.
*/
function implementation() external view returns (address);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 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://diligence.consensys.net/posts/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.5.11/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");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(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 functionCall(target, data, "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");
require(isContract(target), "Address: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return _verifyCallResult(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) {
require(isContract(target), "Address: static call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(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) {
require(isContract(target), "Address: delegate call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {
// 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
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
assembly {
r.slot := slot
}
}
}
File 3 of 3: Executor
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import "./interfaces/IExecutor.sol";
string constant executorVersion = "Executor-0.0.1";
contract Executor is IExecutor {
string public constant EXECUTOR_VERSION = executorVersion;
uint16 public immutable ourChain;
constructor(uint16 _ourChain) {
ourChain = _ourChain;
}
error QuoteSrcChainMismatch(uint16 quoteSrcChain, uint16 requestSrcChain);
error QuoteDstChainMismatch(uint16 quoteDstChain, uint16 requestDstChain);
error QuoteExpired(uint64 expiryTime);
error NotAnEvmAddress(bytes32);
function requestExecution(
uint16 dstChain,
bytes32 dstAddr,
address refundAddr,
bytes calldata signedQuoteBytes,
bytes calldata requestBytes,
bytes calldata relayInstructions
) public payable {
{
uint16 quoteSrcChain;
uint16 quoteDstChain;
uint64 expiryTime;
assembly {
quoteSrcChain := shr(240, calldataload(add(signedQuoteBytes.offset, 56)))
quoteDstChain := shr(240, calldataload(add(signedQuoteBytes.offset, 58)))
expiryTime := shr(192, calldataload(add(signedQuoteBytes.offset, 60)))
}
if (quoteSrcChain != ourChain) {
revert QuoteSrcChainMismatch(quoteSrcChain, ourChain);
}
if (quoteDstChain != dstChain) {
revert QuoteDstChainMismatch(quoteDstChain, dstChain);
}
if (expiryTime <= block.timestamp) {
revert QuoteExpired(expiryTime);
}
}
uint160 quoterAddress;
bytes32 universalPayeeAddress;
assembly {
quoterAddress := shr(96, calldataload(add(signedQuoteBytes.offset, 4)))
universalPayeeAddress := calldataload(add(signedQuoteBytes.offset, 24))
}
// Check if the higher 96 bits (left-most 12 bytes) are non-zero
if (uint256(universalPayeeAddress) >> 160 != 0) {
revert NotAnEvmAddress(universalPayeeAddress);
}
address payeeAddress = address(uint160(uint256(universalPayeeAddress)));
payable(payeeAddress).transfer(msg.value);
emit RequestForExecution(
address(quoterAddress),
msg.value,
dstChain,
dstAddr,
refundAddr,
signedQuoteBytes,
requestBytes,
relayInstructions
);
}
}
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.19;
interface IExecutor {
struct SignedQuoteHeader {
bytes4 prefix;
address quoterAddress;
bytes32 payeeAddress;
uint16 srcChain;
uint16 dstChain;
uint64 expiryTime;
}
event RequestForExecution(
address indexed quoterAddress,
uint256 amtPaid,
uint16 dstChain,
bytes32 dstAddr,
address refundAddr,
bytes signedQuote,
bytes requestBytes,
bytes relayInstructions
);
function requestExecution(
uint16 dstChain,
bytes32 dstAddr,
address refundAddr,
bytes calldata signedQuote,
bytes calldata requestBytes,
bytes calldata relayInstructions
) external payable;
}