Address Contract Verified
Address
0xc2d0D6C49979703C524Be2D21776C4874C92180e
Balance
0 ETH
Nonce
1
Code Size
8601 bytes
Creator
0x8e0507C1...1df4 at tx 0x0b0fd5e9...8d8b49
Last Active
Indexed Transactions
6 (24,321,149 → 24,460,781)
Value (indexed)
↓ 0.180000 ETH
Gas Used (indexed)
11,733,245
Contract Bytecode
8601 bytes
0x60806040526004361061005b575f3560e01c80639861329b116100415780639861329b146100ae578063d7ca9d72146100bc578063f3f85dcb146100ae575f5ffd5b806313990539146100665780633a3b18e01461009b575f5ffd5b3661006257005b5f5ffd5b348015610071575f5ffd5b50610085610080366004610e8c565b6100cf565b6040516100929190610ec4565b60405180910390f35b6100856100a9366004610f44565b610112565b610085610080366004610e8c565b6100856100ca366004610f76565b610189565b6100f16040518060600160405280606081526020015f81526020015f81525090565b6100f961036f565b610102826103e0565b905061010d60015f55565b919050565b6101346040518060600160405280606081526020015f81526020015f81525090565b61013c61036f565b5f5a905061017d61014c846112c5565b610177610160610100870160e088016113d3565b610172610120880161010089016113d3565b610599565b8361066b565b91505061010d60015f55565b6101ab6040518060600160405280606081526020015f81526020015f81525090565b6101b361036f565b5f5a90506101f56101ca60408701602088016113d3565b86604001357f000000000000000000000000726e3244e2a73405ce13d82862d81e1c5dd0004e61099f565b6040517f741308e30000000000000000000000000000000000000000000000000000000081525f9073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000726e3244e2a73405ce13d82862d81e1c5dd0004e169063741308e39061026d90899089908990600401611b64565b5f604051808303815f875af1158015610288573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526102cd9190810190611d6c565b6020808201519085018190528151855290915060c087013510610351576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f494e53554646494349454e545f4f55540000000000000000000000000000000060448201526064015b60405180910390fd5b5a61035c9083611e02565b6040840152505060015f555b9392505050565b60025f54036103da576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610348565b60025f55565b6104026040518060600160405280606081526020015f81526020015f81525090565b5f5a6040805161012081019091529091505f90806104208680611e3a565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050509082525060209081019061046c90604088019088016113d3565b73ffffffffffffffffffffffffffffffffffffffff16815260408087013560208301520161049d6060870187611e9b565b808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152505050908252506020016104e06080870187611e9b565b6104e991611eff565b81526020016104fb60a0870187611e9b565b808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050509082525060c0860135602082015260400161054c610100870160e088016113d3565b73ffffffffffffffffffffffffffffffffffffffff1681523360209091015290506105918161058b610585610100880160e089016113d3565b33610599565b8461066b565b949350505050565b5f73ffffffffffffffffffffffffffffffffffffffff83161561064b576040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301528416906370a0823190602401602060405180830381865afa158015610620573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106449190611f0b565b9050610665565b5073ffffffffffffffffffffffffffffffffffffffff8116315b92915050565b61068d6040518060600160405280606081526020015f81526020015f81525090565b83604001515f036106fa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f494e56414c49445f494e5055545f414d4f554e540000000000000000000000006044820152606401610348565b8360c001515f03610767576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f494e56414c49445f4f55545055545f414d4f554e5400000000000000000000006044820152606401610348565b61079a846020015185604001517f000000000000000000000000726e3244e2a73405ce13d82862d81e1c5dd0004e61099f565b83516060850151608086015160a08701516040517f662110ef00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000726e3244e2a73405ce13d82862d81e1c5dd0004e169463662110ef9461081f9491939092600401611fb4565b5f604051808303815f875af115801561083a573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261087f9190810190612096565b51815260e08401516101008501515f9161089891610599565b9050838111610903576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f494e56414c49445f4e45575f42414c414e4345000000000000000000000000006044820152606401610348565b5f61090e8583611e02565b90508560c0015181101561097e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f494e53554646494349454e545f4f5554000000000000000000000000000000006044820152606401610348565b602083018190525a6109909085611e02565b60408401525090949350505050565b73ffffffffffffffffffffffffffffffffffffffff8316156109cc576109c783338385610ade565b505050565b81341015610a36576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f494e53554646494349454e545f455448000000000000000000000000000000006044820152606401610348565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff1663d0e30db0836040518263ffffffff1660e01b81526004015f604051808303818588803b158015610a9c575f5ffd5b505af1158015610aae573d5f5f3e3d5ffd5b50505050506109c77f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28284610bc0565b60405173ffffffffffffffffffffffffffffffffffffffff80851660248301528316604482015260648101829052610bba9085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152610c16565b50505050565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526109c79084907fa9059cbb0000000000000000000000000000000000000000000000000000000090606401610b38565b5f610c77826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff16610d219092919063ffffffff16565b8051909150156109c75780806020019051810190610c959190612120565b6109c7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610348565b606061059184845f85855f5f8673ffffffffffffffffffffffffffffffffffffffff168587604051610d53919061213b565b5f6040518083038185875af1925050503d805f8114610d8d576040519150601f19603f3d011682016040523d82523d5f602084013e610d92565b606091505b5091509150610da387838387610dae565b979650505050505050565b60608315610e435782515f03610e3c5773ffffffffffffffffffffffffffffffffffffffff85163b610e3c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610348565b5081610591565b6105918383815115610e585781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103489190612151565b5f60208284031215610e9c575f5ffd5b813567ffffffffffffffff811115610eb2575f5ffd5b82016101008185031215610368575f5ffd5b602080825282516060838301528051608084018190525f929190910190829060a08501905b80831015610f0c5783518252602082019150602084019350600183019250610ee9565b50602086015160408601526040860151606086015280935050505092915050565b5f6101208284031215610f3e575f5ffd5b50919050565b5f60208284031215610f54575f5ffd5b813567ffffffffffffffff811115610f6a575f5ffd5b61059184828501610f2d565b5f5f5f60608486031215610f88575f5ffd5b833567ffffffffffffffff811115610f9e575f5ffd5b610faa86828701610f2d565b935050602084013567ffffffffffffffff811115610fc6575f5ffd5b84016101808187031215610fd8575f5ffd5b929592945050506040919091013590565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051610120810167ffffffffffffffff8111828210171561103a5761103a610fe9565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561108757611087610fe9565b604052919050565b5f82601f83011261109e575f5ffd5b813567ffffffffffffffff8111156110b8576110b8610fe9565b6110e960207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601611040565b8181528460208386010111156110fd575f5ffd5b816020850160208301375f918101602001919091529392505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461113a575f5ffd5b50565b803561010d81611119565b5f67ffffffffffffffff82111561116157611161610fe9565b5060051b60200190565b5f82601f83011261117a575f5ffd5b813561118d61118882611148565b611040565b8082825260208201915060208360051b8601019250858311156111ae575f5ffd5b602085015b838110156111cb5780358352602092830192016111b3565b5095945050505050565b5f6111e261118884611148565b838152905060208101600584901b8301858111156111fe575f5ffd5b835b8181101561123957803567ffffffffffffffff81111561121e575f5ffd5b61122a8882880161108f565b84525060209283019201611200565b5050509392505050565b5f82601f830112611252575f5ffd5b610368838335602085016111d5565b5f82601f830112611270575f5ffd5b813561127e61118882611148565b8082825260208201915060208360051b86010192508583111561129f575f5ffd5b602085015b838110156111cb5780356112b781611119565b8352602092830192016112a4565b5f61012082360312156112d6575f5ffd5b6112de611016565b823567ffffffffffffffff8111156112f4575f5ffd5b6113003682860161108f565b82525061130f6020840161113d565b602082015260408381013590820152606083013567ffffffffffffffff811115611337575f5ffd5b6113433682860161116b565b606083015250608083013567ffffffffffffffff811115611362575f5ffd5b61136e36828601611243565b60808301525060a083013567ffffffffffffffff81111561138d575f5ffd5b61139936828601611261565b60a08301525060c083810135908201526113b560e0840161113d565b60e08201526113c7610100840161113d565b61010082015292915050565b5f602082840312156113e3575f5ffd5b813561036881611119565b5f5f83357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112611421575f5ffd5b830160208101925035905067ffffffffffffffff811115611440575f5ffd5b80360382131561144e575f5ffd5b9250929050565b81835281816020850137505f602082840101525f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b5f5f83357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126114cf575f5ffd5b830160208101925035905067ffffffffffffffff8111156114ee575f5ffd5b8060051b360382131561144e575f5ffd5b8183525f7f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83111561152f575f5ffd5b8260051b80836020870137939093016020019392505050565b5f8383855260208501945060208460051b820101835f5b868110156115bb577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe084840301885261159882876113ee565b6115a3858284611455565b60209a8b019a9095509390930192505060010161155f565b50909695505050505050565b8183526020830192505f815f5b848110156116125781356115e781611119565b73ffffffffffffffffffffffffffffffffffffffff16865260209586019591909101906001016115d4565b5093949350505050565b5f82357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6183360301811261164e575f5ffd5b90910192915050565b8183526020830192505f815f5b8481101561161257813561167781611119565b73ffffffffffffffffffffffffffffffffffffffff1686526020958601959190910190600101611664565b5f6116ad82836113ee565b60a085526116bf60a086018284611455565b9150506116cf60208401846113ee565b85830360208701526116e2838284611455565b925050506116f3604084018461149c565b8583036040870152611706838284611657565b92505050611717606084018461149c565b858303606087015261172a8382846114ff565b608095860135969095019590955250919392505050565b8183526020830192505f815f5b8481101561161257813561176181611119565b73ffffffffffffffffffffffffffffffffffffffff16865260208201356bffffffffffffffffffffffff8116808214611798575f5ffd5b602088015250604095860195919091019060010161174e565b803582525f6020820135368390037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe10181126117eb575f5ffd5b820160208101903567ffffffffffffffff811115611807575f5ffd5b8060061b3603821315611818575f5ffd5b60a0602086015261182d60a086018284611741565b60408581013590870152606080860135908701529150611852905060808401846113ee565b8583036080870152611865838284611455565b9695505050505050565b801515811461113a575f5ffd5b803561010d8161186f565b80356118928161186f565b1515825260208101356118a48161186f565b151560208301526040810135600381108015906118bf575f5ffd5b506040929092019190915250565b5f82357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa183360301811261164e575f5ffd5b5f61190a828361149c565b6060855261191c606086018284611657565b91505061192c602084018461149c565b858303602087015261193f838284611657565b92505050611950604084018461149c565b8583036040870152611865838284611657565b5f82357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4183360301811261164e575f5ffd5b5f813565ffffffffffff81168082146119ac575f5ffd5b845250602082013563ffffffff811681146119c5575f5ffd5b63ffffffff1660208401526040828101359084015260608083013590840152608080830135908401526119fb60a083018361149c565b60c060a0860152611a1060c086018284611657565b95945050505050565b611a4082611a268361113d565b73ffffffffffffffffffffffffffffffffffffffff169052565b5f611a4e602083018361161c565b6101806020850152611a646101808501826116a2565b9050611a73604084018461161c565b8482036040860152611a8582826117b1565b915050611a986060850160608501611887565b611aa560c08401846118cd565b84820360c0860152611ab782826118ff565b915050611ac660e0840161187c565b151560e0850152611ada610100840161113d565b73ffffffffffffffffffffffffffffffffffffffff16610100850152611b03610120840161113d565b73ffffffffffffffffffffffffffffffffffffffff16610120850152611b2d610140840184611963565b848203610140860152611b408282611995565b915050611b51610160840184611963565b848203610160860152611a108282611995565b606081525f611b7385866113ee565b6101206060850152611b8a61018085018284611455565b915050611b996020870161113d565b73ffffffffffffffffffffffffffffffffffffffff166080840152604086013560a0840152611bcb606087018761149c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08584030160c0860152611c008382846114ff565b92505050611c11608087018761149c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08584030160e0860152611c46838284611548565b92505050611c5760a087018761149c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa085840301610100860152611c8d8382846115c7565b60c08901356101208701529250611ca991505060e0870161113d565b73ffffffffffffffffffffffffffffffffffffffff16610140840152611cd2610100870161113d565b73ffffffffffffffffffffffffffffffffffffffff166101608401528281036020840152611d008186611a19565b915050826040830152949350505050565b5f82601f830112611d20575f5ffd5b8151611d2e61118882611148565b8082825260208201915060208360051b860101925085831115611d4f575f5ffd5b602085015b838110156111cb578051835260209283019201611d54565b5f60208284031215611d7c575f5ffd5b815167ffffffffffffffff811115611d92575f5ffd5b820160408185031215611da3575f5ffd5b6040805190810167ffffffffffffffff81118282101715611dc657611dc6610fe9565b604052815167ffffffffffffffff811115611ddf575f5ffd5b611deb86828501611d11565b825250602091820151918101919091529392505050565b81810381811115610665577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f5f83357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112611e6d575f5ffd5b83018035915067ffffffffffffffff821115611e87575f5ffd5b60200191503681900382131561144e575f5ffd5b5f5f83357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112611ece575f5ffd5b83018035915067ffffffffffffffff821115611ee8575f5ffd5b6020019150600581901b360382131561144e575f5ffd5b5f6103683684846111d5565b5f60208284031215611f1b575f5ffd5b5051919050565b5f81518084528060208401602086015e5f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b5f8151808452602084019350602083015f5b8281101561161257815173ffffffffffffffffffffffffffffffffffffffff16865260209586019590910190600101611f80565b608081525f611fc66080830187611f22565b82810360208401528086518083526020830191506020880192505f5b81811015612000578351835260209384019390920191600101611fe2565b50508381036040850152855180825260208083019350600582901b830181019088015f5b83811015612074577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe085840301865261205e838351611f22565b6020968701969093509190910190600101612024565b505085810360608701526120888188611f6e565b9a9950505050505050505050565b5f602082840312156120a6575f5ffd5b815167ffffffffffffffff8111156120bc575f5ffd5b8201602081850312156120cd575f5ffd5b6040516020810167ffffffffffffffff811182821017156120f0576120f0610fe9565b604052815167ffffffffffffffff811115612109575f5ffd5b61211586828501611d11565b825250949350505050565b5f60208284031215612130575f5ffd5b81516103688161186f565b5f82518060208501845e5f920191825250919050565b602081525f6103686020830184611f2256fea26469706673582212209ef1dc5ba68fb69b77d16c0c475d8e599361ab524236e28f9d7eaa0e1f9339bc64736f6c634300081e0033
Verified Source Code Full Match
Compiler: v0.8.30+commit.73712a01
EVM: cancun
Optimization: Yes (50000 runs)
IVotes.sol 61 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (governance/utils/IVotes.sol)
pragma solidity ^0.8.0;
/**
* @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts.
*
* _Available since v4.5._
*/
interface IVotes {
/**
* @dev Emitted when an account changes their delegate.
*/
event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
/**
* @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes.
*/
event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);
/**
* @dev Returns the current amount of votes that `account` has.
*/
function getVotes(address account) external view returns (uint256);
/**
* @dev Returns the amount of votes that `account` had at the end of a past block (`blockNumber`).
*/
function getPastVotes(address account, uint256 blockNumber) external view returns (uint256);
/**
* @dev Returns the total supply of votes available at the end of a past block (`blockNumber`).
*
* NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.
* Votes that have not been delegated are still part of total supply, even though they would not participate in a
* vote.
*/
function getPastTotalSupply(uint256 blockNumber) external view returns (uint256);
/**
* @dev Returns the delegate that `account` has chosen.
*/
function delegates(address account) external view returns (address);
/**
* @dev Delegates votes from the sender to `delegatee`.
*/
function delegate(address delegatee) external;
/**
* @dev Delegates votes from signer to `delegatee`.
*/
function delegateBySig(
address delegatee,
uint256 nonce,
uint256 expiry,
uint8 v,
bytes32 r,
bytes32 s
) external;
}
ERC2771Context.sol 43 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (metatx/ERC2771Context.sol)
pragma solidity ^0.8.9;
import "../utils/Context.sol";
/**
* @dev Context variant with ERC2771 support.
*/
abstract contract ERC2771Context is Context {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable _trustedForwarder;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(address trustedForwarder) {
_trustedForwarder = trustedForwarder;
}
function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
return forwarder == _trustedForwarder;
}
function _msgSender() internal view virtual override returns (address sender) {
if (isTrustedForwarder(msg.sender)) {
// The assembly code is more direct than the Solidity version using `abi.decode`.
/// @solidity memory-safe-assembly
assembly {
sender := shr(96, calldataload(sub(calldatasize(), 20)))
}
} else {
return super._msgSender();
}
}
function _msgData() internal view virtual override returns (bytes calldata) {
if (isTrustedForwarder(msg.sender)) {
return msg.data[:msg.data.length - 20];
} else {
return super._msgData();
}
}
}
ReentrancyGuard.sol 69 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}
draft-IERC20Permit.sol 60 lines
// 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);
}
IERC20.sol 82 lines
// 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);
}
SafeERC20.sol 116 lines
// 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");
}
}
}
Address.sol 244 lines
// 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);
}
}
}
Context.sol 24 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
Math.sol 345 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1);
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator,
Rounding rounding
) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10**64) {
value /= 10**64;
result += 64;
}
if (value >= 10**32) {
value /= 10**32;
result += 32;
}
if (value >= 10**16) {
value /= 10**16;
result += 16;
}
if (value >= 10**8) {
value /= 10**8;
result += 8;
}
if (value >= 10**4) {
value /= 10**4;
result += 4;
}
if (value >= 10**2) {
value /= 10**2;
result += 2;
}
if (value >= 10**1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
}
}
}
IFolio_400.sol 292 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IVotes } from "@openzeppelin/contracts/governance/utils/IVotes.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
interface IFolio_400 is IERC20 {
// === Events ===
event AuctionOpened(
uint256 indexed rebalanceNonce,
uint256 indexed auctionId,
address[] tokens,
WeightRange[] weights,
PriceRange[] prices,
RebalanceLimits limits,
uint256 startTime,
uint256 endTime
);
event AuctionBid(
uint256 indexed auctionId,
address indexed sellToken,
address indexed buyToken,
uint256 sellAmount,
uint256 buyAmount
);
event AuctionClosed(uint256 indexed auctionId);
event AuctionTrustedFillCreated(uint256 indexed auctionId, address filler);
event FolioFeePaid(address indexed recipient, uint256 amount);
event ProtocolFeePaid(address indexed recipient, uint256 amount);
event BasketTokenAdded(address indexed token);
event BasketTokenRemoved(address indexed token);
event TVLFeeSet(uint256 newFee, uint256 feeAnnually);
event MintFeeSet(uint256 newFee);
event FeeRecipientsSet(FeeRecipient[] recipients);
event AuctionDelaySet(uint256 newAuctionDelay);
event AuctionLengthSet(uint256 newAuctionLength);
event DustAmountSet(address token, uint256 newDustAmount);
event MandateSet(string newMandate);
event TrustedFillerRegistrySet(address trustedFillerRegistry, bool isEnabled);
event FolioDeprecated();
event RebalanceControlSet(RebalanceControl newControl);
event RebalanceStarted(
uint256 nonce,
PriceControl priceControl,
address[] tokens,
WeightRange[] weights,
PriceRange[] prices,
RebalanceLimits limits,
uint256 restrictedUntil,
uint256 availableUntil
);
event RebalanceEnded(uint256 nonce);
// === Errors ===
error Folio__FolioDeprecated();
error Folio__Unauthorized();
error Folio__EmptyAssets();
error Folio__BasketModificationFailed();
error Folio__BalanceNotRemovable();
error Folio__FeeRecipientInvalidAddress();
error Folio__FeeRecipientInvalidFeeShare();
error Folio__BadFeeTotal();
error Folio__TVLFeeTooHigh();
error Folio__TVLFeeTooLow();
error Folio__MintFeeTooHigh();
error Folio__ZeroInitialShares();
error Folio__InvalidAsset();
error Folio__DuplicateAsset();
error Folio__InvalidAssetAmount(address asset);
error Folio__InvalidAuctionLength();
error Folio__InvalidLimits();
error Folio__InvalidWeights();
error Folio__AuctionCannotBeOpenedWithoutRestriction();
error Folio__AuctionNotOngoing();
error Folio__InvalidPrices();
error Folio__SlippageExceeded();
error Folio__InsufficientSellAvailable();
error Folio__InsufficientBuyAvailable();
error Folio__InsufficientBid();
error Folio__InsufficientSharesOut();
error Folio__TooManyFeeRecipients();
error Folio__InvalidArrayLengths();
error Folio__InvalidTransferToSelf();
error Folio__InvalidRegistry();
error Folio__TrustedFillerRegistryNotEnabled();
error Folio__TrustedFillerRegistryAlreadySet();
error Folio__InvalidTTL();
error Folio__NotRebalancing();
error Folio__EmptyAuction();
error Folio__MixedAtomicSwaps();
// === Structures ===
/// Price control AUCTION_LAUNCHER has on rebalancing
enum PriceControl {
NONE, // cannot change prices
PARTIAL, // can set auction prices within bounds of initial prices
ATOMIC_SWAP // PARTIAL + atomic swaps
}
struct FolioBasicDetails {
string name;
string symbol;
address[] assets;
uint256[] amounts; // {tok}
uint256 initialShares; // {share}
}
struct FolioAdditionalDetails {
uint256 auctionLength; // {s}
FeeRecipient[] feeRecipients;
uint256 tvlFee; // D18{1/s}
uint256 mintFee; // D18{1}
string mandate;
}
struct FolioRegistryIndex {
address daoFeeRegistry;
address trustedFillerRegistry;
}
struct FolioFlags {
bool trustedFillerEnabled;
RebalanceControl rebalanceControl;
}
struct FeeRecipient {
address recipient;
uint96 portion; // D18{1}
}
/// AUCTION_LAUNCHER control over rebalancing
struct RebalanceControl {
bool weightControl; // if AUCTION_LAUNCHER can move weights
PriceControl priceControl; // if AUCTION_LAUNCHER can narrow prices
}
/// Basket limits for rebalancing
struct RebalanceLimits {
uint256 low; // D18{BU/share} (0, 1e36] to buy assets up to
uint256 spot; // D18{BU/share} (0, 1e36] point estimate to be used only in the event of unrestricted caller
uint256 high; // D18{BU/share} (0, 1e36] to sell assets down to
}
/// Range of basket weights for BU definition
struct WeightRange {
uint256 low; // D27{tok/BU} [0, 1e54] to buy assets up to
uint256 spot; // D27{tok/BU} [0, 1e54] point estimate to be used in the event of unrestricted caller
uint256 high; // D27{tok/BU} [0, 1e54] to sell assets down to
}
/// Individual token price ranges
/// @dev Unit of Account (UoA) can be anything as long as it's consistent; USD is most common
struct PriceRange {
uint256 low; // D27{UoA/tok} (0, 1e54]
uint256 high; // D27{UoA/tok} (0, 1e54]
}
/// Rebalance details for a token
struct RebalanceDetails {
bool inRebalance;
WeightRange weights; // D27{tok/BU} [0, 1e54]
PriceRange initialPrices; // D27{UoA/tok} (0, 1e54]
}
/// Singleton rebalance state
// struct Rebalance {
// uint256 nonce;
// mapping(address token => RebalanceDetails) details;
// RebalanceLimits limits; // D18{BU/share} (0, 1e36]
// uint256 startedAt; // {s} timestamp rebalancing started, inclusive
// uint256 restrictedUntil; // {s} timestamp rebalancing is unrestricted to everyone, exclusive
// uint256 availableUntil; // {s} timestamp rebalancing ends overall, exclusive
// PriceControl priceControl; // AUCTION_LAUNCHER control over auction pricing
// }
/// 1 running auction at a time; N per rebalance overall
/// Auction states:
/// - APPROVED: startTime == 0 && endTime == 0
/// - PENDING: block.timestamp < startTime
/// - OPEN: block.timestamp >= startTime && block.timestamp <= endTime
/// - CLOSED: block.timestamp > endTime
// struct Auction {
// uint256 rebalanceNonce;
// mapping(address token => PriceRange) prices; // D27{UoA/tok} (0, 1e54]
// uint256 startTime; // {s} inclusive
// uint256 endTime; // {s} inclusive
// }
/// Used to mark old storage slots now deprecated
struct DeprecatedStruct {
bytes32 EMPTY;
}
function distributeFees() external;
function toAssets(uint256 shares, Math.Rounding rounding) external view returns (address[] memory _assets, uint256[] memory _amounts);
function mint(uint256 shares, address receiver, uint256 minSharesOut) external returns (address[] memory _assets, uint256[] memory _amounts);
function redeem(
uint256 shares,
address receiver,
address[] calldata assets,
uint256[] calldata minAmountsOut
) external returns (uint256[] memory _amounts);
function mandate() external returns (string memory);
function tvlFee() external returns (uint256);
function mintFee() external returns (uint256);
function lastPoke() external returns (uint256);
function isDeprecated() external returns (bool);
function nextAuctionId() external returns (uint256);
function version() external returns (string memory);
}
interface IGovernanceDeployer_400 {
struct GovParams {
// Basic Parameters
uint48 votingDelay; // {s}
uint32 votingPeriod; // {s}
uint256 proposalThreshold; // D18{1}
uint256 quorumThreshold; // D18{1}
uint256 timelockDelay; // {s}
// Roles
address[] guardians; // Canceller Role
}
struct GovRoles {
address[] existingBasketManagers;
address[] auctionLaunchers;
address[] brandManagers;
}
function deployGovernanceWithTimelock(
IGovernanceDeployer_400.GovParams calldata govParams,
IVotes stToken,
bytes32 deploymentNonce
) external returns (address governor, address timelock);
}
interface IFolioDeployer_400 {
error FolioDeployer__LengthMismatch();
event FolioDeployed(address indexed folioOwner, address indexed folio, address folioAdmin);
event GovernedFolioDeployed(
address indexed stToken,
address indexed folio,
address ownerGovernor,
address ownerTimelock,
address tradingGovernor,
address tradingTimelock
);
function folioImplementation() external view returns (address);
function deployFolio(
IFolio_400.FolioBasicDetails calldata basicDetails,
IFolio_400.FolioAdditionalDetails calldata additionalDetails,
IFolio_400.FolioFlags calldata folioFlags,
address owner,
address[] memory basketManagers,
address[] memory auctionLaunchers,
address[] memory brandManagers,
bytes32 deploymentNonce
) external returns (address folio, address proxyAdmin);
function deployGovernedFolio(
IVotes stToken,
IFolio_400.FolioBasicDetails calldata basicDetails,
IFolio_400.FolioAdditionalDetails calldata additionalDetails,
IFolio_400.FolioFlags calldata folioFlags,
IGovernanceDeployer_400.GovParams calldata ownerGovParams,
IGovernanceDeployer_400.GovParams calldata tradingGovParams,
IGovernanceDeployer_400.GovRoles calldata govRoles,
bytes32 deploymentNonce
) external returns (address folio, address proxyAdmin);
function version() external returns (string memory);
}
IFolio.sol 212 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IVotes } from "@openzeppelin/contracts/governance/utils/IVotes.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
interface IFolio is IERC20 {
// === Events ===
event AuctionApproved(uint256 indexed auctionId, address indexed from, address indexed to, Auction auction);
event AuctionOpened(uint256 indexed auctionId, Auction auction);
event AuctionBid(uint256 indexed auctionId, uint256 sellAmount, uint256 buyAmount);
event AuctionClosed(uint256 indexed auctionId);
event FolioFeePaid(address indexed recipient, uint256 amount);
event ProtocolFeePaid(address indexed recipient, uint256 amount);
event BasketTokenAdded(address indexed token);
event BasketTokenRemoved(address indexed token);
event TVLFeeSet(uint256 newFee, uint256 feeAnnually);
event MintFeeSet(uint256 newFee);
event FeeRecipientSet(address indexed recipient, uint96 portion);
event AuctionDelaySet(uint256 newAuctionDelay);
event AuctionLengthSet(uint256 newAuctionLength);
event MandateSet(string newMandate);
event FolioKilled();
// === Errors ===
error Folio__FolioKilled();
error Folio__Unauthorized();
error Folio__EmptyAssets();
error Folio__BasketModificationFailed();
error Folio__FeeRecipientInvalidAddress();
error Folio__FeeRecipientInvalidFeeShare();
error Folio__BadFeeTotal();
error Folio__TVLFeeTooHigh();
error Folio__TVLFeeTooLow();
error Folio__MintFeeTooHigh();
error Folio__ZeroInitialShares();
error Folio__InvalidAsset();
error Folio__InvalidAssetAmount(address asset);
error Folio__InvalidAuctionLength();
error Folio__InvalidSellLimit();
error Folio__InvalidBuyLimit();
error Folio__AuctionCannotBeOpened();
error Folio__AuctionCannotBeOpenedPermissionlesslyYet();
error Folio__AuctionNotOngoing();
error Folio__AuctionCollision();
error Folio__InvalidPrices();
error Folio__AuctionTimeout();
error Folio__SlippageExceeded();
error Folio__InsufficientBalance();
error Folio__InsufficientBid();
error Folio__ExcessiveBid();
error Folio__InvalidAuctionTokens();
error Folio__InvalidAuctionDelay();
error Folio__InvalidAuctionTTL();
error Folio__TooManyFeeRecipients();
error Folio__InvalidArrayLengths();
// === Structures ===
struct FolioBasicDetails {
string name;
string symbol;
address[] assets;
uint256[] amounts; // {tok}
uint256 initialShares; // {share}
}
struct FolioAdditionalDetails {
uint256 auctionDelay; // {s}
uint256 auctionLength; // {s}
FeeRecipient[] feeRecipients;
uint256 tvlFee; // D18{1/s}
uint256 mintFee; // D18{1}
string mandate;
}
struct FeeRecipient {
address recipient;
uint96 portion; // D18{1}
}
struct BasketRange {
uint256 spot; // D27{buyTok/share}
uint256 low; // D27{buyTok/share} inclusive
uint256 high; // D27{buyTok/share} inclusive
}
struct Prices {
uint256 start; // D27{buyTok/sellTok}
uint256 end; // D27{buyTok/sellTok}
}
/// Auction states:
/// - APPROVED: start == 0 && end == 0
/// - OPEN: block.timestamp >= start && block.timestamp <= end
/// - CLOSED: block.timestamp > end
struct Auction {
uint256 id;
IERC20 sell;
IERC20 buy;
BasketRange sellLimit; // D27{sellTok/share} min ratio of sell token in the basket, inclusive
BasketRange buyLimit; // D27{buyTok/share} max ratio of buy token in the basket, exclusive
Prices prices; // D27{buyTok/sellTok}
uint256 availableAt; // {s} inclusive
uint256 launchTimeout; // {s} inclusive
uint256 start; // {s} inclusive
uint256 end; // {s} inclusive
// === Gas optimization ===
uint256 k; // D18{1} price = startPrice * e ^ -kt
}
function distributeFees() external;
function folio() external view returns (address[] memory _assets, uint256[] memory _amounts);
function toAssets(uint256 shares, Math.Rounding rounding) external view returns (address[] memory _assets, uint256[] memory _amounts);
function AUCTION_APPROVER() external view returns (bytes32);
function AUCTION_LAUNCHER() external view returns (bytes32);
function BRAND_MANAGER() external view returns (bytes32);
function mintFee() external view returns (uint256);
function mint(uint256 shares, address receiver) external returns (address[] memory _assets, uint256[] memory _amounts);
function redeem(
uint256 shares,
address receiver,
address[] calldata assets,
uint256[] calldata minAmountsOut
) external returns (uint256[] memory _amounts);
function version() external returns (string memory);
}
interface IGovernanceDeployer {
struct GovParams {
// Basic Parameters
uint48 votingDelay; // {s}
uint32 votingPeriod; // {s}
uint256 proposalThreshold; // D18{1}
uint256 quorumPercent; // in percent, e.g 4 for 4%
uint256 timelockDelay; // {s}
// Roles
address[] guardians; // Canceller Role
}
function deployGovernanceWithTimelock(
IGovernanceDeployer.GovParams calldata govParams,
IVotes stToken
) external returns (address governor, address timelock);
}
struct GovRoles {
address[] existingTradeProposers;
address[] tradeLaunchers;
address[] vibesOfficers;
}
interface IFolioDeployer {
error FolioDeployer__LengthMismatch();
event FolioDeployed(address indexed folioOwner, address indexed folio, address folioAdmin);
event GovernedFolioDeployed(
address indexed stToken,
address indexed folio,
address ownerGovernor,
address ownerTimelock,
address tradingGovernor,
address tradingTimelock
);
function folioImplementation() external view returns (address);
function deployFolio(
IFolio.FolioBasicDetails calldata basicDetails,
IFolio.FolioAdditionalDetails calldata additionalDetails,
address owner,
address[] memory auctionApprovers,
address[] memory auctionLaunchers,
address[] memory brandManagers,
bytes32 deploymentNonce
) external returns (address folio, address proxyAdmin);
function deployGovernedFolio(
IVotes stToken,
IFolio.FolioBasicDetails calldata basicDetails,
IFolio.FolioAdditionalDetails calldata additionalDetails,
IGovernanceDeployer.GovParams calldata ownerGovParams,
IGovernanceDeployer.GovParams calldata tradingGovParams,
GovRoles calldata govRoles,
bytes32 deploymentNonce
)
external
returns (
address folio,
address proxyAdmin,
address ownerGovernor,
address ownerTimelock,
address tradingGovernor,
address tradingTimelock
);
function version() external returns (string memory);
}
IRTokenZapper.sol 51 lines
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.30;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
struct Call {
address to;
bytes data;
uint256 value;
}
struct ZapERC20Params {
bytes program;
// Token to zap
IERC20 tokenIn;
// Total amount to zap / pull from user
uint256 amountIn;
// Weiroll code to execute to produce 'amountOut' of 'tokenOut'
bytes32[] commands;
bytes[] state;
IERC20[] tokens;
// RTokens the user requested
uint256 amountOut;
// RToken to issue
IERC20 tokenOut;
}
struct ZapParams {
// OpCodes to execute to produce 'amountOut' of 'tokenOut'
bytes program;
// Token to zap
address tokenIn;
// Total amount to zap / pull from user
uint256 amountIn;
// Weiroll code to execute to produce 'amountOut' of 'tokenOut'
bytes32[] commands;
bytes[] state;
IERC20[] tokens;
// RTokens the user requested
uint256 amountOut;
// RToken to issue
address tokenOut;
address recipient;
}
IWrappedNative.sol 9 lines
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.30;
interface IWrappedNative {
function deposit() external payable;
function withdraw(uint256 amount) external;
function balanceOf(address account) external view returns (uint256);
}
PreventTampering.sol 40 lines
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.30;
abstract contract PreventTampering {
modifier revertOnCodeHashChange() {
bytes32 hashBefore;
assembly {
hashBefore := extcodehash(address())
}
_;
bytes32 hashPostExecution;
assembly {
hashPostExecution := extcodehash(address())
}
require(hashPostExecution == hashBefore, "PreventTampering: Code has changed");
}
}
contract SelfDestruct {
function destroy() external {
selfdestruct(payable(msg.sender));
}
function doNothing() external {}
}
contract TestPreventTampering is PreventTampering {
function shouldNotRevert() external {
SelfDestruct selfDestruct = new SelfDestruct();
address(selfDestruct).delegatecall(abi.encodeWithSelector(selfDestruct.destroy.selector));
}
function shouldRevert() revertOnCodeHashChange() external {
SelfDestruct selfDestruct = new SelfDestruct();
address(selfDestruct).delegatecall(abi.encodeWithSelector(selfDestruct.destroy.selector));
}
function markedRevertOnCodeHashChangeDontRevert() revertOnCodeHashChange() external {
SelfDestruct selfDestruct = new SelfDestruct();
address(selfDestruct).delegatecall(abi.encodeWithSelector(selfDestruct.doNothing.selector));
}
}
Command.sol 457 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
import "./Constants.sol" as constants;
import "./Indices.sol" as indices;
import {Indices, Index, IndicesImpl} from "./Indices.sol";
type Command is bytes32;
type Flags is bytes32;
// Utility for manipulating the byte 5 of a command
/** Command format:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
┌───────┬─┬─┬─────────────────────────────────────────┬──┬──┬──┐
│ sel │f│ │ 10 args |S │ o│ |
└───────┴─┴─┴─────────────────────────────────────────┴──┴──┴──┘
byte 4 flags => 32, 216
byte 6-26: args => 48, 208
byte 26-27: size of args
byte 28-29: output
byte 30-31: unused
S: The number of words in the fixed part of the arguments
flags
0 1 2 3 4 5 6 7
┌───┬───┬───────────────┬────────┐
│tup│ext│ reserved │calltype│
└───┴───┴───────────────┴────────┘
calltype:
┌──────┬───────────────────┐
│ 0x00 │ DELEGATECALL │
├──────┼───────────────────┤
│ 0x01 │ CALL │
├──────┼───────────────────┤
│ 0x02 │ STATICCALL │
├──────┼───────────────────┤
│ 0x03 │ CALL with value │
└──────┴───────────────────┘
args, out and T are all an Index, which is a 2 byte value containing a 15 bit offset, and a 1 bit flag
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
┌─────────────────────────────────────────────────────────────────┬──┐
│ offset |f |
└─────────────────────────────────────────────────────────────────┴──┘
*/
library CommandImpl {
uint256 constant FLAG_MASK =
0x00000000ff000000000000000000000000000000000000000000000000000000;
uint256 constant INV_FLAG_MASK =
0xffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffff;
uint256 constant TEN_FIRST_ARGS_MASK =
0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff;
uint256 constant UPDATE_OUTPUT_MASK =
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffff;
uint256 constant UPDATE_ARGS_MASK =
0xffffffffffff0000000000000000000000000000000000000000ffffffffffff;
using IndicesImpl for Indices;
/**
* @notice Checks if the command returns a tuple (multiple values)
* @param self The Flags to check
* @return out True if the tuple return flag (bit 0) is set
*/
function isTupleReturn(Flags self) internal pure returns (bool out) {
// Check if bit 33 is set
assembly ("memory-safe") {
out := eq(
and(
self,
0x80000000000000000000000000000000000000000000000000000000
),
0x80000000000000000000000000000000000000000000000000000000
)
}
}
/**
* @notice Checks if the command returns a tuple (multiple values)
* @param self The Command to check
* @return out True if the tuple return flag (bit 0) is set
*/
function isTupleReturn(Command self) internal pure returns (bool out) {
// Check if bit 33 is set
assembly ("memory-safe") {
out := eq(
and(
self,
0x80000000000000000000000000000000000000000000000000000000
),
0x80000000000000000000000000000000000000000000000000000000
)
}
}
/**
* @notice Checks if this is an extended command
* @dev Extended commands support additional features for future extensibility
* @param self The Flags to check
* @return out True if the extended command flag (bit 1) is set
*/
function isExtendedCommand(Flags self) internal pure returns (bool out) {
// Check if bit 34 is set
assembly ("memory-safe") {
out := eq(
and(
self,
0x40000000000000000000000000000000000000000000000000000000
),
0x40000000000000000000000000000000000000000000000000000000
)
}
}
/**
* @notice Checks if this is an extended command
* @dev Extended commands support additional features for future extensibility
* @param self The Flags to check
* @return out True if the extended command flag (bit 1) is set
*/
function isExtendedCommand(Command self) internal pure returns (bool out) {
// Check if bit 34 is set
assembly ("memory-safe") {
out := eq(
and(
self,
0x40000000000000000000000000000000000000000000000000000000
),
0x40000000000000000000000000000000000000000000000000000000
)
}
}
/**
* @notice Extracts the call type from the flags
* @param self The Flags to extract from
* @return out The call type (0=DELEGATECALL, 1=CALL, 2=STATICCALL, 3=VALUECALL)
*/
function callType(Flags self) internal pure returns (uint256 out) {
assembly ("memory-safe") {
out := and(shr(216, self), 3)
}
}
/**
* @notice Extracts the call type from the flags
* @param self The Command to extract from
* @return out The call type (0=DELEGATECALL, 1=CALL, 2=STATICCALL, 3=VALUECALL)
*/
function callType(Command self) internal pure returns (uint256 out) {
assembly ("memory-safe") {
out := and(shr(216, self), 3)
}
}
/**
* @notice Checks if the command uses DELEGATECALL
* @param self The Command to check
* @return out True if call type is DELEGATECALL
*/
function isDelegateCall(Command self) internal pure returns (bool out) {
return callType(self) == constants.FLAG_CT_DELEGATECALL;
}
/**
* @notice Checks if the command uses CALL
* @param self The Command to check
* @return out True if call type is CALL
*/
function isCall(Command self) internal pure returns (bool out) {
return callType(self) == constants.FLAG_CT_CALL;
}
/**
* @notice Checks if the command uses STATICCALL
* @param self The Command to check
* @return out True if call type is STATICCALL
*/
function isStaticCall(Command self) internal pure returns (bool out) {
return callType(self) == constants.FLAG_CT_STATICCALL;
}
/**
* @notice Checks if the command uses CALL with value transfer
* @param self The Command to check
* @return out True if call type is VALUECALL
*/
function isValueCall(Command self) internal pure returns (bool out) {
return callType(self) == constants.FLAG_CT_VALUECALL;
}
/**
* @notice Checks if the command uses DELEGATECALL
* @param self The Flags to check
* @return out True if call type is DELEGATECALL
*/
function isDelegateCall(Flags self) internal pure returns (bool out) {
return callType(self) == constants.FLAG_CT_DELEGATECALL;
}
/**
* @notice Checks if the command uses CALL
* @param self The Flags to check
* @return out True if call type is CALL
*/
function isCall(Flags self) internal pure returns (bool out) {
return callType(self) == constants.FLAG_CT_CALL;
}
/**
* @notice Checks if the command uses STATICCALL
* @param self The Flags to check
* @return out True if call type is STATICCALL
*/
function isStaticCall(Flags self) internal pure returns (bool out) {
return callType(self) == constants.FLAG_CT_STATICCALL;
}
/**
* @notice Checks if the command uses CALL with value transfer
* @param self The Flags to check
* @return out True if call type is VALUECALL
*/
function isValueCall(Flags self) internal pure returns (bool out) {
return callType(self) == constants.FLAG_CT_VALUECALL;
}
/**
* @notice Extracts the flags from a command
* @dev Returns the command itself as Flags are embedded in the command
* @param self The Command to extract flags from
* @return out The Flags embedded in the command
*/
function flags(Command self) internal pure returns (Flags out) {
assembly ("memory-safe") {
out := self
}
}
/**
* @notice Extracts the function selector from a command
* @dev Returns the first 4 bytes of the command
* @param self The Command to extract from
* @return out The function selector as bytes32 (only first 4 bytes are significant)
*/
function selector(Command self) internal pure returns (bytes32 out) {
assembly ("memory-safe") {
out := and(
self,
0xffffffff00000000000000000000000000000000000000000000000000000000
)
}
}
/**
* @notice Extracts the argument indices from a command
* @dev Returns bytes 6-25 containing up to 10 Index values
* @param self The Command to extract from
* @return out The Indices collection containing argument references
*/
function args(Command self) internal pure returns (Indices out) {
assembly ("memory-safe") {
out := or(
shr(48, self),
0xffffffffffffffffffffffff0000000000000000000000000000000000000000
)
}
}
/**
* @dev abi calls are recursively encoded as |head|tail|. Given the arguments (uint256, (uint256, uint256), string) the head will
* contain 3 elements, first element is 1 word, second element is 2 words, the their element is also 32 bytes, and will store an
* and offset to the variable length data in the tail.
*/
function fixedArgsWords(Command self) internal pure returns (uint256 out) {
assembly ("memory-safe") {
out := and(shr(32, self), 0xFFFF)
}
}
/**
* @notice Checks if the command has no return value
* @param self The Command to check
* @return out True if output index is 0xFFFF (NO_RETURN)
*/
function noReturn(Command self) internal pure returns (bool out) {
assembly ("memory-safe") {
out := eq(and(self, 0xffff0000), 0xffff0000)
}
}
/**
* @notice Extracts the output index from a command
* @dev Returns bytes 28-29 containing the output storage location
* @param self The Command to extract from
* @return out The Index where the function return value should be stored
*/
function output(Command self) internal pure returns (Index out) {
assembly ("memory-safe") {
out := and(shr(16, self), 0xffff)
}
}
/**
* @notice Creates a new Command with specified selector, arguments, and output
* @param sel The function selector (4 bytes)
* @param _args The Indices containing argument references
* @param out The Index where the return value should be stored
* @return cmd The constructed Command
*/
function makeCommand(
uint256 sel,
Indices _args,
Index out
) internal pure returns (Command cmd) {
uint256 argsCount = _args.argCount();
assembly ("memory-safe") {
// Shift selector to the left
cmd := or(
or(
or(shl(224, sel), shl(48, and(_args, TEN_FIRST_ARGS_MASK))),
shl(32, and(argsCount, 0xFFFF))
),
shl(16, and(out, 0xFFFF))
)
}
}
/**
* @notice Creates a new Command with no return value
* @param sel The function selector (4 bytes)
* @param _args The Indices containing argument references
* @return cmd The constructed Command with NO_RETURN output
*/
function makeCommand(
uint256 sel,
Indices _args
) internal pure returns (Command cmd) {
return makeCommand(sel, _args, IndicesImpl.NO_RETURN);
}
/**
* @notice Creates a new Command with no arguments and no return value
* @param sel The function selector (4 bytes)
* @return cmd The constructed Command with empty args and NO_RETURN output
*/
function makeCommand(uint256 sel) internal pure returns (Command cmd) {
return makeCommand(sel, IndicesImpl.initEmpty(), IndicesImpl.NO_RETURN);
}
/**
* @notice Creates a new Command with no arguments and no return value
* @param sel The function selector (4 bytes)
* @return cmd The constructed Command with empty args and NO_RETURN output
*/
function makeCommand(
uint256 sel,
Index output
) internal pure returns (Command cmd) {
return makeCommand(sel, IndicesImpl.initEmpty(), output);
}
/**
* @notice Updates the output index in a command
* @param self The Command to update
* @param idx The Index where the return value should be stored
* @return out The updated Command with new output index
*/
function withOutput(
Command self,
Index idx
) internal pure returns (Command out) {
assembly ("memory-safe") {
out := or(and(self, UPDATE_OUTPUT_MASK), shl(16, and(idx, 0xFFFF)))
}
}
/**
* @notice Updates the arguments in a command
* @param self The Command to update
* @param _args The new Indices containing argument references
* @param argsCount The number of arguments (for fixed args word count)
* @return out The updated Command with new arguments
*/
function withArgs(
Command self,
Indices _args,
uint256 argsCount
) internal pure returns (Command out) {
assembly ("memory-safe") {
out := or(
or(
and(self, UPDATE_ARGS_MASK),
shl(48, and(_args, TEN_FIRST_ARGS_MASK))
),
shl(32, and(argsCount, 0xFFFF))
)
}
}
/**
* @notice Updates the flags in a command
* @param self The Command to update
* @param _flags The new Flags to set
* @return out The updated Command with new flags
*/
function withFlags(
Command self,
Flags _flags
) internal pure returns (Command out) {
assembly ("memory-safe") {
out := or(and(self, INV_FLAG_MASK), and(_flags, FLAG_MASK))
}
}
/**
* @notice Creates Flags with specified attributes
* @param tupleReturn Whether the function returns multiple values
* @param extendedCommand Whether this is an extended command
* @param ct The call type (0=DELEGATECALL, 1=CALL, 2=STATICCALL, 3=VALUECALL)
* @return out The constructed Flags value
*/
function makeFlags(
bool tupleReturn,
bool extendedCommand,
uint256 ct
) internal pure returns (Flags out) {
assembly ("memory-safe") {
// Validate callType is within valid range (0-3)
if gt(ct, 3) {
revert(0, 0)
}
// Set flags to match the bit positions used by the reader functions
// Tuple return: bit position to match isTupleReturn check
if tupleReturn {
// Set bit 8
out := or(out, 128)
}
// Extended command: bit position to match isExtendedCommand check
if extendedCommand {
out := or(out, 64)
}
out := or(out, ct)
out := shl(216, out)
}
}
/**
* @notice Unwraps Flags to its raw bytes32 value
* @param self The Flags to unwrap
* @return out The underlying bytes32 value
*/
function unwrap(Flags self) internal pure returns (bytes32 out) {
assembly ("memory-safe") {
out := self
}
}
function unwrap(Command self) internal pure returns (bytes32 out) {
assembly ("memory-safe") {
out := self
}
}
}
CommandBuilder.sol 243 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
import "./Constants.sol";
import {Index, Indices, IndicesImpl, SHIFT_ARGS_FILL, INDEX_MASK, INDEX_VARIABLE_LENGTH_FLAG, INDEX_NUM_BITS, OFFSET_MASK} from "./Indices.sol";
import {Command, CommandImpl} from "./Command.sol";
library CommandBuilder {
using IndicesImpl for Index;
using CommandImpl for Command;
using IndicesImpl for Indices;
function buildInputs(
bytes[] memory state,
Command command,
Indices indices
) internal view returns (bytes memory argsBuffer) {
assembly ("memory-safe") {
state := add(state, 0x20)
let headPtr := mload(0x40)
argsBuffer := headPtr
headPtr := add(headPtr, 0x20)
mstore(headPtr, command)
headPtr := add(headPtr, 0x4)
let tailOffset := mul(and(shr(32, command), INDEX_MASK), 0x20)
for {
} 1 {
} {
if eq(and(indices, INDEX_MASK), INDEX_MASK) {
break
}
let slot := mload(
add(state, mul(and(indices, OFFSET_MASK), 0x20))
)
switch and(INDEX_VARIABLE_LENGTH_FLAG, indices)
case 0 {
mstore(headPtr, mload(add(slot, 0x20)))
}
default {
// Variable length
let argLen := mload(slot)
mstore(headPtr, tailOffset)
pop(
staticcall(
gas(),
4,
add(slot, 0x20),
argLen,
add(argsBuffer, add(tailOffset, 36)),
argLen
)
)
tailOffset := add(tailOffset, argLen)
}
headPtr := add(headPtr, 0x20)
indices := or(shr(INDEX_NUM_BITS, indices), SHIFT_ARGS_FILL)
}
mstore(argsBuffer, add(tailOffset, 4))
mstore(0x40, add(argsBuffer, add(0x20, mload(argsBuffer))))
}
}
// function buildInputs(
// bytes[] memory state,
// Command command,
// Indices indices
// ) internal view returns (bytes memory) {
// unchecked {
// uint256 headPtr;
// bytes memory argsBuffer;
// // We're doing something slightly unsafe here, as we're using the stack-space above the free memory pointer as a buffer
// // to construct the calldata. But we're doing it within an isolated function body, so it's likely fine. Just do not add any calls, or forge/hardhat
// // within this body as it will trash the buffer we're constructing.
// assembly ("memory-safe") {
// headPtr := mload(0x40)
// argsBuffer := headPtr
// headPtr := add(headPtr, 32)
// }
// // ==== MEMORY UNSAFE CODE STARTS HERE ====
// {
// // Write the selector
// bytes32 sel = command.selector();
// assembly ("memory-safe") {
// mstore(headPtr, sel)
// headPtr := add(headPtr, 4)
// }
// }
// uint256 tailOffset = command.fixedArgsWords() * 32;
// for (uint256 i; i < 32; i++) {
// Index idx = indices.peek();
// if (idx.isEndOfArgs()) {
// break;
// }
// uint256 offset = idx.slotIndex();
// if (idx.isVariableLength()) {
// uint256 arglen = state[offset].length;
// // Write the pointer to the variable length data
// assembly ("memory-safe") {
// mstore(headPtr, tailOffset)
// headPtr := add(headPtr, 32)
// }
// // Copy the data to the tail buffer
// memcpy(
// state[offset],
// 32,
// argsBuffer,
// tailOffset + 36,
// arglen
// );
// assembly ("memory-safe") {
// tailOffset := add(tailOffset, arglen)
// }
// } else {
// assembly ("memory-safe") {
// let stateVariablePtr := mload(
// add(add(state, 32), mul(offset, 32))
// )
// mstore(headPtr, mload(add(stateVariablePtr, 32)))
// headPtr := add(headPtr, 32)
// }
// }
// indices = indices.shift();
// }
// assembly ("memory-safe") {
// mstore(argsBuffer, add(tailOffset, 4))
// mstore(0x40, add(argsBuffer, add(32, mload(argsBuffer))))
// }
// // ==== MEMORY UNSAFE CODE ENDS HERE ====
// return argsBuffer;
// }
// }
function writeOutputs(
bytes[] memory state,
Index outputIndex,
bytes memory output
) internal view {
assembly ("memory-safe") {
state := add(add(state, 32), mul(and(outputIndex, 0x7FFF), 0x20))
switch and(0x8000, outputIndex)
case 0 {
mstore(state, output)
}
default {
// Overwrite the first word of the return data with the length - 32
mstore(add(output, 32), sub(mload(output), 32))
// Insert a pointer to the return data, starting at the second word, into state
mstore(state, add(output, 32))
}
}
}
// function writeOutputs(
// bytes[] memory state,
// Index outputIndex,
// bytes memory output
// ) internal view {
// uint256 offset = outputIndex.slotIndex();
// if (outputIndex.isVariableLength()) {
// // Check the first field is 0x20 (because we have only a single return value)
// uint256 argptr;
// assembly ("memory-safe") {
// argptr := mload(add(output, 32))
// }
// require(argptr == 32, "Only one return value permitted (variable)");
// assembly {
// // Overwrite the first word of the return data with the length - 32
// mstore(add(output, 32), sub(mload(output), 32))
// // Insert a pointer to the return data, starting at the second word, into state
// mstore(add(add(state, 32), mul(offset, 32)), add(output, 32))
// }
// } else {
// require(output.length >= 32, "Return at least 32 bytes");
// // There are rare instances of contracts whoes ABI indicate a single word return returning more than 1 word
// // returndata buffers containing a single word of data.
// if (output.length > 32) {
// // Truncate returndata to proper size
// bytes memory newOutput = new bytes(32);
// memcpy(output, 0, newOutput, 0, output.length);
// output = newOutput;
// }
// state[offset] = output;
// }
// }
// Wraps the tuple in a buffer
function writeTuple(
bytes[] memory state,
Index outputIndex,
bytes memory output
) internal view {
unchecked {
uint256 offset = outputIndex.slotIndex();
bytes memory entry = state[offset] = new bytes(output.length + 32);
memcpy(output, 32, entry, 64, output.length);
assembly ("memory-safe") {
let l := mload(output)
mstore(add(entry, 32), l)
}
}
}
function memcpy(
bytes memory src,
uint256 srcidx,
bytes memory dest,
uint256 destidx,
uint256 len
) internal view {
assembly ("memory-safe") {
pop(
staticcall(
gas(),
4,
add(src, srcidx),
len,
add(dest, destidx),
len
)
)
}
}
}
Constants.sol 12 lines
// SPDX-License-Identifier: MIT pragma solidity 0.8.30; uint256 constant FLAG_CT_DELEGATECALL = 0x00; uint256 constant FLAG_CT_CALL = 0x01; uint256 constant FLAG_CT_STATICCALL = 0x02; uint256 constant FLAG_CT_VALUECALL = 0x03; uint256 constant FLAG_CT_MASK = 0x03; uint256 constant FLAG_EXTENDED_COMMAND = 0x80; uint256 constant FLAG_TUPLE_RETURN = 0x40; uint256 constant SHORT_COMMAND_FILL = 0x000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
Indices.sol 214 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
import "./Constants.sol" as constants;
uint256 constant INDEX_MASK = 0xFFFF;
uint256 constant INDEX_NUM_BITS = 16;
uint256 constant INDEX_VARIABLE_LENGTH_FLAG = 0x8000;
uint256 constant OFFSET_MASK = 0x7FFF;
uint256 constant SHIFT_ARGS_FILL = 0xFFFF000000000000000000000000000000000000000000000000000000000000;
type Indices is bytes32;
type Index is uint256;
// Structure of indices:
// Each Index is 2 bytes
// First bit is the variable length flag
// last 15 bits is the offset
library IndicesImpl {
Index constant END_OF_ARGS = Index.wrap(0xFFFF);
Index constant NO_RETURN = Index.wrap(0xFFFF);
/**
* @notice Creates an Index with the specified offset and variable length flag
* @param offset The position in the state array (must be < 32768)
* @param variableLen Whether this index points to variable-length data
* @return out The constructed Index value
*/
function makeIndex(
uint256 offset,
bool variableLen
) internal pure returns (Index out) {
assembly ("memory-safe") {
out := and(offset, OFFSET_MASK)
if variableLen {
out := or(out, INDEX_VARIABLE_LENGTH_FLAG)
}
}
}
function writeWord(
Index self,
bytes[] memory state,
uint256 value
) internal pure returns (bytes memory out) {
assembly ("memory-safe") {
let slotPtr := mload(
add(state, add(mul(and(self, 0x7FFF), 0x20), 0x20))
)
mstore(add(slotPtr, 32), value)
}
}
function getSlot(
Index self,
bytes[] memory state
) internal pure returns (bytes memory out) {
assembly ("memory-safe") {
out := mload(
add(state, add(mul(and(self, OFFSET_MASK), 0x20), 0x20))
)
}
}
function getSlotWord(
Index self,
bytes[] memory state
) internal pure returns (uint256 out) {
assembly ("memory-safe") {
out := mload(
add(
mload(
add(state, add(mul(and(self, OFFSET_MASK), 0x20), 0x20))
),
0x20
)
)
}
}
/**
* @notice Counts the number of arguments in an Indices collection
* @dev Iterates through indices until END_OF_ARGS (0xFFFF) is encountered
* @param self The Indices to count
* @return out The number of arguments (excluding the terminator)
*/
function argCount(Indices self) internal pure returns (uint256 out) {
while (!isEndOfArgs(peek(self))) {
out++;
self = shift(self);
}
}
/**
* @notice Initializes an empty Indices collection
* @dev Returns a bytes32 filled with 0xFF bytes (all END_OF_ARGS markers)
* @return out An empty Indices ready to accept push operations
*/
function initEmpty() internal pure returns (Indices out) {
assembly ("memory-safe") {
out := 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
}
}
/**
* @notice Adds an Index to the Indices collection
* @dev Shifts existing indices left by 16 bits and adds the new index at the rightmost position
* @param self The current Indices collection
* @param index The Index to add
* @return out The updated Indices with the new index added
*/
function push(
Indices self,
Index index
) internal pure returns (Indices out) {
assembly ("memory-safe") {
out := or(shl(INDEX_NUM_BITS, self), and(index, INDEX_MASK))
}
}
/**
* @notice Checks if an Index represents the end of arguments marker
* @param self The Index to check
* @return out True if the index equals 0xFFFF (END_OF_ARGS)
*/
function isEndOfArgs(Index self) internal pure returns (bool out) {
assembly ("memory-safe") {
out := eq(self, INDEX_MASK)
}
}
/**
* @notice Checks if an Index represents variable-length data
* @dev Checks the least significant bit (bit 0) of the index
* @param self The Index to check
* @return out True if the variable length flag is set
*/
function isVariableLength(Index self) internal pure returns (bool out) {
assembly ("memory-safe") {
out := eq(
and(self, INDEX_VARIABLE_LENGTH_FLAG),
INDEX_VARIABLE_LENGTH_FLAG
)
}
}
/**
* @notice Extracts the state array offset from an Index
* @dev Masks out the variable length flag to get the 15-bit offset
* @param self The Index to extract from
* @return out The offset value (0-32767)
*/
function slotIndex(Index self) internal pure returns (uint256 out) {
assembly ("memory-safe") {
out := and(self, OFFSET_MASK)
}
}
/**
* @notice Returns the rightmost (most recently pushed) Index without removing it
* @param self The Indices to peek at
* @return inner The rightmost Index in the collection
*/
function peek(Indices self) internal pure returns (Index inner) {
assembly ("memory-safe") {
inner := and(self, INDEX_MASK)
}
}
/**
* @notice Returns the rightmost (most recently pushed) Index without removing it
* @param self The Indices to peek at
* @param state The state array
* @return out The rightmost Index in the collection
*/
function peekWord(
Indices self,
bytes[] memory state
) internal pure returns (uint256 out) {
assembly ("memory-safe") {
out := mload(
add(
mload(
add(state, add(mul(and(self, OFFSET_MASK), 0x20), 0x20))
),
0x20
)
)
}
}
/**
* @notice Unwraps an Index to its raw uint256 value
* @param self The Index to unwrap
* @return out The underlying uint256 value
*/
function unwrap(Index self) internal pure returns (uint256 out) {
assembly ("memory-safe") {
out := self
}
}
/**
* @notice Unwraps an Indices to its raw bytes32 value
* @param self The Indices to unwrap
* @return out The underlying bytes32 value
*/
function unwrap(Indices self) internal pure returns (bytes32 out) {
assembly ("memory-safe") {
out := self
}
}
/**
* @notice Removes the rightmost Index and shifts remaining indices right
* @dev Shifts right by 16 bits and fills left with 0xFF bytes
* @param self The Indices to shift
* @return out The shifted Indices with the rightmost element removed
*/
function shift(Indices self) internal pure returns (Indices out) {
assembly ("memory-safe") {
out := or(shr(INDEX_NUM_BITS, self), SHIFT_ARGS_FILL)
}
}
}
OpCodes.sol 580 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
import "./Constants.sol" as constants;
import {Index, Indices, IndicesImpl, INDEX_MASK, INDEX_NUM_BITS, INDEX_VARIABLE_LENGTH_FLAG, OFFSET_MASK, SHIFT_ARGS_FILL} from "./Indices.sol";
import {Command, Flags, CommandImpl} from "./Command.sol";
import {CommandBuilder} from "./CommandBuilder.sol";
type OpCode is bytes32;
type OpType is uint256;
type OpBinOp is uint256;
type OpUnOp is uint256;
// |byte cmdIndex|Index target| => executes cmd at index cmdIndex on target
OpType constant OP_CMD = OpType.wrap(0);
// |byte op|Index a|Index b|Index out| => slot[out] = op(slot[a], slot[b])
OpType constant OP_BIN_OP = OpType.wrap(1);
// |byte op|Index a|Index out| => slot[out] = op slot[a]
OpType constant OP_UN_OP = OpType.wrap(2);
OpType constant OP_HALT = OpType.wrap(3);
// |Index slot|1byte (offset in words)|1byte (size in words)|Index out| => slot[out] = slot[offset:offset+size]
OpType constant OP_TUPLE_READ = OpType.wrap(4);
// |Index slot|1byte (offset in words)|Index data| => slot[offset] = data
// (Only supports word sized fields)
OpType constant OP_TUPLE_WRITE = OpType.wrap(5);
// |1byte (size in bytes)|Index out|1..29 bytes data => slot[out] = data
// data is shifted to the right
OpType constant OP_WRITE_CONSTANT = OpType.wrap(6);
OpBinOp constant OP_BIN_OP_ADD = OpBinOp.wrap(0);
OpBinOp constant OP_BIN_OP_SUB = OpBinOp.wrap(1);
OpBinOp constant OP_BIN_OP_MUL = OpBinOp.wrap(2);
OpBinOp constant OP_BIN_OP_DIV = OpBinOp.wrap(3);
OpBinOp constant OP_BIN_OP_EQ = OpBinOp.wrap(4);
OpBinOp constant OP_BIN_OP_NEQ = OpBinOp.wrap(5);
OpBinOp constant OP_BIN_OP_LT = OpBinOp.wrap(6);
OpBinOp constant OP_BIN_OP_LTE = OpBinOp.wrap(7);
OpBinOp constant OP_BIN_OP_GT = OpBinOp.wrap(8);
OpBinOp constant OP_BIN_OP_GTE = OpBinOp.wrap(9);
OpBinOp constant OP_BIN_OP_MUL_X18 = OpBinOp.wrap(10);
OpBinOp constant OP_BIN_OP_DIV_X18 = OpBinOp.wrap(11);
OpUnOp constant OP_UN_OP_NOT = OpUnOp.wrap(0);
OpUnOp constant OP_UN_OP_NEG = OpUnOp.wrap(1);
OpUnOp constant OP_UN_OP_ABS = OpUnOp.wrap(2);
OpUnOp constant OP_UN_OP_INC = OpUnOp.wrap(3);
OpUnOp constant OP_UN_OP_DEC = OpUnOp.wrap(4);
/**
* OpCodes is the program that is executed by the VM.
*
* Each OpCode is stored packed in a byte array, and the OpCodes library provides a way to decode them.
*
* The decode functionality will read the next 32 bytes from the program on each call, the bytes are shifted such that the MSB contains the OpCode, and params
* be stored in the next 31 bytes somewhere.
*/
library OpCodeImpl {
function unwrap(OpCode op) internal pure returns (uint256 out) {
assembly ("memory-safe") {
out := op
}
}
function unwrap(OpType op) internal pure returns (uint256 out) {
assembly ("memory-safe") {
out := op
}
}
function consumeByte(OpCode op) internal pure returns (OpCode out) {
assembly ("memory-safe") {
out := or(shl(8, op), 0xff)
}
}
function consume2Bytes(OpCode op) internal pure returns (OpCode out) {
assembly ("memory-safe") {
out := or(shl(16, op), 0xffff)
}
}
function readOpType(OpCode op) internal pure returns (OpType out) {
assembly ("memory-safe") {
out := shr(248, op)
}
}
function readByte(OpCode op) internal pure returns (uint256 out) {
assembly ("memory-safe") {
out := shr(248, op)
}
}
function readIndex(OpCode op) internal pure returns (Index out) {
assembly ("memory-safe") {
out := and(shr(240, op), 0xffff)
}
}
/**
* @dev offset and size are converted into byte sizes
**/
function unpackOpTupleRead(
OpCode op
)
internal
pure
returns (Index slot, uint256 offset, uint256 size, Index out)
{
slot = readIndex(op);
op = consume2Bytes(op);
offset = readByte(op) * 32;
op = consumeByte(op);
size = readByte(op) * 32;
op = consumeByte(op);
out = readIndex(op);
}
function unpackOpTupleWrite(
OpCode op
) internal pure returns (Index slot, uint256 offset, Index data) {
slot = readIndex(op);
op = consume2Bytes(op);
offset = readByte(op) * 32;
op = consumeByte(op);
data = readIndex(op);
}
function unpackOpWriteConstant(
OpCode op
) internal pure returns (Index slot, bytes32 data) {
uint256 size = readByte(op);
op = consumeByte(op);
slot = readIndex(op);
op = consume2Bytes(op);
assembly ("memory-safe") {
data := shr(sub(256, mul(size, 8)), op)
}
}
function isHalt(OpType opType) internal pure returns (bool out) {
assembly ("memory-safe") {
out := eq(opType, 3)
}
}
function eq(OpType a, OpType b) internal pure returns (bool out) {
unchecked {
assembly ("memory-safe") {
out := eq(a, b)
}
}
}
function isOpCmd(OpType opType) internal pure returns (bool out) {
assembly ("memory-safe") {
out := eq(opType, 0)
}
}
function isOpBinOp(OpType opType) internal pure returns (bool out) {
assembly ("memory-safe") {
out := eq(opType, 1)
}
}
function isOpUnOp(OpType opType) internal pure returns (bool out) {
assembly ("memory-safe") {
out := eq(opType, 2)
}
}
function isOpTupleRead(OpType opType) internal pure returns (bool out) {
assembly ("memory-safe") {
out := eq(opType, 4)
}
}
function isOpTupleWrite(OpType opType) internal pure returns (bool out) {
assembly ("memory-safe") {
out := eq(opType, 5)
}
}
function isOpWriteConstant(OpType opType) internal pure returns (bool out) {
assembly ("memory-safe") {
out := eq(opType, 6)
}
}
}
interface VMOpCodes {
function cmd(uint256 cmdIndex) external view returns (uint256);
function add(uint256 a, uint256 b) external view returns (uint256);
function sub(uint256 a, uint256 b) external view returns (uint256);
function mul(uint256 a, uint256 b) external view returns (uint256);
function div(uint256 a, uint256 b) external view returns (uint256);
function eq(uint256 a, uint256 b) external view returns (uint256);
function neq(uint256 a, uint256 b) external view returns (uint256);
function lt(uint256 a, uint256 b) external view returns (uint256);
function lte(uint256 a, uint256 b) external view returns (uint256);
function gt(uint256 a, uint256 b) external view returns (uint256);
function gte(uint256 a, uint256 b) external view returns (uint256);
function mulx18(uint256 a, uint256 b) external view returns (uint256);
function divx18(uint256 a, uint256 b) external view returns (uint256);
function not(uint256 a) external view returns (uint256);
function neg(uint256 a) external view returns (uint256);
function abs(uint256 a) external view returns (uint256);
function inc(uint256 a) external view returns (uint256);
function dec(uint256 a) external view returns (uint256);
function tupleRead(
bytes memory tuple,
uint256 offset,
uint256 size
) external pure returns (uint256);
function tupleWrite(
bytes memory tuple,
uint256 offset,
uint256 data
) external;
function writeConstant(
uint256 size,
uint256 data
) external pure returns (uint256);
}
library BinOpImpl {
uint256 constant CODE = 1;
uint256 constant OP_BIN_LENGTH = 8;
function evalOpBin(OpCode opCodeArgs, bytes[] memory state) internal pure {
assembly ("memory-safe") {
state := add(state, 32)
let a := mload(
add(
mload(
add(
state,
mul(and(shr(232, opCodeArgs), INDEX_MASK), 0x20)
)
),
0x20
)
)
let b := mload(
add(
mload(
add(
state,
mul(and(shr(216, opCodeArgs), INDEX_MASK), 0x20)
)
),
0x20
)
)
let out := 0
switch shr(248, opCodeArgs)
case 0 {
out := add(a, b)
}
case 1 {
out := sub(a, b)
}
case 2 {
out := mul(a, b)
}
case 3 {
out := div(a, b)
}
case 4 {
out := eq(a, b)
}
case 5 {
out := iszero(eq(a, b))
}
case 6 {
out := lt(a, b)
}
case 7 {
out := or(eq(a, b), lt(a, b))
}
case 8 {
out := gt(a, b)
}
case 9 {
out := or(eq(a, b), gt(a, b))
}
case 10 {
out := div(mul(a, b), 1000000000000000000)
}
case 11 {
out := div(mul(a, 1000000000000000000), b)
}
default {
revert(0, 0)
}
mstore(
add(
mload(
add(
state,
mul(and(shr(200, opCodeArgs), INDEX_MASK), 0x20)
)
),
0x20
),
out
)
}
}
}
library UnOpImpl {
uint256 constant CODE = 2;
uint256 constant OP_UN_LENGTH = 6;
function evalOpUn(OpCode opCodeArgs, bytes[] memory state) internal pure {
assembly ("memory-safe") {
state := add(state, 32)
let a := mload(
add(
mload(
add(
state,
mul(and(shr(232, opCodeArgs), INDEX_MASK), 0x20)
)
),
0x20
)
)
let out := 0
switch shr(248, opCodeArgs)
case 0 {
out := not(a)
}
case 1 {
out := sub(0, a)
}
case 2 {
out := a
if slt(a, 0) {
out := sub(0, a)
}
}
case 3 {
out := add(a, 1)
}
case 4 {
out := sub(a, 1)
}
default {
revert(0, 0)
}
mstore(
add(
mload(
add(
state,
mul(and(shr(216, opCodeArgs), INDEX_MASK), 0x20)
)
),
0x20
),
out
)
}
}
}
library CmdOpImpl {
uint256 constant CODE = 0;
uint256 constant OP_CMD_LENGTH = 4;
using OpCodeImpl for OpCode;
using CommandImpl for Flags;
using CommandImpl for Command;
using CommandImpl for Command;
using IndicesImpl for Indices;
using CommandBuilder for bytes[];
using IndicesImpl for Index;
error ExecutionFailed(uint256 commandIndex, address target, string message);
uint256 constant CMD_INDICES_MASK =
0xffffffffffffffffffffffff0000000000000000000000000000000000000000;
uint256 constant EXT_FLAG =
0x40000000000000000000000000000000000000000000000000000000;
function evalOpCmd(
OpCode opCodeArgs,
uint256 instructionIndex,
bytes32[] calldata commands,
bytes[] memory state
) internal {
address target;
Command command;
Indices indices;
assembly ("memory-safe") {
command := calldataload(
add(commands.offset, mul(shr(248, opCodeArgs), 0x20))
)
target := mload(
add(
mload(
add(
state,
add(
mul(
and(shr(232, opCodeArgs), OFFSET_MASK),
0x20
),
0x20
)
)
),
0x20
)
)
indices := or(shr(48, command), CMD_INDICES_MASK)
if and(command, EXT_FLAG) {
indices := calldataload(
add(
commands.offset,
add(mul(shr(248, opCodeArgs), 0x20), 0x20)
)
)
}
}
bool success;
bytes memory outdata;
uint256 callType = command.callType();
if (callType == constants.FLAG_CT_CALL) {
(success, outdata) = target.call(
state.buildInputs(command, indices)
);
} else if (callType == constants.FLAG_CT_DELEGATECALL) {
(success, outdata) = target.delegatecall(
state.buildInputs(command, indices)
);
} else if (callType == constants.FLAG_CT_STATICCALL) {
(success, outdata) = target.staticcall(
state.buildInputs(command, indices)
);
} else if (callType == constants.FLAG_CT_VALUECALL) {
(success, outdata) = target.call{value: indices.peekWord(state)}(
state.buildInputs(command, indices.shift())
);
} else {
revert("Invalid calltype");
}
if (success) {
if (command.noReturn()) {
return;
}
if (command.isTupleReturn()) {
state.writeTuple(command.output(), outdata);
} else {
state.writeOutputs(command.output(), outdata);
}
} else {
assembly {
if gt(mload(outdata), 0) {
outdata := add(outdata, 68)
}
}
revert ExecutionFailed({
commandIndex: instructionIndex,
target: target,
message: string(outdata)
});
}
}
}
library TupleReadImpl {
uint256 constant CODE = 4;
uint256 constant OP_TUPLE_READ_LENGTH = 7;
using OpCodeImpl for OpCode;
using IndicesImpl for Indices;
using IndicesImpl for Index;
using CommandBuilder for bytes[];
error TupleReadOutOfBounds(
Index slot,
uint256 offset,
uint256 size,
uint256 tupleLength
);
function evalOpTupleRead(
OpCode opCodeArgs,
bytes[] memory state
) internal view {
(Index slot, uint256 offset, uint256 size, Index out) = opCodeArgs
.unpackOpTupleRead();
bytes memory tuple = slot.getSlot(state);
if (tuple.length < offset + size) {
revert TupleReadOutOfBounds(slot, offset, size, tuple.length);
}
if (size == 32) {
uint256 value;
assembly ("memory-safe") {
value := mload(add(tuple, offset))
}
out.writeWord(state, value);
} else {
bytes memory outData = new bytes(size);
assembly ("memory-safe") {
let buff := add(outData, 32)
tuple := add(tuple, 32)
for {
let i := offset
} lt(i, add(offset, size)) {
i := add(i, 32)
} {
mstore(buff, mload(add(tuple, i)))
buff := add(buff, 32)
}
}
state.writeTuple(out, outData);
}
}
}
library TupleWriteImpl {
uint256 constant CODE = 5;
uint256 constant OP_TUPLE_WRITE_LENGTH = 6;
using OpCodeImpl for OpCode;
using IndicesImpl for Indices;
using IndicesImpl for Index;
using CommandBuilder for bytes[];
error TupleWriteOutOfBounds(
Index slot,
uint256 offset,
uint256 size,
uint256 tupleLength
);
function evalOpTupleWrite(
OpCode opCodeArgs,
bytes[] memory state
) internal pure {
(Index slot, uint256 offset, Index dataIdx) = opCodeArgs
.unpackOpTupleWrite();
uint256 dataVal = dataIdx.getSlotWord(state);
bytes memory tupleData = slot.getSlot(state);
if (tupleData.length < offset + 32) {
revert TupleWriteOutOfBounds(slot, offset, 32, tupleData.length);
}
assembly ("memory-safe") {
mstore(add(tupleData, offset), dataVal)
}
}
}
library WriteConstantImpl {
uint256 constant CODE = 6;
using OpCodeImpl for OpCode;
using IndicesImpl for Indices;
using IndicesImpl for Index;
using CommandBuilder for bytes[];
function evalOpWriteConstant(
OpCode opCodeArgs,
bytes[] memory state
) internal pure returns (uint256 out) {
assembly ("memory-safe") {
out := add(4, shr(248, opCodeArgs))
}
(Index slot, bytes32 data) = opCodeArgs.unpackOpWriteConstant();
slot.writeWord(state, uint256(data));
}
}
VM.sol 74 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
import "./CommandBuilder.sol";
import "./Constants.sol";
import {Index, Indices, IndicesImpl} from "./Indices.sol";
import {Command, Flags, CommandImpl} from "./Command.sol";
import {UnOpImpl, CmdOpImpl, OpType, BinOpImpl, OpCodeImpl, OpCode, WriteConstantImpl, TupleReadImpl, TupleWriteImpl} from "./OpCodes.sol";
import "./OpCodes.sol" as ops;
abstract contract VM {
using BinOpImpl for OpCode;
using UnOpImpl for OpCode;
using OpCodeImpl for OpCode;
using OpCodeImpl for OpType;
using CommandImpl for Command;
using CommandImpl for Flags;
using IndicesImpl for Indices;
using IndicesImpl for Index;
using CmdOpImpl for OpCode;
using CommandBuilder for bytes[];
using WriteConstantImpl for OpCode;
using TupleReadImpl for OpCode;
using TupleWriteImpl for OpCode;
error InvalidOpCode(OpType opCode);
function _execute(
bytes calldata program,
bytes32[] calldata commands,
bytes[] memory state
) internal returns (bytes[] memory) {
unchecked {
uint256 ip = 0;
while (true) {
OpCode opCodeArgs;
OpType opCode;
assembly ("memory-safe") {
opCodeArgs := calldataload(add(program.offset, ip))
opCode := shr(248, opCodeArgs)
opCodeArgs := shl(8, opCodeArgs)
}
if (opCode.isOpCmd()) {
opCodeArgs.evalOpCmd(ip, commands, state);
ip += CmdOpImpl.OP_CMD_LENGTH;
} else if (opCode.isOpBinOp()) {
opCodeArgs.evalOpBin(state);
ip += BinOpImpl.OP_BIN_LENGTH;
} else if (opCode.isOpUnOp()) {
opCodeArgs.evalOpUn(state);
ip += UnOpImpl.OP_UN_LENGTH;
} else if (opCode.isOpTupleRead()) {
opCodeArgs.evalOpTupleRead(state);
ip += TupleReadImpl.OP_TUPLE_READ_LENGTH;
} else if (opCode.isOpTupleWrite()) {
opCodeArgs.evalOpTupleWrite(state);
ip += TupleWriteImpl.OP_TUPLE_WRITE_LENGTH;
} else if (opCode.isOpWriteConstant()) {
ip += opCodeArgs.evalOpWriteConstant(state);
} else if (opCode.isHalt()) {
break;
} else {
revert InvalidOpCode(opCode);
}
if (ip >= program.length) {
break;
}
}
return state;
}
}
}
Zapper2.sol 178 lines
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.30;
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol";
import {IWrappedNative} from "./IWrappedNative.sol";
import {VM} from "./weiroll/VM.sol";
import {PreventTampering} from "./PreventTampering.sol";
import {ZapParams, ZapERC20Params} from "./IRTokenZapper.sol";
import {ZapperExecutor, DeployFolioConfig, ExecuteDeployOutput} from "./ZapperExecutor.sol";
struct ZapperOutput {
uint256[] dust;
uint256 amountOut;
uint256 gasUsed;
}
contract Zapper2 is ReentrancyGuard {
IWrappedNative internal immutable wrappedNative;
ZapperExecutor internal immutable zapperExecutor;
constructor(IWrappedNative wrappedNative_, ZapperExecutor executor_) {
wrappedNative = wrappedNative_;
zapperExecutor = executor_;
}
receive() external payable {}
function zap(
ZapParams calldata params
) external payable nonReentrant returns (ZapperOutput memory) {
uint256 startGas = gasleft();
return
zapInner(
params,
balanceOf(params.tokenOut, params.recipient),
startGas
);
}
function zapDeploy(
ZapParams calldata params,
DeployFolioConfig calldata config,
bytes32 nonce
) external payable nonReentrant returns (ZapperOutput memory out) {
uint256 startGas = gasleft();
pullFundsFromSender(
params.tokenIn,
params.amountIn,
address(zapperExecutor)
);
// STEP 1: Execute
ExecuteDeployOutput memory deployOutput = zapperExecutor.executeDeploy(
params,
config,
nonce
);
out.amountOut = deployOutput.amountOut;
out.dust = deployOutput.dust;
require(out.amountOut > params.amountOut, "INSUFFICIENT_OUT");
out.gasUsed = startGas - gasleft();
}
function validateTokenOut(address tokenOut) private {
uint256 codeSizeTokenOut = 0;
assembly {
codeSizeTokenOut := extcodesize(tokenOut)
}
require(codeSizeTokenOut == 0, "RETRY");
}
function zapInner(
ZapParams memory params,
uint256 initialBalance,
uint256 startGas
) private returns (ZapperOutput memory out) {
require(params.amountIn != 0, "INVALID_INPUT_AMOUNT");
require(params.amountOut != 0, "INVALID_OUTPUT_AMOUNT");
pullFundsFromSender(
params.tokenIn,
params.amountIn,
address(zapperExecutor)
);
// STEP 1: Execute
out.dust = zapperExecutor
.execute(
params.program,
params.commands,
params.state,
params.tokens
)
.dust;
// STEP 2: Verify that the user has gotten the tokens they requested
uint256 newBalance = balanceOf(params.tokenOut, params.recipient);
require(newBalance > initialBalance, "INVALID_NEW_BALANCE");
uint256 difference = newBalance - initialBalance;
require(difference >= params.amountOut, "INSUFFICIENT_OUT");
out.amountOut = difference;
out.gasUsed = startGas - gasleft();
}
function pullFundsFromSender(
address token,
uint256 amount,
address to
) private {
if (token != address(0)) {
SafeERC20.safeTransferFrom(IERC20(token), msg.sender, to, amount);
} else {
require(msg.value >= amount, "INSUFFICIENT_ETH");
wrappedNative.deposit{value: amount}();
SafeERC20.safeTransfer(IERC20(address(wrappedNative)), to, amount);
}
}
function balanceOf(
address token,
address account
) private view returns (uint256) {
if (token != address(0)) {
// Check if token address contains bytecode
return IERC20(token).balanceOf(account);
} else {
return account.balance;
}
}
/** Stubs for old interface */
function translateOldStyleZap(
ZapERC20Params calldata params
) private returns (ZapperOutput memory) {
uint256 startGas = gasleft();
ZapParams memory zapParams = ZapParams({
program: params.program,
tokenIn: address(params.tokenIn),
amountIn: params.amountIn,
commands: params.commands,
state: params.state,
tokens: params.tokens,
amountOut: params.amountOut,
tokenOut: address(params.tokenOut),
recipient: msg.sender
});
return
zapInner(
zapParams,
balanceOf(address(params.tokenOut), msg.sender),
startGas
);
}
function zapERC20(
ZapERC20Params calldata params
) external nonReentrant returns (ZapperOutput memory) {
return translateOldStyleZap(params);
}
function zapETH(
ZapERC20Params calldata params
) external payable nonReentrant returns (ZapperOutput memory) {
return translateOldStyleZap(params);
}
function zapToETH(
ZapERC20Params calldata params
) external payable nonReentrant returns (ZapperOutput memory) {
return translateOldStyleZap(params);
}
}
ZapperExecutor.sol 153 lines
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.30;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {VM} from "./weiroll/VM.sol";
import {PreventTampering} from "./PreventTampering.sol";
import {IVotes} from "./IFolio.sol";
import {IFolio_400, IFolioDeployer_400, IGovernanceDeployer_400} from "./IFolio_400.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ZapParams} from "./IRTokenZapper.sol";
struct DeployFolioConfig {
address deployer;
IFolio_400.FolioBasicDetails basicDetails;
IFolio_400.FolioAdditionalDetails additionalDetails;
IFolio_400.FolioFlags folioFlags;
IGovernanceDeployer_400.GovRoles govRoles;
bool isGoverned;
IVotes stToken;
address owner;
IGovernanceDeployer_400.GovParams ownerGovParams;
IGovernanceDeployer_400.GovParams tradingGovParams;
}
struct ExecuteOutput {
uint256[] dust;
}
struct ExecuteDeployOutput {
uint256[] dust;
uint256 amountOut;
}
contract ZapperExecutor is VM, PreventTampering {
receive() external payable {}
/** @dev Main endpoint to call
* @param commands - Weiroll code to execute
* @param state - Intiaial Weiroll state to use
* @param tokens - All tokens used by the Zap in order to calculate dust
*/
function execute(
bytes calldata program,
bytes32[] calldata commands,
bytes[] memory state,
IERC20[] memory tokens
) public payable revertOnCodeHashChange returns (ExecuteOutput memory out) {
_execute(program, commands, state);
out.dust = new uint256[](tokens.length);
for (uint256 i; i < tokens.length; i++) {
out.dust[i] = tokens[i].balanceOf(address(this));
}
}
function _prepareFolio(
address deployer,
address[] memory assets,
uint256[] memory amounts
) internal returns (uint256 initialShares) {
initialShares = type(uint256).max;
for (uint256 i = 0; i < assets.length; i++) {
uint256 balance = IERC20(assets[i]).balanceOf(address(this));
if (balance == 0) {
revert("ZERO BALANCE");
}
uint256 quantityPrShare = amounts[i];
if (quantityPrShare == 0) {
revert("ZERO QUANTITY");
}
uint256 shares = (balance * 1e18) / quantityPrShare;
if (shares < initialShares) {
initialShares = shares;
}
SafeERC20.safeApprove(IERC20(assets[i]), deployer, 0);
SafeERC20.safeApprove(
IERC20(assets[i]),
deployer,
type(uint256).max
);
}
if (initialShares == type(uint256).max) {
revert("NO SHARES");
}
}
function _transferDust(
IERC20[] memory tokens,
address recipient
) internal returns (uint256[] memory dust) {
dust = new uint256[](tokens.length);
for (uint256 i; i < tokens.length; i++) {
dust[i] = tokens[i].balanceOf(address(this));
SafeERC20.safeTransfer(tokens[i], recipient, dust[i]);
}
}
function executeDeploy(
ZapParams calldata params,
DeployFolioConfig memory config,
bytes32 nonce
)
public
payable
revertOnCodeHashChange
returns (ExecuteDeployOutput memory out)
{
_execute(params.program, params.commands, params.state);
// STEP 2: Deploy folio
uint256 initialShares = _prepareFolio(
config.deployer,
config.basicDetails.assets,
config.basicDetails.amounts
);
for (uint256 i = 0; i < config.basicDetails.assets.length; i++) {
config.basicDetails.amounts[i] =
(initialShares * config.basicDetails.amounts[i]) /
1e18;
}
config.basicDetails.initialShares = initialShares;
address folio;
if (config.isGoverned) {
(folio, ) = IFolioDeployer_400(config.deployer).deployGovernedFolio(
config.stToken,
config.basicDetails,
config.additionalDetails,
config.folioFlags,
config.ownerGovParams,
config.tradingGovParams,
config.govRoles,
nonce
);
} else {
(folio, ) = IFolioDeployer_400(config.deployer).deployFolio(
config.basicDetails,
config.additionalDetails,
config.folioFlags,
config.owner,
config.govRoles.existingBasketManagers,
config.govRoles.auctionLaunchers,
config.govRoles.brandManagers,
nonce
);
}
out.amountOut = IERC20(folio).balanceOf(address(this));
SafeERC20.safeTransfer(IERC20(folio), params.recipient, out.amountOut);
out.dust = _transferDust(params.tokens, params.recipient);
}
}
Write Contract 5 functions
These functions modify contract state and require a wallet transaction to execute.
zap 0x0d69de01
tuple params
returns: tuple
zapDeploy 0x7b928330
tuple params
tuple config
bytes32 nonce
returns: tuple
zapERC20 0x52abf338
tuple params
returns: tuple
zapETH 0xda673b16
tuple params
returns: tuple
zapToETH 0xbe355c46
tuple params
returns: tuple
Top Interactions
| Address | Txns | Sent | Received |
|---|---|---|---|
| 0x3A6fb4fA...eaDE | 2 | 2 | |
| 0x9eCE7936...3d04 | 1 | 1 | |
| 0x03d03A02...da99 | 1 | 1 | |
| 0xA3b43a51...af79 | 1 | 1 | |
| 0x304F3500...b5ac | 1 | 1 |
Recent Transactions
|
| Hash | Block | Age | From/To | Value | |
|---|---|---|---|---|---|
| 0x252c591d...74cbda | 24,460,781 | IN | 0x3A6fb4fA...eaDE | 0 ETH | |
| 0xb06412fc...37b916 | 24,460,512 | IN | 0x9eCE7936...3d04 | 0 ETH | |
| 0xf045466a...b1a4d0 | 24,460,082 | IN | 0x3A6fb4fA...eaDE | 0 ETH | |
| 0x244c3a33...c91a87 | 24,322,925 | IN | 0x304F3500...b5ac | 0 ETH | |
| 0x3c5c4852...378b3c | 24,321,904 | IN | 0xA3b43a51...af79 | 0.180000 ETH | |
| 0xd42f2ffb...8cf72d | 24,321,149 | IN | 0x03d03A02...da99 | 0 ETH |