Skip to content
Open
Show file tree
Hide file tree
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 Apr 16, 2025
3d4725e
create pool command
fadeev Apr 17, 2025
5c52248
pools show
fadeev Apr 17, 2025
2209951
pools show
fadeev Apr 17, 2025
ba4ae4b
refactor
fadeev Apr 17, 2025
dc308ae
add liquidity
fadeev Apr 18, 2025
9fd0071
refactor
fadeev Apr 18, 2025
442977d
lint
fadeev Apr 18, 2025
50362ab
refactor
fadeev Apr 18, 2025
fe543e4
lint
fadeev Apr 18, 2025
691b2fb
refactor
fadeev Apr 18, 2025
f6a7c28
refactor
fadeev Apr 18, 2025
2f6a239
check pool exists
fadeev Apr 18, 2025
f49f222
check pool exists
fadeev Apr 18, 2025
28cad95
balances
fadeev Apr 18, 2025
3ea1b78
merge main
fadeev Apr 18, 2025
c6bfb85
zod
fadeev Apr 18, 2025
d16debf
remove duplicate cmd
fadeev Apr 18, 2025
b15c2db
custom price
fadeev Apr 18, 2025
27a0062
refactor
fadeev Apr 18, 2025
0234c46
fix show
fadeev Apr 18, 2025
6f1553b
price
fadeev May 5, 2025
2985c00
updated factory
fadeev May 5, 2025
e09172a
update constants
fadeev May 5, 2025
4904414
merge main
fadeev May 5, 2025
1aa83e7
wip
fadeev May 5, 2025
6907609
Merge branch 'main' into pools-commands
hernan-clich May 28, 2025
4bc23f3
merge main
fadeev Jul 7, 2025
653ad1f
lint
fadeev Jul 7, 2025
cfea79d
node 22
fadeev Jul 7, 2025
cfb9973
fix build
fadeev Jul 7, 2025
8b6e857
fix build
fadeev Jul 7, 2025
4dc7f2c
move pools command inside zetachain namespace
fadeev Jul 7, 2025
1fd27fd
show quotes
fadeev Jul 7, 2025
583a152
wip
fadeev Jul 7, 2025
dff4ad9
wip
fadeev Jul 7, 2025
d26804c
lint
fadeev Jul 7, 2025
1795013
wip
fadeev Jul 8, 2025
228b66d
Merge branch 'main' into pools-commands
fadeev Jul 25, 2025
01acd19
fix pools show
fadeev Jul 25, 2025
a3d96c5
update defaults
fadeev Jul 25, 2025
439a441
update defaults
fadeev Jul 25, 2025
de14ad5
fix
fadeev Jul 25, 2025
cf8946f
seems to be working
fadeev Jul 25, 2025
b8e2bd3
Add fixes for undefined decimals, precision loss and rounding errors
hernan-clich Jul 31, 2025
16bca71
Merge branch 'main' into pools-commands
hernan-clich Jul 31, 2025
01757b2
Merge branch 'main' into pools-commands
hernan-clich Jul 31, 2025
e01d06f
Type fixes
hernan-clich Jul 31, 2025
4cfa442
Lint fixes
hernan-clich Jul 31, 2025
66e1330
Swapped places in array destructuring
hernan-clich Aug 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: "21"
node-version: "22"
registry-url: "https://registry.npmjs.org"

- name: Install Foundry
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: "21"
node-version: "22"
registry-url: "https://registry.npmjs.org"

- name: Install Foundry
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-npm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: "21"
node-version: "22"
registry-url: "https://registry.npmjs.org"

- name: Install Foundry
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: "21"
node-version: "22"
registry-url: "https://registry.npmjs.org"

- name: Install Foundry
Expand Down
7 changes: 7 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"@types/chai": "^4.2.0",
"@types/jest": "^29.5.14",
"@types/lodash": "^4.14.202",
"@types/minimatch": "^6.0.0",
"@types/mocha": ">=9.1.0",
"@types/node": ">=12.0.0",
"@types/spinnies": "^0.5.3",
Expand Down Expand Up @@ -128,8 +129,11 @@
"@ton/core": "^0.60.1",
"@ton/crypto": "^3.3.0",
"@ton/ton": "^15.2.1",
"@types/inquirer": "^9.0.7",
"@uniswap/v2-periphery": "^1.1.0-beta.0",
"@uniswap/v3-core": "^1.0.1",
"@uniswap/v3-periphery": "^1.4.4",
"@uniswap/v3-sdk": "^3.25.2",
"@zetachain/networks": "14.0.0-rc1",
"@zetachain/protocol-contracts": "13.0.0",
"@zetachain/protocol-contracts-solana": "^5.0.0",
Expand All @@ -150,7 +154,10 @@
"form-data": "^4.0.0",
"handlebars": "4.7.7",
"hardhat": "^2.22.8",
"inquirer": "^12.5.2",
"jsbi": "^4.3.2",
"lodash": "^4.17.21",
"minimatch": "^10.0.3",
"ora": "5.4.1",
"spinnies": "^0.5.1",
"table": "^6.9.0",
Expand Down
5 changes: 3 additions & 2 deletions packages/client/src/getPools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ export const getPools = async function (

// Step 6: Format pools with token details
const foreignCoins = await this.getForeignCoins();
return formatPoolsWithTokenDetails(
return await formatPoolsWithTokenDetails(
pools,
foreignCoins,
addresses.zetaTokenAddress
addresses.zetaTokenAddress,
provider
);
};
2 changes: 2 additions & 0 deletions packages/commands/src/zetachain/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Command } from "commander";

import { callCommand } from "./call";
import { poolsCommand } from "./pools";
import { withdrawCommand } from "./withdraw";
import { withdrawAndCallCommand } from "./withdrawAndCall";

Expand All @@ -10,4 +11,5 @@ export const zetachainCommand = new Command("zetachain")
.addCommand(callCommand)
.addCommand(withdrawCommand)
.addCommand(withdrawAndCallCommand)
.addCommand(poolsCommand)
.helpCommand(false);
172 changes: 172 additions & 0 deletions packages/commands/src/zetachain/pools/create.ts
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);
Loading