Skip to content

Commit 53b05d6

Browse files
committed
Use SolanaErrors for generated errors
1 parent 70e5736 commit 53b05d6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+396
-162
lines changed

src/fragments/discriminatorCondition.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ function getFieldConditionFragment(
9191
): Fragment {
9292
const field = scope.struct.fields.find(f => f.name === discriminator.name);
9393
if (!field || !field.defaultValue) {
94-
// TODO: Coded error.
9594
throw new Error(
9695
`Field discriminator "${discriminator.name}" does not have a matching argument with default value.`,
9796
);

src/fragments/instructionParseFunction.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,10 @@ function getFunctionFragment(
120120

121121
let accountHelpers: Fragment | undefined;
122122
if (hasAccounts) {
123+
const solanaError = use('SolanaError', 'solanaErrors');
124+
const solanaErrorCode = use('SOLANA_ERROR__PROGRAM_CLIENTS__INSUFFICIENT_ACCOUNT_METAS', 'solanaErrors');
123125
accountHelpers = fragment`if (instruction.accounts.length < ${minimumNumberOfAccounts}) {
124-
// TODO: Coded error.
125-
throw new Error('Not enough accounts');
126+
throw new ${solanaError}(${solanaErrorCode}, { actualAccountMetas: instruction.accounts.length, expectedAccountMetas: ${minimumNumberOfAccounts} });
126127
}
127128
let accountIndex = 0;
128129
const getNextAccount = () => {

src/fragments/programAccounts.ts

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { ProgramNode, resolveNestedTypeNode } from '@codama/nodes';
2-
import { mapFragmentContent } from '@codama/renderers-core';
3-
import { pipe } from '@codama/visitors-core';
42

5-
import { addFragmentImports, Fragment, fragment, mergeFragments, RenderScope } from '../utils';
3+
import { Fragment, fragment, mergeFragments, RenderScope, use } from '../utils';
64
import { getDiscriminatorConditionFragment } from './discriminatorCondition';
75

86
export function getProgramAccountsFragment(
@@ -45,32 +43,28 @@ function getProgramAccountsIdentifierFunctionFragment(
4543
const programAccountsEnum = nameApi.programAccountsEnum(programNode.name);
4644
const programAccountsIdentifierFunction = nameApi.programAccountsIdentifierFunction(programNode.name);
4745

48-
return pipe(
49-
mergeFragments(
50-
accountsWithDiscriminators.map((account): Fragment => {
51-
const variant = nameApi.programAccountsEnumVariant(account.name);
52-
return getDiscriminatorConditionFragment({
53-
...scope,
54-
dataName: 'data',
55-
discriminators: account.discriminators ?? [],
56-
ifTrue: `return ${programAccountsEnum}.${variant};`,
57-
struct: resolveNestedTypeNode(account.data),
58-
});
59-
}),
60-
c => c.join('\n'),
61-
),
62-
f =>
63-
mapFragmentContent(
64-
f,
65-
discriminators =>
66-
`export function ${programAccountsIdentifierFunction}(` +
67-
`account: { data: ReadonlyUint8Array } | ReadonlyUint8Array` +
68-
`): ${programAccountsEnum} {\n` +
69-
`const data = 'data' in account ? account.data : account;\n` +
70-
`${discriminators}\n` +
71-
`throw new Error("The provided account could not be identified as a ${programNode.name} account.")\n` +
72-
`}`,
73-
),
74-
f => addFragmentImports(f, 'solanaCodecsCore', ['type ReadonlyUint8Array']),
46+
const discriminatorsFragment = mergeFragments(
47+
accountsWithDiscriminators.map((account): Fragment => {
48+
const variant = nameApi.programAccountsEnumVariant(account.name);
49+
return getDiscriminatorConditionFragment({
50+
...scope,
51+
dataName: 'data',
52+
discriminators: account.discriminators ?? [],
53+
ifTrue: `return ${programAccountsEnum}.${variant};`,
54+
struct: resolveNestedTypeNode(account.data),
55+
});
56+
}),
57+
c => c.join('\n'),
7558
);
59+
60+
const readonlyUint8Array = use('type ReadonlyUint8Array', 'solanaCodecsCore');
61+
// TODO(loris): Waiting for Kit to have this missing error code.
62+
// const solanaError = use('SolanaError', 'solanaErrors');
63+
// const solanaErrorCode = use('SOLANA_ERROR__PROGRAM_CLIENTS__FAILED_TO_IDENTIFY_ACCOUNT', 'solanaErrors');
64+
65+
return fragment`export function ${programAccountsIdentifierFunction}(account: { data: ${readonlyUint8Array} } | ${readonlyUint8Array}): ${programAccountsEnum} {
66+
const data = 'data' in account ? account.data : account;
67+
${discriminatorsFragment}
68+
throw new Error("The provided account could not be identified as a ${programNode.name} account.");
69+
}`;
7670
}

src/fragments/programInstructions.ts

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@ import {
44
ProgramNode,
55
structTypeNodeFromInstructionArgumentNodes,
66
} from '@codama/nodes';
7-
import { mapFragmentContent } from '@codama/renderers-core';
8-
import { pipe } from '@codama/visitors-core';
97

10-
import { addFragmentImports, Fragment, fragment, mergeFragments, RenderScope, use } from '../utils';
8+
import { Fragment, fragment, mergeFragments, RenderScope, use } from '../utils';
119
import { getDiscriminatorConditionFragment } from './discriminatorCondition';
1210

1311
export function getProgramInstructionsFragment(
@@ -76,22 +74,15 @@ function getProgramInstructionsIdentifierFunctionFragment(
7674
c => c.join('\n'),
7775
);
7876

79-
return pipe(
80-
discriminatorsFragment,
81-
f =>
82-
mapFragmentContent(
83-
f,
84-
discriminators =>
85-
`export function ${programInstructionsIdentifierFunction}(` +
86-
`instruction: { data: ReadonlyUint8Array } | ReadonlyUint8Array` +
87-
`): ${programInstructionsEnum} {\n` +
88-
`const data = 'data' in instruction ? instruction.data : instruction;\n` +
89-
`${discriminators}\n` +
90-
`throw new Error("The provided instruction could not be identified as a ${programNode.name} instruction.")\n` +
91-
`}`,
92-
),
93-
f => addFragmentImports(f, 'solanaCodecsCore', ['type ReadonlyUint8Array']),
94-
);
77+
const readonlyUint8Array = use('type ReadonlyUint8Array', 'solanaCodecsCore');
78+
const solanaError = use('SolanaError', 'solanaErrors');
79+
const solanaErrorCode = use('SOLANA_ERROR__PROGRAM_CLIENTS__FAILED_TO_IDENTIFY_INSTRUCTION', 'solanaErrors');
80+
81+
return fragment`export function ${programInstructionsIdentifierFunction}(instruction: { data: ${readonlyUint8Array} } | ${readonlyUint8Array}): ${programInstructionsEnum} {
82+
const data = 'data' in instruction ? instruction.data : instruction;
83+
${discriminatorsFragment}
84+
throw new ${solanaError}(${solanaErrorCode}, { instructionData: data, programName: "${programNode.name}" });
85+
}`;
9586
}
9687

9788
function getProgramInstructionsParsedUnionTypeFragment(
@@ -159,6 +150,9 @@ function getProgramInstructionsParseFunctionFragment(
159150
c => c.join('\n'),
160151
);
161152

153+
const solanaError = use('SolanaError', 'solanaErrors');
154+
const solanaErrorCode = use('SOLANA_ERROR__PROGRAM_CLIENTS__UNRECOGNIZED_INSTRUCTION_TYPE', 'solanaErrors');
155+
162156
return fragment`
163157
export function ${parseFunction}<TProgram extends string>(
164158
instruction: ${use('type Instruction', 'solanaInstructions')}<TProgram>
@@ -167,7 +161,7 @@ function getProgramInstructionsParseFunctionFragment(
167161
const instructionType = ${programInstructionsIdentifierFunction}(instruction);
168162
switch (instructionType) {
169163
${switchCases}
170-
default: throw new Error(\`Unrecognized instruction type: \${instructionType as string}\`);
164+
default: throw new ${solanaError}(${solanaErrorCode}, { instructionType: instructionType as string, programName: "${programNode.name}" });
171165
}
172166
}`;
173167
}

test/e2e/anchor/src/generated/instructions/createGuard.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import {
2626
getU32Encoder,
2727
getUtf8Decoder,
2828
getUtf8Encoder,
29+
SOLANA_ERROR__PROGRAM_CLIENTS__INSUFFICIENT_ACCOUNT_METAS,
30+
SolanaError,
2931
transformEncoder,
3032
type AccountMeta,
3133
type AccountSignerMeta,
@@ -457,8 +459,10 @@ export function parseCreateGuardInstruction<TProgram extends string, TAccountMet
457459
InstructionWithData<ReadonlyUint8Array>,
458460
): ParsedCreateGuardInstruction<TProgram, TAccountMetas> {
459461
if (instruction.accounts.length < 8) {
460-
// TODO: Coded error.
461-
throw new Error('Not enough accounts');
462+
throw new SolanaError(SOLANA_ERROR__PROGRAM_CLIENTS__INSUFFICIENT_ACCOUNT_METAS, {
463+
actualAccountMetas: instruction.accounts.length,
464+
expectedAccountMetas: 8,
465+
});
462466
}
463467
let accountIndex = 0;
464468
const getNextAccount = () => {

test/e2e/anchor/src/generated/instructions/execute.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import {
1818
getStructEncoder,
1919
getU64Decoder,
2020
getU64Encoder,
21+
SOLANA_ERROR__PROGRAM_CLIENTS__INSUFFICIENT_ACCOUNT_METAS,
22+
SolanaError,
2123
transformEncoder,
2224
type AccountMeta,
2325
type Address,
@@ -334,8 +336,10 @@ export function parseExecuteInstruction<TProgram extends string, TAccountMetas e
334336
InstructionWithData<ReadonlyUint8Array>,
335337
): ParsedExecuteInstruction<TProgram, TAccountMetas> {
336338
if (instruction.accounts.length < 7) {
337-
// TODO: Coded error.
338-
throw new Error('Not enough accounts');
339+
throw new SolanaError(SOLANA_ERROR__PROGRAM_CLIENTS__INSUFFICIENT_ACCOUNT_METAS, {
340+
actualAccountMetas: instruction.accounts.length,
341+
expectedAccountMetas: 7,
342+
});
339343
}
340344
let accountIndex = 0;
341345
const getNextAccount = () => {

test/e2e/anchor/src/generated/instructions/initialize.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import {
1616
getProgramDerivedAddress,
1717
getStructDecoder,
1818
getStructEncoder,
19+
SOLANA_ERROR__PROGRAM_CLIENTS__INSUFFICIENT_ACCOUNT_METAS,
20+
SolanaError,
1921
transformEncoder,
2022
type AccountMeta,
2123
type AccountSignerMeta,
@@ -306,8 +308,10 @@ export function parseInitializeInstruction<TProgram extends string, TAccountMeta
306308
InstructionWithData<ReadonlyUint8Array>,
307309
): ParsedInitializeInstruction<TProgram, TAccountMetas> {
308310
if (instruction.accounts.length < 6) {
309-
// TODO: Coded error.
310-
throw new Error('Not enough accounts');
311+
throw new SolanaError(SOLANA_ERROR__PROGRAM_CLIENTS__INSUFFICIENT_ACCOUNT_METAS, {
312+
actualAccountMetas: instruction.accounts.length,
313+
expectedAccountMetas: 6,
314+
});
311315
}
312316
let accountIndex = 0;
313317
const getNextAccount = () => {

test/e2e/anchor/src/generated/instructions/updateGuard.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import {
2020
getProgramDerivedAddress,
2121
getStructDecoder,
2222
getStructEncoder,
23+
SOLANA_ERROR__PROGRAM_CLIENTS__INSUFFICIENT_ACCOUNT_METAS,
24+
SolanaError,
2325
transformEncoder,
2426
type AccountMeta,
2527
type AccountSignerMeta,
@@ -377,8 +379,10 @@ export function parseUpdateGuardInstruction<TProgram extends string, TAccountMet
377379
InstructionWithData<ReadonlyUint8Array>,
378380
): ParsedUpdateGuardInstruction<TProgram, TAccountMetas> {
379381
if (instruction.accounts.length < 6) {
380-
// TODO: Coded error.
381-
throw new Error('Not enough accounts');
382+
throw new SolanaError(SOLANA_ERROR__PROGRAM_CLIENTS__INSUFFICIENT_ACCOUNT_METAS, {
383+
actualAccountMetas: instruction.accounts.length,
384+
expectedAccountMetas: 6,
385+
});
382386
}
383387
let accountIndex = 0;
384388
const getNextAccount = () => {

test/e2e/anchor/src/generated/programs/wenTransferGuard.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import {
1111
containsBytes,
1212
fixEncoderSize,
1313
getBytesEncoder,
14+
SOLANA_ERROR__PROGRAM_CLIENTS__FAILED_TO_IDENTIFY_INSTRUCTION,
15+
SOLANA_ERROR__PROGRAM_CLIENTS__UNRECOGNIZED_INSTRUCTION_TYPE,
16+
SolanaError,
1417
type Address,
1518
type Instruction,
1619
type InstructionWithData,
@@ -97,7 +100,10 @@ export function identifyWenTransferGuardInstruction(
97100
) {
98101
return WenTransferGuardInstruction.UpdateGuard;
99102
}
100-
throw new Error('The provided instruction could not be identified as a wenTransferGuard instruction.');
103+
throw new SolanaError(SOLANA_ERROR__PROGRAM_CLIENTS__FAILED_TO_IDENTIFY_INSTRUCTION, {
104+
instructionData: data,
105+
programName: 'wenTransferGuard',
106+
});
101107
}
102108

103109
export type ParsedWenTransferGuardInstruction<TProgram extends string = 'LockdqYQ9X2kwtWB99ioSbxubAmEi8o9jqYwbXgrrRw'> =
@@ -137,6 +143,9 @@ export function parseWenTransferGuardInstruction<TProgram extends string>(
137143
};
138144
}
139145
default:
140-
throw new Error(`Unrecognized instruction type: ${instructionType as string}`);
146+
throw new SolanaError(SOLANA_ERROR__PROGRAM_CLIENTS__UNRECOGNIZED_INSTRUCTION_TYPE, {
147+
instructionType: instructionType as string,
148+
programName: 'wenTransferGuard',
149+
});
141150
}
142151
}

test/e2e/dummy/src/generated/instructions/instruction6.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
*/
88

99
import {
10+
SOLANA_ERROR__PROGRAM_CLIENTS__INSUFFICIENT_ACCOUNT_METAS,
11+
SolanaError,
1012
type AccountMeta,
1113
type Address,
1214
type Instruction,
@@ -67,8 +69,10 @@ export function parseInstruction6Instruction<TProgram extends string, TAccountMe
6769
instruction: Instruction<TProgram> & InstructionWithAccounts<TAccountMetas>,
6870
): ParsedInstruction6Instruction<TProgram, TAccountMetas> {
6971
if (instruction.accounts.length < 1) {
70-
// TODO: Coded error.
71-
throw new Error('Not enough accounts');
72+
throw new SolanaError(SOLANA_ERROR__PROGRAM_CLIENTS__INSUFFICIENT_ACCOUNT_METAS, {
73+
actualAccountMetas: instruction.accounts.length,
74+
expectedAccountMetas: 1,
75+
});
7276
}
7377
let accountIndex = 0;
7478
const getNextAccount = () => {

0 commit comments

Comments
 (0)