erc20-pool

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit b5fbc778c040b4fdbd17381f5d7eae78dee52ad4
parent a4fc3c1ddaf645d4c67633b98fd3ce0b81a5c4e0
Author: lash <dev@holbrook.no>
Date:   Wed, 26 Jul 2023 17:02:01 +0100

Implement python interface, deposit and swap test

Diffstat:
A.gitignore | 7+++++++
Apython/erc20_pool/__init__.py | 1+
Apython/erc20_pool/data/SwapPool.bin | 2++
Apython/erc20_pool/data/SwapPool.json | 1+
Apython/erc20_pool/data/SwapPool.metadata.json | 1+
Apython/erc20_pool/data/__init__.py | 3+++
Apython/erc20_pool/pool.py | 119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apython/erc20_pool/unittest/__init__.py | 1+
Apython/erc20_pool/unittest/base.py | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apython/requirements.txt | 3+++
Apython/test_requirements.txt | 4++++
Apython/tests/test_base.py | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msolidity/Makefile | 4++--
Msolidity/SwapPool.sol | 116++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
14 files changed, 394 insertions(+), 8 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,7 @@ +__pycache__ +*.pyc +*.egg-info +build/ +dist/ +solidity/*.json +solidity/*.bin diff --git a/python/erc20_pool/__init__.py b/python/erc20_pool/__init__.py @@ -0,0 +1 @@ +from .pool import Pool diff --git a/python/erc20_pool/data/SwapPool.bin b/python/erc20_pool/data/SwapPool.bin @@ -0,0 +1 @@ +60a06040523480156200001157600080fd5b5060405162002a9c38038062002a9c83398181016040528101906200003791906200032a565b846006908162000048919062000641565b5083600790816200005a919062000641565b508260ff166080818152505080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600581905550505050505062000728565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6200012282620000d7565b810181811067ffffffffffffffff82111715620001445762000143620000e8565b5b80604052505050565b600062000159620000b9565b905062000167828262000117565b919050565b600067ffffffffffffffff8211156200018a5762000189620000e8565b5b6200019582620000d7565b9050602081019050919050565b60005b83811015620001c2578082015181840152602081019050620001a5565b60008484015250505050565b6000620001e5620001df846200016c565b6200014d565b905082815260208101848484011115620002045762000203620000d2565b5b62000211848285620001a2565b509392505050565b600082601f830112620002315762000230620000cd565b5b815162000243848260208601620001ce565b91505092915050565b600060ff82169050919050565b62000264816200024c565b81146200027057600080fd5b50565b600081519050620002848162000259565b92915050565b6000819050919050565b6200029f816200028a565b8114620002ab57600080fd5b50565b600081519050620002bf8162000294565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620002f282620002c5565b9050919050565b6200030481620002e5565b81146200031057600080fd5b50565b6000815190506200032481620002f9565b92915050565b600080600080600060a08688031215620003495762000348620000c3565b5b600086015167ffffffffffffffff8111156200036a5762000369620000c8565b5b620003788882890162000219565b955050602086015167ffffffffffffffff8111156200039c576200039b620000c8565b5b620003aa8882890162000219565b9450506040620003bd8882890162000273565b9350506060620003d088828901620002ae565b9250506080620003e38882890162000313565b9150509295509295909350565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200044357607f821691505b602082108103620004595762000458620003fb565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b60008160020a8302905092915050565b600060088302620004c67fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000484565b620004d2868362000484565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006200051f620005196200051384620004ea565b620004f4565b620004ea565b9050919050565b6000819050919050565b6200053b83620004fe565b620005536200054a8262000526565b84845462000494565b825550505050565b600090565b6200056a6200055b565b6200057781848462000530565b505050565b5b818110156200059f576200059360008262000560565b6001810190506200057d565b5050565b601f821115620005ee57620005b8816200045f565b620005c38462000474565b81016020851015620005d3578190505b620005eb620005e28562000474565b8301826200057c565b50505b505050565b60008160020a8304905092915050565b60006200061660001984600802620005f3565b1980831691505092915050565b600062000631838362000603565b9150826002028217905092915050565b6200064c82620003f0565b67ffffffffffffffff811115620006685762000667620000e8565b5b6200067482546200042a565b62000681828285620005a3565b600060209050601f831160018114620006b95760008415620006a4578287015190505b620006b0858262000623565b86555062000720565b601f198416620006c9866200045f565b60005b82811015620006f357848901518255600182019150602085019450602081019050620006cc565b868310156200071357848901516200070f601f89168262000603565b8355505b6001600288020188555050505b505050505050565b60805161235862000744600039600061055f01526123586000f3fe608060405234801561001057600080fd5b5060043610610128576000357c01000000000000000000000000000000000000000000000000000000009004806351cff8d9116100bf5780638da5cb5b1161008e5780638da5cb5b146102d757806395d89b41146102f5578063d9caed1214610313578063f2fde38b1461032f578063f3fef3a31461035f57610128565b806351cff8d91461023f57806369fe0e2d1461026f57806386fe212d1461028b5780638705fcd4146102bb57610128565b80632c1758c1116100fb5780632c1758c1146101b7578063313ce567146101e757806331a5995d1461020557806347e7ef241461022357610128565b806301ffc9a71461012d57806306fdde031461015d57806318160ddd1461017b57806318cbbcfc14610199575b600080fd5b610147600480360381019061014291906117ba565b61038f565b6040516101549190611802565b60405180910390f35b610165610491565b60405161017291906118ad565b60405180910390f35b61018361051f565b60405161019091906118e8565b60405180910390f35b6101a1610525565b6040516101ae91906118e8565b60405180910390f35b6101d160048036038101906101cc919061192f565b61052a565b6040516101de9190611802565b60405180910390f35b6101ef61055d565b6040516101fc91906118e8565b60405180910390f35b61020d610581565b60405161021a91906118e8565b60405180910390f35b61023d600480360381019061023891906119ba565b610587565b005b610259600480360381019061025491906119fa565b61076d565b60405161026691906118e8565b60405180910390f35b6102896004803603810190610284919061192f565b6107c4565b005b6102a560048036038101906102a0919061192f565b6108ee565b6040516102b291906118e8565b60405180910390f35b6102d560048036038101906102d091906119fa565b6109d6565b005b6102df610af5565b6040516102ec9190611a36565b60405180910390f35b6102fd610b19565b60405161030a91906118ad565b60405180910390f35b61032d60048036038101906103289190611a51565b610ba7565b005b610349600480360381019061034491906119fa565b610fcd565b6040516103569190611802565b60405180910390f35b610379600480360381019061037491906119ba565b61110f565b60405161038691906118e8565b60405180910390f35b60006301ffc9a77c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916036103e3576001905061048c565b639493f8b27c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191603610435576001905061048c565b630d7491f87c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191603610487576001905061048c565b600090505b919050565b6006805461049e90611ad3565b80601f01602080910402602001604051908101604052809291908181526020018280546104ca90611ad3565b80156105175780601f106104ec57610100808354040283529160200191610517565b820191906000526020600020905b8154815290600101906020018083116104fa57829003601f168201915b505050505081565b60085481565b600381565b60006003821061053957600080fd5b6000820361054e576003600a54149050610558565b81600a5483161490505b919050565b7f000000000000000000000000000000000000000000000000000000000000000081565b600a5481565b600060606105b784600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16611369565b8373ffffffffffffffffffffffffffffffffffffffff163330856040516024016105e393929190611b63565b6040516020818303038152906040527f23b872dd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161066d9190611be1565b6000604051808303816000865af19150503d80600081146106aa576040519150601f19603f3d011682016040523d82523d6000602084013e6106af565b606091505b508092508193505050816106f8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106ef90611c44565b60405180910390fd5b8080602001905181019061070c9190611c90565b91508161074e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161074590611d09565b60405180910390fd5b82600860008282546107609190611d58565b9250508190555050505050565b600080600960008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490506107bc838261110f565b915050919050565b6107d1600160ff1661052a565b15610811576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161080890611dd8565b60405180910390fd5b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461089f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161089690611e44565b60405180910390fd5b620f424081106108e4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108db90611eb0565b60405180910390fd5b8060038190555050565b60006003821115610934576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161092b90611f1c565b60405180910390fd5b6000600a5483161461097b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161097290611f88565b60405180910390fd5b81600a600082825417925050819055506003600a541415157f6b7e2e653f93b645d4ed7292d6429f96637084363e477c8aaea1a43ed13c284e600a546040516109c491906118e8565b60405180910390a2600a549050919050565b6109e3600260ff1661052a565b15610a23576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a1a90611dd8565b60405180910390fd5b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610ab1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610aa890611e44565b60405180910390fd5b80600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60078054610b2690611ad3565b80601f0160208091040260200160405190810160405280929190818152602001828054610b5290611ad3565b8015610b9f5780601f10610b7457610100808354040283529160200191610b9f565b820191906000526020600020905b815481529060010190602001808311610b8257829003601f168201915b505050505081565b600060606000806000610bb986611542565b90508086610bc79190611fa8565b9250610bd488888561156f565b92508773ffffffffffffffffffffffffffffffffffffffff1630604051602401610bfe9190611fdc565b6040516020818303038152906040527f70a08231000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610c889190611be1565b6000604051808303816000865af19150503d8060008114610cc5576040519150601f19603f3d011682016040523d82523d6000602084013e610cca565b606091505b50809550819650505084610d13576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d0a90611c44565b60405180910390fd5b83806020019051810190610d27919061200c565b91508083610d359190611d58565b821015610d77576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d6e90612085565b60405180910390fd5b610d818787610587565b8773ffffffffffffffffffffffffffffffffffffffff163384604051602401610dab9291906120a5565b6040516020818303038152906040527fa9059cbb000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610e359190611be1565b6000604051808303816000865af19150503d8060008114610e72576040519150601f19603f3d011682016040523d82523d6000602084013e610e77565b606091505b50809550819650505084610ec0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610eb790611c44565b60405180910390fd5b83806020019051810190610ed49190611c90565b945084610f16576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f0d90611d09565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614610fc35780600960008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610fbb9190611d58565b925050819055505b5050505050505050565b60008060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461102857600080fd5b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050826000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a36001915050919050565b6000806060600073ffffffffffffffffffffffffffffffffffffffff16600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16036111a5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161119c90611e44565b60405180910390fd5b8473ffffffffffffffffffffffffffffffffffffffff1630600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16866040516024016111f3939291906120ce565b6040516020818303038152906040527f23b872dd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161127d9190611be1565b6000604051808303816000865af19150503d80600081146112ba576040519150601f19603f3d011682016040523d82523d6000602084013e6112bf565b606091505b50809250819350505081611308576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112ff90611c44565b60405180910390fd5b8080602001905181019061131c9190611c90565b91508161135e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161135590611d09565b60405180910390fd5b839250505092915050565b60006060600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036113a857505061153e565b8273ffffffffffffffffffffffffffffffffffffffff16846040516024016113d09190611a36565b6040516020818303038152906040527f3ef25013000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161145a9190611be1565b6000604051808303816000865af19150503d8060008114611497576040519150601f19603f3d011682016040523d82523d6000602084013e61149c565b606091505b508092508193505050816114e5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114dc90612151565b60405180910390fd5b808060200190518101906114f99190611c90565b91508161153b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611532906121bd565b60405180910390fd5b50505b5050565b6000806003548361155391906121dd565b9050620f424081611564919061224e565b905080915050919050565b600080606060008073ffffffffffffffffffffffffffffffffffffffff16600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16036115d657849350505050611756565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168787876040516024016116249392919061227f565b6040516020818303038152906040527fdbb21d40000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516116ae9190611be1565b6000604051808303816000865af19150503d80600081146116eb576040519150601f19603f3d011682016040523d82523d6000602084013e6116f0565b606091505b50809350819450505082611739576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161173090612302565b60405180910390fd5b8180602001905181019061174d919061200c565b90508093505050505b9392505050565b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61179781611762565b81146117a257600080fd5b50565b6000813590506117b48161178e565b92915050565b6000602082840312156117d0576117cf61175d565b5b60006117de848285016117a5565b91505092915050565b60008115159050919050565b6117fc816117e7565b82525050565b600060208201905061181760008301846117f3565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561185757808201518184015260208101905061183c565b60008484015250505050565b6000601f19601f8301169050919050565b600061187f8261181d565b6118898185611828565b9350611899818560208601611839565b6118a281611863565b840191505092915050565b600060208201905081810360008301526118c78184611874565b905092915050565b6000819050919050565b6118e2816118cf565b82525050565b60006020820190506118fd60008301846118d9565b92915050565b61190c816118cf565b811461191757600080fd5b50565b60008135905061192981611903565b92915050565b6000602082840312156119455761194461175d565b5b60006119538482850161191a565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006119878261195c565b9050919050565b6119978161197c565b81146119a257600080fd5b50565b6000813590506119b48161198e565b92915050565b600080604083850312156119d1576119d061175d565b5b60006119df858286016119a5565b92505060206119f08582860161191a565b9150509250929050565b600060208284031215611a1057611a0f61175d565b5b6000611a1e848285016119a5565b91505092915050565b611a308161197c565b82525050565b6000602082019050611a4b6000830184611a27565b92915050565b600080600060608486031215611a6a57611a6961175d565b5b6000611a78868287016119a5565b9350506020611a89868287016119a5565b9250506040611a9a8682870161191a565b9150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680611aeb57607f821691505b602082108103611afe57611afd611aa4565b5b50919050565b6000819050919050565b6000611b29611b24611b1f8461195c565b611b04565b61195c565b9050919050565b6000611b3b82611b0e565b9050919050565b6000611b4d82611b30565b9050919050565b611b5d81611b42565b82525050565b6000606082019050611b786000830186611a27565b611b856020830185611b54565b611b9260408301846118d9565b949350505050565b600081519050919050565b600081905092915050565b6000611bbb82611b9a565b611bc58185611ba5565b9350611bd5818560208601611839565b80840191505092915050565b6000611bed8284611bb0565b915081905092915050565b7f4552525f544f4b454e0000000000000000000000000000000000000000000000600082015250565b6000611c2e600983611828565b9150611c3982611bf8565b602082019050919050565b60006020820190508181036000830152611c5d81611c21565b9050919050565b611c6d816117e7565b8114611c7857600080fd5b50565b600081519050611c8a81611c64565b92915050565b600060208284031215611ca657611ca561175d565b5b6000611cb484828501611c7b565b91505092915050565b7f4552525f5452414e534645520000000000000000000000000000000000000000600082015250565b6000611cf3600c83611828565b9150611cfe82611cbd565b602082019050919050565b60006020820190508181036000830152611d2281611ce6565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000611d63826118cf565b9150611d6e836118cf565b9250828201905080821115611d8657611d85611d29565b5b92915050565b7f4552525f5345414c000000000000000000000000000000000000000000000000600082015250565b6000611dc2600883611828565b9150611dcd82611d8c565b602082019050919050565b60006020820190508181036000830152611df181611db5565b9050919050565b7f4552525f41585800000000000000000000000000000000000000000000000000600082015250565b6000611e2e600783611828565b9150611e3982611df8565b602082019050919050565b60006020820190508181036000830152611e5d81611e21565b9050919050565b7f4552525f4645455f544f4f5f4849474800000000000000000000000000000000600082015250565b6000611e9a601083611828565b9150611ea582611e64565b602082019050919050565b60006020820190508181036000830152611ec981611e8d565b9050919050565b7f4552525f494e56414c49445f5354415445000000000000000000000000000000600082015250565b6000611f06601183611828565b9150611f1182611ed0565b602082019050919050565b60006020820190508181036000830152611f3581611ef9565b9050919050565b7f4552525f414c52454144595f4c4f434b45440000000000000000000000000000600082015250565b6000611f72601283611828565b9150611f7d82611f3c565b602082019050919050565b60006020820190508181036000830152611fa181611f65565b9050919050565b6000611fb3826118cf565b9150611fbe836118cf565b9250828203905081811115611fd657611fd5611d29565b5b92915050565b6000602082019050611ff16000830184611b54565b92915050565b60008151905061200681611903565b92915050565b6000602082840312156120225761202161175d565b5b600061203084828501611ff7565b91505092915050565b7f4552525f42414c414e4345000000000000000000000000000000000000000000600082015250565b600061206f600b83611828565b915061207a82612039565b602082019050919050565b6000602082019050818103600083015261209e81612062565b9050919050565b60006040820190506120ba6000830185611a27565b6120c760208301846118d9565b9392505050565b60006060820190506120e36000830186611b54565b6120f06020830185611a27565b6120fd60408301846118d9565b949350505050565b7f4552525f52454749535452590000000000000000000000000000000000000000600082015250565b600061213b600c83611828565b915061214682612105565b602082019050919050565b6000602082019050818103600083015261216a8161212e565b9050919050565b7f4552525f554e415554485f544f4b454e00000000000000000000000000000000600082015250565b60006121a7601083611828565b91506121b282612171565b602082019050919050565b600060208201905081810360008301526121d68161219a565b9050919050565b60006121e8826118cf565b91506121f3836118cf565b9250828202612201816118cf565b9150828204841483151761221857612217611d29565b5b5092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000612259826118cf565b9150612264836118cf565b9250826122745761227361221f565b5b828204905092915050565b60006060820190506122946000830186611a27565b6122a16020830185611a27565b6122ae60408301846118d9565b949350505050565b7f4552525f51554f54455200000000000000000000000000000000000000000000600082015250565b60006122ec600a83611828565b91506122f7826122b6565b602082019050919050565b6000602082019050818103600083015261231b816122df565b905091905056fea2646970667358221220d38fee131e8fea6c9d1b9f58721fb65c2c5eb44a9498caf17e179a79ef7e9b6764736f6c63430008130033 +\ No newline at end of file diff --git a/python/erc20_pool/data/SwapPool.json b/python/erc20_pool/data/SwapPool.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint8","name":"_decimals","type":"uint8"},{"internalType":"bytes32","name":"_declaration","type":"bytes32"},{"internalType":"address","name":"_tokenRegistry","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bool","name":"_final","type":"bool"},{"indexed":false,"internalType":"uint256","name":"_sealState","type":"uint256"}],"name":"SealStateChange","type":"event"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_state","type":"uint256"}],"name":"isSealed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSealState","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_state","type":"uint256"}],"name":"seal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sealState","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_feeAddress","type":"address"}],"name":"setFeeAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_sum","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_outToken","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_outToken","type":"address"},{"internalType":"address","name":"_inToken","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_outToken","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}] diff --git a/python/erc20_pool/data/SwapPool.metadata.json b/python/erc20_pool/data/SwapPool.metadata.json @@ -0,0 +1 @@ +{"compiler":{"version":"0.8.19+commit.7dd6d404"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint8","name":"_decimals","type":"uint8"},{"internalType":"bytes32","name":"_declaration","type":"bytes32"},{"internalType":"address","name":"_tokenRegistry","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bool","name":"_final","type":"bool"},{"indexed":false,"internalType":"uint256","name":"_sealState","type":"uint256"}],"name":"SealStateChange","type":"event"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_state","type":"uint256"}],"name":"isSealed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSealState","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_state","type":"uint256"}],"name":"seal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sealState","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_feeAddress","type":"address"}],"name":"setFeeAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_sum","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_outToken","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_outToken","type":"address"},{"internalType":"address","name":"_inToken","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_outToken","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"SwapPool.sol":"SwapPool"},"evmVersion":"byzantium","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"SwapPool.sol":{"keccak256":"0xe353d1255baee8fd9937c51600b0710e041108f041062a0130e6833a51c5c873","license":"AGPL-3.0-or-later","urls":["bzz-raw://56811f458f4310e26fbe8cf177eff3165898f324bfe74a6ca954771d20f3b498","dweb:/ipfs/QmYa1bep3hAvcNr7w1PHjRa65CLUkcJKmZRftKXZGG8wwS"]}},"version":1} diff --git a/python/erc20_pool/data/__init__.py b/python/erc20_pool/data/__init__.py @@ -0,0 +1,3 @@ +import os + +data_dir = os.path.realpath(os.path.dirname(__file__)) diff --git a/python/erc20_pool/pool.py b/python/erc20_pool/pool.py @@ -0,0 +1,119 @@ +# standard imports +import logging +import os +import enum + +# external imports +from chainlib.eth.constant import ZERO_ADDRESS +from chainlib.eth.constant import ZERO_CONTENT +from chainlib.eth.contract import ( + ABIContractEncoder, + ABIContractDecoder, + ABIContractType, + abi_decode_single, +) +from chainlib.eth.jsonrpc import to_blockheight_param +from chainlib.eth.error import RequestMismatchException +from chainlib.eth.tx import ( + TxFactory, + TxFormat, +) +from chainlib.jsonrpc import JSONRPCRequest +from chainlib.block import BlockSpec +from hexathon import ( + add_0x, + strip_0x, +) +from chainlib.eth.cli.encode import CLIEncoder + +# local imports +from erc20_pool.data import data_dir + +logg = logging.getLogger() + + +class Pool(TxFactory): + + __abi = None + __bytecode = None + + def constructor(self, sender_address, name, symbol, decimals, declaration=None, accounts_registry=None, tx_format=TxFormat.JSONRPC, version=None): + code = self.cargs(name, symbol, decimals, declaration=declaration, accounts_registry=accounts_registry, version=version) + tx = self.template(sender_address, None, use_nonce=True) + tx = self.set_code(tx, code) + return self.finalize(tx, tx_format) + + + @staticmethod + def cargs(name, symbol, decimals, declaration=None, accounts_registry=None, version=None): + if declaration == None: + declaration = ZERO_CONTENT + if accounts_registry == None: + accounts_registry = ZERO_ADDRESS + code = Pool.bytecode(version=version) + enc = ABIContractEncoder() + enc.string(name) + enc.string(symbol) + enc.uint256(decimals) + enc.bytes32(declaration) + enc.address(accounts_registry) + args = enc.get() + code += args + logg.debug('constructor code: ' + args) + return code + + + @staticmethod + def gas(code=None): + return 4000000 + + + @staticmethod + def abi(): + if Pool.__abi == None: + f = open(os.path.join(data_dir, 'SwapPool.json'), 'r') + Pool.__abi = json.load(f) + f.close() + return Pool.__abi + + + @staticmethod + def bytecode(version=None): + if Pool.__bytecode == None: + f = open(os.path.join(data_dir, 'SwapPool.bin')) + Pool.__bytecode = f.read() + f.close() + return Pool.__bytecode + + + def deposit(self, contract_address, sender_address, token_address, value, tx_format=TxFormat.JSONRPC, id_generator=None): + enc = ABIContractEncoder() + enc.method('deposit') + enc.typ(ABIContractType.ADDRESS) + enc.typ(ABIContractType.UINT256) + enc.address(token_address) + enc.uint256(value) + data = add_0x(enc.get()) + tx = self.template(sender_address, contract_address, use_nonce=True) + tx = self.set_code(tx, data) + tx = self.finalize(tx, tx_format, id_generator=id_generator) + return tx + + + def swap(self, contract_address, sender_address, token_address_out, token_address_in, value, tx_format=TxFormat.JSONRPC, id_generator=None): + enc = ABIContractEncoder() + enc.method('withdraw') + enc.typ(ABIContractType.ADDRESS) + enc.typ(ABIContractType.ADDRESS) + enc.typ(ABIContractType.UINT256) + enc.address(token_address_out) + enc.address(token_address_in) + enc.uint256(value) + data = add_0x(enc.get()) + tx = self.template(sender_address, contract_address, use_nonce=True) + tx = self.set_code(tx, data) + tx = self.finalize(tx, tx_format, id_generator=id_generator) + return tx + + + diff --git a/python/erc20_pool/unittest/__init__.py b/python/erc20_pool/unittest/__init__.py @@ -0,0 +1 @@ +from .base import * diff --git a/python/erc20_pool/unittest/base.py b/python/erc20_pool/unittest/base.py @@ -0,0 +1,63 @@ +# standard imports +import logging +import time + +# external imports +from chainlib.eth.unittest.ethtester import EthTesterCase +from chainlib.connection import RPCConnection +from chainlib.eth.nonce import RPCNonceOracle +from chainlib.eth.tx import receipt +from chainlib.eth.address import to_checksum_address +from giftable_erc20_token.unittest import TestGiftableToken +from eth_erc20 import ERC20 +from chainlib.eth.block import block_latest +from eth_accounts_index.unittest import TestAccountsIndex +from eth_accounts_index.registry import AccountRegistry +from giftable_erc20_token import GiftableToken + +# local imports +from erc20_pool import Pool + +logg = logging.getLogger(__name__) + +hash_of_foo = '2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae' +hash_of_bar = 'fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9' +hash_of_baz = 'baa5a0964d3320fbc0c6a922140453c8513ea24ab8fd0577034804a967248096' + + +class TestERC20Pool(TestGiftableToken): + + expire = 0 + + def setUp(self): + super(TestERC20Pool, self).setUp() + + self.foo_address = self.address + self.bar_address = self.publish_giftable_token('Bar Token', 'BAR', 16) + self.baz_address = self.publish_giftable_token('Baz Token', 'BAZ', 16) + self.initial_supply_bar = 1 << 20 + self.initial_supply_baz = 1 << 15 + + nonce_oracle = RPCNonceOracle(self.accounts[0], conn=self.conn) + c = GiftableToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash, o) = c.mint_to(self.bar_address, self.accounts[0], self.accounts[1], self.initial_supply_bar) + r = self.conn.do(o) + o = receipt(tx_hash) + r = self.conn.do(o) + self.assertEqual(r['status'], 1) + + (tx_hash, o) = c.mint_to(self.baz_address, self.accounts[0], self.accounts[2], self.initial_supply_baz) + r = self.conn.do(o) + o = receipt(tx_hash) + r = self.conn.do(o) + self.assertEqual(r['status'], 1) + + c = Pool(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash, o) = c.constructor(self.accounts[0], "Big Pool", "BIG", 16) + self.rpc.do(o) + o = receipt(tx_hash) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + self.voter_address = to_checksum_address(r['contract_address']) + logg.debug('published bar token {}, baz token {}'.format(self.bar_address, self.baz_address)) + logg.debug('published voter on address {} with hash {}'.format(self.voter_address, tx_hash)) diff --git a/python/requirements.txt b/python/requirements.txt @@ -0,0 +1,3 @@ +eth-erc20~=0.7.3 +chainlib-eth~=0.4.22 +#chainlib~=0.4.14 diff --git a/python/test_requirements.txt b/python/test_requirements.txt @@ -0,0 +1,4 @@ +eth_tester==0.5.0b3 +py-evm==0.3.0a20 +eth-interface==0.1.1 +eth-accounts-index~=0.5.3 diff --git a/python/tests/test_base.py b/python/tests/test_base.py @@ -0,0 +1,77 @@ +# standard imports +import unittest +import logging +import os +from chainlib.eth.nonce import RPCNonceOracle +from chainlib.eth.tx import receipt +from chainlib.eth.block import block_latest +from hexathon import same as same_hex +from eth_erc20 import ERC20 +from giftable_erc20_token import GiftableToken + +# local imports +from erc20_pool.unittest import TestERC20Pool +from erc20_pool import Pool +#from evm_tokenvote.unittest.base import hash_of_foo +#from evm_tokenvote import Voter +#from evm_tokenvote import ProposalState + + +logging.basicConfig(level=logging.DEBUG) +logg = logging.getLogger() + +class TestPoolBase(TestERC20Pool): + + def test_deposit(self): + nonce_oracle = RPCNonceOracle(self.accounts[0], conn=self.conn) + c = ERC20(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash, o) = c.approve(self.foo_address, self.accounts[0], self.voter_address, 1024) + self.rpc.do(o) + + c = Pool(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash, o) = c.deposit(self.voter_address, self.accounts[0], self.foo_address, 1024) + self.rpc.do(o) + o = receipt(tx_hash) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + c = ERC20(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + o = c.balance_of(self.foo_address, self.voter_address, sender_address=self.accounts[0]) + r = self.rpc.do(o) + self.assertEqual(int(r, 16), 1024) + + + def test_swap(self): + nonce_oracle = RPCNonceOracle(self.accounts[0], conn=self.conn) + c = ERC20(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash, o) = c.approve(self.foo_address, self.accounts[0], self.voter_address, 1024) + self.rpc.do(o) + + c = Pool(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash, o) = c.deposit(self.voter_address, self.accounts[0], self.foo_address, 1024) + self.rpc.do(o) + + nonce_oracle = RPCNonceOracle(self.accounts[1], conn=self.conn) + c = ERC20(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash, o) = c.approve(self.bar_address, self.accounts[1], self.voter_address, 768) + self.rpc.do(o) + + c = ERC20(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash, o) = c.approve(self.foo_address, self.accounts[1], self.voter_address, 768) + self.rpc.do(o) + + c = Pool(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash, o) = c.swap(self.voter_address, self.accounts[1], self.foo_address, self.bar_address, 768) + self.rpc.do(o) + o = receipt(tx_hash) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + c = ERC20(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + o = c.balance_of(self.foo_address, self.accounts[1], sender_address=self.accounts[0]) + r = self.rpc.do(o) + self.assertEqual(int(r, 16), 768) + + +if __name__ == '__main__': + unittest.main() diff --git a/solidity/Makefile b/solidity/Makefile @@ -7,6 +7,6 @@ all: truncate -s -1 SwapPool.bin install: all - cp -v *.json ../python/erc20_swap_pool/data/ - cp -v *.bin ../python/erc20_swap_pool/data/ + cp -v *.json ../python/erc20_pool/data/ + cp -v *.bin ../python/erc20_pool/data/ diff --git a/solidity/SwapPool.sol b/solidity/SwapPool.sol @@ -6,27 +6,98 @@ pragma solidity ^0.8.0; // Description: Voting contract using ERC20 tokens as shares contract SwapPool { + // Implements EIP173 + address public owner; + address registry; address quoter; uint256 feePpm; address feeAddress; + bytes32 declaration; + + string public name; + string public symbol; + uint256 public immutable decimals; + uint256 public totalSupply; + mapping ( address => uint256 ) fees; - - constructor(address _tokenRegistry) { + + // Implements Seal + uint256 public sealState; + uint8 constant FEE_STATE = 1; + uint8 constant FEEADDRESS_STATE = 2; + uint256 constant public maxSealState = 3; + + // Implements Seal + event SealStateChange(bool indexed _final, uint256 _sealState); + + // EIP173 + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); // EIP173 + + constructor(string memory _name, string memory _symbol, uint8 _decimals, bytes32 _declaration, address _tokenRegistry) { + name = _name; + symbol = _symbol; + decimals = _decimals; registry = _tokenRegistry; + declaration = _declaration; + } + + function seal(uint256 _state) public returns(uint256) { + require(_state <= maxSealState, 'ERR_INVALID_STATE'); + require(_state & sealState == 0, 'ERR_ALREADY_LOCKED'); + sealState |= _state; + emit SealStateChange(sealState == maxSealState, sealState); + return uint256(sealState); + } + + function isSealed(uint256 _state) public view returns(bool) { + require(_state < maxSealState); + if (_state == 0) { + return sealState == maxSealState; + } + return _state & sealState == _state; + } + + // Change address for collecting fees + function setFeeAddress(address _feeAddress) public { + require(!isSealed(FEEADDRESS_STATE), "ERR_SEAL"); + require(msg.sender == owner, "ERR_AXX"); + feeAddress = _feeAddress; + } + + // Change address for collecting fees + function setFee(uint256 _fee) public { + require(!isSealed(FEE_STATE), "ERR_SEAL"); + require(msg.sender == owner, "ERR_AXX"); + require(_fee < 1000000, "ERR_FEE_TOO_HIGH"); + feePpm = _fee; + } + + // Implements EIP173 + function transferOwnership(address _newOwner) public returns (bool) { + address oldOwner; + + require(msg.sender == owner); + oldOwner = owner; + owner = _newOwner; + + emit OwnershipTransferred(oldOwner, owner); + return true; } function deposit(address _token, uint256 _value) public { bool r; bytes memory v; - allowedToken(_token, registry); + mustAllowedToken(_token, registry); (r, v) = _token.call(abi.encodeWithSignature('transferFrom(address,address,uint256)', msg.sender, this, _value)); require(r, "ERR_TOKEN"); r = abi.decode(v, (bool)); require(r, "ERR_TRANSFER"); + + totalSupply += _value; } function getFee(uint256 _value) private view returns (uint256) { @@ -71,7 +142,7 @@ contract SwapPool { deposit(_inToken, _value); - (r, v) = _outToken.call(abi.encodeWithSignature('transferFrom(address,address,uint256)', this, msg.sender, netValue)); + (r, v) = _outToken.call(abi.encodeWithSignature('transfer(address,uint256)', msg.sender, netValue)); require(r, "ERR_TOKEN"); r = abi.decode(v, (bool)); require(r, "ERR_TRANSFER"); @@ -81,11 +152,30 @@ contract SwapPool { } } - function withdraw(address _token, uint256 _value) public returns (uint256) { + // Withdraw token to fee address + function withdraw(address _outToken) public returns (uint256) { + uint256 balance; + balance = fees[_outToken]; + + return withdraw(_outToken, balance); } - function allowedToken(address _token, address _registry) private { + function withdraw(address _outToken, uint256 _value) public returns (uint256) { + bool r; + bytes memory v; + + require(feeAddress != address(0), "ERR_AXX"); + + (r, v) = _outToken.call(abi.encodeWithSignature('transferFrom(address,address,uint256)', this, feeAddress, _value)); + require(r, "ERR_TOKEN"); + r = abi.decode(v, (bool)); + require(r, "ERR_TRANSFER"); + + return _value; + } + + function mustAllowedToken(address _token, address _registry) private { bool r; bytes memory v; @@ -98,4 +188,18 @@ contract SwapPool { r = abi.decode(v, (bool)); require(r, "ERR_UNAUTH_TOKEN"); } + + // Implements EIP165 + function supportsInterface(bytes4 _sum) public pure returns (bool) { + if (_sum == 0x01ffc9a7) { // ERC165 + return true; + } + if (_sum == 0x9493f8b2) { // ERC173 + return true; + } + if (_sum == 0x0d7491f8) { // Seal + return true; + } + return false; + } }