Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
5984c19
cherrypick part of changes
Dec 1, 2021
613a469
create indexer.py
Dec 1, 2021
c0d0e6c
remove solana_receipts_update.py
Dec 1, 2021
3f6b5c4
Cherry pick files from old branch
Dec 1, 2021
0790298
add requirement
Dec 1, 2021
340c854
fix refactoring issues
Dec 1, 2021
7449b38
Fix inspection issues
Dec 1, 2021
b3dacfa
fix last issue
Dec 1, 2021
a50cd47
Merge branch '336_indexer_refactoring' into 337_сreate_base_airdroppe…
Dec 1, 2021
2b8f879
Merge remote-tracking branch 'origin/develop' into 337_сreate_base_ai…
Dec 2, 2021
f51f2ed
simplify tests
Dec 2, 2021
6678924
add test
Dec 2, 2021
5d454b7
Merge remote-tracking branch 'origin/develop' into 337_сreate_base_ai…
Dec 3, 2021
add136a
add price provider
Dec 1, 2021
9a4be44
fix PriceProvider, add test
Dec 1, 2021
07aaca8
Add tests. Check worn on all nets
Dec 1, 2021
7a46c12
refactoring
Dec 1, 2021
2fc1424
integrate price_provider into airdropper
Dec 2, 2021
d157d67
integrate price provider
Dec 2, 2021
8a6abfd
use new faucet method
Dec 3, 2021
3d4dec9
add new parameter to airdropper main
Dec 3, 2021
5c29832
Test discriptions for airdropper
Dec 3, 2021
6a4efdc
Comments for price provider tests
Dec 3, 2021
14aeeed
remove unnecessary comment
Dec 3, 2021
bd35791
Merge remote-tracking branch 'origin/develop' into 338_create_sol_pri…
Dec 3, 2021
ff1f557
Merge remote-tracking branch 'origin/develop' into 338_create_sol_pri…
Dec 6, 2021
c28ca8e
fix error
Dec 6, 2021
07dd112
copy from erc20 contract test
Dec 5, 2021
023993d
Merge remote-tracking branch 'origin/develop' into 344_сreate_integra…
Dec 7, 2021
8c68755
Merge remote-tracking branch 'origin/develop' into 338_create_sol_pri…
Dec 7, 2021
f372b08
Merge branch '338_create_sol_price_provider' into 344_сreate_integrat…
Dec 7, 2021
ae5bddb
create integration tests
Dec 7, 2021
7f4c62c
fix oneline
Dec 7, 2021
b9cf0a6
revert changes on test_neon_faucet
Dec 7, 2021
1012a6b
fix some errors
Dec 7, 2021
dab9c2a
not working
Dec 7, 2021
0a4a2a0
add old variant
Dec 8, 2021
941f5ba
add helper functions
Dec 8, 2021
e223c46
transaction works!
Dec 8, 2021
24bc1f1
prepare refactoring
Dec 8, 2021
2401784
remove unnecessary code
Dec 8, 2021
5325895
Merge remote-tracking branch 'origin/develop' into 338_create_sol_pri…
Dec 8, 2021
9743411
Merge branch '338_create_sol_price_provider' into 344_сreate_integrat…
Dec 8, 2021
0f5c559
add account creation methods
Dec 9, 2021
b3cda28
first test almost ready
Dec 9, 2021
8e74e86
fix tests
Dec 9, 2021
18d88c7
fir airdropper startup
Dec 9, 2021
9ec00c9
One test is completely ready!
Dec 9, 2021
89714f6
add complex test case
Dec 9, 2021
48a96e7
add services to docker-compose file
Dec 9, 2021
89b951a
improve tests
Dec 10, 2021
9acbb38
add price update interval parameter
Dec 10, 2021
726cb1c
remove unnecessary imports
Dec 10, 2021
a1cbad2
fix airdropper run
Dec 10, 2021
5167613
Merge branch '338_create_sol_price_provider' into 344_сreate_integrat…
Dec 10, 2021
34dcedf
prepare for CI
Dec 10, 2021
4ec437f
remove commented
Dec 10, 2021
5a03842
fix compose-file
Dec 10, 2021
6119fe5
create wrapper class
Dec 13, 2021
1ac8877
make tests work!
Dec 13, 2021
0f3d41b
fix naming
Dec 13, 2021
50faa45
refactor test_erc20_wrapper_contract
Dec 13, 2021
4ed6d78
remove duplicated code
Dec 15, 2021
c3ce59b
Merge remote-tracking branch 'origin/develop' into 338_create_sol_pri…
Dec 15, 2021
e7d92fb
Merge branch '338_create_sol_price_provider' into 344_сreate_integrat…
Dec 15, 2021
36178d0
Merge remote-tracking branch 'origin/develop' into 344_сreate_integra…
Dec 16, 2021
be99c4c
Merge remote-tracking branch 'origin/develop' into 344_сreate_integra…
Dec 22, 2021
b77ed35
Fix tests after merge
Dec 22, 2021
146f530
improve deployment scripts, get rid of unnecessary delays in integrat…
Dec 22, 2021
df0f1c4
change wait airdrop logic
Dec 22, 2021
e07cbe8
use create-test-account.sh script
Dec 22, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
!setup.py
!README.md
!run-test-faucet.sh
!run-test-airdropper.sh
!run-faucet.sh
!run-airdropper.sh

# Ignore __pycache__ directory
proxy/__pycache__
38 changes: 16 additions & 22 deletions proxy/common_neon/erc20_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from solana.system_program import SYS_PROGRAM_ID
from solana.sysvar import SYSVAR_RENT_PUBKEY
from solana.rpc.types import TxOpts, RPCResponse, Commitment
from solana.transaction import Transaction
import spl.token.instructions as spl_token
from typing import Union, Dict
from logging import getLogger
import struct
Expand Down Expand Up @@ -46,26 +48,6 @@

pragma solidity >=0.5.12;


interface IERC20 {
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address who) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);

event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);


function approveSolana(bytes32 spender, uint64 value) external returns (bool);
event ApprovalSolana(address indexed owner, bytes32 indexed spender, uint64 value);
}



/*abstract*/ contract NeonERC20Wrapper /*is IERC20*/ {
address constant NeonERC20 = 0xff00000000000000000000000000000000000001;

Expand Down Expand Up @@ -158,8 +140,17 @@ def get_neon_erc20_account_address(self, neon_account_address: str):
neon_account_address_bytes]
return PublicKey.find_program_address(seeds, self.evm_loader_id)[0]

def create_associated_token_account(self, owner: PublicKey):
return self.token.create_associated_token_account(owner)
def create_associated_token_account(self, owner: PublicKey, payer: SolanaAccount):
# Construct transaction
# This part of code is based on original implementation of Token.create_associated_token_account
# except that skip_preflight is set to True
txn = Transaction()
create_txn = spl_token.create_associated_token_account(
payer=payer.public_key(), owner=owner, mint=self.token.pubkey
)
txn.add(create_txn)
self.token._conn.send_transaction(txn, payer, opts=TxOpts(skip_preflight = True, skip_confirmation=False))
return create_txn.keys[1].pubkey

def create_neon_erc20_account_instruction(self, payer: PublicKey, eth_address: str):
return TransactionInstruction(
Expand Down Expand Up @@ -198,6 +189,9 @@ def mint_to(self, destination: Union[PublicKey, str], amount: int) -> RPCRespons
return self.token.mint_to(destination, self.mint_authority, amount,
opts=TxOpts(skip_preflight=True, skip_confirmation=False))

def erc20_interface(self):
return self.proxy.eth.contract(address=self.neon_contract_address, abi=self.interface['abi'])

def get_balance(self, address: Union[PublicKey, str]) -> int:
if isinstance(address, PublicKey):
return int(self.token.get_balance(address, Commitment('confirmed'))['result']['value']['amount'])
Expand Down
4 changes: 2 additions & 2 deletions proxy/docker-compose-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ services:
SOLANA_URL: http://solana:8899
NEON_OPERATOR_KEYFILE: /root/.config/solana/id.json
NEON_ETH_MAX_AMOUNT: 10
TEST_FAUCET_INIT_NEON_BALANCE: 10000
hostname: faucet
expose:
- "3333"
Expand Down Expand Up @@ -131,7 +132,6 @@ services:
container_name: airdropper
image: neonlabsorg/proxy:${REVISION}
environment:
AIRDROPPER_MODE: 'true'
POSTGRES_DB: airdropper-db
POSTGRES_USER: neon-airdropper
POSTGRES_PASSWORD: neon-airdropper-pass
Expand All @@ -144,7 +144,7 @@ services:
PRICE_UPDATE_INTERVAL: 10
START_SLOT: LATEST
hostname: airdropper
entrypoint: ./run-test-airdropper.sh
entrypoint: ./run-airdropper.sh
networks:
- net
depends_on:
Expand Down
57 changes: 42 additions & 15 deletions proxy/testing/test_airdropper_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import os
import json


MAX_AIRDROP_WAIT_TIME = 45
EVM_LOADER_ID = PublicKey(EVM_LOADER_ID)
PROXY_URL = os.environ.get('PROXY_URL', 'http://localhost:9090/solana')
FAUCET_RPC_PORT = 3333
Expand Down Expand Up @@ -65,7 +65,6 @@ def create_token_mint(self):
9,
TOKEN_PROGRAM_ID,
)
sleep(20)

def deploy_erc20_wrapper_contract(self):
self.wrapper = ERC20Wrapper(proxy, NAME, SYMBOL,
Expand Down Expand Up @@ -98,7 +97,7 @@ def create_sol_account(self):
return account

def create_token_account(self, owner: PublicKey, mint_amount: int):
new_token_account = self.wrapper.create_associated_token_account(owner)
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

Expand All @@ -113,24 +112,36 @@ def test_success_airdrop_simple_case(self):
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)

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,
123456))
TRANSFER_AMOUNT))

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

sleep(15)
self.assertEqual(self.wrapper.get_balance(from_spl_token_acc), mint_amount - 123456)
self.assertEqual(self.wrapper.get_balance(to_neon_acc), 123456)
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
Expand All @@ -141,12 +152,13 @@ def test_success_airdrop_complex_case(self):
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
sleep(15)

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()))
Expand All @@ -155,19 +167,34 @@ def test_success_airdrop_complex_case(self):
trx.add(self.wrapper.create_input_liquidity_instruction(from_owner.public_key(),
from_spl_token_acc,
to_neon_acc1,
123456))
TRANSFER_AMOUNT1))
trx.add(self.wrapper.create_input_liquidity_instruction(from_owner.public_key(),
from_spl_token_acc,
to_neon_acc2,
654321))
TRANSFER_AMOUNT2))

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

sleep(45)
self.assertEqual(self.wrapper.get_balance(from_spl_token_acc), mint_amount - 123456 - 654321)
self.assertEqual(self.wrapper.get_balance(to_neon_acc1), 123456)
self.assertEqual(self.wrapper.get_balance(to_neon_acc2), 654321)
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)
Expand Down
36 changes: 18 additions & 18 deletions proxy/testing/test_erc20_wrapper_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from proxy.environment import EVM_LOADER_ID
from proxy.common_neon.erc20_wrapper import ERC20Wrapper
from proxy.common_neon.neon_instruction import NeonInstruction
from solana.rpc.types import TokenAccountOpts

proxy_url = os.environ.get('PROXY_URL', 'http://127.0.0.1:9090/solana')
solana_url = os.environ.get("SOLANA_URL", "http://127.0.0.1:8899")
Expand Down Expand Up @@ -94,24 +95,29 @@ def test_erc20_symbol(self):
self.assertEqual(sym, SYMBOL)

def test_erc20_decimals(self):
erc20 = proxy.eth.contract(address=self.wrapper.neon_contract_address, abi=self.wrapper.interface['abi'])
erc20 = self.wrapper.erc20_interface()
decs = erc20.functions.decimals().call()
self.assertEqual(decs, 9)

def test_erc20_totalSupply(self):
erc20 = proxy.eth.contract(address=self.wrapper.neon_contract_address, abi=self.wrapper.interface['abi'])
erc20 = self.wrapper.erc20_interface()
ts = erc20.functions.totalSupply().call()
self.assertGreater(ts, 0)

def test_erc20_balanceOf(self):
erc20 = proxy.eth.contract(address=self.wrapper.neon_contract_address, abi=self.wrapper.interface['abi'])
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):
erc20 = proxy.eth.contract(address=self.wrapper.neon_contract_address, abi=self.wrapper.interface['abi'])
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)
Expand All @@ -129,7 +135,7 @@ def test_erc20_transfer(self):

def test_erc20_transfer_not_enough_funds(self):
transfer_value = 100_000_000_000_000
erc20 = proxy.eth.contract(address=self.contract_address, abi=self.interface['abi'])
erc20 = self.wrapper.erc20_interface()

admin_balance_before = erc20.functions.balanceOf(admin.address).call()
user_balance_before = erc20.functions.balanceOf(user.address).call()
Expand All @@ -145,14 +151,14 @@ def test_erc20_transfer_not_enough_funds(self):

def test_erc20_transfer_out_of_bounds(self):
transfer_value = 0xFFFF_FFFF_FFFF_FFFF + 1
erc20 = proxy.eth.contract(address=self.contract_address, abi=self.interface['abi'])
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 = proxy.eth.contract(address=self.contract_address, abi=self.interface['abi'])
erc20 = self.wrapper.erc20_interface()

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

Expand All @@ -171,7 +177,7 @@ def test_erc20_approve(self):
def test_erc20_transferFrom(self):
approve_value = 1000
transfer_value = 100
erc20 = proxy.eth.contract(address=self.contract_address, abi=self.interface['abi'])
erc20 = self.wrapper.erc20_interface()

nonce = proxy.eth.get_transaction_count(admin.address)
tx = erc20.functions.approve(user.address, approve_value).buildTransaction({'nonce': nonce})
Expand Down Expand Up @@ -205,7 +211,7 @@ def test_erc20_transferFrom(self):

def test_erc20_transferFrom_beyond_approve(self):
transfer_value = 10_000_000
erc20 = proxy.eth.contract(address=self.contract_address, abi=self.interface['abi'])
erc20 = self.wrapper.erc20_interface()

with self.assertRaisesRegex(Exception, "ERC20 transferFrom failed"):
erc20.functions.transferFrom(admin.address, user.address, transfer_value).buildTransaction(
Expand All @@ -214,7 +220,7 @@ def test_erc20_transferFrom_beyond_approve(self):

def test_erc20_transferFrom_out_of_bounds(self):
transfer_value = 0xFFFF_FFFF_FFFF_FFFF + 1
erc20 = proxy.eth.contract(address=self.contract_address, abi=self.interface['abi'])
erc20 = self.wrapper.erc20_interface()

with self.assertRaisesRegex(Exception, "ERC20 transferFrom failed"):
erc20.functions.transferFrom(admin.address, user.address, transfer_value).buildTransaction(
Expand All @@ -224,7 +230,7 @@ def test_erc20_transferFrom_out_of_bounds(self):
def test_erc20_approveSolana(self):
delegate = SolanaAccount()
approve_value = 1000
erc20 = proxy.eth.contract(address=self.contract_address, abi=self.interface['abi'])
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})
Expand All @@ -234,16 +240,10 @@ def test_erc20_approveSolana(self):
self.assertEqual(tx_receipt.status, 1)

self.assertIsNotNone(tx_receipt)

contract_address_bytes = bytes.fromhex(self.contract_address[2:])
admin_address_bytes = bytes.fromhex(admin.address[2:])
admin_token_seeds = [ b"\1", b"ERC20Balance", bytes(self.token.pubkey), contract_address_bytes, admin_address_bytes ]
admin_solana_token = PublicKey.find_program_address(admin_token_seeds, evm_loader_id)[0]

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(admin_solana_token, accounts)
self.assertIn(self.wrapper.get_neon_erc20_account_address(admin.address), accounts)


if __name__ == '__main__':
Expand Down
10 changes: 10 additions & 0 deletions run-airdropper.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash

if [ -z "$EVM_LOADER" ]; then
echo "Extracting EVM_LOADER address from keypair file..."
export EVM_LOADER=$(solana address -k /spl/bin/evm_loader-keypair.json)
echo "EVM_LOADER=$EVM_LOADER"
fi
export AIRDROPPER_MODE='true'

python3 -m proxy
23 changes: 23 additions & 0 deletions run-faucet.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/sh

if [ -z "$SOLANA_URL" ]; then
echo "SOLANA_URL is not set"
exit 1
fi

echo "Extracting NEON-EVM's ELF parameters"
export EVM_LOADER=$(solana address -k /spl/bin/evm_loader-keypair.json)
export $(/spl/bin/neon-cli --commitment confirmed --url $SOLANA_URL --evm_loader="$EVM_LOADER" neon-elf-params)

BALANCE=$(solana balance | tr '.' '\t'| tr '[:space:]' '\t' | cut -f1)
if [ "$BALANCE" -eq 0 ]; then
echo "SOL balance is 0"
exit 1
fi

if [ "$(spl-token balance "$NEON_TOKEN_MINT" || echo 0)" -eq 0 ]; then
echo "NEON balance is 0"
exit 1
fi

faucet run --workers 1
4 changes: 0 additions & 4 deletions run-test-airdropper.sh

This file was deleted.

Loading