Initial commit: add .gitignore and README
This commit is contained in:
186
examples/ts/uniswap-v3-oracle.ts
Normal file
186
examples/ts/uniswap-v3-oracle.ts
Normal file
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* Uniswap v3: TWAP Oracle usage
|
||||
*
|
||||
* This example demonstrates how to use Uniswap v3 pools as price oracles
|
||||
* by querying time-weighted average prices (TWAP).
|
||||
*
|
||||
* Note: Always use TWAP, not spot prices, to protect against manipulation.
|
||||
*/
|
||||
|
||||
import { createRpcClient } from '../../src/utils/chain-config.js';
|
||||
import { getTokenMetadata } from '../../src/utils/tokens.js';
|
||||
import type { Address } from 'viem';
|
||||
|
||||
const CHAIN_ID = 1; // Mainnet
|
||||
|
||||
// Uniswap v3 Pool ABI
|
||||
const POOL_ABI = [
|
||||
{
|
||||
name: 'slot0',
|
||||
type: 'function',
|
||||
stateMutability: 'view',
|
||||
inputs: [],
|
||||
outputs: [
|
||||
{ name: 'sqrtPriceX96', type: 'uint160' },
|
||||
{ name: 'tick', type: 'int24' },
|
||||
{ name: 'observationIndex', type: 'uint16' },
|
||||
{ name: 'observationCardinality', type: 'uint16' },
|
||||
{ name: 'observationCardinalityNext', type: 'uint16' },
|
||||
{ name: 'feeProtocol', type: 'uint8' },
|
||||
{ name: 'unlocked', type: 'bool' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'observations',
|
||||
type: 'function',
|
||||
stateMutability: 'view',
|
||||
inputs: [{ name: 'index', type: 'uint16' }],
|
||||
outputs: [
|
||||
{ name: 'blockTimestamp', type: 'uint32' },
|
||||
{ name: 'tickCumulative', type: 'int56' },
|
||||
{ name: 'secondsPerLiquidityCumulativeX128', type: 'uint160' },
|
||||
{ name: 'initialized', type: 'bool' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'token0',
|
||||
type: 'function',
|
||||
stateMutability: 'view',
|
||||
inputs: [],
|
||||
outputs: [{ name: '', type: 'address' }],
|
||||
},
|
||||
{
|
||||
name: 'token1',
|
||||
type: 'function',
|
||||
stateMutability: 'view',
|
||||
inputs: [],
|
||||
outputs: [{ name: '', type: 'address' }],
|
||||
},
|
||||
{
|
||||
name: 'fee',
|
||||
type: 'function',
|
||||
stateMutability: 'view',
|
||||
inputs: [],
|
||||
outputs: [{ name: '', type: 'uint24' }],
|
||||
},
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* Calculate price from sqrtPriceX96
|
||||
* price = (sqrtPriceX96 / 2^96)^2
|
||||
*/
|
||||
function calculatePriceFromSqrtPriceX96(sqrtPriceX96: bigint): number {
|
||||
const Q96 = 2n ** 96n;
|
||||
const price = Number(sqrtPriceX96) ** 2 / Number(Q96) ** 2;
|
||||
return price;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate TWAP from observations
|
||||
*
|
||||
* TWAP = (tickCumulative1 - tickCumulative0) / (time1 - time0)
|
||||
*/
|
||||
function calculateTWAP(
|
||||
tickCumulative0: bigint,
|
||||
tickCumulative1: bigint,
|
||||
time0: number,
|
||||
time1: number
|
||||
): number {
|
||||
if (time1 === time0) {
|
||||
throw new Error('Time difference cannot be zero');
|
||||
}
|
||||
const tickDelta = Number(tickCumulative1 - tickCumulative0);
|
||||
const timeDelta = time1 - time0;
|
||||
const avgTick = tickDelta / timeDelta;
|
||||
|
||||
// Convert tick to price: price = 1.0001^tick
|
||||
const price = 1.0001 ** avgTick;
|
||||
return price;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pool address from Uniswap v3 Factory
|
||||
* In production, use the official Uniswap v3 SDK to compute pool addresses
|
||||
*/
|
||||
async function getPoolAddress(
|
||||
client: any,
|
||||
token0: Address,
|
||||
token1: Address,
|
||||
fee: number
|
||||
): Promise<Address> {
|
||||
// This is a simplified example. In production, use:
|
||||
// 1. Uniswap v3 Factory to get pool address
|
||||
// 2. Or compute pool address using CREATE2 (see Uniswap v3 SDK)
|
||||
// For now, this is a placeholder
|
||||
throw new Error('Implement pool address resolution using Factory or SDK');
|
||||
}
|
||||
|
||||
async function queryOracle() {
|
||||
const publicClient = createRpcClient(CHAIN_ID);
|
||||
|
||||
const token0 = getTokenMetadata(CHAIN_ID, 'USDC');
|
||||
const token1 = getTokenMetadata(CHAIN_ID, 'WETH');
|
||||
const fee = 3000; // 0.3% fee tier
|
||||
|
||||
console.log(`Querying Uniswap v3 TWAP oracle for ${token0.symbol}/${token1.symbol}`);
|
||||
console.log(`Fee tier: ${fee} (0.3%)`);
|
||||
|
||||
// Note: In production, you need to:
|
||||
// 1. Get the pool address from Uniswap v3 Factory
|
||||
// 2. Or use the Uniswap v3 SDK to compute it
|
||||
// For this example, we'll demonstrate the concept
|
||||
|
||||
// Example: Query current slot0 (spot price - not recommended for production!)
|
||||
// const poolAddress = await getPoolAddress(publicClient, token0.address, token1.address, fee);
|
||||
|
||||
// const slot0 = await publicClient.readContract({
|
||||
// address: poolAddress,
|
||||
// abi: POOL_ABI,
|
||||
// functionName: 'slot0',
|
||||
// });
|
||||
|
||||
// const sqrtPriceX96 = slot0[0];
|
||||
// const currentPrice = calculatePriceFromSqrtPriceX96(sqrtPriceX96);
|
||||
// console.log(`Current spot price: ${currentPrice} ${token1.symbol} per ${token0.symbol}`);
|
||||
|
||||
// Example: Query TWAP from observations
|
||||
// const observationIndex = slot0[2];
|
||||
// const observation0 = await publicClient.readContract({
|
||||
// address: poolAddress,
|
||||
// abi: POOL_ABI,
|
||||
// functionName: 'observations',
|
||||
// args: [observationIndex],
|
||||
// });
|
||||
|
||||
// Query a previous observation (e.g., 1 hour ago)
|
||||
// const previousIndex = (observationIndex - 3600) % observationCardinality;
|
||||
// const observation1 = await publicClient.readContract({
|
||||
// address: poolAddress,
|
||||
// abi: POOL_ABI,
|
||||
// functionName: 'observations',
|
||||
// args: [previousIndex],
|
||||
// });
|
||||
|
||||
// const twap = calculateTWAP(
|
||||
// observation0.tickCumulative,
|
||||
// observation1.tickCumulative,
|
||||
// observation0.blockTimestamp,
|
||||
// observation1.blockTimestamp
|
||||
// );
|
||||
// console.log(`TWAP (1 hour): ${twap} ${token1.symbol} per ${token0.symbol}`);
|
||||
|
||||
console.log('\n⚠️ This is a conceptual example.');
|
||||
console.log('In production, use:');
|
||||
console.log(' 1. Uniswap v3 OracleLibrary (see Uniswap v3 periphery contracts)');
|
||||
console.log(' 2. Uniswap v3 SDK for price calculations');
|
||||
console.log(' 3. Always use TWAP, never spot prices');
|
||||
console.log(' 4. Ensure sufficient observation cardinality for your TWAP window');
|
||||
}
|
||||
|
||||
// Run if executed directly
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
queryOracle().catch(console.error);
|
||||
}
|
||||
|
||||
export { queryOracle, calculatePriceFromSqrtPriceX96, calculateTWAP };
|
||||
|
||||
Reference in New Issue
Block a user