Address Contract Partially Verified
Address
0x85CBBB1ede2B3e389235AE56Ec54BeC8159001e5
Balance
0 ETH
Nonce
1
Code Size
22534 bytes
Creator
0x5bEBA4D3...Fe22 at tx 0xf267a4cf...7c268c
Indexed Transactions
0
Contract Bytecode
22534 bytes
0x6080604052600436106101a05760003560e01c80637c53dc0b116100ec578063caee4c8f1161008a578063dd639b1b11610064578063dd639b1b14610474578063e06174e414610494578063e4a76726146104a9578063f2fde38b146104bc576101a7565b8063caee4c8f14610437578063d4ee1d901461044a578063d80528ae1461045f576101a7565b8063975057e7116100c6578063975057e7146103c2578063b636d486146103d7578063bf3b1101146103f7578063c2250a9914610417576101a7565b80637c53dc0b1461036b57806389d94b461461038b5780638da5cb5b146103a0576101a7565b80634008348011610159578063630d8c6311610133578063630d8c63146102e75780636d533e9b14610307578063782ed90c1461033657806379ba509714610356576101a7565b8063400834801461028757806355bd513f146102a75780635e718c37146102c7576101a7565b80631e83958b146101ac57806324afe2d9146101ce57806324e82c611461020557806326e6b6971461023257806328790b5a146102525780633fad2db314610267576101a7565b366101a757005b600080fd5b3480156101b857600080fd5b506101cc6101c7366004614b8d565b6104dc565b005b3480156101da57600080fd5b506101ee6101e9366004614a89565b610766565b6040516101fc929190615774565b60405180910390f35b34801561021157600080fd5b50610225610220366004614a89565b610798565b6040516101fc91906150db565b34801561023e57600080fd5b506101cc61024d366004614cca565b6107d2565b34801561025e57600080fd5b506101cc6107ed565b34801561027357600080fd5b506101cc610282366004614bcd565b61086a565b34801561029357600080fd5b506102256102a2366004614dcc565b6108e7565b3480156102b357600080fd5b506102256102c2366004614d9d565b6109a9565b3480156102d357600080fd5b506101cc6102e2366004614cca565b6109f3565b3480156102f357600080fd5b506101cc610302366004614e66565b610a15565b34801561031357600080fd5b50610327610322366004614ece565b610c9a565b6040516101fc93929190615782565b34801561034257600080fd5b506101cc610351366004614eaa565b610d95565b34801561036257600080fd5b506101cc610de2565b34801561037757600080fd5b506101cc610386366004614d42565b610e70565b34801561039757600080fd5b506101cc610ea8565b3480156103ac57600080fd5b506103b5610f0b565b6040516101fc9190614f9e565b3480156103ce57600080fd5b506103b5610f1a565b3480156103e357600080fd5b506102256103f2366004614a89565b610f3e565b34801561040357600080fd5b506101cc610412366004614a89565b610f59565b34801561042357600080fd5b506101cc610432366004614a89565b610fdb565b610225610445366004614ac1565b61102f565b34801561045657600080fd5b506103b56110a9565b34801561046b57600080fd5b506103b56110b8565b34801561048057600080fd5b5061022561048f366004614d6d565b6110dc565b3480156104a057600080fd5b506103b5611140565b6102256104b7366004614d02565b611164565b3480156104c857600080fd5b506101cc6104d7366004614a89565b6111d2565b6002805414156105075760405162461bcd60e51b81526004016104fe906155f3565b60405180910390fd5b6002805561051361122a565b8060005b8181101561075b57600084848381811061052d57fe5b90506020020160208101906105429190614a89565b905080600061055082611256565b90507f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c600061057f83836112cf565b9050600061058e8585846113f7565b9050806105a057505050505050610753565b6105aa8582611687565b6060806105b78585611838565b915091506060866001600160a01b031663b127c0a58585856040518463ffffffff1660e01b81526004016105ed93929190615749565b600060405180830381600087803b15801561060757600080fd5b505af115801561061b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526106439190810190614c36565b9050610663898260008151811061065657fe5b602002602001015161191f565b6106898360018151811061067357fe5b60200260200101516001600160a01b0316611a21565b156106e3576106de8160018151811061069e57fe5b60200260200101517f000000000000000000000000649765821d9f64198c905ec0b2b037a4a52bc3736001600160a01b0316611a4690919063ffffffff16565b610749565b6107497f000000000000000000000000649765821d9f64198c905ec0b2b037a4a52bc3738260018151811061071457fe5b60200260200101518560018151811061072957fe5b60200260200101516001600160a01b0316611ae79092919063ffffffff16565b5050505050505050505b600101610517565b505060016002555050565b6000808261077381611b59565b61077c81611c14565b61078584611ccc565b61078e85611df1565b9250925050915091565b60006107a2614905565b6107ab83611f03565b90506107c9620f424063ffffffff168260000151836020015161214d565b9150505b919050565b6107da61122a565b6004805460ff1916911515919091179055565b6107f561122a565b7f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b556001600160a01b03166379ba50976040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561085057600080fd5b505af1158015610864573d6000803e3d6000fd5b50505050565b61087261122a565b8281146108915760405162461bcd60e51b81526004016104fe90615117565b60005b838110156108e0576108d88585838181106108ab57fe5b90506020020160208101906108c09190614a89565b8484848181106108cc57fe5b90506020020135610e70565b600101610894565b5050505050565b600060028054141561090b5760405162461bcd60e51b81526004016104fe906155f3565b60028055846109198161215d565b846109238161215d565b6000610930338a8a612183565b604051635c2ba84560e01b81529091506001600160a01b03881690635c2ba8459061096590849033908b908b90600401615702565b600060405180830381600087803b15801561097f57600080fd5b505af1158015610993573d6000803e3d6000fd5b5050600160025550909998505050505050505050565b60006002805414156109cd5760405162461bcd60e51b81526004016104fe906155f3565b60028055816109db8161215d565b6109e6338585612183565b6001600255949350505050565b6109fb61122a565b600480549115156101000261ff0019909216919091179055565b600280541415610a375760405162461bcd60e51b81526004016104fe906155f3565b60028055604051637a1036f560e11b815260609081906001600160a01b037f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb55169063f4206dea90610a9090339088908890600401614fcb565b60006040518083038186803b158015610aa857600080fd5b505afa158015610abc573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610ae49190810190614c69565b915091506000808351905082518114610af957fe5b805b8015610be3576000198101610b0e6121bf565b858281518110610b1a57fe5b60200260200101511115610b2e5750610bda565b6040516390e0661b60e01b81526001600160a01b037f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb5516906390e0661b90610b7e9033908c860190600401614fb2565b600060405180830381600087803b158015610b9857600080fd5b505af1158015610bac573d6000803e3d6000fd5b50505050610bd6868281518110610bbf57fe5b6020026020010151856121c390919063ffffffff16565b9350505b60001901610afb565b508115610c8d57604051632f1a9acf60e11b81526001600160a01b037f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b551690635e35359e90610c5a907f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c9033908790600401615006565b600060405180830381600087803b158015610c7457600080fd5b505af1158015610c88573d6000803e3d6000fd5b505050505b5050600160025550505050565b600080600084610ca9816121e8565b610cb161491f565b610cba88612223565b80519091506001600160a01b0316610ce45760405162461bcd60e51b81526004016104fe90615680565b8060e00151861015610d085760405162461bcd60e51b81526004016104fe906156d3565b63ffffffff8716620f424014610d3c57610d31816060015182608001518963ffffffff1661230a565b608083015260608201525b610d4461497f565b610d60826020015183604001518460a001518560c00151612336565b90506000610d8183602001518460400151856060015186608001518661239a565b509a8b9a5060009950975050505050505050565b600280541415610db75760405162461bcd60e51b81526004016104fe906155f3565b60028055610dc3612507565b80610dcd816121e8565b610dd833848461251b565b5050600160025550565b6001546001600160a01b03163314610e0c5760405162461bcd60e51b81526004016104fe906156a8565b600154600080546040516001600160a01b0393841693909116917f343765429aea5a34b3ff6a3785a98a5abb2597aca87bfbb58632c173d585373a91a360018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b610e7861122a565b81610e8281611b59565b610e8b81611c14565b506001600160a01b03909116600090815260036020526040902055565b610eb061122a565b7f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b03166379ba50976040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561085057600080fd5b6000546001600160a01b031690565b7f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb5590565b6001600160a01b031660009081526003602052604090205490565b610f6161122a565b60405163f2fde38b60e01b81526001600160a01b037f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b55169063f2fde38b90610fad908490600401614f9e565b600060405180830381600087803b158015610fc757600080fd5b505af11580156108e0573d6000803e3d6000fd5b610fe361122a565b60405163f2fde38b60e01b81526001600160a01b037f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb55169063f2fde38b90610fad908490600401614f9e565b60006002805414156110535760405162461bcd60e51b81526004016104fe906155f3565b60028055846110618161215d565b8461106b81611b59565b61107481611c14565b85856110808282612942565b8561108a81612a0f565b6110968a8a8a8a612a2f565b60016002559a9950505050505050505050565b6001546001600160a01b031690565b7f0000000000000000000000009712bb50dc6efb8a3d7d12cea500a50967d2d47190565b60006110e661491f565b6110ef83612223565b90506110f961497f565b611115826020015183604001518460a001518560c00151612336565b9050600061113683602001518460400151856060015186608001518661239a565b9695505050505050565b7f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf4690565b60006002805414156111885760405162461bcd60e51b81526004016104fe906155f3565b600280558361119681611b59565b61119f81611c14565b84846111ab8282612942565b846111b581612a0f565b6111c133898989612a2f565b600160025598975050505050505050565b6111da61122a565b6000546001600160a01b03828116911614156112085760405162461bcd60e51b81526004016104fe9061517b565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000546001600160a01b031633146112545760405162461bcd60e51b81526004016104fe906156a8565b565b6000816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561129157600080fd5b505afa1580156112a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112c99190614aa5565b92915050565b600080836001600160a01b03166319b6401560006040518263ffffffff1660e01b81526004016112ff91906150db565b60206040518083038186803b15801561131757600080fd5b505afa15801561132b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061134f9190614aa5565b9050826001600160a01b0316816001600160a01b031614156113ed576040516319b6401560e01b81526001600160a01b038516906319b6401590611398906001906004016150db565b60206040518083038186803b1580156113b057600080fd5b505afa1580156113c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113e89190614aa5565b6113ef565b805b949350505050565b60008061140385610f3e565b90506000846001600160a01b031663dc8de379856040518263ffffffff1660e01b81526004016114339190614f9e565b60206040518083038186803b15801561144b57600080fd5b505afa15801561145f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114839190614d85565b90506000866001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156114c057600080fd5b505afa1580156114d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114f89190614d85565b9050600061150782858561214d565b90506000886001600160a01b03166370a082317f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b556040518263ffffffff1660e01b81526004016115579190614f9e565b60206040518083038186803b15801561156f57600080fd5b505afa158015611583573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115a79190614d85565b90508082106115be57600095505050505050611680565b60006115ca8284612a8e565b905060007f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b0316635121220c8c6040518263ffffffff1660e01b815260040161161a9190614f9e565b60206040518083038186803b15801561163257600080fd5b505afa158015611646573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061166a9190614d85565b90506116768282612ab6565b9750505050505050505b9392505050565b604051631448488360e21b81526000906001600160a01b037f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871311690635121220c906116d6908690600401614f9e565b60206040518083038186803b1580156116ee57600080fd5b505afa158015611702573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117269190614d85565b90507f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b03166319c6a5e4846117628585612ab6565b6040518363ffffffff1660e01b815260040161177f929190614fb2565b600060405180830381600087803b15801561179957600080fd5b505af11580156117ad573d6000803e3d6000fd5b5050604051632f1a9acf60e11b81526001600160a01b037f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b55169250635e35359e915061180190869030908790600401615006565b600060405180830381600087803b15801561181b57600080fd5b505af115801561182f573d6000803e3d6000fd5b50505050505050565b60408051600280825260608281019093528291829181602001602082028036833750506040805160028082526060808301845294955090925090602083019080368337019050509050858260008151811061188f57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505084826001815181106118bd57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250506001816000815181106118ec57fe5b60200260200101818152505060018160018151811061190757fe5b602090810291909101015290925090505b9250929050565b604051634017d1dd60e11b81526001600160a01b037f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b87131169063802fa3ba9061196d9085908590600401614fb2565b600060405180830381600087803b15801561198757600080fd5b505af115801561199b573d6000803e3d6000fd5b5050604051630852cd8d60e31b81526001600160a01b037f000000000000000000000000a489c2b5b36835a327851ab917a80562b5afc2441692506342966c6891506119eb9084906004016150db565b600060405180830381600087803b158015611a0557600080fd5b505af1158015611a19573d6000803e3d6000fd5b505050505050565b6001600160a01b03811673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14919050565b80471015611a665760405162461bcd60e51b81526004016104fe906152a5565b6000826001600160a01b031682604051611a7f90612acc565b60006040518083038185875af1925050503d8060008114611abc576040519150601f19603f3d011682016040523d82523d6000602084013e611ac1565b606091505b5050905080611ae25760405162461bcd60e51b81526004016104fe90615248565b505050565b80611af157611ae2565b611afa83611a21565b15611b3b576040516001600160a01b0383169082156108fc029083906000818181858888f19350505050158015611b35573d6000803e3d6000fd5b50611ae2565b611ae28282611b4986612acc565b6001600160a01b03169190612acf565b604051631a9ec62960e31b81526001600160a01b037f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf46169063d4f6314890611ba5908490600401614f9e565b60206040518083038186803b158015611bbd57600080fd5b505afa158015611bd1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bf59190614ce6565b611c115760405162461bcd60e51b81526004016104fe9061547a565b50565b60405163159354c160e11b81526001600160a01b037f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf461690632b26a98290611c60908490600401614f9e565b60206040518083038186803b158015611c7857600080fd5b505afa158015611c8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cb09190614ce6565b611c115760405162461bcd60e51b81526004016104fe906151a3565b600080611cd883611256565b90507f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c6000611d0783836112cf565b9050600080611d17858486612b25565b915091506000611d2688612c20565b905060007f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b031663350ed8e78a6040518263ffffffff1660e01b8152600401611d769190614f9e565b60206040518083038186803b158015611d8e57600080fd5b505afa158015611da2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dc69190614d85565b9050600081611dd58484612d5f565b039050611de381868661214d565b9a9950505050505050505050565b6000817f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c611e1d614905565b611e278383612d6f565b9050611efa8160200151611ef46001611eee8560000151611ee887600001517f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b0316635121220c8c6040518263ffffffff1660e01b8152600401611e929190614f9e565b60206040518083038186803b158015611eaa57600080fd5b505afa158015611ebe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ee29190614d85565b90612eab565b906121c3565b90612a8e565b90612ee5565b95945050505050565b611f0b614905565b6000611f1683611256565b90506000611f44827f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c6112cf565b90506000826001600160a01b031663dc8de379836040518263ffffffff1660e01b8152600401611f749190614f9e565b60206040518083038186803b158015611f8c57600080fd5b505afa158015611fa0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fc49190614d85565b90506000856001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561200157600080fd5b505afa158015612015573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120399190614d85565b90506000866001600160a01b03166370a082317f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b556040518263ffffffff1660e01b81526004016120899190614f9e565b60206040518083038186803b1580156120a157600080fd5b505afa1580156120b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120d99190614d85565b905060006120e884838561214d565b905060006120f589610f3e565b90508082106121235760405180604001604052806000815260200160018152509750505050505050506107cd565b60408051808201909152806121388385612a8e565b81526020019190915298975050505050505050565b60006113ef82611ef48686612eab565b6001600160a01b038116611c115760405162461bcd60e51b81526004016104fe906154aa565b600061218d61491f565b61219b8585620f4240612f17565b9050611efa8382602001518360400151846060015185608001518660e00151613151565b4290565b6000828201838110156113ed5760405162461bcd60e51b81526004016104fe90615144565b60008163ffffffff161180156122075750620f424063ffffffff821611155b611c115760405162461bcd60e51b81526004016104fe906155c6565b61222b61491f565b61223361491f565b604051635290ffbb60e01b81526001600160a01b037f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb551690635290ffbb9061227f9086906004016150db565b6101006040518083038186803b15801561229857600080fd5b505afa1580156122ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122d09190614b11565b60e089015260c088015260a0870152608086015260608501526001600160a01b039081166040850152908116602084015216815292915050565b60008061231b8584620f424061214d565b6123298585620f424061214d565b915091505b935093915050565b61233e61497f565b612346614905565b61234e614905565b6123588787613372565b915091506001600160801b038160200151826000015184602001518560000151171717111561238357fe5b61238f85858484613463565b979650505050505050565b60008060006123a7614905565b604051806040016040528086600001516001600160801b0316815260200186602001516001600160801b031681525090506123e0614905565b50604080518082018252908601516001600160801b039081168252606087015116602082015261240e614905565b6124188b8b612d6f565b9050612426898285856134ce565b93506124318a6135d3565b1561244557838495509550505050506124fd565b61244d614905565b604051806040016040528089608001516001600160801b031681526020018960a001516001600160801b03168152509050612486614905565b6124908583613605565b90506124a561249f8b88612d5f565b826136d3565b95506124af614905565b6124b88e611f03565b90506124c2614905565b50604080518082019091528151602080840180519290920380845291519083018190526124f0918a9161214d565b9950969750505050505050505b9550959350505050565b600454610100900460ff1661125457600080fd5b63ffffffff8116620f4240146125435760405162461bcd60e51b81526004016104fe90615322565b61254b61491f565b612556848484612f17565b60208101516060820151604051631990807d60e11b81529293506001600160a01b037f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b87131169263332100fa926125af929091600401614fb2565b600060405180830381600087803b1580156125c957600080fd5b505af11580156125dd573d6000803e3d6000fd5b505050506125ee81604001516135d3565b156126b3576080810151612630906001600160a01b037f00000000000000000000000048fb253446873234f2febbf9bdeaa72d9d387f9416908690309061371b565b6080810151604051630852cd8d60e31b81526001600160a01b037f0000000000000000000000000887ae1251e180d7d453aedebee26e1639f2011316916342966c689161268091906004016150db565b600060405180830381600087803b15801561269a57600080fd5b505af11580156126ae573d6000803e3d6000fd5b505050505b6126bb61497f565b6126d7826020015183604001518460a001518560c00151612336565b905061271981604001516001600160801b031682606001516001600160801b031683608001516001600160801b03168460a001516001600160801b031661373c565b60008061273984602001518560400151866060015187608001518761239a565b9150915061274a84604001516135d3565b156127915761277e7f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b5585602001518461385f565b612788878361392d565b50505050611ae2565b612799614905565b6127ab85602001518660400151612d6f565b905060006127be84838860200151613a70565b90506127ce866020015182611687565b61280286602001518288604001517f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c613b3d565b6020808701516001600160a01b031660009081526003909152604090205461282e84611eee8382612d5f565b6020808901516001600160a01b0390811660009081526003909252604080832093909355918901519091612863911630613be2565b604089015190915061287f906001600160a01b03168c83611ae7565b6040516370a0823160e01b81526000906001600160a01b037f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c16906370a08231906128ce903090600401614f9e565b60206040518083038186803b1580156128e657600080fd5b505afa1580156128fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061291e9190614d85565b905080156129345761293489602001518261191f565b505050505050505050505050565b60045460ff1680156129ef5750604051634f2b8d3560e11b81526001600160a01b037f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf461690639e571a6a9061299d9085908590600401614fec565b60206040518083038186803b1580156129b557600080fd5b505afa1580156129c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129ed9190614ce6565b155b612a0b5760405162461bcd60e51b81526004016104fe906151da565b5050565b60008111611c115760405162461bcd60e51b81526004016104fe906153c0565b6000612a3a836135d3565b15612a5b57612a496000613c87565b612a54858584613ca6565b90506113ef565b612a82612a70846001600160a01b0316611a21565b612a7b576000612a7d565b825b613c87565b611efa85858585613e5b565b600082821115612ab05760405162461bcd60e51b81526004016104fe90615211565b50900390565b6000818310612ac55781611680565b5090919050565b90565b611ae28363a9059cbb60e01b8484604051602401612aee929190614fb2565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526141ff565b600080846001600160a01b031663d8959512856040518263ffffffff1660e01b8152600401612b549190614f9e565b60206040518083038186803b158015612b6c57600080fd5b505afa158015612b80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ba49190614d85565b604051636c4aca8960e11b81526001600160a01b0387169063d895951290612bd0908790600401614f9e565b60206040518083038186803b158015612be857600080fd5b505afa158015612bfc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123299190614d85565b6000807f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b031663943fd08a846040518263ffffffff1660e01b8152600401612c6f9190614f9e565b60206040518083038186803b158015612c8757600080fd5b505afa158015612c9b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cbf9190614d85565b9050600081116112c9577f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b031663b97b55ce6040518163ffffffff1660e01b815260040160206040518083038186803b158015612d2257600080fd5b505afa158015612d36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d5a9190614d85565b6107c9565b600081831015612ac55781611680565b612d77614905565b6000836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015612db257600080fd5b505afa158015612dc6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dea9190614d85565b90506000612df785611256565b90506000816001600160a01b031663d8959512866040518263ffffffff1660e01b8152600401612e279190614f9e565b60206040518083038186803b158015612e3f57600080fd5b505afa158015612e53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e779190614d85565b90506040518060400160405280612e98600284612eab90919063ffffffff16565b8152602001939093525090949350505050565b600082612eba575060006112c9565b82820282848281612ec757fe5b04146113ed5760405162461bcd60e51b81526004016104fe90615439565b6000808211612f065760405162461bcd60e51b81526004016104fe90615389565b818381612f0f57fe5b049392505050565b612f1f61491f565b612f2761491f565b612f31848661428e565b9050612f408160200151611c14565b612f486121bf565b8160e0015110612f6a5760405162461bcd60e51b81526004016104fe906153e8565b63ffffffff8316620f42401415612ffe57604051636f366b7160e01b81526001600160a01b037f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb551690636f366b7190612fc79087906004016150db565b600060405180830381600087803b158015612fe157600080fd5b505af1158015612ff5573d6000803e3d6000fd5b505050506130ae565b60608101516080820151613019828263ffffffff881661230a565b608085018190526060850182905260405163161139bd60e31b81526001600160a01b037f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb55169263b089cde892613079928b92880391870390600401615782565b600060405180830381600087803b15801561309357600080fd5b505af11580156130a7573d6000803e3d6000fd5b5050505050505b7f0000000000000000000000009712bb50dc6efb8a3d7d12cea500a50967d2d4716001600160a01b03166327396b6d826000015183602001518460400151856060015186608001516040518663ffffffff1660e01b815260040161311695949392919061502a565b600060405180830381600087803b15801561313057600080fd5b505af1158015613144573d6000803e3d6000fd5b5092979650505050505050565b600061315b614905565b613163614905565b61316d8888613372565b9150915061318d826000015183602001518360000151846020015161373c565b604051630aa558ef60e41b81526001600160a01b037f0000000000000000000000009712bb50dc6efb8a3d7d12cea500a50967d2d471169063aa558ef0906131e1908c908c908c908c908c9060040161502a565b600060405180830381600087803b1580156131fb57600080fd5b505af115801561320f573d6000803e3d6000fd5b5050604051637ea5e0f360e11b81526001600160a01b037f0000000000000000000000009712bb50dc6efb8a3d7d12cea500a50967d2d47116925063fd4bc1e69150613261908c908c90600401614fec565b602060405180830381600087803b15801561327b57600080fd5b505af115801561328f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132b39190614ce6565b50815160208301516040516361d5f08760e01b81526001600160a01b037f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb5516926361d5f08792613313928e928e928e928e928e9290918e9060040161505e565b602060405180830381600087803b15801561332d57600080fd5b505af1158015613341573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133659190614d85565b9998505050505050505050565b61337a614905565b613382614905565b600061338d85611256565b9050600061339b82866112cf565b90506000806133ab848489612b25565b91509150600080856001600160a01b0316631f0181bc8a6040518263ffffffff1660e01b81526004016133de9190614f9e565b604080518083038186803b1580156133f557600080fd5b505afa158015613409573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061342d9190614e87565b60408051808201825296875260208088019690965280518082019091529182529381019390935250919890975095505050505050565b61346b61497f565b6001600160801b03848617111561347e57fe5b506040805160c0810182526001600160801b03958616815293851660208086019190915283518616918501919091529182015184166060840152805184166080840152015190911660a082015290565b82518151602084015160009283926134f392611ee2916134ee9190612eab565b6142de565b905060006135228660200151611ee261351d87602001518960000151612eab90919063ffffffff16565b6142fe565b90508187028783828161353157fe5b04141561354c5781818161354157fe5b0493505050506113ef565b60008089851161355d578985613560565b848a5b9150915060008061357d8487856000198161357757fe5b04614354565b9150915060006135978488878161359057fe5b0490612eab565b905081156135c3576135b48183868602816135ae57fe5b04612d5f565b985050505050505050506113ef565b9c9b505050505050505050505050565b7f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c6001600160a01b0390811691161490565b61360d614905565b602083015182516000916136219190612eab565b8451602085015191925060009161363791612eab565b905081810260008284838161364857fe5b041461366657613657836142fe565b613660856142fe565b0261366f565b61366f826142fe565b9050600061367d85856121c3565b9050600281066136b0576002810490506040518060400160405280838303815260200182815250955050505050506112c9565b604080518082019091526002909202810382526020820152935050505092915050565b6000806136e1600185612d5f565b90506000806136fe85600001518660200151856000198161357757fe5b909250905061113681611ef46137148286612a8e565b8990612eab565b610864846323b872dd60e01b858585604051602401612aee93929190615006565b60007f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b03166324a088686040518163ffffffff1660e01b815260040160206040518083038186803b15801561379757600080fd5b505afa1580156137ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137cf9190614ef4565b620f42400363ffffffff16905060006137f982611ee284611ee2878b612eab90919063ffffffff16565b90506000613810620f4240611ee285818a8a612eab565b90506000613827620f4240611ee281818c8a612eab565b90508183111580156138395750808211155b6138555760405162461bcd60e51b81526004016104fe9061540f565b5050505050505050565b604051636f566c2760e11b81526001600160a01b037f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b87131169063deacd84e906138ad9085908590600401614fb2565b600060405180830381600087803b1580156138c757600080fd5b505af11580156138db573d6000803e3d6000fd5b50506040516340c10f1960e01b81526001600160a01b037f000000000000000000000000a489c2b5b36835a327851ab917a80562b5afc2441692506340c10f1991506118019086908590600401614fb2565b60006139cb7f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b031663045544436040518163ffffffff1660e01b815260040160206040518083038186803b15801561398b57600080fd5b505afa15801561399f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139c39190614d85565b611ee86121bf565b60405163dbae3a5d60e01b81529091506001600160a01b037f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb55169063dbae3a5d90613a1e90869086908690600401614fcb565b602060405180830381600087803b158015613a3857600080fd5b505af1158015613a4c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108649190614d85565b600080613a9685613a8f60028760200151612eab90919063ffffffff16565b865161214d565b9050611efa81846001600160a01b03166370a082317f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b556040518263ffffffff1660e01b8152600401613ae89190614f9e565b60206040518083038186803b158015613b0057600080fd5b505afa158015613b14573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b389190614d85565b612ab6565b6000613b4885611256565b9050606080613b578585611838565b60405163b127c0a560e01b815291935091506001600160a01b0384169063b127c0a590613b8c90899086908690600401615749565b600060405180830381600087803b158015613ba657600080fd5b505af1158015613bba573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526138559190810190614c36565b6000613bed83611a21565b15613c0357506001600160a01b038116316112c9565b613c0c83612acc565b6001600160a01b03166370a08231836040518263ffffffff1660e01b8152600401613c379190614f9e565b60206040518083038186803b158015613c4f57600080fd5b505afa158015613c63573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116809190614d85565b803414611c115760405162461bcd60e51b81526004016104fe906154d7565b6000827f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c613cd2614905565b613cdc8383612d6f565b90506000613cf3868360200151846000015161214d565b604051630671a97960e21b81529091506001600160a01b037f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b8713116906319c6a5e490613d449087908590600401614fb2565b600060405180830381600087803b158015613d5e57600080fd5b505af1158015613d72573d6000803e3d6000fd5b505050506000613d8d898686858b613d886121bf565b613151565b9050613dc46001600160a01b037f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c1633308a61371b565b613dce888861191f565b6040516340c10f1960e01b81526001600160a01b037f0000000000000000000000000887ae1251e180d7d453aedebee26e1639f2011316906340c10f1990613e1c908c908b90600401614fb2565b600060405180830381600087803b158015613e3657600080fd5b505af1158015613e4a573d6000803e3d6000fd5b50929b9a5050505050505050505050565b6000837f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c82613e8983611256565b9050600080613e99838986612b25565b915091507f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b03166312588d0e6040518163ffffffff1660e01b815260040160206040518083038186803b158015613ef657600080fd5b505afa158015613f0a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f2e9190614d85565b811015613f4d5760405162461bcd60e51b81526004016104fe9061550e565b6000613f5a88838561214d565b90506000613f678b612c20565b9050600061400b837f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b031663350ed8e78f6040518263ffffffff1660e01b8152600401613fbb9190614f9e565b60206040518083038186803b158015613fd357600080fd5b505afa158015613fe7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ee89190614d85565b90508181111561402d5760405162461bcd60e51b81526004016104fe90615359565b614038308d8561385f565b61404c6001600160a01b038816878561439c565b61405e8b6001600160a01b0316611a21565b61408b576140776001600160a01b038c1633308d6143cd565b61408b6001600160a01b038c16878c61439c565b614099868c898d8734614408565b6040516370a0823160e01b81526000906001600160a01b038a16906370a08231906140c8903090600401614f9e565b60206040518083038186803b1580156140e057600080fd5b505afa1580156140f4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141189190614d85565b905061414e6001600160a01b038a167f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b5583612acf565b604051631990807d60e11b81527f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b03169063332100fa906141a1908c9060028604860390600401614fb2565b600060405180830381600087803b1580156141bb57600080fd5b505af11580156141cf573d6000803e3d6000fd5b505050506141ed8e8a8e600285816141e357fe5b048f613d886121bf565b9e9d5050505050505050505050505050565b6060614254826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166145489092919063ffffffff16565b805190915015611ae257808060200190518101906142729190614ce6565b611ae25760405162461bcd60e51b81526004016104fe9061557c565b61429661491f565b61429e61491f565b6142a784612223565b9050826001600160a01b031681600001516001600160a01b0316146113ed5760405162461bcd60e51b81526004016104fe906156a8565b6000806142ea836142fe565b905082818202146112c957806001016107c9565b6000806002830460010190506000600282858161431757fe5b0483018161432157fe5b0490505b8082111561434d57809150600282858161433b57fe5b0483018161434557fe5b049050614325565b5092915050565b60008084848482118061436657508481115b1561437c57614376828287614557565b90925090505b80821461438d57909250905061232e565b50600196879650945050505050565b6143a583611a21565b156143af57611ae2565b611ae282826143bd86612acc565b6001600160a01b03169190614590565b8015806143de57506143de84611a21565b156143e857610864565b6108648383836143f788612acc565b6001600160a01b031692919061371b565b604080516002808252606080830184529260208301908036833750506040805160028082526060808301845294955090925090602083019080368337019050509050868260008151811061445857fe5b60200260200101906001600160a01b031690816001600160a01b031681525050858260018151811061448657fe5b60200260200101906001600160a01b031690816001600160a01b03168152505084816000815181106144b457fe5b60200260200101818152505083816001815181106144ce57fe5b6020908102919091010152604051637d8916bd60e01b81526001600160a01b03891690637d8916bd90859061450c90869086906001906004016150a5565b6000604051808303818588803b15801561452557600080fd5b505af1158015614539573d6000803e3d6000fd5b50505050505050505050505050565b60606113ef8484600085614659565b6000808385116145755761456c85858561470f565b9150915061232e565b60008061458386888761470f565b9890975095505050505050565b8061459a57611ae2565b604051636eb1769f60e11b81526000906001600160a01b0385169063dd62ed3e906145cb9030908790600401614fec565b60206040518083038186803b1580156145e357600080fd5b505afa1580156145f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061461b9190614d85565b905081811061462a5750611ae2565b8015614645576146456001600160a01b0385168460006147d3565b6108646001600160a01b03851684846147d3565b60608247101561467b5760405162461bcd60e51b81526004016104fe906152dc565b61468485614896565b6146a05760405162461bcd60e51b81526004016104fe90615545565b60006060866001600160a01b031685876040516146bd9190614f82565b60006040518083038185875af1925050503d80600081146146fa576040519150601f19603f3d011682016040523d82523d6000602084013e6146ff565b606091505b509150915061238f82828661489c565b6000806000836000198161471f57fe5b0490508086111561475857600081600101878161473857fe5b04600101905080878161474757fe5b04965080868161475357fe5b049550505b8486146147c357858402600061476e88886148d5565b905087811061479457600061478383836148d9565b95505050838503925061232e915050565b60028888030487038210156147b2576000869450945050505061232e565b60018087039450945050505061232e565b5050600290910493849350915050565b80158061485b5750604051636eb1769f60e11b81526001600160a01b0384169063dd62ed3e906148099030908690600401614fec565b60206040518083038186803b15801561482157600080fd5b505afa158015614835573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148599190614d85565b155b6148775760405162461bcd60e51b81526004016104fe9061562a565b611ae28363095ea7b360e01b8484604051602401612aee929190614fb2565b3b151590565b606083156148ab575081611680565b8251156148bb5782518084602001fd5b8160405162461bcd60e51b81526004016104fe91906150e4565b0190565b60006002820482038284816148ea57fe5b06816148f257fe5b048284816148fc57fe5b04019392505050565b604051806040016040528060008152602001600081525090565b60405180610100016040528060006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915290565b60008083601f8401126149c5578182fd5b50813567ffffffffffffffff8111156149dc578182fd5b602083019150836020808302850101111561191857600080fd5b600082601f830112614a06578081fd5b815167ffffffffffffffff80821115614a1d578283fd5b602080830260405182828201018181108582111715614a3a578687fd5b604052848152945081850192508582018187018301881015614a5b57600080fd5b600091505b84821015614a7e578051845292820192600191909101908201614a60565b505050505092915050565b600060208284031215614a9a578081fd5b81356113ed816157c4565b600060208284031215614ab6578081fd5b81516113ed816157c4565b60008060008060808587031215614ad6578283fd5b8435614ae1816157c4565b93506020850135614af1816157c4565b92506040850135614b01816157c4565b9396929550929360600135925050565b600080600080600080600080610100898b031215614b2d578384fd5b8851614b38816157c4565b60208a0151909850614b49816157c4565b60408a0151909750614b5a816157c4565b60608a015160808b015160a08c015160c08d015160e0909d01519b9e9a9d50929b919a9099929850909650945092505050565b60008060208385031215614b9f578182fd5b823567ffffffffffffffff811115614bb5578283fd5b614bc1858286016149b4565b90969095509350505050565b60008060008060408587031215614be2578384fd5b843567ffffffffffffffff80821115614bf9578586fd5b614c05888389016149b4565b90965094506020870135915080821115614c1d578384fd5b50614c2a878288016149b4565b95989497509550505050565b600060208284031215614c47578081fd5b815167ffffffffffffffff811115614c5d578182fd5b6113ef848285016149f6565b60008060408385031215614c7b578182fd5b825167ffffffffffffffff80821115614c92578384fd5b614c9e868387016149f6565b93506020850151915080821115614cb3578283fd5b50614cc0858286016149f6565b9150509250929050565b600060208284031215614cdb578081fd5b81356113ed816157d9565b600060208284031215614cf7578081fd5b81516113ed816157d9565b600080600060608486031215614d16578081fd5b8335614d21816157c4565b92506020840135614d31816157c4565b929592945050506040919091013590565b60008060408385031215614d54578182fd5b8235614d5f816157c4565b946020939093013593505050565b600060208284031215614d7e578081fd5b5035919050565b600060208284031215614d96578081fd5b5051919050565b60008060408385031215614daf578182fd5b823591506020830135614dc1816157c4565b809150509250929050565b600080600080600060808688031215614de3578283fd5b853594506020860135614df5816157c4565b93506040860135614e05816157c4565b9250606086013567ffffffffffffffff80821115614e21578283fd5b818801915088601f830112614e34578283fd5b813581811115614e42578384fd5b896020828501011115614e53578384fd5b9699959850939650602001949392505050565b60008060408385031215614e78578182fd5b50508035926020909101359150565b60008060408385031215614e99578182fd5b505080516020909101519092909150565b60008060408385031215614ebc578182fd5b823591506020830135614dc1816157e7565b600080600060608486031215614ee2578081fd5b833592506020840135614d31816157e7565b600060208284031215614f05578081fd5b81516113ed816157e7565b6000815180845260208085019450808401835b83811015614f485781516001600160a01b031687529582019590820190600101614f23565b509495945050505050565b6000815180845260208085019450808401835b83811015614f4857815187529582019590820190600101614f66565b60008251614f94818460208701615798565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b039390931683526020830191909152604082015260600190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03958616815293851660208501529190931660408301526060820192909252608081019190915260a00190565b6001600160a01b03988916815296881660208801529490961660408601526060850192909252608084015260a083015260c082019290925260e08101919091526101000190565b6000606082526150b86060830186614f10565b82810360208401526150ca8186614f53565b915050826040830152949350505050565b90815260200190565b6000602082528251806020840152615103816040850160208701615798565b601f01601f19169190910160400192915050565b60208082526013908201527208aa4a4be988a9c8ea890be9a92a69a82a8869606b1b604082015260600190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b6020808252600e908201526d22a9292fa9a0a6a2afa7aba722a960911b604082015260600190565b60208082526018908201527f4552525f504f4f4c5f4e4f545f57484954454c49535445440000000000000000604082015260600190565b6020808252601a908201527f4552525f4144445f4c49515549444954595f44495341424c4544000000000000604082015260600190565b6020808252601e908201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604082015260600190565b6020808252603a908201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260408201527f6563697069656e74206d61792068617665207265766572746564000000000000606082015260800190565b6020808252601d908201527f416464726573733a20696e73756666696369656e742062616c616e6365000000604082015260600190565b60208082526026908201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6040820152651c8818d85b1b60d21b606082015260800190565b60208082526019908201527f4552525f504f5254494f4e5f4e4f545f535550504f5254454400000000000000604082015260600190565b60208082526016908201527511549497d3505617d05353d5539517d4915050d2115160521b604082015260600190565b6020808252601a908201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604082015260600190565b6020808252600e908201526d4552525f5a45524f5f56414c554560901b604082015260600190565b6020808252600d908201526c4552525f544f4f5f4541524c5960981b604082015260600190565b60208082526010908201526f4552525f494e56414c49445f5241544560801b604082015260600190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b60208082526016908201527511549497d413d3d317d393d517d4d5541413d495115160521b604082015260600190565b6020808252601390820152724552525f494e56414c49445f4144445245535360681b604082015260600190565b60208082526017908201527f4552525f4554485f414d4f554e545f4d49534d41544348000000000000000000604082015260600190565b60208082526018908201527f4552525f4e4f545f454e4f5547485f4c49515549444954590000000000000000604082015260600190565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b60208082526013908201527222a9292fa4a72b20a624a22fa827a92a24a7a760691b604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60208082526036908201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60408201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b606082015260800190565b6020808252600e908201526d11549497d253959053125117d25160921b604082015260600190565b60208082526011908201527011549497d050d0d154d4d7d11153925151607a1b604082015260600190565b60208082526015908201527404552525f494e56414c49445f54494d455354414d5605c1b604082015260600190565b8481526001600160a01b03841660208201526060604082018190528101829052600082846080840137818301608090810191909152601f909201601f191601019392505050565b6000848252606060208301526157626060830185614f10565b82810360408401526111368185614f53565b918252602082015260400190565b9283526020830191909152604082015260600190565b60005b838110156157b357818101518382015260200161579b565b838111156108645750506000910152565b6001600160a01b0381168114611c1157600080fd5b8015158114611c1157600080fd5b63ffffffff81168114611c1157600080fdfea164736f6c634300060c000a
Verified Source Code Partial Match
Compiler: v0.6.12+commit.27d51765
EVM: istanbul
Optimization: Yes (200 runs)
ITransferPositionCallback.sol 13 lines
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.6.12;
/**
* @dev Transfer position event callback interface
*/
interface ITransferPositionCallback {
function onTransferPosition(
uint256 newId,
address provider,
bytes calldata data
) external;
}
ITokenGovernance.sol 23 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.12;
import "./IMintableToken.sol";
/// @title The interface for mintable/burnable token governance.
interface ITokenGovernance {
// The address of the mintable ERC20 token.
function token() external view returns (IMintableToken);
/// @dev Mints new tokens.
///
/// @param to Account to receive the new amount.
/// @param amount Amount to increase the supply by.
///
function mint(address to, uint256 amount) external;
/// @dev Burns tokens from the caller.
///
/// @param amount Amount to decrease the supply by.
///
function burn(uint256 amount) external;
}
ILiquidityProtectionStore.sol 75 lines
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.6.12;
import "../../converter/interfaces/IConverterAnchor.sol";
import "../../token/interfaces/IDSToken.sol";
import "../../token/interfaces/IReserveToken.sol";
import "../../utility/interfaces/IOwned.sol";
/**
* @dev Liquidity Protection Store interface
*/
interface ILiquidityProtectionStore is IOwned {
function withdrawTokens(
IReserveToken token,
address recipient,
uint256 amount
) external;
function protectedLiquidity(uint256 id)
external
view
returns (
address,
IDSToken,
IReserveToken,
uint256,
uint256,
uint256,
uint256,
uint256
);
function addProtectedLiquidity(
address provider,
IDSToken poolToken,
IReserveToken reserveToken,
uint256 poolAmount,
uint256 reserveAmount,
uint256 reserveRateN,
uint256 reserveRateD,
uint256 timestamp
) external returns (uint256);
function updateProtectedLiquidityAmounts(
uint256 id,
uint256 poolNewAmount,
uint256 reserveNewAmount
) external;
function removeProtectedLiquidity(uint256 id) external;
function lockedBalance(address provider, uint256 index) external view returns (uint256, uint256);
function lockedBalanceRange(
address provider,
uint256 startIndex,
uint256 endIndex
) external view returns (uint256[] memory, uint256[] memory);
function addLockedBalance(
address provider,
uint256 reserveAmount,
uint256 expirationTime
) external returns (uint256);
function removeLockedBalance(address provider, uint256 index) external;
function systemBalance(IReserveToken poolToken) external view returns (uint256);
function incSystemBalance(IReserveToken poolToken, uint256 poolAmount) external;
function decSystemBalance(IReserveToken poolToken, uint256 poolAmount) external;
}
LiquidityProtection.sol 1405 lines
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts/math/Math.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@bancor/token-governance/contracts/ITokenGovernance.sol";
import "../utility/MathEx.sol";
import "../utility/Types.sol";
import "../utility/Time.sol";
import "../utility/Utils.sol";
import "../utility/Owned.sol";
import "../token/interfaces/IDSToken.sol";
import "../token/ReserveToken.sol";
import "../converter/interfaces/IConverterAnchor.sol";
import "../converter/interfaces/IConverter.sol";
import "../converter/interfaces/IConverterRegistry.sol";
import "./interfaces/ILiquidityProtection.sol";
interface ILiquidityPoolConverter is IConverter {
function addLiquidity(
IReserveToken[] memory reserveTokens,
uint256[] memory reserveAmounts,
uint256 minReturn
) external payable;
function removeLiquidity(
uint256 amount,
IReserveToken[] memory reserveTokens,
uint256[] memory reserveMinReturnAmounts
) external returns (uint256[] memory);
function recentAverageRate(IReserveToken reserveToken) external view returns (uint256, uint256);
}
/**
* @dev This contract implements the liquidity protection mechanism.
*/
contract LiquidityProtection is ILiquidityProtection, Utils, Owned, ReentrancyGuard, Time {
using Math for uint256;
using SafeMath for uint256;
using ReserveToken for IReserveToken;
using SafeERC20 for IERC20;
using SafeERC20 for IDSToken;
using SafeERC20Ex for IERC20;
using Address for address payable;
struct Position {
address provider; // liquidity provider
IDSToken poolToken; // pool token address
IReserveToken reserveToken; // reserve token address
uint256 poolAmount; // pool token amount
uint256 reserveAmount; // reserve token amount
uint256 reserveRateN; // rate of 1 protected reserve token in units of the other reserve token (numerator)
uint256 reserveRateD; // rate of 1 protected reserve token in units of the other reserve token (denominator)
uint256 timestamp; // timestamp
}
// various rates between the two reserve tokens. the rate is of 1 unit of the protected reserve token in units of the other reserve token
struct PackedRates {
uint128 addSpotRateN; // spot rate of 1 A in units of B when liquidity was added (numerator)
uint128 addSpotRateD; // spot rate of 1 A in units of B when liquidity was added (denominator)
uint128 removeSpotRateN; // spot rate of 1 A in units of B when liquidity is removed (numerator)
uint128 removeSpotRateD; // spot rate of 1 A in units of B when liquidity is removed (denominator)
uint128 removeAverageRateN; // average rate of 1 A in units of B when liquidity is removed (numerator)
uint128 removeAverageRateD; // average rate of 1 A in units of B when liquidity is removed (denominator)
}
uint256 internal constant MAX_UINT128 = 2**128 - 1;
uint256 internal constant MAX_UINT256 = uint256(-1);
address payable private immutable _vaultV3;
ILiquidityProtectionSettings private immutable _settings;
ILiquidityProtectionStore private immutable _store;
ILiquidityProtectionStats private immutable _stats;
ILiquidityProtectionSystemStore private immutable _systemStore;
ITokenHolder private immutable _wallet;
IERC20 private immutable _networkToken;
ITokenGovernance private immutable _networkTokenGovernance;
IERC20 private immutable _govToken;
ITokenGovernance private immutable _govTokenGovernance;
/**
* @dev maps a pool anchor to the total value of its positions
* if this value is greater than the total protected liquidity,
* the pool is in deficit, and withdrawing from this pool will
* be decreased by an amount proportional to the deficit
* the value is expected to be set manually
*/
mapping(IConverterAnchor => uint256) private _totalPositionsValue;
bool private _addingEnabled = false;
bool private _removingEnabled = false;
/**
* @dev initializes a new LiquidityProtection contract
*/
constructor(
address payable vaultV3,
ILiquidityProtectionSettings settings,
ILiquidityProtectionStore store,
ILiquidityProtectionStats stats,
ILiquidityProtectionSystemStore systemStore,
ITokenHolder wallet,
ITokenGovernance networkTokenGovernance,
ITokenGovernance govTokenGovernance
) public {
_validAddress(address(vaultV3));
_validAddress(address(settings));
_validAddress(address(store));
_validAddress(address(stats));
_validAddress(address(systemStore));
_validAddress(address(wallet));
_vaultV3 = vaultV3;
_settings = settings;
_store = store;
_stats = stats;
_systemStore = systemStore;
_wallet = wallet;
_networkTokenGovernance = networkTokenGovernance;
_govTokenGovernance = govTokenGovernance;
_networkToken = networkTokenGovernance.token();
_govToken = govTokenGovernance.token();
}
// ensures that the pool is supported and whitelisted
modifier poolSupportedAndWhitelisted(IConverterAnchor poolAnchor) {
_poolSupported(poolAnchor);
_poolWhitelisted(poolAnchor);
_;
}
// ensures that add liquidity is enabled
modifier addLiquidityEnabled(IConverterAnchor poolAnchor, IReserveToken reserveToken) {
_addLiquidityEnabled(poolAnchor, reserveToken);
_;
}
// ensures that remove liquidity is enabled
modifier removeLiquidityEnabled() {
_removeLiquidityEnabled();
_;
}
// error message binary size optimization
function _poolSupported(IConverterAnchor poolAnchor) internal view {
require(_settings.isPoolSupported(poolAnchor), "ERR_POOL_NOT_SUPPORTED");
}
// error message binary size optimization
function _poolWhitelisted(IConverterAnchor poolAnchor) internal view {
require(_settings.isPoolWhitelisted(poolAnchor), "ERR_POOL_NOT_WHITELISTED");
}
// error message binary size optimization
function _addLiquidityEnabled(IConverterAnchor poolAnchor, IReserveToken reserveToken) internal view {
require(
_addingEnabled && !_settings.addLiquidityDisabled(poolAnchor, reserveToken),
"ERR_ADD_LIQUIDITY_DISABLED"
);
}
// error message binary size optimization
function _removeLiquidityEnabled() internal view {
require(_removingEnabled);
}
// error message binary size optimization
function _verifyEthAmount(uint256 value) internal view {
require(msg.value == value, "ERR_ETH_AMOUNT_MISMATCH");
}
/**
* @dev returns the LP store
*/
function store() external view override returns (ILiquidityProtectionStore) {
return _store;
}
/**
* @dev returns the LP stats
*/
function stats() external view override returns (ILiquidityProtectionStats) {
return _stats;
}
/**
* @dev returns the LP settings
*/
function settings() external view override returns (ILiquidityProtectionSettings) {
return _settings;
}
/**
* @dev accept ETH
*/
receive() external payable {}
/**
* @dev transfers the ownership of the store
*
* Requirements:
*
* - the caller must be the owner of the contract
*/
function transferStoreOwnership(address newOwner) external ownerOnly {
_store.transferOwnership(newOwner);
}
/**
* @dev accepts the ownership of the store
*
* Requirements:
*
* - the caller must be the owner of the contract
*/
function acceptStoreOwnership() external ownerOnly {
_store.acceptOwnership();
}
/**
* @dev transfers the ownership of the wallet
*
* Requirements:
*
* - the caller must be the owner of the contract
*/
function transferWalletOwnership(address newOwner) external ownerOnly {
_wallet.transferOwnership(newOwner);
}
/**
* @dev accepts the ownership of the wallet
*
* Requirements:
*
* - the caller must be the owner of the contract
*/
function acceptWalletOwnership() external ownerOnly {
_wallet.acceptOwnership();
}
/**
* @dev adds protected liquidity to a pool for a specific recipient, mints new governance tokens for the caller
* if the caller adds network tokens, and returns the new position id
*/
function addLiquidityFor(
address owner,
IConverterAnchor poolAnchor,
IReserveToken reserveToken,
uint256 amount
)
external
payable
override
nonReentrant
validAddress(owner)
poolSupportedAndWhitelisted(poolAnchor)
addLiquidityEnabled(poolAnchor, reserveToken)
greaterThanZero(amount)
returns (uint256)
{
return _addLiquidity(owner, poolAnchor, reserveToken, amount);
}
/**
* @dev adds protected liquidity to a pool, mints new governance tokens for the caller if the caller adds network
* tokens, and returns the new position id
*/
function addLiquidity(
IConverterAnchor poolAnchor,
IReserveToken reserveToken,
uint256 amount
)
external
payable
override
nonReentrant
poolSupportedAndWhitelisted(poolAnchor)
addLiquidityEnabled(poolAnchor, reserveToken)
greaterThanZero(amount)
returns (uint256)
{
return _addLiquidity(msg.sender, poolAnchor, reserveToken, amount);
}
/**
* @dev adds protected liquidity to a pool for a specific recipient, mints new governance tokens for the caller if
* the caller adds network tokens, and returns the new position id
*/
function _addLiquidity(
address owner,
IConverterAnchor poolAnchor,
IReserveToken reserveToken,
uint256 amount
) private returns (uint256) {
if (_isNetworkToken(reserveToken)) {
_verifyEthAmount(0);
return _addNetworkTokenLiquidity(owner, poolAnchor, amount);
}
// verify that ETH was passed with the call if needed
_verifyEthAmount(reserveToken.isNativeToken() ? amount : 0);
return _addBaseTokenLiquidity(owner, poolAnchor, reserveToken, amount);
}
/**
* @dev adds network token liquidity to a pool, mints new governance tokens for the caller, and returns the new ]
* position id
*/
function _addNetworkTokenLiquidity(
address owner,
IConverterAnchor poolAnchor,
uint256 amount
) internal returns (uint256) {
IDSToken poolToken = IDSToken(address(poolAnchor));
IReserveToken networkToken = IReserveToken(address(_networkToken));
// get the rate between the pool token and the reserve
Fraction memory poolRate = _poolTokenRate(poolToken, networkToken);
// calculate the amount of pool tokens based on the amount of reserve tokens
uint256 poolTokenAmount = _mulDivF(amount, poolRate.d, poolRate.n);
// remove the pool tokens from the system's ownership (will revert if not enough tokens are available)
_systemStore.decSystemBalance(poolToken, poolTokenAmount);
// add the position for the recipient
uint256 id = _addPosition(owner, poolToken, networkToken, poolTokenAmount, amount, _time());
// burns the network tokens from the caller. we need to transfer the tokens to the contract itself, since only
// token holders can burn their tokens
_networkToken.safeTransferFrom(msg.sender, address(this), amount);
_burnNetworkTokens(poolAnchor, amount);
// mint governance tokens to the recipient
_govTokenGovernance.mint(owner, amount);
return id;
}
/**
* @dev adds base token liquidity to a pool
*/
function _addBaseTokenLiquidity(
address owner,
IConverterAnchor poolAnchor,
IReserveToken baseToken,
uint256 amount
) internal returns (uint256) {
IDSToken poolToken = IDSToken(address(poolAnchor));
IReserveToken networkToken = IReserveToken(address(_networkToken));
// get the reserve balances
ILiquidityPoolConverter converter = ILiquidityPoolConverter(payable(_ownedBy(poolAnchor)));
(uint256 reserveBalanceBase, uint256 reserveBalanceNetwork) = _converterReserveBalances(
converter,
baseToken,
networkToken
);
require(reserveBalanceNetwork >= _settings.minNetworkTokenLiquidityForMinting(), "ERR_NOT_ENOUGH_LIQUIDITY");
// calculate and mint the required amount of network tokens for adding liquidity
uint256 newNetworkLiquidityAmount = _mulDivF(amount, reserveBalanceNetwork, reserveBalanceBase);
// get network token minting limit
uint256 mintingLimit = _networkTokenMintingLimit(poolAnchor);
uint256 newNetworkTokensMinted = _systemStore.networkTokensMinted(poolAnchor).add(newNetworkLiquidityAmount);
require(newNetworkTokensMinted <= mintingLimit, "ERR_MAX_AMOUNT_REACHED");
// issue new network tokens to the system
_mintNetworkTokens(address(this), poolAnchor, newNetworkLiquidityAmount);
// transfer the base tokens from the caller and approve the converter
networkToken.ensureApprove(address(converter), newNetworkLiquidityAmount);
if (!baseToken.isNativeToken()) {
baseToken.safeTransferFrom(msg.sender, address(this), amount);
baseToken.ensureApprove(address(converter), amount);
}
// add the liquidity to the converter
_addLiquidity(converter, baseToken, networkToken, amount, newNetworkLiquidityAmount, msg.value);
// transfer the new pool tokens to the wallet
uint256 poolTokenAmount = poolToken.balanceOf(address(this));
poolToken.safeTransfer(address(_wallet), poolTokenAmount);
// the system splits the pool tokens with the caller
// increase the system's pool token balance and add the position for the caller
_systemStore.incSystemBalance(poolToken, poolTokenAmount - poolTokenAmount / 2); // account for rounding errors
return _addPosition(owner, poolToken, baseToken, poolTokenAmount / 2, amount, _time());
}
/**
* @dev returns the single-side staking base and network token limits of a given pool
*/
function poolAvailableSpace(IConverterAnchor poolAnchor)
external
view
poolSupportedAndWhitelisted(poolAnchor)
returns (uint256, uint256)
{
return (_baseTokenAvailableSpace(poolAnchor), _networkTokenAvailableSpace(poolAnchor));
}
/**
* @dev returns the base token staking limits of a given pool
*/
function _baseTokenAvailableSpace(IConverterAnchor poolAnchor) internal view returns (uint256) {
// get the pool converter
ILiquidityPoolConverter converter = ILiquidityPoolConverter(payable(_ownedBy(poolAnchor)));
// get the base token
IReserveToken networkToken = IReserveToken(address(_networkToken));
IReserveToken baseToken = _converterOtherReserve(converter, networkToken);
// get the reserve balances
(uint256 reserveBalanceBase, uint256 reserveBalanceNetwork) = _converterReserveBalances(
converter,
baseToken,
networkToken
);
// get the network token minting limit
uint256 mintingLimit = _networkTokenMintingLimit(poolAnchor);
// get the amount of network tokens already minted for the pool
uint256 networkTokensMinted = _systemStore.networkTokensMinted(poolAnchor);
// get the amount of network tokens which can minted for the pool
uint256 networkTokensCanBeMinted = Math.max(mintingLimit, networkTokensMinted) - networkTokensMinted;
// return the maximum amount of base token liquidity that can be single-sided staked in the pool
return _mulDivF(networkTokensCanBeMinted, reserveBalanceBase, reserveBalanceNetwork);
}
/**
* @dev returns the network token staking limits of a given pool
*/
function _networkTokenAvailableSpace(IConverterAnchor poolAnchor) internal view returns (uint256) {
// get the pool token
IDSToken poolToken = IDSToken(address(poolAnchor));
IReserveToken networkToken = IReserveToken(address(_networkToken));
// get the pool token rate
Fraction memory poolRate = _poolTokenRate(poolToken, networkToken);
// return the maximum amount of network token liquidity that can be single-sided staked in the pool
return _systemStore.systemBalance(poolToken).mul(poolRate.n).add(poolRate.n).sub(1).div(poolRate.d);
}
/**
* @dev returns the expected, actual, and network token compensation amounts the provider will receive for removing
* liquidity
*
* note that it's also possible to provide the remove liquidity time to get an estimation for the return at that
* given point
*/
function removeLiquidityReturn(
uint256 id,
uint32 portion,
uint256 removeTimestamp
)
external
view
validPortion(portion)
returns (
uint256,
uint256,
uint256
)
{
Position memory pos = _position(id);
require(pos.provider != address(0), "ERR_INVALID_ID");
require(removeTimestamp >= pos.timestamp, "ERR_INVALID_TIMESTAMP");
// calculate the portion of the liquidity to remove
if (portion != PPM_RESOLUTION) {
(pos.poolAmount, pos.reserveAmount) = _portionAmounts(pos.poolAmount, pos.reserveAmount, portion);
}
// get the various rates between the reserves upon adding liquidity and now
PackedRates memory packedRates = _packRates(
pos.poolToken,
pos.reserveToken,
pos.reserveRateN,
pos.reserveRateD
);
(uint256 targetAmount,) = _removeLiquidityAmounts(
pos.poolToken,
pos.reserveToken,
pos.poolAmount,
pos.reserveAmount,
packedRates
);
return (targetAmount, targetAmount, 0);
}
/**
* @dev removes protected liquidity from a pool and also burns governance tokens from the caller if the caller
* removes network tokens
*/
function removeLiquidity(uint256 id, uint32 portion)
external
override
nonReentrant
removeLiquidityEnabled
validPortion(portion)
{
_removeLiquidity(msg.sender, id, portion);
}
/**
* @dev removes a position from a pool and burns governance tokens from the caller if the caller removes network tokens
*/
function _removeLiquidity(
address payable provider,
uint256 id,
uint32 portion
) internal {
require(portion == PPM_RESOLUTION, "ERR_PORTION_NOT_SUPPORTED");
// remove the position from the store and update the stats
Position memory removedPos = _removePosition(provider, id, portion);
// add the pool tokens to the system
_systemStore.incSystemBalance(removedPos.poolToken, removedPos.poolAmount);
// if removing network token liquidity, burn the governance tokens from the caller. we need to transfer the
// tokens to the contract itself, since only token holders can burn their tokens
if (_isNetworkToken(removedPos.reserveToken)) {
_govToken.safeTransferFrom(provider, address(this), removedPos.reserveAmount);
_govTokenGovernance.burn(removedPos.reserveAmount);
}
// get the various rates between the reserves upon adding liquidity and now
PackedRates memory packedRates = _packRates(
removedPos.poolToken,
removedPos.reserveToken,
removedPos.reserveRateN,
removedPos.reserveRateD
);
// verify rate deviation as early as possible in order to reduce gas-cost for failing transactions
_verifyRateDeviation(
packedRates.removeSpotRateN,
packedRates.removeSpotRateD,
packedRates.removeAverageRateN,
packedRates.removeAverageRateD
);
// get the target token amount
(uint256 targetAmount, uint256 posValue) = _removeLiquidityAmounts(
removedPos.poolToken,
removedPos.reserveToken,
removedPos.poolAmount,
removedPos.reserveAmount,
packedRates
);
// remove network token liquidity
if (_isNetworkToken(removedPos.reserveToken)) {
// mint network tokens for the caller and lock them
_mintNetworkTokens(address(_wallet), removedPos.poolToken, targetAmount);
_lockTokens(provider, targetAmount);
return;
}
// remove base token liquidity
// calculate the amount of pool tokens required for liquidation
// note that the amount is doubled since it's not possible to liquidate one reserve only
Fraction memory poolRate = _poolTokenRate(removedPos.poolToken, removedPos.reserveToken);
uint256 poolAmount = _liquidationAmount(targetAmount, poolRate, removedPos.poolToken);
// withdraw the pool tokens from the wallet
_withdrawPoolTokens(removedPos.poolToken, poolAmount);
// remove liquidity
_removeLiquidity(
removedPos.poolToken,
poolAmount,
removedPos.reserveToken,
IReserveToken(address(_networkToken))
);
// reduce the total positions value
uint256 totalValue = _totalPositionsValue[removedPos.poolToken];
_totalPositionsValue[removedPos.poolToken] = Math.max(totalValue, posValue).sub(posValue);
// transfer the base tokens to the caller
uint256 baseBalance = removedPos.reserveToken.balanceOf(address(this));
removedPos.reserveToken.safeTransfer(provider, baseBalance);
// if the contract still holds network tokens, burn them
uint256 networkBalance = _networkToken.balanceOf(address(this));
if (networkBalance > 0) {
_burnNetworkTokens(removedPos.poolToken, networkBalance);
}
}
/**
* @dev returns the value of the specific position, based on the initial stake, fees
* and positional IL
*/
function positionValue(uint256 id) external view returns (uint256) {
Position memory pos = _position(id);
// get the various rates between the reserves upon adding liquidity and now
PackedRates memory packedRates = _packRates(
pos.poolToken,
pos.reserveToken,
pos.reserveRateN,
pos.reserveRateD
);
(, uint256 posValue) = _removeLiquidityAmounts(
pos.poolToken,
pos.reserveToken,
pos.poolAmount,
pos.reserveAmount,
packedRates
);
return posValue;
}
/**
* @dev returns the amount the provider will receive for removing liquidity
* as well as the specific position value (before deficit reduction)
*/
function _removeLiquidityAmounts(
IDSToken poolToken,
IReserveToken reserveToken,
uint256 poolAmount,
uint256 reserveAmount,
PackedRates memory packedRates
) internal view returns (uint256, uint256) {
uint256 targetAmount;
// get the rate between the reserves upon adding liquidity and now
Fraction memory addSpotRate = Fraction({ n: packedRates.addSpotRateN, d: packedRates.addSpotRateD });
Fraction memory removeSpotRate = Fraction({ n: packedRates.removeSpotRateN, d: packedRates.removeSpotRateD });
// get the rate between the pool token and the reserve token
Fraction memory poolRate = _poolTokenRate(poolToken, reserveToken);
// calculate the protected amount of reserve tokens plus accumulated fee
targetAmount = _protectedAmountPlusFee(poolAmount, poolRate, addSpotRate, removeSpotRate);
// for the network token, return the target amount
if (_isNetworkToken(reserveToken)) {
return (targetAmount, targetAmount);
}
Fraction memory removeAverageRate = Fraction({
n: packedRates.removeAverageRateN,
d: packedRates.removeAverageRateD
});
// calculate the position impermanent loss
Fraction memory loss = _impLoss(addSpotRate, removeAverageRate);
// deduct the position IL from the target amount
targetAmount = _deductIL(Math.max(reserveAmount, targetAmount), loss);
// get the pool deficit
Fraction memory poolDeficit = _poolDeficit(poolToken);
// calculate the available liquidity portion
Fraction memory availablePortion = Fraction({ n: poolDeficit.d - poolDeficit.n, d: poolDeficit.d});
// return the amount the provider will receive for removing liquidity
// as well as the specific position value (before deficit reduction)
return (_mulDivF(targetAmount, availablePortion.n, availablePortion.d), targetAmount);
}
/**
* @dev returns the pool deficit based on the total protected amount vs. total
* positions value, in PPM
*/
function poolDeficitPPM(IDSToken poolToken)
external
view
returns (uint256)
{
Fraction memory poolDeficit = _poolDeficit(poolToken);
return _mulDivF(PPM_RESOLUTION, poolDeficit.n, poolDeficit.d);
}
/**
* @dev returns the pool deficit based on the total protected amount vs. total
* positions value, as a fraction.
* note that 0/1 is returned if the pool is not in deficit
*/
function _poolDeficit(IDSToken poolToken)
private
view
returns (Fraction memory)
{
// get the converter balance
IConverter converter = IConverter(payable(_ownedBy(poolToken)));
IReserveToken reserveToken = _converterOtherReserve(converter, IReserveToken(address(_networkToken)));
uint256 reserveBalance = converter.reserveBalance(reserveToken);
// calculate the protected liquidity amount
uint256 poolTokenSupply = poolToken.totalSupply();
uint256 protectedPoolTokenAmount = poolToken.balanceOf(address(_wallet));
uint256 protectedLiquidity = _mulDivF(reserveBalance, protectedPoolTokenAmount, poolTokenSupply);
// get the total positions value
uint256 totalValue = totalPositionsValue(poolToken);
// if the protected liquidity is equal or greater than the total value,
// the pool is not in deficit
if (protectedLiquidity >= totalValue) {
return Fraction({ n: 0, d: 1 });
}
// the pool is in deficit
return Fraction({
n: totalValue.sub(protectedLiquidity),
d: totalValue
});
}
/**
* @dev transfers a position to a new provider
*
* Requirements:
*
* - the caller must be the owner of the position
*/
function transferPosition(uint256 id, address newProvider)
external
override
nonReentrant
validAddress(newProvider)
returns (uint256)
{
return _transferPosition(msg.sender, id, newProvider);
}
/**
* @dev transfers a position to a new provider and optionally notifies another contract
*
* Requirements:
*
* - the caller must be the owner of the position
*/
function transferPositionAndNotify(
uint256 id,
address newProvider,
ITransferPositionCallback callback,
bytes calldata data
) external override nonReentrant validAddress(newProvider) validAddress(address(callback)) returns (uint256) {
uint256 newId = _transferPosition(msg.sender, id, newProvider);
callback.onTransferPosition(newId, msg.sender, data);
return newId;
}
/**
* @dev migrates system pool tokens to v3
*
* Requirements:
*
* - the caller must be the owner of this contract
*/
function migrateSystemPoolTokens(IConverterAnchor[] calldata poolAnchors) external nonReentrant ownerOnly {
uint256 length = poolAnchors.length;
for (uint256 i = 0; i < length; i++) {
IConverterAnchor poolAnchor = poolAnchors[i];
IDSToken poolToken = IDSToken(address(poolAnchor));
ILiquidityPoolConverter converter = ILiquidityPoolConverter(payable(_ownedBy(poolToken)));
IReserveToken reserveToken1 = IReserveToken(address(_networkToken));
IReserveToken reserveToken2 = _converterOtherReserve(converter, IReserveToken(address(_networkToken)));
uint256 poolAmount = _poolTokensToMigrate(poolToken, converter, reserveToken2);
if (poolAmount == 0) {
continue;
}
_withdrawPoolTokens(poolToken, poolAmount);
(IReserveToken[] memory reserveTokens, uint256[] memory minReturns) = _removeLiquidityInput(
reserveToken1,
reserveToken2
);
uint256[] memory reserveAmounts = converter.removeLiquidity(poolAmount, reserveTokens, minReturns);
_burnNetworkTokens(poolAnchor, reserveAmounts[0]);
if (reserveTokens[1].isNativeToken()) {
_vaultV3.sendValue(reserveAmounts[1]);
} else {
reserveTokens[1].safeTransfer(_vaultV3, reserveAmounts[1]);
}
}
}
/**
* @dev amount of pool tokens to migrate to v3
* @param poolToken pool token
* @param converter pool converter
* @param reserveToken the reserve tokens whose pool tokens we'll migrate
* @return poolAmount number of pool tokens to migrate to v3
* if the pool is in deficit don't migrate it (return 0)
*
*/
function _poolTokensToMigrate(
IDSToken poolToken,
ILiquidityPoolConverter converter,
IReserveToken reserveToken
) private view returns (uint256) {
// calcualte the total positions pool token amount
uint256 totalPositionsValue = totalPositionsValue(poolToken);
uint256 reserveBalance = converter.reserveBalance(reserveToken);
uint256 poolTokenSupply = poolToken.totalSupply();
uint256 positionsPoolTokenAmount = _mulDivF(poolTokenSupply, totalPositionsValue, reserveBalance);
// get the total protected pool tokens amount
uint256 protectedPoolTokenAmount = poolToken.balanceOf(address(_wallet));
// if the positions pool token amount is greater or equal to the total
// protected pool token amount, there's nothing to migrate
if (positionsPoolTokenAmount >= protectedPoolTokenAmount) {
return 0;
}
// deduct the positions pool toke amount from the total protected pool tokens amount
// and limit it by the system balance
uint256 poolAmountToMigrate = protectedPoolTokenAmount.sub(positionsPoolTokenAmount);
uint256 systemPoolAmount = _systemStore.systemBalance(poolToken);
return Math.min(poolAmountToMigrate, systemPoolAmount);
}
/**
* @dev transfers a position to a new provider
*/
function _transferPosition(
address provider,
uint256 id,
address newProvider
) internal returns (uint256) {
// remove the position from the store and update the stats
Position memory removedPos = _removePosition(provider, id, PPM_RESOLUTION);
// add the position to the store, update the stats, and return the new id
return
_addPosition(
newProvider,
removedPos.poolToken,
removedPos.reserveToken,
removedPos.poolAmount,
removedPos.reserveAmount,
removedPos.timestamp
);
}
/**
* @dev allows the caller to claim network token balance that is no longer locked
*
* note that the function can revert if the range is too large
*/
function claimBalance(uint256 startIndex, uint256 endIndex) external nonReentrant {
// get the locked balances from the store
(uint256[] memory amounts, uint256[] memory expirationTimes) = _store.lockedBalanceRange(
msg.sender,
startIndex,
endIndex
);
uint256 totalAmount = 0;
uint256 length = amounts.length;
assert(length == expirationTimes.length);
// reverse iteration since we're removing from the list
for (uint256 i = length; i > 0; i--) {
uint256 index = i - 1;
if (expirationTimes[index] > _time()) {
continue;
}
// remove the locked balance item
_store.removeLockedBalance(msg.sender, startIndex + index);
totalAmount = totalAmount.add(amounts[index]);
}
if (totalAmount > 0) {
// transfer the tokens to the caller in a single call
_wallet.withdrawTokens(IReserveToken(address(_networkToken)), msg.sender, totalAmount);
}
}
/**
* @dev adds the position to the store and updates the stats
*/
function _addPosition(
address provider,
IDSToken poolToken,
IReserveToken reserveToken,
uint256 poolAmount,
uint256 reserveAmount,
uint256 timestamp
) internal returns (uint256) {
// verify rate deviation as early as possible in order to reduce gas-cost for failing transactions
(Fraction memory spotRate, Fraction memory averageRate) = _reserveTokenRates(poolToken, reserveToken);
_verifyRateDeviation(spotRate.n, spotRate.d, averageRate.n, averageRate.d);
_stats.increaseTotalAmounts(provider, poolToken, reserveToken, poolAmount, reserveAmount);
_stats.addProviderPool(provider, poolToken);
return
_store.addProtectedLiquidity(
provider,
poolToken,
reserveToken,
poolAmount,
reserveAmount,
spotRate.n,
spotRate.d,
timestamp
);
}
/**
* @dev removes the position from the store and updates the stats
*/
function _removePosition(
address provider,
uint256 id,
uint32 portion
) private returns (Position memory) {
Position memory pos = _providerPosition(id, provider);
// verify that the pool is whitelisted
_poolWhitelisted(pos.poolToken);
// verify that the position is not removed on the same block in which it was added
require(pos.timestamp < _time(), "ERR_TOO_EARLY");
if (portion == PPM_RESOLUTION) {
// remove the position from the provider
_store.removeProtectedLiquidity(id);
} else {
// remove a portion of the position from the provider
uint256 fullPoolAmount = pos.poolAmount;
uint256 fullReserveAmount = pos.reserveAmount;
(pos.poolAmount, pos.reserveAmount) = _portionAmounts(pos.poolAmount, pos.reserveAmount, portion);
_store.updateProtectedLiquidityAmounts(
id,
fullPoolAmount - pos.poolAmount,
fullReserveAmount - pos.reserveAmount
);
}
// update the statistics
_stats.decreaseTotalAmounts(pos.provider, pos.poolToken, pos.reserveToken, pos.poolAmount, pos.reserveAmount);
return pos;
}
/**
* @dev locks network tokens for the provider and emits the tokens locked event
*/
function _lockTokens(address provider, uint256 amount) internal {
uint256 expirationTime = _time().add(_settings.lockDuration());
_store.addLockedBalance(provider, amount, expirationTime);
}
/**
* @dev returns the rate of 1 pool token in reserve token units
*/
function _poolTokenRate(IDSToken poolToken, IReserveToken reserveToken)
internal
view
virtual
returns (Fraction memory)
{
// get the pool token supply
uint256 poolTokenSupply = poolToken.totalSupply();
// get the reserve balance
IConverter converter = IConverter(payable(_ownedBy(poolToken)));
uint256 reserveBalance = converter.getConnectorBalance(reserveToken);
// for standard pools, 50% of the pool supply value equals the value of each reserve
return Fraction({ n: reserveBalance.mul(2), d: poolTokenSupply });
}
/**
* @dev returns the spot rate and average rate of 1 reserve token in the other reserve token units
*/
function _reserveTokenRates(IDSToken poolToken, IReserveToken reserveToken)
internal
view
returns (Fraction memory, Fraction memory)
{
ILiquidityPoolConverter converter = ILiquidityPoolConverter(payable(_ownedBy(poolToken)));
IReserveToken otherReserve = _converterOtherReserve(converter, reserveToken);
(uint256 spotRateN, uint256 spotRateD) = _converterReserveBalances(converter, otherReserve, reserveToken);
(uint256 averageRateN, uint256 averageRateD) = converter.recentAverageRate(reserveToken);
return (Fraction({ n: spotRateN, d: spotRateD }), Fraction({ n: averageRateN, d: averageRateD }));
}
/**
* @dev returns the various rates between the reserves
*/
function _packRates(
IDSToken poolToken,
IReserveToken reserveToken,
uint256 addSpotRateN,
uint256 addSpotRateD
) internal view returns (PackedRates memory) {
(Fraction memory removeSpotRate, Fraction memory removeAverageRate) = _reserveTokenRates(
poolToken,
reserveToken
);
assert((removeSpotRate.n | removeSpotRate.d | removeAverageRate.n | removeAverageRate.d) <= MAX_UINT128);
return _packRates(addSpotRateN, addSpotRateD, removeSpotRate, removeAverageRate);
}
/**
* @dev returns the various rates between the reserves
*/
function _packRates(
uint256 addSpotRateN,
uint256 addSpotRateD,
Fraction memory removeSpotRate,
Fraction memory removeAverageRate
) internal pure returns (PackedRates memory) {
assert((addSpotRateN | addSpotRateD) <= MAX_UINT128);
return
PackedRates({
addSpotRateN: uint128(addSpotRateN),
addSpotRateD: uint128(addSpotRateD),
removeSpotRateN: uint128(removeSpotRate.n),
removeSpotRateD: uint128(removeSpotRate.d),
removeAverageRateN: uint128(removeAverageRate.n),
removeAverageRateD: uint128(removeAverageRate.d)
});
}
/**
* @dev verifies that the deviation of the average rate from the spot rate is within the permitted range
*
* for example, if the maximum permitted deviation is 5%, then verify `95/100 <= average/spot <= 100/95`
*/
function _verifyRateDeviation(
uint256 spotRateN,
uint256 spotRateD,
uint256 averageRateN,
uint256 averageRateD
) internal view {
uint256 ppmDelta = PPM_RESOLUTION - _settings.averageRateMaxDeviation();
uint256 min = spotRateN.mul(averageRateD).mul(ppmDelta).mul(ppmDelta);
uint256 mid = spotRateD.mul(averageRateN).mul(ppmDelta).mul(PPM_RESOLUTION);
uint256 max = spotRateN.mul(averageRateD).mul(PPM_RESOLUTION).mul(PPM_RESOLUTION);
require(min <= mid && mid <= max, "ERR_INVALID_RATE");
}
/**
* @dev utility to add liquidity to a converter
*/
function _addLiquidity(
ILiquidityPoolConverter converter,
IReserveToken reserveToken1,
IReserveToken reserveToken2,
uint256 reserveAmount1,
uint256 reserveAmount2,
uint256 value
) internal {
IReserveToken[] memory reserveTokens = new IReserveToken[](2);
uint256[] memory amounts = new uint256[](2);
reserveTokens[0] = reserveToken1;
reserveTokens[1] = reserveToken2;
amounts[0] = reserveAmount1;
amounts[1] = reserveAmount2;
converter.addLiquidity{ value: value }(reserveTokens, amounts, 1);
}
/**
* @dev utility to remove liquidity from a converter
*/
function _removeLiquidity(
IDSToken poolToken,
uint256 poolAmount,
IReserveToken reserveToken1,
IReserveToken reserveToken2
) internal {
ILiquidityPoolConverter converter = ILiquidityPoolConverter(payable(_ownedBy(poolToken)));
(IReserveToken[] memory reserveTokens, uint256[] memory minReturns) = _removeLiquidityInput(
reserveToken1,
reserveToken2
);
converter.removeLiquidity(poolAmount, reserveTokens, minReturns);
}
/**
* @dev returns a position from the store
*/
function _position(uint256 id) internal view returns (Position memory) {
Position memory pos;
(
pos.provider,
pos.poolToken,
pos.reserveToken,
pos.poolAmount,
pos.reserveAmount,
pos.reserveRateN,
pos.reserveRateD,
pos.timestamp
) = _store.protectedLiquidity(id);
return pos;
}
/**
* @dev returns a position from the store
*/
function _providerPosition(uint256 id, address provider) internal view returns (Position memory) {
Position memory pos = _position(id);
require(pos.provider == provider, "ERR_ACCESS_DENIED");
return pos;
}
/**
* @dev returns the protected amount of reserve tokens plus accumulated fee before compensation
*/
function _protectedAmountPlusFee(
uint256 poolAmount,
Fraction memory poolRate,
Fraction memory addRate,
Fraction memory removeRate
) internal pure returns (uint256) {
uint256 n = MathEx.ceilSqrt(addRate.d.mul(removeRate.n)).mul(poolRate.n);
uint256 d = MathEx.floorSqrt(addRate.n.mul(removeRate.d)).mul(poolRate.d);
uint256 x = n * poolAmount;
if (x / n == poolAmount) {
return x / d;
}
(uint256 hi, uint256 lo) = n > poolAmount ? (n, poolAmount) : (poolAmount, n);
(uint256 p, uint256 q) = MathEx.reducedRatio(hi, d, MAX_UINT256 / lo);
uint256 min = (hi / d).mul(lo);
if (q > 0) {
return Math.max(min, (p * lo) / q);
}
return min;
}
/**
* @dev returns the impermanent loss incurred due to the change in rates between the reserve tokens
*/
function _impLoss(Fraction memory prevRate, Fraction memory newRate) internal pure returns (Fraction memory) {
uint256 ratioN = newRate.n.mul(prevRate.d);
uint256 ratioD = newRate.d.mul(prevRate.n);
uint256 prod = ratioN * ratioD;
uint256 root = prod / ratioN == ratioD
? MathEx.floorSqrt(prod)
: MathEx.floorSqrt(ratioN) * MathEx.floorSqrt(ratioD);
uint256 sum = ratioN.add(ratioD);
// the arithmetic below is safe because `x + y >= sqrt(x * y) * 2`
if (sum % 2 == 0) {
sum /= 2;
return Fraction({ n: sum - root, d: sum });
}
return Fraction({ n: sum - root * 2, d: sum });
}
/**
* @dev deducts the IL amount from the given position value
*/
function _deductIL(uint256 value, Fraction memory loss) internal pure returns (uint256) {
uint256 maxVal = Math.max(1, value);
(uint256 lossN, uint256 lossD) = MathEx.reducedRatio(loss.n, loss.d, MAX_UINT256 / maxVal);
return value.mul(lossD.sub(lossN)).div(lossD);
}
/**
* @dev utility to mint network tokens
*/
function _mintNetworkTokens(
address owner,
IConverterAnchor poolAnchor,
uint256 amount
) private {
_systemStore.incNetworkTokensMinted(poolAnchor, amount);
_networkTokenGovernance.mint(owner, amount);
}
/**
* @dev utility to burn network tokens
*/
function _burnNetworkTokens(IConverterAnchor poolAnchor, uint256 amount) private {
_systemStore.decNetworkTokensMinted(poolAnchor, amount);
_networkTokenGovernance.burn(amount);
}
/**
* @dev utility to get the reserve balances
*/
function _converterReserveBalances(
IConverter converter,
IReserveToken reserveToken1,
IReserveToken reserveToken2
) private view returns (uint256, uint256) {
return (converter.getConnectorBalance(reserveToken1), converter.getConnectorBalance(reserveToken2));
}
/**
* @dev utility to get the other reserve
*/
function _converterOtherReserve(IConverter converter, IReserveToken thisReserve)
private
view
returns (IReserveToken)
{
IReserveToken otherReserve = converter.connectorTokens(0);
return otherReserve != thisReserve ? otherReserve : converter.connectorTokens(1);
}
/**
* @dev utility to get the owner
*/
function _ownedBy(IOwned owned) private view returns (address) {
return owned.owner();
}
/**
* @dev returns whether the provided reserve token is the network token
*/
function _isNetworkToken(IReserveToken reserveToken) private view returns (bool) {
return address(reserveToken) == address(_networkToken);
}
/**
* @dev returns custom input for the `removeLiquidity` converter function
*/
function _removeLiquidityInput(IReserveToken reserveToken1, IReserveToken reserveToken2)
private
pure
returns (IReserveToken[] memory, uint256[] memory)
{
IReserveToken[] memory reserveTokens = new IReserveToken[](2);
uint256[] memory minReturns = new uint256[](2);
reserveTokens[0] = reserveToken1;
reserveTokens[1] = reserveToken2;
minReturns[0] = 1;
minReturns[1] = 1;
return (reserveTokens, minReturns);
}
/**
* @dev returns the relative position amounts
*/
function _portionAmounts(
uint256 poolAmount,
uint256 reserveAmount,
uint256 portion
) private pure returns (uint256, uint256) {
return (_mulDivF(poolAmount, portion, PPM_RESOLUTION), _mulDivF(reserveAmount, portion, PPM_RESOLUTION));
}
/**
* @dev returns the network token minting limit
*/
function _networkTokenMintingLimit(IConverterAnchor poolAnchor) private view returns (uint256) {
uint256 mintingLimit = _settings.networkTokenMintingLimits(poolAnchor);
return mintingLimit > 0 ? mintingLimit : _settings.defaultNetworkTokenMintingLimit();
}
/**
* @dev returns the amount of pool tokens required for liquidation
*/
function _liquidationAmount(
uint256 targetAmount,
Fraction memory poolRate,
IDSToken poolToken
) private view returns (uint256) {
// note that the amount is doubled since it's not possible to liquidate one reserve only
uint256 poolAmount = _mulDivF(targetAmount, poolRate.d.mul(2), poolRate.n);
// limit the amount of pool tokens by the amount the system/caller holds
return Math.min(poolAmount, poolToken.balanceOf(address(_wallet)));
}
/**
* @dev withdraw pool tokens from the wallet
*/
function _withdrawPoolTokens(IDSToken poolToken, uint256 poolAmount) private {
uint256 systemBalance = _systemStore.systemBalance(poolToken);
_systemStore.decSystemBalance(poolToken, Math.min(poolAmount, systemBalance));
_wallet.withdrawTokens(IReserveToken(address(poolToken)), address(this), poolAmount);
}
/**
* @dev returns `x * y / z`
*/
function _mulDivF(
uint256 x,
uint256 y,
uint256 z
) private pure returns (uint256) {
return x.mul(y).div(z);
}
/**
* @dev enables/disabled deposits
*
* Requirements:
*
* - the caller must be the owner of the contract
*/
function enableDepositing(bool state) external ownerOnly {
_addingEnabled = state;
}
/**
* @dev enables/disabled removals
*
* Requirements:
*
* - the caller must be the owner of the contract
*/
function enableRemoving(bool state) external ownerOnly {
_removingEnabled = state;
}
/**
* Sets the total positions value of the pool to the given amount
* @param poolAnchor pool anchor
* @param amount total positions value amount in wei
*
* Requirements:
*
* - the caller must be the owner of the contract
* - the pool must exist and be whitelisted
*/
function setTotalPositionsValue(IConverterAnchor poolAnchor, uint256 amount)
public
ownerOnly
poolSupportedAndWhitelisted(poolAnchor)
{
_totalPositionsValue[poolAnchor] = amount;
}
/**
* Sets the total positions value of multiple pools in a single call
* @param poolAnchors list of pool anchor
* @param amounts list of total positions value amount in wei
*
* Requirements:
...
// [truncated — 50822 bytes total]
MathEx.sol 291 lines
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.6.12;
/**
* @dev This library provides a set of complex math operations.
*/
library MathEx {
uint256 private constant MAX_EXP_BIT_LEN = 4;
uint256 private constant MAX_EXP = 2**MAX_EXP_BIT_LEN - 1;
uint256 private constant MAX_UINT256 = uint256(-1);
/**
* @dev returns the largest integer smaller than or equal to the square root of a positive integer
*/
function floorSqrt(uint256 num) internal pure returns (uint256) {
uint256 x = num / 2 + 1;
uint256 y = (x + num / x) / 2;
while (x > y) {
x = y;
y = (x + num / x) / 2;
}
return x;
}
/**
* @dev returns the smallest integer larger than or equal to the square root of a positive integer
*/
function ceilSqrt(uint256 num) internal pure returns (uint256) {
uint256 x = floorSqrt(num);
return x * x == num ? x : x + 1;
}
/**
* @dev computes the product of two given ratios
*/
function productRatio(
uint256 xn,
uint256 yn,
uint256 xd,
uint256 yd
) internal pure returns (uint256, uint256) {
uint256 n = mulDivC(xn, yn, MAX_UINT256);
uint256 d = mulDivC(xd, yd, MAX_UINT256);
uint256 z = n > d ? n : d;
if (z > 1) {
return (mulDivC(xn, yn, z), mulDivC(xd, yd, z));
}
return (xn * yn, xd * yd);
}
/**
* @dev computes a reduced-scalar ratio
*/
function reducedRatio(
uint256 n,
uint256 d,
uint256 max
) internal pure returns (uint256, uint256) {
(uint256 newN, uint256 newD) = (n, d);
if (newN > max || newD > max) {
(newN, newD) = normalizedRatio(newN, newD, max);
}
if (newN != newD) {
return (newN, newD);
}
return (1, 1);
}
/**
* @dev computes "scale * a / (a + b)" and "scale * b / (a + b)".
*/
function normalizedRatio(
uint256 a,
uint256 b,
uint256 scale
) internal pure returns (uint256, uint256) {
if (a <= b) {
return accurateRatio(a, b, scale);
}
(uint256 y, uint256 x) = accurateRatio(b, a, scale);
return (x, y);
}
/**
* @dev computes "scale * a / (a + b)" and "scale * b / (a + b)", assuming that "a <= b".
*/
function accurateRatio(
uint256 a,
uint256 b,
uint256 scale
) internal pure returns (uint256, uint256) {
uint256 maxVal = MAX_UINT256 / scale;
if (a > maxVal) {
uint256 c = a / (maxVal + 1) + 1;
a /= c; // we can now safely compute `a * scale`
b /= c;
}
if (a != b) {
uint256 newN = a * scale;
uint256 newD = unsafeAdd(a, b); // can overflow
if (newD >= a) {
// no overflow in `a + b`
uint256 x = roundDiv(newN, newD); // we can now safely compute `scale - x`
uint256 y = scale - x;
return (x, y);
}
if (newN < b - (b - a) / 2) {
return (0, scale); // `a * scale < (a + b) / 2 < MAX_UINT256 < a + b`
}
return (1, scale - 1); // `(a + b) / 2 < a * scale < MAX_UINT256 < a + b`
}
return (scale / 2, scale / 2); // allow reduction to `(1, 1)` in the calling function
}
/**
* @dev computes the nearest integer to a given quotient without overflowing or underflowing.
*/
function roundDiv(uint256 n, uint256 d) internal pure returns (uint256) {
return n / d + (n % d) / (d - d / 2);
}
/**
* @dev returns the average number of decimal digits in a given list of positive integers
*/
function geometricMean(uint256[] memory values) internal pure returns (uint256) {
uint256 numOfDigits = 0;
uint256 length = values.length;
for (uint256 i = 0; i < length; ++i) {
numOfDigits += decimalLength(values[i]);
}
return uint256(10)**(roundDivUnsafe(numOfDigits, length) - 1);
}
/**
* @dev returns the number of decimal digits in a given positive integer
*/
function decimalLength(uint256 x) internal pure returns (uint256) {
uint256 y = 0;
for (uint256 tmpX = x; tmpX > 0; tmpX /= 10) {
++y;
}
return y;
}
/**
* @dev returns the nearest integer to a given quotient
*
* note the computation is overflow-safe assuming that the input is sufficiently small
*/
function roundDivUnsafe(uint256 n, uint256 d) internal pure returns (uint256) {
return (n + d / 2) / d;
}
/**
* @dev returns the largest integer smaller than or equal to `x * y / z`
*/
function mulDivF(
uint256 x,
uint256 y,
uint256 z
) internal pure returns (uint256) {
(uint256 xyh, uint256 xyl) = mul512(x, y);
// if `x * y < 2 ^ 256`
if (xyh == 0) {
return xyl / z;
}
// assert `x * y / z < 2 ^ 256`
require(xyh < z, "ERR_OVERFLOW");
uint256 m = mulMod(x, y, z); // `m = x * y % z`
(uint256 nh, uint256 nl) = sub512(xyh, xyl, m); // `n = x * y - m` hence `n / z = floor(x * y / z)`
// if `n < 2 ^ 256`
if (nh == 0) {
return nl / z;
}
uint256 p = unsafeSub(0, z) & z; // `p` is the largest power of 2 which `z` is divisible by
uint256 q = div512(nh, nl, p); // `n` is divisible by `p` because `n` is divisible by `z` and `z` is divisible by `p`
uint256 r = inv256(z / p); // `z / p = 1 mod 2` hence `inverse(z / p) = 1 mod 2 ^ 256`
return unsafeMul(q, r); // `q * r = (n / p) * inverse(z / p) = n / z`
}
/**
* @dev returns the smallest integer larger than or equal to `x * y / z`
*/
function mulDivC(
uint256 x,
uint256 y,
uint256 z
) internal pure returns (uint256) {
uint256 w = mulDivF(x, y, z);
if (mulMod(x, y, z) > 0) {
require(w < MAX_UINT256, "ERR_OVERFLOW");
return w + 1;
}
return w;
}
/**
* @dev returns the value of `x * y` as a pair of 256-bit values
*/
function mul512(uint256 x, uint256 y) private pure returns (uint256, uint256) {
uint256 p = mulModMax(x, y);
uint256 q = unsafeMul(x, y);
if (p >= q) {
return (p - q, q);
}
return (unsafeSub(p, q) - 1, q);
}
/**
* @dev returns the value of `2 ^ 256 * xh + xl - y`, where `2 ^ 256 * xh + xl >= y`
*/
function sub512(
uint256 xh,
uint256 xl,
uint256 y
) private pure returns (uint256, uint256) {
if (xl >= y) {
return (xh, xl - y);
}
return (xh - 1, unsafeSub(xl, y));
}
/**
* @dev returns the value of `(2 ^ 256 * xh + xl) / pow2n`, where `xl` is divisible by `pow2n`
*/
function div512(
uint256 xh,
uint256 xl,
uint256 pow2n
) private pure returns (uint256) {
uint256 pow2nInv = unsafeAdd(unsafeSub(0, pow2n) / pow2n, 1); // `1 << (256 - n)`
return unsafeMul(xh, pow2nInv) | (xl / pow2n); // `(xh << (256 - n)) | (xl >> n)`
}
/**
* @dev returns the inverse of `d` modulo `2 ^ 256`, where `d` is congruent to `1` modulo `2`
*/
function inv256(uint256 d) private pure returns (uint256) {
// approximate the root of `f(x) = 1 / x - d` using the newton–raphson convergence method
uint256 x = 1;
for (uint256 i = 0; i < 8; ++i) {
x = unsafeMul(x, unsafeSub(2, unsafeMul(x, d))); // `x = x * (2 - x * d) mod 2 ^ 256`
}
return x;
}
/**
* @dev returns `(x + y) % 2 ^ 256`
*/
function unsafeAdd(uint256 x, uint256 y) private pure returns (uint256) {
return x + y;
}
/**
* @dev returns `(x - y) % 2 ^ 256`
*/
function unsafeSub(uint256 x, uint256 y) private pure returns (uint256) {
return x - y;
}
/**
* @dev returns `(x * y) % 2 ^ 256`
*/
function unsafeMul(uint256 x, uint256 y) private pure returns (uint256) {
return x * y;
}
/**
* @dev returns `x * y % (2 ^ 256 - 1)`
*/
function mulModMax(uint256 x, uint256 y) private pure returns (uint256) {
return mulmod(x, y, MAX_UINT256);
}
/**
* @dev returns `x * y % z`
*/
function mulMod(
uint256 x,
uint256 y,
uint256 z
) private pure returns (uint256) {
return mulmod(x, y, z);
}
}
Types.sol 11 lines
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.6.12;
/**
* @dev This contract provides types which can be used by various contracts.
*/
struct Fraction {
uint256 n; // numerator
uint256 d; // denominator
}
IConverterRegistry.sol 52 lines
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.6.12;
import "../../token/interfaces/IReserveToken.sol";
import "./IConverterAnchor.sol";
/**
* @dev Converter Registry interface
*/
interface IConverterRegistry {
function getAnchorCount() external view returns (uint256);
function getAnchors() external view returns (address[] memory);
function getAnchor(uint256 index) external view returns (IConverterAnchor);
function isAnchor(address value) external view returns (bool);
function getLiquidityPoolCount() external view returns (uint256);
function getLiquidityPools() external view returns (address[] memory);
function getLiquidityPool(uint256 index) external view returns (IConverterAnchor);
function isLiquidityPool(address value) external view returns (bool);
function getConvertibleTokenCount() external view returns (uint256);
function getConvertibleTokens() external view returns (address[] memory);
function getConvertibleToken(uint256 index) external view returns (IReserveToken);
function isConvertibleToken(address value) external view returns (bool);
function getConvertibleTokenAnchorCount(IReserveToken convertibleToken) external view returns (uint256);
function getConvertibleTokenAnchors(IReserveToken convertibleToken) external view returns (address[] memory);
function getConvertibleTokenAnchor(IReserveToken convertibleToken, uint256 index)
external
view
returns (IConverterAnchor);
function isConvertibleTokenAnchor(IReserveToken convertibleToken, address value) external view returns (bool);
function getLiquidityPoolByConfig(
uint16 converterType,
IReserveToken[] memory reserveTokens,
uint32[] memory reserveWeights
) external view returns (IConverterAnchor);
}
ILiquidityProtectionStats.sol 44 lines
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.6.12;
import "../../converter/interfaces/IConverterAnchor.sol";
import "../../token/interfaces/IDSToken.sol";
import "../../token/interfaces/IReserveToken.sol";
/**
* @dev Liquidity Protection Stats interface
*/
interface ILiquidityProtectionStats {
function increaseTotalAmounts(
address provider,
IDSToken poolToken,
IReserveToken reserveToken,
uint256 poolAmount,
uint256 reserveAmount
) external;
function decreaseTotalAmounts(
address provider,
IDSToken poolToken,
IReserveToken reserveToken,
uint256 poolAmount,
uint256 reserveAmount
) external;
function addProviderPool(address provider, IDSToken poolToken) external returns (bool);
function removeProviderPool(address provider, IDSToken poolToken) external returns (bool);
function totalPoolAmount(IDSToken poolToken) external view returns (uint256);
function totalReserveAmount(IDSToken poolToken, IReserveToken reserveToken) external view returns (uint256);
function totalProviderAmount(
address provider,
IDSToken poolToken,
IReserveToken reserveToken
) external view returns (uint256);
function providerPools(address provider) external view returns (IDSToken[] memory);
}
ILiquidityProtectionSystemStore.sol 23 lines
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.6.12;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../../converter/interfaces/IConverterAnchor.sol";
/**
* @dev Liquidity Protection System Store interface
*/
interface ILiquidityProtectionSystemStore {
function systemBalance(IERC20 poolToken) external view returns (uint256);
function incSystemBalance(IERC20 poolToken, uint256 poolAmount) external;
function decSystemBalance(IERC20 poolToken, uint256 poolAmount) external;
function networkTokensMinted(IConverterAnchor poolAnchor) external view returns (uint256);
function incNetworkTokensMinted(IConverterAnchor poolAnchor, uint256 amount) external;
function decNetworkTokensMinted(IConverterAnchor poolAnchor, uint256 amount) external;
}
Math.sol 31 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow, so we distribute
return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
}
}
IERC20.sol 77 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
IOwned.sol 13 lines
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.6.12;
/**
* @dev Owned interface
*/
interface IOwned {
function owner() external view returns (address);
function transferOwnership(address newOwner) external;
function acceptOwnership() external;
}
ILiquidityProtectionSettings.sol 39 lines
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.6.12;
import "../../converter/interfaces/IConverterAnchor.sol";
import "../../token/interfaces/IReserveToken.sol";
import "./ILiquidityProvisionEventsSubscriber.sol";
/**
* @dev Liquidity Protection Settings interface
*/
interface ILiquidityProtectionSettings {
function isPoolWhitelisted(IConverterAnchor poolAnchor) external view returns (bool);
function poolWhitelist() external view returns (address[] memory);
function subscribers() external view returns (address[] memory);
function isPoolSupported(IConverterAnchor poolAnchor) external view returns (bool);
function minNetworkTokenLiquidityForMinting() external view returns (uint256);
function defaultNetworkTokenMintingLimit() external view returns (uint256);
function networkTokenMintingLimits(IConverterAnchor poolAnchor) external view returns (uint256);
function addLiquidityDisabled(IConverterAnchor poolAnchor, IReserveToken reserveToken) external view returns (bool);
function minProtectionDelay() external view returns (uint256);
function maxProtectionDelay() external view returns (uint256);
function minNetworkCompensation() external view returns (uint256);
function lockDuration() external view returns (uint256);
function averageRateMaxDeviation() external view returns (uint32);
}
ILiquidityProtection.sol 49 lines
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.6.12;
import "./ILiquidityProtectionStore.sol";
import "./ILiquidityProtectionStats.sol";
import "./ILiquidityProtectionSettings.sol";
import "./ILiquidityProtectionSystemStore.sol";
import "./ITransferPositionCallback.sol";
import "../../utility/interfaces/ITokenHolder.sol";
import "../../token/interfaces/IReserveToken.sol";
import "../../converter/interfaces/IConverterAnchor.sol";
/**
* @dev Liquidity Protection interface
*/
interface ILiquidityProtection {
function store() external view returns (ILiquidityProtectionStore);
function stats() external view returns (ILiquidityProtectionStats);
function settings() external view returns (ILiquidityProtectionSettings);
function addLiquidityFor(
address owner,
IConverterAnchor poolAnchor,
IReserveToken reserveToken,
uint256 amount
) external payable returns (uint256);
function addLiquidity(
IConverterAnchor poolAnchor,
IReserveToken reserveToken,
uint256 amount
) external payable returns (uint256);
function removeLiquidity(uint256 id, uint32 portion) external;
function transferPosition(uint256 id, address newProvider) external returns (uint256);
function transferPositionAndNotify(
uint256 id,
address newProvider,
ITransferPositionCallback callback,
bytes calldata data
) external returns (uint256);
}
IClaimable.sol 11 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.12;
/// @title Claimable contract interface
interface IClaimable {
function owner() external view returns (address);
function transferOwnership(address newOwner) external;
function acceptOwnership() external;
}
ITokenHolder.sol 25 lines
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.6.12;
import "../../token/interfaces/IReserveToken.sol";
import "./IOwned.sol";
/**
* @dev Token Holder interface
*/
interface ITokenHolder is IOwned {
receive() external payable;
function withdrawTokens(
IReserveToken reserveToken,
address payable to,
uint256 amount
) external;
function withdrawTokensMultiple(
IReserveToken[] calldata reserveTokens,
address payable to,
uint256[] calldata amounts
) external;
}
Address.sol 189 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.8.0;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain`call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return _verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
IDSToken.sol 16 lines
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.6.12;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../../converter/interfaces/IConverterAnchor.sol";
import "../../utility/interfaces/IOwned.sol";
/**
* @dev DSToken interface
*/
interface IDSToken is IConverterAnchor, IERC20 {
function issue(address recipient, uint256 amount) external;
function destroy(address recipient, uint256 amount) external;
}
ILiquidityProvisionEventsSubscriber.sol 28 lines
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.6.12;
import "../../converter/interfaces/IConverterAnchor.sol";
import "../../token/interfaces/IReserveToken.sol";
/**
* @dev Liquidity provision events subscriber interface
*/
interface ILiquidityProvisionEventsSubscriber {
function onAddingLiquidity(
address provider,
IConverterAnchor poolAnchor,
IReserveToken reserveToken,
uint256 poolAmount,
uint256 reserveAmount
) external;
function onRemovingLiquidity(
uint256 id,
address provider,
IConverterAnchor poolAnchor,
IReserveToken reserveToken,
uint256 poolAmount,
uint256 reserveAmount
) external;
}
SafeMath.sol 214 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
/**
* @dev Returns the substraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a / b);
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a % b);
}
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) return 0;
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: modulo by zero");
return a % b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting with custom message on
* division by zero. The result is rounded towards zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryDiv}.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a % b;
}
}
Owned.sol 77 lines
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.6.12;
import "./interfaces/IOwned.sol";
/**
* @dev This contract provides support and utilities for contract ownership.
*/
contract Owned is IOwned {
address private _owner;
address private _newOwner;
/**
* @dev triggered when the owner is updated
*/
event OwnerUpdate(address indexed prevOwner, address indexed newOwner);
/**
* @dev initializes a new Owned instance
*/
constructor() public {
_owner = msg.sender;
}
// allows execution by the owner only
modifier ownerOnly() {
_ownerOnly();
_;
}
// error message binary size optimization
function _ownerOnly() private view {
require(msg.sender == _owner, "ERR_ACCESS_DENIED");
}
/**
* @dev allows transferring the contract ownership
*
* Requirements:
*
* - the caller must be the owner of the contract
*
* note the new owner still needs to accept the transfer
*/
function transferOwnership(address newOwner) public override ownerOnly {
require(newOwner != _owner, "ERR_SAME_OWNER");
_newOwner = newOwner;
}
/**
* @dev used by a new owner to accept an ownership transfer
*/
function acceptOwnership() public override {
require(msg.sender == _newOwner, "ERR_ACCESS_DENIED");
emit OwnerUpdate(_owner, _newOwner);
_owner = _newOwner;
_newOwner = address(0);
}
/**
* @dev returns the address of the current owner
*/
function owner() public view override returns (address) {
return _owner;
}
/**
* @dev returns the address of the new owner candidate
*/
function newOwner() external view returns (address) {
return _newOwner;
}
}
IMintableToken.sol 13 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.12;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./IClaimable.sol";
/// @title Mintable Token interface
interface IMintableToken is IERC20, IClaimable {
function issue(address to, uint256 amount) external;
function destroy(address from, uint256 amount) external;
}
IReserveToken.sol 13 lines
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.6.12;
/**
* @dev This contract is used to represent reserve tokens, which are tokens that can either be regular ERC20 tokens or
* native ETH (represented by the NATIVE_TOKEN_ADDRESS address)
*
* Please note that this interface is intentionally doesn't inherit from IERC20, so that it'd be possible to effectively
* override its balanceOf() function in the ReserveToken library
*/
interface IReserveToken {
}
SafeERC20Ex.sol 34 lines
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.6.12;
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
/**
* @dev Extends the SafeERC20 library with additional operations
*/
library SafeERC20Ex {
using SafeERC20 for IERC20;
/**
* @dev ensures that the spender has sufficient allowance
*/
function ensureApprove(
IERC20 token,
address spender,
uint256 amount
) internal {
if (amount == 0) {
return;
}
uint256 allowance = token.allowance(address(this), spender);
if (allowance >= amount) {
return;
}
if (allowance > 0) {
token.safeApprove(spender, 0);
}
token.safeApprove(spender, amount);
}
}
SafeERC20.sol 75 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
// solhint-disable-next-line max-line-length
require((value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) { // Return data is optional
// solhint-disable-next-line max-line-length
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
ReentrancyGuard.sol 62 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor () internal {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}
Time.sol 14 lines
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.6.12;
/*
Time implementing contract
*/
contract Time {
/**
* @dev returns the current time
*/
function _time() internal view virtual returns (uint256) {
return block.timestamp;
}
}
Utils.sol 71 lines
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.6.12;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @dev Utilities & Common Modifiers
*/
contract Utils {
uint32 internal constant PPM_RESOLUTION = 1000000;
// verifies that a value is greater than zero
modifier greaterThanZero(uint256 value) {
_greaterThanZero(value);
_;
}
// error message binary size optimization
function _greaterThanZero(uint256 value) internal pure {
require(value > 0, "ERR_ZERO_VALUE");
}
// validates an address - currently only checks that it isn't null
modifier validAddress(address addr) {
_validAddress(addr);
_;
}
// error message binary size optimization
function _validAddress(address addr) internal pure {
require(addr != address(0), "ERR_INVALID_ADDRESS");
}
// ensures that the portion is valid
modifier validPortion(uint32 _portion) {
_validPortion(_portion);
_;
}
// error message binary size optimization
function _validPortion(uint32 _portion) internal pure {
require(_portion > 0 && _portion <= PPM_RESOLUTION, "ERR_INVALID_PORTION");
}
// validates an external address - currently only checks that it isn't null or this
modifier validExternalAddress(address addr) {
_validExternalAddress(addr);
_;
}
// error message binary size optimization
function _validExternalAddress(address addr) internal view {
require(addr != address(0) && addr != address(this), "ERR_INVALID_EXTERNAL_ADDRESS");
}
// ensures that the fee is valid
modifier validFee(uint32 fee) {
_validFee(fee);
_;
}
// error message binary size optimization
function _validFee(uint32 fee) internal pure {
require(fee <= PPM_RESOLUTION, "ERR_INVALID_FEE");
}
}
ReserveToken.sol 98 lines
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.6.12;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "./interfaces/IReserveToken.sol";
import "./SafeERC20Ex.sol";
/**
* @dev This library implements ERC20 and SafeERC20 utilities for reserve tokens, which can be either ERC20 tokens or ETH
*/
library ReserveToken {
using SafeERC20 for IERC20;
using SafeERC20Ex for IERC20;
// the address that represents an ETH reserve
IReserveToken public constant NATIVE_TOKEN_ADDRESS = IReserveToken(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
/**
* @dev returns whether the provided token represents an ERC20 or ETH reserve
*/
function isNativeToken(IReserveToken reserveToken) internal pure returns (bool) {
return reserveToken == NATIVE_TOKEN_ADDRESS;
}
/**
* @dev returns the balance of the reserve token
*/
function balanceOf(IReserveToken reserveToken, address account) internal view returns (uint256) {
if (isNativeToken(reserveToken)) {
return account.balance;
}
return toIERC20(reserveToken).balanceOf(account);
}
/**
* @dev transfers a specific amount of the reserve token
*/
function safeTransfer(
IReserveToken reserveToken,
address to,
uint256 amount
) internal {
if (amount == 0) {
return;
}
if (isNativeToken(reserveToken)) {
payable(to).transfer(amount);
} else {
toIERC20(reserveToken).safeTransfer(to, amount);
}
}
/**
* @dev transfers a specific amount of the reserve token from a specific holder using the allowance mechanism
*
* note that the function ignores a reserve token which represents an ETH reserve
*/
function safeTransferFrom(
IReserveToken reserveToken,
address from,
address to,
uint256 amount
) internal {
if (amount == 0 || isNativeToken(reserveToken)) {
return;
}
toIERC20(reserveToken).safeTransferFrom(from, to, amount);
}
/**
* @dev ensures that the spender has sufficient allowance
*
* note that this function ignores a reserve token which represents an ETH reserve
*/
function ensureApprove(
IReserveToken reserveToken,
address spender,
uint256 amount
) internal {
if (isNativeToken(reserveToken)) {
return;
}
toIERC20(reserveToken).ensureApprove(spender, amount);
}
/**
* @dev utility function that converts an IReserveToken to an IERC20
*/
function toIERC20(IReserveToken reserveToken) private pure returns (IERC20) {
return IERC20(address(reserveToken));
}
}
IConverterAnchor.sol 11 lines
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.6.12;
import "../../utility/interfaces/IOwned.sol";
/**
* @dev Converter Anchor interface
*/
interface IConverterAnchor is IOwned {
}
IConverter.sol 112 lines
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.6.12;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./IConverterAnchor.sol";
import "../../utility/interfaces/IOwned.sol";
import "../../token/interfaces/IReserveToken.sol";
/**
* @dev Converter interface
*/
interface IConverter is IOwned {
function converterType() external pure returns (uint16);
function anchor() external view returns (IConverterAnchor);
function isActive() external view returns (bool);
function targetAmountAndFee(
IReserveToken sourceToken,
IReserveToken targetToken,
uint256 sourceAmount
) external view returns (uint256, uint256);
function convert(
IReserveToken sourceToken,
IReserveToken targetToken,
uint256 sourceAmount,
address trader,
address payable beneficiary
) external payable returns (uint256);
function conversionFee() external view returns (uint32);
function maxConversionFee() external view returns (uint32);
function reserveBalance(IReserveToken reserveToken) external view returns (uint256);
receive() external payable;
function transferAnchorOwnership(address newOwner) external;
function acceptAnchorOwnership() external;
function setConversionFee(uint32 fee) external;
function addReserve(IReserveToken token, uint32 weight) external;
function transferReservesOnUpgrade(address newConverter) external;
function onUpgradeComplete() external;
// deprecated, backward compatibility
function token() external view returns (IConverterAnchor);
function transferTokenOwnership(address newOwner) external;
function acceptTokenOwnership() external;
function reserveTokenCount() external view returns (uint16);
function reserveTokens() external view returns (IReserveToken[] memory);
function connectors(IReserveToken reserveToken)
external
view
returns (
uint256,
uint32,
bool,
bool,
bool
);
function getConnectorBalance(IReserveToken connectorToken) external view returns (uint256);
function connectorTokens(uint256 index) external view returns (IReserveToken);
function connectorTokenCount() external view returns (uint16);
/**
* @dev triggered when the converter is activated
*/
event Activation(uint16 indexed converterType, IConverterAnchor indexed anchor, bool indexed activated);
/**
* @dev triggered when a conversion between two tokens occurs
*/
event Conversion(
IReserveToken indexed sourceToken,
IReserveToken indexed targetToken,
address indexed trader,
uint256 sourceAmount,
uint256 targetAmount,
int256 conversionFee
);
/**
* @dev triggered when the rate between two tokens in the converter changes
*
* note that the event might be dispatched for rate updates between any two tokens in the converter
*/
event TokenRateUpdate(address indexed token1, address indexed token2, uint256 rateN, uint256 rateD);
/**
* @dev triggered when the conversion fee is updated
*/
event ConversionFeeUpdate(uint32 prevFee, uint32 newFee);
}
Read Contract
newOwner 0xd4ee1d90 → address
owner 0x8da5cb5b → address
poolAvailableSpace 0x24afe2d9 → uint256, uint256
poolDeficitPPM 0x24e82c61 → uint256
positionValue 0xdd639b1b → uint256
removeLiquidityReturn 0x6d533e9b → uint256, uint256, uint256
settings 0xe06174e4 → address
stats 0xd80528ae → address
store 0x975057e7 → address
totalPositionsValue 0xb636d486 → uint256
Write Contract 17 functions
These functions modify contract state and require a wallet transaction to execute.
acceptOwnership 0x79ba5097
No parameters
acceptStoreOwnership 0x89d94b46
No parameters
acceptWalletOwnership 0x28790b5a
No parameters
addLiquidity 0xe4a76726
address poolAnchor
address reserveToken
uint256 amount
returns: uint256
addLiquidityFor 0xcaee4c8f
address owner
address poolAnchor
address reserveToken
uint256 amount
returns: uint256
claimBalance 0x630d8c63
uint256 startIndex
uint256 endIndex
enableDepositing 0x26e6b697
bool state
enableRemoving 0x5e718c37
bool state
migrateSystemPoolTokens 0x1e83958b
address[] poolAnchors
removeLiquidity 0x782ed90c
uint256 id
uint32 portion
setTotalPositionsValue 0x7c53dc0b
address poolAnchor
uint256 amount
setTotalPositionsValueMultiple 0x3fad2db3
address[] poolAnchors
uint256[] amounts
transferOwnership 0xf2fde38b
address newOwner
transferPosition 0x55bd513f
uint256 id
address newProvider
returns: uint256
transferPositionAndNotify 0x40083480
uint256 id
address newProvider
address callback
bytes data
returns: uint256
transferStoreOwnership 0xc2250a99
address newOwner
transferWalletOwnership 0xbf3b1101
address newOwner
Recent Transactions
No transactions found for this address