erc20-limiter

ERC20 balance limit registry
Log | Files | Refs | README

commit ccc616a1dc8505f37406722a4a11a554ed7075e1
parent 902d4d790ed35f61e7a1ab2ac596e9961e0dddb7
Author: lash <dev@holbrook.no>
Date:   Thu, 27 Jul 2023 18:10:48 +0100

Add python harness

Diffstat:
A.gitignore | 7+++++++
Apython/erc20_limiter/__init__.py | 1+
Apython/erc20_limiter/data/Limiter.bin | 2++
Apython/erc20_limiter/data/Limiter.json | 1+
Apython/erc20_limiter/data/Limiter.metadata.json | 1+
Apython/erc20_limiter/data/__init__.py | 3+++
Apython/erc20_limiter/limiter.py | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apython/erc20_limiter/unittest/__init__.py | 1+
Apython/erc20_limiter/unittest/base.py | 35+++++++++++++++++++++++++++++++++++
Apython/requirements.txt | 1+
Apython/test_requirements.txt | 3+++
Apython/tests/test_base.py | 30++++++++++++++++++++++++++++++
12 files changed, 176 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,7 @@ +__pycache__ +*.pyc +*.egg-info +dist/ +build/ +solidity/*.json +solidity/*.bin diff --git a/python/erc20_limiter/__init__.py b/python/erc20_limiter/__init__.py @@ -0,0 +1 @@ +from .limiter import Limiter diff --git a/python/erc20_limiter/data/Limiter.bin b/python/erc20_limiter/data/Limiter.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b506102c6806100206000396000f3fe608060405234801561001057600080fd5b5060043610610053576000357c010000000000000000000000000000000000000000000000000000000090048063237786131461005857806336db43b514610088575b600080fd5b610072600480360381019061006d91906101b0565b6100a4565b60405161007f9190610209565b60405180910390f35b6100a2600480360381019061009d9190610250565b6100c9565b005b6000602052816000526040600020602052806000526040600020600091509150505481565b806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061017d82610152565b9050919050565b61018d81610172565b811461019857600080fd5b50565b6000813590506101aa81610184565b92915050565b600080604083850312156101c7576101c661014d565b5b60006101d58582860161019b565b92505060206101e68582860161019b565b9150509250929050565b6000819050919050565b610203816101f0565b82525050565b600060208201905061021e60008301846101fa565b92915050565b61022d816101f0565b811461023857600080fd5b50565b60008135905061024a81610224565b92915050565b600080604083850312156102675761026661014d565b5b60006102758582860161019b565b92505060206102868582860161023b565b915050925092905056fea2646970667358221220b0b99ecbbe087d46a73d112695aeff90259e3319f10106bab0b8330733dbea4364736f6c63430008130033 +\ No newline at end of file diff --git a/python/erc20_limiter/data/Limiter.json b/python/erc20_limiter/data/Limiter.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"limitOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"setLimit","outputs":[],"stateMutability":"nonpayable","type":"function"}] diff --git a/python/erc20_limiter/data/Limiter.metadata.json b/python/erc20_limiter/data/Limiter.metadata.json @@ -0,0 +1 @@ +{"compiler":{"version":"0.8.19+commit.7dd6d404"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"limitOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"setLimit","outputs":[],"stateMutability":"nonpayable","type":"function"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"Limiter.sol":"Limiter"},"evmVersion":"byzantium","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"Limiter.sol":{"keccak256":"0x24351d76034fded7d12ba485309035752a6c960c49e0079c3ad22eaeb11dad9d","license":"AGPL-3.0-or-later","urls":["bzz-raw://2982f178296a2675687b4b65110529f439cd2bcb837579e9658bbf913a978d0b","dweb:/ipfs/Qmf6g95sC1RaRZ6DJ6w2yDnBRoDPFKGid73yefj1pv4jsM"]}},"version":1} diff --git a/python/erc20_limiter/data/__init__.py b/python/erc20_limiter/data/__init__.py @@ -0,0 +1,3 @@ +import os + +data_dir = os.path.realpath(os.path.dirname(__file__)) diff --git a/python/erc20_limiter/limiter.py b/python/erc20_limiter/limiter.py @@ -0,0 +1,91 @@ +# 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_limiter.data import data_dir + +logg = logging.getLogger() + + +class Limiter(TxFactory): + + __abi = None + __bytecode = None + + def constructor(self, sender_address, tx_format=TxFormat.JSONRPC, version=None): + code = self.cargs(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(version=None): + code = Limiter.bytecode(version=version) + enc = ABIContractEncoder() + args = enc.get() + code += args + logg.debug('constructor code: ' + args) + return code + + + @staticmethod + def gas(code=None): + return 4000000 + + + @staticmethod + def abi(): + if Limiter.__abi == None: + f = open(os.path.join(data_dir, 'Limiter.json'), 'r') + Limiter.__abi = json.load(f) + f.close() + return Limiter.__abi + + + @staticmethod + def bytecode(version=None): + if Limiter.__bytecode == None: + f = open(os.path.join(data_dir, 'Limiter.bin')) + Limiter.__bytecode = f.read() + f.close() + return Limiter.__bytecode + + + def set_limit(self, contract_address, sender_address, token_address, limit, tx_format=TxFormat.JSONRPC, id_generator=None): + enc = ABIContractEncoder() + enc.method('setLimit') + enc.typ(ABIContractType.ADDRESS) + enc.typ(ABIContractType.UINT256) + enc.address(token_address) + enc.uint256(limit) + 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_limiter/unittest/__init__.py b/python/erc20_limiter/unittest/__init__.py @@ -0,0 +1 @@ +from .base import * diff --git a/python/erc20_limiter/unittest/base.py b/python/erc20_limiter/unittest/base.py @@ -0,0 +1,35 @@ +# 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 chainlib.eth.block import block_latest + +# local imports +from erc20_limiter import Limiter + +logg = logging.getLogger(__name__) + +class TestLimiter(EthTesterCase): + + def setUp(self): + super(TestLimiter, self).setUp() + self.conn = RPCConnection.connect(self.chain_spec, 'default') + self.publish_limiter() + + + def publish_limiter(self): + nonce_oracle = RPCNonceOracle(self.accounts[0], conn=self.conn) + c = Limiter(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash, o) = c.constructor(self.accounts[0]) + self.rpc.do(o) + o = receipt(tx_hash) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + self.address = to_checksum_address(r['contract_address']) + logg.debug('published limiter on address {} with hash {}'.format(self.address, tx_hash)) diff --git a/python/requirements.txt b/python/requirements.txt @@ -0,0 +1 @@ +chainlib-eth~=0.4.22 diff --git a/python/test_requirements.txt b/python/test_requirements.txt @@ -0,0 +1,3 @@ +eth_tester==0.5.0b3 +py-evm==0.3.0a20 +eth-interface==0.1.1 diff --git a/python/tests/test_base.py b/python/tests/test_base.py @@ -0,0 +1,30 @@ +# standard imports +import unittest +import logging + +# external imports +from chainlib.eth.nonce import RPCNonceOracle +from chainlib.eth.tx import receipt + +# local imports +from erc20_limiter import Limiter +from erc20_limiter.unittest import TestLimiter + +logging.basicConfig(level=logging.DEBUG) +logg = logging.getLogger() + + +class TestLimiterBase(TestLimiter): + + def test_limit(self): + nonce_oracle = RPCNonceOracle(self.accounts[0], conn=self.conn) + c = Limiter(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) + (tx_hash, o) = c.set_limit(self.address, self.accounts[0], '2c26b46b68ffc68ff99b453c1d30413413422d70', 256) + self.rpc.do(o) + o = receipt(tx_hash) + r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + + +if __name__ == '__main__': + unittest.main()