Skip to content

Commit e935d5a

Browse files
cli: consolidate error handling and confirmation prompt (#896)
Synced from glamsystems/glam bb75daf2897d0974eebd0999d205a12dd6b4932b
1 parent 00c247e commit e935d5a

19 files changed

+1558
-1563
lines changed

src/cmds/alt.ts

Lines changed: 39 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { Command } from "commander";
22
import {
33
CliContext,
4-
confirmOperation,
5-
parseTxError,
4+
executeTxWithErrorHandling,
65
validatePublicKey,
76
} from "../utils";
87
import { PublicKey, Transaction, VersionedTransaction } from "@solana/web3.js";
@@ -54,29 +53,28 @@ export function installAltCommands(alt: Command, context: CliContext) {
5453
const { tables, tx: b64Txs } = result;
5554

5655
const table = tables[0];
57-
options?.yes ||
58-
(await confirmOperation(
59-
`Confirm creating address lookup table ${table}?`,
60-
));
6156

6257
// It might need multiple txs to set up tables[0]
6358
// Build and send txs
64-
try {
65-
const txSigs = [];
66-
for (const b64Tx of b64Txs) {
67-
const vTx = await context.glamClient.intoVersionedTransaction(
68-
buildLegacyTxFromBase64(b64Tx),
69-
context.txOptions,
70-
);
71-
const txSig = await context.glamClient.sendAndConfirm(vTx);
72-
txSigs.push(txSig);
73-
}
74-
75-
console.log(`Address lookup table ${table} created:`, txSigs);
76-
} catch (e) {
77-
console.error(parseTxError(e));
78-
process.exit(1);
79-
}
59+
await executeTxWithErrorHandling(
60+
async () => {
61+
const txSigs = [];
62+
for (const b64Tx of b64Txs) {
63+
const vTx = await context.glamClient.intoVersionedTransaction(
64+
buildLegacyTxFromBase64(b64Tx),
65+
context.txOptions,
66+
);
67+
const txSig = await context.glamClient.sendAndConfirm(vTx);
68+
txSigs.push(txSig);
69+
}
70+
return txSigs.join(", ");
71+
},
72+
{
73+
skip: options?.yes,
74+
message: `Confirm creating address lookup table ${table}?`,
75+
},
76+
(txSigs) => `Address lookup table ${table} created: ${txSigs}`,
77+
);
8078
});
8179

8280
alt
@@ -106,28 +104,26 @@ export function installAltCommands(alt: Command, context: CliContext) {
106104
`Address lookup table ${table} from api.glam.systems does not match`,
107105
);
108106
}
109-
options?.yes ||
110-
(await confirmOperation(
111-
`Confirm extending address lookup table ${table}?`,
112-
));
113-
114107
// Build and send txs
115-
try {
116-
const txSigs = [];
117-
for (const b64Tx of b64Txs) {
118-
const vTx = await context.glamClient.intoVersionedTransaction(
119-
buildLegacyTxFromBase64(b64Tx),
120-
context.txOptions,
121-
);
122-
const txSig = await context.glamClient.sendAndConfirm(vTx);
123-
txSigs.push(txSig);
124-
}
125-
126-
console.log(`Address lookup table ${table} extended:`, txSigs);
127-
} catch (e) {
128-
console.error(parseTxError(e));
129-
process.exit(1);
130-
}
108+
await executeTxWithErrorHandling(
109+
async () => {
110+
const txSigs = [];
111+
for (const b64Tx of b64Txs) {
112+
const vTx = await context.glamClient.intoVersionedTransaction(
113+
buildLegacyTxFromBase64(b64Tx),
114+
context.txOptions,
115+
);
116+
const txSig = await context.glamClient.sendAndConfirm(vTx);
117+
txSigs.push(txSig);
118+
}
119+
return txSigs.join(", ");
120+
},
121+
{
122+
skip: options?.yes,
123+
message: `Confirm extending address lookup table ${table}?`,
124+
},
125+
(txSigs) => `Address lookup table ${table} extended: ${txSigs}`,
126+
);
131127
});
132128

133129
alt

src/cmds/cctp.ts

Lines changed: 95 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Command } from "commander";
2-
import { CliContext, parseTxError } from "../utils";
2+
import { CliContext, executeTxWithErrorHandling } from "../utils";
33
import { BN } from "@coral-xyz/anchor";
44
import { CctpPolicy, publicKeyToEvmAddress } from "@glamsystems/glam-sdk";
55
import { evmAddressToPublicKey } from "@glamsystems/glam-sdk";
@@ -32,8 +32,9 @@ export function installCctpCommands(program: Command, context: CliContext) {
3232
.argument("<domain>", "CCTP domain", parseInt)
3333
.argument("<destination_address>", "Destination address")
3434
.option("--base58", "Address is a base58 string")
35+
.option("-y, --yes", "Skip confirmation prompt", false)
3536
.description("Add a destination to the allowlist")
36-
.action(async (domain, destinationAddress, { base58 }) => {
37+
.action(async (domain, destinationAddress, { base58, yes }) => {
3738
const recipientPubkey = base58
3839
? new PublicKey(destinationAddress)
3940
: evmAddressToPublicKey(destinationAddress);
@@ -56,30 +57,31 @@ export function installCctpCommands(program: Command, context: CliContext) {
5657
}
5758

5859
cctpPolicy.allowedDestinations.push({ domain, address: recipientPubkey });
59-
try {
60-
const txSig = await context.glamClient.access.setProtocolPolicy(
61-
context.glamClient.extCctpProgram.programId,
62-
0b01,
63-
cctpPolicy.encode(),
64-
context.txOptions,
65-
);
66-
console.log(
67-
`Destination ${destinationAddress} (domain ${domain}) added to allowlist:`,
68-
txSig,
69-
);
70-
} catch (e) {
71-
console.error(parseTxError(e));
72-
process.exit(1);
73-
}
60+
await executeTxWithErrorHandling(
61+
() =>
62+
context.glamClient.access.setProtocolPolicy(
63+
context.glamClient.extCctpProgram.programId,
64+
0b01,
65+
cctpPolicy.encode(),
66+
context.txOptions,
67+
),
68+
{
69+
skip: yes,
70+
message: `Confirm adding destination ${destinationAddress} (domain ${domain}) to allowlist`,
71+
},
72+
(txSig) =>
73+
`Destination ${destinationAddress} (domain ${domain}) added to allowlist: ${txSig}`,
74+
);
7475
});
7576

7677
program
7778
.command("remove-destination")
7879
.argument("<domain>", "CCTP domain", parseInt)
7980
.argument("<destination_address>", "Destination address")
8081
.option("--base58", "Address is a base58 string")
82+
.option("-y, --yes", "Skip confirmation prompt", false)
8183
.description("Remove a destination from the allowlist")
82-
.action(async (domain, destinationAddress, { base58 }) => {
84+
.action(async (domain, destinationAddress, { base58, yes }) => {
8385
const recipientPubkey = base58
8486
? new PublicKey(destinationAddress)
8587
: evmAddressToPublicKey(destinationAddress);
@@ -96,21 +98,21 @@ export function installCctpCommands(program: Command, context: CliContext) {
9698
cctpPolicy.allowedDestinations = cctpPolicy.allowedDestinations.filter(
9799
(d) => !(d.domain === domain && d.address.equals(recipientPubkey)),
98100
);
99-
try {
100-
const txSig = await context.glamClient.access.setProtocolPolicy(
101-
context.glamClient.extCctpProgram.programId,
102-
0b01,
103-
cctpPolicy.encode(),
104-
context.txOptions,
105-
);
106-
console.log(
107-
`Destination ${destinationAddress} (domain ${domain}) removed from allowlist:`,
108-
txSig,
109-
);
110-
} catch (e) {
111-
console.error(parseTxError(e));
112-
process.exit(1);
113-
}
101+
await executeTxWithErrorHandling(
102+
() =>
103+
context.glamClient.access.setProtocolPolicy(
104+
context.glamClient.extCctpProgram.programId,
105+
0b01,
106+
cctpPolicy.encode(),
107+
context.txOptions,
108+
),
109+
{
110+
skip: yes,
111+
message: `Confirm removing destination ${destinationAddress} (domain ${domain}) from allowlist`,
112+
},
113+
(txSig) =>
114+
`Destination ${destinationAddress} (domain ${domain}) removed from allowlist: ${txSig}`,
115+
);
114116
});
115117

116118
// https://developers.circle.com/cctp/cctp-supported-blockchains#cctp-v2-supported-domains
@@ -121,52 +123,57 @@ export function installCctpCommands(program: Command, context: CliContext) {
121123
.argument("<destination_address>", "EVM address")
122124
.option("--base58", "Address is a base58 string")
123125
.option("--fast", "Fast transfer (lower finality threshold)", false)
126+
.option("-y, --yes", "Skip confirmation prompt", false)
124127
.description("Bridge USDC to an EVM chain")
125-
.action(async (amount, domain, destinationAddress, { base58, fast }) => {
126-
const recipientPubkey = base58
127-
? new PublicKey(destinationAddress)
128-
: evmAddressToPublicKey(destinationAddress);
128+
.action(
129+
async (amount, domain, destinationAddress, { base58, fast, yes }) => {
130+
const recipientPubkey = base58
131+
? new PublicKey(destinationAddress)
132+
: evmAddressToPublicKey(destinationAddress);
129133

130-
const cctpPolicy = await context.glamClient.fetchProtocolPolicy(
131-
context.glamClient.extCctpProgram.programId,
132-
0b01,
133-
CctpPolicy,
134-
);
135-
if (
136-
cctpPolicy &&
137-
!cctpPolicy.allowedDestinations.find(
138-
(d) => d.domain === domain && d.address.equals(recipientPubkey),
139-
)
140-
) {
141-
console.error(
142-
`Destination (${domain}, ${destinationAddress}) not in allowlist`,
134+
const cctpPolicy = await context.glamClient.fetchProtocolPolicy(
135+
context.glamClient.extCctpProgram.programId,
136+
0b01,
137+
CctpPolicy,
143138
);
144-
process.exit(1);
145-
}
139+
if (
140+
cctpPolicy &&
141+
!cctpPolicy.allowedDestinations.find(
142+
(d) => d.domain === domain && d.address.equals(recipientPubkey),
143+
)
144+
) {
145+
console.error(
146+
`Destination (${domain}, ${destinationAddress}) not in allowlist`,
147+
);
148+
process.exit(1);
149+
}
146150

147-
const amountBN = new BN(amount * 10 ** 6);
151+
const amountBN = new BN(amount * 10 ** 6);
148152

149-
// https://developers.circle.com/cctp/technical-guide#cctp-finality-thresholds
150-
const maxFee = amountBN.mul(new BN(1)).div(new BN(10 ** 4));
151-
const minFinalityThreshold = fast ? 1000 : 2000;
153+
// https://developers.circle.com/cctp/technical-guide#cctp-finality-thresholds
154+
const maxFee = amountBN.mul(new BN(1)).div(new BN(10 ** 4));
155+
const minFinalityThreshold = fast ? 1000 : 2000;
152156

153-
try {
154-
const txSig = await context.glamClient.cctp.bridgeUsdc(
155-
amountBN,
156-
domain,
157-
recipientPubkey,
157+
await executeTxWithErrorHandling(
158+
() =>
159+
context.glamClient.cctp.bridgeUsdc(
160+
amountBN,
161+
domain,
162+
recipientPubkey,
163+
{
164+
maxFee,
165+
minFinalityThreshold,
166+
},
167+
context.txOptions,
168+
),
158169
{
159-
maxFee,
160-
minFinalityThreshold,
170+
skip: yes,
171+
message: `Confirm bridging ${amount} USDC to ${destinationAddress} (domain ${domain})`,
161172
},
162-
context.txOptions,
173+
(txSig) => `USDC burned: ${txSig}`,
163174
);
164-
console.log(`Deposit for burn:`, txSig);
165-
} catch (e) {
166-
console.error(parseTxError(e));
167-
process.exit(1);
168-
}
169-
});
175+
},
176+
);
170177

171178
program
172179
.command("receive")
@@ -180,24 +187,24 @@ export function installCctpCommands(program: Command, context: CliContext) {
180187
"Receive USDC from an EVM chain. Either txHash or nonce is required.",
181188
)
182189
.action(async (sourceDomain, { txHash, nonce }) => {
183-
try {
184-
await context.glamClient.cctp.receiveUsdc(
185-
sourceDomain,
186-
{
187-
txHash,
188-
nonce,
189-
},
190-
{
191-
...context.txOptions,
192-
lookupTables: [
193-
new PublicKey("qj4EYgsGpnRdt9rvQW3wWZR8JVaKPg9rG9EB8DNgfz8"), // CCTP lookup table
194-
],
195-
},
196-
);
197-
} catch (e) {
198-
console.error(parseTxError(e));
199-
process.exit(1);
200-
}
190+
await executeTxWithErrorHandling(
191+
() =>
192+
context.glamClient.cctp.receiveUsdc(
193+
sourceDomain,
194+
{
195+
txHash,
196+
nonce,
197+
},
198+
{
199+
...context.txOptions,
200+
lookupTables: [
201+
new PublicKey("qj4EYgsGpnRdt9rvQW3wWZR8JVaKPg9rG9EB8DNgfz8"), // CCTP lookup table
202+
],
203+
},
204+
),
205+
{ skip: true },
206+
(txSig) => `Received USDC: ${txSig}`,
207+
);
201208
});
202209

203210
program

0 commit comments

Comments
 (0)