Skip to content

Commit 32c3f53

Browse files
authored
Merge pull request #201 from neonlabsorg/197-integration-test-for-erc20-wrapper-contract
197 integration test for erc20 wrapper contract
2 parents b2b28c9 + acd3486 commit 32c3f53

File tree

3 files changed

+179
-7
lines changed

3 files changed

+179
-7
lines changed

proxy/plugin/solana_rest_api_tools.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,7 @@ def create_account_list_by_emulate(signer, client, ethTrx):
760760
sender_ether = bytes.fromhex(ethTrx.sender())
761761
add_keys_05 = []
762762
trx = Transaction()
763+
new_neon_token_acccounts = []
763764

764765
output_json = call_emulated(ethTrx.toAddress.hex(), sender_ether.hex(), ethTrx.callData.hex(), hex(ethTrx.value))
765766
logger.debug("emulator returns: %s", json.dumps(output_json, indent=3))
@@ -789,7 +790,11 @@ def create_account_list_by_emulate(signer, client, ethTrx):
789790
code_account_balance = client.get_minimum_balance_for_rent_exemption(code_account_size)["result"]
790791
trx.add(createAccountWithSeedTrx(signer.public_key(), signer.public_key(), seed, code_account_balance, code_account_size, PublicKey(evm_loader_id)))
791792
add_keys_05.append(AccountMeta(pubkey=code_account, is_signer=False, is_writable=acc_desc["writable"]))
792-
trx.add(createEtherAccountTrx(client, address, evm_loader_id, signer, code_account)[0])
793+
794+
(create_trx, solana_address, token_address) = createEtherAccountTrx(client, address, evm_loader_id, signer, code_account)
795+
trx.add(create_trx)
796+
new_neon_token_acccounts.append(token_address)
797+
793798
if address == sender_ether and NEW_USER_AIRDROP_AMOUNT > 0:
794799
trx.add(transfer2(Transfer2Params(
795800
amount=NEW_USER_AIRDROP_AMOUNT*1_000_000_000,
@@ -806,13 +811,13 @@ def create_account_list_by_emulate(signer, client, ethTrx):
806811
str(NEW_USER_AIRDROP_AMOUNT))
807812

808813
for token_account in output_json["token_accounts"]:
809-
add_keys_05.append(AccountMeta(pubkey=token_account["key"], is_signer=False, is_writable=True))
814+
add_keys_05.append(AccountMeta(pubkey=PublicKey(token_account["key"]), is_signer=False, is_writable=True))
810815

811-
if token_account["new"]:
812-
trx.add(create_associated_token_account(signer.public_key(), token_account["owner"], token_account["mint"]))
816+
if token_account["new"] and (PublicKey(token_account["key"]) not in new_neon_token_acccounts):
817+
trx.add(create_associated_token_account(signer.public_key(), PublicKey(token_account["owner"]), PublicKey(token_account["mint"])))
813818

814819
for account_meta in output_json["solana_accounts"]:
815-
add_keys_05.append(AccountMeta(pubkey=account_meta["pubkey"], is_signer=account_meta["is_signer"], is_writable=account_meta["is_writable"]))
820+
add_keys_05.append(AccountMeta(pubkey=PublicKey(account_meta["pubkey"]), is_signer=account_meta["is_signer"], is_writable=account_meta["is_writable"]))
816821

817822
caller_token = get_associated_token_address(PublicKey(sender_sol), ETH_TOKEN_MINT_ID)
818823

@@ -969,7 +974,7 @@ def createEtherAccountTrx(client, ether, evm_loader_id, signer, code_acc=None):
969974
AccountMeta(pubkey=ASSOCIATED_TOKEN_PROGRAM_ID, is_signer=False, is_writable=False),
970975
AccountMeta(pubkey=rentid, is_signer=False, is_writable=False),
971976
]))
972-
return (trx, sol)
977+
return (trx, sol, associated_token)
973978

974979

975980
def write_trx_to_holder_account(signer, client, holder, ethTrx):
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
## File: test_erc20_wrapper_contract.py
2+
## Integration test for the Neon ERC20 Wrapper contract.
3+
4+
import unittest
5+
import os
6+
from web3 import Web3
7+
from solcx import install_solc
8+
9+
# install_solc(version='latest')
10+
install_solc(version='0.7.6')
11+
from solcx import compile_source
12+
13+
EXTRA_GAS = int(os.environ.get("EXTRA_GAS", "100000"))
14+
proxy_url = os.environ.get('PROXY_URL', 'http://localhost:9090/solana')
15+
proxy = Web3(Web3.HTTPProvider(proxy_url))
16+
admin = proxy.eth.account.create('issues/neonlabsorg/proxy-model.py/197/admin')
17+
user = proxy.eth.account.create('issues/neonlabsorg/proxy-model.py/197/user')
18+
proxy.eth.default_account = admin.address
19+
20+
NAME = 'NEON'
21+
SYMBOL = 'NEO'
22+
23+
# token_mint::id = "HPsV9Deocecw3GeZv1FkAPNCBRfuVyfw9MMwjwRe1xaU" in Base58
24+
# Convert Base58 to hex number:
25+
TOKEN_MINT = bytes.fromhex('f396da383e57418540f8caa598584f49a3b50d256f75cb6d94d101681d6d9d21')
26+
27+
# Standard interface of ERC20 contract to generate ABI for wrapper
28+
ERC20_INTERFACE_SOURCE = '''
29+
pragma solidity >=0.7.0;
30+
31+
interface IERC20 {
32+
function decimals() external view returns (uint8);
33+
function totalSupply() external view returns (uint256);
34+
function balanceOf(address who) external view returns (uint256);
35+
function allowance(address owner, address spender) external view returns (uint256);
36+
function transfer(address to, uint256 value) external returns (bool);
37+
function approve(address spender, uint256 value) external returns (bool);
38+
function transferFrom(address from, address to, uint256 value) external returns (bool);
39+
40+
event Transfer(address indexed from, address indexed to, uint256 value);
41+
event Approval(address indexed owner, address indexed spender, uint256 value);
42+
}
43+
'''
44+
45+
# Copy of contract: https://github.com/neonlabsorg/neon-evm/blob/develop/evm_loader/SPL_ERC20_Wrapper.sol
46+
ERC20_WRAPPER_SOURCE = '''
47+
pragma solidity >=0.7.0;
48+
49+
interface IERC20 {
50+
function decimals() external view returns (uint8);
51+
function totalSupply() external view returns (uint256);
52+
function balanceOf(address who) external view returns (uint256);
53+
function allowance(address owner, address spender) external view returns (uint256);
54+
function transfer(address to, uint256 value) external returns (bool);
55+
function approve(address spender, uint256 value) external returns (bool);
56+
function transferFrom(address from, address to, uint256 value) external returns (bool);
57+
58+
event Transfer(address indexed from, address indexed to, uint256 value);
59+
event Approval(address indexed owner, address indexed spender, uint256 value);
60+
61+
function approveSolana(bytes32 spender, uint64 value) external returns (bool);
62+
event ApprovalSolana(address indexed owner, bytes32 indexed spender, uint64 value);
63+
}
64+
65+
/*abstract*/ contract NeonERC20Wrapper /*is IERC20*/ {
66+
address constant NeonERC20 = 0xff00000000000000000000000000000000000001;
67+
68+
string public name;
69+
string public symbol;
70+
bytes32 public tokenMint;
71+
72+
constructor(
73+
string memory _name,
74+
string memory _symbol,
75+
bytes32 _tokenMint
76+
) {
77+
name = _name;
78+
symbol = _symbol;
79+
tokenMint = _tokenMint;
80+
}
81+
82+
fallback() external {
83+
bytes memory call_data = abi.encodePacked(tokenMint, msg.data);
84+
(bool success, bytes memory result) = NeonERC20.delegatecall(call_data);
85+
86+
require(success, string(result));
87+
88+
assembly {
89+
return(add(result, 0x20), mload(result))
90+
}
91+
}
92+
}
93+
'''
94+
95+
class Test_erc20_wrapper_contract(unittest.TestCase):
96+
@classmethod
97+
def setUpClass(cls):
98+
print("\n\nhttps://github.com/neonlabsorg/proxy-model.py/issues/197")
99+
print('admin.key:', admin.key.hex())
100+
print('admin.address:', admin.address)
101+
print('user.key:', user.key.hex())
102+
print('user.address:', user.address)
103+
cls.deploy_erc20_wrapper_contract(cls)
104+
105+
def deploy_erc20_wrapper_contract(self):
106+
compiled_interface = compile_source(ERC20_INTERFACE_SOURCE)
107+
interface_id, interface = compiled_interface.popitem()
108+
self.interface = interface
109+
110+
compiled_wrapper = compile_source(ERC20_WRAPPER_SOURCE)
111+
wrapper_id, wrapper_interface = compiled_wrapper.popitem()
112+
self.wrapper = wrapper_interface
113+
114+
erc20 = proxy.eth.contract(abi=self.wrapper['abi'], bytecode=wrapper_interface['bin'])
115+
nonce = proxy.eth.get_transaction_count(proxy.eth.default_account)
116+
tx = {'nonce': nonce}
117+
tx_constructor = erc20.constructor(NAME, SYMBOL, TOKEN_MINT).buildTransaction(tx)
118+
tx_deploy = proxy.eth.account.sign_transaction(tx_constructor, admin.key)
119+
#print('tx_deploy:', tx_deploy)
120+
tx_deploy_hash = proxy.eth.send_raw_transaction(tx_deploy.rawTransaction)
121+
print('tx_deploy_hash:', tx_deploy_hash.hex())
122+
tx_deploy_receipt = proxy.eth.wait_for_transaction_receipt(tx_deploy_hash)
123+
print('tx_deploy_receipt:', tx_deploy_receipt)
124+
print('deploy status:', tx_deploy_receipt.status)
125+
self.contract_address= tx_deploy_receipt.contractAddress
126+
127+
def test_erc20_name(self):
128+
erc20 = proxy.eth.contract(address=self.contract_address, abi=self.wrapper['abi'])
129+
name = erc20.functions.name().call()
130+
self.assertEqual(name, NAME)
131+
132+
def test_erc20_symbol(self):
133+
erc20 = proxy.eth.contract(address=self.contract_address, abi=self.wrapper['abi'])
134+
sym = erc20.functions.symbol().call()
135+
self.assertEqual(sym, SYMBOL)
136+
137+
def test_erc20_decimals(self):
138+
erc20 = proxy.eth.contract(address=self.contract_address, abi=self.interface['abi'])
139+
decs = erc20.functions.decimals().call()
140+
self.assertEqual(decs, 9)
141+
142+
def test_erc20_totalSupply(self):
143+
erc20 = proxy.eth.contract(address=self.contract_address, abi=self.interface['abi'])
144+
ts = erc20.functions.totalSupply().call()
145+
self.assertGreater(ts, 0)
146+
147+
def test_erc20_balanceOf(self):
148+
erc20 = proxy.eth.contract(address=self.contract_address, abi=self.interface['abi'])
149+
b = erc20.functions.balanceOf(admin.address).call()
150+
self.assertGreater(b, 0)
151+
b = erc20.functions.balanceOf(user.address).call()
152+
self.assertEqual(b, 0)
153+
154+
def test_erc20_transfer(self):
155+
erc20 = proxy.eth.contract(address=self.contract_address, abi=self.interface['abi'])
156+
nonce = proxy.eth.get_transaction_count(proxy.eth.default_account)
157+
tx = {'nonce': nonce}
158+
tx = erc20.functions.transfer(user.address, 1000).buildTransaction(tx)
159+
tx = proxy.eth.account.sign_transaction(tx, admin.key)
160+
tx_hash = proxy.eth.send_raw_transaction(tx.rawTransaction)
161+
print('tx_hash:',tx_hash)
162+
tx_receipt = proxy.eth.wait_for_transaction_receipt(tx_hash)
163+
self.assertIsNotNone(tx_receipt)
164+
165+
if __name__ == '__main__':
166+
unittest.main()

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ ecdsa==0.16.0
33
pysha3==1.0.2
44
eth-keys==0.3.3
55
rlp==2.0.1
6-
web3
6+
web3==5.22.0
77
solana==0.10.0
8+
py-solc-x==1.1.0

0 commit comments

Comments
 (0)