Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(rpc): migrate signPsbt methods #658

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion packages/rpc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
},
"dependencies": {
"@leather.io/models": "workspace:*",
"zod": "3.23.8"
"@leather.io/utils": "workspace:*",
"@scure/btc-signer": "1.4.0",
"zod": "3.23.8",
"zod-validation-error": "3.4.0"
},
"devDependencies": {
"tsup": "8.1.0",
Expand Down
3 changes: 3 additions & 0 deletions packages/rpc/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ export * from './methods/send-transfer';
export * from './methods/sign-message';
export * from './methods/stx-sign-message';

export * from './schemas/network-schemas';
export * from './schemas/validators';

export type LeatherRpcMethodMap = DefineGetInfoMethod &
DefineGetAddressesMethod &
DefineSignPsbtMethod &
Expand Down
52 changes: 43 additions & 9 deletions packages/rpc/src/methods/sign-psbt.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { DefaultNetworkConfigurations } from '@leather.io/models';
import { SigHash } from '@scure/btc-signer/transaction';
import { z } from 'zod';

import { testIsNumberOrArrayOfNumbers } from '../rpc/helpers';
import { DefineRpcMethod, RpcRequest, RpcResponse } from '../rpc/schemas';
import { baseNetworkSchema } from '../schemas/network-schemas';
import {
formatValidationErrors,
getRpcParamErrors,
validateRpcParams,
} from '../schemas/validators';

/**
* DEFAULT -- all inputs, all outputs
Expand All @@ -21,16 +29,42 @@ export enum SignatureHash {
SINGLE_ANYONECANPAY = 0x83,
}

export interface SignPsbtRequestParams {
account?: number;
allowedSighash?: SignatureHash[];
broadcast: boolean;
hex: string;
network: DefaultNetworkConfigurations;
signAtIndex?: number | number[];
[x: string]: unknown;
export type AllowedSighashTypes = SignatureHash | SigHash;

// Pass all sighashTypes through as allowed to btc-signer
export const allSighashTypes = [
SigHash.DEFAULT,
SignatureHash.ALL,
SignatureHash.NONE,
SignatureHash.SINGLE,
SigHash.ALL_ANYONECANPAY,
SignatureHash.ALL_ANYONECANPAY,
SignatureHash.NONE_ANYONECANPAY,
SignatureHash.SINGLE_ANYONECANPAY,
];

export const rpcSignPsbtParamsSchema = z.object({
account: z.number().int().optional(),
allowedSighash: z.array(z.any()).optional(),
broadcast: z.boolean().optional(),
hex: z.string(),
network: baseNetworkSchema.optional(),
signAtIndex: z
.union([z.number(), z.array(z.number())])
.optional()
.refine(testIsNumberOrArrayOfNumbers),
Copy link
Contributor

@tigranpetrossian tigranpetrossian Nov 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this by any chance a leftover? The refinement seems to be doing the same as the two rules above.
Also curious what's the context of testIsNumberOrArrayOfNumbers being a wrapper around isNumberOrNumberList + isUndefined?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that's a very good point, I've been in yup → zod migration mode and indeed, this is the same thing 🤔

});

export function validateRpcSignPsbtParams(obj: unknown) {
return validateRpcParams(obj, rpcSignPsbtParamsSchema);
}

export function getRpcSignPsbtParamErrors(obj: unknown) {
return formatValidationErrors(getRpcParamErrors(obj, rpcSignPsbtParamsSchema));
}

export type SignPsbtRequestParams = z.infer<typeof rpcSignPsbtParamsSchema>;

export interface SignPsbtResponseBody {
hex: string;
txid?: string;
Expand Down
6 changes: 6 additions & 0 deletions packages/rpc/src/rpc/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { isNumberOrNumberList, isUndefined } from '@leather.io/utils';

export function testIsNumberOrArrayOfNumbers(value: unknown) {
if (isUndefined(value)) return true;
return isNumberOrNumberList(value);
}
15 changes: 15 additions & 0 deletions packages/rpc/src/schemas/network-schemas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { z } from 'zod';

import {
type DefaultNetworkConfigurations,
WalletDefaultNetworkConfigurationIds,
} from '@leather.io/models';

type NonEmptyDefaultNetworkIdList = [
DefaultNetworkConfigurations,
...DefaultNetworkConfigurations[],
];

export const baseNetworkSchema = z.enum(
Object.values(WalletDefaultNetworkConfigurationIds) as NonEmptyDefaultNetworkIdList
);
28 changes: 28 additions & 0 deletions packages/rpc/src/schemas/validators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { z } from 'zod';
import { fromError } from 'zod-validation-error';

export function validateRpcParams(obj: unknown, validator: z.ZodSchema) {
try {
validator.parse(obj);
return true;
} catch (e) {
return false;
}
}

export function getRpcParamErrors(obj: unknown, validator: z.ZodTypeAny) {
try {
validator.parse(obj);
return [];
} catch (e) {
if (e instanceof z.ZodError) return [e];
return [];
}
}

export function formatValidationErrors(errors: z.ZodError[]) {
return errors
.map(error => fromError(error))
.join('. ')
.trim();
}
1 change: 0 additions & 1 deletion packages/utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
"dependencies": {
"@leather.io/constants": "workspace:*",
"@leather.io/models": "workspace:*",
"@leather.io/rpc": "workspace:*",
"bignumber.js": "9.1.2"
},
"devDependencies": {
Expand Down
5 changes: 5 additions & 0 deletions packages/utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,8 @@
return match[variant];
};
}

export function isNumberOrNumberList(value: unknown): value is number | number[] {
if (Array.isArray(value)) return value.every(item => isNumber(item));
return isNumber(value);
}

Check warning on line 235 in packages/utils/src/index.ts

View check run for this annotation

Codecov / codecov/patch

packages/utils/src/index.ts#L233-L235

Added lines #L233 - L235 were not covered by tests
55 changes: 47 additions & 8 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading