BalancerV3: SwapAdapter and Substreams (#126)

* feat: add balancer swapAdapter and Substreams

* fix: undo tycho-substreams logs, ignore abi on rustmft

* ci: prevent warnings from failing CI

* ci: skip size check on CI

* chore: forge fmt

* feat: vault balance from storage

Vault contract tokenBalance message are set according to the vault
storage changes in the `_reserveOf` storage variable VaultStorage.sol
contract
This was the culprit that caused the failure in simulation since
balancer enforces the invariant that `token.balanceOf(vault_addr) == _reservesOf[token]`

* ci: warnings

* fix: avoid duplicated balance changes

* fix: order by ordinal

* chore: format

* feat: extract new contracts before extracting balance changes

* feat: skip unnecessary steps if no balance change is found

* refactor: filter out account balances for tokens that aren't part of any protocol components.

On the indexer side, when we receive an account balance, we need to know about the token. This commit ensure that the token was introduced before we emit any account balance with it.

* refactor: don't index liquidity buffers.

Liquidity buffers rely on rate providers. Therefore we need DCI (feature to be able to index previously created contract) to deal with them.

* refactor: cleanup tests and add docstrings

* chore: lock tycho-substreams version

* ci: set Foundry workflow to use stable foundry

* feat(DCI): Add DCI Entrypoints to BalancerV3 components (#218)

* refactor: fix typo in weighted_pool_factory_contract name

* feat: add rate_providers static attributes

* feat: add DCI entrypoints to BalancerV3 components

* fix: set default trade price to Fraction(0, 1)

* feat: remove buffers as components

Buffers are to be used internally by Boosted pools (stable/weighted pools that use ERC4626 tokens). They are not to be treated as a separate swap component.

* test: update test blocks

Extend tests some tests block range to ensure liquidity was added to the pool and can be simulated on

* feat: remove buffers as components

Remove balance updates for buffer components

* feat: listen for pool pause/unpause events

* chore: formating

* fix: encoding call data

* test: update Balancer V3 tests to use DCI

* test: set indexer log level to info

* docs: add comment on support of boosted pools

* feat: update balancer v3 package version

---------

Co-authored-by: Thales <thales@datarevenue.com>
Co-authored-by: zizou <111426680+flopell@users.noreply.github.com>
Co-authored-by: Louise Poole <louise@datarevenue.com>
Co-authored-by: Louise Poole <louisecarmenpoole@gmail.com>
This commit is contained in:
mrBovo
2025-06-26 12:19:39 +02:00
committed by GitHub
parent dfa87f662d
commit fc0fb1e540
43 changed files with 25137 additions and 14 deletions

View File

@@ -0,0 +1,108 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library CustomBytesAppend {
// Constants for the custom prefix used in the bytes32 format
string private constant CUSTOM = "_CUSTOM_";
/**
* @dev Extracts an address from a bytes32 input, assuming it is either
* prepended or appended with `_CUSTOM_`.
* @param input The bytes32 input containing the address and custom
* prefix/suffix.
* @return extractedAddress The extracted address.
*/
function extractAddress(bytes32 input)
public
pure
returns (address extractedAddress)
{
// Convert the bytes32 input into a dynamic bytes array for manipulation
bytes memory inputBytes = abi.encodePacked(input);
// Check if the bytes contain the custom prefix
if (hasPrefix(inputBytes)) {
// If prefixed, extract the 20 bytes after the prefix as the address
extractedAddress =
bytesToAddress(slice(inputBytes, bytes(CUSTOM).length, 20));
}
// Check if the bytes contain the custom suffix
else if (hasSuffix(inputBytes)) {
// If suffixed, extract the first 20 bytes as the address
extractedAddress = bytesToAddress(slice(inputBytes, 0, 20));
} else {
// Revert if neither prefix nor suffix is found
revert("Invalid input format");
}
}
/**
* @dev Checks if the bytes data has the custom prefix.
* @param data The bytes array to check.
* @return True if the prefix matches, false otherwise.
*/
function hasPrefix(bytes memory data) internal pure returns (bool) {
// Compare the first bytes of the input with the prefix using keccak256
// for hashing
return keccak256(slice(data, 0, bytes(CUSTOM).length))
== keccak256(bytes(CUSTOM));
}
/**
* @dev Checks if the bytes data has the custom suffix.
* @param data The bytes array to check.
* @return True if the suffix matches, false otherwise.
*/
function hasSuffix(bytes memory data) internal pure returns (bool) {
// Compare the last bytes of the input with the suffix using keccak256
// for hashing
return keccak256(
slice(
data, data.length - bytes(CUSTOM).length, bytes(CUSTOM).length
)
) == keccak256(bytes(CUSTOM));
}
/**
* @dev Slices a bytes array.
* @param data The bytes array to slice.
* @param start The starting index of the slice.
* @param length The length of the slice.
* @return The sliced bytes array.
*/
function slice(bytes memory data, uint256 start, uint256 length)
internal
pure
returns (bytes memory)
{
// Ensure the slice operation does not exceed the bounds of the array
require(data.length >= start + length, "Invalid slice");
// Create a new bytes array to hold the sliced data
bytes memory result = new bytes(length);
for (uint256 i = 0; i < length; i++) {
result[i] = data[start + i];
}
return result;
}
/**
* @dev Converts a bytes array of length 20 into an address.
* @param data The bytes array (must be 20 bytes long).
* @return addr The converted address.
*/
function bytesToAddress(bytes memory data)
internal
pure
returns (address addr)
{
// Ensure the input length is exactly 20 bytes (size of an Ethereum
// address)
require(data.length == 20, "Invalid address length");
// Use inline assembly to efficiently convert the bytes to an address
assembly {
addr := mload(add(data, 20))
}
}
}