mirror of
https://github.com/grassrootseconomics/erc20-pool.git
synced 2024-11-21 11:46:47 +01:00
Implement python interface, deposit and swap test
This commit is contained in:
parent
a4fc3c1dda
commit
b5fbc778c0
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
|
*.egg-info
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
solidity/*.json
|
||||||
|
solidity/*.bin
|
1
python/erc20_pool/__init__.py
Normal file
1
python/erc20_pool/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from .pool import Pool
|
1
python/erc20_pool/data/SwapPool.bin
Normal file
1
python/erc20_pool/data/SwapPool.bin
Normal file
File diff suppressed because one or more lines are too long
1
python/erc20_pool/data/SwapPool.json
Normal file
1
python/erc20_pool/data/SwapPool.json
Normal file
@ -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"}]
|
1
python/erc20_pool/data/SwapPool.metadata.json
Normal file
1
python/erc20_pool/data/SwapPool.metadata.json
Normal file
@ -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}
|
3
python/erc20_pool/data/__init__.py
Normal file
3
python/erc20_pool/data/__init__.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
data_dir = os.path.realpath(os.path.dirname(__file__))
|
119
python/erc20_pool/pool.py
Normal file
119
python/erc20_pool/pool.py
Normal file
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
1
python/erc20_pool/unittest/__init__.py
Normal file
1
python/erc20_pool/unittest/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from .base import *
|
63
python/erc20_pool/unittest/base.py
Normal file
63
python/erc20_pool/unittest/base.py
Normal file
@ -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))
|
3
python/requirements.txt
Normal file
3
python/requirements.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
eth-erc20~=0.7.3
|
||||||
|
chainlib-eth~=0.4.22
|
||||||
|
#chainlib~=0.4.14
|
4
python/test_requirements.txt
Normal file
4
python/test_requirements.txt
Normal file
@ -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
|
77
python/tests/test_base.py
Normal file
77
python/tests/test_base.py
Normal file
@ -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()
|
@ -7,6 +7,6 @@ all:
|
|||||||
truncate -s -1 SwapPool.bin
|
truncate -s -1 SwapPool.bin
|
||||||
|
|
||||||
install: all
|
install: all
|
||||||
cp -v *.json ../python/erc20_swap_pool/data/
|
cp -v *.json ../python/erc20_pool/data/
|
||||||
cp -v *.bin ../python/erc20_swap_pool/data/
|
cp -v *.bin ../python/erc20_pool/data/
|
||||||
|
|
||||||
|
@ -6,27 +6,98 @@ pragma solidity ^0.8.0;
|
|||||||
// Description: Voting contract using ERC20 tokens as shares
|
// Description: Voting contract using ERC20 tokens as shares
|
||||||
|
|
||||||
contract SwapPool {
|
contract SwapPool {
|
||||||
|
// Implements EIP173
|
||||||
|
address public owner;
|
||||||
|
|
||||||
address registry;
|
address registry;
|
||||||
address quoter;
|
address quoter;
|
||||||
uint256 feePpm;
|
uint256 feePpm;
|
||||||
address feeAddress;
|
address feeAddress;
|
||||||
|
bytes32 declaration;
|
||||||
|
|
||||||
|
string public name;
|
||||||
|
string public symbol;
|
||||||
|
uint256 public immutable decimals;
|
||||||
|
|
||||||
uint256 public totalSupply;
|
uint256 public totalSupply;
|
||||||
|
|
||||||
mapping ( address => uint256 ) fees;
|
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;
|
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 {
|
function deposit(address _token, uint256 _value) public {
|
||||||
bool r;
|
bool r;
|
||||||
bytes memory v;
|
bytes memory v;
|
||||||
|
|
||||||
allowedToken(_token, registry);
|
mustAllowedToken(_token, registry);
|
||||||
|
|
||||||
(r, v) = _token.call(abi.encodeWithSignature('transferFrom(address,address,uint256)', msg.sender, this, _value));
|
(r, v) = _token.call(abi.encodeWithSignature('transferFrom(address,address,uint256)', msg.sender, this, _value));
|
||||||
require(r, "ERR_TOKEN");
|
require(r, "ERR_TOKEN");
|
||||||
r = abi.decode(v, (bool));
|
r = abi.decode(v, (bool));
|
||||||
require(r, "ERR_TRANSFER");
|
require(r, "ERR_TRANSFER");
|
||||||
|
|
||||||
|
totalSupply += _value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFee(uint256 _value) private view returns (uint256) {
|
function getFee(uint256 _value) private view returns (uint256) {
|
||||||
@ -71,7 +142,7 @@ contract SwapPool {
|
|||||||
|
|
||||||
deposit(_inToken, _value);
|
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");
|
require(r, "ERR_TOKEN");
|
||||||
r = abi.decode(v, (bool));
|
r = abi.decode(v, (bool));
|
||||||
require(r, "ERR_TRANSFER");
|
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;
|
bool r;
|
||||||
bytes memory v;
|
bytes memory v;
|
||||||
|
|
||||||
@ -98,4 +188,18 @@ contract SwapPool {
|
|||||||
r = abi.decode(v, (bool));
|
r = abi.decode(v, (bool));
|
||||||
require(r, "ERR_UNAUTH_TOKEN");
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user