Skip to content

Commit

Permalink
add custom program errors (#130)
Browse files Browse the repository at this point in the history
  • Loading branch information
swaggymarie authored and kilgaloon committed Aug 6, 2024
1 parent 6d48e7b commit 1617b9f
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 64 deletions.
12 changes: 9 additions & 3 deletions lib/client/rpc/market-clients/ammMarkets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,9 @@ export class FutarchyAmmMarketsRPCClient implements FutarchyAmmMarketsClient {
this.rpcProvider.publicKey
);
const tx = await ix.transaction();
return this.transactionSender.send([tx], this.rpcProvider.connection);
return this.transactionSender.send([tx], this.rpcProvider.connection, {
customErrors: [this.ammClient.program.idl.errors]
});
}

simulateAddLiquidity(
Expand Down Expand Up @@ -300,7 +302,9 @@ export class FutarchyAmmMarketsRPCClient implements FutarchyAmmMarketsClient {
new BN(minQuoteWithSlippage)
);
const tx = await ix.transaction();
return this.transactionSender?.send([tx], this.rpcProvider.connection);
return this.transactionSender?.send([tx], this.rpcProvider.connection, {
customErrors: [this.ammClient.program.idl.errors]
});
}

simulateRemoveLiquidity(lpTokensToBurn: number, ammMarket: AmmMarket) {
Expand Down Expand Up @@ -371,7 +375,9 @@ export class FutarchyAmmMarketsRPCClient implements FutarchyAmmMarketsClient {
)
.transaction();

return this.transactionSender?.send([tx], this.rpcProvider.connection);
return this.transactionSender?.send([tx], this.rpcProvider.connection, {
customErrors: [this.ammClient.program.idl.errors]
});
}

async getSwapPreview(
Expand Down
19 changes: 10 additions & 9 deletions lib/client/rpc/market-clients/openbookMarkets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,15 @@ export class FutarchyOpenbookMarketsRPCClient
const baseTokenWithSymbol = !baseToken.isFallback
? baseToken
: {
...baseToken,
symbol: marketName.split("/")[0]
};
...baseToken,
symbol: marketName.split("/")[0]
};
const quoteTokenWithSymbol = !quoteToken.isFallback
? quoteToken
: {
...quoteToken,
symbol: marketName.split("/")[0]
};
...quoteToken,
symbol: marketName.split("/")[0]
};

return {
baseMint: obMarket.account.baseMint,
Expand Down Expand Up @@ -295,7 +295,7 @@ export class FutarchyOpenbookMarketsRPCClient
.preInstructions(openTx.instructions)
.transaction();

return this.transactionSender.send([placeTx], this.rpcProvider.connection);
return this.transactionSender.send([placeTx], this.rpcProvider.connection, { customErrors: [market.twapProgram.idl.errors] });
}

async getOrCreateOpenOrdersIndexer({
Expand Down Expand Up @@ -403,7 +403,7 @@ export class FutarchyOpenbookMarketsRPCClient
]
};
const tx = await this.cancelAndSettleFundsTransactions(order, market);
return this.transactionSender.send([tx], this.rpcProvider.connection);
return this.transactionSender.send([tx], this.rpcProvider.connection, { customErrors: [this.openbookClient.program.idl.errors] });
}

private async cancelAndSettleFundsTransactions(
Expand Down Expand Up @@ -608,7 +608,8 @@ export class FutarchyOpenbookMarketsRPCClient
placeTx.add(placeIx);
return this.transactionSender.send(
[placeTx],
this.rpcProvider.connection
this.rpcProvider.connection,
{ customErrors: [market.twapProgram.idl.errors] }
);
}

Expand Down
10 changes: 8 additions & 2 deletions lib/client/rpc/proposals/finalizeProposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,10 @@ export class FinalizeProposalClient implements FinalizeProposal {

return await this.transactionSender?.send(
[tx],
this.rpcProvider.connection
this.rpcProvider.connection,
{
customErrors: [autocrat.idl.errors]
}
);
}

Expand All @@ -138,7 +141,10 @@ export class FinalizeProposalClient implements FinalizeProposal {

return await this.transactionSender?.send(
[finalizeProposalTx],
this.rpcProvider.connection
this.rpcProvider.connection,
{
customErrors: [this.autocratClient.autocrat.idl.errors]
}
);
} catch (e) {
console.log("error", e);
Expand Down
21 changes: 15 additions & 6 deletions lib/client/rpc/proposals/proposals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,9 @@ export class FutarchyRPCProposalsClient implements FutarchyProposalsClient {
mintConditionalsIx
];
const tx = new Transaction().add(...ixs);
return this.transactionSender.send([tx], this.rpcProvider.connection);
return this.transactionSender.send([tx], this.rpcProvider.connection, {
customErrors: [vaultAccount.protocol.vault.idl.errors]
});
}

public async createProposal(
Expand Down Expand Up @@ -270,7 +272,7 @@ export class FutarchyRPCProposalsClient implements FutarchyProposalsClient {
if (programVersion == "V0.3" || programVersion == "V0.2") {
const vaultForVersion =
autocratVersionToConditionalVaultMap[
proposal.protocol.deploymentVersion
proposal.protocol.deploymentVersion
];
const vaultProgram = new Program(
vaultForVersion.idl,
Expand Down Expand Up @@ -302,7 +304,10 @@ export class FutarchyRPCProposalsClient implements FutarchyProposalsClient {

const resp = await this.transactionSender?.send(
[mergeTx],
this.rpcProvider.connection
this.rpcProvider.connection,
{
customErrors: [vaultProgram.idl.errors]
}
);
return resp;
} else throw Error("Version not compatible");
Expand Down Expand Up @@ -351,12 +356,16 @@ export class FutarchyRPCProposalsClient implements FutarchyProposalsClient {
const tx = new Transaction().add(...redeeemBaseIx).add(...redeeemQuoteIx);
const resp = await this.transactionSender?.send(
[tx],
this.rpcProvider.connection
this.rpcProvider.connection,
{
customErrors: [vaultProgram.idl.errors]
}

);
return resp;
}

// TO DO INDEXER
public async saveProposalDetails(proposalDetails: ProposalDetails) {}
public async updateProposalAccounts(accounts: ProposalAccounts) {}
public async saveProposalDetails(proposalDetails: ProposalDetails) { }
public async updateProposalAccounts(accounts: ProposalAccounts) { }
}
100 changes: 56 additions & 44 deletions lib/transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ export class TransactionSender {
async send<T extends Transaction | VersionedTransaction>(
txs: SingleOrArray<T>[],
connection: Connection,
// alreadySignedTxs?: T[],
opts?: { sequential?: boolean, commitment?: Commitment, CUs?: SingleOrArray<number> }
opts?: { sequential?: boolean, commitment?: Commitment, CUs?: SingleOrArray<number>, customErrors?: { code: number, name: string, msg: string }[][] }
): SendTransactionResponse {
if (!connection || !this.owner || !this.signAllTransactions) {
throw new Error("Bad wallet connection");
Expand All @@ -68,36 +67,35 @@ export class TransactionSender {
}
}

const sequence =
txs[0] instanceof Array ? (txs as T[][]) : ([txs] as T[][]);

const latestBlockhash = await connection.getLatestBlockhash();
const timedTxs = sequence.map((set) =>
set.map((e: T, i: number) => {
const tx = e;
if (!(tx instanceof VersionedTransaction)) {
tx.recentBlockhash = latestBlockhash.blockhash;
tx.feePayer = this.owner!;
// Compute limit ix & priority fee ix
const units = Array.isArray(opts?.CUs) ? opts.CUs[i] as number : opts?.CUs;
tx.instructions = [
//MAX 1M
ComputeBudgetProgram.setComputeUnitLimit({ units: units ?? 200_000 }),
ComputeBudgetProgram.setComputeUnitPrice({
microLamports: this.priorityFee,
}),
...tx.instructions,
];
}
return tx;
})
);

const errors: TransactionError[] = []
const signatures: string[] = [];
try {
const signedTxs = await this.signAllTransactions(timedTxs.flat()).catch(e => { errors.push({ name: e.name, message: e.message }); return })
if (!signedTxs) return { signatures: signatures, errors: errors }
const sequence =
txs[0] instanceof Array ? (txs as T[][]) : ([txs] as T[][]);

const latestBlockhash = await connection.getLatestBlockhash();
const timedTxs = sequence.map((set) =>
set.map((e: T, i: number) => {
const tx = e;
if (!(tx instanceof VersionedTransaction)) {
tx.recentBlockhash = latestBlockhash.blockhash;
tx.feePayer = this.owner!;
// Compute limit ix & priority fee ix
const units = Array.isArray(opts?.CUs) ? opts.CUs[i] as number : opts?.CUs;
tx.instructions = [
//MAX 1M
ComputeBudgetProgram.setComputeUnitLimit({ units: units ?? 200_000 }),
ComputeBudgetProgram.setComputeUnitPrice({
microLamports: this.priorityFee,
}),
...tx.instructions,
];
}
return tx;
})
);

const signedTxs = await this.signAllTransactions(timedTxs.flat())

// Reconstruct signed sequence
let i = 0;
Expand All @@ -107,34 +105,48 @@ export class TransactionSender {

if (!opts?.sequential) {
await Promise.all(signedSequence.map(async (set) =>
await Promise.all(
set.map(async (tx) => {
return connection
.sendRawTransaction(tx.serialize(), { skipPreflight: true })
.then((txSignature) =>
connection
.confirmTransaction(txSignature, opts?.commitment ?? "confirmed")
.then(() => signatures.push(txSignature))
)
await Promise.all(
set.map(async (tx, index) => {
const txSignature = await connection.sendRawTransaction(tx.serialize(), { skipPreflight: true })
const confirmation = await connection.confirmTransaction(txSignature, opts?.commitment ?? "confirmed")
if (confirmation.value.err) {
//@ts-ignore
confirmation.value.err.InstructionError.forEach((error) => {
if (error.Custom) {
const _error: { code: number, msg: string, name: string } | undefined = opts?.customErrors?.[index]?.find(e => e.code == error.Custom)
errors.push({ message: _error?.msg || "Custom program Error, check on explorer.", name: _error?.name || "Anchor Error" })
}
})
}
signatures.push(txSignature)
}
)
)
));
}
else {
for (const set of signedSequence) {
for (const tx of set) {
set.forEach(async (tx, index) => {
const txSignature = await connection.sendRawTransaction(tx.serialize(), { skipPreflight: true })
if (txSignature) {
const confirmation = await connection.confirmTransaction(txSignature, opts?.commitment ?? "confirmed");
signatures.push(txSignature); // Push only on successful confirmation
const confirmation = await connection.confirmTransaction(txSignature, opts?.commitment ?? "confirmed")
if (confirmation.value.err) {
//@ts-ignore
confirmation.value.err.InstructionError.forEach((error) => {
if (error.Custom) {
const _error: { code: number, msg: string, name: string } | undefined = opts?.customErrors?.[index]?.find(e => e.code == error.Custom)
errors.push({ message: _error?.msg || "Custom program Error, check on explorer.", name: _error?.name || "Anchor Error" })
}
})
}
signatures.push(txSignature);
}
}
})
}
}
}
catch (e) {
errors.push(e as any)
catch (e: any) {
errors.push({ message: e.message, name: e.name ?? "Transaction Error" })
}
return {
signatures: signatures,
Expand Down

0 comments on commit 1617b9f

Please sign in to comment.