Address Contract Partially Verified
Address
0x833362A84d5ACd4b366014382f0f9d15b21bD12f
Balance
0 ETH
Nonce
1
Code Size
14586 bytes
Creator
0xcE6a1395...aeC3 at tx 0x74fc6c25...5048ed
Indexed Transactions
0
Contract Bytecode
14586 bytes
0x6080604052600436106101405760003560e01c8063080c25b31461014c578063174da6211461017557806328be42f4146101975780633596f9a2146101b75780633b2d081c146101d75780634567bfba146101fb5780634886c6751461021b57806361d027b31461023757806363c3e25f1461026457806368f2692f146102845780636c082c1314610297578063715018a6146102af57806377e741c7146102c45780637bf2d6d4146102e457806384a7f3dd146102f75780638da5cb5b146102ff5780639286b93d14610314578063acdf4f1814610334578063b810fb4314610354578063cc32d17614610374578063e10895f91461038a578063e1f1c4a7146103aa578063e7d3fc60146103c0578063f0f44260146103d6578063f2fde38b146103f6578063f3856e7614610416578063f827065e1461043657600080fd5b3661014757005b600080fd5b61015f61015a366004612e26565b6104b8565b60405161016c9190612f3f565b60405180910390f35b34801561018157600080fd5b50610195610190366004612f9d565b61095c565b005b3480156101a357600080fd5b5061015f6101b236600461301d565b610a64565b3480156101c357600080fd5b506101956101d23660046130c4565b610f19565b3480156101e357600080fd5b506101ed60045481565b60405190815260200161016c565b34801561020757600080fd5b50610195610216366004613105565b610f92565b34801561022757600080fd5b506101ed670de0b6b3a764000081565b34801561024357600080fd5b50600154610257906001600160a01b031681565b60405161016c919061311e565b34801561027057600080fd5b506101ed61027f3660046131cf565b611002565b6101ed610292366004613281565b6110d1565b3480156102a357600080fd5b506101ed638000000081565b3480156102bb57600080fd5b506101956110ec565b3480156102d057600080fd5b506101956102df366004613105565b611100565b61015f6102f23660046132fe565b611168565b61015f611179565b34801561030b57600080fd5b50610257611415565b34801561032057600080fd5b5061019561032f366004613105565b611424565b34801561034057600080fd5b50600254610257906001600160a01b031681565b34801561036057600080fd5b5061025761036f366004613105565b611463565b34801561038057600080fd5b506101ed60035481565b34801561039657600080fd5b506101956103a53660046133a9565b61148d565b3480156103b657600080fd5b506101ed61271081565b3480156103cc57600080fd5b506101ed60065481565b3480156103e257600080fd5b506101956103f13660046133fa565b61162d565b34801561040257600080fd5b506101956104113660046133fa565b611694565b34801561042257600080fd5b506101956104313660046133fa565b61170d565b34801561044257600080fd5b50610489610451366004613415565b6007602052600090815260409020546001600160401b03811690600160401b81046001600160a01b031690600160e01b900460ff1683565b604080516001600160401b0390941684526001600160a01b03909216602084015215159082015260600161016c565b60606104de60405180606001604052806060815260200160008152602001600081525090565b606060008034116104f0578a516104fe565b60018b516104fe9190613446565b90506040518060600160405280826001600160401b0381111561052357610523612baf565b60405190808252806020026020018201604052801561056857816020015b60408051808201909152600080825260208201528152602001906001900390816105415790505b5081526020018d6020015181526020018d604001518152509250806001600160401b0381111561059a5761059a612baf565b6040519080825280602002602001820160405280156105df57816020015b60408051808201909152600080825260208201528152602001906001900390816105b85790505b509150506000805b8b518110156108a95760006001600160a01b03168c828151811061060d5761060d613459565b6020026020010151600001516001600160a01b031603610695578b818151811061063957610639613459565b60200260200101516020015160000361067057348c828151811061065f5761065f613459565b602002602001015160200181815250505b8b818151811061068257610682613459565b60200260200101516020015191506108a1565b8b81815181106106a7576106a7613459565b602002602001015160200151600003610767578b81815181106106cc576106cc613459565b6020026020010151600001516001600160a01b03166370a08231336040518263ffffffff1660e01b8152600401610703919061311e565b602060405180830381865afa158015610720573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610744919061346f565b8c828151811061075657610756613459565b602002602001015160200181815250505b6000821561077f5761077a600183613446565b610781565b815b90508c828151811061079557610795613459565b602002602001015160000151856000015182815181106107b7576107b7613459565b60209081029190910101516001600160a01b0390911690528c518d90839081106107e3576107e3613459565b6020026020010151602001518560000151828151811061080557610805613459565b602002602001015160200181815250508c828151811061082757610827613459565b60200260200101516040015184828151811061084557610845613459565b60209081029190910101516001600160a01b0390911690528c518d908390811061087157610871613459565b60200260200101516020015184828151811061088f5761088f613459565b60200260200101516020018181525050505b6001016105e7565b508034146108d25760405162461bcd60e51b81526004016108c990613488565b60405180910390fd5b508a5160608c015160405163edd9444b60e01b81526001600160a01b039092169163edd9444b9161090c9186918691339190600401613554565b600060405180830381600087803b15801561092657600080fd5b505af115801561093a573d6000803e3d6000fd5b5050505061094d8a8a8a8a8a8a8a611768565b9b9a5050505050505050505050565b610964611f54565b8382146109ac5760405162461bcd60e51b815260206004820152601660248201527524b73b30b634b210333ab73239903a3930b739b332b960511b60448201526064016108c9565b60005b84811015610a5c57610a548686838181106109cc576109cc613459565b90506020020160208101906109e191906133fa565b838686858181106109f4576109f4613459565b90506020020135600014610a2057868685818110610a1457610a14613459565b90506020020135612042565b610a4f898986818110610a3557610a35613459565b9050602002016020810190610a4a91906133fa565b611fb3565b612042565b6001016109af565b505050505050565b6060610a6e611f54565b600087516001600160401b03811115610a8957610a89612baf565b604051908082528060200260200182016040528015610ab2578160200160208202803683370190505b509050600088516001600160401b03811115610ad057610ad0612baf565b604051908082528060200260200182016040528015610af9578160200160208202803683370190505b50905060005b8951811015610c4257898181518110610b1a57610b1a613459565b602002602001015160000151828281518110610b3857610b38613459565b60200260200101906001600160a01b031690816001600160a01b031681525050898181518110610b6a57610b6a613459565b602002602001015160200151600014610ba057898181518110610b8f57610b8f613459565b602002602001015160200151610bc2565b610bc2828281518110610bb557610bb5613459565b6020026020010151611fb3565b838281518110610bd457610bd4613459565b602002602001018181525050610c3a828281518110610bf557610bf5613459565b60200260200101518b8381518110610c0f57610c0f613459565b602002602001015160400151858481518110610c2d57610c2d613459565b6020026020010151612042565b600101610aff565b50600088516001600160401b03811115610c5e57610c5e612baf565b604051908082528060200260200182016040528015610c87578160200160208202803683370190505b509050600089516001600160401b03811115610ca557610ca5612baf565b604051908082528060200260200182016040528015610cce578160200160208202803683370190505b50905060005b8a51811015610d67578a8181518110610cef57610cef613459565b602002602001015160000151828281518110610d0d57610d0d613459565b60200260200101906001600160a01b031690816001600160a01b031681525050610d42828281518110610bb557610bb5613459565b838281518110610d5457610d54613459565b6020908102919091010152600101610cd4565b5060008a516001600160401b03811115610d8357610d83612baf565b604051908082528060200260200182016040528015610dac578160200160208202803683370190505b50955060005b8b51811015610eb957838181518110610dcd57610dcd613459565b6020026020010151610dea848381518110610bb557610bb5613459565b610df49190613446565b878281518110610e0657610e06613459565b602002602001018181525050610e638c8281518110610e2757610e27613459565b6020026020010151600001518d8381518110610e4557610e45613459565b602002602001015160400151898481518110610c2d57610c2d613459565b8b8181518110610e7557610e75613459565b602002602001015160200151878281518110610e9357610e93613459565b6020026020010151610ea591906135fa565b610eaf9083613611565b9150600101610db2565b5089811015610eda5760405162461bcd60e51b81526004016108c990613624565b6000805160206138ce83398151915233868689866000604051610f029695949392919061368f565b60405180910390a150505050509695505050505050565b610f21611f54565b60005b81811015610f8d576005838383818110610f4057610f40613459565b9050602002016020810190610f5591906133fa565b815460018082018455600093845260209093200180546001600160a01b0319166001600160a01b039290921691909117905501610f24565b505050565b610f9a611f54565b6103e8811115610fbc5760405162461bcd60e51b81526004016108c990613707565b600480549082905560408051838152602081018390527f57bf1b8bd3f568dd8cb74f6aba41345a0476d5da09135f4101a0f71d3164e9fa91015b60405180910390a15050565b86516040805160a08101825288516001600160a01b0390811660608084019182526020808d0180516080870152928552808e015181860152858e015185870152855180870187528d870151851681529251908301528c0151935163187945bd60e11b815260009592909216936330f28b7a9361108593909291339160040161372c565b600060405180830381600087803b15801561109f57600080fd5b505af11580156110b3573d6000803e3d6000fd5b505050506110c5878787878787612103565b98975050505050505050565b60006110e18787878787876124a3565b979650505050505050565b6110f4611f54565b6110fe60006125a8565b565b611108611f54565b6103e881111561112a5760405162461bcd60e51b81526004016108c990613707565b600380549082905560408051838152602081018390527f9a10196edf96c0372716a6ac6dbef8263f5b96bc990302224eec716beeae70379101610ff6565b60606110c5888888888888886125f8565b6060600080828060063360043560f890811c90600535901c816001600160401b038111156111a9576111a9612baf565b6040519080825280602002602001820160405280156111f457816020015b60408051606081018252600080825260208083018290529282015282526000199092019101816111c75790505b509550806001600160401b0381111561120f5761120f612baf565b60405190808252806020026020018201604052801561125a57816020015b604080516060810182526000808252602080830182905292820152825260001990920191018161122d5790505b5094506112d3565b600080823560f01c8080156112aa57600181146112b5577fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf482015493506002850192506112cc565b6002850192506112cc565b6001600160a01b03853560501c1693506016850192505b5050915091565b6112dc84611262565b94509750833560f81c6001850194508435600882602003021c975080850194505060008060005b8481101561137057602080820201890151915061131f87611262565b8184526001810198509093503560f81c801561134a578735600882602003021c602084015280880197505b5061135487611262565b9750925082611361578a92505b60408201839052600101611303565b5060005b838110156113dc57602080820201880151915061139087611262565b97509250828252863560f81c6001880197508735600882602003021c60208401528088019750506113c087611262565b97509250826113cd578592505b60408201839052600101611374565b50506004850194803560e01c945060050192505050823560f81c60200261140886868985858d896125f8565b9850505050505050505090565b6000546001600160a01b031690565b61142c611f54565b61143f60c8670de0b6b3a764000061377f565b81111561145e5760405162461bcd60e51b81526004016108c9906137a1565b600655565b6005818154811061147357600080fd5b6000918252602090912001546001600160a01b0316905081565b63ffffffff8316600090815260076020526040902054600160e01b900460ff16156114e85760405162461bcd60e51b815260206004820152600b60248201526a436f646520696e2075736560a81b60448201526064016108c9565b6114fb6032670de0b6b3a764000061377f565b826001600160401b031611156115235760405162461bcd60e51b81526004016108c9906137a1565b63800000008363ffffffff1611611560576001600160401b0382161561155b5760405162461bcd60e51b81526004016108c9906137c7565b6115d2565b6000826001600160401b0316116115895760405162461bcd60e51b81526004016108c9906137c7565b6001600160a01b0381166115d25760405162461bcd60e51b815260206004820152601060248201526f4e756c6c2062656e656669636961727960801b60448201526064016108c9565b63ffffffff9290921660009081526007602052604090208054600160e01b6001600160401b03939093166001600160e01b031990911617600160401b6001600160a01b03909416939093029290921760ff60e01b1916179055565b611635611f54565b600180546001600160a01b038381166001600160a01b031983168117909355604051911691907fd101a15f9e9364a1c0a7c4cc8eb4cd9220094e83353915b0c74e09f72ec73edb9061168890849061311e565b60405180910390a25050565b61169c611f54565b6001600160a01b0381166117015760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016108c9565b61170a816125a8565b50565b611715611f54565b600280546001600160a01b038381166001600160a01b031983168117909355604051911691907f762398cd916e3a75f872ea6b6674f748bff4a6272a3014d07d9d799cb67dd7a29061168890849061311e565b6060600088516001600160401b0381111561178557611785612baf565b6040519080825280602002602001820160405280156117ae578160200160208202803683370190505b509050600089516001600160401b038111156117cc576117cc612baf565b6040519080825280602002602001820160405280156117f5578160200160208202803683370190505b50905060005b8a518110156119c1578a818151811061181657611816613459565b60200260200101516020015183828151811061183457611834613459565b6020026020010181815250508a818151811061185257611852613459565b60200260200101516000015182828151811061187057611870613459565b60200260200101906001600160a01b031690816001600160a01b03168152505060005b81811015611938578b81815181106118ad576118ad613459565b6020026020010151600001516001600160a01b03168c83815181106118d4576118d4613459565b6020026020010151600001516001600160a01b0316036119305760405162461bcd60e51b81526020600482015260176024820152764475706c696361746520736f7572636520746f6b656e7360481b60448201526064016108c9565b600101611893565b5060005b8a518110156119b8578a818151811061195757611957613459565b6020026020010151600001516001600160a01b03168c838151811061197e5761197e613459565b6020026020010151600001516001600160a01b0316036119b05760405162461bcd60e51b81526004016108c9906137f5565b60010161193c565b506001016117fb565b50600089516001600160401b038111156119dd576119dd612baf565b604051908082528060200260200182016040528015611a06578160200160208202803683370190505b50905060005b8a51811015611b0e5760005b81811015611ac2578b8181518110611a3257611a32613459565b6020026020010151600001516001600160a01b03168c8381518110611a5957611a59613459565b6020026020010151600001516001600160a01b031603611aba5760405162461bcd60e51b815260206004820152601c60248201527b4475706c69636174652064657374696e6174696f6e20746f6b656e7360201b60448201526064016108c9565b600101611a18565b50611ae98b8281518110611ad857611ad8613459565b602002602001015160000151611fb3565b828281518110611afb57611afb613459565b6020908102919091010152600101611a0c565b50604080516060810182526000808252602082018190529181019190915263800000008663ffffffff161115611b9a575063ffffffff8516600090815260076020908152604091829020825160608101845290546001600160401b0381168252600160401b81046001600160a01b031692820192909252600160e01b90910460ff161515918101919091525b6006548b51600091906001600160401b03811115611bba57611bba612baf565b604051908082528060200260200182016040528015611be3578160200160208202803683370190505b50965060005b8d51811015611e4e57848181518110611c0457611c04613459565b6020026020010151611c218f8381518110611ad857611ad8613459565b611c2b9190613446565b888281518110611c3d57611c3d613459565b6020908102919091010152670de0b6b3a7640000611c5b8382613446565b898381518110611c6d57611c6d613459565b6020026020010151611c7f91906135fa565b611c89919061377f565b888281518110611c9b57611c9b613459565b60200260200101818152505063800000008963ffffffff161115611da757611d3b8e8281518110611cce57611cce613459565b6020026020010151600001518560200151670de0b6b3a7640000600a611cf491906135fa565b87600001516001600160401b03168c8681518110611d1457611d14613459565b6020026020010151611d2691906135fa565b611d319060086135fa565b610a4f919061377f565b8351670de0b6b3a764000090611d5a906001600160401b031682613446565b898381518110611d6c57611d6c613459565b6020026020010151611d7e91906135fa565b611d88919061377f565b888281518110611d9a57611d9a613459565b6020026020010181815250505b611df88e8281518110611dbc57611dbc613459565b6020026020010151600001518f8381518110611dda57611dda613459565b6020026020010151604001518a8481518110610c2d57610c2d613459565b8d8181518110611e0a57611e0a613459565b602002602001015160200151888281518110611e2857611e28613459565b6020026020010151611e3a91906135fa565b611e449084613611565b9250600101611be9565b508b821015611e6f5760405162461bcd60e51b81526004016108c990613624565b505060008b516001600160401b03811115611e8c57611e8c612baf565b604051908082528060200260200182016040528015611eb5578160200160208202803683370190505b50905060005b8c51811015611f14578c8181518110611ed657611ed6613459565b602002602001015160000151828281518110611ef457611ef4613459565b6001600160a01b0390921660209283029190910190910152600101611ebb565b506000805160206138ce83398151915233868689858c604051611f3c9695949392919061368f565b60405180910390a15050505050979650505050505050565b33611f5d611415565b6001600160a01b0316146110fe5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016108c9565b60006001600160a01b038216611fca575047919050565b6040516370a0823160e01b81526001600160a01b038316906370a0823190611ff690309060040161311e565b602060405180830381865afa158015612013573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612037919061346f565b92915050565b919050565b6001600160a01b0383166120ef576000826001600160a01b03168260405160006040518083038185875af1925050503d806000811461209d576040519150601f19603f3d011682016040523d82523d6000602084013e6120a2565b606091505b50509050806120e95760405162461bcd60e51b8152602060048201526013602482015272115512081d1c985b9cd9995c8819985a5b1959606a1b60448201526064016108c9565b50505050565b610f8d6001600160a01b0384168383612839565b600086608001518760a00151111561215a5760405162461bcd60e51b815260206004820152601a6024820152794d696e696d756d2067726561746572207468616e2071756f746560301b60448201526064016108c9565b86606001516001600160a01b031687600001516001600160a01b0316036121935760405162461bcd60e51b81526004016108c9906137f5565b60006121a28860600151611fb3565b885190915034906001600160a01b031661221f5760006127106003548b602001516121cd91906135fa565b6121d7919061377f565b905060006127106004548c602001516121f091906135fa565b6121fa919061377f565b9050612206828261289c565b6122108183613611565b61221a9084613446565b925050505b885160208a0151604051633290280160e11b81526001600160a01b03891692636520500292859261225a928b928f928f929190600401613826565b6000604051808303818588803b15801561227357600080fd5b505af1158015612287573d6000803e3d6000fd5b50505050508161229a8a60600151611fb3565b6122a49190613446565b60608a01519093506001600160a01b031661231a576000612710600354856122cc91906135fa565b6122d6919061377f565b90506000612710600454866122eb91906135fa565b6122f5919061377f565b9050612301828261289c565b61230b8183613611565b6123159086613446565b945050505b63800000008463ffffffff1611156123eb5763ffffffff841660009081526007602090815260409182902082516060808201855291546001600160401b0381168252600160401b81046001600160a01b0316938201849052600160e01b900460ff161515938101939093528b01516123b49161239f670de0b6b3a7640000600a6135fa565b8451611d26906001600160401b0316896135fa565b8051670de0b6b3a7640000906123d3906001600160401b031682613446565b6123dd90866135fa565b6123e7919061377f565b9350505b8860a0015183101561240f5760405162461bcd60e51b81526004016108c990613624565b61242289606001518a60c0015185612042565b6020898101518a516060808d015160408051338152958601949094526001600160a01b0392831685850152908401879052166080830152600060a083015263ffffffff861660c0830152517f823eaf01002d7353fbcadb2ea3305cc46fa35d799cb0914846d185ac06f8ad059181900360e00190a150509695505050505050565b85516000906001600160a01b03166124f45786602001516000036124cc5734602088015261259a565b866020015134146124ef5760405162461bcd60e51b81526004016108c990613488565b61259a565b86602001516000036125765786516040516370a0823160e01b81526001600160a01b03909116906370a082319061252f90339060040161311e565b602060405180830381865afa15801561254c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612570919061346f565b60208801525b60408701516020880151885161259a926001600160a01b0390911691339190612922565b6110e1878787878787612103565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60606000805b89518110156127fd5760006001600160a01b03168a828151811061262457612624613459565b6020026020010151600001516001600160a01b0316036126ac5789818151811061265057612650613459565b60200260200101516020015160000361268757348a828151811061267657612676613459565b602002602001015160200181815250505b89818151811061269957612699613459565b60200260200101516020015191506127f5565b8981815181106126be576126be613459565b60200260200101516020015160000361277e578981815181106126e3576126e3613459565b6020026020010151600001516001600160a01b03166370a08231336040518263ffffffff1660e01b815260040161271a919061311e565b602060405180830381865afa158015612737573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061275b919061346f565b8a828151811061276d5761276d613459565b602002602001015160200181815250505b6127f5338b838151811061279457612794613459565b6020026020010151604001518c84815181106127b2576127b2613459565b6020026020010151602001518d85815181106127d0576127d0613459565b6020026020010151600001516001600160a01b0316612922909392919063ffffffff16565b6001016125fe565b5080341461281d5760405162461bcd60e51b81526004016108c990613488565b61282c89898989898989611768565b9998505050505050505050565b6040516001600160a01b038316602482015260448101829052610f8d90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261295a565b81156128de576001546040516001600160a01b039091169083156108fc029084906000818181858888f193505050501580156128dc573d6000803e3d6000fd5b505b801561291e576002546040516001600160a01b039091169082156108fc029083906000818181858888f19350505050158015610f8d573d6000803e3d6000fd5b5050565b6040516001600160a01b03808516602483015283166044820152606481018290526120e99085906323b872dd60e01b90608401612865565b60006129af826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316612a2f9092919063ffffffff16565b90508051600014806129d05750808060200190518101906129d0919061387c565b610f8d5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016108c9565b6060612a3e8484600085612a46565b949350505050565b606082471015612aa75760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016108c9565b600080866001600160a01b03168587604051612ac3919061389e565b60006040518083038185875af1925050503d8060008114612b00576040519150601f19603f3d011682016040523d82523d6000602084013e612b05565b606091505b50915091506110e18783838760608315612b80578251600003612b79576001600160a01b0385163b612b795760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016108c9565b5081612a3e565b612a3e8383815115612b955781518083602001fd5b8060405162461bcd60e51b81526004016108c991906138ba565b634e487b7160e01b600052604160045260246000fd5b604051608081016001600160401b0381118282101715612be757612be7612baf565b60405290565b604051601f8201601f191681016001600160401b0381118282101715612c1557612c15612baf565b604052919050565b80356001600160a01b038116811461203d57600080fd5b600060808284031215612c4657600080fd5b612c4e612bc5565b9050612c5982612c1d565b8152602082810135818301526040808401359083015260608301356001600160401b0380821115612c8957600080fd5b818501915085601f830112612c9d57600080fd5b813581811115612caf57612caf612baf565b612cc1601f8201601f19168501612bed565b91508082528684828501011115612cd757600080fd5b808484018584013760008482840101525080606085015250505092915050565b600082601f830112612d0857600080fd5b813560206001600160401b0380831115612d2457612d24612baf565b612d32828460051b01612bed565b83815260609384028601830193838201919088861115612d5157600080fd5b8488015b86811015612dbc5781818b031215612d6d5760008081fd5b604080518381018181108882111715612d8857612d88612baf565b8252612d9383612c1d565b81528783013588820152612da8828401612c1d565b918101919091528452928501928101612d55565b509098975050505050505050565b60008083601f840112612ddc57600080fd5b5081356001600160401b03811115612df357600080fd5b602083019150836020828501011115612e0b57600080fd5b9250929050565b803563ffffffff8116811461203d57600080fd5b60008060008060008060008060e0898b031215612e4257600080fd5b88356001600160401b0380821115612e5957600080fd5b612e658c838d01612c34565b995060208b0135915080821115612e7b57600080fd5b612e878c838d01612cf7565b985060408b0135915080821115612e9d57600080fd5b612ea98c838d01612cf7565b975060608b0135965060808b0135915080821115612ec657600080fd5b50612ed38b828c01612dca565b9095509350612ee6905060a08a01612c1d565b9150612ef460c08a01612e12565b90509295985092959890939650565b60008151808452602080850194506020840160005b83811015612f3457815187529582019590820190600101612f18565b509495945050505050565b602081526000612f526020830184612f03565b9392505050565b60008083601f840112612f6b57600080fd5b5081356001600160401b03811115612f8257600080fd5b6020830191508360208260051b8501011115612e0b57600080fd5b600080600080600060608688031215612fb557600080fd5b85356001600160401b0380821115612fcc57600080fd5b612fd889838a01612f59565b90975095506020880135915080821115612ff157600080fd5b50612ffe88828901612f59565b9094509250613011905060408701612c1d565b90509295509295909350565b60008060008060008060a0878903121561303657600080fd5b86356001600160401b038082111561304d57600080fd5b6130598a838b01612cf7565b9750602089013591508082111561306f57600080fd5b61307b8a838b01612cf7565b965060408901359550606089013591508082111561309857600080fd5b506130a589828a01612dca565b90945092506130b8905060808801612c1d565b90509295509295509295565b600080602083850312156130d757600080fd5b82356001600160401b038111156130ed57600080fd5b6130f985828601612f59565b90969095509350505050565b60006020828403121561311757600080fd5b5035919050565b6001600160a01b0391909116815260200190565b600060e0828403121561314457600080fd5b60405160e081016001600160401b038111828210171561316657613166612baf565b60405290508061317583612c1d565b81526020830135602082015261318d60408401612c1d565b604082015261319e60608401612c1d565b60608201526080830135608082015260a083013560a08201526131c360c08401612c1d565b60c08201525092915050565b6000806000806000806000610180888a0312156131eb57600080fd5b87356001600160401b038082111561320257600080fd5b61320e8b838c01612c34565b985061321d8b60208c01613132565b97506101008a013591508082111561323457600080fd5b506132418a828b01612dca565b909650945061325590506101208901612c1d565b92506132646101408901612c1d565b91506132736101608901612e12565b905092959891949750929550565b600080600080600080610160878903121561329b57600080fd5b6132a58888613132565b955060e08701356001600160401b038111156132c057600080fd5b6132cc89828a01612dca565b90965094506132e090506101008801612c1d565b92506132ef6101208801612c1d565b91506130b86101408801612e12565b600080600080600080600060c0888a03121561331957600080fd5b87356001600160401b038082111561333057600080fd5b61333c8b838c01612cf7565b985060208a013591508082111561335257600080fd5b61335e8b838c01612cf7565b975060408a0135965060608a013591508082111561337b57600080fd5b506133888a828b01612dca565b909550935061339b905060808901612c1d565b915061327360a08901612e12565b6000806000606084860312156133be57600080fd5b6133c784612e12565b925060208401356001600160401b03811681146133e357600080fd5b91506133f160408501612c1d565b90509250925092565b60006020828403121561340c57600080fd5b612f5282612c1d565b60006020828403121561342757600080fd5b612f5282612e12565b634e487b7160e01b600052601160045260246000fd5b8181038181111561203757612037613430565b634e487b7160e01b600052603260045260246000fd5b60006020828403121561348157600080fd5b5051919050565b6020808252600f908201526e57726f6e67206d73672e76616c756560881b604082015260600190565b80516001600160a01b03168252602090810151910152565b60008151808452602080850194506020840160005b83811015612f34576134f18783516134b1565b60409690960195908201906001016134de565b60005b8381101561351f578181015183820152602001613507565b50506000910152565b60008151808452613540816020860160208601613504565b601f01601f19169290920160200192915050565b60808152600060e08201865160606080850152818151808452610100860191506020935060208301925060005b818110156135a7576135948385516134b1565b9284019260409290920191600101613581565b5050602089015160a0860152604089015160c086015284810360208601526135cf81896134c9565b925050506135e860408401866001600160a01b03169052565b82810360608401526110e18185613528565b808202811582820484141761203757612037613430565b8082018082111561203757612037613430565b60208082526017908201527614db1a5c1c1859d948131a5b5a5d08115e18d959591959604a1b604082015260600190565b60008151808452602080850194506020840160005b83811015612f345781516001600160a01b03168752958201959082019060010161366a565b6001600160a01b038716815260c0602082018190526000906136b390830188612f03565b82810360408401526136c58188613655565b905082810360608401526136d98187612f03565b905082810360808401526136ed8186613655565b91505063ffffffff831660a0830152979650505050505050565b6020808252600b908201526a496e76616c69642066656560a81b604082015260600190565b600061010061373c8388516134b1565b602087015160408401526040870151606084015261375d60808401876134b1565b6001600160a01b03851660c084015260e083018190526110e181840185613528565b60008261379c57634e487b7160e01b600052601260045260246000fd5b500490565b6020808252600c908201526b08ccaca40e8dede40d0d2ced60a31b604082015260600190565b602080825260149082015273496e76616c69642066656520666f7220636f646560601b604082015260600190565b602080825260179082015276105c989a5d1c9859d9481b9bdd081cdd5c1c1bdc9d1959604a1b604082015260600190565b6001600160a01b0386811682526080602083018190528201859052600090858760a0850137600060a0878501015260a0601f19601f88011684010191508085166040840152508260608301529695505050505050565b60006020828403121561388e57600080fd5b81518015158114612f5257600080fd5b600082516138b0818460208701613504565b9190910192915050565b602081526000612f52602083018461352856fe7d7fb03518253ae01913536628b78d6d82e63e19b943aab5f4948356021259bea164736f6c6343000817000a
Verified Source Code Partial Match
Compiler: v0.8.23+commit.f704f362
EVM: london
Optimization: Yes (10 runs)
Ownable.sol 83 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
IERC20.sol 78 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @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);
/**
* @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 `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, 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 `from` to `to` 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 from, address to, uint256 amount) external returns (bool);
}
IERC20Permit.sol 90 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
SafeERC20.sol 143 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.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 Address for address;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
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'
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 Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @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");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @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).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// 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 cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}
Address.sol 244 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @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
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 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://consensys.net/diligence/blog/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.8.0/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");
(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 functionCallWithValue(target, data, 0, "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");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// 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
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
Context.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
OdosRouterV2.sol 806 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import { IOdosExecutor } from "./interfaces/IOdosExecutor.sol";
import { ISignatureTransfer } from "./interfaces/ISignatureTransfer.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
/// @title Routing contract for Odos SOR
/// @author Semiotic AI
/// @notice Wrapper with security gaurentees around execution of arbitrary operations on user tokens
contract OdosRouterV2 is Ownable {
using SafeERC20 for IERC20;
/// @dev The zero address is uniquely used to represent eth since it is already
/// recognized as an invalid ERC20, and due to its gas efficiency
address internal constant _ETH = address(0);
address public treasury;
address public buyBack;
uint256 public constant BASIS_POINTS = 10_000;
uint256 public treasuryFee = 50; // 0.5%
uint256 public buybackFee = 50; // 0.5%
/// @dev Address list where addresses can be cached for use when reading from storage is cheaper
// than reading from calldata. addressListStart is the storage slot of the first dynamic array element
uint256 private constant addressListStart = 80084422859880547211683076133703299733277748156566366325829078699459944778998;
address[] public addressList;
// @dev constants for managing referrals and fees
uint256 public constant REFERRAL_WITH_FEE_THRESHOLD = 1 << 31;
uint256 public constant FEE_DENOM = 1e18;
// @dev fee taken on multi-input and multi-output swaps instead of positive slippage
uint256 public swapMultiFee;
/// @dev Contains all information needed to describe the input and output for a swap
struct Permit2Info {
address contractAddress;
uint256 nonce;
uint256 deadline;
bytes signature;
}
/// @dev Contains all information needed to describe the input and output for a swap
struct SwapTokenInfo {
address inputToken;
uint256 inputAmount;
address inputReceiver;
address outputToken;
uint256 outputQuote;
uint256 outputMin;
address outputReceiver;
}
/// @dev Contains all information needed to describe an intput token for swapMulti
struct InputTokenInfo {
address tokenAddress;
uint256 amountIn;
address receiver;
}
/// @dev Contains all information needed to describe an output token for swapMulti
struct OutputTokenInfo {
address tokenAddress;
uint256 relativeValue;
address receiver;
}
// @dev event for swapping one token for another
event Swap(address sender, uint256 inputAmount, address inputToken, uint256 amountOut, address outputToken, int256 slippage, uint32 referralCode);
/// @dev event for swapping multiple input and/or output tokens
event SwapMulti(address sender, uint256[] amountsIn, address[] tokensIn, uint256[] amountsOut, address[] tokensOut, uint32 referralCode);
event UpdateTreasury(address indexed newTreasury, address oldTreasury);
event UpdateBuyBack(address indexed newBuyBack, address oldBuyBack);
event UpdateTreasuryFee(uint256 newTreasuryFee, uint256 oldTreasuryFee);
event UpdateBuyBackFee(uint256 newBuyBackFee, uint256 oldBuyBackFee);
/// @dev Holds all information for a given referral
struct ReferralInfo {
uint64 referralFee;
address beneficiary;
bool registered;
}
/// @dev Register referral fee and information
mapping(uint32 => ReferralInfo) public referralLookup;
/// @dev Set the null referralCode as "Unregistered" with no additional fee
constructor(address _treasury, address _buyBack) {
referralLookup[0].referralFee = 0;
referralLookup[0].beneficiary = address(0);
referralLookup[0].registered = true;
swapMultiFee = 5e14;
treasury = _treasury;
buyBack = _buyBack;
}
/// @dev Must exist in order for contract to receive eth
receive() external payable {}
/// @notice Custom decoder to swap with compact calldata for efficient execution on L2s
// function swapCompact() external payable returns (uint256) {
// SwapTokenInfo memory tokenInfo;
// address executor;
// uint32 referralCode;
// bytes calldata pathDefinition;
// {
// address msgSender = msg.sender;
// assembly {
// // Define function to load in token address, either from calldata or from storage
// function getAddress(currPos) -> result, newPos {
// let inputPos := shr(240, calldataload(currPos))
// switch inputPos
// // Reserve the null address as a special case that can be specified with 2 null bytes
// case 0x0000 {
// newPos := add(currPos, 2)
// }
// // This case means that the address is encoded in the calldata directly following the code
// case 0x0001 {
// result := and(shr(80, calldataload(currPos)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
// newPos := add(currPos, 22)
// }
// // Otherwise we use the case to load in from the cached address list
// default {
// result := sload(add(addressListStart, sub(inputPos, 2)))
// newPos := add(currPos, 2)
// }
// }
// let result := 0
// let pos := 4
// // Load in the input and output token addresses
// result, pos := getAddress(pos)
// mstore(tokenInfo, result)
// result, pos := getAddress(pos)
// mstore(add(tokenInfo, 0x60), result)
// // Load in the input amount - a 0 byte means the full balance is to be used
// let inputAmountLength := shr(248, calldataload(pos))
// pos := add(pos, 1)
// if inputAmountLength {
// mstore(add(tokenInfo, 0x20), shr(mul(sub(32, inputAmountLength), 8), calldataload(pos)))
// pos := add(pos, inputAmountLength)
// }
// // Load in the quoted output amount
// let quoteAmountLength := shr(248, calldataload(pos))
// pos := add(pos, 1)
// let outputQuote := shr(mul(sub(32, quoteAmountLength), 8), calldataload(pos))
// mstore(add(tokenInfo, 0x80), outputQuote)
// pos := add(pos, quoteAmountLength)
// // Load the slippage tolerance and use to get the minimum output amount
// {
// let slippageTolerance := shr(232, calldataload(pos))
// mstore(add(tokenInfo, 0xA0), div(mul(outputQuote, sub(0xFFFFFF, slippageTolerance)), 0xFFFFFF))
// }
// pos := add(pos, 3)
// // Load in the executor address
// executor, pos := getAddress(pos)
// // Load in the destination to send the input to - Zero denotes the executor
// result, pos := getAddress(pos)
// if eq(result, 0) {
// result := executor
// }
// mstore(add(tokenInfo, 0x40), result)
// // Load in the destination to send the output to - Zero denotes msg.sender
// result, pos := getAddress(pos)
// if eq(result, 0) {
// result := msgSender
// }
// mstore(add(tokenInfo, 0xC0), result)
// // Load in the referralCode
// referralCode := shr(224, calldataload(pos))
// pos := add(pos, 4)
// // Set the offset and size for the pathDefinition portion of the msg.data
// pathDefinition.length := mul(shr(248, calldataload(pos)), 32)
// pathDefinition.offset := add(pos, 1)
// }
// }
// // return _swapApproval(tokenInfo, pathDefinition, executor, referralCode);
// }
/// @notice Externally facing interface for swapping two tokens
/// @param tokenInfo All information about the tokens being swapped
/// @param pathDefinition Encoded path definition for executor
/// @param executor Address of contract that will execute the path
/// @param referralCode referral code to specify the source of the swap
function swap(
SwapTokenInfo memory tokenInfo,
bytes calldata pathDefinition,
address executor,
address targetRouter,
uint32 referralCode
) external payable returns (uint256 amountOut) {
return _swapApproval(tokenInfo, pathDefinition, executor, targetRouter, referralCode);
}
/// @notice Internal function for initiating approval transfers
/// @param tokenInfo All information about the tokens being swapped
/// @param pathDefinition Encoded path definition for executor
/// @param executor Address of contract that will execute the path
/// @param referralCode referral code to specify the source of the swap
function _swapApproval(
SwapTokenInfo memory tokenInfo,
bytes calldata pathDefinition,
address executor,
address targetRouter,
uint32 referralCode
) internal returns (uint256 amountOut) {
if (tokenInfo.inputToken == _ETH) {
// Support rebasing tokens by allowing the user to trade the entire balance
if (tokenInfo.inputAmount == 0) {
tokenInfo.inputAmount = msg.value;
} else {
require(msg.value == tokenInfo.inputAmount, "Wrong msg.value");
}
} else {
// Support rebasing tokens by allowing the user to trade the entire balance
if (tokenInfo.inputAmount == 0) {
tokenInfo.inputAmount = IERC20(tokenInfo.inputToken).balanceOf(msg.sender);
}
IERC20(tokenInfo.inputToken).safeTransferFrom(msg.sender, tokenInfo.inputReceiver, tokenInfo.inputAmount);
}
return _swap(tokenInfo, pathDefinition, executor, targetRouter, referralCode);
}
/// @notice Externally facing interface for swapping two tokens
/// @param permit2 All additional info for Permit2 transfers
/// @param tokenInfo All information about the tokens being swapped
/// @param pathDefinition Encoded path definition for executor
/// @param executor Address of contract that will execute the path
/// @param referralCode referral code to specify the source of the swap
function swapPermit2(
Permit2Info memory permit2,
SwapTokenInfo memory tokenInfo,
bytes calldata pathDefinition,
address executor,
address targetRouter,
uint32 referralCode
) external returns (uint256 amountOut) {
ISignatureTransfer(permit2.contractAddress).permitTransferFrom(
ISignatureTransfer.PermitTransferFrom(
ISignatureTransfer.TokenPermissions(tokenInfo.inputToken, tokenInfo.inputAmount),
permit2.nonce,
permit2.deadline
),
ISignatureTransfer.SignatureTransferDetails(tokenInfo.inputReceiver, tokenInfo.inputAmount),
msg.sender,
permit2.signature
);
return _swap(tokenInfo, pathDefinition, executor, targetRouter, referralCode);
}
/// @notice contains the main logic for swapping one token for another
/// Assumes input tokens have already been sent to their destinations and
/// that msg.value is set to expected ETH input value, or 0 for ERC20 input
/// @param tokenInfo All information about the tokens being swapped
/// @param pathDefinition Encoded path definition for executor
/// @param executor Address of contract that will execute the path
/// @param referralCode referral code to specify the source of the swap
function _swap(
SwapTokenInfo memory tokenInfo,
bytes calldata pathDefinition,
address executor,
address targetRouter,
uint32 referralCode
) internal returns (uint256 amountOut) {
// Check for valid output specifications
require(tokenInfo.outputMin <= tokenInfo.outputQuote, "Minimum greater than quote");
// require(tokenInfo.outputMin > 0, "Slippage limit too low");
require(tokenInfo.inputToken != tokenInfo.outputToken, "Arbitrage not supported");
uint256 balanceBefore = _universalBalance(tokenInfo.outputToken);
// Delegate the execution of the path to the specified Odos Executor
uint256 msgValue = msg.value;
if (tokenInfo.inputToken == address(0)) {
uint256 treasuryFee = (tokenInfo.inputAmount * treasuryFee) / BASIS_POINTS;
uint256 buybackFee = (tokenInfo.inputAmount * buybackFee) / BASIS_POINTS;
_collectFee(treasuryFee, buybackFee);
msgValue -= treasuryFee + buybackFee;
}
IOdosExecutor(executor).executePath{ value: msgValue }(targetRouter, pathDefinition, tokenInfo.inputToken, tokenInfo.inputAmount);
amountOut = _universalBalance(tokenInfo.outputToken) - balanceBefore;
if (tokenInfo.outputToken == address(0)) {
uint256 treasuryFee = (amountOut * treasuryFee) / BASIS_POINTS;
uint256 buybackFee = (amountOut * buybackFee) / BASIS_POINTS;
_collectFee(treasuryFee, buybackFee);
amountOut -= treasuryFee + buybackFee;
}
if (referralCode > REFERRAL_WITH_FEE_THRESHOLD) {
ReferralInfo memory thisReferralInfo = referralLookup[referralCode];
_universalTransfer(tokenInfo.outputToken, thisReferralInfo.beneficiary, (amountOut * thisReferralInfo.referralFee * 8) / (FEE_DENOM * 10));
amountOut = (amountOut * (FEE_DENOM - thisReferralInfo.referralFee)) / FEE_DENOM;
}
require(amountOut >= tokenInfo.outputMin, "Slippage Limit Exceeded");
// Transfer out the final output to the end user
_universalTransfer(tokenInfo.outputToken, tokenInfo.outputReceiver, amountOut);
emit Swap(msg.sender, tokenInfo.inputAmount, tokenInfo.inputToken, amountOut, tokenInfo.outputToken, 0, referralCode);
}
/// @notice Custom decoder to swapMulti with compact calldata for efficient execution on L2s
// solhint-disable-next-line
function swapMultiCompact() external payable returns (uint256[] memory amountsOut) {
address executor;
uint256 valueOutMin;
InputTokenInfo[] memory inputs;
OutputTokenInfo[] memory outputs;
uint256 pos = 6;
{
address msgSender = msg.sender;
uint256 numInputs;
uint256 numOutputs;
assembly {
numInputs := shr(248, calldataload(4))
numOutputs := shr(248, calldataload(5))
}
inputs = new InputTokenInfo[](numInputs);
outputs = new OutputTokenInfo[](numOutputs);
assembly {
// Define function to load in token address, either from calldata or from storage
function getAddress(currPos) -> result, newPos {
let inputPos := shr(240, calldataload(currPos))
switch inputPos
// Reserve the null address as a special case that can be specified with 2 null bytes
case 0x0000 {
newPos := add(currPos, 2)
}
// This case means that the address is encoded in the calldata directly following the code
case 0x0001 {
result := and(shr(80, calldataload(currPos)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
newPos := add(currPos, 22)
}
// Otherwise we use the case to load in from the cached address list
default {
result := sload(add(addressListStart, sub(inputPos, 2)))
newPos := add(currPos, 2)
}
}
executor, pos := getAddress(pos)
// Load in the quoted output amount
let outputMinAmountLength := shr(248, calldataload(pos))
pos := add(pos, 1)
valueOutMin := shr(mul(sub(32, outputMinAmountLength), 8), calldataload(pos))
pos := add(pos, outputMinAmountLength)
let result := 0
let memPos := 0
for {
let element := 0
} lt(element, numInputs) {
element := add(element, 1)
} {
memPos := mload(add(inputs, add(mul(element, 0x20), 0x20)))
// Load in the token address
result, pos := getAddress(pos)
mstore(memPos, result)
// Load in the input amount - a 0 byte means the full balance is to be used
let inputAmountLength := shr(248, calldataload(pos))
pos := add(pos, 1)
if inputAmountLength {
mstore(add(memPos, 0x20), shr(mul(sub(32, inputAmountLength), 8), calldataload(pos)))
pos := add(pos, inputAmountLength)
}
result, pos := getAddress(pos)
if eq(result, 0) {
result := executor
}
mstore(add(memPos, 0x40), result)
}
for {
let element := 0
} lt(element, numOutputs) {
element := add(element, 1)
} {
memPos := mload(add(outputs, add(mul(element, 0x20), 0x20)))
// Load in the token address
result, pos := getAddress(pos)
mstore(memPos, result)
// Load in the quoted output amount
let outputAmountLength := shr(248, calldataload(pos))
pos := add(pos, 1)
mstore(add(memPos, 0x20), shr(mul(sub(32, outputAmountLength), 8), calldataload(pos)))
pos := add(pos, outputAmountLength)
result, pos := getAddress(pos)
if eq(result, 0) {
result := msgSender
}
mstore(add(memPos, 0x40), result)
}
}
}
uint32 referralCode;
bytes calldata pathDefinition;
assembly {
// Load in the referralCode
referralCode := shr(224, calldataload(pos))
pos := add(pos, 4)
// Set the offset and size for the pathDefinition portion of the msg.data
pathDefinition.length := mul(shr(248, calldataload(pos)), 32)
pathDefinition.offset := add(pos, 1)
}
return _swapMultiApproval(inputs, outputs, valueOutMin, pathDefinition, executor, referralCode);
}
/// @notice Externally facing interface for swapping between two sets of tokens
/// @param inputs list of input token structs for the path being executed
/// @param outputs list of output token structs for the path being executed
/// @param valueOutMin minimum amount of value out the user will accept
/// @param pathDefinition Encoded path definition for executor
/// @param executor Address of contract that will execute the path
/// @param referralCode referral code to specify the source of the swap
function swapMulti(
InputTokenInfo[] memory inputs,
OutputTokenInfo[] memory outputs,
uint256 valueOutMin,
bytes calldata pathDefinition,
address executor,
uint32 referralCode
) external payable returns (uint256[] memory amountsOut) {
return _swapMultiApproval(inputs, outputs, valueOutMin, pathDefinition, executor, referralCode);
}
/// @notice Internal logic for swapping between two sets of tokens with approvals
/// @param inputs list of input token structs for the path being executed
/// @param outputs list of output token structs for the path being executed
/// @param valueOutMin minimum amount of value out the user will accept
/// @param pathDefinition Encoded path definition for executor
/// @param executor Address of contract that will execute the path
/// @param referralCode referral code to specify the source of the swap
function _swapMultiApproval(
InputTokenInfo[] memory inputs,
OutputTokenInfo[] memory outputs,
uint256 valueOutMin,
bytes calldata pathDefinition,
address executor,
uint32 referralCode
) internal returns (uint256[] memory amountsOut) {
// If input amount is still 0 then that means the maximum possible input is to be used
uint256 expectedMsgValue = 0;
for (uint256 i = 0; i < inputs.length; i++) {
if (inputs[i].tokenAddress == _ETH) {
if (inputs[i].amountIn == 0) {
inputs[i].amountIn = msg.value;
}
expectedMsgValue = inputs[i].amountIn;
} else {
if (inputs[i].amountIn == 0) {
inputs[i].amountIn = IERC20(inputs[i].tokenAddress).balanceOf(msg.sender);
}
IERC20(inputs[i].tokenAddress).safeTransferFrom(msg.sender, inputs[i].receiver, inputs[i].amountIn);
}
}
require(msg.value == expectedMsgValue, "Wrong msg.value");
return _swapMulti(inputs, outputs, valueOutMin, pathDefinition, executor, referralCode);
}
/// @notice Externally facing interface for swapping between two sets of tokens with Permit2
/// @param permit2 All additional info for Permit2 transfers
/// @param inputs list of input token structs for the path being executed
/// @param outputs list of output token structs for the path being executed
/// @param valueOutMin minimum amount of value out the user will accept
/// @param pathDefinition Encoded path definition for executor
/// @param executor Address of contract that will execute the path
/// @param referralCode referral code to specify the source of the swap
function swapMultiPermit2(
Permit2Info memory permit2,
InputTokenInfo[] memory inputs,
OutputTokenInfo[] memory outputs,
uint256 valueOutMin,
bytes calldata pathDefinition,
address executor,
uint32 referralCode
) external payable returns (uint256[] memory amountsOut) {
ISignatureTransfer.PermitBatchTransferFrom memory permit;
ISignatureTransfer.SignatureTransferDetails[] memory transferDetails;
{
uint256 permitLength = msg.value > 0 ? inputs.length - 1 : inputs.length;
permit = ISignatureTransfer.PermitBatchTransferFrom(new ISignatureTransfer.TokenPermissions[](permitLength), permit2.nonce, permit2.deadline);
transferDetails = new ISignatureTransfer.SignatureTransferDetails[](permitLength);
}
{
uint256 expectedMsgValue = 0;
for (uint256 i = 0; i < inputs.length; i++) {
if (inputs[i].tokenAddress == _ETH) {
if (inputs[i].amountIn == 0) {
inputs[i].amountIn = msg.value;
}
expectedMsgValue = inputs[i].amountIn;
} else {
if (inputs[i].amountIn == 0) {
inputs[i].amountIn = IERC20(inputs[i].tokenAddress).balanceOf(msg.sender);
}
uint256 permitIndex = expectedMsgValue == 0 ? i : i - 1;
permit.permitted[permitIndex].token = inputs[i].tokenAddress;
permit.permitted[permitIndex].amount = inputs[i].amountIn;
transferDetails[permitIndex].to = inputs[i].receiver;
transferDetails[permitIndex].requestedAmount = inputs[i].amountIn;
}
}
require(msg.value == expectedMsgValue, "Wrong msg.value");
}
ISignatureTransfer(permit2.contractAddress).permitTransferFrom(permit, transferDetails, msg.sender, permit2.signature);
return _swapMulti(inputs, outputs, valueOutMin, pathDefinition, executor, referralCode);
}
/// @notice contains the main logic for swapping between two sets of tokens
/// assumes that inputs have already been sent to the right location and msg.value
/// is set correctly to be 0 for no native input and match native inpuit otherwise
/// @param inputs list of input token structs for the path being executed
/// @param outputs list of output token structs for the path being executed
/// @param valueOutMin minimum amount of value out the user will accept
/// @param pathDefinition Encoded path definition for executor
/// @param executor Address of contract that will execute the path
/// @param referralCode referral code to specify the source of the swap
// solhint-disable-next-line
function _swapMulti(
InputTokenInfo[] memory inputs,
OutputTokenInfo[] memory outputs,
uint256 valueOutMin,
// solhint-disable-next-line
bytes calldata pathDefinition,
// solhint-disable-next-line
address executor,
uint32 referralCode
) internal returns (uint256[] memory amountsOut) {
// Check for valid output specifications
// require(valueOutMin > 0, "Slippage limit too low");
// Extract arrays of input amount values and tokens from the inputs struct list
uint256[] memory amountsIn = new uint256[](inputs.length);
address[] memory tokensIn = new address[](inputs.length);
// Check input specification validity and transfer input tokens to executor
{
for (uint256 i = 0; i < inputs.length; i++) {
amountsIn[i] = inputs[i].amountIn;
tokensIn[i] = inputs[i].tokenAddress;
for (uint256 j = 0; j < i; j++) {
require(inputs[i].tokenAddress != inputs[j].tokenAddress, "Duplicate source tokens");
}
for (uint256 j = 0; j < outputs.length; j++) {
require(inputs[i].tokenAddress != outputs[j].tokenAddress, "Arbitrage not supported");
}
}
}
// Check outputs for duplicates and record balances before swap
uint256[] memory balancesBefore = new uint256[](outputs.length);
for (uint256 i = 0; i < outputs.length; i++) {
for (uint256 j = 0; j < i; j++) {
require(outputs[i].tokenAddress != outputs[j].tokenAddress, "Duplicate destination tokens");
}
balancesBefore[i] = _universalBalance(outputs[i].tokenAddress);
}
// Delegate the execution of the path to the specified Odos Executor
// IOdosExecutor(executor).executePath{ value: msg.value }(pathDefinition, amountsIn, msg.sender);
ReferralInfo memory thisReferralInfo;
if (referralCode > REFERRAL_WITH_FEE_THRESHOLD) {
thisReferralInfo = referralLookup[referralCode];
}
{
uint256 valueOut;
uint256 _swapMultiFee = swapMultiFee;
amountsOut = new uint256[](outputs.length);
for (uint256 i = 0; i < outputs.length; i++) {
// Record the destination token balance before the path is executed
amountsOut[i] = _universalBalance(outputs[i].tokenAddress) - balancesBefore[i];
// Remove the swapMulti Fee (taken instead of positive slippage)
amountsOut[i] = (amountsOut[i] * (FEE_DENOM - _swapMultiFee)) / FEE_DENOM;
if (referralCode > REFERRAL_WITH_FEE_THRESHOLD) {
_universalTransfer(
outputs[i].tokenAddress,
thisReferralInfo.beneficiary,
(amountsOut[i] * thisReferralInfo.referralFee * 8) / (FEE_DENOM * 10)
);
amountsOut[i] = (amountsOut[i] * (FEE_DENOM - thisReferralInfo.referralFee)) / FEE_DENOM;
}
_universalTransfer(outputs[i].tokenAddress, outputs[i].receiver, amountsOut[i]);
// Add the amount out sent to the user to the total value of output
valueOut += amountsOut[i] * outputs[i].relativeValue;
}
require(valueOut >= valueOutMin, "Slippage Limit Exceeded");
}
address[] memory tokensOut = new address[](outputs.length);
for (uint256 i = 0; i < outputs.length; i++) {
tokensOut[i] = outputs[i].tokenAddress;
}
emit SwapMulti(msg.sender, amountsIn, tokensIn, amountsOut, tokensOut, referralCode);
}
/// @notice Register a new referrer, optionally with an additional swap fee
/// @param _referralCode the referral code to use for the new referral
/// @param _referralFee the additional fee to add to each swap using this code
/// @param _beneficiary the address to send the referral's share of fees to
function registerReferralCode(uint32 _referralCode, uint64 _referralFee, address _beneficiary) external {
// Do not allow for any overwriting of referral codes
require(!referralLookup[_referralCode].registered, "Code in use");
// Maximum additional fee a referral can set is 2%
require(_referralFee <= FEE_DENOM / 50, "Fee too high");
// Reserve the lower half of referral codes to be informative only
if (_referralCode <= REFERRAL_WITH_FEE_THRESHOLD) {
require(_referralFee == 0, "Invalid fee for code");
} else {
require(_referralFee > 0, "Invalid fee for code");
// Make sure the beneficiary is not the null address if there is a fee
require(_beneficiary != address(0), "Null beneficiary");
}
referralLookup[_referralCode].referralFee = _referralFee;
referralLookup[_referralCode].beneficiary = _beneficiary;
referralLookup[_referralCode].registered = true;
}
/// @notice Set the fee used for swapMulti
/// @param _swapMultiFee the new fee for swapMulti
function setSwapMultiFee(uint256 _swapMultiFee) external onlyOwner {
// Maximum swapMultiFee that can be set is 0.5%
require(_swapMultiFee <= FEE_DENOM / 200, "Fee too high");
swapMultiFee = _swapMultiFee;
}
/// @notice Push new addresses to the cached address list for when storage is cheaper than calldata
/// @param addresses list of addresses to be added to the cached address list
function writeAddressList(address[] calldata addresses) external onlyOwner {
for (uint256 i = 0; i < addresses.length; i++) {
addressList.push(addresses[i]);
}
}
/// @notice Allows the owner to transfer funds held by the router contract
/// @param tokens List of token address to be transferred
/// @param amounts List of amounts of each token to be transferred
/// @param dest Address to which the funds should be sent
function transferRouterFunds(address[] calldata tokens, uint256[] calldata amounts, address dest) external onlyOwner {
require(tokens.length == amounts.length, "Invalid funds transfer");
for (uint256 i = 0; i < tokens.length; i++) {
_universalTransfer(tokens[i], dest, amounts[i] == 0 ? _universalBalance(tokens[i]) : amounts[i]);
}
}
/// @notice Directly swap funds held in router
/// @param inputs list of input token structs for the path being executed
/// @param outputs list of output token structs for the path being executed
/// @param valueOutMin minimum amount of value out the user will accept
/// @param pathDefinition Encoded path definition for executor
/// @param executor Address of contract that will execute the path
function swapRouterFunds(
InputTokenInfo[] memory inputs,
OutputTokenInfo[] memory outputs,
uint256 valueOutMin,
// solhint-disable-next-line
bytes calldata pathDefinition,
// solhint-disable-next-line
address executor
) external onlyOwner returns (uint256[] memory amountsOut) {
uint256[] memory amountsIn = new uint256[](inputs.length);
address[] memory tokensIn = new address[](inputs.length);
for (uint256 i = 0; i < inputs.length; i++) {
tokensIn[i] = inputs[i].tokenAddress;
amountsIn[i] = inputs[i].amountIn == 0 ? _universalBalance(tokensIn[i]) : inputs[i].amountIn;
_universalTransfer(tokensIn[i], inputs[i].receiver, amountsIn[i]);
}
// Check outputs for duplicates and record balances before swap
uint256[] memory balancesBefore = new uint256[](outputs.length);
address[] memory tokensOut = new address[](outputs.length);
for (uint256 i = 0; i < outputs.length; i++) {
tokensOut[i] = outputs[i].tokenAddress;
balancesBefore[i] = _universalBalance(tokensOut[i]);
}
// Delegate the execution of the path to the specified Odos Executor
// IOdosExecutor(executor).executePath{ value: 0 }(pathDefinition, amountsIn, msg.sender);
uint256 valueOut;
amountsOut = new uint256[](outputs.length);
for (uint256 i = 0; i < outputs.length; i++) {
// Record the destination token balance before the path is executed
amountsOut[i] = _universalBalance(tokensOut[i]) - balancesBefore[i];
_universalTransfer(outputs[i].tokenAddress, outputs[i].receiver, amountsOut[i]);
// Add the amount out sent to the user to the total value of output
valueOut += amountsOut[i] * outputs[i].relativeValue;
}
require(valueOut >= valueOutMin, "Slippage Limit Exceeded");
emit SwapMulti(msg.sender, amountsIn, tokensIn, amountsOut, tokensOut, 0);
}
function setTreasury(address _treasury) external onlyOwner {
address oldTreasury = treasury;
treasury = _treasury;
emit UpdateTreasury(_treasury, oldTreasury);
}
function setBuyback(address _buyBack) external onlyOwner {
address oldBuyBack = buyBack;
buyBack = _buyBack;
emit UpdateBuyBack(_buyBack, oldBuyBack);
}
function setTreasuryFee(uint256 _treasuryFee) external onlyOwner {
require(_treasuryFee <= 1000, "Invalid fee");
uint256 oldTreasuryFee = treasuryFee;
treasuryFee = _treasuryFee;
emit UpdateTreasuryFee(_treasuryFee, oldTreasuryFee);
}
function setBuybackFee(uint256 _buybackFee) external onlyOwner {
require(_buybackFee <= 1000, "Invalid fee");
uint256 oldBuybackFee = buybackFee;
buybackFee = _buybackFee;
emit UpdateBuyBackFee(_buybackFee, oldBuybackFee);
}
/// @notice helper function to get balance of ERC20 or native coin for this contract
/// @param token address of the token to check, null for native coin
/// @return balance of specified coin or token
function _universalBalance(address token) private view returns (uint256) {
if (token == _ETH) {
return address(this).balance;
} else {
return IERC20(token).balanceOf(address(this));
}
}
/// @notice helper function to transfer ERC20 or native coin
/// @param token address of the token being transferred, null for native coin
/// @param to address to transfer to
/// @param amount to transfer
function _universalTransfer(address token, address to, uint256 amount) private {
if (token == _ETH) {
(bool success, ) = payable(to).call{ value: amount }("");
require(success, "ETH transfer failed");
} else {
IERC20(token).safeTransfer(to, amount);
}
}
function _collectFee(uint256 _treasuryFee, uint256 _buybackFee) private {
if (_treasuryFee > 0) {
payable(treasury).transfer(_treasuryFee);
}
if (_buybackFee > 0) {
payable(buyBack).transfer(_buybackFee);
}
}
}
IOdosExecutor.sol 7 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
interface IOdosExecutor {
function executePath(address target, bytes calldata bytecode, address inputToken, uint256 inputAmount) external payable;
// function executePath(bytes calldata bytecode, uint256[] memory inputAmount, address msgSender) external payable;
}
ISignatureTransfer.sol 133 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
/// @title SignatureTransfer
/// @notice Handles ERC20 token transfers through signature based actions
/// @dev Requires user's token approval on the Permit2 contract
interface ISignatureTransfer {
/// @notice Thrown when the requested amount for a transfer is larger than the permissioned amount
/// @param maxAmount The maximum amount a spender can request to transfer
error InvalidAmount(uint256 maxAmount);
/// @notice Thrown when the number of tokens permissioned to a spender does not match the number of tokens being transferred
/// @dev If the spender does not need to transfer the number of tokens permitted, the spender can request amount 0 to be transferred
error LengthMismatch();
/// @notice Emits an event when the owner successfully invalidates an unordered nonce.
event UnorderedNonceInvalidation(address indexed owner, uint256 word, uint256 mask);
/// @notice The token and amount details for a transfer signed in the permit transfer signature
struct TokenPermissions {
// ERC20 token address
address token;
// the maximum amount that can be spent
uint256 amount;
}
/// @notice The signed permit message for a single token transfer
struct PermitTransferFrom {
TokenPermissions permitted;
// a unique value for every token owner's signature to prevent signature replays
uint256 nonce;
// deadline on the permit signature
uint256 deadline;
}
/// @notice Specifies the recipient address and amount for batched transfers.
/// @dev Recipients and amounts correspond to the index of the signed token permissions array.
/// @dev Reverts if the requested amount is greater than the permitted signed amount.
struct SignatureTransferDetails {
// recipient address
address to;
// spender requested amount
uint256 requestedAmount;
}
/// @notice Used to reconstruct the signed permit message for multiple token transfers
/// @dev Do not need to pass in spender address as it is required that it is msg.sender
/// @dev Note that a user still signs over a spender address
struct PermitBatchTransferFrom {
// the tokens and corresponding amounts permitted for a transfer
TokenPermissions[] permitted;
// a unique value for every token owner's signature to prevent signature replays
uint256 nonce;
// deadline on the permit signature
uint256 deadline;
}
/// @notice A map from token owner address and a caller specified word index to a bitmap. Used to set bits in the bitmap
/// to prevent against signature replay protection
/// @dev Uses unordered nonces so that permit messages do not need to be spent in a certain order
/// @dev The mapping is indexed first by the token owner, then by an index specified in the nonce
/// @dev It returns a uint256 bitmap
/// @dev The index, or wordPosition is capped at type(uint248).max
function nonceBitmap(address, uint256) external view returns (uint256);
/// @notice Transfers a token using a signed permit message
/// @dev Reverts if the requested amount is greater than the permitted signed amount
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails The spender's requested transfer details for the permitted token
/// @param signature The signature to verify
function permitTransferFrom(
PermitTransferFrom memory permit,
SignatureTransferDetails calldata transferDetails,
address owner,
bytes calldata signature
) external;
/// @notice Transfers a token using a signed permit message
/// @notice Includes extra data provided by the caller to verify signature over
/// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition
/// @dev Reverts if the requested amount is greater than the permitted signed amount
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails The spender's requested transfer details for the permitted token
/// @param witness Extra data to include when checking the user signature
/// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash
/// @param signature The signature to verify
function permitWitnessTransferFrom(
PermitTransferFrom memory permit,
SignatureTransferDetails calldata transferDetails,
address owner,
bytes32 witness,
string calldata witnessTypeString,
bytes calldata signature
) external;
/// @notice Transfers multiple tokens using a signed permit message
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails Specifies the recipient and requested amount for the token transfer
/// @param signature The signature to verify
function permitTransferFrom(
PermitBatchTransferFrom memory permit,
SignatureTransferDetails[] calldata transferDetails,
address owner,
bytes calldata signature
) external;
/// @notice Transfers multiple tokens using a signed permit message
/// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition
/// @notice Includes extra data provided by the caller to verify signature over
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails Specifies the recipient and requested amount for the token transfer
/// @param witness Extra data to include when checking the user signature
/// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash
/// @param signature The signature to verify
function permitWitnessTransferFrom(
PermitBatchTransferFrom memory permit,
SignatureTransferDetails[] calldata transferDetails,
address owner,
bytes32 witness,
string calldata witnessTypeString,
bytes calldata signature
) external;
/// @notice Invalidates the bits specified in mask for the bitmap at the word position
/// @dev The wordPos is maxed at type(uint248).max
/// @param wordPos A number to index the nonceBitmap at
/// @param mask A bitmap masked against msg.sender's current bitmap at the word position
function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) external;
}
Read Contract
BASIS_POINTS 0xe1f1c4a7 → uint256
FEE_DENOM 0x4886c675 → uint256
REFERRAL_WITH_FEE_THRESHOLD 0x6c082c13 → uint256
addressList 0xb810fb43 → address
buyBack 0xacdf4f18 → address
buybackFee 0x3b2d081c → uint256
owner 0x8da5cb5b → address
referralLookup 0xf827065e → uint64, address, bool
swapMultiFee 0xe7d3fc60 → uint256
treasury 0x61d027b3 → address
treasuryFee 0xcc32d176 → uint256
Write Contract 16 functions
These functions modify contract state and require a wallet transaction to execute.
registerReferralCode 0xe10895f9
uint32 _referralCode
uint64 _referralFee
address _beneficiary
renounceOwnership 0x715018a6
No parameters
setBuyback 0xf3856e76
address _buyBack
setBuybackFee 0x4567bfba
uint256 _buybackFee
setSwapMultiFee 0x9286b93d
uint256 _swapMultiFee
setTreasury 0xf0f44260
address _treasury
setTreasuryFee 0x77e741c7
uint256 _treasuryFee
swap 0xa5969280
tuple tokenInfo
bytes pathDefinition
address executor
address targetRouter
uint32 referralCode
returns: uint256
swapMulti 0x29249a36
tuple[] inputs
tuple[] outputs
uint256 valueOutMin
bytes pathDefinition
address executor
uint32 referralCode
returns: uint256[]
swapMultiCompact 0x84a7f3dd
No parameters
returns: uint256[]
swapMultiPermit2 0x3dd6f352
tuple permit2
tuple[] inputs
tuple[] outputs
uint256 valueOutMin
bytes pathDefinition
address executor
uint32 referralCode
returns: uint256[]
swapPermit2 0x2e42d1a8
tuple permit2
tuple tokenInfo
bytes pathDefinition
address executor
address targetRouter
uint32 referralCode
returns: uint256
swapRouterFunds 0x324cdbd7
tuple[] inputs
tuple[] outputs
uint256 valueOutMin
bytes pathDefinition
address executor
returns: uint256[]
transferOwnership 0xf2fde38b
address newOwner
transferRouterFunds 0x174da621
address[] tokens
uint256[] amounts
address dest
writeAddressList 0x3596f9a2
address[] addresses
Recent Transactions
No transactions found for this address