Address Contract Partially Verified
Address
0xA5d7d68D7975e89FEb240f42feD1D77bb71b1cAF
Balance
0 ETH
Nonce
1
Code Size
17266 bytes
Creator
0x46cf7ddb...5D11 at tx 0xddb598f9...9dd9f9
Indexed Transactions
0
Contract Bytecode
17266 bytes
0x608060405234801561001057600080fd5b506004361061016e576000357c010000000000000000000000000000000000000000000000000000000090048063a90cf0af116100ea578063c9b5ef8e1161009e578063d89784fc11610083578063d89784fc1461054d578063e8ca0ca314610555578063ec9e13aa1461058f5761016e565b8063c9b5ef8e146104e0578063cac7495c146105065761016e565b8063ac5f8d51116100cf578063ac5f8d5114610428578063b02c808d14610462578063b352d4af146104a65761016e565b8063a90cf0af1461030c578063aacaaf88146103385761016e565b80635fe3b5671161014157806385a13f381161012657806385a13f381461027657806398d53428146102b05780639be65a60146102e65761016e565b80635fe3b5671461022557806381c44b5c1461022d5761016e565b806319ab453c146101735780632d0335ab1461019b57806347eb8d43146101d35780635a1db8c4146101f7575b600080fd5b6101996004803603602081101561018957600080fd5b5035600160a060020a03166105cb565b005b6101c1600480360360208110156101b157600080fd5b5035600160a060020a0316610671565b60408051918252519081900360200190f35b6101db61068c565b60408051600160a060020a039092168252519081900360200190f35b6101996004803603604081101561020d57600080fd5b50600160a060020a038135811691602001351661069b565b6101db610860565b6102596004803603604081101561024357600080fd5b50600160a060020a03813516906020013561086f565b6040805160ff909316835260208301919091528051918290030190f35b6101996004803603608081101561028c57600080fd5b50600160a060020a03813581169160208101359160408201351690606001356109a6565b610199600480360360608110156102c657600080fd5b50600160a060020a03813581169160208101359091169060400135610be9565b610199600480360360208110156102fc57600080fd5b5035600160a060020a0316610f0e565b6101996004803603604081101561032257600080fd5b50600160a060020a038135169060200135611041565b610414600480360360c081101561034e57600080fd5b600160a060020a03823516919081019060408101602082013564010000000081111561037957600080fd5b82018360208201111561038b57600080fd5b803590602001918460018302840111640100000000831117156103ad57600080fd5b919390928235926040810190602001356401000000008111156103cf57600080fd5b8201836020820111156103e157600080fd5b8035906020019184600183028401116401000000008311171561040357600080fd5b9193509150803590602001356115d6565b604080519115158252519081900360200190f35b6101996004803603608081101561043e57600080fd5b50600160a060020a03813581169160208101359160408201351690606001356118be565b6101c1600480360360a081101561047857600080fd5b50600160a060020a038135811691602081013582169160408201359160608101359091169060800135611b00565b610199600480360360808110156104bc57600080fd5b50600160a060020a0381358116916020810135916040820135169060600135611ff6565b6101c1600480360360208110156104f657600080fd5b5035600160a060020a031661223a565b6105346004803603604081101561051c57600080fd5b50600160a060020a038135811691602001351661224c565b6040805192835260208301919091528051918290030190f35b6101db612433565b6101996004803603608081101561056b57600080fd5b50600160a060020a0381358116916020810135916040820135169060600135612442565b6101c1600480360360808110156105a557600080fd5b50600160a060020a03813581169160208101359091169060408101359060600135612684565b8033600160a060020a03821614610631576040805160008051602061424f833981519152815260206004820152601960248201527f424d3a2063616c6c6572206d7573742062652077616c6c657400000000000000604482015290519081900360640190fd5b60408051600160a060020a038416815290517f9fcca3f73f85397e2bf03647abf243c20b753bd54463ff3cae74de2971c112fa9181900360200190a15050565b600160a060020a031660009081526001602052604090205490565b600454600160a060020a031681565b816106a681336128ba565b15156106eb5760405160008051602061424f833981519152815260040180806020018281038252602e815260200180614221602e913960400191505060405180910390fd5b600054604080517f0bcd4ebb000000000000000000000000000000000000000000000000000000008152600160a060020a03858116600483015291519190921691630bcd4ebb916024808301926020929190829003018186803b15801561075157600080fd5b505afa158015610765573d6000803e3d6000fd5b505050506040513d602081101561077b57600080fd5b505115156107d8576040805160008051602061424f833981519152815260206004820152601c60248201527f424d3a206d6f64756c65206973206e6f74207265676973746572656400000000604482015290519081900360640190fd5b604080517f1f17732d000000000000000000000000000000000000000000000000000000008152600160a060020a03848116600483015260016024830152915191851691631f17732d9160448082019260009290919082900301818387803b15801561084357600080fd5b505af1158015610857573d6000803e3d6000fd5b50505050505050565b600354600160a060020a031681565b600354604080517f5ec88c79000000000000000000000000000000000000000000000000000000008152600160a060020a038581166004830152915160009384938493849384939290921691635ec88c7991602480820192606092909190829003018186803b1580156108e157600080fd5b505afa1580156108f5573d6000803e3d6000fd5b505050506040513d606081101561090b57600080fd5b5080516020820151604090920151909450909250905082156109665760405160008051602061424f833981519152815260040180806020018281038252602981526020018061431e6029913960400191505060405180910390fd5b600082111561097d575060019350915061099f9050565b60008111156109945760029450925061099f915050565b506000935083925050505b9250929050565b83333014806109ba57506109ba81336128ba565b15156109ff5760405160008051602061424f83398151915281526004018080602001828103825260238152602001806142b76023913960400191505060405180910390fd5b600254604080517f4a4fbeec000000000000000000000000000000000000000000000000000000008152600160a060020a038089166004830152915188939290921691634a4fbeec91602480820192602092909190829003018186803b158015610a6857600080fd5b505afa158015610a7c573d6000803e3d6000fd5b505050506040513d6020811015610a9257600080fd5b505115610ad85760405160008051602061424f833981519152815260040180806020018281038252602881526020018061428f6028913960400191505060405180910390fd5b60048054604080517f7e5a4eb9000000000000000000000000000000000000000000000000000000008152600160a060020a0388811694820194909452905160009390921691637e5a4eb991602480820192602092909190829003018186803b158015610b4457600080fd5b505afa158015610b58573d6000803e3d6000fd5b505050506040513d6020811015610b6e57600080fd5b5051600354909150610b8c9088908390600160a060020a031661295a565b610b97878286612be5565b60408051600160a060020a0387811682526020820187905282518993918b16927ff6669d5e7ff92997c22e9fe3b54f53ed18448cf5fdccb3eb1dc5004798fbb41492908290030190a350505050505050565b8233301480610bfd5750610bfd81336128ba565b1515610c425760405160008051602061424f83398151915281526004018080602001828103825260238152602001806142b76023913960400191505060405180910390fd5b600254604080517f4a4fbeec000000000000000000000000000000000000000000000000000000008152600160a060020a038088166004830152915187939290921691634a4fbeec91602480820192602092909190829003018186803b158015610cab57600080fd5b505afa158015610cbf573d6000803e3d6000fd5b505050506040513d6020811015610cd557600080fd5b505115610d1b5760405160008051602061424f833981519152815260040180806020018281038252602881526020018061428f6028913960400191505060405180910390fd5b612710831115610d645760405160008051602061424f83398151915281526004018080602001828103825260228152602001806141b56022913960400191505060405180910390fd5b60048054604080517f7e5a4eb9000000000000000000000000000000000000000000000000000000008152600160a060020a0388811694820194909452905160009390921691637e5a4eb991602480820192602092909190829003018186803b158015610dd057600080fd5b505afa158015610de4573d6000803e3d6000fd5b505050506040513d6020811015610dfa57600080fd5b5051604080517f70a08231000000000000000000000000000000000000000000000000000000008152600160a060020a0389811660048301529151929350600092918416916370a0823191602480820192602092909190829003018186803b158015610e6557600080fd5b505afa158015610e79573d6000803e3d6000fd5b505050506040513d6020811015610e8f57600080fd5b50519050610ebf8783610eba612710610eae868b63ffffffff612da716565b9063ffffffff612dd916565b612dfd565b60408051600160a060020a038881168252602082018890528251908a16927f9aa275f858ea6286ee2cacd32cd2ae55f918a310ee6d0320caad244ee9d6109e928290030190a250505050505050565b604080517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529051600091600160a060020a038416916370a0823191602480820192602092909190829003018186803b158015610f7157600080fd5b505afa158015610f85573d6000803e3d6000fd5b505050506040513d6020811015610f9b57600080fd5b505160008054604080517fa9059cbb000000000000000000000000000000000000000000000000000000008152600160a060020a0392831660048201526024810185905290519394509085169263a9059cbb92604480840193602093929083900390910190829087803b15801561101157600080fd5b505af1158015611025573d6000803e3d6000fd5b505050506040513d602081101561103b57600080fd5b50505050565b8133301480611055575061105581336128ba565b151561109a5760405160008051602061424f83398151915281526004018080602001828103825260238152602001806142b76023913960400191505060405180910390fd5b600254604080517f4a4fbeec000000000000000000000000000000000000000000000000000000008152600160a060020a038087166004830152915186939290921691634a4fbeec91602480820192602092909190829003018186803b15801561110357600080fd5b505afa158015611117573d6000803e3d6000fd5b505050506040513d602081101561112d57600080fd5b5051156111735760405160008051602061424f833981519152815260040180806020018281038252602881526020018061428f6028913960400191505060405180910390fd5b600354604080517fabfceffc000000000000000000000000000000000000000000000000000000008152600160a060020a0387811660048301529151606093929092169163abfceffc91602480820192600092909190829003018186803b1580156111dd57600080fd5b505afa1580156111f1573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561121a57600080fd5b81019080805164010000000081111561123257600080fd5b8201602081018481111561124557600080fd5b815185602082028301116401000000008211171561126257600080fd5b50909450600093505050505b8151811015611598576000828281518110151561128757fe5b906020019060200201519050600081600160a060020a03166317bfdfbc896040518263ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018082600160a060020a0316600160a060020a03168152602001915050602060405180830381600087803b15801561130957600080fd5b505af115801561131d573d6000803e3d6000fd5b505050506040513d602081101561133357600080fd5b50519050600081111561158e5761134b888383612f70565b600082600160a060020a03166370a082318a6040518263ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018082600160a060020a0316600160a060020a0316815260200191505060206040518083038186803b1580156113bf57600080fd5b505afa1580156113d3573d6000803e3d6000fd5b505050506040513d60208110156113e957600080fd5b5051905080151561158c5788600160a060020a0316638f6f0332600360009054906101000a9004600160a060020a03166000866040516024018082600160a060020a0316600160a060020a031681526020019150506040516020818303038152906040527fede4edd0000000000000000000000000000000000000000000000000000000006000805160206142da83398151915219166020820180516000805160206142da83398151915283818316178352505050506040518463ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018084600160a060020a0316600160a060020a0316815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561152557818101518382015260200161150d565b50505050905090810190601f1680156115525780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b15801561157357600080fd5b505af1158015611587573d6000803e3d6000fd5b505050505b505b505060010161126e565b506040518490600160a060020a038716907f07e4a0c6f06275f83bcf78e5a10eb7f5515574d593bce993377961f4133b951c90600090a35050505050565b6000805a90506000611626308c60008d8d8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508f92508c91508b905061356a565b90506116338b89836136ad565b151561168e576040805160008051602061424f833981519152815260206004820152601560248201527f524d3a204475706c696361746520726571756573740000000000000000000000604482015290519081900360640190fd5b6116ce8b8b8b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506136c192505050565b15156117135760405160008051602061424f833981519152815260040180806020018281038252604a8152602001806141d7604a913960600191505060405180910390fd5b60006117558c8c8c8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061373a92505050565b90506041810287141561186d5761176e8c868884613742565b1561186d578015806117f157506117f18c8c8c8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604080516020601f8f018190048102820181019092528d815288935091508d908d908190840183828082843760009201919091525061381492505050565b1561186d5730600160a060020a03168b8b604051808383808284376040519201945060009350909150508083038183865af19150503d8060008114611852576040519150601f19603f3d011682016040523d82523d6000602084013e611857565b606091505b50508094505061186d8c5a850388888533613839565b60408051838152905185151591600160a060020a038f16917f6bb0b384ce772133df63560651bc8c727c53306cec1d51e2cbf8ea35fb8f2ec19181900360200190a350505098975050505050505050565b83333014806118d257506118d281336128ba565b15156119175760405160008051602061424f83398151915281526004018080602001828103825260238152602001806142b76023913960400191505060405180910390fd5b600254604080517f4a4fbeec000000000000000000000000000000000000000000000000000000008152600160a060020a038089166004830152915188939290921691634a4fbeec91602480820192602092909190829003018186803b15801561198057600080fd5b505afa158015611994573d6000803e3d6000fd5b505050506040513d60208110156119aa57600080fd5b5051156119f05760405160008051602061424f833981519152815260040180806020018281038252602881526020018061428f6028913960400191505060405180910390fd5b60048054604080517f7e5a4eb9000000000000000000000000000000000000000000000000000000008152600160a060020a0388811694820194909452905160009390921691637e5a4eb991602480820192602092909190829003018186803b158015611a5c57600080fd5b505afa158015611a70573d6000803e3d6000fd5b505050506040513d6020811015611a8657600080fd5b50519050611a958782866138f6565b600354611aae9088908390600160a060020a0316613a69565b60408051600160a060020a0387811682526020820187905282518993918b16927ff1dec7a92ef063b16d2098bb05803ed604936a7ff30f0d245fe881baf96e8e6b92908290030190a350505050505050565b60008533301480611b165750611b1681336128ba565b1515611b5b5760405160008051602061424f83398151915281526004018080602001828103825260238152602001806142b76023913960400191505060405180910390fd5b600254604080517f4a4fbeec000000000000000000000000000000000000000000000000000000008152600160a060020a03808b16600483015291518a939290921691634a4fbeec91602480820192602092909190829003018186803b158015611bc457600080fd5b505afa158015611bd8573d6000803e3d6000fd5b505050506040513d6020811015611bee57600080fd5b505115611c345760405160008051602061424f833981519152815260040180806020018281038252602881526020018061428f6028913960400191505060405180910390fd5b6040805160028082526060808301845292602083019080388339505060048054604080517f7e5a4eb9000000000000000000000000000000000000000000000000000000008152600160a060020a038e8116948201949094529051949550911692637e5a4eb992506024808301926020929190829003018186803b158015611cbb57600080fd5b505afa158015611ccf573d6000803e3d6000fd5b505050506040513d6020811015611ce557600080fd5b5051815182906000908110611cf657fe5b600160a060020a03928316602091820290920181019190915260048054604080517f7e5a4eb90000000000000000000000000000000000000000000000000000000081528b86169381019390935251931692637e5a4eb992602480840193919291829003018186803b158015611d6b57600080fd5b505afa158015611d7f573d6000803e3d6000fd5b505050506040513d6020811015611d9557600080fd5b5051815182906001908110611da657fe5b600160a060020a039283166020918202909201810191909152600354604051602481018381528551604483015285518e861695638f6f03329594169360009388939283926064909201918581019102808383895b83811015611e12578181015183820152602001611dfa565b50506040805193909501838103601f19018452855250506020810180517fc2998238000000000000000000000000000000000000000000000000000000006000805160206142da833981519152909116178152925163ffffffff8a167c0100000000000000000000000000000000000000000000000000000000028152600160a060020a0389166004820190815260248201899052606060448301908152835160648401528351939850909650945060840192915080838360005b83811015611ee5578181015183820152602001611ecd565b50505050905090810190601f168015611f125780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b158015611f3357600080fd5b505af1158015611f47573d6000803e3d6000fd5b50505050611f6f89826000815181101515611f5e57fe5b906020019060200201518a8a613c83565b611f9289826001815181101515611f8257fe5b9060200190602002015187612be5565b60408051600160a060020a038a81168252602082018a90528881168284015260608201889052915186928c16917ff9d7e11fb5d8d1e0a4be344a6a6adfb912161747edf926fb283150d07c39c108919081900360800190a350505095945050505050565b833330148061200a575061200a81336128ba565b151561204f5760405160008051602061424f83398151915281526004018080602001828103825260238152602001806142b76023913960400191505060405180910390fd5b600254604080517f4a4fbeec000000000000000000000000000000000000000000000000000000008152600160a060020a038089166004830152915188939290921691634a4fbeec91602480820192602092909190829003018186803b1580156120b857600080fd5b505afa1580156120cc573d6000803e3d6000fd5b505050506040513d60208110156120e257600080fd5b5051156121285760405160008051602061424f833981519152815260040180806020018281038252602881526020018061428f6028913960400191505060405180910390fd5b60048054604080517f7e5a4eb9000000000000000000000000000000000000000000000000000000008152600160a060020a0388811694820194909452905160009390921691637e5a4eb991602480820192602092909190829003018186803b15801561219457600080fd5b505afa1580156121a8573d6000803e3d6000fd5b505050506040513d60208110156121be57600080fd5b50516003549091506121dc9088908390600160a060020a031661295a565b6121e887828787613c83565b60408051600160a060020a0387811682526020820187905282518993918b16927fdd016248708c391ccb5c72f9258127c75b9b291ac1479513fe4f331c3489013792908290030190a350505050505050565b60016020526000908152604090205481565b60048054604080517f7e5a4eb9000000000000000000000000000000000000000000000000000000008152600160a060020a0385811694820194909452905160009384938493911691637e5a4eb991602480820192602092909190829003018186803b1580156122bb57600080fd5b505afa1580156122cf573d6000803e3d6000fd5b505050506040513d60208110156122e557600080fd5b5051604080517f70a08231000000000000000000000000000000000000000000000000000000008152600160a060020a0388811660048301529151929350600092918416916370a0823191602480820192602092909190829003018186803b15801561235057600080fd5b505afa158015612364573d6000803e3d6000fd5b505050506040513d602081101561237a57600080fd5b5051604080517f182df0f50000000000000000000000000000000000000000000000000000000081529051919250600091600160a060020a0385169163182df0f5916004808301926020929190829003018186803b1580156123db57600080fd5b505afa1580156123ef573d6000803e3d6000fd5b505050506040513d602081101561240557600080fd5b50519050612425670de0b6b3a7640000610eae848463ffffffff612da716565b976000975095505050505050565b600254600160a060020a031681565b8333301480612456575061245681336128ba565b151561249b5760405160008051602061424f83398151915281526004018080602001828103825260238152602001806142b76023913960400191505060405180910390fd5b600254604080517f4a4fbeec000000000000000000000000000000000000000000000000000000008152600160a060020a038089166004830152915188939290921691634a4fbeec91602480820192602092909190829003018186803b15801561250457600080fd5b505afa158015612518573d6000803e3d6000fd5b505050506040513d602081101561252e57600080fd5b5051156125745760405160008051602061424f833981519152815260040180806020018281038252602881526020018061428f6028913960400191505060405180910390fd5b60048054604080517f7e5a4eb9000000000000000000000000000000000000000000000000000000008152600160a060020a0388811694820194909452905160009390921691637e5a4eb991602480820192602092909190829003018186803b1580156125e057600080fd5b505afa1580156125f4573d6000803e3d6000fd5b505050506040513d602081101561260a57600080fd5b50519050612619878286612f70565b6003546126329088908390600160a060020a0316613a69565b60408051600160a060020a0387811682526020820187905282518993918b16927fbfa42ea7d37497e6a09441fadba47d64b610b6b1183d895dd0a42507973430bf92908290030190a350505050505050565b6000843330148061269a575061269a81336128ba565b15156126df5760405160008051602061424f83398151915281526004018080602001828103825260238152602001806142b76023913960400191505060405180910390fd5b600254604080517f4a4fbeec000000000000000000000000000000000000000000000000000000008152600160a060020a03808a166004830152915189939290921691634a4fbeec91602480820192602092909190829003018186803b15801561274857600080fd5b505afa15801561275c573d6000803e3d6000fd5b505050506040513d602081101561277257600080fd5b5051156127b85760405160008051602061424f833981519152815260040180806020018281038252602881526020018061428f6028913960400191505060405180910390fd5b60048054604080517f7e5a4eb9000000000000000000000000000000000000000000000000000000008152600160a060020a038a811694820194909452905160009390921691637e5a4eb991602480820192602092909190829003018186803b15801561282457600080fd5b505afa158015612838573d6000803e3d6000fd5b505050506040513d602081101561284e57600080fd5b5051905061285e88828989613c83565b60408051600160a060020a038981168252602082018990528183018890529151889650918a16917ff8eece150ed2126815122a6def9737751aecd814379d4ce8c9edd07133a49cdb9181900360600190a2505050949350505050565b600081600160a060020a031683600160a060020a0316638da5cb5b6040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801561291b57600080fd5b505afa15801561292f573d6000803e3d6000fd5b505050506040513d602081101561294557600080fd5b5051600160a060020a03161490505b92915050565b604080517f929fe9a1000000000000000000000000000000000000000000000000000000008152600160a060020a0385811660048301528481166024830152915160009284169163929fe9a1916044808301926020929190829003018186803b1580156129c657600080fd5b505afa1580156129da573d6000803e3d6000fd5b505050506040513d60208110156129f057600080fd5b5051905080151561103b576040805160018082528183019092526060916020808301908038833901905050905083816000815181101515612a2d57fe5b90602001906020020190600160a060020a03169081600160a060020a03168152505084600160a060020a0316638f6f0332846000846040516024018080602001828103825283818151815260200191508051906020019060200280838360005b83811015612aa5578181015183820152602001612a8d565b50506040805193909501838103601f19018452855250506020810180517fc2998238000000000000000000000000000000000000000000000000000000006000805160206142da833981519152909116178152925163ffffffff8a167c0100000000000000000000000000000000000000000000000000000000028152600160a060020a0389166004820190815260248201899052606060448301908152835160648401528351939850909650945060840192915080838360005b83811015612b78578181015183820152602001612b60565b50505050905090810190601f168015612ba55780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b158015612bc657600080fd5b505af1158015612bda573d6000803e3d6000fd5b505050505050505050565b600160a060020a0382161515612c345760405160008051602061424f83398151915281526004018080602001828103825260248152602001806142fa6024913960400191505060405180910390fd5b60008111612c91576040805160008051602061424f833981519152815260206004820152601c60248201527f436f6d706f756e643a20616d6f756e742063616e6e6f74206265203000000000604482015290519081900360640190fd5b604080516024808201849052825180830382018152604492830184526020810180516000805160206142da833981519152167fc5ebeaec00000000000000000000000000000000000000000000000000000000178152935160008051602061426f8339815191528152600160a060020a038781166004830190815260009483018590526060958301958652835160648401528351918a1696638f6f0332968a969594929390926084019190808383895b83811015612d59578181015183820152602001612d41565b50505050905090810190601f168015612d865780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b15801561084357600080fd5b6000821515612db857506000612954565b828202828482811515612dc757fe5b0414612dd257600080fd5b9392505050565b6000808211612de757600080fd5b60008284811515612df457fe5b04949350505050565b600160a060020a0382161515612e4c5760405160008051602061424f83398151915281526004018080602001828103825260248152602001806142fa6024913960400191505060405180910390fd5b60008111612ea9576040805160008051602061424f833981519152815260206004820152601c60248201527f436f6d706f756e643a20616d6f756e742063616e6e6f74206265203000000000604482015290519081900360640190fd5b604080516024808201849052825180830382018152604492830184526020810180516000805160206142da833981519152167fdb006a7500000000000000000000000000000000000000000000000000000000178152935160008051602061426f8339815191528152600160a060020a038781166004830190815260009483018590526060958301958652835160648401528351918a1696638f6f0332968a9695949293909260840191908083838983811015612d59578181015183820152602001612d41565b600160a060020a0382161515612fbf5760405160008051602061424f83398151915281526004018080602001828103825260248152602001806142fa6024913960400191505060405180910390fd5b6000811161301c576040805160008051602061424f833981519152815260206004820152601c60248201527f436f6d706f756e643a20616d6f756e742063616e6e6f74206265203000000000604482015290519081900360640190fd5b606082600160a060020a03166395d89b416040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b15801561307357600080fd5b505afa158015613087573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260208110156130b057600080fd5b8101908080516401000000008111156130c857600080fd5b820160208101848111156130db57600080fd5b81516401000000008111828201871017156130f557600080fd5b5050929190505050905060405160200180807f6345544800000000000000000000000000000000000000000000000000000000815250600401905060405160208183030381529060405280519060200120816040516020018082805190602001908083835b602083106131795780518252601f19909201916020918201910161315a565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040528051906020012014156132e257604080516004808252602480830184526020830180516000805160206142da833981519152167f4e4d9fea00000000000000000000000000000000000000000000000000000000178152935160008051602061426f8339815191528152600160a060020a03888116938201938452918101879052606060448201908152845160648301528451928a1695638f6f0332958a958a95919490939260849091019180838360005b8381101561327757818101518382015260200161325f565b50505050905090810190601f1680156132a45780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b1580156132c557600080fd5b505af11580156132d9573d6000803e3d6000fd5b5050505061103b565b600083600160a060020a0316636f307dc36040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801561333957600080fd5b505afa15801561334d573d6000803e3d6000fd5b505050506040513d602081101561336357600080fd5b505160408051600160a060020a038781166024808401919091526044808401899052845180850382018152606494850186526020810180516000805160206142da833981519152167f095ea7b300000000000000000000000000000000000000000000000000000000178152955160008051602061426f833981519152815284881660048201908152600094820185905260609382019384528251968201969096528151979850938c1696638f6f033296899694959294939260840191808383895b8381101561343d578181015183820152602001613425565b50505050905090810190601f16801561346a5780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b15801561348b57600080fd5b505af115801561349f573d6000803e3d6000fd5b5050604080516024808201889052825180830382018152604492830184526020810180516000805160206142da833981519152167f0e75270200000000000000000000000000000000000000000000000000000000178152935160008051602061426f8339815191528152600160a060020a038b81166004830190815260009483018590526060958301958652835160648401528351918e169850638f6f033297508c9694959394909392608401918083838983811015612b78578181015183820152602001612b60565b6040517f190000000000000000000000000000000000000000000000000000000000000060208083018281526000602185018190526c01000000000000000000000000600160a060020a03808e16820260228801528c16026036860152604a85018a90528851909485938d938d938d938d938d938d938d939192606a909201918701908083835b602083106136105780518252601f1990920191602091820191016135f1565b51815160209384036101000a600019018019909216911617905292019586525084810193909352506040808401919091528051808403820181526060840182528051908301207f19457468657265756d205369676e6564204d6573736167653a0a3332000000006080850152609c808501919091528151808503909101815260bc909301905281519101209e9d5050505050505050505050505050565b60006136b98484614080565b949350505050565b60006024825110151515613724576040805160008051602061424f833981519152815260206004820152601660248201527f524d3a20496e76616c6964206461746157616c6c657400000000000000000000604482015290519081900360640190fd5b5060240151600160a060020a0391821691161490565b600192915050565b600080831180156137535750600182115b80156137fc575082840285600160a060020a03163110806137fc5750604080517fd6eb1bbf0000000000000000000000000000000000000000000000000000000081523060048201529051600160a060020a0387169163d6eb1bbf916024808301926020929190829003018186803b1580156137ce57600080fd5b505afa1580156137e2573d6000803e3d6000fd5b505050506040513d60208110156137f857600080fd5b5051155b15613809575060006136b9565b506001949350505050565b6000806138238484600061410a565b905061382f86826128ba565b9695505050505050565b61726c850160008511801561384e5750600183115b801561385a5750838111155b15610857573a85111561386e573a02613871565b84025b6040805160008051602061426f8339815191528152600160a060020a03848116600483015260248201849052606060448301526000606483018190529251908a1692638f6f03329260a4808201939182900301818387803b1580156138d557600080fd5b505af11580156138e9573d6000803e3d6000fd5b5050505050505050505050565b600160a060020a03821615156139455760405160008051602061424f83398151915281526004018080602001828103825260248152602001806142fa6024913960400191505060405180910390fd5b600081116139a2576040805160008051602061424f833981519152815260206004820152601c60248201527f436f6d706f756e643a20616d6f756e742063616e6e6f74206265203000000000604482015290519081900360640190fd5b604080516024808201849052825180830382018152604492830184526020810180516000805160206142da833981519152167f852a12e300000000000000000000000000000000000000000000000000000000178152935160008051602061426f8339815191528152600160a060020a038781166004830190815260009483018590526060958301958652835160648401528351918a1696638f6f0332968a9695949293909260840191908083838983811015612d59578181015183820152602001612d41565b600082600160a060020a03166370a08231856040518263ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018082600160a060020a0316600160a060020a0316815260200191505060206040518083038186803b158015613add57600080fd5b505afa158015613af1573d6000803e3d6000fd5b505050506040513d6020811015613b0757600080fd5b5051604080517f95dd9193000000000000000000000000000000000000000000000000000000008152600160a060020a0387811660048301529151929350600092918616916395dd919391602480820192602092909190829003018186803b158015613b7257600080fd5b505afa158015613b86573d6000803e3d6000fd5b505050506040513d6020811015613b9c57600080fd5b5051905081158015613bac575080155b15613c7c5760408051600160a060020a03868116602480840191909152835180840382018152604493840185526020810180516000805160206142da833981519152167fede4edd000000000000000000000000000000000000000000000000000000000178152945160008051602061426f83398151915281528884166004820190815260009382018490526060958201958652825160648301528251948c1696638f6f0332968b9692939092608490910191908083838983811015612b78578181015183820152602001612b60565b5050505050565b600160a060020a0383161515613cd25760405160008051602061424f83398151915281526004018080602001828103825260248152602001806142fa6024913960400191505060405180910390fd5b60008111613d2f576040805160008051602061424f833981519152815260206004820152601c60248201527f436f6d706f756e643a20616d6f756e742063616e6e6f74206265203000000000604482015290519081900360640190fd5b600160a060020a03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1415613e1057604080516004808252602480830184526020830180516000805160206142da833981519152167f1249c58b00000000000000000000000000000000000000000000000000000000178152935160008051602061426f8339815191528152600160a060020a03888116938201938452918101869052606060448201908152845160648301528451928a1695638f6f0332958a958995919490939260849091019180838360008381101561327757818101518382015260200161325f565b60408051600160a060020a038581166024808401919091526044808401869052845180850382018152606494850186526020810180516000805160206142da833981519152167f095ea7b300000000000000000000000000000000000000000000000000000000178152955160008051602061426f833981519152815288851660048201908152600094820185905260609382019384528251968201969096528151948b1696638f6f0332968a969394909390926084019190808383895b83811015613ee6578181015183820152602001613ece565b50505050905090810190601f168015613f135780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b158015613f3457600080fd5b505af1158015613f48573d6000803e3d6000fd5b5050604080516024808201869052825180830382018152604492830184526020810180516000805160206142da833981519152167fa0712d6800000000000000000000000000000000000000000000000000000000178152935160008051602061426f8339815191528152600160a060020a038a81166004830190815260009483018590526060958301958652835160648401528351918d169850638f6f033297508b969495939490939260840191808383895b83811015614014578181015183820152602001613ffc565b50505050905090810190601f1680156140415780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b15801561406257600080fd5b505af1158015614076573d6000803e3d6000fd5b5050505050505050565b600160a060020a03821660009081526001602052604081205482116140a757506000612954565b7001000000000000000000000000000000006fffffffffffffffffffffffffffffffff1983160443612710018111156140e4576000915050612954565b5050600160a060020a038216600090815260016020819052604090912082905592915050565b6041808202830160208101516040820151919092015160009260ff9190911691601b83148061413c57508260ff16601c145b151561414757600080fd5b604080516000815260208082018084528a905260ff8616828401526060820185905260808201849052915160019260a0808401939192601f1981019281900390910190855afa15801561419e573d6000803e3d6000fd5b5050604051601f1901519897505050505050505056fe436f6d706f756e6456323a20696e76616c6964206672616374696f6e2076616c7565524d3a207468652077616c6c657420617574686f72697a656420697320646966666572656e74207468656e2074686520746172676574206f66207468652072656c617965642064617461424d3a206d73672e73656e646572206d75737420626520616e206f776e657220666f72207468652077616c6c657408c379a0000000000000000000000000000000000000000000000000000000008f6f033200000000000000000000000000000000000000000000000000000000436f6d706f756e644d616e616765723a2077616c6c6574206d75737420626520756e6c6f636b6564424d3a206d75737420626520616e206f776e657220666f72207468652077616c6c657400000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff436f6d706f756e643a204e6f206d61726b657420666f722074617267657420746f6b656e436f6d706f756e643a206661696c656420746f20676574206163636f756e74206c6971756964697479a165627a7a72305820df2afe3c428b7d5f34ac8bdd119c67d13b70d5a8ff1b2c4b49c5005e4a4ba98f0029
Verified Source Code Partial Match
Compiler: v0.5.4+commit.9549d8ff
EVM: byzantium
Optimization: Yes (999 runs)
CompoundManager.sol 1124 lines
/**
*Submitted for verification at Etherscan.io on 2019-12-02
*/
pragma solidity ^0.5.4;
/**
* ERC20 contract interface.
*/
contract ERC20 {
function totalSupply() public view returns (uint);
function decimals() public view returns (uint);
function balanceOf(address tokenOwner) public view returns (uint balance);
function allowance(address tokenOwner, address spender) public view returns (uint remaining);
function transfer(address to, uint tokens) public returns (bool success);
function approve(address spender, uint tokens) public returns (bool success);
function transferFrom(address from, address to, uint tokens) public returns (bool success);
}
/**
* @title Module
* @dev Interface for a module.
* A module MUST implement the addModule() method to ensure that a wallet with at least one module
* can never end up in a "frozen" state.
* @author Julien Niset - <[email protected]>
*/
interface Module {
function init(BaseWallet _wallet) external;
function addModule(BaseWallet _wallet, Module _module) external;
function recoverToken(address _token) external;
}
/**
* @title BaseWallet
* @dev Simple modular wallet that authorises modules to call its invoke() method.
* Based on https://gist.github.com/Arachnid/a619d31f6d32757a4328a428286da186 by
* @author Julien Niset - <[email protected]>
*/
contract BaseWallet {
address public implementation;
address public owner;
mapping (address => bool) public authorised;
mapping (bytes4 => address) public enabled;
uint public modules;
function init(address _owner, address[] calldata _modules) external;
function authoriseModule(address _module, bool _value) external;
function enableStaticCall(address _module, bytes4 _method) external;
function setOwner(address _newOwner) external;
function invoke(address _target, uint _value, bytes calldata _data) external;
function() external payable;
}
/**
* @title ModuleRegistry
* @dev Registry of authorised modules.
* Modules must be registered before they can be authorised on a wallet.
* @author Julien Niset - <[email protected]>
*/
contract ModuleRegistry {
function registerModule(address _module, bytes32 _name) external;
function deregisterModule(address _module) external;
function registerUpgrader(address _upgrader, bytes32 _name) external;
function deregisterUpgrader(address _upgrader) external;
function recoverToken(address _token) external;
function moduleInfo(address _module) external view returns (bytes32);
function upgraderInfo(address _upgrader) external view returns (bytes32);
function isRegisteredModule(address _module) external view returns (bool);
function isRegisteredModule(address[] calldata _modules) external view returns (bool);
function isRegisteredUpgrader(address _upgrader) external view returns (bool);
}
/**
* @title GuardianStorage
* @dev Contract storing the state of wallets related to guardians and lock.
* The contract only defines basic setters and getters with no logic. Only modules authorised
* for a wallet can modify its state.
* @author Julien Niset - <[email protected]>
* @author Olivier Van Den Biggelaar - <[email protected]>
*/
contract GuardianStorage {
function addGuardian(BaseWallet _wallet, address _guardian) external;
function revokeGuardian(BaseWallet _wallet, address _guardian) external;
function guardianCount(BaseWallet _wallet) external view returns (uint256);
function getGuardians(BaseWallet _wallet) external view returns (address[] memory);
function isGuardian(BaseWallet _wallet, address _guardian) external view returns (bool);
function setLock(BaseWallet _wallet, uint256 _releaseAfter) external;
function isLocked(BaseWallet _wallet) external view returns (bool);
function getLock(BaseWallet _wallet) external view returns (uint256);
function getLocker(BaseWallet _wallet) external view returns (address);
}
interface Comptroller {
function enterMarkets(address[] calldata _cTokens) external returns (uint[] memory);
function exitMarket(address _cToken) external returns (uint);
function getAssetsIn(address _account) external view returns (address[] memory);
function getAccountLiquidity(address _account) external view returns (uint, uint, uint);
function checkMembership(address account, CToken cToken) external view returns (bool);
}
interface CToken {
function comptroller() external view returns (address);
function underlying() external view returns (address);
function symbol() external view returns (string memory);
function exchangeRateCurrent() external returns (uint256);
function exchangeRateStored() external view returns (uint256);
function balanceOf(address _account) external view returns (uint256);
function borrowBalanceCurrent(address _account) external returns (uint256);
function borrowBalanceStored(address _account) external view returns (uint256);
}
/**
* @title CompoundRegistry
* @dev Simple registry containing a mapping between underlying assets and their corresponding cToken.
* @author Julien Niset - <[email protected]>
*/
contract CompoundRegistry {
function addCToken(address _underlying, address _cToken) external;
function removeCToken(address _underlying) external;
function getCToken(address _underlying) external view returns (address);
function listUnderlyings() external view returns (address[] memory);
}
/**
* @title Interface for a contract that can invest tokens in order to earn an interest.
* @author Julien Niset - <[email protected]>
*/
interface Invest {
event InvestmentAdded(address indexed _wallet, address _token, uint256 _invested, uint256 _period);
event InvestmentRemoved(address indexed _wallet, address _token, uint256 _fraction);
/**
* @dev Invest tokens for a given period.
* @param _wallet The target wallet.
* @param _token The token address.
* @param _amount The amount of tokens to invest.
* @param _period The period over which the tokens may be locked in the investment (optional).
* @return The exact amount of tokens that have been invested.
*/
function addInvestment(
BaseWallet _wallet,
address _token,
uint256 _amount,
uint256 _period
)
external
returns (uint256 _invested);
/**
* @dev Exit invested postions.
* @param _wallet The target wallet.
* @param _token The token address.
* @param _fraction The fraction of invested tokens to exit in per 10000.
*/
function removeInvestment(
BaseWallet _wallet,
address _token,
uint256 _fraction
)
external;
/**
* @dev Get the amount of investment in a given token.
* @param _wallet The target wallet.
* @param _token The token address.
* @return The value in tokens of the investment (including interests) and the time at which the investment can be removed.
*/
function getInvestment(
BaseWallet _wallet,
address _token
)
external
view
returns (uint256 _tokenValue, uint256 _periodEnd);
}
/**
* @title Interface for a contract that can loan tokens to a wallet.
* @author Julien Niset - <[email protected]>
*/
interface Loan {
event LoanOpened(address indexed _wallet, bytes32 indexed _loanId, address _collateral, uint256 _collateralAmount, address _debtToken, uint256 _debtAmount);
event LoanClosed(address indexed _wallet, bytes32 indexed _loanId);
event CollateralAdded(address indexed _wallet, bytes32 indexed _loanId, address _collateral, uint256 _collateralAmount);
event CollateralRemoved(address indexed _wallet, bytes32 indexed _loanId, address _collateral, uint256 _collateralAmount);
event DebtAdded(address indexed _wallet, bytes32 indexed _loanId, address _debtToken, uint256 _debtAmount);
event DebtRemoved(address indexed _wallet, bytes32 indexed _loanId, address _debtToken, uint256 _debtAmount);
/**
* @dev Opens a collateralized loan.
* @param _wallet The target wallet.
* @param _collateral The token used as a collateral.
* @param _collateralAmount The amount of collateral token provided.
* @param _debtToken The token borrowed.
* @param _debtAmount The amount of tokens borrowed.
* @return (optional) An ID for the loan when the provider enables users to create multiple distinct loans.
*/
function openLoan(
BaseWallet _wallet,
address _collateral,
uint256 _collateralAmount,
address _debtToken,
uint256 _debtAmount
)
external
returns (bytes32 _loanId);
/**
* @dev Closes a collateralized loan by repaying all debts (plus interest) and redeeming all collateral (plus interest).
* @param _wallet The target wallet.
* @param _loanId The ID of the loan if any, 0 otherwise.
*/
function closeLoan(
BaseWallet _wallet,
bytes32 _loanId
)
external;
/**
* @dev Adds collateral to a loan identified by its ID.
* @param _wallet The target wallet.
* @param _loanId The ID of the loan if any, 0 otherwise.
* @param _collateral The token used as a collateral.
* @param _collateralAmount The amount of collateral to add.
*/
function addCollateral(
BaseWallet _wallet,
bytes32 _loanId,
address _collateral,
uint256 _collateralAmount
)
external;
/**
* @dev Removes collateral from a loan identified by its ID.
* @param _wallet The target wallet.
* @param _loanId The ID of the loan if any, 0 otherwise.
* @param _collateral The token used as a collateral.
* @param _collateralAmount The amount of collateral to remove.
*/
function removeCollateral(
BaseWallet _wallet,
bytes32 _loanId,
address _collateral,
uint256 _collateralAmount
)
external;
/**
* @dev Increases the debt by borrowing more token from a loan identified by its ID.
* @param _wallet The target wallet.
* @param _loanId The ID of the loan if any, 0 otherwise.
* @param _debtToken The token borrowed.
* @param _debtAmount The amount of token to borrow.
*/
function addDebt(
BaseWallet _wallet,
bytes32 _loanId,
address _debtToken,
uint256 _debtAmount
)
external;
/**
* @dev Decreases the debt by repaying some token from a loan identified by its ID.
* @param _wallet The target wallet.
* @param _loanId The ID of the loan if any, 0 otherwise.
* @param _debtToken The token to repay.
* @param _debtAmount The amount of token to repay.
*/
function removeDebt(
BaseWallet _wallet,
bytes32 _loanId,
address _debtToken,
uint256 _debtAmount
)
external;
/**
* @dev Gets information about a loan identified by its ID.
* @param _wallet The target wallet.
* @param _loanId The ID of the loan if any, 0 otherwise.
* @return a status [0: no loan, 1: loan is safe, 2: loan is unsafe and can be liquidated, 3: unable to provide info]
* and a value (in ETH) representing the value that could still be borrowed when status = 1; or the value of the collateral
* that should be added to avoid liquidation when status = 2.
*/
function getLoan(
BaseWallet _wallet,
bytes32 _loanId
)
external
view
returns (uint8 _status, uint256 _ethValue);
}
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, reverts on overflow.
*/
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-solidity/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b);
return c;
}
/**
* @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0); // Solidity only automatically asserts when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
uint256 c = a - b;
return c;
}
/**
* @dev Adds two numbers, reverts on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
/**
* @dev Divides two numbers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0);
return a % b;
}
/**
* @dev Returns ceil(a / b).
*/
function ceil(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a / b;
if(a % b == 0) {
return c;
}
else {
return c + 1;
}
}
}
/**
* @title BaseModule
* @dev Basic module that contains some methods common to all modules.
* @author Julien Niset - <[email protected]>
*/
contract BaseModule is Module {
// The adddress of the module registry.
ModuleRegistry internal registry;
event ModuleCreated(bytes32 name);
event ModuleInitialised(address wallet);
constructor(ModuleRegistry _registry, bytes32 _name) public {
registry = _registry;
emit ModuleCreated(_name);
}
/**
* @dev Throws if the sender is not the target wallet of the call.
*/
modifier onlyWallet(BaseWallet _wallet) {
require(msg.sender == address(_wallet), "BM: caller must be wallet");
_;
}
/**
* @dev Throws if the sender is not the owner of the target wallet or the module itself.
*/
modifier onlyWalletOwner(BaseWallet _wallet) {
require(msg.sender == address(this) || isOwner(_wallet, msg.sender), "BM: must be an owner for the wallet");
_;
}
/**
* @dev Throws if the sender is not the owner of the target wallet.
*/
modifier strictOnlyWalletOwner(BaseWallet _wallet) {
require(isOwner(_wallet, msg.sender), "BM: msg.sender must be an owner for the wallet");
_;
}
/**
* @dev Inits the module for a wallet by logging an event.
* The method can only be called by the wallet itself.
* @param _wallet The wallet.
*/
function init(BaseWallet _wallet) external onlyWallet(_wallet) {
emit ModuleInitialised(address(_wallet));
}
/**
* @dev Adds a module to a wallet. First checks that the module is registered.
* @param _wallet The target wallet.
* @param _module The modules to authorise.
*/
function addModule(BaseWallet _wallet, Module _module) external strictOnlyWalletOwner(_wallet) {
require(registry.isRegisteredModule(address(_module)), "BM: module is not registered");
_wallet.authoriseModule(address(_module), true);
}
/**
* @dev Utility method enbaling anyone to recover ERC20 token sent to the
* module by mistake and transfer them to the Module Registry.
* @param _token The token to recover.
*/
function recoverToken(address _token) external {
uint total = ERC20(_token).balanceOf(address(this));
ERC20(_token).transfer(address(registry), total);
}
/**
* @dev Helper method to check if an address is the owner of a target wallet.
* @param _wallet The target wallet.
* @param _addr The address.
*/
function isOwner(BaseWallet _wallet, address _addr) internal view returns (bool) {
return _wallet.owner() == _addr;
}
}
/**
* @title RelayerModule
* @dev Base module containing logic to execute transactions signed by eth-less accounts and sent by a relayer.
* @author Julien Niset - <[email protected]>
*/
contract RelayerModule is Module {
uint256 constant internal BLOCKBOUND = 10000;
mapping (address => RelayerConfig) public relayer;
struct RelayerConfig {
uint256 nonce;
mapping (bytes32 => bool) executedTx;
}
event TransactionExecuted(address indexed wallet, bool indexed success, bytes32 signedHash);
/**
* @dev Throws if the call did not go through the execute() method.
*/
modifier onlyExecute {
require(msg.sender == address(this), "RM: must be called via execute()");
_;
}
/* ***************** Abstract method ************************* */
/**
* @dev Gets the number of valid signatures that must be provided to execute a
* specific relayed transaction.
* @param _wallet The target wallet.
* @param _data The data of the relayed transaction.
* @return The number of required signatures.
*/
function getRequiredSignatures(BaseWallet _wallet, bytes memory _data) internal view returns (uint256);
/**
* @dev Validates the signatures provided with a relayed transaction.
* The method MUST throw if one or more signatures are not valid.
* @param _wallet The target wallet.
* @param _data The data of the relayed transaction.
* @param _signHash The signed hash representing the relayed transaction.
* @param _signatures The signatures as a concatenated byte array.
*/
function validateSignatures(BaseWallet _wallet, bytes memory _data, bytes32 _signHash, bytes memory _signatures) internal view returns (bool);
/* ************************************************************ */
/**
* @dev Executes a relayed transaction.
* @param _wallet The target wallet.
* @param _data The data for the relayed transaction
* @param _nonce The nonce used to prevent replay attacks.
* @param _signatures The signatures as a concatenated byte array.
* @param _gasPrice The gas price to use for the gas refund.
* @param _gasLimit The gas limit to use for the gas refund.
*/
function execute(
BaseWallet _wallet,
bytes calldata _data,
uint256 _nonce,
bytes calldata _signatures,
uint256 _gasPrice,
uint256 _gasLimit
)
external
returns (bool success)
{
uint startGas = gasleft();
bytes32 signHash = getSignHash(address(this), address(_wallet), 0, _data, _nonce, _gasPrice, _gasLimit);
require(checkAndUpdateUniqueness(_wallet, _nonce, signHash), "RM: Duplicate request");
require(verifyData(address(_wallet), _data), "RM: the wallet authorized is different then the target of the relayed data");
uint256 requiredSignatures = getRequiredSignatures(_wallet, _data);
if((requiredSignatures * 65) == _signatures.length) {
if(verifyRefund(_wallet, _gasLimit, _gasPrice, requiredSignatures)) {
if(requiredSignatures == 0 || validateSignatures(_wallet, _data, signHash, _signatures)) {
// solium-disable-next-line security/no-call-value
(success,) = address(this).call(_data);
refund(_wallet, startGas - gasleft(), _gasPrice, _gasLimit, requiredSignatures, msg.sender);
}
}
}
emit TransactionExecuted(address(_wallet), success, signHash);
}
/**
* @dev Gets the current nonce for a wallet.
* @param _wallet The target wallet.
*/
function getNonce(BaseWallet _wallet) external view returns (uint256 nonce) {
return relayer[address(_wallet)].nonce;
}
/**
* @dev Generates the signed hash of a relayed transaction according to ERC 1077.
* @param _from The starting address for the relayed transaction (should be the module)
* @param _to The destination address for the relayed transaction (should be the wallet)
* @param _value The value for the relayed transaction
* @param _data The data for the relayed transaction
* @param _nonce The nonce used to prevent replay attacks.
* @param _gasPrice The gas price to use for the gas refund.
* @param _gasLimit The gas limit to use for the gas refund.
*/
function getSignHash(
address _from,
address _to,
uint256 _value,
bytes memory _data,
uint256 _nonce,
uint256 _gasPrice,
uint256 _gasLimit
)
internal
pure
returns (bytes32)
{
return keccak256(
abi.encodePacked(
"\x19Ethereum Signed Message:\n32",
keccak256(abi.encodePacked(byte(0x19), byte(0), _from, _to, _value, _data, _nonce, _gasPrice, _gasLimit))
));
}
/**
* @dev Checks if the relayed transaction is unique.
* @param _wallet The target wallet.
* @param _nonce The nonce
* @param _signHash The signed hash of the transaction
*/
function checkAndUpdateUniqueness(BaseWallet _wallet, uint256 _nonce, bytes32 _signHash) internal returns (bool) {
if(relayer[address(_wallet)].executedTx[_signHash] == true) {
return false;
}
relayer[address(_wallet)].executedTx[_signHash] = true;
return true;
}
/**
* @dev Checks that a nonce has the correct format and is valid.
* It must be constructed as nonce = {block number}{timestamp} where each component is 16 bytes.
* @param _wallet The target wallet.
* @param _nonce The nonce
*/
function checkAndUpdateNonce(BaseWallet _wallet, uint256 _nonce) internal returns (bool) {
if(_nonce <= relayer[address(_wallet)].nonce) {
return false;
}
uint256 nonceBlock = (_nonce & 0xffffffffffffffffffffffffffffffff00000000000000000000000000000000) >> 128;
if(nonceBlock > block.number + BLOCKBOUND) {
return false;
}
relayer[address(_wallet)].nonce = _nonce;
return true;
}
/**
* @dev Recovers the signer at a given position from a list of concatenated signatures.
* @param _signedHash The signed hash
* @param _signatures The concatenated signatures.
* @param _index The index of the signature to recover.
*/
function recoverSigner(bytes32 _signedHash, bytes memory _signatures, uint _index) internal pure returns (address) {
uint8 v;
bytes32 r;
bytes32 s;
// we jump 32 (0x20) as the first slot of bytes contains the length
// we jump 65 (0x41) per signature
// for v we load 32 bytes ending with v (the first 31 come from s) then apply a mask
// solium-disable-next-line security/no-inline-assembly
assembly {
r := mload(add(_signatures, add(0x20,mul(0x41,_index))))
s := mload(add(_signatures, add(0x40,mul(0x41,_index))))
v := and(mload(add(_signatures, add(0x41,mul(0x41,_index)))), 0xff)
}
require(v == 27 || v == 28);
return ecrecover(_signedHash, v, r, s);
}
/**
* @dev Refunds the gas used to the Relayer.
* For security reasons the default behavior is to not refund calls with 0 or 1 signatures.
* @param _wallet The target wallet.
* @param _gasUsed The gas used.
* @param _gasPrice The gas price for the refund.
* @param _gasLimit The gas limit for the refund.
* @param _signatures The number of signatures used in the call.
* @param _relayer The address of the Relayer.
*/
function refund(BaseWallet _wallet, uint _gasUsed, uint _gasPrice, uint _gasLimit, uint _signatures, address _relayer) internal {
uint256 amount = 29292 + _gasUsed; // 21000 (transaction) + 7620 (execution of refund) + 672 to log the event + _gasUsed
// only refund if gas price not null, more than 1 signatures, gas less than gasLimit
if(_gasPrice > 0 && _signatures > 1 && amount <= _gasLimit) {
if(_gasPrice > tx.gasprice) {
amount = amount * tx.gasprice;
}
else {
amount = amount * _gasPrice;
}
_wallet.invoke(_relayer, amount, "");
}
}
/**
* @dev Returns false if the refund is expected to fail.
* @param _wallet The target wallet.
* @param _gasUsed The expected gas used.
* @param _gasPrice The expected gas price for the refund.
*/
function verifyRefund(BaseWallet _wallet, uint _gasUsed, uint _gasPrice, uint _signatures) internal view returns (bool) {
if(_gasPrice > 0
&& _signatures > 1
&& (address(_wallet).balance < _gasUsed * _gasPrice || _wallet.authorised(address(this)) == false)) {
return false;
}
return true;
}
/**
* @dev Checks that the wallet address provided as the first parameter of the relayed data is the same
* as the wallet passed as the input of the execute() method.
@return false if the addresses are different.
*/
function verifyData(address _wallet, bytes memory _data) private pure returns (bool) {
require(_data.length >= 36, "RM: Invalid dataWallet");
address dataWallet;
// solium-disable-next-line security/no-inline-assembly
assembly {
//_data = {length:32}{sig:4}{_wallet:32}{...}
dataWallet := mload(add(_data, 0x24))
}
return dataWallet == _wallet;
}
/**
* @dev Parses the data to extract the method signature.
*/
function functionPrefix(bytes memory _data) internal pure returns (bytes4 prefix) {
require(_data.length >= 4, "RM: Invalid functionPrefix");
// solium-disable-next-line security/no-inline-assembly
assembly {
prefix := mload(add(_data, 0x20))
}
}
}
/**
* @title OnlyOwnerModule
* @dev Module that extends BaseModule and RelayerModule for modules where the execute() method
* must be called with one signature frm the owner.
* @author Julien Niset - <[email protected]>
*/
contract OnlyOwnerModule is BaseModule, RelayerModule {
// *************** Implementation of RelayerModule methods ********************* //
// Overrides to use the incremental nonce and save some gas
function checkAndUpdateUniqueness(BaseWallet _wallet, uint256 _nonce, bytes32 _signHash) internal returns (bool) {
return checkAndUpdateNonce(_wallet, _nonce);
}
function validateSignatures(BaseWallet _wallet, bytes memory _data, bytes32 _signHash, bytes memory _signatures) internal view returns (bool) {
address signer = recoverSigner(_signHash, _signatures, 0);
return isOwner(_wallet, signer); // "OOM: signer must be owner"
}
function getRequiredSignatures(BaseWallet _wallet, bytes memory _data) internal view returns (uint256) {
return 1;
}
}
/**
* @title CompoundManager
* @dev Module to invest and borrow tokens with CompoundV2
* @author Julien Niset - <[email protected]>
*/
contract CompoundManager is Loan, Invest, BaseModule, RelayerModule, OnlyOwnerModule {
bytes32 constant NAME = "CompoundManager";
// The Guardian storage contract
GuardianStorage public guardianStorage;
// The Compound Comptroller contract
Comptroller public comptroller;
// The registry mapping underlying with cTokens
CompoundRegistry public compoundRegistry;
// Mock token address for ETH
address constant internal ETH_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
using SafeMath for uint256;
/**
* @dev Throws if the wallet is locked.
*/
modifier onlyWhenUnlocked(BaseWallet _wallet) {
// solium-disable-next-line security/no-block-members
require(!guardianStorage.isLocked(_wallet), "CompoundManager: wallet must be unlocked");
_;
}
constructor(
ModuleRegistry _registry,
GuardianStorage _guardianStorage,
Comptroller _comptroller,
CompoundRegistry _compoundRegistry
)
BaseModule(_registry, NAME)
public
{
guardianStorage = _guardianStorage;
comptroller = _comptroller;
compoundRegistry = _compoundRegistry;
}
/* ********************************** Implementation of Loan ************************************* */
/**
* @dev Opens a collateralized loan.
* @param _wallet The target wallet.
* @param _collateral The token used as a collateral.
* @param _collateralAmount The amount of collateral token provided.
* @param _debtToken The token borrowed.
* @param _debtAmount The amount of tokens borrowed.
* @return bytes32(0) as Compound does not allow the creation of multiple loans.
*/
function openLoan(
BaseWallet _wallet,
address _collateral,
uint256 _collateralAmount,
address _debtToken,
uint256 _debtAmount
)
external
onlyWalletOwner(_wallet)
onlyWhenUnlocked(_wallet)
returns (bytes32 _loanId)
{
address[] memory markets = new address[](2);
markets[0] = compoundRegistry.getCToken(_collateral);
markets[1] = compoundRegistry.getCToken(_debtToken);
_wallet.invoke(address(comptroller), 0, abi.encodeWithSignature("enterMarkets(address[])", markets));
mint(_wallet, markets[0], _collateral, _collateralAmount);
borrow(_wallet, markets[1], _debtAmount);
emit LoanOpened(address(_wallet), _loanId, _collateral, _collateralAmount, _debtToken, _debtAmount);
}
/**
* @dev Closes the collateralized loan in all markets by repaying all debts (plus interest). Note that it does not redeem the collateral.
* @param _wallet The target wallet.
* @param _loanId bytes32(0) as Compound does not allow the creation of multiple loans.
*/
function closeLoan(
BaseWallet _wallet,
bytes32 _loanId
)
external
onlyWalletOwner(_wallet)
onlyWhenUnlocked(_wallet)
{
address[] memory markets = comptroller.getAssetsIn(address(_wallet));
for(uint i = 0; i < markets.length; i++) {
address cToken = markets[i];
uint debt = CToken(cToken).borrowBalanceCurrent(address(_wallet));
if(debt > 0) {
repayBorrow(_wallet, cToken, debt);
uint collateral = CToken(cToken).balanceOf(address(_wallet));
if(collateral == 0) {
_wallet.invoke(address(comptroller), 0, abi.encodeWithSignature("exitMarket(address)", address(cToken)));
}
}
}
emit LoanClosed(address(_wallet), _loanId);
}
/**
* @dev Adds collateral to a loan identified by its ID.
* @param _wallet The target wallet.
* @param _loanId bytes32(0) as Compound does not allow the creation of multiple loans.
* @param _collateral The token used as a collateral.
* @param _collateralAmount The amount of collateral to add.
*/
function addCollateral(
BaseWallet _wallet,
bytes32 _loanId,
address _collateral,
uint256 _collateralAmount
)
external
onlyWalletOwner(_wallet)
onlyWhenUnlocked(_wallet)
{
address cToken = compoundRegistry.getCToken(_collateral);
enterMarketIfNeeded(_wallet, cToken, address(comptroller));
mint(_wallet, cToken, _collateral, _collateralAmount);
emit CollateralAdded(address(_wallet), _loanId, _collateral, _collateralAmount);
}
/**
* @dev Removes collateral from a loan identified by its ID.
* @param _wallet The target wallet.
* @param _loanId bytes32(0) as Compound does not allow the creation of multiple loans.
* @param _collateral The token used as a collateral.
* @param _collateralAmount The amount of collateral to remove.
*/
function removeCollateral(
BaseWallet _wallet,
bytes32 _loanId,
address _collateral,
uint256 _collateralAmount
)
external
onlyWalletOwner(_wallet)
onlyWhenUnlocked(_wallet)
{
address cToken = compoundRegistry.getCToken(_collateral);
redeemUnderlying(_wallet, cToken, _collateralAmount);
exitMarketIfNeeded(_wallet, cToken, address(comptroller));
emit CollateralRemoved(address(_wallet), _loanId, _collateral, _collateralAmount);
}
/**
* @dev Increases the debt by borrowing more token from a loan identified by its ID.
* @param _wallet The target wallet.
* @param _loanId bytes32(0) as Compound does not allow the creation of multiple loans.
* @param _debtToken The token borrowed.
* @param _debtAmount The amount of token to borrow.
*/
function addDebt(
BaseWallet _wallet,
bytes32 _loanId,
address _debtToken,
uint256 _debtAmount
)
external
onlyWalletOwner(_wallet)
onlyWhenUnlocked(_wallet)
{
address dToken = compoundRegistry.getCToken(_debtToken);
enterMarketIfNeeded(_wallet, dToken, address(comptroller));
borrow(_wallet, dToken, _debtAmount);
emit DebtAdded(address(_wallet), _loanId, _debtToken, _debtAmount);
}
/**
* @dev Decreases the debt by repaying some token from a loan identified by its ID.
* @param _wallet The target wallet.
* @param _loanId bytes32(0) as Compound does not allow the creation of multiple loans.
* @param _debtToken The token to repay.
* @param _debtAmount The amount of token to repay.
*/
function removeDebt(
BaseWallet _wallet,
bytes32 _loanId,
address _debtToken,
uint256 _debtAmount
)
external
onlyWalletOwner(_wallet)
onlyWhenUnlocked(_wallet)
{
address dToken = compoundRegistry.getCToken(_debtToken);
repayBorrow(_wallet, dToken, _debtAmount);
exitMarketIfNeeded(_wallet, dToken, address(comptroller));
emit DebtRemoved(address(_wallet), _loanId, _debtToken, _debtAmount);
}
/**
* @dev Gets information about a loan identified by its ID.
* @param _wallet The target wallet.
* @param _loanId bytes32(0) as Compound does not allow the creation of multiple loans
* @return a status [0: no loan, 1: loan is safe, 2: loan is unsafe and can be liquidated]
* and a value (in ETH) representing the value that could still be borrowed when status = 1; or the value of the collateral
* that should be added to avoid liquidation when status = 2.
*/
function getLoan(
BaseWallet _wallet,
bytes32 _loanId
)
external
view
returns (uint8 _status, uint256 _ethValue)
{
(uint error, uint liquidity, uint shortfall) = comptroller.getAccountLiquidity(address(_wallet));
require(error == 0, "Compound: failed to get account liquidity");
if(liquidity > 0) {
return (1, liquidity);
}
if(shortfall > 0) {
return (2, shortfall);
}
return (0,0);
}
/* ********************************** Implementation of Invest ************************************* */
/**
* @dev Invest tokens for a given period.
* @param _wallet The target wallet.
* @param _token The token address.
* @param _amount The amount of tokens to invest.
* @param _period The period over which the tokens may be locked in the investment (optional).
* @return The exact amount of tokens that have been invested.
*/
function addInvestment(
BaseWallet _wallet,
address _token,
uint256 _amount,
uint256 _period
)
external
onlyWalletOwner(_wallet)
onlyWhenUnlocked(_wallet)
returns (uint256 _invested)
{
address cToken = compoundRegistry.getCToken(_token);
mint(_wallet, cToken, _token, _amount);
_invested = _amount;
emit InvestmentAdded(address(_wallet), _token, _amount, _period);
}
/**
* @dev Exit invested postions.
* @param _wallet The target wallet.
* @param _token The token address.
* @param _fraction The fraction of invested tokens to exit in per 10000.
*/
function removeInvestment(
BaseWallet _wallet,
address _token,
uint256 _fraction
)
external
onlyWalletOwner(_wallet)
onlyWhenUnlocked(_wallet)
{
require(_fraction <= 10000, "CompoundV2: invalid fraction value");
address cToken = compoundRegistry.getCToken(_token);
uint shares = CToken(cToken).balanceOf(address(_wallet));
redeem(_wallet, cToken, shares.mul(_fraction).div(10000));
emit InvestmentRemoved(address(_wallet), _token, _fraction);
}
/**
* @dev Get the amount of investment in a given token.
* @param _wallet The target wallet.
* @param _token The token address.
* @return The value in tokens of the investment (including interests) and the time at which the investment can be removed.
*/
function getInvestment(
BaseWallet _wallet,
address _token
)
external
view
returns (uint256 _tokenValue, uint256 _periodEnd)
{
address cToken = compoundRegistry.getCToken(_token);
uint amount = CToken(cToken).balanceOf(address(_wallet));
uint exchangeRateMantissa = CToken(cToken).exchangeRateStored();
_tokenValue = amount.mul(exchangeRateMantissa).div(10 ** 18);
_periodEnd = 0;
}
/* ****************************************** Compound wrappers ******************************************* */
/**
* @dev Adds underlying tokens to a cToken contract.
* @param _wallet The target wallet.
* @param _cToken The cToken contract.
* @param _token The underlying token.
* @param _amount The amount of underlying token to add.
*/
function mint(BaseWallet _wallet, address _cToken, address _token, uint256 _amount) internal {
require(_cToken != address(0), "Compound: No market for target token");
require(_amount > 0, "Compound: amount cannot be 0");
if(_token == ETH_TOKEN_ADDRESS) {
_wallet.invoke(_cToken, _amount, abi.encodeWithSignature("mint()"));
}
else {
_wallet.invoke(_token, 0, abi.encodeWithSignature("approve(address,uint256)", _cToken, _amount));
_wallet.invoke(_cToken, 0, abi.encodeWithSignature("mint(uint256)", _amount));
}
}
/**
* @dev Redeems underlying tokens from a cToken contract.
* @param _wallet The target wallet.
* @param _cToken The cToken contract.
* @param _amount The amount of cToken to redeem.
*/
function redeem(BaseWallet _wallet, address _cToken, uint256 _amount) internal {
require(_cToken != address(0), "Compound: No market for target token");
require(_amount > 0, "Compound: amount cannot be 0");
_wallet.invoke(_cToken, 0, abi.encodeWithSignature("redeem(uint256)", _amount));
}
/**
* @dev Redeems underlying tokens from a cToken contract.
* @param _wallet The target wallet.
* @param _cToken The cToken contract.
* @param _amount The amount of underlying token to redeem.
*/
function redeemUnderlying(BaseWallet _wallet, address _cToken, uint256 _amount) internal {
require(_cToken != address(0), "Compound: No market for target token");
require(_amount > 0, "Compound: amount cannot be 0");
_wallet.invoke(_cToken, 0, abi.encodeWithSignature("redeemUnderlying(uint256)", _amount));
}
/**
* @dev Borrows underlying tokens from a cToken contract.
* @param _wallet The target wallet.
* @param _cToken The cToken contract.
* @param _amount The amount of underlying tokens to borrow.
*/
function borrow(BaseWallet _wallet, address _cToken, uint256 _amount) internal {
require(_cToken != address(0), "Compound: No market for target token");
require(_amount > 0, "Compound: amount cannot be 0");
_wallet.invoke(_cToken, 0, abi.encodeWithSignature("borrow(uint256)", _amount));
}
/**
* @dev Repays some borrowed underlying tokens to a cToken contract.
* @param _wallet The target wallet.
* @param _cToken The cToken contract.
* @param _amount The amount of underlying to repay.
*/
function repayBorrow(BaseWallet _wallet, address _cToken, uint256 _amount) internal {
require(_cToken != address(0), "Compound: No market for target token");
require(_amount > 0, "Compound: amount cannot be 0");
string memory symbol = CToken(_cToken).symbol();
if(keccak256(abi.encodePacked(symbol)) == keccak256(abi.encodePacked("cETH"))) {
_wallet.invoke(_cToken, _amount, abi.encodeWithSignature("repayBorrow()"));
}
else {
address token = CToken(_cToken).underlying();
_wallet.invoke(token, 0, abi.encodeWithSignature("approve(address,uint256)", _cToken, _amount));
_wallet.invoke(_cToken, 0, abi.encodeWithSignature("repayBorrow(uint256)", _amount));
}
}
/**
* @dev Enters a cToken market if it was not entered before.
* @param _wallet The target wallet.
* @param _cToken The cToken contract.
* @param _comptroller The comptroller contract.
*/
function enterMarketIfNeeded(BaseWallet _wallet, address _cToken, address _comptroller) internal {
bool isEntered = Comptroller(_comptroller).checkMembership(address(_wallet), CToken(_cToken));
if(!isEntered) {
address[] memory market = new address[](1);
market[0] = _cToken;
_wallet.invoke(_comptroller, 0, abi.encodeWithSignature("enterMarkets(address[])", market));
}
}
/**
* @dev Exits a cToken market if there is no more collateral and debt.
* @param _wallet The target wallet.
* @param _cToken The cToken contract.
* @param _comptroller The comptroller contract.
*/
function exitMarketIfNeeded(BaseWallet _wallet, address _cToken, address _comptroller) internal {
uint collateral = CToken(_cToken).balanceOf(address(_wallet));
uint debt = CToken(_cToken).borrowBalanceStored(address(_wallet));
if(collateral == 0 && debt == 0) {
_wallet.invoke(_comptroller, 0, abi.encodeWithSignature("exitMarket(address)", _cToken));
}
}
}
Read Contract
compoundRegistry 0x47eb8d43 → address
comptroller 0x5fe3b567 → address
getInvestment 0xcac7495c → uint256, uint256
getLoan 0x81c44b5c → uint8, uint256
getNonce 0x2d0335ab → uint256
guardianStorage 0xd89784fc → address
relayer 0xc9b5ef8e → uint256
Write Contract 12 functions
These functions modify contract state and require a wallet transaction to execute.
addCollateral 0xb352d4af
address _wallet
bytes32 _loanId
address _collateral
uint256 _collateralAmount
addDebt 0x85a13f38
address _wallet
bytes32 _loanId
address _debtToken
uint256 _debtAmount
addInvestment 0xec9e13aa
address _wallet
address _token
uint256 _amount
uint256 _period
returns: uint256
addModule 0x5a1db8c4
address _wallet
address _module
closeLoan 0xa90cf0af
address _wallet
bytes32 _loanId
execute 0xaacaaf88
address _wallet
bytes _data
uint256 _nonce
bytes _signatures
uint256 _gasPrice
uint256 _gasLimit
returns: bool
init 0x19ab453c
address _wallet
openLoan 0xb02c808d
address _wallet
address _collateral
uint256 _collateralAmount
address _debtToken
uint256 _debtAmount
returns: bytes32
recoverToken 0x9be65a60
address _token
removeCollateral 0xac5f8d51
address _wallet
bytes32 _loanId
address _collateral
uint256 _collateralAmount
removeDebt 0xe8ca0ca3
address _wallet
bytes32 _loanId
address _debtToken
uint256 _debtAmount
removeInvestment 0x98d53428
address _wallet
address _token
uint256 _fraction
Recent Transactions
No transactions found for this address