Ekubo TWAMM & MEV-resist integration (#192)
* Add Ekubo TWAMM support * Change order of words * Account TWAMM order balances * Fix tracking wrong component balance deltas Swapped and PositionUpdated are the only events affecting pool TVL * Fix fee addition Fees are a .64 instead of a .128 since v2 & the result is rounded * Consistent naming * cargo fmt * Add method for selecting store method from change type * Only store the affected sale rate delta on OrderUpdated events * Remove unnecessary parameterization * Index Ekubo MEV-resist pools * cargo clippy
This commit is contained in:
3
substreams/Cargo.lock
generated
3
substreams/Cargo.lock
generated
@@ -299,7 +299,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethereum-ekubo-v2"
|
name = "ethereum-ekubo-v2"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"ethabi 18.0.0",
|
"ethabi 18.0.0",
|
||||||
@@ -312,6 +312,7 @@ dependencies = [
|
|||||||
"substreams",
|
"substreams",
|
||||||
"substreams-ethereum",
|
"substreams-ethereum",
|
||||||
"substreams-helper 0.0.2 (git+https://github.com/propeller-heads/tycho-protocol-sdk.git?rev=e4609be)",
|
"substreams-helper 0.0.2 (git+https://github.com/propeller-heads/tycho-protocol-sdk.git?rev=e4609be)",
|
||||||
|
"tiny-keccak",
|
||||||
"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=3c08359)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ethereum-ekubo-v2"
|
name = "ethereum-ekubo-v2"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
@@ -20,6 +20,7 @@ hex = { version = "0.4", features = ["serde"] }
|
|||||||
itertools = "0.10.5"
|
itertools = "0.10.5"
|
||||||
serde = "1.0.217"
|
serde = "1.0.217"
|
||||||
serde_qs = "0.13.0"
|
serde_qs = "0.13.0"
|
||||||
|
tiny-keccak = { version = "2.0.2", features = ["keccak"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
|
|||||||
720
substreams/ethereum-ekubo-v2/abi/twamm.json
Normal file
720
substreams/ethereum-ekubo-v2/abi/twamm.json
Normal file
@@ -0,0 +1,720 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "constructor",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "core",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "contract ICore"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stateMutability": "nonpayable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"name": "afterCollectFees",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "tuple",
|
||||||
|
"internalType": "struct PoolKey",
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "token0",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "token1",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "config",
|
||||||
|
"type": "bytes32",
|
||||||
|
"internalType": "Config"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "bytes32",
|
||||||
|
"internalType": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "tuple",
|
||||||
|
"internalType": "struct Bounds",
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "lower",
|
||||||
|
"type": "int32",
|
||||||
|
"internalType": "int32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "upper",
|
||||||
|
"type": "int32",
|
||||||
|
"internalType": "int32"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint128",
|
||||||
|
"internalType": "uint128"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint128",
|
||||||
|
"internalType": "uint128"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"name": "afterInitializePool",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"type": "tuple",
|
||||||
|
"internalType": "struct PoolKey",
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "token0",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "token1",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "config",
|
||||||
|
"type": "bytes32",
|
||||||
|
"internalType": "Config"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "int32",
|
||||||
|
"internalType": "int32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint96",
|
||||||
|
"internalType": "SqrtRatio"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"name": "afterSwap",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "tuple",
|
||||||
|
"internalType": "struct PoolKey",
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "token0",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "token1",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "config",
|
||||||
|
"type": "bytes32",
|
||||||
|
"internalType": "Config"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "int128",
|
||||||
|
"internalType": "int128"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "bool",
|
||||||
|
"internalType": "bool"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint96",
|
||||||
|
"internalType": "SqrtRatio"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256",
|
||||||
|
"internalType": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "int128",
|
||||||
|
"internalType": "int128"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "int128",
|
||||||
|
"internalType": "int128"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"name": "afterUpdatePosition",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "tuple",
|
||||||
|
"internalType": "struct PoolKey",
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "token0",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "token1",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "config",
|
||||||
|
"type": "bytes32",
|
||||||
|
"internalType": "Config"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "tuple",
|
||||||
|
"internalType": "struct UpdatePositionParameters",
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "salt",
|
||||||
|
"type": "bytes32",
|
||||||
|
"internalType": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "bounds",
|
||||||
|
"type": "tuple",
|
||||||
|
"internalType": "struct Bounds",
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "lower",
|
||||||
|
"type": "int32",
|
||||||
|
"internalType": "int32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "upper",
|
||||||
|
"type": "int32",
|
||||||
|
"internalType": "int32"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "liquidityDelta",
|
||||||
|
"type": "int128",
|
||||||
|
"internalType": "int128"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "int128",
|
||||||
|
"internalType": "int128"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "int128",
|
||||||
|
"internalType": "int128"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"name": "beforeCollectFees",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "tuple",
|
||||||
|
"internalType": "struct PoolKey",
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "token0",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "token1",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "config",
|
||||||
|
"type": "bytes32",
|
||||||
|
"internalType": "Config"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "bytes32",
|
||||||
|
"internalType": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "tuple",
|
||||||
|
"internalType": "struct Bounds",
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "lower",
|
||||||
|
"type": "int32",
|
||||||
|
"internalType": "int32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "upper",
|
||||||
|
"type": "int32",
|
||||||
|
"internalType": "int32"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"name": "beforeInitializePool",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "tuple",
|
||||||
|
"internalType": "struct PoolKey",
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "token0",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "token1",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "config",
|
||||||
|
"type": "bytes32",
|
||||||
|
"internalType": "Config"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "int32",
|
||||||
|
"internalType": "int32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"name": "beforeSwap",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "poolKey",
|
||||||
|
"type": "tuple",
|
||||||
|
"internalType": "struct PoolKey",
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "token0",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "token1",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "config",
|
||||||
|
"type": "bytes32",
|
||||||
|
"internalType": "Config"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "int128",
|
||||||
|
"internalType": "int128"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "bool",
|
||||||
|
"internalType": "bool"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint96",
|
||||||
|
"internalType": "SqrtRatio"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256",
|
||||||
|
"internalType": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"name": "beforeUpdatePosition",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "poolKey",
|
||||||
|
"type": "tuple",
|
||||||
|
"internalType": "struct PoolKey",
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "token0",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "token1",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "config",
|
||||||
|
"type": "bytes32",
|
||||||
|
"internalType": "Config"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "tuple",
|
||||||
|
"internalType": "struct UpdatePositionParameters",
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "salt",
|
||||||
|
"type": "bytes32",
|
||||||
|
"internalType": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "bounds",
|
||||||
|
"type": "tuple",
|
||||||
|
"internalType": "struct Bounds",
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "lower",
|
||||||
|
"type": "int32",
|
||||||
|
"internalType": "int32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "upper",
|
||||||
|
"type": "int32",
|
||||||
|
"internalType": "int32"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "liquidityDelta",
|
||||||
|
"type": "int128",
|
||||||
|
"internalType": "int128"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"name": "forwarded",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"type": "uint256",
|
||||||
|
"internalType": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "originalLocker",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"name": "locked",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256",
|
||||||
|
"internalType": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"name": "sload",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "slot",
|
||||||
|
"type": "bytes32",
|
||||||
|
"internalType": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "bytes32",
|
||||||
|
"internalType": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stateMutability": "view"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"name": "tload",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "slot",
|
||||||
|
"type": "bytes32",
|
||||||
|
"internalType": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "bytes32",
|
||||||
|
"internalType": "bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stateMutability": "view"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "event",
|
||||||
|
"name": "OrderProceedsWithdrawn",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "owner",
|
||||||
|
"type": "address",
|
||||||
|
"indexed": false,
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "salt",
|
||||||
|
"type": "bytes32",
|
||||||
|
"indexed": false,
|
||||||
|
"internalType": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "orderKey",
|
||||||
|
"type": "tuple",
|
||||||
|
"indexed": false,
|
||||||
|
"internalType": "struct OrderKey",
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "sellToken",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "buyToken",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fee",
|
||||||
|
"type": "uint64",
|
||||||
|
"internalType": "uint64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "startTime",
|
||||||
|
"type": "uint256",
|
||||||
|
"internalType": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "endTime",
|
||||||
|
"type": "uint256",
|
||||||
|
"internalType": "uint256"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "amount",
|
||||||
|
"type": "uint128",
|
||||||
|
"indexed": false,
|
||||||
|
"internalType": "uint128"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"anonymous": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "event",
|
||||||
|
"name": "OrderUpdated",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "owner",
|
||||||
|
"type": "address",
|
||||||
|
"indexed": false,
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "salt",
|
||||||
|
"type": "bytes32",
|
||||||
|
"indexed": false,
|
||||||
|
"internalType": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "orderKey",
|
||||||
|
"type": "tuple",
|
||||||
|
"indexed": false,
|
||||||
|
"internalType": "struct OrderKey",
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "sellToken",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "buyToken",
|
||||||
|
"type": "address",
|
||||||
|
"internalType": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fee",
|
||||||
|
"type": "uint64",
|
||||||
|
"internalType": "uint64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "startTime",
|
||||||
|
"type": "uint256",
|
||||||
|
"internalType": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "endTime",
|
||||||
|
"type": "uint256",
|
||||||
|
"internalType": "uint256"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "saleRateDelta",
|
||||||
|
"type": "int112",
|
||||||
|
"indexed": false,
|
||||||
|
"internalType": "int112"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"anonymous": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "error",
|
||||||
|
"name": "BaseForwardeeAccountantOnly",
|
||||||
|
"inputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "error",
|
||||||
|
"name": "CallPointNotImplemented",
|
||||||
|
"inputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "error",
|
||||||
|
"name": "CoreOnly",
|
||||||
|
"inputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "error",
|
||||||
|
"name": "InvalidTimestamps",
|
||||||
|
"inputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "error",
|
||||||
|
"name": "MaxSaleRateDeltaPerTick",
|
||||||
|
"inputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "error",
|
||||||
|
"name": "MustCollectProceedsBeforeCanceling",
|
||||||
|
"inputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "error",
|
||||||
|
"name": "OrderAlreadyEnded",
|
||||||
|
"inputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "error",
|
||||||
|
"name": "TickSpacingMustBeMaximum",
|
||||||
|
"inputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "error",
|
||||||
|
"name": "TimeNumOrdersOverflow",
|
||||||
|
"inputs": []
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -26,10 +26,10 @@ fn main() -> Result<()> {
|
|||||||
|
|
||||||
let contract_name = file_name.split('.').next().unwrap();
|
let contract_name = file_name.split('.').next().unwrap();
|
||||||
|
|
||||||
let input_path = format!("{}/{}", abi_folder, file_name);
|
let input_path = format!("{abi_folder}/{file_name}");
|
||||||
let output_path = format!("{}/{}.rs", output_folder, contract_name);
|
let output_path = format!("{output_folder}/{contract_name}.rs");
|
||||||
|
|
||||||
mod_rs_content.push_str(&format!("pub mod {};\n", contract_name));
|
mod_rs_content.push_str(&format!("pub mod {contract_name};\n"));
|
||||||
|
|
||||||
if std::path::Path::new(&output_path).exists() {
|
if std::path::Path::new(&output_path).exists() {
|
||||||
continue;
|
continue;
|
||||||
@@ -40,7 +40,7 @@ fn main() -> Result<()> {
|
|||||||
.write_to_file(&output_path)?;
|
.write_to_file(&output_path)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mod_rs_path = format!("{}/mod.rs", output_folder);
|
let mod_rs_path = format!("{output_folder}/mod.rs");
|
||||||
let mut mod_rs_file = fs::File::create(mod_rs_path)?;
|
let mut mod_rs_file = fs::File::create(mod_rs_path)?;
|
||||||
|
|
||||||
mod_rs_file.write_all(mod_rs_content.as_bytes())?;
|
mod_rs_file.write_all(mod_rs_content.as_bytes())?;
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ syntax = "proto3";
|
|||||||
|
|
||||||
package ekubo;
|
package ekubo;
|
||||||
|
|
||||||
// Copy of tycho.evm.v1.Transaction to be able to implement conversions to/from TransactionTrace
|
// Copy of tycho.evm.v1.Transaction to be able to implement conversions to/from
|
||||||
|
// TransactionTrace
|
||||||
message Transaction {
|
message Transaction {
|
||||||
bytes hash = 1;
|
bytes hash = 1;
|
||||||
bytes from = 2;
|
bytes from = 2;
|
||||||
@@ -18,27 +19,45 @@ message TickDelta {
|
|||||||
Transaction transaction = 5;
|
Transaction transaction = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message TickDeltas {
|
message TickDeltas { repeated TickDelta deltas = 1; }
|
||||||
repeated TickDelta deltas = 1;
|
|
||||||
|
message OrderSaleRateDelta {
|
||||||
|
bytes pool_id = 1; // bytes32
|
||||||
|
uint64 time = 2;
|
||||||
|
bytes sale_rate_delta = 3; // int112
|
||||||
|
bool is_token1 = 4;
|
||||||
|
uint64 ordinal = 5;
|
||||||
|
Transaction transaction = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum LiquidityChangeType {
|
message OrderSaleRateDeltas { repeated OrderSaleRateDelta deltas = 1; }
|
||||||
|
|
||||||
|
enum ChangeType {
|
||||||
DELTA = 0;
|
DELTA = 0;
|
||||||
ABSOLUTE = 1;
|
ABSOLUTE = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message LiquidityChange {
|
message LiquidityChange {
|
||||||
bytes pool_id = 1; // bytes32
|
bytes pool_id = 1; // bytes32
|
||||||
bytes value = 2; // uint128 or int128, depending on change_type
|
bytes value = 2; // uint128 or int128, depending on change_type
|
||||||
LiquidityChangeType change_type = 3;
|
ChangeType change_type = 3;
|
||||||
uint64 ordinal = 4;
|
uint64 ordinal = 4;
|
||||||
Transaction transaction = 5;
|
Transaction transaction = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message LiquidityChanges {
|
message LiquidityChanges { repeated LiquidityChange changes = 1; }
|
||||||
repeated LiquidityChange changes = 1;
|
|
||||||
|
message SaleRateChange {
|
||||||
|
bytes pool_id = 1; // bytes32
|
||||||
|
bytes token0_value = 2; // uint112 or int112, depending on change_type
|
||||||
|
bytes token1_value = 3; // uint112 or int112, depending on change_type
|
||||||
|
ChangeType change_type = 4;
|
||||||
|
uint64 ordinal = 5;
|
||||||
|
Transaction transaction = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message SaleRateChanges { repeated SaleRateChange changes = 1; }
|
||||||
|
|
||||||
message PoolDetails {
|
message PoolDetails {
|
||||||
bytes token0 = 1; // address
|
bytes token0 = 1; // address
|
||||||
bytes token1 = 2; // address
|
bytes token1 = 2; // address
|
||||||
@@ -47,6 +66,7 @@ message PoolDetails {
|
|||||||
|
|
||||||
message BlockTransactionEvents {
|
message BlockTransactionEvents {
|
||||||
repeated TransactionEvents block_transaction_events = 1;
|
repeated TransactionEvents block_transaction_events = 1;
|
||||||
|
uint64 timestamp = 2; // block timestamp
|
||||||
|
|
||||||
message TransactionEvents {
|
message TransactionEvents {
|
||||||
Transaction transaction = 1;
|
Transaction transaction = 1;
|
||||||
@@ -59,37 +79,32 @@ message BlockTransactionEvents {
|
|||||||
oneof event {
|
oneof event {
|
||||||
Swapped swapped = 3;
|
Swapped swapped = 3;
|
||||||
PositionUpdated position_updated = 4;
|
PositionUpdated position_updated = 4;
|
||||||
PositionFeesCollected position_fees_collected = 5;
|
PoolInitialized pool_initialized = 5;
|
||||||
PoolInitialized pool_initialized = 6;
|
VirtualOrdersExecuted virtual_orders_executed = 6;
|
||||||
FeesAccumulated fees_accumulated = 7;
|
OrderUpdated order_updated = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Swapped {
|
message Swapped {
|
||||||
bytes delta0 = 1; // int128
|
bytes delta0 = 1; // int128
|
||||||
bytes delta1 = 2; // int128
|
bytes delta1 = 2; // int128
|
||||||
bytes sqrt_ratio_after = 3; // uint192
|
bytes sqrt_ratio_after = 3; // uint192
|
||||||
bytes liquidity_after = 4; // uint128
|
bytes liquidity_after = 4; // uint128
|
||||||
sint32 tick_after = 5; // int32
|
sint32 tick_after = 5; // int32
|
||||||
}
|
}
|
||||||
|
|
||||||
message PositionUpdated {
|
message PositionUpdated {
|
||||||
sint32 lower = 1; // int32
|
sint32 lower = 1; // int32
|
||||||
sint32 upper = 2; // int32
|
sint32 upper = 2; // int32
|
||||||
bytes liquidity_delta = 3; // int128
|
bytes liquidity_delta = 3; // int128
|
||||||
bytes delta0 = 4; // int128
|
bytes delta0 = 4; // int128
|
||||||
bytes delta1 = 5; // int128
|
bytes delta1 = 5; // int128
|
||||||
}
|
|
||||||
|
|
||||||
message PositionFeesCollected {
|
|
||||||
bytes amount0 = 1; // uint128
|
|
||||||
bytes amount1 = 2; // uint128
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message PoolInitialized {
|
message PoolInitialized {
|
||||||
bytes token0 = 1; // address
|
bytes token0 = 1; // address
|
||||||
bytes token1 = 2; // address
|
bytes token1 = 2; // address
|
||||||
bytes config = 3; // bytes32
|
bytes config = 3; // bytes32
|
||||||
sint32 tick = 4; // int32
|
sint32 tick = 4; // int32
|
||||||
bytes sqrt_ratio = 5; // uint192
|
bytes sqrt_ratio = 5; // uint192
|
||||||
Extension extension = 6;
|
Extension extension = 6;
|
||||||
|
|
||||||
@@ -97,12 +112,27 @@ message BlockTransactionEvents {
|
|||||||
EXTENSION_UNKNOWN = 0;
|
EXTENSION_UNKNOWN = 0;
|
||||||
EXTENSION_BASE = 1;
|
EXTENSION_BASE = 1;
|
||||||
EXTENSION_ORACLE = 2;
|
EXTENSION_ORACLE = 2;
|
||||||
|
EXTENSION_TWAMM = 3;
|
||||||
|
EXTENSION_MEV_RESIST = 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
message FeesAccumulated {
|
message VirtualOrdersExecuted {
|
||||||
bytes amount0 = 1; // uint128
|
bytes token0_sale_rate = 1; // int112
|
||||||
bytes amount1 = 2; // uint128
|
bytes token1_sale_rate = 2; // int112
|
||||||
|
}
|
||||||
|
|
||||||
|
message OrderUpdated {
|
||||||
|
OrderKey order_key = 1;
|
||||||
|
bytes sale_rate_delta = 2; // int112
|
||||||
|
|
||||||
|
message OrderKey {
|
||||||
|
bytes sell_token = 1; // address
|
||||||
|
bytes buy_token = 2; // address
|
||||||
|
fixed64 fee = 3;
|
||||||
|
uint64 start_time = 4; // block timestamp
|
||||||
|
uint64 end_time = 5; // block timestamp
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
#![allow(clippy::all)]
|
#![allow(clippy::all)]
|
||||||
pub mod core;
|
pub mod core;
|
||||||
|
pub mod twamm;
|
||||||
|
|||||||
2146
substreams/ethereum-ekubo-v2/src/abi/twamm.rs
Normal file
2146
substreams/ethereum-ekubo-v2/src/abi/twamm.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -6,4 +6,8 @@ pub struct DeploymentConfig {
|
|||||||
pub core: Vec<u8>,
|
pub core: Vec<u8>,
|
||||||
#[serde(with = "hex::serde")]
|
#[serde(with = "hex::serde")]
|
||||||
pub oracle: Vec<u8>,
|
pub oracle: Vec<u8>,
|
||||||
|
#[serde(with = "hex::serde")]
|
||||||
|
pub twamm: Vec<u8>,
|
||||||
|
#[serde(with = "hex::serde")]
|
||||||
|
pub mev_resist: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,7 @@ mod abi;
|
|||||||
mod deployment_config;
|
mod deployment_config;
|
||||||
mod modules;
|
mod modules;
|
||||||
mod pb;
|
mod pb;
|
||||||
mod pool_config;
|
mod pool_key;
|
||||||
mod sqrt_ratio;
|
mod sqrt_ratio;
|
||||||
|
mod store;
|
||||||
|
mod twamm;
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ use substreams_ethereum::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
abi::core::events as abi_events,
|
abi::{core::events as core_events, twamm::events as twamm_events},
|
||||||
deployment_config::DeploymentConfig,
|
deployment_config::DeploymentConfig,
|
||||||
pb::ekubo::{
|
pb::ekubo::{
|
||||||
block_transaction_events::{
|
block_transaction_events::{
|
||||||
transaction_events::{
|
transaction_events::{
|
||||||
pool_log::{
|
pool_log::{
|
||||||
pool_initialized::Extension, Event, FeesAccumulated, PoolInitialized,
|
order_updated::OrderKey, pool_initialized::Extension, Event, OrderUpdated,
|
||||||
PositionFeesCollected, PositionUpdated, Swapped,
|
PoolInitialized, PositionUpdated, Swapped, VirtualOrdersExecuted,
|
||||||
},
|
},
|
||||||
PoolLog,
|
PoolLog,
|
||||||
},
|
},
|
||||||
@@ -22,7 +22,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
BlockTransactionEvents,
|
BlockTransactionEvents,
|
||||||
},
|
},
|
||||||
pool_config::PoolConfig,
|
pool_key::{PoolConfig, PoolKey},
|
||||||
sqrt_ratio::float_sqrt_ratio_to_fixed,
|
sqrt_ratio::float_sqrt_ratio_to_fixed,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -43,84 +43,114 @@ fn map_events(params: String, block: eth::v2::Block) -> BlockTransactionEvents {
|
|||||||
.then(|| TransactionEvents { transaction: Some(trace.into()), pool_logs })
|
.then(|| TransactionEvents { transaction: Some(trace.into()), pool_logs })
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
timestamp: block
|
||||||
|
.header
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.timestamp
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.seconds
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn maybe_pool_log(log: &Log, config: &DeploymentConfig) -> Option<PoolLog> {
|
fn maybe_pool_log(log: &Log, config: &DeploymentConfig) -> Option<PoolLog> {
|
||||||
if log.address != config.core {
|
let (pool_id, ev) = if log.address == config.core {
|
||||||
return None;
|
if log.topics.is_empty() {
|
||||||
}
|
let data = &log.data;
|
||||||
|
|
||||||
let (pool_id, ev) = if log.topics.is_empty() {
|
assert!(data.len() == 116, "swap event data length mismatch");
|
||||||
let data = &log.data;
|
|
||||||
|
|
||||||
assert!(data.len() == 116, "swap event data length mismatch");
|
(
|
||||||
|
data[20..52].to_vec(),
|
||||||
|
Event::Swapped(Swapped {
|
||||||
|
delta0: data[52..68].to_vec(),
|
||||||
|
delta1: data[68..84].to_vec(),
|
||||||
|
liquidity_after: data[84..100].to_vec(),
|
||||||
|
sqrt_ratio_after: float_sqrt_ratio_to_fixed(BigInt::from_unsigned_bytes_be(
|
||||||
|
&data[100..112],
|
||||||
|
)),
|
||||||
|
tick_after: i32::from_be_bytes(data[112..116].try_into().unwrap()),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
} else if let Some(ev) = core_events::PositionUpdated::match_and_decode(log) {
|
||||||
|
(
|
||||||
|
ev.pool_id.to_vec(),
|
||||||
|
Event::PositionUpdated(PositionUpdated {
|
||||||
|
lower: ev.params.1 .0.to_i32(),
|
||||||
|
upper: ev.params.1 .1.to_i32(),
|
||||||
|
liquidity_delta: ev.params.2.to_signed_bytes_be(),
|
||||||
|
delta0: ev.delta0.to_signed_bytes_be(),
|
||||||
|
delta1: ev.delta1.to_signed_bytes_be(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
} else if let Some(ev) = core_events::PoolInitialized::match_and_decode(log) {
|
||||||
|
let pool_config = PoolConfig::from(ev.pool_key.2);
|
||||||
|
|
||||||
(
|
let extension = {
|
||||||
data[20..52].to_vec(),
|
let extension = pool_config.extension.as_bytes();
|
||||||
Event::Swapped(Swapped {
|
|
||||||
delta0: data[52..68].to_vec(),
|
|
||||||
delta1: data[68..84].to_vec(),
|
|
||||||
liquidity_after: data[84..100].to_vec(),
|
|
||||||
sqrt_ratio_after: float_sqrt_ratio_to_fixed(BigInt::from_unsigned_bytes_be(
|
|
||||||
&data[100..112],
|
|
||||||
)),
|
|
||||||
tick_after: i32::from_be_bytes(data[112..116].try_into().unwrap()),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
} else if let Some(ev) = abi_events::PositionUpdated::match_and_decode(log) {
|
|
||||||
(
|
|
||||||
ev.pool_id.to_vec(),
|
|
||||||
Event::PositionUpdated(PositionUpdated {
|
|
||||||
lower: ev.params.1 .0.to_i32(),
|
|
||||||
upper: ev.params.1 .1.to_i32(),
|
|
||||||
liquidity_delta: ev.params.2.to_signed_bytes_be(),
|
|
||||||
delta0: ev.delta0.to_signed_bytes_be(),
|
|
||||||
delta1: ev.delta1.to_signed_bytes_be(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
} else if let Some(ev) = abi_events::PositionFeesCollected::match_and_decode(log) {
|
|
||||||
(
|
|
||||||
ev.pool_id.to_vec(),
|
|
||||||
Event::PositionFeesCollected(PositionFeesCollected {
|
|
||||||
amount0: ev.amount0.to_bytes_be().1,
|
|
||||||
amount1: ev.amount1.to_bytes_be().1,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
} else if let Some(ev) = abi_events::PoolInitialized::match_and_decode(log) {
|
|
||||||
let pool_config = PoolConfig::from(ev.pool_key.2);
|
|
||||||
|
|
||||||
let extension = {
|
if extension == Address::zero().as_bytes() {
|
||||||
let extension = pool_config.extension;
|
Extension::Base
|
||||||
|
} else if extension == config.oracle {
|
||||||
|
Extension::Oracle
|
||||||
|
} else if extension == config.twamm {
|
||||||
|
Extension::Twamm
|
||||||
|
} else if extension == config.mev_resist {
|
||||||
|
Extension::MevResist
|
||||||
|
} else {
|
||||||
|
Extension::Unknown
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if extension == Address::zero().as_bytes() {
|
(
|
||||||
Extension::Base
|
ev.pool_id.to_vec(),
|
||||||
} else if extension == config.oracle {
|
Event::PoolInitialized(PoolInitialized {
|
||||||
Extension::Oracle
|
token0: ev.pool_key.0,
|
||||||
} else {
|
token1: ev.pool_key.1,
|
||||||
Extension::Unknown
|
config: ev.pool_key.2.to_vec(),
|
||||||
}
|
tick: ev.tick.to_i32(),
|
||||||
};
|
sqrt_ratio: float_sqrt_ratio_to_fixed(ev.sqrt_ratio),
|
||||||
|
extension: extension.into(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
} else if log.address == config.twamm {
|
||||||
|
if log.topics.is_empty() {
|
||||||
|
let data = &log.data;
|
||||||
|
|
||||||
(
|
assert!(data.len() == 60, "virtual orders executed event data length mismatch");
|
||||||
ev.pool_id.to_vec(),
|
|
||||||
Event::PoolInitialized(PoolInitialized {
|
(
|
||||||
token0: ev.pool_key.0,
|
data[0..32].to_vec(),
|
||||||
token1: ev.pool_key.1,
|
Event::VirtualOrdersExecuted(VirtualOrdersExecuted {
|
||||||
config: ev.pool_key.2.to_vec(),
|
token0_sale_rate: data[32..46].to_vec(),
|
||||||
tick: ev.tick.to_i32(),
|
token1_sale_rate: data[46..60].to_vec(),
|
||||||
sqrt_ratio: float_sqrt_ratio_to_fixed(ev.sqrt_ratio),
|
}),
|
||||||
extension: extension.into(),
|
)
|
||||||
}),
|
} else if let Some(ev) = twamm_events::OrderUpdated::match_and_decode(log) {
|
||||||
)
|
let key = ev.order_key;
|
||||||
} else if let Some(ev) = abi_events::FeesAccumulated::match_and_decode(log) {
|
|
||||||
(
|
(
|
||||||
ev.pool_id.to_vec(),
|
PoolKey::from_order_key(&key, &log.address).into_pool_id(),
|
||||||
Event::FeesAccumulated(FeesAccumulated {
|
Event::OrderUpdated(OrderUpdated {
|
||||||
amount0: ev.amount0.to_bytes_be().1,
|
order_key: Some(OrderKey {
|
||||||
amount1: ev.amount1.to_bytes_be().1,
|
sell_token: key.0,
|
||||||
}),
|
buy_token: key.1,
|
||||||
)
|
fee: key.2.to_u64(),
|
||||||
|
start_time: key.3.to_u64(),
|
||||||
|
end_time: key.4.to_u64(),
|
||||||
|
}),
|
||||||
|
sale_rate_delta: ev.sale_rate_delta.to_signed_bytes_be(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,10 +8,13 @@ use tycho_substreams::models::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
pb::ekubo::{
|
pb::ekubo::{
|
||||||
block_transaction_events::transaction_events::{pool_log::Event, PoolLog},
|
block_transaction_events::transaction_events::{
|
||||||
|
pool_log::{pool_initialized::Extension, Event},
|
||||||
|
PoolLog,
|
||||||
|
},
|
||||||
BlockTransactionEvents,
|
BlockTransactionEvents,
|
||||||
},
|
},
|
||||||
pool_config::PoolConfig,
|
pool_key::PoolConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[substreams::handlers::map]
|
#[substreams::handlers::map]
|
||||||
@@ -25,7 +28,7 @@ fn map_components(block_tx_events: BlockTransactionEvents) -> BlockChanges {
|
|||||||
let (components, entities, balance_changes): (Vec<_>, Vec<_>, Vec<_>) = tx_events
|
let (components, entities, balance_changes): (Vec<_>, Vec<_>, Vec<_>) = tx_events
|
||||||
.pool_logs
|
.pool_logs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(maybe_create_component)
|
.filter_map(|log| maybe_create_component(log, block_tx_events.timestamp))
|
||||||
.multiunzip();
|
.multiunzip();
|
||||||
|
|
||||||
(!components.is_empty()).then(|| TransactionChanges {
|
(!components.is_empty()).then(|| TransactionChanges {
|
||||||
@@ -45,8 +48,56 @@ fn map_components(block_tx_events: BlockTransactionEvents) -> BlockChanges {
|
|||||||
|
|
||||||
fn maybe_create_component(
|
fn maybe_create_component(
|
||||||
log: PoolLog,
|
log: PoolLog,
|
||||||
|
timestamp: u64,
|
||||||
) -> Option<(ProtocolComponent, EntityChanges, Vec<BalanceChange>)> {
|
) -> Option<(ProtocolComponent, EntityChanges, Vec<BalanceChange>)> {
|
||||||
if let Event::PoolInitialized(pi) = log.event.unwrap() {
|
if let Event::PoolInitialized(pi) = log.event.unwrap() {
|
||||||
|
let entity_attributes = (pi.extension() == Extension::Twamm)
|
||||||
|
.then(|| {
|
||||||
|
[
|
||||||
|
Attribute {
|
||||||
|
change: ChangeType::Creation.into(),
|
||||||
|
name: "token0_sale_rate".to_string(),
|
||||||
|
value: vec![],
|
||||||
|
},
|
||||||
|
Attribute {
|
||||||
|
change: ChangeType::Creation.into(),
|
||||||
|
name: "token1_sale_rate".to_string(),
|
||||||
|
value: vec![],
|
||||||
|
},
|
||||||
|
Attribute {
|
||||||
|
change: ChangeType::Creation.into(),
|
||||||
|
name: "last_execution_time".to_string(),
|
||||||
|
value: timestamp.to_be_bytes().to_vec(),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.chain([
|
||||||
|
Attribute {
|
||||||
|
change: ChangeType::Creation.into(),
|
||||||
|
name: "liquidity".to_string(),
|
||||||
|
value: 0_u128.to_be_bytes().to_vec(),
|
||||||
|
},
|
||||||
|
Attribute {
|
||||||
|
change: ChangeType::Creation.into(),
|
||||||
|
name: "tick".to_string(),
|
||||||
|
value: pi.tick.to_be_bytes().to_vec(),
|
||||||
|
},
|
||||||
|
Attribute {
|
||||||
|
change: ChangeType::Creation.into(),
|
||||||
|
name: "sqrt_ratio".to_string(),
|
||||||
|
value: pi.sqrt_ratio,
|
||||||
|
},
|
||||||
|
Attribute {
|
||||||
|
change: ChangeType::Creation.into(),
|
||||||
|
name: "balance_owner".to_string(), /* TODO: We should use AccountBalances
|
||||||
|
* instead */
|
||||||
|
value: hex!("e0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444").to_vec(),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.collect();
|
||||||
|
|
||||||
let config = PoolConfig::from(<[u8; 32]>::try_from(pi.config).unwrap());
|
let config = PoolConfig::from(<[u8; 32]>::try_from(pi.config).unwrap());
|
||||||
let component_id = log.pool_id.to_hex();
|
let component_id = log.pool_id.to_hex();
|
||||||
|
|
||||||
@@ -77,17 +128,23 @@ fn maybe_create_component(
|
|||||||
Attribute {
|
Attribute {
|
||||||
change: ChangeType::Creation.into(),
|
change: ChangeType::Creation.into(),
|
||||||
name: "fee".to_string(),
|
name: "fee".to_string(),
|
||||||
value: config.fee,
|
value: config.fee.to_be_bytes().to_vec(),
|
||||||
},
|
},
|
||||||
Attribute {
|
Attribute {
|
||||||
change: ChangeType::Creation.into(),
|
change: ChangeType::Creation.into(),
|
||||||
name: "tick_spacing".to_string(),
|
name: "tick_spacing".to_string(),
|
||||||
value: config.tick_spacing,
|
value: config
|
||||||
|
.tick_spacing
|
||||||
|
.to_be_bytes()
|
||||||
|
.to_vec(),
|
||||||
},
|
},
|
||||||
Attribute {
|
Attribute {
|
||||||
change: ChangeType::Creation.into(),
|
change: ChangeType::Creation.into(),
|
||||||
name: "extension".to_string(),
|
name: "extension".to_string(),
|
||||||
value: config.extension,
|
value: config
|
||||||
|
.extension
|
||||||
|
.to_fixed_bytes()
|
||||||
|
.to_vec(),
|
||||||
},
|
},
|
||||||
Attribute {
|
Attribute {
|
||||||
change: ChangeType::Creation.into(),
|
change: ChangeType::Creation.into(),
|
||||||
@@ -96,32 +153,7 @@ fn maybe_create_component(
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
EntityChanges {
|
EntityChanges { component_id: component_id.clone(), attributes: entity_attributes },
|
||||||
component_id: component_id.clone(),
|
|
||||||
attributes: vec![
|
|
||||||
Attribute {
|
|
||||||
change: ChangeType::Creation.into(),
|
|
||||||
name: "liquidity".to_string(),
|
|
||||||
value: 0_u128.to_be_bytes().to_vec(),
|
|
||||||
},
|
|
||||||
Attribute {
|
|
||||||
change: ChangeType::Creation.into(),
|
|
||||||
name: "tick".to_string(),
|
|
||||||
value: pi.tick.to_be_bytes().to_vec(),
|
|
||||||
},
|
|
||||||
Attribute {
|
|
||||||
change: ChangeType::Creation.into(),
|
|
||||||
name: "sqrt_ratio".to_string(),
|
|
||||||
value: pi.sqrt_ratio,
|
|
||||||
},
|
|
||||||
Attribute {
|
|
||||||
change: ChangeType::Creation.into(),
|
|
||||||
name: "balance_owner".to_string(), /* TODO: We should use AccountBalances
|
|
||||||
* instead */
|
|
||||||
value: hex!("e0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444").to_vec(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
vec![
|
vec![
|
||||||
BalanceChange {
|
BalanceChange {
|
||||||
component_id: component_id.clone().into_bytes(),
|
component_id: component_id.clone().into_bytes(),
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
use substreams::scalar::BigInt;
|
||||||
|
|
||||||
|
use crate::pb::ekubo::{
|
||||||
|
block_transaction_events::transaction_events::pool_log::Event, BlockTransactionEvents,
|
||||||
|
OrderSaleRateDelta, OrderSaleRateDeltas,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[substreams::handlers::map]
|
||||||
|
pub fn map_order_sale_rate_deltas(block_tx_events: BlockTransactionEvents) -> OrderSaleRateDeltas {
|
||||||
|
OrderSaleRateDeltas {
|
||||||
|
deltas: block_tx_events
|
||||||
|
.block_transaction_events
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|tx_events| {
|
||||||
|
let tx = tx_events.transaction;
|
||||||
|
|
||||||
|
tx_events
|
||||||
|
.pool_logs
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(move |log| {
|
||||||
|
let tx = tx.clone();
|
||||||
|
|
||||||
|
order_sale_rate_deltas(log.event.unwrap())
|
||||||
|
.into_iter()
|
||||||
|
.map(move |partial| OrderSaleRateDelta {
|
||||||
|
pool_id: log.pool_id.clone(),
|
||||||
|
time: partial.time,
|
||||||
|
sale_rate_delta: partial.sale_rate_delta,
|
||||||
|
is_token1: partial.is_token1,
|
||||||
|
ordinal: log.ordinal,
|
||||||
|
transaction: tx.clone(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PartialOrderSaleRateDelta {
|
||||||
|
time: u64,
|
||||||
|
sale_rate_delta: Vec<u8>,
|
||||||
|
is_token1: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn order_sale_rate_deltas(ev: Event) -> Vec<PartialOrderSaleRateDelta> {
|
||||||
|
match ev {
|
||||||
|
Event::OrderUpdated(ev) => {
|
||||||
|
let key = ev.order_key.unwrap();
|
||||||
|
|
||||||
|
let is_token1 = key.sell_token > key.buy_token;
|
||||||
|
let sale_rate_delta = ev.sale_rate_delta;
|
||||||
|
|
||||||
|
vec![
|
||||||
|
PartialOrderSaleRateDelta {
|
||||||
|
time: key.start_time,
|
||||||
|
sale_rate_delta: sale_rate_delta.clone(),
|
||||||
|
is_token1,
|
||||||
|
},
|
||||||
|
PartialOrderSaleRateDelta {
|
||||||
|
time: key.end_time,
|
||||||
|
sale_rate_delta: BigInt::from_signed_bytes_be(&sale_rate_delta)
|
||||||
|
.neg()
|
||||||
|
.to_signed_bytes_be(),
|
||||||
|
is_token1,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
_ => vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
use crate::pb::ekubo::{
|
||||||
|
block_transaction_events::transaction_events::{pool_log::Event, PoolLog},
|
||||||
|
BlockTransactionEvents, ChangeType, SaleRateChange, SaleRateChanges,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[substreams::handlers::map]
|
||||||
|
pub fn map_sale_rate_changes(block_tx_events: BlockTransactionEvents) -> SaleRateChanges {
|
||||||
|
SaleRateChanges {
|
||||||
|
changes: block_tx_events
|
||||||
|
.block_transaction_events
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|tx_events| {
|
||||||
|
tx_events
|
||||||
|
.pool_logs
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(move |log| {
|
||||||
|
maybe_sale_rate_change(&log, block_tx_events.timestamp).map(|partial| {
|
||||||
|
SaleRateChange {
|
||||||
|
change_type: partial.change_type.into(),
|
||||||
|
pool_id: log.pool_id,
|
||||||
|
token0_value: partial.token0_value,
|
||||||
|
token1_value: partial.token1_value,
|
||||||
|
ordinal: log.ordinal,
|
||||||
|
transaction: tx_events.transaction.clone(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PartialSaleRateChange {
|
||||||
|
token0_value: Vec<u8>,
|
||||||
|
token1_value: Vec<u8>,
|
||||||
|
change_type: ChangeType,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn maybe_sale_rate_change(log: &PoolLog, timestamp: u64) -> Option<PartialSaleRateChange> {
|
||||||
|
match log.event.as_ref().unwrap() {
|
||||||
|
Event::VirtualOrdersExecuted(ev) => Some(PartialSaleRateChange {
|
||||||
|
change_type: ChangeType::Absolute,
|
||||||
|
token0_value: ev.token0_sale_rate.clone(),
|
||||||
|
token1_value: ev.token1_sale_rate.clone(),
|
||||||
|
}),
|
||||||
|
Event::OrderUpdated(ev) => {
|
||||||
|
// A virtual order execution always happens before an order update
|
||||||
|
let last_execution_time = timestamp;
|
||||||
|
|
||||||
|
let key = ev.order_key.as_ref().unwrap();
|
||||||
|
|
||||||
|
(last_execution_time >= key.start_time && last_execution_time < key.end_time).then(
|
||||||
|
|| {
|
||||||
|
let (token0_sale_rate_delta, token1_sale_rate_delta) =
|
||||||
|
if key.sell_token > key.buy_token {
|
||||||
|
(vec![], ev.sale_rate_delta.clone())
|
||||||
|
} else {
|
||||||
|
(ev.sale_rate_delta.clone(), vec![])
|
||||||
|
};
|
||||||
|
|
||||||
|
PartialSaleRateChange {
|
||||||
|
change_type: ChangeType::Delta,
|
||||||
|
token0_value: token0_sale_rate_delta,
|
||||||
|
token1_value: token1_sale_rate_delta,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ use crate::pb::ekubo::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[substreams::handlers::map]
|
#[substreams::handlers::map]
|
||||||
pub fn map_tick_changes(block_tx_events: BlockTransactionEvents) -> TickDeltas {
|
pub fn map_tick_deltas(block_tx_events: BlockTransactionEvents) -> TickDeltas {
|
||||||
TickDeltas {
|
TickDeltas {
|
||||||
deltas: block_tx_events
|
deltas: block_tx_events
|
||||||
.block_transaction_events
|
.block_transaction_events
|
||||||
@@ -4,7 +4,7 @@ use substreams_helper::hex::Hexable;
|
|||||||
|
|
||||||
use crate::pb::ekubo::{
|
use crate::pb::ekubo::{
|
||||||
block_transaction_events::transaction_events::{pool_log::Event, PoolLog},
|
block_transaction_events::transaction_events::{pool_log::Event, PoolLog},
|
||||||
BlockTransactionEvents, LiquidityChange, LiquidityChangeType, LiquidityChanges,
|
BlockTransactionEvents, ChangeType, LiquidityChange, LiquidityChanges,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[substreams::handlers::map]
|
#[substreams::handlers::map]
|
||||||
@@ -40,7 +40,7 @@ pub fn map_liquidity_changes(
|
|||||||
|
|
||||||
struct PartialLiquidityChange {
|
struct PartialLiquidityChange {
|
||||||
value: Vec<u8>,
|
value: Vec<u8>,
|
||||||
change_type: LiquidityChangeType,
|
change_type: ChangeType,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn maybe_liquidity_change(
|
fn maybe_liquidity_change(
|
||||||
@@ -50,7 +50,7 @@ fn maybe_liquidity_change(
|
|||||||
match log.event.as_ref().unwrap() {
|
match log.event.as_ref().unwrap() {
|
||||||
Event::Swapped(swapped) => Some(PartialLiquidityChange {
|
Event::Swapped(swapped) => Some(PartialLiquidityChange {
|
||||||
value: swapped.liquidity_after.clone(),
|
value: swapped.liquidity_after.clone(),
|
||||||
change_type: LiquidityChangeType::Absolute,
|
change_type: ChangeType::Absolute,
|
||||||
}),
|
}),
|
||||||
Event::PositionUpdated(position_updated) => {
|
Event::PositionUpdated(position_updated) => {
|
||||||
let current_tick = current_tick_store
|
let current_tick = current_tick_store
|
||||||
@@ -61,7 +61,7 @@ fn maybe_liquidity_change(
|
|||||||
current_tick < position_updated.upper.into())
|
current_tick < position_updated.upper.into())
|
||||||
.then(|| PartialLiquidityChange {
|
.then(|| PartialLiquidityChange {
|
||||||
value: position_updated.liquidity_delta.clone(),
|
value: position_updated.liquidity_delta.clone(),
|
||||||
change_type: LiquidityChangeType::Delta,
|
change_type: ChangeType::Delta,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
use substreams::{
|
||||||
|
scalar::BigInt,
|
||||||
|
store::{StoreSetSum, StoreSetSumBigInt},
|
||||||
|
};
|
||||||
|
use substreams_helper::hex::Hexable;
|
||||||
|
|
||||||
|
use crate::{pb::ekubo::SaleRateChanges, store::store_method_from_change_type};
|
||||||
|
|
||||||
|
#[substreams::handlers::store]
|
||||||
|
pub fn store_active_sale_rates(sale_rate_changes: SaleRateChanges, store: StoreSetSumBigInt) {
|
||||||
|
sale_rate_changes
|
||||||
|
.changes
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|changes| {
|
||||||
|
let pool_id = changes.pool_id.to_hex();
|
||||||
|
|
||||||
|
let store_method = store_method_from_change_type(changes.change_type());
|
||||||
|
|
||||||
|
store_method(
|
||||||
|
&store,
|
||||||
|
changes.ordinal,
|
||||||
|
format!("pool:{pool_id}:token0"),
|
||||||
|
BigInt::from_signed_bytes_be(&changes.token0_value),
|
||||||
|
);
|
||||||
|
store_method(
|
||||||
|
&store,
|
||||||
|
changes.ordinal,
|
||||||
|
format!("pool:{pool_id}:token1"),
|
||||||
|
BigInt::from_signed_bytes_be(&changes.token1_value),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
use substreams::{
|
||||||
|
scalar::BigInt,
|
||||||
|
store::{StoreAdd, StoreAddBigInt, StoreNew},
|
||||||
|
};
|
||||||
|
use substreams_helper::hex::Hexable;
|
||||||
|
|
||||||
|
use crate::pb::ekubo::OrderSaleRateDeltas;
|
||||||
|
|
||||||
|
#[substreams::handlers::store]
|
||||||
|
pub fn store_order_sale_rates(order_sale_rate_deltas: OrderSaleRateDeltas, store: StoreAddBigInt) {
|
||||||
|
order_sale_rate_deltas
|
||||||
|
.deltas
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|delta| {
|
||||||
|
let pool_id = delta.pool_id.to_hex();
|
||||||
|
let time = delta.time;
|
||||||
|
let token = if delta.is_token1 { "token1" } else { "token0" };
|
||||||
|
|
||||||
|
store.add(
|
||||||
|
delta.ordinal,
|
||||||
|
format!("pool:{pool_id}:{token}:time:{time}:"),
|
||||||
|
BigInt::from_signed_bytes_be(&delta.sale_rate_delta),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -57,54 +57,28 @@ struct ReducedBalanceDelta {
|
|||||||
|
|
||||||
fn balance_deltas(ev: Event, pool_details: PoolDetails) -> Vec<ReducedBalanceDelta> {
|
fn balance_deltas(ev: Event, pool_details: PoolDetails) -> Vec<ReducedBalanceDelta> {
|
||||||
match ev {
|
match ev {
|
||||||
Event::Swapped(swapped) => {
|
Event::Swapped(ev) => vec![
|
||||||
vec![
|
ReducedBalanceDelta { token: pool_details.token0, delta: ev.delta0 },
|
||||||
ReducedBalanceDelta { token: pool_details.token0, delta: swapped.delta0 },
|
ReducedBalanceDelta { token: pool_details.token1, delta: ev.delta1 },
|
||||||
ReducedBalanceDelta { token: pool_details.token1, delta: swapped.delta1 },
|
],
|
||||||
]
|
Event::PositionUpdated(ev) => vec![
|
||||||
}
|
ReducedBalanceDelta {
|
||||||
Event::PositionUpdated(position_updated) => {
|
token: pool_details.token0,
|
||||||
vec![
|
delta: adjust_delta_by_fee(
|
||||||
ReducedBalanceDelta {
|
BigInt::from_signed_bytes_be(&ev.delta0),
|
||||||
token: pool_details.token0,
|
pool_details.fee,
|
||||||
delta: adjust_delta_by_fee(
|
)
|
||||||
BigInt::from_signed_bytes_be(&position_updated.delta0),
|
.to_signed_bytes_be(),
|
||||||
pool_details.fee,
|
},
|
||||||
)
|
ReducedBalanceDelta {
|
||||||
.to_signed_bytes_be(),
|
token: pool_details.token1,
|
||||||
},
|
delta: adjust_delta_by_fee(
|
||||||
ReducedBalanceDelta {
|
BigInt::from_signed_bytes_be(&ev.delta1),
|
||||||
token: pool_details.token1,
|
pool_details.fee,
|
||||||
delta: adjust_delta_by_fee(
|
)
|
||||||
BigInt::from_signed_bytes_be(&position_updated.delta1),
|
.to_signed_bytes_be(),
|
||||||
pool_details.fee,
|
},
|
||||||
)
|
],
|
||||||
.to_signed_bytes_be(),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
Event::PositionFeesCollected(position_fees_collected) => {
|
|
||||||
vec![
|
|
||||||
ReducedBalanceDelta {
|
|
||||||
token: pool_details.token0,
|
|
||||||
delta: BigInt::from_unsigned_bytes_be(&position_fees_collected.amount0)
|
|
||||||
.neg()
|
|
||||||
.to_signed_bytes_be(),
|
|
||||||
},
|
|
||||||
ReducedBalanceDelta {
|
|
||||||
token: pool_details.token1,
|
|
||||||
delta: BigInt::from_unsigned_bytes_be(&position_fees_collected.amount1)
|
|
||||||
.neg()
|
|
||||||
.to_signed_bytes_be(),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
Event::FeesAccumulated(fees_accumulated) => {
|
|
||||||
vec![
|
|
||||||
ReducedBalanceDelta { token: pool_details.token0, delta: fees_accumulated.amount0 },
|
|
||||||
ReducedBalanceDelta { token: pool_details.token1, delta: fees_accumulated.amount1 },
|
|
||||||
]
|
|
||||||
}
|
|
||||||
_ => vec![],
|
_ => vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,8 +87,10 @@ fn balance_deltas(ev: Event, pool_details: PoolDetails) -> Vec<ReducedBalanceDel
|
|||||||
// here (i.e. subtract from the component's balance)
|
// here (i.e. subtract from the component's balance)
|
||||||
fn adjust_delta_by_fee(delta: BigInt, fee: u64) -> BigInt {
|
fn adjust_delta_by_fee(delta: BigInt, fee: u64) -> BigInt {
|
||||||
if delta < BigInt::zero() {
|
if delta < BigInt::zero() {
|
||||||
let denom = BigInt::from_signed_bytes_be(&hex!("0100000000000000000000000000000000"));
|
let denom = BigInt::from_signed_bytes_be(&hex!("010000000000000000"));
|
||||||
(delta * denom.clone()) / (denom - fee)
|
let (quotient, remainder) = (delta * denom.clone()).div_rem(&(denom - fee));
|
||||||
|
|
||||||
|
quotient - (!remainder.is_zero()) as u8
|
||||||
} else {
|
} else {
|
||||||
delta
|
delta
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
use substreams::{
|
||||||
|
scalar::BigInt,
|
||||||
|
store::{StoreSetSum, StoreSetSumBigInt},
|
||||||
|
};
|
||||||
|
use substreams_helper::hex::Hexable;
|
||||||
|
|
||||||
|
use crate::{pb::ekubo::LiquidityChanges, store::store_method_from_change_type};
|
||||||
|
|
||||||
|
#[substreams::handlers::store]
|
||||||
|
pub fn store_active_liquidities(liquidity_changes: LiquidityChanges, store: StoreSetSumBigInt) {
|
||||||
|
liquidity_changes
|
||||||
|
.changes
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|changes| {
|
||||||
|
store_method_from_change_type(changes.change_type())(
|
||||||
|
&store,
|
||||||
|
changes.ordinal,
|
||||||
|
format!("pool:{}", changes.pool_id.to_hex()),
|
||||||
|
BigInt::from_signed_bytes_be(&changes.value),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
use substreams::{
|
|
||||||
scalar::BigInt,
|
|
||||||
store::{StoreSetSum, StoreSetSumBigInt},
|
|
||||||
};
|
|
||||||
use substreams_helper::hex::Hexable;
|
|
||||||
|
|
||||||
use crate::pb::ekubo::{LiquidityChangeType, LiquidityChanges};
|
|
||||||
|
|
||||||
#[substreams::handlers::store]
|
|
||||||
pub fn store_liquidities(liquidity_changes: LiquidityChanges, store: StoreSetSumBigInt) {
|
|
||||||
liquidity_changes
|
|
||||||
.changes
|
|
||||||
.into_iter()
|
|
||||||
.for_each(|changes| match changes.change_type() {
|
|
||||||
LiquidityChangeType::Delta => {
|
|
||||||
store.sum(
|
|
||||||
changes.ordinal,
|
|
||||||
format!("pool:{0}", changes.pool_id.to_hex()),
|
|
||||||
BigInt::from_signed_bytes_be(&changes.value),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
LiquidityChangeType::Absolute => {
|
|
||||||
store.set(
|
|
||||||
changes.ordinal,
|
|
||||||
format!("pool:{0}", changes.pool_id.to_hex()),
|
|
||||||
BigInt::from_signed_bytes_be(&changes.value),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -14,7 +14,7 @@ use tycho_substreams::{
|
|||||||
|
|
||||||
use crate::pb::ekubo::{
|
use crate::pb::ekubo::{
|
||||||
block_transaction_events::transaction_events::pool_log::Event, BlockTransactionEvents,
|
block_transaction_events::transaction_events::pool_log::Event, BlockTransactionEvents,
|
||||||
LiquidityChanges, TickDeltas,
|
LiquidityChanges, OrderSaleRateDeltas, SaleRateChanges, TickDeltas,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Aggregates protocol components and balance changes by transaction.
|
/// Aggregates protocol components and balance changes by transaction.
|
||||||
@@ -30,8 +30,12 @@ fn map_protocol_changes(
|
|||||||
balances_store_deltas: StoreDeltas,
|
balances_store_deltas: StoreDeltas,
|
||||||
ticks_map_deltas: TickDeltas,
|
ticks_map_deltas: TickDeltas,
|
||||||
ticks_store_deltas: StoreDeltas,
|
ticks_store_deltas: StoreDeltas,
|
||||||
|
order_sale_rate_map_deltas: OrderSaleRateDeltas,
|
||||||
|
order_sale_rate_store_deltas: StoreDeltas,
|
||||||
liquidity_changes: LiquidityChanges,
|
liquidity_changes: LiquidityChanges,
|
||||||
liquidity_store_deltas: StoreDeltas,
|
liquidity_store_deltas: StoreDeltas,
|
||||||
|
sale_rate_changes: SaleRateChanges,
|
||||||
|
sale_rate_store_deltas: StoreDeltas,
|
||||||
) -> Result<BlockChanges, substreams::errors::Error> {
|
) -> Result<BlockChanges, substreams::errors::Error> {
|
||||||
let mut transaction_changes: HashMap<_, TransactionChangesBuilder> = HashMap::new();
|
let mut transaction_changes: HashMap<_, TransactionChangesBuilder> = HashMap::new();
|
||||||
|
|
||||||
@@ -90,21 +94,11 @@ fn map_protocol_changes(
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.zip(ticks_map_deltas.deltas)
|
.zip(ticks_map_deltas.deltas)
|
||||||
.for_each(|(store_delta, tick_delta)| {
|
.for_each(|(store_delta, tick_delta)| {
|
||||||
let new_value_bigint = BigInt::from_store_bytes(&store_delta.new_value);
|
let (old_value, new_value) = (
|
||||||
|
BigInt::from_store_bytes(&store_delta.old_value),
|
||||||
|
BigInt::from_store_bytes(&store_delta.new_value),
|
||||||
|
);
|
||||||
|
|
||||||
let is_creation = BigInt::from_store_bytes(&store_delta.old_value).is_zero();
|
|
||||||
|
|
||||||
let attribute = Attribute {
|
|
||||||
name: format!("ticks/{}", tick_delta.tick_index),
|
|
||||||
value: new_value_bigint.to_signed_bytes_be(),
|
|
||||||
change: if is_creation {
|
|
||||||
ChangeType::Creation.into()
|
|
||||||
} else if new_value_bigint.is_zero() {
|
|
||||||
ChangeType::Deletion.into()
|
|
||||||
} else {
|
|
||||||
ChangeType::Update.into()
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let tx = tick_delta.transaction.unwrap();
|
let tx = tick_delta.transaction.unwrap();
|
||||||
let builder = transaction_changes
|
let builder = transaction_changes
|
||||||
.entry(tx.index)
|
.entry(tx.index)
|
||||||
@@ -112,7 +106,39 @@ fn map_protocol_changes(
|
|||||||
|
|
||||||
builder.add_entity_change(&EntityChanges {
|
builder.add_entity_change(&EntityChanges {
|
||||||
component_id: tick_delta.pool_id.to_hex(),
|
component_id: tick_delta.pool_id.to_hex(),
|
||||||
attributes: vec![attribute],
|
attributes: vec![Attribute {
|
||||||
|
name: format!("ticks/{}", tick_delta.tick_index),
|
||||||
|
value: new_value.to_signed_bytes_be(),
|
||||||
|
change: change_type_from_delta(&old_value, &new_value).into(),
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// TWAMM order sale rate deltas
|
||||||
|
order_sale_rate_store_deltas
|
||||||
|
.deltas
|
||||||
|
.into_iter()
|
||||||
|
.zip(order_sale_rate_map_deltas.deltas)
|
||||||
|
.for_each(|(store_delta, sale_rate_delta)| {
|
||||||
|
let tx = sale_rate_delta.transaction.unwrap();
|
||||||
|
let builder = transaction_changes
|
||||||
|
.entry(tx.index)
|
||||||
|
.or_insert_with(|| TransactionChangesBuilder::new(&tx.into()));
|
||||||
|
|
||||||
|
let (old_value, new_value) = (
|
||||||
|
BigInt::from_store_bytes(&store_delta.old_value),
|
||||||
|
BigInt::from_store_bytes(&store_delta.new_value),
|
||||||
|
);
|
||||||
|
|
||||||
|
let token = if sale_rate_delta.is_token1 { "token1" } else { "token0" };
|
||||||
|
|
||||||
|
builder.add_entity_change(&EntityChanges {
|
||||||
|
component_id: sale_rate_delta.pool_id.to_hex(),
|
||||||
|
attributes: vec![Attribute {
|
||||||
|
name: format!("orders/{}/{}", token, sale_rate_delta.time),
|
||||||
|
value: new_value.to_signed_bytes_be(),
|
||||||
|
change: change_type_from_delta(&old_value, &new_value).into(),
|
||||||
|
}],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -127,22 +153,50 @@ fn map_protocol_changes(
|
|||||||
.entry(tx.index)
|
.entry(tx.index)
|
||||||
.or_insert_with(|| TransactionChangesBuilder::new(&tx.into()));
|
.or_insert_with(|| TransactionChangesBuilder::new(&tx.into()));
|
||||||
|
|
||||||
let new_value_bigint = BigInt::from_str(key::segment_at(
|
|
||||||
&String::from_utf8(store_delta.new_value).unwrap(),
|
|
||||||
1,
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
builder.add_entity_change(&EntityChanges {
|
builder.add_entity_change(&EntityChanges {
|
||||||
component_id: change.pool_id.to_hex(),
|
component_id: change.pool_id.to_hex(),
|
||||||
attributes: vec![Attribute {
|
attributes: vec![Attribute {
|
||||||
name: "liquidity".to_string(),
|
name: "liquidity".to_string(),
|
||||||
value: new_value_bigint.to_signed_bytes_be(),
|
value: bigint_from_set_sum_store_delta_value(store_delta.new_value)
|
||||||
|
.to_signed_bytes_be(),
|
||||||
change: ChangeType::Update.into(),
|
change: ChangeType::Update.into(),
|
||||||
}],
|
}],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// TWAMM active sale rates
|
||||||
|
sale_rate_store_deltas
|
||||||
|
.deltas
|
||||||
|
.chunks(2)
|
||||||
|
.zip(sale_rate_changes.changes)
|
||||||
|
.for_each(|(store_deltas, change)| {
|
||||||
|
let tx = change.transaction.unwrap();
|
||||||
|
let builder = transaction_changes
|
||||||
|
.entry(tx.index)
|
||||||
|
.or_insert_with(|| TransactionChangesBuilder::new(&tx.into()));
|
||||||
|
|
||||||
|
let (token0_sale_rate, token1_sale_rate) = (
|
||||||
|
bigint_from_set_sum_store_delta_value(store_deltas[0].new_value.clone()),
|
||||||
|
bigint_from_set_sum_store_delta_value(store_deltas[1].new_value.clone()),
|
||||||
|
);
|
||||||
|
|
||||||
|
builder.add_entity_change(&EntityChanges {
|
||||||
|
component_id: change.pool_id.to_hex(),
|
||||||
|
attributes: vec![
|
||||||
|
Attribute {
|
||||||
|
name: "token0_sale_rate".to_string(),
|
||||||
|
value: token0_sale_rate.to_bytes_be().1,
|
||||||
|
change: ChangeType::Update.into(),
|
||||||
|
},
|
||||||
|
Attribute {
|
||||||
|
name: "token1_sale_rate".to_string(),
|
||||||
|
value: token1_sale_rate.to_bytes_be().1,
|
||||||
|
change: ChangeType::Update.into(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Remaining event changes not subject to special treatment
|
// Remaining event changes not subject to special treatment
|
||||||
block_tx_events
|
block_tx_events
|
||||||
.block_transaction_events
|
.block_transaction_events
|
||||||
@@ -156,12 +210,17 @@ fn map_protocol_changes(
|
|||||||
.flat_map(move |log| {
|
.flat_map(move |log| {
|
||||||
let tx = tx.clone();
|
let tx = tx.clone();
|
||||||
|
|
||||||
maybe_attribute_updates(log.event.unwrap()).map(|attrs| {
|
maybe_attribute_updates(log.event.unwrap(), block_tx_events.timestamp).map(
|
||||||
(
|
|attrs| {
|
||||||
tx,
|
(
|
||||||
EntityChanges { component_id: log.pool_id.to_hex(), attributes: attrs },
|
tx,
|
||||||
)
|
EntityChanges {
|
||||||
})
|
component_id: log.pool_id.to_hex(),
|
||||||
|
attributes: attrs,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.for_each(|(tx, entity_changes)| {
|
.for_each(|(tx, entity_changes)| {
|
||||||
@@ -181,23 +240,39 @@ fn map_protocol_changes(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn maybe_attribute_updates(ev: Event) -> Option<Vec<Attribute>> {
|
fn maybe_attribute_updates(ev: Event, timestamp: u64) -> Option<Vec<Attribute>> {
|
||||||
match ev {
|
match ev {
|
||||||
Event::Swapped(swapped) => Some(vec![
|
Event::Swapped(ev) => Some(vec![
|
||||||
Attribute {
|
Attribute {
|
||||||
name: "tick".into(),
|
name: "tick".into(),
|
||||||
value: swapped
|
value: ev.tick_after.to_be_bytes().to_vec(),
|
||||||
.tick_after
|
|
||||||
.to_be_bytes()
|
|
||||||
.to_vec(),
|
|
||||||
change: ChangeType::Update.into(),
|
change: ChangeType::Update.into(),
|
||||||
},
|
},
|
||||||
Attribute {
|
Attribute {
|
||||||
name: "sqrt_ratio".into(),
|
name: "sqrt_ratio".into(),
|
||||||
value: swapped.sqrt_ratio_after,
|
value: ev.sqrt_ratio_after,
|
||||||
change: ChangeType::Update.into(),
|
change: ChangeType::Update.into(),
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
|
Event::VirtualOrdersExecuted(_) => Some(vec![Attribute {
|
||||||
|
name: "last_execution_time".to_string(),
|
||||||
|
value: timestamp.to_be_bytes().to_vec(),
|
||||||
|
change: ChangeType::Update.into(),
|
||||||
|
}]),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn change_type_from_delta(old_value: &BigInt, new_value: &BigInt) -> ChangeType {
|
||||||
|
if old_value.is_zero() {
|
||||||
|
ChangeType::Creation
|
||||||
|
} else if new_value.is_zero() {
|
||||||
|
ChangeType::Deletion
|
||||||
|
} else {
|
||||||
|
ChangeType::Update
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bigint_from_set_sum_store_delta_value(value: Vec<u8>) -> BigInt {
|
||||||
|
BigInt::from_str(key::segment_at(&String::from_utf8(value).unwrap(), 1)).unwrap()
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,13 +7,21 @@ mod map_events;
|
|||||||
|
|
||||||
#[path = "2_map_components.rs"]
|
#[path = "2_map_components.rs"]
|
||||||
mod map_components;
|
mod map_components;
|
||||||
#[path = "2_map_tick_changes.rs"]
|
#[path = "2_map_order_sale_rate_deltas.rs"]
|
||||||
mod map_tick_changes;
|
mod map_order_sale_rate_deltas;
|
||||||
|
#[path = "2_map_sale_rate_changes.rs"]
|
||||||
|
mod map_sale_rate_changes;
|
||||||
|
#[path = "2_map_tick_deltas.rs"]
|
||||||
|
mod map_tick_deltas;
|
||||||
#[path = "2_store_active_ticks.rs"]
|
#[path = "2_store_active_ticks.rs"]
|
||||||
mod store_active_ticks;
|
mod store_active_ticks;
|
||||||
|
|
||||||
#[path = "3_map_liquidity_changes.rs"]
|
#[path = "3_map_liquidity_changes.rs"]
|
||||||
mod map_liquidity_changes;
|
mod map_liquidity_changes;
|
||||||
|
#[path = "3_store_active_sale_rates.rs"]
|
||||||
|
mod store_active_sale_rates;
|
||||||
|
#[path = "3_store_order_sale_rates.rs"]
|
||||||
|
mod store_order_sale_rates;
|
||||||
#[path = "3_store_pool_details.rs"]
|
#[path = "3_store_pool_details.rs"]
|
||||||
mod store_pool_details;
|
mod store_pool_details;
|
||||||
#[path = "3_store_tick_liquidities.rs"]
|
#[path = "3_store_tick_liquidities.rs"]
|
||||||
@@ -21,8 +29,8 @@ mod store_tick_liquidities;
|
|||||||
|
|
||||||
#[path = "4_map_balance_changes.rs"]
|
#[path = "4_map_balance_changes.rs"]
|
||||||
mod map_balance_changes;
|
mod map_balance_changes;
|
||||||
#[path = "4_store_liquidities.rs"]
|
#[path = "4_store_active_liquidities.rs"]
|
||||||
mod store_liquidities;
|
mod store_active_liquidities;
|
||||||
|
|
||||||
#[path = "5_store_balance_changes.rs"]
|
#[path = "5_store_balance_changes.rs"]
|
||||||
mod store_balance_changes;
|
mod store_balance_changes;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// @generated
|
// @generated
|
||||||
/// Copy of tycho.evm.v1.Transaction to be able to implement conversions to/from TransactionTrace
|
/// Copy of tycho.evm.v1.Transaction to be able to implement conversions to/from
|
||||||
|
/// TransactionTrace
|
||||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
pub struct Transaction {
|
pub struct Transaction {
|
||||||
@@ -36,6 +37,30 @@ pub struct TickDeltas {
|
|||||||
}
|
}
|
||||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct OrderSaleRateDelta {
|
||||||
|
/// bytes32
|
||||||
|
#[prost(bytes="vec", tag="1")]
|
||||||
|
pub pool_id: ::prost::alloc::vec::Vec<u8>,
|
||||||
|
#[prost(uint64, tag="2")]
|
||||||
|
pub time: u64,
|
||||||
|
/// int112
|
||||||
|
#[prost(bytes="vec", tag="3")]
|
||||||
|
pub sale_rate_delta: ::prost::alloc::vec::Vec<u8>,
|
||||||
|
#[prost(bool, tag="4")]
|
||||||
|
pub is_token1: bool,
|
||||||
|
#[prost(uint64, tag="5")]
|
||||||
|
pub ordinal: u64,
|
||||||
|
#[prost(message, optional, tag="6")]
|
||||||
|
pub transaction: ::core::option::Option<Transaction>,
|
||||||
|
}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct OrderSaleRateDeltas {
|
||||||
|
#[prost(message, repeated, tag="1")]
|
||||||
|
pub deltas: ::prost::alloc::vec::Vec<OrderSaleRateDelta>,
|
||||||
|
}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
pub struct LiquidityChange {
|
pub struct LiquidityChange {
|
||||||
/// bytes32
|
/// bytes32
|
||||||
#[prost(bytes="vec", tag="1")]
|
#[prost(bytes="vec", tag="1")]
|
||||||
@@ -43,7 +68,7 @@ pub struct LiquidityChange {
|
|||||||
/// uint128 or int128, depending on change_type
|
/// uint128 or int128, depending on change_type
|
||||||
#[prost(bytes="vec", tag="2")]
|
#[prost(bytes="vec", tag="2")]
|
||||||
pub value: ::prost::alloc::vec::Vec<u8>,
|
pub value: ::prost::alloc::vec::Vec<u8>,
|
||||||
#[prost(enumeration="LiquidityChangeType", tag="3")]
|
#[prost(enumeration="ChangeType", tag="3")]
|
||||||
pub change_type: i32,
|
pub change_type: i32,
|
||||||
#[prost(uint64, tag="4")]
|
#[prost(uint64, tag="4")]
|
||||||
pub ordinal: u64,
|
pub ordinal: u64,
|
||||||
@@ -58,6 +83,31 @@ pub struct LiquidityChanges {
|
|||||||
}
|
}
|
||||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct SaleRateChange {
|
||||||
|
/// bytes32
|
||||||
|
#[prost(bytes="vec", tag="1")]
|
||||||
|
pub pool_id: ::prost::alloc::vec::Vec<u8>,
|
||||||
|
/// uint112 or int112, depending on change_type
|
||||||
|
#[prost(bytes="vec", tag="2")]
|
||||||
|
pub token0_value: ::prost::alloc::vec::Vec<u8>,
|
||||||
|
/// uint112 or int112, depending on change_type
|
||||||
|
#[prost(bytes="vec", tag="3")]
|
||||||
|
pub token1_value: ::prost::alloc::vec::Vec<u8>,
|
||||||
|
#[prost(enumeration="ChangeType", tag="4")]
|
||||||
|
pub change_type: i32,
|
||||||
|
#[prost(uint64, tag="5")]
|
||||||
|
pub ordinal: u64,
|
||||||
|
#[prost(message, optional, tag="6")]
|
||||||
|
pub transaction: ::core::option::Option<Transaction>,
|
||||||
|
}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct SaleRateChanges {
|
||||||
|
#[prost(message, repeated, tag="1")]
|
||||||
|
pub changes: ::prost::alloc::vec::Vec<SaleRateChange>,
|
||||||
|
}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
pub struct PoolDetails {
|
pub struct PoolDetails {
|
||||||
/// address
|
/// address
|
||||||
#[prost(bytes="vec", tag="1")]
|
#[prost(bytes="vec", tag="1")]
|
||||||
@@ -73,6 +123,9 @@ pub struct PoolDetails {
|
|||||||
pub struct BlockTransactionEvents {
|
pub struct BlockTransactionEvents {
|
||||||
#[prost(message, repeated, tag="1")]
|
#[prost(message, repeated, tag="1")]
|
||||||
pub block_transaction_events: ::prost::alloc::vec::Vec<block_transaction_events::TransactionEvents>,
|
pub block_transaction_events: ::prost::alloc::vec::Vec<block_transaction_events::TransactionEvents>,
|
||||||
|
/// block timestamp
|
||||||
|
#[prost(uint64, tag="2")]
|
||||||
|
pub timestamp: u64,
|
||||||
}
|
}
|
||||||
/// Nested message and enum types in `BlockTransactionEvents`.
|
/// Nested message and enum types in `BlockTransactionEvents`.
|
||||||
pub mod block_transaction_events {
|
pub mod block_transaction_events {
|
||||||
@@ -138,16 +191,6 @@ pub mod block_transaction_events {
|
|||||||
pub delta1: ::prost::alloc::vec::Vec<u8>,
|
pub delta1: ::prost::alloc::vec::Vec<u8>,
|
||||||
}
|
}
|
||||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
|
||||||
pub struct PositionFeesCollected {
|
|
||||||
/// uint128
|
|
||||||
#[prost(bytes="vec", tag="1")]
|
|
||||||
pub amount0: ::prost::alloc::vec::Vec<u8>,
|
|
||||||
/// uint128
|
|
||||||
#[prost(bytes="vec", tag="2")]
|
|
||||||
pub amount1: ::prost::alloc::vec::Vec<u8>,
|
|
||||||
}
|
|
||||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
|
||||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
pub struct PoolInitialized {
|
pub struct PoolInitialized {
|
||||||
/// address
|
/// address
|
||||||
@@ -176,6 +219,8 @@ pub mod block_transaction_events {
|
|||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
Base = 1,
|
Base = 1,
|
||||||
Oracle = 2,
|
Oracle = 2,
|
||||||
|
Twamm = 3,
|
||||||
|
MevResist = 4,
|
||||||
}
|
}
|
||||||
impl Extension {
|
impl Extension {
|
||||||
/// String value of the enum field names used in the ProtoBuf definition.
|
/// String value of the enum field names used in the ProtoBuf definition.
|
||||||
@@ -187,6 +232,8 @@ pub mod block_transaction_events {
|
|||||||
Extension::Unknown => "EXTENSION_UNKNOWN",
|
Extension::Unknown => "EXTENSION_UNKNOWN",
|
||||||
Extension::Base => "EXTENSION_BASE",
|
Extension::Base => "EXTENSION_BASE",
|
||||||
Extension::Oracle => "EXTENSION_ORACLE",
|
Extension::Oracle => "EXTENSION_ORACLE",
|
||||||
|
Extension::Twamm => "EXTENSION_TWAMM",
|
||||||
|
Extension::MevResist => "EXTENSION_MEV_RESIST",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||||
@@ -195,6 +242,8 @@ pub mod block_transaction_events {
|
|||||||
"EXTENSION_UNKNOWN" => Some(Self::Unknown),
|
"EXTENSION_UNKNOWN" => Some(Self::Unknown),
|
||||||
"EXTENSION_BASE" => Some(Self::Base),
|
"EXTENSION_BASE" => Some(Self::Base),
|
||||||
"EXTENSION_ORACLE" => Some(Self::Oracle),
|
"EXTENSION_ORACLE" => Some(Self::Oracle),
|
||||||
|
"EXTENSION_TWAMM" => Some(Self::Twamm),
|
||||||
|
"EXTENSION_MEV_RESIST" => Some(Self::MevResist),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -202,13 +251,43 @@ pub mod block_transaction_events {
|
|||||||
}
|
}
|
||||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
pub struct FeesAccumulated {
|
pub struct VirtualOrdersExecuted {
|
||||||
/// uint128
|
/// int112
|
||||||
#[prost(bytes="vec", tag="1")]
|
#[prost(bytes="vec", tag="1")]
|
||||||
pub amount0: ::prost::alloc::vec::Vec<u8>,
|
pub token0_sale_rate: ::prost::alloc::vec::Vec<u8>,
|
||||||
/// uint128
|
/// int112
|
||||||
#[prost(bytes="vec", tag="2")]
|
#[prost(bytes="vec", tag="2")]
|
||||||
pub amount1: ::prost::alloc::vec::Vec<u8>,
|
pub token1_sale_rate: ::prost::alloc::vec::Vec<u8>,
|
||||||
|
}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct OrderUpdated {
|
||||||
|
#[prost(message, optional, tag="1")]
|
||||||
|
pub order_key: ::core::option::Option<order_updated::OrderKey>,
|
||||||
|
/// int112
|
||||||
|
#[prost(bytes="vec", tag="2")]
|
||||||
|
pub sale_rate_delta: ::prost::alloc::vec::Vec<u8>,
|
||||||
|
}
|
||||||
|
/// Nested message and enum types in `OrderUpdated`.
|
||||||
|
pub mod order_updated {
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct OrderKey {
|
||||||
|
/// address
|
||||||
|
#[prost(bytes="vec", tag="1")]
|
||||||
|
pub sell_token: ::prost::alloc::vec::Vec<u8>,
|
||||||
|
/// address
|
||||||
|
#[prost(bytes="vec", tag="2")]
|
||||||
|
pub buy_token: ::prost::alloc::vec::Vec<u8>,
|
||||||
|
#[prost(fixed64, tag="3")]
|
||||||
|
pub fee: u64,
|
||||||
|
/// block timestamp
|
||||||
|
#[prost(uint64, tag="4")]
|
||||||
|
pub start_time: u64,
|
||||||
|
/// block timestamp
|
||||||
|
#[prost(uint64, tag="5")]
|
||||||
|
pub end_time: u64,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
#[derive(Clone, PartialEq, ::prost::Oneof)]
|
#[derive(Clone, PartialEq, ::prost::Oneof)]
|
||||||
@@ -218,30 +297,30 @@ pub mod block_transaction_events {
|
|||||||
#[prost(message, tag="4")]
|
#[prost(message, tag="4")]
|
||||||
PositionUpdated(PositionUpdated),
|
PositionUpdated(PositionUpdated),
|
||||||
#[prost(message, tag="5")]
|
#[prost(message, tag="5")]
|
||||||
PositionFeesCollected(PositionFeesCollected),
|
|
||||||
#[prost(message, tag="6")]
|
|
||||||
PoolInitialized(PoolInitialized),
|
PoolInitialized(PoolInitialized),
|
||||||
|
#[prost(message, tag="6")]
|
||||||
|
VirtualOrdersExecuted(VirtualOrdersExecuted),
|
||||||
#[prost(message, tag="7")]
|
#[prost(message, tag="7")]
|
||||||
FeesAccumulated(FeesAccumulated),
|
OrderUpdated(OrderUpdated),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
pub enum LiquidityChangeType {
|
pub enum ChangeType {
|
||||||
Delta = 0,
|
Delta = 0,
|
||||||
Absolute = 1,
|
Absolute = 1,
|
||||||
}
|
}
|
||||||
impl LiquidityChangeType {
|
impl ChangeType {
|
||||||
/// String value of the enum field names used in the ProtoBuf definition.
|
/// 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
|
/// 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.
|
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
|
||||||
pub fn as_str_name(&self) -> &'static str {
|
pub fn as_str_name(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
LiquidityChangeType::Delta => "DELTA",
|
ChangeType::Delta => "DELTA",
|
||||||
LiquidityChangeType::Absolute => "ABSOLUTE",
|
ChangeType::Absolute => "ABSOLUTE",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
pub struct PoolConfig {
|
|
||||||
pub fee: Vec<u8>,
|
|
||||||
pub tick_spacing: Vec<u8>,
|
|
||||||
pub extension: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<[u8; 32]> for PoolConfig {
|
|
||||||
fn from(value: [u8; 32]) -> Self {
|
|
||||||
Self {
|
|
||||||
tick_spacing: value[28..32].into(),
|
|
||||||
fee: value[20..28].into(),
|
|
||||||
extension: value[..20].into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
83
substreams/ethereum-ekubo-v2/src/pool_key.rs
Normal file
83
substreams/ethereum-ekubo-v2/src/pool_key.rs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
use ethabi::{Address, FixedBytes, Token};
|
||||||
|
use tiny_keccak::{Hasher, Keccak};
|
||||||
|
|
||||||
|
pub struct PoolConfig {
|
||||||
|
pub fee: u64,
|
||||||
|
pub tick_spacing: u32,
|
||||||
|
pub extension: Address,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<[u8; 32]> for PoolConfig {
|
||||||
|
fn from(value: [u8; 32]) -> Self {
|
||||||
|
Self {
|
||||||
|
tick_spacing: u32::from_be_bytes(value[28..32].try_into().unwrap()),
|
||||||
|
fee: u64::from_be_bytes(value[20..28].try_into().unwrap()),
|
||||||
|
extension: <[u8; 20]>::try_from(&value[0..20])
|
||||||
|
.unwrap()
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PoolConfig> for FixedBytes {
|
||||||
|
fn from(value: PoolConfig) -> Self {
|
||||||
|
[
|
||||||
|
value
|
||||||
|
.extension
|
||||||
|
.to_fixed_bytes()
|
||||||
|
.as_slice(),
|
||||||
|
&value.fee.to_be_bytes(),
|
||||||
|
&value.tick_spacing.to_be_bytes(),
|
||||||
|
]
|
||||||
|
.concat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PoolKey {
|
||||||
|
pub token0: Address,
|
||||||
|
pub token1: Address,
|
||||||
|
pub config: PoolConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PoolKey {
|
||||||
|
pub fn into_pool_id(self) -> Vec<u8> {
|
||||||
|
let mut hasher = Keccak::v256();
|
||||||
|
|
||||||
|
hasher.update(ðabi::encode(&[
|
||||||
|
Token::Address(self.token0),
|
||||||
|
Token::Address(self.token1),
|
||||||
|
Token::FixedBytes(self.config.into()),
|
||||||
|
]));
|
||||||
|
|
||||||
|
let mut output = vec![0; 32];
|
||||||
|
hasher.finalize(&mut output);
|
||||||
|
output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use ethabi::ethereum_types::H160;
|
||||||
|
use substreams_helper::hex::Hexable;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pool_id_computation() {
|
||||||
|
let pool_key = PoolKey {
|
||||||
|
token0: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" // USDC
|
||||||
|
.parse()
|
||||||
|
.unwrap(),
|
||||||
|
token1: "0xdAC17F958D2ee523a2206206994597C13D831ec7" // USDT
|
||||||
|
.parse()
|
||||||
|
.unwrap(),
|
||||||
|
config: PoolConfig { fee: 922337203685477, tick_spacing: 100, extension: H160::zero() },
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://etherscan.io/tx/0x8d8c4aaee4cc5a23670f7b4894eb63f2eba82779b691e3a97bb073ae857d82e2#eventlog#153
|
||||||
|
assert_eq!(
|
||||||
|
pool_key.into_pool_id().to_hex(),
|
||||||
|
"0x91ffc128bf8e0afbd2c0f14722e2fd5b6625341a5e5f551aa36242d98756798d"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
12
substreams/ethereum-ekubo-v2/src/store.rs
Normal file
12
substreams/ethereum-ekubo-v2/src/store.rs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
use substreams::store::StoreSetSum;
|
||||||
|
|
||||||
|
use crate::pb::ekubo::ChangeType;
|
||||||
|
|
||||||
|
pub fn store_method_from_change_type<T, S: StoreSetSum<T>>(
|
||||||
|
change_type: ChangeType,
|
||||||
|
) -> fn(&S, u64, String, T) {
|
||||||
|
match change_type {
|
||||||
|
ChangeType::Delta => StoreSetSum::sum,
|
||||||
|
ChangeType::Absolute => StoreSetSum::set,
|
||||||
|
}
|
||||||
|
}
|
||||||
27
substreams/ethereum-ekubo-v2/src/twamm.rs
Normal file
27
substreams/ethereum-ekubo-v2/src/twamm.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
use substreams::scalar::BigInt;
|
||||||
|
|
||||||
|
use crate::pool_key::{PoolConfig, PoolKey};
|
||||||
|
|
||||||
|
pub type OrderKey = (Vec<u8>, Vec<u8>, BigInt, BigInt, BigInt);
|
||||||
|
|
||||||
|
impl PoolKey {
|
||||||
|
pub fn from_order_key(key: &OrderKey, twamm_address: &Vec<u8>) -> Self {
|
||||||
|
let (token0, token1) = if key.1 > key.0 { (&key.0, &key.1) } else { (&key.1, &key.0) };
|
||||||
|
|
||||||
|
Self {
|
||||||
|
token0: <&[u8; 20]>::try_from(token0.as_slice())
|
||||||
|
.unwrap()
|
||||||
|
.into(),
|
||||||
|
token1: <&[u8; 20]>::try_from(token1.as_slice())
|
||||||
|
.unwrap()
|
||||||
|
.into(),
|
||||||
|
config: PoolConfig {
|
||||||
|
fee: key.2.to_u64(),
|
||||||
|
tick_spacing: 0,
|
||||||
|
extension: <&[u8; 20]>::try_from(twamm_address.as_slice())
|
||||||
|
.unwrap()
|
||||||
|
.into(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
specVersion: v0.1.0
|
specVersion: v0.1.0
|
||||||
package:
|
package:
|
||||||
name: "ethereum_ekubo_v2"
|
name: "ethereum_ekubo_v2"
|
||||||
version: v0.1.0
|
version: v0.2.0
|
||||||
url: "https://github.com/propeller-heads/tycho-protocol-sdk/tree/main/substreams/ethereum-ekubo-v2"
|
url: "https://github.com/propeller-heads/tycho-protocol-sdk/tree/main/substreams/ethereum-ekubo-v2"
|
||||||
|
|
||||||
protobuf:
|
protobuf:
|
||||||
@@ -15,6 +15,7 @@ protobuf:
|
|||||||
excludePaths:
|
excludePaths:
|
||||||
- google
|
- google
|
||||||
- tycho
|
- tycho
|
||||||
|
- sf/substreams
|
||||||
|
|
||||||
binaries:
|
binaries:
|
||||||
default:
|
default:
|
||||||
@@ -27,7 +28,7 @@ networks:
|
|||||||
initialBlock:
|
initialBlock:
|
||||||
map_events: 22048334 # First pool initialization https://etherscan.io/tx/0x7c2e697e73dc1f114a5473d1015c411f10585b2b671bee0bd6d5706895e16b27
|
map_events: 22048334 # First pool initialization https://etherscan.io/tx/0x7c2e697e73dc1f114a5473d1015c411f10585b2b671bee0bd6d5706895e16b27
|
||||||
params:
|
params:
|
||||||
map_events: "core=e0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444&oracle=51d02A5948496a67827242EaBc5725531342527C"
|
map_events: "core=e0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444&oracle=51d02A5948496a67827242EaBc5725531342527C&twamm=D4279c050DA1F5c5B2830558C7A08E57e12b54eC&mev_resist=553a2EFc570c9e104942cEC6aC1c18118e54C091"
|
||||||
|
|
||||||
modules:
|
modules:
|
||||||
- name: map_events
|
- name: map_events
|
||||||
@@ -45,7 +46,21 @@ modules:
|
|||||||
output:
|
output:
|
||||||
type: proto:tycho.evm.v1.BlockChanges
|
type: proto:tycho.evm.v1.BlockChanges
|
||||||
|
|
||||||
- name: map_tick_changes
|
- name: map_order_sale_rate_deltas
|
||||||
|
kind: map
|
||||||
|
inputs:
|
||||||
|
- map: map_events
|
||||||
|
output:
|
||||||
|
type: proto:ekubo.OrderSaleRateDeltas
|
||||||
|
|
||||||
|
- name: map_sale_rate_changes
|
||||||
|
kind: map
|
||||||
|
inputs:
|
||||||
|
- map: map_events
|
||||||
|
output:
|
||||||
|
type: proto:ekubo.SaleRateChanges
|
||||||
|
|
||||||
|
- name: map_tick_deltas
|
||||||
kind: map
|
kind: map
|
||||||
inputs:
|
inputs:
|
||||||
- map: map_events
|
- map: map_events
|
||||||
@@ -67,6 +82,20 @@ modules:
|
|||||||
output:
|
output:
|
||||||
type: proto:ekubo.LiquidityChanges
|
type: proto:ekubo.LiquidityChanges
|
||||||
|
|
||||||
|
- name: store_active_sale_rates
|
||||||
|
kind: store
|
||||||
|
valueType: bigint
|
||||||
|
updatePolicy: set_sum
|
||||||
|
inputs:
|
||||||
|
- map: map_sale_rate_changes
|
||||||
|
|
||||||
|
- name: store_order_sale_rates
|
||||||
|
kind: store
|
||||||
|
valueType: bigint
|
||||||
|
updatePolicy: add
|
||||||
|
inputs:
|
||||||
|
- map: map_order_sale_rate_deltas
|
||||||
|
|
||||||
- name: store_pool_details
|
- name: store_pool_details
|
||||||
kind: store
|
kind: store
|
||||||
valueType: proto:ekubo.PoolDetails
|
valueType: proto:ekubo.PoolDetails
|
||||||
@@ -79,7 +108,7 @@ modules:
|
|||||||
valueType: bigint
|
valueType: bigint
|
||||||
updatePolicy: add
|
updatePolicy: add
|
||||||
inputs:
|
inputs:
|
||||||
- map: map_tick_changes
|
- map: map_tick_deltas
|
||||||
|
|
||||||
- name: map_balance_changes
|
- name: map_balance_changes
|
||||||
kind: map
|
kind: map
|
||||||
@@ -89,7 +118,7 @@ modules:
|
|||||||
output:
|
output:
|
||||||
type: proto:tycho.evm.v1.BlockBalanceDeltas
|
type: proto:tycho.evm.v1.BlockBalanceDeltas
|
||||||
|
|
||||||
- name: store_liquidities
|
- name: store_active_liquidities
|
||||||
kind: store
|
kind: store
|
||||||
valueType: bigint
|
valueType: bigint
|
||||||
updatePolicy: set_sum
|
updatePolicy: set_sum
|
||||||
@@ -112,11 +141,17 @@ modules:
|
|||||||
- map: map_balance_changes
|
- map: map_balance_changes
|
||||||
- store: store_balance_changes
|
- store: store_balance_changes
|
||||||
mode: deltas
|
mode: deltas
|
||||||
- map: map_tick_changes
|
- map: map_tick_deltas
|
||||||
- store: store_tick_liquidities
|
- store: store_tick_liquidities
|
||||||
mode: deltas
|
mode: deltas
|
||||||
|
- map: map_order_sale_rate_deltas
|
||||||
|
- store: store_order_sale_rates
|
||||||
|
mode: deltas
|
||||||
- map: map_liquidity_changes
|
- map: map_liquidity_changes
|
||||||
- store: store_liquidities
|
- store: store_active_liquidities
|
||||||
|
mode: deltas
|
||||||
|
- map: map_sale_rate_changes
|
||||||
|
- store: store_active_sale_rates
|
||||||
mode: deltas
|
mode: deltas
|
||||||
output:
|
output:
|
||||||
type: proto:tycho.evm.v1.BlockChanges
|
type: proto:tycho.evm.v1.BlockChanges
|
||||||
|
|||||||
Reference in New Issue
Block a user