Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tests for transaction improvements #5879

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 12 additions & 3 deletions integration-tests/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,17 @@
},
"Networks": [
{
"ChainID": 31337,
"RPCURL": "http://anvil:8545"
}
"ChainID": 31337,
"ChainName": "Anvil",
"DefaultRPCURL": "http://anvil:8545",
"RPCURL": "http://anvil:8545",
"ShortName": "eth",
"NativeCurrencyName": "Ether",
"NativeCurrencySymbol": "ETH",
"NativeCurrencyDecimals": 18,
"IsTest": false,
"Layer": 1,
"Enabled": true
}
]
}
10 changes: 8 additions & 2 deletions integration-tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ def pytest_addoption(parser):
help="",
default="ws://0.0.0.0:8354",
)
parser.addoption(
"--anvil_url",
action="store",
help="",
default="http://0.0.0.0:8545",
)
parser.addoption(
"--password",
action="store",
Expand All @@ -35,11 +41,11 @@ class Account():
private_key: str

user_1 = Account(
address="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
address="0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
private_key="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
)
user_2 = Account(
address="0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
address="0x70997970c51812dc3a010c7d01b50e0d17dc79c8",
private_key="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d",
)

Expand Down
2 changes: 1 addition & 1 deletion integration-tests/docker-compose.anvil.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ services:
image: ghcr.io/foundry-rs/foundry:latest
platform: linux/amd64
command:
- anvil --host 0.0.0.0
- anvil --host 0.0.0.0 --block-time 2

deploy-sntv2:
platform: linux/amd64
Expand Down
8 changes: 5 additions & 3 deletions integration-tests/docker-compose.test.status-go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ services:
"--password", "Strong12345",
"--dir", "/tmp/status-go-data", # Keep in sync with `config.json/DataDir` value. Later this arg will not be needed.
]
# ports:
# - 3333:3333
# - 8354:8354 # use for local debbuging only
ports:
- 3333:3333
- 8354:8354 # use for local debbuging only
healthcheck:
test: ["CMD-SHELL", "curl -X POST --data '{\"jsonrpc\":\"2.0\",\"method\":\"net_version\",\"params\":[],\"id\":1}' -H 'Content-Type: application/json' http://0.0.0.0:3333 || exit 1"]
interval: 5s
Expand Down Expand Up @@ -89,6 +89,8 @@ services:
"-m", "wallet",
"--rpc_url=http://status-go:3333",
"--rpc_url_2=http://status-go-no-funds:3333",
"--anvil_url=http://anvil:8545",
"--ws_url=ws://status-go:8354",
"--junitxml=/tests-rpc/reports/report.xml",
]
volumes:
Expand Down
54 changes: 34 additions & 20 deletions integration-tests/tests/test_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import threading
import logging
import jsonschema
import time
import requests
from conftest import option, user_1, user_2

Expand All @@ -16,9 +17,11 @@ def _try_except_JSONDecodeError_KeyError(self, response, key: str):
try:
return response.json()[key]
except json.JSONDecodeError:
raise AssertionError(f"Invalid JSON in response: {response.content}")
raise AssertionError(
f"Invalid JSON in response: {response.content}")
except KeyError:
raise AssertionError(f"Key '{key}' not found in the JSON response.")
raise AssertionError(
f"Key '{key}' not found in the JSON response: {response.content}")

def verify_is_valid_json_rpc_response(self, response, _id=None):
assert response.status_code == 200
Expand All @@ -40,7 +43,6 @@ def verify_is_json_rpc_error(self, response):
assert response.content
self._try_except_JSONDecodeError_KeyError(response, "error")


def rpc_request(self, method, params=[], _id=None, client=None, url=None):
client = client if client else requests.Session()
url = url if url else option.rpc_url
Expand All @@ -60,28 +62,27 @@ def verify_json_schema(self, response, method):
schema=json.load(schema))



class TransactionTestCase(RpcTestCase):


def wallet_create_multi_transaction(self, **kwargs):
method = "wallet_createMultiTransaction"
transferTx_data = {
"data": "",
"from": user_1.address,
"gas": "0x5BBF",
"input": "",
"maxFeePerGas": "0xbcc0f04fd",
"maxPriorityFeePerGas": "0xbcc0f04fd",
"to": user_2.address,
"type": "0x02",
"value": "0x5af3107a4000",
}
"data": "",
"from": user_1.address,
"gas": "0x5BBF",
"input": "",
"maxFeePerGas": "0xbcc0f04fd",
"maxPriorityFeePerGas": "0xbcc0f04fd",
"to": user_2.address,
"type": "0x02",
"value": "0x5af3107a4000",
}
for key, new_value in kwargs.items():
if key in transferTx_data:
transferTx_data[key] = new_value
else:
print(f"Warning: The key '{key}' does not exist in the transferTx parameters and will be ignored.")
print(
f"Warning: The key '{key}' does not exist in the transferTx parameters and will be ignored.")
params = [
{
"fromAddress": user_1.address,
Expand Down Expand Up @@ -116,10 +117,23 @@ def setup_method(self):

class SignalTestCase(RpcTestCase):

received_signals = []
await_signals = []
received_signals = {}

def on_message(self, ws, signal):
signal = json.loads(signal)
if signal.get("type") in self.await_signals:
self.received_signals[signal["type"]] = signal

def _on_message(self, ws, signal):
self.received_signals.append(signal)
def wait_for_signal(self, signal_type, timeout=10):
start_time = time.time()
while signal_type not in self.received_signals:
time_passed = time.time() - start_time
if time_passed >= timeout:
raise TimeoutError(
f"Signal {signal_type} is not received in {timeout} seconds")
time.sleep(0.5)
return self.received_signals[signal_type]

def _on_error(self, ws, error):
logging.info(f"Error: {error}")
Expand All @@ -134,7 +148,7 @@ def _connect(self):
self.url = f"{option.ws_url}/signals"

ws = websocket.WebSocketApp(self.url,
on_message=self._on_message,
on_message=self.on_message,
on_error=self._on_error,
on_close=self._on_close)

Expand Down
121 changes: 121 additions & 0 deletions integration-tests/tests/test_router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import pytest
import time
import uuid
from conftest import user_1, user_2, option
from test_cases import SignalTestCase


@pytest.mark.rpc
@pytest.mark.transaction
@pytest.mark.wallet
class TestTransactionFromRoute(SignalTestCase):

await_signals = [
"wallet.suggested.routes",
"wallet.router.sign-transactions",
"wallet.router.sending-transactions-started",
"wallet.transaction.status-changed",
"wallet.router.transactions-sent"
]

def test_tx_from_route(self):

_uuid = str(uuid.uuid4())
amount_in = "0xde0b6b3a7640000"

method = "wallet_getSuggestedRoutesAsync"
params = [
{
"uuid": _uuid,
"sendType": 0,
"addrFrom": user_1.address,
"addrTo": user_2.address,
"amountIn": amount_in,
"amountOut": "0x0",
"tokenID": "ETH",
"tokenIDIsOwnerToken": False,
"toTokenID": "",
"disabledFromChainIDs": [10, 42161],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@antdanchenko since we're resolving selected chains based on the entry in the networks table, and since the statusgo is run with the config set in integration-tests/config.json (that provides config for the Anvil chain only), the result will be the same either we provide an empty list here or the list with all but the Anvil chain id.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the note, we will add more anvil networks and bridges in future to simulate proper route, but for now let's keep as it is

"disabledToChainIDs": [10, 42161],
"gasFeeMode": 1,
"fromLockedAmount": {}
}
]
response = self.rpc_request(method, params)
self.verify_is_valid_json_rpc_response(response)

self.wait_for_signal("wallet.suggested.routes")
assert self.received_signals[
'wallet.suggested.routes']['event']['Uuid'] == _uuid

method = "wallet_buildTransactionsFromRoute"
params = [
{
"uuid": _uuid,
"slippagePercentage": 0
}
]
response = self.rpc_request(method, params)
self.verify_is_valid_json_rpc_response(response)

self.wait_for_signal("wallet.router.sign-transactions")

assert self.received_signals[
'wallet.router.sign-transactions']['event']['signingDetails']['signOnKeycard'] == False
transaction_hashes = self.received_signals[
'wallet.router.sign-transactions']['event']['signingDetails']['hashes']

assert transaction_hashes, "Transaction hashes are empty!"

tx_signatures = {}

for hash in transaction_hashes:

method = "wallet_signMessage"
params = [
hash,
user_1.address,
option.password
]

response = self.rpc_request(method, params)
self.verify_is_valid_json_rpc_response(response)

if response.json()["result"].startswith("0x"):
tx_signature = response.json()["result"][2:]

signature = {
"r": tx_signature[:64],
"s": tx_signature[64:128],
"v": tx_signature[128:]
}

tx_signatures[hash] = signature

method = "wallet_sendRouterTransactionsWithSignatures"
params = [
{
"uuid": _uuid,
"Signatures": tx_signatures
}
]
response = self.rpc_request(method, params)
self.verify_is_valid_json_rpc_response(response)

tx_status = self.wait_for_signal("wallet.transaction.status-changed")


assert tx_status["event"]["chainId"] == 31337
assert tx_status["event"]["status"] == "Success"
tx_hash = tx_status["event"]["hash"]

method = "eth_getTransactionByHash"
params = [tx_hash]

response = self.rpc_request(method, params, url=option.anvil_url)
self.verify_is_valid_json_rpc_response(response)
tx_details = response.json()["result"]

assert tx_details["value"] == amount_in
assert tx_details["to"] == user_2.address
assert tx_details["from"] == user_1.address
3 changes: 1 addition & 2 deletions integration-tests/tests/test_wallet_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,7 @@ class TestRpc(RpcTestCase):
("wallet_getEthereumChains", []),
("wallet_getTokenList", []),
("wallet_getCryptoOnRamps", []),
("wallet_getCachedCurrencyFormats", []),
("wallet_fetchAllCurrencyFormats", [])
("wallet_getCachedCurrencyFormats", [])
],
)
def test_(self, method, params):
Expand Down
1 change: 1 addition & 0 deletions services/wallet/common/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const (
ArbitrumSepolia uint64 = 421614
BinanceChainID uint64 = 56 // obsolete?
BinanceTestChainID uint64 = 97 // obsolete?
AnvilMainnet uint64 = 31337
)

var (
Expand Down
22 changes: 15 additions & 7 deletions services/wallet/router/router_updates.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import (
)

var (
newBlockCheckIntervalMainnet = 3 * time.Second
newBlockCheckIntervalOptimism = 1 * time.Second
newBlockCheckIntervalArbitrum = 200 * time.Millisecond
newBlockCheckIntervalMainnet = 3 * time.Second
newBlockCheckIntervalOptimism = 1 * time.Second
newBlockCheckIntervalArbitrum = 200 * time.Millisecond
newBlockCheckIntervalAnvilMainnet = 2 * time.Second

feeRecalculationTimeout = 5 * time.Minute
feeRecalculationTimeout = 5 * time.Minute
feeRecalculationAnvilTimeout = 5 * time.Second
)

type fetchingLastBlock struct {
Expand All @@ -41,7 +43,11 @@ func (r *Router) subscribeForUdates(chainID uint64) error {
}
r.clientsForUpdatesPerChains.Store(chainID, flb)

r.startTimeoutForUpdates(flb.closeCh)
timeout := feeRecalculationTimeout
if chainID == walletCommon.AnvilMainnet {
timeout = feeRecalculationAnvilTimeout
}
r.startTimeoutForUpdates(flb.closeCh, timeout)

var ticker *time.Ticker
switch chainID {
Expand All @@ -54,6 +60,8 @@ func (r *Router) subscribeForUdates(chainID uint64) error {
case walletCommon.ArbitrumMainnet,
walletCommon.ArbitrumSepolia:
ticker = time.NewTicker(newBlockCheckIntervalArbitrum)
case walletCommon.AnvilMainnet:
ticker = time.NewTicker(newBlockCheckIntervalAnvilMainnet)
}

ctx, cancelCtx := context.WithCancel(context.Background())
Expand Down Expand Up @@ -121,8 +129,8 @@ func (r *Router) subscribeForUdates(chainID uint64) error {
return nil
}

func (r *Router) startTimeoutForUpdates(closeCh chan struct{}) {
dedlineTicker := time.NewTicker(feeRecalculationTimeout)
func (r *Router) startTimeoutForUpdates(closeCh chan struct{}, timeout time.Duration) {
dedlineTicker := time.NewTicker(timeout)
go func() {
for {
select {
Expand Down
Loading