From d3fa2103a182da345a2ce046c18e5cee4c2633bd Mon Sep 17 00:00:00 2001 From: Florian Pellissier <111426680+flopell@users.noreply.github.com> Date: Wed, 21 Aug 2024 15:42:34 +0200 Subject: [PATCH] Merge branch 'docs' into zz/update-docs-summary --- docs/.gitbook/assets/image.png | Bin 0 -> 52225 bytes docs/README.md | 5 +- docs/SUMMARY.md | 17 +-- docs/indexing/getting-started.md | 204 ++++++++++++++++++++++++++++ docs/indexing/overview.md | 53 ++++++++ docs/logic/vm-integration/README.md | 56 ++++---- 6 files changed, 300 insertions(+), 35 deletions(-) create mode 100644 docs/.gitbook/assets/image.png create mode 100644 docs/indexing/getting-started.md create mode 100644 docs/indexing/overview.md diff --git a/docs/.gitbook/assets/image.png b/docs/.gitbook/assets/image.png new file mode 100644 index 0000000000000000000000000000000000000000..64708a0cdc84081fd54c66ebfa11412cfe8f2036 GIT binary patch literal 52225 zcmeFZRa{iv`v(d*C@CQzAtBvJN_Te;jYziwL)R!J(%s!DT@s3PH-dCG(#_d}@9Xb> zbuP}ieJ|$2uxIa;&$HIEzTfY&KPf9pp`#F@z`?!ofYF0X~;V2*8z(_g$I5 zA9zPqDN(qx!53S=KjNlZGG+=2aP+`;Bslm$OE?hh7T`k&e1JzY;Ncztf8k-*G7$d# z^$|_RqrcxTVKVi>SN8@1`PuRnWNZI

&ip9+3uoz8=;=qulqnt;} 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 map_components; + store_components[store: store_components]; + map_components --> store_components; + map_relative_balances[map: map_relative_balances]; + sf.ethereum.type.v2.Block[source: sf.ethereum.type.v2.Block] --> map_relative_balances; + store_components --> map_relative_balances; + store_balances[store: store_balances]; + map_relative_balances --> store_balances; + map_changes[map: map_changes]; + sf.ethereum.type.v2.Block[source: sf.ethereum.type.v2.Block] --> map_changes; + map_components --> map_changes; + map_relative_balances --> map_changes; + store_components --> map_changes; + store_balances -- deltas --> map_changes; +``` + +### Tracking Components + +Usually the first step consists of detecting the creation of new components and storing their contract addresses in a store so they can be properly tracked further downstream. + +Later we'll have to emit balance and state changes based on the set of currently tracked components. + +{% hint style="info" %} +Emitting state changes of components that have not been previously announced is considered an error. +{% endhint %} + +Usually you want to start by implementing a map module that emits any newly created components. These newly created components are detected by inspecting the `sf.ethereum.type.v2.Block` model. + +The output message should then contain as much information about the component available at that time, as well as the transaction that created the protocol component. + +The recommended action here is to implement a `factory.rs` module that will help with detecting newly deployed components. Then use that module within a map handler to detect any newly emitted protocol components. The recommended output model for this first handler is `BlockTransactionProtocolComponents`: + +```protobuf +// A message containing protocol components that were created by a single tx. +message TransactionProtocolComponents { + Transaction tx = 1; + repeated ProtocolComponent components = 2; +} + +// All protocol components that were created within a block with their corresponding tx. +message BlockTransactionProtocolComponents { + repeated TransactionProtocolComponents tx_components = 1; +} +``` + +Note that a single transaction may emit multiple newly created components. In this case it is expected that the `TransactionProtocolComponents.components` contains multiple `ProtocolComponents`. + +Once emitted, the protocol components should be stored in a Store since we will later have to use this store to decide whether a contract is interesting to us or not. + +### Tracking Absolute Balances + +Tracking balances can be tricky since often balance information is only available in relative values. + +This means the relative values have to be aggregated by component and token to arrive at an absolute value. Additionally, throughout this aggregation we need to track the balance changes per transaction within a block. + +Since this is challenging the following approach is recommended: + +#### 1. Index relative balance changes + +To accurately process a block and report the respective balance changes, implement a handler that utilises the `BlockBalanceDeltas` struct. It is crucial to ensure that each `BalanceDelta` within a component token pair is assigned a strictly increasing ordinal. This specificity is key to maintaining the integrity of aggregated values at the transaction level. Incorrect ordinal sequencing could lead to inaccurate balance reporting. + +Below is an example of an interface for a handler. This handler interfaces with a store that employs an integer as an indicator to denote whether a specific address (identified by the keys) is a component. + +```rust +#[substreams::handlers::map] +pub fn map_relative_balances( + block: eth::v2::Block, + components_store: StoreGetInt64, +) -> Result { + todo!() +} +``` + +Our Substreams SDK provides the `tycho_substream::balances::extract_balance_deltas_from_tx` function that extracts all relevant `BalanceDelta` from ERC20 `Transfer` events for a given transaction (see Curve implementation). + +#### 2. Aggregate balances with an additive store + +To aggregate `BlockBalanceDeltas` messages into absolute values efficiently while maintaining transaction level granularity, we can leverage the additive `StoreAddBigInt` type with a store module. + +The `tycho_substream::balances::store_balance_changes` helper function is available for this purpose, streamlining the implementation significantly. + +Thus, the typical use case can be addressed with the provided snippet: + +```rust +#[substreams::handlers::store] +pub fn store_balances(deltas: BlockBalanceDeltas, store: StoreAddBigInt) { + tycho_substreams::balances::store_balance_changes(deltas, store); +} +``` + +#### 3. Combine absolute values with component and address + +Last but not least, we have to associate the absolute balances with their respective transaction, component and token. + +To simplify the final step of aggregating balance changes, utilise the `tycho_substream::balances::aggregate_balances_changes` helper function. Detailed instructions are available in the function's docstring. Essentially, it outputs aggregated `BalanceChange` structs per transaction. These aggregates can then be seamlessly integrated into `map_protocol_changes` for retrieving absolute balance changes associated with each transaction. + +Below is a snip on how it is usually used: + +```rust +#[substreams::handlers::map] +pub fn map_protocol_changes( + block: eth::v2::Block, + grouped_components: BlockTransactionProtocolComponents, + deltas: BlockBalanceDeltas, + components_store: StoreGetInt64, + balance_store: StoreDeltas, +) -> Result { + let mut transaction_contract_changes: HashMap<_, TransactionChanges> = HashMap::new(); + + aggregate_balances_changes(balance_store, deltas) + .into_iter() + .for_each(|(_, (tx, balances))| { + transaction_contract_changes + .entry(tx.index) + .or_insert_with(|| TransactionChanges::new(&tx)) + .balance_changes + .extend(balances.into_values()); + }); +} +``` + +### Tracking State Changes + +In vm implementations, it's crucial to accurately identify and extract all relevant contract changes. Typically, there's a one-to-one mapping between contracts and components which allows us to follow the convention of utilising the hex-encoded address as the component's ID. + +To facilitate the extraction of pertinent changes from the expanded block model, we recommend using the `tycho_substreams::contract::extract_contract_changes` helper function. This function significantly simplifies the process. + +Below, we illustrate how to leverage a component store to define a predicate. This predicate serves to pinpoint the contract addresses that are of particular interest: + +```rust +use tycho_substreams::contract::extract_contract_changes; + +let mut transaction_contract_changes: HashMap<_, TransactionChanges> = HashMap::new(); + +extract_contract_changes( + &block, + |addr| { + components_store + .get_last(format!("pool:{0}", hex::encode(addr))) + .is_some() + }, + &mut transaction_contract_changes, +); +``` diff --git a/docs/indexing/overview.md b/docs/indexing/overview.md new file mode 100644 index 0000000..8d8175a --- /dev/null +++ b/docs/indexing/overview.md @@ -0,0 +1,53 @@ +# Overview + +This page gives an overview over the data model required to ingest protocol state into the PropellerHeads solver. + +To integrate a protocol PropellerHeads rely on either native or vm logic. Most integration will likely choose to use the VM, as this is usually less effort, the guide will focus mostly on providing state for vm integrations. + +Native integration should operate following exactly the same pattern, just that they should emit changed attributes instead of changes contract storage slots. + +### Understanding the Data Model + +PropellerHeads ingest all data versioned by block and transaction. This helps maintain a low latency feed and deal correctly with chains that can experience reverts. + +This means each state change that is communicated must be communicated with its respective transaction that caused the change. + +Next, for each emitted transactions that carries state changes, the corresponding block must be provided as well. + +So basically when processing a block we need to emit the block itself, all transactions that introduced protocol state changes and last but not least the state changes themselves, associated to their corresponding transaction. + +**The data model that encodes changes, transaction and blocks in messages, can be found** [**here**](https://github.com/propeller-heads/propeller-protocol-lib/tree/main/proto/tycho/evm/v1)**.** + +#### Models + +The models below are used for communication between Substreams and Tycho indexer, as well as between Substreams modules. + +Our indexer expects to receive a `BlockChanges` output from your Substreams package. + +{% @github-files/github-code-block url="https://github.com/propeller-heads/propeller-protocol-lib/blob/main/proto/tycho/evm/v1/" %} + +Please be aware that changes need to be aggregated on the transaction level, it is considered an error to emit `BlockChanges` with duplicated transactions present in the `changes` attributes. + +#### Integer Byte encoding + +Many of the types above are variable length bytes. This allows for flexibility across blockchains but require agreeing on an informal interface, so later applications know how to interpret these bytes. + +**Integers:** especially integers used to communicate balances, should always be encoded as unsigned big-endian integer. This is simply because balances serve multiple purposes within the system and need to be decoded in multiple location of the messages journey. + +**Strings**: If you need to store strings, please use utf-8 encoding to store them as bytes. + +**Attributes:** the value encoding for attributes is variable. It depends on the use case. Since the attributes are highly dynamic they are only used by the corresponding logic components, so the encoding can be tailored to the logic implementation: E.g. since Rust uses little endian one may choose to use little endian encoding for integers if the native logic module is written in Rust. + +#### Special attribute names + +Certain attribute names are reserved exclusively for specific purposes in our simulation process. Please use them only for their intended functions. See the [list of reserved attributes](./reserved-attributes.md) + +### Changes of interest + +PropellerHeads integrations should at least communicate the following changes: + +- Any changes to the protocol state, for VM integrations that usually means contract storage changes of all contracts whose state may be accessed during a swap operation. +- Any newly added protocol component such as a pool, pair, market, etc. Basically anything that signifies that a new operation can be executed now using the protocol. +- ERC20 Balances, whenever the balances of one contracts involved with the protocol change, this change should be communicated in terms of absolute balances. + +Please see the getting started page to see how to actually implement an integration. diff --git a/docs/logic/vm-integration/README.md b/docs/logic/vm-integration/README.md index 90a19d9..f6083cd 100644 --- a/docs/logic/vm-integration/README.md +++ b/docs/logic/vm-integration/README.md @@ -8,28 +8,31 @@ To create a VM integration, it is required to provide a manifest file as well as Following exchanges have been integrated using VM approach: -- Uniswap V2 (see `/evm/src/uniswap-v2`) -- Balancer V2 (see `/evm/src/balancer-v2`) +* Uniswap V2 (see `/evm/src/uniswap-v2`) +* Balancer V2 (see `/evm/src/balancer-v2`) ## Step by step ### Prerequisites -1. Install [Foundry](https://book.getfoundry.sh/getting-started/installation#using-foundryup), start by downloading and installing the Foundry installer: +1. Install [Foundry](https://book.getfoundry.sh/getting-started/installation#using-foundryup), start by downloading and installing the Foundry installer: + ```bash curl -L https://foundry.paradigm.xyz | bash ``` - then start a new terminal session and run - ```bash - foundryup - ``` -1. Start by making a local copy of the Propeller Protocol Lib repository: + then start a new terminal session and run + + ```bash + foundryup + ``` +2. Start by making a local copy of the Propeller Protocol Lib repository: + ```bash git clone https://github.com/propeller-heads/propeller-protocol-lib ``` +3. Install forge dependencies: -1. Install forge dependencies: ```bash cd ./propeller-protocol-lib/evm/ forge install @@ -37,27 +40,32 @@ Following exchanges have been integrated using VM approach: ### Understanding the ISwapAdapter -Read the the documentation of the [Ethereum Solidity interface](ethereum-solidity.md). It describes the functions that need to be implemented as well as the manifest file. -Additionally read through the docstring of the [ISwapAdapter.sol](../../../evm/src/interfaces/ISwapAdapter.sol) interface and the [ISwapAdapterTypes.sol](../../../evm/src/interfaces/ISwapAdapterTypes.sol) interface which defines the data types and errors used by the adapter interface. -You can also generate the documentation locally and the look at the generated documentation in the `./docs` folder: - ```bash - cd ./evm/ - forge doc - ``` +Read the the documentation of the [Ethereum Solidity interface](ethereum-solidity.md). It describes the functions that need to be implemented as well as the manifest file. Additionally read through the docstring of the [ISwapAdapter.sol](https://github.com/propeller-heads/propeller-venue-lib/blob/main/evm/src/interfaces/ISwapAdapter.sol) interface and the [ISwapAdapterTypes.sol](https://github.com/propeller-heads/propeller-venue-lib/blob/main/evm/src/interfaces/ISwapAdapterTypes.sol) interface which defines the data types and errors used by the adapter interface. You can also generate the documentation locally and the look at the generated documentation in the `./docs` folder: + +```bash +cd ./evm/ +forge doc +``` + ### Implementing the ISwapAdapter interface + Your integration should be in a separate directory in the `evm/src` folder. Start by cloning the template directory: - ```bash - cp ./evm/src/template ./evm/src/ - ``` -Implement the `ISwapAdapter` interface in the `./evm/src/.sol` file. There are two reference implementations, one for Uniswap V2 and the other for Balancer V2. + +```bash +cp ./evm/src/template ./evm/src/ +``` + +Implement the `ISwapAdapter` interface in the `./evm/src/.sol` file. There are two reference implementations, one for Uniswap V2 and the other for Balancer V2. ### Testing your implementation + Clone the `evm/test/TemplateSwapAdapter.t.sol` file and rename it to `.t.sol`. Implement the tests for your adapter, make sure all implemented functions are tested and working correctly. Look at the examples of `UniswapV2SwapAdapter.t.sol` and `BalancerV2SwapAdapter.t.sol` for reference. The [Foundry test guide](https://book.getfoundry.sh/forge/tests) is a good reference, especially the chapter for [Fuzz testing](https://book.getfoundry.sh/forge/fuzz-testing), which is used in both the Uniswap and Balancer tests. We are using fork testing, i.e. we are running a local Ethereum node and fork the mainnet state. This allows us to test the integration against the real contracts and real data. To run the tests, you need to set the `ETH_RPC_URL` environment variable to the URL of an ethereum RPC. It can be your own node or a public one, like [Alchemy](https://www.alchemy.com/) or [Infura](https://infura.io/). Finally, run the tests with: - ```bash - cd ./evm - forge test - ``` + +```bash +cd ./evm +forge test +```