Address Contract Partially Verified
Address
0x48193776062991c2fE024D9c99C35576A51DaDe0
Balance
0 ETH
Nonce
1
Code Size
19113 bytes
Creator
0xa38fd21d...aBe4 at tx 0x9679de15...430522
Indexed Transactions
0
Contract Bytecode
19113 bytes
0x608060405234801561001057600080fd5b50600436106102c85760003560e01c8063860420e31161017b578063c0857ba0116100d8578063e72cb05b1161008c578063f2fde38b11610071578063f2fde38b146107b1578063f3fa98a7146107c4578063f953cec7146107d757600080fd5b8063e72cb05b14610765578063f23a6e611461077857600080fd5b8063c7059488116100bd578063c70594881461071d578063de9b771f14610725578063dfcedeee1461074557600080fd5b8063c0857ba0146106ea578063c5bf09641461070a57600080fd5b8063abbe08181161012f578063bbb781cc11610114578063bbb781cc1461068f578063bc197c811461069c578063bd88119f146106d757600080fd5b8063abbe081814610674578063aea4e49e1461067c57600080fd5b80638da5cb5b116101605780638da5cb5b14610623578063972c492814610641578063aaa687771461066157600080fd5b8063860420e3146105fd578063891bc3d21461061057600080fd5b806350acac5511610229578063652972d4116101dd57806370a08231116101c257806370a08231146105cf578063715018a6146105e25780638282052b146105ea57600080fd5b8063652972d41461059c5780636f251724146105bc57600080fd5b80635d939ed01161020e5780635d939ed014610542578063607f2d421461055557806363c28db11461057857600080fd5b806350acac551461050f578063584a22061461052257600080fd5b80631820743d116102805780632176dfce116102655780632176dfce146104d65780633b0d79fa146104e95780634f9ce271146104fc57600080fd5b80631820743d1461042f5780631959a0021461044257600080fd5b80630f147692116102b15780630f1476921461036c578063150b7a02146103b157806315b31bbb1461041a57600080fd5b806301ffc9a7146102cd5780630e387de614610337575b600080fd5b6103226102db366004613e0b565b7fffffffff00000000000000000000000000000000000000000000000000000000167f7f5828d0000000000000000000000000000000000000000000000000000000001490565b60405190151581526020015b60405180910390f35b61035e7f8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b03681565b60405190815260200161032e565b60055461038c9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161032e565b6103e96103bf366004613eb8565b7f150b7a020000000000000000000000000000000000000000000000000000000095945050505050565b6040517fffffffff00000000000000000000000000000000000000000000000000000000909116815260200161032e565b61042d610428366004613f40565b6107ea565b005b61042d61043d366004613f7b565b61083f565b61049e610450366004613f96565b6009602052600090815260409020600181015460028201546003909201549091906fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041684565b6040805194855260208501939093526fffffffffffffffffffffffffffffffff9182169284019290925216606082015260800161032e565b61042d6104e43660046140b1565b610a63565b61042d6104f73660046140e6565b610e28565b61042d61050a366004614119565b610e6e565b61042d61051d366004614119565b611031565b60065461038c9073ffffffffffffffffffffffffffffffffffffffff1681565b61042d610550366004613f7b565b611359565b610322610563366004614119565b60036020526000908152604090205460ff1681565b61058b610586366004613f96565b611535565b60405161032e959493929190614132565b60085461038c9073ffffffffffffffffffffffffffffffffffffffff1681565b61042d6105ca3660046141a3565b611642565b61035e6105dd366004613f96565b61167a565b61042d611752565b61042d6105f83660046140b1565b6117cd565b61042d61060b366004613f7b565b611a2c565b61042d61061e366004614213565b611c4f565b60045473ffffffffffffffffffffffffffffffffffffffff1661038c565b60025461038c9073ffffffffffffffffffffffffffffffffffffffff1681565b61042d61066f366004614293565b611c85565b61042d611d1e565b61042d61068a366004613f96565b611ed2565b600a546103229060ff1681565b6103e96106aa366004614334565b7fbc197c810000000000000000000000000000000000000000000000000000000098975050505050505050565b61042d6106e53660046140e6565b611fa5565b60015461038c9073ffffffffffffffffffffffffffffffffffffffff1681565b61042d610718366004614119565b611fe7565b61042d61211c565b60005461038c9073ffffffffffffffffffffffffffffffffffffffff1681565b60075461038c9073ffffffffffffffffffffffffffffffffffffffff1681565b61042d610773366004613f7b565b6122b8565b6103e96107863660046143f3565b7ff23a6e61000000000000000000000000000000000000000000000000000000009695505050505050565b61042d6107bf366004613f96565b61247d565b61042d6107d2366004614119565b612515565b61042d6107e53660046144b5565b6126b7565b60045473ffffffffffffffffffffffffffffffffffffffff16331461080e57600080fd5b600a80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055565b600a5460ff16156108975760405162461bcd60e51b815260206004820152601c60248201527f5374616b696e672069732063757272656e746c79207061757365642e0000000060448201526064015b60405180910390fd5b806fffffffffffffffffffffffffffffffff166000036108f95760405162461bcd60e51b815260206004820152601960248201527f5374616b696e672030206973206e6f7420616c6c6f7765642e00000000000000604482015260640161088e565b336000908152600960205260408120600301805483929061092d9084906fffffffffffffffffffffffffffffffff16614564565b82546101009290920a6fffffffffffffffffffffffffffffffff8181021990931691831602179091556007546040517ff242432a00000000000000000000000000000000000000000000000000000000815233600482015230602482015260026044820152918416606483015260a06084830152600060a483015273ffffffffffffffffffffffffffffffffffffffff16915063f242432a9060c401600060405180830381600087803b1580156109e357600080fd5b505af11580156109f7573d6000803e3d6000fd5b50610a609250339150600290505b6040805173ffffffffffffffffffffffffffffffffffffffff90931660208401528201526fffffffffffffffffffffffffffffffff831660608201526001608082015260a0015b6040516020818303038152906040526126c2565b50565b600a5460ff1615610ab65760405162461bcd60e51b815260206004820152601c60248201527f5374616b696e672069732063757272656e746c79207061757365642e00000000604482015260640161088e565b336000908152600960205260408120815b8351811015610d79576000848281518110610ae457610ae46145c7565b60200260200101519050610b4a83600001805480602002602001604051908101604052809291908181526020018280548015610b3f57602002820191906000526020600020905b815481526020019060010190808311610b2b575b50505050508261274c565b610b965760405162461bcd60e51b815260206004820152601a60248201527f596f7520646f206e6f74206f776e2074686973206c6c616d612e000000000000604482015260640161088e565b6101f4811015610bac57610ba9846145f6565b93505b6005546040517f23b872dd0000000000000000000000000000000000000000000000000000000081523060048201523360248201526044810183905273ffffffffffffffffffffffffffffffffffffffff909116906323b872dd90606401600060405180830381600087803b158015610c2457600080fd5b505af1158015610c38573d6000803e3d6000fd5b5050845460408051602080840282018101909252828152600094509250869190830182828015610c8757602002820191906000526020600020905b815481526020019060010190808311610c73575b505050505090506000805b8251811015610cd15783838281518110610cae57610cae6145c7565b602002602001015103610cbf578091505b80610cc9816145f6565b915050610c92565b5082828281518110610ce557610ce56145c7565b602002602001015103610d635784548290610d029060019061462e565b81518110610d1257610d126145c7565b6020026020010151856000018281548110610d2f57610d2f6145c7565b6000918252602090912001558454859080610d4c57610d4c614645565b600190038181906000526020600020016000905590555b5050508080610d71906145f6565b915050610ac7565b508115610dc057610dc03360016040805173ffffffffffffffffffffffffffffffffffffffff9093166020840152820152606081018490526000608082015260a001610a4c565b6000828451610dcf919061462e565b1115610e2357610e23336000848651610de8919061462e565b6040805173ffffffffffffffffffffffffffffffffffffffff909416602085015283019190915260608201526000608082015260a001610a4c565b505050565b6fffffffffffffffffffffffffffffffff821615610e4957610e498261083f565b6fffffffffffffffffffffffffffffffff811615610e6a57610e6a81611359565b5050565b600a5460ff1615610ec15760405162461bcd60e51b815260206004820152601c60248201527f5374616b696e672069732063757272656e746c79207061757365642e00000000604482015260640161088e565b3360009081526009602052604090206002015415610f475760405162461bcd60e51b815260206004820152602560248201527f596f7520616c726561647920686176652061206c6c616d61647261777320737460448201527f616b65642e000000000000000000000000000000000000000000000000000000606482015260840161088e565b336000818152600960205260409081902060020183905560065490517f23b872dd00000000000000000000000000000000000000000000000000000000815260048101929092523060248301526044820183905273ffffffffffffffffffffffffffffffffffffffff16906323b872dd90606401600060405180830381600087803b158015610fd557600080fd5b505af1158015610fe9573d6000803e3d6000fd5b50610a609250339150600590505b6040805173ffffffffffffffffffffffffffffffffffffffff9093166020840152820152600160608201819052608082015260a001610a4c565b600a5460ff16156110845760405162461bcd60e51b815260206004820152601c60248201527f5374616b696e672069732063757272656e746c79207061757365642e00000000604482015260640161088e565b3360009081526009602090815260409182902080548351818402810184019094528084526101f485109391926110f1929184918301828280156110e657602002820191906000526020600020905b8154815260200190600101908083116110d2575b50505050508461274c565b61113d5760405162461bcd60e51b815260206004820152601a60248201527f596f7520646f206e6f74206f776e2074686973206c6c616d612e000000000000604482015260640161088e565b6005546040517f23b872dd0000000000000000000000000000000000000000000000000000000081523060048201523360248201526044810185905273ffffffffffffffffffffffffffffffffffffffff909116906323b872dd90606401600060405180830381600087803b1580156111b557600080fd5b505af11580156111c9573d6000803e3d6000fd5b505082546040805160208084028201810190925282815260009450925084919083018282801561121857602002820191906000526020600020905b815481526020019060010190808311611204575b505050505090506000805b8251811015611262578583828151811061123f5761123f6145c7565b602002602001015103611250578091505b8061125a816145f6565b915050611223565b5084828281518110611276576112766145c7565b602002602001015103611352576112e13385611293576000611296565b60015b60058111156112a7576112a7614598565b6040805173ffffffffffffffffffffffffffffffffffffffff9093166020840152820152600160608201526000608082015260a001610a4c565b825482906112f19060019061462e565b81518110611301576113016145c7565b602002602001015183600001828154811061131e5761131e6145c7565b600091825260209091200155825483908061133b5761133b614645565b600190038181906000526020600020016000905590555b5050505050565b600a5460ff16156113ac5760405162461bcd60e51b815260206004820152601c60248201527f5374616b696e672069732063757272656e746c79207061757365642e00000000604482015260640161088e565b806fffffffffffffffffffffffffffffffff1660000361140e5760405162461bcd60e51b815260206004820152601960248201527f5374616b696e672030206973206e6f7420616c6c6f7765642e00000000000000604482015260640161088e565b336000908152600960205260409020600301805482919060109061145990849070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16614564565b82546101009290920a6fffffffffffffffffffffffffffffffff8181021990931691831602179091556007546040517ff242432a00000000000000000000000000000000000000000000000000000000815233600482015230602482015260016044820152918416606483015260a06084830152600060a483015273ffffffffffffffffffffffffffffffffffffffff16915063f242432a9060c401600060405180830381600087803b15801561150f57600080fd5b505af1158015611523573d6000803e3d6000fd5b50610a60925033915060039050610a05565b73ffffffffffffffffffffffffffffffffffffffff811660009081526009602090815260408083208151815460c09481028201850190935260a081018381526060959485948594859485949093919284928491908401828280156115b857602002820191906000526020600020905b8154815260200190600101908083116115a4575b5050509183525050600182015460208083019190915260028301546040808401919091526003909301546fffffffffffffffffffffffffffffffff8082166060808601919091527001000000000000000000000000000000009092041660809384015284519185015193850151908501519490920151909b929a5090985091965090945092505050565b84511561165257611652856117cd565b61165c8484610e28565b811561166b5761166b82612515565b80156113525761135281610e6e565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600960209081526040808320805482518185028101850190935280835284938301828280156116e457602002820191906000526020600020905b8154815260200190600101908083116116d0575b5050505050905080516000036116fd5750600092915050565b60005b8151811015611748576101f482828151811061171e5761171e6145c7565b60200260200101511015611736575060019392505050565b80611740816145f6565b915050611700565b5060029392505050565b60045473ffffffffffffffffffffffffffffffffffffffff1633146117a3576040517f30cd747100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b600a5460ff16156118205760405162461bcd60e51b815260206004820152601c60248201527f5374616b696e672069732063757272656e746c79207061757365642e00000000604482015260640161088e565b336000908152600960205260408120815b8351811015611982576101f484828151811061184f5761184f6145c7565b6020026020010151101561186957611866836145f6565b92505b8160000184828151811061187f5761187f6145c7565b60209081029190910181015182546001810184556000938452919092200155600554845173ffffffffffffffffffffffffffffffffffffffff909116906323b872dd90339030908890869081106118d8576118d86145c7565b60209081029190910101516040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16815273ffffffffffffffffffffffffffffffffffffffff93841660048201529290911660248301526044820152606401600060405180830381600087803b15801561195757600080fd5b505af115801561196b573d6000803e3d6000fd5b50505050808061197a906145f6565b915050611831565b5081156119c9576119c93360016040805173ffffffffffffffffffffffffffffffffffffffff9093166020840152820152606081018490526001608082015260a001610a4c565b60008284516119d8919061462e565b1115610e2357610e233360008486516119f1919061462e565b6040805173ffffffffffffffffffffffffffffffffffffffff909416602085015283019190915260608201526001608082015260a001610a4c565b600a5460ff1615611a7f5760405162461bcd60e51b815260206004820152601c60248201527f5374616b696e672069732063757272656e746c79207061757365642e00000000604482015260640161088e565b806fffffffffffffffffffffffffffffffff16600003611ae15760405162461bcd60e51b815260206004820152601b60248201527f556e7374616b696e672030206973206e6f7420616c6c6f7765642e0000000000604482015260640161088e565b3360009081526009602052604090206003018054829190601090611b2c90849070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16614674565b82546101009290920a6fffffffffffffffffffffffffffffffff8181021990931691831602179091556007546040517ff242432a00000000000000000000000000000000000000000000000000000000815230600482015233602482015260016044820152918416606483015260a06084830152600060a483015273ffffffffffffffffffffffffffffffffffffffff16915063f242432a9060c401600060405180830381600087803b158015611be257600080fd5b505af1158015611bf6573d6000803e3d6000fd5b50610a609250339150600390505b6040805173ffffffffffffffffffffffffffffffffffffffff90931660208401528201526fffffffffffffffffffffffffffffffff831660608201526000608082015260a001610a4c565b845115611c5f57611c5f85610a63565b611c698484611fa5565b8115611c7757611c77611d1e565b80156113525761135261211c565b60045473ffffffffffffffffffffffffffffffffffffffff163314611ca957600080fd5b6005805473ffffffffffffffffffffffffffffffffffffffff9586167fffffffffffffffffffffffff0000000000000000000000000000000000000000918216179091556006805494861694821694909417909355600780549285169284169290921790915560088054919093169116179055565b600a5460ff1615611d715760405162461bcd60e51b815260206004820152601c60248201527f5374616b696e672069732063757272656e746c79207061757365642e00000000604482015260640161088e565b336000908152600960205260408120600101549003611df85760405162461bcd60e51b815260206004820152602760248201527f596f7520646f206e6f7420686176652061207069786c65742063616e7661732060448201527f7374616b65642e00000000000000000000000000000000000000000000000000606482015260840161088e565b6008543360008181526009602052604080822060019081015491517ff242432a00000000000000000000000000000000000000000000000000000000815230600482015260248101949094526044840191909152606483015260a0608483015260a482015273ffffffffffffffffffffffffffffffffffffffff9091169063f242432a9060c401600060405180830381600087803b158015611e9957600080fd5b505af1158015611ead573d6000803e3d6000fd5b505033600081815260096020526040812060010155611ed09250905060046112a7565b565b60025473ffffffffffffffffffffffffffffffffffffffff1615611f5e5760405162461bcd60e51b815260206004820152602a60248201527f467842617365526f6f7454756e6e656c3a204348494c445f54554e4e454c5f4160448201527f4c52454144595f53455400000000000000000000000000000000000000000000606482015260840161088e565b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6fffffffffffffffffffffffffffffffff821615611fc657611fc6826122b8565b6fffffffffffffffffffffffffffffffff811615610e6a57610e6a81611a2c565b600a5460ff161561203a5760405162461bcd60e51b815260206004820152601c60248201527f5374616b696e672069732063757272656e746c79207061757365642e00000000604482015260640161088e565b33600081815260096020908152604080832080546001810182558185529290932090910184905560055490517f23b872dd0000000000000000000000000000000000000000000000000000000081526004810193909352306024840152604483018490526101f484109273ffffffffffffffffffffffffffffffffffffffff909116906323b872dd90606401600060405180830381600087803b1580156120e057600080fd5b505af11580156120f4573d6000803e3d6000fd5b50505050610e23338361210857600061210b565b60015b6005811115610ff757610ff7614598565b600a5460ff161561216f5760405162461bcd60e51b815260206004820152601c60248201527f5374616b696e672069732063757272656e746c79207061757365642e00000000604482015260640161088e565b3360009081526009602052604081206002015490036121f55760405162461bcd60e51b8152602060048201526024808201527f596f7520646f206e6f7420686176652061206c6c616d6164726177732073746160448201527f6b65642e00000000000000000000000000000000000000000000000000000000606482015260840161088e565b60065433600081815260096020526040908190206002015490517f23b872dd0000000000000000000000000000000000000000000000000000000081523060048201526024810192909252604482015273ffffffffffffffffffffffffffffffffffffffff909116906323b872dd90606401600060405180830381600087803b15801561228157600080fd5b505af1158015612295573d6000803e3d6000fd5b505033600081815260096020526040812060020155611ed09250905060056112a7565b600a5460ff161561230b5760405162461bcd60e51b815260206004820152601c60248201527f5374616b696e672069732063757272656e746c79207061757365642e00000000604482015260640161088e565b806fffffffffffffffffffffffffffffffff1660000361236d5760405162461bcd60e51b815260206004820152601b60248201527f556e7374616b696e672030206973206e6f7420616c6c6f7765642e0000000000604482015260640161088e565b33600090815260096020526040812060030180548392906123a19084906fffffffffffffffffffffffffffffffff16614674565b82546101009290920a6fffffffffffffffffffffffffffffffff8181021990931691831602179091556007546040517ff242432a00000000000000000000000000000000000000000000000000000000815230600482015233602482015260026044820152918416606483015260a06084830152600060a483015273ffffffffffffffffffffffffffffffffffffffff16915063f242432a9060c401600060405180830381600087803b15801561245757600080fd5b505af115801561246b573d6000803e3d6000fd5b50610a60925033915060029050611c04565b60045473ffffffffffffffffffffffffffffffffffffffff1633146124ce576040517f30cd747100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b600a5460ff16156125685760405162461bcd60e51b815260206004820152601c60248201527f5374616b696e672069732063757272656e746c79207061757365642e00000000604482015260640161088e565b33600090815260096020526040902060010154156125ee5760405162461bcd60e51b815260206004820152602860248201527f596f7520616c726561647920686176652061207069786c65742063616e76617360448201527f207374616b65642e000000000000000000000000000000000000000000000000606482015260840161088e565b33600081815260096020526040808220600190810185905560085491517ff242432a000000000000000000000000000000000000000000000000000000008152600481019490945230602485015260448401859052606484015260a0608484015260a483019190915273ffffffffffffffffffffffffffffffffffffffff169063f242432a9060c401600060405180830381600087803b15801561269157600080fd5b505af11580156126a5573d6000803e3d6000fd5b50610a60925033915060049050610ff7565b6000610e23826127a1565b6000546002546040517fb472047700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9283169263b47204779261271e9291169085906004016146d5565b600060405180830381600087803b15801561273857600080fd5b505af1158015611352573d6000803e3d6000fd5b6000805b8351811015612795578284828151811061276c5761276c6145c7565b60200260200101510361278357600191505061279b565b8061278d816145f6565b915050612750565b50600090505b92915050565b606060006127ae83612b1b565b905060006127bb82612b7a565b905060006127c883612ba3565b90506000816127d684612bcc565b6127df86612dba565b6040516020016127f193929190614743565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291815281516020928301206000818152600390935291205490915060ff16156128ac5760405162461bcd60e51b8152602060048201526024808201527f4678526f6f7454756e6e656c3a20455849545f414c52454144595f50524f434560448201527f5353454400000000000000000000000000000000000000000000000000000000606482015260840161088e565b600081815260036020526040812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556128ec85612dd6565b905060006128f982612f20565b905061290481612fb0565b60025473ffffffffffffffffffffffffffffffffffffffff9081169116146129945760405162461bcd60e51b815260206004820152602560248201527f4678526f6f7454756e6e656c3a20494e56414c49445f46585f4348494c445f5460448201527f554e4e454c000000000000000000000000000000000000000000000000000000606482015260840161088e565b600061299f87612fd9565b90506129bf6129af846020015190565b876129b98a612ff5565b84613011565b612a315760405162461bcd60e51b815260206004820152602360248201527f4678526f6f7454756e6e656c3a20494e56414c49445f524543454950545f505260448201527f4f4f460000000000000000000000000000000000000000000000000000000000606482015260840161088e565b612a5f85612a3e896132c4565b612a478a6132e0565b84612a518c6132fc565b612a5a8d613318565b613334565b506000612a6b83613480565b90507f8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b036612aa1612a9c8360006134bc565b6134f4565b14612aee5760405162461bcd60e51b815260206004820152601f60248201527f4678526f6f7454756e6e656c3a20494e56414c49445f5349474e415455524500604482015260640161088e565b6000612af98461356f565b806020019051810190612b0c9190614770565b9b9a5050505050505050505050565b6040805160208101909152606081526000612b65612b608460408051808201825260008082526020918201528151808301909252825182529182019181019190915290565b61358b565b60408051602081019091529081529392505050565b606061279b8260000151600881518110612b9657612b966145c7565b60200260200101516136a1565b600061279b8260000151600281518110612bbf57612bbf6145c7565b60200260200101516134f4565b6040805160208101909152600081528151606091901561279b57600080612bf460008661373e565b60f81c90506001811480612c0b57508060ff166003145b15612ccb57600185516002612c2091906147e7565b612c2a919061462e565b67ffffffffffffffff811115612c4257612c42613fb3565b6040519080825280601f01601f191660200182016040528015612c6c576020820181803683370190505b5092506000612c7c60018761373e565b90508084600081518110612c9257612c926145c7565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506001925050612d2f565b600285516002612cdb91906147e7565b612ce5919061462e565b67ffffffffffffffff811115612cfd57612cfd613fb3565b6040519080825280601f01601f191660200182016040528015612d27576020820181803683370190505b509250600091505b60ff82165b8351811015612db157612d5e612d4d60ff85168361462e565b612d58906002614824565b8761373e565b848281518110612d7057612d706145c7565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535080612da9816145f6565b915050612d34565b50505092915050565b600061279b8260000151600981518110612bbf57612bbf6145c7565b612dfa60405180606001604052806060815260200160608152602001600081525090565b612e148260000151600681518110612b9657612b966145c7565b602082810182905260408051808201825260008082529083015280518082019091528251815291810190820152612e4a816137bf565b15612e5f57612e588161358b565b8252612f0c565b60208201518051600090612e759060019061462e565b67ffffffffffffffff811115612e8d57612e8d613fb3565b6040519080825280601f01601f191660200182016040528015612eb7576020820181803683370190505b509050600080836021019150826020019050612ed5828285516137fa565b604080518082018252600080825260209182015281518083019092528451825280850190820152612f059061358b565b8652505050505b612f1583612dba565b604083015250919050565b604080516080810182526000918101828152606080830193909352815260208101919091526000612f6e8360000151600381518110612f6157612f616145c7565b602002602001015161358b565b836040015181518110612f8357612f836145c7565b602002602001015190506040518060400160405280828152602001612fa78361358b565b90529392505050565b600061279b8260200151600081518110612fcc57612fcc6145c7565b6020026020010151613878565b600061279b8260000151600581518110612bbf57612bbf6145c7565b606061279b8260000151600781518110612b9657612b966145c7565b6000806130458460408051808201825260008082526020918201528151808301909252825182529182019181019190915290565b905060006130528261358b565b9050606080856000806130648b612bcc565b9050805160000361307f5760009750505050505050506132bc565b60005b86518110156132b35781518311156130a5576000985050505050505050506132bc565b6130c78782815181106130ba576130ba6145c7565b6020026020010151613892565b9550858051906020012084146130e8576000985050505050505050506132bc565b6130fd878281518110612f6157612f616145c7565b945084516011036131cf578151830361315c578c8051906020012061312e86601081518110612b9657612b966145c7565b805190602001200361314b576001985050505050505050506132bc565b6000985050505050505050506132bc565b6000828481518110613170576131706145c7565b016020015160f81c9050601081111561319557600099505050505050505050506132bc565b6131ba868260ff16815181106131ad576131ad6145c7565b6020026020010151613911565b94506131c7600185614824565b9350506132a1565b845160020361314b5760006131fa6131f387600081518110612b9657612b966145c7565b848661393f565b83519091506132098286614824565b0361325c578d8051906020012061322c87600181518110612b9657612b966145c7565b805190602001200361324a57600199505050505050505050506132bc565b600099505050505050505050506132bc565b8060000361327657600099505050505050505050506132bc565b6132808185614824565b9350613298866001815181106131ad576131ad6145c7565b94506132a19050565b806132ab816145f6565b915050613082565b50505050505050505b949350505050565b600061279b8260000151600381518110612bbf57612bbf6145c7565b600061279b8260000151600481518110612bbf57612bbf6145c7565b600061279b8260000151600081518110612bbf57612bbf6145c7565b606061279b8260000151600181518110612b9657612b966145c7565b6001546040517f41539d4a0000000000000000000000000000000000000000000000000000000081526004810184905260009182918291829173ffffffffffffffffffffffffffffffffffffffff909116906341539d4a9060240160a060405180830381865afa1580156133ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133d0919061483c565b5093505092509250613427828b6133e7919061462e565b6040805160208082018f90528183018e9052606082018d905260808083018d90528351808403909101815260a09092019092528051910120908588613a77565b6134735760405162461bcd60e51b815260206004820152601c60248201527f4678526f6f7454756e6e656c3a20494e56414c49445f48454144455200000000604482015260640161088e565b9998505050505050505050565b60408051602081019091526060815260405180602001604052806134b48460200151600181518110612f6157612f616145c7565b905292915050565b604080518082019091526000808252602082015282518051839081106134e4576134e46145c7565b6020026020010151905092915050565b80516000901580159061350957508151602110155b61351257600080fd5b60006135218360200151613bf0565b90506000818460000151613535919061462e565b905060008083866020015161354a9190614824565b905080519150602083101561356657826020036101000a820491505b50949350505050565b606061279b8260200151600281518110612b9657612b966145c7565b6060613596826137bf565b61359f57600080fd5b60006135aa83613c72565b905060008167ffffffffffffffff8111156135c7576135c7613fb3565b60405190808252806020026020018201604052801561360c57816020015b60408051808201909152600080825260208201528152602001906001900390816135e55790505b509050600061361e8560200151613bf0565b856020015161362d9190614824565b90506000805b848110156136965761364483613cf7565b915060405180604001604052808381526020018481525084828151811061366d5761366d6145c7565b60209081029190910101526136828284614824565b92508061368e816145f6565b915050613633565b509195945050505050565b80516060906136af57600080fd5b60006136be8360200151613bf0565b905060008184600001516136d2919061462e565b905060008167ffffffffffffffff8111156136ef576136ef613fb3565b6040519080825280601f01601f191660200182016040528015613719576020820181803683370190505b50905060008160200190506135668487602001516137379190614824565b8285613db9565b600061374b6002846148b8565b156137855760108261375e6002866148cc565b8151811061376e5761376e6145c7565b0160200151613780919060f81c6148e0565b6137b5565b6010826137936002866148cc565b815181106137a3576137a36145c7565b01602001516137b5919060f81c614902565b60f81b9392505050565b805160009081036137d257506000919050565b6020820151805160001a9060c08210156137f0575060009392505050565b5060019392505050565b8060000361380757505050565b6020811061383f578251825261381e602084614824565b925061382b602083614824565b915061383860208261462e565b9050613807565b6000600161384e83602061462e565b61385a90610100614a44565b613864919061462e565b935183518516941916939093179091525050565b805160009060151461388957600080fd5b61279b826134f4565b60606000826000015167ffffffffffffffff8111156138b3576138b3613fb3565b6040519080825280601f01601f1916602001820160405280156138dd576020820181803683370190505b50905080516000036138ef5792915050565b600081602001905061390a8460200151828660000151613db9565b5092915050565b805160009060211461392257600080fd5b600080836020015160016139369190614824565b51949350505050565b6000808061394c86612bcc565b90506000815167ffffffffffffffff81111561396a5761396a613fb3565b6040519080825280601f01601f191660200182016040528015613994576020820181803683370190505b509050845b82516139a59087614824565b811015613a485760008782815181106139c0576139c06145c7565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016905080836139f5898561462e565b81518110613a0557613a056145c7565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350508080613a40906145f6565b915050613999565b508080519060200120828051906020012003613a675781519250613a6c565b600092505b509095945050505050565b600060208251613a8791906148b8565b15613ad45760405162461bcd60e51b815260206004820152601460248201527f496e76616c69642070726f6f66206c656e677468000000000000000000000000604482015260640161088e565b600060208351613ae491906148cc565b9050613af1816002614a44565b8510613b3f5760405162461bcd60e51b815260206004820152601560248201527f4c65616620696e64657820697320746f6f206269670000000000000000000000604482015260640161088e565b60008660205b85518111613be257858101519250613b5e6002896148b8565b600003613b96576040805160208101849052908101849052606001604051602081830303815290604052805190602001209150613bc3565b60408051602081018590529081018390526060016040516020818303038152906040528051906020012091505b613bce6002896148cc565b9750613bdb602082614824565b9050613b45565b509094149695505050505050565b8051600090811a6080811015613c095750600092915050565b60b8811080613c24575060c08110801590613c24575060f881105b15613c325750600192915050565b60c0811015613c6657613c47600160b8614a50565b613c549060ff168261462e565b613c5f906001614824565b9392505050565b613c47600160f8614a50565b80516000908103613c8557506000919050565b600080613c958460200151613bf0565b8460200151613ca49190614824565b9050600084600001518560200151613cbc9190614824565b90505b80821015613cee57613cd082613cf7565b613cda9083614824565b915082613ce6816145f6565b935050613cbf565b50909392505050565b80516000908190811a6080811015613d12576001915061390a565b60b8811015613d3857613d2660808261462e565b613d31906001614824565b915061390a565b60c0811015613d655760b78103600185019450806020036101000a8551046001820181019350505061390a565b60f8811015613d7957613d2660c08261462e565b60019390930151602084900360f7016101000a90049092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0a0192915050565b80600003613dc657505050565b60208110613dfe5782518252613ddd602084614824565b9250613dea602083614824565b9150613df760208261462e565b9050613dc6565b8060000361383f57505050565b600060208284031215613e1d57600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114613c5f57600080fd5b73ffffffffffffffffffffffffffffffffffffffff81168114610a6057600080fd5b60008083601f840112613e8157600080fd5b50813567ffffffffffffffff811115613e9957600080fd5b602083019150836020828501011115613eb157600080fd5b9250929050565b600080600080600060808688031215613ed057600080fd5b8535613edb81613e4d565b94506020860135613eeb81613e4d565b935060408601359250606086013567ffffffffffffffff811115613f0e57600080fd5b613f1a88828901613e6f565b969995985093965092949392505050565b80358015158114613f3b57600080fd5b919050565b600060208284031215613f5257600080fd5b613c5f82613f2b565b80356fffffffffffffffffffffffffffffffff81168114613f3b57600080fd5b600060208284031215613f8d57600080fd5b613c5f82613f5b565b600060208284031215613fa857600080fd5b8135613c5f81613e4d565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561402957614029613fb3565b604052919050565b600082601f83011261404257600080fd5b8135602067ffffffffffffffff82111561405e5761405e613fb3565b8160051b61406d828201613fe2565b928352848101820192828101908785111561408757600080fd5b83870192505b848310156140a65782358252918301919083019061408d565b979650505050505050565b6000602082840312156140c357600080fd5b813567ffffffffffffffff8111156140da57600080fd5b6132bc84828501614031565b600080604083850312156140f957600080fd5b61410283613f5b565b915061411060208401613f5b565b90509250929050565b60006020828403121561412b57600080fd5b5035919050565b60a0808252865190820181905260009060209060c0840190828a01845b8281101561416b5781518452928401929084019060010161414f565b505050908301969096525060408101939093526fffffffffffffffffffffffffffffffff918216606084015216608090910152919050565b600080600080600060a086880312156141bb57600080fd5b853567ffffffffffffffff8111156141d257600080fd5b6141de88828901614031565b9550506141ed60208701613f5b565b93506141fb60408701613f5b565b94979396509394606081013594506080013592915050565b600080600080600060a0868803121561422b57600080fd5b853567ffffffffffffffff81111561424257600080fd5b61424e88828901614031565b95505061425d60208701613f5b565b935061426b60408701613f5b565b925061427960608701613f2b565b915061428760808701613f2b565b90509295509295909350565b600080600080608085870312156142a957600080fd5b84356142b481613e4d565b935060208501356142c481613e4d565b925060408501356142d481613e4d565b915060608501356142e481613e4d565b939692955090935050565b60008083601f84011261430157600080fd5b50813567ffffffffffffffff81111561431957600080fd5b6020830191508360208260051b8501011115613eb157600080fd5b60008060008060008060008060a0898b03121561435057600080fd5b883561435b81613e4d565b9750602089013561436b81613e4d565b9650604089013567ffffffffffffffff8082111561438857600080fd5b6143948c838d016142ef565b909850965060608b01359150808211156143ad57600080fd5b6143b98c838d016142ef565b909650945060808b01359150808211156143d257600080fd5b506143df8b828c01613e6f565b999c989b5096995094979396929594505050565b60008060008060008060a0878903121561440c57600080fd5b863561441781613e4d565b9550602087013561442781613e4d565b94506040870135935060608701359250608087013567ffffffffffffffff81111561445157600080fd5b61445d89828a01613e6f565b979a9699509497509295939492505050565b600067ffffffffffffffff82111561448957614489613fb3565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b6000602082840312156144c757600080fd5b813567ffffffffffffffff8111156144de57600080fd5b8201601f810184136144ef57600080fd5b80356145026144fd8261446f565b613fe2565b81815285602083850101111561451757600080fd5b81602084016020830137600091810160200191909152949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006fffffffffffffffffffffffffffffffff80831681851680830382111561458f5761458f614535565b01949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361462757614627614535565b5060010190565b60008282101561464057614640614535565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b60006fffffffffffffffffffffffffffffffff8381169083168181101561469d5761469d614535565b039392505050565b60005b838110156146c05781810151838201526020016146a8565b838111156146cf576000848401525b50505050565b73ffffffffffffffffffffffffffffffffffffffff8316815260406020820152600082518060408401526147108160608501602087016146a5565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016919091016060019392505050565b8381526000835161475b8160208501602088016146a5565b60209201918201929092526040019392505050565b60006020828403121561478257600080fd5b815167ffffffffffffffff81111561479957600080fd5b8201601f810184136147aa57600080fd5b80516147b86144fd8261446f565b8181528560208385010111156147cd57600080fd5b6147de8260208301602086016146a5565b95945050505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561481f5761481f614535565b500290565b6000821982111561483757614837614535565b500190565b600080600080600060a0868803121561485457600080fd5b85519450602086015193506040860151925060608601519150608086015161487b81613e4d565b809150509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826148c7576148c7614889565b500690565b6000826148db576148db614889565b500490565b600060ff8316806148f3576148f3614889565b8060ff84160691505092915050565b600060ff83168061491557614915614889565b8060ff84160491505092915050565b600181815b8085111561497d57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561496357614963614535565b8085161561497057918102915b93841c9390800290614929565b509250929050565b6000826149945750600161279b565b816149a15750600061279b565b81600181146149b757600281146149c1576149dd565b600191505061279b565b60ff8411156149d2576149d2614535565b50506001821b61279b565b5060208310610133831016604e8410600b8410161715614a00575081810a61279b565b614a0a8383614924565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115614a3c57614a3c614535565b029392505050565b6000613c5f8383614985565b600060ff821660ff841680821015614a6a57614a6a614535565b9003939250505056fea26469706673582212209217d194649c8ac2c51aa00479d69ea51ac54bd1aa40db13a821cd0d44e3efe164736f6c634300080d0033
Verified Source Code Partial Match
Compiler: v0.8.13+commit.abaa5c0e
EVM: london
Optimization: Yes (9999 runs)
ERC20.sol 195 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*///////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*///////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*///////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*///////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
bytes32 public constant PERMIT_TYPEHASH =
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*///////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*///////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*///////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
bytes32 digest = keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
)
);
address recoveredAddress = ecrecover(digest, v, r, s);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*///////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
ERC721.sol 216 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)
/// @dev Note that balanceOf does not revert if passed the zero address, in defiance of the ERC.
abstract contract ERC721 {
/*///////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 indexed id);
event Approval(address indexed owner, address indexed spender, uint256 indexed id);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/*///////////////////////////////////////////////////////////////
METADATA STORAGE/LOGIC
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
function tokenURI(uint256 id) public view virtual returns (string memory);
/*///////////////////////////////////////////////////////////////
ERC721 STORAGE
//////////////////////////////////////////////////////////////*/
mapping(address => uint256) public balanceOf;
mapping(uint256 => address) public ownerOf;
mapping(uint256 => address) public getApproved;
mapping(address => mapping(address => bool)) public isApprovedForAll;
/*///////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
}
/*///////////////////////////////////////////////////////////////
ERC721 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 id) public virtual {
address owner = ownerOf[id];
require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
getApproved[id] = spender;
emit Approval(owner, spender, id);
}
function setApprovalForAll(address operator, bool approved) public virtual {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function transferFrom(
address from,
address to,
uint256 id
) public virtual {
require(from == ownerOf[id], "WRONG_FROM");
require(to != address(0), "INVALID_RECIPIENT");
require(
msg.sender == from || msg.sender == getApproved[id] || isApprovedForAll[from][msg.sender],
"NOT_AUTHORIZED"
);
// Underflow of the sender's balance is impossible because we check for
// ownership above and the recipient's balance can't realistically overflow.
unchecked {
balanceOf[from]--;
balanceOf[to]++;
}
ownerOf[id] = to;
delete getApproved[id];
emit Transfer(from, to, id);
}
function safeTransferFrom(
address from,
address to,
uint256 id
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function safeTransferFrom(
address from,
address to,
uint256 id,
bytes memory data
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
/*///////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/
function supportsInterface(bytes4 interfaceId) public pure virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
}
/*///////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 id) internal virtual {
require(to != address(0), "INVALID_RECIPIENT");
require(ownerOf[id] == address(0), "ALREADY_MINTED");
// Counter overflow is incredibly unrealistic.
unchecked {
balanceOf[to]++;
}
ownerOf[id] = to;
emit Transfer(address(0), to, id);
}
function _burn(uint256 id) internal virtual {
address owner = ownerOf[id];
require(ownerOf[id] != address(0), "NOT_MINTED");
// Ownership check above ensures no underflow.
unchecked {
balanceOf[owner]--;
}
delete ownerOf[id];
delete getApproved[id];
emit Transfer(owner, address(0), id);
}
/*///////////////////////////////////////////////////////////////
INTERNAL SAFE MINT LOGIC
//////////////////////////////////////////////////////////////*/
function _safeMint(address to, uint256 id) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function _safeMint(
address to,
uint256 id,
bytes memory data
) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
}
/// @notice A generic interface for a contract which properly accepts ERC721 tokens.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)
interface ERC721TokenReceiver {
function onERC721Received(
address operator,
address from,
uint256 id,
bytes calldata data
) external returns (bytes4);
}
Merkle.sol 34 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library Merkle {
function checkMembership(
bytes32 leaf,
uint256 index,
bytes32 rootHash,
bytes memory proof
) internal pure returns (bool) {
require(proof.length % 32 == 0, "Invalid proof length");
uint256 proofHeight = proof.length / 32;
// Proof of size n means, height of the tree is n+1.
// In a tree of height n+1, max #leafs possible is 2 ^ n
require(index < 2**proofHeight, "Leaf index is too big");
bytes32 proofElement;
bytes32 computedHash = leaf;
for (uint256 i = 32; i <= proof.length; i += 32) {
assembly {
proofElement := mload(add(proof, i))
}
if (index % 2 == 0) {
computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
} else {
computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
}
index = index / 2;
}
return computedHash == rootHash;
}
}
ERC1155.sol 253 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Minimalist and gas efficient standard ERC1155 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC1155.sol)
abstract contract ERC1155 {
/*///////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event TransferSingle(
address indexed operator,
address indexed from,
address indexed to,
uint256 id,
uint256 amount
);
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] amounts
);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
event URI(string value, uint256 indexed id);
/*///////////////////////////////////////////////////////////////
ERC1155 STORAGE
//////////////////////////////////////////////////////////////*/
mapping(address => mapping(uint256 => uint256)) public balanceOf;
mapping(address => mapping(address => bool)) public isApprovedForAll;
/*///////////////////////////////////////////////////////////////
METADATA LOGIC
//////////////////////////////////////////////////////////////*/
function uri(uint256 id) public view virtual returns (string memory);
/*///////////////////////////////////////////////////////////////
ERC1155 LOGIC
//////////////////////////////////////////////////////////////*/
function setApprovalForAll(address operator, bool approved) public virtual {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes memory data
) public virtual {
require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");
balanceOf[from][id] -= amount;
balanceOf[to][id] += amount;
emit TransferSingle(msg.sender, from, to, id, amount);
require(
to.code.length == 0
? to != address(0)
: ERC1155TokenReceiver(to).onERC1155Received(msg.sender, from, id, amount, data) ==
ERC1155TokenReceiver.onERC1155Received.selector,
"UNSAFE_RECIPIENT"
);
}
function safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) public virtual {
uint256 idsLength = ids.length; // Saves MLOADs.
require(idsLength == amounts.length, "LENGTH_MISMATCH");
require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");
for (uint256 i = 0; i < idsLength; ) {
uint256 id = ids[i];
uint256 amount = amounts[i];
balanceOf[from][id] -= amount;
balanceOf[to][id] += amount;
// An array can't have a total length
// larger than the max uint256 value.
unchecked {
i++;
}
}
emit TransferBatch(msg.sender, from, to, ids, amounts);
require(
to.code.length == 0
? to != address(0)
: ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, from, ids, amounts, data) ==
ERC1155TokenReceiver.onERC1155BatchReceived.selector,
"UNSAFE_RECIPIENT"
);
}
function balanceOfBatch(address[] memory owners, uint256[] memory ids)
public
view
virtual
returns (uint256[] memory balances)
{
uint256 ownersLength = owners.length; // Saves MLOADs.
require(ownersLength == ids.length, "LENGTH_MISMATCH");
balances = new uint256[](owners.length);
// Unchecked because the only math done is incrementing
// the array index counter which cannot possibly overflow.
unchecked {
for (uint256 i = 0; i < ownersLength; i++) {
balances[i] = balanceOf[owners[i]][ids[i]];
}
}
}
/*///////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/
function supportsInterface(bytes4 interfaceId) public pure virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155
interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI
}
/*///////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(
address to,
uint256 id,
uint256 amount,
bytes memory data
) internal {
balanceOf[to][id] += amount;
emit TransferSingle(msg.sender, address(0), to, id, amount);
require(
to.code.length == 0
? to != address(0)
: ERC1155TokenReceiver(to).onERC1155Received(msg.sender, address(0), id, amount, data) ==
ERC1155TokenReceiver.onERC1155Received.selector,
"UNSAFE_RECIPIENT"
);
}
function _batchMint(
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal {
uint256 idsLength = ids.length; // Saves MLOADs.
require(idsLength == amounts.length, "LENGTH_MISMATCH");
for (uint256 i = 0; i < idsLength; ) {
balanceOf[to][ids[i]] += amounts[i];
// An array can't have a total length
// larger than the max uint256 value.
unchecked {
i++;
}
}
emit TransferBatch(msg.sender, address(0), to, ids, amounts);
require(
to.code.length == 0
? to != address(0)
: ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, address(0), ids, amounts, data) ==
ERC1155TokenReceiver.onERC1155BatchReceived.selector,
"UNSAFE_RECIPIENT"
);
}
function _batchBurn(
address from,
uint256[] memory ids,
uint256[] memory amounts
) internal {
uint256 idsLength = ids.length; // Saves MLOADs.
require(idsLength == amounts.length, "LENGTH_MISMATCH");
for (uint256 i = 0; i < idsLength; ) {
balanceOf[from][ids[i]] -= amounts[i];
// An array can't have a total length
// larger than the max uint256 value.
unchecked {
i++;
}
}
emit TransferBatch(msg.sender, from, address(0), ids, amounts);
}
function _burn(
address from,
uint256 id,
uint256 amount
) internal {
balanceOf[from][id] -= amount;
emit TransferSingle(msg.sender, from, address(0), id, amount);
}
}
/// @notice A generic interface for a contract which properly accepts ERC1155 tokens.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC1155.sol)
interface ERC1155TokenReceiver {
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 amount,
bytes calldata data
) external returns (bytes4);
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) external returns (bytes4);
}
Ownable.sol 48 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.10;
error NotOwner();
// https://github.com/m1guelpf/erc721-drop/blob/main/src/LilOwnable.sol
abstract contract Ownable {
address internal _owner;
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
modifier onlyOwner() {
require(_owner == msg.sender);
_;
}
constructor() {
_owner = msg.sender;
}
function owner() external view returns (address) {
return _owner;
}
function transferOwnership(address _newOwner) external {
if (msg.sender != _owner) revert NotOwner();
_owner = _newOwner;
}
function renounceOwnership() public {
if (msg.sender != _owner) revert NotOwner();
_owner = address(0);
}
function supportsInterface(bytes4 interfaceId)
public
pure
virtual
returns (bool)
{
return interfaceId == 0x7f5828d0; // ERC165 Interface ID for ERC173
}
}
LlamaZoo.sol 551 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
import "./ERC1155.sol";
import "./SpitToken.sol";
import "./ERC721.sol";
import "./FxBaseRootTunnel.sol";
import "./Ownable.sol";
/**
__ _
/ /| | __ _ _ __ ___ __ _/\ /\___ _ __ ___ ___
/ / | |/ _` | '_ ` _ \ / _` \ \ / / _ \ '__/ __|/ _ \
/ /__| | (_| | | | | | | (_| |\ V / __/ | \__ \ __/
\____/_|\__,_|_| |_| |_|\__,_| \_/ \___|_| |___/\___|
**/
/// @title Llama Zoo
/// @author delta devs (https://twitter.com/deltadevelopers)
contract LlamaZoo is FxBaseRootTunnel, Ownable {
/*///////////////////////////////////////////////////////////////
STORAGE
//////////////////////////////////////////////////////////////*/
/// @notice ERC721 instance of the PixelatedLlama contract.
ERC721 public pixellatedLlamaContract;
/// @notice ERC721 instance of the LlamaDraws contract.
ERC721 public llamaDrawsContract;
/// @notice ERC1155 instance of the StakingBoost contract.
ERC1155 public boostContract;
/// @notice ERC1155 instance of the Pixlet contract.
ERC1155 public pixletContract;
struct Staker {
uint256[] stakedLlamas;
uint256 stakedPixletCanvas;
uint256 stakedLlamaDraws;
uint128 stakedSilverBoosts;
uint128 stakedGoldBoosts;
}
mapping(address => Staker) public userInfo;
bool public stakingPaused;
/*///////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
address checkpointManager,
address fxRoot,
address _pixellatedLlamaContract,
address _llamaDrawsContract,
address _boostContract,
address _pixletContract
) FxBaseRootTunnel(checkpointManager, fxRoot) {
pixellatedLlamaContract = ERC721(_pixellatedLlamaContract);
llamaDrawsContract = ERC721(_llamaDrawsContract);
boostContract = ERC1155(_boostContract);
pixletContract = ERC1155(_pixletContract);
}
/*///////////////////////////////////////////////////////////////
CONTRACT SETTERS
//////////////////////////////////////////////////////////////*/
/// @notice Set the contract addresses for all contract instances.
/// @param _pixellatedLlamaContract The contract address of PixellatedLlama.
/// @param _llamaDrawsContract The contract address of LlamaDraws.
/// @param _boostContract The contract address of RewardBooster.
/// @param _pixletContract The contract address of the Pixlet contract.
function setContractAddresses(
address _pixellatedLlamaContract,
address _llamaDrawsContract,
address _boostContract,
address _pixletContract
) public onlyOwner {
pixellatedLlamaContract = ERC721(_pixellatedLlamaContract);
llamaDrawsContract = ERC721(_llamaDrawsContract);
boostContract = ERC1155(_boostContract);
pixletContract = ERC1155(_pixletContract);
}
/// @notice Pauses staking and unstaking, for emergency purposes
/// @dev If we have to migrate because of Polygon instability or state sync issues, this will save us
function setStakingPaused(bool paused) public onlyOwner {
stakingPaused = paused;
}
/// @notice For collab.land to give a role based on staking status
function balanceOf(address owner) public view returns (uint256) {
uint[] memory llamas = userInfo[owner].stakedLlamas;
if(llamas.length == 0) return 0;
for (uint256 i = 0; i < llamas.length; i++) {
if(llamas[i] < 500) return 1;
}
return 2;
}
/// @dev Using the mapping directly wasn't returning the array, so we made this helper fuction.
function getStakedTokens(address user)
public
view
returns (
uint256[] memory llamas,
uint256 pixletCanvas,
uint256 llamaDraws,
uint128 silverBoosts,
uint128 goldBoosts
)
{
Staker memory staker = userInfo[user];
return (
staker.stakedLlamas,
staker.stakedPixletCanvas,
staker.stakedLlamaDraws,
staker.stakedSilverBoosts,
staker.stakedGoldBoosts
);
}
/*///////////////////////////////////////////////////////////////
UTILITY STAKING LOGIC
//////////////////////////////////////////////////////////////*/
function bulkStake(
uint256[] memory llamas,
uint128 silverBoosts,
uint128 goldBoosts,
uint256 pixletStudio,
uint256 llamaDraws
) public {
if (llamas.length > 0) stakeMultipleLlamas(llamas);
stakeBoosts(silverBoosts, goldBoosts);
if (pixletStudio != 0) stakePixletCanvas(pixletStudio);
if (llamaDraws != 0) stakeLlamaDraws(llamaDraws);
}
function bulkUnstake(
uint256[] memory llamas,
uint128 silverBoosts,
uint128 goldBoosts,
bool pixletStudio,
bool llamaDraws
) public {
if (llamas.length > 0) unstakeMultipleLlamas(llamas);
unstakeBoosts(silverBoosts, goldBoosts);
if (pixletStudio) unstakePixletCanvas();
if (llamaDraws) unstakeLlamaDraws();
}
function stakeMultipleLlamas(uint256[] memory tokenIds) public {
require(!stakingPaused, "Staking is currently paused.");
uint256 animatedCount = 0;
Staker storage staker = userInfo[msg.sender];
for (uint256 i = 0; i < tokenIds.length; i++) {
if (tokenIds[i] < 500) ++animatedCount;
staker.stakedLlamas.push(tokenIds[i]);
pixellatedLlamaContract.transferFrom(
msg.sender,
address(this),
tokenIds[i]
);
}
if (animatedCount > 0) {
_sendMessageToChild(
abi.encode(
msg.sender,
uint256(TokenType.AnimatedLlama),
animatedCount,
true
)
);
}
if ((tokenIds.length - animatedCount) > 0) {
_sendMessageToChild(
abi.encode(
msg.sender,
uint256(TokenType.StaticLlama),
tokenIds.length - animatedCount,
true
)
);
}
}
function unstakeMultipleLlamas(uint256[] memory tokenIds) public {
require(!stakingPaused, "Staking is currently paused.");
uint256 animatedCount = 0;
Staker storage staker = userInfo[msg.sender];
for (uint256 i = 0; i < tokenIds.length; i++) {
uint256 tokenId = tokenIds[i];
require(containsElement(staker.stakedLlamas, tokenId), "You do not own this llama.");
if (tokenId < 500) ++animatedCount;
pixellatedLlamaContract.transferFrom(
address(this),
msg.sender,
tokenId
);
uint256[] memory stakedLlamas = staker.stakedLlamas;
uint256 index;
for (uint256 j; j < stakedLlamas.length; j++) {
if (stakedLlamas[j] == tokenId) index = j;
}
if (stakedLlamas[index] == tokenId) {
staker.stakedLlamas[index] = stakedLlamas[
staker.stakedLlamas.length - 1
];
staker.stakedLlamas.pop();
}
}
if (animatedCount > 0) {
_sendMessageToChild(
abi.encode(
msg.sender,
uint256(TokenType.AnimatedLlama),
animatedCount,
false
)
);
}
if ((tokenIds.length - animatedCount) > 0) {
_sendMessageToChild(
abi.encode(
msg.sender,
uint256(TokenType.StaticLlama),
tokenIds.length - animatedCount,
false
)
);
}
}
/// @notice Stake a LlamaVerse llama.
/// @param tokenId The tokenId of the llama to stake
function stakeLlama(uint256 tokenId) external {
require(!stakingPaused, "Staking is currently paused.");
bool animated = tokenId < 500;
Staker storage staker = userInfo[msg.sender];
staker.stakedLlamas.push(tokenId);
pixellatedLlamaContract.transferFrom(
msg.sender,
address(this),
tokenId
);
_sendMessageToChild(
abi.encode(
msg.sender,
uint256(
animated ? TokenType.AnimatedLlama : TokenType.StaticLlama
),
1,
true
)
);
}
/// @notice Unstake a LlamaVerse llama.
/// @param tokenId The tokenId of the llama to unstake
function unstakeLlama(uint256 tokenId) external {
require(!stakingPaused, "Staking is currently paused.");
bool animated = tokenId < 500;
Staker storage staker = userInfo[msg.sender];
require(containsElement(staker.stakedLlamas, tokenId), "You do not own this llama.");
pixellatedLlamaContract.transferFrom(
address(this),
msg.sender,
tokenId
);
uint256[] memory stakedLlamas = staker.stakedLlamas;
uint256 index;
for (uint256 i; i < stakedLlamas.length; i++) {
if (stakedLlamas[i] == tokenId) index = i;
}
if (stakedLlamas[index] == tokenId) {
_sendMessageToChild(
abi.encode(
msg.sender,
uint256(
animated
? TokenType.AnimatedLlama
: TokenType.StaticLlama
),
1,
false
)
);
staker.stakedLlamas[index] = stakedLlamas[
staker.stakedLlamas.length - 1
];
staker.stakedLlamas.pop();
}
}
/// @notice Stake silver boosts.
/// @param amount The amount of boosts to stake.
function stakeSilverBoosts(uint128 amount) public {
require(!stakingPaused, "Staking is currently paused.");
require(amount != 0, "Staking 0 is not allowed.");
userInfo[msg.sender].stakedSilverBoosts += amount;
boostContract.safeTransferFrom(
msg.sender,
address(this),
2,
amount,
""
);
_sendMessageToChild(
abi.encode(msg.sender, uint256(TokenType.SilverBoost), amount, true)
);
}
/// @notice Unstake silver boosts.
/// @param amount The amount of boosts to unstake.
function unstakeSilverBoosts(uint128 amount) public {
require(!stakingPaused, "Staking is currently paused.");
require(amount != 0, "Unstaking 0 is not allowed.");
userInfo[msg.sender].stakedSilverBoosts -= amount;
boostContract.safeTransferFrom(
address(this),
msg.sender,
2,
amount,
""
);
_sendMessageToChild(
abi.encode(
msg.sender,
uint256(TokenType.SilverBoost),
amount,
false
)
);
}
/// @notice Stake gold boosts with the requested tokenID.
/// @param amount The amount of boosts to stake.
function stakeGoldBoosts(uint128 amount) public {
require(!stakingPaused, "Staking is currently paused.");
require(amount != 0, "Staking 0 is not allowed.");
userInfo[msg.sender].stakedGoldBoosts += amount;
boostContract.safeTransferFrom(
msg.sender,
address(this),
1,
amount,
""
);
_sendMessageToChild(
abi.encode(msg.sender, uint256(TokenType.GoldBoost), amount, true)
);
}
/// @notice Unstake gold boosts with the requested tokenID.
/// @param amount The amount of boosts to stake.
function unstakeGoldBoosts(uint128 amount) public {
require(!stakingPaused, "Staking is currently paused.");
require(amount != 0, "Unstaking 0 is not allowed.");
userInfo[msg.sender].stakedGoldBoosts -= amount;
boostContract.safeTransferFrom(
address(this),
msg.sender,
1,
amount,
""
);
_sendMessageToChild(
abi.encode(msg.sender, uint256(TokenType.GoldBoost), amount, false)
);
}
function stakeBoosts(uint128 silverAmount, uint128 goldAmount) public {
if (silverAmount != 0) stakeSilverBoosts(silverAmount);
if (goldAmount != 0) stakeGoldBoosts(goldAmount);
}
function unstakeBoosts(uint128 silverAmount, uint128 goldAmount) public {
if (silverAmount != 0) unstakeSilverBoosts(silverAmount);
if (goldAmount != 0) unstakeGoldBoosts(goldAmount);
}
/// @notice Stake a Pixlet Canvas with the requested tokenID.
/// @param tokenId The token ID of the pixlet canvas to stake.
function stakePixletCanvas(uint256 tokenId) public {
require(!stakingPaused, "Staking is currently paused.");
require(
userInfo[msg.sender].stakedPixletCanvas == 0,
"You already have a pixlet canvas staked."
);
userInfo[msg.sender].stakedPixletCanvas = tokenId;
pixletContract.safeTransferFrom(
msg.sender,
address(this),
tokenId,
1,
""
);
_sendMessageToChild(
abi.encode(msg.sender, uint256(TokenType.PixletCanvas), 1, true)
);
}
/// @notice Unstake your Pixlet Canvas.
function unstakePixletCanvas() public {
require(!stakingPaused, "Staking is currently paused.");
require(
userInfo[msg.sender].stakedPixletCanvas != 0,
"You do not have a pixlet canvas staked."
);
pixletContract.safeTransferFrom(
address(this),
msg.sender,
userInfo[msg.sender].stakedPixletCanvas,
1,
""
);
userInfo[msg.sender].stakedPixletCanvas = 0;
_sendMessageToChild(
abi.encode(msg.sender, uint256(TokenType.PixletCanvas), 1, false)
);
}
/// @notice Stake a Llamadraws.
/// @param tokenId The token ID of the llamadraws to stake.
function stakeLlamaDraws(uint256 tokenId) public {
require(!stakingPaused, "Staking is currently paused.");
require(
userInfo[msg.sender].stakedLlamaDraws == 0,
"You already have a llamadraws staked."
);
userInfo[msg.sender].stakedLlamaDraws = tokenId;
llamaDrawsContract.transferFrom(msg.sender, address(this), tokenId);
_sendMessageToChild(
abi.encode(msg.sender, uint256(TokenType.LlamaDraws), 1, true)
);
}
/// @notice Unstake your Llamadraws.
function unstakeLlamaDraws() public {
require(!stakingPaused, "Staking is currently paused.");
require(
userInfo[msg.sender].stakedLlamaDraws != 0,
"You do not have a llamadraws staked."
);
llamaDrawsContract.transferFrom(
address(this),
msg.sender,
userInfo[msg.sender].stakedLlamaDraws
);
userInfo[msg.sender].stakedLlamaDraws = 0;
_sendMessageToChild(
abi.encode(msg.sender, uint256(TokenType.LlamaDraws), 1, false)
);
}
function _processMessageFromChild(bytes memory message) internal override {
// We don't need a message from child
}
function containsElement(uint[] memory elements, uint tokenId) internal returns (bool) {
for (uint256 i = 0; i < elements.length; i++) {
if(elements[i] == tokenId) return true;
}
return false;
}
/*///////////////////////////////////////////////////////////////
ERC ON RECEIVED LOGIC
//////////////////////////////////////////////////////////////*/
/**
* @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 `IERC721.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 id,
bytes calldata data
) external returns (bytes4) {
return ERC721TokenReceiver.onERC721Received.selector;
}
/**
* @notice Handle the receipt of a single ERC1155 token type.
* @dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeTransferFrom` after the balance has been updated.
* This function MAY throw to revert and reject the transfer.
* Return of other amount than the magic value MUST result in the transaction being reverted.
* Note: The token contract address is always the message sender.
* @param operator The address which called the `safeTransferFrom` function.
* @param from The address which previously owned the token.
* @param id The id of the token being transferred.
* @param amount The amount of tokens being transferred.
* @param data Additional data with no specified format.
* @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`.
*/
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 amount,
bytes calldata data
) external returns (bytes4) {
return ERC1155TokenReceiver.onERC1155Received.selector;
}
/**
* @notice Handle the receipt of multiple ERC1155 token types.
* @dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeBatchTransferFrom` after the balances have been updated.
* This function MAY throw to revert and reject the transfer.
* Return of other amount than the magic value WILL result in the transaction being reverted.
* Note: The token contract address is always the message sender.
* @param operator The address which called the `safeBatchTransferFrom` function.
* @param from The address which previously owned the token.
* @param ids An array containing ids of each token being transferred.
* @param amounts An array containing amounts of each token being transferred.
* @param data Additional data with no specified format.
* @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`.
*/
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) external returns (bytes4) {
return ERC1155TokenReceiver.onERC1155BatchReceived.selector;
}
}
RLPReader.sol 340 lines
/* * @author Hamdi Allam [email protected] * Please reach out with any questions or concerns */ pragma solidity ^0.8.0; library RLPReader { uint8 constant STRING_SHORT_START = 0x80; uint8 constant STRING_LONG_START = 0xb8; uint8 constant LIST_SHORT_START = 0xc0; uint8 constant LIST_LONG_START = 0xf8; uint8 constant WORD_SIZE = 32; struct RLPItem { uint256 len; uint256 memPtr; } struct Iterator { RLPItem item; // Item that's being iterated over. uint256 nextPtr; // Position of the next item in the list. } /* * @dev Returns the next element in the iteration. Reverts if it has not next element. * @param self The iterator. * @return The next element in the iteration. */ function next(Iterator memory self) internal pure returns (RLPItem memory) { require(hasNext(self)); uint256 ptr = self.nextPtr; uint256 itemLength = _itemLength(ptr); self.nextPtr = ptr + itemLength; return RLPItem(itemLength, ptr); } /* * @dev Returns true if the iteration has more elements. * @param self The iterator. * @return true if the iteration has more elements. */ function hasNext(Iterator memory self) internal pure returns (bool) { RLPItem memory item = self.item; return self.nextPtr < item.memPtr + item.len; } /* * @param item RLP encoded bytes */ function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) { uint256 memPtr; assembly { memPtr := add(item, 0x20) } return RLPItem(item.length, memPtr); } /* * @dev Create an iterator. Reverts if item is not a list. * @param self The RLP item. * @return An 'Iterator' over the item. */ function iterator(RLPItem memory self) internal pure returns (Iterator memory) { require(isList(self)); uint256 ptr = self.memPtr + _payloadOffset(self.memPtr); return Iterator(self, ptr); } /* * @param item RLP encoded bytes */ function rlpLen(RLPItem memory item) internal pure returns (uint256) { return item.len; } /* * @param item RLP encoded bytes */ function payloadLen(RLPItem memory item) internal pure returns (uint256) { return item.len - _payloadOffset(item.memPtr); } /* * @param item RLP encoded list in bytes */ function toList(RLPItem memory item) internal pure returns (RLPItem[] memory) { require(isList(item)); uint256 items = numItems(item); RLPItem[] memory result = new RLPItem[](items); uint256 memPtr = item.memPtr + _payloadOffset(item.memPtr); uint256 dataLen; for (uint256 i = 0; i < items; i++) { dataLen = _itemLength(memPtr); result[i] = RLPItem(dataLen, memPtr); memPtr = memPtr + dataLen; } return result; } // @return indicator whether encoded payload is a list. negate this function call for isData. function isList(RLPItem memory item) internal pure returns (bool) { if (item.len == 0) return false; uint8 byte0; uint256 memPtr = item.memPtr; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < LIST_SHORT_START) return false; return true; } /* * @dev A cheaper version of keccak256(toRlpBytes(item)) that avoids copying memory. * @return keccak256 hash of RLP encoded bytes. */ function rlpBytesKeccak256(RLPItem memory item) internal pure returns (bytes32) { uint256 ptr = item.memPtr; uint256 len = item.len; bytes32 result; assembly { result := keccak256(ptr, len) } return result; } function payloadLocation(RLPItem memory item) internal pure returns (uint256, uint256) { uint256 offset = _payloadOffset(item.memPtr); uint256 memPtr = item.memPtr + offset; uint256 len = item.len - offset; // data length return (memPtr, len); } /* * @dev A cheaper version of keccak256(toBytes(item)) that avoids copying memory. * @return keccak256 hash of the item payload. */ function payloadKeccak256(RLPItem memory item) internal pure returns (bytes32) { (uint256 memPtr, uint256 len) = payloadLocation(item); bytes32 result; assembly { result := keccak256(memPtr, len) } return result; } /** RLPItem conversions into data types **/ // @returns raw rlp encoding in bytes function toRlpBytes(RLPItem memory item) internal pure returns (bytes memory) { bytes memory result = new bytes(item.len); if (result.length == 0) return result; uint256 ptr; assembly { ptr := add(0x20, result) } copy(item.memPtr, ptr, item.len); return result; } // any non-zero byte is considered true function toBoolean(RLPItem memory item) internal pure returns (bool) { require(item.len == 1); uint256 result; uint256 memPtr = item.memPtr; assembly { result := byte(0, mload(memPtr)) } return result == 0 ? false : true; } function toAddress(RLPItem memory item) internal pure returns (address) { // 1 byte for the length prefix require(item.len == 21); return address(uint160(toUint(item))); } function toUint(RLPItem memory item) internal pure returns (uint256) { require(item.len > 0 && item.len <= 33); uint256 offset = _payloadOffset(item.memPtr); uint256 len = item.len - offset; uint256 result; uint256 memPtr = item.memPtr + offset; assembly { result := mload(memPtr) // shfit to the correct location if neccesary if lt(len, 32) { result := div(result, exp(256, sub(32, len))) } } return result; } // enforces 32 byte length function toUintStrict(RLPItem memory item) internal pure returns (uint256) { // one byte prefix require(item.len == 33); uint256 result; uint256 memPtr = item.memPtr + 1; assembly { result := mload(memPtr) } return result; } function toBytes(RLPItem memory item) internal pure returns (bytes memory) { require(item.len > 0); uint256 offset = _payloadOffset(item.memPtr); uint256 len = item.len - offset; // data length bytes memory result = new bytes(len); uint256 destPtr; assembly { destPtr := add(0x20, result) } copy(item.memPtr + offset, destPtr, len); return result; } /* * Private Helpers */ // @return number of payload items inside an encoded list. function numItems(RLPItem memory item) private pure returns (uint256) { if (item.len == 0) return 0; uint256 count = 0; uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr); uint256 endPtr = item.memPtr + item.len; while (currPtr < endPtr) { currPtr = currPtr + _itemLength(currPtr); // skip over an item count++; } return count; } // @return entire rlp item byte length function _itemLength(uint256 memPtr) private pure returns (uint256) { uint256 itemLen; uint256 byte0; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < STRING_SHORT_START) itemLen = 1; else if (byte0 < STRING_LONG_START) itemLen = byte0 - STRING_SHORT_START + 1; else if (byte0 < LIST_SHORT_START) { assembly { let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is memPtr := add(memPtr, 1) // skip over the first byte /* 32 byte word size */ let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len itemLen := add(dataLen, add(byteLen, 1)) } } else if (byte0 < LIST_LONG_START) { itemLen = byte0 - LIST_SHORT_START + 1; } else { assembly { let byteLen := sub(byte0, 0xf7) memPtr := add(memPtr, 1) let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length itemLen := add(dataLen, add(byteLen, 1)) } } return itemLen; } // @return number of bytes until the data function _payloadOffset(uint256 memPtr) private pure returns (uint256) { uint256 byte0; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < STRING_SHORT_START) return 0; else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START)) return 1; else if (byte0 < LIST_SHORT_START) // being explicit return byte0 - (STRING_LONG_START - 1) + 1; else return byte0 - (LIST_LONG_START - 1) + 1; } /* * @param src Pointer to source * @param dest Pointer to destination * @param len Amount of memory to copy from the source */ function copy( uint256 src, uint256 dest, uint256 len ) private pure { if (len == 0) return; // copy as many word sizes as possible for (; len >= WORD_SIZE; len -= WORD_SIZE) { assembly { mstore(dest, mload(src)) } src += WORD_SIZE; dest += WORD_SIZE; } if (len == 0) return; // left over bytes. Mask is used to remove unwanted bytes from the word uint256 mask = 256**(WORD_SIZE - len) - 1; assembly { let srcpart := and(mload(src), not(mask)) // zero out src let destpart := and(mload(dest), mask) // retrieve the bytes mstore(dest, or(destpart, srcpart)) } } }
SpitToken.sol 298 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
import "./ERC20.sol";
import "./FXBaseChildTunnel.sol";
import "./Ownable.sol";
/**
__ _
/ /| | __ _ _ __ ___ __ _/\ /\___ _ __ ___ ___
/ / | |/ _` | '_ ` _ \ / _` \ \ / / _ \ '__/ __|/ _ \
/ /__| | (_| | | | | | | (_| |\ V / __/ | \__ \ __/
\____/_|\__,_|_| |_| |_|\__,_| \_/ \___|_| |___/\___|
**/
/// @title $SPIT Token
/// @author delta devs (https://twitter.com/deltadevelopers)
enum TokenType {
StaticLlama,
AnimatedLlama,
SilverBoost,
GoldBoost,
PixletCanvas,
LlamaDraws
}
contract SpitToken is ERC20, FxBaseChildTunnel, Ownable {
/*///////////////////////////////////////////////////////////////
STORAGE
/////////////////////////////////////////////////////////////*/
struct Rewards {
uint256 staticLlama;
uint256 animatedLlama;
uint256 silverEnergy;
uint256 goldEnergy;
uint256 pixletCanvas;
uint256 llamaDraws;
}
/// @notice The current reward rates per token type.
Rewards public rewards;
/// @notice Keeps track of the staking balances (how much is being staked) of each token type for all holders.
mapping(address => mapping(uint256 => uint256)) public balances;
/// @notice Keeps track of the timestamp of when a holder last withdrew their rewards.
mapping(address => uint256) public lastUpdated;
/*///////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(address _fxChild)
FxBaseChildTunnel(_fxChild)
ERC20("Spit Token", "SPIT", 18)
{
rewards.staticLlama = (uint256(10) * 1e18) / 1 days;
rewards.animatedLlama = (uint256(30) * 1e18) / 1 days;
rewards.silverEnergy = (uint256(4) * 1e18) / 1 days;
rewards.goldEnergy = (uint256(12) * 1e18) / 1 days;
rewards.llamaDraws = (uint256(1) * 1e18) / 1 days;
rewards.pixletCanvas = (uint256(1) * 1e18) / 1 days;
_mint(address(this), 100_000_000 * 1e18);
uint256 allocation = (30_000_000 + 5_000_000 + 5_000_000 + 2_500_000) *
1e18;
balanceOf[address(this)] -= allocation;
unchecked {
balanceOf[
0xcc5cDaB325689Bcd654aB8611c528e60CC8CBe6A
] += (30_000_000 * 1e18);
balanceOf[
0x58B96f5C8ef1CdD7e12a9b71Bbbe575E7B26b142
] += (5_000_000 * 1e18);
balanceOf[
0x58caDf06fcC222f573F81B08B6Cc156e420D35d7
] += (5_000_000 * 1e18);
balanceOf[
0x5D31E4A33470e1a15e54aAdD1d913b613fd0E9ED
] += (2_500_000 * 1e18);
}
emit Transfer(
address(this),
0xcc5cDaB325689Bcd654aB8611c528e60CC8CBe6A,
30_000_000 * 1e18
);
emit Transfer(
address(this),
0x58B96f5C8ef1CdD7e12a9b71Bbbe575E7B26b142,
5_000_000 * 1e18
);
emit Transfer(
address(this),
0x58caDf06fcC222f573F81B08B6Cc156e420D35d7,
5_000_000 * 1e18
);
emit Transfer(
address(this),
0x5D31E4A33470e1a15e54aAdD1d913b613fd0E9ED,
2_500_000 * 1e18
);
}
/*///////////////////////////////////////////////////////////////
STAKING LOGIC
//////////////////////////////////////////////////////////////*/
/// @notice Called when withdrawing rewards. $SPIT is transferred to the address, and the lastUpdated field is updated.
/// @param account The address to mint to.
modifier updateReward(address account) {
uint256 amount = earned(account);
balanceOf[address(this)] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[account] += amount;
}
lastUpdated[account] = block.timestamp;
emit Transfer(address(this), account, amount);
_;
}
/// @notice Internal call to stake an amount of a specific token type.
/// @param account The address which will be staking.
/// @param tokenType The token type to stake.
/// @param amount The amount to stake.
function processStake(
address account,
TokenType tokenType,
uint256 amount
) internal updateReward(account) {
balances[account][uint256(tokenType)] += amount;
}
/// @notice Internal call to unstake an amount of a specific token type.
/// @param account The address which will be unstaking.
/// @param tokenType The token type to unstake.
/// @param amount The amount to unstake.
function processUnstake(
address account,
TokenType tokenType,
uint256 amount
) internal updateReward(account) {
balances[account][uint256(tokenType)] -= amount;
}
/**
* @notice Process message received from FxChild
* @param stateId unique state id
* @param sender root message sender
* @param message bytes message that was sent from Root Tunnel
*/
function _processMessageFromRoot(
uint256 stateId,
address sender,
bytes memory message
) internal override validateSender(sender) {
(address from, uint256 token, uint256 count, bool action) = abi.decode(
message,
(address, uint256, uint256, bool)
);
action
? processStake(from, TokenType(token), count)
: processUnstake(from, TokenType(token), count);
}
/*///////////////////////////////////////////////////////////////
USER UTILITIES
//////////////////////////////////////////////////////////////*/
/// @notice Forwards a user's purchase in SPIT to this contract using EIP-2612
/// @dev This function exists so that the permit and transfer can be done in a single transaction.
function purchaseUtility(
address owner,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public updateReward(owner) {
permit(owner, msg.sender, value, deadline, v, r, s);
transferFrom(owner, address(this), value);
}
/// @notice Calculates the total amount of rewards accumulated for a staker, for staking all owned token types.
/// @dev Calculates based on when the staker last withdrew rewards, and compares it with the current block's timestamp.
/// @param account The account to calculate the accumulated rewards for.
function earned(address account) public view returns (uint256) {
return
spitPerSecond(account) * (block.timestamp - lastUpdated[account]);
}
/// @notice Calculates the current balance of the user including the unclaimed rewards.
/// @dev Unclaimed rewards are withdrawn automatically when a utility purchase is made or an unstake/stake occurs.
function totalBalance(address account) public view returns (uint256) {
return balanceOf[account] + earned(account);
}
/// @notice Calculates the amount of SPIT earned per second by the given user
/// @param account The account to calculate the accumulated rewards for.
function spitPerSecond(address account) public view returns (uint256) {
return ((balances[account][0] * rewards.staticLlama) +
(balances[account][1] * rewards.animatedLlama) +
(min(balances[account][2], balances[account][0]) *
rewards.silverEnergy) +
(min(balances[account][3], balances[account][1]) *
rewards.goldEnergy) +
(balances[account][4] * rewards.pixletCanvas) +
(balances[account][5] * rewards.llamaDraws));
}
/*///////////////////////////////////////////////////////////////
ADMIN UTILITIES
//////////////////////////////////////////////////////////////*/
/// @notice Allows the contract owner to burn SPIT owned by the contract.
function burn(uint256 amount) public onlyOwner {
_burn(address(this), amount);
}
/// @notice Allows the contract owner to airdrop SPIT owned by the contract.
function airdrop(address[] calldata accounts, uint256[] calldata amounts) public onlyOwner {
require(accounts.length == amounts.length);
for(uint i = 0; i < accounts.length; i++) {
uint amount = amounts[i];
balanceOf[address(this)] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[accounts[i]] += amount;
}
emit Transfer(address(this), accounts[i], amount);
}
}
/// @notice Allows the contract owner to mint SPIT to the contract.
function mint(uint256 amount) public onlyOwner {
_mint(address(this), amount);
}
/// @notice Withdraw $SPIT being held on this contract to the requested address.
/// @param recipient The address to withdraw the funds to.
/// @param amount The amount of SPIT to withdraw
function withdrawSpit(address recipient, uint256 amount) public onlyOwner {
balanceOf[address(this)] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[recipient] += amount;
}
emit Transfer(address(this), recipient, amount);
}
/// @notice Allows the contract deployer to sets the reward rates for each token type.
/// @param staticLlama The reward rate for staking a static llama.
/// @param animatedLlama The reward rate for staking an animated llama.
/// @param silverEnergy The reward rate for staking a silver llama boost.
/// @param goldEnergy The reward rate for staking a gold llama boost.
/// @param pixletCanvas The reward rate for staking a pixlet canvas.
function setRewardRates(
uint256 staticLlama,
uint256 animatedLlama,
uint256 silverEnergy,
uint256 goldEnergy,
uint256 pixletCanvas,
uint256 llamaDraws
) public onlyOwner {
rewards.staticLlama = staticLlama;
rewards.animatedLlama = animatedLlama;
rewards.silverEnergy = silverEnergy;
rewards.goldEnergy = goldEnergy;
rewards.pixletCanvas = pixletCanvas;
rewards.llamaDraws = llamaDraws;
}
function updateFxRootRunnel(address _fxRootTunnel) external onlyOwner {
fxRootTunnel = _fxRootTunnel;
}
/*///////////////////////////////////////////////////////////////
HELPERS
//////////////////////////////////////////////////////////////*/
function min(uint256 x, uint256 y) internal pure returns (uint256) {
return x < y ? x : y;
}
}
FxBaseRootTunnel.sol 179 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RLPReader} from "./RLPReader.sol";
import {MerklePatriciaProof} from "./MerklePatriciaProof.sol";
import {Merkle} from "./Merkle.sol";
import "./ExitPayloadReader.sol";
interface IFxStateSender {
function sendMessageToChild(address _receiver, bytes calldata _data) external;
}
contract ICheckpointManager {
struct HeaderBlock {
bytes32 root;
uint256 start;
uint256 end;
uint256 createdAt;
address proposer;
}
/**
* @notice mapping of checkpoint header numbers to block details
* @dev These checkpoints are submited by plasma contracts
*/
mapping(uint256 => HeaderBlock) public headerBlocks;
}
abstract contract FxBaseRootTunnel {
using RLPReader for RLPReader.RLPItem;
using Merkle for bytes32;
using ExitPayloadReader for bytes;
using ExitPayloadReader for ExitPayloadReader.ExitPayload;
using ExitPayloadReader for ExitPayloadReader.Log;
using ExitPayloadReader for ExitPayloadReader.LogTopics;
using ExitPayloadReader for ExitPayloadReader.Receipt;
// keccak256(MessageSent(bytes))
bytes32 public constant SEND_MESSAGE_EVENT_SIG = 0x8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b036;
// state sender contract
IFxStateSender public fxRoot;
// root chain manager
ICheckpointManager public checkpointManager;
// child tunnel contract which receives and sends messages
address public fxChildTunnel;
// storage to avoid duplicate exits
mapping(bytes32 => bool) public processedExits;
constructor(address _checkpointManager, address _fxRoot) {
checkpointManager = ICheckpointManager(_checkpointManager);
fxRoot = IFxStateSender(_fxRoot);
}
// set fxChildTunnel if not set already
function setFxChildTunnel(address _fxChildTunnel) public {
require(fxChildTunnel == address(0x0), "FxBaseRootTunnel: CHILD_TUNNEL_ALREADY_SET");
fxChildTunnel = _fxChildTunnel;
}
/**
* @notice Send bytes message to Child Tunnel
* @param message bytes message that will be sent to Child Tunnel
* some message examples -
* abi.encode(tokenId);
* abi.encode(tokenId, tokenMetadata);
* abi.encode(messageType, messageData);
*/
function _sendMessageToChild(bytes memory message) internal {
fxRoot.sendMessageToChild(fxChildTunnel, message);
}
function _validateAndExtractMessage(bytes memory inputData) internal returns (bytes memory) {
ExitPayloadReader.ExitPayload memory payload = inputData.toExitPayload();
bytes memory branchMaskBytes = payload.getBranchMaskAsBytes();
uint256 blockNumber = payload.getBlockNumber();
// checking if exit has already been processed
// unique exit is identified using hash of (blockNumber, branchMask, receiptLogIndex)
bytes32 exitHash = keccak256(
abi.encodePacked(
blockNumber,
// first 2 nibbles are dropped while generating nibble array
// this allows branch masks that are valid but bypass exitHash check (changing first 2 nibbles only)
// so converting to nibble array and then hashing it
MerklePatriciaProof._getNibbleArray(branchMaskBytes),
payload.getReceiptLogIndex()
)
);
require(processedExits[exitHash] == false, "FxRootTunnel: EXIT_ALREADY_PROCESSED");
processedExits[exitHash] = true;
ExitPayloadReader.Receipt memory receipt = payload.getReceipt();
ExitPayloadReader.Log memory log = receipt.getLog();
// check child tunnel
require(fxChildTunnel == log.getEmitter(), "FxRootTunnel: INVALID_FX_CHILD_TUNNEL");
bytes32 receiptRoot = payload.getReceiptRoot();
// verify receipt inclusion
require(
MerklePatriciaProof.verify(receipt.toBytes(), branchMaskBytes, payload.getReceiptProof(), receiptRoot),
"FxRootTunnel: INVALID_RECEIPT_PROOF"
);
// verify checkpoint inclusion
_checkBlockMembershipInCheckpoint(
blockNumber,
payload.getBlockTime(),
payload.getTxRoot(),
receiptRoot,
payload.getHeaderNumber(),
payload.getBlockProof()
);
ExitPayloadReader.LogTopics memory topics = log.getTopics();
require(
bytes32(topics.getField(0).toUint()) == SEND_MESSAGE_EVENT_SIG, // topic0 is event sig
"FxRootTunnel: INVALID_SIGNATURE"
);
// received message data
bytes memory message = abi.decode(log.getData(), (bytes)); // event decodes params again, so decoding bytes to get message
return message;
}
function _checkBlockMembershipInCheckpoint(
uint256 blockNumber,
uint256 blockTime,
bytes32 txRoot,
bytes32 receiptRoot,
uint256 headerNumber,
bytes memory blockProof
) private view returns (uint256) {
(bytes32 headerRoot, uint256 startBlock, , uint256 createdAt, ) = checkpointManager.headerBlocks(headerNumber);
require(
keccak256(abi.encodePacked(blockNumber, blockTime, txRoot, receiptRoot)).checkMembership(
blockNumber - startBlock,
headerRoot,
blockProof
),
"FxRootTunnel: INVALID_HEADER"
);
return createdAt;
}
/**
* @notice receive message from L2 to L1, validated by proof
* @dev This function verifies if the transaction actually happened on child chain
*
* @param inputData RLP encoded data of the reference tx containing following list of fields
* 0 - headerNumber - Checkpoint header block number containing the reference tx
* 1 - blockProof - Proof that the block header (in the child chain) is a leaf in the submitted merkle root
* 2 - blockNumber - Block number containing the reference tx on child chain
* 3 - blockTime - Reference tx block time
* 4 - txRoot - Transactions root of block
* 5 - receiptRoot - Receipts root of block
* 6 - receipt - Receipt of the reference transaction
* 7 - receiptProof - Merkle proof of the reference receipt
* 8 - branchMask - 32 bits denoting the path of receipt in merkle tree
* 9 - receiptLogIndex - Log Index to read from the receipt
*/
function receiveMessage(bytes memory inputData) public virtual {
bytes memory message = _validateAndExtractMessage(inputData);
_processMessageFromChild(message);
}
/**
* @notice Process message received from Child Tunnel
* @dev function needs to be implemented to handle message as per requirement
* This is called by onStateReceive function.
* Since it is called via a system call, any event will not be emitted during its execution.
* @param message bytes message that was sent from Child Tunnel
*/
function _processMessageFromChild(bytes memory message) internal virtual;
}
ExitPayloadReader.sol 160 lines
pragma solidity ^0.8.0;
import {RLPReader} from "./RLPReader.sol";
library ExitPayloadReader {
using RLPReader for bytes;
using RLPReader for RLPReader.RLPItem;
uint8 constant WORD_SIZE = 32;
struct ExitPayload {
RLPReader.RLPItem[] data;
}
struct Receipt {
RLPReader.RLPItem[] data;
bytes raw;
uint256 logIndex;
}
struct Log {
RLPReader.RLPItem data;
RLPReader.RLPItem[] list;
}
struct LogTopics {
RLPReader.RLPItem[] data;
}
// copy paste of private copy() from RLPReader to avoid changing of existing contracts
function copy(
uint256 src,
uint256 dest,
uint256 len
) private pure {
if (len == 0) return;
// copy as many word sizes as possible
for (; len >= WORD_SIZE; len -= WORD_SIZE) {
assembly {
mstore(dest, mload(src))
}
src += WORD_SIZE;
dest += WORD_SIZE;
}
// left over bytes. Mask is used to remove unwanted bytes from the word
uint256 mask = 256**(WORD_SIZE - len) - 1;
assembly {
let srcpart := and(mload(src), not(mask)) // zero out src
let destpart := and(mload(dest), mask) // retrieve the bytes
mstore(dest, or(destpart, srcpart))
}
}
function toExitPayload(bytes memory data) internal pure returns (ExitPayload memory) {
RLPReader.RLPItem[] memory payloadData = data.toRlpItem().toList();
return ExitPayload(payloadData);
}
function getHeaderNumber(ExitPayload memory payload) internal pure returns (uint256) {
return payload.data[0].toUint();
}
function getBlockProof(ExitPayload memory payload) internal pure returns (bytes memory) {
return payload.data[1].toBytes();
}
function getBlockNumber(ExitPayload memory payload) internal pure returns (uint256) {
return payload.data[2].toUint();
}
function getBlockTime(ExitPayload memory payload) internal pure returns (uint256) {
return payload.data[3].toUint();
}
function getTxRoot(ExitPayload memory payload) internal pure returns (bytes32) {
return bytes32(payload.data[4].toUint());
}
function getReceiptRoot(ExitPayload memory payload) internal pure returns (bytes32) {
return bytes32(payload.data[5].toUint());
}
function getReceipt(ExitPayload memory payload) internal pure returns (Receipt memory receipt) {
receipt.raw = payload.data[6].toBytes();
RLPReader.RLPItem memory receiptItem = receipt.raw.toRlpItem();
if (receiptItem.isList()) {
// legacy tx
receipt.data = receiptItem.toList();
} else {
// pop first byte before parsting receipt
bytes memory typedBytes = receipt.raw;
bytes memory result = new bytes(typedBytes.length - 1);
uint256 srcPtr;
uint256 destPtr;
assembly {
srcPtr := add(33, typedBytes)
destPtr := add(0x20, result)
}
copy(srcPtr, destPtr, result.length);
receipt.data = result.toRlpItem().toList();
}
receipt.logIndex = getReceiptLogIndex(payload);
return receipt;
}
function getReceiptProof(ExitPayload memory payload) internal pure returns (bytes memory) {
return payload.data[7].toBytes();
}
function getBranchMaskAsBytes(ExitPayload memory payload) internal pure returns (bytes memory) {
return payload.data[8].toBytes();
}
function getBranchMaskAsUint(ExitPayload memory payload) internal pure returns (uint256) {
return payload.data[8].toUint();
}
function getReceiptLogIndex(ExitPayload memory payload) internal pure returns (uint256) {
return payload.data[9].toUint();
}
// Receipt methods
function toBytes(Receipt memory receipt) internal pure returns (bytes memory) {
return receipt.raw;
}
function getLog(Receipt memory receipt) internal pure returns (Log memory) {
RLPReader.RLPItem memory logData = receipt.data[3].toList()[receipt.logIndex];
return Log(logData, logData.toList());
}
// Log methods
function getEmitter(Log memory log) internal pure returns (address) {
return RLPReader.toAddress(log.list[0]);
}
function getTopics(Log memory log) internal pure returns (LogTopics memory) {
return LogTopics(log.list[1].toList());
}
function getData(Log memory log) internal pure returns (bytes memory) {
return log.list[2].toBytes();
}
function toRlpBytes(Log memory log) internal pure returns (bytes memory) {
return log.data.toRlpBytes();
}
// LogTopics methods
function getField(LogTopics memory topics, uint256 index) internal pure returns (RLPReader.RLPItem memory) {
return topics.data[index];
}
}
FXBaseChildTunnel.sol 78 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// IFxMessageProcessor represents interface to process message
interface IFxMessageProcessor {
function processMessageFromRoot(
uint256 stateId,
address rootMessageSender,
bytes calldata data
) external;
}
/**
* @notice Mock child tunnel contract to receive and send message from L2
*/
abstract contract FxBaseChildTunnel is IFxMessageProcessor {
// MessageTunnel on L1 will get data from this event
event MessageSent(bytes message);
// fx child
address public fxChild;
// fx root tunnel
address public fxRootTunnel;
constructor(address _fxChild) {
fxChild = _fxChild;
}
// Sender must be fxRootTunnel in case of ERC20 tunnel
modifier validateSender(address sender) {
require(sender == fxRootTunnel, "FxBaseChildTunnel: INVALID_SENDER_FROM_ROOT");
_;
}
// set fxRootTunnel if not set already
function setFxRootTunnel(address _fxRootTunnel) external {
require(fxRootTunnel == address(0x0), "FxBaseChildTunnel: ROOT_TUNNEL_ALREADY_SET");
fxRootTunnel = _fxRootTunnel;
}
function processMessageFromRoot(
uint256 stateId,
address rootMessageSender,
bytes calldata data
) external override {
require(msg.sender == fxChild, "FxBaseChildTunnel: INVALID_SENDER");
_processMessageFromRoot(stateId, rootMessageSender, data);
}
/**
* @notice Emit message that can be received on Root Tunnel
* @dev Call the internal function when need to emit message
* @param message bytes message that will be sent to Root Tunnel
* some message examples -
* abi.encode(tokenId);
* abi.encode(tokenId, tokenMetadata);
* abi.encode(messageType, messageData);
*/
function _sendMessageToRoot(bytes memory message) internal {
emit MessageSent(message);
}
/**
* @notice Process message received from Root Tunnel
* @dev function needs to be implemented to handle message as per requirement
* This is called by onStateReceive function.
* Since it is called via a system call, any event will not be emitted during its execution.
* @param stateId unique state id
* @param sender root message sender
* @param message bytes message that was sent from Root Tunnel
*/
function _processMessageFromRoot(
uint256 stateId,
address sender,
bytes memory message
) internal virtual;
}
MerklePatriciaProof.sol 137 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RLPReader} from "./RLPReader.sol";
library MerklePatriciaProof {
/*
* @dev Verifies a merkle patricia proof.
* @param value The terminating value in the trie.
* @param encodedPath The path in the trie leading to value.
* @param rlpParentNodes The rlp encoded stack of nodes.
* @param root The root hash of the trie.
* @return The boolean validity of the proof.
*/
function verify(
bytes memory value,
bytes memory encodedPath,
bytes memory rlpParentNodes,
bytes32 root
) internal pure returns (bool) {
RLPReader.RLPItem memory item = RLPReader.toRlpItem(rlpParentNodes);
RLPReader.RLPItem[] memory parentNodes = RLPReader.toList(item);
bytes memory currentNode;
RLPReader.RLPItem[] memory currentNodeList;
bytes32 nodeKey = root;
uint256 pathPtr = 0;
bytes memory path = _getNibbleArray(encodedPath);
if (path.length == 0) {
return false;
}
for (uint256 i = 0; i < parentNodes.length; i++) {
if (pathPtr > path.length) {
return false;
}
currentNode = RLPReader.toRlpBytes(parentNodes[i]);
if (nodeKey != keccak256(currentNode)) {
return false;
}
currentNodeList = RLPReader.toList(parentNodes[i]);
if (currentNodeList.length == 17) {
if (pathPtr == path.length) {
if (keccak256(RLPReader.toBytes(currentNodeList[16])) == keccak256(value)) {
return true;
} else {
return false;
}
}
uint8 nextPathNibble = uint8(path[pathPtr]);
if (nextPathNibble > 16) {
return false;
}
nodeKey = bytes32(RLPReader.toUintStrict(currentNodeList[nextPathNibble]));
pathPtr += 1;
} else if (currentNodeList.length == 2) {
uint256 traversed = _nibblesToTraverse(RLPReader.toBytes(currentNodeList[0]), path, pathPtr);
if (pathPtr + traversed == path.length) {
//leaf node
if (keccak256(RLPReader.toBytes(currentNodeList[1])) == keccak256(value)) {
return true;
} else {
return false;
}
}
//extension node
if (traversed == 0) {
return false;
}
pathPtr += traversed;
nodeKey = bytes32(RLPReader.toUintStrict(currentNodeList[1]));
} else {
return false;
}
}
}
function _nibblesToTraverse(
bytes memory encodedPartialPath,
bytes memory path,
uint256 pathPtr
) private pure returns (uint256) {
uint256 len = 0;
// encodedPartialPath has elements that are each two hex characters (1 byte), but partialPath
// and slicedPath have elements that are each one hex character (1 nibble)
bytes memory partialPath = _getNibbleArray(encodedPartialPath);
bytes memory slicedPath = new bytes(partialPath.length);
// pathPtr counts nibbles in path
// partialPath.length is a number of nibbles
for (uint256 i = pathPtr; i < pathPtr + partialPath.length; i++) {
bytes1 pathNibble = path[i];
slicedPath[i - pathPtr] = pathNibble;
}
if (keccak256(partialPath) == keccak256(slicedPath)) {
len = partialPath.length;
} else {
len = 0;
}
return len;
}
// bytes b must be hp encoded
function _getNibbleArray(bytes memory b) internal pure returns (bytes memory) {
bytes memory nibbles = "";
if (b.length > 0) {
uint8 offset;
uint8 hpNibble = uint8(_getNthNibbleOfBytes(0, b));
if (hpNibble == 1 || hpNibble == 3) {
nibbles = new bytes(b.length * 2 - 1);
bytes1 oddNibble = _getNthNibbleOfBytes(1, b);
nibbles[0] = oddNibble;
offset = 1;
} else {
nibbles = new bytes(b.length * 2 - 2);
offset = 0;
}
for (uint256 i = offset; i < nibbles.length; i++) {
nibbles[i] = _getNthNibbleOfBytes(i - offset + 2, b);
}
}
return nibbles;
}
function _getNthNibbleOfBytes(uint256 n, bytes memory str) private pure returns (bytes1) {
return bytes1(n % 2 == 0 ? uint8(str[n / 2]) / 0x10 : uint8(str[n / 2]) % 0x10);
}
}
Read Contract
SEND_MESSAGE_EVENT_SIG 0x0e387de6 → bytes32
balanceOf 0x70a08231 → uint256
boostContract 0xdfcedeee → address
checkpointManager 0xc0857ba0 → address
fxChildTunnel 0x972c4928 → address
fxRoot 0xde9b771f → address
getStakedTokens 0x63c28db1 → uint256[], uint256, uint256, uint128, uint128
llamaDrawsContract 0x584a2206 → address
owner 0x8da5cb5b → address
pixellatedLlamaContract 0x0f147692 → address
pixletContract 0x652972d4 → address
processedExits 0x607f2d42 → bool
stakingPaused 0xbbb781cc → bool
supportsInterface 0x01ffc9a7 → bool
userInfo 0x1959a002 → uint256, uint256, uint128, uint128
Write Contract 25 functions
These functions modify contract state and require a wallet transaction to execute.
bulkStake 0x6f251724
uint256[] llamas
uint128 silverBoosts
uint128 goldBoosts
uint256 pixletStudio
uint256 llamaDraws
bulkUnstake 0x891bc3d2
uint256[] llamas
uint128 silverBoosts
uint128 goldBoosts
bool pixletStudio
bool llamaDraws
onERC1155BatchReceived 0xbc197c81
address operator
address from
uint256[] ids
uint256[] amounts
bytes data
returns: bytes4
onERC1155Received 0xf23a6e61
address operator
address from
uint256 id
uint256 amount
bytes data
returns: bytes4
onERC721Received 0x150b7a02
address operator
address from
uint256 id
bytes data
returns: bytes4
receiveMessage 0xf953cec7
bytes inputData
renounceOwnership 0x715018a6
No parameters
setContractAddresses 0xaaa68777
address _pixellatedLlamaContract
address _llamaDrawsContract
address _boostContract
address _pixletContract
setFxChildTunnel 0xaea4e49e
address _fxChildTunnel
setStakingPaused 0x15b31bbb
bool paused
stakeBoosts 0x3b0d79fa
uint128 silverAmount
uint128 goldAmount
stakeGoldBoosts 0x5d939ed0
uint128 amount
stakeLlama 0xc5bf0964
uint256 tokenId
stakeLlamaDraws 0x4f9ce271
uint256 tokenId
stakeMultipleLlamas 0x8282052b
uint256[] tokenIds
stakePixletCanvas 0xf3fa98a7
uint256 tokenId
stakeSilverBoosts 0x1820743d
uint128 amount
transferOwnership 0xf2fde38b
address _newOwner
unstakeBoosts 0xbd88119f
uint128 silverAmount
uint128 goldAmount
unstakeGoldBoosts 0x860420e3
uint128 amount
unstakeLlama 0x50acac55
uint256 tokenId
unstakeLlamaDraws 0xc7059488
No parameters
unstakeMultipleLlamas 0x2176dfce
uint256[] tokenIds
unstakePixletCanvas 0xabbe0818
No parameters
unstakeSilverBoosts 0xe72cb05b
uint128 amount
Recent Transactions
No transactions found for this address