fix(curve): fix curve tests after SDKv2 update, also add a few missing tests

This commit is contained in:
Florian Pellissier
2024-07-31 11:40:26 +02:00
parent 115e552f2f
commit 4d528ddab3
4 changed files with 383 additions and 80 deletions

View File

@@ -5,12 +5,19 @@ use itertools::Itertools;
use substreams::{
pb::substreams::StoreDeltas,
scalar::BigInt,
store::{StoreAddBigInt, StoreGet, StoreGetString, StoreNew, StoreSet, StoreSetString},
store::{
StoreAddBigInt, StoreGet, StoreGetInt64, StoreGetString, StoreNew, StoreSet, StoreSetInt64,
StoreSetString,
},
};
use substreams_ethereum::pb::eth;
use crate::{pool_changes::emit_eth_deltas, pool_factories, pools::emit_specific_pools};
use crate::{
consts::{CRYPTO_SWAP_NG_FACTORY, TRICRYPTO_FACTORY},
pool_changes::emit_eth_deltas,
pool_factories,
pools::emit_specific_pools,
};
use tycho_substreams::{
balances::{extract_balance_deltas_from_tx, store_balance_changes},
contract::extract_contract_changes,
@@ -99,6 +106,43 @@ pub fn store_component_tokens(map: BlockTransactionProtocolComponents, store: St
});
}
/// Stores contracts required by components, for example LP tokens if they are different from the
/// pool.
/// This is later used to index them with `extract_contract_changes`
#[substreams::handlers::store]
pub fn store_non_component_accounts(map: BlockTransactionProtocolComponents, store: StoreSetInt64) {
map.tx_components
.iter()
.flat_map(|tx_components| &tx_components.components)
.for_each(|component| {
// Crypto pool factory creates LP token separated from the pool, we need to index it so we add it to the store if the new protocol component comes from this factory
if component
.static_att
.contains(&Attribute {
name: "pool_type".into(),
value: "crypto_pool".into(),
change: ChangeType::Creation.into(),
}) &&
component
.static_att
.contains(&Attribute {
name: "factory_name".into(),
value: "crypto_pool_factory".into(),
change: ChangeType::Creation.into(),
})
{
let lp_token = component
.static_att
.iter()
.find(|attr| attr.name == "lp_token")
.unwrap()
.value
.clone();
store.set(0, hex::encode(lp_token), &1);
}
});
}
/// Since the `PoolBalanceChanged` events administer only deltas, we need to leverage a map and a
/// store to be able to tally up final balances for tokens in a pool.
#[substreams::handlers::map]
@@ -153,6 +197,7 @@ pub fn map_protocol_changes(
grouped_components: BlockTransactionProtocolComponents,
deltas: BlockBalanceDeltas,
components_store: StoreGetString,
non_component_accounts_store: StoreGetInt64,
balance_store: StoreDeltas, // Note, this map module is using the `deltas` mode for the store.
) -> Result<BlockChanges> {
// We merge contract changes by transaction (identified by transaction index) making it easy to
@@ -242,7 +287,12 @@ pub fn map_protocol_changes(
|addr| {
components_store
.get_last(format!("pool:{0}", hex::encode(addr)))
.is_some()
.is_some() ||
non_component_accounts_store
.get_last(hex::encode(addr))
.is_some() ||
addr.eq(&CRYPTO_SWAP_NG_FACTORY) ||
addr.eq(&TRICRYPTO_FACTORY)
},
&mut transaction_changes,
);

View File

@@ -1,3 +1,4 @@
use substreams::hex;
use substreams_ethereum::{
pb::eth::v2::{Call, Log, TransactionTrace},
Event, Function,
@@ -66,9 +67,9 @@ fn swap_weth_for_eth(tokens: Vec<Vec<u8>>) -> Vec<Vec<u8>> {
/// The basic flow of this function is as follows:
/// - Match the factory address
/// - Decode the relevant event from the log
/// - Attempt to decode the cooresponding function call (based on the permutation of the ABI)
/// - Attempt to decode the corresponding function call (based on the permutation of the ABI)
/// - Optionally make an RPC call to produce further information (see metapools)
/// - Construct the cooresponding `ProtocolComponent`
/// - Construct the corresponding `ProtocolComponent`
pub fn address_map(
call_address: &[u8; 20],
log: &Log,
@@ -84,6 +85,12 @@ pub fn address_map(
let component_id = &call.return_data[12..];
let pool_implementation = abi::crypto_pool_factory::functions::PoolImplementation {}
.call(CRYPTO_POOL_FACTORY.to_vec())?;
let token_implementation = abi::crypto_pool_factory::functions::TokenImplementation {}
.call(CRYPTO_POOL_FACTORY.to_vec())?;
Some(ProtocolComponent {
id: hex::encode(component_id),
tx: Some(Transaction {
@@ -93,7 +100,7 @@ pub fn address_map(
index: tx.index.into(),
}),
tokens,
contracts: vec![component_id.into()],
contracts: vec![component_id.into(), pool_added.token.clone()],
static_att: vec![
Attribute {
name: "pool_type".into(),
@@ -115,6 +122,21 @@ pub fn address_map(
value: address_to_bytes_with_0x(&CRYPTO_POOL_FACTORY),
change: ChangeType::Creation.into(),
},
Attribute {
name: "lp_token".into(),
value: pool_added.token,
change: ChangeType::Creation.into(),
},
Attribute {
name: "stateless_contract_addr_0".into(),
value: address_to_bytes_with_0x(&pool_implementation.try_into().unwrap()),
change: ChangeType::Creation.into(),
},
Attribute {
name: "stateless_contract_addr_1".into(),
value: address_to_bytes_with_0x(&token_implementation.try_into().unwrap()),
change: ChangeType::Creation.into(),
},
],
change: ChangeType::Creation.into(),
protocol_type: Some(ProtocolType {
@@ -164,6 +186,17 @@ pub fn address_map(
// The return data of several of these calls contain the actual component id
let component_id = &call.return_data[12..];
let tokens: Vec<_> = pool_added
.coins
.into_iter()
.filter(|token| *token != [0; 20])
.collect();
let pool_implementation = abi::meta_pool_factory::functions::PlainImplementations {
arg0: BigInt::from(tokens.len()),
arg1: add_pool.implementation_idx,
}
.call(META_POOL_FACTORY.to_vec())?;
Some(ProtocolComponent {
id: hex::encode(component_id),
@@ -173,11 +206,7 @@ pub fn address_map(
hash: tx.hash.clone(),
index: tx.index.into(),
}),
tokens: pool_added
.coins
.into_iter()
.filter(|token| *token != [0; 20])
.collect(),
tokens,
contracts: vec![component_id.into()],
static_att: vec![
Attribute {
@@ -200,6 +229,13 @@ pub fn address_map(
value: address_to_bytes_with_0x(&META_POOL_FACTORY),
change: ChangeType::Creation.into(),
},
Attribute {
name: "stateless_contract_addr_0".into(),
value: address_to_bytes_with_0x(
&pool_implementation.try_into().unwrap(),
),
change: ChangeType::Creation.into(),
},
],
change: ChangeType::Creation.into(),
protocol_type: Some(ProtocolType {
@@ -238,6 +274,14 @@ pub fn address_map(
abi::meta_registry::functions::GetLpToken1 { pool: add_pool.base_pool.clone() };
let lp_token = get_lp_token.call(META_REGISTRY.to_vec())?;
let pool_implementation =
abi::meta_pool_factory::functions::MetapoolImplementations {
base_pool: add_pool.base_pool.clone(),
}
.call(META_POOL_FACTORY.to_vec())?
[usize::try_from(add_pool.implementation_idx.to_u64()).unwrap()]
.clone();
Some(ProtocolComponent {
id: hex::encode(component_id),
tx: Some(Transaction {
@@ -247,7 +291,7 @@ pub fn address_map(
index: tx.index.into(),
}),
tokens: vec![pool_added.coin, lp_token],
contracts: vec![component_id.into()],
contracts: vec![component_id.into(), add_pool.base_pool.clone()],
static_att: vec![
Attribute {
name: "pool_type".into(),
@@ -276,6 +320,13 @@ pub fn address_map(
),
change: ChangeType::Creation.into(),
},
Attribute {
name: "stateless_contract_addr_0".into(),
value: address_to_bytes_with_0x(
&pool_implementation.try_into().unwrap(),
),
change: ChangeType::Creation.into(),
},
],
change: ChangeType::Creation.into(),
protocol_type: Some(ProtocolType {
@@ -309,6 +360,20 @@ pub fn address_map(
call,
)
})?;
let filtered: Vec<_> = tx
.calls
.iter()
.filter(|call| !call.account_creations.is_empty())
.collect();
let implementation = extract_eip1167_target_from_code(
filtered
.first()?
.code_changes
.first()?
.new_code
.clone(),
);
let component_id = &call.return_data[12..];
let lp_token = get_token_from_pool(&pool_added.base_pool);
@@ -351,6 +416,11 @@ pub fn address_map(
),
change: ChangeType::Creation.into(),
},
Attribute {
name: "stateless_contract_addr_0".into(),
value: address_to_bytes_with_0x(&implementation.try_into().unwrap()),
change: ChangeType::Creation.into(),
},
],
change: ChangeType::Creation.into(),
protocol_type: Some(ProtocolType {
@@ -382,7 +452,7 @@ pub fn address_map(
index: tx.index.into(),
}),
tokens: pool_added.coins,
contracts: vec![component_id.into()],
contracts: vec![component_id.into(), CRYPTO_SWAP_NG_FACTORY.into()],
static_att: vec![
Attribute {
name: "pool_type".into(),
@@ -404,6 +474,16 @@ pub fn address_map(
value: address_to_bytes_with_0x(&CRYPTO_SWAP_NG_FACTORY),
change: ChangeType::Creation.into(),
},
Attribute {
name: "stateless_contract_addr_0".into(),
// Call views_implementation() on CRYPTO_SWAP_NG_FACTORY
value: format!(
"call:{}:views_implementation()",
format!("0x{}", hex::encode(CRYPTO_SWAP_NG_FACTORY))
)
.into(),
change: ChangeType::Creation.into(),
},
],
change: ChangeType::Creation.into(),
protocol_type: Some(ProtocolType {
@@ -430,7 +510,11 @@ pub fn address_map(
index: tx.index.into(),
}),
tokens: vec![pool_added.coin, lp_token],
contracts: vec![component_id.into()],
contracts: vec![
component_id.into(),
CRYPTO_SWAP_NG_FACTORY.into(),
pool_added.base_pool.clone(),
],
static_att: vec![
Attribute {
name: "pool_type".into(),
@@ -459,6 +543,26 @@ pub fn address_map(
),
change: ChangeType::Creation.into(),
},
Attribute {
name: "stateless_contract_addr_0".into(),
// Call views_implementation() on CRYPTO_SWAP_NG_FACTORY
value: format!(
"call:{}:views_implementation()",
format!("0x{}", hex::encode(CRYPTO_SWAP_NG_FACTORY))
)
.into(),
change: ChangeType::Creation.into(),
},
Attribute {
name: "stateless_contract_addr_1".into(),
// Call math_implementation() on CRYPTO_SWAP_NG_FACTORY
value: format!(
"call:{}:math_implementation()",
format!("0x{}", hex::encode(CRYPTO_SWAP_NG_FACTORY))
)
.into(),
change: ChangeType::Creation.into(),
},
],
change: ChangeType::Creation.into(),
protocol_type: Some(ProtocolType {
@@ -477,6 +581,60 @@ pub fn address_map(
abi::tricrypto_factory::events::TricryptoPoolDeployed::match_and_decode(log)
{
let tokens = swap_weth_for_eth(pool_added.coins.into());
let mut static_attributes = vec![
Attribute {
name: "pool_type".into(),
value: "tricrypto".into(),
change: ChangeType::Creation.into(),
},
Attribute {
name: "name".into(),
value: pool_added.name.into(),
change: ChangeType::Creation.into(),
},
Attribute {
name: "factory_name".into(),
value: "tricrypto_factory".into(),
change: ChangeType::Creation.into(),
},
Attribute {
name: "factory".into(),
value: address_to_bytes_with_0x(&TRICRYPTO_FACTORY),
change: ChangeType::Creation.into(),
},
Attribute {
name: "stateless_contract_addr_0".into(),
// Call views_implementation() on TRICRYPTO_FACTORY
value: format!(
"call:{}:views_implementation()",
format!("0x{}", hex::encode(TRICRYPTO_FACTORY))
)
.into(),
change: ChangeType::Creation.into(),
},
Attribute {
name: "stateless_contract_addr_1".into(),
// Call math_implementation() on TRICRYPTO_FACTORY
value: format!(
"call:{}:math_implementation()",
format!("0x{}", hex::encode(TRICRYPTO_FACTORY))
)
.into(),
change: ChangeType::Creation.into(),
},
];
// This is relevant only if the contract has ETH
if tokens.contains(&ETH_ADDRESS.into()) {
static_attributes.push(Attribute {
name: "stateless_contract_addr_2".into(),
// WETH
value: address_to_bytes_with_0x(&hex!(
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
)),
change: ChangeType::Creation.into(),
})
}
Some(ProtocolComponent {
id: hex::encode(&pool_added.pool),
@@ -487,29 +645,8 @@ pub fn address_map(
index: tx.index.into(),
}),
tokens,
contracts: vec![pool_added.pool],
static_att: vec![
Attribute {
name: "pool_type".into(),
value: "tricrypto".into(),
change: ChangeType::Creation.into(),
},
Attribute {
name: "name".into(),
value: pool_added.name.into(),
change: ChangeType::Creation.into(),
},
Attribute {
name: "factory_name".into(),
value: "tricrypto_factory".into(),
change: ChangeType::Creation.into(),
},
Attribute {
name: "factory".into(),
value: address_to_bytes_with_0x(&TRICRYPTO_FACTORY),
change: ChangeType::Creation.into(),
},
],
contracts: vec![pool_added.pool, TRICRYPTO_FACTORY.into()],
static_att: static_attributes,
change: ChangeType::Creation.into(),
protocol_type: Some(ProtocolType {
name: "curve_pool".into(),
@@ -558,6 +695,20 @@ pub fn address_map(
return None;
};
let component_id = &call.return_data[12..];
let tokens: Vec<_> = pool_added
.coins
.into_iter()
.filter(|token| *token != [0; 20])
.collect();
let pool_implementation =
abi::stableswap_factory::functions::PlainImplementations {
arg0: BigInt::from(tokens.len()),
arg1: add_pool.implementation_idx,
}
.call(STABLESWAP_FACTORY.to_vec())?;
Some(ProtocolComponent {
id: hex::encode(component_id),
tx: Some(Transaction {
@@ -566,11 +717,7 @@ pub fn address_map(
hash: tx.hash.clone(),
index: tx.index.into(),
}),
tokens: pool_added
.coins
.into_iter()
.filter(|token| *token != [0; 20])
.collect(),
tokens,
contracts: vec![component_id.into()],
static_att: vec![
Attribute {
@@ -593,6 +740,13 @@ pub fn address_map(
value: address_to_bytes_with_0x(&STABLESWAP_FACTORY),
change: ChangeType::Creation.into(),
},
Attribute {
name: "stateless_contract_addr_0".into(),
value: address_to_bytes_with_0x(
&pool_implementation.try_into().unwrap(),
),
change: ChangeType::Creation.into(),
},
],
change: ChangeType::Creation.into(),
protocol_type: Some(ProtocolType {
@@ -694,7 +848,7 @@ pub fn address_map(
index: tx.index.into(),
}),
tokens: pool_added.coins.into(),
contracts: vec![pool_added.pool],
contracts: vec![pool_added.pool, TWOCRYPTO_FACTORY.into()],
static_att: vec![
Attribute {
name: "pool_type".into(),
@@ -716,6 +870,26 @@ pub fn address_map(
value: address_to_bytes_with_0x(&TWOCRYPTO_FACTORY),
change: ChangeType::Creation.into(),
},
Attribute {
name: "stateless_contract_addr_0".into(),
// Call views_implementation() on TWOCRYPTO_FACTORY
value: format!(
"call:{}:views_implementation()",
format!("0x{}", hex::encode(TWOCRYPTO_FACTORY))
)
.into(),
change: ChangeType::Creation.into(),
},
Attribute {
name: "stateless_contract_addr_1".into(),
// Call math_implementation() on TWOCRYPTO_FACTORY
value: format!(
"call:{}:math_implementation()",
format!("0x{}", hex::encode(TWOCRYPTO_FACTORY))
)
.into(),
change: ChangeType::Creation.into(),
},
],
change: ChangeType::Creation.into(),
protocol_type: Some(ProtocolType {
@@ -769,3 +943,20 @@ fn get_token_from_pool(pool: &Vec<u8>) -> Vec<u8> {
})
.unwrap()
}
//TODO: We can most likely replace many RPC calls above using this function.
fn extract_eip1167_target_from_code(code: Vec<u8>) -> [u8; 20] {
let mut target = [0u8; 20];
// Depending on the Vyper version, they use different implementation of EIP1167.
// We use the first 10 bytes of the code to make a clear distinction.
match code[0..10] {
[54, 61, 61, 55, 61, 61, 61, 54, 61, 115] => target.copy_from_slice(&code[10..30]),
[54, 96, 0, 96, 0, 55, 97, 16, 0, 96] => target.copy_from_slice(&code[15..35]),
_ => {
panic!("unknown implementation")
}
}
target
}