refactor(substreams-testing): Use Pydantic to deserialize test_assets.yaml

This commit is contained in:
Florian Pellissier
2024-08-06 13:32:58 +02:00
committed by tvinagre
parent 09d266a810
commit 95efda0423
10 changed files with 365 additions and 233 deletions

View File

@@ -0,0 +1,169 @@
substreams_yaml_path: ./substreams.yaml
protocol_type_names:
- "balancer_pool"
adapter_contract: "BalancerV2SwapAdapter"
adapter_build_signature: "constructor(address)"
adapter_build_args: "0xBA12222222228d8Ba445958a75a0704d566BF2C8"
skip_balance_check: true
initialized_accounts:
- "0xba12222222228d8ba445958a75a0704d566bf2c8"
# Uncomment entries below to include composable stable pool dependencies
# wstETH dependencies
# - "0x72D07D7DcA67b8A406aD1Ec34ce969c90bFEE768"
# - "0xb8ffc3cd6e7cf5a098a1c92f48009765b24088dc"
# - "0xae7ab96520de3a18e5e111b5eaab095312d7fe84"
# - "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0"
# - "0x2b33cf282f867a7ff693a66e11b0fcc5552e4425"
# - "0x17144556fd3424edc8fc8a4c940b2d04936d17eb"
# sfrxETH dependencies
# - "0x302013E7936a39c358d07A3Df55dc94EC417E3a1"
# - "0xac3e018457b222d93114458476f3e3416abbe38f"
# rETH dependencies
# - "0x1a8F81c256aee9C640e14bB0453ce247ea0DFE6F"
# - "0x07fcabcbe4ff0d80c2b1eb42855c0131b6cba2f4"
# - "0x1d8f8f00cfa6758d7be78336684788fb0ee0fa46"
# - "0xae78736cd615f374d3085123a210448e74fc6393"
tests:
# WeightedPoolFactory - 0x897888115Ada5773E02aA29F775430BFB5F34c51
- name: test_weighted_pool_creation
start_block: 20128706
stop_block: 20128806
expected_components:
- id: "0xe96a45f66bdDA121B24F0a861372A72E8889523d"
tokens:
- "0x38C2a4a7330b22788374B8Ff70BBa513C8D848cA"
- "0x514910771AF9Ca656af840dff83E8264EcF986CA"
static_attributes:
rate_providers: "0x5b22307830303030303030303030303030303030303030303030303030303030303030303030303030303030222c22307830303030303030303030303030303030303030303030303030303030303030303030303030303030225d"
pool_id: "0x307865393661343566363662646461313231623234663061383631333732613732653838383935323364303030323030303030303030303030303030303030363962"
normalized_weights: "0x5b22307830623161326263326563353030303030222c22307830326336386166306262313430303030225d"
fee: "0x11c37937e08000"
manual_updates: "0x01"
pool_type: "0x5765696768746564506f6f6c466163746f7279"
creation_tx: "0xa63c671046ad2075ec8ea83ac21199cf3e3a5f433e72ec4c117cbabfb9b18de2"
# WeightedPool2TokensFactory - 0xA5bf2ddF098bb0Ef6d120C98217dD6B141c74EE0
- name: weighted_legacy_creation
start_block: 13148365
stop_block: 13148465
expected_components:
- id: "0xBF96189Eee9357a95C7719f4F5047F76bdE804E5"
tokens:
- "0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32"
- "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
static_attributes:
pool_id: "0x307862663936313839656565393335376139356337373139663466353034376637366264653830346535303030323030303030303030303030303030303030303837"
weights: "0x5b22307830623161326263326563353030303030222c22307830326336386166306262313430303030225d"
fee: "0x08e1bc9bf04000"
manual_updates: "0x01"
pool_type: "0x5765696768746564506f6f6c32546f6b656e73466163746f7279"
creation_tx: "0xdced662e41b1608c386551bbc89894a10321fd8bd58782e22077d1044cf99cb5"
# ComposableStablePoolFactory - 0xDB8d758BCb971e482B2C45f7F8a7740283A1bd3A
- name: test_composable_stable_pool_creation
start_block: 17677300
stop_block: 17678400
expected_components:
- id: "0x42ED016F826165C2e5976fe5bC3df540C5aD0Af7"
tokens:
- "0x42ed016f826165c2e5976fe5bc3df540c5ad0af7"
- "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"
- "0xac3E018457B222d93114458476f3E3416Abbe38F"
- "0xae78736Cd615f374D3085123A210448E74Fc6393"
static_attributes:
rate_providers: "0x5b22307837326430376437646361363762386134303661643165633334636539363963393062666565373638222c22307833303230313365373933366133396333353864303761336466353564633934656334313765336131222c22307831613866383163323536616565396336343065313462623034353363653234376561306466653666225d"
pool_id: "0x307834326564303136663832363136356332653539373666653562633364663534306335616430616637303030303030303030303030303030303030303030353862"
bpt: "0x42ed016f826165c2e5976fe5bc3df540c5ad0af7"
fee: "0x5af3107a4000"
manual_updates: "0x01"
pool_type: "0x436f6d706f7361626c65537461626c65506f6f6c466163746f7279"
skip_simulation: true
creation_tx: "0x53ff6bab0d8a76a998e29e59da8068ad906ae85507a1c2fbf2505e2cb52fd754"
# ERC4626LinearPoolFactory - 0x813EE7a840CE909E7Fea2117A44a90b8063bd4fd
- name: test_erc4626_linear_pool_creation
start_block: 17480142
stop_block: 17480242
expected_components:
- id: "0x3fCb7085B8F2F473F80bF6D879cAe99eA4DE9344"
tokens:
- "0x39Dd7790e75C6F663731f7E1FdC0f35007D3879b"
- "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
- "0x3fcb7085b8f2f473f80bf6d879cae99ea4de9344"
static_attributes:
pool_id: "0x307833666362373038356238663266343733663830626636643837396361653939656134646539333434303030303030303030303030303030303030303030353664"
wrapped_token: "0x39dd7790e75c6f663731f7e1fdc0f35007d3879b"
fee: "0x00b5e620f48000"
manual_updates: "0x01"
pool_type: "0x455243343632364c696e656172506f6f6c466163746f7279"
upper_target: "0x108b2a2c28029094000000"
bpt: "0x3fcb7085b8f2f473f80bf6d879cae99ea4de9344"
main_token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
skip_simulation: true
creation_tx: "0x5ff97870685370bab3876a4335d28c42e24659064fe78b486d6fb1b37b992877"
# EulerLinearPoolFactory - 0x5F43FBa61f63Fa6bFF101a0A0458cEA917f6B347
- name: test_euler_linear_pool_creation
start_block: 16588117
stop_block: 16588217
expected_components:
- id: "0xD4e7C1F3DA1144c9E2CfD1b015eDA7652b4a4399"
tokens:
- "0xD4e7C1F3DA1144c9E2CfD1b015eDA7652b4a4399"
- "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
- "0xEb91861f8A4e1C12333F42DCE8fB0Ecdc28dA716"
static_attributes:
pool_id: "0x307864346537633166336461313134346339653263666431623031356564613736353262346134333939303030303030303030303030303030303030303030343661"
wrapped_token: "0xeb91861f8a4e1c12333f42dce8fb0ecdc28da716"
fee: "0x00b5e620f48000"
manual_updates: "0x01"
pool_type: "0x45756c65724c696e656172506f6f6c466163746f7279"
upper_target: "0x108b2a2c28029094000000"
bpt: "0xd4e7c1f3da1144c9e2cfd1b015eda7652b4a4399"
main_token: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
skip_simulation: true
creation_tx: "0x4a9ea683052afefdae3d189862868c3a7dc8f431d1d9828b6bfd9451a8816426"
# SiloLinearPoolFactory - 0x4E11AEec21baF1660b1a46472963cB3DA7811C89
- name: test_silo_linear_pool_creation
start_block: 17173185
stop_block: 17173187
expected_components:
- id: "0x74CBfAF94A3577c539a9dCEE9870A6349a33b34f"
tokens:
- "0x192E67544694a7bAA2DeA94f9B1Df58BB3395A12"
- "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
- "0x74cbfaf94a3577c539a9dcee9870a6349a33b34f"
static_attributes:
pool_id: "0x307837346362666166393461333537376335333961396463656539383730613633343961333362333466303030303030303030303030303030303030303030353334"
wrapped_token: "0x192e67544694a7baa2dea94f9b1df58bb3395a12"
fee: "0x00e8d4a51000"
manual_updates: "0x01"
pool_type: "0x53696c6f4c696e656172506f6f6c466163746f7279"
upper_target: "0x00"
bpt: "0x74cbfaf94a3577c539a9dcee9870a6349a33b34f"
main_token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
skip_simulation: true
creation_tx: "0x215c9f4256ab450368132f4063611ae8cdd98e80bea7e44ecf0600ed1d757018"
# YearnLinearPoolFactory - 0x5F5222Ffa40F2AEd6380D022184D6ea67C776eE0a
- name: test_yearn_linear_pool_creation
start_block: 17052601
stop_block: 17052605
expected_components:
- id: "0xac5b4ef7ede2f2843a704e96dcaa637f4ba3dc3f"
tokens:
- "0x806E02Dea8d4a0882caD9fA3Fa75B212328692dE"
- "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
- "0xac5b4ef7ede2f2843a704e96dcaa637f4ba3dc3f"
static_attributes:
pool_id: "0x307861633562346566376564653266323834336137303465393664636161363337663462613364633366303030303030303030303030303030303030303030353164"
wrapped_token: "0x806e02dea8d4a0882cad9fa3fa75b212328692de"
fee: "0x00e8d4a51000"
manual_updates: "0x01"
pool_type: "0x596561726e4c696e656172506f6f6c466163746f7279"
upper_target: "0x00"
bpt: "0xac5b4ef7ede2f2843a704e96dcaa637f4ba3dc3f"
main_token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
skip_simulation: true
creation_tx: "0x497aa03ce84d236c183204ddfc6762c8e4158da1ebc5e7e18e7f6cceaa497a2a"

View File

@@ -1,127 +0,0 @@
substreams_yaml_path: ./substreams.yaml
protocol_type_names:
- "balancer_pool"
adapter_contract: "BalancerV2SwapAdapter"
adapter_build_signature: "constructor(address)"
adapter_build_args: "0xBA12222222228d8Ba445958a75a0704d566BF2C8"
skip_balance_check: true
initialized_accounts:
- "0xba12222222228d8ba445958a75a0704d566bf2c8"
# Uncomment entries below to include composable stable pool dependencies
# wstETH dependencies
# - "0x72D07D7DcA67b8A406aD1Ec34ce969c90bFEE768"
# - "0xb8ffc3cd6e7cf5a098a1c92f48009765b24088dc"
# - "0xae7ab96520de3a18e5e111b5eaab095312d7fe84"
# - "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0"
# - "0x2b33cf282f867a7ff693a66e11b0fcc5552e4425"
# - "0x17144556fd3424edc8fc8a4c940b2d04936d17eb"
# sfrxETH dependencies
# - "0x302013E7936a39c358d07A3Df55dc94EC417E3a1"
# - "0xac3e018457b222d93114458476f3e3416abbe38f"
# rETH dependencies
# - "0x1a8F81c256aee9C640e14bB0453ce247ea0DFE6F"
# - "0x07fcabcbe4ff0d80c2b1eb42855c0131b6cba2f4"
# - "0x1d8f8f00cfa6758d7be78336684788fb0ee0fa46"
# - "0xae78736cd615f374d3085123a210448e74fc6393"
tests:
# WeightedPoolFactory - 0x897888115Ada5773E02aA29F775430BFB5F34c51
- name: test_weighted_pool_creation
start_block: 20128706
stop_block: 20128806
expected_state:
protocol_components:
- id: "0xe96a45f66bdDA121B24F0a861372A72E8889523d"
tokens:
- "0x38C2a4a7330b22788374B8Ff70BBa513C8D848cA"
- "0x514910771AF9Ca656af840dff83E8264EcF986CA"
static_attributes: null
creation_tx: "0xa63c671046ad2075ec8ea83ac21199cf3e3a5f433e72ec4c117cbabfb9b18de2"
# WeightedPool2TokensFactory - 0xA5bf2ddF098bb0Ef6d120C98217dD6B141c74EE0
- name: weighted_legacy_creation
start_block: 13148365
stop_block: 13148465
expected_state:
protocol_components:
- id: "0xBF96189Eee9357a95C7719f4F5047F76bdE804E5"
tokens:
- "0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32"
- "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
static_attributes: null
creation_tx: "0xdced662e41b1608c386551bbc89894a10321fd8bd58782e22077d1044cf99cb5"
# ComposableStablePoolFactory - 0xDB8d758BCb971e482B2C45f7F8a7740283A1bd3A
- name: test_composable_stable_pool_creation
start_block: 17677300
stop_block: 17678400
expected_state:
protocol_components:
- id: "0x42ED016F826165C2e5976fe5bC3df540C5aD0Af7"
tokens:
- "0x42ed016f826165c2e5976fe5bc3df540c5ad0af7"
- "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"
- "0xac3E018457B222d93114458476f3E3416Abbe38F"
- "0xae78736Cd615f374D3085123A210448E74Fc6393"
static_attributes: null
skip_simulation: true
creation_tx: "0x53ff6bab0d8a76a998e29e59da8068ad906ae85507a1c2fbf2505e2cb52fd754"
# ERC4626LinearPoolFactory - 0x813EE7a840CE909E7Fea2117A44a90b8063bd4fd
- name: test_erc4626_linear_pool_creation
start_block: 17480142
stop_block: 17480242
expected_state:
protocol_components:
- id: "0x3fCb7085B8F2F473F80bF6D879cAe99eA4DE9344"
tokens:
- "0x39Dd7790e75C6F663731f7E1FdC0f35007D3879b"
- "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
- "0x3fcb7085b8f2f473f80bf6d879cae99ea4de9344"
static_attributes: null
skip_simulation: true
creation_tx: "0x5ff97870685370bab3876a4335d28c42e24659064fe78b486d6fb1b37b992877"
# EulerLinearPoolFactory - 0x5F43FBa61f63Fa6bFF101a0A0458cEA917f6B347
- name: test_euler_linear_pool_creation
start_block: 16588117
stop_block: 16588217
expected_state:
protocol_components:
- id: "0xD4e7C1F3DA1144c9E2CfD1b015eDA7652b4a4399"
tokens:
- "0xD4e7C1F3DA1144c9E2CfD1b015eDA7652b4a4399"
- "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
- "0xEb91861f8A4e1C12333F42DCE8fB0Ecdc28dA716"
static_attributes: null
skip_simulation: true
creation_tx: "0x4a9ea683052afefdae3d189862868c3a7dc8f431d1d9828b6bfd9451a8816426"
# SiloLinearPoolFactory - 0x4E11AEec21baF1660b1a46472963cB3DA7811C89
- name: test_silo_linear_pool_creation
start_block: 17173185
stop_block: 17173187
expected_state:
protocol_components:
- id: "0x74CBfAF94A3577c539a9dCEE9870A6349a33b34f"
tokens:
- "0x192E67544694a7bAA2DeA94f9B1Df58BB3395A12"
- "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
- "0x74cbfaf94a3577c539a9dcee9870a6349a33b34f"
static_attributes: null
skip_simulation: true
creation_tx: "0x215c9f4256ab450368132f4063611ae8cdd98e80bea7e44ecf0600ed1d757018"
# YearnLinearPoolFactory - 0x5F5222Ffa40F2AEd6380D022184D6ea67C776eE0a
- name: test_yearn_linear_pool_creation
start_block: 17052601
stop_block: 17052605
expected_state:
protocol_components:
- id: "0xac5b4ef7ede2f2843a704e96dcaa637f4ba3dc3f"
tokens:
- "0x806E02Dea8d4a0882caD9fA3Fa75B212328692dE"
- "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
- "0xac5b4ef7ede2f2843a704e96dcaa637f4ba3dc3f"
static_attributes: null
skip_simulation: true
creation_tx: "0x497aa03ce84d236c183204ddfc6762c8e4158da1ebc5e7e18e7f6cceaa497a2a"

View File

@@ -1,9 +1,9 @@
#![allow(clippy::all)] #![allow(clippy::all)]
pub mod crypto_pool_factory; pub mod crypto_pool_factory;
pub mod crypto_swap_ng_factory;
pub mod erc20;
pub mod meta_pool_factory;
pub mod meta_registry;
pub mod stableswap_factory; pub mod stableswap_factory;
pub mod crypto_swap_ng_factory;
pub mod meta_registry;
pub mod tricrypto_factory; pub mod tricrypto_factory;
pub mod twocrypto_factory; pub mod twocrypto_factory;
pub mod erc20;
pub mod meta_pool_factory;

View File

@@ -0,0 +1,38 @@
substreams_yaml_path: ./substreams.yaml
adapter_contract: "SwapAdapter.evm.runtime"
adapter_build_signature: "constructor(address)"
adapter_build_args: "0x0000000000000000000000000000000000000000"
skip_balance_check: false
initialized_accounts:
- "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84" # Needed for ....
protocol_type_names:
- "type_name_1"
- "type_name_2"
tests:
- name: test_pool_creation
start_block: 123
stop_block: 456
initialized_accounts:
- "0x0c0e5f2fF0ff18a3be9b835635039256dC4B4963" # Needed for ....
expected_components:
- id: "0xbebc44782c7db0a1a60cb6fe97d0b483032ff1c7"
tokens:
- "0xdac17f958d2ee523a2206206994597c13d831ec7"
- "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
- "0x6b175474e89094c44da98b954eedeac495271d0f"
static_attributes:
attr_1: "value"
attr_2: "value"
creation_tx: "0x20793bbf260912aae189d5d261ff003c9b9166da8191d8f9d63ff1c7722f3ac6"
skip_simulation: false
- name: test_something_else
start_block: 123
stop_block: 456
expected_components:
- id: "0xdc24316b9ae028f1497c275eb9192a3ea0f67022"
tokens:
- "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"
- "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84"
static_attributes: null
creation_tx: "0xfac67ecbd423a5b915deff06045ec9343568edaec34ae95c43d35f2c018afdaa"
skip_simulation: true # If true, always add a reason

View File

@@ -1,36 +0,0 @@
substreams_yaml_path: ./substreams.yaml
adapter_contract: "SwapAdapter.evm.runtime"
adapter_build_signature: "constructor(address)"
adapter_build_args: "0x0000000000000000000000000000000000000000"
skip_balance_check: false
protocol_type_names:
- "type_name_1"
- "type_name_2"
tests:
- name: test_pool_creation
start_block: 123
stop_block: 456
initialized_accounts:
- "0x0c0e5f2fF0ff18a3be9b835635039256dC4B4963" # Needed for ....
expected_state:
protocol_components:
- id: "0xbebc44782c7db0a1a60cb6fe97d0b483032ff1c7"
tokens:
- "0xdac17f958d2ee523a2206206994597c13d831ec7"
- "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
- "0x6b175474e89094c44da98b954eedeac495271d0f"
static_attributes:
creation_tx: "0x20793bbf260912aae189d5d261ff003c9b9166da8191d8f9d63ff1c7722f3ac6"
skip_simulation: false
- name: test_something_else
start_block: 123
stop_block: 456
expected_state:
protocol_components:
- id: "0xdc24316b9ae028f1497c275eb9192a3ea0f67022"
tokens:
- "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"
- "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84"
static_attributes:
creation_tx: "0xfac67ecbd423a5b915deff06045ec9343568edaec34ae95c43d35f2c018afdaa"
skip_simulation: true # If true, always add a reason

View File

@@ -14,7 +14,8 @@ The testing suite builds the `.spkg` for your Substreams module, indexes a speci
## Test Configuration ## Test Configuration
Tests are defined in a `yaml` file. A template can be found at `substreams/ethereum-template/test_assets.yaml`. The configuration file should include: Tests are defined in a `yaml` file. A template can be found at
`substreams/ethereum-template/integration_test.tycho.yaml`. The configuration file should include:
- The target Substreams config file. - The target Substreams config file.
- The expected protocol types. - The expected protocol types.

View File

@@ -0,0 +1,104 @@
from pydantic import BaseModel, Field
from typing import List, Dict, Optional
from tycho_client.dto import ProtocolComponent
class ProtocolComponentExpectation(BaseModel):
"""Represents a ProtocolComponent with its main attributes."""
id: str = Field(..., description="Identifier of the protocol component")
tokens: List[str] = Field(
...,
description="List of token addresses associated with the protocol component",
)
static_attributes: Optional[Dict[str, Optional[str]]] = Field(
default_factory=dict, description="Static attributes of the protocol component"
)
creation_tx: str = Field(
..., description="Hash of the transaction that created the protocol component"
)
def __init__(self, **data):
super().__init__(**data)
self.id = self.id.lower()
self.tokens = sorted([t.lower() for t in self.tokens])
def compare(self, other: "ProtocolComponentExpectation") -> Optional[str]:
"""Compares the current instance with another ProtocolComponent instance and returns a message with the differences or None if there are no differences."""
differences = []
for field_name, field_value in self.__dict__.items():
other_value = getattr(other, field_name, None)
if field_value != other_value:
differences.append(
f"Field '{field_name}' mismatch: '{field_value}' != '{other_value}'"
)
if not differences:
return None
return "\n".join(differences)
@staticmethod
def from_dto(dto: ProtocolComponent) -> "ProtocolComponentExpectation":
return ProtocolComponentExpectation(
id=dto.id,
tokens=[t.hex() for t in dto.tokens],
static_attributes={
key: value.hex() for key, value in dto.static_attributes.items()
},
creation_tx=dto.creation_tx.hex(),
)
class ProtocolComponentWithTestConfig(ProtocolComponentExpectation):
"""Represents a ProtocolComponent with its main attributes and test configuration."""
skip_simulation: Optional[bool] = Field(
False,
description="Flag indicating whether to skip simulation for this component",
)
def into_protocol_component(self) -> ProtocolComponentExpectation:
return ProtocolComponentExpectation(**self.dict())
class IntegrationTest(BaseModel):
"""Configuration for an individual test."""
name: str = Field(..., description="Name of the test")
start_block: int = Field(..., description="Starting block number for the test")
stop_block: int = Field(..., description="Stopping block number for the test")
initialized_accounts: Optional[List[str]] = Field(
None, description="List of initialized account addresses"
)
expected_components: List[ProtocolComponentWithTestConfig] = Field(
..., description="List of protocol components expected in the indexed state"
)
class IntegrationTestsConfig(BaseModel):
"""Main integration test configuration."""
substreams_yaml_path: str = Field(
"./substreams.yaml", description="Path of the Substreams YAML file"
)
adapter_contract: str = Field(
..., description="Name of the SwapAdapter contract for this protocol"
)
adapter_build_signature: Optional[str] = Field(
None, description="Signatre of the SwapAdapter constructor for this protocol"
)
adapter_build_args: Optional[str] = Field(
None, description="Arguments for the SwapAdapter constructor for this protocol"
)
initialized_accounts: Optional[List[str]] = Field(
None,
description="List of initialized account addresses. These accounts will be initialized for every tests",
)
skip_balance_check: bool = Field(
..., description="Flag to skip balance check for all tests"
)
protocol_type_names: List[str] = Field(
..., description="List of protocol type names for the tested protocol"
)
tests: List[IntegrationTest] = Field(..., description="List of integration tests")

View File

@@ -7,6 +7,7 @@ from collections import defaultdict
from datetime import datetime from datetime import datetime
from decimal import Decimal from decimal import Decimal
from pathlib import Path from pathlib import Path
from typing import List
import yaml import yaml
from protosim_py.evm.decoders import ThirdPartyPoolTychoDecoder from protosim_py.evm.decoders import ThirdPartyPoolTychoDecoder
@@ -27,6 +28,11 @@ from tycho_client.dto import (
) )
from tycho_client.rpc_client import TychoRPCClient from tycho_client.rpc_client import TychoRPCClient
from models import (
IntegrationTestsConfig,
ProtocolComponentWithTestConfig,
ProtocolComponentExpectation,
)
from adapter_handler import AdapterContractHandler from adapter_handler import AdapterContractHandler
from evm import get_token_balance, get_block_header from evm import get_token_balance, get_block_header
from tycho import TychoRunner from tycho import TychoRunner
@@ -47,10 +53,10 @@ class TestResult:
return cls(success=False, message=message) return cls(success=False, message=message)
def load_config(yaml_path: str) -> dict: def parse_config(yaml_path: str) -> IntegrationTestsConfig:
"""Load YAML configuration from a specified file path."""
with open(yaml_path, "r") as file: with open(yaml_path, "r") as file:
return yaml.safe_load(file) yaml_content = yaml.safe_load(file)
return IntegrationTestsConfig(**yaml_content)
class SimulationFailure(BaseModel): class SimulationFailure(BaseModel):
@@ -66,13 +72,13 @@ class TestRunner:
): ):
self.repo_root = os.getcwd() self.repo_root = os.getcwd()
config_path = os.path.join( config_path = os.path.join(
self.repo_root, "substreams", package, "test_assets.yaml" self.repo_root, "substreams", package, "integration_test.tycho.yaml"
) )
self.config = load_config(config_path) self.config: IntegrationTestsConfig = parse_config(config_path)
self.spkg_src = os.path.join(self.repo_root, "substreams", package) self.spkg_src = os.path.join(self.repo_root, "substreams", package)
self.adapters_src = os.path.join(self.repo_root, "evm") self.adapters_src = os.path.join(self.repo_root, "evm")
self.tycho_runner = TychoRunner( self.tycho_runner = TychoRunner(
db_url, with_binary_logs, self.config["initialized_accounts"] db_url, with_binary_logs, self.config.initialized_accounts
) )
self.tycho_rpc_client = TychoRPCClient() self.tycho_rpc_client = TychoRPCClient()
self._token_factory_func = token_factory(self.tycho_rpc_client) self._token_factory_func = token_factory(self.tycho_rpc_client)
@@ -83,32 +89,35 @@ class TestRunner:
def run_tests(self) -> None: def run_tests(self) -> None:
"""Run all tests specified in the configuration.""" """Run all tests specified in the configuration."""
print(f"Running tests ...") print(f"Running tests ...")
for test in self.config["tests"]: for test in self.config.tests:
self.tycho_runner.empty_database(self.db_url) self.tycho_runner.empty_database(self.db_url)
spkg_path = self.build_spkg( spkg_path = self.build_spkg(
os.path.join(self.spkg_src, self.config["substreams_yaml_path"]), os.path.join(self.spkg_src, self.config.substreams_yaml_path),
lambda data: self.update_initial_block(data, test["start_block"]), lambda data: self.update_initial_block(data, test.start_block),
) )
self.tycho_runner.run_tycho( self.tycho_runner.run_tycho(
spkg_path, spkg_path,
test["start_block"], test.start_block,
test["stop_block"], test.stop_block,
self.config["protocol_type_names"], self.config.protocol_type_names,
test.get("initialized_accounts", []), test.initialized_accounts or [],
) )
result = self.tycho_runner.run_with_rpc_server( result = self.tycho_runner.run_with_rpc_server(
self.validate_state, test["expected_state"], test["stop_block"] self.validate_state, test.expected_components, test.stop_block
) )
if result.success: if result.success:
print(f"{test['name']} passed.") print(f"{test.name} passed.")
else: else:
print(f"❗️ {test['name']} failed: {result.message}") print(f"❗️ {test.name} failed: {result.message}")
def validate_state(self, expected_state: dict, stop_block: int) -> TestResult: def validate_state(
self,
expected_components: List[ProtocolComponentWithTestConfig],
stop_block: int,
) -> TestResult:
"""Validate the current protocol state against the expected state.""" """Validate the current protocol state against the expected state."""
protocol_components = self.tycho_rpc_client.get_protocol_components( protocol_components = self.tycho_rpc_client.get_protocol_components(
ProtocolComponentsParams(protocol_system="test_protocol") ProtocolComponentsParams(protocol_system="test_protocol")
@@ -121,43 +130,18 @@ class TestRunner:
} }
try: try:
for expected_component in expected_state.get("protocol_components", []): for expected_component in expected_components:
comp_id = expected_component["id"].lower() comp_id = expected_component.id.lower()
if comp_id not in components_by_id: if comp_id not in components_by_id:
return TestResult.Failed( return TestResult.Failed(
f"'{comp_id}' not found in protocol components." f"'{comp_id}' not found in protocol components."
) )
# TODO: Manipulate pydantic objects instead of dict diff = ProtocolComponentExpectation.from_dto(
component = components_by_id[comp_id].dict() components_by_id[comp_id]
for key, value in expected_component.items(): ).compare(expected_component.into_protocol_component())
if key not in ["tokens", "static_attributes", "creation_tx"]: if diff is not None:
continue return TestResult.Failed(diff)
if key not in component:
return TestResult.Failed(
f"Missing '{key}' in component '{comp_id}'."
)
if key == "tokens":
if set(map(HexBytes, value)) != set(component[key]):
return TestResult.Failed(
f"Token mismatch for key '{key}': {value} != {component[key]}"
)
elif key == "creation_tx":
if HexBytes(value) != component[key]:
return TestResult.Failed(
f"Creation tx mismatch for key '{key}': {value} != {component[key]}"
)
elif isinstance(value, list):
if set(map(str.lower, value)) != set(
map(str.lower, component[key])
):
return TestResult.Failed(
f"List mismatch for key '{key}': {value} != {component[key]}"
)
elif value is not None and value.lower() != component[key]:
return TestResult.Failed(
f"Value mismatch for key '{key}': {value} != {component[key]}"
)
token_balances: dict[str, dict[HexBytes, int]] = defaultdict(dict) token_balances: dict[str, dict[HexBytes, int]] = defaultdict(dict)
for component in protocol_components: for component in protocol_components:
@@ -178,7 +162,7 @@ class TestRunner:
tycho_balance = int(balance_hex) tycho_balance = int(balance_hex)
token_balances[comp_id][token] = tycho_balance token_balances[comp_id][token] = tycho_balance
if self.config["skip_balance_check"] is not True: if self.config.skip_balance_check is not True:
node_balance = get_token_balance(token, comp_id, stop_block) node_balance = get_token_balance(token, comp_id, stop_block)
if node_balance != tycho_balance: if node_balance != tycho_balance:
return TestResult.Failed( return TestResult.Failed(
@@ -198,11 +182,7 @@ class TestRunner:
pc pc
for pc in protocol_components for pc in protocol_components
if pc.id if pc.id
in [ in [c.id for c in expected_components if c.skip_simulation is False]
c["id"].lower()
for c in expected_state["protocol_components"]
if c.get("skip_simulation", False) is False
]
] ]
simulation_failures = self.simulate_get_amount_out( simulation_failures = self.simulate_get_amount_out(
stop_block, protocol_states, filtered_components, contract_states stop_block, protocol_states, filtered_components, contract_states
@@ -231,7 +211,7 @@ class TestRunner:
contract_states: list[ResponseAccount], contract_states: list[ResponseAccount],
) -> dict[str, list[SimulationFailure]]: ) -> dict[str, list[SimulationFailure]]:
TychoDBSingleton.initialize() TychoDBSingleton.initialize()
protocol_type_names = self.config["protocol_type_names"] protocol_type_names = self.config.protocol_type_names
block_header = get_block_header(block_number) block_header = get_block_header(block_number)
block: EVMBlock = EVMBlock( block: EVMBlock = EVMBlock(
@@ -245,17 +225,17 @@ class TestRunner:
adapter_contract = os.path.join( adapter_contract = os.path.join(
self.adapters_src, self.adapters_src,
"out", "out",
f"{self.config['adapter_contract']}.sol", f"{self.config.adapter_contract}.sol",
f"{self.config['adapter_contract']}.evm.runtime", f"{self.config.adapter_contract}.evm.runtime",
) )
if not os.path.exists(adapter_contract): if not os.path.exists(adapter_contract):
print("Adapter contract not found. Building it ...") print("Adapter contract not found. Building it ...")
AdapterContractHandler.build_target( AdapterContractHandler.build_target(
self.adapters_src, self.adapters_src,
self.config["adapter_contract"], self.config.adapter_contract,
self.config["adapter_build_signature"], self.config.adapter_build_signature,
self.config["adapter_build_args"], self.config.adapter_build_args,
) )
decoder = ThirdPartyPoolTychoDecoder( decoder = ThirdPartyPoolTychoDecoder(

View File

@@ -82,11 +82,14 @@ class TychoRunner:
"--stop-block", "--stop-block",
# +2 is to make up for the cache in the index side. # +2 is to make up for the cache in the index side.
str(end_block + 2), str(end_block + 2),
"--initialization-block",
str(start_block),
] ]
+ ( + (
["--initialized-accounts", ",".join(all_accounts)] [
"--initialized-accounts",
",".join(all_accounts),
"--initialization-block",
str(start_block),
]
if all_accounts if all_accounts
else [] else []
), ),