Address Contract Partially Verified
Address
0x982C8E5803561b684301Ed04578Dae167a835d41
Balance
0 ETH
Nonce
1
Code Size
16560 bytes
Creator
0x986BB057...dDFe at tx 0x3f20e274...848fc0
Indexed Transactions
0 (1 on-chain, 0.7% indexed)
Contract Bytecode
16560 bytes
0x6080604052600436106101f25760003560e01c8063643466af1161010d578063b1ffd471116100a0578063c9d4623f1161006f578063c9d4623f146107a6578063e216ea2b146107bb578063ec622892146107ee578063edb9a5c014610869578063f16ab6dc1461089c576101f9565b8063b1ffd471146106d0578063be040fb014610752578063bec6146214610767578063bf7e214f14610791576101f9565b80637baf5929116100dc5780637baf592914610678578063851b16f51461069e5780638a471df9146106a65780638da5cb5b146106bb576101f9565b8063643466af1461056857806374adad1d146105cd5780637a9e5e4b146106305780637b10399914610663576101f9565b806344ed98dd1161018557806354c3b8c51161015457806354c3b8c51461043957806354fd4d501461046c5780635810a54c146104815780635d58287014610536576101f9565b806344ed98dd146103a0578063474e19f2146103dc57806349837b5e146103f15780634a248e2714610406576101f9565b806320531bc9116101c157806320531bc9146102ac578063212f6066146102dd578063365a86fc14610358578063429f41a71461036d576101f9565b80630b797141146101fe5780630d2485b11461022a57806313af403514610250578063158ef93e14610283576101f9565b366101f957005b600080fd5b34801561020a57600080fd5b506102286004803603602081101561022157600080fd5b50356108c6565b005b6102286004803603602081101561024057600080fd5b50356001600160a01b03166109e2565b34801561025c57600080fd5b506102286004803603602081101561027357600080fd5b50356001600160a01b0316610d94565b34801561028f57600080fd5b50610298610e42565b604080519115158252519081900360200190f35b3480156102b857600080fd5b506102c1610e4b565b604080516001600160a01b039092168252519081900360200190f35b3480156102e957600080fd5b506102286004803603602081101561030057600080fd5b810190602081018135600160201b81111561031a57600080fd5b82018360208201111561032c57600080fd5b803590602001918460208302840111600160201b8311171561034d57600080fd5b509092509050610e5a565b34801561036457600080fd5b506102c1610ef4565b34801561037957600080fd5b506102986004803603602081101561039057600080fd5b50356001600160a01b0316610f03565b3480156103ac57600080fd5b506103ca600480360360208110156103c357600080fd5b5035611087565b60408051918252519081900360200190f35b3480156103e857600080fd5b506103ca611199565b3480156103fd57600080fd5b506103ca61119e565b34801561041257600080fd5b506102986004803603602081101561042957600080fd5b50356001600160a01b03166111a5565b34801561044557600080fd5b506102986004803603602081101561045c57600080fd5b50356001600160a01b03166111d6565b34801561047857600080fd5b506102c16111eb565b34801561048d57600080fd5b50610228600480360360408110156104a457600080fd5b81359190810190604081016020820135600160201b8111156104c557600080fd5b8201836020820111156104d757600080fd5b803590602001918460208302840111600160201b831117156104f857600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506111fa945050505050565b6102286004803603606081101561054c57600080fd5b50803590602081013590604001356001600160a01b0316611b9a565b34801561057457600080fd5b5061057d6123d1565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156105b95781810151838201526020016105a1565b505050509050019250505060405180910390f35b3480156105d957600080fd5b50610600600480360360208110156105f057600080fd5b50356001600160a01b0316612433565b604080516001600160a01b0390951685526020850193909352838301919091526060830152519081900360800190f35b34801561063c57600080fd5b506102286004803603602081101561065357600080fd5b50356001600160a01b0316612464565b34801561066f57600080fd5b506102c161250e565b6102286004803603602081101561068e57600080fd5b50356001600160a01b0316612518565b610228612aaa565b3480156106b257600080fd5b506102c1612e50565b3480156106c757600080fd5b506102c1612e5a565b3480156106dc57600080fd5b506106e5612e69565b604080516001600160a01b039c8d1681529a8c1660208c0152988b168a8a0152968a1660608a0152948916608089015292881660a088015290871660c0870152861660e0860152851661010085015284166101208401529092166101408201529051908190036101600190f35b34801561075e57600080fd5b50610228612ebe565b34801561077357600080fd5b50610228600480360361016081101561078b57600080fd5b50612f43565b34801561079d57600080fd5b506102c161326f565b3480156107b257600080fd5b506102c161327e565b3480156107c757600080fd5b50610298600480360360208110156107de57600080fd5b50356001600160a01b0316613288565b3480156107fa57600080fd5b506102286004803603602081101561081157600080fd5b810190602081018135600160201b81111561082b57600080fd5b82018360208201111561083d57600080fd5b803590602001918460208302840111600160201b8311171561085e57600080fd5b5090925090506132a8565b34801561087557600080fd5b506102986004803603602081101561088c57600080fd5b50356001600160a01b03166133c4565b3480156108a857600080fd5b506102c1600480360360208110156108bf57600080fd5b50356133d9565b60035460408051630a616f2560e21b815290516060926001600160a01b031691632985bc94916004808301926000929190829003018186803b15801561090b57600080fd5b505afa15801561091f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561094857600080fd5b8101908080516040519392919084600160201b82111561096757600080fd5b90830190602082018581111561097c57600080fd5b82518660208202830111600160201b8211171561099857600080fd5b82525081516020918201928201910280838360005b838110156109c55781810151838201526020016109ad565b5050505090500160405250505090506109de82826111fa565b5050565b6000805a90506109f183613400565b60005a90506000610a0061327e565b6001600160a01b031663709bb5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610a3857600080fd5b505afa158015610a4c573d6000803e3d6000fd5b505050506040513d6020811015610a6257600080fd5b505190506000610a7b82610a768686613703565b613759565b90506000610a8761250e565b6001600160a01b03166374d32ad46040518163ffffffff1660e01b815260040160206040518083038186803b158015610abf57600080fd5b505afa158015610ad3573d6000803e3d6000fd5b505050506040513d6020811015610ae957600080fd5b505190506000610af7610e4b565b6001600160a01b0316637e3bfc2f84610b0e612e50565b856040518463ffffffff1660e01b815260040180848152602001836001600160a01b03166001600160a01b03168152602001826001600160a01b03166001600160a01b03168152602001935050505060206040518083038186803b158015610b7557600080fd5b505afa158015610b89573d6000803e3d6000fd5b505050506040513d6020811015610b9f57600080fd5b5051905060008715610c1e57610bb361250e565b6001600160a01b0316631d4632ac6040518163ffffffff1660e01b815260040160206040518083038186803b158015610beb57600080fd5b505afa158015610bff573d6000803e3d6000fd5b505050506040513d6020811015610c1557600080fd5b50519050610c22565b5060005b610c2c82826137bc565b341015610c6a5760405162461bcd60e51b81526004018080602001828103825260218152602001806140366021913960400191505060405180910390fd5b610c7261327e565b6001600160a01b0316635ce1fb54836040518263ffffffff1660e01b81526004016000604051808303818588803b158015610cac57600080fd5b505af1158015610cc0573d6000803e3d6000fd5b5050505050336001600160a01b03166108fc610ce5610cdf3486613703565b84613703565b6040518115909202916000818181858888f19350505050610d3d576040805162461bcd60e51b815260206004820152600d60248201526c1499599d5b990819985a5b1959609a1b604482015290519081900360640190fd5b337f0fa722789511f8feef9c02f613ad3ad10699034c1725894b9e7040552af4ffb983610d6a8a8a613703565b604080519283526020830191909152818101859052519081900360600190a2505050505050505050565b610daa336000356001600160e01b03191661380b565b610df2576040805162461bcd60e51b8152602060048201526014602482015273191ccb585d5d1a0b5d5b985d5d1a1bdc9a5e995960621b604482015290519081900360640190fd5b600180546001600160a01b0319166001600160a01b0383811691909117918290556040519116907fce241d7ca1f669fee44b6fc00b8eba2df3bb514eed0f6f668f8f89096e81ed9490600090a250565b600e5460ff1681565b6000610e556138f2565b905090565b610e70336000356001600160e01b03191661380b565b610eb8576040805162461bcd60e51b8152602060048201526014602482015273191ccb585d5d1a0b5d5b985d5d1a1bdc9a5e995960621b604482015290519081900360640190fd5b6109de82828080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061396892505050565b6002546001600160a01b031681565b600080610f0e610e4b565b90506000816001600160a01b0316634c89867f6040518163ffffffff1660e01b815260040160206040518083038186803b158015610f4b57600080fd5b505afa158015610f5f573d6000803e3d6000fd5b505050506040513d6020811015610f7557600080fd5b50516001600160a01b0385166000908152600f6020526040902060030154108061100d5750600754604080516318160ddd60e01b815290516001600160a01b03909216916318160ddd91600481810192602092909190829003018186803b158015610fdf57600080fd5b505afa158015610ff3573d6000803e3d6000fd5b505050506040513d602081101561100957600080fd5b5051155b905061101884613288565b80156110215750805b80156110335750611031846111a5565b155b801561105957506001600160a01b0384166000908152600f602052604090206001015415155b801561107f57506001600160a01b0384166000908152600f602052604090206002015415155b949350505050565b60075460048054604080516335ff1bb760e01b815290516000946001600160a01b03908116948694909116926335ff1bb7928183019260209282900301818787803b1580156110d557600080fd5b505af11580156110e9573d6000803e3d6000fd5b505050506040513d60208110156110ff57600080fd5b5051604080516318160ddd60e01b8152905191925060009161117e916001600160a01b038616916318160ddd91600480820192602092909190829003018186803b15801561114c57600080fd5b505afa158015611160573d6000803e3d6000fd5b505050506040513d602081101561117657600080fd5b5051836137bc565b6111888387613759565b8161118f57fe5b0495945050505050565b601281565b6201518081565b6001600160a01b0381166000908152600f60205260408120600301546111ce90620151806137bc565b421192915050565b60106020526000908152604090205460ff1681565b600b546001600160a01b031690565b600754604080516370a0823160e01b815233600482015290516001600160a01b0390921691849183916370a0823191602480820192602092909190829003018186803b15801561124957600080fd5b505afa15801561125d573d6000803e3d6000fd5b505050506040513d602081101561127357600080fd5b5051108015906112f65750604080516370a0823160e01b815233600482015290516000916001600160a01b038416916370a0823191602480820192602092909190829003018186803b1580156112c857600080fd5b505afa1580156112dc573d6000803e3d6000fd5b505050506040513d60208110156112f257600080fd5b5051115b6113315760405162461bcd60e51b8152600401808060200182810382526035815260200180613fb16035913960400191505060405180910390fd5b600061133b610e4b565b6001600160a01b03166380971a36846040518263ffffffff1660e01b81526004018080602001828103825283818151815260200191508051906020019060200280838360005b83811015611399578181015183820152602001611381565b505050509050019250505060206040518083038186803b1580156113bc57600080fd5b505afa1580156113d0573d6000803e3d6000fd5b505050506040513d60208110156113e657600080fd5b505180156114765750600260009054906101000a90046001600160a01b03166001600160a01b031663481c6a756040518163ffffffff1660e01b815260040160206040518083038186803b15801561143d57600080fd5b505afa158015611451573d6000803e3d6000fd5b505050506040513d602081101561146757600080fd5b50516001600160a01b03163314155b15611638576004805460408051631bb7381160e31b815290516001600160a01b039092169263ddb9c08892828201926000929082900301818387803b1580156114be57600080fd5b505af11580156114d2573d6000803e3d6000fd5b505050506114df84611087565b604080516366bde28b60e11b81523360048201526024810183905290519192506001600160a01b0384169163cd7bc5169160448082019260009290919082900301818387803b15801561153157600080fd5b505af1158015611545573d6000803e3d6000fd5b50505050816001600160a01b0316631d48946c600260009054906101000a90046001600160a01b03166001600160a01b031663481c6a756040518163ffffffff1660e01b815260040160206040518083038186803b1580156115a657600080fd5b505afa1580156115ba573d6000803e3d6000fd5b505050506040513d60208110156115d057600080fd5b5051604080516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820185905251604480830192600092919082900301818387803b15801561161f57600080fd5b505af1158015611633573d6000803e3d6000fd5b505050505b60006116448583613703565b9050600060608551604051908082528060200260200182016040528015611675578160200160208202803883390190505b509050606086516040519080825280602002602001820160405280156116a5578160200160208202803883390190505b506003549091506001600160a01b031660005b885181101561195c578881815181106116cd57fe5b60200260200101519450816001600160a01b0316630e7a2d4e866040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561172d57600080fd5b505afa158015611741573d6000803e3d6000fd5b505050506040513d602081101561175757600080fd5b50516117945760405162461bcd60e51b8152600401808060200182810382526021815260200180613f6f6021913960400191505060405180910390fd5b60005b8351811015611822578381815181106117ac57fe5b60200260200101516001600160a01b0316866001600160a01b0316141561181a576040805162461bcd60e51b815260206004820152601f60248201527f41737365742063616e206f6e6c792062652072656465656d6564206f6e636500604482015290519081900360640190fd5b600101611797565b508483828151811061183057fe5b6001600160a01b039283166020918202929092018101919091526040805163ca334fe560e01b81528884166004820152905160009386169263ca334fe5926024808201939182900301818787803b15801561188a57600080fd5b505af115801561189e573d6000803e3d6000fd5b505050506040513d60208110156118b457600080fd5b50519050806118c35750611954565b886001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156118fc57600080fd5b505afa158015611910573d6000803e3d6000fd5b505050506040513d602081101561192657600080fd5b50516119328289613759565b8161193957fe5b0485838151811061194657fe5b602002602001018181525050505b6001016116b8565b50604080516366bde28b60e11b81523360048201526024810187905290516001600160a01b0389169163cd7bc51691604480830192600092919082900301818387803b1580156119ab57600080fd5b505af11580156119bf573d6000803e3d6000fd5b506000925050505b8851811015611ac1578881815181106119dc57fe5b602002602001015194508381815181106119f257fe5b602002602001015160001415611a0757611ab9565b60095484516001600160a01b039091169063f3fef3a3908790879085908110611a2c57fe5b60200260200101516040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050600060405180830381600087803b158015611a8357600080fd5b505af1158015611a97573d6000803e3d6000fd5b50505050611ab98533868481518110611aac57fe5b6020026020010151613b1e565b6001016119c7565b50336001600160a01b03167f976b5f495b9d6400bbe35e173a78de5e77ffd728e55e900f45a495dec4143e17898588604051808060200180602001848152602001838103835286818151815260200191508051906020019060200280838360005b83811015611b3a578181015183820152602001611b22565b50505050905001838103825285818151815260200191508051906020019060200280838360005b83811015611b79578181015183820152602001611b61565b505050509050019550505050505060405180910390a2505050505050505050565b600260009054906101000a90046001600160a01b03166001600160a01b031663ff9475256040518163ffffffff1660e01b815260040160206040518083038186803b158015611be857600080fd5b505afa158015611bfc573d6000803e3d6000fd5b505050506040513d6020811015611c1257600080fd5b505115611c59576040805162461bcd60e51b815260206004820152601060248201526f243ab11034b99039b43aba103237bbb760811b604482015290519081900360640190fd5b600160005a600e5490915060ff16611cb8576040805162461bcd60e51b815260206004820152601d60248201527f436f6d706f6e656e74206e6f742079657420696e697469616c697a6564000000604482015290519081900360640190fd5b6006546040805160a08082018352338252600060208084018290528385018290526001600160a01b038981166060808701919091526080860184905286519081018752838152918201839052818601839052945163da6670d360e01b81526001600160e01b031983351660048201818152969097169663da6670d3969095949293929091602401908590808383875b83811015611d5f578181015183820152602001611d47565b5050505090500183600360200280838360005b83811015611d8a578181015183820152602001611d72565b50505050905001828152602001945050505050600060405180830381600087803b158015611db757600080fd5b505af1158015611dcb573d6000803e3d6000fd5b505050506001600160a01b03831660009081526010602052604090205460ff16611e265760405162461bcd60e51b81526004018080602001828103825260248152602001806140576024913960400191505060405180910390fd5b611e3283333087613ceb565b336000908152600f602052604090206003015415611e815760405162461bcd60e51b8152600401808060200182810382526024815260200180613f4b6024913960400191505060405180910390fd5b60408051608080820183526001600160a01b0386811680845260208085018a81528587018c8152426060808901918252336000818152600f87528b81209a518b546001600160a01b031916908a16178b55945160018b0155925160028a01559051600390980197909755600654885160a08082018b52928152808501849052808a018490528089019590955295840182905287519687018852818752918601819052858701819052955163185f31cf60e31b815286356001600160e01b03191660048201818152959094169663c2f98e7896949593949390929091602401908590808383875b83811015611f7f578181015183820152602001611f67565b5050505090500183600360200280838360005b83811015611faa578181015183820152602001611f92565b50505050905001828152602001945050505050600060405180830381600087803b158015611fd757600080fd5b505af1158015611feb573d6000803e3d6000fd5b5050604080518881526020810188905281516001600160a01b03881694503393507f7ac4e9fa94c85b700a3c05a368f0cbc8503fba59158d3e01cc96f608c82bb202929181900390910190a360005a9050600061204661327e565b6001600160a01b031663709bb5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561207e57600080fd5b505afa158015612092573d6000803e3d6000fd5b505050506040513d60208110156120a857600080fd5b5051905060006120bc82610a768686613703565b905060006120c861250e565b6001600160a01b03166374d32ad46040518163ffffffff1660e01b815260040160206040518083038186803b15801561210057600080fd5b505afa158015612114573d6000803e3d6000fd5b505050506040513d602081101561212a57600080fd5b505190506000612138610e4b565b6001600160a01b0316637e3bfc2f8461214f612e50565b856040518463ffffffff1660e01b815260040180848152602001836001600160a01b03166001600160a01b03168152602001826001600160a01b03166001600160a01b03168152602001935050505060206040518083038186803b1580156121b657600080fd5b505afa1580156121ca573d6000803e3d6000fd5b505050506040513d60208110156121e057600080fd5b505190506000871561225f576121f461250e565b6001600160a01b0316631d4632ac6040518163ffffffff1660e01b815260040160206040518083038186803b15801561222c57600080fd5b505afa158015612240573d6000803e3d6000fd5b505050506040513d602081101561225657600080fd5b50519050612263565b5060005b61226d82826137bc565b3410156122ab5760405162461bcd60e51b81526004018080602001828103825260218152602001806140366021913960400191505060405180910390fd5b6122b361327e565b6001600160a01b0316635ce1fb54836040518263ffffffff1660e01b81526004016000604051808303818588803b1580156122ed57600080fd5b505af1158015612301573d6000803e3d6000fd5b5050505050336001600160a01b03166108fc612320610cdf3486613703565b6040518115909202916000818181858888f19350505050612378576040805162461bcd60e51b815260206004820152600d60248201526c1499599d5b990819985a5b1959609a1b604482015290519081900360640190fd5b337f0fa722789511f8feef9c02f613ad3ad10699034c1725894b9e7040552af4ffb9836123a58a8a613703565b604080519283526020830191909152818101859052519081900360600190a25050505050505050505050565b6060601280548060200260200160405190810160405280929190818152602001828054801561242957602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161240b575b5050505050905090565b600f6020526000908152604090208054600182015460028301546003909301546001600160a01b0390921692909184565b61247a336000356001600160e01b03191661380b565b6124c2576040805162461bcd60e51b8152602060048201526014602482015273191ccb585d5d1a0b5d5b985d5d1a1bdc9a5e995960621b604482015290519081900360640190fd5b600080546001600160a01b0319166001600160a01b03838116919091178083556040519116917f1abebea81bfa2637f28358c371278fb15ede7ea8dd28d2e03b112ff6d936ada491a250565b6000610e55613ec1565b600260009054906101000a90046001600160a01b03166001600160a01b031663ff9475256040518163ffffffff1660e01b815260040160206040518083038186803b15801561256657600080fd5b505afa15801561257a573d6000803e3d6000fd5b505050506040513d602081101561259057600080fd5b5051156125d7576040805162461bcd60e51b815260206004820152601060248201526f243ab11034b99039b43aba103237bbb760811b604482015290519081900360640190fd5b6000805a90506125e5613eee565b506001600160a01b038084166000908152600f602090815260409182902082516080810184528154909416845260018101549184019190915260028101549183019190915260030154606082015261263c84610f03565b6126775760405162461bcd60e51b81526004018080602001828103825260218152602001806140156021913960400191505060405180910390fd5b6004805460408051631bb7381160e31b815290516001600160a01b039092169263ddb9c08892828201926000929082900301818387803b1580156126ba57600080fd5b505af11580156126ce573d6000803e3d6000fd5b50506003546040808501518551825163896aad5360e01b815260048101929092526001600160a01b03908116602483015291516000955091909216925063896aad539160448082019260209290919082900301818787803b15801561273257600080fd5b505af1158015612746573d6000803e3d6000fd5b505050506040513d602081101561275c57600080fd5b505160208301519091508111156127ba576040805162461bcd60e51b815260206004820152601760248201527f496e76657374656420616d6f756e7420746f6f206c6f77000000000000000000604482015290519081900360640190fd5b81516009546127d391906001600160a01b031683613b1e565b60006127e3836020015183613703565b905080156127f85782516127f8908783613b1e565b600a54604080516307518cab60e21b8152905133926108fc926001600160a01b0390911691631d4632ac91600480820192602092909190829003018186803b15801561284357600080fd5b505afa158015612857573d6000803e3d6000fd5b505050506040513d602081101561286d57600080fd5b50516040518115909202916000818181858888f19350505050158015612897573d6000803e3d6000fd5b506007546040808501518151630752251b60e21b81526001600160a01b038a8116600483015260248201929092529151921691631d48946c9160448082019260009290919082900301818387803b1580156128f157600080fd5b505af1158015612905573d6000803e3d6000fd5b50506003548551604080516301ebef9d60e21b81526001600160a01b03928316600482015290519190921693506307afbe749250602480830192600092919082900301818387803b15801561295957600080fd5b505af115801561296d573d6000803e3d6000fd5b505050506001600160a01b03861660009081526011602052604090205460ff166129f7576001600160a01b0386166000818152601160205260408120805460ff191660019081179091556012805491820181559091527fbb8a6a4669ba250d26cd7a459eca9d215f8307e33aebe50379bc5a3617ec34440180546001600160a01b03191690911790555b82600001516001600160a01b0316336001600160a01b0316876001600160a01b03167fd826cdb072fceaa49209a7f9dc6283914ae87fb72f26cc328c67f8b54599957b86602001518760400151604051808381526020018281526020019250505060405180910390a45050506001600160a01b0383166000908152600f6020526040812080546001600160a01b031916815560018101829055600281018290556003018190555a90506000610a0061327e565b6000805a9050612ab933613400565b60005a90506000612ac861327e565b6001600160a01b031663709bb5676040518163ffffffff1660e01b815260040160206040518083038186803b158015612b0057600080fd5b505afa158015612b14573d6000803e3d6000fd5b505050506040513d6020811015612b2a57600080fd5b505190506000612b3e82610a768686613703565b90506000612b4a61250e565b6001600160a01b03166374d32ad46040518163ffffffff1660e01b815260040160206040518083038186803b158015612b8257600080fd5b505afa158015612b96573d6000803e3d6000fd5b505050506040513d6020811015612bac57600080fd5b505190506000612bba610e4b565b6001600160a01b0316637e3bfc2f84612bd1612e50565b856040518463ffffffff1660e01b815260040180848152602001836001600160a01b03166001600160a01b03168152602001826001600160a01b03166001600160a01b03168152602001935050505060206040518083038186803b158015612c3857600080fd5b505afa158015612c4c573d6000803e3d6000fd5b505050506040513d6020811015612c6257600080fd5b5051905060008715612ce157612c7661250e565b6001600160a01b0316631d4632ac6040518163ffffffff1660e01b815260040160206040518083038186803b158015612cae57600080fd5b505afa158015612cc2573d6000803e3d6000fd5b505050506040513d6020811015612cd857600080fd5b50519050612ce5565b5060005b612cef82826137bc565b341015612d2d5760405162461bcd60e51b81526004018080602001828103825260218152602001806140366021913960400191505060405180910390fd5b612d3561327e565b6001600160a01b0316635ce1fb54836040518263ffffffff1660e01b81526004016000604051808303818588803b158015612d6f57600080fd5b505af1158015612d83573d6000803e3d6000fd5b5050505050336001600160a01b03166108fc612da2610cdf3486613703565b6040518115909202916000818181858888f19350505050612dfa576040805162461bcd60e51b815260206004820152600d60248201526c1499599d5b990819985a5b1959609a1b604482015290519081900360640190fd5b337f0fa722789511f8feef9c02f613ad3ad10699034c1725894b9e7040552af4ffb983612e278a8a613703565b604080519283526020830191909152818101859052519081900360600190a25050505050505050565b6000610e55613ed0565b6001546001600160a01b031681565b600354600454600554600654600754600854600954600a54600b54600c54600d546001600160a01b039a8b169a998a16999889169897881697968716969586169594851694938416939283169291821691168b565b600754604080516370a0823160e01b815233600482015290516000926001600160a01b0316916370a08231916024808301926020929190829003018186803b158015612f0957600080fd5b505afa158015612f1d573d6000803e3d6000fd5b505050506040513d6020811015612f3357600080fd5b50519050612f40816108c6565b50565b612f59336000356001600160e01b03191661380b565b612fa1576040805162461bcd60e51b8152602060048201526014602482015273191ccb585d5d1a0b5d5b985d5d1a1bdc9a5e995960621b604482015290519081900360640190fd5b6002546001600160a01b03163314612fb857600080fd5b600e5460ff1615613006576040805162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b604482015290519081900360640190fd5b60408051610160810182526001600160a01b038335811682526020808501358216908301528383013516918101919091526060810182600360200201356001600160a01b03166001600160a01b03168152602001826004600b811061306757fe5b60200201356001600160a01b03166001600160a01b03168152602001826005600b811061309057fe5b60200201356001600160a01b03166001600160a01b03168152602001826006600b81106130b957fe5b60200201356001600160a01b03166001600160a01b03168152602001826007600b81106130e257fe5b60200201356001600160a01b03166001600160a01b03168152602001826008600b811061310b57fe5b60200201356001600160a01b03166001600160a01b03168152602001826009600b811061313457fe5b60200201356001600160a01b03166001600160a01b0316815260200182600a600b811061315d57fe5b602090810291909101356001600160a01b039081169092528251600380546001600160a01b03199081169285169290921790559083015160048054831691841691909117905560408301516005805483169184169190911790556060830151600680548316918416919091179055608083015160078054831691841691909117905560a083015160088054831691841691909117905560c083015160098054831691841691909117905560e0830151600a80548316918416919091179055610100830151600b80548316918416919091179055610120830151600c8054831691841691909117905561014090920151600d80549093169116179055600e805460ff19166001179055612f406000610d94565b6000546001600160a01b031681565b6000610e55613edf565b6001600160a01b03166000908152600f6020526040902060030154151590565b6132be336000356001600160e01b03191661380b565b613306576040805162461bcd60e51b8152602060048201526014602482015273191ccb585d5d1a0b5d5b985d5d1a1bdc9a5e995960621b604482015290519081900360640190fd5b60005b8181101561335c5760006010600085858581811061332357fe5b602090810292909201356001600160a01b0316835250810191909152604001600020805460ff1916911515919091179055600101613309565b507f3dc6367c991e43485e267da6f765655c7afd64b44b9414b04ff80de370cf5121828260405180806020018281038252848482818152602001925060200280828437600083820152604051601f909101601f19169092018290039550909350505050a15050565b60116020526000908152604090205460ff1681565b601281815481106133e657fe5b6000918252602090912001546001600160a01b0316905081565b61340981613288565b613451576040805162461bcd60e51b8152602060048201526014602482015273139bc81c995c5d595cdd081d1bc818d85b98d95b60621b604482015290519081900360640190fd5b600061345b610e4b565b9050613465613eee565b506001600160a01b038083166000908152600f6020908152604091829020825160808101845281548516808252600183015482850152600283015482860152600390920154606082015283516313533b5560e01b81526004810192909252925192938516926313533b5592602480840193919291829003018186803b1580156134ed57600080fd5b505afa158015613501573d6000803e3d6000fd5b505050506040513d602081101561351757600080fd5b505115806135295750613529836111a5565b806135aa5750600260009054906101000a90046001600160a01b03166001600160a01b031663ff9475256040518163ffffffff1660e01b815260040160206040518083038186803b15801561357d57600080fd5b505afa158015613591573d6000803e3d6000fd5b505050506040513d60208110156135a757600080fd5b50515b6135e55760405162461bcd60e51b8152600401808060200182810382526021815260200180613f906021913960400191505060405180910390fd5b80516020808301516001600160a01b038087166000908152600f8452604080822080546001600160a01b0319168155600181018390556002810183905560030191909155600a5481516307518cab60e21b81529151939433946108fc949290921692631d4632ac9260048083019392829003018186803b15801561366857600080fd5b505afa15801561367c573d6000803e3d6000fd5b505050506040513d602081101561369257600080fd5b50516040518115909202916000818181858888f193505050501580156136bc573d6000803e3d6000fd5b506136c8828683613b1e565b6040516001600160a01b038616907f8a46aebe3d6766257a923c4a998b5fd32d693bd7877df2e25ea7b7099310da2b90600090a25050505050565b80820382811115613753576040805162461bcd60e51b815260206004820152601560248201527464732d6d6174682d7375622d756e646572666c6f7760581b604482015290519081900360640190fd5b92915050565b60008115806137745750508082028282828161377157fe5b04145b613753576040805162461bcd60e51b815260206004820152601460248201527364732d6d6174682d6d756c2d6f766572666c6f7760601b604482015290519081900360640190fd5b80820182811015613753576040805162461bcd60e51b815260206004820152601460248201527364732d6d6174682d6164642d6f766572666c6f7760601b604482015290519081900360640190fd5b60006001600160a01b03831630141561382657506001613753565b6001546001600160a01b038481169116141561384457506001613753565b6000546001600160a01b031661385c57506000613753565b6000546040805163b700961360e01b81526001600160a01b0386811660048301523060248301526001600160e01b0319861660448301529151919092169163b7009613916064808301926020929190829003018186803b1580156138bf57600080fd5b505afa1580156138d3573d6000803e3d6000fd5b505050506040513d60208110156138e957600080fd5b50519050613753565b600254604080516320531bc960e01b815290516000926001600160a01b0316916320531bc9916004808301926020929190829003018186803b15801561393757600080fd5b505afa15801561394b573d6000803e3d6000fd5b505050506040513d602081101561396157600080fd5b5051905090565b60005b8151811015613aa557600a5482516001600160a01b0390911690631f8d99a99084908490811061399757fe5b60200260200101516040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156139e557600080fd5b505afa1580156139f9573d6000803e3d6000fd5b505050506040513d6020811015613a0f57600080fd5b5051613a59576040805162461bcd60e51b8152602060048201526014602482015273105cdcd95d081b9bdd081c9959da5cdd195c995960621b604482015290519081900360640190fd5b600160106000848481518110613a6b57fe5b6020908102919091018101516001600160a01b03168252810191909152604001600020805460ff191691151591909117905560010161396b565b507f144a61ed164a4708d46e9929ca93ac8226b76226e0ceb5182c6eaa4d9cdc3429816040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015613b08578181015183820152602001613af0565b505050509050019250505060405180910390a150565b6000836001600160a01b03166370a08231846040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015613b7657600080fd5b505afa158015613b8a573d6000803e3d6000fd5b505050506040513d6020811015613ba057600080fd5b50516040805163a9059cbb60e01b81526001600160a01b0386811660048301526024820186905291519293509086169163a9059cbb916044808201926020929091908290030181600087803b158015613bf857600080fd5b505af1158015613c0c573d6000803e3d6000fd5b505050506040513d6020811015613c2257600080fd5b5050604080516370a0823160e01b81526001600160a01b03858116600483015291516000928716916370a08231916024808301926020929190829003018186803b158015613c6f57600080fd5b505afa158015613c83573d6000803e3d6000fd5b505050506040513d6020811015613c9957600080fd5b5051905080613ca883856137bc565b14613ce45760405162461bcd60e51b815260040180806020018281038252602b815260200180613f20602b913960400191505060405180910390fd5b5050505050565b6000846001600160a01b03166370a08231846040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015613d4357600080fd5b505afa158015613d57573d6000803e3d6000fd5b505050506040513d6020811015613d6d57600080fd5b5051604080516323b872dd60e01b81526001600160a01b0387811660048301528681166024830152604482018690529151929350908716916323b872dd916064808201926020929091908290030181600087803b158015613dcd57600080fd5b505af1158015613de1573d6000803e3d6000fd5b505050506040513d6020811015613df757600080fd5b5050604080516370a0823160e01b81526001600160a01b03858116600483015291516000928816916370a08231916024808301926020929190829003018186803b158015613e4457600080fd5b505afa158015613e58573d6000803e3d6000fd5b505050506040513d6020811015613e6e57600080fd5b5051905080613e7d83856137bc565b14613eb95760405162461bcd60e51b815260040180806020018281038252602f815260200180613fe6602f913960400191505060405180910390fd5b505050505050565b600a546001600160a01b031690565b600d546001600160a01b031690565b600c546001600160a01b031690565b604051806080016040528060006001600160a01b03168152602001600081526020016000815260200160008152509056fe526563656976657220646964206e6f74207265636569766520746f6b656e7320696e207472616e736665724f6e6c79206f6e6520726571756573742063616e20657869737420617420612074696d65526571756573746564206173736574206e6f7420696e206173736574206c6973744e6f2063616e63656c6c6174696f6e20636f6e646974696f6e20776173206d657453656e64657220646f6573206e6f74206861766520656e6f7567682073686172657320746f2066756c66696c6c2072657175657374526563656976657220646964206e6f74207265636569766520746f6b656e7320696e207472616e7366657246726f6d4e6f2076616c6964207265717565737420666f7220746869732061646472657373496e737566666963656e7420414d475520616e642f6f7220696e63656e74697665496e766573746d656e74206e6f7420616c6c6f77656420696e2074686973206173736574a2646970667358221220c8c881c31bf73e6260fa333441b2a86f898fe182daeacbdee21155d2d6683c2d64736f6c63430006010033
Verified Source Code Partial Match
Compiler: v0.6.1+commit.e6f7d5a4
EVM: istanbul
Optimization: Yes (200 runs)
Hub.sol 106 lines
pragma solidity 0.6.1;
import "../../dependencies/DSGuard.sol";
import "./Spoke.sol";
import "../../version/Registry.sol";
/// @notice Router for communication between components
/// @notice Has one or more Spokes
contract Hub is DSGuard {
event FundShutDown();
struct Routes {
address accounting;
address feeManager;
address participation;
address policyManager;
address shares;
address trading;
address vault;
address registry;
address version;
address engine;
address mlnToken;
}
Routes public routes;
address public manager;
address public creator;
string public name;
bool public isShutDown;
bool public fundInitialized;
uint public creationTime;
mapping (address => bool) public isSpoke;
constructor(address _manager, string memory _name) public {
creator = msg.sender;
manager = _manager;
name = _name;
creationTime = block.timestamp;
}
modifier onlyCreator() {
require(msg.sender == creator, "Only creator can do this");
_;
}
function shutDownFund() external {
require(msg.sender == routes.version);
isShutDown = true;
emit FundShutDown();
}
function initializeAndSetPermissions(address[11] calldata _spokes) external onlyCreator {
require(!fundInitialized, "Fund is already initialized");
for (uint i = 0; i < _spokes.length; i++) {
isSpoke[_spokes[i]] = true;
}
routes.accounting = _spokes[0];
routes.feeManager = _spokes[1];
routes.participation = _spokes[2];
routes.policyManager = _spokes[3];
routes.shares = _spokes[4];
routes.trading = _spokes[5];
routes.vault = _spokes[6];
routes.registry = _spokes[7];
routes.version = _spokes[8];
routes.engine = _spokes[9];
routes.mlnToken = _spokes[10];
Spoke(routes.accounting).initialize(_spokes);
Spoke(routes.feeManager).initialize(_spokes);
Spoke(routes.participation).initialize(_spokes);
Spoke(routes.policyManager).initialize(_spokes);
Spoke(routes.shares).initialize(_spokes);
Spoke(routes.trading).initialize(_spokes);
Spoke(routes.vault).initialize(_spokes);
permit(routes.participation, routes.vault, bytes4(keccak256('withdraw(address,uint256)')));
permit(routes.trading, routes.vault, bytes4(keccak256('withdraw(address,uint256)')));
permit(routes.participation, routes.shares, bytes4(keccak256('createFor(address,uint256)')));
permit(routes.participation, routes.shares, bytes4(keccak256('destroyFor(address,uint256)')));
permit(routes.feeManager, routes.shares, bytes4(keccak256('createFor(address,uint256)')));
permit(routes.participation, routes.accounting, bytes4(keccak256('addAssetToOwnedAssets(address)')));
permit(routes.trading, routes.accounting, bytes4(keccak256('addAssetToOwnedAssets(address)')));
permit(routes.trading, routes.accounting, bytes4(keccak256('removeFromOwnedAssets(address)')));
permit(routes.accounting, routes.feeManager, bytes4(keccak256('rewardAllFees()')));
permit(manager, routes.policyManager, bytes4(keccak256('register(bytes4,address)')));
permit(manager, routes.policyManager, bytes4(keccak256('batchRegister(bytes4[],address[])')));
permit(manager, routes.participation, bytes4(keccak256('enableInvestment(address[])')));
permit(manager, routes.participation, bytes4(keccak256('disableInvestment(address[])')));
permit(manager, routes.trading, bytes4(keccak256('addExchange(address,address)')));
fundInitialized = true;
}
function vault() external view returns (address) { return routes.vault; }
function accounting() external view returns (address) { return routes.accounting; }
function priceSource() external view returns (address) { return Registry(routes.registry).priceSource(); }
function participation() external view returns (address) { return routes.participation; }
function trading() external view returns (address) { return routes.trading; }
function shares() external view returns (address) { return routes.shares; }
function registry() external view returns (address) { return routes.registry; }
function version() external view returns (address) { return routes.version; }
function policyManager() external view returns (address) { return routes.policyManager; }
}
IEngine.sol 7 lines
pragma solidity 0.6.1;
interface IEngine {
function payAmguInEther() external payable;
function getAmguPrice() external view returns (uint256);
}
IFee.sol 14 lines
pragma solidity 0.6.1;
/// @dev Exposes "feeAmount", which maps fund state and fee state to uint
/// @dev Notice that "feeAmount" *may* change contract state
/// @dev Also exposes "updateState", which changes fee's internal state
interface IFee {
function initializeForUser(uint feeRate, uint feePeriod, address denominationAsset) external;
function feeAmount() external returns (uint);
function updateState() external;
/// @notice Used to enforce a convention
function identifier() external view returns (uint);
}
Spoke.sol 54 lines
pragma solidity 0.6.1;
import "./Hub.sol";
import "../../dependencies/DSAuth.sol";
/// @notice Has one Hub
contract Spoke is DSAuth {
Hub public hub;
Hub.Routes public routes;
bool public initialized;
modifier onlyInitialized() {
require(initialized, "Component not yet initialized");
_;
}
modifier notShutDown() {
require(!hub.isShutDown(), "Hub is shut down");
_;
}
constructor(address _hub) public {
hub = Hub(_hub);
setAuthority(hub);
setOwner(address(hub)); // temporary, to allow initialization
}
function initialize(address[11] calldata _spokes) external auth {
require(msg.sender == address(hub));
require(!initialized, "Already initialized");
routes = Hub.Routes(
_spokes[0],
_spokes[1],
_spokes[2],
_spokes[3],
_spokes[4],
_spokes[5],
_spokes[6],
_spokes[7],
_spokes[8],
_spokes[9],
_spokes[10]
);
initialized = true;
setOwner(address(0));
}
function engine() public view virtual returns (address) { return routes.engine; }
function mlnToken() public view virtual returns (address) { return routes.mlnToken; }
function priceSource() public view virtual returns (address) { return hub.priceSource(); }
function version() public view virtual returns (address) { return routes.version; }
function registry() public view virtual returns (address) { return routes.registry; }
}
Factory.sol 15 lines
pragma solidity 0.6.1;
contract Factory {
mapping (address => bool) public childExists;
event NewInstance(
address indexed hub,
address indexed instance
);
function isInstance(address _child) public view returns (bool) {
return childExists[_child];
}
}
Vault.sol 25 lines
pragma solidity 0.6.1;
import "../hub/Spoke.sol";
import "../../factory/Factory.sol";
import "../../dependencies/TokenUser.sol";
/// @notice Dumb custody component
contract Vault is TokenUser, Spoke {
constructor(address _hub) public Spoke(_hub) {}
function withdraw(address token, uint amount) external auth {
safeTransfer(token, msg.sender, amount);
}
}
contract VaultFactory is Factory {
function createInstance(address _hub) external returns (address) {
address vault = address(new Vault(_hub));
childExists[vault] = true;
emit NewInstance(_hub, vault);
return vault;
}
}
IVersion.sol 6 lines
pragma solidity 0.6.1;
interface IVersion {
function shutDownFund(address) external;
}
Registry.sol 474 lines
pragma solidity 0.6.1;
import "../dependencies/DSAuth.sol";
import "../fund/hub/Hub.sol";
import "../dependencies/token/IERC20.sol";
contract Registry is DSAuth {
// EVENTS
event AssetUpsert (
address indexed asset,
string name,
string symbol,
uint decimals,
string url,
uint reserveMin,
uint[] standards,
bytes4[] sigs
);
event ExchangeAdapterUpsert (
address indexed exchange,
address indexed adapter,
bool takesCustody,
bytes4[] sigs
);
event AssetRemoval (address indexed asset);
event EfxWrapperRegistryChange(address indexed registry);
event EngineChange(address indexed engine);
event ExchangeAdapterRemoval (address indexed exchange);
event IncentiveChange(uint incentiveAmount);
event MGMChange(address indexed MGM);
event MlnTokenChange(address indexed mlnToken);
event NativeAssetChange(address indexed nativeAsset);
event PriceSourceChange(address indexed priceSource);
event VersionRegistration(address indexed version);
// TYPES
struct Asset {
bool exists;
string name;
string symbol;
uint decimals;
string url;
uint reserveMin;
uint[] standards;
bytes4[] sigs;
}
struct Exchange {
bool exists;
address exchangeAddress;
bool takesCustody;
bytes4[] sigs;
}
struct Version {
bool exists;
bytes32 name;
}
// CONSTANTS
uint public constant MAX_REGISTERED_ENTITIES = 20;
uint public constant MAX_FUND_NAME_BYTES = 66;
// FIELDS
mapping (address => Asset) public assetInformation;
address[] public registeredAssets;
// Mapping from adapter address to exchange Information (Adapters are unique)
mapping (address => Exchange) public exchangeInformation;
address[] public registeredExchangeAdapters;
mapping (address => Version) public versionInformation;
address[] public registeredVersions;
mapping (address => bool) public isFeeRegistered;
mapping (address => address) public fundsToVersions;
mapping (bytes32 => bool) public versionNameExists;
mapping (bytes32 => address) public fundNameHashToOwner;
uint public incentive = 10 finney;
address public priceSource;
address public mlnToken;
address public nativeAsset;
address public engine;
address public ethfinexWrapperRegistry;
address public MGM;
modifier onlyVersion() {
require(
versionInformation[msg.sender].exists,
"Only a Version can do this"
);
_;
}
// METHODS
constructor(address _postDeployOwner) public {
setOwner(_postDeployOwner);
}
// PUBLIC METHODS
/// @notice Whether _name has only valid characters
function isValidFundName(string memory _name) public pure returns (bool) {
bytes memory b = bytes(_name);
if (b.length > MAX_FUND_NAME_BYTES) return false;
for (uint i; i < b.length; i++){
bytes1 char = b[i];
if(
!(char >= 0x30 && char <= 0x39) && // 9-0
!(char >= 0x41 && char <= 0x5A) && // A-Z
!(char >= 0x61 && char <= 0x7A) && // a-z
!(char == 0x20 || char == 0x2D) && // space, dash
!(char == 0x2E || char == 0x5F) && // period, underscore
!(char == 0x2A) // *
) {
return false;
}
}
return true;
}
/// @notice Whether _user can use _name for their fund
function canUseFundName(address _user, string memory _name) public view returns (bool) {
bytes32 nameHash = keccak256(bytes(_name));
return (
isValidFundName(_name) &&
(
fundNameHashToOwner[nameHash] == address(0) ||
fundNameHashToOwner[nameHash] == _user
)
);
}
function reserveFundName(address _owner, string calldata _name)
external
onlyVersion
{
require(canUseFundName(_owner, _name), "Fund name cannot be used");
fundNameHashToOwner[keccak256(bytes(_name))] = _owner;
}
function registerFund(address _fund, address _owner, string calldata _name)
external
onlyVersion
{
require(canUseFundName(_owner, _name), "Fund name cannot be used");
fundsToVersions[_fund] = msg.sender;
}
/// @notice Registers an Asset information entry
/// @dev Pre: Only registrar owner should be able to register
/// @dev Post: Address _asset is registered
/// @param _asset Address of asset to be registered
/// @param _name Human-readable name of the Asset
/// @param _symbol Human-readable symbol of the Asset
/// @param _url Url for extended information of the asset
/// @param _standards Integers of EIP standards this asset adheres to
/// @param _sigs Function signatures for whitelisted asset functions
function registerAsset(
address _asset,
string calldata _name,
string calldata _symbol,
string calldata _url,
uint _reserveMin,
uint[] calldata _standards,
bytes4[] calldata _sigs
) external auth {
require(registeredAssets.length < MAX_REGISTERED_ENTITIES);
require(!assetInformation[_asset].exists);
assetInformation[_asset].exists = true;
registeredAssets.push(_asset);
updateAsset(
_asset,
_name,
_symbol,
_url,
_reserveMin,
_standards,
_sigs
);
}
/// @notice Register an exchange information entry (A mapping from exchange adapter -> Exchange information)
/// @dev Adapters are unique so are used as the mapping key. There may be different adapters for same exchange (0x / Ethfinex)
/// @dev Pre: Only registrar owner should be able to register
/// @dev Post: Address _exchange is registered
/// @param _exchange Address of the exchange for the adapter
/// @param _adapter Address of exchange adapter
/// @param _takesCustody Whether this exchange takes custody of tokens before trading
/// @param _sigs Function signatures for whitelisted exchange functions
function registerExchangeAdapter(
address _exchange,
address _adapter,
bool _takesCustody,
bytes4[] calldata _sigs
) external auth {
require(!exchangeInformation[_adapter].exists, "Adapter already exists");
exchangeInformation[_adapter].exists = true;
require(registeredExchangeAdapters.length < MAX_REGISTERED_ENTITIES, "Exchange limit reached");
registeredExchangeAdapters.push(_adapter);
updateExchangeAdapter(
_exchange,
_adapter,
_takesCustody,
_sigs
);
}
/// @notice Versions cannot be removed from registry
/// @param _version Address of the version contract
/// @param _name Name of the version
function registerVersion(
address _version,
bytes32 _name
) external auth {
require(!versionInformation[_version].exists, "Version already exists");
require(!versionNameExists[_name], "Version name already exists");
versionInformation[_version].exists = true;
versionNameExists[_name] = true;
versionInformation[_version].name = _name;
registeredVersions.push(_version);
emit VersionRegistration(_version);
}
function setIncentive(uint _weiAmount) external auth {
incentive = _weiAmount;
emit IncentiveChange(_weiAmount);
}
function setPriceSource(address _priceSource) external auth {
priceSource = _priceSource;
emit PriceSourceChange(_priceSource);
}
function setMlnToken(address _mlnToken) external auth {
mlnToken = _mlnToken;
emit MlnTokenChange(_mlnToken);
}
function setNativeAsset(address _nativeAsset) external auth {
nativeAsset = _nativeAsset;
emit NativeAssetChange(_nativeAsset);
}
function setEngine(address _engine) external auth {
engine = _engine;
emit EngineChange(_engine);
}
function setMGM(address _MGM) external auth {
MGM = _MGM;
emit MGMChange(_MGM);
}
function setEthfinexWrapperRegistry(address _registry) external auth {
ethfinexWrapperRegistry = _registry;
emit EfxWrapperRegistryChange(_registry);
}
/// @notice Updates description information of a registered Asset
/// @dev Pre: Owner can change an existing entry
/// @dev Post: Changed Name, Symbol, URL and/or IPFSHash
/// @param _asset Address of the asset to be updated
/// @param _name Human-readable name of the Asset
/// @param _symbol Human-readable symbol of the Asset
/// @param _url Url for extended information of the asset
function updateAsset(
address _asset,
string memory _name,
string memory _symbol,
string memory _url,
uint _reserveMin,
uint[] memory _standards,
bytes4[] memory _sigs
) public auth {
require(assetInformation[_asset].exists);
Asset storage asset = assetInformation[_asset];
asset.name = _name;
asset.symbol = _symbol;
asset.decimals = ERC20WithFields(_asset).decimals();
asset.url = _url;
asset.reserveMin = _reserveMin;
asset.standards = _standards;
asset.sigs = _sigs;
emit AssetUpsert(
_asset,
_name,
_symbol,
asset.decimals,
_url,
_reserveMin,
_standards,
_sigs
);
}
function updateExchangeAdapter(
address _exchange,
address _adapter,
bool _takesCustody,
bytes4[] memory _sigs
) public auth {
require(exchangeInformation[_adapter].exists, "Exchange with adapter doesn't exist");
Exchange storage exchange = exchangeInformation[_adapter];
exchange.exchangeAddress = _exchange;
exchange.takesCustody = _takesCustody;
exchange.sigs = _sigs;
emit ExchangeAdapterUpsert(
_exchange,
_adapter,
_takesCustody,
_sigs
);
}
/// @notice Deletes an existing entry
/// @dev Owner can delete an existing entry
/// @param _asset address for which specific information is requested
function removeAsset(
address _asset,
uint _assetIndex
) external auth {
require(assetInformation[_asset].exists);
require(registeredAssets[_assetIndex] == _asset);
delete assetInformation[_asset];
delete registeredAssets[_assetIndex];
for (uint i = _assetIndex; i < registeredAssets.length-1; i++) {
registeredAssets[i] = registeredAssets[i+1];
}
registeredAssets.pop();
emit AssetRemoval(_asset);
}
/// @notice Deletes an existing entry
/// @dev Owner can delete an existing entry
/// @param _adapter address of the adapter of the exchange that is to be removed
/// @param _adapterIndex index of the exchange in array
function removeExchangeAdapter(
address _adapter,
uint _adapterIndex
) external auth {
require(exchangeInformation[_adapter].exists, "Exchange with adapter doesn't exist");
require(registeredExchangeAdapters[_adapterIndex] == _adapter, "Incorrect adapter index");
delete exchangeInformation[_adapter];
delete registeredExchangeAdapters[_adapterIndex];
for (uint i = _adapterIndex; i < registeredExchangeAdapters.length-1; i++) {
registeredExchangeAdapters[i] = registeredExchangeAdapters[i+1];
}
registeredExchangeAdapters.pop();
emit ExchangeAdapterRemoval(_adapter);
}
function registerFees(address[] calldata _fees) external auth {
for (uint i; i < _fees.length; i++) {
isFeeRegistered[_fees[i]] = true;
}
}
function deregisterFees(address[] calldata _fees) external auth {
for (uint i; i < _fees.length; i++) {
delete isFeeRegistered[_fees[i]];
}
}
// PUBLIC VIEW METHODS
// get asset specific information
function getName(address _asset) external view returns (string memory) {
return assetInformation[_asset].name;
}
function getSymbol(address _asset) external view returns (string memory) {
return assetInformation[_asset].symbol;
}
function getDecimals(address _asset) external view returns (uint) {
return assetInformation[_asset].decimals;
}
function getReserveMin(address _asset) external view returns (uint) {
return assetInformation[_asset].reserveMin;
}
function assetIsRegistered(address _asset) external view returns (bool) {
return assetInformation[_asset].exists;
}
function getRegisteredAssets() external view returns (address[] memory) {
return registeredAssets;
}
function assetMethodIsAllowed(address _asset, bytes4 _sig)
external
view
returns (bool)
{
bytes4[] memory signatures = assetInformation[_asset].sigs;
for (uint i = 0; i < signatures.length; i++) {
if (signatures[i] == _sig) {
return true;
}
}
return false;
}
// get exchange-specific information
function exchangeAdapterIsRegistered(address _adapter) external view returns (bool) {
return exchangeInformation[_adapter].exists;
}
function getRegisteredExchangeAdapters() external view returns (address[] memory) {
return registeredExchangeAdapters;
}
function getExchangeInformation(address _adapter)
public
view
returns (address, bool)
{
Exchange memory exchange = exchangeInformation[_adapter];
return (
exchange.exchangeAddress,
exchange.takesCustody
);
}
function exchangeForAdapter(address _adapter) external view returns (address) {
Exchange memory exchange = exchangeInformation[_adapter];
return exchange.exchangeAddress;
}
function getAdapterFunctionSignatures(address _adapter)
public
view
returns (bytes4[] memory)
{
return exchangeInformation[_adapter].sigs;
}
function adapterMethodIsAllowed(
address _adapter, bytes4 _sig
)
external
view
returns (bool)
{
bytes4[] memory signatures = exchangeInformation[_adapter].sigs;
for (uint i = 0; i < signatures.length; i++) {
if (signatures[i] == _sig) {
return true;
}
}
return false;
}
// get version and fund information
function getRegisteredVersions() external view returns (address[] memory) {
return registeredVersions;
}
function isFund(address _who) external view returns (bool) {
if (fundsToVersions[_who] != address(0)) {
return true; // directly from a hub
} else {
Hub hub = Hub(Spoke(_who).hub());
require(
hub.isSpoke(_who),
"Call from either a spoke or hub"
);
return fundsToVersions[address(hub)] != address(0);
}
}
function isFundFactory(address _who) external view returns (bool) {
return versionInformation[_who].exists;
}
}
Shares.sol 77 lines
pragma solidity 0.6.1;
import "../hub/Spoke.sol";
import "../../dependencies/token/StandardToken.sol";
import "../../factory/Factory.sol";
contract Shares is Spoke, StandardToken {
string public symbol;
string public name;
uint8 public decimals;
constructor(address _hub) public Spoke(_hub) {
name = hub.name();
symbol = "MLNF";
decimals = 18;
}
function createFor(address who, uint amount) public auth {
_mint(who, amount);
}
function destroyFor(address who, uint amount) public auth {
_burn(who, amount);
}
function transfer(address to, uint amount) public override returns (bool) {
revert("Unimplemented");
}
function transferFrom(
address from,
address to,
uint amount
)
public
override
returns (bool)
{
revert("Unimplemented");
}
function approve(address spender, uint amount) public override returns (bool) {
revert("Unimplemented");
}
function increaseApproval(
address spender,
uint amount
)
public
override
returns (bool)
{
revert("Unimplemented");
}
function decreaseApproval(
address spender,
uint amount
)
public
override
returns (bool)
{
revert("Unimplemented");
}
}
contract SharesFactory is Factory {
function createInstance(address _hub) external returns (address) {
address shares = address(new Shares(_hub));
childExists[shares] = true;
emit NewInstance(_hub, shares);
return shares;
}
}
DSAuth.sol 57 lines
/// @notice Modified from DappHub (https://git.io/fpwrq)
pragma solidity 0.6.1;
abstract contract DSAuthority {
function canCall(
address src, address dst, bytes4 sig
) public view virtual returns (bool);
}
contract DSAuthEvents {
event LogSetAuthority (address indexed authority);
event LogSetOwner (address indexed owner);
}
contract DSAuth is DSAuthEvents {
DSAuthority public authority;
address public owner;
constructor() public {
owner = msg.sender;
emit LogSetOwner(msg.sender);
}
function setOwner(address owner_)
public
auth
{
owner = owner_;
emit LogSetOwner(owner);
}
function setAuthority(DSAuthority authority_)
public
auth
{
authority = authority_;
emit LogSetAuthority(address(authority));
}
modifier auth {
require(isAuthorized(msg.sender, msg.sig), "ds-auth-unauthorized");
_;
}
function isAuthorized(address src, bytes4 sig) internal view returns (bool) {
if (src == address(this)) {
return true;
} else if (src == owner) {
return true;
} else if (authority == DSAuthority(0)) {
return false;
} else {
return authority.canCall(src, address(this), sig);
}
}
}
DSMath.sol 84 lines
/// DSMath.sol -- mixin for inline numerical wizardry
// 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 <http://www.gnu.org/licenses/>.
pragma solidity >0.4.13;
contract DSMath {
function add(uint x, uint y) internal pure returns (uint z) {
require((z = x + y) >= x, "ds-math-add-overflow");
}
function sub(uint x, uint y) internal pure returns (uint z) {
require((z = x - y) <= x, "ds-math-sub-underflow");
}
function mul(uint x, uint y) internal pure returns (uint z) {
require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
}
function min(uint x, uint y) internal pure returns (uint z) {
return x <= y ? x : y;
}
function max(uint x, uint y) internal pure returns (uint z) {
return x >= y ? x : y;
}
function imin(int x, int y) internal pure returns (int z) {
return x <= y ? x : y;
}
function imax(int x, int y) internal pure returns (int z) {
return x >= y ? x : y;
}
uint constant WAD = 10 ** 18;
uint constant RAY = 10 ** 27;
function wmul(uint x, uint y) internal pure returns (uint z) {
z = add(mul(x, y), WAD / 2) / WAD;
}
function rmul(uint x, uint y) internal pure returns (uint z) {
z = add(mul(x, y), RAY / 2) / RAY;
}
function wdiv(uint x, uint y) internal pure returns (uint z) {
z = add(mul(x, WAD), y / 2) / y;
}
function rdiv(uint x, uint y) internal pure returns (uint z) {
z = add(mul(x, RAY), y / 2) / y;
}
// This famous algorithm is called "exponentiation by squaring"
// and calculates x^n with x as fixed-point and n as regular unsigned.
//
// It's O(log n), instead of O(n) for naive repeated multiplication.
//
// These facts are why it works:
//
// If n is even, then x^n = (x^2)^(n/2).
// If n is odd, then x^n = x * x^(n-1),
// and applying the equation for even x gives
// x^n = x * (x^2)^((n-1) / 2).
//
// Also, EVM division is flooring and
// floor[(n-1) / 2] = floor[n / 2].
//
function rpow(uint x, uint n) internal pure returns (uint z) {
z = n % 2 != 0 ? x : RAY;
for (n /= 2; n != 0; n /= 2) {
x = rmul(x, x);
if (n % 2 != 0) {
z = rmul(z, x);
}
}
}
}
AmguConsumer.sol 61 lines
pragma solidity 0.6.1;
import "../dependencies/DSMath.sol";
import "../dependencies/token/IERC20.sol";
import "../prices/IPriceSource.sol";
import "../version/IVersion.sol";
import "./IEngine.sol";
import "../version/Registry.sol";
/// @notice Abstract contracts
/// @notice inherit this to pay AMGU on a function call
abstract contract AmguConsumer is DSMath {
/// @dev each of these must be implemented by the inheriting contract
function engine() public view virtual returns (address);
function mlnToken() public view virtual returns (address);
function priceSource() public view virtual returns (address);
function registry() public view virtual returns (address);
event AmguPaid(address indexed payer, uint256 totalAmguPaidInEth, uint256 amguChargableGas, uint256 incentivePaid);
/// bool deductIncentive is used when sending extra eth beyond amgu
modifier amguPayable(bool deductIncentive) {
uint preGas = gasleft();
_;
uint postGas = gasleft();
uint mlnPerAmgu = IEngine(engine()).getAmguPrice();
uint mlnQuantity = mul(
mlnPerAmgu,
sub(preGas, postGas)
);
address nativeAsset = Registry(registry()).nativeAsset();
uint ethToPay = IPriceSource(priceSource()).convertQuantity(
mlnQuantity,
mlnToken(),
nativeAsset
);
uint incentiveAmount;
if (deductIncentive) {
incentiveAmount = Registry(registry()).incentive();
} else {
incentiveAmount = 0;
}
require(
msg.value >= add(ethToPay, incentiveAmount),
"Insufficent AMGU and/or incentive"
);
IEngine(engine()).payAmguInEther.value(ethToPay)();
require(
msg.sender.send(
sub(
sub(msg.value, ethToPay),
incentiveAmount
)
),
"Refund failed"
);
emit AmguPaid(msg.sender, ethToPay, sub(preGas, postGas), incentiveAmount);
}
}
IPriceSource.sol 28 lines
pragma solidity 0.6.1;
/// @notice Must return a value for an asset
interface IPriceSource {
function getQuoteAsset() external view returns (address);
function getLastUpdate() external view returns (uint);
/// @notice Returns false if asset not applicable, or price not recent
function hasValidPrice(address) external view returns (bool);
function hasValidPrices(address[] calldata) external view returns (bool);
/// @notice Return the last known price, and when it was issued
function getPrice(address _asset) external view returns (uint price, uint timestamp);
function getPrices(address[] calldata _assets) external view returns (uint[] memory prices, uint[] memory timestamps);
/// @notice Get price info, and revert if not valid
function getPriceInfo(address _asset) external view returns (uint price, uint decimals);
function getInvertedPriceInfo(address ofAsset) external view returns (uint price, uint decimals);
function getReferencePriceInfo(address _base, address _quote) external view returns (uint referencePrice, uint decimal);
function getOrderPriceInfo(address sellAsset, uint sellQuantity, uint buyQuantity) external view returns (uint orderPrice);
function existsPriceOnAssetPair(address sellAsset, address buyAsset) external view returns (bool isExistent);
function convertQuantity(
uint fromAssetQuantity,
address fromAsset,
address toAsset
) external view returns (uint);
}
DSGuard.sol 69 lines
/// @notice Retrieved from DappHub (https://git.io/fpwMi)
pragma solidity 0.6.1;
import "./DSAuth.sol";
contract DSGuardEvents {
event LogPermit(
bytes32 indexed src,
bytes32 indexed dst,
bytes32 indexed sig
);
event LogForbid(
bytes32 indexed src,
bytes32 indexed dst,
bytes32 indexed sig
);
}
contract DSGuard is DSAuth, DSAuthority, DSGuardEvents {
bytes32 constant public ANY = bytes32(uint(-1));
mapping (bytes32 => mapping (bytes32 => mapping (bytes32 => bool))) acl;
function canCall(
address src_, address dst_, bytes4 sig
) public view override returns (bool) {
bytes32 src = bytes32(bytes20(src_));
bytes32 dst = bytes32(bytes20(dst_));
return acl[src][dst][sig]
|| acl[src][dst][ANY]
|| acl[src][ANY][sig]
|| acl[src][ANY][ANY]
|| acl[ANY][dst][sig]
|| acl[ANY][dst][ANY]
|| acl[ANY][ANY][sig]
|| acl[ANY][ANY][ANY];
}
function permit(bytes32 src, bytes32 dst, bytes32 sig) public auth {
acl[src][dst][sig] = true;
emit LogPermit(src, dst, sig);
}
function forbid(bytes32 src, bytes32 dst, bytes32 sig) public auth {
acl[src][dst][sig] = false;
emit LogForbid(src, dst, sig);
}
function permit(address src, address dst, bytes32 sig) public {
permit(bytes32(bytes20(src)), bytes32(bytes20(dst)), sig);
}
function forbid(address src, address dst, bytes32 sig) public {
forbid(bytes32(bytes20(src)), bytes32(bytes20(dst)), sig);
}
}
contract DSGuardFactory {
mapping (address => bool) public isGuard;
function newGuard() public returns (DSGuard guard) {
guard = new DSGuard();
guard.setOwner(msg.sender);
isGuard[address(guard)] = true;
}
}
FeeManager.sol 119 lines
pragma solidity 0.6.1;
pragma experimental ABIEncoderV2;
import "./IFee.sol";
import "../hub/Spoke.sol";
import "../shares/Shares.sol";
import "../../factory/Factory.sol";
import "../../version/Registry.sol";
import "../../dependencies/DSMath.sol";
import "./IFeeManager.sol";
/// @notice Manages and allocates fees for a particular fund
contract FeeManager is DSMath, Spoke {
event FeeReward(uint shareQuantity);
event FeeRegistration(address fee);
struct FeeInfo {
address feeAddress;
uint feeRate;
uint feePeriod;
}
IFee[] public fees;
mapping (address => bool) public feeIsRegistered;
constructor(address _hub, address _denominationAsset, address[] memory _fees, uint[] memory _rates, uint[] memory _periods, address _registry) Spoke(_hub) public {
for (uint i = 0; i < _fees.length; i++) {
require(
Registry(_registry).isFeeRegistered(_fees[i]),
"Fee must be known to Registry"
);
register(_fees[i], _rates[i], _periods[i], _denominationAsset);
}
if (fees.length > 0) {
require(
fees[0].identifier() == 0,
"Management fee must be at 0 index"
);
}
if (fees.length > 1) {
require(
fees[1].identifier() == 1,
"Performance fee must be at 1 index"
);
}
}
function register(address feeAddress, uint feeRate, uint feePeriod, address denominationAsset) internal {
require(!feeIsRegistered[feeAddress], "Fee already registered");
feeIsRegistered[feeAddress] = true;
fees.push(IFee(feeAddress));
IFee(feeAddress).initializeForUser(feeRate, feePeriod, denominationAsset); // initialize state
emit FeeRegistration(feeAddress);
}
function totalFeeAmount() external returns (uint total) {
for (uint i = 0; i < fees.length; i++) {
total = add(total, fees[i].feeAmount());
}
return total;
}
/// @dev Shares to be inflated after update state
function _rewardFee(IFee fee) internal {
require(feeIsRegistered[address(fee)], "Fee is not registered");
uint rewardShares = fee.feeAmount();
fee.updateState();
Shares(routes.shares).createFor(hub.manager(), rewardShares);
emit FeeReward(rewardShares);
}
function _rewardAllFees() internal {
for (uint i = 0; i < fees.length; i++) {
_rewardFee(fees[i]);
}
}
/// @dev Used when calling from other components
function rewardAllFees() public auth { _rewardAllFees(); }
/// @dev Convenience function; anyone can reward management fee any time
/// @dev Convention that management fee is 0
function rewardManagementFee() public {
if (fees.length >= 1) _rewardFee(fees[0]);
}
/// @dev Convenience function
/// @dev Convention that management fee is 0
function managementFeeAmount() external returns (uint) {
if (fees.length < 1) return 0;
return fees[0].feeAmount();
}
/// @dev Convenience function
/// @dev Convention that performace fee is 1
function performanceFeeAmount() external returns (uint) {
if (fees.length < 2) return 0;
return fees[1].feeAmount();
}
}
contract FeeManagerFactory is Factory {
function createInstance(
address _hub,
address _denominationAsset,
address[] memory _fees,
uint[] memory _feeRates,
uint[] memory _feePeriods,
address _registry
) public returns (address) {
address feeManager = address(
new FeeManager(_hub, _denominationAsset, _fees, _feeRates, _feePeriods, _registry)
);
childExists[feeManager] = true;
emit NewInstance(_hub, feeManager);
return feeManager;
}
}
SafeMath.sol 66 lines
pragma solidity 0.6.1;
/**
* @title SafeMath
* @dev Math operations with safety checks that revert on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, reverts on overflow.
*/
function mul(uint256 _a, uint256 _b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (_a == 0) {
return 0;
}
uint256 c = _a * _b;
require(c / _a == _b);
return c;
}
/**
* @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
*/
function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
require(_b > 0); // Solidity only automatically asserts when dividing by 0
uint256 c = _a / _b;
// assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
require(_b <= _a);
uint256 c = _a - _b;
return c;
}
/**
* @dev Adds two numbers, reverts on overflow.
*/
function add(uint256 _a, uint256 _b) internal pure returns (uint256) {
uint256 c = _a + _b;
require(c >= _a);
return c;
}
/**
* @dev Divides two numbers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0);
return a % b;
}
}
IFeeManager.sol 12 lines
pragma solidity 0.6.1;
interface IFeeManagerFactory {
function createInstance(
address _hub,
address _denominationAsset,
address[] calldata _fees,
uint[] calldata _feeRates,
uint[] calldata _feePeriods,
address _registry
) external returns (address);
}
IPolicy.sol 16 lines
pragma solidity 0.6.1;
interface IPolicy {
enum Applied { pre, post }
// In Trading context:
// addresses: Order maker, Order taker, Order maker asset, Order taker asset, Exchange address
// values: Maker token quantity, Taker token quantity, Fill Taker Quantity
// In Participation context:
// address[0]: Investor address, address[3]: Investment asset
function rule(bytes4 sig, address[5] calldata addresses, uint[3] calldata values, bytes32 identifier) external returns (bool);
function position() external view returns (Applied);
function identifier() external view returns (string memory);
}
ITrading.sol 44 lines
pragma solidity 0.6.1;
pragma experimental ABIEncoderV2;
// TODO: Restore indexed params
/// @notice Mediation between a Fund and exchanges
interface ITrading {
function callOnExchange(
uint exchangeIndex,
string calldata methodSignature,
address[8] calldata orderAddresses,
uint[8] calldata orderValues,
bytes[4] calldata orderData,
bytes32 identifier,
bytes calldata signature
) external;
function addOpenMakeOrder(
address ofExchange,
address ofSellAsset,
address ofBuyAsset,
address ofFeeAsset,
uint orderId,
uint expiryTime
) external;
function removeOpenMakeOrder(
address ofExchange,
address ofSellAsset
) external;
function updateAndGetQuantityBeingTraded(address _asset) external returns (uint256);
function getOpenMakeOrdersAgainstAsset(address _asset) external view returns (uint256);
}
interface ITradingFactory {
function createInstance(
address _hub,
address[] calldata _exchanges,
address[] calldata _adapters,
address _registry
) external returns (address);
}
TokenUser.sol 36 lines
pragma solidity 0.6.1;
import "./token/IERC20.sol";
import "./DSMath.sol";
/// @notice Wrapper to ensure tokens are received
contract TokenUser is DSMath {
function safeTransfer(
address _token,
address _to,
uint _value
) internal {
uint receiverPreBalance = IERC20(_token).balanceOf(_to);
IERC20(_token).transfer(_to, _value);
uint receiverPostBalance = IERC20(_token).balanceOf(_to);
require(
add(receiverPreBalance, _value) == receiverPostBalance,
"Receiver did not receive tokens in transfer"
);
}
function safeTransferFrom(
address _token,
address _from,
address _to,
uint _value
) internal {
uint receiverPreBalance = IERC20(_token).balanceOf(_to);
IERC20(_token).transferFrom(_from, _to, _value);
uint receiverPostBalance = IERC20(_token).balanceOf(_to);
require(
add(receiverPreBalance, _value) == receiverPostBalance,
"Receiver did not receive tokens in transferFrom"
);
}
}
IERC20.sol 40 lines
pragma solidity 0.6.1;
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
* Altered from https://github.com/OpenZeppelin/openzeppelin-solidity/blob/a466e76d26c394b1faa6e2797aefe34668566392/contracts/token/ERC20/ERC20.sol
*/
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address _who) external view returns (uint256);
function allowance(address _owner, address _spender)
external view returns (uint256);
function transfer(address _to, uint256 _value) external returns (bool);
function approve(address _spender, uint256 _value) external returns (bool);
function transferFrom(address _from, address _to, uint256 _value) external returns (bool);
event Transfer(
address indexed from,
address indexed to,
uint256 value
);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
}
/// @dev Just adds extra functions that we use elsewhere
abstract contract ERC20WithFields is IERC20 {
string public symbol;
string public name;
uint8 public decimals;
}
Accounting.sol 258 lines
pragma solidity 0.6.1;
import "../../dependencies/token/StandardToken.sol";
import "../../factory/Factory.sol";
import "../../prices/IPriceSource.sol";
import "../fees/FeeManager.sol";
import "../hub/Spoke.sol";
import "../shares/Shares.sol";
import "../trading/ITrading.sol";
import "../vault/Vault.sol";
import "../../engine/AmguConsumer.sol";
contract Accounting is AmguConsumer, Spoke {
event AssetAddition(address indexed asset);
event AssetRemoval(address indexed asset);
struct Calculations {
uint gav;
uint nav;
uint allocatedFees;
uint totalSupply;
uint timestamp;
}
uint constant public MAX_OWNED_ASSETS = 20;
address[] public ownedAssets;
mapping (address => bool) public isInAssetList;
uint public constant SHARES_DECIMALS = 18;
address public NATIVE_ASSET;
address public DENOMINATION_ASSET;
uint public DENOMINATION_ASSET_DECIMALS;
uint public DEFAULT_SHARE_PRICE;
Calculations public atLastAllocation;
constructor(address _hub, address _denominationAsset, address _nativeAsset)
public
Spoke(_hub)
{
DENOMINATION_ASSET = _denominationAsset;
NATIVE_ASSET = _nativeAsset;
DENOMINATION_ASSET_DECIMALS = ERC20WithFields(DENOMINATION_ASSET).decimals();
DEFAULT_SHARE_PRICE = 10 ** uint(DENOMINATION_ASSET_DECIMALS);
}
function getOwnedAssetsLength() external view returns (uint256) {
return ownedAssets.length;
}
function getOwnedAssets() external view returns (address[] memory) {
return ownedAssets;
}
function assetHoldings(address _asset) public returns (uint256) {
return add(
uint256(ERC20WithFields(_asset).balanceOf(routes.vault)),
ITrading(routes.trading).updateAndGetQuantityBeingTraded(_asset)
);
}
/// @dev Returns sparse array
function getFundHoldings() external returns (uint[] memory, address[] memory) {
uint[] memory _quantities = new uint[](ownedAssets.length);
address[] memory _assets = new address[](ownedAssets.length);
for (uint i = 0; i < ownedAssets.length; i++) {
address ofAsset = ownedAssets[i];
// assetHoldings formatting: mul(exchangeHoldings, 10 ** assetDecimal)
uint quantityHeld = assetHoldings(ofAsset);
_assets[i] = ofAsset;
_quantities[i] = quantityHeld;
}
return (_quantities, _assets);
}
function calcAssetGAV(address _queryAsset) external returns (uint) {
uint queryAssetQuantityHeld = assetHoldings(_queryAsset);
return IPriceSource(priceSource()).convertQuantity(
queryAssetQuantityHeld, _queryAsset, DENOMINATION_ASSET
);
}
// prices are quoted in DENOMINATION_ASSET so they use denominationDecimals
function calcGav() public returns (uint gav) {
for (uint i = 0; i < ownedAssets.length; ++i) {
address asset = ownedAssets[i];
// assetHoldings formatting: mul(exchangeHoldings, 10 ** assetDecimals)
uint quantityHeld = assetHoldings(asset);
// Dont bother with the calculations if the balance of the asset is 0
if (quantityHeld == 0) {
continue;
}
// gav as sum of mul(assetHoldings, assetPrice) with formatting: mul(mul(exchangeHoldings, exchangePrice), 10 ** shareDecimals)
gav = add(
gav,
IPriceSource(priceSource()).convertQuantity(
quantityHeld, asset, DENOMINATION_ASSET
)
);
}
return gav;
}
function calcNav(uint gav, uint unclaimedFeesInDenominationAsset) public pure returns (uint) {
return sub(gav, unclaimedFeesInDenominationAsset);
}
function valuePerShare(uint totalValue, uint numShares) public pure returns (uint) {
require(numShares > 0, "No shares to calculate value for");
return (totalValue * 10 ** uint(SHARES_DECIMALS)) / numShares;
}
function performCalculations()
public
returns (
uint gav,
uint feesInDenominationAsset, // unclaimed amount
uint feesInShares, // unclaimed amount
uint nav,
uint sharePrice,
uint gavPerShareNetManagementFee
)
{
gav = calcGav();
uint totalSupply = Shares(routes.shares).totalSupply();
feesInShares = FeeManager(routes.feeManager).totalFeeAmount();
feesInDenominationAsset = (totalSupply == 0) ?
0 :
mul(feesInShares, gav) / add(totalSupply, feesInShares);
nav = calcNav(gav, feesInDenominationAsset);
// The total share supply including the value of feesInDenominationAsset, measured in shares of this fund
uint totalSupplyAccountingForFees = add(totalSupply, feesInShares);
sharePrice = (totalSupply > 0) ?
valuePerShare(gav, totalSupplyAccountingForFees) :
DEFAULT_SHARE_PRICE;
gavPerShareNetManagementFee = (totalSupply > 0) ?
valuePerShare(gav, add(totalSupply, FeeManager(routes.feeManager).managementFeeAmount())) :
DEFAULT_SHARE_PRICE;
return (gav, feesInDenominationAsset, feesInShares, nav, sharePrice, gavPerShareNetManagementFee);
}
function calcGavPerShareNetManagementFee()
public
returns (uint gavPerShareNetManagementFee)
{
(,,,,,gavPerShareNetManagementFee) = performCalculations();
return gavPerShareNetManagementFee;
}
function getShareCostInAsset(uint _numShares, address _altAsset)
external
returns (uint)
{
uint denominationAssetQuantity = mul(
_numShares,
calcGavPerShareNetManagementFee()
) / 10 ** uint(SHARES_DECIMALS);
return IPriceSource(priceSource()).convertQuantity(
denominationAssetQuantity, DENOMINATION_ASSET, _altAsset
);
}
/// @notice Reward all fees and perform some updates
/// @dev Anyone can call this
function triggerRewardAllFees()
external
amguPayable(false)
payable
{
updateOwnedAssets();
uint256 gav;
uint256 feesInDenomination;
uint256 feesInShares;
uint256 nav;
(gav, feesInDenomination, feesInShares, nav,,) = performCalculations();
uint256 totalSupply = Shares(routes.shares).totalSupply();
FeeManager(routes.feeManager).rewardAllFees();
atLastAllocation = Calculations({
gav: gav,
nav: nav,
allocatedFees: feesInDenomination,
totalSupply: totalSupply,
timestamp: block.timestamp
});
}
/// @dev Check holdings for all assets, and adjust list
function updateOwnedAssets() public {
for (uint i = 0; i < ownedAssets.length; i++) {
address asset = ownedAssets[i];
if (
assetHoldings(asset) == 0 &&
!(asset == address(DENOMINATION_ASSET)) &&
ITrading(routes.trading).getOpenMakeOrdersAgainstAsset(asset) == 0
) {
_removeFromOwnedAssets(asset);
}
}
}
function addAssetToOwnedAssets(address _asset) external auth {
_addAssetToOwnedAssets(_asset);
}
function removeFromOwnedAssets(address _asset) external auth {
_removeFromOwnedAssets(_asset);
}
/// @dev Just pass if asset already in list
function _addAssetToOwnedAssets(address _asset) internal {
if (isInAssetList[_asset]) { return; }
require(
ownedAssets.length < MAX_OWNED_ASSETS,
"Max owned asset limit reached"
);
isInAssetList[_asset] = true;
ownedAssets.push(_asset);
emit AssetAddition(_asset);
}
/// @dev Just pass if asset not in list
function _removeFromOwnedAssets(address _asset) internal {
if (!isInAssetList[_asset]) { return; }
isInAssetList[_asset] = false;
for (uint i; i < ownedAssets.length; i++) {
if (ownedAssets[i] == _asset) {
ownedAssets[i] = ownedAssets[ownedAssets.length - 1];
ownedAssets.pop();
break;
}
}
emit AssetRemoval(_asset);
}
function engine() public view override(AmguConsumer, Spoke) returns (address) { return Spoke.engine(); }
function mlnToken() public view override(AmguConsumer, Spoke) returns (address) { return Spoke.mlnToken(); }
function priceSource() public view override(AmguConsumer, Spoke) returns (address) { return Spoke.priceSource(); }
function registry() public view override(AmguConsumer, Spoke) returns (address) { return Spoke.registry(); }
}
contract AccountingFactory is Factory {
event NewInstance(
address indexed hub,
address indexed instance,
address denominationAsset,
address nativeAsset
);
function createInstance(address _hub, address _denominationAsset, address _nativeAsset) external returns (address) {
address accounting = address(new Accounting(_hub, _denominationAsset, _nativeAsset));
childExists[accounting] = true;
emit NewInstance(_hub, accounting, _denominationAsset, _nativeAsset);
return accounting;
}
}
PolicyManager.sol 92 lines
pragma solidity 0.6.1;
import "../../factory/Factory.sol";
import "../hub/Spoke.sol";
import "./IPolicy.sol";
contract PolicyManager is Spoke {
event Registration(
bytes4 indexed sig,
IPolicy.Applied position,
address indexed policy
);
struct Entry {
IPolicy[] pre;
IPolicy[] post;
}
mapping(bytes4 => Entry) policies;
constructor (address _hub) public Spoke(_hub) {}
function register(bytes4 sig, address _policy) public auth {
IPolicy.Applied position = IPolicy(_policy).position();
if (position == IPolicy.Applied.pre) {
policies[sig].pre.push(IPolicy(_policy));
} else if (position == IPolicy.Applied.post) {
policies[sig].post.push(IPolicy(_policy));
} else {
revert("Only pre and post allowed");
}
emit Registration(sig, position, _policy);
}
function batchRegister(bytes4[] memory sig, address[] memory _policies) public auth {
require(sig.length == _policies.length, "Arrays lengths unequal");
for (uint i = 0; i < sig.length; i++) {
register(sig[i], _policies[i]);
}
}
function PoliciesToAddresses(IPolicy[] storage _policies) internal view returns (address[] memory) {
address[] memory res = new address[](_policies.length);
for(uint i = 0; i < _policies.length; i++) {
res[i] = address(_policies[i]);
}
return res;
}
function getPoliciesBySig(bytes4 sig) public view returns (address[] memory, address[] memory) {
return (PoliciesToAddresses(policies[sig].pre), PoliciesToAddresses(policies[sig].post));
}
modifier isValidPolicyBySig(bytes4 sig, address[5] memory addresses, uint[3] memory values, bytes32 identifier) {
preValidate(sig, addresses, values, identifier);
_;
postValidate(sig, addresses, values, identifier);
}
modifier isValidPolicy(address[5] memory addresses, uint[3] memory values, bytes32 identifier) {
preValidate(msg.sig, addresses, values, identifier);
_;
postValidate(msg.sig, addresses, values, identifier);
}
function preValidate(bytes4 sig, address[5] memory addresses, uint[3] memory values, bytes32 identifier) public {
validate(policies[sig].pre, sig, addresses, values, identifier);
}
function postValidate(bytes4 sig, address[5] memory addresses, uint[3] memory values, bytes32 identifier) public {
validate(policies[sig].post, sig, addresses, values, identifier);
}
function validate(IPolicy[] storage aux, bytes4 sig, address[5] memory addresses, uint[3] memory values, bytes32 identifier) internal {
for(uint i = 0; i < aux.length; i++) {
require(
aux[i].rule(sig, addresses, values, identifier),
string(abi.encodePacked("Rule evaluated to false: ", aux[i].identifier()))
);
}
}
}
contract PolicyManagerFactory is Factory {
function createInstance(address _hub) external returns (address) {
address policyManager = address(new PolicyManager(_hub));
childExists[policyManager] = true;
emit NewInstance(_hub, policyManager);
return policyManager;
}
}
StandardToken.sol 206 lines
pragma solidity 0.6.1;
import "./IERC20.sol";
import "../SafeMath.sol";
/**
* @title Standard ERC20 token
*
* @dev Implementation of the basic standard token.
* https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
* Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
* Modified from https://github.com/OpenZeppelin/openzeppelin-solidity/blob/a466e76d26c394b1faa6e2797aefe34668566392/contracts/token/ERC20/StandardToken.sol
*/
contract StandardToken is IERC20 {
using SafeMath for uint256;
mapping (address => uint256) balances;
mapping (address => mapping (address => uint256)) allowed;
uint256 totalSupply_;
/**
* @dev Total number of tokens in existence
*/
function totalSupply() public view override returns (uint256) {
return totalSupply_;
}
/**
* @dev Gets the balance of the specified address.
* @param _owner The address to query the the balance of.
* @return An uint256 representing the amount owned by the passed address.
*/
function balanceOf(address _owner) public view override returns (uint256) {
return balances[_owner];
}
/**
* @dev Function to check the amount of tokens that an owner allowed to a spender.
* @param _owner address The address which owns the funds.
* @param _spender address The address which will spend the funds.
* @return A uint256 specifying the amount of tokens still available for the spender.
*/
function allowance(
address _owner,
address _spender
)
public
view
override
returns (uint256)
{
return allowed[_owner][_spender];
}
/**
* @dev Transfer token for a specified address
* @param _to The address to transfer to.
* @param _value The amount to be transferred.
*/
function transfer(address _to, uint256 _value) public virtual override returns (bool) {
require(_value <= balances[msg.sender]);
require(_to != address(0));
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
emit Transfer(msg.sender, _to, _value);
return true;
}
/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
* Beware that changing an allowance with this method brings the risk that someone may use both the old
* and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
* race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
* @param _spender The address which will spend the funds.
* @param _value The amount of tokens to be spent.
*/
function approve(address _spender, uint256 _value) public virtual override returns (bool) {
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
/**
* @dev Transfer tokens from one address to another
* @param _from address The address which you want to send tokens from
* @param _to address The address which you want to transfer to
* @param _value uint256 the amount of tokens to be transferred
*/
function transferFrom(
address _from,
address _to,
uint256 _value
)
public
virtual
override
returns (bool)
{
require(_value <= balances[_from]);
require(_value <= allowed[_from][msg.sender]);
require(_to != address(0));
balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(_value);
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
emit Approval(_from, msg.sender, allowed[_from][msg.sender]);
emit Transfer(_from, _to, _value);
return true;
}
/**
* @dev Increase the amount of tokens that an owner allowed to a spender.
* approve should be called when allowed[_spender] == 0. To increment
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* @param _spender The address which will spend the funds.
* @param _addedValue The amount of tokens to increase the allowance by.
*/
function increaseApproval(
address _spender,
uint256 _addedValue
)
public
virtual
returns (bool)
{
allowed[msg.sender][_spender] = (allowed[msg.sender][_spender].add(_addedValue));
emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
/**
* @dev Decrease the amount of tokens that an owner allowed to a spender.
* approve should be called when allowed[_spender] == 0. To decrement
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* @param _spender The address which will spend the funds.
* @param _subtractedValue The amount of tokens to decrease the allowance by.
*/
function decreaseApproval(
address _spender,
uint256 _subtractedValue
)
public
virtual
returns (bool)
{
uint256 oldValue = allowed[msg.sender][_spender];
if (_subtractedValue >= oldValue) {
allowed[msg.sender][_spender] = 0;
} else {
allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
}
emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
/**
* @dev Internal function that mints an amount of the token and assigns it to
* an account. This encapsulates the modification of balances such that the
* proper events are emitted.
* @param _account The account that will receive the created tokens.
* @param _amount The amount that will be created.
*/
function _mint(address _account, uint256 _amount) internal {
require(_account != address(0));
totalSupply_ = totalSupply_.add(_amount);
balances[_account] = balances[_account].add(_amount);
emit Transfer(address(0), _account, _amount);
}
/**
* @dev Internal function that burns an amount of the token of a given
* account.
* @param _account The account whose tokens will be burnt.
* @param _amount The amount that will be burnt.
*/
function _burn(address _account, uint256 _amount) internal {
require(_account != address(0));
require(_amount <= balances[_account]);
totalSupply_ = totalSupply_.sub(_amount);
balances[_account] = balances[_account].sub(_amount);
emit Transfer(_account, address(0), _amount);
}
/**
* @dev Internal function that burns an amount of the token of a given
* account, deducting from the sender's allowance for said account. Uses the
* internal _burn function.
* @param _account The account whose tokens will be burnt.
* @param _amount The amount that will be burnt.
*/
function _burnFrom(address _account, uint256 _amount) internal {
require(_amount <= allowed[_account][msg.sender]);
allowed[_account][msg.sender] = allowed[_account][msg.sender].sub(_amount);
emit Approval(_account, msg.sender, allowed[_account][msg.sender]);
_burn(_account, _amount);
}
}
Participation.sol 390 lines
pragma solidity 0.6.1;
import "../vault/Vault.sol";
import "../shares/Shares.sol";
import "../policies/PolicyManager.sol";
import "../hub/Spoke.sol";
import "../accounting/Accounting.sol";
import "../../prices/IPriceSource.sol";
import "../../factory/Factory.sol";
import "../../engine/AmguConsumer.sol";
import "../../dependencies/token/IERC20.sol";
import "../../dependencies/DSMath.sol";
import "../../dependencies/TokenUser.sol";
/// @notice Entry and exit point for investors
contract Participation is TokenUser, AmguConsumer, Spoke {
event EnableInvestment (address[] asset);
event DisableInvestment (address[] assets);
event InvestmentRequest (
address indexed requestOwner,
address indexed investmentAsset,
uint requestedShares,
uint investmentAmount
);
event RequestExecution (
address indexed requestOwner,
address indexed executor,
address indexed investmentAsset,
uint investmentAmount,
uint requestedShares
);
event CancelRequest (
address indexed requestOwner
);
event Redemption (
address indexed redeemer,
address[] assets,
uint[] assetQuantities,
uint redeemedShares
);
struct Request {
address investmentAsset;
uint investmentAmount;
uint requestedShares;
uint timestamp;
}
uint constant public SHARES_DECIMALS = 18;
uint constant public REQUEST_LIFESPAN = 1 days;
mapping (address => Request) public requests;
mapping (address => bool) public investAllowed;
mapping (address => bool) public hasInvested; // for information purposes only (read)
address[] public historicalInvestors; // for information purposes only (read)
constructor(address _hub, address[] memory _defaultAssets, address _registry)
public
Spoke(_hub)
{
routes.registry = _registry;
_enableInvestment(_defaultAssets);
}
receive() external payable {}
function _enableInvestment(address[] memory _assets) internal {
for (uint i = 0; i < _assets.length; i++) {
require(
Registry(routes.registry).assetIsRegistered(_assets[i]),
"Asset not registered"
);
investAllowed[_assets[i]] = true;
}
emit EnableInvestment(_assets);
}
function enableInvestment(address[] calldata _assets) external auth {
_enableInvestment(_assets);
}
function disableInvestment(address[] calldata _assets) external auth {
for (uint i = 0; i < _assets.length; i++) {
investAllowed[_assets[i]] = false;
}
emit DisableInvestment(_assets);
}
function hasRequest(address _who) public view returns (bool) {
return requests[_who].timestamp > 0;
}
function hasExpiredRequest(address _who) public view returns (bool) {
return block.timestamp > add(requests[_who].timestamp, REQUEST_LIFESPAN);
}
/// @notice Whether request is OK and invest delay is being respected
/// @dev Request valid if price update happened since request and not expired
/// @dev If no shares exist and not expired, request can be executed immediately
function hasValidRequest(address _who) public view returns (bool) {
IPriceSource priceSource = IPriceSource(priceSource());
bool delayRespectedOrNoShares = requests[_who].timestamp < priceSource.getLastUpdate() ||
Shares(routes.shares).totalSupply() == 0;
return hasRequest(_who) &&
delayRespectedOrNoShares &&
!hasExpiredRequest(_who) &&
requests[_who].investmentAmount > 0 &&
requests[_who].requestedShares > 0;
}
function requestInvestment(
uint requestedShares,
uint investmentAmount,
address investmentAsset
)
external
notShutDown
payable
amguPayable(true)
onlyInitialized
{
PolicyManager(routes.policyManager).preValidate(
msg.sig,
[msg.sender, address(0), address(0), investmentAsset, address(0)],
[uint(0), uint(0), uint(0)],
bytes32(0)
);
require(
investAllowed[investmentAsset],
"Investment not allowed in this asset"
);
safeTransferFrom(
investmentAsset, msg.sender, address(this), investmentAmount
);
require(
requests[msg.sender].timestamp == 0,
"Only one request can exist at a time"
);
requests[msg.sender] = Request({
investmentAsset: investmentAsset,
investmentAmount: investmentAmount,
requestedShares: requestedShares,
timestamp: block.timestamp
});
PolicyManager(routes.policyManager).postValidate(
msg.sig,
[msg.sender, address(0), address(0), investmentAsset, address(0)],
[uint(0), uint(0), uint(0)],
bytes32(0)
);
emit InvestmentRequest(
msg.sender,
investmentAsset,
requestedShares,
investmentAmount
);
}
function _cancelRequestFor(address requestOwner) internal {
require(hasRequest(requestOwner), "No request to cancel");
IPriceSource priceSource = IPriceSource(priceSource());
Request memory request = requests[requestOwner];
require(
!priceSource.hasValidPrice(request.investmentAsset) ||
hasExpiredRequest(requestOwner) ||
hub.isShutDown(),
"No cancellation condition was met"
);
IERC20 investmentAsset = IERC20(request.investmentAsset);
uint investmentAmount = request.investmentAmount;
delete requests[requestOwner];
msg.sender.transfer(Registry(routes.registry).incentive());
safeTransfer(address(investmentAsset), requestOwner, investmentAmount);
emit CancelRequest(requestOwner);
}
/// @notice Can only cancel when no price, request expired or fund shut down
/// @dev Only request owner can cancel their request
function cancelRequest() external payable amguPayable(false) {
_cancelRequestFor(msg.sender);
}
function cancelRequestFor(address requestOwner)
external
payable
amguPayable(false)
{
_cancelRequestFor(requestOwner);
}
function executeRequestFor(address requestOwner)
external
notShutDown
amguPayable(false)
payable
{
Request memory request = requests[requestOwner];
require(
hasValidRequest(requestOwner),
"No valid request for this address"
);
FeeManager(routes.feeManager).rewardManagementFee();
uint totalShareCostInInvestmentAsset = Accounting(routes.accounting)
.getShareCostInAsset(
request.requestedShares,
request.investmentAsset
);
require(
totalShareCostInInvestmentAsset <= request.investmentAmount,
"Invested amount too low"
);
// send necessary amount of investmentAsset to vault
safeTransfer(
request.investmentAsset,
routes.vault,
totalShareCostInInvestmentAsset
);
uint investmentAssetChange = sub(
request.investmentAmount,
totalShareCostInInvestmentAsset
);
// return investmentAsset change to request owner
if (investmentAssetChange > 0) {
safeTransfer(
request.investmentAsset,
requestOwner,
investmentAssetChange
);
}
msg.sender.transfer(Registry(routes.registry).incentive());
Shares(routes.shares).createFor(requestOwner, request.requestedShares);
Accounting(routes.accounting).addAssetToOwnedAssets(request.investmentAsset);
if (!hasInvested[requestOwner]) {
hasInvested[requestOwner] = true;
historicalInvestors.push(requestOwner);
}
emit RequestExecution(
requestOwner,
msg.sender,
request.investmentAsset,
request.investmentAmount,
request.requestedShares
);
delete requests[requestOwner];
}
function getOwedPerformanceFees(uint shareQuantity)
public
returns (uint remainingShareQuantity)
{
Shares shares = Shares(routes.shares);
uint totalPerformanceFee = FeeManager(routes.feeManager).performanceFeeAmount();
// The denominator is augmented because performanceFeeAmount() accounts for inflation
// Since shares are directly transferred, we don't need to account for inflation in this case
uint performanceFeePortion = mul(
totalPerformanceFee,
shareQuantity
) / add(shares.totalSupply(), totalPerformanceFee);
return performanceFeePortion;
}
/// @dev "Happy path" (no asset throws & quantity available)
/// @notice Redeem all shares and across all assets
function redeem() external {
uint ownedShares = Shares(routes.shares).balanceOf(msg.sender);
redeemQuantity(ownedShares);
}
/// @notice Redeem shareQuantity across all assets
function redeemQuantity(uint shareQuantity) public {
address[] memory assetList;
assetList = Accounting(routes.accounting).getOwnedAssets();
redeemWithConstraints(shareQuantity, assetList);
}
// TODO: reconsider the scenario where the user has enough funds to force shutdown on a large trade (any way around this?)
/// @dev Redeem only selected assets (used only when an asset throws)
function redeemWithConstraints(uint shareQuantity, address[] memory requestedAssets) public {
Shares shares = Shares(routes.shares);
require(
shares.balanceOf(msg.sender) >= shareQuantity &&
shares.balanceOf(msg.sender) > 0,
"Sender does not have enough shares to fulfill request"
);
uint owedPerformanceFees = 0;
if (
IPriceSource(priceSource()).hasValidPrices(requestedAssets) &&
msg.sender != hub.manager()
) {
FeeManager(routes.feeManager).rewardManagementFee();
owedPerformanceFees = getOwedPerformanceFees(shareQuantity);
shares.destroyFor(msg.sender, owedPerformanceFees);
shares.createFor(hub.manager(), owedPerformanceFees);
}
uint remainingShareQuantity = sub(shareQuantity, owedPerformanceFees);
address ofAsset;
uint[] memory ownershipQuantities = new uint[](requestedAssets.length);
address[] memory redeemedAssets = new address[](requestedAssets.length);
// Check whether enough assets held by fund
Accounting accounting = Accounting(routes.accounting);
for (uint i = 0; i < requestedAssets.length; ++i) {
ofAsset = requestedAssets[i];
require(
accounting.isInAssetList(ofAsset),
"Requested asset not in asset list"
);
for (uint j = 0; j < redeemedAssets.length; j++) {
require(
ofAsset != redeemedAssets[j],
"Asset can only be redeemed once"
);
}
redeemedAssets[i] = ofAsset;
uint quantityHeld = accounting.assetHoldings(ofAsset);
if (quantityHeld == 0) continue;
// participant's ownership percentage of asset holdings
ownershipQuantities[i] = mul(quantityHeld, remainingShareQuantity) / shares.totalSupply();
}
shares.destroyFor(msg.sender, remainingShareQuantity);
// Transfer owned assets
for (uint k = 0; k < requestedAssets.length; ++k) {
ofAsset = requestedAssets[k];
if (ownershipQuantities[k] == 0) {
continue;
} else {
Vault(routes.vault).withdraw(ofAsset, ownershipQuantities[k]);
safeTransfer(ofAsset, msg.sender, ownershipQuantities[k]);
}
}
emit Redemption(
msg.sender,
requestedAssets,
ownershipQuantities,
remainingShareQuantity
);
}
function getHistoricalInvestors() external view returns (address[] memory) {
return historicalInvestors;
}
function engine() public view override(AmguConsumer, Spoke) returns (address) { return Spoke.engine(); }
function mlnToken() public view override(AmguConsumer, Spoke) returns (address) { return Spoke.mlnToken(); }
function priceSource() public view override(AmguConsumer, Spoke) returns (address) { return Spoke.priceSource(); }
function registry() public view override(AmguConsumer, Spoke) returns (address) { return Spoke.registry(); }
}
contract ParticipationFactory is Factory {
event NewInstance(
address indexed hub,
address indexed instance,
address[] defaultAssets,
address registry
);
function createInstance(address _hub, address[] calldata _defaultAssets, address _registry)
external
returns (address)
{
address participation = address(
new Participation(_hub, _defaultAssets, _registry)
);
childExists[participation] = true;
emit NewInstance(_hub, participation, _defaultAssets, _registry);
return participation;
}
}
Read Contract
REQUEST_LIFESPAN 0x49837b5e → uint256
SHARES_DECIMALS 0x474e19f2 → uint256
authority 0xbf7e214f → address
engine 0xc9d4623f → address
getHistoricalInvestors 0x643466af → address[]
hasExpiredRequest 0x4a248e27 → bool
hasInvested 0xedb9a5c0 → bool
hasRequest 0xe216ea2b → bool
hasValidRequest 0x429f41a7 → bool
historicalInvestors 0xf16ab6dc → address
hub 0x365a86fc → address
initialized 0x158ef93e → bool
investAllowed 0x54c3b8c5 → bool
mlnToken 0x8a471df9 → address
owner 0x8da5cb5b → address
priceSource 0x20531bc9 → address
registry 0x7b103999 → address
requests 0x74adad1d → address, uint256, uint256, uint256
routes 0xb1ffd471 → address, address, address, address, address, address, address, address, address, address, address
version 0x54fd4d50 → address
Write Contract 13 functions
These functions modify contract state and require a wallet transaction to execute.
cancelRequest 0x851b16f5
No parameters
cancelRequestFor 0x0d2485b1
address requestOwner
disableInvestment 0xec622892
address[] _assets
enableInvestment 0x212f6066
address[] _assets
executeRequestFor 0x7baf5929
address requestOwner
getOwedPerformanceFees 0x44ed98dd
uint256 shareQuantity
returns: uint256
initialize 0xbec61462
address[11] _spokes
redeem 0xbe040fb0
No parameters
redeemQuantity 0x0b797141
uint256 shareQuantity
redeemWithConstraints 0x5810a54c
uint256 shareQuantity
address[] requestedAssets
requestInvestment 0x5d582870
uint256 requestedShares
uint256 investmentAmount
address investmentAsset
setAuthority 0x7a9e5e4b
address authority_
setOwner 0x13af4035
address owner_
Recent Transactions
This address has 1 on-chain transactions, but only 0.7% of the chain is indexed. Transactions will appear as indexing progresses. View on Etherscan →