Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0xc70eF137f08fa2574891234EfE3c2b0e46D44Fb4
Balance 0 ETH
Nonce 1
Code Size 22883 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

22883 bytes
0x608060405234801561000f575f80fd5b5060043610610272575f3560e01c80637e9859511161014f578063c63d75b6116100c1578063d905777e11610085578063d905777e146107a4578063dd62ed3e146107d4578063e30c397814610804578063ef8b30f714610822578063f2fde38b14610852578063f77c47911461086e57610272565b8063c63d75b6146106ea578063c6e6f5921461071a578063ccc485b91461074a578063ce96cb771461076a578063d294f0931461079a57610272565b806394bf804d1161011357806394bf804d146105dc57806395d89b411461060c578063a9059cbb1461062a578063b3d7f6b91461065a578063b460af941461068a578063ba087652146106ba57610272565b80637e9859511461055c5780638456cb591461057a57806387788782146105845780638da5cb5b146105a257806392eefe9b146105c057610272565b80633f4ba83a116101e857806360c6d8ae116101ac57806360c6d8ae1461049a578063682c2058146104b85780636e553f65146104d657806370897b231461050657806370a082311461052257806379ba50971461055257610272565b80633f4ba83a146103f4578063402d267d146103fe5780634cdad5061461042e578063568efc071461045e5780635c975abb1461047c57610272565b806318160ddd1161023a57806318160ddd1461034257806323452b9c1461036057806323b872dd1461036a578063313ce5671461039a57806338626450146103b857806338d52e0f146103d657610272565b806301e1d1141461027657806306fdde031461029457806307a2d13a146102b2578063095ea7b3146102e25780630a28a47714610312575b5f80fd5b61027e61088c565b60405161028b919061444f565b60405180910390f35b61029c610c65565b6040516102a991906144f2565b60405180910390f35b6102cc60048036038101906102c7919061454d565b610cf1565b6040516102d9919061444f565b60405180910390f35b6102fc60048036038101906102f791906145d2565b610d5b565b604051610309919061462a565b60405180910390f35b61032c6004803603810190610327919061454d565b610d71565b604051610339919061444f565b60405180910390f35b61034a610dcf565b604051610357919061444f565b60405180910390f35b610368610dd8565b005b610384600480360381019061037f9190614643565b610fb3565b604051610391919061462a565b60405180910390f35b6103a26110bf565b6040516103af91906146ae565b60405180910390f35b6103c06110e3565b6040516103cd919061444f565b60405180910390f35b6103de6110e9565b6040516103eb91906146d6565b60405180910390f35b6103fc611110565b005b610418600480360381019061041391906146ef565b6111f2565b604051610425919061444f565b60405180910390f35b6104486004803603810190610443919061454d565b611236565b604051610455919061444f565b60405180910390f35b610466611247565b604051610473919061444f565b60405180910390f35b61048461124d565b604051610491919061462a565b60405180910390f35b6104a2611260565b6040516104af919061444f565b60405180910390f35b6104c0611266565b6040516104cd919061444f565b60405180910390f35b6104f060048036038101906104eb919061471a565b61126c565b6040516104fd919061444f565b60405180910390f35b610520600480360381019061051b919061454d565b61162c565b005b61053c600480360381019061053791906146ef565b61174a565b604051610549919061444f565b60405180910390f35b61055a611790565b005b610564611963565b604051610571919061444f565b60405180910390f35b610582611969565b005b61058c611a4c565b604051610599919061444f565b60405180910390f35b6105aa611a52565b6040516105b791906146d6565b60405180910390f35b6105da60048036038101906105d591906146ef565b611a77565b005b6105f660048036038101906105f1919061471a565b611bc9565b604051610603919061444f565b60405180910390f35b610614611f89565b60405161062191906144f2565b60405180910390f35b610644600480360381019061063f91906145d2565b612015565b604051610651919061462a565b60405180910390f35b610674600480360381019061066f919061454d565b61202b565b604051610681919061444f565b60405180910390f35b6106a4600480360381019061069f9190614758565b612082565b6040516106b1919061444f565b60405180910390f35b6106d460048036038101906106cf9190614758565b61249f565b6040516106e1919061444f565b60405180910390f35b61070460048036038101906106ff91906146ef565b6128fe565b604051610711919061444f565b60405180910390f35b610734600480360381019061072f919061454d565b612942565b604051610741919061444f565b60405180910390f35b6107526129b3565b604051610761939291906147a8565b60405180910390f35b610784600480360381019061077f91906146ef565b6129ca565b604051610791919061444f565b60405180910390f35b6107a26129e3565b005b6107be60048036038101906107b991906146ef565b612d8f565b6040516107cb919061444f565b60405180910390f35b6107ee60048036038101906107e991906147dd565b612da0565b6040516107fb919061444f565b60405180910390f35b61080c612e22565b60405161081991906146d6565b60405180910390f35b61083c6004803603810190610837919061454d565b612e47565b604051610849919061444f565b60405180910390f35b61086c600480360381019061086791906146ef565b612e58565b005b6108766130a2565b60405161088391906146d6565b60405180910390f35b5f8073ffffffffffffffffffffffffffffffffffffffff1660065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1603610980577f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161093a91906146d6565b602060405180830381865afa158015610955573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610979919061482f565b9050610c62565b5f808060065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c3b288646040518163ffffffff1660e01b81526004015f60405180830381865afa1580156109ec573d5f803e3d5ffd5b505050506040513d5f823e3d601f19601f82011682018060405250810190610a149190614b2e565b915091505f5b8151811015610af5575f828281518110610a3757610a36614ba4565b5b602002602001015190508073ffffffffffffffffffffffffffffffffffffffff166370a0823160065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401610a9b91906146d6565b602060405180830381865afa158015610ab6573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ada919061482f565b85610ae59190614bfe565b9450508080600101915050610a1a565b50827f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff166370a0823160065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401610b7191906146d6565b602060405180830381865afa158015610b8c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bb0919061482f565b7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401610c0991906146d6565b602060405180830381865afa158015610c24573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c48919061482f565b610c529190614bfe565b610c5c9190614bfe565b93505050505b90565b60038054610c7290614c5e565b80601f0160208091040260200160405190810160405280929190818152602001828054610c9e90614c5e565b8015610ce95780601f10610cc057610100808354040283529160200191610ce9565b820191905f5260205f20905b815481529060010190602001808311610ccc57829003601f168201915b505050505081565b5f80610cfb610dcf565b90505f8103610d0d5782915050610d56565b5f610d1661088c565b90505f600a548211610d285781610d37565b600a5482610d369190614c8e565b5b9050828186610d469190614cc1565b610d509190614d2f565b93505050505b919050565b5f610d673384846130ca565b6001905092915050565b5f80610d7b610dcf565b90505f8114610dc557610d8c61088c565b6001610d9661088c565b8386610da29190614cc1565b610dac9190614bfe565b610db69190614c8e565b610dc09190614d2f565b610dc7565b825b915050919050565b5f600554905090565b60075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610e67576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e5e90614da9565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff1660085f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1603610ef6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610eed90614e11565b60405180910390fd5b5f60085f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f73ffffffffffffffffffffffffffffffffffffffff1660075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167fb150023a879fd806e3599b6ca8ee3b60f0e360ab3846d128d67ebce1a391639a60405160405180910390a3565b5f8060025f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205490507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146110a8578281101561109a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161109190614e79565b60405180910390fd5b6110a785338584036130ca565b5b6110b385858561328d565b60019150509392505050565b7f000000000000000000000000000000000000000000000000000000000000000681565b600e5481565b5f7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48905090565b60075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461119f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161119690614da9565b60405180910390fd5b5f600660146101000a81548160ff0219169083151502179055507f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa336040516111e891906146d6565b60405180910390a1565b5f600660149054906101000a900460ff1661122d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61122f565b5f5b9050919050565b5f61124082610cf1565b9050919050565b600b5481565b600660149054906101000a900460ff1681565b600c5481565b600a5481565b5f600660149054906101000a900460ff16156112bd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112b490614ee1565b60405180910390fd5b6112c56134e6565b5f8311611307576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112fe90614f49565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603611375576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161136c90614fb1565b60405180910390fd5b5f61137e610dcf565b111561138d5761138c61352a565b5b61139683612e47565b90505f81116113da576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113d190615019565b60405180910390fd5b7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff166323b872dd3330866040518463ffffffff1660e01b815260040161143793929190615037565b6020604051808303815f875af1158015611453573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114779190615096565b5061148282826135fc565b61148a61088c565b600b819055505f73ffffffffffffffffffffffffffffffffffffffff1660065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146115b7575f6114ed613735565b90505f8111156115b5577f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff1663a9059cbb60065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16836040518363ffffffff1660e01b81526004016115739291906150c1565b6020604051808303815f875af115801561158f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115b39190615096565b505b505b8173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d785846040516116169291906150e8565b60405180910390a36116266137f5565b92915050565b60075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146116bb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116b290614da9565b60405180910390fd5b610bb8811115611700576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116f790615159565b60405180910390fd5b5f6009549050816009819055507f607b1c943753982194530bf7133a5972ea2626e028005410efa54ab20035caf8818360405161173e9291906150e8565b60405180910390a15050565b5f60015f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f20549050919050565b60085f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461181f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611816906151c1565b60405180910390fd5b5f60075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905060085f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1660075f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f60085f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a350565b600d5481565b60075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146119f8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119ef90614da9565b60405180910390fd5b6001600660146101000a81548160ff0219169083151502179055507f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25833604051611a4291906146d6565b60405180910390a1565b60095481565b60075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611b06576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611afd90614da9565b60405180910390fd5b5f60065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508160065f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f1c87e2bbc4e5fa5d7f6f8c44d66cb241dff224b8602eb5435ca2076d2a5c6fc260405160405180910390a35050565b5f600660149054906101000a900460ff1615611c1a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c1190614ee1565b60405180910390fd5b611c226134e6565b5f8311611c64576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c5b90615019565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603611cd2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611cc990614fb1565b60405180910390fd5b5f611cdb610dcf565b1115611cea57611ce961352a565b5b611cf38361202b565b90505f8111611d37576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611d2e90614f49565b60405180910390fd5b7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff166323b872dd3330846040518463ffffffff1660e01b8152600401611d9493929190615037565b6020604051808303815f875af1158015611db0573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611dd49190615096565b50611ddf82846135fc565b611de761088c565b600b819055505f73ffffffffffffffffffffffffffffffffffffffff1660065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611f14575f611e4a613735565b90505f811115611f12577f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff1663a9059cbb60065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16836040518363ffffffff1660e01b8152600401611ed09291906150c1565b6020604051808303815f875af1158015611eec573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f109190615096565b505b505b8173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d78386604051611f739291906150e8565b60405180910390a3611f836137f5565b92915050565b60048054611f9690614c5e565b80601f0160208091040260200160405190810160405280929190818152602001828054611fc290614c5e565b801561200d5780601f10611fe45761010080835404028352916020019161200d565b820191905f5260205f20905b815481529060010190602001808311611ff057829003601f168201915b505050505081565b5f61202133848461328d565b6001905092915050565b5f80612035610dcf565b90505f8114612078578060018261204a61088c565b866120559190614cc1565b61205f9190614bfe565b6120699190614c8e565b6120739190614d2f565b61207a565b825b915050919050565b5f61208b6134e6565b5f84116120cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016120c490614f49565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361213b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161213290614fb1565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036121a9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016121a090615229565b60405180910390fd5b6121b161352a565b6121ba84610d71565b90508173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146122e5575f60025f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205490507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146122e357818110156122d5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016122cc90615291565b60405180910390fd5b6122e283338484036130ca565b5b505b6122ef82826137fe565b5f6122f8613735565b9050848110801561235657505f73ffffffffffffffffffffffffffffffffffffffff1660065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b15612375575f81866123689190614c8e565b9050612373816139ad565b505b7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff1663a9059cbb85876040518363ffffffff1660e01b81526004016123d09291906150c1565b6020604051808303815f875af11580156123ec573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906124109190615096565b508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db88866040516124879291906150e8565b60405180910390a4506124986137f5565b9392505050565b5f6124a86134e6565b5f84116124ea576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016124e190615019565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603612558576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161254f90614fb1565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036125c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016125bd90615229565b60405180910390fd5b6125ce61352a565b6125d784611236565b90505f811161261b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161261290614f49565b60405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612744575f60025f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205490507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146127425784811015612734576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161272b90615291565b60405180910390fd5b61274183338784036130ca565b5b505b61274e82856137fe565b5f612757613735565b905081811080156127b557505f73ffffffffffffffffffffffffffffffffffffffff1660065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b156127d4575f81836127c79190614c8e565b90506127d2816139ad565b505b7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff1663a9059cbb85846040518363ffffffff1660e01b815260040161282f9291906150c1565b6020604051808303815f875af115801561284b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061286f9190615096565b508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db85896040516128e69291906150e8565b60405180910390a4506128f76137f5565b9392505050565b5f600660149054906101000a900460ff16612939577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61293b565b5f5b9050919050565b5f8061294c610dcf565b90505f61295761088c565b90505f600a5482116129695781612978565b600a54826129779190614c8e565b5b90505f83148061298757505f81145b6129a7578083866129989190614cc1565b6129a29190614d2f565b6129a9565b845b9350505050919050565b5f805f600a549250600d549150600c549050909192565b5f6129dc6129d78361174a565b610cf1565b9050919050565b60075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612a72576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612a6990614da9565b60405180910390fd5b612a7a6134e6565b5f600a5490505f8111612ac2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612ab9906152f9565b60405180910390fd5b5f7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401612b1c91906146d6565b602060405180830381865afa158015612b37573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612b5b919061482f565b905081811015612b7f575f8183612b729190614c8e565b9050612b7d816139ad565b505b5f7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401612bd991906146d6565b602060405180830381865afa158015612bf4573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612c18919061482f565b90505f8390508382108015612c38575060018285612c369190614c8e565b145b15612c4d578190506001600a81905550612c55565b5f600a819055505b7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff1663a9059cbb60075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16836040518363ffffffff1660e01b8152600401612cd19291906150c1565b6020604051808303815f875af1158015612ced573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d119190615096565b5060075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f9dc46f23cfb5ddcad0ae7ea2be38d47fec07bb9382ec7e564efc69e036dd66ce82604051612d79919061444f565b60405180910390a250505050612d8d6137f5565b565b5f612d998261174a565b9050919050565b5f60025f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f2054905092915050565b60085f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b5f612e5182612942565b9050919050565b60075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612ee7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612ede90614da9565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603612f55576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612f4c90615361565b60405180910390fd5b60075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603612fe4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612fdb906153ef565b60405180910390fd5b8060085f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff1660075f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167fb150023a879fd806e3599b6ca8ee3b60f0e360ab3846d128d67ebce1a391639a60405160405180910390a350565b5f60065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b5f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603613138576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161312f90615457565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036131a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161319d906154bf565b60405180910390fd5b8060025f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f20819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92583604051613280919061444f565b60405180910390a3505050565b5f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036132fb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016132f290615527565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603613369576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016133609061558f565b60405180910390fd5b5f60015f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f20549050818110156133ed576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016133e4906155f7565b60405180910390fd5b81810360015f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f20819055508160015f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f82825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516134d8919061444f565b60405180910390a350505050565b60025f5403613521576040517f3ee5aeb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025f81905550565b5f61353361088c565b90505f61353e610dcf565b11801561354c5750600b5481115b156135f9575f600b54826135609190614c8e565b90505f612710600954836135749190614cc1565b61357e9190614d2f565b905080600a5f8282546135919190614bfe565b9250508190555080600c5f8282546135a99190614bfe565b9250508190555062015180600e54426135c29190614c8e565b11156135d7575f600d8190555042600e819055505b80600d5f8282546135e89190614bfe565b9250508190555082600b8190555050505b50565b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361366a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016136619061565f565b60405180910390fd5b8060055f82825461367b9190614bfe565b925050819055508060015f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f82825401925050819055508173ffffffffffffffffffffffffffffffffffffffff165f73ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051613729919061444f565b60405180910390a35050565b5f807f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161379091906146d6565b602060405180830381865afa1580156137ab573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906137cf919061482f565b9050600a5481116137e0575f6137ef565b600a54816137ee9190614c8e565b5b91505090565b60015f81905550565b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361386c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613863906156c7565b60405180910390fd5b5f60015f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f20549050818110156138f0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016138e7906155f7565b60405180910390fd5b81810360015f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f20819055508160055f82825403925050819055505f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516139a0919061444f565b60405180910390a3505050565b5f73ffffffffffffffffffffffffffffffffffffffff1660065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1603613a3c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613a339061572f565b60405180910390fd5b5f8190505f7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff166370a0823160065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401613abb91906146d6565b602060405180830381865afa158015613ad6573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613afa919061482f565b90505f811115613bbd575f82821015613b135781613b15565b825b905060065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663ebd546ff826040518263ffffffff1660e01b8152600401613b719190615770565b5f604051808303815f87803b158015613b88575f80fd5b505af1158015613b9a573d5f803e3d5ffd5b505050508083613baa9190614c8e565b92505f8303613bbb57505050614434565b505b5f8060065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c3b288646040518163ffffffff1660e01b81526004015f60405180830381865afa158015613c28573d5f803e3d5ffd5b505050506040513d5f823e3d601f19601f82011682018060405250810190613c509190614b2e565b915091505f805b8251811015613d32575f838281518110613c7457613c73614ba4565b5b602002602001015190508073ffffffffffffffffffffffffffffffffffffffff166370a0823160065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401613cd891906146d6565b602060405180830381865afa158015613cf3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613d17919061482f565b83613d229190614bfe565b9250508080600101915050613c57565b5084825182613d419190614bfe565b1015613d82576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613d799061580c565b60405180910390fd5b5f8590505f5b835181108015613d9757505f87115b156140cf575f848281518110613db057613daf614ba4565b5b602002602001015190505f8173ffffffffffffffffffffffffffffffffffffffff166370a0823160065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401613e1591906146d6565b602060405180830381865afa158015613e30573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613e54919061482f565b90505f8111156140ba575f858286613e6c9190614cc1565b613e769190614d2f565b905089811115613e84578990505b60018751613e929190614c8e565b84148015613e9f57505f8a115b15613ea8578990505b5f8111156140b8575f7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401613f0a91906146d6565b602060405180830381865afa158015613f25573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613f49919061482f565b905060065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663ebd546ff8a8781518110613f9b57613f9a614ba4565b5b6020026020010151846040518363ffffffff1660e01b8152600401613fc192919061582a565b5f604051808303815f87803b158015613fd8575f80fd5b505af1158015613fea573d5f803e3d5ffd5b505050505f7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161404891906146d6565b602060405180830381865afa158015614063573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614087919061482f565b90505f82826140969190614c8e565b9050808d116140a5575f6140b2565b808d6140b19190614c8e565b5b9c505050505b505b505080806140c790615858565b915050613d88565b505f861180156140ec5750600283516140e89190614cc1565b8611155b156143e9575f805f5b85518110156141d6575f86828151811061411257614111614ba4565b5b602002602001015190505f8173ffffffffffffffffffffffffffffffffffffffff166370a0823160065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b815260040161417791906146d6565b602060405180830381865afa158015614192573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906141b6919061482f565b9050848111156141c7578094508293505b505080806001019150506140f5565b508782106143e6575f7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161423891906146d6565b602060405180830381865afa158015614253573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614277919061482f565b905060065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663ebd546ff8884815181106142c9576142c8614ba4565b5b60200260200101518b6040518363ffffffff1660e01b81526004016142ef92919061582a565b5f604051808303815f87803b158015614306575f80fd5b505af1158015614318573d5f803e3d5ffd5b505050505f7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161437691906146d6565b602060405180830381865afa158015614391573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906143b5919061482f565b90505f82826143c49190614c8e565b9050808b116143d3575f6143e0565b808b6143df9190614c8e565b5b9a505050505b50505b600186111561442d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016144249061590f565b60405180910390fd5b5050505050505b50565b5f819050919050565b61444981614437565b82525050565b5f6020820190506144625f830184614440565b92915050565b5f81519050919050565b5f82825260208201905092915050565b5f5b8381101561449f578082015181840152602081019050614484565b5f8484015250505050565b5f601f19601f8301169050919050565b5f6144c482614468565b6144ce8185614472565b93506144de818560208601614482565b6144e7816144aa565b840191505092915050565b5f6020820190508181035f83015261450a81846144ba565b905092915050565b5f604051905090565b5f80fd5b5f80fd5b61452c81614437565b8114614536575f80fd5b50565b5f8135905061454781614523565b92915050565b5f602082840312156145625761456161451b565b5b5f61456f84828501614539565b91505092915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6145a182614578565b9050919050565b6145b181614597565b81146145bb575f80fd5b50565b5f813590506145cc816145a8565b92915050565b5f80604083850312156145e8576145e761451b565b5b5f6145f5858286016145be565b925050602061460685828601614539565b9150509250929050565b5f8115159050919050565b61462481614610565b82525050565b5f60208201905061463d5f83018461461b565b92915050565b5f805f6060848603121561465a5761465961451b565b5b5f614667868287016145be565b9350506020614678868287016145be565b925050604061468986828701614539565b9150509250925092565b5f60ff82169050919050565b6146a881614693565b82525050565b5f6020820190506146c15f83018461469f565b92915050565b6146d081614597565b82525050565b5f6020820190506146e95f8301846146c7565b92915050565b5f602082840312156147045761470361451b565b5b5f614711848285016145be565b91505092915050565b5f80604083850312156147305761472f61451b565b5b5f61473d85828601614539565b925050602061474e858286016145be565b9150509250929050565b5f805f6060848603121561476f5761476e61451b565b5b5f61477c86828701614539565b935050602061478d868287016145be565b925050604061479e868287016145be565b9150509250925092565b5f6060820190506147bb5f830186614440565b6147c86020830185614440565b6147d56040830184614440565b949350505050565b5f80604083850312156147f3576147f261451b565b5b5f614800858286016145be565b9250506020614811858286016145be565b9150509250929050565b5f8151905061482981614523565b92915050565b5f602082840312156148445761484361451b565b5b5f6148518482850161481b565b91505092915050565b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b614894826144aa565b810181811067ffffffffffffffff821117156148b3576148b261485e565b5b80604052505050565b5f6148c5614512565b90506148d1828261488b565b919050565b5f67ffffffffffffffff8211156148f0576148ef61485e565b5b602082029050602081019050919050565b5f80fd5b5f80fd5b5f67ffffffffffffffff8211156149235761492261485e565b5b61492c826144aa565b9050602081019050919050565b5f61494b61494684614909565b6148bc565b90508281526020810184848401111561496757614966614905565b5b614972848285614482565b509392505050565b5f82601f83011261498e5761498d61485a565b5b815161499e848260208601614939565b91505092915050565b5f6149b96149b4846148d6565b6148bc565b905080838252602082019050602084028301858111156149dc576149db614901565b5b835b81811015614a2357805167ffffffffffffffff811115614a0157614a0061485a565b5b808601614a0e898261497a565b855260208501945050506020810190506149de565b5050509392505050565b5f82601f830112614a4157614a4061485a565b5b8151614a518482602086016149a7565b91505092915050565b5f67ffffffffffffffff821115614a7457614a7361485e565b5b602082029050602081019050919050565b5f81519050614a93816145a8565b92915050565b5f614aab614aa684614a5a565b6148bc565b90508083825260208201905060208402830185811115614ace57614acd614901565b5b835b81811015614af75780614ae38882614a85565b845260208401935050602081019050614ad0565b5050509392505050565b5f82601f830112614b1557614b1461485a565b5b8151614b25848260208601614a99565b91505092915050565b5f8060408385031215614b4457614b4361451b565b5b5f83015167ffffffffffffffff811115614b6157614b6061451f565b5b614b6d85828601614a2d565b925050602083015167ffffffffffffffff811115614b8e57614b8d61451f565b5b614b9a85828601614b01565b9150509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f614c0882614437565b9150614c1383614437565b9250828201905080821115614c2b57614c2a614bd1565b5b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f6002820490506001821680614c7557607f821691505b602082108103614c8857614c87614c31565b5b50919050565b5f614c9882614437565b9150614ca383614437565b9250828203905081811115614cbb57614cba614bd1565b5b92915050565b5f614ccb82614437565b9150614cd683614437565b9250828202614ce481614437565b91508282048414831517614cfb57614cfa614bd1565b5b5092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f614d3982614437565b9150614d4483614437565b925082614d5457614d53614d02565b5b828204905092915050565b7f5661756c743a206e6f74206f776e6572000000000000000000000000000000005f82015250565b5f614d93601083614472565b9150614d9e82614d5f565b602082019050919050565b5f6020820190508181035f830152614dc081614d87565b9050919050565b7f5661756c743a206e6f2070656e64696e67206f776e65720000000000000000005f82015250565b5f614dfb601783614472565b9150614e0682614dc7565b602082019050919050565b5f6020820190508181035f830152614e2881614def565b9050919050565b7f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000005f82015250565b5f614e63601d83614472565b9150614e6e82614e2f565b602082019050919050565b5f6020820190508181035f830152614e9081614e57565b9050919050565b7f5661756c743a20706175736564000000000000000000000000000000000000005f82015250565b5f614ecb600d83614472565b9150614ed682614e97565b602082019050919050565b5f6020820190508181035f830152614ef881614ebf565b9050919050565b7f5661756c743a207a65726f2061737365747300000000000000000000000000005f82015250565b5f614f33601283614472565b9150614f3e82614eff565b602082019050919050565b5f6020820190508181035f830152614f6081614f27565b9050919050565b7f5661756c743a207a65726f2072656365697665720000000000000000000000005f82015250565b5f614f9b601483614472565b9150614fa682614f67565b602082019050919050565b5f6020820190508181035f830152614fc881614f8f565b9050919050565b7f5661756c743a207a65726f2073686172657300000000000000000000000000005f82015250565b5f615003601283614472565b915061500e82614fcf565b602082019050919050565b5f6020820190508181035f83015261503081614ff7565b9050919050565b5f60608201905061504a5f8301866146c7565b61505760208301856146c7565b6150646040830184614440565b949350505050565b61507581614610565b811461507f575f80fd5b50565b5f815190506150908161506c565b92915050565b5f602082840312156150ab576150aa61451b565b5b5f6150b884828501615082565b91505092915050565b5f6040820190506150d45f8301856146c7565b6150e16020830184614440565b9392505050565b5f6040820190506150fb5f830185614440565b6151086020830184614440565b9392505050565b7f5661756c743a2066656520746f6f2068696768000000000000000000000000005f82015250565b5f615143601383614472565b915061514e8261510f565b602082019050919050565b5f6020820190508181035f83015261517081615137565b9050919050565b7f5661756c743a206e6f742070656e64696e67206f776e657200000000000000005f82015250565b5f6151ab601883614472565b91506151b682615177565b602082019050919050565b5f6020820190508181035f8301526151d88161519f565b9050919050565b7f5661756c743a207a65726f206f776e65720000000000000000000000000000005f82015250565b5f615213601183614472565b915061521e826151df565b602082019050919050565b5f6020820190508181035f83015261524081615207565b9050919050565b7f5661756c743a20696e73756666696369656e7420616c6c6f77616e63650000005f82015250565b5f61527b601d83614472565b915061528682615247565b602082019050919050565b5f6020820190508181035f8301526152a88161526f565b9050919050565b7f5661756c743a206e6f206665657320746f20636c61696d0000000000000000005f82015250565b5f6152e3601783614472565b91506152ee826152af565b602082019050919050565b5f6020820190508181035f830152615310816152d7565b9050919050565b7f5661756c743a206e6577206f776e6572206973207a65726f20616464726573735f82015250565b5f61534b602083614472565b915061535682615317565b602082019050919050565b5f6020820190508181035f8301526153788161533f565b9050919050565b7f5661756c743a206e6577206f776e65722069732063757272656e74206f776e655f8201527f7200000000000000000000000000000000000000000000000000000000000000602082015250565b5f6153d9602183614472565b91506153e48261537f565b604082019050919050565b5f6020820190508181035f830152615406816153cd565b9050919050565b7f45524332303a20617070726f76652066726f6d207a65726f00000000000000005f82015250565b5f615441601883614472565b915061544c8261540d565b602082019050919050565b5f6020820190508181035f83015261546e81615435565b9050919050565b7f45524332303a20617070726f766520746f207a65726f000000000000000000005f82015250565b5f6154a9601683614472565b91506154b482615475565b602082019050919050565b5f6020820190508181035f8301526154d68161549d565b9050919050565b7f45524332303a207472616e736665722066726f6d207a65726f000000000000005f82015250565b5f615511601983614472565b915061551c826154dd565b602082019050919050565b5f6020820190508181035f83015261553e81615505565b9050919050565b7f45524332303a207472616e7366657220746f207a65726f0000000000000000005f82015250565b5f615579601783614472565b915061558482615545565b602082019050919050565b5f6020820190508181035f8301526155a68161556d565b9050919050565b7f45524332303a20696e73756666696369656e742062616c616e636500000000005f82015250565b5f6155e1601b83614472565b91506155ec826155ad565b602082019050919050565b5f6020820190508181035f83015261560e816155d5565b9050919050565b7f45524332303a206d696e7420746f207a65726f000000000000000000000000005f82015250565b5f615649601383614472565b915061565482615615565b602082019050919050565b5f6020820190508181035f8301526156768161563d565b9050919050565b7f45524332303a206275726e2066726f6d207a65726f00000000000000000000005f82015250565b5f6156b1601583614472565b91506156bc8261567d565b602082019050919050565b5f6020820190508181035f8301526156de816156a5565b9050919050565b7f5661756c743a206e6f20636f6e74726f6c6c65720000000000000000000000005f82015250565b5f615719601483614472565b9150615724826156e5565b602082019050919050565b5f6020820190508181035f8301526157468161570d565b9050919050565b50565b5f61575b5f83614472565b91506157668261574d565b5f82019050919050565b5f6040820190508181035f83015261578781615750565b90506157966020830184614440565b92915050565b7f5661756c743a20696e73756666696369656e742066756e647320696e207374725f8201527f6174656769657300000000000000000000000000000000000000000000000000602082015250565b5f6157f6602783614472565b91506158018261579c565b604082019050919050565b5f6020820190508181035f830152615823816157ea565b9050919050565b5f6040820190508181035f83015261584281856144ba565b90506158516020830184614440565b9392505050565b5f61586282614437565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361589457615893614bd1565b5b600182019050919050565b7f5661756c743a207769746864726177616c206661696c65642064756520746f205f8201527f726f756e64696e67000000000000000000000000000000000000000000000000602082015250565b5f6158f9602883614472565b91506159048261589f565b604082019050919050565b5f6020820190508181035f830152615926816158ed565b905091905056fea2646970667358221220748c5b54b8232b3005ae0ff1c34eb54c00755e191cad4321a54ae5961d7d683364736f6c63430008170033

Verified Source Code Full Match

Compiler: v0.8.23+commit.f704f362 EVM: shanghai Optimization: No
VaultV2.sol 600 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "../interfaces/IVault.sol";
import "../interfaces/IController.sol";
import "../interfaces/IStrategyAdapter.sol";
import {IERC20} from "lib/forge-std/src/interfaces/IERC20.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

interface IERC20Metadata is IERC20 {
    function decimals() external view returns (uint8);
}

/**
 * @title VaultV2
 * @notice Enhanced Vault with ownership transfer capability for timelock integration
 * @dev Adds 2-step ownership transfer pattern for safety and reentrancy protection
 */
contract VaultV2 is IVault, ReentrancyGuard {
    mapping(address => uint256) private _balances;
    mapping(address => mapping(address => uint256)) private _allowances;
    
    string public name;
    string public symbol;
    uint8 public immutable decimals;
    uint256 private _totalSupply;
    
    IERC20 private immutable _asset;
    IController private _controller;
    
    bool public paused;
    address public owner;
    address public pendingOwner;  // For 2-step ownership transfer
    uint256 public performanceFee; // in basis points (e.g., 1000 = 10%)
    uint256 public accruedFees;
    uint256 public lastTotalAssets;
    
    uint256 private constant MAX_PERFORMANCE_FEE = 3000; // 30%
    uint256 private constant FEE_PRECISION = 10000;

    // Additional state variables for fee analytics
    uint256 public totalFeesCollected;
    uint256 public feesCollected24h;
    uint256 public lastFeeCollectionTime;
    
    // Events
    event ControllerUpdated(address indexed oldController, address indexed newController);
    event PerformanceFeeUpdated(uint256 oldFee, uint256 newFee);
    event FeesCollected(address indexed collector, uint256 amount);
    event Paused(address account);
    event Unpaused(address account);
    event OwnershipTransferInitiated(address indexed currentOwner, address indexed pendingOwner);
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
    modifier onlyOwner() {
        require(msg.sender == owner, "Vault: not owner");
        _;
    }
    
    modifier whenNotPaused() {
        require(!paused, "Vault: paused");
        _;
    }
    
    constructor(
        address assetAddress,
        string memory _name,
        string memory _symbol
    ) {
        _asset = IERC20(assetAddress);
        name = _name;
        symbol = _symbol;
        decimals = IERC20Metadata(assetAddress).decimals();
        owner = msg.sender;
        performanceFee = 1000; // Default 10% performance fee
    }
    
    // ============ Ownership Functions (NEW) ============
    
    /**
     * @notice Initiate ownership transfer (2-step process for safety)
     * @param newOwner The address of the new owner
     */
    function transferOwnership(address newOwner) external onlyOwner {
        require(newOwner != address(0), "Vault: new owner is zero address");
        require(newOwner != owner, "Vault: new owner is current owner");
        
        pendingOwner = newOwner;
        emit OwnershipTransferInitiated(owner, newOwner);
    }
    
    /**
     * @notice Accept ownership transfer (must be called by pending owner)
     */
    function acceptOwnership() external {
        require(msg.sender == pendingOwner, "Vault: not pending owner");
        
        address oldOwner = owner;
        owner = pendingOwner;
        pendingOwner = address(0);
        
        emit OwnershipTransferred(oldOwner, owner);
    }
    
    /**
     * @notice Cancel pending ownership transfer
     */
    function cancelOwnershipTransfer() external onlyOwner {
        require(pendingOwner != address(0), "Vault: no pending owner");
        
        pendingOwner = address(0);
        emit OwnershipTransferInitiated(owner, address(0));
    }
    
    // ============ Original Functions (unchanged) ============
    
    // Getter functions for IERC4626 compliance
    function asset() public view override returns (address) {
        return address(_asset);
    }
    
    function controller() public view override returns (address) {
        return address(_controller);
    }
    
    // ERC20 Implementation
    function totalSupply() public view override returns (uint256) {
        return _totalSupply;
    }
    
    function balanceOf(address account) public view override returns (uint256) {
        return _balances[account];
    }
    
    function transfer(address to, uint256 amount) public override returns (bool) {
        _transfer(msg.sender, to, amount);
        return true;
    }
    
    function allowance(address tokenOwner, address spender) public view override returns (uint256) {
        return _allowances[tokenOwner][spender];
    }
    
    function approve(address spender, uint256 amount) public override returns (bool) {
        _approve(msg.sender, spender, amount);
        return true;
    }
    
    function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
        uint256 currentAllowance = _allowances[from][msg.sender];
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(from, msg.sender, currentAllowance - amount);
            }
        }
        _transfer(from, to, amount);
        return true;
    }
    
    // ERC4626 Implementation
    function totalAssets() public view override returns (uint256) {
        if (address(_controller) == address(0)) {
            return _asset.balanceOf(address(this));
        }
        
        // Get total assets across all strategies from controller
        uint256 totalInStrategies = 0;
        (string[] memory names, address[] memory adapters) = _controller.getAllStrategies();
        
        for (uint256 i = 0; i < adapters.length; i++) {
            IStrategyAdapter strategy = IStrategyAdapter(adapters[i]);
            totalInStrategies += strategy.balanceOf(address(_controller));
        }
        
        // Add any idle funds in the vault and controller
        return _asset.balanceOf(address(this)) + _asset.balanceOf(address(_controller)) + totalInStrategies;
    }
    
    function convertToShares(uint256 assets) public view override returns (uint256) {
        uint256 supply = totalSupply();
        uint256 _totalAssets = totalAssets();
        // When calculating shares, consider fees as already claimed
        uint256 assetsAfterFees = _totalAssets > accruedFees ? _totalAssets - accruedFees : _totalAssets;
        return supply == 0 || assetsAfterFees == 0 ? assets : assets * supply / assetsAfterFees;
    }
    
    function convertToAssets(uint256 shares) public view override returns (uint256) {
        uint256 supply = totalSupply();
        if (supply == 0) return shares;
        // When calculating assets for redemption, exclude fees
        uint256 _totalAssets = totalAssets();
        uint256 assetsAfterFees = _totalAssets > accruedFees ? _totalAssets - accruedFees : _totalAssets;
        return shares * assetsAfterFees / supply;
    }
    
    function maxDeposit(address) public view override returns (uint256) {
        return paused ? 0 : type(uint256).max;
    }
    
    function maxMint(address) public view override returns (uint256) {
        return paused ? 0 : type(uint256).max;
    }
    
    function maxWithdraw(address tokenOwner) public view override returns (uint256) {
        return convertToAssets(balanceOf(tokenOwner));
    }
    
    function maxRedeem(address tokenOwner) public view override returns (uint256) {
        return balanceOf(tokenOwner);
    }
    
    function previewDeposit(uint256 assets) public view override returns (uint256) {
        return convertToShares(assets);
    }
    
    function previewMint(uint256 shares) public view override returns (uint256) {
        uint256 supply = totalSupply();
        return supply == 0 ? shares : (shares * totalAssets() + supply - 1) / supply;
    }
    
    function previewWithdraw(uint256 assets) public view override returns (uint256) {
        uint256 supply = totalSupply();
        return supply == 0 ? assets : (assets * supply + totalAssets() - 1) / totalAssets();
    }
    
    function previewRedeem(uint256 shares) public view override returns (uint256) {
        return convertToAssets(shares);
    }
    
    function deposit(uint256 assets, address receiver) public override whenNotPaused nonReentrant returns (uint256 shares) {
        require(assets > 0, "Vault: zero assets");
        require(receiver != address(0), "Vault: zero receiver");
        
        // Calculate and accrue performance fees before deposit (only if not first deposit)
        if (totalSupply() > 0) {
            _accruePerformanceFees();
        }
        
        shares = previewDeposit(assets);
        require(shares > 0, "Vault: zero shares");
        
        _asset.transferFrom(msg.sender, address(this), assets);
        _mint(receiver, shares);
        
        // Update lastTotalAssets to include the new deposit
        // This prevents the deposit from being treated as yield
        lastTotalAssets = totalAssets();
        
        // Deploy funds to controller if set
        if (address(_controller) != address(0)) {
            uint256 idleBalance = _getAvailableBalance();
            if (idleBalance > 0) {
                _asset.transfer(address(_controller), idleBalance);
            }
        }
        
        emit Deposit(msg.sender, receiver, assets, shares);
    }
    
    function mint(uint256 shares, address receiver) public override whenNotPaused nonReentrant returns (uint256 assets) {
        require(shares > 0, "Vault: zero shares");
        require(receiver != address(0), "Vault: zero receiver");
        
        // Calculate and accrue performance fees before mint (only if not first deposit)
        if (totalSupply() > 0) {
            _accruePerformanceFees();
        }
        
        assets = previewMint(shares);
        require(assets > 0, "Vault: zero assets");
        
        _asset.transferFrom(msg.sender, address(this), assets);
        _mint(receiver, shares);
        
        // Update lastTotalAssets to include the new deposit
        // This prevents the deposit from being treated as yield
        lastTotalAssets = totalAssets();
        
        // Deploy funds to controller if set
        if (address(_controller) != address(0)) {
            uint256 idleBalance = _getAvailableBalance();
            if (idleBalance > 0) {
                _asset.transfer(address(_controller), idleBalance);
            }
        }
        
        emit Deposit(msg.sender, receiver, assets, shares);
    }
    
    function withdraw(
        uint256 assets,
        address receiver,
        address tokenOwner
    ) public override nonReentrant returns (uint256 shares) {
        require(assets > 0, "Vault: zero assets");
        require(receiver != address(0), "Vault: zero receiver");
        require(tokenOwner != address(0), "Vault: zero owner");
        
        // Calculate and accrue performance fees before withdrawal
        _accruePerformanceFees();
        
        shares = previewWithdraw(assets);
        
        if (msg.sender != tokenOwner) {
            uint256 currentAllowance = _allowances[tokenOwner][msg.sender];
            if (currentAllowance != type(uint256).max) {
                require(currentAllowance >= shares, "Vault: insufficient allowance");
                unchecked {
                    _approve(tokenOwner, msg.sender, currentAllowance - shares);
                }
            }
        }
        
        _burn(tokenOwner, shares);
        
        // Withdraw from strategies if needed
        uint256 vaultBalance = _getAvailableBalance();
        if (vaultBalance < assets && address(_controller) != address(0)) {
            uint256 needed = assets - vaultBalance;
            _withdrawFromStrategies(needed);
        }
        
        _asset.transfer(receiver, assets);
        
        emit Withdraw(msg.sender, receiver, tokenOwner, assets, shares);
    }
    
    function redeem(
        uint256 shares,
        address receiver,
        address tokenOwner
    ) public override nonReentrant returns (uint256 assets) {
        require(shares > 0, "Vault: zero shares");
        require(receiver != address(0), "Vault: zero receiver");
        require(tokenOwner != address(0), "Vault: zero owner");
        
        // Calculate and accrue performance fees before redemption
        _accruePerformanceFees();
        
        assets = previewRedeem(shares);
        require(assets > 0, "Vault: zero assets");
        
        if (msg.sender != tokenOwner) {
            uint256 currentAllowance = _allowances[tokenOwner][msg.sender];
            if (currentAllowance != type(uint256).max) {
                require(currentAllowance >= shares, "Vault: insufficient allowance");
                unchecked {
                    _approve(tokenOwner, msg.sender, currentAllowance - shares);
                }
            }
        }
        
        _burn(tokenOwner, shares);
        
        // Withdraw from strategies if needed
        uint256 vaultBalance = _getAvailableBalance();
        if (vaultBalance < assets && address(_controller) != address(0)) {
            uint256 needed = assets - vaultBalance;
            _withdrawFromStrategies(needed);
        }
        
        _asset.transfer(receiver, assets);
        
        emit Withdraw(msg.sender, receiver, tokenOwner, assets, shares);
    }
    
    // IVault specific functions
    function pause() external override onlyOwner {
        paused = true;
        emit Paused(msg.sender);
    }
    
    function unpause() external override onlyOwner {
        paused = false;
        emit Unpaused(msg.sender);
    }
    
    function setController(address newController) external override onlyOwner {
        address oldController = address(_controller);
        _controller = IController(newController);
        emit ControllerUpdated(oldController, newController);
    }
    
    function setPerformanceFee(uint256 fee) external override onlyOwner {
        require(fee <= MAX_PERFORMANCE_FEE, "Vault: fee too high");
        uint256 oldFee = performanceFee;
        performanceFee = fee;
        emit PerformanceFeeUpdated(oldFee, fee);
    }
    
    function claimFees() external override onlyOwner nonReentrant {
        uint256 fees = accruedFees;
        require(fees > 0, "Vault: no fees to claim");
        
        // Check if vault has enough balance
        uint256 vaultBalance = _asset.balanceOf(address(this));
        if (vaultBalance < fees) {
            // Need to withdraw the difference from strategies
            uint256 needed = fees - vaultBalance;
            _withdrawFromStrategies(needed);
        }
        
        // After withdrawal, get the actual balance
        uint256 actualBalance = _asset.balanceOf(address(this));
        
        // If we're short by just 1 wei due to rounding, adjust
        uint256 feesToTransfer = fees;
        if (actualBalance < fees && fees - actualBalance == 1) {
            feesToTransfer = actualBalance;
            accruedFees = 1; // Keep track of the rounding dust
        } else {
            accruedFees = 0;
        }
        
        _asset.transfer(owner, feesToTransfer);
        
        emit FeesCollected(owner, feesToTransfer);
    }
    
    // Internal functions
    function _getAvailableBalance() internal view returns (uint256) {
        uint256 balance = _asset.balanceOf(address(this));
        return balance > accruedFees ? balance - accruedFees : 0;
    }
    
    function _transfer(address from, address to, uint256 amount) internal {
        require(from != address(0), "ERC20: transfer from zero");
        require(to != address(0), "ERC20: transfer to zero");
        
        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: insufficient balance");
        unchecked {
            _balances[from] = fromBalance - amount;
            _balances[to] += amount;
        }
        
        emit Transfer(from, to, amount);
    }
    
    function _mint(address account, uint256 amount) internal {
        require(account != address(0), "ERC20: mint to zero");
        
        _totalSupply += amount;
        unchecked {
            _balances[account] += amount;
        }
        emit Transfer(address(0), account, amount);
    }
    
    function _burn(address account, uint256 amount) internal {
        require(account != address(0), "ERC20: burn from zero");
        
        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: insufficient balance");
        unchecked {
            _balances[account] = accountBalance - amount;
            _totalSupply -= amount;
        }
        
        emit Transfer(account, address(0), amount);
    }
    
    function _approve(address tokenOwner, address spender, uint256 amount) internal {
        require(tokenOwner != address(0), "ERC20: approve from zero");
        require(spender != address(0), "ERC20: approve to zero");
        
        _allowances[tokenOwner][spender] = amount;
        emit Approval(tokenOwner, spender, amount);
    }
    
    function _accruePerformanceFees() internal {
        uint256 currentTotalAssets = totalAssets();
        
        // Only accrue fees if there are existing shares (not first deposit)
        if (totalSupply() > 0 && currentTotalAssets > lastTotalAssets) {
            uint256 yield = currentTotalAssets - lastTotalAssets;
            uint256 fee = (yield * performanceFee) / FEE_PRECISION;
            accruedFees += fee;
            totalFeesCollected += fee;

            if (block.timestamp - lastFeeCollectionTime > 1 days) {
                feesCollected24h = 0;
                lastFeeCollectionTime = block.timestamp;
            }
            feesCollected24h += fee;
            
            // Update lastTotalAssets to current value after accruing fees
            // This is only updated here for withdrawals/redeems to track the new baseline
            lastTotalAssets = currentTotalAssets;
        }
    }

    // View function to return fee analytics
    function getFeeAnalytics() external view returns (
        uint256 _accruedFees,
        uint256 _feesCollected24h,
        uint256 _totalFeesCollected
    ) {
        _accruedFees = accruedFees;
        _feesCollected24h = feesCollected24h;
        _totalFeesCollected = totalFeesCollected;
    }
    
    function _withdrawFromStrategies(uint256 amount) internal {
        require(address(_controller) != address(0), "Vault: no controller");
        
        uint256 remaining = amount;
        
        // First, check if controller has idle USDC
        uint256 controllerBalance = _asset.balanceOf(address(_controller));
        if (controllerBalance > 0) {
            uint256 toTransfer = controllerBalance >= remaining ? remaining : controllerBalance;
            // Transfer idle funds from controller to vault
            _controller.withdrawFromStrategy("", toTransfer); // Empty string means withdraw idle funds
            remaining -= toTransfer;
            
            if (remaining == 0) return;
        }
        
        // Get strategies from controller
        (string[] memory names, address[] memory adapters) = _controller.getAllStrategies();
        
        uint256 totalInStrategies = 0;
        
        // First calculate total balance across all strategies
        for (uint256 i = 0; i < adapters.length; i++) {
            IStrategyAdapter strategy = IStrategyAdapter(adapters[i]);
            totalInStrategies += strategy.balanceOf(address(_controller));
        }
        
        // Allow for small rounding errors (up to number of strategies)
        // Check if strategies have enough for the REMAINING amount after idle funds withdrawal
        require(totalInStrategies + adapters.length >= remaining, "Vault: insufficient funds in strategies");
        
        // Save the amount we need to withdraw from strategies
        uint256 amountFromStrategies = remaining;
        
        // Withdraw proportionally from each strategy
        for (uint256 i = 0; i < adapters.length && remaining > 0; i++) {
            IStrategyAdapter strategy = IStrategyAdapter(adapters[i]);
            uint256 strategyBalance = strategy.balanceOf(address(_controller));
            
            if (strategyBalance > 0) {
                // Calculate proportional amount to withdraw from this strategy
                // Use the total amount needed from strategies for proportional calculation
                uint256 toWithdraw = (amountFromStrategies * strategyBalance) / totalInStrategies;
                
                // Ensure we don't withdraw more than remaining
                if (toWithdraw > remaining) {
                    toWithdraw = remaining;
                }
                
                // If it's the last strategy, withdraw exactly what's remaining
                if (i == adapters.length - 1 && remaining > 0) {
                    toWithdraw = remaining;
                }
                
                if (toWithdraw > 0) {
                    uint256 balanceBefore = _asset.balanceOf(address(this));
                    
                    // Controller will withdraw and send funds to vault
                    _controller.withdrawFromStrategy(names[i], toWithdraw);
                    
                    uint256 balanceAfter = _asset.balanceOf(address(this));
                    uint256 received = balanceAfter - balanceBefore;
                    
                    remaining = remaining > received ? remaining - received : 0;
                }
            }
        }
        
        // Handle any rounding errors
        if (remaining > 0 && remaining <= adapters.length * 2) {
            // Try one more withdrawal from the strategy with the most balance
            uint256 maxBalance = 0;
            uint256 maxIndex = 0;
            
            for (uint256 i = 0; i < adapters.length; i++) {
                IStrategyAdapter strategy = IStrategyAdapter(adapters[i]);
                uint256 balance = strategy.balanceOf(address(_controller));
                if (balance > maxBalance) {
                    maxBalance = balance;
                    maxIndex = i;
                }
            }
            
            if (maxBalance >= remaining) {
                uint256 balanceBefore = _asset.balanceOf(address(this));
                _controller.withdrawFromStrategy(names[maxIndex], remaining);
                uint256 balanceAfter = _asset.balanceOf(address(this));
                uint256 received = balanceAfter - balanceBefore;
                remaining = remaining > received ? remaining - received : 0;
            }
        }
        
        // Final check - allow for tiny rounding errors
        require(remaining <= 1, "Vault: withdrawal failed due to rounding");
    }
}
IVault.sol 23 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {IERC20} from "lib/forge-std/src/interfaces/IERC20.sol";
import {IERC4626} from "lib/forge-std/src/interfaces/IERC4626.sol";

interface IVault is IERC4626 {
    function pause() external;
    
    function unpause() external;
    
    function setController(address newController) external;
    
    function controller() external view returns (address);
    
    function performanceFee() external view returns (uint256);
    
    function setPerformanceFee(uint256 fee) external;
    
    function claimFees() external;
    
    function accruedFees() external view returns (uint256);
}
IController.sol 24 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

interface IController {
    function rebalance() external;
    
    function addStrategy(string memory name, address adapter) external;
    
    function removeStrategy(string memory name) external;
    
    function getStrategy(string memory name) external view returns (address);
    
    function getAllStrategies() external view returns (string[] memory names, address[] memory adapters);
    
    function pause() external;
    
    function unpause() external;
    
    function emergencyWithdraw(string memory strategyName) external;
    
    function withdrawFromStrategy(string memory strategyName, uint256 amount) external;
    
    function deployToStrategy(string memory strategyName, uint256 amount) external;
}
IStrategyAdapter.sol 19 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

interface IStrategyAdapter {
    function deposit(uint256 amount) external returns (uint256 shares);
    
    function withdraw(uint256 amount) external returns (uint256 withdrawn);
    
    function balanceOf(address account) external view returns (uint256);
    
    function getCurrentAPY() external view returns (uint256);
    
    // APY tracking functions
    function updateMetrics() external;
    
    function getLastUpdateTime() external view returns (uint256);
    
    function isAPYStale() external view returns (bool);
}
IERC20.sol 43 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2;

/// @dev Interface of the ERC20 standard as defined in the EIP.
/// @dev This includes the optional name, symbol, and decimals metadata.
interface IERC20 {
    /// @dev Emitted when `value` tokens are moved from one account (`from`) to another (`to`).
    event Transfer(address indexed from, address indexed to, uint256 value);

    /// @dev Emitted when the allowance of a `spender` for an `owner` is set, where `value`
    /// is the new allowance.
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /// @notice Returns the amount of tokens in existence.
    function totalSupply() external view returns (uint256);

    /// @notice Returns the amount of tokens owned by `account`.
    function balanceOf(address account) external view returns (uint256);

    /// @notice Moves `amount` tokens from the caller's account to `to`.
    function transfer(address to, uint256 amount) external returns (bool);

    /// @notice Returns the remaining number of tokens that `spender` is allowed
    /// to spend on behalf of `owner`
    function allowance(address owner, address spender) external view returns (uint256);

    /// @notice Sets `amount` as the allowance of `spender` over the caller's tokens.
    /// @dev Be aware of front-running risks: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
    function approve(address spender, uint256 amount) external returns (bool);

    /// @notice Moves `amount` tokens from `from` to `to` using the allowance mechanism.
    /// `amount` is then deducted from the caller's allowance.
    function transferFrom(address from, address to, uint256 amount) external returns (bool);

    /// @notice Returns the name of the token.
    function name() external view returns (string memory);

    /// @notice Returns the symbol of the token.
    function symbol() external view returns (string memory);

    /// @notice Returns the decimals places of the token.
    function decimals() external view returns (uint8);
}
ReentrancyGuard.sol 87 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
 * consider using {ReentrancyGuardTransient} instead.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}
IERC4626.sol 190 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2;

import {IERC20} from "./IERC20.sol";

/// @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
/// https://eips.ethereum.org/EIPS/eip-4626
interface IERC4626 is IERC20 {
    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares
    );

    /// @notice Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
    /// @dev
    /// - MUST be an ERC-20 token contract.
    /// - MUST NOT revert.
    function asset() external view returns (address assetTokenAddress);

    /// @notice Returns the total amount of the underlying asset that is “managed” by Vault.
    /// @dev
    /// - SHOULD include any compounding that occurs from yield.
    /// - MUST be inclusive of any fees that are charged against assets in the Vault.
    /// - MUST NOT revert.
    function totalAssets() external view returns (uint256 totalManagedAssets);

    /// @notice Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
    /// scenario where all the conditions are met.
    /// @dev
    /// - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
    /// - MUST NOT show any variations depending on the caller.
    /// - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
    /// - MUST NOT revert.
    ///
    /// NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
    /// “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
    /// from.
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    /// @notice Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
    /// scenario where all the conditions are met.
    /// @dev
    /// - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
    /// - MUST NOT show any variations depending on the caller.
    /// - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
    /// - MUST NOT revert.
    ///
    /// NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
    /// “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
    /// from.
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    /// @notice Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
    /// through a deposit call.
    /// @dev
    /// - MUST return a limited value if receiver is subject to some deposit limit.
    /// - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
    /// - MUST NOT revert.
    function maxDeposit(address receiver) external view returns (uint256 maxAssets);

    /// @notice Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
    /// current on-chain conditions.
    /// @dev
    /// - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
    ///   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
    ///   in the same transaction.
    /// - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
    ///   deposit would be accepted, regardless if the user has enough tokens approved, etc.
    /// - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
    /// - MUST NOT revert.
    ///
    /// NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
    /// share price or some other type of condition, meaning the depositor will lose assets by depositing.
    function previewDeposit(uint256 assets) external view returns (uint256 shares);

    /// @notice Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
    /// @dev
    /// - MUST emit the Deposit event.
    /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
    ///   deposit execution, and are accounted for during deposit.
    /// - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
    ///   approving enough underlying tokens to the Vault contract, etc).
    ///
    /// NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    /// @notice Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
    /// @dev
    /// - MUST return a limited value if receiver is subject to some mint limit.
    /// - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
    /// - MUST NOT revert.
    function maxMint(address receiver) external view returns (uint256 maxShares);

    /// @notice Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
    /// current on-chain conditions.
    /// @dev
    /// - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
    ///   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
    ///   same transaction.
    /// - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
    ///   would be accepted, regardless if the user has enough tokens approved, etc.
    /// - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
    /// - MUST NOT revert.
    ///
    /// NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
    /// share price or some other type of condition, meaning the depositor will lose assets by minting.
    function previewMint(uint256 shares) external view returns (uint256 assets);

    /// @notice Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
    /// @dev
    /// - MUST emit the Deposit event.
    /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
    ///   execution, and are accounted for during mint.
    /// - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
    ///   approving enough underlying tokens to the Vault contract, etc).
    ///
    /// NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    /// @notice Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
    /// Vault, through a withdrawal call.
    /// @dev
    /// - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
    /// - MUST NOT revert.
    function maxWithdraw(address owner) external view returns (uint256 maxAssets);

    /// @notice Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
    /// given current on-chain conditions.
    /// @dev
    /// - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
    ///   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
    ///   called
    ///   in the same transaction.
    /// - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
    ///   the withdrawal would be accepted, regardless if the user has enough shares, etc.
    /// - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
    /// - MUST NOT revert.
    ///
    /// NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
    /// share price or some other type of condition, meaning the depositor will lose assets by depositing.
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);

    /// @notice Burns shares from owner and sends exactly assets of underlying tokens to receiver.
    /// @dev
    /// - MUST emit the Withdraw event.
    /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
    ///   withdraw execution, and are accounted for during withdrawal.
    /// - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
    ///   not having enough shares, etc).
    ///
    /// Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
    /// Those methods should be performed separately.
    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);

    /// @notice Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
    /// through a redeem call.
    /// @dev
    /// - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
    /// - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
    /// - MUST NOT revert.
    function maxRedeem(address owner) external view returns (uint256 maxShares);

    /// @notice Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
    /// given current on-chain conditions.
    /// @dev
    /// - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
    ///   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
    ///   same transaction.
    /// - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
    ///   redemption would be accepted, regardless if the user has enough shares, etc.
    /// - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
    /// - MUST NOT revert.
    ///
    /// NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
    /// share price or some other type of condition, meaning the depositor will lose assets by redeeming.
    function previewRedeem(uint256 shares) external view returns (uint256 assets);

    /// @notice Burns exactly shares from owner and sends assets of underlying tokens to receiver.
    /// @dev
    /// - MUST emit the Withdraw event.
    /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
    ///   redeem execution, and are accounted for during redeem.
    /// - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
    ///   not having enough shares, etc).
    ///
    /// NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
    /// Those methods should be performed separately.
    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}

Read Contract

accruedFees 0x682c2058 → uint256
allowance 0xdd62ed3e → uint256
asset 0x38d52e0f → address
balanceOf 0x70a08231 → uint256
controller 0xf77c4791 → address
convertToAssets 0x07a2d13a → uint256
convertToShares 0xc6e6f592 → uint256
decimals 0x313ce567 → uint8
feesCollected24h 0x7e985951 → uint256
getFeeAnalytics 0xccc485b9 → uint256, uint256, uint256
lastFeeCollectionTime 0x38626450 → uint256
lastTotalAssets 0x568efc07 → uint256
maxDeposit 0x402d267d → uint256
maxMint 0xc63d75b6 → uint256
maxRedeem 0xd905777e → uint256
maxWithdraw 0xce96cb77 → uint256
name 0x06fdde03 → string
owner 0x8da5cb5b → address
paused 0x5c975abb → bool
pendingOwner 0xe30c3978 → address
performanceFee 0x87788782 → uint256
previewDeposit 0xef8b30f7 → uint256
previewMint 0xb3d7f6b9 → uint256
previewRedeem 0x4cdad506 → uint256
previewWithdraw 0x0a28a477 → uint256
symbol 0x95d89b41 → string
totalAssets 0x01e1d114 → uint256
totalFeesCollected 0x60c6d8ae → uint256
totalSupply 0x18160ddd → uint256

Write Contract 15 functions

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

acceptOwnership 0x79ba5097
No parameters
approve 0x095ea7b3
address spender
uint256 amount
returns: bool
cancelOwnershipTransfer 0x23452b9c
No parameters
claimFees 0xd294f093
No parameters
deposit 0x6e553f65
uint256 assets
address receiver
returns: uint256
mint 0x94bf804d
uint256 shares
address receiver
returns: uint256
pause 0x8456cb59
No parameters
redeem 0xba087652
uint256 shares
address receiver
address tokenOwner
returns: uint256
setController 0x92eefe9b
address newController
setPerformanceFee 0x70897b23
uint256 fee
transfer 0xa9059cbb
address to
uint256 amount
returns: bool
transferFrom 0x23b872dd
address from
address to
uint256 amount
returns: bool
transferOwnership 0xf2fde38b
address newOwner
unpause 0x3f4ba83a
No parameters
withdraw 0xb460af94
uint256 assets
address receiver
address tokenOwner
returns: uint256

Recent Transactions

No transactions found for this address