Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x8F7E1728d2e5Ac772faDd5e294A30FCC1AEFf7C2
Balance 0 ETH
Nonce 1
Code Size 17094 bytes
Last Active
Indexed Transactions 3 (24,322,66224,322,690)
Gas Used (indexed) 4,001,951
External Etherscan · Sourcify

Contract Bytecode

17094 bytes
0x608060405234801561000f575f5ffd5b5060043610610234575f3560e01c806382b22aa811610135578063aefb6e55116100b4578063f1bf253611610079578063f1bf2536146105dc578063f9f15f74146105ef578063fc0c546a14610669578063fd4c98d21461067c578063fd850fd91461068f575f5ffd5b8063aefb6e5514610534578063b71b70841461056e578063ba086518146105a1578063e00fe3fd146105b4578063f1850af8146105d4575f5ffd5b8063963671be116100fa578063963671be146104c057806399745105146104d357806399995ce7146104e65780639b19cd6a146104f9578063ad7a672f14610521575f5ffd5b806382b22aa81461046c57806383ce394f14610474578063882d17651461048757806389e4de1b1461049a5780638da5cb5b146104ad575f5ffd5b8063313ce567116101c1578063707c630e11610186578063707c630e146103d257806370a08231146103f35780637316d9091461043357806375c7b72f146104465780637d32d49e14610459575f5ffd5b8063313ce5671461034b57806342c5c299146103545780634b22465c146103675780634f142468146103925780636422d783146103b2575f5ffd5b806318f9b78f1161020757806318f9b78f146102b05780631bf87de0146102c357806323c9469c146103005780632bce9e7b146103225780632e0f262514610335575f5ffd5b80630739545b1461023857806314a8ec451461024d578063150b7a0214610260578063159faaae1461029d575b5f5ffd5b61024b610246366004613997565b6106a2565b005b61024b61025b3660046139cf565b61084a565b61027f61026e3660046139fc565b630a85bd0160e11b95945050505050565b6040516001600160e01b031990911681526020015b60405180910390f35b61024b6102ab366004613a93565b6108d2565b61024b6102be3660046139cf565b610d63565b6102f06102d1366004613a93565b600860209081525f928352604080842090915290825290205460ff1681565b6040519015158152602001610294565b6102f061030e366004613abd565b60036020525f908152604090205460ff1681565b61024b610330366004613ad8565b611269565b61033d61137f565b604051908152602001610294565b61033d600b5481565b61024b610362366004613abd565b611394565b61037a6103753660046139cf565b611423565b6040516001600160a01b039091168152602001610294565b6103a56103a03660046139cf565b6114af565b6040516102949190613b02565b6103c56103c0366004613abd565b611538565b6040516102949190613b6b565b6103e56103e0366004613abd565b611814565b604051610294929190613c0c565b61041b610401366004613abd565b60066020525f90815260409020546001600160401b031681565b6040516001600160401b039091168152602001610294565b61024b610441366004613c99565b6119f9565b6103a56104543660046139cf565b611c4f565b61024b610467366004613cb5565b611cb2565b6103c56122ad565b61024b610482366004613cf7565b612493565b61024b6104953660046139cf565b61254a565b61024b6104a8366004613a93565b612775565b60015461037a906001600160a01b031681565b60025461037a906001600160a01b031681565b61024b6104e1366004613cb5565b612962565b61024b6104f43660046139cf565b612ace565b60025461050e90600160a01b900461ffff1681565b60405161ffff9091168152602001610294565b60055461041b906001600160401b031681565b610547610542366004613d21565b612c70565b604080516001600160a01b0390931683526001600160401b03909116602083015201610294565b61041b61057c366004613a93565b600760209081525f92835260408084209091529082529020546001600160401b031681565b61024b6105af3660046139cf565b612cb5565b6105c76105c23660046139cf565b612d8b565b6040516102949190613d3b565b6103a5612e16565b61024b6105ea3660046139cf565b612e8f565b6106576105fd3660046139cf565b60046020525f90815260409020805460019091015461ffff8216916001600160a01b03620100009091048116919081169060ff600160a01b82048116916001600160401b03600160a81b82041691600160e81b9091041686565b60405161029496959493929190613d90565b600a5461037a906001600160a01b031681565b6103c561068a366004613abd565b612ff5565b61024b61069d366004613a93565b6131d7565b6001546001600160a01b031633146106d55760405162461bcd60e51b81526004016106cc90613de1565b60405180910390fd5b5f8261ffff1611806106f7575060025461ffff600160a01b9091048116908316105b6107325760405162461bcd60e51b815260206004820152600c60248201526b13dd5d081bd988189bdd5b9960a21b60448201526064016106cc565b600481600481111561074657610746613b37565b146107955761ffff82165f90815260046020526040902060010154600160a81b90046001600160401b03161561079557604051634e98f54b60e11b815261ffff831660048201526024016106cc565b61ffff82165f9081526004602081905260409091206001018054839260ff60a01b1990911690600160a01b9084908111156107d2576107d2613b37565b02179055505f8160048111156107ea576107ea613b37565b14806108075750600481600481111561080557610805613b37565b145b15610846577fec5ecef5d88d96b4d9e0d298d3b4fa0d6a8f942d5278d5f1a67cdb76cdd9c9cf828260405161083d929190613e04565b60405180910390a15b5050565b6108526132c7565b61ffff81165f908152600460205260409020600101546001600160a01b0316331461088f5760405162461bcd60e51b81526004016106cc90613e1c565b60405161ffff821681527f9cda168a48fe98cb81e5968811e5f48c365d681d21921064d3af879db41b46049060200160405180910390a16108cf60015f55565b50565b6108da6132c7565b5f6108e483611423565b90506001600160a01b038116331461090e5760405162461bcd60e51b81526004016106cc90613e1c565b61ffff83165f90815260046020526040902060010154600160e81b900460ff1661094a5760405162461bcd60e51b81526004016106cc90613e44565b600461ffff84165f90815260046020819052604090912060010154600160a01b900460ff169081111561097f5761097f613b37565b1461099c5760405162461bcd60e51b81526004016106cc90613e68565b61ffff83165f9081526007602090815260408083206001600160a01b03861684529091528120546001600160401b03169003610a095760405162461bcd60e51b815260206004820152600c60248201526b2737ba1030903134b23232b960a11b60448201526064016106cc565b610a1383836132ef565b61ffff83165f908152600460209081526040808320600101805467ffffffffffffffff60a81b19169055600782528083206001600160a01b03861684529091528120805467ffffffffffffffff198116909155600580546001600160401b039283169384939091610a8691859116613ea2565b82546101009290920a6001600160401b038181021990931691831602179091556001600160a01b0385165f90815260066020526040812080548594509092610ad091859116613ea2565b82546101009290920a6001600160401b038181021990931691909216919091021790555061ffff84165f9081526004602081905260408083206001810180546001600160a01b0319166001600160a01b038981169190911790915590549151632142170760e11b81526201000090920416926342842e0e92610b589233928992909101613ec7565b5f604051808303815f87803b158015610b6f575f5ffd5b505af1158015610b81573d5f5f3e3d5ffd5b505050505f610b8e61137f565b610ba1906001600160401b038416613eeb565b90505f6103e8610bb2836005613eeb565b610bbc9190613f02565b9050610bc88183613f21565b600a5460405163a9059cbb60e01b8152336004820152602481018390529193506001600160a01b03169063a9059cbb906044016020604051808303815f875af1158015610c17573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c3b9190613f34565b50600a5460015460405163a9059cbb60e01b81526001600160a01b0391821660048201526024810184905291169063a9059cbb906044016020604051808303815f875af1158015610c8e573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cb29190613f34565b506040805161ffff881681526001600160a01b03871660208201526001600160401b0385168183015290517fe46744cf290a6f70a97669e5b45fb220f1b221aab41933ad327ddee52257f96f9181900360600190a15f610d1061137f565b610d1a9083613f02565b90507f557809284da7314475b1582804ae28e5f1349efc1fe970ea25d50fce75eb4f4381604051610d4d91815260200190565b60405180910390a1505050505061084660015f55565b610d6b6132c7565b61ffff81165f90815260046020819052604090912060010154600160a01b900460ff1690816004811115610da157610da1613b37565b14610dbe5760405162461bcd60e51b81526004016106cc90613e68565b61ffff82165f90815260046020526040902060010154600160e81b900460ff16610dfa5760405162461bcd60e51b81526004016106cc90613e44565b61ffff82165f90815260046020526040812060010154600160a81b90046001600160401b03169003610e5c5760405162461bcd60e51b815260206004820152600b60248201526a4e6f74206c697374696e6760a81b60448201526064016106cc565b610e6461137f565b61ffff83165f90815260046020526040902060010154610e949190600160a81b90046001600160401b0316613eeb565b600a546040516370a0823160e01b81523360048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015610eda573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610efe9190613f4f565b1015610f435760405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e742062616c616e636560601b60448201526064016106cc565b61ffff82165f908152600460205260408120600101805467ffffffffffffffff60a81b198116909155600160a81b90046001600160401b031690610f8684611423565b61ffff85165f908152600460208190526040808320549051632142170760e11b81529394506201000090046001600160a01b0316926342842e0e92610fd092869233929101613ec7565b5f604051808303815f87803b158015610fe7575f5ffd5b505af1158015610ff9573d5f5f3e3d5ffd5b50505061ffff85165f90815260046020526040812060010180546001600160a01b03191633179055905061102b61137f565b61103e906001600160401b038516613eeb565b600a546040516323b872dd60e01b81529192506001600160a01b0316906323b872dd9061107390339030908690600401613ec7565b6020604051808303815f875af115801561108f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110b39190613f34565b505f6103e86110c3836005613eeb565b6110cd9190613f02565b90506110d98183613f21565b600a5460405163a9059cbb60e01b81526001600160a01b0386811660048301526024820184905292945091169063a9059cbb906044016020604051808303815f875af115801561112b573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061114f9190613f34565b50600a5460015460405163a9059cbb60e01b81526001600160a01b0391821660048201526024810184905291169063a9059cbb906044016020604051808303815f875af11580156111a2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111c69190613f34565b506040805161ffff881681526001600160401b03861660208201527f905902f846e88c3e866c8850513d275d18b96f447b136d91bb98324a4c9a1697910160405180910390a15f61121561137f565b61121f9083613f02565b90507f557809284da7314475b1582804ae28e5f1349efc1fe970ea25d50fce75eb4f438160405161125291815260200190565b60405180910390a15050505050506108cf60015f55565b6001546001600160a01b031633146112935760405162461bcd60e51b81526004016106cc90613de1565b6005546001600160401b0316156112e25760405162461bcd60e51b81526020600482015260136024820152724163636f756e74206861732062616c616e636560681b60448201526064016106cc565b60128111156113265760405162461bcd60e51b815260206004820152601060248201526f115e18d95959081b585e08189bdd5b9960821b60448201526064016106cc565b600a80546001600160a01b0319166001600160a01b038416908117909155600b82905560408051918252602082018390527fd431031d4b71a56115ead459a63b443f7d0ccd495e8e8c525ec48869d491763d910161083d565b5f600b54600a61138f9190614049565b905090565b6001546001600160a01b031633146113be5760405162461bcd60e51b81526004016106cc90613de1565b6002546001600160a01b0316156114015760405162461bcd60e51b8152602060048201526007602482015266155c19185d195960ca1b60448201526064016106cc565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b61ffff81165f9081526004602081905260408083205490516331a9108f60e11b81529182018390526201000090046001600160a01b03169081908190636352211e90602401602060405180830381865afa158015611483573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114a79190614054565b949350505050565b61ffff81165f9081526004602081905260408083205481516306fdde0360e01b81529151606094620100009092046001600160a01b031693849384936306fdde03938284019391929091908290030181865afa158015611511573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526114a791908101906140b3565b60605f60015b60025461ffff600160a01b909104811690821610156116115761ffff81165f908152600460205260409020600101546001600160a01b0385811691161480156115b7575061ffff81165f908152600460208190526040822060010154600160a01b900460ff16908111156115b4576115b4613b37565b14155b80156115f65750600461ffff82165f90815260046020819052604090912060010154600160a01b900460ff16908111156115f3576115f3613b37565b14155b15611609578161160581614145565b9250505b60010161153e565b50806001600160401b0381111561162a5761162a61406f565b60405190808252806020026020018201604052801561166357816020015b61165061394d565b8152602001906001900390816116485790505b5091505f60015b60025461ffff600160a01b9091048116908216101561180c5761ffff81165f908152600460205260409020600101546001600160a01b0386811691161480156116e3575061ffff81165f908152600460208190526040822060010154600160a01b900460ff16908111156116e0576116e0613b37565b14155b80156117225750600461ffff82165f90815260046020819052604090912060010154600160a01b900460ff169081111561171f5761171f613b37565b14155b156118045761ffff8181165f90815260046020818152604092839020835160c08101855281549586168152620100009095046001600160a01b03908116928601929092526001810154918216938501939093526060840191600160a01b90910460ff169081111561179557611795613b37565b60048111156117a6576117a6613b37565b815260019190910154600160a81b81046001600160401b03166020830152600160e81b900460ff16151560409091015284518590849081106117ea576117ea61415d565b6020026020010181905250818061180090614145565b9250505b60010161166a565b505050919050565b6060805f60015b60025461ffff600160a01b9091048116908216101561187f5761ffff81165f9081526007602090815260408083206001600160a01b03891684529091529020546001600160401b031615611877578161187381614145565b9250505b60010161181b565b50806001600160401b038111156118985761189861406f565b6040519080825280602002602001820160405280156118c1578160200160208202803683370190505b509250806001600160401b038111156118dc576118dc61406f565b604051908082528060200260200182016040528015611905578160200160208202803683370190505b5091505f60015b60025461ffff600160a01b909104811690821610156119f15761ffff81165f9081526007602090815260408083206001600160a01b038a1684529091529020546001600160401b0316156119e9578085838151811061196d5761196d61415d565b61ffff9283166020918202929092018101919091529082165f9081526007825260408082206001600160a01b038a168352909252205484516001600160401b03909116908590849081106119c3576119c361415d565b6001600160401b0390921660209283029190910190910152816119e581614145565b9250505b60010161190c565b505050915091565b6001546001600160a01b03163314611a235760405162461bcd60e51b81526004016106cc90613de1565b611a2c826134bc565b611a6a5760405162461bcd60e51b815260206004820152600f60248201526e4e6f742066726f6d2057656972756960881b60448201526064016106cc565b6001600160a01b0382165f9081526003602052604090205460ff1615611ac75760405162461bcd60e51b8152602060048201526012602482015271105b1c9958591e481c9959da5cdd195c995960721b60448201526064016106cc565b6001600160a01b038281165f8181526003602090815260408083208054600160ff19909116811790915560028054600160a01b9081900461ffff9081168088526004909652848720805461ffff1916909617909555815481900485168652838620805462010000600160b01b0319166201000090980297909717909655805486900484168552828520820180546001600160a01b031916978916979097179096558554859004831684528184208101805460ff60a01b191690558554859004831684528184208101805467ffffffffffffffff60a81b191690558554859004831684529220909101805460ff60e81b1916600160e81b17905582549190910416906014611bd383614171565b91906101000a81548161ffff021916908361ffff160217905550507f38df96e211a88dbfd43c7dceccea8bbe215e02bc16376aa583f11463bb7ae83b826001600260149054906101000a900461ffff16611c2d9190614191565b604080516001600160a01b03909316835261ffff90911660208301520161083d565b61ffff81165f90815260046020819052604080832054905163c87b56dd60e01b8152918201929092526060916201000090046001600160a01b0316908190819063c87b56dd906024015f60405180830381865afa158015611511573d5f5f3e3d5ffd5b611cba6132c7565b61ffff82165f90815260046020526040902060010154600160e81b900460ff16611cf65760405162461bcd60e51b81526004016106cc90613e44565b61ffff82165f9081526007602090815260408083203384529091528120546001600160401b03908116916001918416831015611d3d57611d368385613ea2565b9050611d4d565b611d478484613ea2565b90505f91505b8115611e0257611d5b61137f565b611d6e906001600160401b038316613eeb565b600a546040516370a0823160e01b81523360048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015611db4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611dd89190613f4f565b1015611e0257604051633037d54f60e21b81526001600160401b03821660048201526024016106cc565b61ffff85165f90815260096020526040902054600190600a03611e2d57611e2a863387613587565b90505b80611e705760405162461bcd60e51b81526020600482015260136024820152722732b2b21037baba3134b2103a37b8103a32b760691b60448201526064016106cc565b61ffff86165f90815260086020908152604080832033845290915281205460ff1615159003611f2f5761ffff86165f81815260086020908152604080832033808552908352818420805460ff191660019081179091559484526009835281842082518084019093529082526001600160401b03808b1683850190815282549687018355918552929093209051930180549251909116600160a01b026001600160e01b03199092166001600160a01b039390931692909217179055611fef565b5f5b61ffff87165f90815260096020526040902054811015611fed5761ffff87165f908152600960205260409020805433919083908110611f7257611f7261415d565b5f918252602090912001546001600160a01b031603611fe55761ffff87165f908152600960205260409020805487919083908110611fb257611fb261415d565b905f5260205f20015f0160146101000a8154816001600160401b0302191690836001600160401b03160217905550611fed565b600101611f31565b505b61ffff86165f9081526007602090815260408083203384529091529020805467ffffffffffffffff19166001600160401b038716179055821561213f57600580548391905f906120499084906001600160401b03166141ab565b82546101009290920a6001600160401b03818102199093169183160217909155335f9081526006602052604081208054869450909261208a918591166141ab565b82546001600160401b039182166101009390930a928302919092021990911617905550600a546001600160a01b03166323b872dd33306120c861137f565b6120db906001600160401b038816613eeb565b6040518463ffffffff1660e01b81526004016120f993929190613ec7565b6020604051808303815f875af1158015612115573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121399190613f34565b5061225b565b600580548391905f9061215c9084906001600160401b0316613ea2565b82546101009290920a6001600160401b03818102199093169183160217909155335f9081526006602052604081208054869450909261219d91859116613ea2565b82546001600160401b039182166101009390930a928302919092021990911617905550600a546001600160a01b031663a9059cbb336121da61137f565b6121ed906001600160401b038716613eeb565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044016020604051808303815f875af1158015612235573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122599190613f34565b505b6040805161ffff881681526001600160401b03871660208201527f3fc0f0e546887e68835347b811dab3ec815a6f940aa4952807bb6d8936d6d6bd910160405180910390a15050505061084660015f55565b60605f60015b60025461ffff600160a01b9091048116908216101561230c5761ffff81165f908152600460205260409020600190810154600160e81b900460ff1615159003612304578161230081614145565b9250505b6001016122b3565b50806001600160401b038111156123255761232561406f565b60405190808252806020026020018201604052801561235e57816020015b61234b61394d565b8152602001906001900390816123435790505b5091505f60015b60025461ffff600160a01b9091048116908216101561248d5761ffff81165f908152600460205260409020600190810154600160e81b900460ff16151590036124855761ffff8181165f90815260046020818152604092839020835160c08101855281549586168152620100009095046001600160a01b03908116928601929092526001810154918216938501939093526060840191600160a01b90910460ff169081111561241657612416613b37565b600481111561242757612427613b37565b815260019190910154600160a81b81046001600160401b03166020830152600160e81b900460ff161515604090910152845185908490811061246b5761246b61415d565b6020026020010181905250818061248190614145565b9250505b600101612365565b50505090565b6001546001600160a01b031633146124bd5760405162461bcd60e51b81526004016106cc90613de1565b80156124ee5761ffff82165f908152600460205260409020600101805460ff60e81b1916600160e81b17905561250f565b61ffff82165f908152600460205260409020600101805460ff60e81b191690555b6040805161ffff8416815282151560208201527fb7bd5cc1fda624d9f1c11565058aea294e6bf49b6a39390e90cf4cdbaa306f36910161083d565b6125526132c7565b61ffff81165f9081526007602090815260408083203384529091528120546001600160401b0316908190036125b75760405162461bcd60e51b815260206004820152600b60248201526a111a59081b9bdd08189a5960aa1b60448201526064016106cc565b335f908152600660205260409020546001600160401b03808316911610156126185760405162461bcd60e51b8152602060048201526014602482015273696e73756666696369656e742062616c616e636560601b60448201526064016106cc565b61262282336132ef565b600580548291905f9061263f9084906001600160401b0316613ea2565b82546101009290920a6001600160401b03818102199093169183160217909155335f9081526006602052604081208054859450909261268091859116613ea2565b82546001600160401b039182166101009390930a92830291909202199091161790555061ffff82165f908152600760209081526040808320338085529252909120805467ffffffffffffffff19169055600a546001600160a01b03169063a9059cbb906126eb61137f565b6126fe906001600160401b038616613eeb565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044016020604051808303815f875af1158015612746573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061276a9190613f34565b50506108cf60015f55565b6001546001600160a01b0316331461279f5760405162461bcd60e51b81526004016106cc90613de1565b6127a8816134bc565b6127e65760405162461bcd60e51b815260206004820152600f60248201526e4e6f742066726f6d2057656972756960881b60448201526064016106cc565b6001600160a01b0381165f9081526003602052604090205460ff16156128435760405162461bcd60e51b8152602060048201526012602482015271105b1c9958591e481c9959da5cdd195c995960721b60448201526064016106cc565b61ffff82165f90815260046020526040902060010154600160e81b900460ff16156128a15760405162461bcd60e51b815260206004820152600e60248201526d417274776f726b2061637469766560901b60448201526064016106cc565b61ffff82165f81815260046020818152604080842080546001600160a01b036201000091829004811680885260038652848820805460ff19908116909155918a1680895285892080549093166001908117909355978990529585528201805460ff60e81b1916600160e81b179055815490860262010000600160b01b0319909116179055805194855290840192909252917fd79a24629cc0fc54678e803fc5060511ebd13417c26b03dd54aba40853767bda910160405180910390a1505050565b61296a6132c7565b5f61297483611423565b90506001600160a01b038116331461299e5760405162461bcd60e51b81526004016106cc90613e1c565b600461ffff84165f90815260046020819052604090912060010154600160a01b900460ff16908111156129d3576129d3613b37565b146129f05760405162461bcd60e51b81526004016106cc90613e68565b61ffff83165f908152600460205260409020600190810154600160e81b900460ff16151514612a4e5760405162461bcd60e51b815260206004820152600a602482015269125b9d985b1a59081a5960b21b60448201526064016106cc565b61ffff83165f81815260046020908152604091829020600101805467ffffffffffffffff60a81b1916600160a81b6001600160401b038816908102919091179091558251938452908301527f7d64f09a8ca7e540742aac9a771b78b22b1795a467cb1767e2d70a968a90b39c910160405180910390a15061084660015f55565b6001546001600160a01b03163314612af85760405162461bcd60e51b81526004016106cc90613de1565b612b006132c7565b61ffff81165f90815260046020526040902060010154600160e81b900460ff1680612b3d5760405162461bcd60e51b81526004016106cc90613e44565b61ffff82165f90815260046020526040812060010154600160a01b900460ff1690816004811115612b7057612b70613b37565b03612ba75760405162461bcd60e51b8152602060048201526007602482015266139bdd081e595d60ca1b60448201526064016106cc565b61ffff83165f90815260046020819052604080832080546001909101549151632142170760e11b8152620100009091046001600160a01b039081169492169284926342842e0e92612bfe9230928792909101613ec7565b5f604051808303815f87803b158015612c15575f5ffd5b505af1158015612c27573d5f5f3e3d5ffd5b505060405161ffff881681527f19f93fdff124050053556aae37eca2c1e36fd42a5c1e34cbaee711b567b5c93a9250602001905060405180910390a1505050506108cf60015f55565b6009602052815f5260405f208181548110612c89575f80fd5b5f918252602090912001546001600160a01b0381169250600160a01b90046001600160401b0316905082565b612cbd6132c7565b5f612cc782611423565b90506001600160a01b0381163314612cf15760405162461bcd60e51b81526004016106cc90613e1c565b61ffff82165f90815260046020526040812060010154600160a81b90046001600160401b03169003612d5a5760405162461bcd60e51b8152602060048201526012602482015271105c9d1ddbdc9ac81b9bdd081b1a5cdd195960721b60448201526064016106cc565b5061ffff81165f908152600460205260409020600101805467ffffffffffffffff60a81b191690556108cf60015f55565b61ffff81165f908152600960209081526040808320805482518185028101850190935280835260609492939192909184015b82821015612e0b575f84815260209081902060408051808201909152908401546001600160a01b0381168252600160a01b90046001600160401b031681830152825260019092019101612dbd565b505050509050919050565b600a54604080516395d89b4160e01b815290516060926001600160a01b031691829182916395d89b41916004808301925f9291908290030181865afa158015612e61573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052612e8891908101906140b3565b9250505090565b612e976132c7565b5f612ea182611423565b9050336001600160a01b03821614612ecb5760405162461bcd60e51b81526004016106cc90613e1c565b61ffff82165f908152600460208190526040822060010154600160a01b900460ff1690811115612efd57612efd613b37565b14612f3c5760405162461bcd60e51b815260206004820152600f60248201526e139bdd08185c9c9a5d9959081e595d608a1b60448201526064016106cc565b61ffff82165f908152600460208190526040808320549051632142170760e11b8152620100009091046001600160a01b03169283926342842e0e92612f85928792309201613ec7565b5f604051808303815f87803b158015612f9c575f5ffd5b505af1158015612fae573d5f5f3e3d5ffd5b505060405161ffff861681527ff283610df40ddbeeb2ab7a0e237b45e84e8337d735198862e323df454b4c35d89250602001905060405180910390a150506108cf60015f55565b60605f60015b60025461ffff600160a01b909104811690821610156130555761ffff81165f908152600460205260409020600101546001600160a01b03908116908516810361304c578261304881614145565b9350505b50600101612ffb565b50806001600160401b0381111561306e5761306e61406f565b6040519080825280602002602001820160405280156130a757816020015b61309461394d565b81526020019060019003908161308c5790505b5091505f60015b60025461ffff600160a01b9091048116908216101561180c5761ffff81165f908152600460205260409020600101546001600160a01b0390811690861681036131ce5761ffff8281165f90815260046020818152604092839020835160c08101855281549586168152620100009095046001600160a01b03908116928601929092526001810154918216938501939093526060840191600160a01b90910460ff169081111561315f5761315f613b37565b600481111561317057613170613b37565b815260019190910154600160a81b81046001600160401b03166020830152600160e81b900460ff16151560409091015285518690859081106131b4576131b461415d565b602002602001018190525082806131ca90614145565b9350505b506001016130ae565b6001546001600160a01b031633146132015760405162461bcd60e51b81526004016106cc90613de1565b61ffff82165f90815260046020526040902060010154600160a81b90046001600160401b0316156132615760405162461bcd60e51b815260206004820152600a6024820152694973206c697374696e6760b01b60448201526064016106cc565b61ffff82165f8181526004602090815260409182902060010180546001600160a01b0319166001600160a01b0386169081179091558251938452908301527fdda9aad2be8bd036fd0ca82bdae28e925e476759f4d5057d16804503fc281f53910161083d565b60025f54036132e957604051633ee5aeb560e01b815260040160405180910390fd5b60025f55565b61ffff82165f9081526008602090815260408083206001600160a01b038516845290915290205460ff16156108465761ffff82165f9081526008602090815260408083206001600160a01b03851684529091528120805460ff19169055805b61ffff84165f908152600960205260409020548110156133c15761ffff84165f90815260096020526040902080546001600160a01b0385169190839081106133985761339861415d565b5f918252602090912001546001600160a01b0316036133b9578091506133c1565b60010161334e565b5061ffff83165f90815260096020526040902080546133e290600190613f21565b815481106133f2576133f261415d565b905f5260205f200160095f8561ffff1661ffff1681526020019081526020015f2082815481106134245761342461415d565b5f91825260208083208454920180546001600160a01b031981166001600160a01b03909416938417825594546001600160401b03600160a01b9182900416026001600160e01b031990951690921793909317905561ffff85168152600990915260409020805480613497576134976141ca565b5f8281526020902081015f1990810180546001600160e01b0319169055019055505050565b6002546040805162a7152760e01b815290515f926001600160a01b0316918391839162a7152791600480830192869291908290030181865afa158015613504573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261352b91908101906141de565b90505f5b815181101561357d57846001600160a01b03168282815181106135545761355461415d565b60200260200101516001600160a01b03160361357557506001949350505050565b60010161352f565b505f949350505050565b61ffff83165f90815260096020526040812080548291829182906135ad576135ad61415d565b5f918252602082200154600160a01b90046001600160401b03169150805b61ffff88165f9081526009602052604090205481101561393e5761ffff88165f90815260096020526040902080546001600160a01b0389169190839081106136155761361561415d565b5f918252602090912001546001600160a01b0316036136905761ffff88165f9081526009602052604090208054879190839081106136555761365561415d565b905f5260205f20015f0160146101000a8154816001600160401b0302191690836001600160401b031602179055506001945050505050613946565b61ffff88165f90815260096020526040902080546001600160401b0388169190839081106136c0576136c061415d565b5f91825260209091200154600160a01b90046001600160401b031610156137b55761ffff88165f90815260096020526040902080546001600160401b0385169190839081106137115761371161415d565b5f91825260209091200154600160a01b90046001600160401b031610156137b55761ffff88165f9081526009602052604090208054829081106137565761375661415d565b5f91825260208083209091015461ffff8b1683526009909152604090912080546001600160a01b03909216955090829081106137945761379461415d565b5f91825260209091200154600160a01b90046001600160401b031692509050805b61ffff88165f908152600960205260409020546137d490600190613f21565b8103613936576001600160a01b0384166137f4575f945050505050613946565b61ffff88165f9081526009602052604081208054849081106138185761381861415d565b5f91825260208083209091015461ffff8c168084526008835260408085206001600160a01b03909316808652928452808520805460ff191690559084526009909252912080549192508991859081106138735761387361415d565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03949094169390931790925561ffff8b1681526009909152604090208054889190859081106138c4576138c461415d565b5f918252602080832090910180546001600160401b0394909416600160a01b0267ffffffffffffffff60a01b199094169390931790925561ffff8b1681526008825260408082206001600160a01b038c16835290925220805460ff191660019081179091559550613946945050505050565b6001016135cb565b505f93505050505b9392505050565b6040805160c0810182525f8082526020820181905291810182905290606082019081525f6020820181905260409091015290565b803561ffff81168114613992575f5ffd5b919050565b5f5f604083850312156139a8575f5ffd5b6139b183613981565b91506020830135600581106139c4575f5ffd5b809150509250929050565b5f602082840312156139df575f5ffd5b61394682613981565b6001600160a01b03811681146108cf575f5ffd5b5f5f5f5f5f60808688031215613a10575f5ffd5b8535613a1b816139e8565b94506020860135613a2b816139e8565b93506040860135925060608601356001600160401b03811115613a4c575f5ffd5b8601601f81018813613a5c575f5ffd5b80356001600160401b03811115613a71575f5ffd5b886020828401011115613a82575f5ffd5b959894975092955050506020019190565b5f5f60408385031215613aa4575f5ffd5b613aad83613981565b915060208301356139c4816139e8565b5f60208284031215613acd575f5ffd5b8135613946816139e8565b5f5f60408385031215613ae9575f5ffd5b8235613af4816139e8565b946020939093013593505050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b634e487b7160e01b5f52602160045260245ffd5b60058110613b6757634e487b7160e01b5f52602160045260245ffd5b9052565b602080825282518282018190525f918401906040840190835b81811015613c0157835161ffff815116845260018060a01b03602082015116602085015260018060a01b0360408201511660408501526060810151613bcc6060860182613b4b565b506080818101516001600160401b03169085015260a0908101511515908401526020939093019260c090920191600101613b84565b509095945050505050565b604080825283519082018190525f9060208501906060840190835b81811015613c4957835161ffff16835260209384019390920191600101613c27565b5050838103602080860191909152855180835291810192508501905f5b81811015613c8d5782516001600160401b0316845260209384019390920191600101613c66565b50919695505050505050565b5f5f60408385031215613caa575f5ffd5b8235613aad816139e8565b5f5f60408385031215613cc6575f5ffd5b613ccf83613981565b915060208301356001600160401b03811681146139c4575f5ffd5b80151581146108cf575f5ffd5b5f5f60408385031215613d08575f5ffd5b613d1183613981565b915060208301356139c481613cea565b5f5f60408385031215613d32575f5ffd5b613af483613981565b602080825282518282018190525f918401906040840190835b81811015613c0157835180516001600160a01b031684526020908101516001600160401b03168185015290930192604090920191600101613d54565b61ffff871681526001600160a01b0386811660208301528516604082015260c08101613dbf6060830186613b4b565b6001600160401b038416608083015282151560a0830152979650505050505050565b6020808252600990820152682737ba1037bbb732b960b91b604082015260600190565b61ffff83168152604081016139466020830184613b4b565b6020808252600e908201526d139bdd08185d5d1a1bdc9a5e995960921b604082015260600190565b6020808252600a90820152694e6f742061637469766560b01b604082015260600190565b6020808252600c908201526b139bdd081a5b881d985d5b1d60a21b604082015260600190565b634e487b7160e01b5f52601160045260245ffd5b6001600160401b038281168282160390811115613ec157613ec1613e8e565b92915050565b6001600160a01b039384168152919092166020820152604081019190915260600190565b8082028115828204841417613ec157613ec1613e8e565b5f82613f1c57634e487b7160e01b5f52601260045260245ffd5b500490565b81810381811115613ec157613ec1613e8e565b5f60208284031215613f44575f5ffd5b815161394681613cea565b5f60208284031215613f5f575f5ffd5b5051919050565b6001815b6001841115613fa157808504811115613f8557613f85613e8e565b6001841615613f9357908102905b60019390931c928002613f6a565b935093915050565b5f82613fb757506001613ec1565b81613fc357505f613ec1565b8160018114613fd95760028114613fe357613fff565b6001915050613ec1565b60ff841115613ff457613ff4613e8e565b50506001821b613ec1565b5060208310610133831016604e8410600b8410161715614022575081810a613ec1565b61402e5f198484613f66565b805f190482111561404157614041613e8e565b029392505050565b5f6139468383613fa9565b5f60208284031215614064575f5ffd5b8151613946816139e8565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b03811182821017156140ab576140ab61406f565b604052919050565b5f602082840312156140c3575f5ffd5b81516001600160401b038111156140d8575f5ffd5b8201601f810184136140e8575f5ffd5b80516001600160401b038111156141015761410161406f565b614114601f8201601f1916602001614083565b818152856020838501011115614128575f5ffd5b8160208401602083015e5f91810160200191909152949350505050565b5f6001820161415657614156613e8e565b5060010190565b634e487b7160e01b5f52603260045260245ffd5b5f61ffff821661ffff810361418857614188613e8e565b60010192915050565b61ffff8281168282160390811115613ec157613ec1613e8e565b6001600160401b038181168382160190811115613ec157613ec1613e8e565b634e487b7160e01b5f52603160045260245ffd5b5f602082840312156141ee575f5ffd5b81516001600160401b03811115614203575f5ffd5b8201601f81018413614213575f5ffd5b80516001600160401b0381111561422c5761422c61406f565b8060051b61423c60208201614083565b91825260208184018101929081019087841115614257575f5ffd5b6020850194505b838510156142855784519250614273836139e8565b8282526020948501949091019061425e565b97965050505050505056fea2646970667358221220a8a2dbb2e165f739616fe52e712b2d747abf6074131ada80ee6c4a7f254dfba364736f6c634300081f0033

Verified Source Code Full Match

Compiler: v0.8.31+commit.fd3a2265 EVM: osaka Optimization: Yes (200 runs)
WeiRui.sol 487 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";

interface IFactory {
    function getArtworks() external view returns (address[] memory);
}

contract WeiRui is ReentrancyGuard, IERC721Receiver {
    event UpdateToken(address tokenAddr, uint256 decimal);
    event UpdateCollector(uint16 artworkId, address collector);
    event AddArtwork(address artworkAddr, uint16 artworkId);
    event UpdateArtworkAddress(uint16 artworkId, address newAddr);
    event UpdateStatus(uint16 artworkId, Status status);
    event ActivateArtwork(uint16 artworkId, bool isActive);
    event Request(uint16 artworkId);
    event Confirm(uint16 artworkId);
    event Redeem(uint16 artworkId);
    event List(uint16 artworkId, uint64 amount);
    event PlaceBid(uint16 artworkId, uint64 amount);
    event AcceptBid(uint16 artworkId, address bidder, uint64 amount);
    event Buy(uint16 artworkId, uint64 amount);
    event Fee(uint256 fee);

    address public owner;
    address public factoryAddr;
    uint16 public nextArtworkId;
    mapping(address => bool) public isArtwork;
    mapping(uint16 => Artwork) public artworks;

    uint64 public totalBalance;
    mapping(address => uint64) public balanceOf;
    mapping(uint16 => mapping(address => uint64)) public bids; // Keep track of collector deposit for each bid
    mapping(uint16 => mapping(address => bool)) public isBid; // This will return true if the bidder bid is in the topBids
    mapping(uint16 => Bid[]) public topBids;

    IERC20 public token;
    uint256 public decimals;

    enum Status {
        COLLECTORHOLDING, 
        TRANSPORTINGFROM, 
        ARTISTREVIEWING, 
        TRANSPORTINGTO, 
        INVAULT
    }

    struct Artwork {
        uint16 id;
        address artwork;
        address collector;
        Status status;
        uint64 listingPriceInEth;
        bool isActive; // In case someone lost his private key
    }

    struct Bid {
        address from;
        uint64 price;
    }

    modifier isOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }

    constructor(
        address _tokenAddr, 
        uint256 _decimals
    ) {
        owner = msg.sender;
        nextArtworkId = 1;
        token = IERC20(_tokenAddr);
        decimals = _decimals;
    }

    function initiateFactory(address _factoryAddr) public isOwner {
        require(factoryAddr == address(0), "Updated");
        factoryAddr = _factoryAddr;
    }

    function updateToken(address _tokenAddr, uint256 _decimals) public isOwner {
        require(totalBalance == 0, "Account has balance");
        require(_decimals <= 18, "Exceed max bound");
        token = IERC20(_tokenAddr);
        decimals = _decimals;
        emit UpdateToken(_tokenAddr, _decimals);
    }

    function getTokenSymbol() public view returns (string memory) {
        address _tokenAddr = address(token);
        IERC20Metadata tokenMeta = IERC20Metadata(_tokenAddr);
        return tokenMeta.symbol();
    }

    function DECIMALS() public view returns (uint256) {
        return 10 ** decimals;
    }

    /// Update the current real holder of the artwork
    function updateCollector(uint16 _artworkId, address _collector) public isOwner {
        require(artworks[_artworkId].listingPriceInEth == 0, "Is listing");
        artworks[_artworkId].collector = _collector;
        emit UpdateCollector(_artworkId, _collector);
    }

    function getAllActiveArtworks() public view returns (Artwork[] memory artworksInfo) {
        uint256 _count;
        for (uint16 i = 1; i < nextArtworkId; i++) {
            if (artworks[i].isActive == true) {
                _count++;
            }
        }
        artworksInfo = new Artwork[](_count);
        uint256 _index;
        for (uint16 i = 1; i < nextArtworkId; i++) {
            if (artworks[i].isActive == true) {
                artworksInfo[_index] = artworks[i];
                _index++;
            }
        }
    }

    function addArtwork(address _artworkAddr, address _collector) public isOwner {
        require(_isArtwork(_artworkAddr), "Not from Weirui");
        require(!isArtwork[_artworkAddr], "Already registered");
        isArtwork[_artworkAddr] = true;
        artworks[nextArtworkId].id = nextArtworkId;
        artworks[nextArtworkId].artwork = _artworkAddr;
        artworks[nextArtworkId].collector = _collector;
        artworks[nextArtworkId].status = Status.COLLECTORHOLDING;
        artworks[nextArtworkId].listingPriceInEth = 0;
        artworks[nextArtworkId].isActive = true;
        nextArtworkId++;
        emit AddArtwork(_artworkAddr, nextArtworkId - 1);
    }

    function updateArtworkAddress(uint16 _artworkId, address _newArtworkAddr) public isOwner {
        require(_isArtwork(_newArtworkAddr), "Not from Weirui");
        require(!isArtwork[_newArtworkAddr], "Already registered");
        require(artworks[_artworkId].isActive == false, "Artwork active");
        address _oldArtworkAddr = artworks[_artworkId].artwork;
        isArtwork[_oldArtworkAddr] = false;
        isArtwork[_newArtworkAddr] = true;
        artworks[_artworkId].isActive = true;
        artworks[_artworkId].artwork = _newArtworkAddr;
        emit UpdateArtworkAddress(_artworkId, _newArtworkAddr);
    }

    /// Check the artwork address is created from the factory contract
    function _isArtwork(address _artwork) internal view returns (bool) {
        IFactory factory = IFactory(factoryAddr);
        address[] memory _artworks = factory.getArtworks();
        for (uint256 i = 0; i < _artworks.length; i++) {
            if (_artworks[i] == _artwork) {
                return true;
            }
        }
        return false;
    }

    function getArtworkName(uint16 _artworkId) public view returns (string memory name) {
        address _artworkAddr = artworks[_artworkId].artwork;
        IERC721Metadata _artwork = IERC721Metadata(_artworkAddr);
        return _artwork.name();
    }

    function getArtworkOwner(uint16 _artworkId) public view returns (address collector) {
        address _artworkAddr = artworks[_artworkId].artwork;
        IERC721 _artwork = IERC721(_artworkAddr);
        return _artwork.ownerOf(0);
    }

    function getArtworkTokenURI(uint16 _artworkId) public view returns (string memory) {
        address _artworkAddr = artworks[_artworkId].artwork;
        IERC721Metadata _artwork = IERC721Metadata(_artworkAddr);
        return _artwork.tokenURI(0);
    }

    function updateStatus(uint16 _artworkId, Status _status) public isOwner {
        require(_artworkId > 0 || _artworkId < nextArtworkId, "Out of bound");
        if (Status(_status) != Status.INVAULT) {
            if (artworks[_artworkId].listingPriceInEth != 0) {
                revert artworkIsListing(_artworkId); // If change to status other than INVAULT, the listing price must be 0;
            }
        }
        artworks[_artworkId].status = _status;
        if (_status == Status.COLLECTORHOLDING || _status == Status.INVAULT) {
            emit UpdateStatus(_artworkId, _status);
        }
    }

    /// The artwork is active or not, only affects if it can list and show on the frontend
    function activateArtwork(uint16 _artworkId, bool _toActive) public isOwner {
        if (_toActive) {
            artworks[_artworkId].isActive = true;
        } else {
            artworks[_artworkId].isActive = false;
        }
        emit ActivateArtwork(_artworkId, _toActive);
    }

    /// Collectors can still check all their collections including those got deactivated
    function getCollectorCollections(address _collector) public view returns (Artwork[] memory artworksInfo) {
        uint256 _count;
        for (uint16 i = 1; i < nextArtworkId; i++) {
            address _artworkCollector = artworks[i].collector;
            if (_artworkCollector == _collector) {
                _count++;
            }
        }
        artworksInfo = new Artwork[](_count);
        uint256 _index;
        for (uint16 i = 1; i < nextArtworkId; i++) {
            address _artworkCollector = artworks[i].collector;
            if (_artworkCollector == _collector) {
                artworksInfo[_index] = artworks[i];
                _index++;
            }
        }
    }

    function getCollectorOrders(address _collector) public view returns (Artwork[] memory collectorArtworks) {
        uint256 _count;
        for (uint16 i = 1; i < nextArtworkId; i++) {
            if (
                artworks[i].collector == _collector && 
                artworks[i].status != Status.COLLECTORHOLDING && 
                artworks[i].status != Status.INVAULT
            ) {
                _count++;
            }
        }
        collectorArtworks = new Artwork[](_count);
        uint256 _index;
        for (uint16 i = 1; i < nextArtworkId; i++) {
            if (
                artworks[i].collector == _collector && 
                artworks[i].status != Status.COLLECTORHOLDING && 
                artworks[i].status != Status.INVAULT
            ) {
                collectorArtworks[_index] = artworks[i];
                _index++;
            }
        }
    }

    function requestVault(uint16 _artworkId) public nonReentrant {
        require(artworks[_artworkId].collector == msg.sender, "Not authorized");
        emit Request(_artworkId);
    }

    /// Transfer artwork to vault
    function sendVault(uint16 _artworkId) public isOwner nonReentrant {
        bool _isActive = artworks[_artworkId].isActive;
        require(_isActive, "Not active");
        Status _status = artworks[_artworkId].status;
        require(_status != Status.COLLECTORHOLDING, "Not yet");
        address _artworkAddr = artworks[_artworkId].artwork;
        address _collector = artworks[_artworkId].collector;
        IERC721(_artworkAddr).safeTransferFrom(address(this), _collector, 0);
        emit Confirm(_artworkId);
    }

    /// Transfer artwork from vault
    function redeemArtwork(uint16 _artworkId) public nonReentrant {
        address _owner = getArtworkOwner(_artworkId);
        require(msg.sender == _owner, "Not authorized");
        require(artworks[_artworkId].status == Status.COLLECTORHOLDING, "Not arrived yet");
        address _artworkAddr = artworks[_artworkId].artwork;
        IERC721(_artworkAddr).safeTransferFrom(_owner, address(this), 0);
        emit Redeem(_artworkId);
    }

    function listArtwork(uint16 _artworkId, uint64 _amount) public nonReentrant {
        address _owner = getArtworkOwner(_artworkId);
        require(_owner == msg.sender, "Not authorized");
        require(artworks[_artworkId].status == Status.INVAULT, "Not in vault");
        require(artworks[_artworkId].isActive == true, "Invalid id");
        artworks[_artworkId].listingPriceInEth = _amount;
        emit List(_artworkId, _amount);
    }

    function delist(uint16 _artworkId) public nonReentrant {
        address _owner = getArtworkOwner(_artworkId);
        require(_owner == msg.sender, "Not authorized");
        require(artworks[_artworkId].listingPriceInEth != 0, "Artwork not listed");
        artworks[_artworkId].listingPriceInEth = 0;
    }

    /// Only the top 10 bidding price will be kept
    /// Bidders whose bids have been forced out, need to either bid with higher price or withdraw bid manually
    /// Bidders who wish to bid higher, can input the desired amount and only the extra part will be charged
    function bid(uint16 _artworkId, uint64 _amount) public nonReentrant {
        require(artworks[_artworkId].isActive, "Not active");
        uint64 _pastBid = bids[_artworkId][msg.sender];
        bool higherBid = true;
        uint64 _bidGap;
        if (_amount > _pastBid) {
            _bidGap = _amount - _pastBid;
        } else {
            _bidGap = _pastBid - _amount;
            higherBid = false;
        }
        if (higherBid) {
            if (token.balanceOf(msg.sender) < uint256(_bidGap) * DECIMALS()) {
                revert insufficientBalance(_bidGap);
            }
        }
        bool outbid = true;
        if (topBids[_artworkId].length == 10) {
            outbid = _removeLowestBid(_artworkId, msg.sender, _amount);
        }
        require(outbid, "Need outbid top ten");
        if (isBid[_artworkId][msg.sender] == false) { // when topBids length less than 10
            isBid[_artworkId][msg.sender] = true;
            topBids[_artworkId].push(Bid({
                from: msg.sender,
                price: _amount
            }));
        } else { // when topBids length less than 10 but bid price changed
            for (uint256 i = 0; i < topBids[_artworkId].length; i++) {
                if (topBids[_artworkId][i].from == msg.sender) {
                    topBids[_artworkId][i].price = _amount;
                    break;
                }
            }
        }
        bids[_artworkId][msg.sender] = _amount;
        if (higherBid) {
            totalBalance += _bidGap;
            balanceOf[msg.sender] += _bidGap;
            token.transferFrom(msg.sender, address(this), uint256(_bidGap) * DECIMALS());
        } else {
            totalBalance -= _bidGap;
            balanceOf[msg.sender] -= _bidGap;
            token.transfer(msg.sender, uint256(_bidGap) * DECIMALS());
        }
        emit PlaceBid(_artworkId, _amount);
    }

    function _removeLowestBid(uint16 _artworkId, address _bidder, uint64 _bidderPrice) internal returns (bool) {
        address _lowestBidder;
        uint64 _lowestBid = topBids[_artworkId][0].price;
        uint256 _index;
        for (uint256 i = 0; i < topBids[_artworkId].length; i++) {
            if (topBids[_artworkId][i].from == _bidder) {
                topBids[_artworkId][i].price = _bidderPrice;
                return true; // bidder outbid himself
            }
            if (topBids[_artworkId][i].price < _bidderPrice) {
                if (topBids[_artworkId][i].price < _lowestBid) {
                    _lowestBidder = topBids[_artworkId][i].from;
                    _lowestBid = topBids[_artworkId][i].price;
                    _index = i;
                }
            }
            if (i == topBids[_artworkId].length -1) {
                if (_lowestBidder == address(0)) {
                    return false; // bidder outbid no one
                } else {
                    address _removedAddr = topBids[_artworkId][_index].from;
                    isBid[_artworkId][_removedAddr] = false;
                    topBids[_artworkId][_index].from = _bidder;
                    topBids[_artworkId][_index].price = _bidderPrice;
                    isBid[_artworkId][_bidder] = true;
                    return true;
                }
            }
        }
        return false;
    }

    function withdrawBid(uint16 _artworkId) public nonReentrant {
        uint64 _bid = bids[_artworkId][msg.sender];
        require(_bid != 0, "Did not bid");
        require(balanceOf[msg.sender] >= _bid, "insufficient balance");
        _removeFromTopBids(_artworkId, msg.sender);
        totalBalance -= _bid;
        balanceOf[msg.sender] -= _bid;
        bids[_artworkId][msg.sender] = 0;
        token.transfer(msg.sender, uint256(_bid) * DECIMALS());
    }

    function acceptBid(uint16 _artworkId, address _bidder) public nonReentrant {
        address _owner = getArtworkOwner(_artworkId);
        require(_owner == msg.sender, "Not authorized");
        require(artworks[_artworkId].isActive, "Not active");
        require(Status(artworks[_artworkId].status) == Status.INVAULT, "Not in vault");
        require(bids[_artworkId][_bidder] != 0, "Not a bidder");
        _removeFromTopBids(_artworkId, _bidder);
        artworks[_artworkId].listingPriceInEth = 0;
        uint64 _acceptedBid = bids[_artworkId][_bidder];
        bids[_artworkId][_bidder] = 0;
        totalBalance -= _acceptedBid;
        balanceOf[_bidder] -= _acceptedBid;
        artworks[_artworkId].collector = _bidder;
        IERC721(artworks[_artworkId].artwork).safeTransferFrom(msg.sender, _bidder, 0);
        uint256 _acceptedBidInWei = uint256(_acceptedBid) * DECIMALS();
        uint256 _fee = _acceptedBidInWei * 5 / 1000;
        _acceptedBidInWei -= _fee;
        token.transfer(msg.sender, _acceptedBidInWei);
        token.transfer(owner, _fee);
        emit AcceptBid(_artworkId, _bidder, _acceptedBid);
        uint256 _feeInEth = _fee / DECIMALS();
        emit Fee(_feeInEth);
    }

    function _removeFromTopBids(uint16 _artworkId, address _bidder) internal {
        if (isBid[_artworkId][_bidder]) {
            isBid[_artworkId][_bidder] = false;
            uint256 _tobeRemovedBidderIndex;
            for (uint256 i = 0; i < topBids[_artworkId].length; i++) {
                if (topBids[_artworkId][i].from == _bidder) {
                    _tobeRemovedBidderIndex = i;
                    break;
                }
            }
            topBids[_artworkId][_tobeRemovedBidderIndex] = topBids[_artworkId][topBids[_artworkId].length - 1];
            topBids[_artworkId].pop();
        }
    }

    function getAllBids(uint16 _artworkId) public view returns (Bid[] memory) {
        return topBids[_artworkId];
    }

    /// Bidders can use this function to check their bids on all artworks
    function getBidderAllBids(address _bidder) public view returns (uint16[] memory bidArtwork, uint64[] memory bidPrice) {
        uint256 _count;
        for (uint16 i = 1; i < nextArtworkId; i++) {
            if (bids[i][_bidder] != 0) {
                _count++;
            }
        }
        bidArtwork = new uint16[](_count);
        bidPrice = new uint64[](_count);
        uint256 _index;
        for (uint16 i = 1; i < nextArtworkId; i++) {
            if (bids[i][_bidder] != 0) {
                bidArtwork[_index] = i;
                bidPrice[_index] = bids[i][_bidder];
                _index++;
            }
        }
    }

    function buyArtwork(uint16 _artworkId) public nonReentrant {
        Status _status = artworks[_artworkId].status;
        require(Status(_status) == Status.INVAULT, "Not in vault");
        require(artworks[_artworkId].isActive, "Not active");
        require(artworks[_artworkId].listingPriceInEth != 0, "Not listing");
        require(token.balanceOf(msg.sender) >= artworks[_artworkId].listingPriceInEth * DECIMALS(), "Insufficient balance");
        uint64 _acceptedPrice = artworks[_artworkId].listingPriceInEth;
        artworks[_artworkId].listingPriceInEth = 0;
        address _originalOwner = getArtworkOwner(_artworkId);
        IERC721(artworks[_artworkId].artwork).safeTransferFrom(_originalOwner, msg.sender, 0);
        artworks[_artworkId].collector = msg.sender;
        uint256 _acceptedPriceInWei = uint256(_acceptedPrice) * DECIMALS();
        token.transferFrom(msg.sender, address(this), _acceptedPriceInWei);
        uint256 _fee = _acceptedPriceInWei * 5 / 1000;
        _acceptedPriceInWei -= _fee;
        token.transfer(_originalOwner, _acceptedPriceInWei);
        token.transfer(owner, _fee);
        emit Buy(_artworkId, _acceptedPrice);
        uint256 _feeInEth = _fee / DECIMALS();
        emit Fee(_feeInEth);
    }

    function onERC721Received(
        address /*operator*/,
        address /*from*/,
        uint256 /*tokenId*/,
        bytes calldata /*data*/
    ) external pure override returns (bytes4) {
        return IERC721Receiver.onERC721Received.selector;
    }

    error artworkIsListing(uint16 artworkId);
    error insufficientBalance(uint64 requiredAmount);
}
IERC721Receiver.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity >=0.5.0;

/**
 * @title ERC-721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC-721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be
     * reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}
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;
    }
}
IERC721.sol 135 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/IERC721.sol)

pragma solidity >=0.6.2;

import {IERC165} from "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC-721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC-721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
     *   {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the address zero.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
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 value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` 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 value) external returns (bool);
}
IERC721Metadata.sol 27 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity >=0.6.2;

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

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}
IERC20Metadata.sol 26 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Interface for the optional metadata functions from the ERC-20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

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

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)

pragma solidity >=0.4.16;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

Read Contract

DECIMALS 0x2e0f2625 → uint256
artworks 0xf9f15f74 → uint16, address, address, uint8, uint64, bool
balanceOf 0x70a08231 → uint64
bids 0xb71b7084 → uint64
decimals 0x313ce567 → uint256
factoryAddr 0x963671be → address
getAllActiveArtworks 0x82b22aa8 → tuple[]
getAllBids 0xe00fe3fd → tuple[]
getArtworkName 0x4f142468 → string
getArtworkOwner 0x4b22465c → address
getArtworkTokenURI 0x75c7b72f → string
getBidderAllBids 0x707c630e → uint16[], uint64[]
getCollectorCollections 0xfd4c98d2 → tuple[]
getCollectorOrders 0x6422d783 → tuple[]
getTokenSymbol 0xf1850af8 → string
isArtwork 0x23c9469c → bool
isBid 0x1bf87de0 → bool
nextArtworkId 0x9b19cd6a → uint16
onERC721Received 0x150b7a02 → bytes4
owner 0x8da5cb5b → address
token 0xfc0c546a → address
topBids 0xaefb6e55 → address, uint64
totalBalance 0xad7a672f → uint64

Write Contract 16 functions

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

acceptBid 0x159faaae
uint16 _artworkId
address _bidder
activateArtwork 0x83ce394f
uint16 _artworkId
bool _toActive
addArtwork 0x7316d909
address _artworkAddr
address _collector
bid 0x7d32d49e
uint16 _artworkId
uint64 _amount
buyArtwork 0x18f9b78f
uint16 _artworkId
delist 0xba086518
uint16 _artworkId
initiateFactory 0x42c5c299
address _factoryAddr
listArtwork 0x99745105
uint16 _artworkId
uint64 _amount
redeemArtwork 0xf1bf2536
uint16 _artworkId
requestVault 0x14a8ec45
uint16 _artworkId
sendVault 0x99995ce7
uint16 _artworkId
updateArtworkAddress 0x89e4de1b
uint16 _artworkId
address _newArtworkAddr
updateCollector 0xfd850fd9
uint16 _artworkId
address _collector
updateStatus 0x0739545b
uint16 _artworkId
uint8 _status
updateToken 0x2bce9e7b
address _tokenAddr
uint256 _decimals
withdrawBid 0x882d1765
uint16 _artworkId

Top Interactions

AddressTxnsSentReceived
0x4c8f853b...6fFE 3 3

Recent Transactions

CSV
|
Hash Method Block Age From/To Value Txn Fee Type
0x6d610cee...58a656 0x7316d909 24,322,690 IN 0x4c8f853b...6fFE 0 ETH 0.000223945132 ETH EIP-1559
0x6cac2c9e...ecd40c 0x42c5c299 24,322,687 IN 0x4c8f853b...6fFE 0 ETH 0.000059910426 ETH EIP-1559
0x159105e6...b90461 0x60806040 24,322,662 IN 0x4c8f853b...6fFE 0 ETH 0.000229158702 ETH EIP-1559