|
| 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() |
0 commit comments