From 8a8879e72b4bbf65a06610725369adb066a3f9ff Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Fri, 18 Apr 2025 11:55:03 -0500 Subject: [PATCH 01/15] Formatting --- .../packages/uniswap/src/lib/spec/pricesFromUniswap3.spec.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/spec/pricesFromUniswap3.spec.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/spec/pricesFromUniswap3.spec.ts index ce52bb2c5..05c00ec2f 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/spec/pricesFromUniswap3.spec.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/spec/pricesFromUniswap3.spec.ts @@ -2,8 +2,7 @@ import '@xylabs/vitest-extended' import { getProviderFromEnv } from '@xyo-network/witness-blockchain-abstract' import { - describe, expect, - test, + describe, expect, test, } from 'vitest' import { createUniswapPoolContracts } from '../Ethers/index.ts' From 9e6b6737f1f046f8684f374c9c5ebda815638587 Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Fri, 18 Apr 2025 13:42:07 -0500 Subject: [PATCH 02/15] Add uniswap v4 SDK --- .../market/packages/uniswap/package.json | 1 + yarn.lock | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/package.json b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/package.json index 8e35a3103..29c4ca593 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/package.json +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/package.json @@ -31,6 +31,7 @@ "dependencies": { "@uniswap/sdk-core": "^7.7.2", "@uniswap/v3-sdk": "^3.25.2", + "@uniswap/v4-sdk": "^1.21.3", "@xylabs/assert": "^4.8.0", "@xylabs/delay": "^4.8.0", "@xylabs/enum": "^4.8.0", diff --git a/yarn.lock b/yarn.lock index 8cd0121a2..8709a502e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3124,7 +3124,7 @@ __metadata: languageName: node linkType: hard -"@uniswap/v3-sdk@npm:^3.25.2": +"@uniswap/v3-sdk@npm:3.25.2, @uniswap/v3-sdk@npm:^3.25.2": version: 3.25.2 resolution: "@uniswap/v3-sdk@npm:3.25.2" dependencies: @@ -3151,6 +3151,19 @@ __metadata: languageName: node linkType: hard +"@uniswap/v4-sdk@npm:^1.21.3": + version: 1.21.3 + resolution: "@uniswap/v4-sdk@npm:1.21.3" + dependencies: + "@ethersproject/solidity": "npm:^5.0.9" + "@uniswap/sdk-core": "npm:^7.7.1" + "@uniswap/v3-sdk": "npm:3.25.2" + tiny-invariant: "npm:^1.1.0" + tiny-warning: "npm:^1.0.3" + checksum: 10/3b0812cdedf4adf7b4873d410c0dfb2b0602e649999514a27583579ba9fc2ea6fdf53d1624a88f98ee7031142efd984c35ad5fcdeb754f1fc645a958708cad85 + languageName: node + linkType: hard + "@unrs/resolver-binding-darwin-arm64@npm:1.4.1": version: 1.4.1 resolution: "@unrs/resolver-binding-darwin-arm64@npm:1.4.1" @@ -7166,6 +7179,7 @@ __metadata: dependencies: "@uniswap/sdk-core": "npm:^7.7.2" "@uniswap/v3-sdk": "npm:^3.25.2" + "@uniswap/v4-sdk": "npm:^1.21.3" "@xylabs/assert": "npm:^4.8.0" "@xylabs/delay": "npm:^4.8.0" "@xylabs/enum": "npm:^4.8.0" From 3d4bae53dd1f73af0a6ea34699928113961d5f24 Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Fri, 18 Apr 2025 13:42:28 -0500 Subject: [PATCH 03/15] pricesFromUniswap4 --- .../uniswap/src/lib/pricesFromUniswap4.ts | 54 +++++++++++++++++++ .../src/lib/spec/pricesFromUniswap4.spec.ts | 16 ++++++ 2 files changed, 70 insertions(+) create mode 100644 packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap4.ts create mode 100644 packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/spec/pricesFromUniswap4.spec.ts diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap4.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap4.ts new file mode 100644 index 000000000..67d11fa10 --- /dev/null +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap4.ts @@ -0,0 +1,54 @@ +import { ethers, Provider, ZeroAddress } from 'ethers'; +import { Token, Price, Currency } from '@uniswap/sdk-core'; +import { tickToPrice } from '@uniswap/v4-sdk'; + +// Minimal ABI for pool slot0 +const POOL_ABI = [ + "function slot0() view returns (uint160 sqrtPriceX96, int24 tick, uint16, uint16, uint16, uint8, bool)" +]; + +// Minimal ABI for Uniswap v4 factory +const FACTORY_ABI = [ + "function getPool(address tokenA, address tokenB, uint24 fee, address hook) view returns (address)" +]; + +// Constants +const CHAIN_ID = 1; // Ethereum Mainnet +const UNISWAP_V4_FACTORY = "0x000000000004444c5dc75cB358380D2e3dE08A90"; // replace with actual v4 factory address + +const getExchangeRate = async ( + provider: Provider, + tokenA: Token, + tokenB: Token, + fee: number, + hookAddress: string = ZeroAddress +): Promise | null> => { + + // Init factory + const factory = new ethers.Contract(UNISWAP_V4_FACTORY, FACTORY_ABI, provider); + + // Sort tokens lexicographically as per Uniswap convention + const [token0, token1] = tokenA.address.toLowerCase() < tokenB.address.toLowerCase() + ? [tokenA.address, tokenB.address] + : [tokenB.address, tokenA.address]; + + const poolAddress: string = await factory.getPool(token0, token1, fee, hookAddress); + if (poolAddress === ZeroAddress) { + console.warn("Pool does not exist for these parameters."); + return null; + } + + const pool = new ethers.Contract(poolAddress, POOL_ABI, provider); + const slot0 = await pool.slot0(); + const currentTick = slot0.tick; + + const price = tickToPrice(tokenA, tokenB, currentTick); + return price; +} + +// export const pricesFromUniswap4 = async (provider: Provider): Promise => { +export const pricesFromUniswap4 = async (provider: Provider) => { + const tokenA = new Token(CHAIN_ID, "0x55296f69f40ea6d20e478533c15a6b08b654e758", 18, 'XYO'); + const tokenB = new Token(CHAIN_ID, "0xdac17f958d2ee523a2206206994597c13d831ec7", 18, 'USDT'); + const rate = await getExchangeRate(provider, tokenA, tokenB, 3000); +} diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/spec/pricesFromUniswap4.spec.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/spec/pricesFromUniswap4.spec.ts new file mode 100644 index 000000000..ab860a9a8 --- /dev/null +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/spec/pricesFromUniswap4.spec.ts @@ -0,0 +1,16 @@ +import '@xylabs/vitest-extended' + +import { getProviderFromEnv } from '@xyo-network/witness-blockchain-abstract' +import { + describe, expect, test, +} from 'vitest' + +import { pricesFromUniswap4 } from '../pricesFromUniswap4.ts' + +describe.skipIf(!(process.env.INFURA_PROJECT_ID && process.env.INFURA_PROJECT_SECRET))('pricesFromUniswap3', () => { + test('pricesFromUniswap4', async () => { + const provider = getProviderFromEnv() + const value = pricesFromUniswap4(provider) + expect(value).toBeDefined() + }) +}) From 006eaad879684b10975e7fdb62ec593530b20739 Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Fri, 18 Apr 2025 14:45:11 -0500 Subject: [PATCH 04/15] Remove uniswap SDK --- .../crypto/packages/market/packages/uniswap/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/package.json b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/package.json index 29c4ca593..8e35a3103 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/package.json +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/package.json @@ -31,7 +31,6 @@ "dependencies": { "@uniswap/sdk-core": "^7.7.2", "@uniswap/v3-sdk": "^3.25.2", - "@uniswap/v4-sdk": "^1.21.3", "@xylabs/assert": "^4.8.0", "@xylabs/delay": "^4.8.0", "@xylabs/enum": "^4.8.0", From 35020e4cfe20a8ee308ee3fe4553c55fd219ea81 Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Mon, 21 Apr 2025 15:34:23 -0500 Subject: [PATCH 05/15] Manually add StateViewABI --- .../uniswap/src/lib/pricesFromUniswap4.ts | 64 ++++++++++--------- .../src/lib/spec/pricesFromUniswap4.spec.ts | 4 +- 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap4.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap4.ts index 67d11fa10..3aed0fc1f 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap4.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap4.ts @@ -1,20 +1,29 @@ -import { ethers, Provider, ZeroAddress } from 'ethers'; +import { assert, Contract, ethers, Provider, ZeroAddress, keccak256, AbiCoder } from 'ethers'; import { Token, Price, Currency } from '@uniswap/sdk-core'; -import { tickToPrice } from '@uniswap/v4-sdk'; +import { IPoolManager, IPoolManager__factory } from '@xyo-network/uniswap-typechain/v4' -// Minimal ABI for pool slot0 -const POOL_ABI = [ - "function slot0() view returns (uint160 sqrtPriceX96, int24 tick, uint16, uint16, uint16, uint8, bool)" -]; +const CHAIN_ID = 1; // Ethereum Mainnet +const STATE_VIEW_ADDRESS = "0x7ffe42c4a5deea5b0fec41c94c136cf115597227"; // State view contract address +const STATE_VIEW_ABI = [{"inputs":[{"internalType":"contract IPoolManager","name":"_poolManager","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"}],"name":"getFeeGrowthGlobals","outputs":[{"internalType":"uint256","name":"feeGrowthGlobal0","type":"uint256"},{"internalType":"uint256","name":"feeGrowthGlobal1","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"}],"name":"getFeeGrowthInside","outputs":[{"internalType":"uint256","name":"feeGrowthInside0X128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthInside1X128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"}],"name":"getLiquidity","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"bytes32","name":"positionId","type":"bytes32"}],"name":"getPositionInfo","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint256","name":"feeGrowthInside0LastX128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthInside1LastX128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"getPositionInfo","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint256","name":"feeGrowthInside0LastX128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthInside1LastX128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"bytes32","name":"positionId","type":"bytes32"}],"name":"getPositionLiquidity","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"}],"name":"getSlot0","outputs":[{"internalType":"uint160","name":"sqrtPriceX96","type":"uint160"},{"internalType":"int24","name":"tick","type":"int24"},{"internalType":"uint24","name":"protocolFee","type":"uint24"},{"internalType":"uint24","name":"lpFee","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int16","name":"tick","type":"int16"}],"name":"getTickBitmap","outputs":[{"internalType":"uint256","name":"tickBitmap","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int24","name":"tick","type":"int24"}],"name":"getTickFeeGrowthOutside","outputs":[{"internalType":"uint256","name":"feeGrowthOutside0X128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthOutside1X128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int24","name":"tick","type":"int24"}],"name":"getTickInfo","outputs":[{"internalType":"uint128","name":"liquidityGross","type":"uint128"},{"internalType":"int128","name":"liquidityNet","type":"int128"},{"internalType":"uint256","name":"feeGrowthOutside0X128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthOutside1X128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int24","name":"tick","type":"int24"}],"name":"getTickLiquidity","outputs":[{"internalType":"uint128","name":"liquidityGross","type":"uint128"},{"internalType":"int128","name":"liquidityNet","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolManager","outputs":[{"internalType":"contract IPoolManager","name":"","type":"address"}],"stateMutability":"view","type":"function"}] -// Minimal ABI for Uniswap v4 factory -const FACTORY_ABI = [ - "function getPool(address tokenA, address tokenB, uint24 fee, address hook) view returns (address)" -]; -// Constants -const CHAIN_ID = 1; // Ethereum Mainnet -const UNISWAP_V4_FACTORY = "0x000000000004444c5dc75cB358380D2e3dE08A90"; // replace with actual v4 factory address +const getPoolId = ( + currencyA: Token, + currencyB: Token, + fee: number, + tickSpacing: number, + hooks: string +): string =>{ + const [currency0, currency1] = currencyA.sortsBefore(currencyB) ? [currencyA, currencyB] : [currencyB, currencyA] + const currency0Addr = currency0.isNative ? ZeroAddress : currency0.wrapped.address + const currency1Addr = currency1.isNative ? ZeroAddress : currency1.wrapped.address + return keccak256( + AbiCoder.defaultAbiCoder().encode( + ['address', 'address', 'uint24', 'int24', 'address'], + [currency0Addr, currency1Addr, fee, tickSpacing, hooks] + ), + ) +} const getExchangeRate = async ( provider: Provider, @@ -22,28 +31,20 @@ const getExchangeRate = async ( tokenB: Token, fee: number, hookAddress: string = ZeroAddress -): Promise | null> => { - - // Init factory - const factory = new ethers.Contract(UNISWAP_V4_FACTORY, FACTORY_ABI, provider); +): Promise => { - // Sort tokens lexicographically as per Uniswap convention - const [token0, token1] = tokenA.address.toLowerCase() < tokenB.address.toLowerCase() - ? [tokenA.address, tokenB.address] - : [tokenB.address, tokenA.address]; + const stateView = new Contract(STATE_VIEW_ADDRESS, STATE_VIEW_ABI, provider); - const poolAddress: string = await factory.getPool(token0, token1, fee, hookAddress); - if (poolAddress === ZeroAddress) { - console.warn("Pool does not exist for these parameters."); - return null; - } + const [token0, token1] = tokenA.sortsBefore(tokenB) + ? [tokenA, tokenB] + : [tokenB, tokenA]; - const pool = new ethers.Contract(poolAddress, POOL_ABI, provider); - const slot0 = await pool.slot0(); - const currentTick = slot0.tick; + const poolId: string = getPoolId(token0, token1, fee, 60, hookAddress); + if (poolId === ZeroAddress) throw new Error("Invalid poolId"); + const response = await stateView.getSlot0(poolId); + console.log("response", response) + return "" - const price = tickToPrice(tokenA, tokenB, currentTick); - return price; } // export const pricesFromUniswap4 = async (provider: Provider): Promise => { @@ -51,4 +52,5 @@ export const pricesFromUniswap4 = async (provider: Provider) => { const tokenA = new Token(CHAIN_ID, "0x55296f69f40ea6d20e478533c15a6b08b654e758", 18, 'XYO'); const tokenB = new Token(CHAIN_ID, "0xdac17f958d2ee523a2206206994597c13d831ec7", 18, 'USDT'); const rate = await getExchangeRate(provider, tokenA, tokenB, 3000); + return rate } diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/spec/pricesFromUniswap4.spec.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/spec/pricesFromUniswap4.spec.ts index ab860a9a8..840a0d601 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/spec/pricesFromUniswap4.spec.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/spec/pricesFromUniswap4.spec.ts @@ -7,10 +7,10 @@ import { import { pricesFromUniswap4 } from '../pricesFromUniswap4.ts' -describe.skipIf(!(process.env.INFURA_PROJECT_ID && process.env.INFURA_PROJECT_SECRET))('pricesFromUniswap3', () => { +describe.skipIf(!(process.env.INFURA_PROJECT_ID && process.env.INFURA_PROJECT_SECRET))('pricesFromUniswap4', () => { test('pricesFromUniswap4', async () => { const provider = getProviderFromEnv() - const value = pricesFromUniswap4(provider) + const value = await pricesFromUniswap4(provider) expect(value).toBeDefined() }) }) From 29e1663fc2a8ae1be31851277e8a0534f17b38fb Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Mon, 21 Apr 2025 15:51:37 -0500 Subject: [PATCH 06/15] Retrieving price from V4 Uniswap --- .../uniswap/src/lib/pricesFromUniswap4.ts | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap4.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap4.ts index 3aed0fc1f..47da3c5d7 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap4.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap4.ts @@ -25,6 +25,31 @@ const getPoolId = ( ) } + +function getPriceFromSqrtX96( + sqrtPriceX96: bigint, + decimalsA: number, + decimalsB: number +): number { + const Q96 = 2n ** 96n; + + // Scale to avoid floating point math by using a 1e18 factor + const numerator = sqrtPriceX96 * sqrtPriceX96 * 10n ** 18n; + const denominator = Q96 * Q96; + + let price = numerator / denominator; + + // Adjust for decimal differences + const decimalAdjustment = decimalsB - decimalsA; + if (decimalAdjustment > 0) { + price *= 10n ** BigInt(decimalAdjustment); + } else if (decimalAdjustment < 0) { + price /= 10n ** BigInt(-decimalAdjustment); + } + + return Number(price) / 1e18; +} + const getExchangeRate = async ( provider: Provider, tokenA: Token, @@ -42,15 +67,17 @@ const getExchangeRate = async ( const poolId: string = getPoolId(token0, token1, fee, 60, hookAddress); if (poolId === ZeroAddress) throw new Error("Invalid poolId"); const response = await stateView.getSlot0(poolId); + const sqrtPriceX96 = response[0]; console.log("response", response) - return "" + const price = getPriceFromSqrtX96(sqrtPriceX96, token1.decimals, token0.decimals); + return `${price}`; } // export const pricesFromUniswap4 = async (provider: Provider): Promise => { export const pricesFromUniswap4 = async (provider: Provider) => { const tokenA = new Token(CHAIN_ID, "0x55296f69f40ea6d20e478533c15a6b08b654e758", 18, 'XYO'); - const tokenB = new Token(CHAIN_ID, "0xdac17f958d2ee523a2206206994597c13d831ec7", 18, 'USDT'); + const tokenB = new Token(CHAIN_ID, "0xdac17f958d2ee523a2206206994597c13d831ec7", 6, 'USDT'); const rate = await getExchangeRate(provider, tokenA, tokenB, 3000); return rate } From 40ee3587e29e4f7caf4e5b2f32c0587837df67ee Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Mon, 21 Apr 2025 15:52:21 -0500 Subject: [PATCH 07/15] Remove unused imports --- .../market/packages/uniswap/src/lib/pricesFromUniswap4.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap4.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap4.ts index 47da3c5d7..0090996e7 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap4.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap4.ts @@ -1,12 +1,10 @@ -import { assert, Contract, ethers, Provider, ZeroAddress, keccak256, AbiCoder } from 'ethers'; -import { Token, Price, Currency } from '@uniswap/sdk-core'; -import { IPoolManager, IPoolManager__factory } from '@xyo-network/uniswap-typechain/v4' +import { Contract, Provider, ZeroAddress, keccak256, AbiCoder } from 'ethers'; +import { Token } from '@uniswap/sdk-core'; const CHAIN_ID = 1; // Ethereum Mainnet const STATE_VIEW_ADDRESS = "0x7ffe42c4a5deea5b0fec41c94c136cf115597227"; // State view contract address const STATE_VIEW_ABI = [{"inputs":[{"internalType":"contract IPoolManager","name":"_poolManager","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"}],"name":"getFeeGrowthGlobals","outputs":[{"internalType":"uint256","name":"feeGrowthGlobal0","type":"uint256"},{"internalType":"uint256","name":"feeGrowthGlobal1","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"}],"name":"getFeeGrowthInside","outputs":[{"internalType":"uint256","name":"feeGrowthInside0X128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthInside1X128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"}],"name":"getLiquidity","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"bytes32","name":"positionId","type":"bytes32"}],"name":"getPositionInfo","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint256","name":"feeGrowthInside0LastX128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthInside1LastX128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"getPositionInfo","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint256","name":"feeGrowthInside0LastX128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthInside1LastX128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"bytes32","name":"positionId","type":"bytes32"}],"name":"getPositionLiquidity","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"}],"name":"getSlot0","outputs":[{"internalType":"uint160","name":"sqrtPriceX96","type":"uint160"},{"internalType":"int24","name":"tick","type":"int24"},{"internalType":"uint24","name":"protocolFee","type":"uint24"},{"internalType":"uint24","name":"lpFee","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int16","name":"tick","type":"int16"}],"name":"getTickBitmap","outputs":[{"internalType":"uint256","name":"tickBitmap","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int24","name":"tick","type":"int24"}],"name":"getTickFeeGrowthOutside","outputs":[{"internalType":"uint256","name":"feeGrowthOutside0X128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthOutside1X128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int24","name":"tick","type":"int24"}],"name":"getTickInfo","outputs":[{"internalType":"uint128","name":"liquidityGross","type":"uint128"},{"internalType":"int128","name":"liquidityNet","type":"int128"},{"internalType":"uint256","name":"feeGrowthOutside0X128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthOutside1X128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int24","name":"tick","type":"int24"}],"name":"getTickLiquidity","outputs":[{"internalType":"uint128","name":"liquidityGross","type":"uint128"},{"internalType":"int128","name":"liquidityNet","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolManager","outputs":[{"internalType":"contract IPoolManager","name":"","type":"address"}],"stateMutability":"view","type":"function"}] - const getPoolId = ( currencyA: Token, currencyB: Token, From 955b35b3c2843d2723797cac0e6b671dee2d543b Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Mon, 21 Apr 2025 19:56:47 -0500 Subject: [PATCH 08/15] Change helpers to v3 --- .../crypto/packages/market/packages/uniswap/src/lib/index.ts | 2 +- .../market/packages/uniswap/src/lib/pricesFromUniswap3.ts | 2 +- .../packages/uniswap/src/lib/spec/pricesFromUniswap3.spec.ts | 2 +- .../packages/uniswap/src/lib/{Ethers => v3}/UniSwap3Pair.ts | 0 .../uniswap/src/lib/{Ethers => v3}/Uniswap3PoolSlot0Wrapper.ts | 0 .../uniswap/src/lib/{Ethers => v3}/UniswapV3Slot0Fields.ts | 0 .../src/lib/{Ethers => v3}/createUniswapPoolContracts.ts | 0 .../market/packages/uniswap/src/lib/{Ethers => v3}/index.ts | 0 8 files changed, 3 insertions(+), 3 deletions(-) rename packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/{Ethers => v3}/UniSwap3Pair.ts (100%) rename packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/{Ethers => v3}/Uniswap3PoolSlot0Wrapper.ts (100%) rename packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/{Ethers => v3}/UniswapV3Slot0Fields.ts (100%) rename packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/{Ethers => v3}/createUniswapPoolContracts.ts (100%) rename packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/{Ethers => v3}/index.ts (100%) diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/index.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/index.ts index 2c241dcee..4032d55e4 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/index.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/index.ts @@ -1,3 +1,3 @@ -export * from './Ethers/index.ts' +export * from './v3/index.ts' export * from './pricesFromUniswap3.ts' export * from './UniswapPoolContracts.ts' diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap3.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap3.ts index 4cd83c8fe..7de6de3ff 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap3.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap3.ts @@ -1,7 +1,7 @@ import { fulfilled } from '@xylabs/promise' import type { UniswapCryptoPair } from '@xyo-network/uniswap-crypto-market-payload-plugin' -import type { EthersUniSwap3Pair } from './Ethers/index.ts' +import type { EthersUniSwap3Pair } from './v3/index.ts' import { logErrorsAsync } from './logErrors.ts' export const pricesFromUniswap3 = async (pools: EthersUniSwap3Pair[]): Promise => { diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/spec/pricesFromUniswap3.spec.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/spec/pricesFromUniswap3.spec.ts index 05c00ec2f..71fb4e6ec 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/spec/pricesFromUniswap3.spec.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/spec/pricesFromUniswap3.spec.ts @@ -5,7 +5,7 @@ import { describe, expect, test, } from 'vitest' -import { createUniswapPoolContracts } from '../Ethers/index.ts' +import { createUniswapPoolContracts } from '../v3/index.ts' import { pricesFromUniswap3 } from '../pricesFromUniswap3.ts' import { UniswapPoolContracts } from '../UniswapPoolContracts.ts' diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/Ethers/UniSwap3Pair.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/UniSwap3Pair.ts similarity index 100% rename from packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/Ethers/UniSwap3Pair.ts rename to packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/UniSwap3Pair.ts diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/Ethers/Uniswap3PoolSlot0Wrapper.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/Uniswap3PoolSlot0Wrapper.ts similarity index 100% rename from packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/Ethers/Uniswap3PoolSlot0Wrapper.ts rename to packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/Uniswap3PoolSlot0Wrapper.ts diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/Ethers/UniswapV3Slot0Fields.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/UniswapV3Slot0Fields.ts similarity index 100% rename from packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/Ethers/UniswapV3Slot0Fields.ts rename to packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/UniswapV3Slot0Fields.ts diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/Ethers/createUniswapPoolContracts.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/createUniswapPoolContracts.ts similarity index 100% rename from packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/Ethers/createUniswapPoolContracts.ts rename to packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/createUniswapPoolContracts.ts diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/Ethers/index.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/index.ts similarity index 100% rename from packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/Ethers/index.ts rename to packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/index.ts From f561a7e0c74e2acd05d886ee7ca39946679ee77d Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Mon, 21 Apr 2025 20:09:26 -0500 Subject: [PATCH 09/15] Nest v3/v4 --- .../market/packages/uniswap/src/lib/UniswapV3Slot0Fields.ts | 1 - .../crypto/packages/market/packages/uniswap/src/lib/index.ts | 3 +-- .../packages/uniswap/src/lib/{ => v3}/UniswapPoolContracts.ts | 0 .../packages/uniswap/src/lib/v3/UniswapV3Slot0Fields.ts | 1 + .../packages/market/packages/uniswap/src/lib/v3/index.ts | 2 ++ .../packages/uniswap/src/lib/{ => v3}/pricesFromUniswap3.ts | 4 ++-- .../uniswap/src/lib/{ => v3}/spec/pricesFromUniswap3.spec.ts | 2 +- .../packages/market/packages/uniswap/src/lib/v4/index.ts | 1 + .../packages/uniswap/src/lib/{ => v4}/pricesFromUniswap4.ts | 0 .../uniswap/src/lib/{ => v4}/spec/pricesFromUniswap4.spec.ts | 0 10 files changed, 8 insertions(+), 6 deletions(-) delete mode 100644 packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/UniswapV3Slot0Fields.ts rename packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/{ => v3}/UniswapPoolContracts.ts (100%) rename packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/{ => v3}/pricesFromUniswap3.ts (84%) rename packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/{ => v3}/spec/pricesFromUniswap3.spec.ts (89%) create mode 100644 packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/index.ts rename packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/{ => v4}/pricesFromUniswap4.ts (100%) rename packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/{ => v4}/spec/pricesFromUniswap4.spec.ts (100%) diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/UniswapV3Slot0Fields.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/UniswapV3Slot0Fields.ts deleted file mode 100644 index 853c6e132..000000000 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/UniswapV3Slot0Fields.ts +++ /dev/null @@ -1 +0,0 @@ -export type UniswapV3Slot0Fields = [bigint, number, number, number, number, number, boolean] diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/index.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/index.ts index 4032d55e4..2eb019569 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/index.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/index.ts @@ -1,3 +1,2 @@ export * from './v3/index.ts' -export * from './pricesFromUniswap3.ts' -export * from './UniswapPoolContracts.ts' +export * from './v4/index.ts' diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/UniswapPoolContracts.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/UniswapPoolContracts.ts similarity index 100% rename from packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/UniswapPoolContracts.ts rename to packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/UniswapPoolContracts.ts diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/UniswapV3Slot0Fields.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/UniswapV3Slot0Fields.ts index d734550ac..67de47c72 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/UniswapV3Slot0Fields.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/UniswapV3Slot0Fields.ts @@ -1 +1,2 @@ export type EthersUniswapV3Slot0Fields = [bigint, bigint, bigint, bigint, bigint, bigint, boolean] +export type UniswapV3Slot0Fields = [bigint, number, number, number, number, number, boolean] diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/index.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/index.ts index b18313833..1ebb0a978 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/index.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/index.ts @@ -1,4 +1,6 @@ export * from './createUniswapPoolContracts.ts' +export * from './pricesFromUniswap3.ts' export * from './UniSwap3Pair.ts' export * from './Uniswap3PoolSlot0Wrapper.ts' +export * from './UniswapPoolContracts.ts' export * from './UniswapV3Slot0Fields.ts' diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap3.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/pricesFromUniswap3.ts similarity index 84% rename from packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap3.ts rename to packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/pricesFromUniswap3.ts index 7de6de3ff..f6c02e332 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap3.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/pricesFromUniswap3.ts @@ -1,8 +1,8 @@ import { fulfilled } from '@xylabs/promise' import type { UniswapCryptoPair } from '@xyo-network/uniswap-crypto-market-payload-plugin' -import type { EthersUniSwap3Pair } from './v3/index.ts' -import { logErrorsAsync } from './logErrors.ts' +import type { EthersUniSwap3Pair } from './index.ts' +import { logErrorsAsync } from '../logErrors.ts' export const pricesFromUniswap3 = async (pools: EthersUniSwap3Pair[]): Promise => { return await logErrorsAsync(async () => { diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/spec/pricesFromUniswap3.spec.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/spec/pricesFromUniswap3.spec.ts similarity index 89% rename from packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/spec/pricesFromUniswap3.spec.ts rename to packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/spec/pricesFromUniswap3.spec.ts index 71fb4e6ec..d93723879 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/spec/pricesFromUniswap3.spec.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v3/spec/pricesFromUniswap3.spec.ts @@ -5,8 +5,8 @@ import { describe, expect, test, } from 'vitest' -import { createUniswapPoolContracts } from '../v3/index.ts' import { pricesFromUniswap3 } from '../pricesFromUniswap3.ts' +import { createUniswapPoolContracts } from '../createUniswapPoolContracts.ts' import { UniswapPoolContracts } from '../UniswapPoolContracts.ts' describe.skipIf(!(process.env.INFURA_PROJECT_ID && process.env.INFURA_PROJECT_SECRET))('pricesFromUniswap3', () => { diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/index.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/index.ts new file mode 100644 index 000000000..ae00149b2 --- /dev/null +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/index.ts @@ -0,0 +1 @@ +export * from './pricesFromUniswap4.ts' \ No newline at end of file diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap4.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/pricesFromUniswap4.ts similarity index 100% rename from packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/pricesFromUniswap4.ts rename to packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/pricesFromUniswap4.ts diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/spec/pricesFromUniswap4.spec.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/spec/pricesFromUniswap4.spec.ts similarity index 100% rename from packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/spec/pricesFromUniswap4.spec.ts rename to packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/spec/pricesFromUniswap4.spec.ts From 31aa27ce1cbd35a7152bf8977231a7a2260ba400 Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Mon, 21 Apr 2025 20:12:51 -0500 Subject: [PATCH 10/15] Formatting --- .../packages/uniswap/src/lib/v4/spec/pricesFromUniswap4.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/spec/pricesFromUniswap4.spec.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/spec/pricesFromUniswap4.spec.ts index 840a0d601..c1eaa9ec6 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/spec/pricesFromUniswap4.spec.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/spec/pricesFromUniswap4.spec.ts @@ -4,9 +4,9 @@ import { getProviderFromEnv } from '@xyo-network/witness-blockchain-abstract' import { describe, expect, test, } from 'vitest' - import { pricesFromUniswap4 } from '../pricesFromUniswap4.ts' + describe.skipIf(!(process.env.INFURA_PROJECT_ID && process.env.INFURA_PROJECT_SECRET))('pricesFromUniswap4', () => { test('pricesFromUniswap4', async () => { const provider = getProviderFromEnv() From f584706e9396e8a4630e6babacacd31506991259 Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Mon, 21 Apr 2025 20:26:46 -0500 Subject: [PATCH 11/15] Modularize v4 helpers --- .../uniswap/src/lib/v4/getExchangeRate.ts | 34 +++++++++ .../packages/uniswap/src/lib/v4/getPoolId.ts | 22 ++++++ .../uniswap/src/lib/v4/getPriceFromSqrtX96.ts | 24 ++++++ .../uniswap/src/lib/v4/pricesFromUniswap4.ts | 73 +------------------ .../lib/v4/spec/pricesFromUniswap4.spec.ts | 1 - 5 files changed, 82 insertions(+), 72 deletions(-) create mode 100644 packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getExchangeRate.ts create mode 100644 packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getPoolId.ts create mode 100644 packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getPriceFromSqrtX96.ts diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getExchangeRate.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getExchangeRate.ts new file mode 100644 index 000000000..8b3af273b --- /dev/null +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getExchangeRate.ts @@ -0,0 +1,34 @@ +import { Token } from "@uniswap/sdk-core"; +import { ZeroAddress } from "ethers/constants"; +import { Contract } from "ethers/contract"; +import { Provider } from "ethers/providers"; +import { getPoolId } from "./getPoolId.ts"; +import { getPriceFromSqrtX96 } from "./getPriceFromSqrtX96.ts"; + +const STATE_VIEW_ADDRESS = "0x7ffe42c4a5deea5b0fec41c94c136cf115597227"; // State view contract address + +const STATE_VIEW_ABI = [{"inputs":[{"internalType":"contract IPoolManager","name":"_poolManager","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"}],"name":"getFeeGrowthGlobals","outputs":[{"internalType":"uint256","name":"feeGrowthGlobal0","type":"uint256"},{"internalType":"uint256","name":"feeGrowthGlobal1","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"}],"name":"getFeeGrowthInside","outputs":[{"internalType":"uint256","name":"feeGrowthInside0X128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthInside1X128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"}],"name":"getLiquidity","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"bytes32","name":"positionId","type":"bytes32"}],"name":"getPositionInfo","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint256","name":"feeGrowthInside0LastX128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthInside1LastX128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"getPositionInfo","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint256","name":"feeGrowthInside0LastX128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthInside1LastX128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"bytes32","name":"positionId","type":"bytes32"}],"name":"getPositionLiquidity","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"}],"name":"getSlot0","outputs":[{"internalType":"uint160","name":"sqrtPriceX96","type":"uint160"},{"internalType":"int24","name":"tick","type":"int24"},{"internalType":"uint24","name":"protocolFee","type":"uint24"},{"internalType":"uint24","name":"lpFee","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int16","name":"tick","type":"int16"}],"name":"getTickBitmap","outputs":[{"internalType":"uint256","name":"tickBitmap","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int24","name":"tick","type":"int24"}],"name":"getTickFeeGrowthOutside","outputs":[{"internalType":"uint256","name":"feeGrowthOutside0X128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthOutside1X128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int24","name":"tick","type":"int24"}],"name":"getTickInfo","outputs":[{"internalType":"uint128","name":"liquidityGross","type":"uint128"},{"internalType":"int128","name":"liquidityNet","type":"int128"},{"internalType":"uint256","name":"feeGrowthOutside0X128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthOutside1X128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int24","name":"tick","type":"int24"}],"name":"getTickLiquidity","outputs":[{"internalType":"uint128","name":"liquidityGross","type":"uint128"},{"internalType":"int128","name":"liquidityNet","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolManager","outputs":[{"internalType":"contract IPoolManager","name":"","type":"address"}],"stateMutability":"view","type":"function"}] + +export const getExchangeRate = async ( + provider: Provider, + tokenA: Token, + tokenB: Token, + fee: number, + hookAddress: string = ZeroAddress +): Promise => { + + const stateView = new Contract(STATE_VIEW_ADDRESS, STATE_VIEW_ABI, provider); + + const [token0, token1] = tokenA.sortsBefore(tokenB) + ? [tokenA, tokenB] + : [tokenB, tokenA]; + + const poolId: string = getPoolId(token0, token1, fee, 60, hookAddress); + if (poolId === ZeroAddress) throw new Error("Invalid poolId"); + const response = await stateView.getSlot0(poolId); + const sqrtPriceX96 = response[0]; + console.log("response", response) + const price = getPriceFromSqrtX96(sqrtPriceX96, token1.decimals, token0.decimals); + return `${price}`; + +} diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getPoolId.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getPoolId.ts new file mode 100644 index 000000000..c5bb5db95 --- /dev/null +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getPoolId.ts @@ -0,0 +1,22 @@ +import { Token } from "@uniswap/sdk-core" +import { AbiCoder } from "ethers/abi" +import { ZeroAddress } from "ethers/constants" +import { keccak256 } from "ethers/crypto" + +export const getPoolId = ( + currencyA: Token, + currencyB: Token, + fee: number, + tickSpacing: number, + hooks: string +): string =>{ + const [currency0, currency1] = currencyA.sortsBefore(currencyB) ? [currencyA, currencyB] : [currencyB, currencyA] + const currency0Addr = currency0.isNative ? ZeroAddress : currency0.wrapped.address + const currency1Addr = currency1.isNative ? ZeroAddress : currency1.wrapped.address + return keccak256( + AbiCoder.defaultAbiCoder().encode( + ['address', 'address', 'uint24', 'int24', 'address'], + [currency0Addr, currency1Addr, fee, tickSpacing, hooks] + ), + ) +} \ No newline at end of file diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getPriceFromSqrtX96.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getPriceFromSqrtX96.ts new file mode 100644 index 000000000..22198d368 --- /dev/null +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getPriceFromSqrtX96.ts @@ -0,0 +1,24 @@ + +export const getPriceFromSqrtX96 = ( + sqrtPriceX96: bigint, + decimalsA: number, + decimalsB: number +): number => { + const Q96 = 2n ** 96n; + + // Scale to avoid floating point math by using a 1e18 factor + const numerator = sqrtPriceX96 * sqrtPriceX96 * 10n ** 18n; + const denominator = Q96 * Q96; + + let price = numerator / denominator; + + // Adjust for decimal differences + const decimalAdjustment = decimalsB - decimalsA; + if (decimalAdjustment > 0) { + price *= 10n ** BigInt(decimalAdjustment); + } else if (decimalAdjustment < 0) { + price /= 10n ** BigInt(-decimalAdjustment); + } + + return Number(price) / 1e18; +} diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/pricesFromUniswap4.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/pricesFromUniswap4.ts index 0090996e7..d493f3b8c 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/pricesFromUniswap4.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/pricesFromUniswap4.ts @@ -1,78 +1,9 @@ -import { Contract, Provider, ZeroAddress, keccak256, AbiCoder } from 'ethers'; +import { Provider } from 'ethers'; import { Token } from '@uniswap/sdk-core'; +import { getExchangeRate } from './getExchangeRate.ts'; const CHAIN_ID = 1; // Ethereum Mainnet -const STATE_VIEW_ADDRESS = "0x7ffe42c4a5deea5b0fec41c94c136cf115597227"; // State view contract address -const STATE_VIEW_ABI = [{"inputs":[{"internalType":"contract IPoolManager","name":"_poolManager","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"}],"name":"getFeeGrowthGlobals","outputs":[{"internalType":"uint256","name":"feeGrowthGlobal0","type":"uint256"},{"internalType":"uint256","name":"feeGrowthGlobal1","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"}],"name":"getFeeGrowthInside","outputs":[{"internalType":"uint256","name":"feeGrowthInside0X128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthInside1X128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"}],"name":"getLiquidity","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"bytes32","name":"positionId","type":"bytes32"}],"name":"getPositionInfo","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint256","name":"feeGrowthInside0LastX128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthInside1LastX128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"getPositionInfo","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint256","name":"feeGrowthInside0LastX128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthInside1LastX128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"bytes32","name":"positionId","type":"bytes32"}],"name":"getPositionLiquidity","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"}],"name":"getSlot0","outputs":[{"internalType":"uint160","name":"sqrtPriceX96","type":"uint160"},{"internalType":"int24","name":"tick","type":"int24"},{"internalType":"uint24","name":"protocolFee","type":"uint24"},{"internalType":"uint24","name":"lpFee","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int16","name":"tick","type":"int16"}],"name":"getTickBitmap","outputs":[{"internalType":"uint256","name":"tickBitmap","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int24","name":"tick","type":"int24"}],"name":"getTickFeeGrowthOutside","outputs":[{"internalType":"uint256","name":"feeGrowthOutside0X128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthOutside1X128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int24","name":"tick","type":"int24"}],"name":"getTickInfo","outputs":[{"internalType":"uint128","name":"liquidityGross","type":"uint128"},{"internalType":"int128","name":"liquidityNet","type":"int128"},{"internalType":"uint256","name":"feeGrowthOutside0X128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthOutside1X128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int24","name":"tick","type":"int24"}],"name":"getTickLiquidity","outputs":[{"internalType":"uint128","name":"liquidityGross","type":"uint128"},{"internalType":"int128","name":"liquidityNet","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolManager","outputs":[{"internalType":"contract IPoolManager","name":"","type":"address"}],"stateMutability":"view","type":"function"}] -const getPoolId = ( - currencyA: Token, - currencyB: Token, - fee: number, - tickSpacing: number, - hooks: string -): string =>{ - const [currency0, currency1] = currencyA.sortsBefore(currencyB) ? [currencyA, currencyB] : [currencyB, currencyA] - const currency0Addr = currency0.isNative ? ZeroAddress : currency0.wrapped.address - const currency1Addr = currency1.isNative ? ZeroAddress : currency1.wrapped.address - return keccak256( - AbiCoder.defaultAbiCoder().encode( - ['address', 'address', 'uint24', 'int24', 'address'], - [currency0Addr, currency1Addr, fee, tickSpacing, hooks] - ), - ) -} - - -function getPriceFromSqrtX96( - sqrtPriceX96: bigint, - decimalsA: number, - decimalsB: number -): number { - const Q96 = 2n ** 96n; - - // Scale to avoid floating point math by using a 1e18 factor - const numerator = sqrtPriceX96 * sqrtPriceX96 * 10n ** 18n; - const denominator = Q96 * Q96; - - let price = numerator / denominator; - - // Adjust for decimal differences - const decimalAdjustment = decimalsB - decimalsA; - if (decimalAdjustment > 0) { - price *= 10n ** BigInt(decimalAdjustment); - } else if (decimalAdjustment < 0) { - price /= 10n ** BigInt(-decimalAdjustment); - } - - return Number(price) / 1e18; -} - -const getExchangeRate = async ( - provider: Provider, - tokenA: Token, - tokenB: Token, - fee: number, - hookAddress: string = ZeroAddress -): Promise => { - - const stateView = new Contract(STATE_VIEW_ADDRESS, STATE_VIEW_ABI, provider); - - const [token0, token1] = tokenA.sortsBefore(tokenB) - ? [tokenA, tokenB] - : [tokenB, tokenA]; - - const poolId: string = getPoolId(token0, token1, fee, 60, hookAddress); - if (poolId === ZeroAddress) throw new Error("Invalid poolId"); - const response = await stateView.getSlot0(poolId); - const sqrtPriceX96 = response[0]; - console.log("response", response) - const price = getPriceFromSqrtX96(sqrtPriceX96, token1.decimals, token0.decimals); - return `${price}`; - -} - -// export const pricesFromUniswap4 = async (provider: Provider): Promise => { export const pricesFromUniswap4 = async (provider: Provider) => { const tokenA = new Token(CHAIN_ID, "0x55296f69f40ea6d20e478533c15a6b08b654e758", 18, 'XYO'); const tokenB = new Token(CHAIN_ID, "0xdac17f958d2ee523a2206206994597c13d831ec7", 6, 'USDT'); diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/spec/pricesFromUniswap4.spec.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/spec/pricesFromUniswap4.spec.ts index c1eaa9ec6..824b1d94c 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/spec/pricesFromUniswap4.spec.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/spec/pricesFromUniswap4.spec.ts @@ -6,7 +6,6 @@ import { } from 'vitest' import { pricesFromUniswap4 } from '../pricesFromUniswap4.ts' - describe.skipIf(!(process.env.INFURA_PROJECT_ID && process.env.INFURA_PROJECT_SECRET))('pricesFromUniswap4', () => { test('pricesFromUniswap4', async () => { const provider = getProviderFromEnv() From 4a1b9d6c6b603e55ffde2c6f40cc723835444de5 Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Mon, 21 Apr 2025 20:49:46 -0500 Subject: [PATCH 12/15] Documentation & formatting --- .../market/packages/uniswap/src/Witness.ts | 6 +-- .../uniswap/src/lib/v4/getExchangeRate.ts | 50 +++++++++---------- .../packages/uniswap/src/lib/v4/getPoolId.ts | 18 ++++--- .../uniswap/src/lib/v4/getPriceFromSqrtX96.ts | 30 +++++------ .../uniswap/src/lib/v4/pricesFromUniswap4.ts | 13 ++--- .../lib/v4/spec/pricesFromUniswap4.spec.ts | 5 +- 6 files changed, 63 insertions(+), 59 deletions(-) diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/Witness.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/Witness.ts index 2aacf42d3..63e60662c 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/Witness.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/Witness.ts @@ -4,8 +4,7 @@ import type { AnyConfigSchema } from '@xyo-network/module-model' import type { Payload, Schema } from '@xyo-network/payload-model' import type { UniswapCryptoMarketPayload } from '@xyo-network/uniswap-crypto-market-payload-plugin' import { - UniswapCryptoMarketSchema, - UniswapCryptoMarketWitnessConfigSchema, + UniswapCryptoMarketSchema,UniswapCryptoMarketWitnessConfigSchema, } from '@xyo-network/uniswap-crypto-market-payload-plugin' import type { WitnessParams } from '@xyo-network/witness-model' import type { Provider } from 'ethers' @@ -13,8 +12,7 @@ import type { Provider } from 'ethers' import type { UniswapCryptoMarketWitnessConfig } from './Config.ts' import type { EthersUniSwap3Pair } from './lib/index.ts' import { - createUniswapPoolContracts, - pricesFromUniswap3, UniswapPoolContracts, + createUniswapPoolContracts,pricesFromUniswap3, UniswapPoolContracts, } from './lib/index.ts' export type UniswapCryptoMarketWitnessParams = WitnessParams< diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getExchangeRate.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getExchangeRate.ts index 8b3af273b..c828df619 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getExchangeRate.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getExchangeRate.ts @@ -1,34 +1,34 @@ -import { Token } from "@uniswap/sdk-core"; -import { ZeroAddress } from "ethers/constants"; -import { Contract } from "ethers/contract"; -import { Provider } from "ethers/providers"; -import { getPoolId } from "./getPoolId.ts"; -import { getPriceFromSqrtX96 } from "./getPriceFromSqrtX96.ts"; +import { Token, Currency } from "@uniswap/sdk-core" +import { ZeroAddress } from "ethers/constants" +import { Contract } from "ethers/contract" +import { Provider } from "ethers/providers" +import { getPoolId } from "./getPoolId.ts" +import { getPriceFromSqrtX96 } from "./getPriceFromSqrtX96.ts" -const STATE_VIEW_ADDRESS = "0x7ffe42c4a5deea5b0fec41c94c136cf115597227"; // State view contract address +const STATE_VIEW_ADDRESS = "0x7ffe42c4a5deea5b0fec41c94c136cf115597227" // State view contract address const STATE_VIEW_ABI = [{"inputs":[{"internalType":"contract IPoolManager","name":"_poolManager","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"}],"name":"getFeeGrowthGlobals","outputs":[{"internalType":"uint256","name":"feeGrowthGlobal0","type":"uint256"},{"internalType":"uint256","name":"feeGrowthGlobal1","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"}],"name":"getFeeGrowthInside","outputs":[{"internalType":"uint256","name":"feeGrowthInside0X128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthInside1X128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"}],"name":"getLiquidity","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"bytes32","name":"positionId","type":"bytes32"}],"name":"getPositionInfo","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint256","name":"feeGrowthInside0LastX128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthInside1LastX128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"getPositionInfo","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint256","name":"feeGrowthInside0LastX128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthInside1LastX128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"bytes32","name":"positionId","type":"bytes32"}],"name":"getPositionLiquidity","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"}],"name":"getSlot0","outputs":[{"internalType":"uint160","name":"sqrtPriceX96","type":"uint160"},{"internalType":"int24","name":"tick","type":"int24"},{"internalType":"uint24","name":"protocolFee","type":"uint24"},{"internalType":"uint24","name":"lpFee","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int16","name":"tick","type":"int16"}],"name":"getTickBitmap","outputs":[{"internalType":"uint256","name":"tickBitmap","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int24","name":"tick","type":"int24"}],"name":"getTickFeeGrowthOutside","outputs":[{"internalType":"uint256","name":"feeGrowthOutside0X128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthOutside1X128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int24","name":"tick","type":"int24"}],"name":"getTickInfo","outputs":[{"internalType":"uint128","name":"liquidityGross","type":"uint128"},{"internalType":"int128","name":"liquidityNet","type":"int128"},{"internalType":"uint256","name":"feeGrowthOutside0X128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthOutside1X128","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int24","name":"tick","type":"int24"}],"name":"getTickLiquidity","outputs":[{"internalType":"uint128","name":"liquidityGross","type":"uint128"},{"internalType":"int128","name":"liquidityNet","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolManager","outputs":[{"internalType":"contract IPoolManager","name":"","type":"address"}],"stateMutability":"view","type":"function"}] -export const getExchangeRate = async ( - provider: Provider, - tokenA: Token, - tokenB: Token, - fee: number, - hookAddress: string = ZeroAddress -): Promise => { - - const stateView = new Contract(STATE_VIEW_ADDRESS, STATE_VIEW_ABI, provider); - +/** + * Returns the price of the token pair in the Uniswap V4 pool. + * @param provider The EVM provider to use for the transaction. + * @param tokenA The first token in the pair. + * @param tokenB The second token in the pair. + * @param fee The fee tier for the pool. + * @param hookAddress The address of the hook contract. Default is ZeroAddress. + * @returns The price of the token pair. + */ +export const getExchangeRate = async (provider: Provider, tokenA: Token, tokenB: Token, fee: number, hookAddress: string = ZeroAddress): Promise => { + const stateView = new Contract(STATE_VIEW_ADDRESS, STATE_VIEW_ABI, provider) const [token0, token1] = tokenA.sortsBefore(tokenB) ? [tokenA, tokenB] - : [tokenB, tokenA]; - - const poolId: string = getPoolId(token0, token1, fee, 60, hookAddress); - if (poolId === ZeroAddress) throw new Error("Invalid poolId"); - const response = await stateView.getSlot0(poolId); - const sqrtPriceX96 = response[0]; + : [tokenB, tokenA] + const poolId: string = getPoolId(token0, token1, fee, 60, hookAddress) + if (poolId === ZeroAddress) throw new Error("Invalid poolId") + const response = await stateView.getSlot0(poolId) + const sqrtPriceX96 = response[0] console.log("response", response) - const price = getPriceFromSqrtX96(sqrtPriceX96, token1.decimals, token0.decimals); - return `${price}`; + const price = getPriceFromSqrtX96(sqrtPriceX96, token1.decimals, token0.decimals) + return price } diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getPoolId.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getPoolId.ts index c5bb5db95..48eb7b615 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getPoolId.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getPoolId.ts @@ -3,13 +3,17 @@ import { AbiCoder } from "ethers/abi" import { ZeroAddress } from "ethers/constants" import { keccak256 } from "ethers/crypto" -export const getPoolId = ( - currencyA: Token, - currencyB: Token, - fee: number, - tickSpacing: number, - hooks: string -): string =>{ +/** + * Computes the pool address for a given pair of tokens, fee, tick spacing, and hooks which + * is used to identify the pool on the Uniswap V4 protocol. + * @param currencyA - The first token in the pair. + * @param currencyB - The second token in the pair. + * @param fee - The fee tier for the pool. + * @param tickSpacing - The tick spacing for the pool. + * @param hooks - The hooks associated with the pool. + * @returns The computed pool address as a string. + */ +export const getPoolId = (currencyA: Token, currencyB: Token, fee: number, tickSpacing: number, hooks: string): string => { const [currency0, currency1] = currencyA.sortsBefore(currencyB) ? [currencyA, currencyB] : [currencyB, currencyA] const currency0Addr = currency0.isNative ? ZeroAddress : currency0.wrapped.address const currency1Addr = currency1.isNative ? ZeroAddress : currency1.wrapped.address diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getPriceFromSqrtX96.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getPriceFromSqrtX96.ts index 22198d368..54ff3e743 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getPriceFromSqrtX96.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getPriceFromSqrtX96.ts @@ -1,24 +1,26 @@ - -export const getPriceFromSqrtX96 = ( - sqrtPriceX96: bigint, - decimalsA: number, - decimalsB: number -): number => { - const Q96 = 2n ** 96n; +/** + * Calculates the price from the square root price in Q96 format. + * @param sqrtPriceX96 The square root price in Q96 format. + * @param decimalsA The number of decimals for the first token. + * @param decimalsB The number of decimals for the second token. + * @returns The price as a number. + */ +export const getPriceFromSqrtX96 = (sqrtPriceX96: bigint, decimalsA: number, decimalsB: number): number => { + const Q96 = 2n ** 96n // Scale to avoid floating point math by using a 1e18 factor - const numerator = sqrtPriceX96 * sqrtPriceX96 * 10n ** 18n; - const denominator = Q96 * Q96; + const numerator = sqrtPriceX96 * sqrtPriceX96 * 10n ** 18n + const denominator = Q96 * Q96 - let price = numerator / denominator; + let price = numerator / denominator // Adjust for decimal differences - const decimalAdjustment = decimalsB - decimalsA; + const decimalAdjustment = decimalsB - decimalsA if (decimalAdjustment > 0) { - price *= 10n ** BigInt(decimalAdjustment); + price *= 10n ** BigInt(decimalAdjustment) } else if (decimalAdjustment < 0) { - price /= 10n ** BigInt(-decimalAdjustment); + price /= 10n ** BigInt(-decimalAdjustment) } - return Number(price) / 1e18; + return Number(price) / 1e18 } diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/pricesFromUniswap4.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/pricesFromUniswap4.ts index d493f3b8c..eb8cae05a 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/pricesFromUniswap4.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/pricesFromUniswap4.ts @@ -1,12 +1,9 @@ -import { Provider } from 'ethers'; -import { Token } from '@uniswap/sdk-core'; -import { getExchangeRate } from './getExchangeRate.ts'; +import { Provider } from 'ethers' +import { Token } from '@uniswap/sdk-core' +import { getExchangeRate } from './getExchangeRate.ts' -const CHAIN_ID = 1; // Ethereum Mainnet -export const pricesFromUniswap4 = async (provider: Provider) => { - const tokenA = new Token(CHAIN_ID, "0x55296f69f40ea6d20e478533c15a6b08b654e758", 18, 'XYO'); - const tokenB = new Token(CHAIN_ID, "0xdac17f958d2ee523a2206206994597c13d831ec7", 6, 'USDT'); - const rate = await getExchangeRate(provider, tokenA, tokenB, 3000); +export const pricesFromUniswap4 = async (tokenA: Token, tokenB: Token, provider: Provider) => { + const rate = await getExchangeRate(provider, tokenA, tokenB, 3000) return rate } diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/spec/pricesFromUniswap4.spec.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/spec/pricesFromUniswap4.spec.ts index 824b1d94c..0cd68bceb 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/spec/pricesFromUniswap4.spec.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/spec/pricesFromUniswap4.spec.ts @@ -5,11 +5,14 @@ import { describe, expect, test, } from 'vitest' import { pricesFromUniswap4 } from '../pricesFromUniswap4.ts' +import { Token } from '@uniswap/sdk-core' describe.skipIf(!(process.env.INFURA_PROJECT_ID && process.env.INFURA_PROJECT_SECRET))('pricesFromUniswap4', () => { test('pricesFromUniswap4', async () => { const provider = getProviderFromEnv() - const value = await pricesFromUniswap4(provider) + const tokenA = new Token(1, "0x55296f69f40ea6d20e478533c15a6b08b654e758", 18, 'XYO') + const tokenB = new Token(1, "0xdac17f958d2ee523a2206206994597c13d831ec7", 6, 'USDT') + const value = await pricesFromUniswap4(tokenA, tokenB, provider) expect(value).toBeDefined() }) }) From e1f6431f462745c1133245c10e248a2284446a75 Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Mon, 21 Apr 2025 20:59:38 -0500 Subject: [PATCH 13/15] Add standard helpers --- .../lib/v4/UniswapV4TokenContractIdentifier.ts | 8 ++++++++ .../src/lib/v4/UniswapV4TokenIdentifier.ts | 7 +++++++ .../uniswap/src/lib/v4/getExchangeRate.ts | 17 ++++++++++++----- .../uniswap/src/lib/v4/pricesFromUniswap4.ts | 10 +++++----- 4 files changed, 32 insertions(+), 10 deletions(-) create mode 100644 packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/UniswapV4TokenContractIdentifier.ts create mode 100644 packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/UniswapV4TokenIdentifier.ts diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/UniswapV4TokenContractIdentifier.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/UniswapV4TokenContractIdentifier.ts new file mode 100644 index 000000000..72b14baf3 --- /dev/null +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/UniswapV4TokenContractIdentifier.ts @@ -0,0 +1,8 @@ +import { Token } from "@uniswap/sdk-core"; + +export interface UniswapV4TokenContractIdentifier { + tokenA: Token, + tokenB: Token, + fee: number, + hookAddress?: string, +} diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/UniswapV4TokenIdentifier.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/UniswapV4TokenIdentifier.ts new file mode 100644 index 000000000..f3c79b979 --- /dev/null +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/UniswapV4TokenIdentifier.ts @@ -0,0 +1,7 @@ +export interface UniswapV4TokenIdentifier { + chainId: number, + address: string, + decimals: number, + symbol?: string, + name?: string +} \ No newline at end of file diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getExchangeRate.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getExchangeRate.ts index c828df619..54b6e2cdf 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getExchangeRate.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getExchangeRate.ts @@ -1,4 +1,4 @@ -import { Token, Currency } from "@uniswap/sdk-core" +import { Token } from "@uniswap/sdk-core" import { ZeroAddress } from "ethers/constants" import { Contract } from "ethers/contract" import { Provider } from "ethers/providers" @@ -11,24 +11,31 @@ const STATE_VIEW_ABI = [{"inputs":[{"internalType":"contract IPoolManager","name /** * Returns the price of the token pair in the Uniswap V4 pool. - * @param provider The EVM provider to use for the transaction. * @param tokenA The first token in the pair. * @param tokenB The second token in the pair. * @param fee The fee tier for the pool. * @param hookAddress The address of the hook contract. Default is ZeroAddress. + * @param provider The EVM provider to use for the transaction. * @returns The price of the token pair. */ -export const getExchangeRate = async (provider: Provider, tokenA: Token, tokenB: Token, fee: number, hookAddress: string = ZeroAddress): Promise => { +export const getExchangeRate = async ( + tokenA: Token, + tokenB: Token, + fee: number, + hookAddress: string | undefined, + provider: Provider +): Promise => { + const hooks = hookAddress || ZeroAddress const stateView = new Contract(STATE_VIEW_ADDRESS, STATE_VIEW_ABI, provider) const [token0, token1] = tokenA.sortsBefore(tokenB) ? [tokenA, tokenB] : [tokenB, tokenA] - const poolId: string = getPoolId(token0, token1, fee, 60, hookAddress) + + const poolId: string = getPoolId(token0, token1, fee, 60, hooks) if (poolId === ZeroAddress) throw new Error("Invalid poolId") const response = await stateView.getSlot0(poolId) const sqrtPriceX96 = response[0] console.log("response", response) const price = getPriceFromSqrtX96(sqrtPriceX96, token1.decimals, token0.decimals) return price - } diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/pricesFromUniswap4.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/pricesFromUniswap4.ts index eb8cae05a..06570ce11 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/pricesFromUniswap4.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/pricesFromUniswap4.ts @@ -1,9 +1,9 @@ -import { Provider } from 'ethers' -import { Token } from '@uniswap/sdk-core' +import { Provider, ZeroAddress } from 'ethers' import { getExchangeRate } from './getExchangeRate.ts' +import { UniswapV4TokenContractIdentifier } from './UniswapV4TokenContractIdentifier.ts' - -export const pricesFromUniswap4 = async (tokenA: Token, tokenB: Token, provider: Provider) => { - const rate = await getExchangeRate(provider, tokenA, tokenB, 3000) +export const pricesFromUniswap4 = async (contract: UniswapV4TokenContractIdentifier, provider: Provider) => { + const { tokenA, tokenB, fee, hookAddress = ZeroAddress } = contract + const rate = await getExchangeRate(tokenA, tokenB, fee, hookAddress, provider) return rate } From dc574b476b6bedc834a0dba1999a4a889254295e Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Mon, 21 Apr 2025 21:05:42 -0500 Subject: [PATCH 14/15] Use new format --- .../uniswap/src/lib/v4/spec/pricesFromUniswap4.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/spec/pricesFromUniswap4.spec.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/spec/pricesFromUniswap4.spec.ts index 0cd68bceb..0f391954f 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/spec/pricesFromUniswap4.spec.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/spec/pricesFromUniswap4.spec.ts @@ -12,7 +12,8 @@ describe.skipIf(!(process.env.INFURA_PROJECT_ID && process.env.INFURA_PROJECT_SE const provider = getProviderFromEnv() const tokenA = new Token(1, "0x55296f69f40ea6d20e478533c15a6b08b654e758", 18, 'XYO') const tokenB = new Token(1, "0xdac17f958d2ee523a2206206994597c13d831ec7", 6, 'USDT') - const value = await pricesFromUniswap4(tokenA, tokenB, provider) + const tokenContractIdentifier = { tokenA, tokenB, fee: 3000 } + const value = await pricesFromUniswap4(tokenContractIdentifier, provider) expect(value).toBeDefined() }) }) From 4c35eda0a3587f0bc097c2b1e83064553955764f Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Mon, 21 Apr 2025 21:08:28 -0500 Subject: [PATCH 15/15] Formatting and remove logs --- .../market/packages/uniswap/src/lib/v4/getExchangeRate.ts | 1 - .../packages/market/packages/uniswap/src/spec/Plugin.spec.ts | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getExchangeRate.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getExchangeRate.ts index 54b6e2cdf..92b6067d1 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getExchangeRate.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/lib/v4/getExchangeRate.ts @@ -35,7 +35,6 @@ export const getExchangeRate = async ( if (poolId === ZeroAddress) throw new Error("Invalid poolId") const response = await stateView.getSlot0(poolId) const sqrtPriceX96 = response[0] - console.log("response", response) const price = getPriceFromSqrtX96(sqrtPriceX96, token1.decimals, token0.decimals) return price } diff --git a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/spec/Plugin.spec.ts b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/spec/Plugin.spec.ts index 4d1e8002d..aef83fed3 100644 --- a/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/spec/Plugin.spec.ts +++ b/packages/payloadset/packages/crypto/packages/market/packages/uniswap/src/spec/Plugin.spec.ts @@ -2,8 +2,7 @@ import '@xylabs/vitest-extended' import { PayloadSetPluginResolver } from '@xyo-network/payloadset-plugin' import { - describe, expect, - test, + describe, expect, test, } from 'vitest' import { UniswapCryptoMarketPlugin } from '../Plugin.ts'