Skip to content

Commit

Permalink
feat: error code helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
hstove committed May 17, 2024
1 parent 39ebb2f commit 63cf6f3
Show file tree
Hide file tree
Showing 8 changed files with 299 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/seven-rats-laugh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clarigen/core': patch
---

Adds new error-code helpers, including `extractErrors` and `projectErrors`
4 changes: 4 additions & 0 deletions demo-project/contracts/tester.clar
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down
42 changes: 42 additions & 0 deletions demo-project/esm/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -539,13 +539,55 @@ export const contracts = {
>,
},
variables: {
ERR_ONE: {
name: 'ERR_ONE',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
} as TypedAbiVariable<Response<null, bigint>>,
ERR_TWO: {
name: 'ERR_TWO',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
} as TypedAbiVariable<Response<null, bigint>>,
errThree: {
name: 'err-three',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
} as TypedAbiVariable<Response<null, bigint>>,
numVar: {
name: 'num-var',
type: 'uint128',
access: 'variable',
} as TypedAbiVariable<bigint>,
},
constants: {
ERR_ONE: {
isOk: false,
value: 1n,
},
ERR_TWO: {
isOk: false,
value: 2n,
},
errThree: {
isOk: false,
value: 3n,
},
numVar: 0n,
},
non_fungible_tokens: [
Expand Down
42 changes: 42 additions & 0 deletions packages/clarigen-test/test/generated/clarigen-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -539,13 +539,55 @@ export const contracts = {
>,
},
variables: {
ERR_ONE: {
name: 'ERR_ONE',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
} as TypedAbiVariable<Response<null, bigint>>,
ERR_TWO: {
name: 'ERR_TWO',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
} as TypedAbiVariable<Response<null, bigint>>,
errThree: {
name: 'err-three',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
} as TypedAbiVariable<Response<null, bigint>>,
numVar: {
name: 'num-var',
type: 'uint128',
access: 'variable',
} as TypedAbiVariable<bigint>,
},
constants: {
ERR_ONE: {
isOk: false,
value: 1n,
},
ERR_TWO: {
isOk: false,
value: 2n,
},
errThree: {
isOk: false,
value: 3n,
},
numVar: 0n,
},
non_fungible_tokens: [
Expand Down
42 changes: 42 additions & 0 deletions packages/cli/test/generated/clarigen-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -539,13 +539,55 @@ export const contracts = {
>,
},
variables: {
ERR_ONE: {
name: 'ERR_ONE',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
} as TypedAbiVariable<Response<null, bigint>>,
ERR_TWO: {
name: 'ERR_TWO',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
} as TypedAbiVariable<Response<null, bigint>>,
errThree: {
name: 'err-three',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
} as TypedAbiVariable<Response<null, bigint>>,
numVar: {
name: 'num-var',
type: 'uint128',
access: 'variable',
} as TypedAbiVariable<bigint>,
},
constants: {
ERR_ONE: {
isOk: false,
value: 1n,
},
ERR_TWO: {
isOk: false,
value: 2n,
},
errThree: {
isOk: false,
value: 3n,
},
numVar: 0n,
},
non_fungible_tokens: [
Expand Down
71 changes: 71 additions & 0 deletions packages/core/src/utils.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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> = K extends string
? Lowercase<K> extends `err${string}`
? K
: never
: never;

export type ErrorCodes<C extends TypedAbi['constants']> = {
[K in keyof C as ErrorKeyCheck<K>]: C[K] extends {
isOk: boolean;
value: infer V;
}
? V
: C[K];
};

export type ProjectErrors<
T extends {
contracts: AllContracts;
}
> = {
[K in keyof T['contracts']]: ErrorCodes<T['contracts'][K]['constants']>;
};

export function extractErrors<T extends TypedAbi>(contract: T): ErrorCodes<T['constants']> {
const { constants } = contract;

const result: Partial<ErrorCodes<T['constants']>> = {};

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<T['constants']>] = value.value as ErrorCodes<
T['constants']
>[keyof ErrorCodes<T['constants']>];
} else {
result[key as keyof ErrorCodes<T['constants']>] = value as ErrorCodes<
T['constants']
>[keyof ErrorCodes<T['constants']>];
}
}
}

return result as unknown as ErrorCodes<T['constants']>;
}

export function projectErrors<
T extends {
contracts: AllContracts;
}
>(project: T): ProjectErrors<T> {
const { contracts } = project;
const result: Partial<ProjectErrors<T>> = {};

for (const key in contracts) {
result[key as keyof ProjectErrors<T>] = extractErrors(contracts[key]);
}

return result as unknown as ProjectErrors<T>;
}
52 changes: 51 additions & 1 deletion packages/core/test/clarity-types.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -19,6 +19,10 @@ import {
ApiOptionsJsonize,
Response,
ClarityAbiTypeNonFungibleToken,
extractErrors,
ErrorCodes,
ProjectErrors,
projectErrors,
} from '../src';
import {
bufferCV,
Expand Down Expand Up @@ -117,6 +121,52 @@ describe('cvToJSON', () => {
});
});

type TesterErrors = ErrorCodes<typeof devnet.tester.constants>;

type TesterErrorsP = ErrorCodes<typeof devnet.tester.constants>;

type PErrors = ProjectErrors<typeof project>;

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<typeof errors>;

const testErrors: E = {
ERR_ONE: 1n,
errTwo: 2n,
};

type CounterErrors = ErrorCodes<typeof contracts.counter.constants>;

// Jsonize
type Tup = {
a: Uint8Array;
Expand Down
42 changes: 42 additions & 0 deletions packages/core/test/generated/clarigen-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -539,13 +539,55 @@ export const contracts = {
>,
},
variables: {
ERR_ONE: {
name: 'ERR_ONE',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
} as TypedAbiVariable<Response<null, bigint>>,
ERR_TWO: {
name: 'ERR_TWO',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
} as TypedAbiVariable<Response<null, bigint>>,
errThree: {
name: 'err-three',
type: {
response: {
ok: 'none',
error: 'uint128',
},
},
access: 'constant',
} as TypedAbiVariable<Response<null, bigint>>,
numVar: {
name: 'num-var',
type: 'uint128',
access: 'variable',
} as TypedAbiVariable<bigint>,
},
constants: {
ERR_ONE: {
isOk: false,
value: 1n,
},
ERR_TWO: {
isOk: false,
value: 2n,
},
errThree: {
isOk: false,
value: 3n,
},
numVar: 0n,
},
non_fungible_tokens: [
Expand Down

0 comments on commit 63cf6f3

Please sign in to comment.