Forkchoice Ethereum Mainnet

Address Contract Verified

Address 0xFA6AE0942177E41A40f8F01EeD98d1200B66544D
Balance 0 ETH
Nonce 1
Code Size 18094 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

18094 bytes
0x608060405234801561001057600080fd5b50600436106102955760003560e01c80637d72150411610167578063d4b97046116100ce578063ee27c68911610087578063ee27c6891461053d578063f6b06e7014610545578063f811d6921461054d578063fd1ed7e914610555578063fe24cb7f14610570578063fff6cae91461057857610295565b8063d4b97046146104ec578063d505accf146104f4578063dd62ed3e14610507578063dd93f59a1461051a578063e539ef491461052d578063ec2fd46d1461053557610295565b8063ab44a7a311610120578063ab44a7a31461049b578063b56ceaa6146104a3578063bbf5ce78146104b6578063bd6015b4146104be578063bf357dae146104d1578063d0a494e4146104d957610295565b80637d7215041461043b5780637ecebe0014610443578063880a4d871461045657806395d89b411461046b578063a382d1b914610473578063a9059cbb1461048857610295565b8063440966091161020b57806366410a21116101c457806366410a21146103cd57806370a08231146103f057806371f9100c1461040357806373d74cf81461040b57806377f586571461041357806379a048761461042857610295565b8063440966091461036b5780634a248d2a1461037e5780634c85b425146103865780635039972a146103a857806354fd4d50146103bd57806365f6fcbb146103c557610295565b80632df6cb481161025d5780632df6cb481461031357806330adf81f1461031b578063313ce5671461032357806336223ce9146103385780633644e5151461034e5780634322ec831461035657610295565b806306fdde031461029a578063095ea7b3146102b857806317101940146102d857806318160ddd146102eb57806323b872dd14610300575b600080fd5b6102a2610580565b6040516102af91906140db565b60405180910390f35b6102cb6102c6366004613c99565b61060e565b6040516102af9190614034565b6102a26102e6366004613b13565b610625565b6102f361074f565b6040516102af919061403f565b6102cb61030e366004613beb565b610755565b6102cb6108d9565b6102f36108e8565b61032b61090c565b6040516102af91906145a1565b610340610915565b6040516102af929190614511565b6102f3610930565b61035e610936565b6040516102af9190613f0a565b610340610379366004613b13565b61094c565b61035e6109d7565b610399610394366004613b13565b6109e6565b6040516102af9392919061454a565b6103bb6103b6366004613b62565b610d7b565b005b6102a26110b8565b6102f36110dc565b6103e06103db366004613c99565b611183565b6040516102af949392919061451f565b6102f36103fe366004613b13565b611271565b6102f361128c565b6103bb6112d8565b61041b6113d5565b6040516102af91906144fd565b6103e0610436366004613c99565b6113eb565b61041b6114cc565b6102f3610451366004613b13565b6114db565b61045e6114ed565b6040516102af9190614590565b6102a2611500565b61047b61155b565b6040516102af91906144a7565b6102cb610496366004613c99565b6115e8565b6102f36116b7565b6103406104b1366004613cf7565b6116bd565b61041b611abb565b6102f36104cc366004613b13565b611ad1565b61045e611d82565b6103bb6104e7366004613d71565b611d95565b61035e612382565b6103bb610502366004613c2b565b612391565b6102f3610515366004613b2e565b612500565b6102f3610528366004613b13565b61252b565b61041b6127c6565b6102f36127d5565b6102f36127db565b61035e6127ed565b6102f36127fc565b61055d612802565b6040516102af9796959493929190614560565b6102f3612865565b6103bb61286b565b6008805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156106065780601f106105db57610100808354040283529160200191610606565b820191906000526020600020905b8154815290600101906020018083116105e957829003601f168201915b505050505081565b600061061b3384846128af565b5060015b92915050565b604080518082018252601081526f181899199a1a9b1b9c1cb0b131b232b360811b6020820152815160088082528184019093526060926001600160a01b0385169291849160208201818036833701905050905060005b6004811015610744578260048583600c016020811061069657fe5b1a60f81b6001600160f81b031916901c60f81c60ff16815181106106b657fe5b602001015160f81c60f81b8282600202815181106106d057fe5b60200101906001600160f81b031916908160001a905350828482600c01602081106106f757fe5b825191901a600f1690811061070857fe5b602001015160f81c60f81b82826002026001018151811061072557fe5b60200101906001600160f81b031916908160001a90535060010161067b565b50925050505b919050565b60095481565b6001600160a01b0383166000908152600a60205260408120548211156107965760405162461bcd60e51b815260040161078d90614375565b60405180910390fd5b6001600160a01b0384166000908152600b602090815260408083203384529091529020548211156107d95760405162461bcd60e51b815260040161078d90614192565b6001600160a01b0384166000908152600a6020526040902054610802908363ffffffff61291716565b6001600160a01b038086166000908152600a60205260408082209390935590851681522054610837908363ffffffff61293f16565b6001600160a01b038085166000908152600a60209081526040808320949094559187168152600b8252828120338252909152205461087b908363ffffffff61291716565b6001600160a01b038086166000818152600b60209081526040808320338452909152908190209390935591519085169190600080516020614619833981519152906108c790869061403f565b60405180910390a35060019392505050565b60005462010000900460ff1681565b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b60075460ff1681565b6003546001600160701b0380821692600160701b9092041690565b600c5481565b600054630100000090046001600160a01b031681565b600f54600e54604051638198edbf60e01b81526000916001600160a01b031690638198edbf90610980908690600401613f0a565b60206040518083038186803b15801561099857600080fd5b505afa1580156109ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109d09190613cdf565b9050915091565b6001546001600160a01b031681565b600080548190819060ff1615610a0e5760405162461bcd60e51b815260040161078d9061421f565b6000805460ff191660019081178255546040516370a0823160e01b81526001600160a01b03909116906370a0823190610a4b903090600401613f0a565b60206040518083038186803b158015610a6357600080fd5b505afa158015610a77573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a9b9190613cdf565b6002546040516370a0823160e01b81529192506000916001600160a01b03909116906370a0823190610ad1903090600401613f0a565b60206040518083038186803b158015610ae957600080fd5b505afa158015610afd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b219190613cdf565b6003549091506001600160701b0380821691600160701b900416610b4b848363ffffffff61291716565b9550610b5d838263ffffffff61291716565b945060008611610b7f5760405162461bcd60e51b815260040161078d906142ae565b600954610c0157610b928460115461296b565b8310610b9e5783610baa565b610baa83601154612995565b600580546001600160701b0319166001600160701b038316179055601154909750610bd690889061296b565b6005600e6101000a8154816001600160701b0302191690836001600160701b03160217905550610cfd565b600082118015610c115750600081115b15610cfd576000610c228784612995565b90506000610c308784612995565b90506000828210610c415782610c43565b815b9050610c516009548261296b565b600554909a50610c8690610c6e906001600160701b03168361296b565b6005546001600160701b03169063ffffffff61293f16565b600580546001600160701b0319166001600160701b039283161790819055610cd391610cba91600160701b9004168361296b565b600554600160701b90046001600160701b03169061293f565b6005600e6101000a8154816001600160701b0302191690836001600160701b031602179055505050505b610d0788886129b3565b610d118484612aad565b6001600160a01b0388166000908152600a6020526040908190205490517f1c172440bdebb59cd92a7f08f4227903a3305ab6f880cb25f93eddb66843a10291610d5d918b918b91614013565b60405180910390a150506000805460ff191690555092949193509150565b600054610100900460ff1615610da35760405162461bcd60e51b815260040161078d9061410e565b6000805461ff0019166101001790556001600160a01b038781169087161415610dde5760405162461bcd60e51b815260040161078d90614242565b600180546001600160a01b03808a166001600160a01b03199283161790925560028054928916929091169190911790558215801590610e2c57506ec097ce7bc90715b34b9f10000000008311155b610e3557600080fd5b6011839055670de0b6b3a7640000821115610e4f57600080fd5b6010829055600f859055600e80546001600160a01b038087166001600160a01b0319909216919091179091556000805483158015620100000262ff000019948d166301000000026301000000600160b81b03199093169290921793909316179055610ed257600380546001600160e01b03164263ffffffff16600160e01b021790555b60408051808201825260018152605f60f81b602080830191909152825180840190935260038352620444c560ec1b90830152908082610f1030610625565b604051602001610f2293929190613eaa565b60405160208183030381529060405260089080519060200190610f469291906139dd565b50604080518082019091526003808252620444c560ec1b6020909201918252610f71916006916139dd565b50600160009054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610fc057600080fd5b505afa158015610fd4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ff89190613dd8565b6007805460ff191660ff9290921691909117905560405146907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f9061103f90600890613e3a565b60408051918290038220828201825260018352603160f81b602093840152905161109093927fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc69186913091016140af565b60408051601f198184030181529190528051602090910120600c555050505050505050505050565b60408051808201909152600981526844535020312e302e3160b81b60208201525b90565b6003546001546040516370a0823160e01b815260009261117e926001600160701b03909116916001600160a01b03909116906370a0823190611122903090600401613f0a565b60206040518083038186803b15801561113a57600080fd5b505afa15801561114e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111729190613cdf565b9063ffffffff61291716565b905090565b600080600080611191613a5b565b61119961155b565b90506111a58187612b3d565b600f54600e54604051638198edbf60e01b8152939850919550916000916001600160a01b031690638198edbf906111e0908c90600401613f0a565b60206040518083038186803b1580156111f857600080fd5b505afa15801561120c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112309190613cdf565b905061123c878261296b565b955061125c8661117261124f8a8661296b565b8a9063ffffffff61291716565b96508260a00151935050505092959194509250565b6001600160a01b03166000908152600a602052604090205490565b6003546002546040516370a0823160e01b815260009261117e92600160701b9091046001600160701b0316916001600160a01b03909116906370a0823190611122903090600401613f0a565b60055463ffffffff600160e01b90910416600214801561130857506005546003546001600160701b039182169116105b1561135057600580546003546001600160701b03808216600160701b600160e01b0390931692909217600160701b600160e01b031916600160701b9182900492909216021790555b60055463ffffffff600160e01b90910416600114801561138b57506005546003546001600160701b03600160701b9283900481169290910416105b156113d357600580546003546001600160701b03808216600160701b600160e01b0390931692909217600160701b600160e01b031916600160701b9182900492909216021790555b565b600554600160701b90046001600160701b031681565b6000806000806113f9613a5b565b61140161155b565b905061140d8187612c43565b600f54600e54604051638198edbf60e01b8152939850919550916000916001600160a01b031690638198edbf90611448908c90600401613f0a565b60206040518083038186803b15801561146057600080fd5b505afa158015611474573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114989190613cdf565b90506114a4878261296b565b95506114b78661117261124f8a8661296b565b96508260800151935050505092959194509250565b6003546001600160701b031681565b600d6020526000908152604090205481565b600354600160e01b900463ffffffff1681565b6006805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156106065780601f106105db57610100808354040283529160200191610606565b611563613a5b565b601154815260105460208201526003546001600160701b038082166040840152600160701b918290048116606084015260055480821660808501529182041660a0830152600160e01b900463ffffffff1660028111156115bf57fe5b8160c0019060028111156115cf57fe5b908160028111156115dc57fe5b9052506110d981612d44565b336000908152600a60205260408120548211156116175760405162461bcd60e51b815260040161078d90614375565b336000908152600a6020526040902054611637908363ffffffff61291716565b336000908152600a6020526040808220929092556001600160a01b03851681522054611669908363ffffffff61293f16565b6001600160a01b0384166000818152600a6020526040908190209290925590513390600080516020614619833981519152906116a690869061403f565b60405180910390a350600192915050565b600f5481565b60008054819060ff16156116e35760405162461bcd60e51b815260040161078d9061421f565b6000805460ff19166001179055428310156117105760405162461bcd60e51b815260040161078d9061445e565b336000908152600a602052604090205489111561173f5760405162461bcd60e51b815260040161078d9061434d565b6001546040516370a0823160e01b81526000916001600160a01b0316906370a0823190611770903090600401613f0a565b60206040518083038186803b15801561178857600080fd5b505afa15801561179c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117c09190613cdf565b6002546040516370a0823160e01b81529192506000916001600160a01b03909116906370a08231906117f6903090600401613f0a565b60206040518083038186803b15801561180e57600080fd5b505afa158015611822573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118469190613cdf565b60095490915061186c81611860858f63ffffffff612dee16565b9063ffffffff612e2816565b945061188281611860848f63ffffffff612dee16565b93506118e06118c8826118bc8f600560009054906101000a90046001600160701b03166001600160701b0316612dee90919063ffffffff16565b9063ffffffff612e5216565b6005546001600160701b03169063ffffffff61291716565b600560006101000a8154816001600160701b0302191690836001600160701b0316021790555061195761193e826118bc8f6005600e9054906101000a90046001600160701b03166001600160701b0316612dee90919063ffffffff16565b600554600160701b90046001600160701b031690612917565b6005600e6101000a8154816001600160701b0302191690836001600160701b0316021790555089851015801561198d5750888410155b6119a95760405162461bcd60e51b815260040161078d90614320565b6119b3338d612e7f565b6119bd8b86612f4c565b6119c78b85612f6f565b6119cf612f92565b8615611a3e578a6001600160a01b0316632411d338338e88888d8d6040518763ffffffff1660e01b8152600401611a0b96959493929190613f80565b600060405180830381600087803b158015611a2557600080fd5b505af1158015611a39573d6000803e3d6000fd5b505050505b7f55caccde83781f39bfc1296eff45655b6496729443a7d48958b18b3b685600a5338c8e600a6000336001600160a01b03166001600160a01b0316815260200190815260200160002054604051611a989493929190613f1e565b60405180910390a150506000805460ff1916905550909890975095505050505050565b600354600160701b90046001600160701b031681565b6000805460ff1615611af55760405162461bcd60e51b815260040161078d9061421f565b6000805460ff191660019081178255546040516370a0823160e01b81526001600160a01b03909116906370a0823190611b32903090600401613f0a565b60206040518083038186803b158015611b4a57600080fd5b505afa158015611b5e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b829190613cdf565b600354909150600090611ba59083906001600160701b031663ffffffff61291716565b90506000806000611bb632856113eb565b9298509094509092509050611bcb8787612f6f565b600054611be890630100000090046001600160a01b031684612f6f565b806002811115611bf457fe5b600554600160e01b900463ffffffff908116911614611ca1576001600160701b03821115611c345760405162461bcd60e51b815260040161078d906143a1565b600580546001600160701b0319166001600160701b038416179055806002811115611c5b57fe5b6005601c6101000a81548163ffffffff021916908363ffffffff16021790555060008051602061463983398151915281604051611c98919061409a565b60405180910390a15b6002546040516370a0823160e01b8152611d2c9187916001600160a01b03909116906370a0823190611cd7903090600401613f0a565b60206040518083038186803b158015611cef57600080fd5b505afa158015611d03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d279190613cdf565b612aad565b60015460025460405160008051602061465983398151915292611d65926001600160a01b039182169291169088908b9033908e90613fc0565b60405180910390a150506000805460ff1916905550919392505050565b600554600160e01b900463ffffffff1681565b60005460ff1615611db85760405162461bcd60e51b815260040161078d9061421f565b6000805460ff19166001179055611dcf8386612f4c565b611dd98385612f6f565b8015611e465760405163d5b9979760e01b81526001600160a01b0384169063d5b9979790611e139033908990899088908890600401613f47565b600060405180830381600087803b158015611e2d57600080fd5b505af1158015611e41573d6000803e3d6000fd5b505050505b6001546040516370a0823160e01b81526000916001600160a01b0316906370a0823190611e77903090600401613f0a565b60206040518083038186803b158015611e8f57600080fd5b505afa158015611ea3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ec79190613cdf565b6002546040516370a0823160e01b81529192506000916001600160a01b03909116906370a0823190611efd903090600401613f0a565b60206040518083038186803b158015611f1557600080fd5b505afa158015611f29573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f4d9190613cdf565b6003549091506001600160701b031682101580611f7c5750600354600160701b90046001600160701b03168110155b611f985760405162461bcd60e51b815260040161078d90614137565b6003546001600160701b031682101561216257600354600090611fcc908390600160701b90046001600160701b0316612917565b9050600080600080611fde3286611183565b600354939750919550935091508490612006906001600160701b03168963ffffffff61291716565b11156120245760405162461bcd60e51b815260040161078d90614137565b60005461204190630100000090046001600160a01b031684612f4c565b81600281111561204d57fe5b600554600160e01b900463ffffffff908116911614612103576001600160701b0381111561208d5760405162461bcd60e51b815260040161078d906143a1565b60058054600160701b600160e01b031916600160701b6001600160701b038416021790558160028111156120bd57fe5b6005601c6101000a81548163ffffffff021916908363ffffffff160217905550600080516020614639833981519152826040516120fa919061409a565b60405180910390a15b600080516020614659833981519152600260009054906101000a90046001600160a01b0316600160009054906101000a90046001600160a01b03168787338f60405161215496959493929190613fc0565b60405180910390a150505050505b600354600160701b90046001600160701b031681101561232a5760035460009061219c9084906001600160701b031663ffffffff61291716565b90506000806000806121ae32866113eb565b6003549397509195509350915084906121d790600160701b90046001600160701b031688612917565b11156121f55760405162461bcd60e51b815260040161078d90614137565b60005461221290630100000090046001600160a01b031684612f6f565b81600281111561221e57fe5b600554600160e01b900463ffffffff9081169116146122cb576001600160701b0381111561225e5760405162461bcd60e51b815260040161078d906143a1565b600580546001600160701b0319166001600160701b03831617905581600281111561228557fe5b6005601c6101000a81548163ffffffff021916908363ffffffff160217905550600080516020614639833981519152826040516122c2919061409a565b60405180910390a15b600080516020614659833981519152600160009054906101000a90046001600160a01b0316600260009054906101000a90046001600160a01b03168787338f60405161231c96959493929190613fc0565b60405180910390a150505050505b612332612f92565b7f0b82e93068db15abd9fbb2682c65462ea8a0a10582dce93a5664818e296f54eb338689896040516123679493929190613f1e565b60405180910390a150506000805460ff191690555050505050565b6002546001600160a01b031681565b428410156123b15760405162461bcd60e51b815260040161078d906143c3565b600c546001600160a01b0388166000908152600d602090815260408083208054600181019091559051929392612412927f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9928d928d928d92918d9101614048565b60405160208183030381529060405280519060200120604051602001612439929190613eef565b604051602081830303815290604052805190602001209050600060018286868660405160008152602001604052604051612476949392919061407c565b6020604051602081039080840390855afa158015612498573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116158015906124ce5750886001600160a01b0316816001600160a01b0316145b6124ea5760405162461bcd60e51b815260040161078d906141e8565b6124f58989896128af565b505050505050505050565b6001600160a01b039182166000908152600b6020908152604080832093909416825291909152205490565b6000805460ff161561254f5760405162461bcd60e51b815260040161078d9061421f565b6000805460ff191660011781556002546040516370a0823160e01b81526001600160a01b03909116906370a082319061258c903090600401613f0a565b60206040518083038186803b1580156125a457600080fd5b505afa1580156125b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125dc9190613cdf565b600354909150600090612600908390600160701b90046001600160701b0316612917565b905060008060006126113285611183565b92985090945090925090506126268787612f4c565b60005461264390630100000090046001600160a01b031684612f4c565b80600281111561264f57fe5b600554600160e01b900463ffffffff908116911614612705576001600160701b0382111561268f5760405162461bcd60e51b815260040161078d906143a1565b60058054600160701b600160e01b031916600160701b6001600160701b038516021790558060028111156126bf57fe5b6005601c6101000a81548163ffffffff021916908363ffffffff160217905550600080516020614639833981519152816040516126fc919061409a565b60405180910390a15b6001546040516370a0823160e01b815261278d916001600160a01b0316906370a0823190612737903090600401613f0a565b60206040518083038186803b15801561274f57600080fd5b505afa158015612763573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127879190613cdf565b86612aad565b60025460015460405160008051602061465983398151915292611d65926001600160a01b039182169291169088908b9033908e90613fc0565b6005546001600160701b031681565b60105481565b600061117e6127e861155b565b61315a565b600e546001600160a01b031681565b60115481565b6000806000806000806000612815613a5b565b61281d61155b565b905080600001519750806020015196508060400151955080606001519450806080015193508060a0015192508060c00151600281111561285957fe5b91505090919293949596565b60045481565b60005460ff161561288e5760405162461bcd60e51b815260040161078d9061421f565b6000805460ff191660011790556128a3612f92565b6000805460ff19169055565b6001600160a01b038084166000818152600b602090815260408083209487168084529490915290819020849055517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259061290a90859061403f565b60405180910390a3505050565b6000828211156129395760405162461bcd60e51b815260040161078d906142fd565b50900390565b6000828201838110156129645760405162461bcd60e51b815260040161078d906143f1565b9392505050565b6000670de0b6b3a7640000612986848463ffffffff612dee16565b8161298d57fe5b049392505050565b60006129648261186085670de0b6b3a764000063ffffffff612dee16565b6103e881116129d45760405162461bcd60e51b815260040161078d90614162565b6001600160a01b0382166000908152600a60205260409020546129fd908263ffffffff61293f16565b6001600160a01b0383166000908152600a6020526040902055600954612a29908263ffffffff61293f16565b6009556040516001600160a01b038316907f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d412139688590612a6790849061403f565b60405180910390a2816001600160a01b031660006001600160a01b031660008051602061461983398151915283604051612aa1919061403f565b60405180910390a35050565b6001600160701b038211801590612acb57506001600160701b038111155b612ae75760405162461bcd60e51b815260040161078d906143a1565b600380546001600160701b03838116600160701b02600160701b600160e01b03199186166001600160701b0319909316929092171617905560005460ff620100009091041615612b3957612b3961324c565b5050565b600080808460c001516002811115612b5157fe5b1415612b6c57612b6184846132e1565b915060019050612c3c565b60018460c001516002811115612b7e57fe5b1415612b8e57612b618484613308565b6000612bab85606001518660a0015161291790919063ffffffff16565b90506000612bca8660800151876040015161291790919063ffffffff16565b905081851015612bf557612bde8686613325565b93506002925080841115612bf0578093505b612c39565b81851415612c095780935060009250612c39565b612c32612c2587612c20888663ffffffff61291716565b6132e1565b829063ffffffff61293f16565b9350600192505b50505b9250929050565b600080808460c001516002811115612c5757fe5b1415612c7257612c678484613360565b915060029050612c3c565b60018460c001516002811115612c8457fe5b1415612d2f576000612ca78560400151866080015161291790919063ffffffff16565b90506000612cc68660a00151876060015161291790919063ffffffff16565b905081851015612cf157612cda868661337f565b93506001925080841115612cec578093505b612d28565b81851415612d055780935060009250612d28565b612d21612c2587612d1c888663ffffffff61291716565b613360565b9350600292505b5050612c3c565b612d3984846133b1565b946002945092505050565b60028160c001516002811115612d5657fe5b1415612d9557612d8b8160600151612d7f8360800151846040015161291790919063ffffffff16565b835160208501516133d0565b60a0820152612deb565b60018160c001516002811115612da757fe5b1415612deb57612de58160400151612dd08360a00151846060015161291790919063ffffffff16565b8351612ddb906134f6565b84602001516133d0565b60808201525b50565b600082612dfd5750600061061f565b82820282848281612e0a57fe5b04146129645760405162461bcd60e51b815260040161078d90614484565b6000808211612e495760405162461bcd60e51b815260040161078d906142d5565b81838161298d57fe5b600080612e5f8484612e28565b905082810284038015612e775750600101905061061f565b50905061061f565b6001600160a01b0382166000908152600a6020526040902054612ea8908263ffffffff61291716565b6001600160a01b0383166000908152600a6020526040902055600954612ed4908263ffffffff61291716565b6009556040516001600160a01b038316907fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca590612f1290849061403f565b60405180910390a260006001600160a01b0316826001600160a01b031660008051602061461983398151915283604051612aa1919061403f565b8015612b3957600154612b39906001600160a01b0316838363ffffffff61351716565b8015612b3957600254612b39906001600160a01b0316838363ffffffff61351716565b6001546040516370a0823160e01b81526000916001600160a01b0316906370a0823190612fc3903090600401613f0a565b60206040518083038186803b158015612fdb57600080fd5b505afa158015612fef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130139190613cdf565b6002546040516370a0823160e01b81529192506000916001600160a01b03909116906370a0823190613049903090600401613f0a565b60206040518083038186803b15801561306157600080fd5b505afa158015613075573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130999190613cdf565b90506001600160701b0382118015906130b957506001600160701b038111155b6130d55760405162461bcd60e51b815260040161078d906143a1565b6003546001600160701b0316821461310357600380546001600160701b0319166001600160701b0384161790555b600354600160701b90046001600160701b031681146131415760038054600160701b600160e01b031916600160701b6001600160701b038416021790555b60005462010000900460ff1615612b3957612b3961324c565b600060028260c00151600281111561316e57fe5b14156131f957606082015160a08301516000916131a49161319a9190611860908063ffffffff612dee16565b8460600151612995565b90506131e16131b784602001518361296b565b60208501516131d590670de0b6b3a76400009063ffffffff61291716565b9063ffffffff61293f16565b90506131f1836000015182612995565b91505061074a565b604082015160808301516000916132299161321f9190611860908063ffffffff612dee16565b8460400151612995565b905061323c6131b784602001518361296b565b90506131f183600001518261296b565b60035463ffffffff42811691600160e01b9004811682039081161580159061327e57506003546001600160701b031615155b801561329b5750600354600160701b90046001600160701b031615155b156132bb578063ffffffff166132af6127db565b60048054919092020190555b506003805463ffffffff909216600160e01b026001600160e01b03909216919091179055565b600061296483608001518460800151846132fe87600001516134f6565b8760200151613572565b600061296483608001518460400151846132fe87600001516134f6565b60006129648360a0015161334684866060015161293f90919063ffffffff16565b60608601518651613356906134f6565b87602001516137f2565b60006129648360a001518460a001518486600001518760200151613572565b600061296483608001516133a084866040015161293f90919063ffffffff16565b6040860151865160208801516137f2565b60006129648360a0015184606001518486600001518760200151613572565b6000846133df575060006134ee565b81613405576133fe6133f1848661296b565b869063ffffffff61293f16565b90506134ee565b60008061341b600485028663ffffffff612dee16565b90508061343257670de0b6b3a764000091506134b2565b85818783028161343e57fe5b0414156134795761347261346d6ec097ce7bc90715b34b9f10000000006131d5848a028b63ffffffff612e2816565b6138cb565b91506134b2565b6134af61346d6ec097ce7bc90715b34b9f10000000006131d5896134a3868d63ffffffff612e2816565b9063ffffffff612dee16565b91505b60006134dc670de0b6b3a76400006131d56134d3868363ffffffff61291716565b88600202612995565b90506134e8888261296b565b93505050505b949350505050565b600061061f6ec097ce7bc90715b34b9f10000000008363ffffffff612e2816565b61356d8363a9059cbb60e01b8484604051602401613536929190613ffa565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613902565b505050565b60008086116135935760405162461bcd60e51b815260040161078d906141c0565b836135a0575060006137e9565b816135cd57846135b0848661296b565b116135c4576135bf838561296b565b6135c6565b845b90506137e9565b670de0b6b3a764000082141561368d576000806135f0858763ffffffff612dee16565b9050806136005760009150613659565b86818883028161360c57fe5b04141561363c57613635613626898063ffffffff612dee16565b8289029063ffffffff612e2816565b9150613659565b61365688611860876134a383838c8e63ffffffff612dee16565b91505b61368461367483670de0b6b3a764000063ffffffff61293f16565b611860898563ffffffff612dee16565b925050506137e9565b60006136ba6136a2858763ffffffff612dee16565b6131d5896134a38a611860898463ffffffff612dee16565b905060006136da876134a3670de0b6b3a76400008763ffffffff61291716565b905060008282106136f0575081900360006136f6565b50810360015b61370e82670de0b6b3a764000063ffffffff612e2816565b9150600061374561373260046134a3670de0b6b3a76400008a63ffffffff61291716565b6137408c6134a38a8f61296b565b61296b565b905061375e61346d826131d5868063ffffffff612dee16565b9050600061377f60026134a3670de0b6b3a76400008a63ffffffff61291716565b9050600083156137a057613799838663ffffffff61291716565b90506137b3565b6137b0858463ffffffff61293f16565b90505b60006137bf82846139bf565b90508b8111156137d95760009750505050505050506137e9565b8b0396506137e995505050505050565b95945050505050565b60008086116138135760405162461bcd60e51b815260040161078d906141c0565b6000613835613828878763ffffffff61291716565b859063ffffffff612dee16565b90508261385c5761385481670de0b6b3a764000063ffffffff612e2816565b9150506137e9565b600061387b613875886118608b8063ffffffff612dee16565b87612995565b90506000613889858361296b565b90506138be6ec097ce7bc90715b34b9f1000000000611860856134a3856131d5670de0b6b3a76400008c63ffffffff61291716565b9998505050505050505050565b80600160028204015b818110156138fc578091506002818285816138eb57fe5b0401816138f457fe5b0490506138d4565b50919050565b60006060836001600160a01b03168360405161391e9190613e1e565b6000604051808303816000865af19150503d806000811461395b576040519150601f19603f3d011682016040523d82523d6000602084013e613960565b606091505b5091509150816139825760405162461bcd60e51b815260040161078d90614279565b8051156139b9578080602001905181019061399d9190613cc3565b6139b95760405162461bcd60e51b815260040161078d90614414565b50505050565b6000612964826118bc85670de0b6b3a764000063ffffffff612dee16565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10613a1e57805160ff1916838001178555613a4b565b82800160010185558215613a4b579182015b82811115613a4b578251825591602001919060010190613a30565b50613a57929150613aa2565b5090565b6040518060e0016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160006002811115613a9d57fe5b905290565b6110d991905b80821115613a575760008155600101613aa8565b80356001600160a01b038116811461061f57600080fd5b60008083601f840112613ae4578182fd5b50813567ffffffffffffffff811115613afb578182fd5b602083019150836020828501011115612c3c57600080fd5b600060208284031215613b24578081fd5b6129648383613abc565b60008060408385031215613b40578081fd5b613b4a8484613abc565b9150613b598460208501613abc565b90509250929050565b600080600080600080600080610100898b031215613b7e578384fd5b613b888a8a613abc565b9750613b978a60208b01613abc565b9650613ba68a60408b01613abc565b955060608901359450613bbc8a60808b01613abc565b935060a0890135925060c0890135915060e0890135613bda816145fb565b809150509295985092959890939650565b600080600060608486031215613bff578283fd5b8335613c0a816145e6565b92506020840135613c1a816145e6565b929592945050506040919091013590565b600080600080600080600060e0888a031215613c45578283fd5b613c4f8989613abc565b9650613c5e8960208a01613abc565b955060408801359450606088013593506080880135613c7c81614609565b9699959850939692959460a0840135945060c09093013592915050565b60008060408385031215613cab578182fd5b613cb58484613abc565b946020939093013593505050565b600060208284031215613cd4578081fd5b8151612964816145fb565b600060208284031215613cf0578081fd5b5051919050565b600080600080600080600060c0888a031215613d11578283fd5b873596506020880135613d23816145e6565b95506040880135945060608801359350608088013567ffffffffffffffff811115613d4c578384fd5b613d588a828b01613ad3565b989b979a5095989497959660a090950135949350505050565b600080600080600060808688031215613d88578081fd5b8535945060208601359350613da08760408801613abc565b9250606086013567ffffffffffffffff811115613dbb578182fd5b613dc788828901613ad3565b969995985093965092949392505050565b600060208284031215613de9578081fd5b815161296481614609565b60008284528282602086013780602084860101526020601f19601f85011685010190509392505050565b60008251613e308184602087016145ba565b9190910192915050565b6000808354600180821660008114613e595760018114613e7057613e9f565b60ff198316865260028304607f1686019350613e9f565b600283048786526020808720875b83811015613e975781548a820152908501908201613e7e565b505050860193505b509195945050505050565b60008451613ebc8184602089016145ba565b8451908301613ecf8282602089016145ba565b8451918101613ee28382602089016145ba565b9091019695505050505050565b61190160f01b81526002810192909252602282015260420190565b6001600160a01b0391909116815260200190565b6001600160a01b0394851681529290931660208301526040820152606081019190915260800190565b600060018060a01b038716825285602083015284604083015260806060830152613f75608083018486613df4565b979650505050505050565b600060018060a01b038816825286602083015285604083015284606083015260a06080830152613fb460a083018486613df4565b98975050505050505050565b6001600160a01b0396871681529486166020860152604085019390935260608401919091528316608083015290911660a082015260c00190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b039390931683526020830191909152604082015260600190565b901515815260200190565b90815260200190565b9586526001600160a01b0394851660208701529290931660408501526060840152608083019190915260a082015260c00190565b93845260ff9290921660208401526040830152606082015260800190565b602081016140a7836145af565b825292915050565b9485526020850193909352604084019190915260608301526001600160a01b0316608082015260a00190565b60006020825282518060208401526140fa8160408501602087016145ba565b601f01601f19169190910160400192915050565b6020808252600f908201526e1114d417d253925512505312569151608a1b604082015260600190565b60208082526011908201527011931054d217d313d05397d19052531151607a1b604082015260600190565b60208082526016908201527509a929ca8be829a9eaa9ca8be9c9ea8be8a9c9eaa8e960531b604082015260600190565b60208082526014908201527308298989eae829c868abe9c9ea8be8a9c9eaa8e960631b604082015260600190565b6020808252600e908201526d5441524745545f49535f5a45524f60901b604082015260600190565b6020808252601e908201527f444f444f5f4453505f4c503a20494e56414c49445f5349474e41545552450000604082015260600190565b60208082526009908201526814915153951490539560ba1b604082015260600190565b6020808252601a908201527f424153455f51554f54455f43414e5f4e4f545f42455f53414d45000000000000604082015260600190565b6020808252818101527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604082015260600190565b6020808252600d908201526c1393d7d09054d157d253941555609a1b604082015260600190565b6020808252600e908201526d2224ab24a224a723afa2a92927a960911b604082015260600190565b60208082526009908201526829aaa12fa2a92927a960b91b604082015260600190565b6020808252601390820152720ae92a89088a482aebe9c9ea8be8a9c9eaa8e9606b1b604082015260600190565b6020808252600e908201526d08898a0be9c9ea8be8a9c9eaa8e960931b604082015260600190565b6020808252601290820152710848298829c868abe9c9ea8be8a9c9eaa8e960731b604082015260600190565b6020808252600890820152674f564552464c4f5760c01b604082015260600190565b6020808252601490820152731113d113d7d114d417d3140e881156141254915160621b604082015260600190565b60208082526009908201526820a2222fa2a92927a960b91b604082015260600190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b6020808252600c908201526b1512535157d156141254915160a21b604082015260600190565b60208082526009908201526826aaa62fa2a92927a960b91b604082015260600190565b600060e082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a08301526144f260c08401516145af565b60c083015292915050565b6001600160701b0391909116815260200190565b918252602082015260400190565b8481526020810184905260808101614536846145af565b604083015282606083015295945050505050565b9283526020830191909152604082015260600190565b968752602087019590955260408601939093526060850191909152608084015260a083015260c082015260e00190565b63ffffffff91909116815260200190565b60ff91909116815260200190565b806003811061074a57fe5b60005b838110156145d55781810151838201526020016145bd565b838111156139b95750506000910152565b6001600160a01b0381168114612deb57600080fd5b8015158114612deb57600080fd5b60ff81168114612deb57600080fdfeddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efdf176ad18be4f9f32efaa32f06e9d1175476504739a745f1399a6d3fa4b75917c2c0245e056d5fb095f04cd6373bc770802ebd1e6c918eb78fdef843cdb37b0fa26469706673582212207e43fe3c65a0960d957dd6869ff0e588f6bfb9fc4f4aaec6c20405c858f3167e64736f6c63430006090033

Verified Source Code Full Match

Compiler: v0.6.9+commit.3e3065ac EVM: istanbul Optimization: Yes (200 runs)
DSP.sol 1588 lines
// File: contracts/lib/InitializableOwnable.sol

/*

    Copyright 2020 DODO ZOO.
    SPDX-License-Identifier: Apache-2.0

*/

pragma solidity 0.6.9;
pragma experimental ABIEncoderV2;

/**
 * @title Ownable
 * @author DODO Breeder
 *
 * @notice Ownership related functions
 */
contract InitializableOwnable {
    address public _OWNER_;
    address public _NEW_OWNER_;
    bool internal _INITIALIZED_;

    // ============ Events ============

    event OwnershipTransferPrepared(address indexed previousOwner, address indexed newOwner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    // ============ Modifiers ============

    modifier notInitialized() {
        require(!_INITIALIZED_, "DODO_INITIALIZED");
        _;
    }

    modifier onlyOwner() {
        require(msg.sender == _OWNER_, "NOT_OWNER");
        _;
    }

    // ============ Functions ============

    function initOwner(address newOwner) public notInitialized {
        _INITIALIZED_ = true;
        _OWNER_ = newOwner;
    }

    function transferOwnership(address newOwner) public onlyOwner {
        emit OwnershipTransferPrepared(_OWNER_, newOwner);
        _NEW_OWNER_ = newOwner;
    }

    function claimOwnership() public {
        require(msg.sender == _NEW_OWNER_, "INVALID_CLAIM");
        emit OwnershipTransferred(_OWNER_, _NEW_OWNER_);
        _OWNER_ = _NEW_OWNER_;
        _NEW_OWNER_ = address(0);
    }
}

// File: contracts/lib/FeeRateModel.sol



interface IFeeRateImpl {
    function getFeeRate(address pool, address trader) external view returns (uint256);
}

interface IFeeRateModel {
    function getFeeRate(address trader) external view returns (uint256);
}

contract FeeRateModel is InitializableOwnable {
    address public feeRateImpl;

    function setFeeProxy(address _feeRateImpl) public onlyOwner {
        feeRateImpl = _feeRateImpl;
    }
    
    function getFeeRate(address trader) external view returns (uint256) {
        if(feeRateImpl == address(0))
            return 0;
        return IFeeRateImpl(feeRateImpl).getFeeRate(msg.sender,trader);
    }
}

// File: contracts/intf/IERC20.sol

/**
 * @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);

    function decimals() external view returns (uint8);

    function name() external view returns (string memory);

    function symbol() external view returns (string memory);

    /**
     * @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);
}

// File: contracts/lib/SafeMath.sol



/**
 * @title SafeMath
 * @author DODO Breeder
 *
 * @notice Math operations with safety checks that revert on error
 */
library SafeMath {
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "MUL_ERROR");

        return c;
    }

    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "DIVIDING_ERROR");
        return a / b;
    }

    function divCeil(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 quotient = div(a, b);
        uint256 remainder = a - quotient * b;
        if (remainder > 0) {
            return quotient + 1;
        } else {
            return quotient;
        }
    }

    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SUB_ERROR");
        return a - b;
    }

    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "ADD_ERROR");
        return c;
    }

    function sqrt(uint256 x) internal pure returns (uint256 y) {
        uint256 z = x / 2 + 1;
        y = x;
        while (z < y) {
            y = z;
            z = (x / z + z) / 2;
        }
    }
}

// File: contracts/lib/DecimalMath.sol


/**
 * @title DecimalMath
 * @author DODO Breeder
 *
 * @notice Functions for fixed point number with 18 decimals
 */
library DecimalMath {
    using SafeMath for uint256;

    uint256 internal constant ONE = 10**18;
    uint256 internal constant ONE2 = 10**36;

    function mulFloor(uint256 target, uint256 d) internal pure returns (uint256) {
        return target.mul(d) / (10**18);
    }

    function mulCeil(uint256 target, uint256 d) internal pure returns (uint256) {
        return target.mul(d).divCeil(10**18);
    }

    function divFloor(uint256 target, uint256 d) internal pure returns (uint256) {
        return target.mul(10**18).div(d);
    }

    function divCeil(uint256 target, uint256 d) internal pure returns (uint256) {
        return target.mul(10**18).divCeil(d);
    }

    function reciprocalFloor(uint256 target) internal pure returns (uint256) {
        return uint256(10**36).div(target);
    }

    function reciprocalCeil(uint256 target) internal pure returns (uint256) {
        return uint256(10**36).divCeil(target);
    }
}

// File: contracts/lib/DODOMath.sol


/**
 * @title DODOMath
 * @author DODO Breeder
 *
 * @notice Functions for complex calculating. Including ONE Integration and TWO Quadratic solutions
 */
library DODOMath {
    using SafeMath for uint256;

    /*
        Integrate dodo curve from V1 to V2
        require V0>=V1>=V2>0
        res = (1-k)i(V1-V2)+ikV0*V0(1/V2-1/V1)
        let V1-V2=delta
        res = i*delta*(1-k+k(V0^2/V1/V2))

        i is the price of V-res trading pair

        support k=1 & k=0 case

        [round down]
    */
    function _GeneralIntegrate(
        uint256 V0,
        uint256 V1,
        uint256 V2,
        uint256 i,
        uint256 k
    ) internal pure returns (uint256) {
        require(V0 > 0, "TARGET_IS_ZERO");
        uint256 fairAmount = i.mul(V1.sub(V2)); // i*delta
        if (k == 0) {
            return fairAmount.div(DecimalMath.ONE);
        }
        uint256 V0V0V1V2 = DecimalMath.divFloor(V0.mul(V0).div(V1), V2);
        uint256 penalty = DecimalMath.mulFloor(k, V0V0V1V2); // k(V0^2/V1/V2)
        return DecimalMath.ONE.sub(k).add(penalty).mul(fairAmount).div(DecimalMath.ONE2);
    }

    /*
        Follow the integration function above
        i*deltaB = (Q2-Q1)*(1-k+kQ0^2/Q1/Q2)
        Assume Q2=Q0, Given Q1 and deltaB, solve Q0

        i is the price of delta-V trading pair
        give out target of V

        support k=1 & k=0 case

        [round down]
    */
    function _SolveQuadraticFunctionForTarget(
        uint256 V1,
        uint256 delta,
        uint256 i,
        uint256 k
    ) internal pure returns (uint256) {
        if (V1 == 0) {
            return 0;
        }
        if (k == 0) {
            return V1.add(DecimalMath.mulFloor(i, delta));
        }
        // V0 = V1*(1+(sqrt-1)/2k)
        // sqrt = √(1+4kidelta/V1)
        // premium = 1+(sqrt-1)/2k
        // uint256 sqrt = (4 * k).mul(i).mul(delta).div(V1).add(DecimalMath.ONE2).sqrt();
        uint256 sqrt;
        uint256 ki = (4 * k).mul(i);
        if (ki == 0) {
            sqrt = DecimalMath.ONE;
        } else if ((ki * delta) / ki == delta) {
            sqrt = (ki * delta).div(V1).add(DecimalMath.ONE2).sqrt();
        } else {
            sqrt = ki.div(V1).mul(delta).add(DecimalMath.ONE2).sqrt();
        }
        uint256 premium =
            DecimalMath.divFloor(sqrt.sub(DecimalMath.ONE), k * 2).add(DecimalMath.ONE);
        // V0 is greater than or equal to V1 according to the solution
        return DecimalMath.mulFloor(V1, premium);
    }

    /*
        Follow the integration expression above, we have:
        i*deltaB = (Q2-Q1)*(1-k+kQ0^2/Q1/Q2)
        Given Q1 and deltaB, solve Q2
        This is a quadratic function and the standard version is
        aQ2^2 + bQ2 + c = 0, where
        a=1-k
        -b=(1-k)Q1-kQ0^2/Q1+i*deltaB
        c=-kQ0^2 
        and Q2=(-b+sqrt(b^2+4(1-k)kQ0^2))/2(1-k)
        note: another root is negative, abondan

        if deltaBSig=true, then Q2>Q1, user sell Q and receive B
        if deltaBSig=false, then Q2<Q1, user sell B and receive Q
        return |Q1-Q2|

        as we only support sell amount as delta, the deltaB is always negative
        the input ideltaB is actually -ideltaB in the equation

        i is the price of delta-V trading pair

        support k=1 & k=0 case

        [round down]
    */
    function _SolveQuadraticFunctionForTrade(
        uint256 V0,
        uint256 V1,
        uint256 delta,
        uint256 i,
        uint256 k
    ) internal pure returns (uint256) {
        require(V0 > 0, "TARGET_IS_ZERO");
        if (delta == 0) {
            return 0;
        }

        if (k == 0) {
            return DecimalMath.mulFloor(i, delta) > V1 ? V1 : DecimalMath.mulFloor(i, delta);
        }

        if (k == DecimalMath.ONE) {
            // if k==1
            // Q2=Q1/(1+ideltaBQ1/Q0/Q0)
            // temp = ideltaBQ1/Q0/Q0
            // Q2 = Q1/(1+temp)
            // Q1-Q2 = Q1*(1-1/(1+temp)) = Q1*(temp/(1+temp))
            // uint256 temp = i.mul(delta).mul(V1).div(V0.mul(V0));
            uint256 temp;
            uint256 idelta = i.mul(delta);
            if (idelta == 0) {
                temp = 0;
            } else if ((idelta * V1) / idelta == V1) {
                temp = (idelta * V1).div(V0.mul(V0));
            } else {
                temp = delta.mul(V1).div(V0).mul(i).div(V0);
            }
            return V1.mul(temp).div(temp.add(DecimalMath.ONE));
        }

        // calculate -b value and sig
        // b = kQ0^2/Q1-i*deltaB-(1-k)Q1
        // part1 = (1-k)Q1 >=0
        // part2 = kQ0^2/Q1-i*deltaB >=0
        // bAbs = abs(part1-part2)
        // if part1>part2 => b is negative => bSig is false
        // if part2>part1 => b is positive => bSig is true
        uint256 part2 = k.mul(V0).div(V1).mul(V0).add(i.mul(delta)); // kQ0^2/Q1-i*deltaB
        uint256 bAbs = DecimalMath.ONE.sub(k).mul(V1); // (1-k)Q1

        bool bSig;
        if (bAbs >= part2) {
            bAbs = bAbs - part2;
            bSig = false;
        } else {
            bAbs = part2 - bAbs;
            bSig = true;
        }
        bAbs = bAbs.div(DecimalMath.ONE);

        // calculate sqrt
        uint256 squareRoot =
            DecimalMath.mulFloor(
                DecimalMath.ONE.sub(k).mul(4),
                DecimalMath.mulFloor(k, V0).mul(V0)
            ); // 4(1-k)kQ0^2
        squareRoot = bAbs.mul(bAbs).add(squareRoot).sqrt(); // sqrt(b*b+4(1-k)kQ0*Q0)

        // final res
        uint256 denominator = DecimalMath.ONE.sub(k).mul(2); // 2(1-k)
        uint256 numerator;
        if (bSig) {
            numerator = squareRoot.sub(bAbs);
        } else {
            numerator = bAbs.add(squareRoot);
        }

        uint256 V2 = DecimalMath.divCeil(numerator, denominator);
        if (V2 > V1) {
            return 0;
        } else {
            return V1 - V2;
        }
    }
}

// File: contracts/lib/PMMPricing.sol



/**
 * @title Pricing
 * @author DODO Breeder
 *
 * @notice DODO Pricing model
 */

library PMMPricing {
    using SafeMath for uint256;

    enum RState {ONE, ABOVE_ONE, BELOW_ONE}

    struct PMMState {
        uint256 i;
        uint256 K;
        uint256 B;
        uint256 Q;
        uint256 B0;
        uint256 Q0;
        RState R;
    }

    // ============ buy & sell ============

    function sellBaseToken(PMMState memory state, uint256 payBaseAmount)
        internal
        pure
        returns (uint256 receiveQuoteAmount, RState newR)
    {
        if (state.R == RState.ONE) {
            // case 1: R=1
            // R falls below one
            receiveQuoteAmount = _ROneSellBaseToken(state, payBaseAmount);
            newR = RState.BELOW_ONE;
        } else if (state.R == RState.ABOVE_ONE) {
            uint256 backToOnePayBase = state.B0.sub(state.B);
            uint256 backToOneReceiveQuote = state.Q.sub(state.Q0);
            // case 2: R>1
            // complex case, R status depends on trading amount
            if (payBaseAmount < backToOnePayBase) {
                // case 2.1: R status do not change
                receiveQuoteAmount = _RAboveSellBaseToken(state, payBaseAmount);
                newR = RState.ABOVE_ONE;
                if (receiveQuoteAmount > backToOneReceiveQuote) {
                    // [Important corner case!] may enter this branch when some precision problem happens. And consequently contribute to negative spare quote amount
                    // to make sure spare quote>=0, mannually set receiveQuote=backToOneReceiveQuote
                    receiveQuoteAmount = backToOneReceiveQuote;
                }
            } else if (payBaseAmount == backToOnePayBase) {
                // case 2.2: R status changes to ONE
                receiveQuoteAmount = backToOneReceiveQuote;
                newR = RState.ONE;
            } else {
                // case 2.3: R status changes to BELOW_ONE
                receiveQuoteAmount = backToOneReceiveQuote.add(
                    _ROneSellBaseToken(state, payBaseAmount.sub(backToOnePayBase))
                );
                newR = RState.BELOW_ONE;
            }
        } else {
            // state.R == RState.BELOW_ONE
            // case 3: R<1
            receiveQuoteAmount = _RBelowSellBaseToken(state, payBaseAmount);
            newR = RState.BELOW_ONE;
        }
    }

    function sellQuoteToken(PMMState memory state, uint256 payQuoteAmount)
        internal
        pure
        returns (uint256 receiveBaseAmount, RState newR)
    {
        if (state.R == RState.ONE) {
            receiveBaseAmount = _ROneSellQuoteToken(state, payQuoteAmount);
            newR = RState.ABOVE_ONE;
        } else if (state.R == RState.ABOVE_ONE) {
            receiveBaseAmount = _RAboveSellQuoteToken(state, payQuoteAmount);
            newR = RState.ABOVE_ONE;
        } else {
            uint256 backToOnePayQuote = state.Q0.sub(state.Q);
            uint256 backToOneReceiveBase = state.B.sub(state.B0);
            if (payQuoteAmount < backToOnePayQuote) {
                receiveBaseAmount = _RBelowSellQuoteToken(state, payQuoteAmount);
                newR = RState.BELOW_ONE;
                if (receiveBaseAmount > backToOneReceiveBase) {
                    receiveBaseAmount = backToOneReceiveBase;
                }
            } else if (payQuoteAmount == backToOnePayQuote) {
                receiveBaseAmount = backToOneReceiveBase;
                newR = RState.ONE;
            } else {
                receiveBaseAmount = backToOneReceiveBase.add(
                    _ROneSellQuoteToken(state, payQuoteAmount.sub(backToOnePayQuote))
                );
                newR = RState.ABOVE_ONE;
            }
        }
    }

    // ============ R = 1 cases ============

    function _ROneSellBaseToken(PMMState memory state, uint256 payBaseAmount)
        internal
        pure
        returns (
            uint256 // receiveQuoteToken
        )
    {
        // in theory Q2 <= targetQuoteTokenAmount
        // however when amount is close to 0, precision problems may cause Q2 > targetQuoteTokenAmount
        return
            DODOMath._SolveQuadraticFunctionForTrade(
                state.Q0,
                state.Q0,
                payBaseAmount,
                state.i,
                state.K
            );
    }

    function _ROneSellQuoteToken(PMMState memory state, uint256 payQuoteAmount)
        internal
        pure
        returns (
            uint256 // receiveBaseToken
        )
    {
        return
            DODOMath._SolveQuadraticFunctionForTrade(
                state.B0,
                state.B0,
                payQuoteAmount,
                DecimalMath.reciprocalFloor(state.i),
                state.K
            );
    }

    // ============ R < 1 cases ============

    function _RBelowSellQuoteToken(PMMState memory state, uint256 payQuoteAmount)
        internal
        pure
        returns (
            uint256 // receiveBaseToken
        )
    {
        return
            DODOMath._GeneralIntegrate(
                state.Q0,
                state.Q.add(payQuoteAmount),
                state.Q,
                DecimalMath.reciprocalFloor(state.i),
                state.K
            );
    }

    function _RBelowSellBaseToken(PMMState memory state, uint256 payBaseAmount)
        internal
        pure
        returns (
            uint256 // receiveQuoteToken
        )
    {
        return
            DODOMath._SolveQuadraticFunctionForTrade(
                state.Q0,
                state.Q,
                payBaseAmount,
                state.i,
                state.K
            );
    }

    // ============ R > 1 cases ============

    function _RAboveSellBaseToken(PMMState memory state, uint256 payBaseAmount)
        internal
        pure
        returns (
            uint256 // receiveQuoteToken
        )
    {
        return
            DODOMath._GeneralIntegrate(
                state.B0,
                state.B.add(payBaseAmount),
                state.B,
                state.i,
                state.K
            );
    }

    function _RAboveSellQuoteToken(PMMState memory state, uint256 payQuoteAmount)
        internal
        pure
        returns (
            uint256 // receiveBaseToken
        )
    {
        return
            DODOMath._SolveQuadraticFunctionForTrade(
                state.B0,
                state.B,
                payQuoteAmount,
                DecimalMath.reciprocalFloor(state.i),
                state.K
            );
    }

    // ============ Helper functions ============

    function adjustedTarget(PMMState memory state) internal pure {
        if (state.R == RState.BELOW_ONE) {
            state.Q0 = DODOMath._SolveQuadraticFunctionForTarget(
                state.Q,
                state.B.sub(state.B0),
                state.i,
                state.K
            );
        } else if (state.R == RState.ABOVE_ONE) {
            state.B0 = DODOMath._SolveQuadraticFunctionForTarget(
                state.B,
                state.Q.sub(state.Q0),
                DecimalMath.reciprocalFloor(state.i),
                state.K
            );
        }
    }

    function getMidPrice(PMMState memory state) internal pure returns (uint256) {
        if (state.R == RState.BELOW_ONE) {
            uint256 R = DecimalMath.divFloor(state.Q0.mul(state.Q0).div(state.Q), state.Q);
            R = DecimalMath.ONE.sub(state.K).add(DecimalMath.mulFloor(state.K, R));
            return DecimalMath.divFloor(state.i, R);
        } else {
            uint256 R = DecimalMath.divFloor(state.B0.mul(state.B0).div(state.B), state.B);
            R = DecimalMath.ONE.sub(state.K).add(DecimalMath.mulFloor(state.K, R));
            return DecimalMath.mulFloor(state.i, R);
        }
    }
}

// File: contracts/lib/SafeERC20.sol



/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using SafeMath for uint256;

    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)
        );
    }

    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));
    }

    /**
     * @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.

        // A Solidity high level call has three parts:
        //  1. The target address is checked to verify it contains contract code
        //  2. The call itself is made, and success asserted
        //  3. The return value is decoded, which in turn checks the size of the returned data.
        // solhint-disable-next-line max-line-length

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = address(token).call(data);
        require(success, "SafeERC20: low-level call failed");

        if (returndata.length > 0) {
            // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

// File: contracts/lib/ReentrancyGuard.sol


/**
 * @title ReentrancyGuard
 * @author DODO Breeder
 *
 * @notice Protect functions from Reentrancy Attack
 */
contract ReentrancyGuard {
    // https://solidity.readthedocs.io/en/latest/control-structures.html?highlight=zero-state#scoping-and-declarations
    // zero-state of _ENTERED_ is false
    bool private _ENTERED_;

    modifier preventReentrant() {
        require(!_ENTERED_, "REENTRANT");
        _ENTERED_ = true;
        _;
        _ENTERED_ = false;
    }
}

// File: contracts/DODOStablePool/impl/DSPStorage.sol



contract DSPStorage is ReentrancyGuard {
    using SafeMath for uint256;

    bool internal _DSP_INITIALIZED_;
    bool public _IS_OPEN_TWAP_ = false;
    
    // ============ Core Address ============

    address public _MAINTAINER_;

    IERC20 public _BASE_TOKEN_;
    IERC20 public _QUOTE_TOKEN_;

    uint112 public _BASE_RESERVE_;
    uint112 public _QUOTE_RESERVE_;
    uint32 public _BLOCK_TIMESTAMP_LAST_;
    
    uint256 public _BASE_PRICE_CUMULATIVE_LAST_;

    uint112 public _BASE_TARGET_;
    uint112 public _QUOTE_TARGET_;
    uint32 public _RState_;

    // ============ Shares (ERC20) ============

    string public symbol;
    uint8 public decimals;
    string public name;

    uint256 public totalSupply;
    mapping(address => uint256) internal _SHARES_;
    mapping(address => mapping(address => uint256)) internal _ALLOWED_;

    // ================= Permit ======================

    bytes32 public DOMAIN_SEPARATOR;
    // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
    bytes32 public constant PERMIT_TYPEHASH =
        0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
    mapping(address => uint256) public nonces;

    // ============ Variables for Pricing ============

    IFeeRateModel public _MT_FEE_RATE_MODEL_;

    uint256 public _LP_FEE_RATE_;
    uint256 public _K_;
    uint256 public _I_;

    // ============ Helper Functions ============

    function getPMMState() public view returns (PMMPricing.PMMState memory state) {
        state.i = _I_;
        state.K = _K_;
        state.B = _BASE_RESERVE_;
        state.Q = _QUOTE_RESERVE_;
        state.B0 = _BASE_TARGET_; // will be calculated in adjustedTarget
        state.Q0 = _QUOTE_TARGET_;
        state.R = PMMPricing.RState(_RState_);
        PMMPricing.adjustedTarget(state);
    }

    function getPMMStateForCall()
        external
        view
        returns (
            uint256 i,
            uint256 K,
            uint256 B,
            uint256 Q,
            uint256 B0,
            uint256 Q0,
            uint256 R
        )
    {
        PMMPricing.PMMState memory state = getPMMState();
        i = state.i;
        K = state.K;
        B = state.B;
        Q = state.Q;
        B0 = state.B0;
        Q0 = state.Q0;
        R = uint256(state.R);
    }

    function getMidPrice() public view returns (uint256 midPrice) {
        return PMMPricing.getMidPrice(getPMMState());
    }
}

// File: contracts/DODOStablePool/impl/DSPVault.sol




contract DSPVault is DSPStorage {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    // ============ Events ============

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    event Mint(address indexed user, uint256 value);

    event Burn(address indexed user, uint256 value);

    // ============ View Functions ============

    function getVaultReserve() external view returns (uint256 baseReserve, uint256 quoteReserve) {
        baseReserve = _BASE_RESERVE_;
        quoteReserve = _QUOTE_RESERVE_;
    }

    function getUserFeeRate(address user)
        external
        view
        returns (uint256 lpFeeRate, uint256 mtFeeRate)
    {
        lpFeeRate = _LP_FEE_RATE_;
        mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(user);
    }

    // ============ Asset In ============

    function getBaseInput() public view returns (uint256 input) {
        return _BASE_TOKEN_.balanceOf(address(this)).sub(uint256(_BASE_RESERVE_));
    }

    function getQuoteInput() public view returns (uint256 input) {
        return _QUOTE_TOKEN_.balanceOf(address(this)).sub(uint256(_QUOTE_RESERVE_));
    }

    // ============ TWAP UPDATE ===========

    function _twapUpdate() internal {
        uint32 blockTimestamp = uint32(block.timestamp % 2**32);
        uint32 timeElapsed = blockTimestamp - _BLOCK_TIMESTAMP_LAST_;
        if (timeElapsed > 0 && _BASE_RESERVE_ != 0 && _QUOTE_RESERVE_ != 0) {
            _BASE_PRICE_CUMULATIVE_LAST_ += getMidPrice() * timeElapsed;
        }
        _BLOCK_TIMESTAMP_LAST_ = blockTimestamp;
    }

    // ============ Set States ============

    function _setReserve(uint256 baseReserve, uint256 quoteReserve) internal {
        require(baseReserve <= uint112(-1) && quoteReserve <= uint112(-1), "OVERFLOW");
        _BASE_RESERVE_ = uint112(baseReserve);
        _QUOTE_RESERVE_ = uint112(quoteReserve);

        if (_IS_OPEN_TWAP_) _twapUpdate();
    }

    function _sync() internal {
        uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
        uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));
        require(baseBalance <= uint112(-1) && quoteBalance <= uint112(-1), "OVERFLOW");
        if (baseBalance != _BASE_RESERVE_) {
            _BASE_RESERVE_ = uint112(baseBalance);
        }
        if (quoteBalance != _QUOTE_RESERVE_) {
            _QUOTE_RESERVE_ = uint112(quoteBalance);
        }

        if (_IS_OPEN_TWAP_) _twapUpdate();
    }

    function sync() external preventReentrant {
        _sync();
    }

    function correctRState() public {
        if (_RState_ == uint32(PMMPricing.RState.BELOW_ONE) && _BASE_RESERVE_<_BASE_TARGET_) {
          _RState_ = uint32(PMMPricing.RState.ONE);
          _BASE_TARGET_ = _BASE_RESERVE_;
          _QUOTE_TARGET_ = _QUOTE_RESERVE_;
        }
        if (_RState_ == uint32(PMMPricing.RState.ABOVE_ONE) && _QUOTE_RESERVE_<_QUOTE_TARGET_) {
          _RState_ = uint32(PMMPricing.RState.ONE);
          _BASE_TARGET_ = _BASE_RESERVE_;
          _QUOTE_TARGET_ = _QUOTE_RESERVE_;
        }
    }

    // ============ Asset Out ============

    function _transferBaseOut(address to, uint256 amount) internal {
        if (amount > 0) {
            _BASE_TOKEN_.safeTransfer(to, amount);
        }
    }

    function _transferQuoteOut(address to, uint256 amount) internal {
        if (amount > 0) {
            _QUOTE_TOKEN_.safeTransfer(to, amount);
        }
    }

    // ============ Shares (ERC20) ============

    /**
     * @dev transfer token for a specified address
     * @param to The address to transfer to.
     * @param amount The amount to be transferred.
     */
    function transfer(address to, uint256 amount) public returns (bool) {
        require(amount <= _SHARES_[msg.sender], "BALANCE_NOT_ENOUGH");

        _SHARES_[msg.sender] = _SHARES_[msg.sender].sub(amount);
        _SHARES_[to] = _SHARES_[to].add(amount);
        emit Transfer(msg.sender, to, amount);
        return true;
    }

    /**
     * @dev Gets the balance of the specified address.
     * @param owner The address to query the the balance of.
     * @return balance An uint256 representing the amount owned by the passed address.
     */
    function balanceOf(address owner) external view returns (uint256 balance) {
        return _SHARES_[owner];
    }

    /**
     * @dev Transfer tokens from one address to another
     * @param from address The address which you want to send tokens from
     * @param to address The address which you want to transfer to
     * @param amount uint256 the amount of tokens to be transferred
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public returns (bool) {
        require(amount <= _SHARES_[from], "BALANCE_NOT_ENOUGH");
        require(amount <= _ALLOWED_[from][msg.sender], "ALLOWANCE_NOT_ENOUGH");

        _SHARES_[from] = _SHARES_[from].sub(amount);
        _SHARES_[to] = _SHARES_[to].add(amount);
        _ALLOWED_[from][msg.sender] = _ALLOWED_[from][msg.sender].sub(amount);
        emit Transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
     * @param spender The address which will spend the funds.
     * @param amount The amount of tokens to be spent.
     */
    function approve(address spender, uint256 amount) public returns (bool) {
        _approve(msg.sender, spender, amount);
        return true;
    }

    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) private {
        _ALLOWED_[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Function to check the amount of tokens that an owner _ALLOWED_ to a spender.
     * @param owner address The address which owns the funds.
     * @param spender address The address which will spend the funds.
     * @return A uint256 specifying the amount of tokens still available for the spender.
     */
    function allowance(address owner, address spender) public view returns (uint256) {
        return _ALLOWED_[owner][spender];
    }

    function _mint(address user, uint256 value) internal {
        require(value > 1000, "MINT_AMOUNT_NOT_ENOUGH");
        _SHARES_[user] = _SHARES_[user].add(value);
        totalSupply = totalSupply.add(value);
        emit Mint(user, value);
        emit Transfer(address(0), user, value);
    }

    function _burn(address user, uint256 value) internal {
        _SHARES_[user] = _SHARES_[user].sub(value);
        totalSupply = totalSupply.sub(value);
        emit Burn(user, value);
        emit Transfer(user, address(0), value);
    }

    // ============================ Permit ======================================

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        require(deadline >= block.timestamp, "DODO_DSP_LP: EXPIRED");
        bytes32 digest =
            keccak256(
                abi.encodePacked(
                    "\x19\x01",
                    DOMAIN_SEPARATOR,
                    keccak256(
                        abi.encode(
                            PERMIT_TYPEHASH,
                            owner,
                            spender,
                            value,
                            nonces[owner]++,
                            deadline
                        )
                    )
                )
            );
        address recoveredAddress = ecrecover(digest, v, r, s);
        require(
            recoveredAddress != address(0) && recoveredAddress == owner,
            "DODO_DSP_LP: INVALID_SIGNATURE"
        );
        _approve(owner, spender, value);
    }
}

// File: contracts/intf/IDODOCallee.sol


interface IDODOCallee {
    function DVMSellShareCall(
        address sender,
        uint256 burnShareAmount,
        uint256 baseAmount,
        uint256 quoteAmount,
        bytes calldata data
    ) external;

    function DVMFlashLoanCall(
        address sender,
        uint256 baseAmount,
        uint256 quoteAmount,
        bytes calldata data
    ) external;

    function DPPFlashLoanCall(
        address sender,
        uint256 baseAmount,
        uint256 quoteAmount,
        bytes calldata data
    ) external;

    function DSPFlashLoanCall(
        address sender,
        uint256 baseAmount,
        uint256 quoteAmount,
        bytes calldata data
    ) external;

    function CPCancelCall(
        address sender,
        uint256 amount,
        bytes calldata data
    ) external;

	function CPClaimBidCall(
        address sender,
        uint256 baseAmount,
        uint256 quoteAmount,
        bytes calldata data
    ) external;
}

// File: contracts/DODOStablePool/impl/DSPTrader.sol


contract DSPTrader is DSPVault {
    using SafeMath for uint256;

    // ============ Events ============

    event DODOSwap(
        address fromToken,
        address toToken,
        uint256 fromAmount,
        uint256 toAmount,
        address trader,
        address receiver
    );

    event DODOFlashLoan(address borrower, address assetTo, uint256 baseAmount, uint256 quoteAmount);

    event RChange(PMMPricing.RState newRState);

    // ============ Trade Functions ============

    function sellBase(address to) external preventReentrant returns (uint256 receiveQuoteAmount) {
        uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
        uint256 baseInput = baseBalance.sub(uint256(_BASE_RESERVE_));
        uint256 mtFee;
        uint256 newBaseTarget;
        PMMPricing.RState newRState;
        (receiveQuoteAmount, mtFee, newRState, newBaseTarget) = querySellBase(tx.origin, baseInput);

        _transferQuoteOut(to, receiveQuoteAmount);
        _transferQuoteOut(_MAINTAINER_, mtFee);

        // update TARGET
        if (_RState_ != uint32(newRState)) {
            require(newBaseTarget <= uint112(-1), "OVERFLOW");
            _BASE_TARGET_ = uint112(newBaseTarget);
            _RState_ = uint32(newRState);
            emit RChange(newRState);
        }

        _setReserve(baseBalance, _QUOTE_TOKEN_.balanceOf(address(this)));

        emit DODOSwap(
            address(_BASE_TOKEN_),
            address(_QUOTE_TOKEN_),
            baseInput,
            receiveQuoteAmount,
            msg.sender,
            to
        );
    }

    function sellQuote(address to) external preventReentrant returns (uint256 receiveBaseAmount) {
        uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));
        uint256 quoteInput = quoteBalance.sub(uint256(_QUOTE_RESERVE_));
        uint256 mtFee;
        uint256 newQuoteTarget;
        PMMPricing.RState newRState;
        (receiveBaseAmount, mtFee, newRState, newQuoteTarget) = querySellQuote(
            tx.origin,
            quoteInput
        );

        _transferBaseOut(to, receiveBaseAmount);
        _transferBaseOut(_MAINTAINER_, mtFee);

        // update TARGET
        if (_RState_ != uint32(newRState)) {
            require(newQuoteTarget <= uint112(-1), "OVERFLOW");
            _QUOTE_TARGET_ = uint112(newQuoteTarget);
            _RState_ = uint32(newRState);
            emit RChange(newRState);
        }

        _setReserve(_BASE_TOKEN_.balanceOf(address(this)), quoteBalance);

        emit DODOSwap(
            address(_QUOTE_TOKEN_),
            address(_BASE_TOKEN_),
            quoteInput,
            receiveBaseAmount,
            msg.sender,
            to
        );
    }

    function flashLoan(
        uint256 baseAmount,
        uint256 quoteAmount,
        address assetTo,
        bytes calldata data
    ) external preventReentrant {
        _transferBaseOut(assetTo, baseAmount);
        _transferQuoteOut(assetTo, quoteAmount);

        if (data.length > 0)
            IDODOCallee(assetTo).DSPFlashLoanCall(msg.sender, baseAmount, quoteAmount, data);

        uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
        uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));

        // no input -> pure loss
        require(
            baseBalance >= _BASE_RESERVE_ || quoteBalance >= _QUOTE_RESERVE_,
            "FLASH_LOAN_FAILED"
        );

        // sell quote case
        // quote input + base output
        if (baseBalance < _BASE_RESERVE_) {
            uint256 quoteInput = quoteBalance.sub(uint256(_QUOTE_RESERVE_));
            (
                uint256 receiveBaseAmount,
                uint256 mtFee,
                PMMPricing.RState newRState,
                uint256 newQuoteTarget
            ) = querySellQuote(tx.origin, quoteInput); // revert if quoteBalance<quoteReserve
            require(
                uint256(_BASE_RESERVE_).sub(baseBalance) <= receiveBaseAmount,
                "FLASH_LOAN_FAILED"
            );

            _transferBaseOut(_MAINTAINER_, mtFee);
            if (_RState_ != uint32(newRState)) {
                require(newQuoteTarget <= uint112(-1), "OVERFLOW");
                _QUOTE_TARGET_ = uint112(newQuoteTarget);
                _RState_ = uint32(newRState);
                emit RChange(newRState);
            }
            emit DODOSwap(
                address(_QUOTE_TOKEN_),
                address(_BASE_TOKEN_),
                quoteInput,
                receiveBaseAmount,
                msg.sender,
                assetTo
            );
        }

        // sell base case
        // base input + quote output
        if (quoteBalance < _QUOTE_RESERVE_) {
            uint256 baseInput = baseBalance.sub(uint256(_BASE_RESERVE_));
            (
                uint256 receiveQuoteAmount,
                uint256 mtFee,
                PMMPricing.RState newRState,
                uint256 newBaseTarget
            ) = querySellBase(tx.origin, baseInput); // revert if baseBalance<baseReserve
            require(
                uint256(_QUOTE_RESERVE_).sub(quoteBalance) <= receiveQuoteAmount,
                "FLASH_LOAN_FAILED"
            );

            _transferQuoteOut(_MAINTAINER_, mtFee);
            if (_RState_ != uint32(newRState)) {
                require(newBaseTarget <= uint112(-1), "OVERFLOW");
                _BASE_TARGET_ = uint112(newBaseTarget);
                _RState_ = uint32(newRState);
                emit RChange(newRState);
            }
            emit DODOSwap(
                address(_BASE_TOKEN_),
                address(_QUOTE_TOKEN_),
                baseInput,
                receiveQuoteAmount,
                msg.sender,
                assetTo
            );
        }

        _sync();

        emit DODOFlashLoan(msg.sender, assetTo, baseAmount, quoteAmount);
    }

    // ============ Query Functions ============

    function querySellBase(address trader, uint256 payBaseAmount)
        public
        view
        returns (
            uint256 receiveQuoteAmount,
            uint256 mtFee,
            PMMPricing.RState newRState,
            uint256 newBaseTarget
        )
    {
        PMMPricing.PMMState memory state = getPMMState();
        (receiveQuoteAmount, newRState) = PMMPricing.sellBaseToken(state, payBaseAmount);

        uint256 lpFeeRate = _LP_FEE_RATE_;
        uint256 mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(trader);
        mtFee = DecimalMath.mulFloor(receiveQuoteAmount, mtFeeRate);
        receiveQuoteAmount = receiveQuoteAmount
            .sub(DecimalMath.mulFloor(receiveQuoteAmount, lpFeeRate))
            .sub(mtFee);
        newBaseTarget = state.B0;
    }

    function querySellQuote(address trader, uint256 payQuoteAmount)
        public
        view
        returns (
            uint256 receiveBaseAmount,
            uint256 mtFee,
            PMMPricing.RState newRState,
            uint256 newQuoteTarget
        )
    {
        PMMPricing.PMMState memory state = getPMMState();
        (receiveBaseAmount, newRState) = PMMPricing.sellQuoteToken(state, payQuoteAmount);

        uint256 lpFeeRate = _LP_FEE_RATE_;
        uint256 mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(trader);
        mtFee = DecimalMath.mulFloor(receiveBaseAmount, mtFeeRate);
        receiveBaseAmount = receiveBaseAmount
            .sub(DecimalMath.mulFloor(receiveBaseAmount, lpFeeRate))
            .sub(mtFee);
        newQuoteTarget = state.Q0;
    }
}

// File: contracts/DODOStablePool/impl/DSPFunding.sol


contract DSPFunding is DSPVault {
    // ============ Events ============

    event BuyShares(address to, uint256 increaseShares, uint256 totalShares);

    event SellShares(address payer, address to, uint256 decreaseShares, uint256 totalShares);

    // ============ Buy & Sell Shares ============

    // buy shares [round down]
    function buyShares(address to)
        external
        preventReentrant
        returns (
            uint256 shares,
            uint256 baseInput,
            uint256 quoteInput
        )
    {
        uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
        uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));
        uint256 baseReserve = _BASE_RESERVE_;
        uint256 quoteReserve = _QUOTE_RESERVE_;

        baseInput = baseBalance.sub(baseReserve);
        quoteInput = quoteBalance.sub(quoteReserve);
        require(baseInput > 0, "NO_BASE_INPUT");

        // Round down when withdrawing. Therefore, never be a situation occuring balance is 0 but totalsupply is not 0
        // But May Happen,reserve >0 But totalSupply = 0
        if (totalSupply == 0) {
            // case 1. initial supply
            shares = quoteBalance < DecimalMath.mulFloor(baseBalance, _I_)
                ? DecimalMath.divFloor(quoteBalance, _I_)
                : baseBalance;
            _BASE_TARGET_ = uint112(shares);
            _QUOTE_TARGET_ = uint112(DecimalMath.mulFloor(shares, _I_));
        } else if (baseReserve > 0 && quoteReserve > 0) {
            // case 2. normal case
            uint256 baseInputRatio = DecimalMath.divFloor(baseInput, baseReserve);
            uint256 quoteInputRatio = DecimalMath.divFloor(quoteInput, quoteReserve);
            uint256 mintRatio = quoteInputRatio < baseInputRatio ? quoteInputRatio : baseInputRatio;
            shares = DecimalMath.mulFloor(totalSupply, mintRatio);

            _BASE_TARGET_ = uint112(uint256(_BASE_TARGET_).add(DecimalMath.mulFloor(uint256(_BASE_TARGET_), mintRatio)));
            _QUOTE_TARGET_ = uint112(uint256(_QUOTE_TARGET_).add(DecimalMath.mulFloor(uint256(_QUOTE_TARGET_), mintRatio)));
        }
        _mint(to, shares);
        _setReserve(baseBalance, quoteBalance);
        emit BuyShares(to, shares, _SHARES_[to]);
    }

    // sell shares [round down]
    function sellShares(
        uint256 shareAmount,
        address to,
        uint256 baseMinAmount,
        uint256 quoteMinAmount,
        bytes calldata data,
        uint256 deadline
    ) external preventReentrant returns (uint256 baseAmount, uint256 quoteAmount) {
        require(deadline >= block.timestamp, "TIME_EXPIRED");
        require(shareAmount <= _SHARES_[msg.sender], "DLP_NOT_ENOUGH");

        uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
        uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));
        uint256 totalShares = totalSupply;

        baseAmount = baseBalance.mul(shareAmount).div(totalShares);
        quoteAmount = quoteBalance.mul(shareAmount).div(totalShares);

        _BASE_TARGET_ = uint112(uint256(_BASE_TARGET_).sub(uint256(_BASE_TARGET_).mul(shareAmount).divCeil(totalShares)));
        _QUOTE_TARGET_ = uint112(uint256(_QUOTE_TARGET_).sub(uint256(_QUOTE_TARGET_).mul(shareAmount).divCeil(totalShares)));

        require(
            baseAmount >= baseMinAmount && quoteAmount >= quoteMinAmount,
            "WITHDRAW_NOT_ENOUGH"
        );

        _burn(msg.sender, shareAmount);
        _transferBaseOut(to, baseAmount);
        _transferQuoteOut(to, quoteAmount);
        _sync();

        if (data.length > 0) {
            //Same as DVM 
            IDODOCallee(to).DVMSellShareCall(
                msg.sender,
                shareAmount,
                baseAmount,
                quoteAmount,
                data
            );
        }

        emit SellShares(msg.sender, to, shareAmount, _SHARES_[msg.sender]);
    }
}

// File: contracts/DODOStablePool/impl/DSP.sol




/**
 * @title DODO StablePool
 * @author DODO Breeder
 *
 * @notice DODOStablePool initialization
 */
contract DSP is DSPTrader, DSPFunding {
    function init(
        address maintainer,
        address baseTokenAddress,
        address quoteTokenAddress,
        uint256 lpFeeRate,
        address mtFeeRateModel,
        uint256 i,
        uint256 k,
        bool isOpenTWAP
    ) external {
        require(!_DSP_INITIALIZED_, "DSP_INITIALIZED");
        _DSP_INITIALIZED_ = true;
        
        require(baseTokenAddress != quoteTokenAddress, "BASE_QUOTE_CAN_NOT_BE_SAME");
        _BASE_TOKEN_ = IERC20(baseTokenAddress);
        _QUOTE_TOKEN_ = IERC20(quoteTokenAddress);

        require(i > 0 && i <= 10**36);
        _I_ = i;

        require(k <= 10**18);
        _K_ = k;

        _LP_FEE_RATE_ = lpFeeRate;
        _MT_FEE_RATE_MODEL_ = IFeeRateModel(mtFeeRateModel);
        _MAINTAINER_ = maintainer;

        _IS_OPEN_TWAP_ = isOpenTWAP;
        if (isOpenTWAP) _BLOCK_TIMESTAMP_LAST_ = uint32(block.timestamp % 2**32);

        string memory connect = "_";
        string memory suffix = "DLP";

        name = string(abi.encodePacked(suffix, connect, addressToShortString(address(this))));
        symbol = "DLP";
        decimals = _BASE_TOKEN_.decimals();

        // ============================== Permit ====================================
        uint256 chainId;
        assembly {
            chainId := chainid()
        }
        DOMAIN_SEPARATOR = keccak256(
            abi.encode(
                // keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
                0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f,
                keccak256(bytes(name)),
                keccak256(bytes("1")),
                chainId,
                address(this)
            )
        );
        // ==========================================================================
    }

    function addressToShortString(address _addr) public pure returns (string memory) {
        bytes32 value = bytes32(uint256(_addr));
        bytes memory alphabet = "0123456789abcdef";

        bytes memory str = new bytes(8);
        for (uint256 i = 0; i < 4; i++) {
            str[i * 2] = alphabet[uint8(value[i + 12] >> 4)];
            str[1 + i * 2] = alphabet[uint8(value[i + 12] & 0x0f)];
        }
        return string(str);
    }

    // ============ Version Control ============

    function version() external pure ...

// [truncated — 50062 bytes total]

Read Contract

DOMAIN_SEPARATOR 0x3644e515 → bytes32
PERMIT_TYPEHASH 0x30adf81f → bytes32
_BASE_PRICE_CUMULATIVE_LAST_ 0xfe24cb7f → uint256
_BASE_RESERVE_ 0x7d721504 → uint112
_BASE_TARGET_ 0xe539ef49 → uint112
_BASE_TOKEN_ 0x4a248d2a → address
_BLOCK_TIMESTAMP_LAST_ 0x880a4d87 → uint32
_IS_OPEN_TWAP_ 0x2df6cb48 → bool
_I_ 0xf811d692 → uint256
_K_ 0xec2fd46d → uint256
_LP_FEE_RATE_ 0xab44a7a3 → uint256
_MAINTAINER_ 0x4322ec83 → address
_MT_FEE_RATE_MODEL_ 0xf6b06e70 → address
_QUOTE_RESERVE_ 0xbbf5ce78 → uint112
_QUOTE_TARGET_ 0x77f58657 → uint112
_QUOTE_TOKEN_ 0xd4b97046 → address
_RState_ 0xbf357dae → uint32
addressToShortString 0x17101940 → string
allowance 0xdd62ed3e → uint256
balanceOf 0x70a08231 → uint256
decimals 0x313ce567 → uint8
getBaseInput 0x65f6fcbb → uint256
getMidPrice 0xee27c689 → uint256
getPMMState 0xa382d1b9 → tuple
getPMMStateForCall 0xfd1ed7e9 → uint256, uint256, uint256, uint256, uint256, uint256, uint256
getQuoteInput 0x71f9100c → uint256
getUserFeeRate 0x44096609 → uint256, uint256
getVaultReserve 0x36223ce9 → uint256, uint256
name 0x06fdde03 → string
nonces 0x7ecebe00 → uint256
querySellBase 0x79a04876 → uint256, uint256, uint8, uint256
querySellQuote 0x66410a21 → uint256, uint256, uint8, uint256
symbol 0x95d89b41 → string
totalSupply 0x18160ddd → uint256
version 0x54fd4d50 → string

Write Contract 12 functions

These functions modify contract state and require a wallet transaction to execute.

approve 0x095ea7b3
address spender
uint256 amount
returns: bool
buyShares 0x4c85b425
address to
returns: uint256, uint256, uint256
correctRState 0x73d74cf8
No parameters
flashLoan 0xd0a494e4
uint256 baseAmount
uint256 quoteAmount
address assetTo
bytes data
init 0x5039972a
address maintainer
address baseTokenAddress
address quoteTokenAddress
uint256 lpFeeRate
address mtFeeRateModel
uint256 i
uint256 k
bool isOpenTWAP
permit 0xd505accf
address owner
address spender
uint256 value
uint256 deadline
uint8 v
bytes32 r
bytes32 s
sellBase 0xbd6015b4
address to
returns: uint256
sellQuote 0xdd93f59a
address to
returns: uint256
sellShares 0xb56ceaa6
uint256 shareAmount
address to
uint256 baseMinAmount
uint256 quoteMinAmount
bytes data
uint256 deadline
returns: uint256, uint256
sync 0xfff6cae9
No parameters
transfer 0xa9059cbb
address to
uint256 amount
returns: bool
transferFrom 0x23b872dd
address from
address to
uint256 amount
returns: bool

Recent Transactions

No transactions found for this address