-
Notifications
You must be signed in to change notification settings - Fork 34
feat: uniswap v3 deployment and liquidity commands #295
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
fadeev
wants to merge
50
commits into
main
Choose a base branch
from
pools-commands
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
50 commits
Select commit
Hold shift + click to select a range
9b9d630
feat: uniswap v3 deployment and liquidity commands
fadeev 3d4725e
create pool command
fadeev 5c52248
pools show
fadeev 2209951
pools show
fadeev ba4ae4b
refactor
fadeev dc308ae
add liquidity
fadeev 9fd0071
refactor
fadeev 442977d
lint
fadeev 50362ab
refactor
fadeev fe543e4
lint
fadeev 691b2fb
refactor
fadeev f6a7c28
refactor
fadeev 2f6a239
check pool exists
fadeev f49f222
check pool exists
fadeev 28cad95
balances
fadeev 3ea1b78
merge main
fadeev c6bfb85
zod
fadeev d16debf
remove duplicate cmd
fadeev b15c2db
custom price
fadeev 27a0062
refactor
fadeev 0234c46
fix show
fadeev 6f1553b
price
fadeev 2985c00
updated factory
fadeev e09172a
update constants
fadeev 4904414
merge main
fadeev 1aa83e7
wip
fadeev 6907609
Merge branch 'main' into pools-commands
hernan-clich 4bc23f3
merge main
fadeev 653ad1f
lint
fadeev cfea79d
node 22
fadeev cfb9973
fix build
fadeev 8b6e857
fix build
fadeev 4dc7f2c
move pools command inside zetachain namespace
fadeev 1fd27fd
show quotes
fadeev 583a152
wip
fadeev dff4ad9
wip
fadeev d26804c
lint
fadeev 1795013
wip
fadeev 228b66d
Merge branch 'main' into pools-commands
fadeev 01acd19
fix pools show
fadeev a3d96c5
update defaults
fadeev 439a441
update defaults
fadeev de14ad5
fix
fadeev cf8946f
seems to be working
fadeev b8e2bd3
Add fixes for undefined decimals, precision loss and rounding errors
hernan-clich 16bca71
Merge branch 'main' into pools-commands
hernan-clich 01757b2
Merge branch 'main' into pools-commands
hernan-clich e01d06f
Type fixes
hernan-clich 4cfa442
Lint fixes
hernan-clich 66e1330
Swapped places in array destructuring
hernan-clich File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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,172 @@ | ||
/******************************************************************** | ||
* pools create — create a V3 pool and initialise it if required | ||
*******************************************************************/ | ||
import * as UniswapV3Factory from "@uniswap/v3-core/artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json"; | ||
import * as UniswapV3Pool from "@uniswap/v3-core/artifacts/contracts/UniswapV3Pool.sol/UniswapV3Pool.json"; | ||
import { Command } from "commander"; | ||
import { | ||
Contract, | ||
ContractTransactionResponse, | ||
ethers, | ||
JsonRpcProvider, | ||
Wallet, | ||
} from "ethers"; | ||
|
||
import { | ||
DEFAULT_FACTORY, | ||
DEFAULT_FEE, | ||
DEFAULT_RPC, | ||
} from "../../../../../src/constants/pools"; | ||
import { IERC20Metadata__factory } from "../../../../../typechain-types"; | ||
import { | ||
type CreatePoolOptions, | ||
createPoolOptionsSchema, | ||
PoolCreationError, | ||
} from "../../../../../types/pools"; | ||
|
||
/* ─── helpers ---------------------------------------------------- */ | ||
const sqrtBig = (n: bigint): bigint => { | ||
// integer √ | ||
if (n < 2n) return n; | ||
let x = n, | ||
y = (x + 1n) >> 1n; | ||
while (y < x) { | ||
x = y; | ||
y = (x + n / x) >> 1n; | ||
} | ||
return x; | ||
}; | ||
|
||
/** sqrtPriceX96 = √(price₁ / price₀) × 2⁹⁶ (token1/token0) */ | ||
const buildSqrtPriceX96 = ( | ||
usd0: number, | ||
usd1: number, | ||
dec0: number, | ||
dec1: number | ||
): bigint => { | ||
// USD prices mapped to factory order | ||
const pTok0 = BigInt(Math.round(usd0 * 1e18)); | ||
const pTok1 = BigInt(Math.round(usd1 * 1e18)); | ||
|
||
// token1/token0 ratio in base-units, scaled by 2¹⁹² | ||
const num = pTok1 * 10n ** BigInt(dec0); // p₁ × 10^dec₀ | ||
const den = pTok0 * 10n ** BigInt(dec1); // p₀ × 10^dec₁ | ||
const ratioX192 = (num << 192n) / den; // shift before divide | ||
if (ratioX192 === 0n) throw new Error("ratio underflow – raise precision"); | ||
|
||
/* integer √ → Q64.96 */ | ||
return sqrtBig(ratioX192); | ||
}; | ||
|
||
/* ─── main ------------------------------------------------------- */ | ||
const main = async (raw: CreatePoolOptions) => { | ||
try { | ||
const o = createPoolOptionsSchema.parse(raw); | ||
const [usdA, usdB] = o.prices.map(Number); | ||
|
||
const provider = new JsonRpcProvider(o.rpc ?? DEFAULT_RPC); | ||
const signer = new Wallet(o.privateKey, provider); | ||
|
||
/* factory --------------------------------------------------- */ | ||
const factory = new Contract( | ||
o.factory ?? DEFAULT_FACTORY, | ||
UniswapV3Factory.abi, | ||
signer | ||
); | ||
|
||
let poolAddr = (await factory.getPool( | ||
o.tokens[0], | ||
o.tokens[1], | ||
o.fee ?? DEFAULT_FEE | ||
)) as string; | ||
|
||
if (poolAddr === ethers.ZeroAddress) { | ||
console.log("Creating pool …"); | ||
const tx = (await factory.createPool( | ||
o.tokens[0], | ||
o.tokens[1], | ||
o.fee ?? DEFAULT_FEE | ||
)) as ContractTransactionResponse; | ||
await tx.wait(); | ||
poolAddr = (await factory.getPool( | ||
o.tokens[0], | ||
o.tokens[1], | ||
o.fee ?? DEFAULT_FEE | ||
)) as string; | ||
console.log("✦ createPool tx:", tx.hash); | ||
} else { | ||
console.log("Pool already exists:", poolAddr); | ||
} | ||
|
||
/* pool contract -------------------------------------------- */ | ||
const pool = new Contract(poolAddr, UniswapV3Pool.abi, signer); | ||
const [token0, token1] = await Promise.all([ | ||
pool.token0() as Promise<string>, | ||
pool.token1() as Promise<string>, | ||
]); | ||
const [dec0, dec1] = await Promise.all([ | ||
IERC20Metadata__factory.connect(token0, provider).decimals(), | ||
IERC20Metadata__factory.connect(token1, provider).decimals(), | ||
]); | ||
|
||
/* compute initial sqrtPriceX96 ----------------------------- */ | ||
const isUserToken0EqualPoolToken0 = | ||
token0.toLowerCase() === o.tokens[0].toLowerCase(); | ||
const [priceToken0, priceToken1] = isUserToken0EqualPoolToken0 | ||
? [usdA, usdB] // matches pool order already | ||
: [usdB, usdA]; // swap | ||
|
||
const sqrtPriceX96 = buildSqrtPriceX96( | ||
priceToken0, | ||
priceToken1, | ||
Number(dec0), | ||
Number(dec1) | ||
); | ||
|
||
/* check if initialised ------------------------------------- */ | ||
let needInit = false; | ||
try { | ||
const slot0 = (await pool.slot0()) as { sqrtPriceX96: bigint }; | ||
needInit = slot0.sqrtPriceX96 === 0n; | ||
} catch { | ||
needInit = true; // slot0() reverted → not initialised | ||
} | ||
|
||
if (needInit) { | ||
console.log("Initialising pool …"); | ||
const tx = (await pool.initialize( | ||
sqrtPriceX96 | ||
)) as ContractTransactionResponse; | ||
await tx.wait(); | ||
console.log("✓ Pool initialised (tx:", tx.hash, ")"); | ||
} else { | ||
console.log("Pool already initialised; skipped."); | ||
} | ||
|
||
console.log("✔ Done – pool address:", poolAddr); | ||
} catch (err) { | ||
const e = err as PoolCreationError; | ||
console.error("Pool creation failed:", e.message); | ||
if (e.transaction) console.error("tx:", e.transaction); | ||
if (e.receipt) console.error("receipt:", e.receipt); | ||
process.exit(1); | ||
} | ||
}; | ||
|
||
/* ─── CLI ------------------------------------------------------- */ | ||
export const createCommand = new Command("create") | ||
.summary("Create a Uniswap V3 pool and initialise it at a USD price ratio") | ||
.requiredOption("--private-key <pk>", "Private key paying gas") | ||
.requiredOption("--tokens <addrs...>", "Two token addresses (CLI order)") | ||
.requiredOption( | ||
"--prices <usd...>", | ||
"USD prices for the tokens in same order" | ||
) | ||
.option( | ||
"--fee <fee>", | ||
"Fee tier (default 3000 = 0.3%)", | ||
DEFAULT_FEE.toString() | ||
) | ||
.option("--factory <addr>", "Uniswap V3 Factory", DEFAULT_FACTORY) | ||
.option("--rpc <url>", "JSON-RPC endpoint", DEFAULT_RPC) | ||
.action(main); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.