diff --git a/.changeset/seven-rats-laugh.md b/.changeset/seven-rats-laugh.md new file mode 100644 index 0000000..0d5f0ba --- /dev/null +++ b/.changeset/seven-rats-laugh.md @@ -0,0 +1,5 @@ +--- +'@clarigen/core': patch +--- + +Adds new error-code helpers, including `extractErrors` and `projectErrors` diff --git a/demo-project/contracts/tester.clar b/demo-project/contracts/tester.clar index b0edfb3..faf5e28 100644 --- a/demo-project/contracts/tester.clar +++ b/demo-project/contracts/tester.clar @@ -1,5 +1,9 @@ (define-map simple-map uint bool) +(define-constant ERR_ONE (err u1)) +(define-constant ERR_TWO (err u2)) +(define-constant err-three (err u3)) + (define-non-fungible-token nft uint) (define-non-fungible-token tuple-nft { diff --git a/demo-project/esm/index.ts b/demo-project/esm/index.ts index 667057f..b2b995f 100644 --- a/demo-project/esm/index.ts +++ b/demo-project/esm/index.ts @@ -539,6 +539,36 @@ export const contracts = { >, }, variables: { + ERR_ONE: { + name: 'ERR_ONE', + type: { + response: { + ok: 'none', + error: 'uint128', + }, + }, + access: 'constant', + } as TypedAbiVariable>, + ERR_TWO: { + name: 'ERR_TWO', + type: { + response: { + ok: 'none', + error: 'uint128', + }, + }, + access: 'constant', + } as TypedAbiVariable>, + errThree: { + name: 'err-three', + type: { + response: { + ok: 'none', + error: 'uint128', + }, + }, + access: 'constant', + } as TypedAbiVariable>, numVar: { name: 'num-var', type: 'uint128', @@ -546,6 +576,18 @@ export const contracts = { } as TypedAbiVariable, }, constants: { + ERR_ONE: { + isOk: false, + value: 1n, + }, + ERR_TWO: { + isOk: false, + value: 2n, + }, + errThree: { + isOk: false, + value: 3n, + }, numVar: 0n, }, non_fungible_tokens: [ diff --git a/packages/clarigen-test/test/generated/clarigen-types.ts b/packages/clarigen-test/test/generated/clarigen-types.ts index 667057f..b2b995f 100644 --- a/packages/clarigen-test/test/generated/clarigen-types.ts +++ b/packages/clarigen-test/test/generated/clarigen-types.ts @@ -539,6 +539,36 @@ export const contracts = { >, }, variables: { + ERR_ONE: { + name: 'ERR_ONE', + type: { + response: { + ok: 'none', + error: 'uint128', + }, + }, + access: 'constant', + } as TypedAbiVariable>, + ERR_TWO: { + name: 'ERR_TWO', + type: { + response: { + ok: 'none', + error: 'uint128', + }, + }, + access: 'constant', + } as TypedAbiVariable>, + errThree: { + name: 'err-three', + type: { + response: { + ok: 'none', + error: 'uint128', + }, + }, + access: 'constant', + } as TypedAbiVariable>, numVar: { name: 'num-var', type: 'uint128', @@ -546,6 +576,18 @@ export const contracts = { } as TypedAbiVariable, }, constants: { + ERR_ONE: { + isOk: false, + value: 1n, + }, + ERR_TWO: { + isOk: false, + value: 2n, + }, + errThree: { + isOk: false, + value: 3n, + }, numVar: 0n, }, non_fungible_tokens: [ diff --git a/packages/cli/test/generated/clarigen-types.ts b/packages/cli/test/generated/clarigen-types.ts index 667057f..b2b995f 100644 --- a/packages/cli/test/generated/clarigen-types.ts +++ b/packages/cli/test/generated/clarigen-types.ts @@ -539,6 +539,36 @@ export const contracts = { >, }, variables: { + ERR_ONE: { + name: 'ERR_ONE', + type: { + response: { + ok: 'none', + error: 'uint128', + }, + }, + access: 'constant', + } as TypedAbiVariable>, + ERR_TWO: { + name: 'ERR_TWO', + type: { + response: { + ok: 'none', + error: 'uint128', + }, + }, + access: 'constant', + } as TypedAbiVariable>, + errThree: { + name: 'err-three', + type: { + response: { + ok: 'none', + error: 'uint128', + }, + }, + access: 'constant', + } as TypedAbiVariable>, numVar: { name: 'num-var', type: 'uint128', @@ -546,6 +576,18 @@ export const contracts = { } as TypedAbiVariable, }, constants: { + ERR_ONE: { + isOk: false, + value: 1n, + }, + ERR_TWO: { + isOk: false, + value: 2n, + }, + errThree: { + isOk: false, + value: 3n, + }, numVar: 0n, }, non_fungible_tokens: [ diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts index c6b32f6..a76065b 100644 --- a/packages/core/src/utils.ts +++ b/packages/core/src/utils.ts @@ -1,6 +1,8 @@ import { contractPrincipalCV, ContractPrincipalCV } from '@stacks/transactions'; import { Contract } from './types'; import { hex } from '@scure/base'; +import { TypedAbi } from './abi-types'; +import { AllContracts } from './factory-types'; export const TESTNET_BURN_ADDRESS = 'ST000000000000000000002AMW42H'; export const MAINNET_BURN_ADDRESS = 'SP000000000000000000002Q6VF78'; @@ -68,3 +70,72 @@ export function bytesToAscii(bytes: Uint8Array) { } export const isNumber = (value: number | string): value is number => typeof value === 'number'; + +// Error code helpers + +type ErrorKeyCheck = K extends string + ? Lowercase extends `err${string}` + ? K + : never + : never; + +export type ErrorCodes = { + [K in keyof C as ErrorKeyCheck]: C[K] extends { + isOk: boolean; + value: infer V; + } + ? V + : C[K]; +}; + +export type ProjectErrors< + T extends { + contracts: AllContracts; + } +> = { + [K in keyof T['contracts']]: ErrorCodes; +}; + +export function extractErrors(contract: T): ErrorCodes { + const { constants } = contract; + + const result: Partial> = {}; + + for (const key in constants) { + if (key.toLowerCase().startsWith('err')) { + const value = constants[key]; + if ( + typeof value === 'object' && + value && + 'isOk' in value && + !value.isOk && + 'value' in value + ) { + result[key as keyof ErrorCodes] = value.value as ErrorCodes< + T['constants'] + >[keyof ErrorCodes]; + } else { + result[key as keyof ErrorCodes] = value as ErrorCodes< + T['constants'] + >[keyof ErrorCodes]; + } + } + } + + return result as unknown as ErrorCodes; +} + +export function projectErrors< + T extends { + contracts: AllContracts; + } +>(project: T): ProjectErrors { + const { contracts } = project; + const result: Partial> = {}; + + for (const key in contracts) { + result[key as keyof ProjectErrors] = extractErrors(contracts[key]); + } + + return result as unknown as ProjectErrors; +} diff --git a/packages/core/test/clarity-types.test.ts b/packages/core/test/clarity-types.test.ts index 2d275d9..2a63f18 100644 --- a/packages/core/test/clarity-types.test.ts +++ b/packages/core/test/clarity-types.test.ts @@ -9,7 +9,7 @@ import { AbiTupleTo, getTypeString, } from '../src/clarity-types'; -import { ClarityAbiType } from '../src/abi-types'; +import { ClarityAbiType, ErrType, ResponseErr, TypedAbi } from '../src/abi-types'; import { projectFactory, ro, @@ -19,6 +19,10 @@ import { ApiOptionsJsonize, Response, ClarityAbiTypeNonFungibleToken, + extractErrors, + ErrorCodes, + ProjectErrors, + projectErrors, } from '../src'; import { bufferCV, @@ -117,6 +121,52 @@ describe('cvToJSON', () => { }); }); +type TesterErrors = ErrorCodes; + +type TesterErrorsP = ErrorCodes; + +type PErrors = ProjectErrors; + +type PTesterErrors = PErrors['tester']; + +describe('extracting errors', () => { + const contract = devnet.tester; + + it('can extract a contracts errors', () => { + const errors = extractErrors(contract); + expect(errors.ERR_ONE).toEqual(1n); + expect(errors.ERR_TWO).toEqual(2n); + expect(errors.errThree).toEqual(3n); + expect(extractErrors(project.contracts.tester)).toEqual(errors); + }); + + it('can get project errors', () => { + const errors = projectErrors(project); + expect(errors.tester.ERR_ONE).toEqual(1n); + expect(errors.tester.ERR_TWO).toEqual(2n); + expect(errors.tester.errThree).toEqual(3n); + expect(errors.counter).toEqual({}); + }); +}); + +const errors = { + ERR_ONE: 1n, + errTwo: { + isOk: false, + value: 2n, + }, + base: 123n, +} as const; + +type E = ErrorCodes; + +const testErrors: E = { + ERR_ONE: 1n, + errTwo: 2n, +}; + +type CounterErrors = ErrorCodes; + // Jsonize type Tup = { a: Uint8Array; diff --git a/packages/core/test/generated/clarigen-types.ts b/packages/core/test/generated/clarigen-types.ts index 667057f..b2b995f 100644 --- a/packages/core/test/generated/clarigen-types.ts +++ b/packages/core/test/generated/clarigen-types.ts @@ -539,6 +539,36 @@ export const contracts = { >, }, variables: { + ERR_ONE: { + name: 'ERR_ONE', + type: { + response: { + ok: 'none', + error: 'uint128', + }, + }, + access: 'constant', + } as TypedAbiVariable>, + ERR_TWO: { + name: 'ERR_TWO', + type: { + response: { + ok: 'none', + error: 'uint128', + }, + }, + access: 'constant', + } as TypedAbiVariable>, + errThree: { + name: 'err-three', + type: { + response: { + ok: 'none', + error: 'uint128', + }, + }, + access: 'constant', + } as TypedAbiVariable>, numVar: { name: 'num-var', type: 'uint128', @@ -546,6 +576,18 @@ export const contracts = { } as TypedAbiVariable, }, constants: { + ERR_ONE: { + isOk: false, + value: 1n, + }, + ERR_TWO: { + isOk: false, + value: 2n, + }, + errThree: { + isOk: false, + value: 3n, + }, numVar: 0n, }, non_fungible_tokens: [