コード例 #1
0
class transacton_cost(unittest.TestCase):
    @classmethod
    def setUpClass(self):
        print(
            "\n\nhttps://app.zenhub.com/workspaces/solana-evm-6007c75a9dc141001100ccb8/issues/neonlabsorg/proxy-model.py/245"
        )
        self.account = proxy.eth.account.create()
        print('account.address:', self.account.address)
        request_airdrop(self.account.address)

        self.client = Client(solana_url)
        wallet = WalletAccount(wallet_path())
        self.acc = wallet.get_acc()

    @unittest.skip("only for debug")
    def test_deploy_cost(self):
        print("\n\ntest_deploy_cost")

        compiled = compile_source(CONTRACT)
        id, interface = compiled.popitem()
        contract = proxy.eth.contract(abi=interface['abi'],
                                      bytecode=interface['bin'])
        trx = proxy.eth.account.sign_transaction(
            dict(nonce=proxy.eth.get_transaction_count(self.account.address),
                 chainId=proxy.eth.chain_id,
                 gas=987654321,
                 gasPrice=1000000000,
                 to='',
                 value=0,
                 data=contract.bytecode), self.account.key)
        print("trx_hash", trx.hash.hex()[2:])

        balance_pre = int(
            self.client.get_balance(self.acc.public_key(),
                                    commitment=Confirmed)['result']['value'])
        print("incoming balance  {:,}".format(balance_pre).replace(',', ' '))

        signature = proxy.eth.send_raw_transaction(trx.rawTransaction)
        receipt = proxy.eth.wait_for_transaction_receipt(signature)
        # self.contract = proxy.eth.contract(
        #     address=receipt.contractAddress,
        #     abi=contract.abi
        # )

        balance_post = int(
            self.client.get_balance(self.acc.public_key(),
                                    commitment=Confirmed)['result']['value'])
        print("outgoing  balance {:,}".format(balance_post).replace(',', ' '))
        print("cost {:,}".format(balance_pre - balance_post).replace(',', ' '))
コード例 #2
0
def test_request_air_drop(stubbed_sender: Keypair, stubbed_receiver: Keypair,
                          test_http_client: Client):
    """Test air drop to stubbed_sender and stubbed_receiver."""
    # Airdrop to stubbed_sender
    resp = test_http_client.request_airdrop(stubbed_sender.public_key,
                                            AIRDROP_AMOUNT)
    assert_valid_response(resp)
    test_http_client.confirm_transaction(resp["result"])
    balance = test_http_client.get_balance(stubbed_sender.public_key)
    assert balance["result"]["value"] == AIRDROP_AMOUNT
    # Airdrop to stubbed_receiver
    resp = test_http_client.request_airdrop(stubbed_receiver, AIRDROP_AMOUNT)
    assert_valid_response(resp)
    test_http_client.confirm_transaction(resp["result"])
    balance = test_http_client.get_balance(stubbed_receiver)
    assert balance["result"]["value"] == AIRDROP_AMOUNT
コード例 #3
0
class TestAirdropperIntegration(TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        cls.create_token_mint(cls)
        cls.deploy_erc20_wrapper_contract(cls)
        cls.acc_num = 0

    def create_token_mint(self):
        self.solana_client = SolanaClient(SOLANA_URL)

        with open("proxy/operator-keypairs/id.json") as f:
            d = json.load(f)
        self.mint_authority = SolanaAccount(d[0:32])
        self.solana_client.request_airdrop(self.mint_authority.public_key(),
                                           1000_000_000_000, Confirmed)

        while True:
            balance = self.solana_client.get_balance(
                self.mint_authority.public_key(), Confirmed)["result"]["value"]
            if balance > 0:
                break
            sleep(1)
        print('create_token_mint mint, SolanaAccount: ',
              self.mint_authority.public_key())

        self.token = SplToken.create_mint(
            self.solana_client,
            self.mint_authority,
            self.mint_authority.public_key(),
            9,
            TOKEN_PROGRAM_ID,
        )

    def deploy_erc20_wrapper_contract(self):
        self.wrapper = ERC20Wrapper(proxy, NAME, SYMBOL, self.token, admin,
                                    self.mint_authority, EVM_LOADER_ID)
        self.wrapper.deploy_wrapper()

    def create_account_instruction(self, eth_address: str, payer: PublicKey):
        dest_address_solana, nonce = get_evm_loader_account_address(
            eth_address)
        neon_token_account = get_associated_token_address(
            dest_address_solana, ETH_TOKEN_MINT_ID)
        return TransactionInstruction(
            program_id=EVM_LOADER_ID,
            data=create_account_layout(0, 0, bytes.fromhex(eth_address[2:]),
                                       nonce),
            keys=[
                AccountMeta(pubkey=payer, is_signer=True, is_writable=True),
                AccountMeta(pubkey=dest_address_solana,
                            is_signer=False,
                            is_writable=True),
                AccountMeta(pubkey=neon_token_account,
                            is_signer=False,
                            is_writable=True),
                AccountMeta(pubkey=SYS_PROGRAM_ID,
                            is_signer=False,
                            is_writable=False),
                AccountMeta(pubkey=ETH_TOKEN_MINT_ID,
                            is_signer=False,
                            is_writable=False),
                AccountMeta(pubkey=TOKEN_PROGRAM_ID,
                            is_signer=False,
                            is_writable=False),
                AccountMeta(pubkey=ASSOCIATED_TOKEN_PROGRAM_ID,
                            is_signer=False,
                            is_writable=False),
                AccountMeta(pubkey=SYSVAR_RENT_PUBKEY,
                            is_signer=False,
                            is_writable=False),
            ])

    def create_sol_account(self):
        account = SolanaAccount()
        print(
            f"New solana account created: {account.public_key().to_base58()}. Airdropping..."
        )
        self.solana_client.request_airdrop(account.public_key(),
                                           1000_000_000_000, Confirmed)
        return account

    def create_token_account(self, owner: PublicKey, mint_amount: int):
        new_token_account = self.wrapper.create_associated_token_account(
            owner, self.mint_authority)
        self.wrapper.mint_to(new_token_account, mint_amount)
        return new_token_account

    def create_eth_account(self):
        self.acc_num += 1
        account = proxy.eth.account.create(
            f'neonlabsorg/proxy-model.py/issues/344/eth_account{self.acc_num}')
        print(f"NEON account created: {account.address}")
        return account

    def test_success_airdrop_simple_case(self):
        from_owner = self.create_sol_account()
        mint_amount = 1000_000_000_000
        from_spl_token_acc = self.create_token_account(from_owner.public_key(),
                                                       mint_amount)
        to_neon_acc = self.create_eth_account().address

        self.assertEqual(self.wrapper.get_balance(from_spl_token_acc),
                         mint_amount)
        self.assertEqual(self.wrapper.get_balance(to_neon_acc), 0)

        TRANSFER_AMOUNT = 123456
        trx = Transaction()
        trx.add(
            self.create_account_instruction(to_neon_acc,
                                            from_owner.public_key()))
        trx.add(
            self.wrapper.create_neon_erc20_account_instruction(
                from_owner.public_key(), to_neon_acc))
        trx.add(
            self.wrapper.create_input_liquidity_instruction(
                from_owner.public_key(), from_spl_token_acc, to_neon_acc,
                TRANSFER_AMOUNT))

        opts = TxOpts(skip_preflight=True, skip_confirmation=False)
        print(self.solana_client.send_transaction(trx, from_owner, opts=opts))

        self.assertEqual(self.wrapper.get_balance(from_spl_token_acc),
                         mint_amount - TRANSFER_AMOUNT)
        self.assertEqual(self.wrapper.get_balance(to_neon_acc),
                         TRANSFER_AMOUNT)

        wait_time = 0
        eth_balance = 0
        while wait_time < MAX_AIRDROP_WAIT_TIME:
            eth_balance = proxy.eth.get_balance(to_neon_acc)
            balance_ready = eth_balance > 0 and eth_balance < 10 * pow(10, 18)
            if balance_ready:
                break
            sleep(1)
            wait_time += 1
        print(f"Wait time for simple transaction (1 airdrop): {wait_time}")

        eth_balance = proxy.eth.get_balance(to_neon_acc)
        print("NEON balance is: ", eth_balance)
        self.assertTrue(
            eth_balance > 0 and
            eth_balance < 10 * pow(10, 18))  # 10 NEON is a max airdrop amount

    def test_success_airdrop_complex_case(self):
        from_owner = self.create_sol_account()
        mint_amount = 1000_000_000_000
        from_spl_token_acc = self.create_token_account(from_owner.public_key(),
                                                       mint_amount)
        to_neon_acc1 = self.create_eth_account().address
        to_neon_acc2 = self.create_eth_account().address

        self.assertEqual(self.wrapper.get_balance(from_spl_token_acc),
                         mint_amount)
        self.assertEqual(self.wrapper.get_balance(to_neon_acc1), 0)
        self.assertEqual(self.wrapper.get_balance(to_neon_acc2), 0)

        TRANSFER_AMOUNT1 = 123456
        TRANSFER_AMOUNT2 = 654321
        trx = Transaction()
        trx.add(
            self.create_account_instruction(to_neon_acc1,
                                            from_owner.public_key()))
        trx.add(
            self.create_account_instruction(to_neon_acc2,
                                            from_owner.public_key()))
        trx.add(
            self.wrapper.create_neon_erc20_account_instruction(
                from_owner.public_key(), to_neon_acc1))
        trx.add(
            self.wrapper.create_neon_erc20_account_instruction(
                from_owner.public_key(), to_neon_acc2))
        trx.add(
            self.wrapper.create_input_liquidity_instruction(
                from_owner.public_key(), from_spl_token_acc, to_neon_acc1,
                TRANSFER_AMOUNT1))
        trx.add(
            self.wrapper.create_input_liquidity_instruction(
                from_owner.public_key(), from_spl_token_acc, to_neon_acc2,
                TRANSFER_AMOUNT2))

        opts = TxOpts(skip_preflight=True, skip_confirmation=False)
        print(self.solana_client.send_transaction(trx, from_owner, opts=opts))

        self.assertEqual(self.wrapper.get_balance(from_spl_token_acc),
                         mint_amount - TRANSFER_AMOUNT1 - TRANSFER_AMOUNT2)
        self.assertEqual(self.wrapper.get_balance(to_neon_acc1),
                         TRANSFER_AMOUNT1)
        self.assertEqual(self.wrapper.get_balance(to_neon_acc2),
                         TRANSFER_AMOUNT2)

        wait_time = 0
        eth_balance1 = 0
        eth_balance2 = 0
        while wait_time < MAX_AIRDROP_WAIT_TIME:
            eth_balance1 = proxy.eth.get_balance(to_neon_acc1)
            eth_balance2 = proxy.eth.get_balance(to_neon_acc2)
            balance1_ready = eth_balance1 > 0 and eth_balance1 < 10 * pow(
                10, 18)
            balance2_ready = eth_balance2 > 0 and eth_balance2 < 10 * pow(
                10, 18)
            if balance1_ready and balance2_ready:
                break
            sleep(1)
            wait_time += 1
        print(f"Wait time for complex transaction (2 airdrops): {wait_time}")

        eth_balance1 = proxy.eth.get_balance(to_neon_acc1)
        eth_balance2 = proxy.eth.get_balance(to_neon_acc2)
        print("NEON balance 1 is: ", eth_balance1)
        print("NEON balance 2 is: ", eth_balance2)
        self.assertTrue(
            eth_balance1 > 0 and
            eth_balance1 < 10 * pow(10, 18))  # 10 NEON is a max airdrop amount
        self.assertTrue(
            eth_balance2 > 0 and
            eth_balance2 < 10 * pow(10, 18))  # 10 NEON is a max airdrop amount

    def test_no_airdrop(self):
        from_owner = self.create_sol_account()
        mint_amount = 1000_000_000_000
        from_spl_token_acc = self.create_token_account(from_owner.public_key(),
                                                       mint_amount)
        to_neon_acc = self.create_eth_account().address
        sleep(15)
        self.assertEqual(self.wrapper.get_balance(from_spl_token_acc),
                         mint_amount)
        self.assertEqual(self.wrapper.get_balance(to_neon_acc), 0)

        trx = Transaction()
        trx.add(
            self.create_account_instruction(to_neon_acc,
                                            from_owner.public_key()))
        trx.add(
            self.wrapper.create_neon_erc20_account_instruction(
                from_owner.public_key(), to_neon_acc))
        # No input liquidity

        opts = TxOpts(skip_preflight=True, skip_confirmation=False)
        print(self.solana_client.send_transaction(trx, from_owner, opts=opts))

        sleep(15)
        eth_balance = proxy.eth.get_balance(to_neon_acc)
        print("NEON balance is: ", eth_balance)
        self.assertEqual(eth_balance, 0)
コード例 #4
0
class Test_read_only_accounts(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.create_token_mint(cls)
        cls.deploy_erc20_wrapper_contract(cls)
        cls.deploy_test_contract(cls)

    def account_exists(self, key: PublicKey) -> Boolean:
        info = self.solana_client.get_account_info(key)
        info["result"]["value"] is not None

    def create_token_mint(self):
        self.solana_client = SolanaClient(solana_url)

        with open("proxy/operator-keypairs/id.json") as f:
            d = json.load(f)
        self.solana_account = SolanaAccount(d[0:32])
        self.solana_client.request_airdrop(self.solana_account.public_key(),
                                           1000_000_000_000, Confirmed)

        while True:
            balance = self.solana_client.get_balance(
                self.solana_account.public_key(), Confirmed)["result"]["value"]
            if balance > 0:
                break
            sleep(1)
        print('create_token_mint mint, SolanaAccount: ',
              self.solana_account.public_key())

        self.token = SplToken.create_mint(
            self.solana_client,
            self.solana_account,
            self.solana_account.public_key(),
            9,
            TOKEN_PROGRAM_ID,
        )

    def deploy_erc20_wrapper_contract(self):
        self.wrapper = ERC20Wrapper(proxy, "NEON", "NEON", self.token, admin,
                                    self.solana_account,
                                    PublicKey(EVM_LOADER_ID))
        self.wrapper.deploy_wrapper()

    def deploy_test_contract(self):
        compiled = compile_source(CONTRACT)
        id, interface = compiled.popitem()
        contract = proxy.eth.contract(abi=interface['abi'],
                                      bytecode=interface['bin'])
        trx = proxy.eth.account.sign_transaction(
            dict(nonce=proxy.eth.get_transaction_count(admin.address),
                 chainId=proxy.eth.chain_id,
                 gas=987654321,
                 gasPrice=1000000000,
                 to='',
                 value=0,
                 data=contract.bytecode), admin.key)
        signature = proxy.eth.send_raw_transaction(trx.rawTransaction)
        receipt = proxy.eth.wait_for_transaction_receipt(signature)

        self.contract = proxy.eth.contract(address=receipt.contractAddress,
                                           abi=contract.abi)

    def test_balanceOf(self):
        account = proxy.eth.account.create()

        solana_account = self.wrapper.get_neon_account_address(account.address)
        self.assertFalse(self.account_exists(solana_account))

        nonce = proxy.eth.get_transaction_count(admin.address)
        tx = self.contract.functions.balanceOf(
            account.address).buildTransaction({"nonce": nonce})
        tx = proxy.eth.account.sign_transaction(tx, admin.key)

        tx_hash = proxy.eth.send_raw_transaction(tx.rawTransaction)

        tx_receipt = proxy.eth.wait_for_transaction_receipt(tx_hash)
        self.assertIsNotNone(tx_receipt)
        self.assertEqual(tx_receipt.status, 1)

        self.assertFalse(self.account_exists(solana_account))

    def test_erc20_balanceOf(self):
        erc20 = self.wrapper.erc20_interface()

        account = proxy.eth.account.create()

        solana_account = self.wrapper.get_neon_account_address(account.address)
        self.assertFalse(self.account_exists(solana_account))

        token_account = self.wrapper.get_neon_erc20_account_address(
            account.address)
        self.assertFalse(self.account_exists(token_account))

        nonce = proxy.eth.get_transaction_count(admin.address)
        tx = erc20.functions.balanceOf(account.address).buildTransaction(
            {"nonce": nonce})
        tx = proxy.eth.account.sign_transaction(tx, admin.key)

        tx_hash = proxy.eth.send_raw_transaction(tx.rawTransaction)

        tx_receipt = proxy.eth.wait_for_transaction_receipt(tx_hash)
        self.assertIsNotNone(tx_receipt)
        self.assertEqual(tx_receipt.status, 1)

        self.assertFalse(self.account_exists(solana_account))
        self.assertFalse(self.account_exists(token_account))
コード例 #5
0
class Test_erc20_wrapper_contract(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        print("\n\nhttps://github.com/neonlabsorg/proxy-model.py/issues/197")
        print('admin.key:', admin.key.hex())
        print('admin.address:', admin.address)
        print('user.key:', user.key.hex())
        print('user.address:', user.address)

        cls.create_token_mint(cls)
        cls.deploy_erc20_wrapper_contract(cls)
        cls.create_token_accounts(cls)

    def create_token_mint(self):
        self.solana_client = SolanaClient(solana_url)

        with open("proxy/operator-keypairs/id.json") as f:
            d = json.load(f)
        self.solana_account = SolanaAccount(d[0:32])
        self.solana_client.request_airdrop(self.solana_account.public_key(),
                                           1000_000_000_000, Confirmed)

        while True:
            balance = self.solana_client.get_balance(
                self.solana_account.public_key(), Confirmed)["result"]["value"]
            if balance > 0:
                break
            sleep(1)
        print('create_token_mint mint, SolanaAccount: ',
              self.solana_account.public_key())

        self.token = SplToken.create_mint(
            self.solana_client,
            self.solana_account,
            self.solana_account.public_key(),
            9,
            TOKEN_PROGRAM_ID,
        )

    def deploy_erc20_wrapper_contract(self):
        self.wrapper = ERC20Wrapper(proxy, NAME, SYMBOL, self.token, admin,
                                    self.solana_account,
                                    PublicKey(EVM_LOADER_ID))
        self.wrapper.deploy_wrapper()

    def create_token_accounts(self):
        admin_token_key = self.wrapper.get_neon_erc20_account_address(
            admin.address)

        admin_token_info = {
            "key": admin_token_key,
            "owner": self.wrapper.get_neon_account_address(admin.address),
            "contract": self.wrapper.solana_contract_address,
            "mint": self.token.pubkey
        }

        instr = NeonInstruction(
            self.solana_account.public_key()).createERC20TokenAccountTrx(
                admin_token_info)
        self.solana_client.send_transaction(instr,
                                            self.solana_account,
                                            opts=TxOpts(
                                                skip_preflight=True,
                                                skip_confirmation=False))
        self.wrapper.mint_to(admin_token_key, 10_000_000_000_000)

    def test_erc20_name(self):
        erc20 = proxy.eth.contract(address=self.wrapper.neon_contract_address,
                                   abi=self.wrapper.wrapper['abi'])
        name = erc20.functions.name().call()
        self.assertEqual(name, NAME)

    def test_erc20_symbol(self):
        erc20 = proxy.eth.contract(address=self.wrapper.neon_contract_address,
                                   abi=self.wrapper.wrapper['abi'])
        sym = erc20.functions.symbol().call()
        self.assertEqual(sym, SYMBOL)

    def test_erc20_decimals(self):
        erc20 = self.wrapper.erc20_interface()
        decs = erc20.functions.decimals().call()
        self.assertEqual(decs, 9)

    def test_erc20_totalSupply(self):
        erc20 = self.wrapper.erc20_interface()
        ts = erc20.functions.totalSupply().call()
        self.assertGreater(ts, 0)

    def test_erc20_balanceOf(self):
        erc20 = self.wrapper.erc20_interface()
        b = erc20.functions.balanceOf(admin.address).call()
        self.assertGreater(b, 0)
        b = erc20.functions.balanceOf(user.address).call()
        self.assertEqual(b, 0)

    def test_erc20_transfer(self):
        transfer_value = 1000
        erc20 = self.wrapper.erc20_interface()

        admin_balance_before = erc20.functions.balanceOf(admin.address).call()
        user_balance_before = erc20.functions.balanceOf(user.address).call()

        nonce = proxy.eth.get_transaction_count(proxy.eth.default_account)
        tx = {'nonce': nonce}
        tx = erc20.functions.transfer(user.address,
                                      transfer_value).buildTransaction(tx)
        tx = proxy.eth.account.sign_transaction(tx, admin.key)
        tx_hash = proxy.eth.send_raw_transaction(tx.rawTransaction)
        tx_receipt = proxy.eth.wait_for_transaction_receipt(tx_hash)
        self.assertIsNotNone(tx_receipt)
        self.assertEqual(tx_receipt.status, 1)

        admin_balance_after = erc20.functions.balanceOf(admin.address).call()
        user_balance_after = erc20.functions.balanceOf(user.address).call()

        self.assertEqual(admin_balance_after,
                         admin_balance_before - transfer_value)
        self.assertEqual(user_balance_after,
                         user_balance_before + transfer_value)

    def test_erc20_transfer_not_enough_funds(self):
        transfer_value = 100_000_000_000_000
        erc20 = self.wrapper.erc20_interface()

        admin_balance_before = erc20.functions.balanceOf(admin.address).call()
        user_balance_before = erc20.functions.balanceOf(user.address).call()

        with self.assertRaisesRegex(Exception, "ERC20 transfer failed"):
            erc20.functions.transfer(user.address,
                                     transfer_value).buildTransaction()

        admin_balance_after = erc20.functions.balanceOf(admin.address).call()
        user_balance_after = erc20.functions.balanceOf(user.address).call()

        self.assertEqual(admin_balance_after, admin_balance_before)
        self.assertEqual(user_balance_after, user_balance_before)

    def test_erc20_transfer_out_of_bounds(self):
        transfer_value = 0xFFFF_FFFF_FFFF_FFFF + 1
        erc20 = self.wrapper.erc20_interface()

        with self.assertRaisesRegex(Exception, "ERC20 transfer failed"):
            erc20.functions.transfer(user.address,
                                     transfer_value).buildTransaction()

    def test_erc20_approve(self):
        approve_value = 1000
        erc20 = self.wrapper.erc20_interface()

        allowance_before = erc20.functions.allowance(admin.address,
                                                     user.address).call()

        nonce = proxy.eth.get_transaction_count(admin.address)
        tx = erc20.functions.approve(
            user.address, approve_value).buildTransaction({'nonce': nonce})
        tx = proxy.eth.account.sign_transaction(tx, admin.key)
        tx_hash = proxy.eth.send_raw_transaction(tx.rawTransaction)
        tx_receipt = proxy.eth.wait_for_transaction_receipt(tx_hash)
        self.assertEqual(tx_receipt.status, 1)

        self.assertIsNotNone(tx_receipt)

        allowance_after = erc20.functions.allowance(admin.address,
                                                    user.address).call()
        self.assertEqual(allowance_after, allowance_before + approve_value)

    def test_erc20_transferFrom(self):
        approve_value = 1000
        transfer_value = 100
        erc20 = self.wrapper.erc20_interface()

        nonce = proxy.eth.get_transaction_count(admin.address)
        tx = erc20.functions.approve(
            user.address, approve_value).buildTransaction({'nonce': nonce})
        tx = proxy.eth.account.sign_transaction(tx, admin.key)
        tx_hash = proxy.eth.send_raw_transaction(tx.rawTransaction)
        tx_receipt = proxy.eth.wait_for_transaction_receipt(tx_hash)
        self.assertIsNotNone(tx_receipt)
        self.assertEqual(tx_receipt.status, 1)

        allowance_before = erc20.functions.allowance(admin.address,
                                                     user.address).call()
        admin_balance_before = erc20.functions.balanceOf(admin.address).call()
        user_balance_before = erc20.functions.balanceOf(user.address).call()

        nonce = proxy.eth.get_transaction_count(user.address)
        tx = erc20.functions.transferFrom(admin.address, user.address,
                                          transfer_value).buildTransaction({
                                              'nonce':
                                              nonce,
                                              'from':
                                              user.address
                                          })
        tx = proxy.eth.account.sign_transaction(tx, user.key)
        tx_hash = proxy.eth.send_raw_transaction(tx.rawTransaction)
        tx_receipt = proxy.eth.wait_for_transaction_receipt(tx_hash)
        self.assertIsNotNone(tx_receipt)
        self.assertEqual(tx_receipt.status, 1)

        allowance_after = erc20.functions.allowance(admin.address,
                                                    user.address).call()
        admin_balance_after = erc20.functions.balanceOf(admin.address).call()
        user_balance_after = erc20.functions.balanceOf(user.address).call()

        self.assertEqual(allowance_after, allowance_before - transfer_value)
        self.assertEqual(admin_balance_after,
                         admin_balance_before - transfer_value)
        self.assertEqual(user_balance_after,
                         user_balance_before + transfer_value)

    def test_erc20_transferFrom_beyond_approve(self):
        transfer_value = 10_000_000
        erc20 = self.wrapper.erc20_interface()

        with self.assertRaisesRegex(Exception, "ERC20 transferFrom failed"):
            erc20.functions.transferFrom(admin.address, user.address,
                                         transfer_value).buildTransaction(
                                             {'from': user.address})

    def test_erc20_transferFrom_out_of_bounds(self):
        transfer_value = 0xFFFF_FFFF_FFFF_FFFF + 1
        erc20 = self.wrapper.erc20_interface()

        with self.assertRaisesRegex(Exception, "ERC20 transferFrom failed"):
            erc20.functions.transferFrom(admin.address, user.address,
                                         transfer_value).buildTransaction(
                                             {'from': user.address})

    def test_erc20_approveSolana(self):
        delegate = SolanaAccount()
        approve_value = 1000
        erc20 = self.wrapper.erc20_interface()

        nonce = proxy.eth.get_transaction_count(admin.address)
        tx = erc20.functions.approveSolana(bytes(delegate.public_key()),
                                           approve_value).buildTransaction(
                                               {'nonce': nonce})
        tx = proxy.eth.account.sign_transaction(tx, admin.key)
        tx_hash = proxy.eth.send_raw_transaction(tx.rawTransaction)
        tx_receipt = proxy.eth.wait_for_transaction_receipt(tx_hash)
        self.assertEqual(tx_receipt.status, 1)

        self.assertIsNotNone(tx_receipt)
        accounts = self.solana_client.get_token_accounts_by_delegate(
            delegate.public_key(),
            TokenAccountOpts(mint=self.token.pubkey),
            commitment=Recent)
        accounts = list(
            map(lambda a: PublicKey(a['pubkey']), accounts['result']['value']))

        self.assertIn(
            self.wrapper.get_neon_erc20_account_address(admin.address),
            accounts)