Address Contract Partially Verified
Address
0x4486c140aFABe2B4ee98CB2a67A0E711eb063baF
Balance
0 ETH
Nonce
1
Code Size
11547 bytes
Creator
0x2F9ddCe4...d725 at tx 0x32039fd5...e1d1a9
Indexed Transactions
0 (1 on-chain, 0.7% indexed)
Contract Bytecode
11547 bytes
0x608060405234801561001057600080fd5b506004361061032b5760003560e01c806370a08231116101b2578063ba087652116100f9578063ce96cb77116100a2578063d905777e1161007c578063d905777e146107ab578063dd62ed3e146107e1578063ef8b30f71461080c578063f6ccaad41461081f57600080fd5b8063ce96cb771461077d578063d505accf14610790578063d71356e6146107a357600080fd5b8063c2ef373a116100d3578063c2ef373a14610757578063c63d75b614610496578063c6e6f5921461076a57600080fd5b8063ba08765214610728578063bd3a13f61461073b578063bd6f36031461074e57600080fd5b806399530b061161015b578063aaf5eb6811610135578063aaf5eb68146106f3578063b3d7f6b914610702578063b460af941461071557600080fd5b806399530b06146106b1578063a80e7547146106b9578063a9059cbb146106e057600080fd5b80638f765d591161018c5780638f765d591461064357806394bf804d1461069657806395d89b41146106a957600080fd5b806370a08231146105f057806375e077c3146106105780637ecebe001461062357600080fd5b8063374010a4116102765780634cdad5061161021f5780635ebae566116101f95780635ebae5661461055357806361c1c5e9146105d45780636e553f65146105dd57600080fd5b80634cdad506146105115780634f8b4ae71461052457806356caf6051461052c57600080fd5b8063450140951161025057806345014095146104c957806348f76e0f146104de5780634bc66f32146104f157600080fd5b8063374010a41461045c57806338d52e0f1461046f578063402d267d1461049657600080fd5b806318160ddd116102d8578063313ce567116102b2578063313ce56714610413578063358245fc1461044c5780633644e5151461045457600080fd5b806318160ddd146103ee57806323b872dd146103f75780632af98d6d1461040a57600080fd5b8063090f3f5011610309578063090f3f5014610373578063095ea7b3146103b85780630a28a477146103db57600080fd5b806301e1d1141461033057806306fdde031461034b57806307a2d13a14610360575b600080fd5b610338610827565b6040519081526020015b60405180910390f35b610353610848565b6040516103429190612490565b61033861036e3660046124fd565b6108d6565b600a546103939073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610342565b6103cb6103c636600461253f565b610903565b6040519015158152602001610342565b6103386103e93660046124fd565b61097d565b61033860025481565b6103cb610405366004612569565b61099d565b610338600c5481565b61043a7f000000000000000000000000000000000000000000000000000000000000001281565b60405160ff9091168152602001610342565b610338610ae1565b610338610b9d565b61033861046a366004612614565b610bf8565b6103937f000000000000000000000000cacd6fd266af91b8aed52accc382b4e165586e2981565b6103386104a4366004612698565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90565b6104dc6104d7366004612698565b610c4b565b005b6104dc6104ec3660046124fd565b610c5f565b600b546103939073ffffffffffffffffffffffffffffffffffffffff1681565b61033861051f3660046124fd565b610ccb565b6104dc610cd6565b6103387f0000000000000000000000000000000000000000000000000000000000093a8081565b6006546007546105939164ffffffffff808216926501000000000090920416907affffffffffffffffffffffffffffffffffffffffffffffffffffff1683565b6040805164ffffffffff94851681529390921660208401527affffffffffffffffffffffffffffffffffffffffffffffffffffff1690820152606001610342565b61033860095481565b6103386105eb3660046126b3565b610cfc565b6103386105fe366004612698565b60036020526000908152604090205481565b61033861061e3660046126f0565b610d10565b610338610631366004612698565b60056020526000908152604090205481565b61064b610e24565b60408051825164ffffffffff908116825260208085015190911690820152918101517affffffffffffffffffffffffffffffffffffffffffffffffffffff1690820152606001610342565b6103386106a43660046126b3565b611072565b610353611086565b610338611093565b6103387f0000000000000000000000000000000000000000000000000de0b6b3a764000081565b6103cb6106ee36600461253f565b6110be565b610338670de0b6b3a764000081565b6103386107103660046124fd565b611143565b610338610723366004612761565b611162565b610338610736366004612761565b61117f565b6104dc610749366004612848565b611194565b61033860085481565b6104dc6107653660046128c6565b61125a565b6103386107783660046124fd565b611369565b61033861078b366004612698565b611389565b6104dc61079e36600461291d565b6113b8565b6104dc6116dc565b6103386107b9366004612698565b73ffffffffffffffffffffffffffffffffffffffff1660009081526003602052604090205490565b6103386107ef36600461296b565b600460209081526000928352604080842090915290825290205481565b61033861081a3660046124fd565b6116ed565b6104dc6116f8565b600080610832610ae1565b90508060095461084291906129c4565b91505090565b60008054610855906129d7565b80601f0160208091040260200160405190810160405280929190818152602001828054610881906129d7565b80156108ce5780601f106108a3576101008083540402835291602001916108ce565b820191906000526020600020905b8154815290600101906020018083116108b157829003601f168201915b505050505081565b60025460009080156108fa576108f56108ed610827565b849083611708565b6108fc565b825b9392505050565b33600081815260046020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259061096b9086815260200190565b60405180910390a35060015b92915050565b60025460009080156108fa576108f581610995610827565b859190611744565b73ffffffffffffffffffffffffffffffffffffffff831660009081526004602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610a31576109ff8382612a2a565b73ffffffffffffffffffffffffffffffffffffffff861660009081526004602090815260408083203384529091529020555b73ffffffffffffffffffffffffffffffffffffffff851660009081526003602052604081208054859290610a66908490612a2a565b909155505073ffffffffffffffffffffffffffffffffffffffff808516600081815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90610ace9087815260200190565b60405180910390a3506001949350505050565b6040805160608101825260065464ffffffffff8082168352650100000000009091041660208201526007547affffffffffffffffffffffffffffffffffffffffffffffffffffff16918101919091526008546000919082610b4142611788565b90506000836000015164ffffffffff168264ffffffffff1611610b7457610b6f8364ffffffffff8416612a2a565b610b88565b8351610b8890849064ffffffffff16612a2a565b9050610b948482610bf8565b94505050505090565b60007f00000000000000000000000000000000000000000000000000000000000000014614610bd357610bce6117a0565b905090565b507f653292c7ceaf13cbb31c937d332031350dd6845e20d442b1942daad1ba5cb0ad90565b6000610c04838361183a565b90506000670de0b6b3a764000060095484600c54610c229190612a3d565b610c2c9190612a3d565b610c369190612a54565b905080821115610c44578091505b5092915050565b610c5361188b565b610c5c81611894565b50565b610c6761188b565b610c6f6116dc565b67ffffffffffffffff811115610c8a575067ffffffffffffffff5b600c5460408051918252602082018390527f05d530f0fd6974b7d995fd3b71870f5301bb9fe086180bdd0bd36526728f5c6b910160405180910390a1600c55565b6000610977826108d6565b610cde61188b565b610ce661190b565b610cf06000611894565b610cfa6000611914565b565b6000610d066116dc565b6108fc83836119a2565b60008085610d1e5788610d40565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b6040517fd505accf000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018290526064810189905260ff8716608482015260a4810186905260c481018590529091507f000000000000000000000000cacd6fd266af91b8aed52accc382b4e165586e2973ffffffffffffffffffffffffffffffffffffffff169063d505accf9060e401600060405180830381600087803b158015610df557600080fd5b505af1158015610e09573d6000803e3d6000fd5b50505050610e178989610cfc565b9998505050505050505050565b60408051606081018252600080825260208201819052918101919091526040805160608101825260065464ffffffffff808216808452650100000000009092041660208301526007547affffffffffffffffffffffffffffffffffffffffffffffffffffff16928201929092529042908111610ea05750919050565b6009546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000919073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000cacd6fd266af91b8aed52accc382b4e165586e2916906370a0823190602401602060405180830381865afa158015610f31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f559190612a8f565b610f5f9190612a2a565b90506000610fab7f0000000000000000000000000000000000000000000000000000000000093a8080610f9281876129c4565b610f9c9190612a54565b610fa69190612a3d565b611788565b9050610fd860287f0000000000000000000000000000000000000000000000000000000000093a80612a54565b610fe98464ffffffffff8416612a2a565b1015611025576110187f0000000000000000000000000000000000000000000000000000000000093a80611788565b6110229082612aa8565b90505b61102e82611ac2565b7affffffffffffffffffffffffffffffffffffffffffffffffffffff16604085015261105983611788565b64ffffffffff9081166020860152168352509092915050565b600061107c6116dc565b6108fc8383611aec565b60018054610855906129d7565b6000610bce7f0000000000000000000000000000000000000000000000000de0b6b3a76400006108d6565b336000908152600360205260408120805483919083906110df908490612a2a565b909155505073ffffffffffffffffffffffffffffffffffffffff8316600081815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9061096b9086815260200190565b60025460009080156108fa576108f561115a610827565b849083611744565b600061116c6116dc565b611177848484611ba2565b949350505050565b60006111896116dc565b611177848484611d0f565b600d54156111ce576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d80549060006111de83612ac6565b90915550600090506111f08582612b4f565b5060016111fd8482612b4f565b50600c829055600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831617905561124b611ee6565b611253612017565b5050505050565b600d54600114611296576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d80549060006112a683612ac6565b9190505550670de0b6b3a7640000600254866112c29190612a3d565b6112cc9190612a54565b600955600c939093556006805464ffffffffff92831665010000000000027fffffffffffffffffffffffffffffffffffffffffffff000000000000000000009091169390921692909217179055600780547affffffffffffffffffffffffffffffffffffffffffffffffffffff9092167fffffffffff00000000000000000000000000000000000000000000000000000090921691909117905550565b60025460009080156108fa576108f581611381610827565b859190611708565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260036020526040812054610977906108d6565b42841015611427576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f5045524d49545f444541444c494e455f4558504952454400000000000000000060448201526064015b60405180910390fd5b60006001611433610b9d565b73ffffffffffffffffffffffffffffffffffffffff8a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e0830190915280519201919091207f190100000000000000000000000000000000000000000000000000000000000061010083015261010282019290925261012281019190915261014201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015611585573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff81161580159061160057508773ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b611666576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f494e56414c49445f5349474e4552000000000000000000000000000000000000604482015260640161141e565b73ffffffffffffffffffffffffffffffffffffffff90811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b6116e4612017565b50610cfa611ee6565b600061097782611369565b61170061190b565b610cfa61207b565b6000827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048411830215820261173d57600080fd5b5091020490565b6000827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048411830215820261177957600080fd5b50910281810615159190040190565b600065010000000000821061179c57600080fd5b5090565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60006040516117d29190612c69565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6020820151825160009161184d91612cfd565b64ffffffffff168284604001517affffffffffffffffffffffffffffffffffffffffffffffffffffff166118819190612a3d565b6108fc9190612a54565b610cfa336120ac565b600a80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217909255600b54604051919216907f162998b90abc2507f3953aa797827b03a14c42dbd9a35f09feaf02e0d592773a90600090a350565b610cfa33612124565b600b5460405173ffffffffffffffffffffffffffffffffffffffff8084169216907f31b6c5a04b069b6ec1b3cef44c4e7c1eadd721349cda9823d0b1877b3551cdc690600090a3600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60006119ad836116ed565b905080600003611a19576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f534841524553000000000000000000000000000000000000000000604482015260640161141e565b611a5b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000cacd6fd266af91b8aed52accc382b4e165586e291633308661219c565b611a658282612287565b604080518481526020810183905273ffffffffffffffffffffffffffffffffffffffff84169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a36109778382612300565b60007b01000000000000000000000000000000000000000000000000000000821061179c57600080fd5b6000611af783611143565b9050611b3b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000cacd6fd266af91b8aed52accc382b4e165586e291633308461219c565b611b458284612287565b604080518281526020810185905273ffffffffffffffffffffffffffffffffffffffff84169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a36109778184612300565b6000611bad8461097d565b90503373ffffffffffffffffffffffffffffffffffffffff831614611c625773ffffffffffffffffffffffffffffffffffffffff821660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114611c6057611c2e8282612a2a565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020555b505b611c6c848261231b565b611c76828261232d565b604080518581526020810183905273ffffffffffffffffffffffffffffffffffffffff808516929086169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a46108fc73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000cacd6fd266af91b8aed52accc382b4e165586e291684866123bb565b60003373ffffffffffffffffffffffffffffffffffffffff831614611dc45773ffffffffffffffffffffffffffffffffffffffff821660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114611dc257611d908582612a2a565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020555b505b611dcd84610ccb565b905080600003611e39576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f415353455453000000000000000000000000000000000000000000604482015260640161141e565b611e43818561231b565b611e4d828561232d565b604080518281526020810186905273ffffffffffffffffffffffffffffffffffffffff808516929086169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a46108fc73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000cacd6fd266af91b8aed52accc382b4e165586e291684836123bb565b6000611ef0610e24565b9050806020015164ffffffffff16611f0742611788565b64ffffffffff16148015611f355750602081015160065465010000000000900464ffffffffff908116911614155b15610c5c5780516006805460208085015164ffffffffff9485167fffffffffffffffffffffffffffffffffffffffffffff000000000000000000009093168317650100000000009590911694850217909255604080850151600780547fffffffffff000000000000000000000000000000000000000000000000000000167affffffffffffffffffffffffffffffffffffffffffffffffffffff9092169182179055815192835292820193909352918201527fc32a546ed958490e37f30335e501e0a39438cb650a4851bfd4b775490af29dad9060600160405180910390a150565b6000612021610ae1565b9050801561207457806009600082825461203b91906129c4565b90915550506040518181527fb9d196a585c1a894f648393ec7d52cc59ff6d94191579d073ba32b0a74d7f7a69060200160405180910390a15b4260085590565b600a80547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055610cfa33611914565b600b5473ffffffffffffffffffffffffffffffffffffffff828116911614610c5c57600b546040517f443dc2b400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9182166004820152908216602482015260440161141e565b600a5473ffffffffffffffffffffffffffffffffffffffff828116911614610c5c57600a546040517fbe5a953700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9182166004820152908216602482015260440161141e565b60006040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff841660248201528260448201526020600060648360008a5af13d15601f3d1160016000511416171691505080611253576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c4544000000000000000000000000604482015260640161141e565b806002600082825461229991906129c4565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a35050565b816009600082825461231291906129c4565b90915550505050565b81600960008282546123129190612a2a565b73ffffffffffffffffffffffffffffffffffffffff821660009081526003602052604081208054839290612362908490612a2a565b909155505060028054829003905560405181815260009073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020016122f4565b60006040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152826024820152602060006044836000895af13d15601f3d116001600051141617169150508061248a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c45440000000000000000000000000000000000604482015260640161141e565b50505050565b60006020808352835180602085015260005b818110156124be578581018301518582016040015282016124a2565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b60006020828403121561250f57600080fd5b5035919050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461253a57600080fd5b919050565b6000806040838503121561255257600080fd5b61255b83612516565b946020939093013593505050565b60008060006060848603121561257e57600080fd5b61258784612516565b925061259560208501612516565b9150604084013590509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b803564ffffffffff8116811461253a57600080fd5b80357affffffffffffffffffffffffffffffffffffffffffffffffffffff8116811461253a57600080fd5b600080828403608081121561262857600080fd5b606081121561263657600080fd5b506040516060810181811067ffffffffffffffff8211171561265a5761265a6125a5565b604052612666846125d4565b8152612674602085016125d4565b6020820152612685604085016125e9565b6040820152946060939093013593505050565b6000602082840312156126aa57600080fd5b6108fc82612516565b600080604083850312156126c657600080fd5b823591506126d660208401612516565b90509250929050565b803560ff8116811461253a57600080fd5b600080600080600080600060e0888a03121561270b57600080fd5b8735965061271b60208901612516565b9550604088013594506060880135801515811461273757600080fd5b9350612745608089016126df565b925060a0880135915060c0880135905092959891949750929550565b60008060006060848603121561277657600080fd5b8335925061278660208501612516565b915061279460408501612516565b90509250925092565b600082601f8301126127ae57600080fd5b813567ffffffffffffffff808211156127c9576127c96125a5565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561280f5761280f6125a5565b8160405283815286602085880101111561282857600080fd5b836020870160208301376000602085830101528094505050505092915050565b6000806000806080858703121561285e57600080fd5b843567ffffffffffffffff8082111561287657600080fd5b6128828883890161279d565b9550602087013591508082111561289857600080fd5b506128a58782880161279d565b935050604085013591506128bb60608601612516565b905092959194509250565b600080600080600060a086880312156128de57600080fd5b85359450602086013593506128f5604087016125d4565b9250612903606087016125d4565b9150612911608087016125e9565b90509295509295909350565b600080600080600080600060e0888a03121561293857600080fd5b61294188612516565b965061294f60208901612516565b95506040880135945060608801359350612745608089016126df565b6000806040838503121561297e57600080fd5b61298783612516565b91506126d660208401612516565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082018082111561097757610977612995565b600181811c908216806129eb57607f821691505b602082108103612a24577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b8181038181111561097757610977612995565b808202811582820484141761097757610977612995565b600082612a8a577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600060208284031215612aa157600080fd5b5051919050565b64ffffffffff818116838216019080821115610c4457610c44612995565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203612af757612af7612995565b5060010190565b601f821115612b4a576000816000526020600020601f850160051c81016020861015612b275750805b601f850160051c820191505b81811015612b4657828155600101612b33565b5050505b505050565b815167ffffffffffffffff811115612b6957612b696125a5565b612b7d81612b7784546129d7565b84612afe565b602080601f831160018114612bd05760008415612b9a5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555612b46565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015612c1d57888601518255948401946001909101908401612bfe565b5085821015612c5957878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b6000808354612c77816129d7565b60018281168015612c8f5760018114612cc257612cf1565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0084168752821515830287019450612cf1565b8760005260208060002060005b85811015612ce85781548a820152908401908201612ccf565b50505082870194505b50929695505050505050565b64ffffffffff828116828216039080821115610c4457610c4461299556
Verified Source Code Partial Match
Compiler: v0.8.25+commit.b61c2a91
EVM: london
Optimization: Yes (777777 runs)
StakedFrxUSD.sol 140 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.21;
// ====================================================================
// | ______ _______ |
// | / _____________ __ __ / ____(_____ ____ _____ ________ |
// | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
// | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
// | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
// | |
// ====================================================================
// =========================== StakedFrxUSD ===========================
// ====================================================================
// Frax Finance: https://github.com/FraxFinance
import { Timelock2Step } from "frax-std/access-control/v2/Timelock2Step.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { SafeCastLib } from "solmate/utils/SafeCastLib.sol";
import { LinearRewardsErc4626, ERC20 } from "./LinearRewardsErc4626.sol";
/// @title Staked frxUSD
/// @notice A ERC4626 Vault implementation with linear rewards, rewards can be capped
contract StakedFrxUSD is LinearRewardsErc4626, Timelock2Step {
using SafeCastLib for *;
/// @notice The maximum amount of rewards that can be distributed per second per 1e18 asset
uint256 public maxDistributionPerSecondPerAsset;
uint256 private initializeStage=2;
/// @param _underlying The erc20 asset deposited
/// @param _name The name of the vault
/// @param _symbol The symbol of the vault
/// @param _rewardsCycleLength The length of the rewards cycle in seconds
/// @param _maxDistributionPerSecondPerAsset The maximum amount of rewards that can be distributed per second per 1e18 asset
/// @param _timelockAddress The address of the timelock/owner contract
constructor(
IERC20 _underlying,
string memory _name,
string memory _symbol,
uint32 _rewardsCycleLength,
uint256 _maxDistributionPerSecondPerAsset,
address _timelockAddress
)
LinearRewardsErc4626(ERC20(address(_underlying)), _name, _symbol, _rewardsCycleLength)
Timelock2Step(_timelockAddress)
{
maxDistributionPerSecondPerAsset = _maxDistributionPerSecondPerAsset;
}
/// @notice The ```SetMaxDistributionPerSecondPerAsset``` event is emitted when the maxDistributionPerSecondPerAsset is set
/// @param oldMax The old maxDistributionPerSecondPerAsset value
/// @param newMax The new maxDistributionPerSecondPerAsset value
event SetMaxDistributionPerSecondPerAsset(uint256 oldMax, uint256 newMax);
error AlreadyInitialized();
function initialize(string memory _name,
string memory _symbol,
uint256 _maxDistributionPerSecondPerAsset,
address _timelockAddress) external {
if (initializeStage!=0) revert AlreadyInitialized();
initializeStage++;
name = _name;
symbol = _symbol;
maxDistributionPerSecondPerAsset = _maxDistributionPerSecondPerAsset;
timelockAddress = _timelockAddress;
// initialize rewardsCycleEnd value
// NOTE: normally distribution of rewards should be done prior to _syncRewards but in this case we know there are no users or rewards yet.
_syncRewards();
// initialize lastRewardsDistribution value
_distributeRewards();
}
/// @notice The ```initializeRewardsCycleData``` function initializes the rewards cycle data
/// @dev This function can only be called once
/// @param _pricePerShare The price per share
/// @param _maxDistributionPerSecondPerAsset The maximum amount of rewards that can be distributed per second per 1e18 asset
/// @param _cycleEnd The end of the rewards cycle
/// @param _lastSync The last sync time
/// @param _rewardCycleAmount The reward cycle amount
function initializeRewardsCycleData(
uint256 _pricePerShare,
uint256 _maxDistributionPerSecondPerAsset,
uint40 _cycleEnd,
uint40 _lastSync,
uint216 _rewardCycleAmount
) external {
if (initializeStage!=1) revert AlreadyInitialized();
initializeStage++;
storedTotalAssets = _pricePerShare*totalSupply/PRECISION;
maxDistributionPerSecondPerAsset = _maxDistributionPerSecondPerAsset;
rewardsCycleData.cycleEnd = _cycleEnd;
rewardsCycleData.lastSync = _lastSync;
rewardsCycleData.rewardCycleAmount = _rewardCycleAmount;
}
/// @notice The ```setMaxDistributionPerSecondPerAsset``` function sets the maxDistributionPerSecondPerAsset
/// @dev This function can only be called by the timelock, caps the value to type(uint64).max
/// @param _maxDistributionPerSecondPerAsset The maximum amount of rewards that can be distributed per second per 1e18 asset
function setMaxDistributionPerSecondPerAsset(uint256 _maxDistributionPerSecondPerAsset) external {
_requireSenderIsTimelock();
syncRewardsAndDistribution();
// NOTE: prevents bricking the contract via overflow
if (_maxDistributionPerSecondPerAsset > type(uint64).max) {
_maxDistributionPerSecondPerAsset = type(uint64).max;
}
emit SetMaxDistributionPerSecondPerAsset({
oldMax: maxDistributionPerSecondPerAsset,
newMax: _maxDistributionPerSecondPerAsset
});
maxDistributionPerSecondPerAsset = _maxDistributionPerSecondPerAsset;
}
/// @notice The ```calculateRewardsToDistribute``` function calculates the amount of rewards to distribute based on the rewards cycle data and the time passed
/// @param _rewardsCycleData The rewards cycle data
/// @param _deltaTime The time passed since the last rewards distribution
/// @return _rewardToDistribute The amount of rewards to distribute
function calculateRewardsToDistribute(
RewardsCycleData memory _rewardsCycleData,
uint256 _deltaTime
) public view override returns (uint256 _rewardToDistribute) {
_rewardToDistribute = super.calculateRewardsToDistribute({
_rewardsCycleData: _rewardsCycleData,
_deltaTime: _deltaTime
});
// Cap rewards
uint256 _maxDistribution = (maxDistributionPerSecondPerAsset * _deltaTime * storedTotalAssets) / PRECISION;
if (_rewardToDistribute > _maxDistribution) {
_rewardToDistribute = _maxDistribution;
}
}
}
Timelock2Step.sol 161 lines
// SPDX-License-Identifier: ISC
pragma solidity >=0.8.0;
// ====================================================================
// | ______ _______ |
// | / _____________ __ __ / ____(_____ ____ _____ ________ |
// | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
// | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
// | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
// | |
// ====================================================================
// ========================== Timelock2Step ===========================
// ====================================================================
// Frax Finance: https://github.com/FraxFinance
// Primary Author
// Drake Evans: https://github.com/DrakeEvans
// Reviewers
// Dennis: https://github.com/denett
// ====================================================================
/// @title Timelock2Step
/// @author Drake Evans (Frax Finance) https://github.com/drakeevans
/// @dev Inspired by OpenZeppelin's Ownable2Step contract
/// @notice An abstract contract which contains 2-step transfer and renounce logic for a timelock address
abstract contract Timelock2Step {
/// @notice The pending timelock address
address public pendingTimelockAddress;
/// @notice The current timelock address
address public timelockAddress;
constructor(address _timelockAddress) {
timelockAddress = _timelockAddress;
}
// ============================================================================================
// Functions: External Functions
// ============================================================================================
/// @notice The ```transferTimelock``` function initiates the timelock transfer
/// @dev Must be called by the current timelock
/// @param _newTimelock The address of the nominated (pending) timelock
function transferTimelock(address _newTimelock) external virtual {
_requireSenderIsTimelock();
_transferTimelock(_newTimelock);
}
/// @notice The ```acceptTransferTimelock``` function completes the timelock transfer
/// @dev Must be called by the pending timelock
function acceptTransferTimelock() external virtual {
_requireSenderIsPendingTimelock();
_acceptTransferTimelock();
}
/// @notice The ```renounceTimelock``` function renounces the timelock after setting pending timelock to current timelock
/// @dev Pending timelock must be set to current timelock before renouncing, creating a 2-step renounce process
function renounceTimelock() external virtual {
_requireSenderIsTimelock();
_requireSenderIsPendingTimelock();
_transferTimelock(address(0));
_setTimelock(address(0));
}
// ============================================================================================
// Functions: Internal Actions
// ============================================================================================
/// @notice The ```_transferTimelock``` function initiates the timelock transfer
/// @dev This function is to be implemented by a public function
/// @param _newTimelock The address of the nominated (pending) timelock
function _transferTimelock(address _newTimelock) internal {
pendingTimelockAddress = _newTimelock;
emit TimelockTransferStarted(timelockAddress, _newTimelock);
}
/// @notice The ```_acceptTransferTimelock``` function completes the timelock transfer
/// @dev This function is to be implemented by a public function
function _acceptTransferTimelock() internal {
pendingTimelockAddress = address(0);
_setTimelock(msg.sender);
}
/// @notice The ```_setTimelock``` function sets the timelock address
/// @dev This function is to be implemented by a public function
/// @param _newTimelock The address of the new timelock
function _setTimelock(address _newTimelock) internal {
emit TimelockTransferred(timelockAddress, _newTimelock);
timelockAddress = _newTimelock;
}
// ============================================================================================
// Functions: Internal Checks
// ============================================================================================
/// @notice The ```_isTimelock``` function checks if _address is current timelock address
/// @param _address The address to check against the timelock
/// @return Whether or not msg.sender is current timelock address
function _isTimelock(address _address) internal view returns (bool) {
return _address == timelockAddress;
}
/// @notice The ```_requireIsTimelock``` function reverts if _address is not current timelock address
/// @param _address The address to check against the timelock
function _requireIsTimelock(address _address) internal view {
if (!_isTimelock(_address)) revert AddressIsNotTimelock(timelockAddress, _address);
}
/// @notice The ```_requireSenderIsTimelock``` function reverts if msg.sender is not current timelock address
/// @dev This function is to be implemented by a public function
function _requireSenderIsTimelock() internal view {
_requireIsTimelock(msg.sender);
}
/// @notice The ```_isPendingTimelock``` function checks if the _address is pending timelock address
/// @dev This function is to be implemented by a public function
/// @param _address The address to check against the pending timelock
/// @return Whether or not _address is pending timelock address
function _isPendingTimelock(address _address) internal view returns (bool) {
return _address == pendingTimelockAddress;
}
/// @notice The ```_requireIsPendingTimelock``` function reverts if the _address is not pending timelock address
/// @dev This function is to be implemented by a public function
/// @param _address The address to check against the pending timelock
function _requireIsPendingTimelock(address _address) internal view {
if (!_isPendingTimelock(_address)) revert AddressIsNotPendingTimelock(pendingTimelockAddress, _address);
}
/// @notice The ```_requirePendingTimelock``` function reverts if msg.sender is not pending timelock address
/// @dev This function is to be implemented by a public function
function _requireSenderIsPendingTimelock() internal view {
_requireIsPendingTimelock(msg.sender);
}
// ============================================================================================
// Functions: Events
// ============================================================================================
/// @notice The ```TimelockTransferStarted``` event is emitted when the timelock transfer is initiated
/// @param previousTimelock The address of the previous timelock
/// @param newTimelock The address of the new timelock
event TimelockTransferStarted(address indexed previousTimelock, address indexed newTimelock);
/// @notice The ```TimelockTransferred``` event is emitted when the timelock transfer is completed
/// @param previousTimelock The address of the previous timelock
/// @param newTimelock The address of the new timelock
event TimelockTransferred(address indexed previousTimelock, address indexed newTimelock);
// ============================================================================================
// Functions: Errors
// ============================================================================================
/// @notice Emitted when timelock is transferred
error AddressIsNotTimelock(address timelockAddress, address actualAddress);
/// @notice Emitted when pending timelock is transferred
error AddressIsNotPendingTimelock(address pendingTimelockAddress, address actualAddress);
}
ERC20.sol 312 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC-20
* applications.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Skips emitting an {Approval} event indicating an allowance update. This is not
* required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
*
* ```solidity
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}
SafeCastLib.sol 193 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Safe unsigned integer casting library that reverts on overflow.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeCastLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeCast.sol)
library SafeCastLib {
function safeCastTo248(uint256 x) internal pure returns (uint248 y) {
require(x < 1 << 248);
y = uint248(x);
}
function safeCastTo240(uint256 x) internal pure returns (uint240 y) {
require(x < 1 << 240);
y = uint240(x);
}
function safeCastTo232(uint256 x) internal pure returns (uint232 y) {
require(x < 1 << 232);
y = uint232(x);
}
function safeCastTo224(uint256 x) internal pure returns (uint224 y) {
require(x < 1 << 224);
y = uint224(x);
}
function safeCastTo216(uint256 x) internal pure returns (uint216 y) {
require(x < 1 << 216);
y = uint216(x);
}
function safeCastTo208(uint256 x) internal pure returns (uint208 y) {
require(x < 1 << 208);
y = uint208(x);
}
function safeCastTo200(uint256 x) internal pure returns (uint200 y) {
require(x < 1 << 200);
y = uint200(x);
}
function safeCastTo192(uint256 x) internal pure returns (uint192 y) {
require(x < 1 << 192);
y = uint192(x);
}
function safeCastTo184(uint256 x) internal pure returns (uint184 y) {
require(x < 1 << 184);
y = uint184(x);
}
function safeCastTo176(uint256 x) internal pure returns (uint176 y) {
require(x < 1 << 176);
y = uint176(x);
}
function safeCastTo168(uint256 x) internal pure returns (uint168 y) {
require(x < 1 << 168);
y = uint168(x);
}
function safeCastTo160(uint256 x) internal pure returns (uint160 y) {
require(x < 1 << 160);
y = uint160(x);
}
function safeCastTo152(uint256 x) internal pure returns (uint152 y) {
require(x < 1 << 152);
y = uint152(x);
}
function safeCastTo144(uint256 x) internal pure returns (uint144 y) {
require(x < 1 << 144);
y = uint144(x);
}
function safeCastTo136(uint256 x) internal pure returns (uint136 y) {
require(x < 1 << 136);
y = uint136(x);
}
function safeCastTo128(uint256 x) internal pure returns (uint128 y) {
require(x < 1 << 128);
y = uint128(x);
}
function safeCastTo120(uint256 x) internal pure returns (uint120 y) {
require(x < 1 << 120);
y = uint120(x);
}
function safeCastTo112(uint256 x) internal pure returns (uint112 y) {
require(x < 1 << 112);
y = uint112(x);
}
function safeCastTo104(uint256 x) internal pure returns (uint104 y) {
require(x < 1 << 104);
y = uint104(x);
}
function safeCastTo96(uint256 x) internal pure returns (uint96 y) {
require(x < 1 << 96);
y = uint96(x);
}
function safeCastTo88(uint256 x) internal pure returns (uint88 y) {
require(x < 1 << 88);
y = uint88(x);
}
function safeCastTo80(uint256 x) internal pure returns (uint80 y) {
require(x < 1 << 80);
y = uint80(x);
}
function safeCastTo72(uint256 x) internal pure returns (uint72 y) {
require(x < 1 << 72);
y = uint72(x);
}
function safeCastTo64(uint256 x) internal pure returns (uint64 y) {
require(x < 1 << 64);
y = uint64(x);
}
function safeCastTo56(uint256 x) internal pure returns (uint56 y) {
require(x < 1 << 56);
y = uint56(x);
}
function safeCastTo48(uint256 x) internal pure returns (uint48 y) {
require(x < 1 << 48);
y = uint48(x);
}
function safeCastTo40(uint256 x) internal pure returns (uint40 y) {
require(x < 1 << 40);
y = uint40(x);
}
function safeCastTo32(uint256 x) internal pure returns (uint32 y) {
require(x < 1 << 32);
y = uint32(x);
}
function safeCastTo24(uint256 x) internal pure returns (uint24 y) {
require(x < 1 << 24);
y = uint24(x);
}
function safeCastTo16(uint256 x) internal pure returns (uint16 y) {
require(x < 1 << 16);
y = uint16(x);
}
function safeCastTo8(uint256 x) internal pure returns (uint8 y) {
require(x < 1 << 8);
y = uint8(x);
}
}
LinearRewardsErc4626.sol 280 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.21;
// ====================================================================
// | ______ _______ |
// | / _____________ __ __ / ____(_____ ____ _____ ________ |
// | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
// | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
// | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
// | |
// ====================================================================
// ======================== LinearRewardsErc4626 ======================
// ====================================================================
// Frax Finance: https://github.com/FraxFinance
import { ERC20, ERC4626 } from "solmate/mixins/ERC4626.sol";
import { SafeCastLib } from "solmate/utils/SafeCastLib.sol";
/// @title LinearRewardsErc4626
/// @notice An ERC4626 Vault implementation with linear rewards
abstract contract LinearRewardsErc4626 is ERC4626 {
using SafeCastLib for *;
/// @notice The precision of all integer calculations
uint256 public constant PRECISION = 1e18;
/// @notice The rewards cycle length in seconds
uint256 public immutable REWARDS_CYCLE_LENGTH;
/// @notice Information about the current rewards cycle
struct RewardsCycleData {
uint40 cycleEnd; // Timestamp of the end of the current rewards cycle
uint40 lastSync; // Timestamp of the last time the rewards cycle was synced
uint216 rewardCycleAmount; // Amount of rewards to be distributed in the current cycle
}
/// @notice The rewards cycle data, stored in a single word to save gas
RewardsCycleData public rewardsCycleData;
/// @notice The timestamp of the last time rewards were distributed
uint256 public lastRewardsDistribution;
/// @notice The total amount of assets that have been distributed and deposited
uint256 public storedTotalAssets;
/// @notice The precision of the underlying asset
uint256 public immutable UNDERLYING_PRECISION;
/// @param _underlying The erc20 asset deposited
/// @param _name The name of the vault
/// @param _symbol The symbol of the vault
/// @param _rewardsCycleLength The length of the rewards cycle in seconds
constructor(
ERC20 _underlying,
string memory _name,
string memory _symbol,
uint256 _rewardsCycleLength
) ERC4626(_underlying, _name, _symbol) {
REWARDS_CYCLE_LENGTH = _rewardsCycleLength;
UNDERLYING_PRECISION = 10 ** _underlying.decimals();
// initialize rewardsCycleEnd value
// NOTE: normally distribution of rewards should be done prior to _syncRewards but in this case we know there are no users or rewards yet.
_syncRewards();
// initialize lastRewardsDistribution value
_distributeRewards();
}
function pricePerShare() external view returns (uint256 _pricePerShare) {
_pricePerShare = convertToAssets(UNDERLYING_PRECISION);
}
/// @notice The ```calculateRewardsToDistribute``` function calculates the amount of rewards to distribute based on the rewards cycle data and the time elapsed
/// @param _rewardsCycleData The rewards cycle data
/// @param _deltaTime The time elapsed since the last rewards distribution
/// @return _rewardToDistribute The amount of rewards to distribute
function calculateRewardsToDistribute(
RewardsCycleData memory _rewardsCycleData,
uint256 _deltaTime
) public view virtual returns (uint256 _rewardToDistribute) {
_rewardToDistribute =
(_rewardsCycleData.rewardCycleAmount * _deltaTime) /
(_rewardsCycleData.cycleEnd - _rewardsCycleData.lastSync);
}
/// @notice The ```previewDistributeRewards``` function is used to preview the rewards distributed at the top of the block
/// @return _rewardToDistribute The amount of underlying to distribute
function previewDistributeRewards() public view virtual returns (uint256 _rewardToDistribute) {
// Cache state for gas savings
RewardsCycleData memory _rewardsCycleData = rewardsCycleData;
uint256 _lastRewardsDistribution = lastRewardsDistribution;
uint40 _timestamp = block.timestamp.safeCastTo40();
// Calculate the delta time, but only include up to the cycle end in case we are passed it
uint256 _deltaTime = _timestamp > _rewardsCycleData.cycleEnd
? _rewardsCycleData.cycleEnd - _lastRewardsDistribution
: _timestamp - _lastRewardsDistribution;
// Calculate the rewards to distribute
_rewardToDistribute = calculateRewardsToDistribute({
_rewardsCycleData: _rewardsCycleData,
_deltaTime: _deltaTime
});
}
/// @notice The ```distributeRewards``` function distributes the rewards once per block
/// @return _rewardToDistribute The amount of underlying to distribute
function _distributeRewards() internal virtual returns (uint256 _rewardToDistribute) {
_rewardToDistribute = previewDistributeRewards();
// Only write to state/emit if we actually distribute rewards
if (_rewardToDistribute != 0) {
storedTotalAssets += _rewardToDistribute;
emit DistributeRewards({ rewardsToDistribute: _rewardToDistribute });
}
lastRewardsDistribution = block.timestamp;
}
/// @notice The ```previewSyncRewards``` function returns the updated rewards cycle data without updating the state
/// @return _newRewardsCycleData The updated rewards cycle data
function previewSyncRewards() public view virtual returns (RewardsCycleData memory _newRewardsCycleData) {
RewardsCycleData memory _rewardsCycleData = rewardsCycleData;
uint256 _timestamp = block.timestamp;
// Only sync if the previous cycle has ended
if (_timestamp <= _rewardsCycleData.cycleEnd) return _rewardsCycleData;
// Calculate rewards for next cycle
uint256 _newRewards = asset.balanceOf(address(this)) - storedTotalAssets;
// Calculate the next cycle end, this keeps cycles at the same time regardless of when sync is called
uint40 _cycleEnd = (((_timestamp + REWARDS_CYCLE_LENGTH) / REWARDS_CYCLE_LENGTH) * REWARDS_CYCLE_LENGTH)
.safeCastTo40();
// This block prevents big jumps in rewards rate in case the sync happens near the end of the cycle
if (_cycleEnd - _timestamp < REWARDS_CYCLE_LENGTH / 40) {
_cycleEnd += REWARDS_CYCLE_LENGTH.safeCastTo40();
}
// Write return values
_rewardsCycleData.rewardCycleAmount = _newRewards.safeCastTo216();
_rewardsCycleData.lastSync = _timestamp.safeCastTo40();
_rewardsCycleData.cycleEnd = _cycleEnd;
return _rewardsCycleData;
}
/// @notice The ```_syncRewards``` function is used to update the rewards cycle data
function _syncRewards() internal virtual {
RewardsCycleData memory _rewardsCycleData = previewSyncRewards();
if (
block
.timestamp
// If true, then preview shows a rewards should be processed
.safeCastTo40() ==
_rewardsCycleData.lastSync &&
// Ensures that we don't write to state twice in the same block
rewardsCycleData.lastSync != _rewardsCycleData.lastSync
) {
rewardsCycleData = _rewardsCycleData;
emit SyncRewards({
cycleEnd: _rewardsCycleData.cycleEnd,
lastSync: _rewardsCycleData.lastSync,
rewardCycleAmount: _rewardsCycleData.rewardCycleAmount
});
}
}
/// @notice The ```syncRewardsAndDistribution``` function is used to update the rewards cycle data and distribute rewards
/// @dev rewards must be distributed before the cycle is synced
function syncRewardsAndDistribution() public virtual {
_distributeRewards();
_syncRewards();
}
/// @notice The ```totalAssets``` function returns the total assets available in the vault
/// @dev This function simulates the rewards that will be distributed at the top of the block
/// @return _totalAssets The total assets available in the vault
function totalAssets() public view virtual override returns (uint256 _totalAssets) {
uint256 _rewardToDistribute = previewDistributeRewards();
_totalAssets = storedTotalAssets + _rewardToDistribute;
}
function afterDeposit(uint256 amount, uint256 shares) internal virtual override {
storedTotalAssets += amount;
}
/// @notice The ```deposit``` function allows a user to mint shares by depositing underlying
/// @param _assets The amount of underlying to deposit
/// @param _receiver The address to send the shares to
/// @return _shares The amount of shares minted
function deposit(uint256 _assets, address _receiver) public override returns (uint256 _shares) {
syncRewardsAndDistribution();
_shares = super.deposit({ assets: _assets, receiver: _receiver });
}
/// @notice The ```mint``` function allows a user to mint a given number of shares
/// @param _shares The amount of shares to mint
/// @param _receiver The address to send the shares to
/// @return _assets The amount of underlying deposited
function mint(uint256 _shares, address _receiver) public override returns (uint256 _assets) {
syncRewardsAndDistribution();
_assets = super.mint({ shares: _shares, receiver: _receiver });
}
function beforeWithdraw(uint256 amount, uint256 shares) internal virtual override {
storedTotalAssets -= amount;
}
/// @notice The ```withdraw``` function allows a user to withdraw a given amount of underlying
/// @param _assets The amount of underlying to withdraw
/// @param _receiver The address to send the underlying to
/// @param _owner The address of the owner of the shares
/// @return _shares The amount of shares burned
function withdraw(uint256 _assets, address _receiver, address _owner) public override returns (uint256 _shares) {
syncRewardsAndDistribution();
_shares = super.withdraw({ assets: _assets, receiver: _receiver, owner: _owner });
}
/// @notice The ```redeem``` function allows a user to redeem their shares for underlying
/// @param _shares The amount of shares to redeem
/// @param _receiver The address to send the underlying to
/// @param _owner The address of the owner of the shares
/// @return _assets The amount of underlying redeemed
function redeem(uint256 _shares, address _receiver, address _owner) public override returns (uint256 _assets) {
syncRewardsAndDistribution();
_assets = super.redeem({ shares: _shares, receiver: _receiver, owner: _owner });
}
/// @notice The ```depositWithSignature``` function allows a user to use signed approvals to deposit
/// @param _assets The amount of underlying to deposit
/// @param _receiver The address to send the shares to
/// @param _deadline The deadline for the signature
/// @param _approveMax Whether or not to approve the maximum amount
/// @param _v The v value of the signature
/// @param _r The r value of the signature
/// @param _s The s value of the signature
/// @return _shares The amount of shares minted
function depositWithSignature(
uint256 _assets,
address _receiver,
uint256 _deadline,
bool _approveMax,
uint8 _v,
bytes32 _r,
bytes32 _s
) external returns (uint256 _shares) {
uint256 _amount = _approveMax ? type(uint256).max : _assets;
asset.permit({
owner: msg.sender,
spender: address(this),
value: _amount,
deadline: _deadline,
v: _v,
r: _r,
s: _s
});
_shares = (deposit({ _assets: _assets, _receiver: _receiver }));
}
//==============================================================================
// Events
//==============================================================================
/// @notice The ```SyncRewards``` event is emitted when the rewards cycle is synced
/// @param cycleEnd The timestamp of the end of the current rewards cycle
/// @param lastSync The timestamp of the last time the rewards cycle was synced
/// @param rewardCycleAmount The amount of rewards to be distributed in the current cycle
event SyncRewards(uint40 cycleEnd, uint40 lastSync, uint216 rewardCycleAmount);
/// @notice The ```DistributeRewards``` event is emitted when rewards are distributed to storedTotalAssets
/// @param rewardsToDistribute The amount of rewards that were distributed
event DistributeRewards(uint256 rewardsToDistribute);
}
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
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 value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` 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 value) external returns (bool);
}
IERC20Metadata.sol 26 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC-20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
Context.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @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;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
draft-IERC6093.sol 161 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC-20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC-721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC-1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}
ERC4626.sol 183 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
import {SafeTransferLib} from "../utils/SafeTransferLib.sol";
import {FixedPointMathLib} from "../utils/FixedPointMathLib.sol";
/// @notice Minimal ERC4626 tokenized Vault implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/mixins/ERC4626.sol)
abstract contract ERC4626 is ERC20 {
using SafeTransferLib for ERC20;
using FixedPointMathLib for uint256;
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);
event Withdraw(
address indexed caller,
address indexed receiver,
address indexed owner,
uint256 assets,
uint256 shares
);
/*//////////////////////////////////////////////////////////////
IMMUTABLES
//////////////////////////////////////////////////////////////*/
ERC20 public immutable asset;
constructor(
ERC20 _asset,
string memory _name,
string memory _symbol
) ERC20(_name, _symbol, _asset.decimals()) {
asset = _asset;
}
/*//////////////////////////////////////////////////////////////
DEPOSIT/WITHDRAWAL LOGIC
//////////////////////////////////////////////////////////////*/
function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) {
// Check for rounding error since we round down in previewDeposit.
require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES");
// Need to transfer before minting or ERC777s could reenter.
asset.safeTransferFrom(msg.sender, address(this), assets);
_mint(receiver, shares);
emit Deposit(msg.sender, receiver, assets, shares);
afterDeposit(assets, shares);
}
function mint(uint256 shares, address receiver) public virtual returns (uint256 assets) {
assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up.
// Need to transfer before minting or ERC777s could reenter.
asset.safeTransferFrom(msg.sender, address(this), assets);
_mint(receiver, shares);
emit Deposit(msg.sender, receiver, assets, shares);
afterDeposit(assets, shares);
}
function withdraw(
uint256 assets,
address receiver,
address owner
) public virtual returns (uint256 shares) {
shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up.
if (msg.sender != owner) {
uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
}
beforeWithdraw(assets, shares);
_burn(owner, shares);
emit Withdraw(msg.sender, receiver, owner, assets, shares);
asset.safeTransfer(receiver, assets);
}
function redeem(
uint256 shares,
address receiver,
address owner
) public virtual returns (uint256 assets) {
if (msg.sender != owner) {
uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
}
// Check for rounding error since we round down in previewRedeem.
require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS");
beforeWithdraw(assets, shares);
_burn(owner, shares);
emit Withdraw(msg.sender, receiver, owner, assets, shares);
asset.safeTransfer(receiver, assets);
}
/*//////////////////////////////////////////////////////////////
ACCOUNTING LOGIC
//////////////////////////////////////////////////////////////*/
function totalAssets() public view virtual returns (uint256);
function convertToShares(uint256 assets) public view virtual returns (uint256) {
uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.
return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets());
}
function convertToAssets(uint256 shares) public view virtual returns (uint256) {
uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.
return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply);
}
function previewDeposit(uint256 assets) public view virtual returns (uint256) {
return convertToShares(assets);
}
function previewMint(uint256 shares) public view virtual returns (uint256) {
uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.
return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply);
}
function previewWithdraw(uint256 assets) public view virtual returns (uint256) {
uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.
return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets());
}
function previewRedeem(uint256 shares) public view virtual returns (uint256) {
return convertToAssets(shares);
}
/*//////////////////////////////////////////////////////////////
DEPOSIT/WITHDRAWAL LIMIT LOGIC
//////////////////////////////////////////////////////////////*/
function maxDeposit(address) public view virtual returns (uint256) {
return type(uint256).max;
}
function maxMint(address) public view virtual returns (uint256) {
return type(uint256).max;
}
function maxWithdraw(address owner) public view virtual returns (uint256) {
return convertToAssets(balanceOf[owner]);
}
function maxRedeem(address owner) public view virtual returns (uint256) {
return balanceOf[owner];
}
/*//////////////////////////////////////////////////////////////
INTERNAL HOOKS LOGIC
//////////////////////////////////////////////////////////////*/
function beforeWithdraw(uint256 assets, uint256 shares) internal virtual {}
function afterDeposit(uint256 assets, uint256 shares) internal virtual {}
}
ERC20.sol 206 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
SafeTransferLib.sol 128 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferETH(address to, uint256 amount) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and store if it succeeded or not.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}
FixedPointMathLib.sol 255 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
/*//////////////////////////////////////////////////////////////
SIMPLIFIED FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
uint256 internal constant MAX_UINT256 = 2**256 - 1;
uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.
function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
}
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
}
function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
}
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
}
/*//////////////////////////////////////////////////////////////
LOW LEVEL FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
function mulDivDown(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// Divide x * y by the denominator.
z := div(mul(x, y), denominator)
}
}
function mulDivUp(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// If x * y modulo the denominator is strictly greater than 0,
// 1 is added to round up the division of x * y by the denominator.
z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
}
}
function rpow(
uint256 x,
uint256 n,
uint256 scalar
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
switch x
case 0 {
switch n
case 0 {
// 0 ** 0 = 1
z := scalar
}
default {
// 0 ** n = 0
z := 0
}
}
default {
switch mod(n, 2)
case 0 {
// If n is even, store scalar in z for now.
z := scalar
}
default {
// If n is odd, store x in z for now.
z := x
}
// Shifting right by 1 is like dividing by 2.
let half := shr(1, scalar)
for {
// Shift n right by 1 before looping to halve it.
n := shr(1, n)
} n {
// Shift n right by 1 each iteration to halve it.
n := shr(1, n)
} {
// Revert immediately if x ** 2 would overflow.
// Equivalent to iszero(eq(div(xx, x), x)) here.
if shr(128, x) {
revert(0, 0)
}
// Store x squared.
let xx := mul(x, x)
// Round to the nearest number.
let xxRound := add(xx, half)
// Revert if xx + half overflowed.
if lt(xxRound, xx) {
revert(0, 0)
}
// Set x to scaled xxRound.
x := div(xxRound, scalar)
// If n is even:
if mod(n, 2) {
// Compute z * x.
let zx := mul(z, x)
// If z * x overflowed:
if iszero(eq(div(zx, x), z)) {
// Revert if x is non-zero.
if iszero(iszero(x)) {
revert(0, 0)
}
}
// Round to the nearest number.
let zxRound := add(zx, half)
// Revert if zx + half overflowed.
if lt(zxRound, zx) {
revert(0, 0)
}
// Return properly scaled zxRound.
z := div(zxRound, scalar)
}
}
}
}
}
/*//////////////////////////////////////////////////////////////
GENERAL NUMBER UTILITIES
//////////////////////////////////////////////////////////////*/
function sqrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
let y := x // We start y at x, which will help us make our initial estimate.
z := 181 // The "correct" value is 1, but this saves a multiplication later.
// This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
// start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
// We check y >= 2^(k + 8) but shift right by k bits
// each branch to ensure that if x >= 256, then y >= 256.
if iszero(lt(y, 0x10000000000000000000000000000000000)) {
y := shr(128, y)
z := shl(64, z)
}
if iszero(lt(y, 0x1000000000000000000)) {
y := shr(64, y)
z := shl(32, z)
}
if iszero(lt(y, 0x10000000000)) {
y := shr(32, y)
z := shl(16, z)
}
if iszero(lt(y, 0x1000000)) {
y := shr(16, y)
z := shl(8, z)
}
// Goal was to get z*z*y within a small factor of x. More iterations could
// get y in a tighter range. Currently, we will have y in [256, 256*2^16).
// We ensured y >= 256 so that the relative difference between y and y+1 is small.
// That's not possible if x < 256 but we can just verify those cases exhaustively.
// Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
// Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
// Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.
// For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
// (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.
// Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
// sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.
// There is no overflow risk here since y < 2^136 after the first branch above.
z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.
// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
// If x+1 is a perfect square, the Babylonian method cycles between
// floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
// See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
// Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
// If you don't care whether the floor or ceil square root is returned, you can remove this statement.
z := sub(z, lt(div(x, z), z))
}
}
function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Mod x by y. Note this will return
// 0 instead of reverting if y is zero.
z := mod(x, y)
}
}
function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
// Divide x by y. Note this will return
// 0 instead of reverting if y is zero.
r := div(x, y)
}
}
function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Add 1 to x * y if x % y > 0. Note this will
// return 0 instead of reverting if y is zero.
z := add(gt(mod(x, y), 0), div(x, y))
}
}
}
Read Contract
DOMAIN_SEPARATOR 0x3644e515 → bytes32
PRECISION 0xaaf5eb68 → uint256
REWARDS_CYCLE_LENGTH 0x56caf605 → uint256
UNDERLYING_PRECISION 0xa80e7547 → uint256
allowance 0xdd62ed3e → uint256
asset 0x38d52e0f → address
balanceOf 0x70a08231 → uint256
calculateRewardsToDistribute 0x2efc18c7 → uint256
convertToAssets 0x07a2d13a → uint256
convertToShares 0xc6e6f592 → uint256
decimals 0x313ce567 → uint8
lastRewardsDistribution 0xbd6f3603 → uint256
maxDeposit 0x402d267d → uint256
maxDistributionPerSecondPerAsset 0x2af98d6d → uint256
maxMint 0xc63d75b6 → uint256
maxRedeem 0xd905777e → uint256
maxWithdraw 0xce96cb77 → uint256
name 0x06fdde03 → string
nonces 0x7ecebe00 → uint256
pendingTimelockAddress 0x090f3f50 → address
previewDeposit 0xef8b30f7 → uint256
previewDistributeRewards 0x358245fc → uint256
previewMint 0xb3d7f6b9 → uint256
previewRedeem 0x4cdad506 → uint256
previewSyncRewards 0x8f765d59 → tuple
previewWithdraw 0x0a28a477 → uint256
pricePerShare 0x99530b06 → uint256
rewardsCycleData 0x5ebae566 → uint40, uint40, uint216
storedTotalAssets 0x61c1c5e9 → uint256
symbol 0x95d89b41 → string
timelockAddress 0x4bc66f32 → address
totalAssets 0x01e1d114 → uint256
totalSupply 0x18160ddd → uint256
Write Contract 16 functions
These functions modify contract state and require a wallet transaction to execute.
acceptTransferTimelock 0xf6ccaad4
No parameters
approve 0x095ea7b3
address spender
uint256 amount
returns: bool
deposit 0x6e553f65
uint256 _assets
address _receiver
returns: uint256
depositWithSignature 0x75e077c3
uint256 _assets
address _receiver
uint256 _deadline
bool _approveMax
uint8 _v
bytes32 _r
bytes32 _s
returns: uint256
initialize 0xbd3a13f6
string _name
string _symbol
uint256 _maxDistributionPerSecondPerAsset
address _timelockAddress
initializeRewardsCycleData 0xc2ef373a
uint256 _pricePerShare
uint256 _maxDistributionPerSecondPerAsset
uint40 _cycleEnd
uint40 _lastSync
uint216 _rewardCycleAmount
mint 0x94bf804d
uint256 _shares
address _receiver
returns: uint256
permit 0xd505accf
address owner
address spender
uint256 value
uint256 deadline
uint8 v
bytes32 r
bytes32 s
redeem 0xba087652
uint256 _shares
address _receiver
address _owner
returns: uint256
renounceTimelock 0x4f8b4ae7
No parameters
setMaxDistributionPerSecondPerAsset 0x48f76e0f
uint256 _maxDistributionPerSecondPerAsset
syncRewardsAndDistribution 0xd71356e6
No parameters
transfer 0xa9059cbb
address to
uint256 amount
returns: bool
transferFrom 0x23b872dd
address from
address to
uint256 amount
returns: bool
transferTimelock 0x45014095
address _newTimelock
withdraw 0xb460af94
uint256 _assets
address _receiver
address _owner
returns: uint256
Recent Transactions
This address has 1 on-chain transactions, but only 0.7% of the chain is indexed. Transactions will appear as indexing progresses. View on Etherscan →