Address Contract Partially Verified
Address
0x85bB8a852C29d8F100Cb97EcdF4589086D1Be2Dd
Balance
0 ETH
Nonce
6854
Code Size
23144 bytes
Creator
0xcdA7139D...8aB4 at tx 0xd91f36c0...4df4af
Indexed Transactions
0 (6,854 on-chain, 0.7% indexed)
Contract Bytecode
23144 bytes
0x608060405260043610620000b65760003560e01c806380a12c0e116200006d57806380a12c0e1462000222578063877337b01462000259578063a7a7d3bc1462000271578063c8cc2fc21462000289578063e2b4ce9714620002c0578063fc6cabe714620002d857620000b6565b806317ca930514620000b85780633f15457f146200017c57806360dbc5de14620001b057806371b9076a14620001c8578063747c31d614620001f25780637d73b231146200020a575b005b348015620000c557600080fd5b50620000b66004803603610100811015620000df57600080fd5b6001600160a01b03823581169260208101359091169160408201351515916060810135151591608082013515159160a08101359160c08201359190810190610100810160e08201356401000000008111156200013a57600080fd5b8201836020820111156200014d57600080fd5b803590602001918460208302840111640100000000831117156200017057600080fd5b50909250905062000306565b3480156200018957600080fd5b506200019462000625565b604080516001600160a01b039092168252519081900360200190f35b348015620001bd57600080fd5b50620000b662000634565b348015620001d557600080fd5b50620001e0620007a8565b60408051918252519081900360200190f35b348015620001ff57600080fd5b50620001e0620007ae565b3480156200021757600080fd5b5062000194620007d2565b3480156200022f57600080fd5b5062000194600480360360208110156200024857600080fd5b50356001600160a01b0316620007e1565b3480156200026657600080fd5b50620001e0620007fc565b3480156200027e57600080fd5b50620001e062000820565b3480156200029657600080fd5b50620000b660048036036020811015620002af57600080fd5b50356001600160a01b031662000826565b348015620002cd57600080fd5b50620001e0620009cd565b348015620002e557600080fd5b506200019460048036036020811015620002fe57600080fd5b5035620009f1565b620003113362000a19565b62000363576040805162461bcd60e51b815260206004820152601a60248201527f73656e646572206973206e6f74206120636f6e74726f6c6c6572000000000000604482015290519081900360640190fd5b6004546001111562000379576200037962000634565b600480546000919060001981019081106200039057fe5b600091825260209091200154600480546001600160a01b0390921692509080620003b657fe5b600082815260209020810160001990810180546001600160a01b031916905501905587156200044057806001600160a01b0316633c672eb7866040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156200042657600080fd5b505af11580156200043b573d6000803e3d6000fd5b505050505b8615620004a857806001600160a01b0316630f3a85d8856040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156200048e57600080fd5b505af1158015620004a3573d6000803e3d6000fd5b505050505b85156200053f57806001600160a01b031663f421764884846040518363ffffffff1660e01b815260040180806020018281038252848482818152602001925060200280828437600081840152601f19601f8201169050808301925050509350505050600060405180830381600087803b1580156200052557600080fd5b505af11580156200053a573d6000803e3d6000fd5b505050505b60408051632c90b94d60e21b81526001600160a01b038c8116600483015260006024830181905292519084169263b242e534926044808201939182900301818387803b1580156200058f57600080fd5b505af1158015620005a4573d6000803e3d6000fd5b505050506001600160a01b038a811660008181526003602090815260409182902080546001600160a01b0319168686169081179091558251908152938d169084015282810191909152517f628666dc1e342232638fe725b30d07a00b36d24d32af174fdaea535df6c1eff0916060908290030190a150505050505050505050565b6005546001600160a01b031681565b60055460065460405160009230926001926001600160a01b03909216917fe84f90570f13fe09f288f2411ff9cf50da611ed0c7db7f73d48053ffc974d396917f7f2ce995617d2816b426c5c8698c5ec2952f7a34bb10f38326f74933d5893697917fd0ff8bd67f6e25e4e4b010df582a36a0ee9b78e49afe6cc1cff5dd5a830403309190620006c39062000b78565b6001600160a01b0397881681529515156020870152939095166040808601919091526060850192909252608084015260a083019390935260c082015290519081900360e001906000f0801580156200071f573d6000803e3d6000fd5b50600480546001810182556000919091527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b0180546001600160a01b0383166001600160a01b0319909116811790915560408051918252519192507f9ede7876a6b2454072ceeaff4b6b4e6eaa5381db241b850f2a46034136fc2e6e919081900360200190a150565b60045490565b7fd0ff8bd67f6e25e4e4b010df582a36a0ee9b78e49afe6cc1cff5dd5a8304033081565b6001546001600160a01b031690565b6003602052600090815260409020546001600160a01b031681565b7fe84f90570f13fe09f288f2411ff9cf50da611ed0c7db7f73d48053ffc974d39681565b60065481565b620008313362000a19565b62000883576040805162461bcd60e51b815260206004820152601a60248201527f73656e646572206973206e6f74206120636f6e74726f6c6c6572000000000000604482015290519081900360640190fd5b6004546001111562000899576200089962000634565b60048054600091906000198101908110620008b057fe5b600091825260209091200154600480546001600160a01b0390921692509080620008d657fe5b600082815260208120820160001990810180546001600160a01b031916905590910190915560408051632c90b94d60e21b81526001600160a01b0385811660048301526024820184905291519184169263b242e5349260448084019382900301818387803b1580156200094857600080fd5b505af11580156200095d573d6000803e3d6000fd5b5050506001600160a01b0380841660008181526003602090815260409182902080549487166001600160a01b031990951685179055815193845283019190915280517fc02db5f4164f89d90905928336769906e16d79c4a77342126eb647ca9440d0789350918290030190a15050565b7f7f2ce995617d2816b426c5c8698c5ec2952f7a34bb10f38326f74933d589369781565b60048181548110620009ff57fe5b6000918252602090912001546001600160a01b0316905081565b600062000a2860025462000ab2565b6001600160a01b031663b429afeb836040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801562000a7e57600080fd5b505afa15801562000a93573d6000803e3d6000fd5b505050506040513d602081101562000aaa57600080fd5b505192915050565b6000805460408051630178b8bf60e01b81526004810185905290516001600160a01b0390921691630178b8bf91602480820192602092909190829003018186803b15801562000b0057600080fd5b505afa15801562000b15573d6000803e3d6000fd5b505050506040513d602081101562000b2c57600080fd5b505160408051631d9dabef60e11b81526004810185905290516001600160a01b0390921691633b3b57de91602480820192602092909190829003018186803b15801562000a7e57600080fd5b614ead8062000b878339019056fe60806040523480156200001157600080fd5b5060405162004ead38038062004ead833981810160405260e08110156200003757600080fd5b508051602082015160408301516060840151608085015160a086015160c090960151600180546001600160a01b038087166001600160a01b03199283161792839055600080548316938216939093179092556002849055600380548815157401000000000000000000000000000000000000000090810260ff60a01b19958c1692909416919091179390931691909117908190559697959694959394929392889188918791879186918491839188918891900460ff166200012f57604080516001600160a01b038416815290517f808639ff9c8e4732d60b6c2330de498035416d229f27a77d259680895efec1229181900360200190a15b60408051600081526001600160a01b038416602082015281517f850b3df64837d7d518b45f5aa64d104652c3b80eb5b34a8e3d9eb666cb7cdea5929181900390910190a150506040805160a080820183528382526020808301859052428385018190526000606080860182905260809586018290526009889055600a97909755600b829055600c819055600d805460ff19908116909155600e98909855855193840186526706f05b59d3b200008085529284018390529483018190529482018490529101829052600f81905560105560119190915560128190556013805490921690915594506200022e9350506001600160e01b03620002cf16915050565b505050505091505060008111620002a657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f737461626c65636f696e206e6f74207365740000000000000000000000000000604482015290519081900360640190fd5b620002be61271082026001600160e01b03620003e516565b5050601a55506200055f9350505050565b6060600080600080600080620002ed600e546200043360201b60201c565b6001600160a01b0316633efec5e96040518163ffffffff1660e01b815260040160006040518083038186803b1580156200032657600080fd5b505afa1580156200033b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260e08110156200036557600080fd5b8101908080516401000000008111156200037e57600080fd5b820160208101848111156200039257600080fd5b8151640100000000811182820187101715620003ad57600080fd5b5050602082015160408301516060840151608085015160a086015160c090960151949f939e50919c509a509850919650945092505050565b60148190556040805160a0810182528281526020810183905242918101829052600060608201819052608090910181905260158390556016929092556017556018556019805460ff19169055565b60008054604080517f0178b8bf0000000000000000000000000000000000000000000000000000000081526004810185905290516001600160a01b0390921691630178b8bf91602480820192602092909190829003018186803b1580156200049a57600080fd5b505afa158015620004af573d6000803e3d6000fd5b505050506040513d6020811015620004c657600080fd5b5051604080517f3b3b57de0000000000000000000000000000000000000000000000000000000081526004810185905290516001600160a01b0390921691633b3b57de91602480820192602092909190829003018186803b1580156200052b57600080fd5b505afa15801562000540573d6000803e3d6000fd5b505050506040513d60208110156200055757600080fd5b505192915050565b61493e806200056f6000396000f3fe60806040526004361061031a5760003560e01c80637d73b231116101ab578063cbd2ac68116100f7578063e2b4ce9711610095578063f36febda1161006f578063f36febda14610c97578063f40b51f814610cd0578063f41c431914610cfa578063f421764814610d245761031a565b8063e2b4ce9714610c2e578063e61c51ca14610c43578063eadd3cea14610c6d5761031a565b8063ce0b5bd5116100d1578063ce0b5bd514610bb0578063d251fefc14610bda578063da84b1ed14610c04578063de212bf314610c195761031a565b8063cbd2ac6814610ac3578063cc0e7e5614610aed578063cd7958dd14610b025761031a565b8063b221f31611610164578063be40ba791161013e578063be40ba7914610a41578063beabacc814610a56578063c4856cd914610a99578063c785141814610aae5761031a565b8063b221f316146109a3578063b242e534146109cd578063b87e21ef14610a085761031a565b80637d73b231146108a35780637d7d0046146108d45780637fd004fa146108e9578063877337b0146109645780638da5cb5b146109795780639b0dfd271461098e5761031a565b806332531c3c1161026a5780634aa46fde116102235780636137d670116101fd5780636137d670146107d4578063715018a61461084f57806374624c5514610864578063747c31d61461088e5761031a565b80634aa46fde146107805780635adc02ab146107955780635d2362a8146107bf5761031a565b806332531c3c146106285780633a43199f1461065b5780633bfec254146106875780633c672eb7146106b15780633f579f42146106db57806347b55a9d1461076b5761031a565b80631efd0299116102d7578063227149a3116102b1578063227149a3146105845780632587a6a21461059957806326d05ab2146105ae578063294f4025146105c35761031a565b80631efd0299146105305780632121dc751461054557806321ce918d1461055a5761031a565b806301ffc9a714610356578063027ef3eb1461039e5780630f3a85d8146103c5578063100f23fd146103f15780631127b57e1461041b5780631aa21fba146104a5575b6040805133815234602082015281517f88a5966d370b9919b20f3e2c13ff65706f196a4e32cc2c12bf57088f88525874929181900390910190a1005b34801561036257600080fd5b5061038a6004803603602081101561037957600080fd5b50356001600160e01b031916610d9f565b604080519115158252519081900360200190f35b3480156103aa57600080fd5b506103b3610db9565b60408051918252519081900360200190f35b3480156103d157600080fd5b506103ef600480360360208110156103e857600080fd5b5035610dc0565b005b3480156103fd57600080fd5b506103ef6004803603602081101561041457600080fd5b5035610eb1565b34801561042757600080fd5b5061043061104c565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561046a578181015183820152602001610452565b50505050905090810190601f1680156104975780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156104b157600080fd5b506103ef600480360360408110156104c857600080fd5b6001600160a01b038235169190810190604081016020820135600160201b8111156104f257600080fd5b82018360208201111561050457600080fd5b803590602001918460208302840111600160201b8311171561052557600080fd5b50909250905061106d565b34801561053c57600080fd5b506103b36111ed565b34801561055157600080fd5b5061038a6111fe565b34801561056657600080fd5b506103ef6004803603602081101561057d57600080fd5b503561120e565b34801561059057600080fd5b5061038a61129d565b3480156105a557600080fd5b506103b36112a6565b3480156105ba57600080fd5b5061038a6112ac565b3480156105cf57600080fd5b506105d86112b5565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156106145781810151838201526020016105fc565b505050509050019250505060405180910390f35b34801561063457600080fd5b5061038a6004803603602081101561064b57600080fd5b50356001600160a01b0316611317565b6103ef6004803603604081101561067157600080fd5b506001600160a01b03813516906020013561132c565b34801561069357600080fd5b506103ef600480360360208110156106aa57600080fd5b503561155b565b3480156106bd57600080fd5b506103ef600480360360208110156106d457600080fd5b5035611646565b3480156106e757600080fd5b50610430600480360360608110156106fe57600080fd5b6001600160a01b0382351691602081013591810190606081016040820135600160201b81111561072d57600080fd5b82018360208201111561073f57600080fd5b803590602001918460018302840111600160201b8311171561076057600080fd5b5090925090506116dd565b34801561077757600080fd5b506105d8611b48565b34801561078c57600080fd5b5061038a611ba8565b3480156107a157600080fd5b506103ef600480360360208110156107b857600080fd5b5035611bb1565b3480156107cb57600080fd5b506103b3611e73565b3480156107e057600080fd5b506103ef600480360360208110156107f757600080fd5b810190602081018135600160201b81111561081157600080fd5b82018360208201111561082357600080fd5b803590602001918460208302840111600160201b8311171561084457600080fd5b509092509050611e7f565b34801561085b57600080fd5b506103ef612066565b34801561087057600080fd5b506103ef6004803603602081101561088757600080fd5b5035612159565b34801561089a57600080fd5b506103b3612242565b3480156108af57600080fd5b506108b8612248565b604080516001600160a01b039092168252519081900360200190f35b3480156108e057600080fd5b506103b3612257565b3480156108f557600080fd5b506103ef6004803603602081101561090c57600080fd5b810190602081018135600160201b81111561092657600080fd5b82018360208201111561093857600080fd5b803590602001918460208302840111600160201b8311171561095957600080fd5b509092509050612263565b34801561097057600080fd5b506103b361254c565b34801561098557600080fd5b506108b8612552565b34801561099a57600080fd5b506103b3612561565b3480156109af57600080fd5b506103ef600480360360208110156109c657600080fd5b5035612567565b3480156109d957600080fd5b506103ef600480360360408110156109f057600080fd5b506001600160a01b038135169060200135151561264a565b348015610a1457600080fd5b506103b360048036036040811015610a2b57600080fd5b506001600160a01b0381351690602001356127f9565b348015610a4d57600080fd5b5061038a612894565b348015610a6257600080fd5b506103ef60048036036060811015610a7957600080fd5b506001600160a01b038135811691602081013590911690604001356128a3565b348015610aa557600080fd5b506103b3612a44565b348015610aba57600080fd5b5061038a612a4a565b348015610acf57600080fd5b506103ef60048036036020811015610ae657600080fd5b5035612a53565b348015610af957600080fd5b506103b3612dc2565b348015610b0e57600080fd5b506103b360048036036020811015610b2557600080fd5b810190602081018135600160201b811115610b3f57600080fd5b820183602082011115610b5157600080fd5b803590602001918460208302840111600160201b83111715610b7257600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550612dc8945050505050565b348015610bbc57600080fd5b506103ef60048036036020811015610bd357600080fd5b5035612e22565b348015610be657600080fd5b506108b860048036036020811015610bfd57600080fd5b5035612fc1565b348015610c1057600080fd5b506103b3612fe8565b348015610c2557600080fd5b5061038a612fee565b348015610c3a57600080fd5b506103b3612ffc565b348015610c4f57600080fd5b506103ef60048036036020811015610c6657600080fd5b5035613002565b348015610c7957600080fd5b506103ef60048036036020811015610c9057600080fd5b5035613166565b348015610ca357600080fd5b506103b360048036036040811015610cba57600080fd5b506001600160a01b0381351690602001356131bf565b348015610cdc57600080fd5b506103ef60048036036020811015610cf357600080fd5b5035613384565b348015610d0657600080fd5b506103ef60048036036020811015610d1d57600080fd5b50356133dd565b348015610d3057600080fd5b506103ef60048036036020811015610d4757600080fd5b810190602081018135600160201b811115610d6157600080fd5b820183602082011115610d7357600080fd5b803590602001918460208302840111600160201b83111715610d9457600080fd5b509092509050613436565b6001600160e01b031981166301ffc9a760e01b145b919050565b600c545b90565b610dc9336136f5565b610e08576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b8066038d7ea4c6800011158015610e2757506706f05b59d3b200008111155b610e625760405162461bcd60e51b815260040180806020018281038252602e815260200180614579602e913960400191505060405180910390fd5b610e73600f8263ffffffff61370916565b604080513381526020810183905281517f41ff5d5ce3b7935893a4e7269ec5caae9cca5e3bf0eb4b21d2f443489667112e929181900390910190a150565b610eba336136f5565b80610ec95750610ec93361377e565b610f17576040805162461bcd60e51b815260206004820152601a60248201527932b4ba3432b91037bbb732b91037b91031b7b73a3937b63632b960311b604482015290519081900360640190fd5b60085460ff16610f585760405162461bcd60e51b81526004018080602001828103825260298152602001806147276029913960400191505060405180910390fd5b610fbb6006805480602002602001604051908101604052809291908181526020018280548015610fb157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610f93575b5050505050612dc8565b8114610ff85760405162461bcd60e51b81526004018080602001828103825260358152602001806147c06035913960400191505060405180910390fd5b6110046006600061444a565b6008805460ff19169055604080513381526020810183905281517f7794eff834d760583543e6e510e717a5e66d2c064e225f4db448343c3e66afcf929181900390910190a150565b604051806040016040528060058152602001640322e322e360dc1b81525081565b611076336136f5565b6110b5576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b80611107576040805162461bcd60e51b815260206004820152601f60248201527f61737365742061727261792073686f756c64206265206e6f6e2d656d70747900604482015290519081900360640190fd5b60005b8181101561116a5760006111393085858581811061112457fe5b905060200201356001600160a01b0316613812565b90506111618585858581811061114b57fe5b905060200201356001600160a01b0316836128a3565b5060010161110a565b507fd4f62f23021706247dcffea245d104ae7ddaec7f23acf3d11d7136d5de6a69ad83838360405180846001600160a01b03166001600160a01b03168152602001806020018281038252848482818152602001925060200280828437600083820152604051601f909101601f1916909201829003965090945050505050a1505050565b60006111f960156138bd565b905090565b600354600160a01b900460ff1690565b611217336136f5565b611256576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b61126760098263ffffffff6138f216565b6040805182815290517f4b1b970c8a0fa761e7803ed70c13d7aca71904b13df60fbe03f981da1730da919181900360200190a150565b60195460ff1690565b600f5490565b60085460ff1681565b6060600780548060200260200160405190810160405280929190818152602001828054801561130d57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116112ef575b5050505050905090565b60046020526000908152604090205460ff1681565b611335336136f5565b611374576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b61137d82613953565b6113c3576040805162461bcd60e51b8152602060048201526012602482015271746f6b656e206e6f74206c6f616461626c6560701b604482015290519081900360640190fd5b60006113cf83836131bf565b90506113e260158263ffffffff61396d16565b60006113ef601a546139da565b90506001600160a01b038416156114975761141a6001600160a01b038516828563ffffffff613a9c16565b806001600160a01b0316631b3c96b485856040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561147a57600080fd5b505af115801561148e573d6000803e3d6000fd5b50505050611511565b806001600160a01b0316631b3c96b48486866040518463ffffffff1660e01b815260040180836001600160a01b03166001600160a01b03168152602001828152602001925050506000604051808303818588803b1580156114f757600080fd5b505af115801561150b573d6000803e3d6000fd5b50505050505b604080516001600160a01b03861681526020810185905281517f5f65674bec9af81f71be68674135a0ea3f163fb91984e3893d06da9f6ea2ce8a929181900390910190a150505050565b611564336136f5565b6115a3576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b8066038d7ea4c68000111580156115bc57506014548111155b6115f75760405162461bcd60e51b815260040180806020018281038252602d8152602001806148dd602d913960400191505060405180910390fd5b61160860158263ffffffff61370916565b604080513381526020810183905281517f0b05243483e17c3f3377aee82b7d47e5700b48288695fc08b7ecc2759afa44ef929181900390910190a150565b61164f336136f5565b61168e576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b61169f60098263ffffffff61370916565b604080513381526020810183905281517f068f112e5ec923d412be64779fe69e0fcbb6784c6617e94cccc8fd348f2e0f21929181900390910190a150565b60606116e8336136f5565b611727576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b6001600160a01b03851660009081526004602052604090205460ff166117585761175860098563ffffffff61396d16565b61176a856001600160a01b0316613bb4565b801561177a575061177a85613bba565b15611999576000806117c28786868080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613bd492505050565b6001600160a01b038216600090815260046020526040902054919350915060ff166118085760006117f388836127f9565b905061180660098263ffffffff61396d16565b505b61185885858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250506001600160a01b038b169291505063ffffffff613cde16565b604080516020808252818301909252606091602082018180388339019050509050600160f81b81601f8151811061188b57fe5b60200101906001600160f81b031916908160001a9053507ff77753fab406ecfff96d6ff2476c64a838fa9f6d37b1bf190f8546e395e3b613888888888560405180866001600160a01b03166001600160a01b03168152602001858152602001806020018060200183810383528686828181526020019250808284376000838201819052601f909101601f191690920185810384528651815286516020918201939188019250908190849084905b83811015611950578181015183820152602001611938565b50505050905090810190601f16801561197d5780820380516001836020036101000a031916815260200191505b5097505050505050505060405180910390a19250611b40915050565b60006060866001600160a01b0316868686604051808383808284376040519201945060009350909150508083038185875af1925050503d80600081146119fb576040519150601f19603f3d011682016040523d82523d6000602084013e611a00565b606091505b509150915081611a4f576040805162461bcd60e51b81526020600482015260156024820152741b1bddcb5b195d995b0818d85b1b0819985a5b1959605a1b604482015290519081900360640190fd5b7ff77753fab406ecfff96d6ff2476c64a838fa9f6d37b1bf190f8546e395e3b613878787878560405180866001600160a01b03166001600160a01b03168152602001858152602001806020018060200183810383528686828181526020019250808284376000838201819052601f909101601f191690920185810384528651815286516020918201939188019250908190849084905b83811015611afd578181015183820152602001611ae5565b50505050905090810190601f168015611b2a5780820380516001836020036101000a031916815260200191505b5097505050505050505060405180910390a19150505b949350505050565b6060600680548060200260200160405190810160405280929190818152602001828054801561130d576020028201919060005260206000209081546001600160a01b031681526001909101906020018083116112ef575050505050905090565b600d5460ff1690565b611bba3361377e565b611bf9576040805162461bcd60e51b815260206004820152601a602482015260008051602061463a833981519152604482015290519081900360640190fd5b60085460ff16611c3a5760405162461bcd60e51b81526004018080602001828103825260298152602001806147276029913960400191505060405180910390fd5b611c9b6006805480602002602001604051908101604052809291908181526020018280548015610fb1576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311610f93575050505050612dc8565b8114611cd85760405162461bcd60e51b81526004018080602001828103825260338152602001806147f56033913960400191505060405180910390fd5b60005b600654811015611dbf576004600060068381548110611cf657fe5b60009182526020808320909101546001600160a01b0316835282019290925260400190205460ff16611db75760016004600060068481548110611d3557fe5b6000918252602080832091909101546001600160a01b031683528201929092526040019020805460ff1916911515919091179055600680546005919083908110611d7b57fe5b60009182526020808320909101548354600181018555938352912090910180546001600160a01b0319166001600160a01b039092169190911790555b600101611cdb565b507fb2f6cccee7a369e23e293c25aa19bef80af11eb26deba3ea0f2a02783f752e4a33600660405180836001600160a01b03166001600160a01b03168152602001806020018281038252838181548152602001915080548015611e4b57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611e2d575b5050935050505060405180910390a1611e666006600061444a565b506008805460ff19169055565b60006111f960096138bd565b611e88336136f5565b611ec7576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b60085460ff16158015611ee25750600854610100900460ff16155b611f1d5760405162461bcd60e51b815260040180806020018281038252602e81526020018061465a602e913960400191505060405180910390fd5b60085462010000900460ff16611f645760405162461bcd60e51b81526004018080602001828103825260228152602001806145d06022913960400191505060405180910390fd5b80611fa05760405162461bcd60e51b815260040180806020018281038252602281526020018061479e6022913960400191505060405180910390fd5b611fac60078383614468565b506008805461ff00191661010017905560408051602080840282810182019093528382527ffbc0e5ca6c7e4858daf0fdb185ef5186203e74ec9c64737e93c0aeaec596e1d1928592859261201b92859185918291850190849080828437600092019190915250612dc892505050565b60405180806020018381526020018281038252858582818152602001925060200280828437600083820152604051601f909101601f1916909201829003965090945050505050a15050565b61206f336136f5565b6120ae576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b600354600160a01b900460ff1661210c576040805162461bcd60e51b815260206004820152601d60248201527f6f776e657273686970206973206e6f74207472616e7366657261626c65000000604482015290519081900360640190fd5b600380546001600160a01b0319169055604080516000808252602082015281517f850b3df64837d7d518b45f5aa64d104652c3b80eb5b34a8e3d9eb666cb7cdea5929181900390910190a1565b612162336136f5565b6121a1576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b8066038d7ea4c68000111580156121c057506706f05b59d3b200008111155b6121fb5760405162461bcd60e51b815260040180806020018281038252602e815260200180614579602e913960400191505060405180910390fd5b61220c600f8263ffffffff6138f216565b6040805182815290517faf2a77cd04c3cc155588dd3bf67b310ab4fb3b1da3cf6b8d7d4d2aa1d09b794c9181900360200190a150565b601a5490565b6001546001600160a01b031690565b60006111f9600f6138bd565b61226c336136f5565b6122ab576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b60085460ff161580156122c65750600854610100900460ff16155b6123015760405162461bcd60e51b815260040180806020018281038252602e81526020018061465a602e913960400191505060405180910390fd5b8181808060200260200160405190810160405280939291908181526020018383602002808284376000920182905250925050505b81518110156124035761235a82828151811061234d57fe5b60200260200101516136f5565b156123965760405162461bcd60e51b815260040180806020018281038252602d815260200180614771602d913960400191505060405180910390fd5b60006001600160a01b03168282815181106123ad57fe5b60200260200101516001600160a01b031614156123fb5760405162461bcd60e51b815260040180806020018281038252602c81526020018061452a602c913960400191505060405180910390fd5b600101612335565b5060085462010000900460ff1661244b5760405162461bcd60e51b81526004018080602001828103825260228152602001806145d06022913960400191505060405180910390fd5b816124875760405162461bcd60e51b81526004018080602001828103825260238152602001806145566023913960400191505060405180910390fd5b61249360068484614468565b506008805460ff1916600117905560408051602080850282810182019093528482527f9c80b3b5f68b3e017766d59e8d09b34efe6462b05c398f35cab9e271d9bc3b9c928692869261250092859185918291850190849080828437600092019190915250612dc892505050565b60405180806020018381526020018281038252858582818152602001925060200280828437600083820152604051601f909101601f1916909201829003965090945050505050a1505050565b600e5490565b6003546001600160a01b031690565b60095490565b612570336136f5565b6125af576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b8066038d7ea4c68000111580156125c857506014548111155b6126035760405162461bcd60e51b815260040180806020018281038252602d8152602001806148dd602d913960400191505060405180910390fd5b61261460158263ffffffff6138f216565b6040805182815290517fc178d379965e5657b6fc57494e392f121a14119215dfb422aad7db4cc03f2d109181900360200190a150565b612653336136f5565b612692576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b600354600160a01b900460ff166126f0576040805162461bcd60e51b815260206004820152601d60248201527f6f776e657273686970206973206e6f74207472616e7366657261626c65000000604482015290519081900360640190fd5b6001600160a01b0382166127355760405162461bcd60e51b81526004018080602001828103825260238152602001806148286023913960400191505060405180910390fd5b6003805460ff60a01b1916600160a01b831515021790558061278e57604080516001600160a01b038416815290517f808639ff9c8e4732d60b6c2330de498035416d229f27a77d259680895efec1229181900360200190a15b600354604080516001600160a01b039283168152918416602083015280517f850b3df64837d7d518b45f5aa64d104652c3b80eb5b34a8e3d9eb666cb7cdea59281900390910190a150600380546001600160a01b0319166001600160a01b0392909216919091179055565b60008060008061280886613e9c565b505050935093509350508015612886578161285c576040805162461bcd60e51b815260206004820152600f60248201526e0746f6b656e2072617465206973203608c1b604482015290519081900360640190fd5b61287c83612870878563ffffffff613fc716565b9063ffffffff61402716565b935050505061288e565b600093505050505b92915050565b60085462010000900460ff1681565b6128ac336136f5565b6128eb576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b808061293e576040805162461bcd60e51b815260206004820152601d60248201527f70726f76696465642076616c75652063616e6e6f74206265207a65726f000000604482015290519081900360640190fd5b6001600160a01b038416612999576040805162461bcd60e51b815260206004820181905260248201527f5f746f20616464726573732063616e6e6f742062652073657420746f20307830604482015290519081900360640190fd5b6001600160a01b03841660009081526004602052604090205460ff166129e957816001600160a01b038416156129d6576129d384846127f9565b90505b6129e760098263ffffffff61396d16565b505b6129f4848484614091565b604080516001600160a01b0380871682528516602082015280820184905290517fd1ba4ac2e2a11b5101f6cb4d978f514a155b421e8ec396d2d9abaf0bb02917ee9181900360600190a150505050565b60185490565b60135460ff1690565b612a5c3361377e565b612a9b576040805162461bcd60e51b815260206004820152601a602482015260008051602061463a833981519152604482015290519081900360640190fd5b600854610100900460ff16612ae15760405162461bcd60e51b81526004018080602001828103825260288152602001806146126028913960400191505060405180910390fd5b612b426007805480602002602001604051908101604052809291908181526020018280548015610fb1576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311610f93575050505050612dc8565b8114612b7f5760405162461bcd60e51b81526004018080602001828103825260478152602001806146e06047913960600191505060405180910390fd5b60005b600754811015612d0d576004600060078381548110612b9d57fe5b60009182526020808320909101546001600160a01b0316835282019290925260400190205460ff1615612d055760006004600060078481548110612bdd57fe5b6000918252602080832091909101546001600160a01b031683528201929092526040018120805460ff1916921515929092179091555b600554612c2790600163ffffffff6140f516565b811015612cef5760078281548110612c3b57fe5b600091825260209091200154600580546001600160a01b039092169183908110612c6157fe5b6000918252602090912001546001600160a01b03161415612ce757600580546000198101908110612c8e57fe5b600091825260209091200154600580546001600160a01b039092169183908110612cb457fe5b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550612cef565b600101612c13565b506005805490612d039060001983016144cb565b505b600101612b82565b507fd218c430fa348f4ce67791021b6b89c0c3eacd4ead1d8f5b83c60038ec28249b33600760405180836001600160a01b03166001600160a01b03168152602001806020018281038252838181548152602001915080548015612d9957602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612d7b575b5050935050505060405180910390a1612db46007600061444a565b506008805461ff0019169055565b60125490565b60008160405160200180828051906020019060200280838360005b83811015612dfb578181015183820152602001612de3565b50505050905001915050604051602081830303815290604052805190602001209050919050565b612e2b336136f5565b80612e3a5750612e3a3361377e565b612e88576040805162461bcd60e51b815260206004820152601a60248201527932b4ba3432b91037bbb732b91037b91031b7b73a3937b63632b960311b604482015290519081900360640190fd5b600854610100900460ff16612ece5760405162461bcd60e51b81526004018080602001828103825260288152602001806146126028913960400191505060405180910390fd5b612f2f6007805480602002602001604051908101604052809291908181526020018280548015610fb1576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311610f93575050505050612dc8565b8114612f6c5760405162461bcd60e51b81526004018080602001828103825260328152602001806148756032913960400191505060405180910390fd5b612f786007600061444a565b6008805461ff0019169055604080513381526020810183905281517f13c935eb475aa0f6e931fece83e2ac44569ce2d53460d29a6dedab40b965c8a3929181900390910190a150565b60058181548110612fce57fe5b6000918252602090912001546001600160a01b0316905081565b60155490565b600854610100900460ff1681565b60025490565b8080613055576040805162461bcd60e51b815260206004820152601d60248201527f70726f76696465642076616c75652063616e6e6f74206265207a65726f000000604482015290519081900360640190fd5b61305e336136f5565b8061306d575061306d3361377e565b6130bb576040805162461bcd60e51b815260206004820152601a60248201527932b4ba3432b91037bbb732b91037b91031b7b73a3937b63632b960311b604482015290519081900360640190fd5b6130cc600f8363ffffffff61396d16565b6130d4612552565b6001600160a01b03166108fc839081150290604051600060405180830381858888f1935050505015801561310c573d6000803e3d6000fd5b507f611b7c0d84fda988026215bef9b3e4d81cbceced7e679be6d5e044b588467c0e33613137612552565b604080516001600160a01b03938416815291909216602082015280820185905290519081900360600190a15050565b61316f3361377e565b6131ae576040805162461bcd60e51b815260206004820152601a602482015260008051602061463a833981519152604482015290519081900360640190fd5b61169f60098263ffffffff61415216565b60006131c96141a6565b6001600160a01b0316836001600160a01b031614156131e957508061288e565b816001600160a01b038416156132ba57600080600061320787613e9c565b505050935093509350508061325c576040805162461bcd60e51b8152602060048201526016602482015275746f6b656e206973206e6f7420617661696c61626c6560501b604482015290519081900360640190fd5b816132a0576040805162461bcd60e51b815260206004820152600f60248201526e0746f6b656e2072617465206973203608c1b604482015290519081900360640190fd5b6132b483612870888563ffffffff613fc716565b93505050505b60008060006132c761421c565b505050935093509350508061331c576040805162461bcd60e51b8152602060048201526016602482015275746f6b656e206973206e6f7420617661696c61626c6560501b604482015290519081900360640190fd5b81613365576040805162461bcd60e51b81526020600482015260146024820152730737461626c65636f696e207261746520697320360641b604482015290519081900360640190fd5b61337982612870868663ffffffff613fc716565b979650505050505050565b61338d3361377e565b6133cc576040805162461bcd60e51b815260206004820152601a602482015260008051602061463a833981519152604482015290519081900360640190fd5b61160860158263ffffffff61415216565b6133e63361377e565b613425576040805162461bcd60e51b815260206004820152601a602482015260008051602061463a833981519152604482015290519081900360640190fd5b610e73600f8263ffffffff61415216565b61343f336136f5565b61347e576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b8181808060200260200160405190810160405280939291908181526020018383602002808284376000920182905250925050505b8151811015613573576134ca82828151811061234d57fe5b156135065760405162461bcd60e51b815260040180806020018281038252602d815260200180614771602d913960400191505060405180910390fd5b60006001600160a01b031682828151811061351d57fe5b60200260200101516001600160a01b0316141561356b5760405162461bcd60e51b815260040180806020018281038252602c81526020018061452a602c913960400191505060405180910390fd5b6001016134b2565b5060085462010000900460ff16156135bc5760405162461bcd60e51b81526004018080602001828103825260268152602001806146ba6026913960400191505060405180910390fd5b60005b82811015613671576001600460008686858181106135d957fe5b905060200201356001600160a01b03166001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff021916908315150217905550600584848381811061362e57fe5b835460018181018655600095865260209586902090910180546001600160a01b0319166001600160a01b03969093029490940135949094161790915550016135bf565b506008805462ff0000191662010000179055604080513380825260208083018481529383018690527fb2f6cccee7a369e23e293c25aa19bef80af11eb26deba3ea0f2a02783f752e4a9391928792879260608301908590850280828437600083820152604051601f909101601f1916909201829003965090945050505050a1505050565b6003546001600160a01b0390811691161490565b600482015460ff1615613763576040805162461bcd60e51b815260206004820152601a60248201527f6461696c79206c696d6974206e6f742075706461746561626c65000000000000604482015290519081900360640190fd5b61376d8282614322565b50600401805460ff19166001179055565b600061378b6002546139da565b6001600160a01b031663b429afeb836040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156137e057600080fd5b505afa1580156137f4573d6000803e3d6000fd5b505050506040513d602081101561380a57600080fd5b505192915050565b60006001600160a01b038216156138ac57816001600160a01b03166370a08231846040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561387957600080fd5b505afa15801561388d573d6000803e3d6000fd5b505050506040513d60208110156138a357600080fd5b5051905061288e565b506001600160a01b0382163161288e565b60028101546000906138d8906201518063ffffffff61434516565b4211156138e757508054610db4565b506001810154610db4565b600482015460ff1661394b576040805162461bcd60e51b815260206004820152601f60248201527f6461696c79206c696d6974206973207374696c6c2075706461746561626c6500604482015290519081900360640190fd5b600390910155565b60008061395f83613e9c565b509098975050505050505050565b6139768261439f565b80826001015410156139b95760405162461bcd60e51b81526004018080602001828103825260328152602001806146886032913960400191505060405180910390fd5b60018201546139ce908263ffffffff6140f516565b82600101819055505050565b6000805460408051630178b8bf60e01b81526004810185905290516001600160a01b0390921691630178b8bf91602480820192602092909190829003018186803b158015613a2757600080fd5b505afa158015613a3b573d6000803e3d6000fd5b505050506040513d6020811015613a5157600080fd5b505160408051631d9dabef60e11b81526004810185905290516001600160a01b0390921691633b3b57de91602480820192602092909190829003018186803b1580156137e057600080fd5b801580613b22575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b158015613af457600080fd5b505afa158015613b08573d6000803e3d6000fd5b505050506040513d6020811015613b1e57600080fd5b5051155b613b5d5760405162461bcd60e51b81526004018080602001828103825260368152602001806148a76036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052613baf908490613cde565b505050565b3b151590565b600080613bc683613e9c565b509198975050505050505050565b600080613be2600e546139da565b6001600160a01b031663afc72e9385856040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200180602001828103825283818151815260200191508051906020019080838360005b83811015613c56578181015183820152602001613c3e565b50505050905090810190601f168015613c835780820380516001836020036101000a031916815260200191505b509350505050604080518083038186803b158015613ca057600080fd5b505afa158015613cb4573d6000803e3d6000fd5b505050506040513d6040811015613cca57600080fd5b508051602090910151909590945092505050565b613cf0826001600160a01b0316613bb4565b613d41576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b60208310613d7f5780518252601f199092019160209182019101613d60565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613de1576040519150601f19603f3d011682016040523d82523d6000602084013e613de6565b606091505b509150915081613e3d576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115613e9657808060200190516020811015613e5957600080fd5b5051613e965760405162461bcd60e51b815260040180806020018281038252602a81526020018061484b602a913960400191505060405180910390fd5b50505050565b6060600080600080600080613eb2600e546139da565b6001600160a01b0316631f69565f896040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060006040518083038186803b158015613f0757600080fd5b505afa158015613f1b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260e0811015613f4457600080fd5b810190808051600160201b811115613f5b57600080fd5b82016020810184811115613f6e57600080fd5b8151600160201b811182820187101715613f8757600080fd5b5050602082015160408301516060840151608085015160a086015160c090960151949e50929c50909a509850965090945092505050919395979092949650565b600082613fd65750600061288e565b82820282848281613fe357fe5b04146140205760405162461bcd60e51b81526004018080602001828103825260218152602001806147506021913960400191505060405180910390fd5b9392505050565b600080821161407d576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b600082848161408857fe5b04949350505050565b6001600160a01b0382166140db576040516001600160a01b0384169082156108fc029083906000818181858888f193505050501580156140d5573d6000803e3d6000fd5b50613baf565b613baf6001600160a01b038316848363ffffffff6143f816565b60008282111561414c576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b808260030154146141945760405162461bcd60e51b81526004018080602001828103825260298152602001806145a76029913960400191505060405180910390fd5b6141a2828360030154614322565b5050565b60006141b3600e546139da565b6001600160a01b031663e9cbd8226040518163ffffffff1660e01b815260040160206040518083038186803b1580156141eb57600080fd5b505afa1580156141ff573d6000803e3d6000fd5b505050506040513d602081101561421557600080fd5b5051905090565b6060600080600080600080614232600e546139da565b6001600160a01b0316633efec5e96040518163ffffffff1660e01b815260040160006040518083038186803b15801561426a57600080fd5b505afa15801561427e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260e08110156142a757600080fd5b810190808051600160201b8111156142be57600080fd5b820160208101848111156142d157600080fd5b8151600160201b8111828201871017156142ea57600080fd5b5050602082015160408301516060840151608085015160a086015160c090960151949f939e50919c509a509850919650945092505050565b61432b8261439f565b80825560018201548110156141a257815460018301555050565b600082820183811015614020576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b60028101546143b7906201518063ffffffff61434516565b4211156143f557426002820155805460018201556040517fe93bc25276d408d390778e7a8b926f2f67209c43ed540081b951fe128f0d3cd290600090a15b50565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052613baf908490613cde565b50805460008255906000526020600020908101906143f591906144eb565b8280548282559060005260206000209081019282156144bb579160200282015b828111156144bb5781546001600160a01b0319166001600160a01b03843516178255602090920191600190910190614488565b506144c7929150614505565b5090565b815481835581811115613baf57600083815260209020613baf9181019083015b610dbd91905b808211156144c757600081556001016144f1565b610dbd91905b808211156144c75780546001600160a01b031916815560010161450b56fe70726f76696465642077686974656c69737420636f6e7461696e7320746865207a65726f206164647265737370656e64696e672077686974656c697374206164646974696f6e20697320656d70747967617320746f7020757020616d6f756e74206973206f75747369646520746865206d696e2f6d61782072616e6765636f6e6669726d656420616e64207375626d6974746564206c696d69747320646f6e74206d6174636877686974656c69737420686173206e6f74206265656e20696e697469616c697a656473656e646572206973206e6f7420616e206f776e65720000000000000000000077686974656c6973742072656d6f76616c20686173206e6f74206265656e207375626d697474656473656e646572206973206e6f74206120636f6e74726f6c6c657200000000000077686974656c697374206f7065726174696f6e2068617320616c7265616479206265656e207375626d6974746564617661696c61626c652068617320746f2062652067726561746572206f7220657175616c20746f2075736520616d6f756e7477686974656c6973742068617320616c7265616479206265656e20696e697469616c697a656468617368206f66207468652070656e64696e672077686974656c6973742072656d6f76616c20646f6573206e6f74206d617463682074686520636f6e6669726d6564206861736877686974656c697374206164646974696f6e20686173206e6f74206265656e207375626d6974746564536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7770726f76696465642077686974656c69737420636f6e7461696e7320746865206f776e6572206164647265737370656e64696e672077686974656c6973742072656d6f76616c20697320656d70747968617368206f66207468652070656e64696e672077686974656c697374206164646974696f6e20646f6573206e6f74206d6174636868617368206f66207468652070656e64696e672077686974656c697374206164646974696f6e20646f206e6f74206d617463686f776e65722063616e6e6f742062652073657420746f207a65726f20616464726573735361666545524332303a204552433230206f7065726174696f6e20646964206e6f74207375636365656468617368206f66207468652070656e64696e672077686974656c6973742072656d6f76616c20646f206e6f74206d617463685361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e636563617264206c6f616420616d6f756e74206973206f75747369646520746865206d696e2f6d61782072616e6765a265627a7a7230582039d1a7c207df4e9874dc1b1a8be5453ec2db2d73fab93e6dc8a826b323ac459264736f6c634300050a0032a265627a7a7230582039e2230799e2b1b8110aa9cd82e7957bafc7e3e93ad2f3e907547017aa513c4464736f6c634300050a0032
Verified Source Code Partial Match
Compiler: v0.5.10+commit.5a6ea5b1
EVM: petersburg
Optimization: Yes (200 runs)
ENS.sol 53 lines
/**
* BSD 2-Clause License
*
* Copyright (c) 2018, True Names Limited
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
pragma solidity ^0.5.0;
interface ENS {
// Logged when the owner of a node assigns a new owner to a subnode.
event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
// Logged when the owner of a node transfers ownership to a new account.
event Transfer(bytes32 indexed node, address owner);
// Logged when the resolver for a node changes.
event NewResolver(bytes32 indexed node, address resolver);
// Logged when the TTL of a node changes
event NewTTL(bytes32 indexed node, uint64 ttl);
function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external;
function setResolver(bytes32 node, address resolver) external;
function setOwner(bytes32 node, address owner) external;
function setTTL(bytes32 node, uint64 ttl) external;
function owner(bytes32 node) external view returns (address);
function resolver(bytes32 node) external view returns (address);
function ttl(bytes32 node) external view returns (uint64);
}
ERC20.sol 12 lines
pragma solidity ^0.5.10;
/// @title ERC20 interface is a subset of the ERC20 specification.
/// @notice see https://github.com/ethereum/EIPs/issues/20
interface ERC20 {
function allowance(address _owner, address _spender) external view returns (uint256);
function approve(address _spender, uint256 _value) external returns (bool);
function balanceOf(address _who) external view returns (uint256);
function totalSupply() external view returns (uint256);
function transfer(address _to, uint256 _value) external returns (bool);
function transferFrom(address _from, address _to, uint256 _value) external returns (bool);
}
ERC165.sol 6 lines
pragma solidity ^0.5.10;
/// @title ERC165 interface specifies a standard way of querying if a contract implements an interface.
interface ERC165 {
function supportsInterface(bytes4) external view returns (bool);
}
wallet.sol 727 lines
/**
* The Consumer Contract Wallet
* Copyright (C) 2019 The Contract Wallet Company Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
pragma solidity ^0.5.10;
import "./licence.sol";
import "./ownable.sol";
import "./controllable.sol";
import "./balanceable.sol";
import "./transferrable.sol";
import "./ensResolvable.sol";
import "./tokenWhitelistable.sol";
import "./SafeMath.sol";
import "./Address.sol";
import "./ERC20.sol";
import "./SafeERC20.sol";
import "./ERC165.sol";
/// @title ControllableOwnable combines Controllable and Ownable
/// @dev providing an additional modifier to check if Owner or Controller
contract ControllableOwnable is Controllable, Ownable {
/// @dev Check if the sender is the Owner or one of the Controllers
modifier onlyOwnerOrController() {
require (_isOwner(msg.sender) || _isController(msg.sender), "either owner or controller");
_;
}
}
/// @title AddressWhitelist provides payee-whitelist functionality.
/// @dev This contract will allow the user to maintain a whitelist of addresses
/// @dev These addresses will live outside of the various spend limits
contract AddressWhitelist is ControllableOwnable {
using SafeMath for uint256;
event AddedToWhitelist(address _sender, address[] _addresses);
event SubmittedWhitelistAddition(address[] _addresses, bytes32 _hash);
event CancelledWhitelistAddition(address _sender, bytes32 _hash);
event RemovedFromWhitelist(address _sender, address[] _addresses);
event SubmittedWhitelistRemoval(address[] _addresses, bytes32 _hash);
event CancelledWhitelistRemoval(address _sender, bytes32 _hash);
mapping(address => bool) public whitelistMap;
address[] public whitelistArray;
address[] private _pendingWhitelistAddition;
address[] private _pendingWhitelistRemoval;
bool public submittedWhitelistAddition;
bool public submittedWhitelistRemoval;
bool public isSetWhitelist;
/// @dev Check if the provided addresses contain the owner or the zero-address address.
modifier hasNoOwnerOrZeroAddress(address[] memory _addresses) {
for (uint i = 0; i < _addresses.length; i++) {
require(!_isOwner(_addresses[i]), "provided whitelist contains the owner address");
require(_addresses[i] != address(0), "provided whitelist contains the zero address");
}
_;
}
/// @dev Check that neither addition nor removal operations have already been submitted.
modifier noActiveSubmission() {
require(!submittedWhitelistAddition && !submittedWhitelistRemoval, "whitelist operation has already been submitted");
_;
}
/// @dev Getter for pending addition array.
function pendingWhitelistAddition() external view returns (address[] memory) {
return _pendingWhitelistAddition;
}
/// @dev Getter for pending removal array.
function pendingWhitelistRemoval() external view returns (address[] memory) {
return _pendingWhitelistRemoval;
}
/// @dev Add initial addresses to the whitelist.
/// @param _addresses are the Ethereum addresses to be whitelisted.
function setWhitelist(address[] calldata _addresses) external onlyOwner hasNoOwnerOrZeroAddress(_addresses) {
// Require that the whitelist has not been initialized.
require(!isSetWhitelist, "whitelist has already been initialized");
// Add each of the provided addresses to the whitelist.
for (uint i = 0; i < _addresses.length; i++) {
// adds to the whitelist mapping
whitelistMap[_addresses[i]] = true;
// adds to the whitelist array
whitelistArray.push(_addresses[i]);
}
isSetWhitelist = true;
// Emit the addition event.
emit AddedToWhitelist(msg.sender, _addresses);
}
/// @dev Add addresses to the whitelist.
/// @param _addresses are the Ethereum addresses to be whitelisted.
function submitWhitelistAddition(address[] calldata _addresses) external onlyOwner noActiveSubmission hasNoOwnerOrZeroAddress(_addresses) {
// Require that the whitelist has been initialized.
require(isSetWhitelist, "whitelist has not been initialized");
// Require this array of addresses not empty
require(_addresses.length > 0, "pending whitelist addition is empty");
// Set the provided addresses to the pending addition addresses.
_pendingWhitelistAddition = _addresses;
// Flag the operation as submitted.
submittedWhitelistAddition = true;
// Emit the submission event.
emit SubmittedWhitelistAddition(_addresses, calculateHash(_addresses));
}
/// @dev Confirm pending whitelist addition.
/// @dev This will only ever be applied post 2FA, by one of the Controllers
/// @param _hash is the hash of the pending whitelist array, a form of lamport lock
function confirmWhitelistAddition(bytes32 _hash) external onlyController {
// Require that the whitelist addition has been submitted.
require(submittedWhitelistAddition, "whitelist addition has not been submitted");
// Require that confirmation hash and the hash of the pending whitelist addition match
require(_hash == calculateHash(_pendingWhitelistAddition), "hash of the pending whitelist addition do not match");
// Whitelist pending addresses.
for (uint i = 0; i < _pendingWhitelistAddition.length; i++) {
// check if it doesn't exist already.
if (!whitelistMap[_pendingWhitelistAddition[i]]) {
// add to the Map and the Array
whitelistMap[_pendingWhitelistAddition[i]] = true;
whitelistArray.push(_pendingWhitelistAddition[i]);
}
}
// Emit the addition event.
emit AddedToWhitelist(msg.sender, _pendingWhitelistAddition);
// Reset pending addresses.
delete _pendingWhitelistAddition;
// Reset the submission flag.
submittedWhitelistAddition = false;
}
/// @dev Cancel pending whitelist addition.
function cancelWhitelistAddition(bytes32 _hash) external onlyOwnerOrController {
// Check if operation has been submitted.
require(submittedWhitelistAddition, "whitelist addition has not been submitted");
// Require that confirmation hash and the hash of the pending whitelist addition match
require(_hash == calculateHash(_pendingWhitelistAddition), "hash of the pending whitelist addition does not match");
// Reset pending addresses.
delete _pendingWhitelistAddition;
// Reset the submitted operation flag.
submittedWhitelistAddition = false;
// Emit the cancellation event.
emit CancelledWhitelistAddition(msg.sender, _hash);
}
/// @dev Remove addresses from the whitelist.
/// @param _addresses are the Ethereum addresses to be removed.
function submitWhitelistRemoval(address[] calldata _addresses) external onlyOwner noActiveSubmission {
// Require that the whitelist has been initialized.
require(isSetWhitelist, "whitelist has not been initialized");
// Require that the array of addresses is not empty
require(_addresses.length > 0, "pending whitelist removal is empty");
// Add the provided addresses to the pending addition list.
_pendingWhitelistRemoval = _addresses;
// Flag the operation as submitted.
submittedWhitelistRemoval = true;
// Emit the submission event.
emit SubmittedWhitelistRemoval(_addresses, calculateHash(_addresses));
}
/// @dev Confirm pending removal of whitelisted addresses.
function confirmWhitelistRemoval(bytes32 _hash) external onlyController {
// Require that the pending whitelist is not empty and the operation has been submitted.
require(submittedWhitelistRemoval, "whitelist removal has not been submitted");
// Require that confirmation hash and the hash of the pending whitelist removal match
require(_hash == calculateHash(_pendingWhitelistRemoval), "hash of the pending whitelist removal does not match the confirmed hash");
// Remove pending addresses.
for (uint i = 0; i < _pendingWhitelistRemoval.length; i++) {
// check if it exists
if (whitelistMap[_pendingWhitelistRemoval[i]]) {
whitelistMap[_pendingWhitelistRemoval[i]] = false;
for (uint j = 0; j < whitelistArray.length.sub(1); j++) {
if (whitelistArray[j] == _pendingWhitelistRemoval[i]) {
whitelistArray[j] = whitelistArray[whitelistArray.length - 1];
break;
}
}
whitelistArray.length--;
}
}
// Emit the removal event.
emit RemovedFromWhitelist(msg.sender, _pendingWhitelistRemoval);
// Reset pending addresses.
delete _pendingWhitelistRemoval;
// Reset the submission flag.
submittedWhitelistRemoval = false;
}
/// @dev Cancel pending removal of whitelisted addresses.
function cancelWhitelistRemoval(bytes32 _hash) external onlyOwnerOrController {
// Check if operation has been submitted.
require(submittedWhitelistRemoval, "whitelist removal has not been submitted");
// Require that confirmation hash and the hash of the pending whitelist removal match
require(_hash == calculateHash(_pendingWhitelistRemoval), "hash of the pending whitelist removal do not match");
// Reset pending addresses.
delete _pendingWhitelistRemoval;
// Reset pending addresses.
submittedWhitelistRemoval = false;
// Emit the cancellation event.
emit CancelledWhitelistRemoval(msg.sender, _hash);
}
/// @dev Method used to hash our whitelist address arrays.
function calculateHash(address[] memory _addresses) public pure returns (bytes32) {
return keccak256(abi.encodePacked(_addresses));
}
}
/// @title DailyLimitTrait This trait allows for daily limits to be included in other contracts.
/// This contract will allow for a DailyLimit object to be instantiated and used.
library DailyLimitTrait {
using SafeMath for uint256;
event UpdatedAvailableLimit();
struct DailyLimit {
uint value;
uint available;
uint limitTimestamp;
uint pending;
bool updateable;
}
/// @dev Returns the available daily balance - accounts for daily limit reset.
/// @return amount of available to spend within the current day in base units.
function _getAvailableLimit(DailyLimit storage self) internal view returns (uint) {
if (now > self.limitTimestamp.add(24 hours)) {
return self.value;
} else {
return self.available;
}
}
/// @dev Use up amount within the daily limit. Will fail if amount is larger than daily limit.
function _enforceLimit(DailyLimit storage self, uint _amount) internal {
// Account for the spend limit daily reset.
_updateAvailableLimit(self);
require(self.available >= _amount, "available has to be greater or equal to use amount");
self.available = self.available.sub(_amount);
}
/// @dev Set the daily limit.
/// @param _amount is the daily limit amount in base units.
function _setLimit(DailyLimit storage self, uint _amount) internal {
// Require that the spend limit has not been set yet.
require(!self.updateable, "daily limit not updateable");
// Modify spend limit based on the provided value.
_modifyLimit(self, _amount);
// Flag the operation as set.
self.updateable = true;
}
/// @dev Submit a daily limit update, needs to be confirmed.
/// @param _amount is the daily limit amount in base units.
function _submitLimitUpdate(DailyLimit storage self, uint _amount) internal {
// Require that the spend limit has been set.
require(self.updateable, "daily limit is still updateable");
// Assign the provided amount to pending daily limit.
self.pending = _amount;
}
/// @dev Confirm pending set daily limit operation.
function _confirmLimitUpdate(DailyLimit storage self, uint _amount) internal {
// Require that pending and confirmed spend limit are the same
require(self.pending == _amount, "confirmed and submitted limits dont match");
// Modify spend limit based on the pending value.
_modifyLimit(self, self.pending);
}
/// @dev Update available spend limit based on the daily reset.
function _updateAvailableLimit(DailyLimit storage self) private {
if (now > self.limitTimestamp.add(24 hours)) {
// Update the current timestamp.
self.limitTimestamp = now;
// Set the available limit to the current spend limit.
self.available = self.value;
emit UpdatedAvailableLimit();
}
}
/// @dev Modify the spend limit and spend available based on the provided value.
/// @dev _amount is the daily limit amount in wei.
function _modifyLimit(DailyLimit storage self, uint _amount) private {
// Account for the spend limit daily reset.
_updateAvailableLimit(self);
// Set the daily limit to the provided amount.
self.value = _amount;
// Lower the available limit if it's higher than the new daily limit.
if (self.available > self.value) {
self.available = self.value;
}
}
}
/// @title it provides daily spend limit functionality.
contract SpendLimit is ControllableOwnable {
event SetSpendLimit(address _sender, uint _amount);
event SubmittedSpendLimitUpdate(uint _amount);
using DailyLimitTrait for DailyLimitTrait.DailyLimit;
DailyLimitTrait.DailyLimit internal _spendLimit;
/// @dev Constructor initializes the daily spend limit in wei.
constructor(uint _limit_) internal {
_spendLimit = DailyLimitTrait.DailyLimit(_limit_, _limit_, now, 0, false);
}
/// @dev Sets the initial daily spend (aka transfer) limit for non-whitelisted addresses.
/// @param _amount is the daily limit amount in wei.
function setSpendLimit(uint _amount) external onlyOwner {
_spendLimit._setLimit(_amount);
emit SetSpendLimit(msg.sender, _amount);
}
/// @dev Submit a daily transfer limit update for non-whitelisted addresses.
/// @param _amount is the daily limit amount in wei.
function submitSpendLimitUpdate(uint _amount) external onlyOwner {
_spendLimit._submitLimitUpdate(_amount);
emit SubmittedSpendLimitUpdate(_amount);
}
/// @dev Confirm pending set daily limit operation.
function confirmSpendLimitUpdate(uint _amount) external onlyController {
_spendLimit._confirmLimitUpdate(_amount);
emit SetSpendLimit(msg.sender, _amount);
}
function spendLimitAvailable() external view returns (uint) {
return _spendLimit._getAvailableLimit();
}
function spendLimitValue() external view returns (uint) {
return _spendLimit.value;
}
function spendLimitUpdateable() external view returns (bool) {
return _spendLimit.updateable;
}
function spendLimitPending() external view returns (uint) {
return _spendLimit.pending;
}
}
//// @title GasTopUpLimit provides daily limit functionality.
contract GasTopUpLimit is ControllableOwnable {
event SetGasTopUpLimit(address _sender, uint _amount);
event SubmittedGasTopUpLimitUpdate(uint _amount);
uint constant private _MINIMUM_GAS_TOPUP_LIMIT = 1 finney;
uint constant private _MAXIMUM_GAS_TOPUP_LIMIT = 500 finney;
using DailyLimitTrait for DailyLimitTrait.DailyLimit;
DailyLimitTrait.DailyLimit internal _gasTopUpLimit;
/// @dev Constructor initializes the daily gas topup limit in wei.
constructor() internal {
_gasTopUpLimit = DailyLimitTrait.DailyLimit(_MAXIMUM_GAS_TOPUP_LIMIT, _MAXIMUM_GAS_TOPUP_LIMIT, now, 0, false);
}
/// @dev Sets the daily gas top up limit.
/// @param _amount is the gas top up amount in wei.
function setGasTopUpLimit(uint _amount) external onlyOwner {
require(_MINIMUM_GAS_TOPUP_LIMIT <= _amount && _amount <= _MAXIMUM_GAS_TOPUP_LIMIT, "gas top up amount is outside the min/max range");
_gasTopUpLimit._setLimit(_amount);
emit SetGasTopUpLimit(msg.sender, _amount);
}
/// @dev Submit a daily gas top up limit update.
/// @param _amount is the daily top up gas limit amount in wei.
function submitGasTopUpLimitUpdate(uint _amount) external onlyOwner {
require(_MINIMUM_GAS_TOPUP_LIMIT <= _amount && _amount <= _MAXIMUM_GAS_TOPUP_LIMIT, "gas top up amount is outside the min/max range");
_gasTopUpLimit._submitLimitUpdate(_amount);
emit SubmittedGasTopUpLimitUpdate(_amount);
}
/// @dev Confirm pending set top up gas limit operation.
function confirmGasTopUpLimitUpdate(uint _amount) external onlyController {
_gasTopUpLimit._confirmLimitUpdate(_amount);
emit SetGasTopUpLimit(msg.sender, _amount);
}
function gasTopUpLimitAvailable() external view returns (uint) {
return _gasTopUpLimit._getAvailableLimit();
}
function gasTopUpLimitValue() external view returns (uint) {
return _gasTopUpLimit.value;
}
function gasTopUpLimitUpdateable() external view returns (bool) {
return _gasTopUpLimit.updateable;
}
function gasTopUpLimitPending() external view returns (uint) {
return _gasTopUpLimit.pending;
}
}
/// @title LoadLimit provides daily load limit functionality.
contract LoadLimit is ControllableOwnable {
event SetLoadLimit(address _sender, uint _amount);
event SubmittedLoadLimitUpdate(uint _amount);
uint constant private _MINIMUM_LOAD_LIMIT = 1 finney;
uint private _maximumLoadLimit;
using DailyLimitTrait for DailyLimitTrait.DailyLimit;
DailyLimitTrait.DailyLimit internal _loadLimit;
/// @dev Sets a daily card load limit.
/// @param _amount is the card load amount in current stablecoin base units.
function setLoadLimit(uint _amount) external onlyOwner {
require(_MINIMUM_LOAD_LIMIT <= _amount && _amount <= _maximumLoadLimit, "card load amount is outside the min/max range");
_loadLimit._setLimit(_amount);
emit SetLoadLimit(msg.sender, _amount);
}
/// @dev Submit a daily load limit update.
/// @param _amount is the daily load limit amount in wei.
function submitLoadLimitUpdate(uint _amount) external onlyOwner {
require(_MINIMUM_LOAD_LIMIT <= _amount && _amount <= _maximumLoadLimit, "card load amount is outside the min/max range");
_loadLimit._submitLimitUpdate(_amount);
emit SubmittedLoadLimitUpdate(_amount);
}
/// @dev Confirm pending set load limit operation.
function confirmLoadLimitUpdate(uint _amount) external onlyController {
_loadLimit._confirmLimitUpdate(_amount);
emit SetLoadLimit(msg.sender, _amount);
}
function loadLimitAvailable() external view returns (uint) {
return _loadLimit._getAvailableLimit();
}
function loadLimitValue() external view returns (uint) {
return _loadLimit.value;
}
function loadLimitUpdateable() external view returns (bool) {
return _loadLimit.updateable;
}
function loadLimitPending() external view returns (uint) {
return _loadLimit.pending;
}
/// @dev initializes the daily load limit.
/// @param _maxLimit is the maximum load limit amount in stablecoin base units.
function _initializeLoadLimit(uint _maxLimit) internal {
_maximumLoadLimit = _maxLimit;
_loadLimit = DailyLimitTrait.DailyLimit(_maximumLoadLimit, _maximumLoadLimit, now, 0, false);
}
}
//// @title Asset store with extra security features.
contract Vault is AddressWhitelist, SpendLimit, ERC165, Transferrable, Balanceable, TokenWhitelistable {
using SafeMath for uint256;
using SafeERC20 for ERC20;
event Received(address _from, uint _amount);
event Transferred(address _to, address _asset, uint _amount);
event BulkTransferred(address _to, address[] _assets);
/// @dev Supported ERC165 interface ID.
bytes4 private constant _ERC165_INTERFACE_ID = 0x01ffc9a7; // solium-disable-line uppercase
/// @dev Constructor initializes the vault with an owner address and spend limit. It also sets up the controllable and tokenWhitelist contracts with the right name registered in ENS.
/// @param _owner_ is the owner account of the wallet contract.
/// @param _transferable_ indicates whether the contract ownership can be transferred.
/// @param _tokenWhitelistNode_ is the ENS node of the Token whitelist.
/// @param _controllerNode_ is the ENS name node of the controller.
/// @param _spendLimit_ is the initial spend limit.
constructor(address payable _owner_, bool _transferable_, bytes32 _tokenWhitelistNode_, bytes32 _controllerNode_, uint _spendLimit_) SpendLimit(_spendLimit_) Ownable(_owner_, _transferable_) Controllable(_controllerNode_) TokenWhitelistable(_tokenWhitelistNode_) public {}
/// @dev Checks if the value is not zero.
modifier isNotZero(uint _value) {
require(_value != 0, "provided value cannot be zero");
_;
}
/// @dev Ether can be deposited from any source, so this contract must be payable by anyone.
function() external payable {
emit Received(msg.sender, msg.value);
}
/// @dev Checks for interface support based on ERC165.
function supportsInterface(bytes4 _interfaceID) external view returns (bool) {
return _interfaceID == _ERC165_INTERFACE_ID;
}
/// @dev This is a bulk transfer convenience function, used to migrate contracts.
/// @notice If any of the transfers fail, this will revert.
/// @param _to is the recipient's address, can't be the zero (0x0) address: transfer() will revert.
/// @param _assets is an array of addresses of ERC20 tokens or 0x0 for ether.
function bulkTransfer(address payable _to, address[] calldata _assets) external onlyOwner {
// check to make sure that _assets isn't empty
require(_assets.length != 0, "asset array should be non-empty");
// This loops through all of the transfers to be made
for (uint i = 0; i < _assets.length; i++) {
uint amount = _balance(address(this), _assets[i]);
// use our safe, daily limit protected transfer
transfer(_to, _assets[i], amount);
}
emit BulkTransferred(_to, _assets);
}
/// @dev Transfers the specified asset to the recipient's address.
/// @param _to is the recipient's address.
/// @param _asset is the address of an ERC20 token or 0x0 for ether.
/// @param _amount is the amount of assets to be transferred in base units.
function transfer(address payable _to, address _asset, uint _amount) public onlyOwner isNotZero(_amount) {
// Checks if the _to address is not the zero-address
require(_to != address(0), "_to address cannot be set to 0x0");
// If address is not whitelisted, take daily limit into account.
if (!whitelistMap[_to]) {
// initialize ether value in case the asset is ETH
uint etherValue = _amount;
// Convert token amount to ether value if asset is an ERC20 token.
if (_asset != address(0)) {
etherValue = convertToEther(_asset, _amount);
}
// Check against the daily spent limit and update accordingly
// Check against the daily spent limit and update accordingly, require that the value is under remaining limit.
_spendLimit._enforceLimit(etherValue);
}
// Transfer token or ether based on the provided address.
_safeTransfer(_to, _asset, _amount);
// Emit the transfer event.
emit Transferred(_to, _asset, _amount);
}
/// @dev Convert ERC20 token amount to the corresponding ether amount.
/// @param _token ERC20 token contract address.
/// @param _amount amount of token in base units.
function convertToEther(address _token, uint _amount) public view returns (uint) {
// Store the token in memory to save map entry lookup gas.
(,uint256 magnitude, uint256 rate, bool available, , , ) = _getTokenInfo(_token);
// If the token exists require that its rate is not zero.
if (available) {
require(rate != 0, "token rate is 0");
// Safely convert the token amount to ether based on the exchange rate.
return _amount.mul(rate).div(magnitude);
}
return 0;
}
}
//// @title Asset wallet with extra security features, gas top up management and card integration.
contract Wallet is ENSResolvable, Vault, GasTopUpLimit, LoadLimit {
using SafeERC20 for ERC20;
using Address for address;
event ToppedUpGas(address _sender, address _owner, uint _amount);
event LoadedTokenCard(address _asset, uint _amount);
event ExecutedTransaction(address _destination, uint _value, bytes _data, bytes _returndata);
event UpdatedAvailableLimit();
string constant public WALLET_VERSION = "2.2.0";
uint constant private _DEFAULT_MAX_STABLECOIN_LOAD_LIMIT = 10000; //10,000 USD
/// @dev Is the registered ENS node identifying the licence contract.
bytes32 private _licenceNode;
/// @dev Constructor initializes the wallet top up limit and the vault contract.
/// @param _owner_ is the owner account of the wallet contract.
/// @param _transferable_ indicates whether the contract ownership can be transferred.
/// @param _ens_ is the address of the ENS registry.
/// @param _tokenWhitelistNode_ is the ENS name node of the Token whitelist.
/// @param _controllerNode_ is the ENS name node of the Controller contract.
/// @param _licenceNode_ is the ENS name node of the Licence contract.
/// @param _spendLimit_ is the initial spend limit.
constructor(address payable _owner_, bool _transferable_, address _ens_, bytes32 _tokenWhitelistNode_, bytes32 _controllerNode_, bytes32 _licenceNode_, uint _spendLimit_) ENSResolvable(_ens_) Vault(_owner_, _transferable_, _tokenWhitelistNode_, _controllerNode_, _spendLimit_) public {
// Get the stablecoin's magnitude.
( ,uint256 stablecoinMagnitude, , , , , ) = _getStablecoinInfo();
require(stablecoinMagnitude > 0, "stablecoin not set");
_initializeLoadLimit(_DEFAULT_MAX_STABLECOIN_LOAD_LIMIT * stablecoinMagnitude);
_licenceNode = _licenceNode_;
}
/// @dev Refill owner's gas balance, revert if the transaction amount is too large
/// @param _amount is the amount of ether to transfer to the owner account in wei.
function topUpGas(uint _amount) external isNotZero(_amount) onlyOwnerOrController {
// Check against the daily spent limit and update accordingly, require that the value is under remaining limit.
_gasTopUpLimit._enforceLimit(_amount);
// Then perform the transfer
owner().transfer(_amount);
// Emit the gas top up event.
emit ToppedUpGas(msg.sender, owner(), _amount);
}
/// @dev Load a token card with the specified asset amount.
/// @dev the amount send should be inclusive of the percent licence.
/// @param _asset is the address of an ERC20 token or 0x0 for ether.
/// @param _amount is the amount of assets to be transferred in base units.
function loadTokenCard(address _asset, uint _amount) external payable onlyOwner {
// check if token is allowed to be used for loading the card
require(_isTokenLoadable(_asset), "token not loadable");
// Convert token amount to stablecoin value.
uint stablecoinValue = convertToStablecoin(_asset, _amount);
// Check against the daily spent limit and update accordingly, require that the value is under remaining limit.
_loadLimit._enforceLimit(stablecoinValue);
// Get the TKN licenceAddress from ENS
address licenceAddress = _ensResolve(_licenceNode);
if (_asset != address(0)) {
ERC20(_asset).safeApprove(licenceAddress, _amount);
ILicence(licenceAddress).load(_asset, _amount);
} else {
ILicence(licenceAddress).load.value(_amount)(_asset, _amount);
}
emit LoadedTokenCard(_asset, _amount);
}
/// @dev This function allows for the owner to send transaction from the Wallet to arbitrary addresses
/// @param _destination address of the transaction
/// @param _value ETH amount in wei
/// @param _data transaction payload binary
function executeTransaction(address _destination, uint _value, bytes calldata _data) external onlyOwner returns (bytes memory) {
// If value is send across as a part of this executeTransaction, this will be sent to any payable
// destination. As a result enforceLimit if destination is not whitelisted.
if (!whitelistMap[_destination]) {
_spendLimit._enforceLimit(_value);
}
// Check if the destination is a Contract and it is one of our supported tokens
if (address(_destination).isContract() && _isTokenAvailable(_destination)) {
// to is the recipient's address and amount is the value to be transferred
address to;
uint amount;
(to, amount) = _getERC20RecipientAndAmount(_destination, _data);
if (!whitelistMap[to]) {
// If the address (of the token contract, e.g) is not in the TokenWhitelist used by the convert method...
// ...then etherValue will be zero
uint etherValue = convertToEther(_destination, amount);
_spendLimit._enforceLimit(etherValue);
}
// use callOptionalReturn provided in SafeERC20 in case the ERC20 method
// returns flase instead of reverting!
ERC20(_destination).callOptionalReturn(_data);
// if ERC20 call completes, return a boolean "true" as bytes emulating ERC20
bytes memory b = new bytes(32);
b[31] = 0x01;
emit ExecutedTransaction(_destination, _value, _data, b);
return b;
}
(bool success, bytes memory returndata) = _destination.call.value(_value)(_data);
require(success, "low-level call failed");
emit ExecutedTransaction(_destination, _value, _data, returndata);
// returns all of the bytes returned by _destination contract
return returndata;
}
/// @return licence contract node registered in ENS.
function licenceNode() external view returns (bytes32) {
return _licenceNode;
}
/// @dev Convert ether or ERC20 token amount to the corresponding stablecoin amount.
/// @param _token ERC20 token contract address.
/// @param _amount amount of token in base units.
function convertToStablecoin(address _token, uint _amount) public view returns (uint) {
// avoid the unnecessary calculations if the token to be loaded is the stablecoin itself
if (_token == _stablecoin()) {
return _amount;
}
uint amountToSend = _amount;
// 0x0 represents ether
if (_token != address(0)) {
// convert to eth first, same as convertToEther()
// Store the token in memory to save map entry lookup gas.
(,uint256 magnitude, uint256 rate, bool available, , , ) = _getTokenInfo(_token);
// require that token both exists in the whitelist and its rate is not zero.
require(available, "token is not available");
require(rate != 0, "token rate is 0");
// Safely convert the token amount to ether based on the exchange rate.
amountToSend = _amount.mul(rate).div(magnitude);
}
// _amountToSend now is in ether
// Get the stablecoin's magnitude and its current rate.
( ,uint256 stablecoinMagnitude, uint256 stablecoinRate, bool stablecoinAvailable, , , ) = _getStablecoinInfo();
// Check if the stablecoin rate is set.
require(stablecoinAvailable, "token is not available");
require(stablecoinRate != 0, "stablecoin rate is 0");
// Safely convert the token amount to stablecoin based on its exchange rate and the stablecoin exchange rate.
return amountToSend.mul(stablecoinMagnitude).div(stablecoinRate);
}
}
Address.sol 27 lines
pragma solidity ^0.5.0;
/**
* @dev Collection of functions related to the address type,
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* This test is non-exhaustive, and there may be false-negatives: during the
* execution of a contract's constructor, its address will be reported as
* not containing a contract.
*
* > It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*/
function isContract(address account) internal view returns (bool) {
// This method relies in extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
}
licence.sol 247 lines
/**
* Licence - The Consumer Contract Wallet
* Copyright (C) 2019 The Contract Wallet Company Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
pragma solidity ^0.5.10;
import "./SafeMath.sol";
import "./SafeERC20.sol";
import "./controllable.sol";
import "./ensResolvable.sol";
import "./transferrable.sol";
/// @title ILicence interface describes methods for loading a TokenCard and updating licence amount.
interface ILicence {
function load(address, uint) external payable;
function updateLicenceAmount(uint) external;
}
/// @title Licence loads the TokenCard and transfers the licence amout to the TKN Holder Contract.
/// @notice the rest of the amount gets sent to the CryptoFloat
contract Licence is Transferrable, ENSResolvable, Controllable {
using SafeMath for uint256;
using SafeERC20 for ERC20;
/*******************/
/* Events */
/*****************/
event UpdatedLicenceDAO(address _newDAO);
event UpdatedCryptoFloat(address _newFloat);
event UpdatedTokenHolder(address _newHolder);
event UpdatedTKNContractAddress(address _newTKN);
event UpdatedLicenceAmount(uint _newAmount);
event TransferredToTokenHolder(address _from, address _to, address _asset, uint _amount);
event TransferredToCryptoFloat(address _from, address _to, address _asset, uint _amount);
event Claimed(address _to, address _asset, uint _amount);
/// @notice This is 100% scaled up by a factor of 10 to give us an extra 1 decimal place of precision
uint constant public MAX_AMOUNT_SCALE = 1000;
uint constant public MIN_AMOUNT_SCALE = 1;
address private _tknContractAddress = 0xaAAf91D9b90dF800Df4F55c205fd6989c977E73a; // solium-disable-line uppercase
address payable private _cryptoFloat;
address payable private _tokenHolder;
address private _licenceDAO;
bool private _lockedCryptoFloat;
bool private _lockedTokenHolder;
bool private _lockedLicenceDAO;
bool private _lockedTKNContractAddress;
/// @notice This is the _licenceAmountScaled by a factor of 10
/// @dev i.e. 1% is 10 _licenceAmountScaled, 0.1% is 1 _licenceAmountScaled
uint private _licenceAmountScaled;
/// @notice Reverts if called by any address other than the DAO contract.
modifier onlyDAO() {
require(msg.sender == _licenceDAO, "the sender isn't the DAO");
_;
}
/// @notice Constructor initializes the card licence contract.
/// @param _licence_ is the initial card licence amount. this number is scaled 10 = 1%, 9 = 0.9%
/// @param _float_ is the address of the multi-sig cryptocurrency float contract.
/// @param _holder_ is the address of the token holder contract
/// @param _tknAddress_ is the address of the TKN ERC20 contract
/// @param _ens_ is the address of the ENS Registry
/// @param _controllerNode_ is the ENS node corresponding to the controller
constructor(uint _licence_, address payable _float_, address payable _holder_, address _tknAddress_, address _ens_, bytes32 _controllerNode_) ENSResolvable(_ens_) Controllable(_controllerNode_) public {
require(MIN_AMOUNT_SCALE <= _licence_ && _licence_ <= MAX_AMOUNT_SCALE, "licence amount out of range");
_licenceAmountScaled = _licence_;
_cryptoFloat = _float_;
_tokenHolder = _holder_;
if (_tknAddress_ != address(0)) {
_tknContractAddress = _tknAddress_;
}
}
/// @notice Ether can be deposited from any source, so this contract should be payable by anyone.
function() external payable {}
/// @notice this allows for people to see the scaled licence amount
/// @return the scaled licence amount, used to calculate the split when loading.
function licenceAmountScaled() external view returns (uint) {
return _licenceAmountScaled;
}
/// @notice allows one to see the address of the CryptoFloat
/// @return the address of the multi-sig cryptocurrency float contract.
function cryptoFloat() external view returns (address) {
return _cryptoFloat;
}
/// @notice allows one to see the address TKN holder contract
/// @return the address of the token holder contract.
function tokenHolder() external view returns (address) {
return _tokenHolder;
}
/// @notice allows one to see the address of the DAO
/// @return the address of the DAO contract.
function licenceDAO() external view returns (address) {
return _licenceDAO;
}
/// @notice The address of the TKN token
/// @return the address of the TKN contract.
function tknContractAddress() external view returns (address) {
return _tknContractAddress;
}
/// @notice This locks the cryptoFloat address
/// @dev so that it can no longer be updated
function lockFloat() external onlyAdmin {
_lockedCryptoFloat = true;
}
/// @notice This locks the TokenHolder address
/// @dev so that it can no longer be updated
function lockHolder() external onlyAdmin {
_lockedTokenHolder = true;
}
/// @notice This locks the DAO address
/// @dev so that it can no longer be updated
function lockLicenceDAO() external onlyAdmin {
_lockedLicenceDAO = true;
}
/// @notice This locks the TKN address
/// @dev so that it can no longer be updated
function lockTKNContractAddress() external onlyAdmin {
_lockedTKNContractAddress = true;
}
/// @notice Updates the address of the cyptoFloat.
/// @param _newFloat This is the new address for the CryptoFloat
function updateFloat(address payable _newFloat) external onlyAdmin {
require(!floatLocked(), "float is locked");
_cryptoFloat = _newFloat;
emit UpdatedCryptoFloat(_newFloat);
}
/// @notice Updates the address of the Holder contract.
/// @param _newHolder This is the new address for the TokenHolder
function updateHolder(address payable _newHolder) external onlyAdmin {
require(!holderLocked(), "holder contract is locked");
_tokenHolder = _newHolder;
emit UpdatedTokenHolder(_newHolder);
}
/// @notice Updates the address of the DAO contract.
/// @param _newDAO This is the new address for the Licence DAO
function updateLicenceDAO(address _newDAO) external onlyAdmin {
require(!licenceDAOLocked(), "DAO is locked");
_licenceDAO = _newDAO;
emit UpdatedLicenceDAO(_newDAO);
}
/// @notice Updates the address of the TKN contract.
/// @param _newTKN This is the new address for the TKN contract
function updateTKNContractAddress(address _newTKN) external onlyAdmin {
require(!tknContractAddressLocked(), "TKN is locked");
_tknContractAddress = _newTKN;
emit UpdatedTKNContractAddress(_newTKN);
}
/// @notice Updates the TKN licence amount
/// @param _newAmount is a number between MIN_AMOUNT_SCALE (1) and MAX_AMOUNT_SCALE
function updateLicenceAmount(uint _newAmount) external onlyDAO {
require(MIN_AMOUNT_SCALE <= _newAmount && _newAmount <= MAX_AMOUNT_SCALE, "licence amount out of range");
_licenceAmountScaled = _newAmount;
emit UpdatedLicenceAmount(_newAmount);
}
/// @notice Load the holder and float contracts based on the licence amount and asset amount.
/// @param _asset is the address of an ERC20 token or 0x0 for ether.
/// @param _amount is the amount of assets to be transferred including the licence amount.
function load(address _asset, uint _amount) external payable {
uint loadAmount = _amount;
// If TKN then no licence to be paid
if (_asset == _tknContractAddress) {
ERC20(_asset).safeTransferFrom(msg.sender, _cryptoFloat, loadAmount);
} else {
loadAmount = _amount.mul(MAX_AMOUNT_SCALE).div(_licenceAmountScaled + MAX_AMOUNT_SCALE);
uint licenceAmount = _amount.sub(loadAmount);
if (_asset != address(0)) {
ERC20(_asset).safeTransferFrom(msg.sender, _tokenHolder, licenceAmount);
ERC20(_asset).safeTransferFrom(msg.sender, _cryptoFloat, loadAmount);
} else {
require(msg.value == _amount, "ETH sent is not equal to amount");
_tokenHolder.transfer(licenceAmount);
_cryptoFloat.transfer(loadAmount);
}
emit TransferredToTokenHolder(msg.sender, _tokenHolder, _asset, licenceAmount);
}
emit TransferredToCryptoFloat(msg.sender, _cryptoFloat, _asset, loadAmount);
}
//// @notice Withdraw tokens from the smart contract to the specified account.
function claim(address payable _to, address _asset, uint _amount) external onlyAdmin {
_safeTransfer(_to, _asset, _amount);
emit Claimed(_to, _asset, _amount);
}
/// @notice returns whether or not the CryptoFloat address is locked
function floatLocked() public view returns (bool) {
return _lockedCryptoFloat;
}
/// @notice returns whether or not the TokenHolder address is locked
function holderLocked() public view returns (bool) {
return _lockedTokenHolder;
}
/// @notice returns whether or not the Licence DAO address is locked
function licenceDAOLocked() public view returns (bool) {
return _lockedLicenceDAO;
}
/// @notice returns whether or not the TKN address is locked
function tknContractAddressLocked() public view returns (bool) {
return _lockedTKNContractAddress;
}
}
ownable.sol 99 lines
/**
* Ownable - The Consumer Contract Wallet
* Copyright (C) 2019 The Contract Wallet Company Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
pragma solidity ^0.5.10;
/// @title Ownable has an owner address and provides basic authorization control functions.
/// This contract is modified version of the MIT OpenZepplin Ownable contract
/// This contract allows for the transferOwnership operation to be made impossible
/// https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ownership/Ownable.sol
contract Ownable {
event TransferredOwnership(address _from, address _to);
event LockedOwnership(address _locked);
address payable private _owner;
bool private _isTransferable;
/// @notice Constructor sets the original owner of the contract and whether or not it is one time transferable.
constructor(address payable _account_, bool _transferable_) internal {
_owner = _account_;
_isTransferable = _transferable_;
// Emit the LockedOwnership event if no longer transferable.
if (!_isTransferable) {
emit LockedOwnership(_account_);
}
emit TransferredOwnership(address(0), _account_);
}
/// @notice Reverts if called by any account other than the owner.
modifier onlyOwner() {
require(_isOwner(msg.sender), "sender is not an owner");
_;
}
/// @notice Allows the current owner to transfer control of the contract to a new address.
/// @param _account address to transfer ownership to.
/// @param _transferable indicates whether to keep the ownership transferable.
function transferOwnership(address payable _account, bool _transferable) external onlyOwner {
// Require that the ownership is transferable.
require(_isTransferable, "ownership is not transferable");
// Require that the new owner is not the zero address.
require(_account != address(0), "owner cannot be set to zero address");
// Set the transferable flag to the value _transferable passed in.
_isTransferable = _transferable;
// Emit the LockedOwnership event if no longer transferable.
if (!_transferable) {
emit LockedOwnership(_account);
}
// Emit the ownership transfer event.
emit TransferredOwnership(_owner, _account);
// Set the owner to the provided address.
_owner = _account;
}
/// @notice check if the ownership is transferable.
/// @return true if the ownership is transferable.
function isTransferable() external view returns (bool) {
return _isTransferable;
}
/// @notice Allows the current owner to relinquish control of the contract.
/// @dev Renouncing to ownership will leave the contract without an owner and unusable.
/// @dev It will not be possible to call the functions with the `onlyOwner` modifier anymore.
function renounceOwnership() external onlyOwner {
// Require that the ownership is transferable.
require(_isTransferable, "ownership is not transferable");
// note that this could be terminal
_owner = address(0);
emit TransferredOwnership(_owner, address(0));
}
/// @notice Find out owner address
/// @return address of the owner.
function owner() public view returns (address payable) {
return _owner;
}
/// @notice Check if owner address
/// @return true if sender is the owner of the contract.
function _isOwner(address _address) internal view returns (bool) {
return _address == _owner;
}
}
strings.sol 733 lines
/* * Copyright 2016 Nick Johnson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * @title String & slice utility library for Solidity contracts. * @author Nick Johnson <[email protected]> * * @dev Functionality in this library is largely implemented using an * abstraction called a 'slice'. A slice represents a part of a string - * anything from the entire string to a single character, or even no * characters at all (a 0-length slice). Since a slice only has to specify * an offset and a length, copying and manipulating slices is a lot less * expensive than copying and manipulating the strings they reference. * * To further reduce gas costs, most functions on slice that need to return * a slice modify the original one instead of allocating a new one; for * instance, `s.split(".")` will return the text up to the first '.', * modifying s to only contain the remainder of the string after the '.'. * In situations where you do not want to modify the original slice, you * can make a copy first with `.copy()`, for example: * `s.copy().split(".")`. Try and avoid using this idiom in loops; since * Solidity has no memory management, it will result in allocating many * short-lived slices that are later discarded. * * Functions that return two slices come in two versions: a non-allocating * version that takes the second slice as an argument, modifying it in * place, and an allocating version that allocates and returns the second * slice; see `nextRune` for example. * * Functions that have to copy string data will return strings rather than * slices; these can be cast back to slices for further processing if * required. * * For convenience, some functions are provided with non-modifying * variants that create a new slice and return both; for instance, * `s.splitNew('.')` leaves s unmodified, and returns two values * corresponding to the left and right parts of the string. */ pragma solidity ^0.5.0; library strings { struct slice { uint _len; uint _ptr; } function memcpy(uint dest, uint src, uint len) private pure { // Copy word-length chunks while possible for(; len >= 32; len -= 32) { assembly { mstore(dest, mload(src)) } dest += 32; src += 32; } // Copy remaining bytes uint mask = 256 ** (32 - len) - 1; assembly { let srcpart := and(mload(src), not(mask)) let destpart := and(mload(dest), mask) mstore(dest, or(destpart, srcpart)) } } /* * @dev Returns a slice containing the entire string. * @param self The string to make a slice from. * @return A newly allocated slice containing the entire string. */ function toSlice(string memory self) internal pure returns (slice memory) { uint ptr; assembly { ptr := add(self, 0x20) } return slice(bytes(self).length, ptr); } /* * @dev Returns the length of a null-terminated bytes32 string. * @param self The value to find the length of. * @return The length of the string, from 0 to 32. */ function len(bytes32 self) internal pure returns (uint) { uint ret; if (self == 0) return 0; if (uint(self) & 0xffffffffffffffffffffffffffffffff == 0) { ret += 16; self = bytes32(uint(self) / 0x100000000000000000000000000000000); } if (uint(self) & 0xffffffffffffffff == 0) { ret += 8; self = bytes32(uint(self) / 0x10000000000000000); } if (uint(self) & 0xffffffff == 0) { ret += 4; self = bytes32(uint(self) / 0x100000000); } if (uint(self) & 0xffff == 0) { ret += 2; self = bytes32(uint(self) / 0x10000); } if (uint(self) & 0xff == 0) { ret += 1; } return 32 - ret; } /* * @dev Returns a slice containing the entire bytes32, interpreted as a * null-terminated utf-8 string. * @param self The bytes32 value to convert to a slice. * @return A new slice containing the value of the input argument up to the * first null. */ function toSliceB32(bytes32 self) internal pure returns (slice memory ret) { // Allocate space for `self` in memory, copy it there, and point ret at it assembly { let ptr := mload(0x40) mstore(0x40, add(ptr, 0x20)) mstore(ptr, self) mstore(add(ret, 0x20), ptr) } ret._len = len(self); } /* * @dev Returns a new slice containing the same data as the current slice. * @param self The slice to copy. * @return A new slice containing the same data as `self`. */ function copy(slice memory self) internal pure returns (slice memory) { return slice(self._len, self._ptr); } /* * @dev Copies a slice to a new string. * @param self The slice to copy. * @return A newly allocated string containing the slice's text. */ function toString(slice memory self) internal pure returns (string memory) { string memory ret = new string(self._len); uint retptr; assembly { retptr := add(ret, 32) } memcpy(retptr, self._ptr, self._len); return ret; } /* * @dev Returns the length in runes of the slice. Note that this operation * takes time proportional to the length of the slice; avoid using it * in loops, and call `slice.empty()` if you only need to know whether * the slice is empty or not. * @param self The slice to operate on. * @return The length of the slice in runes. */ function len(slice memory self) internal pure returns (uint l) { // Starting at ptr-31 means the LSB will be the byte we care about uint ptr = self._ptr - 31; uint end = ptr + self._len; for (l = 0; ptr < end; l++) { uint8 b; assembly { b := and(mload(ptr), 0xFF) } if (b < 0x80) { ptr += 1; } else if (b < 0xE0) { ptr += 2; } else if (b < 0xF0) { ptr += 3; } else if (b < 0xF8) { ptr += 4; } else if (b < 0xFC) { ptr += 5; } else { ptr += 6; } } } /* * @dev Returns true if the slice is empty (has a length of 0). * @param self The slice to operate on. * @return True if the slice is empty, False otherwise. */ function empty(slice memory self) internal pure returns (bool) { return self._len == 0; } /* * @dev Returns a positive number if `other` comes lexicographically after * `self`, a negative number if it comes before, or zero if the * contents of the two slices are equal. Comparison is done per-rune, * on unicode codepoints. * @param self The first slice to compare. * @param other The second slice to compare. * @return The result of the comparison. */ function compare(slice memory self, slice memory other) internal pure returns (int) { uint shortest = self._len; if (other._len < self._len) shortest = other._len; uint selfptr = self._ptr; uint otherptr = other._ptr; for (uint idx = 0; idx < shortest; idx += 32) { uint a; uint b; assembly { a := mload(selfptr) b := mload(otherptr) } if (a != b) { // Mask out irrelevant bytes and check again uint256 mask = uint256(-1); // 0xffff... if (shortest < 32) { mask = ~(2 ** (8 * (32 - shortest + idx)) - 1); } uint256 diff = (a & mask) - (b & mask); if (diff != 0) return int(diff); } selfptr += 32; otherptr += 32; } return int(self._len) - int(other._len); } /* * @dev Returns true if the two slices contain the same text. * @param self The first slice to compare. * @param self The second slice to compare. * @return True if the slices are equal, false otherwise. */ function equals(slice memory self, slice memory other) internal pure returns (bool) { return compare(self, other) == 0; } /* * @dev Extracts the first rune in the slice into `rune`, advancing the * slice to point to the next rune and returning `self`. * @param self The slice to operate on. * @param rune The slice that will contain the first rune. * @return `rune`. */ function nextRune(slice memory self, slice memory rune) internal pure returns (slice memory) { rune._ptr = self._ptr; if (self._len == 0) { rune._len = 0; return rune; } uint l; uint b; // Load the first byte of the rune into the LSBs of b assembly { b := and(mload(sub(mload(add(self, 32)), 31)), 0xFF) } if (b < 0x80) { l = 1; } else if (b < 0xE0) { l = 2; } else if (b < 0xF0) { l = 3; } else { l = 4; } // Check for truncated codepoints if (l > self._len) { rune._len = self._len; self._ptr += self._len; self._len = 0; return rune; } self._ptr += l; self._len -= l; rune._len = l; return rune; } /* * @dev Returns the first rune in the slice, advancing the slice to point * to the next rune. * @param self The slice to operate on. * @return A slice containing only the first rune from `self`. */ function nextRune(slice memory self) internal pure returns (slice memory ret) { nextRune(self, ret); } /* * @dev Returns the number of the first codepoint in the slice. * @param self The slice to operate on. * @return The number of the first codepoint in the slice. */ function ord(slice memory self) internal pure returns (uint ret) { if (self._len == 0) { return 0; } uint word; uint length; uint divisor = 2 ** 248; // Load the rune into the MSBs of b assembly { word:= mload(mload(add(self, 32))) } uint b = word / divisor; if (b < 0x80) { ret = b; length = 1; } else if (b < 0xE0) { ret = b & 0x1F; length = 2; } else if (b < 0xF0) { ret = b & 0x0F; length = 3; } else { ret = b & 0x07; length = 4; } // Check for truncated codepoints if (length > self._len) { return 0; } for (uint i = 1; i < length; i++) { divisor = divisor / 256; b = (word / divisor) & 0xFF; if (b & 0xC0 != 0x80) { // Invalid UTF-8 sequence return 0; } ret = (ret * 64) | (b & 0x3F); } return ret; } /* * @dev Returns the keccak-256 hash of the slice. * @param self The slice to hash. * @return The hash of the slice. */ function keccak(slice memory self) internal pure returns (bytes32 ret) { assembly { ret := keccak256(mload(add(self, 32)), mload(self)) } } /* * @dev Returns true if `self` starts with `needle`. * @param self The slice to operate on. * @param needle The slice to search for. * @return True if the slice starts with the provided text, false otherwise. */ function startsWith(slice memory self, slice memory needle) internal pure returns (bool) { if (self._len < needle._len) { return false; } if (self._ptr == needle._ptr) { return true; } bool equal; assembly { let length := mload(needle) let selfptr := mload(add(self, 0x20)) let needleptr := mload(add(needle, 0x20)) equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } return equal; } /* * @dev If `self` starts with `needle`, `needle` is removed from the * beginning of `self`. Otherwise, `self` is unmodified. * @param self The slice to operate on. * @param needle The slice to search for. * @return `self` */ function beyond(slice memory self, slice memory needle) internal pure returns (slice memory) { if (self._len < needle._len) { return self; } bool equal = true; if (self._ptr != needle._ptr) { assembly { let length := mload(needle) let selfptr := mload(add(self, 0x20)) let needleptr := mload(add(needle, 0x20)) equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } } if (equal) { self._len -= needle._len; self._ptr += needle._len; } return self; } /* * @dev Returns true if the slice ends with `needle`. * @param self The slice to operate on. * @param needle The slice to search for. * @return True if the slice starts with the provided text, false otherwise. */ function endsWith(slice memory self, slice memory needle) internal pure returns (bool) { if (self._len < needle._len) { return false; } uint selfptr = self._ptr + self._len - needle._len; if (selfptr == needle._ptr) { return true; } bool equal; assembly { let length := mload(needle) let needleptr := mload(add(needle, 0x20)) equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } return equal; } /* * @dev If `self` ends with `needle`, `needle` is removed from the * end of `self`. Otherwise, `self` is unmodified. * @param self The slice to operate on. * @param needle The slice to search for. * @return `self` */ function until(slice memory self, slice memory needle) internal pure returns (slice memory) { if (self._len < needle._len) { return self; } uint selfptr = self._ptr + self._len - needle._len; bool equal = true; if (selfptr != needle._ptr) { assembly { let length := mload(needle) let needleptr := mload(add(needle, 0x20)) equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } } if (equal) { self._len -= needle._len; } return self; } // Returns the memory address of the first byte of the first occurrence of // `needle` in `self`, or the first byte after `self` if not found. function findPtr(uint selflen, uint selfptr, uint needlelen, uint needleptr) private pure returns (uint) { uint ptr = selfptr; uint idx; if (needlelen <= selflen) { if (needlelen <= 32) { bytes32 mask = bytes32(~(2 ** (8 * (32 - needlelen)) - 1)); bytes32 needledata; assembly { needledata := and(mload(needleptr), mask) } uint end = selfptr + selflen - needlelen; bytes32 ptrdata; assembly { ptrdata := and(mload(ptr), mask) } while (ptrdata != needledata) { if (ptr >= end) return selfptr + selflen; ptr++; assembly { ptrdata := and(mload(ptr), mask) } } return ptr; } else { // For long needles, use hashing bytes32 hash; assembly { hash := keccak256(needleptr, needlelen) } for (idx = 0; idx <= selflen - needlelen; idx++) { bytes32 testHash; assembly { testHash := keccak256(ptr, needlelen) } if (hash == testHash) return ptr; ptr += 1; } } } return selfptr + selflen; } // Returns the memory address of the first byte after the last occurrence of // `needle` in `self`, or the address of `self` if not found. function rfindPtr(uint selflen, uint selfptr, uint needlelen, uint needleptr) private pure returns (uint) { uint ptr; if (needlelen <= selflen) { if (needlelen <= 32) { bytes32 mask = bytes32(~(2 ** (8 * (32 - needlelen)) - 1)); bytes32 needledata; assembly { needledata := and(mload(needleptr), mask) } ptr = selfptr + selflen - needlelen; bytes32 ptrdata; assembly { ptrdata := and(mload(ptr), mask) } while (ptrdata != needledata) { if (ptr <= selfptr) return selfptr; ptr--; assembly { ptrdata := and(mload(ptr), mask) } } return ptr + needlelen; } else { // For long needles, use hashing bytes32 hash; assembly { hash := keccak256(needleptr, needlelen) } ptr = selfptr + (selflen - needlelen); while (ptr >= selfptr) { bytes32 testHash; assembly { testHash := keccak256(ptr, needlelen) } if (hash == testHash) return ptr + needlelen; ptr -= 1; } } } return selfptr; } /* * @dev Modifies `self` to contain everything from the first occurrence of * `needle` to the end of the slice. `self` is set to the empty slice * if `needle` is not found. * @param self The slice to search and modify. * @param needle The text to search for. * @return `self`. */ function find(slice memory self, slice memory needle) internal pure returns (slice memory) { uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr); self._len -= ptr - self._ptr; self._ptr = ptr; return self; } /* * @dev Modifies `self` to contain the part of the string from the start of * `self` to the end of the first occurrence of `needle`. If `needle` * is not found, `self` is set to the empty slice. * @param self The slice to search and modify. * @param needle The text to search for. * @return `self`. */ function rfind(slice memory self, slice memory needle) internal pure returns (slice memory) { uint ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr); self._len = ptr - self._ptr; return self; } /* * @dev Splits the slice, setting `self` to everything after the first * occurrence of `needle`, and `token` to everything before it. If * `needle` does not occur in `self`, `self` is set to the empty slice, * and `token` is set to the entirety of `self`. * @param self The slice to split. * @param needle The text to search for in `self`. * @param token An output parameter to which the first token is written. * @return `token`. */ function split(slice memory self, slice memory needle, slice memory token) internal pure returns (slice memory) { uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr); token._ptr = self._ptr; token._len = ptr - self._ptr; if (ptr == self._ptr + self._len) { // Not found self._len = 0; } else { self._len -= token._len + needle._len; self._ptr = ptr + needle._len; } return token; } /* * @dev Splits the slice, setting `self` to everything after the first * occurrence of `needle`, and returning everything before it. If * `needle` does not occur in `self`, `self` is set to the empty slice, * and the entirety of `self` is returned. * @param self The slice to split. * @param needle The text to search for in `self`. * @return The part of `self` up to the first occurrence of `delim`. */ function split(slice memory self, slice memory needle) internal pure returns (slice memory token) { split(self, needle, token); } /* * @dev Splits the slice, setting `self` to everything before the last * occurrence of `needle`, and `token` to everything after it. If * `needle` does not occur in `self`, `self` is set to the empty slice, * and `token` is set to the entirety of `self`. * @param self The slice to split. * @param needle The text to search for in `self`. * @param token An output parameter to which the first token is written. * @return `token`. */ function rsplit(slice memory self, slice memory needle, slice memory token) internal pure returns (slice memory) { uint ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr); token._ptr = ptr; token._len = self._len - (ptr - self._ptr); if (ptr == self._ptr) { // Not found self._len = 0; } else { self._len -= token._len + needle._len; } return token; } /* * @dev Splits the slice, setting `self` to everything before the last * occurrence of `needle`, and returning everything after it. If * `needle` does not occur in `self`, `self` is set to the empty slice, * and the entirety of `self` is returned. * @param self The slice to split. * @param needle The text to search for in `self`. * @return The part of `self` after the last occurrence of `delim`. */ function rsplit(slice memory self, slice memory needle) internal pure returns (slice memory token) { rsplit(self, needle, token); } /* * @dev Counts the number of nonoverlapping occurrences of `needle` in `self`. * @param self The slice to search. * @param needle The text to search for in `self`. * @return The number of occurrences of `needle` found in `self`. */ function count(slice memory self, slice memory needle) internal pure returns (uint cnt) { uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr) + needle._len; while (ptr <= self._ptr + self._len) { cnt++; ptr = findPtr(self._len - (ptr - self._ptr), ptr, needle._len, needle._ptr) + needle._len; } } /* * @dev Returns True if `self` contains `needle`. * @param self The slice to search. * @param needle The text to search for in `self`. * @return True if `needle` is found in `self`, false otherwise. */ function contains(slice memory self, slice memory needle) internal pure returns (bool) { return rfindPtr(self._len, self._ptr, needle._len, needle._ptr) != self._ptr; } /* * @dev Returns a newly allocated string containing the concatenation of * `self` and `other`. * @param self The first slice to concatenate. * @param other The second slice to concatenate. * @return The concatenation of the two strings. */ function concat(slice memory self, slice memory other) internal pure returns (string memory) { string memory ret = new string(self._len + other._len); uint retptr; assembly { retptr := add(ret, 32) } memcpy(retptr, self._ptr, self._len); memcpy(retptr + self._len, other._ptr, other._len); return ret; } /* * @dev Joins an array of slices, using `self` as a delimiter, returning a * newly allocated string. * @param self The delimiter to use. * @param parts A list of slices to join. * @return A newly allocated string containing all the slices in `parts`, * joined with `self`. */ function join(slice memory self, slice[] memory parts) internal pure returns (string memory) { if (parts.length == 0) return ""; uint length = self._len * (parts.length - 1); for (uint i = 0; i < parts.length; i++) { length += parts[i]._len; } string memory ret = new string(length); uint retptr; assembly { retptr := add(ret, 32) } for (uint i = 0; i < parts.length; i++) { memcpy(retptr, parts[i]._ptr, parts[i]._len); retptr += parts[i]._len; if (i < parts.length - 1) { memcpy(retptr, self._ptr, self._len); retptr += self._len; } } return ret; } }
SafeMath.sol 107 lines
pragma solidity ^0.5.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot 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, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, "SafeMath: division by zero");
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "SafeMath: modulo by zero");
return a % b;
}
}
SafeERC20.sol 100 lines
/**
* The MIT License (MIT)
*
* Copyright (c) 2016-2019 zOS Global Limited
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
pragma solidity ^0.5.0;
import "./ERC20.sol";
import "./SafeMath.sol";
import "./Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(ERC20 token, address to, uint256 value) internal {
callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(ERC20 token, address from, address to, uint256 value) internal {
callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(ERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
// solhint-disable-next-line max-line-length
require((value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(ERC20 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(ERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function callOptionalReturn(ERC20 token, bytes memory data) internal {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves.
// A Solidity high level call has three parts:
// 1. The target address is checked to verify it contains contract code
// 2. The call itself is made, and success asserted
// 3. The return value is decoded, which in turn checks the size of the returned data.
// solhint-disable-next-line max-line-length
require(address(token).isContract(), "SafeERC20: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = address(token).call(data);
require(success, "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");
}
}
}
bytesUtils.sol 79 lines
/**
* BytesUtils - The Consumer Contract Wallet
* Copyright (C) 2019 The Contract Wallet Company Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
pragma solidity ^0.5.10;
import "./SafeMath.sol";
/// @title BytesUtils provides basic byte slicing and casting functionality.
library BytesUtils {
using SafeMath for uint256;
/// @dev This function converts to an address
/// @param _bts bytes
/// @param _from start position
function _bytesToAddress(bytes memory _bts, uint _from) internal pure returns (address) {
require(_bts.length >= _from.add(20), "slicing out of range");
bytes20 convertedAddress;
uint startByte = _from.add(32); //first 32 bytes denote the array length
assembly {
convertedAddress := mload(add(_bts, startByte))
}
return address(convertedAddress);
}
/// @dev This function slices bytes into bytes4
/// @param _bts some bytes
/// @param _from start position
function _bytesToBytes4(bytes memory _bts, uint _from) internal pure returns (bytes4) {
require(_bts.length >= _from.add(4), "slicing out of range");
bytes4 slicedBytes4;
uint startByte = _from.add(32); //first 32 bytes denote the array length
assembly {
slicedBytes4 := mload(add(_bts, startByte))
}
return slicedBytes4;
}
/// @dev This function slices a uint
/// @param _bts some bytes
/// @param _from start position
// credit to https://ethereum.stackexchange.com/questions/51229/how-to-convert-bytes-to-uint-in-solidity
// and Nick Johnson https://ethereum.stackexchange.com/questions/4170/how-to-convert-a-uint-to-bytes-in-solidity/4177#4177
function _bytesToUint256(bytes memory _bts, uint _from) internal pure returns (uint) {
require(_bts.length >= _from.add(32), "slicing out of range");
uint convertedUint256;
uint startByte = _from.add(32); //first 32 bytes denote the array length
assembly {
convertedUint256 := mload(add(_bts, startByte))
}
return convertedUint256;
}
}
controller.sol 187 lines
/**
* Controller - The Consumer Contract Wallet
* Copyright (C) 2019 The Contract Wallet Company Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
pragma solidity ^0.5.10;
import "./ownable.sol";
import "./transferrable.sol";
/// @title The IController interface provides access to the isController and isAdmin checks.
interface IController {
function isController(address) external view returns (bool);
function isAdmin(address) external view returns (bool);
}
/// @title Controller stores a list of controller addresses that can be used for authentication in other contracts.
/// @notice The Controller implements a hierarchy of concepts, Owner, Admin, and the Controllers.
/// @dev Owner can change the Admins
/// @dev Admins and can the Controllers
/// @dev Controllers are used by the application.
contract Controller is IController, Ownable, Transferrable {
event AddedController(address _sender, address _controller);
event RemovedController(address _sender, address _controller);
event AddedAdmin(address _sender, address _admin);
event RemovedAdmin(address _sender, address _admin);
event Claimed(address _to, address _asset, uint _amount);
event Stopped(address _sender);
event Started(address _sender);
mapping (address => bool) private _isAdmin;
uint private _adminCount;
mapping (address => bool) private _isController;
uint private _controllerCount;
bool private _stopped;
/// @notice Constructor initializes the owner with the provided address.
/// @param _ownerAddress_ address of the owner.
constructor(address payable _ownerAddress_) Ownable(_ownerAddress_, false) public {}
/// @notice Checks if message sender is an admin.
modifier onlyAdmin() {
require(isAdmin(msg.sender), "sender is not an admin");
_;
}
/// @notice Check if Owner or Admin
modifier onlyAdminOrOwner() {
require(_isOwner(msg.sender) || isAdmin(msg.sender), "sender is not an admin");
_;
}
/// @notice Check if controller is stopped
modifier notStopped() {
require(!isStopped(), "controller is stopped");
_;
}
/// @notice Add a new admin to the list of admins.
/// @param _account address to add to the list of admins.
function addAdmin(address _account) external onlyOwner notStopped {
_addAdmin(_account);
}
/// @notice Remove a admin from the list of admins.
/// @param _account address to remove from the list of admins.
function removeAdmin(address _account) external onlyOwner {
_removeAdmin(_account);
}
/// @return the current number of admins.
function adminCount() external view returns (uint) {
return _adminCount;
}
/// @notice Add a new controller to the list of controllers.
/// @param _account address to add to the list of controllers.
function addController(address _account) external onlyAdminOrOwner notStopped {
_addController(_account);
}
/// @notice Remove a controller from the list of controllers.
/// @param _account address to remove from the list of controllers.
function removeController(address _account) external onlyAdminOrOwner {
_removeController(_account);
}
/// @notice count the Controllers
/// @return the current number of controllers.
function controllerCount() external view returns (uint) {
return _controllerCount;
}
/// @notice is an address an Admin?
/// @return true if the provided account is an admin.
function isAdmin(address _account) public view notStopped returns (bool) {
return _isAdmin[_account];
}
/// @notice is an address a Controller?
/// @return true if the provided account is a controller.
function isController(address _account) public view notStopped returns (bool) {
return _isController[_account];
}
/// @notice this function can be used to see if the controller has been stopped
/// @return true is the Controller has been stopped
function isStopped() public view returns (bool) {
return _stopped;
}
/// @notice Internal-only function that adds a new admin.
function _addAdmin(address _account) private {
require(!_isAdmin[_account], "provided account is already an admin");
require(!_isController[_account], "provided account is already a controller");
require(!_isOwner(_account), "provided account is already the owner");
require(_account != address(0), "provided account is the zero address");
_isAdmin[_account] = true;
_adminCount++;
emit AddedAdmin(msg.sender, _account);
}
/// @notice Internal-only function that removes an existing admin.
function _removeAdmin(address _account) private {
require(_isAdmin[_account], "provided account is not an admin");
_isAdmin[_account] = false;
_adminCount--;
emit RemovedAdmin(msg.sender, _account);
}
/// @notice Internal-only function that adds a new controller.
function _addController(address _account) private {
require(!_isAdmin[_account], "provided account is already an admin");
require(!_isController[_account], "provided account is already a controller");
require(!_isOwner(_account), "provided account is already the owner");
require(_account != address(0), "provided account is the zero address");
_isController[_account] = true;
_controllerCount++;
emit AddedController(msg.sender, _account);
}
/// @notice Internal-only function that removes an existing controller.
function _removeController(address _account) private {
require(_isController[_account], "provided account is not a controller");
_isController[_account] = false;
_controllerCount--;
emit RemovedController(msg.sender, _account);
}
/// @notice stop our controllers and admins from being useable
function stop() external onlyAdminOrOwner {
_stopped = true;
emit Stopped(msg.sender);
}
/// @notice start our controller again
function start() external onlyOwner {
_stopped = false;
emit Started(msg.sender);
}
//// @notice Withdraw tokens from the smart contract to the specified account.
function claim(address payable _to, address _asset, uint _amount) external onlyAdmin notStopped {
_safeTransfer(_to, _asset, _amount);
emit Claimed(_to, _asset, _amount);
}
}
ABIResolver.sol 51 lines
pragma solidity ^0.5.0;
import "./ResolverBase.sol";
contract ABIResolver is ResolverBase {
bytes4 constant private ABI_INTERFACE_ID = 0x2203ab56;
event ABIChanged(bytes32 indexed node, uint256 indexed contentType);
mapping(bytes32=>mapping(uint256=>bytes)) abis;
/**
* Sets the ABI associated with an ENS node.
* Nodes may have one ABI of each content type. To remove an ABI, set it to
* the empty string.
* @param node The node to update.
* @param contentType The content type of the ABI
* @param data The ABI data.
*/
function setABI(bytes32 node, uint256 contentType, bytes calldata data) external authorised(node) {
// Content types must be powers of 2
require(((contentType - 1) & contentType) == 0);
abis[node][contentType] = data;
emit ABIChanged(node, contentType);
}
/**
* Returns the ABI associated with an ENS node.
* Defined in EIP205.
* @param node The ENS node to query
* @param contentTypes A bitwise OR of the ABI formats accepted by the caller.
* @return contentType The content type of the return value
* @return data The ABI data
*/
function ABI(bytes32 node, uint256 contentTypes) external view returns (uint256, bytes memory) {
mapping(uint256=>bytes) storage abiset = abis[node];
for (uint256 contentType = 1; contentType <= contentTypes; contentType <<= 1) {
if ((contentType & contentTypes) != 0 && abiset[contentType].length > 0) {
return (contentType, abiset[contentType]);
}
}
return (0, bytes(""));
}
function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
return interfaceID == ABI_INTERFACE_ID || super.supportsInterface(interfaceID);
}
}
balanceable.sol 38 lines
/**
* Balanceable - The Consumer Contract Wallet
* Copyright (C) 2019 The Contract Wallet Company Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
pragma solidity ^0.5.10;
import "./ERC20.sol";
/// @title Balanceable - This is a contract used to get a balance
contract Balanceable {
/// @dev This function is used to get a balance
/// @param _address of which balance we are trying to ascertain
/// @param _asset is the address of an ERC20 token or 0x0 for ether.
/// @return balance associated with an address, for any token, in the wei equivalent
function _balance(address _address, address _asset) internal view returns (uint) {
if (_asset != address(0)) {
return ERC20(_asset).balanceOf(_address);
} else {
return _address.balance;
}
}
}
AddrResolver.sol 35 lines
pragma solidity ^0.5.0;
import "./ResolverBase.sol";
contract AddrResolver is ResolverBase {
bytes4 constant private ADDR_INTERFACE_ID = 0x3b3b57de;
event AddrChanged(bytes32 indexed node, address a);
mapping(bytes32=>address) addresses;
/**
* Sets the address associated with an ENS node.
* May only be called by the owner of that node in the ENS registry.
* @param node The node to update.
* @param addr The address to set.
*/
function setAddr(bytes32 node, address addr) external authorised(node) {
addresses[node] = addr;
emit AddrChanged(node, addr);
}
/**
* Returns the address associated with an ENS node.
* @param node The ENS node to query.
* @return The associated address.
*/
function addr(bytes32 node) public view returns (address) {
return addresses[node];
}
function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
return interfaceID == ADDR_INTERFACE_ID || super.supportsInterface(interfaceID);
}
}
NameResolver.sol 36 lines
pragma solidity ^0.5.0;
import "./ResolverBase.sol";
contract NameResolver is ResolverBase {
bytes4 constant private NAME_INTERFACE_ID = 0x691f3431;
event NameChanged(bytes32 indexed node, string name);
mapping(bytes32=>string) names;
/**
* Sets the name associated with an ENS node, for reverse records.
* May only be called by the owner of that node in the ENS registry.
* @param node The node to update.
* @param name The name to set.
*/
function setName(bytes32 node, string calldata name) external authorised(node) {
names[node] = name;
emit NameChanged(node, name);
}
/**
* Returns the name associated with an ENS node, for reverse records.
* Defined in EIP181.
* @param node The ENS node to query.
* @return The associated name.
*/
function name(bytes32 node) external view returns (string memory) {
return names[node];
}
function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
return interfaceID == NAME_INTERFACE_ID || super.supportsInterface(interfaceID);
}
}
ResolverBase.sol 16 lines
pragma solidity ^0.5.0;
contract ResolverBase {
bytes4 private constant INTERFACE_META_ID = 0x01ffc9a7;
function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
return interfaceID == INTERFACE_META_ID;
}
function isAuthorised(bytes32 node) internal view returns(bool);
modifier authorised(bytes32 node) {
require(isAuthorised(node));
_;
}
}
TextResolver.sol 37 lines
pragma solidity ^0.5.0;
import "./ResolverBase.sol";
contract TextResolver is ResolverBase {
bytes4 constant private TEXT_INTERFACE_ID = 0x59d1d43c;
event TextChanged(bytes32 indexed node, string indexedKey, string key);
mapping(bytes32=>mapping(string=>string)) texts;
/**
* Sets the text data associated with an ENS node and key.
* May only be called by the owner of that node in the ENS registry.
* @param node The node to update.
* @param key The key to set.
* @param value The text data value to set.
*/
function setText(bytes32 node, string calldata key, string calldata value) external authorised(node) {
texts[node][key] = value;
emit TextChanged(node, key, key);
}
/**
* Returns the text data associated with an ENS node and key.
* @param node The ENS node to query.
* @param key The text data key to query.
* @return The associated text data.
*/
function text(bytes32 node, string calldata key) external view returns (string memory) {
return texts[node][key];
}
function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
return interfaceID == TEXT_INTERFACE_ID || super.supportsInterface(interfaceID);
}
}
controllable.sol 63 lines
/**
* Controllable - The Consumer Contract Wallet
* Copyright (C) 2019 The Contract Wallet Company Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
pragma solidity ^0.5.10;
import "./controller.sol";
import "./ensResolvable.sol";
/// @title Controllable implements access control functionality of the Controller found via ENS.
contract Controllable is ENSResolvable {
/// @dev Is the registered ENS node identifying the controller contract.
bytes32 private _controllerNode;
/// @notice Constructor initializes the controller contract object.
/// @param _controllerNode_ is the ENS node of the Controller.
constructor(bytes32 _controllerNode_) internal {
_controllerNode = _controllerNode_;
}
/// @notice Checks if message sender is a controller.
modifier onlyController() {
require(_isController(msg.sender), "sender is not a controller");
_;
}
/// @notice Checks if message sender is an admin.
modifier onlyAdmin() {
require(_isAdmin(msg.sender), "sender is not an admin");
_;
}
/// @return the controller node registered in ENS.
function controllerNode() external view returns (bytes32) {
return _controllerNode;
}
/// @return true if the provided account is a controller.
function _isController(address _account) internal view returns (bool) {
return IController(_ensResolve(_controllerNode)).isController(_account);
}
/// @return true if the provided account is an admin.
function _isAdmin(address _account) internal view returns (bool) {
return IController(_ensResolve(_controllerNode)).isAdmin(_account);
}
}
ensResolvable.sol 52 lines
/**
* ENSResolvable - The Consumer Contract Wallet
* Copyright (C) 2019 The Contract Wallet Company Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
pragma solidity ^0.5.10;
import "./ENS.sol";
import "./PublicResolver.sol";
///@title ENSResolvable - Ethereum Name Service Resolver
///@notice contract should be used to get an address for an ENS node
contract ENSResolvable {
/// @notice _ens is an instance of ENS
ENS private _ens;
/// @notice _ensRegistry points to the ENS registry smart contract.
address private _ensRegistry;
/// @param _ensReg_ is the ENS registry used
constructor(address _ensReg_) internal {
_ensRegistry = _ensReg_;
_ens = ENS(_ensRegistry);
}
/// @notice this is used to that one can observe which ENS registry is being used
function ensRegistry() external view returns (address) {
return _ensRegistry;
}
/// @notice helper function used to get the address of a node
/// @param _node of the ENS entry that needs resolving
/// @return the address of the said node
function _ensResolve(bytes32 _node) internal view returns (address) {
return PublicResolver(_ens.resolver(_node)).addr(_node);
}
}
transferrable.sol 44 lines
/**
* Transferrable - The Consumer Contract Wallet
* Copyright (C) 2019 The Contract Wallet Company Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
pragma solidity ^0.5.10;
import "./ERC20.sol";
import "./SafeERC20.sol";
/// @title SafeTransfer, allowing contract to withdraw tokens accidentally sent to itself
contract Transferrable {
using SafeERC20 for ERC20;
/// @dev This function is used to move tokens sent accidentally to this contract method.
/// @dev The owner can chose the new destination address
/// @param _to is the recipient's address.
/// @param _asset is the address of an ERC20 token or 0x0 for ether.
/// @param _amount is the amount to be transferred in base units.
function _safeTransfer(address payable _to, address _asset, uint _amount) internal {
// address(0) is used to denote ETH
if (_asset == address(0)) {
_to.transfer(_amount);
} else {
ERC20(_asset).safeTransfer(_to, _amount);
}
}
}
PubkeyResolver.sol 41 lines
pragma solidity ^0.5.0;
import "./ResolverBase.sol";
contract PubkeyResolver is ResolverBase {
bytes4 constant private PUBKEY_INTERFACE_ID = 0xc8690233;
event PubkeyChanged(bytes32 indexed node, bytes32 x, bytes32 y);
struct PublicKey {
bytes32 x;
bytes32 y;
}
mapping(bytes32=>PublicKey) pubkeys;
/**
* Sets the SECP256k1 public key associated with an ENS node.
* @param node The ENS node to query
* @param x the X coordinate of the curve point for the public key.
* @param y the Y coordinate of the curve point for the public key.
*/
function setPubkey(bytes32 node, bytes32 x, bytes32 y) external authorised(node) {
pubkeys[node] = PublicKey(x, y);
emit PubkeyChanged(node, x, y);
}
/**
* Returns the SECP256k1 public key associated with an ENS node.
* Defined in EIP 619.
* @param node The ENS node to query
* @return x, y the X and Y coordinates of the curve point for the public key.
*/
function pubkey(bytes32 node) external view returns (bytes32 x, bytes32 y) {
return (pubkeys[node].x, pubkeys[node].y);
}
function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
return interfaceID == PUBKEY_INTERFACE_ID || super.supportsInterface(interfaceID);
}
}
PublicResolver.sol 82 lines
/**
* BSD 2-Clause License
*
* Copyright (c) 2018, True Names Limited
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
pragma solidity ^0.5.0;
import "./ENS.sol";
import "./ABIResolver.sol";
import "./AddrResolver.sol";
import "./ContentHashResolver.sol";
import "./InterfaceResolver.sol";
import "./NameResolver.sol";
import "./PubkeyResolver.sol";
import "./TextResolver.sol";
/**
* A simple resolver anyone can use; only allows the owner of a node to set its
* address.
*/
contract PublicResolver is ABIResolver, AddrResolver, ContentHashResolver, InterfaceResolver, NameResolver, PubkeyResolver, TextResolver {
ENS ens;
/**
* A mapping of authorisations. An address that is authorised for a name
* may make any changes to the name that the owner could, but may not update
* the set of authorisations.
* (node, owner, caller) => isAuthorised
*/
mapping(bytes32=>mapping(address=>mapping(address=>bool))) public authorisations;
event AuthorisationChanged(bytes32 indexed node, address indexed owner, address indexed target, bool isAuthorised);
constructor(ENS _ens) public {
ens = _ens;
}
/**
* @dev Sets or clears an authorisation.
* Authorisations are specific to the caller. Any account can set an authorisation
* for any name, but the authorisation that is checked will be that of the
* current owner of a name. Thus, transferring a name effectively clears any
* existing authorisations, and new authorisations can be set in advance of
* an ownership transfer if desired.
*
* @param node The name to change the authorisation on.
* @param target The address that is to be authorised or deauthorised.
* @param isAuthorised True if the address should be authorised, or false if it should be deauthorised.
*/
function setAuthorisation(bytes32 node, address target, bool isAuthorised) external {
authorisations[node][msg.sender][target] = isAuthorised;
emit AuthorisationChanged(node, msg.sender, target, isAuthorised);
}
function isAuthorised(bytes32 node) internal view returns(bool) {
address owner = ens.owner(node);
return owner == msg.sender || authorisations[node][owner][msg.sender];
}
}
tokenWhitelist.sol 326 lines
/**
* TokenWhitelist - The Consumer Contract Wallet
* Copyright (C) 2019 The Contract Wallet Company Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
pragma solidity ^0.5.10;
import "./controllable.sol";
import "./transferrable.sol";
import "./bytesUtils.sol";
import "./strings.sol";
import "./SafeMath.sol";
/// @title The ITokenWhitelist interface provides access to a whitelist of tokens.
interface ITokenWhitelist {
function getTokenInfo(address) external view returns (string memory, uint256, uint256, bool, bool, bool, uint256);
function getStablecoinInfo() external view returns (string memory, uint256, uint256, bool, bool, bool, uint256);
function tokenAddressArray() external view returns (address[] memory);
function redeemableTokens() external view returns (address[] memory);
function methodIdWhitelist(bytes4) external view returns (bool);
function getERC20RecipientAndAmount(address, bytes calldata) external view returns (address, uint);
function stablecoin() external view returns (address);
function updateTokenRate(address, uint, uint) external;
}
/// @title TokenWhitelist stores a list of tokens used by the Consumer Contract Wallet, the Oracle, the TKN Holder and the TKN Licence Contract
contract TokenWhitelist is ENSResolvable, Controllable, Transferrable {
using strings for *;
using SafeMath for uint256;
using BytesUtils for bytes;
event UpdatedTokenRate(address _sender, address _token, uint _rate);
event UpdatedTokenLoadable(address _sender, address _token, bool _loadable);
event UpdatedTokenRedeemable(address _sender, address _token, bool _redeemable);
event AddedToken(address _sender, address _token, string _symbol, uint _magnitude, bool _loadable, bool _redeemable);
event RemovedToken(address _sender, address _token);
event AddedMethodId(bytes4 _methodId);
event RemovedMethodId(bytes4 _methodId);
event AddedExclusiveMethod(address _token, bytes4 _methodId);
event RemovedExclusiveMethod(address _token, bytes4 _methodId);
event Claimed(address _to, address _asset, uint _amount);
/// @dev these are the methods whitelisted by default in executeTransaction() for protected tokens
bytes4 private constant _APPROVE = 0x095ea7b3; // keccak256(approve(address,uint256)) => 0x095ea7b3
bytes4 private constant _BURN = 0x42966c68; // keccak256(burn(uint256)) => 0x42966c68
bytes4 private constant _TRANSFER= 0xa9059cbb; // keccak256(transfer(address,uint256)) => 0xa9059cbb
bytes4 private constant _TRANSFER_FROM = 0x23b872dd; // keccak256(transferFrom(address,address,uint256)) => 0x23b872dd
struct Token {
string symbol; // Token symbol
uint magnitude; // 10^decimals
uint rate; // Token exchange rate in wei
bool available; // Flags if the token is available or not
bool loadable; // Flags if token is loadable to the TokenCard
bool redeemable; // Flags if token is redeemable in the TKN Holder contract
uint lastUpdate; // Time of the last rate update
}
mapping(address => Token) private _tokenInfoMap;
// @notice specifies whitelisted methodIds for protected tokens in wallet's excuteTranaction() e.g. keccak256(transfer(address,uint256)) => 0xa9059cbb
mapping(bytes4 => bool) private _methodIdWhitelist;
address[] private _tokenAddressArray;
/// @notice keeping track of how many redeemable tokens are in the tokenWhitelist
uint private _redeemableCounter;
/// @notice Address of the stablecoin.
address private _stablecoin;
/// @notice is registered ENS node identifying the oracle contract.
bytes32 private _oracleNode;
/// @notice Constructor initializes ENSResolvable, and Controllable.
/// @param _ens_ is the ENS registry address.
/// @param _oracleNode_ is the ENS node of the Oracle.
/// @param _controllerNode_ is our Controllers node.
/// @param _stablecoinAddress_ is the address of the stablecoint used by the wallet for the card load limit.
constructor(address _ens_, bytes32 _oracleNode_, bytes32 _controllerNode_, address _stablecoinAddress_) ENSResolvable(_ens_) Controllable(_controllerNode_) public {
_oracleNode = _oracleNode_;
_stablecoin = _stablecoinAddress_;
//a priori ERC20 whitelisted methods
_methodIdWhitelist[_APPROVE] = true;
_methodIdWhitelist[_BURN] = true;
_methodIdWhitelist[_TRANSFER] = true;
_methodIdWhitelist[_TRANSFER_FROM] = true;
}
modifier onlyAdminOrOracle() {
address oracleAddress = _ensResolve(_oracleNode);
require (_isAdmin(msg.sender) || msg.sender == oracleAddress, "either oracle or admin");
_;
}
/// @notice Add ERC20 tokens to the list of whitelisted tokens.
/// @param _tokens ERC20 token contract addresses.
/// @param _symbols ERC20 token names.
/// @param _magnitude 10 to the power of number of decimal places used by each ERC20 token.
/// @param _loadable is a bool that states whether or not a token is loadable to the TokenCard.
/// @param _redeemable is a bool that states whether or not a token is redeemable in the TKN Holder Contract.
/// @param _lastUpdate is a unit representing an ISO datetime e.g. 20180913153211.
function addTokens(address[] calldata _tokens, bytes32[] calldata _symbols, uint[] calldata _magnitude, bool[] calldata _loadable, bool[] calldata _redeemable, uint _lastUpdate) external onlyAdmin {
// Require that all parameters have the same length.
require(_tokens.length == _symbols.length && _tokens.length == _magnitude.length && _tokens.length == _loadable.length && _tokens.length == _loadable.length, "parameter lengths do not match");
// Add each token to the list of supported tokens.
for (uint i = 0; i < _tokens.length; i++) {
// Require that the token isn't already available.
require(!_tokenInfoMap[_tokens[i]].available, "token already available");
// Store the intermediate values.
string memory symbol = _symbols[i].toSliceB32().toString();
// Add the token to the token list.
_tokenInfoMap[_tokens[i]] = Token({
symbol : symbol,
magnitude : _magnitude[i],
rate : 0,
available : true,
loadable : _loadable[i],
redeemable: _redeemable[i],
lastUpdate : _lastUpdate
});
// Add the token address to the address list.
_tokenAddressArray.push(_tokens[i]);
//if the token is redeemable increase the redeemableCounter
if (_redeemable[i]){
_redeemableCounter = _redeemableCounter.add(1);
}
// Emit token addition event.
emit AddedToken(msg.sender, _tokens[i], symbol, _magnitude[i], _loadable[i], _redeemable[i]);
}
}
/// @notice Remove ERC20 tokens from the whitelist of tokens.
/// @param _tokens ERC20 token contract addresses.
function removeTokens(address[] calldata _tokens) external onlyAdmin {
// Delete each token object from the list of supported tokens based on the addresses provided.
for (uint i = 0; i < _tokens.length; i++) {
// Store the token address.
address token = _tokens[i];
//token must be available, reverts on duplicates as well
require(_tokenInfoMap[token].available, "token is not available");
//if the token is redeemable decrease the redeemableCounter
if (_tokenInfoMap[token].redeemable){
_redeemableCounter = _redeemableCounter.sub(1);
}
// Delete the token object.
delete _tokenInfoMap[token];
// Remove the token address from the address list.
for (uint j = 0; j < _tokenAddressArray.length.sub(1); j++) {
if (_tokenAddressArray[j] == token) {
_tokenAddressArray[j] = _tokenAddressArray[_tokenAddressArray.length.sub(1)];
break;
}
}
_tokenAddressArray.length--;
// Emit token removal event.
emit RemovedToken(msg.sender, token);
}
}
/// @notice based on the method it returns the recipient address and amount/value, ERC20 specific.
/// @param _data is the transaction payload.
function getERC20RecipientAndAmount(address _token, bytes calldata _data) external view returns (address, uint) {
// Require that there exist enough bytes for encoding at least a method signature + data in the transaction payload:
// 4 (signature) + 32(address or uint256)
require(_data.length >= 4 + 32, "not enough method-encoding bytes");
// Get the method signature
bytes4 signature = _data._bytesToBytes4(0);
// Check if method Id is supported
require(isERC20MethodSupported(_token, signature), "unsupported method");
// returns the recipient's address and amount is the value to be transferred
if (signature == _BURN) {
// 4 (signature) + 32(uint256)
return (_token, _data._bytesToUint256(4));
} else if (signature == _TRANSFER_FROM) {
// 4 (signature) + 32(address) + 32(address) + 32(uint256)
require(_data.length >= 4 + 32 + 32 + 32, "not enough data for transferFrom");
return ( _data._bytesToAddress(4 + 32 + 12), _data._bytesToUint256(4 + 32 + 32));
} else { //transfer or approve
// 4 (signature) + 32(address) + 32(uint)
require(_data.length >= 4 + 32 + 32, "not enough data for transfer/appprove");
return (_data._bytesToAddress(4 + 12), _data._bytesToUint256(4 + 32));
}
}
/// @notice Toggles whether or not a token is loadable or not.
function setTokenLoadable(address _token, bool _loadable) external onlyAdmin {
// Require that the token exists.
require(_tokenInfoMap[_token].available, "token is not available");
// this sets the loadable flag to the value passed in
_tokenInfoMap[_token].loadable = _loadable;
emit UpdatedTokenLoadable(msg.sender, _token, _loadable);
}
/// @notice Toggles whether or not a token is redeemable or not.
function setTokenRedeemable(address _token, bool _redeemable) external onlyAdmin {
// Require that the token exists.
require(_tokenInfoMap[_token].available, "token is not available");
// this sets the redeemable flag to the value passed in
_tokenInfoMap[_token].redeemable = _redeemable;
emit UpdatedTokenRedeemable(msg.sender, _token, _redeemable);
}
/// @notice Update ERC20 token exchange rate.
/// @param _token ERC20 token contract address.
/// @param _rate ERC20 token exchange rate in wei.
/// @param _updateDate date for the token updates. This will be compared to when oracle updates are received.
function updateTokenRate(address _token, uint _rate, uint _updateDate) external onlyAdminOrOracle {
// Require that the token exists.
require(_tokenInfoMap[_token].available, "token is not available");
// Update the token's rate.
_tokenInfoMap[_token].rate = _rate;
// Update the token's last update timestamp.
_tokenInfoMap[_token].lastUpdate = _updateDate;
// Emit the rate update event.
emit UpdatedTokenRate(msg.sender, _token, _rate);
}
//// @notice Withdraw tokens from the smart contract to the specified account.
function claim(address payable _to, address _asset, uint _amount) external onlyAdmin {
_safeTransfer(_to, _asset, _amount);
emit Claimed(_to, _asset, _amount);
}
/// @notice This returns all of the fields for a given token.
/// @param _a is the address of a given token.
/// @return string of the token's symbol.
/// @return uint of the token's magnitude.
/// @return uint of the token's exchange rate to ETH.
/// @return bool whether the token is available.
/// @return bool whether the token is loadable to the TokenCard.
/// @return bool whether the token is redeemable to the TKN Holder Contract.
/// @return uint of the lastUpdated time of the token's exchange rate.
function getTokenInfo(address _a) external view returns (string memory, uint256, uint256, bool, bool, bool, uint256) {
Token storage tokenInfo = _tokenInfoMap[_a];
return (tokenInfo.symbol, tokenInfo.magnitude, tokenInfo.rate, tokenInfo.available, tokenInfo.loadable, tokenInfo.redeemable, tokenInfo.lastUpdate);
}
/// @notice This returns all of the fields for our StableCoin.
/// @return string of the token's symbol.
/// @return uint of the token's magnitude.
/// @return uint of the token's exchange rate to ETH.
/// @return bool whether the token is available.
/// @return bool whether the token is loadable to the TokenCard.
/// @return bool whether the token is redeemable to the TKN Holder Contract.
/// @return uint of the lastUpdated time of the token's exchange rate.
function getStablecoinInfo() external view returns (string memory, uint256, uint256, bool, bool, bool, uint256) {
Token storage stablecoinInfo = _tokenInfoMap[_stablecoin];
return (stablecoinInfo.symbol, stablecoinInfo.magnitude, stablecoinInfo.rate, stablecoinInfo.available, stablecoinInfo.loadable, stablecoinInfo.redeemable, stablecoinInfo.lastUpdate);
}
/// @notice This returns an array of all whitelisted token addresses.
/// @return address[] of whitelisted tokens.
function tokenAddressArray() external view returns (address[] memory) {
return _tokenAddressArray;
}
/// @notice This returns an array of all redeemable token addresses.
/// @return address[] of redeemable tokens.
function redeemableTokens() external view returns (address[] memory) {
address[] memory redeemableAddresses = new address[](_redeemableCounter);
uint redeemableIndex = 0;
for (uint i = 0; i < _tokenAddressArray.length; i++) {
address token = _tokenAddressArray[i];
if (_tokenInfoMap[token].redeemable){
redeemableAddresses[redeemableIndex] = token;
redeemableIndex += 1;
}
}
return redeemableAddresses;
}
/// @notice This returns true if a method Id is supported for the specific token.
/// @return true if _methodId is supported in general or just for the specific token.
function isERC20MethodSupported(address _token, bytes4 _methodId) public view returns (bool) {
require(_tokenInfoMap[_token].available, "non-existing token");
return (_methodIdWhitelist[_methodId]);
}
/// @notice This returns true if the method is supported for all protected tokens.
/// @return true if _methodId is in the method whitelist.
function isERC20MethodWhitelisted(bytes4 _methodId) external view returns (bool) {
return (_methodIdWhitelist[_methodId]);
}
/// @notice This returns the number of redeemable tokens.
/// @return current # of redeemables.
function redeemableCounter() external view returns (uint) {
return _redeemableCounter;
}
/// @notice This returns the address of our stablecoin of choice.
/// @return the address of the stablecoin contract.
function stablecoin() external view returns (address) {
return _stablecoin;
}
/// @notice this returns the node hash of our Oracle.
/// @return the oracle node registered in ENS.
function oracleNode() external view returns (bytes32) {
return _oracleNode;
}
}
walletDeployer.sol 106 lines
/**
* The Consumer Contract Wallet Deployer
* Copyright (C) 2019 The Contract Wallet Company Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
pragma solidity ^0.5.10;
import "./wallet.sol";
import "./controllable.sol";
import "./ensResolvable.sol";
//// @title Wallet deployer with pre-caching if wallets functionality.
contract WalletDeployer is ENSResolvable, Controllable {
event CachedWallet(Wallet _wallet);
event DeployedWallet(Wallet _wallet, address _owner);
event MigratedWallet(Wallet _wallet, Wallet _oldWallet, address _owner);
/***** Constants *****/
bytes32 public constant controllerNode = 0x7f2ce995617d2816b426c5c8698c5ec2952f7a34bb10f38326f74933d5893697;
bytes32 public constant licenceNode = 0xd0ff8bd67f6e25e4e4b010df582a36a0ee9b78e49afe6cc1cff5dd5a83040330;
bytes32 public constant tokenWhitelistNode = 0xe84f90570f13fe09f288f2411ff9cf50da611ed0c7db7f73d48053ffc974d396;
mapping(address => address) public deployedWallets;
Wallet[] public cachedWallets;
address public ens;
uint public defaultSpendLimit;
/// @notice parameters are passed in so that they can be used to construct new instances of the wallet
constructor(address _ens, uint _defaultSpendLimit) ENSResolvable(_ens) Controllable(controllerNode) public {
ens = _ens;
defaultSpendLimit = _defaultSpendLimit;
}
/// @dev This contract must be payable by anyone, as the Wallet Owner needs to be payable
function() external payable {}
/// @notice This public method allows anyone to pre-cache wallets
function cacheWallet() public {
Wallet wallet = new Wallet(address(this), true, ens, tokenWhitelistNode, controllerNode, licenceNode, defaultSpendLimit);
cachedWallets.push(wallet);
emit CachedWallet(wallet);
}
/// @notice This function is used to deploy a Wallet for a given owner address
/// @param _owner is the owner address for the new Wallet to be
function deployWallet(address payable _owner) external onlyController {
if (cachedWallets.length < 1) {
cacheWallet();
}
Wallet wallet = cachedWallets[cachedWallets.length-1];
cachedWallets.pop();
wallet.transferOwnership(_owner, false);
deployedWallets[_owner] = address(wallet);
emit DeployedWallet(wallet, _owner);
}
/// @notice This function is used to migrate an owner's security settings from a previous version of the wallet
/// @param _owner is the owner address for the new Wallet to be
/// @param _spendLimit is the user's set daily spend limit
/// @param _gasTopUpLimit is the user's set daily gas top-up limit
/// @param _whitelistedAddresses is the set of the user's whitelisted addresses
function migrateWallet(address payable _owner, Wallet _oldWallet, bool _initializedSpendLimit, bool _initializedGasTopUpLimit, bool _initializedWhitelist, uint _spendLimit, uint _gasTopUpLimit, address[] calldata _whitelistedAddresses) external onlyController {
if (cachedWallets.length < 1) {
cacheWallet();
}
Wallet wallet = cachedWallets[cachedWallets.length-1];
cachedWallets.pop();
if (_initializedSpendLimit) {
wallet.setSpendLimit(_spendLimit);
}
if (_initializedGasTopUpLimit) {
wallet.setGasTopUpLimit(_gasTopUpLimit);
}
if (_initializedWhitelist) {
wallet.setWhitelist(_whitelistedAddresses);
}
wallet.transferOwnership(_owner, false);
deployedWallets[_owner] = address(wallet);
emit MigratedWallet(wallet, _oldWallet, _owner);
}
/// @notice returns the number of pre-cached wallets
function cachedWalletsCount() external view returns (uint) {
return cachedWallets.length;
}
}
InterfaceResolver.sol 65 lines
pragma solidity ^0.5.0;
import "./ResolverBase.sol";
import "./AddrResolver.sol";
contract InterfaceResolver is ResolverBase, AddrResolver {
bytes4 constant private INTERFACE_INTERFACE_ID = bytes4(keccak256("interfaceImplementer(bytes32,bytes4)"));
bytes4 private constant INTERFACE_META_ID = 0x01ffc9a7;
event InterfaceChanged(bytes32 indexed node, bytes4 indexed interfaceID, address implementer);
mapping(bytes32=>mapping(bytes4=>address)) interfaces;
/**
* Sets an interface associated with a name.
* Setting the address to 0 restores the default behaviour of querying the contract at `addr()` for interface support.
* @param node The node to update.
* @param interfaceID The EIP 168 interface ID.
* @param implementer The address of a contract that implements this interface for this node.
*/
function setInterface(bytes32 node, bytes4 interfaceID, address implementer) external authorised(node) {
interfaces[node][interfaceID] = implementer;
emit InterfaceChanged(node, interfaceID, implementer);
}
/**
* Returns the address of a contract that implements the specified interface for this name.
* If an implementer has not been set for this interfaceID and name, the resolver will query
* the contract at `addr()`. If `addr()` is set, a contract exists at that address, and that
* contract implements EIP168 and returns `true` for the specified interfaceID, its address
* will be returned.
* @param node The ENS node to query.
* @param interfaceID The EIP 168 interface ID to check for.
* @return The address that implements this interface, or 0 if the interface is unsupported.
*/
function interfaceImplementer(bytes32 node, bytes4 interfaceID) external view returns (address) {
address implementer = interfaces[node][interfaceID];
if(implementer != address(0)) {
return implementer;
}
address a = addr(node);
if(a == address(0)) {
return address(0);
}
(bool success, bytes memory returnData) = a.staticcall(abi.encodeWithSignature("supportsInterface(bytes4)", INTERFACE_META_ID));
if(!success || returnData.length < 32 || returnData[31] == 0) {
// EIP 168 not supported by target
return address(0);
}
(success, returnData) = a.staticcall(abi.encodeWithSignature("supportsInterface(bytes4)", interfaceID));
if(!success || returnData.length < 32 || returnData[31] == 0) {
// Specified interface not supported by target
return address(0);
}
return a;
}
function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
return interfaceID == INTERFACE_INTERFACE_ID || super.supportsInterface(interfaceID);
}
}
tokenWhitelistable.sol 121 lines
/**
* TokenWhitelistable - The Consumer Contract Wallet
* Copyright (C) 2019 The Contract Wallet Company Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
pragma solidity ^0.5.10;
import "./tokenWhitelist.sol";
import "./ensResolvable.sol";
/// @title TokenWhitelistable implements access to the TokenWhitelist located behind ENS.
contract TokenWhitelistable is ENSResolvable {
/// @notice Is the registered ENS node identifying the tokenWhitelist contract
bytes32 private _tokenWhitelistNode;
/// @notice Constructor initializes the TokenWhitelistable object.
/// @param _tokenWhitelistNode_ is the ENS node of the TokenWhitelist.
constructor(bytes32 _tokenWhitelistNode_) internal {
_tokenWhitelistNode = _tokenWhitelistNode_;
}
/// @notice This shows what TokenWhitelist is being used
/// @return TokenWhitelist's node registered in ENS.
function tokenWhitelistNode() external view returns (bytes32) {
return _tokenWhitelistNode;
}
/// @notice This returns all of the fields for a given token.
/// @param _a is the address of a given token.
/// @return string of the token's symbol.
/// @return uint of the token's magnitude.
/// @return uint of the token's exchange rate to ETH.
/// @return bool whether the token is available.
/// @return bool whether the token is loadable to the TokenCard.
/// @return bool whether the token is redeemable to the TKN Holder Contract.
/// @return uint of the lastUpdated time of the token's exchange rate.
function _getTokenInfo(address _a) internal view returns (string memory, uint256, uint256, bool, bool, bool, uint256) {
return ITokenWhitelist(_ensResolve(_tokenWhitelistNode)).getTokenInfo(_a);
}
/// @notice This returns all of the fields for our stablecoin token.
/// @return string of the token's symbol.
/// @return uint of the token's magnitude.
/// @return uint of the token's exchange rate to ETH.
/// @return bool whether the token is available.
/// @return bool whether the token is loadable to the TokenCard.
/// @return bool whether the token is redeemable to the TKN Holder Contract.
/// @return uint of the lastUpdated time of the token's exchange rate.
function _getStablecoinInfo() internal view returns (string memory, uint256, uint256, bool, bool, bool, uint256) {
return ITokenWhitelist(_ensResolve(_tokenWhitelistNode)).getStablecoinInfo();
}
/// @notice This returns an array of our whitelisted addresses.
/// @return address[] of our whitelisted tokens.
function _tokenAddressArray() internal view returns (address[] memory) {
return ITokenWhitelist(_ensResolve(_tokenWhitelistNode)).tokenAddressArray();
}
/// @notice This returns an array of all redeemable token addresses.
/// @return address[] of redeemable tokens.
function _redeemableTokens() internal view returns (address[] memory) {
return ITokenWhitelist(_ensResolve(_tokenWhitelistNode)).redeemableTokens();
}
/// @notice Update ERC20 token exchange rate.
/// @param _token ERC20 token contract address.
/// @param _rate ERC20 token exchange rate in wei.
/// @param _updateDate date for the token updates. This will be compared to when oracle updates are received.
function _updateTokenRate(address _token, uint _rate, uint _updateDate) internal {
ITokenWhitelist(_ensResolve(_tokenWhitelistNode)).updateTokenRate(_token, _rate, _updateDate);
}
/// @notice based on the method it returns the recipient address and amount/value, ERC20 specific.
/// @param _data is the transaction payload.
function _getERC20RecipientAndAmount(address _destination, bytes memory _data) internal view returns (address, uint) {
return ITokenWhitelist(_ensResolve(_tokenWhitelistNode)).getERC20RecipientAndAmount(_destination, _data);
}
/// @notice Checks whether a token is available.
/// @return bool available or not.
function _isTokenAvailable(address _a) internal view returns (bool) {
( , , , bool available, , , ) = _getTokenInfo(_a);
return available;
}
/// @notice Checks whether a token is redeemable.
/// @return bool redeemable or not.
function _isTokenRedeemable(address _a) internal view returns (bool) {
( , , , , , bool redeemable, ) = _getTokenInfo(_a);
return redeemable;
}
/// @notice Checks whether a token is loadable.
/// @return bool loadable or not.
function _isTokenLoadable(address _a) internal view returns (bool) {
( , , , , bool loadable, , ) = _getTokenInfo(_a);
return loadable;
}
/// @notice This gets the address of the stablecoin.
/// @return the address of the stablecoin contract.
function _stablecoin() internal view returns (address) {
return ITokenWhitelist(_ensResolve(_tokenWhitelistNode)).stablecoin();
}
}
ContentHashResolver.sol 35 lines
pragma solidity ^0.5.0;
import "./ResolverBase.sol";
contract ContentHashResolver is ResolverBase {
bytes4 constant private CONTENT_HASH_INTERFACE_ID = 0xbc1c58d1;
event ContenthashChanged(bytes32 indexed node, bytes hash);
mapping(bytes32=>bytes) hashes;
/**
* Sets the contenthash associated with an ENS node.
* May only be called by the owner of that node in the ENS registry.
* @param node The node to update.
* @param hash The contenthash to set
*/
function setContenthash(bytes32 node, bytes calldata hash) external authorised(node) {
hashes[node] = hash;
emit ContenthashChanged(node, hash);
}
/**
* Returns the contenthash associated with an ENS node.
* @param node The ENS node to query.
* @return The associated contenthash.
*/
function contenthash(bytes32 node) external view returns (bytes memory) {
return hashes[node];
}
function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
return interfaceID == CONTENT_HASH_INTERFACE_ID || super.supportsInterface(interfaceID);
}
}
Read Contract
cachedWallets 0xfc6cabe7 → address
cachedWalletsCount 0x71b9076a → uint256
controllerNode 0xe2b4ce97 → bytes32
defaultSpendLimit 0xa7a7d3bc → uint256
deployedWallets 0x80a12c0e → address
ens 0x3f15457f → address
ensRegistry 0x7d73b231 → address
licenceNode 0x747c31d6 → bytes32
tokenWhitelistNode 0x877337b0 → bytes32
Write Contract 3 functions
These functions modify contract state and require a wallet transaction to execute.
cacheWallet 0x60dbc5de
No parameters
deployWallet 0xc8cc2fc2
address _owner
migrateWallet 0x17ca9305
address _owner
address _oldWallet
bool _initializedSpendLimit
bool _initializedGasTopUpLimit
bool _initializedWhitelist
uint256 _spendLimit
uint256 _gasTopUpLimit
address[] _whitelistedAddresses
Recent Transactions
This address has 6,854 on-chain transactions, but only 0.7% of the chain is indexed. Transactions will appear as indexing progresses. View on Etherscan →