commit 410ad0f0775a0ba64456584c54b02fef47bca364
parent 3bfe4ba642941b00c3468f7462f741400243eb45
Author: lash <dev@holbrook.no>
Date: Fri, 28 Jul 2023 12:57:08 +0100
Add token registry adapter
Diffstat:
10 files changed, 237 insertions(+), 1 deletion(-)
diff --git a/python/erc20_limiter/data/LimiterTokenRegistry.bin b/python/erc20_limiter/data/LimiterTokenRegistry.bin
@@ -0,0 +1 @@
+608060405234801561001057600080fd5b506040516109673803806109678339818101604052810190610032919061015b565b81600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505061019b565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006100ea826100bf565b9050919050565b6100fa816100df565b811461010557600080fd5b50565b600081519050610117816100f1565b92915050565b6000610128826100df565b9050919050565b6101388161011d565b811461014357600080fd5b50565b6000815190506101558161012f565b92915050565b60008060408385031215610172576101716100ba565b5b600061018085828601610108565b925050602061019185828601610146565b9150509250929050565b6107bd806101aa6000396000f3fe608060405234801561001057600080fd5b5060043610610074576000357c01000000000000000000000000000000000000000000000000000000009004806301ffc9a71461007957806323778613146100a957806336db43b5146100d95780633ef25013146100f5578063bdd5544014610125575b600080fd5b610093600480360381019061008e91906104b5565b610141565b6040516100a091906104fd565b60405180910390f35b6100c360048036038101906100be9190610576565b6101f1565b6040516100d091906105cf565b60405180910390f35b6100f360048036038101906100ee9190610616565b6102b4565b005b61010f600480360381019061010a9190610656565b6102c3565b60405161011c91906104fd565b60405180910390f35b61013f600480360381019061013a9190610683565b6103a8565b005b60006301ffc9a77c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19160361019557600190506101ec565b63b7bca6257c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916036101e757600190506101ec565b600090505b919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16632377861384846040518363ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040161026b9291906106e5565b602060405180830381865afa158015610288573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102ac9190610723565b905092915050565b6102bf8233836103a8565b5050565b60008060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16632377861384600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518363ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040161035f9291906106e5565b602060405180830381865afa15801561037c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103a09190610723565b119050919050565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663bdd554408484846040518463ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040161042193929190610750565b600060405180830381600087803b15801561043b57600080fd5b505af115801561044f573d6000803e3d6000fd5b50505050505050565b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6104928161045d565b811461049d57600080fd5b50565b6000813590506104af81610489565b92915050565b6000602082840312156104cb576104ca610458565b5b60006104d9848285016104a0565b91505092915050565b60008115159050919050565b6104f7816104e2565b82525050565b600060208201905061051260008301846104ee565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061054382610518565b9050919050565b61055381610538565b811461055e57600080fd5b50565b6000813590506105708161054a565b92915050565b6000806040838503121561058d5761058c610458565b5b600061059b85828601610561565b92505060206105ac85828601610561565b9150509250929050565b6000819050919050565b6105c9816105b6565b82525050565b60006020820190506105e460008301846105c0565b92915050565b6105f3816105b6565b81146105fe57600080fd5b50565b600081359050610610816105ea565b92915050565b6000806040838503121561062d5761062c610458565b5b600061063b85828601610561565b925050602061064c85828601610601565b9150509250929050565b60006020828403121561066c5761066b610458565b5b600061067a84828501610561565b91505092915050565b60008060006060848603121561069c5761069b610458565b5b60006106aa86828701610561565b93505060206106bb86828701610561565b92505060406106cc86828701610601565b9150509250925092565b6106df81610538565b82525050565b60006040820190506106fa60008301856106d6565b61070760208301846106d6565b9392505050565b60008151905061071d816105ea565b92915050565b60006020828403121561073957610738610458565b5b60006107478482850161070e565b91505092915050565b600060608201905061076560008301866106d6565b61077260208301856106d6565b61077f60408301846105c0565b94935050505056fea264697066735822122059ed451c7493185b9c26a93c433ec67c46b6c713de36f371528416bd733499c964736f6c63430008130033
+\ No newline at end of file
diff --git a/python/erc20_limiter/data/LimiterTokenRegistry.json b/python/erc20_limiter/data/LimiterTokenRegistry.json
@@ -0,0 +1 @@
+[{"inputs":[{"internalType":"address","name":"_holder","type":"address"},{"internalType":"contract Limiter","name":"_limiter","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"have","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_holder","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"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_holder","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"setLimitFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_sum","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"}]
diff --git a/python/erc20_limiter/data/LimiterTokenRegistry.metadata.json b/python/erc20_limiter/data/LimiterTokenRegistry.metadata.json
@@ -0,0 +1 @@
+{"compiler":{"version":"0.8.19+commit.7dd6d404"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"address","name":"_holder","type":"address"},{"internalType":"contract Limiter","name":"_limiter","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"have","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_holder","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"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_holder","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"setLimitFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_sum","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"LimiterTokenRegistry.sol":"LimiterTokenRegistry"},"evmVersion":"byzantium","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"LimiterTokenRegistry.sol":{"keccak256":"0xca5fd95fd62ac8530df26ea912eadfcb2c2a00bbf3df9ffe497d7a5c50b84497","license":"AGPL-3.0-or-later","urls":["bzz-raw://7d3e45e63f2e7fa721bd1f2c6a453926e5366bad8cfe3154323ee9d0f3476f5c","dweb:/ipfs/QmQDtyCrn7rf8LyxhrfxRd3K8CMuJuP1GL4YPyGAVETgtY"]}},"version":1}
diff --git a/python/erc20_limiter/limiter.py b/python/erc20_limiter/limiter.py
@@ -79,7 +79,9 @@ class Limiter(TxFactory):
def set_limit(self, contract_address, sender_address, token_address, limit, holder_address=None, tx_format=TxFormat.JSONRPC, id_generator=None):
enc = ABIContractEncoder()
- if holder_address != None:
+ if holder_address == None:
+ enc.method('setLimit')
+ else:
enc.method('setLimitFor')
enc.typ(ABIContractType.ADDRESS)
if holder_address != None:
diff --git a/python/erc20_limiter/token.py b/python/erc20_limiter/token.py
@@ -0,0 +1,96 @@
+# 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 LimiterTokenRegistry(TxFactory):
+
+ __abi = None
+ __bytecode = None
+
+ def constructor(self, sender_address, holder_address, limiter_address, tx_format=TxFormat.JSONRPC, version=None):
+ code = self.cargs(holder_address, limiter_address, 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(holder_address, limiter_address, version=None):
+ code = LimiterTokenRegistry.bytecode(version=version)
+ enc = ABIContractEncoder()
+ enc.address(holder_address)
+ enc.address(limiter_address)
+ args = enc.get()
+ code += args
+ logg.debug('constructor code: ' + args)
+ return code
+
+
+ @staticmethod
+ def gas(code=None):
+ return 4000000
+
+
+ @staticmethod
+ def abi():
+ if LimiterTokenRegistry.__abi == None:
+ f = open(os.path.join(data_dir, 'LimiterTokenRegistry.json'), 'r')
+ LimiterTokenRegistry.__abi = json.load(f)
+ f.close()
+ return LimiterTokenRegistry.__abi
+
+
+ @staticmethod
+ def bytecode(version=None):
+ if LimiterTokenRegistry.__bytecode == None:
+ f = open(os.path.join(data_dir, 'LimiterTokenRegistry.bin'))
+ LimiterTokenRegistry.__bytecode = f.read()
+ f.close()
+ return LimiterTokenRegistry.__bytecode
+
+
+ def have(self, contract_address, token_address, sender_address=ZERO_ADDRESS, id_generator=None):
+ j = JSONRPCRequest(id_generator)
+ o = j.template()
+ o['method'] = 'eth_call'
+ enc = ABIContractEncoder()
+ enc.method('have')
+ enc.typ(ABIContractType.ADDRESS)
+ enc.address(token_address)
+ data = add_0x(enc.get())
+ tx = self.template(sender_address, contract_address)
+ tx = self.set_code(tx, data)
+ o['params'].append(self.normalize(tx))
+ o['params'].append('latest')
+ o = j.finalize(o)
+ return o
diff --git a/python/erc20_limiter/unittest/base.py b/python/erc20_limiter/unittest/base.py
@@ -12,6 +12,7 @@ from chainlib.eth.block import block_latest
# local imports
from erc20_limiter import Limiter
+from erc20_limiter.token import LimiterTokenRegistry
logg = logging.getLogger(__name__)
@@ -34,3 +35,18 @@ class TestLimiter(EthTesterCase):
address = to_checksum_address(r['contract_address'])
logg.debug('published limiter on address {} with hash {}'.format(address, tx_hash))
return address
+
+
+class TestLimiterTokenRegistry(TestLimiter):
+
+ def publish_token_registry(self, holder_address, limiter_address):
+ nonce_oracle = RPCNonceOracle(self.accounts[0], conn=self.conn)
+ c = LimiterTokenRegistry(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
+ (tx_hash, o) = c.constructor(self.accounts[0], holder_address, limiter_address)
+ self.rpc.do(o)
+ o = receipt(tx_hash)
+ r = self.rpc.do(o)
+ self.assertEqual(r['status'], 1)
+ address = to_checksum_address(r['contract_address'])
+ logg.debug('published limiter token registry proxy on address {} with hash {}'.format(address, tx_hash))
+ return address
diff --git a/python/run_tests.sh b/python/run_tests.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+set -a
+set -e
+set -x
+default_pythonpath=$PYTHONPATH:.
+export PYTHONPATH=${default_pythonpath:-.}
+>&2 echo using pythonpath $PYTHONPATH
+for f in `ls tests/*.py`; do
+ python $f
+done
+set +x
+set +e
+set +a
diff --git a/python/tests/test_token_registry.py b/python/tests/test_token_registry.py
@@ -0,0 +1,51 @@
+# 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.token import LimiterTokenRegistry
+from erc20_limiter.unittest import TestLimiterTokenRegistry
+
+logging.basicConfig(level=logging.DEBUG)
+logg = logging.getLogger()
+
+
+class TestLimiterBase(TestLimiterTokenRegistry):
+
+ def setUp(self):
+ super(TestLimiterBase, self).setUp()
+ self.publish_limiter()
+ self.token_registry = self.publish_token_registry(self.accounts[0], self.address)
+ logg.debug('tokenreg {}'.format(self.token_registry))
+
+
+ def test_limit(self):
+ nonce_oracle = RPCNonceOracle(self.accounts[0], conn=self.conn)
+
+ foo_token = '2c26b46b68ffc68ff99b453c1d30413413422d70'
+ c = LimiterTokenRegistry(self.chain_spec)
+ o = c.have(self.token_registry, foo_token, sender_address=self.accounts[0])
+ r = self.rpc.do(o)
+ self.assertEqual(int(r, 16), 0)
+
+ c = Limiter(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
+ (tx_hash, o) = c.set_limit(self.address, self.accounts[0], foo_token, 42)
+ self.rpc.do(o)
+ o = receipt(tx_hash)
+ r = self.rpc.do(o)
+ self.assertEqual(r['status'], 1)
+
+ c = LimiterTokenRegistry(self.chain_spec)
+ o = c.have(self.token_registry, foo_token, sender_address=self.accounts[0])
+ r = self.rpc.do(o)
+ self.assertEqual(int(r, 16), 1)
+
+
+if __name__ == '__main__':
+ unittest.main()
+
diff --git a/solidity/LimiterTokenRegistry.sol b/solidity/LimiterTokenRegistry.sol
@@ -0,0 +1,48 @@
+pragma solidity ^0.8.0;
+
+// Author: Louis Holbrook <dev@holbrook.no> 0826EDA1702D1E87C6E2875121D2E7BB88C2A746
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// File-Version: 1
+// Description: Registry of allowed ERC20 balance limits per-token and per-holder.
+
+interface Limiter {
+ function limitOf(address,address) external view returns(uint256);
+ function setLimit(address,uint256) external;
+ function setLimitFor(address,address,uint256) external;
+}
+
+contract LimiterTokenRegistry {
+ Limiter limiter;
+ address holder;
+
+ constructor(address _holder, Limiter _limiter) {
+ holder = _holder;
+ limiter = _limiter;
+ }
+
+ function limitOf(address _token, address _holder) public view returns (uint256) {
+ return limiter.limitOf(_token, _holder);
+ }
+
+ function setLimit(address _token, uint256 _value) public {
+ setLimitFor(_token, msg.sender, _value);
+ }
+
+ function setLimitFor(address _token, address _holder, uint256 _value) public {
+ limiter.setLimitFor(_token, _holder, _value);
+ }
+
+ function have(address _token) public view returns(bool) {
+ return limiter.limitOf(_token, holder) > 0;
+ }
+
+ function supportsInterface(bytes4 _sum) public pure returns (bool) {
+ if (_sum == 0x01ffc9a7) { // ERC165
+ return true;
+ }
+ if (_sum == 0xb7bca625) { // AccountsIndex
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/solidity/Makefile b/solidity/Makefile
@@ -4,7 +4,12 @@ all:
$(SOLC) --bin Limiter.sol --evm-version byzantium | awk 'NR>3' > Limiter.bin
$(SOLC) --abi Limiter.sol --evm-version byzantium | awk 'NR>3' > Limiter.json
$(SOLC) --metadata Limiter.sol --evm-version byzantium | awk 'NR>3' > Limiter.metadata.json
+ $(SOLC) --bin LimiterTokenRegistry.sol --evm-version byzantium | awk 'NR>7' > LimiterTokenRegistry.bin
+ $(SOLC) --abi LimiterTokenRegistry.sol --evm-version byzantium | awk 'NR>7' > LimiterTokenRegistry.json
+ $(SOLC) --metadata LimiterTokenRegistry.sol --evm-version byzantium | awk 'NR>7' > LimiterTokenRegistry.metadata.json
+
truncate -s -1 Limiter.bin
+ truncate -s -1 LimiterTokenRegistry.bin
install: all
cp -v *.json ../python/erc20_limiter/data/