diff --git a/docs/indexing/reserved-attributes.md b/docs/indexing/reserved-attributes.md
new file mode 100644
index 0000000..f1c7ea0
--- /dev/null
+++ b/docs/indexing/reserved-attributes.md
@@ -0,0 +1,164 @@
+# Reserved attribute names
+
+Certain attribute names are reserved exclusively for specific purposes. Please use them only for their intended functions
+
+## Static Attributes
+
+The following attributes names are reserved and must be given using `ProtocolComponent.static_att`.
+
+- ### **pool_id**
+
+#### Description
+
+The `pool_id` static attribute is used to specify the identifier of the pool when it differs from the `ProtocolComponent.id`. For example, Balancer pools have a component ID that corresponds to their contract address, and a separate pool ID used for registration on the Balancer Vault contract.
+
+#### Type
+
+This attribute value must be provided as a UTF-8 encoded string in bytes.
+
+#### Example Usage
+
+```rust
+Attribute {
+name: "pool_id".to_string(),
+value: format!("0x{}", hex::encode(pool_registered.pool_id)).as_bytes(),
+change: ChangeType::Creation.into(),
+}
+```
+
+- ### **manual_updates**
+
+#### Description
+
+The `manual_updates` static attribute determines whether the component update should be manually triggered using the `update_marker` state attribute. By default, updates occur automatically whenever there is a change in any of the required contracts. However, in scenarios where a contract undergoes frequent changes, automatic updates may not be desirable. For instance, a change in Balancer Vault storage should only trigger updates for the specific pools affected by the change, rather than for all pools indiscriminately. The `manual_updates` field helps to control and prevent unnecessary updates in such cases.
+
+If it's set to `[1u8]`, updates on this component are only triggered by emitting an `update_marker` state attribute (described below).
+
+#### Type
+
+This attribute value must be provided as bytes.
+
+#### Example Usage
+
+```rust
+Attribute {
+name: "manual_updates".to_string(),
+value: [1u8],
+change: ChangeType::Creation.into(),
+}
+```
+
+## State Attributes
+
+The following attributes names are reserved and must be given using `EntityChanges`.
+
+- ### **stateless_contract_addr**
+
+#### Description
+
+The `stateless_contract_addr_{index}` field is used to specify the address of a stateless contract required by the component. This field is essential for components that interact with stateless contracts, particularly in scenarios involving `DELEGATECALL`. If the bytecode of this stateless contract can be retreived in Substreams, it must be passed using the `stateless_contract_code` attribute (see below).
+
+An index is used if multiple stateless contracts are needed. This index should start at 0 and increment by 1 for each additional `stateless_contract_addr`.
+
+The value for `stateless_contract_addr_{index}` can be provided in two ways:
+
+1. **Direct Contract Address**: A static contract address can be specified directly.
+2. **Dynamic Address Resolution**: Alternatively, you can define a function or method that dynamically resolves and retrieves the stateless contract address at runtime. This can be particularly useful in complex contract architectures, such as those using a dynamic proxy pattern. It is important to note that the called contract must be indexed by the Substreams module.
+
+#### Type
+
+This attribute value must be provided as a UTF-8 encoded string in bytes.
+
+#### Example Usage
+
+##### 1. Direct Contract Address
+
+To specify a direct contract address:
+
+```rust
+Attribute {
+ name: "stateless_contract_addr_0".into(),
+ value: format!("0x{}", hex::encode(address)).into_bytes(),
+ change: ChangeType::Creation.into(),
+}
+Attribute {
+ name: "stateless_contract_addr_1".into(),
+ value: format!("0x{}", hex::encode(other_address)).into_bytes(),
+ change: ChangeType::Creation.into(),
+}
+```
+
+##### 2. Dynamic Address Resolution
+
+To specify a function that dynamically resolves the address:
+
+```rust
+Attribute {
+name: "stateless_contract_addr_0".into(),
+// Call views_implementation() on TRICRYPTO_FACTORY
+value: format!("call:0x{}:views_implementation()", hex::encode(TRICRYPTO_FACTORY)).into_bytes(),
+change: ChangeType::Creation.into(),
+}
+```
+
+- ### **stateless_contract_code**
+
+#### Description
+
+The `stateless_contract_code_{index}` field is used to specify the code for a given `stateless_contract_addr`.
+
+An index is used if multiple stateless contracts are needed. This index must match with the related `stateless_contract_addr`.
+
+#### Type
+
+This attribute value must be provided as bytes.
+
+#### Example Usage
+
+```rust
+Attribute {
+name: "stateless_contract_code_0".to_string(),
+value: code.to_vec(),
+change: ChangeType::Creation.into(),
+}
+```
+
+- ### **balance_owner**
+
+#### Description
+
+The `balance_owner` field is used to specify the address of the account that owns the protocol component tokens, in cases where the tokens are not owned by the protocol component itself. This is particularly useful for protocols that use a vault, for example Balancer.
+
+#### Type
+
+This attribute value must be provided as bytes.
+
+#### Example Usage
+
+```rust
+Attribute {
+name: "balance_owner".to_string(),
+value: VAULT_ADDRESS.to_vec(),
+change: ChangeType::Creation.into(),
+}
+```
+
+- ### **update_marker**
+
+#### Description
+
+The `update_marker` field is used to indicate that a pool has changed, thereby triggering an update on the protocol component when `manual_update` is enabled.
+
+#### Type
+
+This attribute value must be provided as bytes.
+
+#### Example Usage
+
+```rust
+Attribute {
+ name: "update_marker".to_string(),
+ value: vec![1u8],
+ change: ChangeType::Update.into(),
+};
+```
diff --git a/docs/indexing/substreams-integration.md b/docs/indexing/substreams-integration.md
index f4f71a4..7f747d7 100644
--- a/docs/indexing/substreams-integration.md
+++ b/docs/indexing/substreams-integration.md
@@ -38,6 +38,10 @@ Many of the types above are variable length bytes. This allows for flexibility a
**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 integration should at least communicate the following changes:
@@ -88,7 +92,33 @@ Newly created components are detected by mapping over the `sf.ethereum.type.v2.B
The output message should usually contain as much information about the component available at that time as well as the transaction that created the protocol component.
-We have found that using the final model (`BlockChanges`) prefilled with only component changes is usually good enough since it holds all the information that will be necessary at the end.
+We have found that using the final model (see `BlockChanges` below) prefilled with only component changes is usually good enough since it holds all the information that will be necessary at the end.
+
+```protobuf
+// A set of changes aggregated by transaction.
+message TransactionChanges {
+ // The transaction instance that results in the changes.
+ Transaction tx = 1;
+ // 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.
+ repeated ContractChange contract_changes = 2;
+ // Contains the entity changes induced by the above transaction.
+ // Usually for tracking native components or used for VM extensions (plugins).
+ repeated EntityChanges entity_changes = 3;
+ // An array of newly added components.
+ repeated ProtocolComponent component_changes = 4;
+ // An array of balance changes to components.
+ repeated BalanceChange balance_changes = 5;
+}
+// A set of transaction changes within a single block.
+// This message must be the output of your substreams module.
+message BlockChanges {
+ // The block for which these changes are collectively computed.
+ Block block = 1;
+ // The set of transaction changes observed in the specified block.
+ repeated TransactionChanges changes = 2;
+}
+```
Note that a single transaction may emit multiple newly created components. In this case it is expected that the `TransactionChanges.component_changes`, contains multiple `ProtocolComponents`.
@@ -106,7 +136,7 @@ Since this is challenging the following approach is recommended:
- Aggregate the BalanceDelta messages using a `BigIntAddStore`.
- In a final handler, use as inputs: A `DeltaStore` input from step 2 and the `BlockBalanceDeltas` from step 1. You can now zip the deltas from the store with the balance deltas from step 1. The store deltas contains the aggregated (absolute) balance at each version and the balance deltas contain the corresponding transaction.
-Our Substreams SDK provide the `extract_balance_deltas_from_tx` function that extracts all relevant `BalanceDelta` from ERC20 `Transfer` events in a given transaction (see Curve implementation).
+Our Substreams SDK provides the `extract_balance_deltas_from_tx` function that extracts all relevant `BalanceDelta` from ERC20 `Transfer` events for a given transaction (see Curve implementation).
#### Tracking State Changes
diff --git a/docs/indexing/vm-integration/README.md b/docs/indexing/vm-integration/README.md
index 7f2f94a..e7de893 100644
--- a/docs/indexing/vm-integration/README.md
+++ b/docs/indexing/vm-integration/README.md
@@ -81,7 +81,7 @@ Read our [Substreams README.md](../../../substreams/README.md) for more informat
block: eth::v2::Block,
) -> Result {}
```
- The `map_protocol_changes` function takes a raw block as input and returns a `BlockChanges` struct, which is derived from the `BlockChanges` protobuf message in [vm.proto](../../../proto/tycho/evm/v1/vm.proto).
+ The `map_protocol_changes` function takes a raw block as input and returns a `BlockChanges` struct, which is derived from the `BlockChanges` protobuf message in [common.proto](../../../proto/tycho/evm/v1/common.proto).
1. The `BlockChanges` is a list of `TransactionChanges`, which includes these main fields: