diff --git a/packages/subgraph/.gitignore b/packages/subgraph/.gitignore index 79f9079..7f0f8c0 100644 --- a/packages/subgraph/.gitignore +++ b/packages/subgraph/.gitignore @@ -4,6 +4,7 @@ generated/ # Generated subgraph config subgraph.yaml +src/addresses.ts # Dependency directories node_modules/ diff --git a/packages/subgraph/abis/INonfungiblePositionManager.json b/packages/subgraph/abis/INonfungiblePositionManager.json new file mode 100644 index 0000000..03be3e6 --- /dev/null +++ b/packages/subgraph/abis/INonfungiblePositionManager.json @@ -0,0 +1,989 @@ +[ + { + "type": "function", + "name": "DOMAIN_SEPARATOR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "PERMIT_TYPEHASH", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "stateMutability": "pure" + }, + { + "type": "function", + "name": "WETH9", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "approve", + "inputs": [ + { + "name": "to", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenId", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "balanceOf", + "inputs": [ + { + "name": "owner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "balance", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "burn", + "inputs": [ + { + "name": "tokenId", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "collect", + "inputs": [ + { + "name": "params", + "type": "tuple", + "internalType": "struct INonfungiblePositionManager.CollectParams", + "components": [ + { + "name": "tokenId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "amount0Max", + "type": "uint128", + "internalType": "uint128" + }, + { + "name": "amount1Max", + "type": "uint128", + "internalType": "uint128" + } + ] + } + ], + "outputs": [ + { + "name": "amount0", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "amount1", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "createAndInitializePoolIfNecessary", + "inputs": [ + { + "name": "token0", + "type": "address", + "internalType": "address" + }, + { + "name": "token1", + "type": "address", + "internalType": "address" + }, + { + "name": "fee", + "type": "uint24", + "internalType": "uint24" + }, + { + "name": "sqrtPriceX96", + "type": "uint160", + "internalType": "uint160" + } + ], + "outputs": [ + { + "name": "pool", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "decreaseLiquidity", + "inputs": [ + { + "name": "params", + "type": "tuple", + "internalType": "struct INonfungiblePositionManager.DecreaseLiquidityParams", + "components": [ + { + "name": "tokenId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "liquidity", + "type": "uint128", + "internalType": "uint128" + }, + { + "name": "amount0Min", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "amount1Min", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + } + ] + } + ], + "outputs": [ + { + "name": "amount0", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "amount1", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "factory", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getApproved", + "inputs": [ + { + "name": "tokenId", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "operator", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "increaseLiquidity", + "inputs": [ + { + "name": "params", + "type": "tuple", + "internalType": "struct INonfungiblePositionManager.IncreaseLiquidityParams", + "components": [ + { + "name": "tokenId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "amount0Desired", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "amount1Desired", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "amount0Min", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "amount1Min", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + } + ] + } + ], + "outputs": [ + { + "name": "liquidity", + "type": "uint128", + "internalType": "uint128" + }, + { + "name": "amount0", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "amount1", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "isApprovedForAll", + "inputs": [ + { + "name": "owner", + "type": "address", + "internalType": "address" + }, + { + "name": "operator", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "mint", + "inputs": [ + { + "name": "params", + "type": "tuple", + "internalType": "struct INonfungiblePositionManager.MintParams", + "components": [ + { + "name": "token0", + "type": "address", + "internalType": "address" + }, + { + "name": "token1", + "type": "address", + "internalType": "address" + }, + { + "name": "fee", + "type": "uint24", + "internalType": "uint24" + }, + { + "name": "tickLower", + "type": "int24", + "internalType": "int24" + }, + { + "name": "tickUpper", + "type": "int24", + "internalType": "int24" + }, + { + "name": "amount0Desired", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "amount1Desired", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "amount0Min", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "amount1Min", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + } + ] + } + ], + "outputs": [ + { + "name": "tokenId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "liquidity", + "type": "uint128", + "internalType": "uint128" + }, + { + "name": "amount0", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "amount1", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "name", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "string", + "internalType": "string" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "ownerOf", + "inputs": [ + { + "name": "tokenId", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "owner", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "permit", + "inputs": [ + { + "name": "spender", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "v", + "type": "uint8", + "internalType": "uint8" + }, + { + "name": "r", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "s", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "positions", + "inputs": [ + { + "name": "tokenId", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "nonce", + "type": "uint96", + "internalType": "uint96" + }, + { + "name": "operator", + "type": "address", + "internalType": "address" + }, + { + "name": "token0", + "type": "address", + "internalType": "address" + }, + { + "name": "token1", + "type": "address", + "internalType": "address" + }, + { + "name": "fee", + "type": "uint24", + "internalType": "uint24" + }, + { + "name": "tickLower", + "type": "int24", + "internalType": "int24" + }, + { + "name": "tickUpper", + "type": "int24", + "internalType": "int24" + }, + { + "name": "liquidity", + "type": "uint128", + "internalType": "uint128" + }, + { + "name": "feeGrowthInside0LastX128", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "feeGrowthInside1LastX128", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "tokensOwed0", + "type": "uint128", + "internalType": "uint128" + }, + { + "name": "tokensOwed1", + "type": "uint128", + "internalType": "uint128" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "refundETH", + "inputs": [], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "safeTransferFrom", + "inputs": [ + { + "name": "from", + "type": "address", + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenId", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "safeTransferFrom", + "inputs": [ + { + "name": "from", + "type": "address", + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "data", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setApprovalForAll", + "inputs": [ + { + "name": "operator", + "type": "address", + "internalType": "address" + }, + { + "name": "approved", + "type": "bool", + "internalType": "bool" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "supportsInterface", + "inputs": [ + { + "name": "interfaceId", + "type": "bytes4", + "internalType": "bytes4" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "sweepToken", + "inputs": [ + { + "name": "token", + "type": "address", + "internalType": "address" + }, + { + "name": "amountMinimum", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "recipient", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "symbol", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "string", + "internalType": "string" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "tokenByIndex", + "inputs": [ + { + "name": "index", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "tokenOfOwnerByIndex", + "inputs": [ + { + "name": "owner", + "type": "address", + "internalType": "address" + }, + { + "name": "index", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "tokenURI", + "inputs": [ + { + "name": "tokenId", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "string", + "internalType": "string" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "totalSupply", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "transferFrom", + "inputs": [ + { + "name": "from", + "type": "address", + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenId", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "unwrapWETH9", + "inputs": [ + { + "name": "amountMinimum", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "recipient", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "event", + "name": "Approval", + "inputs": [ + { + "name": "owner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "approved", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "tokenId", + "type": "uint256", + "indexed": true, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ApprovalForAll", + "inputs": [ + { + "name": "owner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "operator", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "approved", + "type": "bool", + "indexed": false, + "internalType": "bool" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Collect", + "inputs": [ + { + "name": "tokenId", + "type": "uint256", + "indexed": true, + "internalType": "uint256" + }, + { + "name": "recipient", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "amount0", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "amount1", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "DecreaseLiquidity", + "inputs": [ + { + "name": "tokenId", + "type": "uint256", + "indexed": true, + "internalType": "uint256" + }, + { + "name": "liquidity", + "type": "uint128", + "indexed": false, + "internalType": "uint128" + }, + { + "name": "amount0", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "amount1", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "IncreaseLiquidity", + "inputs": [ + { + "name": "tokenId", + "type": "uint256", + "indexed": true, + "internalType": "uint256" + }, + { + "name": "liquidity", + "type": "uint128", + "indexed": false, + "internalType": "uint128" + }, + { + "name": "amount0", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "amount1", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Transfer", + "inputs": [ + { + "name": "from", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "tokenId", + "type": "uint256", + "indexed": true, + "internalType": "uint256" + } + ], + "anonymous": false + } +] diff --git a/packages/subgraph/abis/IUniswapV3Pool.json b/packages/subgraph/abis/IUniswapV3Pool.json new file mode 100644 index 0000000..553b79e --- /dev/null +++ b/packages/subgraph/abis/IUniswapV3Pool.json @@ -0,0 +1,1043 @@ +[ + { + "type": "function", + "name": "burn", + "inputs": [ + { + "name": "tickLower", + "type": "int24", + "internalType": "int24" + }, + { + "name": "tickUpper", + "type": "int24", + "internalType": "int24" + }, + { + "name": "amount", + "type": "uint128", + "internalType": "uint128" + } + ], + "outputs": [ + { + "name": "amount0", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "amount1", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "collect", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "tickLower", + "type": "int24", + "internalType": "int24" + }, + { + "name": "tickUpper", + "type": "int24", + "internalType": "int24" + }, + { + "name": "amount0Requested", + "type": "uint128", + "internalType": "uint128" + }, + { + "name": "amount1Requested", + "type": "uint128", + "internalType": "uint128" + } + ], + "outputs": [ + { + "name": "amount0", + "type": "uint128", + "internalType": "uint128" + }, + { + "name": "amount1", + "type": "uint128", + "internalType": "uint128" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "collectProtocol", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "amount0Requested", + "type": "uint128", + "internalType": "uint128" + }, + { + "name": "amount1Requested", + "type": "uint128", + "internalType": "uint128" + } + ], + "outputs": [ + { + "name": "amount0", + "type": "uint128", + "internalType": "uint128" + }, + { + "name": "amount1", + "type": "uint128", + "internalType": "uint128" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "factory", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "fee", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint24", + "internalType": "uint24" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "feeGrowthGlobal0X128", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "feeGrowthGlobal1X128", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "flash", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "amount0", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "amount1", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "data", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "increaseObservationCardinalityNext", + "inputs": [ + { + "name": "observationCardinalityNext", + "type": "uint16", + "internalType": "uint16" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "initialize", + "inputs": [ + { + "name": "sqrtPriceX96", + "type": "uint160", + "internalType": "uint160" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "liquidity", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint128", + "internalType": "uint128" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "maxLiquidityPerTick", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint128", + "internalType": "uint128" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "mint", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "tickLower", + "type": "int24", + "internalType": "int24" + }, + { + "name": "tickUpper", + "type": "int24", + "internalType": "int24" + }, + { + "name": "amount", + "type": "uint128", + "internalType": "uint128" + }, + { + "name": "data", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "amount0", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "amount1", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "observations", + "inputs": [ + { + "name": "index", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "blockTimestamp", + "type": "uint32", + "internalType": "uint32" + }, + { + "name": "tickCumulative", + "type": "int56", + "internalType": "int56" + }, + { + "name": "secondsPerLiquidityCumulativeX128", + "type": "uint160", + "internalType": "uint160" + }, + { + "name": "initialized", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "observe", + "inputs": [ + { + "name": "secondsAgos", + "type": "uint32[]", + "internalType": "uint32[]" + } + ], + "outputs": [ + { + "name": "tickCumulatives", + "type": "int56[]", + "internalType": "int56[]" + }, + { + "name": "secondsPerLiquidityCumulativeX128s", + "type": "uint160[]", + "internalType": "uint160[]" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "positions", + "inputs": [ + { + "name": "key", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "outputs": [ + { + "name": "liquidity", + "type": "uint128", + "internalType": "uint128" + }, + { + "name": "feeGrowthInside0LastX128", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "feeGrowthInside1LastX128", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "tokensOwed0", + "type": "uint128", + "internalType": "uint128" + }, + { + "name": "tokensOwed1", + "type": "uint128", + "internalType": "uint128" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "protocolFees", + "inputs": [], + "outputs": [ + { + "name": "token0", + "type": "uint128", + "internalType": "uint128" + }, + { + "name": "token1", + "type": "uint128", + "internalType": "uint128" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "setFeeProtocol", + "inputs": [ + { + "name": "feeProtocol0", + "type": "uint8", + "internalType": "uint8" + }, + { + "name": "feeProtocol1", + "type": "uint8", + "internalType": "uint8" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "slot0", + "inputs": [], + "outputs": [ + { + "name": "sqrtPriceX96", + "type": "uint160", + "internalType": "uint160" + }, + { + "name": "tick", + "type": "int24", + "internalType": "int24" + }, + { + "name": "observationIndex", + "type": "uint16", + "internalType": "uint16" + }, + { + "name": "observationCardinality", + "type": "uint16", + "internalType": "uint16" + }, + { + "name": "observationCardinalityNext", + "type": "uint16", + "internalType": "uint16" + }, + { + "name": "feeProtocol", + "type": "uint8", + "internalType": "uint8" + }, + { + "name": "unlocked", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "snapshotCumulativesInside", + "inputs": [ + { + "name": "tickLower", + "type": "int24", + "internalType": "int24" + }, + { + "name": "tickUpper", + "type": "int24", + "internalType": "int24" + } + ], + "outputs": [ + { + "name": "tickCumulativeInside", + "type": "int56", + "internalType": "int56" + }, + { + "name": "secondsPerLiquidityInsideX128", + "type": "uint160", + "internalType": "uint160" + }, + { + "name": "secondsInside", + "type": "uint32", + "internalType": "uint32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "swap", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "zeroForOne", + "type": "bool", + "internalType": "bool" + }, + { + "name": "amountSpecified", + "type": "int256", + "internalType": "int256" + }, + { + "name": "sqrtPriceLimitX96", + "type": "uint160", + "internalType": "uint160" + }, + { + "name": "data", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "amount0", + "type": "int256", + "internalType": "int256" + }, + { + "name": "amount1", + "type": "int256", + "internalType": "int256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "tickBitmap", + "inputs": [ + { + "name": "wordPosition", + "type": "int16", + "internalType": "int16" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "tickSpacing", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "int24", + "internalType": "int24" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "ticks", + "inputs": [ + { + "name": "tick", + "type": "int24", + "internalType": "int24" + } + ], + "outputs": [ + { + "name": "liquidityGross", + "type": "uint128", + "internalType": "uint128" + }, + { + "name": "liquidityNet", + "type": "int128", + "internalType": "int128" + }, + { + "name": "feeGrowthOutside0X128", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "feeGrowthOutside1X128", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "tickCumulativeOutside", + "type": "int56", + "internalType": "int56" + }, + { + "name": "secondsPerLiquidityOutsideX128", + "type": "uint160", + "internalType": "uint160" + }, + { + "name": "secondsOutside", + "type": "uint32", + "internalType": "uint32" + }, + { + "name": "initialized", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "token0", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "token1", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "event", + "name": "Burn", + "inputs": [ + { + "name": "owner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "tickLower", + "type": "int24", + "indexed": true, + "internalType": "int24" + }, + { + "name": "tickUpper", + "type": "int24", + "indexed": true, + "internalType": "int24" + }, + { + "name": "amount", + "type": "uint128", + "indexed": false, + "internalType": "uint128" + }, + { + "name": "amount0", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "amount1", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Collect", + "inputs": [ + { + "name": "owner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "recipient", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "tickLower", + "type": "int24", + "indexed": true, + "internalType": "int24" + }, + { + "name": "tickUpper", + "type": "int24", + "indexed": true, + "internalType": "int24" + }, + { + "name": "amount0", + "type": "uint128", + "indexed": false, + "internalType": "uint128" + }, + { + "name": "amount1", + "type": "uint128", + "indexed": false, + "internalType": "uint128" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "CollectProtocol", + "inputs": [ + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "recipient", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount0", + "type": "uint128", + "indexed": false, + "internalType": "uint128" + }, + { + "name": "amount1", + "type": "uint128", + "indexed": false, + "internalType": "uint128" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Flash", + "inputs": [ + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "recipient", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount0", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "amount1", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "paid0", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "paid1", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "IncreaseObservationCardinalityNext", + "inputs": [ + { + "name": "observationCardinalityNextOld", + "type": "uint16", + "indexed": false, + "internalType": "uint16" + }, + { + "name": "observationCardinalityNextNew", + "type": "uint16", + "indexed": false, + "internalType": "uint16" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Initialize", + "inputs": [ + { + "name": "sqrtPriceX96", + "type": "uint160", + "indexed": false, + "internalType": "uint160" + }, + { + "name": "tick", + "type": "int24", + "indexed": false, + "internalType": "int24" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Mint", + "inputs": [ + { + "name": "sender", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "owner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "tickLower", + "type": "int24", + "indexed": true, + "internalType": "int24" + }, + { + "name": "tickUpper", + "type": "int24", + "indexed": true, + "internalType": "int24" + }, + { + "name": "amount", + "type": "uint128", + "indexed": false, + "internalType": "uint128" + }, + { + "name": "amount0", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "amount1", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SetFeeProtocol", + "inputs": [ + { + "name": "feeProtocol0Old", + "type": "uint8", + "indexed": false, + "internalType": "uint8" + }, + { + "name": "feeProtocol1Old", + "type": "uint8", + "indexed": false, + "internalType": "uint8" + }, + { + "name": "feeProtocol0New", + "type": "uint8", + "indexed": false, + "internalType": "uint8" + }, + { + "name": "feeProtocol1New", + "type": "uint8", + "indexed": false, + "internalType": "uint8" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Swap", + "inputs": [ + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "recipient", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount0", + "type": "int256", + "indexed": false, + "internalType": "int256" + }, + { + "name": "amount1", + "type": "int256", + "indexed": false, + "internalType": "int256" + }, + { + "name": "sqrtPriceX96", + "type": "uint160", + "indexed": false, + "internalType": "uint160" + }, + { + "name": "liquidity", + "type": "uint128", + "indexed": false, + "internalType": "uint128" + }, + { + "name": "tick", + "type": "int24", + "indexed": false, + "internalType": "int24" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "AI", + "inputs": [] + }, + { + "type": "error", + "name": "AS", + "inputs": [] + }, + { + "type": "error", + "name": "F0", + "inputs": [] + }, + { + "type": "error", + "name": "F1", + "inputs": [] + }, + { + "type": "error", + "name": "IIA", + "inputs": [] + }, + { + "type": "error", + "name": "L", + "inputs": [] + }, + { + "type": "error", + "name": "LOK", + "inputs": [] + }, + { + "type": "error", + "name": "M0", + "inputs": [] + }, + { + "type": "error", + "name": "M1", + "inputs": [] + }, + { + "type": "error", + "name": "TLM", + "inputs": [] + }, + { + "type": "error", + "name": "TLU", + "inputs": [] + }, + { + "type": "error", + "name": "TUM", + "inputs": [] + } +] diff --git a/packages/subgraph/networks.json b/packages/subgraph/networks.json index f4011a5..57243a3 100644 --- a/packages/subgraph/networks.json +++ b/packages/subgraph/networks.json @@ -12,6 +12,14 @@ "StakingRewardController": { "address": "0xb19Ae25A98d352B36CED60F93db926247535048b", "startBlock": 26550970 + }, + "UniV3ETHxSUPPool": { + "address": "0xBa154BEAa14172fF9384B82499732c669527d85D", + "startBlock": 38292131 + }, + "UniV3PositionManager": { + "address": "0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1", + "startBlock": 38292131 } }, "base-sepolia": { @@ -27,6 +35,14 @@ "StakingRewardController": { "address": "0x9FC0Bb109F3e733Bd84B30F8D89685b0304fC018", "startBlock": 20496038 + }, + "UniV3ETHxSUPPool": { + "address": "0xCa2054E3E5A940473DD6dCC4a67ECdfdFa8c0b72", + "startBlock": 38292131 + }, + "UniV3PositionManager": { + "address": "0x27F971cb582BF9E50F397e4d29a5C7A34f11faA2", + "startBlock": 38292131 } } } diff --git a/packages/subgraph/package.json b/packages/subgraph/package.json index a20ff0c..17087c7 100644 --- a/packages/subgraph/package.json +++ b/packages/subgraph/package.json @@ -1,6 +1,6 @@ { "name": "sup-subgraph", - "version": "2.4.1", + "version": "2.4.3", "scripts": { "prepare:base": "node scripts/prepare-subgraph.js base", "prepare:base-sepolia": "node scripts/prepare-subgraph.js base-sepolia", diff --git a/packages/subgraph/schema.graphql b/packages/subgraph/schema.graphql index b7145e7..5d9dca8 100644 --- a/packages/subgraph/schema.graphql +++ b/packages/subgraph/schema.graphql @@ -257,6 +257,21 @@ type LiquidityPosition @entity(immutable: false) { """ tokenId: BigInt! + """ + The liquidity amount at the time of position creation. + """ + liquidityAmount: BigInt! + + """ + The token0 (ETHx) amount at the time of position creation. + """ + token0Amount: BigInt! + + """ + The token1 (SUP) amount at the time of position creation. + """ + token1Amount: BigInt! + """ Timestamp when the position was created. """ diff --git a/packages/subgraph/scripts/prepare-subgraph.js b/packages/subgraph/scripts/prepare-subgraph.js index 7d18d75..5604885 100644 --- a/packages/subgraph/scripts/prepare-subgraph.js +++ b/packages/subgraph/scripts/prepare-subgraph.js @@ -19,11 +19,16 @@ if (!networks[network]) { process.exit(1); } -// Load template -const template = fs.readFileSync('subgraph.template.yaml', 'utf8'); +// Load templates +const subgraphTemplate = fs.readFileSync('subgraph.template.yaml', 'utf8'); +const addressesTemplate = fs.readFileSync('src/addresses.template.ts', 'utf8'); // Generate subgraph.yaml -const output = mustache.render(template, networks[network]); -fs.writeFileSync('subgraph.yaml', output); +const subgraphOutput = mustache.render(subgraphTemplate, networks[network]); +fs.writeFileSync('subgraph.yaml', subgraphOutput); -console.log(`Generated subgraph.yaml for ${network}`); \ No newline at end of file +// Generate addresses.ts +const addressesOutput = mustache.render(addressesTemplate, networks[network]); +fs.writeFileSync('src/addresses.ts', addressesOutput); + +console.log(`Generated subgraph.yaml and src/addresses.ts for ${network}`); \ No newline at end of file diff --git a/packages/subgraph/src/addresses.template.ts b/packages/subgraph/src/addresses.template.ts new file mode 100644 index 0000000..a558338 --- /dev/null +++ b/packages/subgraph/src/addresses.template.ts @@ -0,0 +1,27 @@ +import { Address } from "@graphprotocol/graph-ts"; + +// This file is a template file which is used for getting addresses +// based on the network we set in the prepare-subgraph.js script. +// The template variables are replaced with values from networks.json +// using mustache during the build process. + +export function getStakingRewardControllerAddress(): Address { + return Address.fromString("{{StakingRewardController.address}}"); +} + +export function getUniV3ETHxSUPPoolAddress(): Address { + return Address.fromString("{{UniV3ETHxSUPPool.address}}"); +} + +export function getUniV3PositionManagerAddress(): Address { + return Address.fromString("{{UniV3PositionManager.address}}"); +} + +export function getFluidEPProgramManagerAddress(): Address { + return Address.fromString("{{FluidEPProgramManager.address}}"); +} + +export function getFluidLockerFactoryAddress(): Address { + return Address.fromString("{{FluidLockerFactory.address}}"); +} + diff --git a/packages/subgraph/src/fluid-locker.ts b/packages/subgraph/src/fluid-locker.ts index 4d4ff5a..093f633 100644 --- a/packages/subgraph/src/fluid-locker.ts +++ b/packages/subgraph/src/fluid-locker.ts @@ -1,4 +1,4 @@ -import { BigInt, Address, Bytes, log } from "@graphprotocol/graph-ts"; +import { BigInt, Bytes, log } from "@graphprotocol/graph-ts"; import { FluidStreamClaimEvent, ClaimEventUnit, @@ -19,6 +19,9 @@ import { LiquidityPositionBurned as LiquidityPositionBurnedEvent, FluidUnlocked as FluidUnlockedEvent } from "../generated/templates/FluidLocker/FluidLocker"; +import { INonfungiblePositionManager } from "../generated/templates/FluidLocker/INonfungiblePositionManager"; +import { IUniswapV3Pool } from "../generated/templates/FluidLocker/IUniswapV3Pool"; +import { getUniV3ETHxSUPPoolAddress, getUniV3PositionManagerAddress } from "./addresses"; export function handleFluidStreamClaimed(event: FluidStreamClaimedEvent): void { const streamClaimEvent = new FluidStreamClaimEvent( @@ -232,6 +235,41 @@ export function handleLiquidityPositionCreated(event: LiquidityPositionCreatedEv position.burnedBlock = null; position.burnedTx = null; + /* + In order to determine the amounts of token0 and token1 provided, we get the liquidity amount from the position + and the current price from the pool. + Note: This isn't guaranteed to be accurate. The calls are done on the state of the complete block. + If there's transactions after this event moving the price, that leads to the token amounts being distorted. + It may be possible to get data which is guaranteed to be exact by instead parsing the other events from the + transaction receipt, and getting the data from the related `IncreaseLiquidity` event. + */ + + let positionManagerAddress = getUniV3PositionManagerAddress(); + let poolAddress = getUniV3ETHxSUPPoolAddress(); + + // Get position data + let positionManager = INonfungiblePositionManager.bind(positionManagerAddress); + let positionData = positionManager.positions(tokenId); + let liquidity = positionData.value7; // liquidity is the 8th return value (0-indexed: 7) + + // Get current pool price + let pool = IUniswapV3Pool.bind(poolAddress); + let slot0 = pool.slot0(); + let sqrtPriceX96 = slot0.value0; + + // Constants for calculation + // Q96 = 2^96 = 79228162514264337593543950336 + const Q96 = BigInt.fromString("79228162514264337593543950336"); + const MIN_SQRT_RATIO = BigInt.fromString("4295128739"); // Minimum sqrt ratio + + let priceDiff = sqrtPriceX96.minus(MIN_SQRT_RATIO); + let token1Amount = liquidity.times(priceDiff).div(Q96); + let token0Amount = liquidity.times(Q96).div(sqrtPriceX96); + + position.liquidityAmount = liquidity; + position.token0Amount = token0Amount; + position.token1Amount = token1Amount; + position.save(); } diff --git a/packages/subgraph/subgraph.template.yaml b/packages/subgraph/subgraph.template.yaml index 03565de..6fc1606 100644 --- a/packages/subgraph/subgraph.template.yaml +++ b/packages/subgraph/subgraph.template.yaml @@ -102,6 +102,10 @@ templates: file: ./abis/FluidLocker.json - name: Fontaine file: ./abis/Fontaine.json + - name: INonfungiblePositionManager + file: ./abis/INonfungiblePositionManager.json + - name: IUniswapV3Pool + file: ./abis/IUniswapV3Pool.json eventHandlers: - event: FluidStreamClaimed(indexed uint256,indexed uint256) handler: handleFluidStreamClaimed diff --git a/packages/subgraph/tests/fluid-locker-utils.ts b/packages/subgraph/tests/fluid-locker-utils.ts index d611fb1..cfd8d37 100644 --- a/packages/subgraph/tests/fluid-locker-utils.ts +++ b/packages/subgraph/tests/fluid-locker-utils.ts @@ -1,10 +1,11 @@ -import { newMockEvent } from "matchstick-as" +import { newMockEvent, createMockedFunction } from "matchstick-as" import { ethereum, Address, BigInt } from "@graphprotocol/graph-ts" import { FluidStaked, FluidUnstaked, FluidStreamClaimed, - FluidStreamsClaimed + FluidStreamsClaimed, + LiquidityPositionCreated } from "../generated/templates/FluidLocker/FluidLocker" export function createFluidStakedEvent( @@ -112,4 +113,81 @@ export function createFluidStreamsClaimedEvent( ) return fluidStreamsClaimedEvent +} + +export function createLiquidityPositionCreatedEvent( + tokenId: BigInt +): LiquidityPositionCreated { + let liquidityPositionCreatedEvent = changetype(newMockEvent()) + + liquidityPositionCreatedEvent.parameters = new Array() + + liquidityPositionCreatedEvent.parameters.push( + new ethereum.EventParam( + "tokenId", + ethereum.Value.fromUnsignedBigInt(tokenId) + ) + ) + + return liquidityPositionCreatedEvent +} + +/** + * Mocks the NonfungiblePositionManager.positions() function to return zero values + * This causes the handler to set amounts to zero (since contract calls revert in tests) + */ +export function mockPositionManagerPositions( + positionManagerAddress: Address, + tokenId: BigInt +): void { + // The positions function returns a tuple with many values + // We'll mock it to return zero values which will cause the handler to set amounts to zero + // Signature: positions(uint256):(uint96,address,address,address,uint24,int24,int24,uint128,uint256,uint256,uint128,uint128) + // We need to return all 12 values, with liquidity at index 7 (value7) + createMockedFunction( + positionManagerAddress, + "positions", + "positions(uint256):(uint96,address,address,address,uint24,int24,int24,uint128,uint256,uint256,uint128,uint128)" + ) + .withArgs([ethereum.Value.fromUnsignedBigInt(tokenId)]) + .returns([ + ethereum.Value.fromUnsignedBigInt(BigInt.zero()), // nonce + ethereum.Value.fromAddress(Address.zero()), // operator + ethereum.Value.fromAddress(Address.zero()), // token0 + ethereum.Value.fromAddress(Address.zero()), // token1 + ethereum.Value.fromUnsignedBigInt(BigInt.zero()), // fee + ethereum.Value.fromSignedBigInt(BigInt.zero()), // tickLower + ethereum.Value.fromSignedBigInt(BigInt.zero()), // tickUpper + ethereum.Value.fromUnsignedBigInt(BigInt.zero()), // liquidity (index 7) + ethereum.Value.fromUnsignedBigInt(BigInt.zero()), // feeGrowthInside0LastX128 + ethereum.Value.fromUnsignedBigInt(BigInt.zero()), // feeGrowthInside1LastX128 + ethereum.Value.fromUnsignedBigInt(BigInt.zero()), // tokensOwed0 + ethereum.Value.fromUnsignedBigInt(BigInt.zero()) // tokensOwed1 + ]) +} + +/** + * Mocks the UniswapV3Pool.slot0() function to return values that result in zero amounts + * Since liquidity is zero, the amounts will be zero, but we need a non-zero sqrtPriceX96 to avoid division by zero + */ +export function mockPoolSlot0(poolAddress: Address): void { + // The slot0 function returns a tuple: (uint160 sqrtPriceX96, int24 tick, uint16 observationIndex, uint16 observationCardinality, uint16 observationCardinalityNext, uint8 feeProtocol, bool unlocked) + // Use a minimal non-zero value for sqrtPriceX96 to avoid division by zero (MIN_SQRT_RATIO = 4295128739) + // Since liquidity is zero, the calculated amounts will still be zero + let minSqrtRatio = BigInt.fromString("4295128739") + createMockedFunction( + poolAddress, + "slot0", + "slot0():(uint160,int24,uint16,uint16,uint16,uint8,bool)" + ) + .withArgs([]) + .returns([ + ethereum.Value.fromUnsignedBigInt(minSqrtRatio), // sqrtPriceX96 (use MIN_SQRT_RATIO to avoid division by zero) + ethereum.Value.fromSignedBigInt(BigInt.zero()), // tick + ethereum.Value.fromUnsignedBigInt(BigInt.zero()), // observationIndex + ethereum.Value.fromUnsignedBigInt(BigInt.zero()), // observationCardinality + ethereum.Value.fromUnsignedBigInt(BigInt.zero()), // observationCardinalityNext + ethereum.Value.fromUnsignedBigInt(BigInt.zero()), // feeProtocol + ethereum.Value.fromBoolean(true) // unlocked + ]) } \ No newline at end of file diff --git a/packages/subgraph/tests/fluid-locker.test.ts b/packages/subgraph/tests/fluid-locker.test.ts index 80140d7..7780f9a 100644 --- a/packages/subgraph/tests/fluid-locker.test.ts +++ b/packages/subgraph/tests/fluid-locker.test.ts @@ -6,18 +6,26 @@ import { beforeEach, afterEach } from "matchstick-as/assembly/index" -import { Address, BigInt } from "@graphprotocol/graph-ts" +import { Address, BigInt, Bytes } from "@graphprotocol/graph-ts" import { handleFluidStaked, handleFluidUnstaked, handleFluidStreamClaimed, - handleFluidStreamClaimedBulk + handleFluidStreamClaimedBulk, + handleLiquidityPositionCreated } from "../src/fluid-locker" +import { + getUniV3PositionManagerAddress, + getUniV3ETHxSUPPoolAddress +} from "../src/addresses" import { createFluidStakedEvent, createFluidUnstakedEvent, createFluidStreamClaimedEvent, - createFluidStreamsClaimedEvent + createFluidStreamsClaimedEvent, + createLiquidityPositionCreatedEvent, + mockPositionManagerPositions, + mockPoolSlot0 } from "./fluid-locker-utils" describe("FluidLocker Staking Tests", () => { @@ -286,4 +294,106 @@ describe("FluidLocker Staking Tests", () => { assert.fieldEquals("LockerStaking", locker3.toHexString(), "currentStakedBalance", "200") }) }) + + describe("LiquidityPositionCreated Event Handler", () => { + test("Should create LiquidityPosition on position creation", () => { + // Use the exact same addresses that the handler will use from addresses.ts + let positionManagerAddress = getUniV3PositionManagerAddress() + let poolAddress = getUniV3ETHxSUPPoolAddress() + + let lockerAddress = Address.fromString("0x0000000000000000000000000000000000000001") + let tokenId = BigInt.fromI32(123) + + // Mock contract calls (these will return zero values, causing amounts to be zero) + mockPositionManagerPositions(positionManagerAddress, tokenId) + mockPoolSlot0(poolAddress) + + let positionCreatedEvent = createLiquidityPositionCreatedEvent(tokenId) + positionCreatedEvent.address = lockerAddress + + handleLiquidityPositionCreated(positionCreatedEvent) + + // Calculate expected position ID: lockerAddress.concat(Bytes.fromByteArray(Bytes.fromBigInt(tokenId))) + let expectedPositionId = lockerAddress.concat(Bytes.fromByteArray(Bytes.fromBigInt(tokenId))) + + // Check that LiquidityPosition entity was created + assert.entityCount("LiquidityPosition", 1) + assert.fieldEquals("LiquidityPosition", expectedPositionId.toHexString(), "tokenId", "123") + assert.fieldEquals("LiquidityPosition", expectedPositionId.toHexString(), "locker", lockerAddress.toHexString()) + + // Since contract calls will revert in tests, amounts should be zero + assert.fieldEquals("LiquidityPosition", expectedPositionId.toHexString(), "liquidityAmount", "0") + assert.fieldEquals("LiquidityPosition", expectedPositionId.toHexString(), "token0Amount", "0") + assert.fieldEquals("LiquidityPosition", expectedPositionId.toHexString(), "token1Amount", "0") + }) + + test("Should handle multiple liquidity positions from same locker", () => { + // Use the exact same addresses that the handler will use from addresses.ts + let positionManagerAddress = getUniV3PositionManagerAddress() + let poolAddress = getUniV3ETHxSUPPoolAddress() + + let lockerAddress = Address.fromString("0x0000000000000000000000000000000000000001") + + // Create first position + let tokenId1 = BigInt.fromI32(1) + mockPositionManagerPositions(positionManagerAddress, tokenId1) + mockPoolSlot0(poolAddress) + let positionCreatedEvent1 = createLiquidityPositionCreatedEvent(tokenId1) + positionCreatedEvent1.address = lockerAddress + handleLiquidityPositionCreated(positionCreatedEvent1) + + // Create second position + let tokenId2 = BigInt.fromI32(2) + mockPositionManagerPositions(positionManagerAddress, tokenId2) + mockPoolSlot0(poolAddress) + let positionCreatedEvent2 = createLiquidityPositionCreatedEvent(tokenId2) + positionCreatedEvent2.address = lockerAddress + handleLiquidityPositionCreated(positionCreatedEvent2) + + // Check both positions exist + assert.entityCount("LiquidityPosition", 2) + + let expectedPositionId1 = lockerAddress.concat(Bytes.fromByteArray(Bytes.fromBigInt(tokenId1))) + let expectedPositionId2 = lockerAddress.concat(Bytes.fromByteArray(Bytes.fromBigInt(tokenId2))) + + assert.fieldEquals("LiquidityPosition", expectedPositionId1.toHexString(), "tokenId", "1") + assert.fieldEquals("LiquidityPosition", expectedPositionId2.toHexString(), "tokenId", "2") + }) + + test("Should handle liquidity positions from different lockers", () => { + // Use the exact same addresses that the handler will use from addresses.ts + let positionManagerAddress = getUniV3PositionManagerAddress() + let poolAddress = getUniV3ETHxSUPPoolAddress() + + let locker1 = Address.fromString("0x0000000000000000000000000000000000000001") + let locker2 = Address.fromString("0x0000000000000000000000000000000000000002") + let tokenId = BigInt.fromI32(100) + + // Mock contract calls for both positions + mockPositionManagerPositions(positionManagerAddress, tokenId) + mockPoolSlot0(poolAddress) + + // Create position for locker1 + let positionCreatedEvent1 = createLiquidityPositionCreatedEvent(tokenId) + positionCreatedEvent1.address = locker1 + handleLiquidityPositionCreated(positionCreatedEvent1) + + // Create position for locker2 (same tokenId, different locker) + // Note: We can reuse the same mocks since they're for the same tokenId + let positionCreatedEvent2 = createLiquidityPositionCreatedEvent(tokenId) + positionCreatedEvent2.address = locker2 + handleLiquidityPositionCreated(positionCreatedEvent2) + + // Check both positions exist with different IDs + assert.entityCount("LiquidityPosition", 2) + + let expectedPositionId1 = locker1.concat(Bytes.fromByteArray(Bytes.fromBigInt(tokenId))) + let expectedPositionId2 = locker2.concat(Bytes.fromByteArray(Bytes.fromBigInt(tokenId))) + + assert.fieldEquals("LiquidityPosition", expectedPositionId1.toHexString(), "locker", locker1.toHexString()) + assert.fieldEquals("LiquidityPosition", expectedPositionId2.toHexString(), "locker", locker2.toHexString()) + assert.fieldEquals("LiquidityPosition", expectedPositionId1.toHexString(), "tokenId", "100") + assert.fieldEquals("LiquidityPosition", expectedPositionId2.toHexString(), "tokenId", "100") + }) + }) }) \ No newline at end of file