-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
656 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,25 @@ | ||
name: Publish package | ||
on: | ||
release: | ||
types: [published] | ||
types: [ published ] | ||
jobs: | ||
npm_publish: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v3 | ||
|
||
- uses: actions/setup-node@v3 | ||
with: | ||
node-version: '18.x' | ||
registry-url: 'https://registry.npmjs.org' | ||
scope: '@aperture_finance' | ||
- run: yarn install --frozen-lockfile | ||
- run: yarn build | ||
|
||
- name: Install dependencies 📦 | ||
run: yarn install --frozen-lockfile | ||
|
||
- name: Generate Typechain types and build | ||
run: yarn build | ||
|
||
- run: npm publish --access public | ||
env: | ||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * as viem from "./viem"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { Abi, AbiFunction } from "abitype"; | ||
import { | ||
CallExecutionError, | ||
ContractFunctionResult, | ||
EncodeDeployDataParameters, | ||
Hex, | ||
PublicClient, | ||
decodeFunctionResult, | ||
encodeDeployData, | ||
} from "viem"; | ||
|
||
/** | ||
* Deploy an ephemeral contract which reverts data in the constructor via `eth_call`. | ||
* @param deployParams The abi, bytecode, and constructor arguments. | ||
* @param publicClient Viem public client. | ||
* @param blockNumber Optional block number to query. | ||
* @returns The result of the contract function call. | ||
*/ | ||
export async function callEphemeralContract<TAbi extends Abi>( | ||
deployParams: EncodeDeployDataParameters<TAbi>, | ||
publicClient: PublicClient, | ||
blockNumber?: bigint, | ||
): Promise<ContractFunctionResult<TAbi>> { | ||
try { | ||
await publicClient.call({ | ||
data: encodeDeployData(deployParams), | ||
blockNumber, | ||
}); | ||
} catch (error) { | ||
const baseError = (error as CallExecutionError).walk(); | ||
if ("data" in baseError) { | ||
const abiFunctions = deployParams.abi.filter((x) => x.type === "function"); | ||
if (abiFunctions.length === 1) { | ||
return decodeFunctionResult({ | ||
abi: abiFunctions as [AbiFunction], | ||
data: baseError.data as Hex, | ||
}) as ContractFunctionResult<TAbi>; | ||
} else { | ||
throw new Error("abi should contain exactly one function"); | ||
} | ||
} else { | ||
throw error; | ||
} | ||
} | ||
throw new Error("deployment should revert"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from "./caller"; | ||
export * from "./poolLens"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
import { AbiParametersToPrimitiveTypes, ExtractAbiFunction } from "abitype"; | ||
import { Address, PublicClient } from "viem"; | ||
import { | ||
EphemeralAllPositionsByOwner__factory, | ||
EphemeralPoolPositions__factory, | ||
EphemeralPoolSlots__factory, | ||
EphemeralPoolTickBitmap__factory, | ||
EphemeralPoolTicks__factory, | ||
EphemeralGetPopulatedTicksInRange__factory, | ||
EphemeralGetPosition__factory, | ||
EphemeralGetPositions__factory, | ||
} from "../../typechain"; | ||
import { callEphemeralContract } from "./caller"; | ||
|
||
/** | ||
* Fetches the liquidity within the tick range for the specified pool by deploying an ephemeral contract via `eth_call`. | ||
* Each tick consumes about 100k gas, so this method may fail if the number of ticks exceeds 3k assuming the provider | ||
* gas limit is 300m. | ||
* @param pool The liquidity pool to fetch the tick to liquidity map for. | ||
* @param tickLower The lower tick to fetch liquidity for. | ||
* @param tickUpper The upper tick to fetch liquidity for. | ||
* @param publicClient Viem public client. | ||
* @param blockNumber Optional block number to query. | ||
*/ | ||
export async function getPopulatedTicksInRange( | ||
pool: Address, | ||
tickLower: number, | ||
tickUpper: number, | ||
publicClient: PublicClient, | ||
blockNumber?: bigint, | ||
) { | ||
return await callEphemeralContract( | ||
{ | ||
abi: EphemeralGetPopulatedTicksInRange__factory.abi, | ||
bytecode: EphemeralGetPopulatedTicksInRange__factory.bytecode, | ||
args: [pool, tickLower, tickUpper], | ||
}, | ||
publicClient, | ||
blockNumber, | ||
); | ||
} | ||
|
||
/** | ||
* Get the position details in a single call by deploying an ephemeral contract via `eth_call` | ||
* @param npm Nonfungible position manager address. | ||
* @param positionId Position id. | ||
* @param publicClient Viem public client. | ||
* @param blockNumber Optional block number to query. | ||
* @returns The position details. | ||
*/ | ||
export async function getPositionDetails( | ||
npm: Address, | ||
positionId: bigint, | ||
publicClient: PublicClient, | ||
blockNumber?: bigint, | ||
) { | ||
return await callEphemeralContract( | ||
{ | ||
abi: EphemeralGetPosition__factory.abi, | ||
bytecode: EphemeralGetPosition__factory.bytecode, | ||
args: [npm, positionId], | ||
}, | ||
publicClient, | ||
blockNumber, | ||
); | ||
} | ||
|
||
/** | ||
* Get the state and pool for all positions in a single call by deploying an ephemeral contract via `eth_call`. | ||
* @param npm Nonfungible position manager address. | ||
* @param positionIds Position ids. | ||
* @param publicClient Viem public client. | ||
* @param blockNumber Optional block number to query. | ||
* @returns The position details for all positions. | ||
*/ | ||
export async function getPositions( | ||
npm: Address, | ||
positionIds: bigint[], | ||
publicClient: PublicClient, | ||
blockNumber?: bigint, | ||
) { | ||
return await callEphemeralContract( | ||
{ | ||
abi: EphemeralGetPositions__factory.abi, | ||
bytecode: EphemeralGetPositions__factory.bytecode, | ||
args: [npm, positionIds], | ||
}, | ||
publicClient, | ||
blockNumber, | ||
); | ||
} | ||
|
||
/** | ||
* Get the state and pool for all positions of the specified owner by deploying an ephemeral contract via `eth_call`. | ||
* Each position consumes about 200k gas, so this method may fail if the number of positions exceeds 1500 assuming the | ||
* provider gas limit is 300m. | ||
* @param npm Nonfungible position manager address. | ||
* @param owner The owner of the positions. | ||
* @param publicClient Viem public client. | ||
* @param blockNumber Optional block number to query. | ||
* @returns The position details for all positions of the specified owner. | ||
*/ | ||
export async function getAllPositionsByOwner( | ||
npm: Address, | ||
owner: Address, | ||
publicClient: PublicClient, | ||
blockNumber?: bigint, | ||
) { | ||
return await callEphemeralContract( | ||
{ | ||
abi: EphemeralAllPositionsByOwner__factory.abi, | ||
bytecode: EphemeralAllPositionsByOwner__factory.bytecode, | ||
args: [npm, owner], | ||
}, | ||
publicClient, | ||
blockNumber, | ||
); | ||
} | ||
|
||
export async function getStaticSlots(pool: Address, publicClient: PublicClient, blockNumber?: bigint) { | ||
return await callEphemeralContract( | ||
{ | ||
abi: EphemeralPoolSlots__factory.abi, | ||
bytecode: EphemeralPoolSlots__factory.bytecode, | ||
args: [pool], | ||
}, | ||
publicClient, | ||
blockNumber, | ||
); | ||
} | ||
|
||
export async function getTicksSlots( | ||
pool: Address, | ||
tickLower: number, | ||
tickUpper: number, | ||
publicClient: PublicClient, | ||
blockNumber?: bigint, | ||
) { | ||
return await callEphemeralContract( | ||
{ | ||
abi: EphemeralPoolTicks__factory.abi, | ||
bytecode: EphemeralPoolTicks__factory.bytecode, | ||
args: [pool, tickLower, tickUpper], | ||
}, | ||
publicClient, | ||
blockNumber, | ||
); | ||
} | ||
|
||
export async function getTickBitmapSlots(pool: Address, publicClient: PublicClient, blockNumber?: bigint) { | ||
return await callEphemeralContract( | ||
{ | ||
abi: EphemeralPoolTickBitmap__factory.abi, | ||
bytecode: EphemeralPoolTickBitmap__factory.bytecode, | ||
args: [pool], | ||
}, | ||
publicClient, | ||
blockNumber, | ||
); | ||
} | ||
|
||
export type PositionKey = AbiParametersToPrimitiveTypes< | ||
ExtractAbiFunction<typeof EphemeralPoolPositions__factory.abi, "getPositions">["inputs"], | ||
"inputs" | ||
>[1][0]; | ||
|
||
export async function getPositionsSlots( | ||
pool: Address, | ||
keys: PositionKey[], | ||
publicClient: PublicClient, | ||
blockNumber?: bigint, | ||
) { | ||
return await callEphemeralContract( | ||
{ | ||
abi: EphemeralPoolPositions__factory.abi, | ||
bytecode: EphemeralPoolPositions__factory.bytecode, | ||
args: [pool, keys], | ||
}, | ||
publicClient, | ||
blockNumber, | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { ApertureSupportedChainId, getChainInfo, viem } from "@aperture_finance/uniswap-v3-automation-sdk"; | ||
import { TickMath } from "@uniswap/v3-sdk"; | ||
import { expect } from "chai"; | ||
import { config as dotenvConfig } from "dotenv"; | ||
import { createPublicClient, getContract, http, toHex } from "viem"; | ||
import { getTicksSlots, getPositionsSlots, getStaticSlots, getTickBitmapSlots } from "../../src/viem/poolLens"; | ||
import { IUniswapV3Pool__factory } from "../../typechain"; | ||
|
||
dotenvConfig(); | ||
|
||
const chainId = ApertureSupportedChainId.ETHEREUM_MAINNET_CHAIN_ID; | ||
const USDC_ADDRESS = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; | ||
const WETH_ADDRESS = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; | ||
|
||
describe("Pool lens test", () => { | ||
const { chain, uniswap_v3_factory } = getChainInfo(chainId); | ||
const publicClient = createPublicClient({ | ||
chain, | ||
transport: http(`https://mainnet.infura.io/v3/${process.env.INFURA_API_KEY}`), | ||
batch: { | ||
multicall: true, | ||
}, | ||
}); | ||
const blockNumber = 17000000n; | ||
const pool = viem.computePoolAddress(uniswap_v3_factory, USDC_ADDRESS, WETH_ADDRESS, 500); | ||
const poolContract = getContract({ | ||
address: pool, | ||
abi: IUniswapV3Pool__factory.abi, | ||
publicClient, | ||
}); | ||
|
||
it("Test getting static storage slots", async () => { | ||
const slots = await getStaticSlots(pool, publicClient, blockNumber); | ||
expect(slots.some(({ data }) => data > 0)).to.be.true; | ||
const address = pool; | ||
const altSlots = await Promise.all([ | ||
publicClient.getStorageAt({ address, slot: toHex(0), blockNumber }), | ||
publicClient.getStorageAt({ address, slot: toHex(1), blockNumber }), | ||
publicClient.getStorageAt({ address, slot: toHex(2), blockNumber }), | ||
publicClient.getStorageAt({ address, slot: toHex(3), blockNumber }), | ||
]); | ||
for (let i = 0; i < 4; i++) { | ||
expect(slots[i].data).to.be.eq(BigInt(altSlots[i]!)); | ||
} | ||
}); | ||
|
||
it("Test getting populated ticks slots", async () => { | ||
const slots = await getTicksSlots(pool, TickMath.MIN_TICK, TickMath.MAX_TICK, publicClient, blockNumber); | ||
expect(slots.some(({ data }) => data > 0)).to.be.true; | ||
}); | ||
|
||
it("Test getting tick bitmap slots", async () => { | ||
const slots = await getTickBitmapSlots(pool, publicClient, blockNumber); | ||
expect(slots.some(({ data }) => data > 0)).to.be.true; | ||
}); | ||
|
||
it("Test getting positions mapping slots", async () => { | ||
const logs = await poolContract.getEvents.Mint( | ||
{}, | ||
{ | ||
fromBlock: blockNumber - 10000n, | ||
toBlock: blockNumber, | ||
}, | ||
); | ||
const positions = logs.map(({ args: { owner, tickLower, tickUpper } }) => ({ | ||
owner: owner!, | ||
tickLower: tickLower!, | ||
tickUpper: tickUpper!, | ||
})); | ||
const slots = await getPositionsSlots(pool, positions, publicClient, blockNumber); | ||
expect(slots.some(({ data }) => data > 0)).to.be.true; | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.