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

Revert "Revert "Add message schema validators"" #23

Merged
merged 21 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
31 changes: 23 additions & 8 deletions package-lock.json

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

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
"jsontokens": "4.0.1",
"lodash.omit": "4.5.0"
},
"peerDependencies": {
"valibot": "0.33.2"
},
"devDependencies": {
"@types/jest": "^29.2.6",
"@types/lodash.omit": "4.5.9",
Expand All @@ -42,7 +45,7 @@
"ts-jest": "^29.0.5",
"ts-loader": "^9.4.1",
"tsup": "^8.0.2",
"typescript": "^4.9.4",
"typescript": "5.4.5",
"util": "^0.12.4",
"vm-browserify": "^1.1.2",
"webpack": "^5.74.0",
Expand Down
14 changes: 8 additions & 6 deletions src/addresses/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as v from 'valibot';
import type { RequestOptions, RequestPayload } from '../types';

export enum AddressPurpose {
Expand All @@ -20,12 +21,13 @@ export enum AddressType {
stacks = 'stacks',
}

export interface Address {
address: string;
publicKey: string;
purpose?: AddressPurpose;
addressType?: AddressType;
}
export const addressSchema = v.object({
address: v.string(),
publicKey: v.string(),
purpose: v.enum(AddressPurpose),
addressType: v.enum(AddressType),
});
export type Address = v.InferOutput<typeof addressSchema>;

export interface GetAddressResponse {
addresses: Address[];
Expand Down
151 changes: 96 additions & 55 deletions src/request/types/btcMethods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,65 +2,109 @@
* Represents the types and interfaces related to BTC methods.
*/

import { Address, AddressPurpose } from '../../addresses';
import { MethodParamsAndResult } from '../../types';
import { AddressPurpose, addressSchema } from '../../addresses';
import { MethodParamsAndResult, rpcRequestMessageSchema } from '../../types';
import * as v from 'valibot';

type GetInfoResult = {
version: number | string;
methods?: Array<string>;
supports?: Array<string>;
};
export const getInfoMethodName = 'getInfo';
export const getInfoParamsSchema = v.null();
export const getInfoResultSchema = v.object({
/**
* Version of the wallet.
*/
version: v.string(),

export type GetInfo = MethodParamsAndResult<null, GetInfoResult>;
/**
* [WBIP](https://wbips.netlify.app/wbips/WBIP002) methods supported by the wallet.
*/
methods: v.optional(v.array(v.string())),

type GetAddressesParams = {
/**
* The purposes for which to generate addresses.
* possible values are "payment", "ordinals", ...
* List of WBIP standards supported by the wallet. Not currently used.
*/
supports: v.array(v.string()),
});
export const getInfoSchema = v.object({
...rpcRequestMessageSchema.entries,
...v.object({
method: v.literal(getInfoMethodName),
params: getInfoParamsSchema,
id: v.string(),
}).entries,
});
export type GetInfo = MethodParamsAndResult<
v.InferOutput<typeof getInfoParamsSchema>,
v.InferOutput<typeof getInfoResultSchema>
>;

export const getAddressesMethodName = 'getAddresses';
export const getAddressesParamsSchema = v.object({
/**
* The purposes for which to generate addresses. See
* {@linkcode AddressPurpose} for available purposes.
*/
purposes: Array<AddressPurpose>;
purposes: v.array(v.enum(AddressPurpose)),
/**
* a message to be displayed to the user in the request prompt.
* A message to be displayed to the user in the request prompt.
*/
message?: string;
};

/**
* The addresses generated for the given purposes.
*/
type GetAddressesResult = {
addresses: Array<Address>;
};

export type GetAddresses = MethodParamsAndResult<GetAddressesParams, GetAddressesResult>;

export type SignMessageParams = {
message: v.optional(v.string()),
});
export const getAddressesResultSchema = v.object({
/**
* The addresses generated for the given purposes.
*/
addresses: v.array(addressSchema),
});
export const getAddressesRequestMessageSchema = v.object({
...rpcRequestMessageSchema.entries,
...v.object({
method: v.literal(getAddressesMethodName),
params: getAddressesParamsSchema,
id: v.string(),
}).entries,
});
export type GetAddresses = MethodParamsAndResult<
v.InferOutput<typeof getAddressesParamsSchema>,
v.InferOutput<typeof getAddressesResultSchema>
>;

export const signMessageMethodName = 'signMessage';
export const signMessageParamsSchema = v.object({
/**
* The address used for signing.
**/
address: string;
address: v.string(),
/**
* The message to sign.
**/
message: string;
};

type SignMessageResult = {
message: v.string(),
});
export const signMessageResultSchema = v.object({
/**
* The signature of the message.
*/
signature: string;
signature: v.string(),
/**
* hash of the message.
*/
messageHash: string;
messageHash: v.string(),
/**
* The address used for signing.
*/
address: string;
};

export type SignMessage = MethodParamsAndResult<SignMessageParams, SignMessageResult>;
address: v.string(),
});
export const signMessageRequestMessageSchema = v.object({
...rpcRequestMessageSchema.entries,
...v.object({
method: v.literal(signMessageMethodName),
params: signMessageParamsSchema,
id: v.string(),
}).entries,
});
export type SignMessage = MethodParamsAndResult<
v.InferOutput<typeof signMessageParamsSchema>,
v.InferOutput<typeof signMessageResultSchema>
>;

type Recipient = {
/**
Expand Down Expand Up @@ -124,21 +168,18 @@ export type SignPsbtResult = {

export type SignPsbt = MethodParamsAndResult<SignPsbtParams, SignPsbtResult>;

export type GetAccountsParams = {
/**
* The purposes for which to generate addresses.
* possible values are "payment", "ordinals", ...
*/
purposes: Array<AddressPurpose>;
/**
* a message to be displayed to the user in the request prompt.
*/
/**
* a message to be displayed to the user in the request prompt.
*/
message?: string;
};

export type GetAccountResult = Address[];

export type GetAccounts = MethodParamsAndResult<GetAccountsParams, GetAccountResult>;
export const getAccountsMethodName = 'getAccounts';
export const getAccountsParamsSchema = getAddressesParamsSchema;
export const getAccountsResultSchema = v.array(addressSchema);
export const getAccountsRequestMessageSchema = v.object({
...rpcRequestMessageSchema.entries,
...v.object({
method: v.literal(getAccountsMethodName),
params: getAccountsParamsSchema,
id: v.string(),
}).entries,
});
export type GetAccounts = MethodParamsAndResult<
v.InferOutput<typeof getAccountsParamsSchema>,
v.InferOutput<typeof getAccountsResultSchema>
>;
9 changes: 8 additions & 1 deletion src/request/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
StxSignTransaction,
StxTransferStx,
} from './stxMethods';
import { Connect, Disconnect } from './walletMethods';

export interface StxRequests {
stx_callContract: StxCallContract;
Expand Down Expand Up @@ -64,10 +65,16 @@ export interface RunesRequests {

export type RunesRequestMethod = keyof RunesRequests;

export type Requests = BtcRequests & StxRequests & RunesRequests;
export interface WalletMethods {
wallet_connect: Connect;
wallet_disconnect: Disconnect;
}

export type Requests = BtcRequests & StxRequests & RunesRequests & WalletMethods;

export type Return<Method> = Method extends keyof Requests ? Requests[Method]['result'] : never;
export type Params<Method> = Method extends keyof Requests ? Requests[Method]['params'] : never;

export * from './stxMethods';
export * from './btcMethods';
export * from './walletMethods';
34 changes: 34 additions & 0 deletions src/request/types/walletMethods.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { MethodParamsAndResult, rpcRequestMessageSchema } from '../../types';
import * as v from 'valibot';

export const connectMethodName = 'wallet_connect';
export const connectParamsSchema = v.undefined();
export const connectResultSchema = v.undefined();
export const connectSchema = v.object({
...rpcRequestMessageSchema.entries,
...v.object({
method: v.literal(connectMethodName),
params: connectParamsSchema,
id: v.string(),
}).entries,
});
export type Connect = MethodParamsAndResult<
v.InferOutput<typeof connectParamsSchema>,
v.InferOutput<typeof connectResultSchema>
>;

export const disconnectMethodName = 'wallet_disconnect';
export const disconnectParamsSchema = v.undefined();
export const disconnectResultSchema = v.undefined();
export const disconnectSchema = v.object({
...rpcRequestMessageSchema.entries,
...v.object({
method: v.literal(disconnectMethodName),
params: disconnectParamsSchema,
id: v.string(),
}).entries,
});
export type Disconnect = MethodParamsAndResult<
v.InferOutput<typeof disconnectParamsSchema>,
v.InferOutput<typeof disconnectResultSchema>
>;
20 changes: 20 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as v from 'valibot';
import type { BitcoinProvider } from './provider';
import { Requests, Return } from './request';

Expand Down Expand Up @@ -25,6 +26,25 @@ export interface RequestOptions<Payload extends RequestPayload, Response> {

// RPC Request and Response types

export const rpcRequestMessageSchema = v.object({
jsonrpc: v.literal('2.0'),
method: v.string(),
params: v.optional(
v.union([
v.array(v.unknown()),
v.looseObject({}),
// Note: This is to support current incorrect usage of RPC 2.0. Params need
// to be either an array or an object when provided. Changing this now would
// be a breaking change, so accepting null values for now. Tracking in
// https://linear.app/xverseapp/issue/ENG-4538.
v.null(),
])
),
id: v.optional(v.union([v.string(), v.number(), v.null()])),
});

export type RpcRequestMessage = v.InferOutput<typeof rpcRequestMessageSchema>;

export type RpcId = string | null;

export interface RpcBase {
Expand Down