Skip to content

Commit d3b8cd2

Browse files
committed
(fix) (openai/gpt-5.5, reviewed T, tested T) select mainnet clients for b20 wraps
1 parent fc99e13 commit d3b8cd2

3 files changed

Lines changed: 50 additions & 12 deletions

File tree

packages/cli/src/b20/b20-ops.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@ import { isAddress, parseAbi, parseEther } from "viem";
33

44
import FCFWrapperAbi from "../abi/FCFWrapper.json" with { type: "json" };
55

6-
const fcfWrapperAbi = FCFWrapperAbi as Abi;
6+
const erc20ErrorAbi = parseAbi([
7+
"error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed)",
8+
"error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed)",
9+
]);
10+
const fcfWrapperAbi = [...FCFWrapperAbi, ...erc20ErrorAbi] as Abi;
711
const erc20Abi = parseAbi([
812
"function allowance(address owner, address spender) view returns (uint256)",
913
"function approve(address spender, uint256 amount) returns (bool)",
14+
"error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed)",
1015
]);
1116

1217
export type TransactionReceipt = {
@@ -152,5 +157,17 @@ async function approveIfNeeded(params: Omit<B20OperationParams, "amount"> & {
152157
account: params.account,
153158
});
154159
const receipt = await params.publicClient.waitForTransactionReceipt({ hash });
155-
return { approved: true, hash, receipt };
160+
if (receipt.status !== "success") throw new Error(`approval reverted: ${hash}`);
161+
for (let attempt = 0; attempt < 10; attempt++) {
162+
const updatedAllowance = await params.publicClient.readContract({
163+
address: params.token,
164+
abi: erc20Abi,
165+
functionName: "allowance",
166+
args: [params.account.address, params.spender],
167+
});
168+
if (typeof updatedAllowance !== "bigint") throw new Error("invalid ERC-20 allowance result");
169+
if (updatedAllowance >= params.amount) return { approved: true, hash, receipt };
170+
await new Promise((resolve) => setTimeout(resolve, 500));
171+
}
172+
throw new Error(`approval not visible after receipt: ${hash}`);
156173
}

packages/cli/src/index.ts

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import { dirname } from "node:path";
44
import { exit } from "node:process";
55

66
import { Command } from "commander";
7-
import type { Abi } from "viem";
7+
import type { Abi, Chain } from "viem";
88
import { createPublicClient, createWalletClient, http, isAddress, parseAbi, parseAbiItem, parseEther, toHex } from "viem";
99
import { privateKeyToAccount } from "viem/accounts";
10-
import { foundry, baseSepolia } from "viem/chains";
10+
import { base, foundry, baseSepolia } from "viem/chains";
1111
import { DopplerSDK } from "@whetstone-research/doppler-sdk/evm";
1212

1313
import { importAbi } from "@/utils/importAbi.js";
@@ -44,6 +44,11 @@ const ENABLE_MARKET_COMMAND = process.env.ENABLE_MARKET_COMMAND ? tr
4444
const RIK_ROYALTY_SPLITTER_ADDRESS = process.env.RIK_ROYALTY_SPLITTER_ADDRESS;
4545
const RIK_LAUNCHER_ADDRESS = process.env.RIK_LAUNCHER_ADDRESS;
4646

47+
type ClientsOptions = {
48+
readonly chain?: Chain;
49+
readonly rpcUrl?: string;
50+
};
51+
4752
const abi = loadAbi();
4853
const RIKLauncherAbi = parseAbi([
4954
"function launch(uint256 repoId, (uint256 initialSupply, uint256 numTokensToSell, address numeraire, address tokenFactory, bytes tokenFactoryData, address governanceFactory, bytes governanceFactoryData, address poolInitializer, bytes poolInitializerData, address liquidityMigrator, bytes liquidityMigratorData, address integrator, bytes32 salt) p) returns (address asset)",
@@ -83,7 +88,10 @@ function addB20Command(program: Command): void {
8388
.argument("<amount>", "fcf amount to wrap")
8489
.option("--wrapper <addr>", "deployed FCFWrapper address")
8590
.action(async (amount: string, opts: { wrapper?: string }) => {
86-
const { account, publicClient, walletClient } = clients();
91+
const { account, publicClient, walletClient } = clients({
92+
chain: base,
93+
rpcUrl: process.env.B20_RPC_URL ?? process.env.BASE_MAINNET_RPC_URL ?? "https://mainnet.base.org",
94+
});
8795
const wrapperAddress = resolveFcfWrapperAddress(opts.wrapper);
8896
try {
8997
const result = await wrapB20({ amount, wrapperAddress, account, publicClient, walletClient });
@@ -98,7 +106,10 @@ function addB20Command(program: Command): void {
98106
.argument("<amount>", "wrapped B20 amount to unwrap")
99107
.option("--wrapper <addr>", "deployed FCFWrapper address")
100108
.action(async (amount: string, opts: { wrapper?: string }) => {
101-
const { account, publicClient, walletClient } = clients();
109+
const { account, publicClient, walletClient } = clients({
110+
chain: base,
111+
rpcUrl: process.env.B20_RPC_URL ?? process.env.BASE_MAINNET_RPC_URL ?? "https://mainnet.base.org",
112+
});
102113
const wrapperAddress = resolveFcfWrapperAddress(opts.wrapper);
103114
try {
104115
const result = await unwrapB20({ amount, wrapperAddress, account, publicClient, walletClient });
@@ -120,9 +131,9 @@ function addMarketCommand(program: Command): void {
120131
if(!RIK_ROYALTY_SPLITTER_ADDRESS) die("Missing RIK_ROYALTY_SPLITTER_ADDRESS");
121132
if(!RIK_LAUNCHER_ADDRESS) die("Missing RIK_ROYALTY_SPLITTER_ADDRESS");
122133

123-
const { account, publicClient, walletClient } = clients();
134+
const { account, publicClient, walletClient, chain } = clients();
124135
let doppler: DopplerSDK | null = null;
125-
try { doppler = new DopplerSDK({ publicClient, walletClient, chainId: baseSepolia.id }); } catch (err) { die(err); }
136+
try { doppler = new DopplerSDK({ publicClient, walletClient, chainId: chain.id }); } catch (err) { die(err); }
126137
if (!doppler) die("Failed to initialize doppler sdk");
127138

128139
// NOTE: share token config is hardcoded right now
@@ -409,19 +420,24 @@ function addGithubVarsCommand(program: Command): void {
409420
});
410421
}
411422

412-
function clients() {
423+
function clients(options: ClientsOptions = {}) {
413424
const privateKey = (() => {
414425
let key;
415426
if (process.env.PRIVATE_KEY) key = process.env.PRIVATE_KEY as `0x${string}`;
416427
else try { const wallet = getLocalWallet(); key = wallet.privateKey; } catch (err) { die(err) };
417428
return key;
418429
})();
419-
const rpcUrl = process.env.RPC_URL ?? "https://sepolia.base.org";
420-
const chain = rpcUrl.includes("sepolia") ? baseSepolia : foundry;
430+
const rpcUrl = options.rpcUrl ?? process.env.RPC_URL ?? "https://sepolia.base.org";
431+
const chain = options.chain ?? (
432+
rpcUrl.includes("mainnet.base.org") ? base :
433+
rpcUrl.includes("sepolia") ? baseSepolia :
434+
foundry
435+
);
421436
const account = privateKeyToAccount(privateKey);
422437

423438
return {
424439
account,
440+
chain,
425441
publicClient: createPublicClient({ chain, transport: http(rpcUrl) }),
426442
walletClient: createWalletClient({ account, chain, transport: http(rpcUrl) }),
427443
};

packages/cli/test/b20-ops.test.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ function makeClients(allowance: bigint): {
2828
} {
2929
const reads: ReadCall[] = [];
3030
const writes: WriteCall[] = [];
31+
let currentAllowance = allowance;
3132

3233
return {
3334
reads,
@@ -37,7 +38,7 @@ function makeClients(allowance: bigint): {
3738
reads.push(args);
3839
if (args.functionName === "fcfToken") return fcfToken;
3940
if (args.functionName === "wrappedB20") return wrappedB20;
40-
if (args.functionName === "allowance") return allowance;
41+
if (args.functionName === "allowance") return currentAllowance;
4142
throw new Error(`unexpected read: ${args.functionName}`);
4243
},
4344
async waitForTransactionReceipt() {
@@ -47,6 +48,10 @@ function makeClients(allowance: bigint): {
4748
walletClient: {
4849
async writeContract(args) {
4950
writes.push(args);
51+
if (args.functionName === "approve") {
52+
const amount = args.args[1];
53+
if (typeof amount === "bigint") currentAllowance = amount;
54+
}
5055
return `0x${String(writes.length).padStart(64, "0")}` as Hash;
5156
},
5257
},

0 commit comments

Comments
 (0)