Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0x57831A0C76Ba6b4FDcbadd6cb48cB26e8fc15e93
Balance 21.6864 ETH
Nonce 1
Code Size 10126 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

10126 bytes
0x60806040526004361061017c5763ffffffff60e060020a6000350416630519ce7981146101cc578063054f7d9c146101fd5780630a0f81681461022657806314806a321461023b5780631c091a5f146102625780631f7474581461028457806327d7874c146102a55780632ba73c15146102c65780632d5796ac146102e7578063302890e1146102ff5780633a3d55a01461031457806345394dad1461032c5780634ae772a4146103415780634e0a3379146103595780634e8dc0031461037a57806356d282fc1461039e5780635ac165af146103a957806362a5af3b146103be57806369250729146103d35780637ae86385146103e85780638c0ceea5146103fd578063930add2914610415578063a269993d1461042a578063b047fb5014610435578063bea12e001461044a578063bff2273e1461045f578063c65b98e714610477578063d531fe2b1461048c578063db341765146104f1578063dd1b7a0f14610506578063e1302e2b1461051b578063ef706adf14610533575b6040805160e560020a62461bcd02815260206004820152601d60248201527f776520646f6e27742061636365707420616e79207061796d656e747321000000604482015290519081900360640190fd5b3480156101d857600080fd5b506101e161054b565b60408051600160a060020a039092168252519081900360200190f35b34801561020957600080fd5b5061021261055a565b604080519115158252519081900360200190f35b34801561023257600080fd5b506101e1610563565b34801561024757600080fd5b50610250610572565b60408051918252519081900360200190f35b34801561026e57600080fd5b506102826004803560248101910135610578565b005b34801561029057600080fd5b50610282600160a060020a036004351661073f565b3480156102b157600080fd5b50610282600160a060020a0360043516610858565b3480156102d257600080fd5b50610282600160a060020a0360043516610971565b3480156102f357600080fd5b50610282600435610a8a565b34801561030b57600080fd5b506101e1610b76565b34801561032057600080fd5b50610282600435610b85565b34801561033857600080fd5b50610282610c71565b34801561034d57600080fd5b50610282600435610d53565b34801561036557600080fd5b50610282600160a060020a0360043516610e42565b34801561038657600080fd5b506102826004356001608060020a0360243516610f5b565b6102826004356113d6565b3480156103b557600080fd5b5061025061165f565b3480156103ca57600080fd5b50610282611665565b3480156103df57600080fd5b50610250611766565b3480156103f457600080fd5b5061025061176c565b34801561040957600080fd5b50610282600435611772565b34801561042157600080fd5b506102826118e1565b6102826004356119ad565b34801561044157600080fd5b506101e1611da3565b34801561045657600080fd5b50610250611db2565b34801561046b57600080fd5b50610282600435611db8565b34801561048357600080fd5b50610250611f23565b34801561049857600080fd5b506104a4600435611f29565b6040805167ffffffffffffffff9096168652600160a060020a03909416602086015261ffff909216848401526001608060020a039081166060850152166080830152519081900360a00190f35b3480156104fd57600080fd5b50610250611f92565b34801561051257600080fd5b506101e1611f98565b34801561052757600080fd5b50610282600435611fa7565b34801561053f57600080fd5b506102826004356121e3565b600254600160a060020a031681565b60065460ff1681565b600054600160a060020a031681565b60075481565b6000806000806000806000806000600660009054906101000a900460ff161515156105db576040805160e560020a62461bcd02815260206004820152601f6024820152600080516020612743833981519152604482015290519081900360640190fd5b899850600097508796505b88871015610720578a8a888181106105fa57fe5b602090810292909201356000818152600c9093526040909220805492985096505067ffffffffffffffff16935061063084612491565b151561063b57610715565b61064484612497565b1561064e57610715565b505082546001808501546000878152600c602052604081208054600160f060020a03191681559092019190915570010000000000000000000000000000000081046001608060020a039081169889019868010000000000000000909304600160a060020a03169350168190036106c586848361249c565b60408051878152600160a060020a03851660208201528082018390526060810184905290517fd876ee5dd9082a3ac4fe7be4799528f1d5f3545cee1516f2821d0f8ecc9d629c9181900360800190a15b6001909601956105e6565b60008811156107325760048054890190555b5050505050505050505050565b600054600160a060020a031633146107a3576040805160e560020a62461bcd02815260206004820152602d602482015260008051602061270383398151915260448201526000805160206126e3833981519152606482015290519081900360840190fd5b600160a060020a0381161515610829576040805160e560020a62461bcd02815260206004820152602d60248201527f6e6577206c6f737420616e6420666f756e642063616e6e6f742062652074686560448201527f207a65726f2d6163636f756e7400000000000000000000000000000000000000606482015290519081900360840190fd5b6003805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600054600160a060020a031633146108bc576040805160e560020a62461bcd02815260206004820152602d602482015260008051602061270383398151915260448201526000805160206126e3833981519152606482015290519081900360840190fd5b600160a060020a0381161515610942576040805160e560020a62461bcd02815260206004820152602a60248201527f6e65772043454f20616464726573732063616e6e6f7420626520746865207a6560448201527f726f2d6163636f756e7400000000000000000000000000000000000000000000606482015290519081900360840190fd5b6000805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600054600160a060020a031633146109d5576040805160e560020a62461bcd02815260206004820152602d602482015260008051602061270383398151915260448201526000805160206126e3833981519152606482015290519081900360840190fd5b600160a060020a0381161515610a5b576040805160e560020a62461bcd02815260206004820152602a60248201527f6e657720434f4f20616464726573732063616e6e6f7420626520746865207a6560448201527f726f2d6163636f756e7400000000000000000000000000000000000000000000606482015290519081900360840190fd5b6001805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600154600160a060020a03163314610aee576040805160e560020a62461bcd02815260206004820152602d602482015260008051602061272383398151915260448201526000805160206126e3833981519152606482015290519081900360840190fd5b60065460ff1615610b37576040805160e560020a62461bcd02815260206004820152601f6024820152600080516020612743833981519152604482015290519081900360640190fd5b610b4081612523565b6040805182815290517f4a13d5076f950a8eb96f30e1f814eecade18fcebc5042f3ce669cea283edaefd9181900360200190a150565b600354600160a060020a031681565b600154600160a060020a03163314610be9576040805160e560020a62461bcd02815260206004820152602d602482015260008051602061272383398151915260448201526000805160206126e3833981519152606482015290519081900360840190fd5b60065460ff1615610c32576040805160e560020a62461bcd02815260206004820152601f6024820152600080516020612743833981519152604482015290519081900360640190fd5b610c3b816125a8565b6040805182815290517f2fa1029ea9a56473df3e068ba4dc7f4343efacb8a54b808f595f78d1d62f00359181900360200190a150565b600354600090600160a060020a031633148015610c8d57503315155b1515610d09576040805160e560020a62461bcd02815260206004820152603660248201527f6f6e6c79204c6f7374416e64466f756e6420697320616c6c6f77656420746f2060448201527f706572666f726d2074686973206f7065726174696f6e00000000000000000000606482015290519081900360840190fd5b50600580546000918290556003546040519192600160a060020a039091169183156108fc0291849190818181858888f19350505050158015610d4f573d6000803e3d6000fd5b5050565b600154600160a060020a03163314610db7576040805160e560020a62461bcd02815260206004820152602d602482015260008051602061272383398151915260448201526000805160206126e3833981519152606482015290519081900360840190fd5b60065460ff1615610e00576040805160e560020a62461bcd02815260206004820152601f6024820152600080516020612743833981519152604482015290519081900360640190fd5b610e0c81600a5461262d565b6040805182815290517f4193e3b6c8a874196e0068cb9b96b2aaef71a2d90fc46254c4508e1003996bd59181900360200190a150565b600054600160a060020a03163314610ea6576040805160e560020a62461bcd02815260206004820152602d602482015260008051602061270383398151915260448201526000805160206126e3833981519152606482015290519081900360840190fd5b600160a060020a0381161515610f2c576040805160e560020a62461bcd02815260206004820152602a60248201527f6e65772043464f20616464726573732063616e6e6f7420626520746865207a6560448201527f726f2d6163636f756e7400000000000000000000000000000000000000000000606482015290519081900360840190fd5b6002805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b60065460009081908190819081908190819060ff1615610fb3576040805160e560020a62461bcd02815260206004820152601f6024820152600080516020612743833981519152604482015290519081900360640190fd5b6000898152600c60205260409020805490975067ffffffffffffffff169550610fdb86612491565b1515611031576040805160e560020a62461bcd02815260206004820152601b60248201527f6f6666657220746f2066756c66696c6c206d7573742065786973740000000000604482015290519081900360640190fd5b61103a86612497565b15156110b5576040805160e560020a62461bcd028152602060048201526024808201527f6f6666657220746f2066756c66696c6c206d757374206e6f742062652065787060448201527f6972656400000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b600d54604080517f6352211e000000000000000000000000000000000000000000000000000000008152600481018c90529051600160a060020a0390921691636352211e916024808201926020929091908290030181600087803b15801561111c57600080fd5b505af1158015611130573d6000803e3d6000fd5b505050506040513d602081101561114657600080fd5b5051600154909550600160a060020a031633148061116c575033600160a060020a038616145b15156111e8576040805160e560020a62461bcd02815260206004820152602760248201527f6f6e6c7920434f4f206f7220746865206f776e65722063616e2066756c66696c60448201527f6c206f7264657200000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b600187015487546001608060020a03909116945061121290859060e060020a900461ffff166126b4565b92506001608060020a03881683101561129b576040805160e560020a62461bcd02815260206004820152602c60248201527f63616e6e6f742066756c66696c6c206f6666657220e28093206f66666572207060448201527f7269636520746f6f206c6f770000000000000000000000000000000000000000606482015290519081900360840190fd5b865460008a8152600c60205260408082208054600160f060020a0319168155600101829055600d5481517f23b872dd000000000000000000000000000000000000000000000000000000008152600160a060020a038a8116600483015268010000000000000000909504851660248201819052604482018f905292519296509316926323b872dd92606480830193919282900301818387803b15801561134057600080fd5b505af1158015611354573d6000803e3d6000fd5b505060048054868803908101909155925061137491508a9050868561249c565b604080518a8152600160a060020a038085166020830152871681830152606081018590526080810183905290517f9accbcf984c4cd67a675ee4d38143974e1fa62aa95da283bd4ca645e408ec2839181900360a00190a1505050505050505050565b6006546000908190819060ff1615611426576040805160e560020a62461bcd02815260206004820152601f6024820152600080516020612743833981519152604482015290519081900360640190fd5b6000848152600c60205260409020805490935067ffffffffffffffff16915061144e82612491565b15156114a4576040805160e560020a62461bcd02815260206004820152601a60248201527f6f6666657220746f20757064617465206d757374206578697374000000000000604482015290519081900360640190fd5b6114ad82612497565b1515611529576040805160e560020a62461bcd02815260206004820152602360248201527f6f6666657220746f20757064617465206d757374206e6f74206265206578706960448201527f7265640000000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b8254680100000000000000009004600160a060020a031633146115bc576040805160e560020a62461bcd02815260206004820152602c60248201527f63616c6c6572206d75737420626520626964646572206f66206f66666572207460448201527f6f20626520757064617465640000000000000000000000000000000000000000606482015290519081900360840190fd5b50600754420160003411156115f8576001830180546001608060020a038082163401166fffffffffffffffffffffffffffffffff199091161790555b825467ffffffffffffffff191667ffffffffffffffff82161783556040805185815233602082015280820183905234606082015290517f56cb95d5e0f3ba4459f610fef3ba603635cef803d945e97c316cdeb392903991916080908290030190a150505050565b60045481565b33158015906116925750600054600160a060020a03163314806116925750600254600160a060020a031633145b151561170e576040805160e560020a62461bcd02815260206004820152603460248201527f6f6e6c792043454f206f722043464f20697320616c6c6f77656420746f20706560448201527f72666f726d2074686973206f7065726174696f6e000000000000000000000000606482015290519081900360840190fd5b60065460ff1615611757576040805160e560020a62461bcd02815260206004820152601f6024820152600080516020612743833981519152604482015290519081900360640190fd5b6006805460ff19166001179055565b60085481565b60055481565b600154600160a060020a031633146117d6576040805160e560020a62461bcd02815260206004820152602d602482015260008051602061272383398151915260448201526000805160206126e3833981519152606482015290519081900360840190fd5b60065460ff161561181f576040805160e560020a62461bcd02815260206004820152601f6024820152600080516020612743833981519152604482015290519081900360640190fd5b67ffffffffffffffff811681146118a6576040805160e560020a62461bcd02815260206004820152602b60248201527f6e657720676c6f62616c4475726174696f6e2076616c7565206d757374206e6f60448201527f7420756e646572666c6f77000000000000000000000000000000000000000000606482015290519081900360840190fd5b60078190556040805182815290517f66be647974b8677ccbdb171c9fd037f0f64af878ddc5b7f0923da6a32173ae479181900360200190a150565b600254600090600160a060020a0316331480156118fd57503315155b1515611967576040805160e560020a62461bcd02815260206004820152602d60248201527f6f6e6c792043464f20697320616c6c6f77656420746f20706572666f726d207460448201526000805160206126e3833981519152606482015290519081900360840190fd5b50600480546000918290556002546040519192600160a060020a039091169183156108fc0291849190818181858888f19350505050158015610d4f573d6000803e3d6000fd5b6000806000806000806000806000806000600660009054906101000a900460ff16151515611a13576040805160e560020a62461bcd02815260206004820152601f6024820152600080516020612743833981519152604482015290519081900360640190fd5b600854341015611a93576040805160e560020a62461bcd02815260206004820152603160248201527f6f6666657220746f74616c2076616c7565206d7573742062652061626f76652060448201527f6d696e696d756d546f74616c56616c7565000000000000000000000000000000606482015290519081900360840190fd5b600b549a50611aa2348c6126b4565b60008d8152600c602052604081208054929c509a5067ffffffffffffffff90911698509650611ad088612491565b15611bca5760018901546001608060020a03169550611aee88612497565b15611b96578854611b0b90879060e060020a900461ffff166126b4565b9450611b16856126d1565b9350838a1015611b96576040805160e560020a62461bcd02815260206004820152603960248201527f6f766572626964207072696365206d757374206d61746368206d696e696d756d60448201527f20707269636520696e6372656d656e7420637269746572696100000000000000606482015290519081900360840190fd5b6001890154600480547001000000000000000000000000000000009092046001608060020a03169182019055808703975092505b600754420191506000871115611bf357508754680100000000000000009004600160a060020a03165b60a0604051908101604052808367ffffffffffffffff16815260200133600160a060020a031681526020018c61ffff168152602001346001608060020a03168152602001600a546001608060020a0316815250600c60008e815260200190815260200160002060008201518160000160006101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555060208201518160000160086101000a815481600160a060020a030219169083600160a060020a03160217905550604082015181600001601c6101000a81548161ffff021916908361ffff16021790555060608201518160010160006101000a8154816001608060020a0302191690836001608060020a0316021790555060808201518160010160106101000a8154816001608060020a0302191690836001608060020a031602179055509050506000871115611d4957611d498c828961249c565b604080518d8152336020820152808201849052346060820152608081018c905290517f05a7f246d4cdcfed1a85bd6331d1638fa8d47fffd48ede9a22f93d06641f309c9181900360a00190a1505050505050505050505050565b600154600160a060020a031681565b600b5481565b600154600160a060020a03163314611e1c576040805160e560020a62461bcd02815260206004820152602d602482015260008051602061272383398151915260448201526000805160206126e3833981519152606482015290519081900360840190fd5b60065460ff1615611e65576040805160e560020a62461bcd02815260206004820152601f6024820152600080516020612743833981519152604482015290519081900360640190fd5b600854600282021115611ee8576040805160e560020a62461bcd02815260206004820152603760248201527f756e7375636365737366756c2076616c7565206d757374206265203c3d20686160448201527f6c66206f66206d696e696d756d546f74616c56616c7565000000000000000000606482015290519081900360840190fd5b600a8190556040805182815290517ffa4a24dc8c85bb39a38146f43e5611e67c18d4df990b85be0fa17a8d9c7f3cc99181900360200190a150565b600a5481565b600c602052600090815260409020805460019091015467ffffffffffffffff821691680100000000000000008104600160a060020a03169160e060020a90910461ffff16906001608060020a038082169170010000000000000000000000000000000090041685565b60095481565b600d54600160a060020a031681565b600654600090819060ff161515612008576040805160e560020a62461bcd02815260206004820152601b60248201527f636f6e7472616374206e6565647320746f2062652066726f7a656e0000000000604482015290519081900360640190fd5b6000838152600c60205260409020805490925061202e9067ffffffffffffffff16612491565b15156120aa576040805160e560020a62461bcd02815260206004820152602760248201527f6f6666657220746f2077697468647261772066756e64732066726f6d206d757360448201527f7420657869737400000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b8154680100000000000000009004600160a060020a0316331461213d576040805160e560020a62461bcd02815260206004820152602f60248201527f6f6e6c7920626964646572732063616e2077697468647261772074686569722060448201527f66756e647320696e20657363726f770000000000000000000000000000000000606482015290519081900360840190fd5b506001808201546000848152600c60205260408082208054600160f060020a031916815590930181905591516001608060020a0390911691339183156108fc0291849190818181858888f1935050505015801561219e573d6000803e3d6000fd5b506040805184815233602082015280820183905290517f2014255827f37802573fb6164526f1d41d309fd57001e38c8f169ffb761170989181900360600190a1505050565b6006546000908190819081908190819060ff1615612239576040805160e560020a62461bcd02815260206004820152601f6024820152600080516020612743833981519152604482015290519081900360640190fd5b6000878152600c60205260409020805490965067ffffffffffffffff16945061226185612491565b15156122b7576040805160e560020a62461bcd02815260206004820152601a60248201527f6f6666657220746f2063616e63656c206d757374206578697374000000000000604482015290519081900360640190fd5b6122c085612497565b151561233c576040805160e560020a62461bcd02815260206004820152602360248201527f6f6666657220746f2063616e63656c206d757374206e6f74206265206578706960448201527f7265640000000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b8554680100000000000000009004600160a060020a031693503384146123d2576040805160e560020a62461bcd02815260206004820152602e60248201527f63616c6c6572206d75737420626520626964646572206f66206f66666572207460448201527f6f2062652063616e63656c6c6564000000000000000000000000000000000000606482015290519081900360840190fd5b600186015486546001608060020a0390911693506123fc90849060e060020a900461ffff166126b4565b6000888152600c602052604081208054600160f060020a03191681556001015560048054828603908101909155909250905061243987858461249c565b60408051888152600160a060020a03861660208201528082018490526060810183905290517f0f30ae9004015531e44539aa93fcbe6e33146abeeed21661204fa432da6bc0759181900360800190a150505050505050565b60001090565b421090565b604051600090600160a060020a0384169083156108fc0290849084818181858888f19350505050905080151561251d57600580548301905560408051858152600160a060020a038516602082015280820184905290517f47b2b7bdacd5004b5f20c86ec07ecda11e5ac511d4ad9ed9ed2c5af4c42cb4bd9181900360600190a15b50505050565b6127108111156125a3576040805160e560020a62461bcd02815260206004820152602560248201527f6f6666657220637574206d75737420626520612076616c69642062617369732060448201527f706f696e74000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b600b55565b612710811115612628576040805160e560020a62461bcd02815260206004820152603360248201527f6d696e696d756d20707269636520696e6372656d656e74206d7573742062652060448201527f612076616c696420626173697320706f696e7400000000000000000000000000606482015290519081900360840190fd5b600955565b600281028210156126ae576040805160e560020a62461bcd02815260206004820152602d60248201527f6d696e696d756d2076616c7565206d757374206265203e3d2032202a20756e7360448201527f75636365737366756c2066656500000000000000000000000000000000000000606482015290519081900360840190fd5b50600855565b6000816127100183612710028115156126c957fe5b049392505050565b6009546127109081019190910204905600686973206f7065726174696f6e000000000000000000000000000000000000006f6e6c792043454f20697320616c6c6f77656420746f20706572666f726d20746f6e6c7920434f4f20697320616c6c6f77656420746f20706572666f726d2074636f6e7472616374206e6565647320746f206e6f742062652066726f7a656e00a165627a7a723058201ff89d31a687ef3fd5ac394fe324d96b89ccbc571da60666a37f7285a49ccade0029

Verified Source Code Partial Match

Compiler: v0.4.24+commit.e67f0147 EVM: byzantium Optimization: Yes (200 runs)
Offers.sol 865 lines
pragma solidity ^0.4.24;

/// @title Contract that manages addresses and access modifiers for certain operations.
/// @author Dapper Labs Inc. (https://www.dapperlabs.com)
contract OffersAccessControl {

    // The address of the account that can replace ceo, coo, cfo, lostAndFound
    address public ceoAddress;
    // The address of the account that can adjust configuration variables and fulfill offer
    address public cooAddress;
    // The address of the CFO account that receives all the fees
    address public cfoAddress;
    // The address where funds of failed "push"es go to
    address public lostAndFoundAddress;

    // The total amount of ether (in wei) in escrow owned by CFO
    uint256 public totalCFOEarnings;
    // The total amount of ether (in wei) in escrow owned by lostAndFound
    uint256 public totalLostAndFoundBalance;

    /// @notice Keeps track whether the contract is frozen.
    ///  When frozen is set to be true, it cannot be set back to false again,
    ///  and all whenNotFrozen actions will be blocked.
    bool public frozen = false;

    /// @notice Access modifier for CEO-only functionality
    modifier onlyCEO() {
        require(msg.sender == ceoAddress, "only CEO is allowed to perform this operation");
        _;
    }

    /// @notice Access modifier for COO-only functionality
    modifier onlyCOO() {
        require(msg.sender == cooAddress, "only COO is allowed to perform this operation");
        _;
    }

    /// @notice Access modifier for CFO-only functionality
    modifier onlyCFO() {
        require(
            msg.sender == cfoAddress &&
            msg.sender != address(0),
            "only CFO is allowed to perform this operation"
        );
        _;
    }

    /// @notice Access modifier for CEO-only or CFO-only functionality
    modifier onlyCeoOrCfo() {
        require(
            msg.sender != address(0) &&
            (
                msg.sender == ceoAddress ||
                msg.sender == cfoAddress
            ),
            "only CEO or CFO is allowed to perform this operation"
        );
        _;
    }

    /// @notice Access modifier for LostAndFound-only functionality
    modifier onlyLostAndFound() {
        require(
            msg.sender == lostAndFoundAddress &&
            msg.sender != address(0),
            "only LostAndFound is allowed to perform this operation"
        );
        _;
    }

    /// @notice Assigns a new address to act as the CEO. Only available to the current CEO.
    /// @param _newCEO The address of the new CEO
    function setCEO(address _newCEO) external onlyCEO {
        require(_newCEO != address(0), "new CEO address cannot be the zero-account");
        ceoAddress = _newCEO;
    }

    /// @notice Assigns a new address to act as the COO. Only available to the current CEO.
    /// @param _newCOO The address of the new COO
    function setCOO(address _newCOO) public onlyCEO {
        require(_newCOO != address(0), "new COO address cannot be the zero-account");
        cooAddress = _newCOO;
    }

    /// @notice Assigns a new address to act as the CFO. Only available to the current CEO.
    /// @param _newCFO The address of the new CFO
    function setCFO(address _newCFO) external onlyCEO {
        require(_newCFO != address(0), "new CFO address cannot be the zero-account");
        cfoAddress = _newCFO;
    }

    /// @notice Assigns a new address to act as the LostAndFound account. Only available to the current CEO.
    /// @param _newLostAndFound The address of the new lostAndFound address
    function setLostAndFound(address _newLostAndFound) external onlyCEO {
        require(_newLostAndFound != address(0), "new lost and found cannot be the zero-account");
        lostAndFoundAddress = _newLostAndFound;
    }

    /// @notice CFO withdraws the CFO earnings
    function withdrawTotalCFOEarnings() external onlyCFO {
        // Obtain reference
        uint256 balance = totalCFOEarnings;
        totalCFOEarnings = 0;
        cfoAddress.transfer(balance);
    }

    /// @notice LostAndFound account withdraws all the lost and found amount
    function withdrawTotalLostAndFoundBalance() external onlyLostAndFound {
        // Obtain reference
        uint256 balance = totalLostAndFoundBalance;
        totalLostAndFoundBalance = 0;
        lostAndFoundAddress.transfer(balance);
    }

    /// @notice Modifier to allow actions only when the contract is not frozen
    modifier whenNotFrozen() {
        require(!frozen, "contract needs to not be frozen");
        _;
    }

    /// @notice Modifier to allow actions only when the contract is frozen
    modifier whenFrozen() {
        require(frozen, "contract needs to be frozen");
        _;
    }

    /// @notice Called by CEO or CFO role to freeze the contract.
    ///  Only intended to be used if a serious exploit is detected.
    /// @notice Allow two C-level roles to call this function in case either CEO or CFO key is compromised.
    /// @notice This is a one-way process; there is no un-freezing.
    /// @dev A frozen contract will be frozen forever, there's no way to undo this action.
    function freeze() external onlyCeoOrCfo whenNotFrozen {
        frozen = true;
    }

}

/// @title Interface for contracts conforming to ERC-721: Non-Fungible Tokens
/// @author Dieter Shirley <[email protected]> (https://github.com/dete)
contract ERC721 {
    // Required methods
    function totalSupply() public view returns (uint256 total);
    function balanceOf(address _owner) public view returns (uint256 balance);
    function ownerOf(uint256 _tokenId) external view returns (address owner);
    function approve(address _to, uint256 _tokenId) external;
    function transfer(address _to, uint256 _tokenId) external;
    function transferFrom(address _from, address _to, uint256 _tokenId) external;

    // Events
    event Transfer(address from, address to, uint256 tokenId);
    event Approval(address owner, address approved, uint256 tokenId);

    // Optional
    // function name() public view returns (string name);
    // function symbol() public view returns (string symbol);
    // function tokensOfOwner(address _owner) external view returns (uint256[] tokenIds);
    // function tokenMetadata(uint256 _tokenId, string _preferredTransport) public view returns (string infoUrl);

    // ERC-165 Compatibility (https://github.com/ethereum/EIPs/issues/165)
    function supportsInterface(bytes4 _interfaceID) external view returns (bool);
}

/// @title Contract that manages configuration values and fee structure for offers.
/// @author Dapper Labs Inc. (https://www.dapperlabs.com)
contract OffersConfig is OffersAccessControl {

    /* ************************* */
    /* ADJUSTABLE CONFIGURATIONS */
    /* ************************* */

    // The duration (in seconds) of all offers that are created. This parameter is also used in calculating
    // new expiration times when extending offers.
    uint256 public globalDuration;
    // The global minimum offer value (price + offer fee, in wei)
    uint256 public minimumTotalValue;
    // The minimum overbid increment % (expressed in basis points, which is 1/100 of a percent)
    // For basis points, values 0-10,000 map to 0%-100%
    uint256 public minimumPriceIncrement;

    /* *************** */
    /* ADJUSTABLE FEES */
    /* *************** */

    // Throughout the various contracts there will be various symbols used for the purpose of a clear display
    // of the underlying mathematical formulation. Specifically,
    //
    //          - T: This is the total amount of funds associated with an offer, comprised of 1) the offer
    //                  price which the bidder is proposing the owner of the token receive, and 2) an amount
    //                  that is the maximum the main Offers contract will ever take - this is when the offer
    //                  is cancelled, or fulfilled. In other scenarios, the amount taken by the main contract
    //                  may be less, depending on other configurations.
    //
    //          - S: This is called the offerCut, expressed as a basis point. This determines the maximum amount
    //                  of ether the main contract can ever take in the various possible outcomes of an offer
    //                  (cancelled, expired, overbid, fulfilled, updated).
    //
    //          - P: This simply refers to the price that the bidder is offering the owner receive, upon
    //                  fulfillment of the offer process.
    //
    //          - Below is the formula that ties the symbols listed above together (S is % for brevity):
    //                  T = P + S * P

    // Flat fee (in wei) the main contract takes when offer has been expired or overbid. The fee serves as a
    // disincentive for abuse and allows recoupment of ether spent calling batchRemoveExpired on behalf of users.
    uint256 public unsuccessfulFee;
    // This is S, the maximum % the main contract takes on each offer. S represents the total amount paid when
    // an offer has been fulfilled or cancelled.
    uint256 public offerCut;

    /* ****** */
    /* EVENTS */
    /* ****** */

    event GlobalDurationUpdated(uint256 value);
    event MinimumTotalValueUpdated(uint256 value);
    event MinimumPriceIncrementUpdated(uint256 value);
    event OfferCutUpdated(uint256 value);
    event UnsuccessfulFeeUpdated(uint256 value);

    /* ********* */
    /* FUNCTIONS */
    /* ********* */

    /// @notice Sets the minimumTotalValue value. This would impact offers created after this has been set, but
    ///  not existing offers.
    /// @notice Only callable by COO, when not frozen.
    /// @param _newMinTotal The minimumTotalValue value to set
    function setMinimumTotalValue(uint256 _newMinTotal) external onlyCOO whenNotFrozen {
        _setMinimumTotalValue(_newMinTotal, unsuccessfulFee);
        emit MinimumTotalValueUpdated(_newMinTotal);
    }

    /// @notice Sets the globalDuration value. All offers that are created or updated will compute a new expiration
    ///  time based on this.
    /// @notice Only callable by COO, when not frozen.
    /// @dev Need to check for underflow since function argument is 256 bits, and the offer expiration time is
    ///  packed into 64 bits in the Offer struct.
    /// @param _newDuration The globalDuration value to set.
    function setGlobalDuration(uint256 _newDuration) external onlyCOO whenNotFrozen {
        require(_newDuration == uint256(uint64(_newDuration)), "new globalDuration value must not underflow");
        globalDuration = _newDuration;
        emit GlobalDurationUpdated(_newDuration);
    }

    /// @notice Sets the offerCut value. All offers will compute a fee taken by this contract based on this
    ///  configuration.
    /// @notice Only callable by COO, when not frozen.
    /// @dev As this configuration is a basis point, the value to set must be less than or equal to 10000.
    /// @param _newOfferCut The offerCut value to set.
    function setOfferCut(uint256 _newOfferCut) external onlyCOO whenNotFrozen {
        _setOfferCut(_newOfferCut);
        emit OfferCutUpdated(_newOfferCut);
    }

    /// @notice Sets the unsuccessfulFee value. All offers that are unsuccessful (overbid or expired)
    ///  will have a flat fee taken by the main contract before being refunded to bidders.
    /// @notice Given Tmin (_minTotal), flat fee (_unsuccessfulFee),
    ///  Tmin ≥ (2 * flat fee) guarantees that offer prices ≥ flat fee, always. This is important to prevent the
    ///  existence of offers that, when overbid or expired, would result in the main contract taking too big of a cut.
    ///  In the case of a sufficiently low offer price, eg. the same as unsuccessfulFee, the most the main contract can
    ///  ever take is simply the amount of unsuccessfulFee.
    /// @notice Only callable by COO, when not frozen.
    /// @param _newUnsuccessfulFee The unsuccessfulFee value to set.
    function setUnsuccessfulFee(uint256 _newUnsuccessfulFee) external onlyCOO whenNotFrozen {
        require(minimumTotalValue >= (2 * _newUnsuccessfulFee), "unsuccessful value must be <= half of minimumTotalValue");
        unsuccessfulFee = _newUnsuccessfulFee;
        emit UnsuccessfulFeeUpdated(_newUnsuccessfulFee);
    }

    /// @notice Sets the minimumPriceIncrement value. All offers that are overbid must have a price greater
    ///  than the minimum increment computed from this basis point.
    /// @notice Only callable by COO, when not frozen.
    /// @dev As this configuration is a basis point, the value to set must be less than or equal to 10000.
    /// @param _newMinimumPriceIncrement The minimumPriceIncrement value to set.
    function setMinimumPriceIncrement(uint256 _newMinimumPriceIncrement) external onlyCOO whenNotFrozen {
        _setMinimumPriceIncrement(_newMinimumPriceIncrement);
        emit MinimumPriceIncrementUpdated(_newMinimumPriceIncrement);
    }

    /// @notice Utility function used internally for the setMinimumTotalValue method.
    /// @notice Given Tmin (_minTotal), flat fee (_unsuccessfulFee),
    ///  Tmin ≥ (2 * flat fee) guarantees that offer prices ≥ flat fee, always. This is important to prevent the
    ///  existence of offers that, when overbid or expired, would result in the main contract taking too big of a cut.
    ///  In the case of a sufficiently low offer price, eg. the same as unsuccessfulFee, the most the main contract can
    ///  ever take is simply the amount of unsuccessfulFee.
    /// @param _newMinTotal The minimumTotalValue value to set.
    /// @param _unsuccessfulFee The unsuccessfulFee value used to check if the _minTotal specified
    ///  is too low.
    function _setMinimumTotalValue(uint256 _newMinTotal, uint256 _unsuccessfulFee) internal {
        require(_newMinTotal >= (2 * _unsuccessfulFee), "minimum value must be >= 2 * unsuccessful fee");
        minimumTotalValue = _newMinTotal;
    }

    /// @dev As offerCut is a basis point, the value to set must be less than or equal to 10000.
    /// @param _newOfferCut The offerCut value to set.
    function _setOfferCut(uint256 _newOfferCut) internal {
        require(_newOfferCut <= 1e4, "offer cut must be a valid basis point");
        offerCut = _newOfferCut;
    }

    /// @dev As minimumPriceIncrement is a basis point, the value to set must be less than or equal to 10000.
    /// @param _newMinimumPriceIncrement The minimumPriceIncrement value to set.
    function _setMinimumPriceIncrement(uint256 _newMinimumPriceIncrement) internal {
        require(_newMinimumPriceIncrement <= 1e4, "minimum price increment must be a valid basis point");
        minimumPriceIncrement = _newMinimumPriceIncrement;
    }
}

/// @title Base contract for CryptoKitties Offers. Holds all common structs, events, and base variables.
/// @author Dapper Labs Inc. (https://www.dapperlabs.com)
contract OffersBase is OffersConfig {
    /*** EVENTS ***/

    /// @notice The OfferCreated event is emitted when an offer is created through
    ///  createOffer method.
    /// @param tokenId The token id that a bidder is offering to buy from the owner.
    /// @param bidder The creator of the offer.
    /// @param expiresAt The timestamp when the offer will be expire.
    /// @param total The total eth value the bidder sent to the Offer contract.
    /// @param offerPrice The eth price that the owner of the token will receive
    ///  if the offer is accepted.
    event OfferCreated(
        uint256 tokenId,
        address bidder,
        uint256 expiresAt,
        uint256 total,
        uint256 offerPrice
    );

    /// @notice The OfferCancelled event is emitted when an offer is cancelled before expired.
    /// @param tokenId The token id that the cancelled offer was offering to buy.
    /// @param bidder The creator of the offer.
    /// @param bidderReceived The eth amount that the bidder received as refund.
    /// @param fee The eth amount that CFO received as the fee for the cancellation.
    event OfferCancelled(
        uint256 tokenId,
        address bidder,
        uint256 bidderReceived,
        uint256 fee
    );

    /// @notice The OfferFulfilled event is emitted when an active offer has been fulfilled, meaning
    ///  the bidder now owns the token, and the orignal owner receives the eth amount from the offer.
    /// @param tokenId The token id that the fulfilled offer was offering to buy.
    /// @param bidder The creator of the offer.
    /// @param owner The original owner of the token who accepted the offer.
    /// @param ownerReceived The eth amount that the original owner received from the offer
    /// @param fee The eth amount that CFO received as the fee for the successfully fulfilling.
    event OfferFulfilled(
        uint256 tokenId,
        address bidder,
        address owner,
        uint256 ownerReceived,
        uint256 fee
    );

    /// @notice The OfferUpdated event is emitted when an active offer was either extended the expiry
    ///  or raised the price.
    /// @param tokenId The token id that the updated offer was offering to buy.
    /// @param bidder The creator of the offer, also is whom updated the offer.
    /// @param newExpiresAt The new expiry date of the updated offer.
    /// @param totalRaised The total eth value the bidder sent to the Offer contract to raise the offer.
    ///  if the totalRaised is 0, it means the offer was extended without raising the price.
    event OfferUpdated(
        uint256 tokenId,
        address bidder,
        uint256 newExpiresAt,
        uint256 totalRaised
    );

    /// @notice The ExpiredOfferRemoved event is emitted when an expired offer gets removed. The eth value will
    ///  be returned to the bidder's account, excluding the fee.
    /// @param tokenId The token id that the removed offer was offering to buy
    /// @param bidder The creator of the offer.
    /// @param bidderReceived The eth amount that the bidder received from the offer.
    /// @param fee The eth amount that CFO received as the fee.
    event ExpiredOfferRemoved(
      uint256 tokenId,
      address bidder,
      uint256 bidderReceived,
      uint256 fee
    );

    /// @notice The BidderWithdrewFundsWhenFrozen event is emitted when a bidder withdrew their eth value of
    ///  the offer when the contract is frozen.
    /// @param tokenId The token id that withdrawed offer was offering to buy
    /// @param bidder The creator of the offer, also is whom withdrawed the fund.
    /// @param amount The total amount that the bidder received.
    event BidderWithdrewFundsWhenFrozen(
        uint256 tokenId,
        address bidder,
        uint256 amount
    );


    /// @dev The PushFundsFailed event is emitted when the Offer contract fails to send certain amount of eth
    ///  to an address, e.g. sending the fund back to the bidder when the offer was overbidden by a higher offer.
    /// @param tokenId The token id of an offer that the sending fund is involved.
    /// @param to The address that is supposed to receive the fund but failed for any reason.
    /// @param amount The eth amount that the receiver fails to receive.
    event PushFundsFailed(
        uint256 tokenId,
        address to,
        uint256 amount
    );

    /*** DATA TYPES ***/

    /// @dev The Offer struct. The struct fits in two 256-bits words.
    struct Offer {
        // Time when offer expires
        uint64 expiresAt;
        // Bidder The creator of the offer
        address bidder;
        // Offer cut in basis points, which ranges from 0-10000.
        // It's the cut that CFO takes when the offer is successfully accepted by the owner.
        // This is stored in the offer struct so that it won't be changed if COO updates
        // the `offerCut` for new offers.
        uint16 offerCut;
        // Total value (in wei) a bidder sent in msg.value to create the offer
        uint128 total;
        // Fee (in wei) that CFO takes when the offer is expired or overbid.
        // This is stored in the offer struct so that it won't be changed if COO updates
        // the `unsuccessfulFee` for new offers.
        uint128 unsuccessfulFee;
    }

    /*** STORAGE ***/
    /// @notice Mapping from token id to its corresponding offer.
    /// @dev One token can only have one offer.
    ///  Making it public so that solc-0.4.24 will generate code to query offer by a given token id.
    mapping (uint256 => Offer) public tokenIdToOffer;

    /// @notice computes the minimum offer price to overbid a given offer with its offer price.
    ///  The new offer price has to be a certain percentage, which defined by `minimumPriceIncrement`,
    ///  higher than the previous offer price.
    /// @dev This won't overflow, because `_offerPrice` is in uint128, and `minimumPriceIncrement`
    ///  is 16 bits max.
    /// @param _offerPrice The amount of ether in wei as the offer price
    /// @return The minimum amount of ether in wei to overbid the given offer price
    function _computeMinimumOverbidPrice(uint256 _offerPrice) internal view returns (uint256) {
        return _offerPrice * (1e4 + minimumPriceIncrement) / 1e4;
    }

    /// @notice Computes the offer price that the owner will receive if the offer is accepted.
    /// @dev This is safe against overflow because msg.value and the total supply of ether is capped within 128 bits.
    /// @param _total The total value of the offer. Also is the msg.value that the bidder sent when
    ///  creating the offer.
    /// @param _offerCut The percentage in basis points that will be taken by the CFO if the offer is fulfilled.
    /// @return The offer price that the owner will receive if the offer is fulfilled.
    function _computeOfferPrice(uint256 _total, uint256 _offerCut) internal pure returns (uint256) {
        return _total * 1e4 / (1e4 + _offerCut);
    }

    /// @notice Check if an offer exists or not by checking the expiresAt field of the offer.
    ///  True if exists, False if not.
    /// @dev Assuming the expiresAt field is from the offer struct in storage.
    /// @dev Since expiry check always come right after the offer existance check, it will save some gas by checking
    /// both existance and expiry on one field, as it only reads from the storage once.
    /// @param _expiresAt The time at which the offer we want to validate expires.
    /// @return True or false (if the offer exists not).
    function _offerExists(uint256 _expiresAt) internal pure returns (bool) {
        return _expiresAt > 0;
    }

    /// @notice Check if an offer is still active by checking the expiresAt field of the offer. True if the offer is,
    ///  still active, False if the offer has expired,
    /// @dev Assuming the expiresAt field is from the offer struct in storage.
    /// @param _expiresAt The time at which the offer we want to validate expires.
    /// @return True or false (if the offer has expired or not).
    function _isOfferActive(uint256 _expiresAt) internal view returns (bool) {
        return now < _expiresAt;
    }

    /// @dev Try pushing the fund to an address.
    /// @notice If sending the fund to the `_to` address fails for whatever reason, then the logic
    ///  will continue and the amount will be kept under the LostAndFound account. Also an event `PushFundsFailed`
    ///  will be emitted for notifying the failure.
    /// @param _tokenId The token id for the offer.
    /// @param _to The address the main contract is attempting to send funds to.
    /// @param _amount The amount of funds (in wei) the main contract is attempting to send.
    function _tryPushFunds(uint256 _tokenId, address _to, uint256 _amount) internal {
        // Sending the amount of eth in wei, and handling the failure.
        // The gas spent transferring funds has a set upper limit
        bool success = _to.send(_amount);
        if (!success) {
            // If failed sending to the `_to` address, then keep the amount under the LostAndFound account by
            // accumulating totalLostAndFoundBalance.
            totalLostAndFoundBalance = totalLostAndFoundBalance + _amount;

            // Emitting the event lost amount.
            emit PushFundsFailed(_tokenId, _to, _amount);
        }
    }
}

/// @title Contract that manages funds from creation to fulfillment for offers made on any ERC-721 token.
/// @author Dapper Labs Inc. (https://www.dapperlabs.com)
/// @notice This generic contract interfaces with any ERC-721 compliant contract
contract Offers is OffersBase {

    // This is the main Offers contract. In order to keep our code separated into logical sections,
    // we've broken it up into multiple files using inheritance. This allows us to keep related code
    // collocated while still avoiding a single large file, which would be harder to maintain. The breakdown
    // is as follows:
    //
    //      - OffersBase: This contract defines the fundamental code that the main contract uses.
    //              This includes our main data storage, data types, events, and internal functions for
    //              managing offers in their lifecycle.
    //
    //      - OffersConfig: This contract manages the various configuration values that determine the
    //              details of the offers that get created, cancelled, overbid, expired, and fulfilled,
    //              as well as the fee structure that the offers will be operating with.
    //
    //      - OffersAccessControl: This contract manages the various addresses and constraints for
    //              operations that can be executed only by specific roles. The roles are: CEO, CFO,
    //              COO, and LostAndFound. Additionally, this contract exposes functions for the CFO
    //              to withdraw earnings and the LostAndFound account to withdraw any lost funds.

    /// @dev The ERC-165 interface signature for ERC-721.
    ///  Ref: https://github.com/ethereum/EIPs/issues/165
    ///  Ref: https://github.com/ethereum/EIPs/issues/721
    bytes4 constant InterfaceSignature_ERC721 = bytes4(0x9a20483d);

    // Reference to contract tracking NFT ownership
    ERC721 public nonFungibleContract;

    /// @notice Creates the main Offers smart contract instance and sets initial configuration values
    /// @param _nftAddress The address of the ERC-721 contract managing NFT ownership
    /// @param _cooAddress The address of the COO to set
    /// @param _globalDuration The initial globalDuration value to set
    /// @param _minimumTotalValue The initial minimumTotalValue value to set
    /// @param _minimumPriceIncrement The initial minimumPriceIncrement value to set
    /// @param _unsuccessfulFee The initial unsuccessfulFee value to set
    /// @param _offerCut The initial offerCut value to set
    constructor(
      address _nftAddress,
      address _cooAddress,
      uint256 _globalDuration,
      uint256 _minimumTotalValue,
      uint256 _minimumPriceIncrement,
      uint256 _unsuccessfulFee,
      uint256 _offerCut
    ) public {
        // The creator of the contract is the ceo
        ceoAddress = msg.sender;

        // Get reference of the address of the NFT contract
        ERC721 candidateContract = ERC721(_nftAddress);
        require(candidateContract.supportsInterface(InterfaceSignature_ERC721), "NFT Contract needs to support ERC721 Interface");
        nonFungibleContract = candidateContract;

        setCOO(_cooAddress);

        // Set initial claw-figuration values
        globalDuration = _globalDuration;
        unsuccessfulFee = _unsuccessfulFee;
        _setOfferCut(_offerCut);
        _setMinimumPriceIncrement(_minimumPriceIncrement);
        _setMinimumTotalValue(_minimumTotalValue, _unsuccessfulFee);
    }

    /// @notice Creates an offer on a token. This contract receives bidders funds and refunds the previous bidder
    ///  if this offer overbids a previously active (unexpired) offer.
    /// @notice When this offer overbids a previously active offer, this offer must have a price greater than
    ///  a certain percentage of the previous offer price, which the minimumOverbidPrice basis point specifies.
    ///  A flat fee is also taken from the previous offer before refund the previous bidder.
    /// @notice When there is a previous offer that has already expired but not yet been removed from storage,
    ///  the new offer can be created with any total value as long as it is greater than the minimumTotalValue.
    /// @notice Works only when contract is not frozen.
    /// @param _tokenId The token a bidder wants to create an offer for.
    function createOffer(uint256 _tokenId) external payable whenNotFrozen {
        // T = msg.value
        // Check that the total amount of the offer isn't below the meow-nimum
        require(msg.value >= minimumTotalValue, "offer total value must be above minimumTotalValue");

        uint256 _offerCut = offerCut;

        // P, the price that owner will see and receive if the offer is accepted.
        uint256 offerPrice = _computeOfferPrice(msg.value, _offerCut);

        Offer storage previousOffer = tokenIdToOffer[_tokenId];
        uint256 previousExpiresAt = previousOffer.expiresAt;

        uint256 toRefund = 0;

        // Check if tokenId already has an offer
        if (_offerExists(previousExpiresAt)) {
            uint256 previousOfferTotal = uint256(previousOffer.total);

            // If the previous offer is still active, the new offer needs to match the previous offer's price
            // plus a minimum required increment (minimumOverbidPrice).
            // We calculate the previous offer's price, the corresponding minimumOverbidPrice, and check if the
            // new offerPrice is greater than or equal to the minimumOverbidPrice
            // The owner is fur-tunate to have such a desirable kitty
            if (_isOfferActive(previousExpiresAt)) {
                uint256 previousPriceForOwner = _computeOfferPrice(previousOfferTotal, uint256(previousOffer.offerCut));
                uint256 minimumOverbidPrice = _computeMinimumOverbidPrice(previousPriceForOwner);
                require(offerPrice >= minimumOverbidPrice, "overbid price must match minimum price increment criteria");
            }

            uint256 cfoEarnings = previousOffer.unsuccessfulFee;
            // Bidder gets refund: T - flat fee
            // The in-fur-ior offer gets refunded for free, how nice.
            toRefund = previousOfferTotal - cfoEarnings;

            totalCFOEarnings += cfoEarnings;
        }

        uint256 newExpiresAt = now + globalDuration;

        // Get a reference of previous bidder address before overwriting with new offer.
        // This is only needed if there is refund
        address previousBidder;
        if (toRefund > 0) {
            previousBidder = previousOffer.bidder;
        }

        tokenIdToOffer[_tokenId] = Offer(
            uint64(newExpiresAt),
            msg.sender,
            uint16(_offerCut),
            uint128(msg.value),
            uint128(unsuccessfulFee)
        );

        // Postpone the refund until the previous offer has been overwritten by the new offer.
        if (toRefund > 0) {
            // Finally, sending funds to this bidder. If failed, the fund will be kept in escrow
            // under lostAndFound's address
            _tryPushFunds(
                _tokenId,
                previousBidder,
                toRefund
            );
        }

        emit OfferCreated(
            _tokenId,
            msg.sender,
            newExpiresAt,
            msg.value,
            offerPrice
        );
    }

    /// @notice Cancels an offer that must exist and be active currently. This moves funds from this contract
    ///  back to the the bidder, after a cut has been taken.
    /// @notice Works only when contract is not frozen.
    /// @param _tokenId The token specified by the offer a bidder wants to cancel
    function cancelOffer(uint256 _tokenId) external whenNotFrozen {
        // Check that offer exists and is active currently
        Offer storage offer = tokenIdToOffer[_tokenId];
        uint256 expiresAt = offer.expiresAt;
        require(_offerExists(expiresAt), "offer to cancel must exist");
        require(_isOfferActive(expiresAt), "offer to cancel must not be expired");

        address bidder = offer.bidder;
        require(msg.sender == bidder, "caller must be bidder of offer to be cancelled");

        // T
        uint256 total = uint256(offer.total);
        // P = T - S; Bidder gets all of P, CFO gets all of T - P
        uint256 toRefund = _computeOfferPrice(total, offer.offerCut);
        uint256 cfoEarnings = total - toRefund;

        // Remove offer from storage
        delete tokenIdToOffer[_tokenId];

        // Add to CFO's balance
        totalCFOEarnings += cfoEarnings;

        // Transfer money in escrow back to bidder
        _tryPushFunds(_tokenId, bidder, toRefund);

        emit OfferCancelled(
            _tokenId,
            bidder,
            toRefund,
            cfoEarnings
        );
    }

    /// @notice Fulfills an offer that must exist and be active currently. This moves the funds of the
    ///  offer held in escrow in this contract to the owner of the token, and atomically transfers the
    ///  token from the owner to the bidder. A cut is taken by this contract.
    /// @notice We also acknowledge the paw-sible difficulties of keeping in-sync with the Ethereum
    ///  blockchain, and have allowed for fulfilling offers by specifying the _minOfferPrice at which the owner
    ///  of the token is happy to accept the offer. Thus, the owner will always receive the latest offer
    ///  price, which can only be at least the _minOfferPrice that was specified. Specifically, this
    ///  implementation is designed to prevent the edge case where the owner accidentally accepts an offer
    ///  with a price lower than intended. For example, this can happen when the owner fulfills the offer
    ///  precisely when the offer expires and is subsequently replaced with a new offer priced lower.
    /// @notice Works only when contract is not frozen.
    /// @dev We make sure that the token is not on auction when we fulfill an offer, because the owner of the
    ///  token would be the auction contract instead of the user. This function requires that this Offers contract
    ///  is approved for the token in order to make the call to transfer token ownership. This is sufficient
    ///  because approvals are cleared on transfer (including transfer to the auction).
    /// @param _tokenId The token specified by the offer that will be fulfilled.
    /// @param _minOfferPrice The minimum price at which the owner of the token is happy to accept the offer.
    function fulfillOffer(uint256 _tokenId, uint128 _minOfferPrice) external whenNotFrozen {
        // Check that offer exists and is active currently
        Offer storage offer = tokenIdToOffer[_tokenId];
        uint256 expiresAt = offer.expiresAt;
        require(_offerExists(expiresAt), "offer to fulfill must exist");
        require(_isOfferActive(expiresAt), "offer to fulfill must not be expired");

        // Get the owner of the token
        address owner = nonFungibleContract.ownerOf(_tokenId);

        require(msg.sender == cooAddress || msg.sender == owner, "only COO or the owner can fulfill order");

        // T
        uint256 total = uint256(offer.total);
        // P = T - S
        uint256 offerPrice = _computeOfferPrice(total, offer.offerCut);

        // Check if the offer price is below the minimum that the owner is happy to accept the offer for
        require(offerPrice >= _minOfferPrice, "cannot fulfill offer – offer price too low");

        // Get a reference of the bidder address befur removing offer from storage
        address bidder = offer.bidder;

        // Remove offer from storage
        delete tokenIdToOffer[_tokenId];

        // Transfer token on behalf of owner to bidder
        nonFungibleContract.transferFrom(owner, bidder, _tokenId);

        // NFT has been transferred! Now calculate fees and transfer fund to the owner
        // T - P, the CFO's earnings
        uint256 cfoEarnings = total - offerPrice;
        totalCFOEarnings += cfoEarnings;

        // Transfer money in escrow to owner
        _tryPushFunds(_tokenId, owner, offerPrice);

        emit OfferFulfilled(
            _tokenId,
            bidder,
            owner,
            offerPrice,
            cfoEarnings
        );
    }

    /// @notice Removes any existing and inactive (expired) offers from storage. In doing so, this contract
    ///  takes a flat fee from the total amount attached to each offer before sending the remaining funds
    ///  back to the bidder.
    /// @notice Nothing will be done if the offer for a token is either non-existent or active.
    /// @param _tokenIds The array of tokenIds that will be removed from storage
    function batchRemoveExpired(uint256[] _tokenIds) external whenNotFrozen {
        uint256 len = _tokenIds.length;

        // Use temporary accumulator
        uint256 cumulativeCFOEarnings = 0;

        for (uint256 i = 0; i < len; i++) {
            uint256 tokenId = _tokenIds[i];
            Offer storage offer = tokenIdToOffer[tokenId];
            uint256 expiresAt = offer.expiresAt;

            // Skip the offer if not exist
            if (!_offerExists(expiresAt)) {
                continue;
            }
            // Skip if the offer has not expired yet
            if (_isOfferActive(expiresAt)) {
                continue;
            }

            // Get a reference of the bidder address before removing offer from storage
            address bidder = offer.bidder;

            // CFO gets the flat fee
            uint256 cfoEarnings = uint256(offer.unsuccessfulFee);

            // Bidder gets refund: T - flat
            uint256 toRefund = uint256(offer.total) - cfoEarnings;

            // Ensure the previous offer has been removed before refunding
            delete tokenIdToOffer[tokenId];

            // Add to cumulative balance of CFO's earnings
            cumulativeCFOEarnings += cfoEarnings;

            // Finally, sending funds to this bidder. If failed, the fund will be kept in escrow
            // under lostAndFound's address
            _tryPushFunds(
                tokenId,
                bidder,
                toRefund
            );

            emit ExpiredOfferRemoved(
                tokenId,
                bidder,
                toRefund,
                cfoEarnings
            );
        }

        // Add to CFO's balance if any expired offer has been removed
        if (cumulativeCFOEarnings > 0) {
            totalCFOEarnings += cumulativeCFOEarnings;
        }
    }

    /// @notice Updates an existing and active offer by setting a new expiration time and, optionally, raise
    ///  the price of the offer.
    /// @notice As the offers are always using the configuration values currently in storage, the updated
    ///  offer may be adhering to configuration values that are different at the time of its original creation.
    /// @dev We check msg.value to determine if the offer price should be raised. If 0, only a new
    ///  expiration time is set.
    /// @param _tokenId The token specified by the offer that will be updated.
    function updateOffer(uint256 _tokenId) external payable whenNotFrozen {
        // Check that offer exists and is active currently
        Offer storage offer = tokenIdToOffer[_tokenId];
        uint256 expiresAt = uint256(offer.expiresAt);
        require(_offerExists(expiresAt), "offer to update must exist");
        require(_isOfferActive(expiresAt), "offer to update must not be expired");

        require(msg.sender == offer.bidder, "caller must be bidder of offer to be updated");

        uint256 newExpiresAt = now + globalDuration;

        // Check if the caller wants to raise the offer as well
        if (msg.value > 0) {
            // Set the new price
            offer.total += uint128(msg.value);
        }

        offer.expiresAt = uint64(newExpiresAt);

        emit OfferUpdated(_tokenId, msg.sender, newExpiresAt, msg.value);

    }

    /// @notice Sends funds of each existing offer held in escrow back to bidders. The function is callable
    ///  by anyone.
    /// @notice Works only when contract is frozen. In this case, we want to allow all funds to be returned
    ///  without taking any fees.
    /// @param _tokenId The token specified by the offer a bidder wants to withdraw funds for.
    function bidderWithdrawFunds(uint256 _tokenId) external whenFrozen {
        // Check that offer exists
        Offer storage offer = tokenIdToOffer[_tokenId];
        require(_offerExists(offer.expiresAt), "offer to withdraw funds from must exist");
        require(msg.sender == offer.bidder, "only bidders can withdraw their funds in escrow");

        // Get a reference of the total to withdraw before removing offer from storage
        uint256 total = uint256(offer.total);

        delete tokenIdToOffer[_tokenId];

        // Send funds back to bidders!
        msg.sender.transfer(total);

        emit BidderWithdrewFundsWhenFrozen(_tokenId, msg.sender, total);
    }

    /// @notice we don't accept any value transfer.
    function() external payable {
        revert("we don't accept any payments!");
    }
}

Read Contract

ceoAddress 0x0a0f8168 → address
cfoAddress 0x0519ce79 → address
cooAddress 0xb047fb50 → address
frozen 0x054f7d9c → bool
globalDuration 0x14806a32 → uint256
lostAndFoundAddress 0x302890e1 → address
minimumPriceIncrement 0xdb341765 → uint256
minimumTotalValue 0x69250729 → uint256
nonFungibleContract 0xdd1b7a0f → address
offerCut 0xbea12e00 → uint256
tokenIdToOffer 0xd531fe2b → uint64, address, uint16, uint128, uint128
totalCFOEarnings 0x5ac165af → uint256
totalLostAndFoundBalance 0x7ae86385 → uint256
unsuccessfulFee 0xc65b98e7 → uint256

Write Contract 18 functions

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

batchRemoveExpired 0x1c091a5f
uint256[] _tokenIds
bidderWithdrawFunds 0xe1302e2b
uint256 _tokenId
cancelOffer 0xef706adf
uint256 _tokenId
createOffer 0xa269993d
uint256 _tokenId
freeze 0x62a5af3b
No parameters
fulfillOffer 0x4e8dc003
uint256 _tokenId
uint128 _minOfferPrice
setCEO 0x27d7874c
address _newCEO
setCFO 0x4e0a3379
address _newCFO
setCOO 0x2ba73c15
address _newCOO
setGlobalDuration 0x8c0ceea5
uint256 _newDuration
setLostAndFound 0x1f747458
address _newLostAndFound
setMinimumPriceIncrement 0x3a3d55a0
uint256 _newMinimumPriceIncrement
setMinimumTotalValue 0x4ae772a4
uint256 _newMinTotal
setOfferCut 0x2d5796ac
uint256 _newOfferCut
setUnsuccessfulFee 0xbff2273e
uint256 _newUnsuccessfulFee
updateOffer 0x56d282fc
uint256 _tokenId
withdrawTotalCFOEarnings 0x930add29
No parameters
withdrawTotalLostAndFoundBalance 0x45394dad
No parameters

Token Balances (1)

View Transfers →
WETH 0

Recent Transactions

No transactions found for this address