From 64ae56be854c06e375467430d00e7612e7e94403 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Fri, 20 Dec 2024 13:42:02 +0700 Subject: [PATCH 01/11] init EulerSwapAdapter --- evm/src/euler-swap/EulerSwapAdapter.sol | 136 ++++++++++++++++++++++++ evm/src/euler-swap/manifest.yaml | 24 +++++ 2 files changed, 160 insertions(+) create mode 100644 evm/src/euler-swap/EulerSwapAdapter.sol create mode 100644 evm/src/euler-swap/manifest.yaml diff --git a/evm/src/euler-swap/EulerSwapAdapter.sol b/evm/src/euler-swap/EulerSwapAdapter.sol new file mode 100644 index 000000000..2b885be59 --- /dev/null +++ b/evm/src/euler-swap/EulerSwapAdapter.sol @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity ^0.8.13; + +import {ISwapAdapter} from "src/interfaces/ISwapAdapter.sol"; +import { + IERC20, + SafeERC20 +} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; + +contract EulerSwapAdapter is ISwapAdapter { + using SafeERC20 for IERC20; + + IMaglevEulerSwapFactory immutable factory; + + constructor(address factory_) { + factory = IMaglevEulerSwapFactory(factory_); + } + + /// @inheritdoc ISwapAdapter + function price( + bytes32 poolId, + address sellToken, + address buyToken, + uint256[] memory specifiedAmounts + ) external view override returns (Fraction[] memory prices) { + prices = new Fraction[](specifiedAmounts.length); + + IMaglevEulerSwap pool = IMaglevEulerSwap(address(bytes20(poolId))); + for (uint256 i = 0; i < specifiedAmounts.length; i++) { + prices[i] = priceSingle(pool, sellToken, buyToken, specifiedAmounts[i]); + } + } + + /// @inheritdoc ISwapAdapter + function getCapabilities(bytes32, address, address) + external + pure + override + returns (Capability[] memory capabilities) + { + capabilities = new Capability[](3); + capabilities[0] = Capability.SellOrder; + capabilities[1] = Capability.BuyOrder; + capabilities[2] = Capability.PriceFunction; + } + + /// @inheritdoc ISwapAdapter + function getTokens(bytes32 poolId) + external + view + override + returns (address[] memory tokens) + { + tokens = new address[](2); + IMaglevEulerSwap pool = IMaglevEulerSwap(address(bytes20(poolId))); + tokens[0] = address(pool.asset0()); + tokens[1] = address(pool.asset1()); + } + + /// @inheritdoc ISwapAdapter + function getPoolIds(uint256 offset, uint256 limit) + external + view + override + returns (bytes32[] memory ids) + { + uint256 endIdx = offset + limit; + if (endIdx > factory.allPoolsLength()) { + endIdx = factory.allPoolsLength(); + } + ids = new bytes32[](endIdx - offset); + for (uint256 i = 0; i < ids.length; i++) { + ids[i] = bytes20(factory.allPools(offset + i)); + } + } + + /// @notice Calculates pool prices for specified amounts + function priceSingle( + IMaglevEulerSwap pool, + address tokenIn, + address tokenOut, + uint256 amountIn + ) internal view returns (Fraction memory calculatedPrice) { + calculatedPrice = + Fraction(pool.quoteExactInput(tokenIn, tokenOut, amountIn), amountIn); + } +} + +interface IMaglevEulerSwapFactory { + event PoolDeployed(address indexed asset0, address indexed asset1, uint256 indexed feeMultiplier, address pool); + + function deployPool( + address vault0, + address vault1, + address holder, + uint112 debtLimit0, + uint112 debtLimit1, + uint256 fee, + uint256 priceX, + uint256 priceY, + uint256 concentrationX, + uint256 concentrationY + ) external returns (address); + + function evc() external view returns (address); + function allPools(uint256 index) external view returns (address); + function getPool(address assetA, address assetB, uint256 fee) external view returns (address); + function allPoolsLength() external view returns (uint256); + function getAllPoolsListSlice(uint256 start, uint256 end) external view returns (address[] memory); +} + +interface IMaglevEulerSwap { + // IMaglevBase + function configure() external; + function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external; + function quoteExactInput(address tokenIn, address tokenOut, uint256 amountIn) external view returns (uint256); + function quoteExactOutput(address tokenIn, address tokenOut, uint256 amountOut) external view returns (uint256); + + function vault0() external view returns (address); + function vault1() external view returns (address); + function asset0() external view returns (address); + function asset1() external view returns (address); + function myAccount() external view returns (address); + function debtLimit0() external view returns (uint112); + function debtLimit1() external view returns (uint112); + function feeMultiplier() external view returns (uint256); + function getReserves() external view returns (uint112, uint112, uint32); + + // IMaglevEulerSwap + function priceX() external view returns (uint256); + function priceY() external view returns (uint256); + function concentrationX() external view returns (uint256); + function concentrationY() external view returns (uint256); + function initialReserve0() external view returns (uint112); + function initialReserve1() external view returns (uint112); +} diff --git a/evm/src/euler-swap/manifest.yaml b/evm/src/euler-swap/manifest.yaml new file mode 100644 index 000000000..886d3adb1 --- /dev/null +++ b/evm/src/euler-swap/manifest.yaml @@ -0,0 +1,24 @@ +# information about the author helps us reach out in case of issues. +author: + name: Haythem + email: haythem@euler.xyz + +# Protocol Constants +constants: + protocol_gas: 30000 + # minimum capabilities we can expect, individual pools may extend these + capabilities: + - SellSide + - BuySide + - PriceFunction + +# The file containing the adapter contract +contract: EulerSwapAdapter.sol + +# Deployment instances used to generate chain specific bytecode. +instances: + - chain: + name: mainnet + id: 1 + arguments: + - "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f" From 7538a592d14eaf3875bf2386e07848a1138c7e0b Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 13 Jan 2025 18:11:08 +0700 Subject: [PATCH 02/11] swap --- evm/src/euler-swap/EulerSwapAdapter.sol | 52 ++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/evm/src/euler-swap/EulerSwapAdapter.sol b/evm/src/euler-swap/EulerSwapAdapter.sol index 2b885be59..bc27d70fa 100644 --- a/evm/src/euler-swap/EulerSwapAdapter.sol +++ b/evm/src/euler-swap/EulerSwapAdapter.sol @@ -27,7 +27,45 @@ contract EulerSwapAdapter is ISwapAdapter { IMaglevEulerSwap pool = IMaglevEulerSwap(address(bytes20(poolId))); for (uint256 i = 0; i < specifiedAmounts.length; i++) { - prices[i] = priceSingle(pool, sellToken, buyToken, specifiedAmounts[i]); + prices[i] = quoteExactInput(pool, sellToken, buyToken, specifiedAmounts[i]); + } + } + + /// @inheritdoc ISwapAdapter + function swap( + bytes32 poolId, + address sellToken, + address buyToken, + OrderSide side, + uint256 specifiedAmount + ) external returns (Trade memory trade) { + IMaglevEulerSwap pool = IMaglevEulerSwap(address(bytes20(poolId))); + + bool isAmountOutAsset0 = buyToken == pool.asset0(); + uint256 amountIn; + uint256 amountOut; + if (side == OrderSide.Buy) { + amountIn = (quoteExactOutput(pool, sellToken, buyToken, specifiedAmount).denominator); + trade.calculatedAmount = amountOut = specifiedAmount; + } else { + trade.calculatedAmount = amountIn = specifiedAmount; + amountOut = + (quoteExactInput(pool, sellToken, buyToken, specifiedAmount).numerator); + } + + IERC20(sellToken).safeTransferFrom( + msg.sender, address(pool), amountIn + ); + + uint256 gasBefore = gasleft(); + (isAmountOutAsset0) ? pool.swap(amountOut, 0, msg.sender, "") : pool.swap(0, amountOut, msg.sender, ""); + trade.gasUsed = gasBefore - gasleft(); + + if (side == OrderSide.Buy) { + trade.price = quoteExactOutput(pool, sellToken, buyToken, specifiedAmount); + } else { + trade.price = + quoteExactInput(pool, sellToken, buyToken, specifiedAmount); } } @@ -75,7 +113,7 @@ contract EulerSwapAdapter is ISwapAdapter { } /// @notice Calculates pool prices for specified amounts - function priceSingle( + function quoteExactInput( IMaglevEulerSwap pool, address tokenIn, address tokenOut, @@ -84,6 +122,16 @@ contract EulerSwapAdapter is ISwapAdapter { calculatedPrice = Fraction(pool.quoteExactInput(tokenIn, tokenOut, amountIn), amountIn); } + + function quoteExactOutput( + IMaglevEulerSwap pool, + address tokenIn, + address tokenOut, + uint256 amountOut + ) internal view returns (Fraction memory calculatedPrice) { + calculatedPrice = + Fraction(amountOut, pool.quoteExactOutput(tokenIn, tokenOut, amountOut)); + } } interface IMaglevEulerSwapFactory { From 4601a773373e2b641601a485f76106a2a8da4795 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Tue, 11 Feb 2025 12:38:59 +0800 Subject: [PATCH 03/11] getLimits --- evm/src/euler-swap/EulerSwapAdapter.sol | 37 +++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/evm/src/euler-swap/EulerSwapAdapter.sol b/evm/src/euler-swap/EulerSwapAdapter.sol index bc27d70fa..143740106 100644 --- a/evm/src/euler-swap/EulerSwapAdapter.sol +++ b/evm/src/euler-swap/EulerSwapAdapter.sol @@ -69,6 +69,29 @@ contract EulerSwapAdapter is ISwapAdapter { } } + /// @inheritdoc ISwapAdapter + function getLimits(bytes32 poolId, address sellToken, address buyToken) + external view override + returns (uint256[] memory limits) { + limits = new uint256[](2); + + IMaglevEulerSwap pool = IMaglevEulerSwap(address(bytes20(poolId))); + address swapAccount = pool.myAccount(); + (address token0, address token1) = (sellToken < buyToken) ? (sellToken, buyToken) : (buyToken, sellToken); + + if(token0 == buyToken) { + // max amount for buyToken + uint256 maxWithdraw = vaultBalance(pool.vault0(), swapAccount); + + limits[1] = maxWithdraw; + } else { + // max amount for buyToken + uint256 maxWithdraw = vaultBalance(pool.vault1(), swapAccount); + + limits[1] = maxWithdraw; + } + } + /// @inheritdoc ISwapAdapter function getCapabilities(bytes32, address, address) external @@ -132,6 +155,14 @@ contract EulerSwapAdapter is ISwapAdapter { calculatedPrice = Fraction(amountOut, pool.quoteExactOutput(tokenIn, tokenOut, amountOut)); } + + function vaultBalance(address vault, address swapAccount) internal view returns (uint256) { + uint256 shares = IEVault(vault).balanceOf(swapAccount); + + return shares == 0 ? 0 : IEVault(vault).convertToAssets(shares); + + // return IEVault(vault).maxWithdraw(swapAccount); + } } interface IMaglevEulerSwapFactory { @@ -182,3 +213,9 @@ interface IMaglevEulerSwap { function initialReserve0() external view returns (uint112); function initialReserve1() external view returns (uint112); } + +interface IEVault { + function balanceOf(address account) external view returns (uint256); + function convertToAssets(uint256 shares) external view returns (uint256); + function maxWithdraw(address owner) external view returns (uint256); +} \ No newline at end of file From 0db4463f273565b78dcd57b0d32da25f0cc0674e Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Fri, 14 Feb 2025 17:07:13 +0100 Subject: [PATCH 04/11] init tests --- evm/src/euler-swap/EulerSwapAdapter.sol | 118 ++++++++++++++++-------- evm/test/EulerSwapAdapter.t.sol | 42 +++++++++ 2 files changed, 122 insertions(+), 38 deletions(-) create mode 100644 evm/test/EulerSwapAdapter.t.sol diff --git a/evm/src/euler-swap/EulerSwapAdapter.sol b/evm/src/euler-swap/EulerSwapAdapter.sol index 143740106..897e1d5ac 100644 --- a/evm/src/euler-swap/EulerSwapAdapter.sol +++ b/evm/src/euler-swap/EulerSwapAdapter.sol @@ -24,10 +24,11 @@ contract EulerSwapAdapter is ISwapAdapter { uint256[] memory specifiedAmounts ) external view override returns (Fraction[] memory prices) { prices = new Fraction[](specifiedAmounts.length); - + IMaglevEulerSwap pool = IMaglevEulerSwap(address(bytes20(poolId))); for (uint256 i = 0; i < specifiedAmounts.length; i++) { - prices[i] = quoteExactInput(pool, sellToken, buyToken, specifiedAmounts[i]); + prices[i] = + quoteExactInput(pool, sellToken, buyToken, specifiedAmounts[i]); } } @@ -45,24 +46,30 @@ contract EulerSwapAdapter is ISwapAdapter { uint256 amountIn; uint256 amountOut; if (side == OrderSide.Buy) { - amountIn = (quoteExactOutput(pool, sellToken, buyToken, specifiedAmount).denominator); + amountIn = ( + quoteExactOutput(pool, sellToken, buyToken, specifiedAmount) + .denominator + ); trade.calculatedAmount = amountOut = specifiedAmount; } else { trade.calculatedAmount = amountIn = specifiedAmount; - amountOut = - (quoteExactInput(pool, sellToken, buyToken, specifiedAmount).numerator); + amountOut = ( + quoteExactInput(pool, sellToken, buyToken, specifiedAmount) + .numerator + ); } - IERC20(sellToken).safeTransferFrom( - msg.sender, address(pool), amountIn - ); + IERC20(sellToken).safeTransferFrom(msg.sender, address(pool), amountIn); uint256 gasBefore = gasleft(); - (isAmountOutAsset0) ? pool.swap(amountOut, 0, msg.sender, "") : pool.swap(0, amountOut, msg.sender, ""); + (isAmountOutAsset0) + ? pool.swap(amountOut, 0, msg.sender, "") + : pool.swap(0, amountOut, msg.sender, ""); trade.gasUsed = gasBefore - gasleft(); if (side == OrderSide.Buy) { - trade.price = quoteExactOutput(pool, sellToken, buyToken, specifiedAmount); + trade.price = + quoteExactOutput(pool, sellToken, buyToken, specifiedAmount); } else { trade.price = quoteExactInput(pool, sellToken, buyToken, specifiedAmount); @@ -71,25 +78,30 @@ contract EulerSwapAdapter is ISwapAdapter { /// @inheritdoc ISwapAdapter function getLimits(bytes32 poolId, address sellToken, address buyToken) - external view override - returns (uint256[] memory limits) { - limits = new uint256[](2); + external + view + override + returns (uint256[] memory limits) + { + limits = new uint256[](2); - IMaglevEulerSwap pool = IMaglevEulerSwap(address(bytes20(poolId))); - address swapAccount = pool.myAccount(); - (address token0, address token1) = (sellToken < buyToken) ? (sellToken, buyToken) : (buyToken, sellToken); + IMaglevEulerSwap pool = IMaglevEulerSwap(address(bytes20(poolId))); + address swapAccount = pool.myAccount(); + (address token0, address token1) = (sellToken < buyToken) + ? (sellToken, buyToken) + : (buyToken, sellToken); - if(token0 == buyToken) { - // max amount for buyToken - uint256 maxWithdraw = vaultBalance(pool.vault0(), swapAccount); + if (token0 == buyToken) { + // max amount for buyToken + uint256 maxWithdraw = vaultBalance(pool.vault0(), swapAccount); - limits[1] = maxWithdraw; - } else { - // max amount for buyToken - uint256 maxWithdraw = vaultBalance(pool.vault1(), swapAccount); + limits[1] = maxWithdraw; + } else { + // max amount for buyToken + uint256 maxWithdraw = vaultBalance(pool.vault1(), swapAccount); - limits[1] = maxWithdraw; - } + limits[1] = maxWithdraw; + } } /// @inheritdoc ISwapAdapter @@ -142,8 +154,9 @@ contract EulerSwapAdapter is ISwapAdapter { address tokenOut, uint256 amountIn ) internal view returns (Fraction memory calculatedPrice) { - calculatedPrice = - Fraction(pool.quoteExactInput(tokenIn, tokenOut, amountIn), amountIn); + calculatedPrice = Fraction( + pool.quoteExactInput(tokenIn, tokenOut, amountIn), amountIn + ); } function quoteExactOutput( @@ -152,11 +165,16 @@ contract EulerSwapAdapter is ISwapAdapter { address tokenOut, uint256 amountOut ) internal view returns (Fraction memory calculatedPrice) { - calculatedPrice = - Fraction(amountOut, pool.quoteExactOutput(tokenIn, tokenOut, amountOut)); + calculatedPrice = Fraction( + amountOut, pool.quoteExactOutput(tokenIn, tokenOut, amountOut) + ); } - function vaultBalance(address vault, address swapAccount) internal view returns (uint256) { + function vaultBalance(address vault, address swapAccount) + internal + view + returns (uint256) + { uint256 shares = IEVault(vault).balanceOf(swapAccount); return shares == 0 ? 0 : IEVault(vault).convertToAssets(shares); @@ -166,8 +184,13 @@ contract EulerSwapAdapter is ISwapAdapter { } interface IMaglevEulerSwapFactory { - event PoolDeployed(address indexed asset0, address indexed asset1, uint256 indexed feeMultiplier, address pool); - + event PoolDeployed( + address indexed asset0, + address indexed asset1, + uint256 indexed feeMultiplier, + address pool + ); + function deployPool( address vault0, address vault1, @@ -183,17 +206,36 @@ interface IMaglevEulerSwapFactory { function evc() external view returns (address); function allPools(uint256 index) external view returns (address); - function getPool(address assetA, address assetB, uint256 fee) external view returns (address); + function getPool(address assetA, address assetB, uint256 fee) + external + view + returns (address); function allPoolsLength() external view returns (uint256); - function getAllPoolsListSlice(uint256 start, uint256 end) external view returns (address[] memory); + function getAllPoolsListSlice(uint256 start, uint256 end) + external + view + returns (address[] memory); } interface IMaglevEulerSwap { // IMaglevBase function configure() external; - function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external; - function quoteExactInput(address tokenIn, address tokenOut, uint256 amountIn) external view returns (uint256); - function quoteExactOutput(address tokenIn, address tokenOut, uint256 amountOut) external view returns (uint256); + function swap( + uint256 amount0Out, + uint256 amount1Out, + address to, + bytes calldata data + ) external; + function quoteExactInput( + address tokenIn, + address tokenOut, + uint256 amountIn + ) external view returns (uint256); + function quoteExactOutput( + address tokenIn, + address tokenOut, + uint256 amountOut + ) external view returns (uint256); function vault0() external view returns (address); function vault1() external view returns (address); @@ -218,4 +260,4 @@ interface IEVault { function balanceOf(address account) external view returns (uint256); function convertToAssets(uint256 shares) external view returns (uint256); function maxWithdraw(address owner) external view returns (uint256); -} \ No newline at end of file +} diff --git a/evm/test/EulerSwapAdapter.t.sol b/evm/test/EulerSwapAdapter.t.sol new file mode 100644 index 000000000..671be1a08 --- /dev/null +++ b/evm/test/EulerSwapAdapter.t.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity ^0.8.13; + +import "./AdapterTest.sol"; +import {EulerSwapAdapter, IERC20} from "src/euler-swap/EulerSwapAdapter.sol"; +import {FractionMath} from "src/libraries/FractionMath.sol"; + +contract EulerSwapAdapterTest is AdapterTest { + using FractionMath for Fraction; + + EulerSwapAdapter adapter; + address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; + + function setUp() public { + uint256 forkBlock = 21845705; + vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); + adapter = + new EulerSwapAdapter(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f); + + vm.label(address(adapter), "EulerSwapAdapter"); + vm.label(WETH, "WETH"); + vm.label(USDC, "USDC"); + } + + function testGetCapabilities( + bytes32 poolId, + address sellToken, + address buyToken + ) public view { + Capability[] memory res = + adapter.getCapabilities(poolId, sellToken, buyToken); + + assertEq(res.length, 3); + } + + // function testGetLimits() public { + // bytes32 pair = bytes32(bytes20(USDC_WETH_POOL)); + // uint256[] memory limits = adapter.getLimits(pair, USDC, WETH); + // assertEq(limits.length, 2); + // } +} \ No newline at end of file From 3a881af1cbb0b578e5225f4083014d6d43c6a5b9 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 17 Feb 2025 10:00:06 +0100 Subject: [PATCH 05/11] init euler-swap substreams --- substreams/ethereum-eulerswap/Cargo.toml | 26 ++ substreams/ethereum-eulerswap/abi/.gitkeep | 1 + substreams/ethereum-eulerswap/buf.gen.yaml | 12 + substreams/ethereum-eulerswap/build.rs | 49 ++++ .../integration_test.tycho.yaml | 57 ++++ .../ethereum-eulerswap/rust-toolchain.toml | 4 + substreams/ethereum-eulerswap/src/abi/mod.rs | 1 + substreams/ethereum-eulerswap/src/lib.rs | 3 + substreams/ethereum-eulerswap/src/modules.rs | 269 ++++++++++++++++++ .../ethereum-eulerswap/src/pool_factories.rs | 44 +++ substreams/ethereum-eulerswap/substreams.yaml | 67 +++++ 11 files changed, 533 insertions(+) create mode 100644 substreams/ethereum-eulerswap/Cargo.toml create mode 100644 substreams/ethereum-eulerswap/abi/.gitkeep create mode 100644 substreams/ethereum-eulerswap/buf.gen.yaml create mode 100644 substreams/ethereum-eulerswap/build.rs create mode 100644 substreams/ethereum-eulerswap/integration_test.tycho.yaml create mode 100644 substreams/ethereum-eulerswap/rust-toolchain.toml create mode 100644 substreams/ethereum-eulerswap/src/abi/mod.rs create mode 100644 substreams/ethereum-eulerswap/src/lib.rs create mode 100644 substreams/ethereum-eulerswap/src/modules.rs create mode 100644 substreams/ethereum-eulerswap/src/pool_factories.rs create mode 100644 substreams/ethereum-eulerswap/substreams.yaml diff --git a/substreams/ethereum-eulerswap/Cargo.toml b/substreams/ethereum-eulerswap/Cargo.toml new file mode 100644 index 000000000..9d4f94a27 --- /dev/null +++ b/substreams/ethereum-eulerswap/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "ethereum-template-factory" +version = "0.1.0" +edition = "2021" + +[lib] +name = "ethereum_template_factory" +crate-type = ["cdylib"] + +[dependencies] +substreams = "0.5.22" +substreams-ethereum = "0.9.9" +prost = "0.11" +tycho-substreams = { git = "https://github.com/propeller-heads/tycho-protocol-sdk.git", rev = "3c08359" } +anyhow = "1.0.95" +ethabi = "18.0.0" +num-bigint = "0.4.6" +hex = "0.4.3" +itertools = "0.10.5" +serde = "1.0.217" +serde-sibor = "0.1.0" + + +[build-dependencies] +anyhow = "1" +substreams-ethereum = "0.9.9" diff --git a/substreams/ethereum-eulerswap/abi/.gitkeep b/substreams/ethereum-eulerswap/abi/.gitkeep new file mode 100644 index 000000000..d18638c31 --- /dev/null +++ b/substreams/ethereum-eulerswap/abi/.gitkeep @@ -0,0 +1 @@ +Please put any abi JSONs into this folder the buildscript will compile these to rust code. \ No newline at end of file diff --git a/substreams/ethereum-eulerswap/buf.gen.yaml b/substreams/ethereum-eulerswap/buf.gen.yaml new file mode 100644 index 000000000..d2e6544e9 --- /dev/null +++ b/substreams/ethereum-eulerswap/buf.gen.yaml @@ -0,0 +1,12 @@ + +version: v1 +plugins: +- plugin: buf.build/community/neoeinstein-prost:v0.2.2 + out: src/pb + opt: + - file_descriptor_set=false + +- plugin: buf.build/community/neoeinstein-prost-crate:v0.3.1 + out: src/pb + opt: + - no_features diff --git a/substreams/ethereum-eulerswap/build.rs b/substreams/ethereum-eulerswap/build.rs new file mode 100644 index 000000000..5e7bd34c5 --- /dev/null +++ b/substreams/ethereum-eulerswap/build.rs @@ -0,0 +1,49 @@ +use anyhow::Result; +use std::{fs, io::Write}; +use substreams_ethereum::Abigen; + +fn main() -> Result<()> { + let abi_folder = "abi"; + let output_folder = "src/abi"; + + let abis = fs::read_dir(abi_folder)?; + + let mut files = abis.collect::, _>>()?; + + // Sort the files by their name + files.sort_by_key(|a| a.file_name()); + + let mut mod_rs_content = String::new(); + mod_rs_content.push_str("#![allow(clippy::all)]\n"); + + for file in files { + let file_name = file.file_name(); + let file_name = file_name.to_string_lossy(); + + if !file_name.ends_with(".json") { + continue; + } + + let contract_name = file_name.split('.').next().unwrap(); + + let input_path = format!("{}/{}", abi_folder, file_name); + let output_path = format!("{}/{}.rs", output_folder, contract_name); + + mod_rs_content.push_str(&format!("pub mod {};\n", contract_name)); + + if std::path::Path::new(&output_path).exists() { + continue; + } + + Abigen::new(contract_name, &input_path)? + .generate()? + .write_to_file(&output_path)?; + } + + let mod_rs_path = format!("{}/mod.rs", output_folder); + let mut mod_rs_file = fs::File::create(mod_rs_path)?; + + mod_rs_file.write_all(mod_rs_content.as_bytes())?; + + Ok(()) +} diff --git a/substreams/ethereum-eulerswap/integration_test.tycho.yaml b/substreams/ethereum-eulerswap/integration_test.tycho.yaml new file mode 100644 index 000000000..bea6aa301 --- /dev/null +++ b/substreams/ethereum-eulerswap/integration_test.tycho.yaml @@ -0,0 +1,57 @@ +# Name of the substreams config file in your substreams module. Usually "./substreams.yaml" +substreams_yaml_path: ./substreams.yaml +# Name of the adapter contract, usually: ProtocolSwapAdapter" +adapter_contract: "SwapAdapter" +# Constructor signature of the Adapter contract" +adapter_build_signature: "constructor(address)" +# A comma separated list of args to be passed to the contructor of the Adapter contract" +adapter_build_args: "0x0000000000000000000000000000000000000000" +# Whether or not the testing script should skip checking balances of the protocol components. +# If set to `true` please always add a reason why it's skipped. +skip_balance_check: false +# A list of accounts that need to be indexed to run the tests properly. +# Usually used when there is a global component required by all pools and created before the tested range of blocks. For example a factory or a vault. +# Please note that this component needs to be indexed by your substreams module, this feature is only for testing purpose. +# Also please always add a reason why this account is needed for your tests. +# This will be applied to each test. +initialized_accounts: + - "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84" # Needed for .... +# A list of protocol types names created by your Substreams module. +protocol_type_names: + - "type_name_1" + - "type_name_2" +# A list of tests. +tests: + # Name of the test + - name: test_pool_creation + # Indexed block range + start_block: 123 + stop_block: 456 + # Same as global `initialized_accounts` but only scoped to this test. + initialized_accounts: + - "0x0c0e5f2fF0ff18a3be9b835635039256dC4B4963" # Needed for .... + # A list of expected component indexed in the block range. Each component must match perfectly the `ProtocolComponent` indexed by your subtreams module. + expected_components: + - id: "0xbebc44782c7db0a1a60cb6fe97d0b483032ff1c7" + tokens: + - "0xdac17f958d2ee523a2206206994597c13d831ec7" + - "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" + - "0x6b175474e89094c44da98b954eedeac495271d0f" + static_attributes: + attr_1: "value" + attr_2: "value" + creation_tx: "0x20793bbf260912aae189d5d261ff003c9b9166da8191d8f9d63ff1c7722f3ac6" + # Whether or not the script should skip trying to simulate a swap on this component. + # If set to `true` please always add a reason why it's skipped. + 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 diff --git a/substreams/ethereum-eulerswap/rust-toolchain.toml b/substreams/ethereum-eulerswap/rust-toolchain.toml new file mode 100644 index 000000000..15b4897de --- /dev/null +++ b/substreams/ethereum-eulerswap/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "1.83.0" +components = [ "rustfmt" ] +targets = [ "wasm32-unknown-unknown" ] diff --git a/substreams/ethereum-eulerswap/src/abi/mod.rs b/substreams/ethereum-eulerswap/src/abi/mod.rs new file mode 100644 index 000000000..76e240afa --- /dev/null +++ b/substreams/ethereum-eulerswap/src/abi/mod.rs @@ -0,0 +1 @@ +#![allow(clippy::all)] diff --git a/substreams/ethereum-eulerswap/src/lib.rs b/substreams/ethereum-eulerswap/src/lib.rs new file mode 100644 index 000000000..27bd15a6f --- /dev/null +++ b/substreams/ethereum-eulerswap/src/lib.rs @@ -0,0 +1,3 @@ +mod abi; +mod modules; +mod pool_factories; diff --git a/substreams/ethereum-eulerswap/src/modules.rs b/substreams/ethereum-eulerswap/src/modules.rs new file mode 100644 index 000000000..5a87592d6 --- /dev/null +++ b/substreams/ethereum-eulerswap/src/modules.rs @@ -0,0 +1,269 @@ +//! Template for Protocols with contract factories +//! +//! This template provides foundational maps and store substream modules for indexing a +//! protocol where each component (e.g., pool) is deployed to a separate contract. Each +//! contract is expected to escrow its ERC-20 token balances. +//! +//! If your protocol supports native ETH, you may need to adjust the balance tracking +//! logic in `map_relative_component_balance` to account for native token handling. +//! +//! ## Assumptions +//! - Assumes each pool has a single newly deployed contract linked to it +//! - Assumes pool identifier equals the deployed contract address +//! - Assumes any price or liquidity updated correlates with a pools contract storage update. +//! +//! ## Alternative Module +//! If your protocol uses a vault-like contract to manage balances, or if pools are +//! registered within a singleton contract, refer to the `ethereum-template-singleton` +//! substream for an appropriate alternative. +//! +//! ## Warning +//! This template provides a general framework for indexing a protocol. However, it is +//! likely that you will need to adapt the steps to suit your specific use case. Use the +//! provided code with care and ensure you fully understand each step before proceeding +//! with your implementation. +//! +//! ## Example Use Case +//! For an Uniswap-like protocol where each liquidity pool is deployed as a separate +//! contract, you can use this template to: +//! - Track relative component balances (e.g., ERC-20 token balances in each pool). +//! - Index individual pool contracts as they are created by the factory contract. +//! +//! Adjustments to the template may include: +//! - Handling native ETH balances alongside token balances. +//! - Customizing indexing logic for specific factory contract behavior. +use crate::pool_factories; +use anyhow::Result; +use itertools::Itertools; +use std::collections::HashMap; +use substreams::{pb::substreams::StoreDeltas, prelude::*}; +use substreams_ethereum::{pb::eth, Event}; +use tycho_substreams::{ + abi::erc20, balances::aggregate_balances_changes, contract::extract_contract_changes_builder, + prelude::*, +}; + +/// Find and create all relevant protocol components +/// +/// This method maps over blocks and instantiates ProtocolComponents with a unique ids +/// as well as all necessary metadata for routing and encoding. +#[substreams::handlers::map] +fn map_protocol_components(block: eth::v2::Block) -> Result { + Ok(BlockTransactionProtocolComponents { + tx_components: block + .transactions() + .filter_map(|tx| { + let components = tx + .logs_with_calls() + .filter_map(|(log, call)| { + // TODO: ensure this method is implemented correctly + pool_factories::maybe_create_component(call.call, log, tx) + }) + .collect::>(); + + if !components.is_empty() { + Some(TransactionProtocolComponents { tx: Some(tx.into()), components }) + } else { + None + } + }) + .collect::>(), + }) +} + +/// Stores all protocol components in a store. +/// +/// Stores information about components in a key value store. This is only necessary if +/// you need to access the whole set of components within your indexing logic. +/// +/// Popular use cases are: +/// - Checking if a contract belongs to a component. In this case suggest to use an address as the +/// store key so lookup operations are O(1). +/// - Tallying up relative balances changes to calcualte absolute erc20 token balances per +/// component. +/// +/// Usually you can skip this step if: +/// - You are interested in a static set of components only +/// - Your protocol emits balance change events with absolute values +#[substreams::handlers::store] +fn store_protocol_components( + map_protocol_components: BlockTransactionProtocolComponents, + store: StoreSetRaw, +) { + map_protocol_components + .tx_components + .into_iter() + .for_each(|tx_pc| { + tx_pc + .components + .into_iter() + .for_each(|pc| { + // Assumes that the component id is a hex encoded contract address + let key = pc.id.clone(); + // we store the components tokens + // TODO: proper error handling + let val = serde_sibor::to_bytes(&pc.tokens).unwrap(); + store.set(0, key, &val); + }) + }); +} + +/// Extracts balance changes per component +/// +/// This template function uses ERC20 transfer events to extract balance changes. It +/// assumes that each component is deployed at a dedicated contract address. If a +/// transfer to the component is detected, it's balanced is increased and if a balance +/// from the component is detected its balance is decreased. +/// +/// ## Note: +/// Changes are necessary if your protocol uses native ETH, uses a vault contract or if +/// your component burn or mint tokens without emitting transfer events. +/// +/// You may want to ignore LP tokens if your protocol emits transfer events for these +/// here. +#[substreams::handlers::map] +fn map_relative_component_balance( + block: eth::v2::Block, + store: StoreGetRaw, +) -> Result { + let res = block + .logs() + .filter_map(|log| { + erc20::events::Transfer::match_and_decode(log).map(|transfer| { + let to_addr = hex::encode(transfer.to.as_slice()); + let from_addr = hex::encode(transfer.from.as_slice()); + let tx = log.receipt.transaction; + if let Some(val) = store.get_last(&to_addr) { + let component_tokens: Vec> = serde_sibor::from_bytes(&val).unwrap(); + if component_tokens.contains(&log.address().to_vec()) { + return Some(BalanceDelta { + ord: log.ordinal(), + tx: Some(tx.into()), + token: log.address().to_vec(), + delta: transfer.value.to_signed_bytes_be(), + component_id: to_addr.into_bytes(), + }); + } + } else if let Some(val) = store.get_last(&from_addr) { + let component_tokens: Vec> = serde_sibor::from_bytes(&val).unwrap(); + if component_tokens.contains(&log.address().to_vec()) { + return Some(BalanceDelta { + ord: log.ordinal(), + tx: Some(tx.into()), + token: log.address().to_vec(), + delta: (transfer.value.neg()).to_signed_bytes_be(), + component_id: to_addr.into_bytes(), + }); + } + } + None + }) + }) + .flatten() + .collect::>(); + + Ok(BlockBalanceDeltas { balance_deltas: res }) +} + +/// Aggregates relative balances values into absolute values +/// +/// Aggregate the relative balances in an additive store since tycho-indexer expects +/// absolute balance inputs. +/// +/// ## Note: +/// This method should usually not require any changes. +#[substreams::handlers::store] +pub fn store_balances(deltas: BlockBalanceDeltas, store: StoreAddBigInt) { + tycho_substreams::balances::store_balance_changes(deltas, store); +} + +/// Aggregates protocol components and balance changes by transaction. +/// +/// This is the main method that will aggregate all changes as well as extract all +/// relevant contract storage deltas. +/// +/// ## Note: +/// You may have to change this method if your components have any default dynamic +/// attributes, or if you need any additional static contracts indexed. +#[substreams::handlers::map] +fn map_protocol_changes( + block: eth::v2::Block, + new_components: BlockTransactionProtocolComponents, + components_store: StoreGetRaw, + balance_store: StoreDeltas, + deltas: BlockBalanceDeltas, +) -> Result { + // We merge contract changes by transaction (identified by transaction index) + // making it easy to sort them at the very end. + let mut transaction_changes: HashMap<_, TransactionChangesBuilder> = HashMap::new(); + + // Aggregate newly created components per tx + new_components + .tx_components + .iter() + .for_each(|tx_component| { + // initialise builder if not yet present for this tx + let tx = tx_component.tx.as_ref().unwrap(); + let builder = transaction_changes + .entry(tx.index) + .or_insert_with(|| TransactionChangesBuilder::new(tx)); + + // iterate over individual components created within this tx + tx_component + .components + .iter() + .for_each(|component| { + builder.add_protocol_component(component); + // TODO: In case you require to add any dynamic attributes to the + // component you can do so here: + /* + builder.add_entity_change(&EntityChanges { + component_id: component.id.clone(), + attributes: default_attributes.clone(), + }); + */ + }); + }); + + // Aggregate absolute balances per transaction. + aggregate_balances_changes(balance_store, deltas) + .into_iter() + .for_each(|(_, (tx, balances))| { + let builder = transaction_changes + .entry(tx.index) + .or_insert_with(|| TransactionChangesBuilder::new(&tx)); + balances + .values() + .for_each(|token_bc_map| { + token_bc_map + .values() + .for_each(|bc| builder.add_balance_change(bc)) + }); + }); + + // Extract and insert any storage changes that happened for any of the components. + extract_contract_changes_builder( + &block, + |addr| { + // we assume that the store holds contract addresses as keys and if it + // contains a value, that contract is of relevance. + // TODO: if you have any additional static contracts that need to be indexed, + // please add them here. + components_store + .get_last(hex::encode(addr)) + .is_some() + }, + &mut transaction_changes, + ); + + // Process all `transaction_changes` for final output in the `BlockChanges`, + // sorted by transaction index (the key). + Ok(BlockChanges { + block: Some((&block).into()), + changes: transaction_changes + .drain() + .sorted_unstable_by_key(|(index, _)| *index) + .filter_map(|(_, builder)| builder.build()) + .collect::>(), + }) +} diff --git a/substreams/ethereum-eulerswap/src/pool_factories.rs b/substreams/ethereum-eulerswap/src/pool_factories.rs new file mode 100644 index 000000000..f6bc6f2e7 --- /dev/null +++ b/substreams/ethereum-eulerswap/src/pool_factories.rs @@ -0,0 +1,44 @@ +use substreams::hex; +use substreams_ethereum::pb::eth::v2::{Call, Log, TransactionTrace}; +use tycho_substreams::models::{ + ChangeType, FinancialType, ImplementationType, ProtocolComponent, ProtocolType, +}; + +/// Potentially constructs a new ProtocolComponent given a call +/// +/// This method is given each individual call within a transaction, the corresponding +/// logs emitted during that call as well as the full transaction trace. +/// +/// If this call creates a component in your protocol please contstruct and return it +/// here. Otherwise, simply return None. +pub fn maybe_create_component( + call: &Call, + _log: &Log, + _tx: &TransactionTrace, +) -> Option { + match *call.address { + // TODO: replace with your logic + hex!("0000000000000000000000000000000000000000") => { + Some(ProtocolComponent { + id: "".to_string(), + tokens: vec![ + // TODO: add the components tokens + ], + contracts: vec![ + // TODO: any contracts required during swapping + ], + static_att: vec![ + // TODO: any additional metadata required, e.g. for swap encoding + ], + change: ChangeType::Creation.into(), + protocol_type: Some(ProtocolType { + name: "template".to_string(), + financial_type: FinancialType::Swap.into(), + attribute_schema: vec![], + implementation_type: ImplementationType::Vm.into(), + }), + }) + } + _ => None, + } +} diff --git a/substreams/ethereum-eulerswap/substreams.yaml b/substreams/ethereum-eulerswap/substreams.yaml new file mode 100644 index 000000000..864e9bc8f --- /dev/null +++ b/substreams/ethereum-eulerswap/substreams.yaml @@ -0,0 +1,67 @@ +specVersion: v0.1.0 +package: + name: "ethereum_template" + version: v0.1.0 + +protobuf: + files: + - tycho/evm/v1/vm.proto + - tycho/evm/v1/common.proto + - tycho/evm/v1/utils.proto + importPaths: + - ../../proto + +binaries: + default: + type: wasm/rust-v1 + file: ../target/wasm32-unknown-unknown/release/ethereum_template_factory.wasm + + +network: mainnet + +modules: + - name: map_protocol_components + kind: map + initialBlock: 1 + inputs: + - source: sf.ethereum.type.v2.Block + output: + type: proto:tycho.evm.v1.BlockTransactionProtocolComponents + + - name: store_protocol_components + kind: store + initialBlock: 1 + updatePolicy: set + valueType: string + inputs: + - map: map_protocol_components + + - name: map_relative_component_balance + kind: map + initialBlock: 1 + inputs: + - source: sf.ethereum.type.v2.Block + - store: store_protocol_components + output: + type: proto:tycho.evm.v1.BlockBalanceDeltas + + - name: store_balances + kind: store + initialBlock: 1 + updatePolicy: add + valueType: bigint + inputs: + - map: map_relative_component_balance + + - name: map_protocol_changes + kind: map + initialBlock: 1 + inputs: + - source: sf.ethereum.type.v2.Block + - map: map_protocol_components + - map: map_relative_component_balance + - store: store_protocol_components + - store: store_balances + mode: deltas + output: + type: proto:tycho.evm.v1.BlockChanges From 2d191daf4285c8a8d5210912edc0da7148d801e2 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 17 Feb 2025 10:31:07 +0100 Subject: [PATCH 06/11] update package & add ABIs --- substreams/Cargo.lock | 17 + substreams/Cargo.toml | 1 + substreams/ethereum-eulerswap/Cargo.toml | 4 +- .../ethereum-eulerswap/abi/eulerswap.json | 531 ++++ .../abi/eulerswap_periphery.json | 101 + .../ethereum-eulerswap/src/abi/eulerswap.rs | 2371 +++++++++++++++++ .../src/abi/eulerswap_periphery.rs | 329 +++ substreams/ethereum-eulerswap/src/abi/mod.rs | 2 + substreams/ethereum-eulerswap/src/pb/mod.rs | 10 + .../ethereum-eulerswap/src/pb/tycho.evm.v1.rs | 382 +++ substreams/ethereum-eulerswap/substreams.yaml | 4 +- 11 files changed, 3748 insertions(+), 4 deletions(-) create mode 100644 substreams/ethereum-eulerswap/abi/eulerswap.json create mode 100644 substreams/ethereum-eulerswap/abi/eulerswap_periphery.json create mode 100644 substreams/ethereum-eulerswap/src/abi/eulerswap.rs create mode 100644 substreams/ethereum-eulerswap/src/abi/eulerswap_periphery.rs create mode 100644 substreams/ethereum-eulerswap/src/pb/mod.rs create mode 100644 substreams/ethereum-eulerswap/src/pb/tycho.evm.v1.rs diff --git a/substreams/Cargo.lock b/substreams/Cargo.lock index 5ff1bbe15..0b7f26b43 100644 --- a/substreams/Cargo.lock +++ b/substreams/Cargo.lock @@ -260,6 +260,23 @@ dependencies = [ "tycho-substreams 0.2.0 (git+https://github.com/propeller-heads/tycho-protocol-sdk.git?rev=b8aeaa3)", ] +[[package]] +name = "ethereum-eulerswap" +version = "0.1.0" +dependencies = [ + "anyhow", + "ethabi 18.0.0", + "hex", + "itertools 0.10.5", + "num-bigint", + "prost 0.11.9", + "serde", + "serde-sibor", + "substreams", + "substreams-ethereum", + "tycho-substreams 0.2.0 (git+https://github.com/propeller-heads/tycho-protocol-sdk.git?rev=3c08359)", +] + [[package]] name = "ethereum-sfrax" version = "0.1.0" diff --git a/substreams/Cargo.toml b/substreams/Cargo.toml index c0e3e9725..956c752f1 100644 --- a/substreams/Cargo.toml +++ b/substreams/Cargo.toml @@ -13,6 +13,7 @@ members = [ "ethereum-template-factory", "ethereum-template-singleton", "ethereum-uniswap-v4", + "ethereum-eulerswap" ] resolver = "2" diff --git a/substreams/ethereum-eulerswap/Cargo.toml b/substreams/ethereum-eulerswap/Cargo.toml index 9d4f94a27..c3f3a52bb 100644 --- a/substreams/ethereum-eulerswap/Cargo.toml +++ b/substreams/ethereum-eulerswap/Cargo.toml @@ -1,10 +1,10 @@ [package] -name = "ethereum-template-factory" +name = "ethereum-eulerswap" version = "0.1.0" edition = "2021" [lib] -name = "ethereum_template_factory" +name = "ethereum_eulerswap" crate-type = ["cdylib"] [dependencies] diff --git a/substreams/ethereum-eulerswap/abi/eulerswap.json b/substreams/ethereum-eulerswap/abi/eulerswap.json new file mode 100644 index 000000000..b8f8541fb --- /dev/null +++ b/substreams/ethereum-eulerswap/abi/eulerswap.json @@ -0,0 +1,531 @@ +[ + { + "type": "constructor", + "inputs": [ + { + "name": "params", + "type": "tuple", + "internalType": "struct EulerSwap.Params", + "components": [ + { + "name": "evc", + "type": "address", + "internalType": "address" + }, + { + "name": "vault0", + "type": "address", + "internalType": "address" + }, + { + "name": "vault1", + "type": "address", + "internalType": "address" + }, + { + "name": "myAccount", + "type": "address", + "internalType": "address" + }, + { + "name": "debtLimit0", + "type": "uint112", + "internalType": "uint112" + }, + { + "name": "debtLimit1", + "type": "uint112", + "internalType": "uint112" + }, + { + "name": "fee", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "curveParams", + "type": "tuple", + "internalType": "struct EulerSwap.CurveParams", + "components": [ + { + "name": "priceX", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "priceY", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "concentrationX", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "concentrationY", + "type": "uint256", + "internalType": "uint256" + } + ] + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "EVC", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "activate", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "asset0", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "asset1", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "concentrationX", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "concentrationY", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "curve", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "debtLimit0", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint112", + "internalType": "uint112" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "debtLimit1", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint112", + "internalType": "uint112" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "feeMultiplier", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getReserves", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint112", + "internalType": "uint112" + }, + { + "name": "", + "type": "uint112", + "internalType": "uint112" + }, + { + "name": "", + "type": "uint32", + "internalType": "uint32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "initialReserve0", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint112", + "internalType": "uint112" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "initialReserve1", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint112", + "internalType": "uint112" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "myAccount", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "priceX", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "priceY", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "reserve0", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint112", + "internalType": "uint112" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "reserve1", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint112", + "internalType": "uint112" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "status", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint32", + "internalType": "uint32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "swap", + "inputs": [ + { + "name": "amount0Out", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "amount1Out", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "to", + "type": "address", + "internalType": "address" + }, + { + "name": "data", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "vault0", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "vault1", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "verify", + "inputs": [ + { + "name": "newReserve0", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "newReserve1", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "event", + "name": "EulerSwapCreated", + "inputs": [ + { + "name": "eulerSwap", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "asset0", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "asset1", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Swap", + "inputs": [ + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount0In", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "amount1In", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "amount0Out", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "amount1Out", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "reserve0", + "type": "uint112", + "indexed": false, + "internalType": "uint112" + }, + { + "name": "reserve1", + "type": "uint112", + "indexed": false, + "internalType": "uint112" + }, + { + "name": "to", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "AssetsOutOfOrderOrEqual", + "inputs": [] + }, + { + "type": "error", + "name": "BadFee", + "inputs": [] + }, + { + "type": "error", + "name": "ControllerDisabled", + "inputs": [] + }, + { + "type": "error", + "name": "CurveViolation", + "inputs": [] + }, + { + "type": "error", + "name": "DifferentEVC", + "inputs": [] + }, + { + "type": "error", + "name": "EVC_InvalidAddress", + "inputs": [] + }, + { + "type": "error", + "name": "Locked", + "inputs": [] + }, + { + "type": "error", + "name": "NotAuthorized", + "inputs": [] + }, + { + "type": "error", + "name": "Overflow", + "inputs": [] + } + ] \ No newline at end of file diff --git a/substreams/ethereum-eulerswap/abi/eulerswap_periphery.json b/substreams/ethereum-eulerswap/abi/eulerswap_periphery.json new file mode 100644 index 000000000..81a358938 --- /dev/null +++ b/substreams/ethereum-eulerswap/abi/eulerswap_periphery.json @@ -0,0 +1,101 @@ +[ + { + "type": "constructor", + "inputs": [ + { + "name": "evc_", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "quoteExactInput", + "inputs": [ + { + "name": "eulerSwap", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenIn", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "amountIn", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "quoteExactOutput", + "inputs": [ + { + "name": "eulerSwap", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenIn", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "amountOut", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "error", + "name": "InsufficientCash", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientReserves", + "inputs": [] + }, + { + "type": "error", + "name": "OperatorNotInstalled", + "inputs": [] + }, + { + "type": "error", + "name": "UnsupportedPair", + "inputs": [] + } + ] \ No newline at end of file diff --git a/substreams/ethereum-eulerswap/src/abi/eulerswap.rs b/substreams/ethereum-eulerswap/src/abi/eulerswap.rs new file mode 100644 index 000000000..05f9590b6 --- /dev/null +++ b/substreams/ethereum-eulerswap/src/abi/eulerswap.rs @@ -0,0 +1,2371 @@ +const INTERNAL_ERR: &'static str = "`ethabi_derive` internal error"; +/// Contract's functions. +#[allow(dead_code, unused_imports, unused_variables)] +pub mod functions { + use super::INTERNAL_ERR; + #[derive(Debug, Clone, PartialEq)] + pub struct Activate {} + impl Activate { + const METHOD_ID: [u8; 4] = [15u8, 21u8, 244u8, 192u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for Activate { + const NAME: &'static str = "activate"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Asset0 {} + impl Asset0 { + const METHOD_ID: [u8; 4] = [197u8, 9u8, 182u8, 162u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode(&[ethabi::ParamType::Address], data.as_ref()) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Asset0 { + const NAME: &'static str = "asset0"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for Asset0 { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Asset1 {} + impl Asset1 { + const METHOD_ID: [u8; 4] = [124u8, 213u8, 243u8, 249u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode(&[ethabi::ParamType::Address], data.as_ref()) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Asset1 { + const NAME: &'static str = "asset1"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for Asset1 { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct ConcentrationX {} + impl ConcentrationX { + const METHOD_ID: [u8; 4] = [108u8, 46u8, 196u8, 238u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for ConcentrationX { + const NAME: &'static str = "concentrationX"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for ConcentrationX { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct ConcentrationY {} + impl ConcentrationY { + const METHOD_ID: [u8; 4] = [218u8, 249u8, 211u8, 194u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for ConcentrationY { + const NAME: &'static str = "concentrationY"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for ConcentrationY { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Curve {} + impl Curve { + const METHOD_ID: [u8; 4] = [113u8, 101u8, 72u8, 93u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result<[u8; 32usize], String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result<[u8; 32usize], String> { + let mut values = ethabi::decode( + &[ethabi::ParamType::FixedBytes(32usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut result = [0u8; 32]; + let v = values + .pop() + .expect("one output data should have existed") + .into_fixed_bytes() + .expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option<[u8; 32usize]> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Curve { + const NAME: &'static str = "curve"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable<[u8; 32usize]> for Curve { + fn output(data: &[u8]) -> Result<[u8; 32usize], String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct DebtLimit0 {} + impl DebtLimit0 { + const METHOD_ID: [u8; 4] = [206u8, 233u8, 37u8, 141u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(112usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for DebtLimit0 { + const NAME: &'static str = "debtLimit0"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for DebtLimit0 { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct DebtLimit1 {} + impl DebtLimit1 { + const METHOD_ID: [u8; 4] = [168u8, 243u8, 33u8, 3u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(112usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for DebtLimit1 { + const NAME: &'static str = "debtLimit1"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for DebtLimit1 { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Evc {} + impl Evc { + const METHOD_ID: [u8; 4] = [167u8, 3u8, 84u8, 161u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode(&[ethabi::ParamType::Address], data.as_ref()) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Evc { + const NAME: &'static str = "EVC"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for Evc { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct FeeMultiplier {} + impl FeeMultiplier { + const METHOD_ID: [u8; 4] = [229u8, 167u8, 14u8, 247u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for FeeMultiplier { + const NAME: &'static str = "feeMultiplier"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for FeeMultiplier { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct GetReserves {} + impl GetReserves { + const METHOD_ID: [u8; 4] = [9u8, 2u8, 241u8, 172u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + ), + String, + > { + Self::output(call.return_data.as_ref()) + } + pub fn output( + data: &[u8], + ) -> Result< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + ), + String, + > { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(112usize), + ethabi::ParamType::Uint(112usize), + ethabi::ParamType::Uint(32usize), + ], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + values.reverse(); + Ok(( + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + )) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call( + &self, + address: Vec, + ) -> Option< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + ), + > { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for GetReserves { + const NAME: &'static str = "getReserves"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + ), + > for GetReserves { + fn output( + data: &[u8], + ) -> Result< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + ), + String, + > { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct InitialReserve0 {} + impl InitialReserve0 { + const METHOD_ID: [u8; 4] = [199u8, 210u8, 90u8, 185u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(112usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for InitialReserve0 { + const NAME: &'static str = "initialReserve0"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for InitialReserve0 { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct InitialReserve1 {} + impl InitialReserve1 { + const METHOD_ID: [u8; 4] = [11u8, 64u8, 194u8, 169u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(112usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for InitialReserve1 { + const NAME: &'static str = "initialReserve1"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for InitialReserve1 { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct MyAccount {} + impl MyAccount { + const METHOD_ID: [u8; 4] = [178u8, 156u8, 98u8, 244u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode(&[ethabi::ParamType::Address], data.as_ref()) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for MyAccount { + const NAME: &'static str = "myAccount"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for MyAccount { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct PriceX {} + impl PriceX { + const METHOD_ID: [u8; 4] = [73u8, 158u8, 235u8, 16u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for PriceX { + const NAME: &'static str = "priceX"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable for PriceX { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct PriceY {} + impl PriceY { + const METHOD_ID: [u8; 4] = [76u8, 77u8, 207u8, 239u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for PriceY { + const NAME: &'static str = "priceY"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable for PriceY { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Reserve0 {} + impl Reserve0 { + const METHOD_ID: [u8; 4] = [68u8, 60u8, 180u8, 188u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(112usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Reserve0 { + const NAME: &'static str = "reserve0"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for Reserve0 { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Reserve1 {} + impl Reserve1 { + const METHOD_ID: [u8; 4] = [90u8, 118u8, 242u8, 94u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(112usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Reserve1 { + const NAME: &'static str = "reserve1"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for Reserve1 { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Status {} + impl Status { + const METHOD_ID: [u8; 4] = [32u8, 13u8, 46u8, 210u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(32usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Status { + const NAME: &'static str = "status"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable for Status { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Swap { + pub amount0_out: substreams::scalar::BigInt, + pub amount1_out: substreams::scalar::BigInt, + pub to: Vec, + pub data: Vec, + } + impl Swap { + const METHOD_ID: [u8; 4] = [2u8, 44u8, 13u8, 159u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Address, + ethabi::ParamType::Bytes, + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + amount0_out: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount1_out: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + to: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + data: values.pop().expect(INTERNAL_ERR).into_bytes().expect(INTERNAL_ERR), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.amount0_out.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.amount1_out.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ethabi::Token::Address(ethabi::Address::from_slice(&self.to)), + ethabi::Token::Bytes(self.data.clone()), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for Swap { + const NAME: &'static str = "swap"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Vault0 {} + impl Vault0 { + const METHOD_ID: [u8; 4] = [161u8, 81u8, 42u8, 43u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode(&[ethabi::ParamType::Address], data.as_ref()) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Vault0 { + const NAME: &'static str = "vault0"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for Vault0 { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Vault1 {} + impl Vault1 { + const METHOD_ID: [u8; 4] = [155u8, 236u8, 98u8, 201u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode(&[ethabi::ParamType::Address], data.as_ref()) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Vault1 { + const NAME: &'static str = "vault1"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for Vault1 { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Verify { + pub new_reserve0: substreams::scalar::BigInt, + pub new_reserve1: substreams::scalar::BigInt, + } + impl Verify { + const METHOD_ID: [u8; 4] = [65u8, 22u8, 27u8, 16u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + new_reserve0: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + new_reserve1: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.new_reserve0.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.new_reserve1.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode(&[ethabi::ParamType::Bool], data.as_ref()) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_bool() + .expect(INTERNAL_ERR), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Verify { + const NAME: &'static str = "verify"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable for Verify { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } +} +/// Contract's events. +#[allow(dead_code, unused_imports, unused_variables)] +pub mod events { + use super::INTERNAL_ERR; + #[derive(Debug, Clone, PartialEq)] + pub struct EulerSwapCreated { + pub euler_swap: Vec, + pub asset0: Vec, + pub asset1: Vec, + } + impl EulerSwapCreated { + const TOPIC_ID: [u8; 32] = [ + 164u8, + 199u8, + 5u8, + 184u8, + 194u8, + 96u8, + 242u8, + 55u8, + 145u8, + 234u8, + 52u8, + 168u8, + 106u8, + 162u8, + 113u8, + 255u8, + 255u8, + 134u8, + 108u8, + 239u8, + 118u8, + 6u8, + 15u8, + 152u8, + 66u8, + 7u8, + 130u8, + 73u8, + 47u8, + 29u8, + 243u8, + 228u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 4usize { + return false; + } + if log.data.len() != 0usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Ok(Self { + euler_swap: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'euler_swap' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + asset0: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[2usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'asset0' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + asset1: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[3usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'asset1' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + } + impl substreams_ethereum::Event for EulerSwapCreated { + const NAME: &'static str = "EulerSwapCreated"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode(log: &substreams_ethereum::pb::eth::v2::Log) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Swap { + pub sender: Vec, + pub amount0_in: substreams::scalar::BigInt, + pub amount1_in: substreams::scalar::BigInt, + pub amount0_out: substreams::scalar::BigInt, + pub amount1_out: substreams::scalar::BigInt, + pub reserve0: substreams::scalar::BigInt, + pub reserve1: substreams::scalar::BigInt, + pub to: Vec, + } + impl Swap { + const TOPIC_ID: [u8; 32] = [ + 72u8, + 19u8, + 176u8, + 173u8, + 21u8, + 134u8, + 166u8, + 196u8, + 127u8, + 8u8, + 138u8, + 7u8, + 180u8, + 136u8, + 193u8, + 234u8, + 220u8, + 88u8, + 231u8, + 231u8, + 169u8, + 195u8, + 241u8, + 167u8, + 27u8, + 63u8, + 51u8, + 197u8, + 55u8, + 145u8, + 51u8, + 170u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 3usize { + return false; + } + if log.data.len() != 192usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(112usize), + ethabi::ParamType::Uint(112usize), + ], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + sender: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'sender' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + to: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[2usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'to' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + amount0_in: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount1_in: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount0_out: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount1_out: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + reserve0: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + reserve1: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + } + impl substreams_ethereum::Event for Swap { + const NAME: &'static str = "Swap"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode(log: &substreams_ethereum::pb::eth::v2::Log) -> Result { + Self::decode(log) + } + } +} \ No newline at end of file diff --git a/substreams/ethereum-eulerswap/src/abi/eulerswap_periphery.rs b/substreams/ethereum-eulerswap/src/abi/eulerswap_periphery.rs new file mode 100644 index 000000000..8781a02ba --- /dev/null +++ b/substreams/ethereum-eulerswap/src/abi/eulerswap_periphery.rs @@ -0,0 +1,329 @@ +const INTERNAL_ERR: &'static str = "`ethabi_derive` internal error"; +/// Contract's functions. +#[allow(dead_code, unused_imports, unused_variables)] +pub mod functions { + use super::INTERNAL_ERR; + #[derive(Debug, Clone, PartialEq)] + pub struct QuoteExactInput { + pub euler_swap: Vec, + pub token_in: Vec, + pub token_out: Vec, + pub amount_in: substreams::scalar::BigInt, + } + impl QuoteExactInput { + const METHOD_ID: [u8; 4] = [83u8, 78u8, 175u8, 15u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Address, + ethabi::ParamType::Address, + ethabi::ParamType::Address, + ethabi::ParamType::Uint(256usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + euler_swap: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + token_in: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + token_out: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + amount_in: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address( + ethabi::Address::from_slice(&self.euler_swap), + ), + ethabi::Token::Address(ethabi::Address::from_slice(&self.token_in)), + ethabi::Token::Address(ethabi::Address::from_slice(&self.token_out)), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.amount_in.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for QuoteExactInput { + const NAME: &'static str = "quoteExactInput"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for QuoteExactInput { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct QuoteExactOutput { + pub euler_swap: Vec, + pub token_in: Vec, + pub token_out: Vec, + pub amount_out: substreams::scalar::BigInt, + } + impl QuoteExactOutput { + const METHOD_ID: [u8; 4] = [153u8, 93u8, 97u8, 170u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Address, + ethabi::ParamType::Address, + ethabi::ParamType::Address, + ethabi::ParamType::Uint(256usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + euler_swap: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + token_in: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + token_out: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + amount_out: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address( + ethabi::Address::from_slice(&self.euler_swap), + ), + ethabi::Token::Address(ethabi::Address::from_slice(&self.token_in)), + ethabi::Token::Address(ethabi::Address::from_slice(&self.token_out)), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.amount_out.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for QuoteExactOutput { + const NAME: &'static str = "quoteExactOutput"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for QuoteExactOutput { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } +} +/// Contract's events. +#[allow(dead_code, unused_imports, unused_variables)] +pub mod events { + use super::INTERNAL_ERR; +} \ No newline at end of file diff --git a/substreams/ethereum-eulerswap/src/abi/mod.rs b/substreams/ethereum-eulerswap/src/abi/mod.rs index 76e240afa..5288da569 100644 --- a/substreams/ethereum-eulerswap/src/abi/mod.rs +++ b/substreams/ethereum-eulerswap/src/abi/mod.rs @@ -1 +1,3 @@ #![allow(clippy::all)] +pub mod eulerswap; +pub mod eulerswap_periphery; diff --git a/substreams/ethereum-eulerswap/src/pb/mod.rs b/substreams/ethereum-eulerswap/src/pb/mod.rs new file mode 100644 index 000000000..43d88383c --- /dev/null +++ b/substreams/ethereum-eulerswap/src/pb/mod.rs @@ -0,0 +1,10 @@ +// @generated +pub mod tycho { + pub mod evm { + // @@protoc_insertion_point(attribute:tycho.evm.v1) + pub mod v1 { + include!("tycho.evm.v1.rs"); + // @@protoc_insertion_point(tycho.evm.v1) + } + } +} diff --git a/substreams/ethereum-eulerswap/src/pb/tycho.evm.v1.rs b/substreams/ethereum-eulerswap/src/pb/tycho.evm.v1.rs new file mode 100644 index 000000000..6365bd489 --- /dev/null +++ b/substreams/ethereum-eulerswap/src/pb/tycho.evm.v1.rs @@ -0,0 +1,382 @@ +// @generated +// This file contains the proto definitions for Substreams common to all integrations. + +/// A struct describing a block. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Block { + /// The blocks hash. + #[prost(bytes="vec", tag="1")] + pub hash: ::prost::alloc::vec::Vec, + /// The parent blocks hash. + #[prost(bytes="vec", tag="2")] + pub parent_hash: ::prost::alloc::vec::Vec, + /// The block number. + #[prost(uint64, tag="3")] + pub number: u64, + /// The block timestamp. + #[prost(uint64, tag="4")] + pub ts: u64, +} +/// A struct describing a transaction. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Transaction { + /// The transaction hash. + #[prost(bytes="vec", tag="1")] + pub hash: ::prost::alloc::vec::Vec, + /// The sender of the transaction. + #[prost(bytes="vec", tag="2")] + pub from: ::prost::alloc::vec::Vec, + /// The receiver of the transaction. + #[prost(bytes="vec", tag="3")] + pub to: ::prost::alloc::vec::Vec, + /// The transactions index within the block. + /// TODO: should this be uint32? to match the type from the native substream type? + #[prost(uint64, tag="4")] + pub index: u64, +} +/// A custom struct representing an arbitrary attribute of a protocol component. +/// This is mainly used by the native integration to track the necessary information about the protocol. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Attribute { + /// The name of the attribute. + #[prost(string, tag="1")] + pub name: ::prost::alloc::string::String, + /// The value of the attribute. + #[prost(bytes="vec", tag="2")] + pub value: ::prost::alloc::vec::Vec, + /// The type of change the attribute underwent. + #[prost(enumeration="ChangeType", tag="3")] + pub change: i32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ProtocolType { + #[prost(string, tag="1")] + pub name: ::prost::alloc::string::String, + #[prost(enumeration="FinancialType", tag="2")] + pub financial_type: i32, + #[prost(message, repeated, tag="3")] + pub attribute_schema: ::prost::alloc::vec::Vec, + #[prost(enumeration="ImplementationType", tag="4")] + pub implementation_type: i32, +} +/// A struct describing a part of the protocol. +/// Note: For example this can be a UniswapV2 pair, that tracks the two ERC20 tokens used by the pair, +/// the component would represent a single contract. In case of VM integration, such component would +/// not need any attributes, because all the relevant info would be tracked via storage slots and balance changes. +/// It can also be a wrapping contract, like WETH, that has a constant price, but it allows swapping tokens. +/// This is why the name ProtocolComponent is used instead of "Pool" or "Pair". +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ProtocolComponent { + /// A unique identifier for the component within the protocol. + /// Can be e.g. a stringified address or a string describing the trading pair. + #[prost(string, tag="1")] + pub id: ::prost::alloc::string::String, + /// Addresses of the ERC20 tokens used by the component. + #[prost(bytes="vec", repeated, tag="2")] + pub tokens: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, + /// Addresses of the contracts used by the component. + /// Usually it is a single contract, but some protocols use multiple contracts. + #[prost(bytes="vec", repeated, tag="3")] + pub contracts: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, + /// Static attributes of the component. + /// These attributes MUST be immutable. If it can ever change, it should be given as an EntityChanges for this component id. + /// The inner ChangeType of the attribute has to match the ChangeType of the ProtocolComponent. + #[prost(message, repeated, tag="4")] + pub static_att: ::prost::alloc::vec::Vec, + /// Type of change the component underwent. + #[prost(enumeration="ChangeType", tag="5")] + pub change: i32, + /// / Represents the functionality of the component. + #[prost(message, optional, tag="6")] + pub protocol_type: ::core::option::Option, +} +/// A struct for following the changes of Total Value Locked (TVL) of a protocol component. +/// Note that if a ProtocolComponent contains multiple contracts, the TVL is tracked for the component as a whole. +/// E.g. for UniswapV2 pair WETH/USDC, this tracks the USDC and WETH balance of the pair contract. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BalanceChange { + /// The address of the ERC20 token whose balance changed. + #[prost(bytes="vec", tag="1")] + pub token: ::prost::alloc::vec::Vec, + /// The new balance of the token. Note: it must be a big endian encoded int. + #[prost(bytes="vec", tag="2")] + pub balance: ::prost::alloc::vec::Vec, + /// The id of the component whose TVL is tracked. Note: This MUST be utf8 encoded. + /// If the protocol component includes multiple contracts, the balance change must be aggregated to reflect how much tokens can be traded. + #[prost(bytes="vec", tag="3")] + pub component_id: ::prost::alloc::vec::Vec, +} +// Native entities + +/// A component is a set of attributes that are associated with a custom entity. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct EntityChanges { + /// A unique identifier of the entity within the protocol. + #[prost(string, tag="1")] + pub component_id: ::prost::alloc::string::String, + /// The set of attributes that are associated with the entity. + #[prost(message, repeated, tag="2")] + pub attributes: ::prost::alloc::vec::Vec, +} +// VM entities + +/// A key value entry into contract storage. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ContractSlot { + /// A contract's storage slot. + #[prost(bytes="vec", tag="2")] + pub slot: ::prost::alloc::vec::Vec, + /// The new value for this storage slot. + #[prost(bytes="vec", tag="3")] + pub value: ::prost::alloc::vec::Vec, +} +/// A struct for following the token balance changes for a contract. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AccountBalanceChange { + /// The address of the ERC20 token whose balance changed. + #[prost(bytes="vec", tag="1")] + pub token: ::prost::alloc::vec::Vec, + /// The new balance of the token. Note: it must be a big endian encoded int. + #[prost(bytes="vec", tag="2")] + pub balance: ::prost::alloc::vec::Vec, +} +/// Changes made to a single contract's state. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ContractChange { + /// The contract's address + #[prost(bytes="vec", tag="1")] + pub address: ::prost::alloc::vec::Vec, + /// The new balance of the contract, empty bytes indicates no change. + #[prost(bytes="vec", tag="2")] + pub balance: ::prost::alloc::vec::Vec, + /// The new code of the contract, empty bytes indicates no change. + #[prost(bytes="vec", tag="3")] + pub code: ::prost::alloc::vec::Vec, + /// The changes to this contract's slots, empty sequence indicates no change. + #[prost(message, repeated, tag="4")] + pub slots: ::prost::alloc::vec::Vec, + /// Whether this is an update, a creation or a deletion. + #[prost(enumeration="ChangeType", tag="5")] + pub change: i32, + /// The new ERC20 balances of the contract. + #[prost(message, repeated, tag="6")] + pub token_balances: ::prost::alloc::vec::Vec, +} +// Aggregate entities + +/// A set of changes aggregated by transaction. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TransactionChanges { + /// The transaction instance that results in the changes. + #[prost(message, optional, tag="1")] + pub tx: ::core::option::Option, + /// Contains the changes induced by the above transaction, aggregated on a per-contract basis. + /// Contains the contract changes induced by the above transaction, usually for tracking VM components. + #[prost(message, repeated, tag="2")] + pub contract_changes: ::prost::alloc::vec::Vec, + /// Contains the entity changes induced by the above transaction. + /// Usually for tracking native components or used for VM extensions (plugins). + #[prost(message, repeated, tag="3")] + pub entity_changes: ::prost::alloc::vec::Vec, + /// An array of newly added components. + #[prost(message, repeated, tag="4")] + pub component_changes: ::prost::alloc::vec::Vec, + /// An array of balance changes to components. + #[prost(message, repeated, tag="5")] + pub balance_changes: ::prost::alloc::vec::Vec, +} +/// A set of transaction changes within a single block. +/// This message must be the output of your substreams module. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlockChanges { + /// The block for which these changes are collectively computed. + #[prost(message, optional, tag="1")] + pub block: ::core::option::Option, + /// The set of transaction changes observed in the specified block. + #[prost(message, repeated, tag="2")] + pub changes: ::prost::alloc::vec::Vec, +} +/// Enum to specify the type of a change. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum ChangeType { + Unspecified = 0, + Update = 1, + Creation = 2, + Deletion = 3, +} +impl ChangeType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + ChangeType::Unspecified => "CHANGE_TYPE_UNSPECIFIED", + ChangeType::Update => "CHANGE_TYPE_UPDATE", + ChangeType::Creation => "CHANGE_TYPE_CREATION", + ChangeType::Deletion => "CHANGE_TYPE_DELETION", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "CHANGE_TYPE_UNSPECIFIED" => Some(Self::Unspecified), + "CHANGE_TYPE_UPDATE" => Some(Self::Update), + "CHANGE_TYPE_CREATION" => Some(Self::Creation), + "CHANGE_TYPE_DELETION" => Some(Self::Deletion), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum FinancialType { + Swap = 0, + Lend = 1, + Leverage = 2, + Psm = 3, +} +impl FinancialType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + FinancialType::Swap => "SWAP", + FinancialType::Lend => "LEND", + FinancialType::Leverage => "LEVERAGE", + FinancialType::Psm => "PSM", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "SWAP" => Some(Self::Swap), + "LEND" => Some(Self::Lend), + "LEVERAGE" => Some(Self::Leverage), + "PSM" => Some(Self::Psm), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum ImplementationType { + Vm = 0, + Custom = 1, +} +impl ImplementationType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + ImplementationType::Vm => "VM", + ImplementationType::Custom => "CUSTOM", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "VM" => Some(Self::Vm), + "CUSTOM" => Some(Self::Custom), + _ => None, + } + } +} +// WARNING: DEPRECATED. Please use common.proto's TransactionChanges and BlockChanges instead. +// This file contains proto definitions specific to the VM integration. + +/// A set of changes aggregated by transaction. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TransactionContractChanges { + /// The transaction instance that results in the changes. + #[prost(message, optional, tag="1")] + pub tx: ::core::option::Option, + /// Contains the changes induced by the above transaction, aggregated on a per-contract basis. + #[prost(message, repeated, tag="2")] + pub contract_changes: ::prost::alloc::vec::Vec, + /// An array of newly added components. + #[prost(message, repeated, tag="3")] + pub component_changes: ::prost::alloc::vec::Vec, + /// An array of balance changes to components. + #[prost(message, repeated, tag="4")] + pub balance_changes: ::prost::alloc::vec::Vec, +} +/// A set of transaction changes within a single block. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlockContractChanges { + /// The block for which these changes are collectively computed. + #[prost(message, optional, tag="1")] + pub block: ::core::option::Option, + /// The set of transaction changes observed in the specified block. + #[prost(message, repeated, tag="2")] + pub changes: ::prost::alloc::vec::Vec, +} +/// A message containing relative balance changes. +/// +/// Used to track token balances of protocol components in case they are only +/// available as relative values within a block. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BalanceDelta { + /// The ordinal of the balance change. Must be unique & deterministic over all balances + /// changes within a block. + #[prost(uint64, tag="1")] + pub ord: u64, + /// The tx hash of the transaction that caused the balance change. + #[prost(message, optional, tag="2")] + pub tx: ::core::option::Option, + /// The address of the ERC20 token whose balance changed. + #[prost(bytes="vec", tag="3")] + pub token: ::prost::alloc::vec::Vec, + /// The delta balance of the token. + #[prost(bytes="vec", tag="4")] + pub delta: ::prost::alloc::vec::Vec, + /// The id of the component whose TVL is tracked. + /// If the protocol component includes multiple contracts, the balance change must be + /// aggregated to reflect how much tokens can be traded. + #[prost(bytes="vec", tag="5")] + pub component_id: ::prost::alloc::vec::Vec, +} +/// A set of balances deltas, usually a group of changes within a single block. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlockBalanceDeltas { + #[prost(message, repeated, tag="1")] + pub balance_deltas: ::prost::alloc::vec::Vec, +} +/// A message containing protocol components that were created by a single tx. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TransactionProtocolComponents { + #[prost(message, optional, tag="1")] + pub tx: ::core::option::Option, + #[prost(message, repeated, tag="2")] + pub components: ::prost::alloc::vec::Vec, +} +/// All protocol components that were created within a block with their corresponding tx. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlockTransactionProtocolComponents { + #[prost(message, repeated, tag="1")] + pub tx_components: ::prost::alloc::vec::Vec, +} +// @@protoc_insertion_point(module) diff --git a/substreams/ethereum-eulerswap/substreams.yaml b/substreams/ethereum-eulerswap/substreams.yaml index 864e9bc8f..eda7391df 100644 --- a/substreams/ethereum-eulerswap/substreams.yaml +++ b/substreams/ethereum-eulerswap/substreams.yaml @@ -1,6 +1,6 @@ specVersion: v0.1.0 package: - name: "ethereum_template" + name: "ethereum_eulerswap" version: v0.1.0 protobuf: @@ -14,7 +14,7 @@ protobuf: binaries: default: type: wasm/rust-v1 - file: ../target/wasm32-unknown-unknown/release/ethereum_template_factory.wasm + file: ../target/wasm32-unknown-unknown/release/ethereum_eulerswap.wasm network: mainnet From 0db52a283c7385c7117c490269ea7182671206b7 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Tue, 18 Feb 2025 12:00:22 +0100 Subject: [PATCH 07/11] Add factory abi --- .../abi/eulerswap_factory.json | 293 ++++ .../src/abi/eulerswap_factory.rs | 1209 +++++++++++++++++ substreams/ethereum-eulerswap/src/abi/mod.rs | 1 + 3 files changed, 1503 insertions(+) create mode 100644 substreams/ethereum-eulerswap/abi/eulerswap_factory.json create mode 100644 substreams/ethereum-eulerswap/src/abi/eulerswap_factory.rs diff --git a/substreams/ethereum-eulerswap/abi/eulerswap_factory.json b/substreams/ethereum-eulerswap/abi/eulerswap_factory.json new file mode 100644 index 000000000..2fb8fe712 --- /dev/null +++ b/substreams/ethereum-eulerswap/abi/eulerswap_factory.json @@ -0,0 +1,293 @@ +[ + { + "type": "constructor", + "inputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "allPools", + "inputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "allPoolsLength", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "deployPool", + "inputs": [ + { + "name": "params", + "type": "tuple", + "internalType": "struct IEulerSwapFactory.DeployParams", + "components": [ + { + "name": "vault0", + "type": "address", + "internalType": "address" + }, + { + "name": "vault1", + "type": "address", + "internalType": "address" + }, + { + "name": "holder", + "type": "address", + "internalType": "address" + }, + { + "name": "fee", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "priceX", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "priceY", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "concentrationX", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "concentrationY", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "debtLimit0", + "type": "uint112", + "internalType": "uint112" + }, + { + "name": "debtLimit1", + "type": "uint112", + "internalType": "uint112" + } + ] + } + ], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "getAllPoolsListSlice", + "inputs": [ + { + "name": "_start", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "_end", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "address[]", + "internalType": "address[]" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getPool", + "inputs": [ + { + "name": "poolKey", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "outputs": [ + { + "name": "pool", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "renounceOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "transferOwnership", + "inputs": [ + { + "name": "newOwner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "OwnershipTransferred", + "inputs": [ + { + "name": "previousOwner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newOwner", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "PoolDeployed", + "inputs": [ + { + "name": "asset0", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "asset1", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "feeMultiplier", + "type": "uint256", + "indexed": true, + "internalType": "uint256" + }, + { + "name": "swapAccount", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "priceX", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "priceY", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "concentrationX", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "concentrationY", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "pool", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "InvalidQuery", + "inputs": [] + }, + { + "type": "error", + "name": "OwnableInvalidOwner", + "inputs": [ + { + "name": "owner", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "OwnableUnauthorizedAccount", + "inputs": [ + { + "name": "account", + "type": "address", + "internalType": "address" + } + ] + } + ] \ No newline at end of file diff --git a/substreams/ethereum-eulerswap/src/abi/eulerswap_factory.rs b/substreams/ethereum-eulerswap/src/abi/eulerswap_factory.rs new file mode 100644 index 000000000..d77557fd7 --- /dev/null +++ b/substreams/ethereum-eulerswap/src/abi/eulerswap_factory.rs @@ -0,0 +1,1209 @@ +const INTERNAL_ERR: &'static str = "`ethabi_derive` internal error"; +/// Contract's functions. +#[allow(dead_code, unused_imports, unused_variables)] +pub mod functions { + use super::INTERNAL_ERR; + #[derive(Debug, Clone, PartialEq)] + pub struct AllPools { + pub param0: substreams::scalar::BigInt, + } + impl AllPools { + const METHOD_ID: [u8; 4] = [65u8, 209u8, 222u8, 151u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + param0: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.param0.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode(&[ethabi::ParamType::Address], data.as_ref()) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for AllPools { + const NAME: &'static str = "allPools"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for AllPools { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct AllPoolsLength {} + impl AllPoolsLength { + const METHOD_ID: [u8; 4] = [239u8, 222u8, 78u8, 100u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for AllPoolsLength { + const NAME: &'static str = "allPoolsLength"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for AllPoolsLength { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct DeployPool { + pub params: ( + Vec, + Vec, + Vec, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + ), + } + impl DeployPool { + const METHOD_ID: [u8; 4] = [213u8, 67u8, 121u8, 59u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Tuple( + vec![ + ethabi::ParamType::Address, ethabi::ParamType::Address, + ethabi::ParamType::Address, + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(112usize), + ethabi::ParamType::Uint(112usize) + ], + ), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + params: { + let tuple_elements = values + .pop() + .expect(INTERNAL_ERR) + .into_tuple() + .expect(INTERNAL_ERR); + ( + tuple_elements[0usize] + .clone() + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + tuple_elements[1usize] + .clone() + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + tuple_elements[2usize] + .clone() + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + { + let mut v = [0 as u8; 32]; + tuple_elements[3usize] + .clone() + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + tuple_elements[4usize] + .clone() + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + tuple_elements[5usize] + .clone() + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + tuple_elements[6usize] + .clone() + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + tuple_elements[7usize] + .clone() + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + tuple_elements[8usize] + .clone() + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + tuple_elements[9usize] + .clone() + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + ) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Tuple( + vec![ + ethabi::Token::Address(ethabi::Address::from_slice(& self + .params.0)), + ethabi::Token::Address(ethabi::Address::from_slice(& self + .params.1)), + ethabi::Token::Address(ethabi::Address::from_slice(& self + .params.2)), + ethabi::Token::Uint(ethabi::Uint::from_big_endian(match self + .params.3.clone().to_bytes_be() { (num_bigint::Sign::Plus, + bytes) => bytes, (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") }, } + .as_slice(),),), + ethabi::Token::Uint(ethabi::Uint::from_big_endian(match self + .params.4.clone().to_bytes_be() { (num_bigint::Sign::Plus, + bytes) => bytes, (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") }, } + .as_slice(),),), + ethabi::Token::Uint(ethabi::Uint::from_big_endian(match self + .params.5.clone().to_bytes_be() { (num_bigint::Sign::Plus, + bytes) => bytes, (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") }, } + .as_slice(),),), + ethabi::Token::Uint(ethabi::Uint::from_big_endian(match self + .params.6.clone().to_bytes_be() { (num_bigint::Sign::Plus, + bytes) => bytes, (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") }, } + .as_slice(),),), + ethabi::Token::Uint(ethabi::Uint::from_big_endian(match self + .params.7.clone().to_bytes_be() { (num_bigint::Sign::Plus, + bytes) => bytes, (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") }, } + .as_slice(),),), + ethabi::Token::Uint(ethabi::Uint::from_big_endian(match self + .params.8.clone().to_bytes_be() { (num_bigint::Sign::Plus, + bytes) => bytes, (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") }, } + .as_slice(),),), + ethabi::Token::Uint(ethabi::Uint::from_big_endian(match self + .params.9.clone().to_bytes_be() { (num_bigint::Sign::Plus, + bytes) => bytes, (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") }, } + .as_slice(),),) + ], + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode(&[ethabi::ParamType::Address], data.as_ref()) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for DeployPool { + const NAME: &'static str = "deployPool"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for DeployPool { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct GetAllPoolsListSlice { + pub start: substreams::scalar::BigInt, + pub end: substreams::scalar::BigInt, + } + impl GetAllPoolsListSlice { + const METHOD_ID: [u8; 4] = [50u8, 154u8, 119u8, 178u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + start: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + end: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.start.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.end.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result>, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result>, String> { + let mut values = ethabi::decode( + &[ethabi::ParamType::Array(Box::new(ethabi::ParamType::Address))], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_array() + .expect(INTERNAL_ERR) + .into_iter() + .map(|inner| { + inner.into_address().expect(INTERNAL_ERR).as_bytes().to_vec() + }) + .collect(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option>> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for GetAllPoolsListSlice { + const NAME: &'static str = "getAllPoolsListSlice"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable>> for GetAllPoolsListSlice { + fn output(data: &[u8]) -> Result>, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct GetPool { + pub pool_key: [u8; 32usize], + } + impl GetPool { + const METHOD_ID: [u8; 4] = [246u8, 192u8, 9u8, 39u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::FixedBytes(32usize)], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + pool_key: { + let mut result = [0u8; 32]; + let v = values + .pop() + .expect(INTERNAL_ERR) + .into_fixed_bytes() + .expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ethabi::Token::FixedBytes(self.pool_key.as_ref().to_vec())], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode(&[ethabi::ParamType::Address], data.as_ref()) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for GetPool { + const NAME: &'static str = "getPool"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for GetPool { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Owner {} + impl Owner { + const METHOD_ID: [u8; 4] = [141u8, 165u8, 203u8, 91u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode(&[ethabi::ParamType::Address], data.as_ref()) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Owner { + const NAME: &'static str = "owner"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for Owner { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct RenounceOwnership {} + impl RenounceOwnership { + const METHOD_ID: [u8; 4] = [113u8, 80u8, 24u8, 166u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for RenounceOwnership { + const NAME: &'static str = "renounceOwnership"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct TransferOwnership { + pub new_owner: Vec, + } + impl TransferOwnership { + const METHOD_ID: [u8; 4] = [242u8, 253u8, 227u8, 139u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + new_owner: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ethabi::Token::Address(ethabi::Address::from_slice(&self.new_owner))], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for TransferOwnership { + const NAME: &'static str = "transferOwnership"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } +} +/// Contract's events. +#[allow(dead_code, unused_imports, unused_variables)] +pub mod events { + use super::INTERNAL_ERR; + #[derive(Debug, Clone, PartialEq)] + pub struct OwnershipTransferred { + pub previous_owner: Vec, + pub new_owner: Vec, + } + impl OwnershipTransferred { + const TOPIC_ID: [u8; 32] = [ + 139u8, + 224u8, + 7u8, + 156u8, + 83u8, + 22u8, + 89u8, + 20u8, + 19u8, + 68u8, + 205u8, + 31u8, + 208u8, + 164u8, + 242u8, + 132u8, + 25u8, + 73u8, + 127u8, + 151u8, + 34u8, + 163u8, + 218u8, + 175u8, + 227u8, + 180u8, + 24u8, + 111u8, + 107u8, + 100u8, + 87u8, + 224u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 3usize { + return false; + } + if log.data.len() != 0usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Ok(Self { + previous_owner: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'previous_owner' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + new_owner: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[2usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'new_owner' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + } + impl substreams_ethereum::Event for OwnershipTransferred { + const NAME: &'static str = "OwnershipTransferred"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode(log: &substreams_ethereum::pb::eth::v2::Log) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct PoolDeployed { + pub asset0: Vec, + pub asset1: Vec, + pub fee_multiplier: substreams::scalar::BigInt, + pub swap_account: Vec, + pub price_x: substreams::scalar::BigInt, + pub price_y: substreams::scalar::BigInt, + pub concentration_x: substreams::scalar::BigInt, + pub concentration_y: substreams::scalar::BigInt, + pub pool: Vec, + } + impl PoolDeployed { + const TOPIC_ID: [u8; 32] = [ + 100u8, + 226u8, + 88u8, + 239u8, + 76u8, + 165u8, + 238u8, + 113u8, + 119u8, + 121u8, + 35u8, + 69u8, + 124u8, + 80u8, + 194u8, + 142u8, + 15u8, + 251u8, + 37u8, + 85u8, + 177u8, + 207u8, + 240u8, + 54u8, + 176u8, + 146u8, + 17u8, + 133u8, + 146u8, + 239u8, + 81u8, + 235u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 4usize { + return false; + } + if log.data.len() != 192usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Address, + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Address, + ], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + asset0: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'asset0' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + asset1: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[2usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'asset1' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + fee_multiplier: { + let mut v = [0 as u8; 32]; + ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + log.topics[3usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'fee_multiplier' from topic of type 'uint256': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + swap_account: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + price_x: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + price_y: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + concentration_x: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + concentration_y: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + pool: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + } + impl substreams_ethereum::Event for PoolDeployed { + const NAME: &'static str = "PoolDeployed"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode(log: &substreams_ethereum::pb::eth::v2::Log) -> Result { + Self::decode(log) + } + } +} \ No newline at end of file diff --git a/substreams/ethereum-eulerswap/src/abi/mod.rs b/substreams/ethereum-eulerswap/src/abi/mod.rs index 5288da569..be364e4d5 100644 --- a/substreams/ethereum-eulerswap/src/abi/mod.rs +++ b/substreams/ethereum-eulerswap/src/abi/mod.rs @@ -1,3 +1,4 @@ #![allow(clippy::all)] pub mod eulerswap; +pub mod eulerswap_factory; pub mod eulerswap_periphery; From 650174f69d9b8ade223b3b9f79943bdecd2abdcf Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Wed, 19 Feb 2025 19:13:16 +0100 Subject: [PATCH 08/11] update indexer to follow uni-v2 architecture --- substreams/Cargo.lock | 3 + substreams/ethereum-eulerswap/Cargo.toml | 3 + .../abi/eulerswap_factory.json | 510 ++++++++---------- substreams/ethereum-eulerswap/build.rs | 56 +- .../proto/v1/eulerswap.proto | 67 +++ .../src/abi/eulerswap_factory.rs | 276 ---------- substreams/ethereum-eulerswap/src/lib.rs | 9 +- substreams/ethereum-eulerswap/src/modules.rs | 269 --------- .../src/modules/1_map_pool_created.rs | 106 ++++ .../src/modules/2_store_pools.rs | 22 + .../src/modules/3_map_pool_events.rs | 223 ++++++++ .../ethereum-eulerswap/src/modules/mod.rs | 10 + .../ethereum-eulerswap/src/pool_factories.rs | 44 -- .../ethereum-eulerswap/src/store_key.rs | 16 + substreams/ethereum-eulerswap/src/traits.rs | 22 + substreams/ethereum-eulerswap/substreams.yaml | 91 ++-- 16 files changed, 769 insertions(+), 958 deletions(-) create mode 100644 substreams/ethereum-eulerswap/proto/v1/eulerswap.proto delete mode 100644 substreams/ethereum-eulerswap/src/modules.rs create mode 100644 substreams/ethereum-eulerswap/src/modules/1_map_pool_created.rs create mode 100644 substreams/ethereum-eulerswap/src/modules/2_store_pools.rs create mode 100644 substreams/ethereum-eulerswap/src/modules/3_map_pool_events.rs create mode 100644 substreams/ethereum-eulerswap/src/modules/mod.rs delete mode 100644 substreams/ethereum-eulerswap/src/pool_factories.rs create mode 100644 substreams/ethereum-eulerswap/src/store_key.rs create mode 100644 substreams/ethereum-eulerswap/src/traits.rs diff --git a/substreams/Cargo.lock b/substreams/Cargo.lock index 0b7f26b43..03fb00555 100644 --- a/substreams/Cargo.lock +++ b/substreams/Cargo.lock @@ -267,13 +267,16 @@ dependencies = [ "anyhow", "ethabi 18.0.0", "hex", + "hex-literal 0.4.1", "itertools 0.10.5", "num-bigint", "prost 0.11.9", "serde", "serde-sibor", + "serde_qs", "substreams", "substreams-ethereum", + "substreams-helper 0.0.2 (git+https://github.com/propeller-heads/tycho-protocol-sdk.git?rev=b8aeaa3)", "tycho-substreams 0.2.0 (git+https://github.com/propeller-heads/tycho-protocol-sdk.git?rev=3c08359)", ] diff --git a/substreams/ethereum-eulerswap/Cargo.toml b/substreams/ethereum-eulerswap/Cargo.toml index c3f3a52bb..cab5bf9f7 100644 --- a/substreams/ethereum-eulerswap/Cargo.toml +++ b/substreams/ethereum-eulerswap/Cargo.toml @@ -20,6 +20,9 @@ itertools = "0.10.5" serde = "1.0.217" serde-sibor = "0.1.0" +hex-literal = "0.4.1" +substreams-helper = { git = "https://github.com/propeller-heads/tycho-protocol-sdk.git", rev = "b8aeaa3" } +serde_qs = "0.13.0" [build-dependencies] anyhow = "1" diff --git a/substreams/ethereum-eulerswap/abi/eulerswap_factory.json b/substreams/ethereum-eulerswap/abi/eulerswap_factory.json index 2fb8fe712..d31bc543e 100644 --- a/substreams/ethereum-eulerswap/abi/eulerswap_factory.json +++ b/substreams/ethereum-eulerswap/abi/eulerswap_factory.json @@ -1,293 +1,219 @@ [ - { - "type": "constructor", - "inputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "allPools", - "inputs": [ - { - "name": "", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "allPoolsLength", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "uint256", - "internalType": "uint256" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "deployPool", - "inputs": [ - { - "name": "params", - "type": "tuple", - "internalType": "struct IEulerSwapFactory.DeployParams", - "components": [ - { - "name": "vault0", - "type": "address", - "internalType": "address" - }, - { - "name": "vault1", - "type": "address", - "internalType": "address" - }, - { - "name": "holder", - "type": "address", - "internalType": "address" - }, - { - "name": "fee", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "priceX", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "priceY", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "concentrationX", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "concentrationY", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "debtLimit0", - "type": "uint112", - "internalType": "uint112" - }, - { - "name": "debtLimit1", - "type": "uint112", - "internalType": "uint112" - } - ] - } - ], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "getAllPoolsListSlice", - "inputs": [ - { - "name": "_start", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "_end", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [ - { - "name": "", - "type": "address[]", - "internalType": "address[]" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "getPool", - "inputs": [ - { - "name": "poolKey", - "type": "bytes32", - "internalType": "bytes32" - } - ], - "outputs": [ - { - "name": "pool", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "owner", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "renounceOwnership", - "inputs": [], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "transferOwnership", - "inputs": [ - { - "name": "newOwner", - "type": "address", - "internalType": "address" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "event", - "name": "OwnershipTransferred", - "inputs": [ - { - "name": "previousOwner", - "type": "address", - "indexed": true, - "internalType": "address" - }, - { - "name": "newOwner", - "type": "address", - "indexed": true, - "internalType": "address" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "PoolDeployed", - "inputs": [ - { - "name": "asset0", - "type": "address", - "indexed": true, - "internalType": "address" - }, - { - "name": "asset1", - "type": "address", - "indexed": true, - "internalType": "address" - }, - { - "name": "feeMultiplier", - "type": "uint256", - "indexed": true, - "internalType": "uint256" - }, - { - "name": "swapAccount", - "type": "address", - "indexed": false, - "internalType": "address" - }, - { - "name": "priceX", - "type": "uint256", - "indexed": false, - "internalType": "uint256" - }, - { - "name": "priceY", - "type": "uint256", - "indexed": false, - "internalType": "uint256" - }, - { - "name": "concentrationX", - "type": "uint256", - "indexed": false, - "internalType": "uint256" - }, - { - "name": "concentrationY", - "type": "uint256", - "indexed": false, - "internalType": "uint256" - }, - { - "name": "pool", - "type": "address", - "indexed": false, - "internalType": "address" - } - ], - "anonymous": false - }, - { - "type": "error", - "name": "InvalidQuery", - "inputs": [] - }, - { - "type": "error", - "name": "OwnableInvalidOwner", - "inputs": [ - { - "name": "owner", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "OwnableUnauthorizedAccount", - "inputs": [ - { - "name": "account", - "type": "address", - "internalType": "address" - } - ] - } - ] \ No newline at end of file + { + "type": "function", + "name": "allPools", + "inputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "allPoolsLength", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "deployPool", + "inputs": [ + { + "name": "params", + "type": "tuple", + "internalType": "struct IEulerSwapFactory.DeployParams", + "components": [ + { + "name": "vault0", + "type": "address", + "internalType": "address" + }, + { + "name": "vault1", + "type": "address", + "internalType": "address" + }, + { + "name": "swapAccount", + "type": "address", + "internalType": "address" + }, + { + "name": "fee", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "priceX", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "priceY", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "concentrationX", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "concentrationY", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "debtLimit0", + "type": "uint112", + "internalType": "uint112" + }, + { + "name": "debtLimit1", + "type": "uint112", + "internalType": "uint112" + } + ] + } + ], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "getAllPoolsListSlice", + "inputs": [ + { + "name": "_start", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "_end", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "address[]", + "internalType": "address[]" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getPool", + "inputs": [ + { + "name": "poolKey", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "outputs": [ + { + "name": "pool", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "event", + "name": "PoolDeployed", + "inputs": [ + { + "name": "asset0", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "asset1", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "feeMultiplier", + "type": "uint256", + "indexed": true, + "internalType": "uint256" + }, + { + "name": "swapAccount", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "priceX", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "priceY", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "concentrationX", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "concentrationY", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "pool", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "AlreadyDeployed", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidQuery", + "inputs": [] + } +] diff --git a/substreams/ethereum-eulerswap/build.rs b/substreams/ethereum-eulerswap/build.rs index 5e7bd34c5..2ff32f328 100644 --- a/substreams/ethereum-eulerswap/build.rs +++ b/substreams/ethereum-eulerswap/build.rs @@ -1,49 +1,15 @@ -use anyhow::Result; -use std::{fs, io::Write}; +use anyhow::{Ok, Result}; use substreams_ethereum::Abigen; -fn main() -> Result<()> { - let abi_folder = "abi"; - let output_folder = "src/abi"; - - let abis = fs::read_dir(abi_folder)?; - - let mut files = abis.collect::, _>>()?; - - // Sort the files by their name - files.sort_by_key(|a| a.file_name()); - - let mut mod_rs_content = String::new(); - mod_rs_content.push_str("#![allow(clippy::all)]\n"); - - for file in files { - let file_name = file.file_name(); - let file_name = file_name.to_string_lossy(); - - if !file_name.ends_with(".json") { - continue; - } - - let contract_name = file_name.split('.').next().unwrap(); - - let input_path = format!("{}/{}", abi_folder, file_name); - let output_path = format!("{}/{}.rs", output_folder, contract_name); - - mod_rs_content.push_str(&format!("pub mod {};\n", contract_name)); - - if std::path::Path::new(&output_path).exists() { - continue; - } - - Abigen::new(contract_name, &input_path)? - .generate()? - .write_to_file(&output_path)?; - } - - let mod_rs_path = format!("{}/mod.rs", output_folder); - let mut mod_rs_file = fs::File::create(mod_rs_path)?; - - mod_rs_file.write_all(mod_rs_content.as_bytes())?; - +fn main() -> Result<(), anyhow::Error> { + Abigen::new("EulerSwapFactory", "abi/eulerswap_factory.json")? + .generate()? + .write_to_file("src/abi/eulerswap_factory.rs")?; + Abigen::new("EulerSwap", "abi/eulerswap.json")? + .generate()? + .write_to_file("src/abi/eulerswap.rs")?; + Abigen::new("EulerSwapPeriphery", "abi/eulerswap_periphery.json")? + .generate()? + .write_to_file("src/abi/eulerswap_periphery.rs")?; Ok(()) } diff --git a/substreams/ethereum-eulerswap/proto/v1/eulerswap.proto b/substreams/ethereum-eulerswap/proto/v1/eulerswap.proto new file mode 100644 index 000000000..0cee9c55a --- /dev/null +++ b/substreams/ethereum-eulerswap/proto/v1/eulerswap.proto @@ -0,0 +1,67 @@ +syntax = "proto3"; + +package tycho.evm.eulerswap.v1; + +message Pools { + repeated Pool pools = 1; +} + +message Pool { + bytes address = 1; + bytes asset0 = 2; + bytes asset1 = 3; + bytes vault0 = 4; + bytes vault1 = 5; + bytes swap_account = 6; + string fee = 7; + string price_x = 8; + string price_y = 9; + string concentration_x = 10; + string concentration_y = 11; + string debt_limit0 = 12; + string debt_limit1 = 13; + bytes created_tx_hash = 14; +} + +message Events { + repeated Event events = 1; +} + +message Event { + oneof type { + PoolDeployedEvent pool_deployed = 10; + SwapEvent swap = 20; + } + + string hash = 100; + uint32 log_index = 101; + uint64 log_ordinal = 102; + string to = 103; + string from = 104; + uint64 block_number = 105; + uint64 timestamp = 106; + string pool = 107; +} + +message PoolDeployedEvent { + bytes asset0 = 1; + bytes asset1 = 2; + string fee_multiplier = 3; + bytes swap_account = 4; + string price_x = 5; + string price_y = 6; + string concentration_x = 7; + string concentration_y = 8; + bytes pool = 9; +} + +message SwapEvent { + bytes sender = 1; + string amount0_in = 2; + string amount1_in = 3; + string amount0_out = 4; + string amount1_out = 5; + string reserve0 = 6; + string reserve1 = 7; + bytes to = 8; +} \ No newline at end of file diff --git a/substreams/ethereum-eulerswap/src/abi/eulerswap_factory.rs b/substreams/ethereum-eulerswap/src/abi/eulerswap_factory.rs index d77557fd7..5ace90890 100644 --- a/substreams/ethereum-eulerswap/src/abi/eulerswap_factory.rs +++ b/substreams/ethereum-eulerswap/src/abi/eulerswap_factory.rs @@ -734,288 +734,12 @@ pub mod functions { Self::output(data) } } - #[derive(Debug, Clone, PartialEq)] - pub struct Owner {} - impl Owner { - const METHOD_ID: [u8; 4] = [141u8, 165u8, 203u8, 91u8]; - pub fn decode( - call: &substreams_ethereum::pb::eth::v2::Call, - ) -> Result { - Ok(Self {}) - } - pub fn encode(&self) -> Vec { - let data = ethabi::encode(&[]); - let mut encoded = Vec::with_capacity(4 + data.len()); - encoded.extend(Self::METHOD_ID); - encoded.extend(data); - encoded - } - pub fn output_call( - call: &substreams_ethereum::pb::eth::v2::Call, - ) -> Result, String> { - Self::output(call.return_data.as_ref()) - } - pub fn output(data: &[u8]) -> Result, String> { - let mut values = ethabi::decode(&[ethabi::ParamType::Address], data.as_ref()) - .map_err(|e| format!("unable to decode output data: {:?}", e))?; - Ok( - values - .pop() - .expect("one output data should have existed") - .into_address() - .expect(INTERNAL_ERR) - .as_bytes() - .to_vec(), - ) - } - pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { - match call.input.get(0..4) { - Some(signature) => Self::METHOD_ID == signature, - None => false, - } - } - pub fn call(&self, address: Vec) -> Option> { - use substreams_ethereum::pb::eth::rpc; - let rpc_calls = rpc::RpcCalls { - calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], - }; - let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; - let response = responses.get(0).expect("one response should have existed"); - if response.failed { - return None; - } - match Self::output(response.raw.as_ref()) { - Ok(data) => Some(data), - Err(err) => { - use substreams_ethereum::Function; - substreams::log::info!( - "Call output for function `{}` failed to decode with error: {}", - Self::NAME, err - ); - None - } - } - } - } - impl substreams_ethereum::Function for Owner { - const NAME: &'static str = "owner"; - fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { - Self::match_call(call) - } - fn decode( - call: &substreams_ethereum::pb::eth::v2::Call, - ) -> Result { - Self::decode(call) - } - fn encode(&self) -> Vec { - self.encode() - } - } - impl substreams_ethereum::rpc::RPCDecodable> for Owner { - fn output(data: &[u8]) -> Result, String> { - Self::output(data) - } - } - #[derive(Debug, Clone, PartialEq)] - pub struct RenounceOwnership {} - impl RenounceOwnership { - const METHOD_ID: [u8; 4] = [113u8, 80u8, 24u8, 166u8]; - pub fn decode( - call: &substreams_ethereum::pb::eth::v2::Call, - ) -> Result { - Ok(Self {}) - } - pub fn encode(&self) -> Vec { - let data = ethabi::encode(&[]); - let mut encoded = Vec::with_capacity(4 + data.len()); - encoded.extend(Self::METHOD_ID); - encoded.extend(data); - encoded - } - pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { - match call.input.get(0..4) { - Some(signature) => Self::METHOD_ID == signature, - None => false, - } - } - } - impl substreams_ethereum::Function for RenounceOwnership { - const NAME: &'static str = "renounceOwnership"; - fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { - Self::match_call(call) - } - fn decode( - call: &substreams_ethereum::pb::eth::v2::Call, - ) -> Result { - Self::decode(call) - } - fn encode(&self) -> Vec { - self.encode() - } - } - #[derive(Debug, Clone, PartialEq)] - pub struct TransferOwnership { - pub new_owner: Vec, - } - impl TransferOwnership { - const METHOD_ID: [u8; 4] = [242u8, 253u8, 227u8, 139u8]; - pub fn decode( - call: &substreams_ethereum::pb::eth::v2::Call, - ) -> Result { - let maybe_data = call.input.get(4..); - if maybe_data.is_none() { - return Err("no data to decode".to_string()); - } - let mut values = ethabi::decode( - &[ethabi::ParamType::Address], - maybe_data.unwrap(), - ) - .map_err(|e| format!("unable to decode call.input: {:?}", e))?; - values.reverse(); - Ok(Self { - new_owner: values - .pop() - .expect(INTERNAL_ERR) - .into_address() - .expect(INTERNAL_ERR) - .as_bytes() - .to_vec(), - }) - } - pub fn encode(&self) -> Vec { - let data = ethabi::encode( - &[ethabi::Token::Address(ethabi::Address::from_slice(&self.new_owner))], - ); - let mut encoded = Vec::with_capacity(4 + data.len()); - encoded.extend(Self::METHOD_ID); - encoded.extend(data); - encoded - } - pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { - match call.input.get(0..4) { - Some(signature) => Self::METHOD_ID == signature, - None => false, - } - } - } - impl substreams_ethereum::Function for TransferOwnership { - const NAME: &'static str = "transferOwnership"; - fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { - Self::match_call(call) - } - fn decode( - call: &substreams_ethereum::pb::eth::v2::Call, - ) -> Result { - Self::decode(call) - } - fn encode(&self) -> Vec { - self.encode() - } - } } /// Contract's events. #[allow(dead_code, unused_imports, unused_variables)] pub mod events { use super::INTERNAL_ERR; #[derive(Debug, Clone, PartialEq)] - pub struct OwnershipTransferred { - pub previous_owner: Vec, - pub new_owner: Vec, - } - impl OwnershipTransferred { - const TOPIC_ID: [u8; 32] = [ - 139u8, - 224u8, - 7u8, - 156u8, - 83u8, - 22u8, - 89u8, - 20u8, - 19u8, - 68u8, - 205u8, - 31u8, - 208u8, - 164u8, - 242u8, - 132u8, - 25u8, - 73u8, - 127u8, - 151u8, - 34u8, - 163u8, - 218u8, - 175u8, - 227u8, - 180u8, - 24u8, - 111u8, - 107u8, - 100u8, - 87u8, - 224u8, - ]; - pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { - if log.topics.len() != 3usize { - return false; - } - if log.data.len() != 0usize { - return false; - } - return log.topics.get(0).expect("bounds already checked").as_ref() - == Self::TOPIC_ID; - } - pub fn decode( - log: &substreams_ethereum::pb::eth::v2::Log, - ) -> Result { - Ok(Self { - previous_owner: ethabi::decode( - &[ethabi::ParamType::Address], - log.topics[1usize].as_ref(), - ) - .map_err(|e| { - format!( - "unable to decode param 'previous_owner' from topic of type 'address': {:?}", - e - ) - })? - .pop() - .expect(INTERNAL_ERR) - .into_address() - .expect(INTERNAL_ERR) - .as_bytes() - .to_vec(), - new_owner: ethabi::decode( - &[ethabi::ParamType::Address], - log.topics[2usize].as_ref(), - ) - .map_err(|e| { - format!( - "unable to decode param 'new_owner' from topic of type 'address': {:?}", - e - ) - })? - .pop() - .expect(INTERNAL_ERR) - .into_address() - .expect(INTERNAL_ERR) - .as_bytes() - .to_vec(), - }) - } - } - impl substreams_ethereum::Event for OwnershipTransferred { - const NAME: &'static str = "OwnershipTransferred"; - fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { - Self::match_log(log) - } - fn decode(log: &substreams_ethereum::pb::eth::v2::Log) -> Result { - Self::decode(log) - } - } - #[derive(Debug, Clone, PartialEq)] pub struct PoolDeployed { pub asset0: Vec, pub asset1: Vec, diff --git a/substreams/ethereum-eulerswap/src/lib.rs b/substreams/ethereum-eulerswap/src/lib.rs index 27bd15a6f..dfbef5763 100644 --- a/substreams/ethereum-eulerswap/src/lib.rs +++ b/substreams/ethereum-eulerswap/src/lib.rs @@ -1,3 +1,10 @@ +#![allow(clippy::not_unsafe_ptr_arg_deref)] + mod abi; mod modules; -mod pool_factories; +mod pb; + +pub use modules::*; + +mod store_key; +mod traits; diff --git a/substreams/ethereum-eulerswap/src/modules.rs b/substreams/ethereum-eulerswap/src/modules.rs deleted file mode 100644 index 5a87592d6..000000000 --- a/substreams/ethereum-eulerswap/src/modules.rs +++ /dev/null @@ -1,269 +0,0 @@ -//! Template for Protocols with contract factories -//! -//! This template provides foundational maps and store substream modules for indexing a -//! protocol where each component (e.g., pool) is deployed to a separate contract. Each -//! contract is expected to escrow its ERC-20 token balances. -//! -//! If your protocol supports native ETH, you may need to adjust the balance tracking -//! logic in `map_relative_component_balance` to account for native token handling. -//! -//! ## Assumptions -//! - Assumes each pool has a single newly deployed contract linked to it -//! - Assumes pool identifier equals the deployed contract address -//! - Assumes any price or liquidity updated correlates with a pools contract storage update. -//! -//! ## Alternative Module -//! If your protocol uses a vault-like contract to manage balances, or if pools are -//! registered within a singleton contract, refer to the `ethereum-template-singleton` -//! substream for an appropriate alternative. -//! -//! ## Warning -//! This template provides a general framework for indexing a protocol. However, it is -//! likely that you will need to adapt the steps to suit your specific use case. Use the -//! provided code with care and ensure you fully understand each step before proceeding -//! with your implementation. -//! -//! ## Example Use Case -//! For an Uniswap-like protocol where each liquidity pool is deployed as a separate -//! contract, you can use this template to: -//! - Track relative component balances (e.g., ERC-20 token balances in each pool). -//! - Index individual pool contracts as they are created by the factory contract. -//! -//! Adjustments to the template may include: -//! - Handling native ETH balances alongside token balances. -//! - Customizing indexing logic for specific factory contract behavior. -use crate::pool_factories; -use anyhow::Result; -use itertools::Itertools; -use std::collections::HashMap; -use substreams::{pb::substreams::StoreDeltas, prelude::*}; -use substreams_ethereum::{pb::eth, Event}; -use tycho_substreams::{ - abi::erc20, balances::aggregate_balances_changes, contract::extract_contract_changes_builder, - prelude::*, -}; - -/// Find and create all relevant protocol components -/// -/// This method maps over blocks and instantiates ProtocolComponents with a unique ids -/// as well as all necessary metadata for routing and encoding. -#[substreams::handlers::map] -fn map_protocol_components(block: eth::v2::Block) -> Result { - Ok(BlockTransactionProtocolComponents { - tx_components: block - .transactions() - .filter_map(|tx| { - let components = tx - .logs_with_calls() - .filter_map(|(log, call)| { - // TODO: ensure this method is implemented correctly - pool_factories::maybe_create_component(call.call, log, tx) - }) - .collect::>(); - - if !components.is_empty() { - Some(TransactionProtocolComponents { tx: Some(tx.into()), components }) - } else { - None - } - }) - .collect::>(), - }) -} - -/// Stores all protocol components in a store. -/// -/// Stores information about components in a key value store. This is only necessary if -/// you need to access the whole set of components within your indexing logic. -/// -/// Popular use cases are: -/// - Checking if a contract belongs to a component. In this case suggest to use an address as the -/// store key so lookup operations are O(1). -/// - Tallying up relative balances changes to calcualte absolute erc20 token balances per -/// component. -/// -/// Usually you can skip this step if: -/// - You are interested in a static set of components only -/// - Your protocol emits balance change events with absolute values -#[substreams::handlers::store] -fn store_protocol_components( - map_protocol_components: BlockTransactionProtocolComponents, - store: StoreSetRaw, -) { - map_protocol_components - .tx_components - .into_iter() - .for_each(|tx_pc| { - tx_pc - .components - .into_iter() - .for_each(|pc| { - // Assumes that the component id is a hex encoded contract address - let key = pc.id.clone(); - // we store the components tokens - // TODO: proper error handling - let val = serde_sibor::to_bytes(&pc.tokens).unwrap(); - store.set(0, key, &val); - }) - }); -} - -/// Extracts balance changes per component -/// -/// This template function uses ERC20 transfer events to extract balance changes. It -/// assumes that each component is deployed at a dedicated contract address. If a -/// transfer to the component is detected, it's balanced is increased and if a balance -/// from the component is detected its balance is decreased. -/// -/// ## Note: -/// Changes are necessary if your protocol uses native ETH, uses a vault contract or if -/// your component burn or mint tokens without emitting transfer events. -/// -/// You may want to ignore LP tokens if your protocol emits transfer events for these -/// here. -#[substreams::handlers::map] -fn map_relative_component_balance( - block: eth::v2::Block, - store: StoreGetRaw, -) -> Result { - let res = block - .logs() - .filter_map(|log| { - erc20::events::Transfer::match_and_decode(log).map(|transfer| { - let to_addr = hex::encode(transfer.to.as_slice()); - let from_addr = hex::encode(transfer.from.as_slice()); - let tx = log.receipt.transaction; - if let Some(val) = store.get_last(&to_addr) { - let component_tokens: Vec> = serde_sibor::from_bytes(&val).unwrap(); - if component_tokens.contains(&log.address().to_vec()) { - return Some(BalanceDelta { - ord: log.ordinal(), - tx: Some(tx.into()), - token: log.address().to_vec(), - delta: transfer.value.to_signed_bytes_be(), - component_id: to_addr.into_bytes(), - }); - } - } else if let Some(val) = store.get_last(&from_addr) { - let component_tokens: Vec> = serde_sibor::from_bytes(&val).unwrap(); - if component_tokens.contains(&log.address().to_vec()) { - return Some(BalanceDelta { - ord: log.ordinal(), - tx: Some(tx.into()), - token: log.address().to_vec(), - delta: (transfer.value.neg()).to_signed_bytes_be(), - component_id: to_addr.into_bytes(), - }); - } - } - None - }) - }) - .flatten() - .collect::>(); - - Ok(BlockBalanceDeltas { balance_deltas: res }) -} - -/// Aggregates relative balances values into absolute values -/// -/// Aggregate the relative balances in an additive store since tycho-indexer expects -/// absolute balance inputs. -/// -/// ## Note: -/// This method should usually not require any changes. -#[substreams::handlers::store] -pub fn store_balances(deltas: BlockBalanceDeltas, store: StoreAddBigInt) { - tycho_substreams::balances::store_balance_changes(deltas, store); -} - -/// Aggregates protocol components and balance changes by transaction. -/// -/// This is the main method that will aggregate all changes as well as extract all -/// relevant contract storage deltas. -/// -/// ## Note: -/// You may have to change this method if your components have any default dynamic -/// attributes, or if you need any additional static contracts indexed. -#[substreams::handlers::map] -fn map_protocol_changes( - block: eth::v2::Block, - new_components: BlockTransactionProtocolComponents, - components_store: StoreGetRaw, - balance_store: StoreDeltas, - deltas: BlockBalanceDeltas, -) -> Result { - // We merge contract changes by transaction (identified by transaction index) - // making it easy to sort them at the very end. - let mut transaction_changes: HashMap<_, TransactionChangesBuilder> = HashMap::new(); - - // Aggregate newly created components per tx - new_components - .tx_components - .iter() - .for_each(|tx_component| { - // initialise builder if not yet present for this tx - let tx = tx_component.tx.as_ref().unwrap(); - let builder = transaction_changes - .entry(tx.index) - .or_insert_with(|| TransactionChangesBuilder::new(tx)); - - // iterate over individual components created within this tx - tx_component - .components - .iter() - .for_each(|component| { - builder.add_protocol_component(component); - // TODO: In case you require to add any dynamic attributes to the - // component you can do so here: - /* - builder.add_entity_change(&EntityChanges { - component_id: component.id.clone(), - attributes: default_attributes.clone(), - }); - */ - }); - }); - - // Aggregate absolute balances per transaction. - aggregate_balances_changes(balance_store, deltas) - .into_iter() - .for_each(|(_, (tx, balances))| { - let builder = transaction_changes - .entry(tx.index) - .or_insert_with(|| TransactionChangesBuilder::new(&tx)); - balances - .values() - .for_each(|token_bc_map| { - token_bc_map - .values() - .for_each(|bc| builder.add_balance_change(bc)) - }); - }); - - // Extract and insert any storage changes that happened for any of the components. - extract_contract_changes_builder( - &block, - |addr| { - // we assume that the store holds contract addresses as keys and if it - // contains a value, that contract is of relevance. - // TODO: if you have any additional static contracts that need to be indexed, - // please add them here. - components_store - .get_last(hex::encode(addr)) - .is_some() - }, - &mut transaction_changes, - ); - - // Process all `transaction_changes` for final output in the `BlockChanges`, - // sorted by transaction index (the key). - Ok(BlockChanges { - block: Some((&block).into()), - changes: transaction_changes - .drain() - .sorted_unstable_by_key(|(index, _)| *index) - .filter_map(|(_, builder)| builder.build()) - .collect::>(), - }) -} diff --git a/substreams/ethereum-eulerswap/src/modules/1_map_pool_created.rs b/substreams/ethereum-eulerswap/src/modules/1_map_pool_created.rs new file mode 100644 index 000000000..7d5dc16ff --- /dev/null +++ b/substreams/ethereum-eulerswap/src/modules/1_map_pool_created.rs @@ -0,0 +1,106 @@ +use std::str::FromStr; + +use ethabi::ethereum_types::Address; +use serde::Deserialize; +use substreams::prelude::BigInt; +use substreams_ethereum::pb::eth::v2::{self as eth}; +use substreams_helper::{event_handler::EventHandler, hex::Hexable}; + +use crate::abi::eulerswap_factory::events::PoolDeployed; + +use tycho_substreams::prelude::*; + +#[derive(Debug, Deserialize)] +struct Params { + factory_address: String, + protocol_type_name: String, +} + +#[substreams::handlers::map] +pub fn map_pools_created( + params: String, + block: eth::Block, +) -> Result { + let mut new_pools: Vec = vec![]; + + let params: Params = serde_qs::from_str(params.as_str()).expect("Unable to deserialize params"); + + get_pools(&block, &mut new_pools, ¶ms); + + let tycho_block: Block = (&block).into(); + + Ok(BlockChanges { block: Some(tycho_block), changes: new_pools }) +} + +fn get_pools(block: ð::Block, new_pools: &mut Vec, params: &Params) { + // Extract new pools from PoolDeployed events + let mut on_pool_deployed = |event: PoolDeployed, _tx: ð::TransactionTrace, _log: ð::Log| { + let tycho_tx: Transaction = _tx.into(); + + new_pools.push(TransactionChanges { + tx: Some(tycho_tx.clone()), + contract_changes: vec![], + entity_changes: vec![EntityChanges { + component_id: event.pool.to_hex(), + // TODO: check this + attributes: vec![ + Attribute { + name: "reserve0".to_string(), + value: BigInt::from(0).to_signed_bytes_be(), + change: ChangeType::Creation.into(), + }, + Attribute { + name: "reserve1".to_string(), + value: BigInt::from(0).to_signed_bytes_be(), + change: ChangeType::Creation.into(), + }, + ], + }], + component_changes: vec![ProtocolComponent { + id: event.pool.to_hex(), + tokens: vec![event.asset0.clone(), event.asset1.clone()], + contracts: vec![], + static_att: vec![ + Attribute { + name: "feeMultiplier".to_string(), + value: event.fee_multiplier.clone().to_signed_bytes_be(), + change: ChangeType::Creation.into(), + }, + Attribute { + name: "pool_address".to_string(), + value: event.pool.clone(), + change: ChangeType::Creation.into(), + }, + ], + change: i32::from(ChangeType::Creation), + protocol_type: Some(ProtocolType { + name: params.protocol_type_name.to_string(), + financial_type: FinancialType::Swap.into(), + attribute_schema: vec![], + implementation_type: ImplementationType::Custom.into(), + }), + // tx: Some(tycho_tx), + }], + balance_changes: vec![ + // TODO: check this + BalanceChange { + token: event.asset0, + balance: BigInt::from(0).to_signed_bytes_be(), + component_id: event.pool.to_hex().as_bytes().to_vec(), + }, + BalanceChange { + token: event.asset1, + balance: BigInt::from(0).to_signed_bytes_be(), + component_id: event.pool.to_hex().as_bytes().to_vec(), + }, + ], + }) + }; + + let mut eh = EventHandler::new(block); + + eh.filter_by_address(vec![Address::from_str(¶ms.factory_address).unwrap()]); + + eh.on::(&mut on_pool_deployed); + eh.handle_events(); +} diff --git a/substreams/ethereum-eulerswap/src/modules/2_store_pools.rs b/substreams/ethereum-eulerswap/src/modules/2_store_pools.rs new file mode 100644 index 000000000..96da34871 --- /dev/null +++ b/substreams/ethereum-eulerswap/src/modules/2_store_pools.rs @@ -0,0 +1,22 @@ +use substreams::store::{StoreNew, StoreSetIfNotExists, StoreSetIfNotExistsProto}; + +use crate::store_key::StoreKey; +use tycho_substreams::prelude::*; + +#[substreams::handlers::store] +pub fn store_pools( + pools_deployed: BlockChanges, + store: StoreSetIfNotExistsProto, +) { + // Store pools. Required so the next steps can match any event to a known pool by their address + for change in pools_deployed.changes { + for new_protocol_component in change.component_changes { + // Use ordinal 0 because the address should be unique, so ordering doesn't matter. + store.set_if_not_exists( + 0, + StoreKey::Pool.get_unique_pool_key(&new_protocol_component.id), + &new_protocol_component, + ); + } + } +} diff --git a/substreams/ethereum-eulerswap/src/modules/3_map_pool_events.rs b/substreams/ethereum-eulerswap/src/modules/3_map_pool_events.rs new file mode 100644 index 000000000..96eeff8d1 --- /dev/null +++ b/substreams/ethereum-eulerswap/src/modules/3_map_pool_events.rs @@ -0,0 +1,223 @@ +use itertools::Itertools; +use std::collections::HashMap; +use substreams::store::{StoreGet, StoreGetProto}; +use substreams_ethereum::pb::eth::v2::{self as eth}; + +use substreams_helper::{event_handler::EventHandler, hex::Hexable}; + +use crate::{abi::eulerswap::events::Swap, store_key::StoreKey, traits::PoolAddresser}; +use tycho_substreams::prelude::*; + +// Auxiliary struct to serve as a key for the HashMaps. +#[derive(Clone, Hash, Eq, PartialEq)] +struct ComponentKey { + component_id: String, + name: T, +} + +impl ComponentKey { + fn new(component_id: String, name: T) -> Self { + ComponentKey { component_id, name } + } +} + +#[derive(Clone)] +struct PartialChanges { + transaction: Transaction, + entity_changes: HashMap, Attribute>, + balance_changes: HashMap>, BalanceChange>, +} + +impl PartialChanges { + // Consolidate the entity changes into a vector of EntityChanges. Initially, the entity changes + // are in a map to prevent duplicates. For each transaction, we need to have only one final + // state change, per state. Example: + // If we have two sync events for the same pool (in the same tx), we need to have only one final + // state change for the reserves. This will be the last sync event, as it is the final state + // of the pool after the transaction. + fn consolidate_entity_changes(self) -> Vec { + self.entity_changes + .into_iter() + .map(|(key, attribute)| (key.component_id, attribute)) + .into_group_map() + .into_iter() + .map(|(component_id, attributes)| EntityChanges { component_id, attributes }) + .collect() + } +} + +#[substreams::handlers::map] +pub fn map_pool_events( + block: eth::Block, + block_entity_changes: BlockChanges, + pools_store: StoreGetProto, +) -> Result { + // Sync event is sufficient for our use-case. Since it's emitted on every reserve-altering + // function call, we can use it as the only event to update the reserves of a pool. + let mut block_entity_changes = block_entity_changes; + let mut tx_changes: HashMap, PartialChanges> = HashMap::new(); + + handle_sync(&block, &mut tx_changes, &pools_store); + merge_block(&mut tx_changes, &mut block_entity_changes); + + Ok(block_entity_changes) +} + +/// Handle the sync events and update the reserves of the pools. +/// +/// This function is called for each block, and it will handle the sync events for each transaction. +/// On UniswapV2, Sync events are emitted on every reserve-altering function call, so we can use +/// only this event to keep track of the pool state. +/// +/// This function also relies on an intermediate HashMap to store the changes for each transaction. +/// This is necessary because we need to consolidate the changes for each transaction before adding +/// them to the block_entity_changes. This HashMap prevents us from having duplicate changes for the +/// same pool and token. See the PartialChanges struct for more details. +fn handle_sync( + block: ð::Block, + tx_changes: &mut HashMap, PartialChanges>, + store: &StoreGetProto, +) { + let mut on_sync = |event: Swap, _tx: ð::TransactionTrace, _log: ð::Log| { + let pool_address_hex = _log.address.to_hex(); + + let pool = + store.must_get_last(StoreKey::Pool.get_unique_pool_key(pool_address_hex.as_str())); + // Convert reserves to bytes + let reserves_bytes = [event.reserve0, event.reserve1]; + + let tx_change = tx_changes + .entry(_tx.hash.clone()) + .or_insert_with(|| PartialChanges { + transaction: _tx.into(), + entity_changes: HashMap::new(), + balance_changes: HashMap::new(), + }); + + for (i, reserve_bytes) in reserves_bytes.iter().enumerate() { + let attribute_name = format!("reserve{}", i); + // By using a HashMap, we can overwrite the previous value of the reserve attribute if + // it is for the same pool and the same attribute name (reserves). + tx_change.entity_changes.insert( + ComponentKey::new(pool_address_hex.clone(), attribute_name.clone()), + Attribute { + name: attribute_name, + value: reserve_bytes + .clone() + .to_signed_bytes_be(), + change: ChangeType::Update.into(), + }, + ); + } + + // Update balance changes for each token + for (index, token) in pool.tokens.iter().enumerate() { + let balance = &reserves_bytes[index]; + // HashMap also prevents having duplicate balance changes for the same pool and token. + tx_change.balance_changes.insert( + ComponentKey::new(pool_address_hex.clone(), token.clone()), + BalanceChange { + token: token.clone(), + balance: balance.clone().to_signed_bytes_be(), + component_id: pool_address_hex.as_bytes().to_vec(), + }, + ); + } + }; + + let mut eh = EventHandler::new(block); + // Filter the sync events by the pool address, to make sure we don't process events for other + // Protocols that use the same event signature. + eh.filter_by_address(PoolAddresser { store }); + eh.on::(&mut on_sync); + eh.handle_events(); +} + +/// Merge the changes from the sync events with the create_pool events previously mapped on +/// block_entity_changes. +/// +/// Parameters: +/// - tx_changes: HashMap with the changes for each transaction. This is the same HashMap used in +/// handle_sync +/// - block_entity_changes: The BlockChanges struct that will be updated with the changes from the +/// sync events. +/// +/// This HashMap comes pre-filled with the changes for the create_pool events, mapped in +/// 1_map_pool_created. +/// +/// This function is called after the handle_sync function, and it is expected that +/// block_entity_changes will be complete after this function ends. +fn merge_block( + tx_changes: &mut HashMap, PartialChanges>, + block_entity_changes: &mut BlockChanges, +) { + let mut tx_entity_changes_map = HashMap::new(); + + // Add created pools to the tx_changes_map + for change in block_entity_changes + .changes + .clone() + .into_iter() + { + let transaction = change.tx.as_ref().unwrap(); + tx_entity_changes_map + .entry(transaction.hash.clone()) + .and_modify(|c: &mut TransactionChanges| { + c.component_changes + .extend(change.component_changes.clone()); + c.entity_changes + .extend(change.entity_changes.clone()); + }) + .or_insert(change); + } + + // First, iterate through the previously created transactions, extracted from the + // map_pool_created step. If there are sync events for this transaction, add them to the + // block_entity_changes and the corresponding balance changes. + for change in tx_entity_changes_map.values_mut() { + let tx = change + .clone() + .tx + .expect("Transaction not found") + .clone(); + + // If there are sync events for this transaction, add them to the block_entity_changes + if let Some(partial_changes) = tx_changes.remove(&tx.hash) { + change.entity_changes = partial_changes + .clone() + .consolidate_entity_changes(); + change.balance_changes = partial_changes + .balance_changes + .into_values() + .collect(); + } + } + + // If there are any transactions left in the tx_changes, it means that they are transactions + // that changed the state of the pools, but were not included in the block_entity_changes. + // This happens for every regular transaction that does not actually create a pool. By the + // end of this function, we expect block_entity_changes to be up-to-date with the changes + // for all sync and new_pools in the block. + for partial_changes in tx_changes.values() { + tx_entity_changes_map.insert( + partial_changes.transaction.hash.clone(), + TransactionChanges { + tx: Some(partial_changes.transaction.clone()), + contract_changes: vec![], + entity_changes: partial_changes + .clone() + .consolidate_entity_changes(), + balance_changes: partial_changes + .balance_changes + .clone() + .into_values() + .collect(), + component_changes: vec![], + }, + ); + } + + block_entity_changes.changes = tx_entity_changes_map + .into_values() + .collect(); +} diff --git a/substreams/ethereum-eulerswap/src/modules/mod.rs b/substreams/ethereum-eulerswap/src/modules/mod.rs new file mode 100644 index 000000000..c5e286806 --- /dev/null +++ b/substreams/ethereum-eulerswap/src/modules/mod.rs @@ -0,0 +1,10 @@ +pub use map_pool_created::map_pools_created; +pub use map_pool_events::map_pool_events; +pub use store_pools::store_pools; + +#[path = "1_map_pool_created.rs"] +mod map_pool_created; +#[path = "2_store_pools.rs"] +mod store_pools; +#[path = "3_map_pool_events.rs"] +mod map_pool_events; diff --git a/substreams/ethereum-eulerswap/src/pool_factories.rs b/substreams/ethereum-eulerswap/src/pool_factories.rs deleted file mode 100644 index f6bc6f2e7..000000000 --- a/substreams/ethereum-eulerswap/src/pool_factories.rs +++ /dev/null @@ -1,44 +0,0 @@ -use substreams::hex; -use substreams_ethereum::pb::eth::v2::{Call, Log, TransactionTrace}; -use tycho_substreams::models::{ - ChangeType, FinancialType, ImplementationType, ProtocolComponent, ProtocolType, -}; - -/// Potentially constructs a new ProtocolComponent given a call -/// -/// This method is given each individual call within a transaction, the corresponding -/// logs emitted during that call as well as the full transaction trace. -/// -/// If this call creates a component in your protocol please contstruct and return it -/// here. Otherwise, simply return None. -pub fn maybe_create_component( - call: &Call, - _log: &Log, - _tx: &TransactionTrace, -) -> Option { - match *call.address { - // TODO: replace with your logic - hex!("0000000000000000000000000000000000000000") => { - Some(ProtocolComponent { - id: "".to_string(), - tokens: vec![ - // TODO: add the components tokens - ], - contracts: vec![ - // TODO: any contracts required during swapping - ], - static_att: vec![ - // TODO: any additional metadata required, e.g. for swap encoding - ], - change: ChangeType::Creation.into(), - protocol_type: Some(ProtocolType { - name: "template".to_string(), - financial_type: FinancialType::Swap.into(), - attribute_schema: vec![], - implementation_type: ImplementationType::Vm.into(), - }), - }) - } - _ => None, - } -} diff --git a/substreams/ethereum-eulerswap/src/store_key.rs b/substreams/ethereum-eulerswap/src/store_key.rs new file mode 100644 index 000000000..f4ea140b3 --- /dev/null +++ b/substreams/ethereum-eulerswap/src/store_key.rs @@ -0,0 +1,16 @@ +#[derive(Clone)] +pub enum StoreKey { + Pool, +} + +impl StoreKey { + pub fn get_unique_pool_key(&self, key: &str) -> String { + format!("{}:{}", self.unique_id(), key) + } + + pub fn unique_id(&self) -> String { + match self { + StoreKey::Pool => "Pool".to_string(), + } + } +} diff --git a/substreams/ethereum-eulerswap/src/traits.rs b/substreams/ethereum-eulerswap/src/traits.rs new file mode 100644 index 000000000..15fbcbe93 --- /dev/null +++ b/substreams/ethereum-eulerswap/src/traits.rs @@ -0,0 +1,22 @@ +use ethabi::ethereum_types::Address; +use substreams::store::{StoreGet, StoreGetProto}; + +use substreams_helper::{common::HasAddresser, hex::Hexable}; + +use tycho_substreams::prelude::*; + +use crate::store_key::StoreKey; + +pub struct PoolAddresser<'a> { + pub store: &'a StoreGetProto, +} + +impl HasAddresser for PoolAddresser<'_> { + fn has_address(&self, key: Address) -> bool { + let pool = self + .store + .get_last(StoreKey::Pool.get_unique_pool_key(&key.to_hex())); + + pool.is_some() + } +} diff --git a/substreams/ethereum-eulerswap/substreams.yaml b/substreams/ethereum-eulerswap/substreams.yaml index eda7391df..756427b98 100644 --- a/substreams/ethereum-eulerswap/substreams.yaml +++ b/substreams/ethereum-eulerswap/substreams.yaml @@ -5,63 +5,92 @@ package: protobuf: files: - - tycho/evm/v1/vm.proto - tycho/evm/v1/common.proto - - tycho/evm/v1/utils.proto + - tycho/evm/v1/entity.proto + - eulerswap.proto importPaths: - - ../../proto + - ./proto/v1 + - ../../proto/ binaries: default: type: wasm/rust-v1 file: ../target/wasm32-unknown-unknown/release/ethereum_eulerswap.wasm - network: mainnet modules: - - name: map_protocol_components - kind: map - initialBlock: 1 - inputs: - - source: sf.ethereum.type.v2.Block - output: - type: proto:tycho.evm.v1.BlockTransactionProtocolComponents + # - name: map_protocol_components + # kind: map + # initialBlock: 1 + # inputs: + # - source: sf.ethereum.type.v2.Block + # output: + # type: proto:tycho.evm.v1.BlockTransactionProtocolComponents - - name: store_protocol_components - kind: store - initialBlock: 1 - updatePolicy: set - valueType: string - inputs: - - map: map_protocol_components + # - name: store_protocol_components + # kind: store + # initialBlock: 1 + # updatePolicy: set + # valueType: string + # inputs: + # - map: map_protocol_components + + # - name: map_relative_component_balance + # kind: map + # initialBlock: 1 + # inputs: + # - source: sf.ethereum.type.v2.Block + # - store: store_protocol_components + # output: + # type: proto:tycho.evm.v1.BlockBalanceDeltas - - name: map_relative_component_balance + # - name: store_balances + # kind: store + # initialBlock: 1 + # updatePolicy: add + # valueType: bigint + # inputs: + # - map: map_relative_component_balance + + # - name: map_protocol_changes + # kind: map + # initialBlock: 1 + # inputs: + # - source: sf.ethereum.type.v2.Block + # - map: map_protocol_components + # - map: map_relative_component_balance + # - store: store_protocol_components + # - store: store_balances + # mode: deltas + # output: + # type: proto:tycho.evm.v1.BlockChanges + - name: map_pools_created kind: map initialBlock: 1 inputs: + - params: string - source: sf.ethereum.type.v2.Block - - store: store_protocol_components output: - type: proto:tycho.evm.v1.BlockBalanceDeltas + type: proto:tycho.evm.v1.BlockChanges - - name: store_balances + - name: store_pools kind: store initialBlock: 1 - updatePolicy: add - valueType: bigint + updatePolicy: set_if_not_exists + valueType: proto:tycho.evm.eulerswap.v1.Pool inputs: - - map: map_relative_component_balance + - map: map_pools_created - - name: map_protocol_changes + - name: map_pool_events kind: map initialBlock: 1 inputs: - source: sf.ethereum.type.v2.Block - - map: map_protocol_components - - map: map_relative_component_balance - - store: store_protocol_components - - store: store_balances - mode: deltas + - map: map_pools_created + - store: store_pools output: type: proto:tycho.evm.v1.BlockChanges + +params: + map_pools_created: factory_address=5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f&protocol_type_name=eulerswap_v1_pool \ No newline at end of file From 38e5b7e2bb65943a4c19a1b6cb5b77200b503ab1 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Thu, 20 Feb 2025 12:29:22 +0100 Subject: [PATCH 09/11] update --- evm/src/euler-swap/EulerSwapAdapter.sol | 170 ++++++++++++++---------- evm/test/EulerSwapAdapter.t.sol | 8 +- 2 files changed, 107 insertions(+), 71 deletions(-) diff --git a/evm/src/euler-swap/EulerSwapAdapter.sol b/evm/src/euler-swap/EulerSwapAdapter.sol index 897e1d5ac..fd989277a 100644 --- a/evm/src/euler-swap/EulerSwapAdapter.sol +++ b/evm/src/euler-swap/EulerSwapAdapter.sol @@ -10,10 +10,12 @@ import { contract EulerSwapAdapter is ISwapAdapter { using SafeERC20 for IERC20; - IMaglevEulerSwapFactory immutable factory; + IEulerSwapFactory immutable factory; + IEulerSwapPeriphery immutable periphery; - constructor(address factory_) { - factory = IMaglevEulerSwapFactory(factory_); + constructor(address factory_, address periphery_) { + factory = IEulerSwapFactory(factory_); + periphery = IEulerSwapPeriphery(periphery_); } /// @inheritdoc ISwapAdapter @@ -25,7 +27,7 @@ contract EulerSwapAdapter is ISwapAdapter { ) external view override returns (Fraction[] memory prices) { prices = new Fraction[](specifiedAmounts.length); - IMaglevEulerSwap pool = IMaglevEulerSwap(address(bytes20(poolId))); + IEulerSwap pool = IEulerSwap(address(bytes20(poolId))); for (uint256 i = 0; i < specifiedAmounts.length; i++) { prices[i] = quoteExactInput(pool, sellToken, buyToken, specifiedAmounts[i]); @@ -40,7 +42,7 @@ contract EulerSwapAdapter is ISwapAdapter { OrderSide side, uint256 specifiedAmount ) external returns (Trade memory trade) { - IMaglevEulerSwap pool = IMaglevEulerSwap(address(bytes20(poolId))); + IEulerSwap pool = IEulerSwap(address(bytes20(poolId))); bool isAmountOutAsset0 = buyToken == pool.asset0(); uint256 amountIn; @@ -84,23 +86,21 @@ contract EulerSwapAdapter is ISwapAdapter { returns (uint256[] memory limits) { limits = new uint256[](2); - - IMaglevEulerSwap pool = IMaglevEulerSwap(address(bytes20(poolId))); + IEulerSwap pool = IEulerSwap(address(bytes20(poolId))); address swapAccount = pool.myAccount(); - (address token0, address token1) = (sellToken < buyToken) - ? (sellToken, buyToken) - : (buyToken, sellToken); - if (token0 == buyToken) { - // max amount for buyToken - uint256 maxWithdraw = vaultBalance(pool.vault0(), swapAccount); + IEVC evc = IEVC(IEVault(pool.vault0()).EVC()); + if (!evc.isAccountOperatorAuthorized(swapAccount, address(pool))) { + return limits; + } - limits[1] = maxWithdraw; + (uint256 r0, uint256 r1,) = pool.getReserves(); + if (sellToken < buyToken) { + limits[0] = r0; + limits[1] = r1; } else { - // max amount for buyToken - uint256 maxWithdraw = vaultBalance(pool.vault1(), swapAccount); - - limits[1] = maxWithdraw; + limits[0] = r1; + limits[1] = r0; } } @@ -125,7 +125,7 @@ contract EulerSwapAdapter is ISwapAdapter { returns (address[] memory tokens) { tokens = new address[](2); - IMaglevEulerSwap pool = IMaglevEulerSwap(address(bytes20(poolId))); + IEulerSwap pool = IEulerSwap(address(bytes20(poolId))); tokens[0] = address(pool.asset0()); tokens[1] = address(pool.asset1()); } @@ -149,24 +149,30 @@ contract EulerSwapAdapter is ISwapAdapter { /// @notice Calculates pool prices for specified amounts function quoteExactInput( - IMaglevEulerSwap pool, + IEulerSwap pool, address tokenIn, address tokenOut, uint256 amountIn ) internal view returns (Fraction memory calculatedPrice) { calculatedPrice = Fraction( - pool.quoteExactInput(tokenIn, tokenOut, amountIn), amountIn + periphery.quoteExactInput( + address(pool), tokenIn, tokenOut, amountIn + ), + amountIn ); } function quoteExactOutput( - IMaglevEulerSwap pool, + IEulerSwap pool, address tokenIn, address tokenOut, uint256 amountOut ) internal view returns (Fraction memory calculatedPrice) { calculatedPrice = Fraction( - amountOut, pool.quoteExactOutput(tokenIn, tokenOut, amountOut) + amountOut, + periphery.quoteExactOutput( + address(pool), tokenIn, tokenOut, amountOut + ) ); } @@ -183,33 +189,26 @@ contract EulerSwapAdapter is ISwapAdapter { } } -interface IMaglevEulerSwapFactory { - event PoolDeployed( - address indexed asset0, - address indexed asset1, - uint256 indexed feeMultiplier, - address pool - ); - - function deployPool( - address vault0, - address vault1, - address holder, - uint112 debtLimit0, - uint112 debtLimit1, - uint256 fee, - uint256 priceX, - uint256 priceY, - uint256 concentrationX, - uint256 concentrationY - ) external returns (address); - - function evc() external view returns (address); - function allPools(uint256 index) external view returns (address); - function getPool(address assetA, address assetB, uint256 fee) +interface IEulerSwapFactory { + struct DeployParams { + address vault0; + address vault1; + address swapAccount; + uint256 fee; + uint256 priceX; + uint256 priceY; + uint256 concentrationX; + uint256 concentrationY; + uint112 debtLimit0; + uint112 debtLimit1; + } + + function deployPool(DeployParams memory params) external - view returns (address); + + function allPools(uint256 index) external view returns (address); + function getPool(bytes32 poolKey) external view returns (address); function allPoolsLength() external view returns (uint256); function getAllPoolsListSlice(uint256 start, uint256 end) external @@ -217,47 +216,82 @@ interface IMaglevEulerSwapFactory { returns (address[] memory); } -interface IMaglevEulerSwap { - // IMaglevBase - function configure() external; +interface IEulerSwap { + struct Params { + address vault0; + address vault1; + address myAccount; + uint112 debtLimit0; + uint112 debtLimit1; + uint256 fee; + } + + struct CurveParams { + uint256 priceX; + uint256 priceY; + uint256 concentrationX; + uint256 concentrationY; + } + function swap( uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data ) external; - function quoteExactInput( - address tokenIn, - address tokenOut, - uint256 amountIn - ) external view returns (uint256); - function quoteExactOutput( - address tokenIn, - address tokenOut, - uint256 amountOut - ) external view returns (uint256); + function activate() external; + function verify(uint256 newReserve0, uint256 newReserve1) + external + view + returns (bool); + function curve() external view returns (bytes32); function vault0() external view returns (address); function vault1() external view returns (address); function asset0() external view returns (address); function asset1() external view returns (address); function myAccount() external view returns (address); - function debtLimit0() external view returns (uint112); - function debtLimit1() external view returns (uint112); + function initialReserve0() external view returns (uint112); + function initialReserve1() external view returns (uint112); function feeMultiplier() external view returns (uint256); - function getReserves() external view returns (uint112, uint112, uint32); - - // IMaglevEulerSwap + function getReserves() + external + view + returns (uint112 reserve0, uint112 reserve1, uint32 status); function priceX() external view returns (uint256); function priceY() external view returns (uint256); function concentrationX() external view returns (uint256); function concentrationY() external view returns (uint256); - function initialReserve0() external view returns (uint112); - function initialReserve1() external view returns (uint112); +} + +interface IEulerSwapPeriphery { + /// @notice How much `tokenOut` can I get for `amountIn` of `tokenIn`? + function quoteExactInput( + address eulerSwap, + address tokenIn, + address tokenOut, + uint256 amountIn + ) external view returns (uint256); + + /// @notice How much `tokenIn` do I need to get `amountOut` of `tokenOut`? + function quoteExactOutput( + address eulerSwap, + address tokenIn, + address tokenOut, + uint256 amountOut + ) external view returns (uint256); } interface IEVault { + function EVC() external view returns (address); function balanceOf(address account) external view returns (uint256); function convertToAssets(uint256 shares) external view returns (uint256); function maxWithdraw(address owner) external view returns (uint256); } + +interface IEVC { + function isAccountOperatorAuthorized(address account, address operator) + external + view + returns (bool authorized); +} diff --git a/evm/test/EulerSwapAdapter.t.sol b/evm/test/EulerSwapAdapter.t.sol index 671be1a08..30342b80e 100644 --- a/evm/test/EulerSwapAdapter.t.sol +++ b/evm/test/EulerSwapAdapter.t.sol @@ -15,8 +15,10 @@ contract EulerSwapAdapterTest is AdapterTest { function setUp() public { uint256 forkBlock = 21845705; vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); - adapter = - new EulerSwapAdapter(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f); + adapter = new EulerSwapAdapter( + 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f, + 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f + ); vm.label(address(adapter), "EulerSwapAdapter"); vm.label(WETH, "WETH"); @@ -39,4 +41,4 @@ contract EulerSwapAdapterTest is AdapterTest { // uint256[] memory limits = adapter.getLimits(pair, USDC, WETH); // assertEq(limits.length, 2); // } -} \ No newline at end of file +} From de2327e1ef58cc406ef7ef5ee054c65bb0d35752 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Thu, 20 Feb 2025 13:36:15 +0100 Subject: [PATCH 10/11] update tycho-substreams version to match uni-v2 --- substreams/Cargo.lock | 2 +- substreams/ethereum-eulerswap/Cargo.toml | 2 +- substreams/ethereum-eulerswap/src/modules/1_map_pool_created.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/substreams/Cargo.lock b/substreams/Cargo.lock index 03fb00555..cfc907c09 100644 --- a/substreams/Cargo.lock +++ b/substreams/Cargo.lock @@ -277,7 +277,7 @@ dependencies = [ "substreams", "substreams-ethereum", "substreams-helper 0.0.2 (git+https://github.com/propeller-heads/tycho-protocol-sdk.git?rev=b8aeaa3)", - "tycho-substreams 0.2.0 (git+https://github.com/propeller-heads/tycho-protocol-sdk.git?rev=3c08359)", + "tycho-substreams 0.2.0 (git+https://github.com/propeller-heads/tycho-protocol-sdk.git?rev=b8aeaa3)", ] [[package]] diff --git a/substreams/ethereum-eulerswap/Cargo.toml b/substreams/ethereum-eulerswap/Cargo.toml index cab5bf9f7..54a72da86 100644 --- a/substreams/ethereum-eulerswap/Cargo.toml +++ b/substreams/ethereum-eulerswap/Cargo.toml @@ -11,7 +11,7 @@ crate-type = ["cdylib"] substreams = "0.5.22" substreams-ethereum = "0.9.9" prost = "0.11" -tycho-substreams = { git = "https://github.com/propeller-heads/tycho-protocol-sdk.git", rev = "3c08359" } +tycho-substreams = { git = "https://github.com/propeller-heads/tycho-protocol-sdk.git", rev = "b8aeaa3" } anyhow = "1.0.95" ethabi = "18.0.0" num-bigint = "0.4.6" diff --git a/substreams/ethereum-eulerswap/src/modules/1_map_pool_created.rs b/substreams/ethereum-eulerswap/src/modules/1_map_pool_created.rs index 7d5dc16ff..c100e2ab0 100644 --- a/substreams/ethereum-eulerswap/src/modules/1_map_pool_created.rs +++ b/substreams/ethereum-eulerswap/src/modules/1_map_pool_created.rs @@ -79,7 +79,7 @@ fn get_pools(block: ð::Block, new_pools: &mut Vec, params attribute_schema: vec![], implementation_type: ImplementationType::Custom.into(), }), - // tx: Some(tycho_tx), + tx: Some(tycho_tx), }], balance_changes: vec![ // TODO: check this From 12fe28327d41f6946aef5404f06e34ef47d46c53 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Mon, 24 Feb 2025 22:30:02 +0800 Subject: [PATCH 11/11] update --- evm/src/euler-swap/EulerSwapAdapter.sol | 2 -- .../src/modules/1_map_pool_created.rs | 27 +++++++++++++- .../src/modules/3_map_pool_events.rs | 35 ++++++++++--------- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/evm/src/euler-swap/EulerSwapAdapter.sol b/evm/src/euler-swap/EulerSwapAdapter.sol index fd989277a..eabf2f20e 100644 --- a/evm/src/euler-swap/EulerSwapAdapter.sol +++ b/evm/src/euler-swap/EulerSwapAdapter.sol @@ -184,8 +184,6 @@ contract EulerSwapAdapter is ISwapAdapter { uint256 shares = IEVault(vault).balanceOf(swapAccount); return shares == 0 ? 0 : IEVault(vault).convertToAssets(shares); - - // return IEVault(vault).maxWithdraw(swapAccount); } } diff --git a/substreams/ethereum-eulerswap/src/modules/1_map_pool_created.rs b/substreams/ethereum-eulerswap/src/modules/1_map_pool_created.rs index c100e2ab0..d31d01955 100644 --- a/substreams/ethereum-eulerswap/src/modules/1_map_pool_created.rs +++ b/substreams/ethereum-eulerswap/src/modules/1_map_pool_created.rs @@ -55,7 +55,7 @@ fn get_pools(block: ð::Block, new_pools: &mut Vec, params change: ChangeType::Creation.into(), }, ], - }], + }], component_changes: vec![ProtocolComponent { id: event.pool.to_hex(), tokens: vec![event.asset0.clone(), event.asset1.clone()], @@ -66,6 +66,31 @@ fn get_pools(block: ð::Block, new_pools: &mut Vec, params value: event.fee_multiplier.clone().to_signed_bytes_be(), change: ChangeType::Creation.into(), }, + Attribute { + name: "price_x".to_string(), + value: event.price_x.clone().to_signed_bytes_be(), + change: ChangeType::Creation.into(), + }, + Attribute { + name: "price_y".to_string(), + value: event.price_y.clone().to_signed_bytes_be(), + change: ChangeType::Creation.into(), + }, + Attribute { + name: "concentration_x".to_string(), + value: event.concentration_x.clone().to_signed_bytes_be(), + change: ChangeType::Creation.into(), + }, + Attribute { + name: "concentration_y".to_string(), + value: event.concentration_y.clone().to_signed_bytes_be(), + change: ChangeType::Creation.into(), + }, + Attribute { + name: "swap_account".to_string(), + value: event.swap_account.clone(), + change: ChangeType::Creation.into(), + }, Attribute { name: "pool_address".to_string(), value: event.pool.clone(), diff --git a/substreams/ethereum-eulerswap/src/modules/3_map_pool_events.rs b/substreams/ethereum-eulerswap/src/modules/3_map_pool_events.rs index 96eeff8d1..56f121fa6 100644 --- a/substreams/ethereum-eulerswap/src/modules/3_map_pool_events.rs +++ b/substreams/ethereum-eulerswap/src/modules/3_map_pool_events.rs @@ -32,8 +32,8 @@ impl PartialChanges { // Consolidate the entity changes into a vector of EntityChanges. Initially, the entity changes // are in a map to prevent duplicates. For each transaction, we need to have only one final // state change, per state. Example: - // If we have two sync events for the same pool (in the same tx), we need to have only one final - // state change for the reserves. This will be the last sync event, as it is the final state + // If we have two swap events for the same pool (in the same tx), we need to have only one final + // state change for the reserves. This will be the last swap event, as it is the final state // of the pool after the transaction. fn consolidate_entity_changes(self) -> Vec { self.entity_changes @@ -52,28 +52,28 @@ pub fn map_pool_events( block_entity_changes: BlockChanges, pools_store: StoreGetProto, ) -> Result { - // Sync event is sufficient for our use-case. Since it's emitted on every reserve-altering + // Swap event is sufficient for our use-case. Since it's emitted on every reserve-altering // function call, we can use it as the only event to update the reserves of a pool. let mut block_entity_changes = block_entity_changes; let mut tx_changes: HashMap, PartialChanges> = HashMap::new(); - handle_sync(&block, &mut tx_changes, &pools_store); + handle_swap(&block, &mut tx_changes, &pools_store); merge_block(&mut tx_changes, &mut block_entity_changes); Ok(block_entity_changes) } -/// Handle the sync events and update the reserves of the pools. +/// Handle the swap events and update the reserves of the pools. /// -/// This function is called for each block, and it will handle the sync events for each transaction. -/// On UniswapV2, Sync events are emitted on every reserve-altering function call, so we can use +/// This function is called for each block, and it will handle the swap events for each transaction. +/// On EulerSwap, Swap events are emitted on every reserve-altering function call, so we can use /// only this event to keep track of the pool state. /// /// This function also relies on an intermediate HashMap to store the changes for each transaction. /// This is necessary because we need to consolidate the changes for each transaction before adding /// them to the block_entity_changes. This HashMap prevents us from having duplicate changes for the /// same pool and token. See the PartialChanges struct for more details. -fn handle_sync( +fn handle_swap( block: ð::Block, tx_changes: &mut HashMap, PartialChanges>, store: &StoreGetProto, @@ -110,6 +110,7 @@ fn handle_sync( ); } + // TODO: check this // Update balance changes for each token for (index, token) in pool.tokens.iter().enumerate() { let balance = &reserves_bytes[index]; @@ -126,26 +127,27 @@ fn handle_sync( }; let mut eh = EventHandler::new(block); - // Filter the sync events by the pool address, to make sure we don't process events for other + // Filter the swap events by the pool address, to make sure we don't process events for other // Protocols that use the same event signature. eh.filter_by_address(PoolAddresser { store }); eh.on::(&mut on_sync); eh.handle_events(); } -/// Merge the changes from the sync events with the create_pool events previously mapped on +// TODO: rename create_pool +/// Merge the changes from the swap events with the create_pool events previously mapped on /// block_entity_changes. /// /// Parameters: /// - tx_changes: HashMap with the changes for each transaction. This is the same HashMap used in -/// handle_sync +/// handle_swap /// - block_entity_changes: The BlockChanges struct that will be updated with the changes from the -/// sync events. +/// swap events. /// /// This HashMap comes pre-filled with the changes for the create_pool events, mapped in /// 1_map_pool_created. /// -/// This function is called after the handle_sync function, and it is expected that +/// This function is called after the handle_swap function, and it is expected that /// block_entity_changes will be complete after this function ends. fn merge_block( tx_changes: &mut HashMap, PartialChanges>, @@ -172,7 +174,7 @@ fn merge_block( } // First, iterate through the previously created transactions, extracted from the - // map_pool_created step. If there are sync events for this transaction, add them to the + // map_pool_created step. If there are swap events for this transaction, add them to the // block_entity_changes and the corresponding balance changes. for change in tx_entity_changes_map.values_mut() { let tx = change @@ -181,7 +183,7 @@ fn merge_block( .expect("Transaction not found") .clone(); - // If there are sync events for this transaction, add them to the block_entity_changes + // If there are swap events for this transaction, add them to the block_entity_changes if let Some(partial_changes) = tx_changes.remove(&tx.hash) { change.entity_changes = partial_changes .clone() @@ -193,11 +195,12 @@ fn merge_block( } } + // TODO: rename new_pools // If there are any transactions left in the tx_changes, it means that they are transactions // that changed the state of the pools, but were not included in the block_entity_changes. // This happens for every regular transaction that does not actually create a pool. By the // end of this function, we expect block_entity_changes to be up-to-date with the changes - // for all sync and new_pools in the block. + // for all swap and new_pools in the block. for partial_changes in tx_changes.values() { tx_entity_changes_map.insert( partial_changes.transaction.hash.clone(),