Address Contract Verified
Address
0xcA71C36D26f515AD0cce1D806B231CBC1185CdfC
Balance
0 ETH
Nonce
1
Code Size
9327 bytes
Creator
0x0B5a3C04...89Fb at tx 0x6075552b...1e67a6
Indexed Transactions
0 (1 on-chain, 1.4% indexed)
Contract Bytecode
9327 bytes
0x60806040526004361061007b5760003560e01c8063b7c9c69f1161004e578063b7c9c69f14610141578063c8d78f5214610161578063f04f270714610181578063f1298ed7146101a157600080fd5b806323e30c8b146100805780633fbeae9f146100b357806360ac8fe6146100d55780637b103999146100f5575b600080fd5b34801561008c57600080fd5b506100a061009b3660046117f2565b6101b4565b6040519081526020015b60405180910390f35b3480156100bf57600080fd5b506100d36100ce36600461186e565b6103c7565b005b3480156100e157600080fd5b506100d36100f03660046118a7565b610429565b34801561010157600080fd5b506101297f0000000000000000000000005e81a7515f956ab642eb698821a449fe8fe7498e81565b6040516001600160a01b0390911681526020016100aa565b34801561014d57600080fd5b506100d361015c3660046118c4565b610470565b34801561016d57600080fd5b506100d361017c366004611b15565b610526565b34801561018d57600080fd5b506100d361019c366004611ba5565b61064d565b6100d36101af366004611cb0565b61089f565b6000806101c383850185611d36565b604080518082018252601281527f4d6364466c6173684d696e744d6f64756c65000000000000000000000000000060208201529051630851f3bd60e01b81529192506000916001600160a01b037f0000000000000000000000005e81a7515f956ab642eb698821a449fe8fe7498e1691630851f3bd916102469190600401611e4f565b602060405180830381865afa158015610263573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102879190611e62565b905061029281610429565b6102a08883602001516103c7565b6102ae888360000151610470565b6102b8828a610ccc565b60006102c48888610f00565b6040516370a0823160e01b815230600482015290915081906001600160a01b038b16906370a0823190602401602060405180830381865afa15801561030d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103319190611e7f565b10156103845760405162461bcd60e51b815260206004820152601e60248201527f496e73756666696369656e742066756e647320666f72207061796261636b000060448201526064015b60405180910390fd5b6103986001600160a01b038a168383610f66565b507f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd99998505050505050505050565b806001600160a01b0316826001600160a01b031614610425576040517f9f2334ed0000000000000000000000000000000000000000000000000000000081526001600160a01b0380841660048301528216602482015260440161037b565b5050565b336001600160a01b0382161461046d576040517fe9048e8c00000000000000000000000000000000000000000000000000000000815233600482015260240161037b565b50565b6040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa1580156104b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104db9190611e7f565b905081811015610521576040517f3afe323f000000000000000000000000000000000000000000000000000000008152600481018290526024810183905260440161037b565b505050565b604080518082018252601381527f4f7065726174696f6e4578656375746f725f320000000000000000000000000060208201529051630851f3bd60e01b81526001600160a01b037f0000000000000000000000005e81a7515f956ab642eb698821a449fe8fe7498e1691630851f3bd916105a39190600401611e4f565b602060405180830381865afa1580156105c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105e49190611e62565b6001600160a01b0316336001600160a01b0316146106445760405162461bcd60e51b815260206004820152601c60248201527f4f704578656375746f723a2043616c6c657220666f7262696464656e00000000604482015260640161037b565b61046d81611011565b60008460008151811061066257610662611e98565b6020026020010151905060007f0000000000000000000000005e81a7515f956ab642eb698821a449fe8fe7498e6001600160a01b0316630851f3bd6040518060400160405280600d81526020017f42616c616e6365725661756c74000000000000000000000000000000000000008152506040518263ffffffff1660e01b81526004016106ef9190611e4f565b602060405180830381865afa15801561070c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107309190611e62565b9050600080848060200190518101906107499190611fe0565b9150915061075683610429565b6107648483602001516103c7565b610772848360000151610470565b61077c8282610ccc565b60006107c58760008151811061079457610794611e98565b6020026020010151896000815181106107af576107af611e98565b6020026020010151610f0090919063ffffffff16565b6040516370a0823160e01b815230600482015290915081906001600160a01b038716906370a0823190602401602060405180830381865afa15801561080e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108329190611e7f565b10156108805760405162461bcd60e51b815260206004820152601e60248201527f496e73756666696369656e742066756e647320666f72207061796261636b0000604482015260640161037b565b6108946001600160a01b038616858361130b565b505050505050505050565b604080518082018252601281527127b832b930ba34b7b729ba37b930b3b2af9960711b60208201529051630851f3bd60e01b81526000916001600160a01b037f0000000000000000000000005e81a7515f956ab642eb698821a449fe8fe7498e1691630851f3bd9161091391600401611e4f565b602060405180830381865afa158015610930573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109549190611e62565b9050806001600160a01b031663f83d08ba6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561099157600080fd5b505af11580156109a5573d6000803e3d6000fd5b5050604080518082018252601481527f4f7065726174696f6e7352656769737472795f3200000000000000000000000060208201529051630851f3bd60e01b8152600093506001600160a01b037f0000000000000000000000005e81a7515f956ab642eb698821a449fe8fe7498e169250630851f3bd91610a2891600401611e4f565b602060405180830381865afa158015610a45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a699190611e62565b9050816001600160a01b03166369bd38a06040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610aa657600080fd5b505af1158015610aba573d6000803e3d6000fd5b50505050600080826001600160a01b0316631fffb05c87876040518363ffffffff1660e01b8152600401610aef9291906120b3565b600060405180830381865afa158015610b0c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610b349190810190612146565b6040517f42a4b81100000000000000000000000000000000000000000000000000000000815291935091506001600160a01b038516906342a4b81190610b809085908590600401612202565b600060405180830381600087803b158015610b9a57600080fd5b505af1158015610bae573d6000803e3d6000fd5b50505050610bbb87611011565b836001600160a01b03166369bd38a06040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610bf657600080fd5b505af1158015610c0a573d6000803e3d6000fd5b50505050836001600160a01b031663a69df4b56040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610c4957600080fd5b505af1158015610c5d573d6000803e3d6000fd5b505050508585604051602001610c7492919061227f565b604051602081830303815290604052610c8c9061228f565b7f71715266c730dfceca3e44620faebf315f8e7d7404b2de5f3121d0d87c17832c88604051610cbb91906122b6565b60405180910390a250505050505050565b816040015115610dc65781516020830151610cf4916001600160a01b0390911690839061130b565b806001600160a01b0316631cff79cd3063c8d78f5260e01b8560a00151604051602401610d2191906122b6565b60408051601f198184030181529181526020820180516001600160e01b03167fffffffff000000000000000000000000000000000000000000000000000000009485161790525160e085901b9092168252610d7f9291600401612339565b6000604051808303816000875af1158015610d9e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610521919081019061235b565b604080518082018252601281527127b832b930ba34b7b729ba37b930b3b2af9960711b60208201529051630851f3bd60e01b81526000916001600160a01b037f0000000000000000000000005e81a7515f956ab642eb698821a449fe8fe7498e1691630851f3bd91610e3a91600401611e4f565b602060405180830381865afa158015610e57573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e7b9190611e62565b6040517fd59dfd610000000000000000000000000000000000000000000000000000000081526001600160a01b0384811660048301529192509082169063d59dfd6190602401600060405180830381600087803b158015610edb57600080fd5b505af1158015610eef573d6000803e3d6000fd5b505050506105218360a00151611011565b600080610f0d83856123a6565b905083811015610f5f5760405162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015260640161037b565b9392505050565b6040516001600160a01b038316602482015260006044820152610fe190849063095ea7b360e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152611354565b6040516001600160a01b03831660248201526044810182905261052190849063095ea7b360e01b90606401610f92565b604080518082018252601281527127b832b930ba34b7b729ba37b930b3b2af9960711b60208201529051630851f3bd60e01b81526000916001600160a01b037f0000000000000000000000005e81a7515f956ab642eb698821a449fe8fe7498e1691630851f3bd9161108591600401611e4f565b602060405180830381865afa1580156110a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110c69190611e62565b90506000816001600160a01b0316634b1824a46040518163ffffffff1660e01b8152600401602060405180830381865afa158015611108573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061112c91906123be565b905060005b83518110156113055781156111e657826001600160a01b031663e7acf55a85838151811061116157611161611e98565b60200260200101516000015186848151811061117f5761117f611e98565b6020026020010151604001516040518363ffffffff1660e01b81526004016111b39291909182521515602082015260400190565b600060405180830381600087803b1580156111cd57600080fd5b505af11580156111e1573d6000803e3d6000fd5b505050505b8381815181106111f8576111f8611e98565b6020026020010151604001516112f35760007f0000000000000000000000005e81a7515f956ab642eb698821a449fe8fe7498e6001600160a01b031663c2527b3286848151811061124b5761124b611e98565b6020026020010151600001516040518263ffffffff1660e01b815260040161127591815260200190565b602060405180830381865afa158015611292573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112b69190611e62565b90506112f18583815181106112cd576112cd611e98565b602002602001015160200151826001600160a01b031661143990919063ffffffff16565b505b806112fd816123db565b915050611131565b50505050565b6040516001600160a01b0383166024820152604481018290526105219084907fa9059cbb0000000000000000000000000000000000000000000000000000000090606401610f92565b60006113a9826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166114bc9092919063ffffffff16565b80519091501561052157808060200190518101906113c791906123be565b6105215760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f74207375636365656400000000000000000000000000000000000000000000606482015260840161037b565b611442816114d3565b61148e5760405162461bcd60e51b815260206004820152601860248201527f4f704578656375746f723a20696c6c6567616c2063616c6c0000000000000000604482015260640161037b565b61052181604051806060016040528060298152602001612411602991396001600160a01b0385169190611561565b60606114cb8484600085611677565b949350505050565b6040805160048152602481019091526020810180516001600160e01b03167f85e92d9800000000000000000000000000000000000000000000000000000000179052600090819061152390611740565b9050600061153084611740565b7fffffffff000000000000000000000000000000000000000000000000000000009081169216919091149392505050565b606061156c8461175b565b6115de5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e74726163740000000000000000000000000000000000000000000000000000606482015260840161037b565b600080856001600160a01b0316856040516115f991906123f4565b600060405180830381855af49150503d8060008114611634576040519150601f19603f3d011682016040523d82523d6000602084013e611639565b606091505b5091509150811561164d579150610f5f9050565b80511561165d5780518082602001fd5b8360405162461bcd60e51b815260040161037b9190611e4f565b60606116828561175b565b6116ce5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161037b565b600080866001600160a01b031685876040516116ea91906123f4565b60006040518083038185875af1925050503d8060008114611727576040519150601f19603f3d011682016040523d82523d6000602084013e61172c565b606091505b5091509150811561164d5791506114cb9050565b6000815160000361175357506000919050565b506020015190565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708181148015906114cb575050151592915050565b6001600160a01b038116811461046d57600080fd5b60008083601f8401126117bb57600080fd5b50813567ffffffffffffffff8111156117d357600080fd5b6020830191508360208285010111156117eb57600080fd5b9250929050565b60008060008060008060a0878903121561180b57600080fd5b863561181681611794565b9550602087013561182681611794565b94506040870135935060608701359250608087013567ffffffffffffffff81111561185057600080fd5b61185c89828a016117a9565b979a9699509497509295939492505050565b6000806040838503121561188157600080fd5b823561188c81611794565b9150602083013561189c81611794565b809150509250929050565b6000602082840312156118b957600080fd5b8135610f5f81611794565b600080604083850312156118d757600080fd5b82356118e281611794565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b6040516060810167ffffffffffffffff81118282101715611929576119296118f0565b60405290565b60405160c0810167ffffffffffffffff81118282101715611929576119296118f0565b604051601f8201601f1916810167ffffffffffffffff8111828210171561197b5761197b6118f0565b604052919050565b600067ffffffffffffffff82111561199d5761199d6118f0565b5060051b60200190565b600067ffffffffffffffff8211156119c1576119c16118f0565b50601f01601f191660200190565b600082601f8301126119e057600080fd5b81356119f36119ee826119a7565b611952565b818152846020838601011115611a0857600080fd5b816020850160208301376000918101602001919091529392505050565b801515811461046d57600080fd5b600082601f830112611a4457600080fd5b81356020611a546119ee83611983565b82815260059290921b84018101918181019086841115611a7357600080fd5b8286015b84811015611b0a57803567ffffffffffffffff80821115611a985760008081fd5b908801906060828b03601f1901811315611ab25760008081fd5b611aba611906565b87840135815260408085013584811115611ad45760008081fd5b611ae28e8b838901016119cf565b838b0152509382013593611af585611a25565b81019390935250508352918301918301611a77565b509695505050505050565b600060208284031215611b2757600080fd5b813567ffffffffffffffff811115611b3e57600080fd5b6114cb84828501611a33565b600082601f830112611b5b57600080fd5b81356020611b6b6119ee83611983565b82815260059290921b84018101918181019086841115611b8a57600080fd5b8286015b84811015611b0a5780358352918301918301611b8e565b60008060008060808587031215611bbb57600080fd5b843567ffffffffffffffff80821115611bd357600080fd5b818701915087601f830112611be757600080fd5b81356020611bf76119ee83611983565b82815260059290921b8401810191818101908b841115611c1657600080fd5b948201945b83861015611c3d578535611c2e81611794565b82529482019490820190611c1b565b98505088013592505080821115611c5357600080fd5b611c5f88838901611b4a565b94506040870135915080821115611c7557600080fd5b611c8188838901611b4a565b93506060870135915080821115611c9757600080fd5b50611ca4878288016119cf565b91505092959194509250565b600080600060408486031215611cc557600080fd5b833567ffffffffffffffff80821115611cdd57600080fd5b611ce987838801611a33565b94506020860135915080821115611cff57600080fd5b50611d0c868287016117a9565b9497909650939450505050565b6002811061046d57600080fd5b8035611d3181611d19565b919050565b600060208284031215611d4857600080fd5b813567ffffffffffffffff80821115611d6057600080fd5b9083019060c08286031215611d7457600080fd5b611d7c61192f565b823581526020830135611d8e81611794565b60208201526040830135611da181611a25565b60408201526060830135611db481611a25565b6060820152611dc560808401611d26565b608082015260a083013582811115611ddc57600080fd5b611de887828601611a33565b60a08301525095945050505050565b60005b83811015611e12578181015183820152602001611dfa565b838111156113055750506000910152565b60008151808452611e3b816020860160208601611df7565b601f01601f19169290920160200192915050565b602081526000610f5f6020830184611e23565b600060208284031215611e7457600080fd5b8151610f5f81611794565b600060208284031215611e9157600080fd5b5051919050565b634e487b7160e01b600052603260045260246000fd5b8051611d3181611d19565b600082601f830112611eca57600080fd5b8151611ed86119ee826119a7565b818152846020838601011115611eed57600080fd5b6114cb826020830160208701611df7565b600082601f830112611f0f57600080fd5b81516020611f1f6119ee83611983565b82815260059290921b84018101918181019086841115611f3e57600080fd5b8286015b84811015611b0a57805167ffffffffffffffff80821115611f635760008081fd5b908801906060828b03601f1901811315611f7d5760008081fd5b611f85611906565b87840151815260408085015184811115611f9f5760008081fd5b611fad8e8b83890101611eb9565b838b0152509382015193611fc085611a25565b81019390935250508352918301918301611f42565b8051611d3181611794565b60008060408385031215611ff357600080fd5b825167ffffffffffffffff8082111561200b57600080fd5b9084019060c0828703121561201f57600080fd5b61202761192f565b82518152602083015161203981611794565b6020820152604083015161204c81611a25565b6040820152606083015161205f81611a25565b606082015261207060808401611eae565b608082015260a08301518281111561208757600080fd5b61209388828601611efe565b60a08301525093506120aa91505060208401611fd5565b90509250929050565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b600082601f8301126120f357600080fd5b815160206121036119ee83611983565b82815260059290921b8401810191818101908684111561212257600080fd5b8286015b84811015611b0a57805161213981611a25565b8352918301918301612126565b6000806040838503121561215957600080fd5b825167ffffffffffffffff8082111561217157600080fd5b818501915085601f83011261218557600080fd5b815160206121956119ee83611983565b82815260059290921b840181019181810190898411156121b457600080fd5b948201945b838610156121d2578551825294820194908201906121b9565b918801519196509093505050808211156121eb57600080fd5b506121f8858286016120e2565b9150509250929050565b604080825283519082018190526000906020906060840190828701845b8281101561223b5781518452928401929084019060010161221f565b5050508381038285015284518082528583019183019060005b81811015612272578351151583529284019291840191600101612254565b5090979650505050505050565b8183823760009101908152919050565b805160208083015191908110156122b0576000198160200360031b1b821691505b50919050565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b8381101561232b57603f19898403018552815160608151855288820151818a87015261230b82870182611e23565b9289015115159589019590955250948701949250908601906001016122dd565b509098975050505050505050565b6001600160a01b03831681526040602082015260006114cb6040830184611e23565b60006020828403121561236d57600080fd5b815167ffffffffffffffff81111561238457600080fd5b6114cb84828501611eb9565b634e487b7160e01b600052601160045260246000fd5b600082198211156123b9576123b9612390565b500190565b6000602082840312156123d057600080fd5b8151610f5f81611a25565b6000600182016123ed576123ed612390565b5060010190565b60008251612406818460208701611df7565b919091019291505056fe4f704578656375746f723a206c6f772d6c6576656c2064656c656761746563616c6c206661696c6564a264697066735822122035e99a2c8eda1790e3749d97c3767ce3469099a7dd2481fe53e00a1781a7afd564736f6c634300080f0033
Verified Source Code Full Match
Compiler: v0.8.15+commit.e14f2714
EVM: london
Optimization: Yes (1000 runs)
Address.sol 105 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.1;
library Address {
function isContract(address account) internal view returns (bool) {
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
// for accounts without code, i.e. `keccak256('')`
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
// solhint-disable-next-line no-inline-assembly
assembly {
codehash := extcodehash(account)
}
return (codehash != accountHash && codehash != 0x0);
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return _functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
return _functionCallWithValue(target, data, value, errorMessage);
}
function _functionCallWithValue(
address target,
bytes memory data,
uint256 weiValue,
string memory errorMessage
) private returns (bytes memory) {
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
if (success) {
return returndata;
}
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
}
revert(errorMessage);
}
}
SafeMath.sol 58 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
SafeERC20.sol 60 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.1;
import { IERC20 } from "../interfaces/tokens/IERC20.sol";
import { Address } from "./Address.sol";
import { SafeMath } from "./SafeMath.sol";
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(
token,
abi.encodeWithSelector(token.transferFrom.selector, from, to, value)
);
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {ERC20-approve}, and its usage is discouraged.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
_callOptionalReturn(
token,
abi.encodeWithSelector(token.approve.selector, spender, newAllowance)
);
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(
value,
"SafeERC20: decreased allowance below zero"
);
_callOptionalReturn(
token,
abi.encodeWithSelector(token.approve.selector, spender, newAllowance)
);
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
// solhint-disable-next-line max-line-length
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
Common.sol 75 lines
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.15;
enum FlashloanProvider {
DssFlash,
Balancer
}
struct FlashloanData {
uint256 amount;
address asset;
bool isProxyFlashloan;
bool isDPMProxy;
FlashloanProvider provider;
Call[] calls;
}
struct PullTokenData {
address asset;
address from;
uint256 amount;
}
struct SendTokenData {
address asset;
address to;
uint256 amount;
}
struct SetApprovalData {
address asset;
address delegate;
uint256 amount;
bool sumAmounts;
}
struct SwapData {
address fromAsset;
address toAsset;
uint256 amount;
uint256 receiveAtLeast;
uint256 fee;
bytes withData;
bool collectFeeInFromToken;
}
struct Call {
bytes32 targetHash;
bytes callData;
bool skipped;
}
struct Operation {
uint8 currentAction;
bytes32[] actions;
}
struct WrapEthData {
uint256 amount;
}
struct UnwrapEthData {
uint256 amount;
}
struct ReturnFundsData {
address asset;
}
struct PositionCreatedData {
string protocol;
string positionType;
address collateralToken;
address debtToken;
}
ActionAddress.sol 31 lines
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.15;
import "./Address.sol";
import "../actions/common/Executable.sol";
library ActionAddress {
using Address for address;
function execute(address action, bytes memory callData) internal {
require(isCallingAnExecutable(callData), "OpExecutor: illegal call");
action.functionDelegateCall(callData, "OpExecutor: low-level delegatecall failed");
}
function isCallingAnExecutable(bytes memory callData) private pure returns (bool) {
bytes4 executeSelector = convertBytesToBytes4(
abi.encodeWithSelector(Executable.execute.selector)
);
bytes4 selector = convertBytesToBytes4(callData);
return selector == executeSelector;
}
function convertBytesToBytes4(bytes memory inBytes) private pure returns (bytes4 outBytes4) {
if (inBytes.length == 0) {
return 0x0;
}
assembly {
outBytes4 := mload(add(inBytes, 32))
}
}
}
ServiceRegistry.sol 119 lines
// SPDX-License-Identifier: AGPL-3.0-or-later
/// ServiceRegistry.sol
// Copyright (C) 2021-2021 Oazo Apps Limited
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.0;
contract ServiceRegistry {
uint256 public constant MAX_DELAY = 30 days;
mapping(bytes32 => uint256) public lastExecuted;
mapping(bytes32 => address) private namedService;
mapping(bytes32 => bool) private invalidHashes;
address public owner;
uint256 public requiredDelay;
modifier validateInput(uint256 len) {
require(msg.data.length == len, "registry/illegal-padding");
_;
}
modifier delayedExecution() {
bytes32 operationHash = keccak256(msg.data);
uint256 reqDelay = requiredDelay;
/* solhint-disable not-rely-on-time */
if (lastExecuted[operationHash] == 0 && reqDelay > 0) {
// not called before, scheduled for execution
lastExecuted[operationHash] = block.timestamp;
emit ChangeScheduled(operationHash, block.timestamp + reqDelay, msg.data);
} else {
require(block.timestamp - reqDelay > lastExecuted[operationHash], "registry/delay-too-small");
emit ChangeApplied(operationHash, block.timestamp, msg.data);
_;
lastExecuted[operationHash] = 0;
}
/* solhint-enable not-rely-on-time */
}
modifier onlyOwner() {
require(msg.sender == owner, "registry/only-owner");
_;
}
constructor(uint256 initialDelay) {
require(initialDelay <= MAX_DELAY, "registry/invalid-delay");
requiredDelay = initialDelay;
owner = msg.sender;
}
function transferOwnership(
address newOwner
) external onlyOwner validateInput(36) delayedExecution {
owner = newOwner;
}
function changeRequiredDelay(
uint256 newDelay
) external onlyOwner validateInput(36) delayedExecution {
require(newDelay <= MAX_DELAY, "registry/invalid-delay");
requiredDelay = newDelay;
}
function getServiceNameHash(string memory name) external pure returns (bytes32) {
return keccak256(abi.encodePacked(name));
}
function addNamedService(
bytes32 serviceNameHash,
address serviceAddress
) external onlyOwner validateInput(68) delayedExecution {
require(invalidHashes[serviceNameHash] == false, "registry/service-name-used-before");
require(namedService[serviceNameHash] == address(0), "registry/service-override");
namedService[serviceNameHash] = serviceAddress;
emit NamedServiceAdded(serviceNameHash, serviceAddress);
}
function removeNamedService(bytes32 serviceNameHash) external onlyOwner validateInput(36) {
require(namedService[serviceNameHash] != address(0), "registry/service-does-not-exist");
namedService[serviceNameHash] = address(0);
invalidHashes[serviceNameHash] = true;
emit NamedServiceRemoved(serviceNameHash);
}
function getRegisteredService(string memory serviceName) external view returns (address) {
return namedService[keccak256(abi.encodePacked(serviceName))];
}
function getServiceAddress(bytes32 serviceNameHash) external view returns (address) {
return namedService[serviceNameHash];
}
function clearScheduledExecution(
bytes32 scheduledExecution
) external onlyOwner validateInput(36) {
require(lastExecuted[scheduledExecution] > 0, "registry/execution-not-scheduled");
lastExecuted[scheduledExecution] = 0;
emit ChangeCancelled(scheduledExecution);
}
event ChangeScheduled(bytes32 dataHash, uint256 scheduledFor, bytes data);
event ChangeApplied(bytes32 dataHash, uint256 appliedAt, bytes data);
event ChangeCancelled(bytes32 dataHash);
event NamedServiceRemoved(bytes32 nameHash);
event NamedServiceAdded(bytes32 nameHash, address service);
}
Maker.sol 10 lines
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity ^0.8.15; string constant FLASH_MINT_MODULE = "McdFlashMintModule"; string constant MCD_MANAGER = "McdManager"; string constant MCD_JUG = "McdJug"; string constant MCD_JOIN_DAI = "McdJoinDai"; string constant MCD_FLASH = "MCD_FLASH";
OperationStorage.sol 137 lines
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.15;
import { ServiceRegistry } from "./ServiceRegistry.sol";
/**
* @title Operation Storage
* @notice Stores the return values from Actions during an Operation's execution
* @dev valuesHolders is an array of t/x initiators (msg.sender) who have pushed values to Operation Storage
* returnValues is a mapping between a msg.sender and an array of Action return values generated by that senders transaction
*/
contract OperationStorage {
uint8 internal action = 0;
bytes32[] public actions;
bool[] public optionals;
mapping(address => bytes32[]) public returnValues;
address[] public valuesHolders;
bool private locked;
address private whoLocked;
address public initiator;
address immutable operationExecutorAddress;
ServiceRegistry internal immutable registry;
constructor(ServiceRegistry _registry, address _operationExecutorAddress) {
registry = _registry;
operationExecutorAddress = _operationExecutorAddress;
}
/**
* @dev Locks storage to protect against re-entrancy attacks.@author
*/
function lock() external {
locked = true;
whoLocked = msg.sender;
}
/**
* @dev Only the original locker can unlock the contract at the end of the transaction
*/
function unlock() external {
require(whoLocked == msg.sender, "Only the locker can unlock");
require(locked, "Not locked");
locked = false;
whoLocked = address(0);
}
/**
* @dev Sets the initiator of the original call
* Is used by Automation Bot branch in the onFlashloan callback in Operation Executor
* Ensures that third party calls to Operation Storage do not maliciously override values in Operation Storage
* @param _initiator Sets the initiator to Operation Executor contract when storing return values from flashloan nested Action
*/
function setInitiator(address _initiator) external {
require(msg.sender == operationExecutorAddress);
initiator = _initiator;
}
/**
* @param _actions Stores the Actions currently being executed for a given Operation and their optionality
*/
function setOperationActions(bytes32[] memory _actions, bool[] memory _optionals) external {
actions = _actions;
optionals = _optionals;
}
/**
* @param actionHash Checks the current action has against the expected action hash
*/
function verifyAction(bytes32 actionHash, bool skipped) external {
if (skipped) {
require(optionals[action], "Action cannot be skipped");
}
require(actions[action] == actionHash, "incorrect-action");
registry.getServiceAddress(actionHash);
action++;
}
/**
* @dev Custom operations have no Actions stored in Operation Registry
* @return Returns true / false depending on whether the Operation has any actions to verify the Operation against
*/
function hasActionsToVerify() external view returns (bool) {
return actions.length > 0;
}
/**
* @param value Pushes a bytes32 to end of the returnValues array
*/
function push(bytes32 value) external {
address who = msg.sender;
if (who == operationExecutorAddress) {
who = initiator;
}
if (returnValues[who].length == 0) {
valuesHolders.push(who);
}
returnValues[who].push(value);
}
/**
* @dev Values are stored against an address (who)
* This ensures that malicious actors looking to push values to Operation Storage mid transaction cannot overwrite values
* @param index The index of the desired value
* @param who The msg.sender address responsible for storing values
*/
function at(uint256 index, address who) external view returns (bytes32) {
if (who == operationExecutorAddress) {
who = initiator;
}
return returnValues[who][index];
}
/**
* @param who The msg.sender address responsible for storing values
* @return The length of return values stored against a given msg.sender address
*/
function len(address who) external view returns (uint256) {
if (who == operationExecutorAddress) {
who = initiator;
}
return returnValues[who].length;
}
/**
* @dev Clears storage in preparation for the next Operation
*/
function clearStorage() external {
delete action;
delete actions;
for (uint256 i = 0; i < valuesHolders.length; i++) {
delete returnValues[valuesHolders[i]];
}
delete valuesHolders;
}
}
Common.sol 28 lines
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity ^0.8.15; string constant OPERATION_STORAGE = "OperationStorage_2"; string constant OPERATION_EXECUTOR = "OperationExecutor_2"; string constant OPERATIONS_REGISTRY = "OperationsRegistry_2"; string constant CHAINLOG_VIEWER = "ChainLogView"; string constant ONE_INCH_AGGREGATOR = "OneInchAggregator"; string constant DS_GUARD_FACTORY = "DSGuardFactory"; string constant WETH = "WETH"; string constant DAI = "DAI"; uint256 constant RAY = 10 ** 27; bytes32 constant NULL = ""; /** * @dev We do not include patch versions in contract names to allow * for hotfixes of Action dma-contracts * and to limit updates to TheGraph * if the types encoded in emitted events change then use a minor version and * update the ServiceRegistry with a new entry * and update TheGraph decoding accordingly */ string constant POSITION_CREATED_ACTION = "PositionCreated"; string constant UNISWAP_ROUTER = "UniswapRouter"; string constant SWAP = "Swap"; address constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
OperationExecutor.sol 208 lines
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.15;
import { ServiceRegistry } from "./ServiceRegistry.sol";
import { OperationStorage } from "./OperationStorage.sol";
import { OperationsRegistry } from "./OperationsRegistry.sol";
import { ActionAddress } from "../libs/ActionAddress.sol";
import { TakeFlashloan } from "../actions/common/TakeFlashloan.sol";
import { Executable } from "../actions/common/Executable.sol";
import { IERC3156FlashBorrower } from "../interfaces/flashloan/IERC3156FlashBorrower.sol";
import { IERC3156FlashLender } from "../interfaces/flashloan/IERC3156FlashLender.sol";
import { IFlashLoanRecipient } from "../interfaces/flashloan/balancer/IFlashLoanRecipient.sol";
import { IDSProxy } from "../interfaces/ds/IDSProxy.sol";
import { SafeERC20, IERC20 } from "../libs/SafeERC20.sol";
import { SafeMath } from "../libs/SafeMath.sol";
import { FlashloanData, Call } from "./types/Common.sol";
import { OPERATION_STORAGE, OPERATIONS_REGISTRY, OPERATION_EXECUTOR } from "./constants/Common.sol";
import { FLASH_MINT_MODULE } from "./constants/Maker.sol";
import { BALANCER_VAULT } from "./constants/Balancer.sol";
error UntrustedLender(address lender);
error InconsistentAsset(address flashloaned, address required);
error InconsistentAmount(uint256 flashloaned, uint256 required);
/**
* @title Operation Executor
* @notice Is responsible for executing sequences of Actions (Operations)
*/
contract OperationExecutor is IERC3156FlashBorrower, IFlashLoanRecipient {
using ActionAddress for address;
using SafeERC20 for IERC20;
using SafeMath for uint256;
ServiceRegistry public immutable registry;
/**
* @dev Emitted once an Operation has completed execution
* @param name The address initiating the deposit
* @param calls An array of Action calls the operation must execute
**/
event Operation(bytes32 indexed name, Call[] calls);
constructor(ServiceRegistry _registry) {
registry = _registry;
}
/**
* @notice Executes an operation
* @dev
* There are operations stored in the OperationsRegistry which guarantee the order of execution of actions for a given Operation.
* There is a possibility to execute an arrays of calls that don't form an official operation.
*
* Operation storage is cleared before and after an operation is executed.
*
* To avoid re-entrancy attack, there is a lock implemented on OpStorage.
* A standard reentrancy modifier is not sufficient because the second call via the onFlashloan handler
* calls aggregateCallback via DSProxy once again but this breaks the special modifier _ behaviour
* and the modifier cannot return the execution flow to the original function.
* This is why re-entrancy defence is immplemented here using an external storage contract via the lock/unlock functions
* @param calls An array of Action calls the operation must execute
* @param operationName The name of the Operation being executed
*/
function executeOp(Call[] memory calls, string calldata operationName) public payable {
OperationStorage opStorage = OperationStorage(registry.getRegisteredService(OPERATION_STORAGE));
opStorage.lock();
OperationsRegistry opRegistry = OperationsRegistry(
registry.getRegisteredService(OPERATIONS_REGISTRY)
);
opStorage.clearStorage();
(bytes32[] memory actions, bool[] memory optional) = opRegistry.getOperation(operationName);
opStorage.setOperationActions(actions, optional);
aggregate(calls);
opStorage.clearStorage();
opStorage.unlock();
// By packing the string into bytes32 which means the max char length is capped at 64
emit Operation(bytes32(abi.encodePacked(operationName)), calls);
}
function aggregate(Call[] memory calls) internal {
OperationStorage opStorage = OperationStorage(registry.getRegisteredService(OPERATION_STORAGE));
bool hasActionsToVerify = opStorage.hasActionsToVerify();
for (uint256 current = 0; current < calls.length; current++) {
if (hasActionsToVerify) {
opStorage.verifyAction(calls[current].targetHash, calls[current].skipped);
}
if (!calls[current].skipped) {
address target = registry.getServiceAddress(calls[current].targetHash);
target.execute(calls[current].callData);
}
}
}
/**
* @notice Not to be called directly
* @dev Is called by the Operation Executor via a user's proxy to execute Actions nested in the FlashloanAction
* @param calls An array of Action calls the operation must execute
*/
function callbackAggregate(Call[] memory calls) external {
require(
msg.sender == registry.getRegisteredService(OPERATION_EXECUTOR),
"OpExecutor: Caller forbidden"
);
aggregate(calls);
}
/**
* @notice Not to be called directly.
* @dev Callback handler for use by a flashloan lender contract.
* If the isProxyFlashloan flag is supplied we reestablish the calling context as the user's proxy (at time of writing DSProxy). Although stored values will
* We set the initiator on Operation Storage such that calls originating from other contracts EG Oasis Automation Bot (see https://github.com/OasisDEX/automation-smartcontracts)
* The initiator address will be used to store values against the original msg.sender.
* This protects against the Operation Storage values being polluted by malicious code from untrusted 3rd party contracts.
* @param asset The address of the asset being flash loaned
* @param amount The size of the flash loan
* @param fee The Fee charged for the loan
* @param data Any calldata sent to the contract for execution later in the callback
*/
function onFlashLoan(
address initiator,
address asset,
uint256 amount,
uint256 fee,
bytes calldata data
) external override returns (bytes32) {
FlashloanData memory flData = abi.decode(data, (FlashloanData));
address lender = registry.getRegisteredService(FLASH_MINT_MODULE);
checkIfLenderIsTrusted(lender);
checkIfFlashloanedAssetIsTheRequiredOne(asset, flData.asset);
checkIfFlashloanedAmountIsTheRequiredOne(asset, flData.amount);
processFlashloan(flData, initiator);
uint256 paybackAmount = amount.add(fee);
require(
IERC20(asset).balanceOf(address(this)) >= paybackAmount,
"Insufficient funds for payback"
);
IERC20(asset).safeApprove(lender, paybackAmount);
return keccak256("ERC3156FlashBorrower.onFlashLoan");
}
function receiveFlashLoan(
IERC20[] memory tokens,
uint256[] memory amounts,
uint256[] memory feeAmounts,
bytes memory data
) external override {
address asset = address(tokens[0]);
address lender = registry.getRegisteredService(BALANCER_VAULT);
(FlashloanData memory flData, address initiator) = abi.decode(data, (FlashloanData, address));
checkIfLenderIsTrusted(lender);
checkIfFlashloanedAssetIsTheRequiredOne(asset, flData.asset);
checkIfFlashloanedAmountIsTheRequiredOne(asset, flData.amount);
processFlashloan(flData, initiator);
uint256 paybackAmount = amounts[0].add(feeAmounts[0]);
require(
IERC20(asset).balanceOf(address(this)) >= paybackAmount,
"Insufficient funds for payback"
);
IERC20(asset).safeTransfer(lender, paybackAmount);
}
function checkIfLenderIsTrusted(address lender) public view {
if (msg.sender != lender) revert UntrustedLender(msg.sender);
}
function checkIfFlashloanedAssetIsTheRequiredOne(
address flashloaned,
address required
) public pure {
if (flashloaned != required) revert InconsistentAsset(flashloaned, required);
}
function checkIfFlashloanedAmountIsTheRequiredOne(
address asset,
uint256 requiredAmount
) public view {
uint256 assetBalance = IERC20(asset).balanceOf(address(this));
if (assetBalance < requiredAmount) revert InconsistentAmount(assetBalance, requiredAmount);
}
function processFlashloan(FlashloanData memory flData, address initiator) private {
if (flData.isProxyFlashloan) {
IERC20(flData.asset).safeTransfer(initiator, flData.amount);
IDSProxy(payable(initiator)).execute(
address(this),
abi.encodeWithSelector(this.callbackAggregate.selector, flData.calls)
);
} else {
OperationStorage opStorage = OperationStorage(
registry.getRegisteredService(OPERATION_STORAGE)
);
opStorage.setInitiator(initiator);
aggregate(flData.calls);
}
}
}
IDSProxy.sol 34 lines
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.15;
interface IDSProxy {
function owner() external returns (address);
function execute(bytes memory, bytes memory) external payable returns (address, bytes memory);
function execute(address, bytes memory) external payable returns (bytes memory);
function setCache(address _cacheAddr) external returns (bool);
}
interface IDSAuthority {
function canCall(address, address, bytes4) external view returns (bool);
}
interface IDSAuth {
function authority() external returns (IDSAuthority);
function setAuthority(IDSAuthority) external;
}
interface IDSGuard {
function canCall(address, address, bytes4) external view returns (bool);
function permit(address, address, bytes32) external;
function forbid(address, address, bytes32) external;
}
interface IDSGuardFactory {
function newGuard() external returns (IDSGuard);
}
OperationsRegistry.sol 69 lines
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.15;
import { Operation } from "./types/Common.sol";
import { OPERATIONS_REGISTRY } from "./constants/Common.sol";
struct StoredOperation {
bytes32[] actions;
bool[] optional;
string name;
}
/**
* @title Operation Registry
* @notice Stores the Actions that constitute a given Operation and information if an Action can be skipped
*/
contract OperationsRegistry {
mapping(string => StoredOperation) private operations;
address public owner;
modifier onlyOwner() {
require(msg.sender == owner, "only-owner");
_;
}
constructor() {
owner = msg.sender;
}
/**
* @notice Stores the Actions that constitute a given Operation
* @param newOwner The address of the new owner of the Operations Registry
*/
function transferOwnership(address newOwner) public onlyOwner {
owner = newOwner;
}
/**
* @dev Emitted when a new operation is added or an existing operation is updated
* @param name The Operation name
**/
event OperationAdded(bytes32 indexed name);
/**
* @notice Adds an Operation's Actions keyed to a an operation name
* @param operation Struct with Operation name, actions and their optionality
*/
function addOperation(StoredOperation calldata operation) external onlyOwner {
operations[operation.name] = operation;
// By packing the string into bytes32 which means the max char length is capped at 64
emit OperationAdded(bytes32(abi.encodePacked(operation.name)));
}
/**
* @notice Gets an Operation from the Registry
* @param name The name of the Operation
* @return actions Returns an array of Actions and array for optionality of coresponding Actions
*/
function getOperation(
string memory name
) external view returns (bytes32[] memory actions, bool[] memory optional) {
if (keccak256(bytes(operations[name].name)) == keccak256(bytes(""))) {
revert("Operation doesn't exist");
}
actions = operations[name].actions;
optional = operations[name].optional;
}
}
Balancer.sol 4 lines
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity ^0.8.15; string constant BALANCER_VAULT = "BalancerVault";
ChainLogView.sol 68 lines
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.15;
import { IChainLog } from "../../interfaces/maker/IChainLog.sol";
/**
* @title ChainLogView
* @notice Reads the Chainlog contract to get the address of a service by its name
*/
contract ChainLogView {
address public immutable chainlogAddress;
constructor(address _chainlogAddress) {
chainlogAddress = _chainlogAddress;
}
/**
* @notice Gets the string representation of a bytes32 value with `-` replaced with `_`
* @param _bytes32 value to decode to string
* @return The decoded string
*/
function bytes32ToString(bytes32 _bytes32) public pure returns (string memory) {
uint8 i = 0;
while (i < 32 && _bytes32[i] != 0) {
i++;
}
bytes memory bytesArray = new bytes(i);
for (i = 0; i < 32 && _bytes32[i] != 0; i++) {
if (_bytes32[i] == bytes1("-")) {
bytesArray[i] = bytes1("_");
} else {
bytesArray[i] = _bytes32[i];
}
}
return string(bytesArray);
}
/**
* @notice Gets the address of a service by its name
* @param serviceName The name of the service
* @return The address of the service
*/
function getServiceAddress(string calldata serviceName) public view returns (address) {
bytes32 serviceHash = bytes32(abi.encodePacked(serviceName));
return IChainLog(chainlogAddress).getAddress(serviceHash);
}
/**
* @notice Gets the address of a join adapter by its ilk name
* @param ilkName The name of the ilk
* @return The address of the join adapter
*/
function getIlkJoinAddressByName(string calldata ilkName) public view returns (address) {
bytes32 ilkHash = bytes32(abi.encodePacked("MCD_JOIN_", ilkName));
return IChainLog(chainlogAddress).getAddress(ilkHash);
}
/**
* @notice Gets the address of a join adapter by its ilk hash
* @param ilkHash The hash of the ilk name
* @return The address of the join adapter
*/
function getIlkJoinAddressByHash(bytes32 ilkHash) public view returns (address) {
bytes32 newIlkHash = bytes32(abi.encodePacked("MCD_JOIN_", bytes32ToString(ilkHash)));
return IChainLog(chainlogAddress).getAddress(newIlkHash);
}
}
ProxyPermission.sol 61 lines
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.15;
import { FlashloanData } from "../../core/types/Common.sol";
import { IAccountImplementation } from "../../interfaces/dpm/IAccountImplementation.sol";
import { IAccountGuard } from "../../interfaces/dpm/IAccountGuard.sol";
import { ServiceRegistry } from "../../core/ServiceRegistry.sol";
import { DS_GUARD_FACTORY } from "../../core/constants/Common.sol";
import { IDSGuardFactory, IDSGuard, IDSAuth, IDSAuthority } from "../../interfaces/ds/IDSProxy.sol";
contract ProxyPermission {
IDSGuardFactory internal immutable dsGuardFactory;
bytes4 public constant ALLOWED_METHOD_HASH = bytes4(keccak256("execute(address,bytes)"));
constructor(address _dsGuardFactory) {
dsGuardFactory = IDSGuardFactory(_dsGuardFactory);
}
function givePermission(bool isDPMProxy, address _contractAddr) public {
if (isDPMProxy) {
// DPM permission
IAccountGuard(IAccountImplementation(address(this)).guard()).permit(
_contractAddr,
address(this),
true
);
} else {
// DSProxy permission
address currAuthority = address(IDSAuth(address(this)).authority());
IDSGuard guard = IDSGuard(currAuthority);
if (currAuthority == address(0)) {
guard = dsGuardFactory.newGuard();
IDSAuth(address(this)).setAuthority(IDSAuthority(address(guard)));
}
if (!guard.canCall(_contractAddr, address(this), ALLOWED_METHOD_HASH)) {
guard.permit(_contractAddr, address(this), ALLOWED_METHOD_HASH);
}
}
}
function removePermission(bool isDPMProxy, address _contractAddr) public {
if (isDPMProxy) {
// DPM permission
IAccountGuard(IAccountImplementation(address(this)).guard()).permit(
_contractAddr,
address(this),
false
);
} else {
// DSProxy permission
address currAuthority = address(IDSAuth(address(this)).authority());
if (currAuthority == address(0)) {
return;
}
IDSGuard guard = IDSGuard(currAuthority);
guard.forbid(_contractAddr, address(this), ALLOWED_METHOD_HASH);
}
}
}
IERC20.sol 19 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
interface IERC20 {
function totalSupply() external view returns (uint256 supply);
function balanceOf(address _owner) external view returns (uint256 balance);
function transfer(address _to, uint256 _value) external returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) external returns (bool success);
function approve(address _spender, uint256 _value) external returns (bool success);
function allowance(address _owner, address _spender) external view returns (uint256 remaining);
function decimals() external view returns (uint256 digits);
}
Executable.sol 10 lines
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.15;
/**
* @title Shared Action Executable interface
* @notice Provides a dma-common interface for an execute method to all Action
*/
interface Executable {
function execute(bytes calldata data, uint8[] memory paramsMap) external payable;
}
IVault.sol 14 lines
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.15;
import { IFlashLoanRecipient } from "../flashloan/balancer/IFlashLoanRecipient.sol";
import { IERC20 } from "../../libs/SafeERC20.sol";
interface IVault {
function flashLoan(
IFlashLoanRecipient recipient,
IERC20[] memory tokens,
uint256[] memory amounts,
bytes memory userData
) external;
}
IChainLog.sol 7 lines
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.15;
abstract contract IChainLog {
function getAddress(bytes32 _key) public view virtual returns (address addr);
}
TakeFlashloan.sol 83 lines
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.15;
import { Executable } from "../common/Executable.sol";
import { ServiceRegistry } from "../../core/ServiceRegistry.sol";
import { IVault } from "../../interfaces/balancer/IVault.sol";
import { IERC3156FlashBorrower } from "../../interfaces/flashloan/IERC3156FlashBorrower.sol";
import { IERC3156FlashLender } from "../../interfaces/flashloan/IERC3156FlashLender.sol";
import { IFlashLoanRecipient } from "../../interfaces/flashloan/balancer/IFlashLoanRecipient.sol";
import { FlashloanData, FlashloanProvider } from "../../core/types/Common.sol";
import { OPERATION_EXECUTOR, DAI, CHAINLOG_VIEWER } from "../../core/constants/Common.sol";
import { MCD_FLASH } from "../../core/constants/Maker.sol";
import { BALANCER_VAULT } from "../../core/constants/Balancer.sol";
import { ChainLogView } from "../../core/views/ChainLogView.sol";
import { ProxyPermission } from "../../libs/DS/ProxyPermission.sol";
import { IERC20 } from "../../libs/SafeERC20.sol";
/**
* @title TakeFlashloan Action contract
* @notice Executes a sequence of Actions after flashloaning funds
*/
contract TakeFlashloan is Executable, ProxyPermission {
address internal immutable dai;
ServiceRegistry private immutable registry;
constructor(
ServiceRegistry _registry,
address _dai,
address _dsGuardFactory
) ProxyPermission(_dsGuardFactory) {
registry = _registry;
dai = _dai;
}
/**
* @dev When the Flashloan lender calls back the Operation Executor we may need to re-establish the calling context.
* @dev The isProxyFlashloan flag is used to give the Operation Executor temporary authority to call the execute method on a user"s proxy. Refers to any proxy wallet (DSProxy or DPMProxy at time of writing)
* @dev isDPMProxy flag switches between regular DSPRoxy and DPMProxy
* @param data Encoded calldata that conforms to the FlashloanData struct
*/
function execute(bytes calldata data, uint8[] memory) external payable override {
FlashloanData memory flData = parseInputs(data);
address operationExecutorAddress = registry.getRegisteredService(OPERATION_EXECUTOR);
if (flData.isProxyFlashloan) {
givePermission(flData.isDPMProxy, operationExecutorAddress);
}
if (flData.provider == FlashloanProvider.DssFlash) {
ChainLogView chainlogView = ChainLogView(registry.getRegisteredService(CHAINLOG_VIEWER));
IERC3156FlashLender(chainlogView.getServiceAddress(MCD_FLASH)).flashLoan(
IERC3156FlashBorrower(operationExecutorAddress),
dai,
flData.amount,
abi.encode(flData, address(this))
);
}
if (flData.provider == FlashloanProvider.Balancer) {
IERC20[] memory tokens = new IERC20[](1);
uint256[] memory amounts = new uint256[](1);
tokens[0] = IERC20(flData.asset);
amounts[0] = flData.amount;
IVault(registry.getRegisteredService(BALANCER_VAULT)).flashLoan(
IFlashLoanRecipient(operationExecutorAddress),
tokens,
amounts,
abi.encode(flData, address(this))
);
}
if (flData.isProxyFlashloan) {
removePermission(flData.isDPMProxy, operationExecutorAddress);
}
}
function parseInputs(bytes memory _callData) public pure returns (FlashloanData memory params) {
return abi.decode(_callData, (FlashloanData));
}
}
IAccountGuard.sol 18 lines
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity >=0.8.15;
interface IAccountGuard {
function owners(address) external view returns (address);
function owner() external view returns (address);
function setWhitelist(address target, bool status) external;
function canCall(address proxy, address operator) external view returns (bool);
function permit(address caller, address target, bool allowance) external;
function isWhitelisted(address target) external view returns (bool);
function isWhitelistedSend(address target) external view returns (bool);
}
IAccountImplementation.sol 12 lines
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity >=0.8.15;
interface IAccountImplementation {
function execute(address _target, bytes memory _data) external payable returns (bytes32 response);
function send(address _target, bytes memory _data) external payable;
function owner() external view returns (address owner);
function guard() external returns (address);
}
IERC3156FlashLender.sol 50 lines
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2021 Dai Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.15;
import "./IERC3156FlashBorrower.sol";
interface IERC3156FlashLender {
/**
* @dev The amount of currency available to be lent.
* @param token The loan currency.
* @return The amount of `token` that can be borrowed.
*/
function maxFlashLoan(address token) external view returns (uint256);
/**
* @dev The fee to be charged for a given loan.
* @param token The loan currency.
* @param amount The amount of tokens lent.
* @return The amount of `token` to be charged for the loan, on top of the returned principal.
*/
function flashFee(address token, uint256 amount) external view returns (uint256);
/**
* @dev Initiate a flash loan.
* @param receiver The receiver of the tokens in the loan, and the receiver of the callback.
* @param token The loan currency.
* @param amount The amount of tokens lent.
* @param data Arbitrary data structure, intended to contain user-defined parameters.
*/
function flashLoan(
IERC3156FlashBorrower receiver,
address token,
uint256 amount,
bytes calldata data
) external returns (bool);
}
IERC3156FlashBorrower.sol 36 lines
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2021 Dai Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.15;
interface IERC3156FlashBorrower {
/**
* @dev Receive a flash loan.
* @param initiator The initiator of the loan.
* @param token The loan currency.
* @param amount The amount of tokens lent.
* @param fee The additional amount of tokens to repay.
* @param data Arbitrary data structure, intended to contain user-defined parameters.
* @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan"
*/
function onFlashLoan(
address initiator,
address token,
uint256 amount,
uint256 fee,
bytes calldata data
) external returns (bytes32);
}
IFlashLoanRecipient.sol 22 lines
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.15;
import { IERC20 } from "../../../libs/SafeERC20.sol";
interface IFlashLoanRecipient {
/**
* @dev When `flashLoan` is called on the Vault, it invokes the `receiveFlashLoan` hook on the recipient.
*
* At the time of the call, the Vault will have transferred `amounts` for `tokens` to the recipient. Before this
* call returns, the recipient must have transferred `amounts` plus `feeAmounts` for each token back to the
* Vault, or else the entire flash loan will revert.
*
* `userData` is the same value passed in the `IVault.flashLoan` call.
*/
function receiveFlashLoan(
IERC20[] memory tokens,
uint256[] memory amounts,
uint256[] memory feeAmounts,
bytes memory userData
) external;
}
Read Contract
checkIfFlashloanedAmountIsTheRequiredOne 0xb7c9c69f
checkIfFlashloanedAssetIsTheRequiredOne 0x3fbeae9f
checkIfLenderIsTrusted 0x60ac8fe6
registry 0x7b103999 → address
Write Contract 4 functions
These functions modify contract state and require a wallet transaction to execute.
callbackAggregate 0xf5d0ef68
tuple[] calls
executeOp 0xb4649e22
tuple[] calls
string operationName
onFlashLoan 0x23e30c8b
address initiator
address asset
uint256 amount
uint256 fee
bytes data
returns: bytes32
receiveFlashLoan 0xf04f2707
address[] tokens
uint256[] amounts
uint256[] feeAmounts
bytes data
Token Balances (3) $0.06
View Transfers →Recent Transactions
This address has 1 on-chain transactions, but only 1.4% of the chain is indexed. Transactions will appear as indexing progresses. View on Etherscan →