Skip to content
Draft
Show file tree
Hide file tree
Changes from 41 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 @@ -70,6 +70,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 @@ -123,8 +124,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/faucet-cli": "^4.1.1",
"@zetachain/networks": "14.0.0-rc1",
"@zetachain/protocol-contracts": "13.0.0",
Expand All @@ -146,7 +150,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
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);
159 changes: 159 additions & 0 deletions packages/commands/src/zetachain/pools/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/********************************************************************
* pools create — create a V3 pool and initialise it if required
*******************************************************************/
import * as UniswapV3Factory from "@uniswap/v3-core/artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json";

Check failure on line 4 in packages/commands/src/zetachain/pools/create.ts

View workflow job for this annotation

GitHub Actions / build

Run autofix to sort these imports!
import * as UniswapV3Pool from "@uniswap/v3-core/artifacts/contracts/UniswapV3Pool.sol/UniswapV3Pool.json";
import { IERC20Metadata__factory } from "../../../../../typechain-types";
import { Command } from "commander";
import { Contract, ethers, JsonRpcProvider, Wallet } from "ethers";

import {
DEFAULT_FACTORY,
DEFAULT_FEE,
DEFAULT_RPC,
} from "../../../../../src/constants/pools";
import {
createPoolOptionsSchema,
type CreatePoolOptions,
PoolCreationError,
} from "../../../../../types/pools";

/* ─── helpers ---------------------------------------------------- */
const SCALE = 1_000_000_000_000_000_000n; // 1e18

Check failure on line 22 in packages/commands/src/zetachain/pools/create.ts

View workflow job for this annotation

GitHub Actions / build

'SCALE' is assigned a value but never used
const TWO_192 = 1n << 192n;

Check failure on line 23 in packages/commands/src/zetachain/pools/create.ts

View workflow job for this annotation

GitHub Actions / build

'TWO_192' is assigned a value but never used
function sqrtBig(n: bigint): bigint {

Check warning on line 24 in packages/commands/src/zetachain/pools/create.ts

View workflow job for this annotation

GitHub Actions / build

Use const or class constructors instead of named functions

Check failure on line 24 in packages/commands/src/zetachain/pools/create.ts

View workflow job for this annotation

GitHub Actions / build

Expected a function expression
// 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) */
function buildSqrtPriceX96(

Check warning on line 36 in packages/commands/src/zetachain/pools/create.ts

View workflow job for this annotation

GitHub Actions / build

Use const or class constructors instead of named functions

Check failure on line 36 in packages/commands/src/zetachain/pools/create.ts

View workflow job for this annotation

GitHub Actions / build

Expected a function expression
usd0: number,
usd1: number,
dec0: number,
dec1: number,
cliToken0: boolean
): bigint {
// USD prices mapped to factory order
const pTok0 = BigInt(Math.round((cliToken0 ? usd0 : usd1) * 1e18));
const pTok1 = BigInt(Math.round((cliToken0 ? usd1 : usd0) * 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(

Check failure on line 73 in packages/commands/src/zetachain/pools/create.ts

View workflow job for this annotation

GitHub Actions / build

Unsafe assignment of an `any` value
o.tokens[0],
o.tokens[1],
o.fee ?? DEFAULT_FEE
);

if (poolAddr === ethers.ZeroAddress) {
console.log("Creating pool …");
const tx = await factory.createPool(

Check failure on line 81 in packages/commands/src/zetachain/pools/create.ts

View workflow job for this annotation

GitHub Actions / build

Unsafe assignment of an `any` value
o.tokens[0],
o.tokens[1],
o.fee ?? DEFAULT_FEE
);
await tx.wait();

Check failure on line 86 in packages/commands/src/zetachain/pools/create.ts

View workflow job for this annotation

GitHub Actions / build

Unsafe member access .wait on an `any` value

Check failure on line 86 in packages/commands/src/zetachain/pools/create.ts

View workflow job for this annotation

GitHub Actions / build

Unsafe call of an `any` typed value
poolAddr = await factory.getPool(

Check failure on line 87 in packages/commands/src/zetachain/pools/create.ts

View workflow job for this annotation

GitHub Actions / build

Unsafe assignment of an `any` value
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Type safety & transaction status check

factory.createPool and tx.wait() are currently typed as any, which hides errors (static analysis flags unsafe calls).

  1. Import and use the generated typings (UniswapV3Factory__factory) so that getPool returns Promise<string> and createPool returns ContractTransaction.
  2. After await tx.wait() inspect receipt.status and throw on failure to prevent falsely reporting success.
-      await tx.wait();
+      const receipt = await tx.wait();
+      if (receipt?.status !== 1) {
+        throw new Error(`createPool failed (tx: ${tx.hash})`);
+      }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let poolAddr = await factory.getPool(
o.tokens[0],
o.tokens[1],
o.fee ?? DEFAULT_FEE
);
if (poolAddr === ethers.ZeroAddress) {
console.log("Creating pool …");
const tx = await factory.createPool(
o.tokens[0],
o.tokens[1],
o.fee ?? DEFAULT_FEE
);
await tx.wait();
poolAddr = await factory.getPool(
let poolAddr = await factory.getPool(
o.tokens[0],
o.tokens[1],
o.fee ?? DEFAULT_FEE
);
if (poolAddr === ethers.ZeroAddress) {
console.log("Creating pool …");
const tx = await factory.createPool(
o.tokens[0],
o.tokens[1],
o.fee ?? DEFAULT_FEE
);
- await tx.wait();
+ const receipt = await tx.wait();
+ if (receipt?.status !== 1) {
+ throw new Error(`createPool failed (tx: ${tx.hash})`);
+ }
poolAddr = await factory.getPool(
o.tokens[0],
o.tokens[1],
o.fee ?? DEFAULT_FEE
);
}
🧰 Tools
🪛 GitHub Check: build

[failure] 87-87:
Unsafe assignment of an any value


[failure] 86-86:
Unsafe member access .wait on an any value


[failure] 86-86:
Unsafe call of an any typed value


[failure] 81-81:
Unsafe assignment of an any value


[failure] 73-73:
Unsafe assignment of an any value

🤖 Prompt for AI Agents
In packages/commands/src/zetachain/pools/create.ts around lines 73 to 87, the
calls to factory.createPool and tx.wait() are typed as any, which disables
static type checking and error detection. To fix this, import the generated
typings UniswapV3Factory__factory and use them to instantiate the factory so
getPool returns Promise<string> and createPool returns ContractTransaction.
After awaiting tx.wait(), check the receipt.status field and throw an error if
the transaction failed to ensure the code does not incorrectly assume success.

o.tokens[0],
o.tokens[1],
o.fee ?? DEFAULT_FEE
);
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(), pool.token1()]);
const [dec0, dec1] = await Promise.all([
IERC20Metadata__factory.connect(token0, provider).decimals(),
IERC20Metadata__factory.connect(token1, provider).decimals(),
]);

/* compute initial sqrtPriceX96 ----------------------------- */
const cliToken0 = token0.toLowerCase() === o.tokens[0].toLowerCase();
const sqrtPriceX96 = buildSqrtPriceX96(
usdA,
usdB,
Number(dec0),
Number(dec1),
cliToken0
);

/* check if initialised ------------------------------------- */
let needInit = false;
try {
const slot0 = await pool.slot0();
needInit = slot0.sqrtPriceX96 === 0n;
} catch {
needInit = true; // slot0() reverted → not initialised
}

if (needInit) {
console.log("Initialising pool …");
const tx = await pool.initialize(sqrtPriceX96);
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