Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0x833362A84d5ACd4b366014382f0f9d15b21bD12f
Balance 0 ETH
Nonce 1
Code Size 14586 bytes
Indexed Transactions 0
External Etherscan · Sourcify

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