From 0ce85693dd7e56c6c77007624949375e44b55bc9 Mon Sep 17 00:00:00 2001 From: Den <36603049+dhriaznov@users.noreply.github.com> Date: Wed, 3 Jul 2024 16:03:56 +0200 Subject: [PATCH] Revert "Revert "Add message schema validators"" (#23) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add "build:watch" script * Add getBalance method schema * Add access denied error code * Switch to bigint * Add getBalance to BtcRequests * Add RPC response schemas * Update getBalance result schema * Set result and error to non-optional * Update types * Use string instead of bigint * Rename method names * Fix method name * added getInscriptions request * remove comments * Use same address definition for Stacks * Support nullish param * Add more types * Revert "Revert "Add message schema validators"" --------- Co-authored-by: Eduard Bardají Puig Co-authored-by: Mahmoud Aboelenein --- package-lock.json | 31 +++-- package.json | 6 +- src/addresses/types.ts | 14 +- src/provider/types.ts | 3 +- src/request/index.ts | 33 +++-- src/request/types/btcMethods.ts | 190 ++++++++++++++++++++------- src/request/types/index.ts | 94 ++++++------- src/request/types/ordinalsMethods.ts | 38 ++++++ src/request/types/runesMethods.ts | 45 +++++-- src/request/types/stxMethods.ts | 48 +++++-- src/request/types/walletMethods.ts | 34 +++++ src/types.ts | 44 ++++++- 12 files changed, 428 insertions(+), 152 deletions(-) create mode 100644 src/request/types/ordinalsMethods.ts create mode 100644 src/request/types/walletMethods.ts diff --git a/package-lock.json b/package-lock.json index 898ffaa..26c35fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,11 +27,14 @@ "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", "webpack-cli": "^4.10.0" + }, + "peerDependencies": { + "valibot": "0.33.2" } }, "node_modules/@ampproject/remapping": { @@ -6794,16 +6797,16 @@ } }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/update-browserslist-db": { @@ -6882,6 +6885,12 @@ "dev": true, "peer": true }, + "node_modules/valibot": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.33.2.tgz", + "integrity": "sha512-ZpFWuI+bs5+PP66q4zVFn4e4t/s5jmMw5iPBZmGUoi8iQqXyU9YY/BLCAyk62Z/bNS8qdUNBEyx52952qdqW3w==", + "peer": true + }, "node_modules/vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", @@ -12040,9 +12049,9 @@ "peer": true }, "typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true }, "update-browserslist-db": { @@ -12104,6 +12113,12 @@ } } }, + "valibot": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.33.2.tgz", + "integrity": "sha512-ZpFWuI+bs5+PP66q4zVFn4e4t/s5jmMw5iPBZmGUoi8iQqXyU9YY/BLCAyk62Z/bNS8qdUNBEyx52952qdqW3w==", + "peer": true + }, "vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", diff --git a/package.json b/package.json index 0e4da11..ce45c99 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "test": "jest", "build-debug": "webpack --mode development", "build": "npm run clean && tsup src/index.ts --format esm --dts", + "build:watch": "npm run clean && tsup src/index.ts --format esm --dts --watch", "clean": "rimraf dist", "lint": "prettier --write .", "prepare": "husky install" @@ -30,6 +31,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", @@ -42,7 +46,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", diff --git a/src/addresses/types.ts b/src/addresses/types.ts index 5b18e18..7501e01 100644 --- a/src/addresses/types.ts +++ b/src/addresses/types.ts @@ -1,3 +1,4 @@ +import * as v from 'valibot'; import type { RequestOptions, RequestPayload } from '../types'; export enum AddressPurpose { @@ -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; export interface GetAddressResponse { addresses: Address[]; diff --git a/src/provider/types.ts b/src/provider/types.ts index 2e24076..376d191 100644 --- a/src/provider/types.ts +++ b/src/provider/types.ts @@ -1,5 +1,6 @@ import { BtcRequestMethod, + OrdinalsRequestMethod, Params, Requests, RunesRequestMethod, @@ -46,7 +47,7 @@ export interface Provider { mozillaAddOnsUrl?: string; googlePlayStoreUrl?: string; iOSAppStoreUrl?: string; - methods?: (StxRequestMethod | BtcRequestMethod | RunesRequestMethod)[]; + methods?: (StxRequestMethod | BtcRequestMethod | RunesRequestMethod | OrdinalsRequestMethod)[]; } export interface SupportedWallet extends Provider { diff --git a/src/request/index.ts b/src/request/index.ts index c901dd6..708f704 100644 --- a/src/request/index.ts +++ b/src/request/index.ts @@ -1,6 +1,12 @@ import { getProviderById } from '../provider'; -import { RpcBase, RpcResult, RpcSuccessResponse } from '../types'; -import { Params, Requests } from './types'; +import { + RpcErrorCode, + RpcResult, + rpcErrorResponseMessageSchema, + rpcSuccessResponseMessageSchema, +} from '../types'; +import * as v from 'valibot'; +import { Params, Requests, Return } from './types'; export const request = async ( method: Method, @@ -20,23 +26,28 @@ export const request = async ( const response = await provider.request(method, params); - if (isRpcSuccessResponse(response)) { + if (v.is(rpcErrorResponseMessageSchema, response)) { + return { + status: 'error', + error: response.error, + }; + } + + if (v.is(rpcSuccessResponseMessageSchema, response)) { return { status: 'success', - result: response.result, + result: response.result as Return, }; } return { status: 'error', - error: response.error, + error: { + code: RpcErrorCode.INTERNAL_ERROR, + message: 'Received unknown response from provider.', + data: response, + }, }; }; -const isRpcSuccessResponse = ( - response: RpcBase -): response is RpcSuccessResponse => { - return Object.hasOwn(response, 'result') && !!(response as RpcSuccessResponse).result; -}; - export * from './types'; diff --git a/src/request/types/btcMethods.ts b/src/request/types/btcMethods.ts index 7499985..53ec675 100644 --- a/src/request/types/btcMethods.ts +++ b/src/request/types/btcMethods.ts @@ -2,65 +2,118 @@ * 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; - supports?: Array; -}; +export const getInfoMethodName = 'getInfo'; +export const getInfoParamsSchema = v.nullish(v.null()); +export type GetInfoParams = v.InferOutput; +export const getInfoResultSchema = v.object({ + /** + * Version of the wallet. + */ + version: v.string(), -export type GetInfo = MethodParamsAndResult; + /** + * [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 type GetInfoResult = v.InferOutput; +export const getInfoRequestMessageSchema = v.object({ + ...rpcRequestMessageSchema.entries, + ...v.object({ + method: v.literal(getInfoMethodName), + params: getInfoParamsSchema, + id: v.string(), + }).entries, +}); +export type GetInfoRequestMessage = v.InferOutput; +export type GetInfo = MethodParamsAndResult< + v.InferOutput, + v.InferOutput +>; + +export const getAddressesMethodName = 'getAddresses'; +export const getAddressesParamsSchema = v.object({ + /** + * The purposes for which to generate addresses. See + * {@linkcode AddressPurpose} for available purposes. */ - purposes: Array; + 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
; -}; - -export type GetAddresses = MethodParamsAndResult; - -export type SignMessageParams = { + message: v.optional(v.string()), +}); +export type GetAddressesParams = v.InferOutput; +export const getAddressesResultSchema = v.object({ + /** + * The addresses generated for the given purposes. + */ + addresses: v.array(addressSchema), +}); +export type GetAddressesResult = v.InferOutput; +export const getAddressesRequestMessageSchema = v.object({ + ...rpcRequestMessageSchema.entries, + ...v.object({ + method: v.literal(getAddressesMethodName), + params: getAddressesParamsSchema, + id: v.string(), + }).entries, +}); +export type GetAddressesRequestMessage = v.InferOutput; +export type GetAddresses = MethodParamsAndResult< + v.InferOutput, + v.InferOutput +>; + +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 type SignMessageParams = v.InferOutput; +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; + address: v.string(), +}); +export type SignMessageResult = v.InferOutput; +export const signMessageRequestMessageSchema = v.object({ + ...rpcRequestMessageSchema.entries, + ...v.object({ + method: v.literal(signMessageMethodName), + params: signMessageParamsSchema, + id: v.string(), + }).entries, +}); +export type SignMessageRequestMessage = v.InferOutput; +export type SignMessage = MethodParamsAndResult< + v.InferOutput, + v.InferOutput +>; type Recipient = { /** @@ -124,21 +177,58 @@ export type SignPsbtResult = { export type SignPsbt = MethodParamsAndResult; -export type GetAccountsParams = { - /** - * The purposes for which to generate addresses. - * possible values are "payment", "ordinals", ... +export const getAccountsMethodName = 'getAccounts'; +export const getAccountsParamsSchema = getAddressesParamsSchema; +export type GetAccountsParams = v.InferOutput; +export const getAccountsResultSchema = v.array(addressSchema); +export type GetAccountsResult = v.InferOutput; +export const getAccountsRequestMessageSchema = v.object({ + ...rpcRequestMessageSchema.entries, + ...v.object({ + method: v.literal(getAccountsMethodName), + params: getAccountsParamsSchema, + id: v.string(), + }).entries, +}); +export type GetAccountsRequestMessage = v.InferOutput; +export type GetAccounts = MethodParamsAndResult< + v.InferOutput, + v.InferOutput +>; + +// Get the balance of the current Bitcoin account. +export const getBalanceMethodName = 'getBalance'; +export const getBalanceParamsSchema = v.nullish(v.null()); +export const getBalanceResultSchema = v.object({ + /** + * The confirmed balance of the wallet in sats. Using a string due to chrome + * messages not supporting bigint + * (https://issues.chromium.org/issues/40116184). */ - purposes: Array; + confirmed: v.string(), + /** - * a message to be displayed to the user in the request prompt. + * The unconfirmed balance of the wallet in sats. Using a string due to chrome + * messages not supporting bigint + * (https://issues.chromium.org/issues/40116184). */ + unconfirmed: v.string(), + /** - * a message to be displayed to the user in the request prompt. + * The total balance (both confirmed and unconfrimed UTXOs) of the wallet in + * sats. Using a string due to chrome messages not supporting bigint + * (https://issues.chromium.org/issues/40116184). */ - message?: string; -}; - -export type GetAccountResult = Address[]; - -export type GetAccounts = MethodParamsAndResult; + total: v.string(), +}); +export const getBalanceRequestMessageSchema = v.object({ + ...rpcRequestMessageSchema.entries, + ...v.object({ + method: v.literal(getBalanceMethodName), + id: v.string(), + }).entries, +}); +export type GetBalance = MethodParamsAndResult< + v.InferOutput, + v.InferOutput +>; diff --git a/src/request/types/index.ts b/src/request/types/index.ts index 202feef..419e970 100644 --- a/src/request/types/index.ts +++ b/src/request/types/index.ts @@ -1,73 +1,65 @@ -import { - GetAccounts, - GetAddresses, - GetInfo, - SendTransfer, - SignMessage, - SignPsbt, -} from './btcMethods'; -import { - EstimateRbfOrder, - EstimateRunesEtch, - EstimateRunesMint, - EtchRunes, - GetOrder, - GetRunesBalance, - MintRunes, - RbfOrder, -} from './runesMethods'; -import { - StxCallContract, - StxDeployContract, - StxGetAccounts, - StxGetAddresses, - StxSignStructuredMessage, - StxSignStxMessage, - StxSignTransaction, - StxTransferStx, -} from './stxMethods'; +import type * as BtcMethods from './btcMethods'; +import { GetInscriptions } from './ordinalsMethods'; +import type * as RunesMethods from './runesMethods'; +import type * as StxMethods from './stxMethods'; +import { RequestPermissions, RenouncePermissions } from './walletMethods'; export interface StxRequests { - stx_callContract: StxCallContract; - stx_deployContract: StxDeployContract; - stx_getAccounts: StxGetAccounts; - stx_getAddresses: StxGetAddresses; - stx_signMessage: StxSignStxMessage; - stx_signStructuredMessage: StxSignStructuredMessage; - stx_signTransaction: StxSignTransaction; - stx_transferStx: StxTransferStx; + stx_callContract: StxMethods.StxCallContract; + stx_deployContract: StxMethods.StxDeployContract; + stx_getAccounts: StxMethods.StxGetAccounts; + stx_getAddresses: StxMethods.StxGetAddresses; + stx_signMessage: StxMethods.StxSignStxMessage; + stx_signStructuredMessage: StxMethods.StxSignStructuredMessage; + stx_signTransaction: StxMethods.StxSignTransaction; + stx_transferStx: StxMethods.StxTransferStx; } export type StxRequestMethod = keyof StxRequests; export interface BtcRequests { - getInfo: GetInfo; - getAddresses: GetAddresses; - getAccounts: GetAccounts; - signMessage: SignMessage; - sendTransfer: SendTransfer; - signPsbt: SignPsbt; + getInfo: BtcMethods.GetInfo; + getAddresses: BtcMethods.GetAddresses; + getAccounts: BtcMethods.GetAccounts; + getBalance: BtcMethods.GetBalance; + signMessage: BtcMethods.SignMessage; + sendTransfer: BtcMethods.SendTransfer; + signPsbt: BtcMethods.SignPsbt; } export type BtcRequestMethod = keyof BtcRequests; export interface RunesRequests { - runes_estimateMint: EstimateRunesMint; - runes_mint: MintRunes; - runes_estimateEtch: EstimateRunesEtch; - runes_etch: EtchRunes; - runes_getOrder: GetOrder; - runes_estimateRbfOrder: EstimateRbfOrder; - runes_rbfOrder: RbfOrder; - runes_getBalance: GetRunesBalance; + runes_estimateMint: RunesMethods.EstimateRunesMint; + runes_mint: RunesMethods.MintRunes; + runes_estimateEtch: RunesMethods.EstimateRunesEtch; + runes_etch: RunesMethods.EtchRunes; + runes_getOrder: RunesMethods.GetOrder; + runes_estimateRbfOrder: RunesMethods.EstimateRbfOrder; + runes_rbfOrder: RunesMethods.RbfOrder; + runes_getBalance: RunesMethods.GetRunesBalance; } export type RunesRequestMethod = keyof RunesRequests; -export type Requests = BtcRequests & StxRequests & RunesRequests; +export interface OrdinalsRequests { + ord_getInscriptions: GetInscriptions; +} + +export type OrdinalsRequestMethod = keyof OrdinalsRequests; + +export interface WalletMethods { + wallet_requestPermissions: RequestPermissions; + wallet_renouncePermissions: RenouncePermissions; +} + +export type Requests = BtcRequests & StxRequests & RunesRequests & WalletMethods & OrdinalsRequests; export type Return = Method extends keyof Requests ? Requests[Method]['result'] : never; export type Params = Method extends keyof Requests ? Requests[Method]['params'] : never; export * from './stxMethods'; export * from './btcMethods'; +export * from './walletMethods'; +export * from './runesMethods'; +export * from './ordinalsMethods'; diff --git a/src/request/types/ordinalsMethods.ts b/src/request/types/ordinalsMethods.ts new file mode 100644 index 0000000..f75461f --- /dev/null +++ b/src/request/types/ordinalsMethods.ts @@ -0,0 +1,38 @@ +import { MethodParamsAndResult, rpcRequestMessageSchema } from '../../types'; +import * as v from 'valibot'; + +export const getInscriptionsMethodName = 'ord_getInscriptions'; +export const getInscriptionsParamsSchema = v.object({ + offset: v.number(), + limit: v.number(), +}); +export const getInscriptionsResultSchema = v.object({ + inscriptions: v.array( + v.object({ + inscriptionId: v.string(), + inscriptionNumber: v.string(), + address: v.string(), + collectionName: v.optional(v.string()), + postage: v.string(), + contentLength: v.string(), + contentType: v.string(), + timestamp: v.number(), + offset: v.number(), + genesisTransaction: v.string(), + output: v.string(), + }) + ), +}); +export const getInscriptionsSchema = v.object({ + ...rpcRequestMessageSchema.entries, + ...v.object({ + method: v.literal(getInscriptionsMethodName), + params: getInscriptionsParamsSchema, + id: v.string(), + }).entries, +}); + +export type GetInscriptions = MethodParamsAndResult< + v.InferOutput, + v.InferOutput +>; diff --git a/src/request/types/runesMethods.ts b/src/request/types/runesMethods.ts index 6103d21..91061e6 100644 --- a/src/request/types/runesMethods.ts +++ b/src/request/types/runesMethods.ts @@ -9,7 +9,8 @@ import { RBFOrderRequest, RBFOrderResponse, } from '../../runes/types'; -import { BitcoinNetworkType, MethodParamsAndResult } from '../../types'; +import { BitcoinNetworkType, MethodParamsAndResult, rpcRequestMessageSchema } from '../../types'; +import * as v from 'valibot'; export interface EstimateRunesMintParams extends EstimateMintOrderRequest { network?: BitcoinNetworkType; @@ -81,15 +82,33 @@ interface RbfOrderResult { export type RbfOrder = MethodParamsAndResult; -type GetRunesBalanceParams = null; -interface GetRunesBalanceResult { - balances: { - runeName: string; - amount: string; - divisibility: number; - symbol: string; - inscriptionId: string | null; - }[]; -} - -export type GetRunesBalance = MethodParamsAndResult; +export const getRunesBalanceMethodName = 'runes_getBalance'; +export const getRunesBalanceParamsSchema = v.nullish(v.null()); +export type GetRunesBalanceParams = v.InferOutput; +export const getRunesBalanceResultSchema = v.object({ + balances: v.array( + v.object({ + runeName: v.string(), + amount: v.string(), + divisibility: v.number(), + symbol: v.string(), + inscriptionId: v.nullish(v.string()), + }) + ), +}); +export type GetRunesBalanceResult = v.InferOutput; +export const getRunesBalanceRequestMessageSchema = v.object({ + ...rpcRequestMessageSchema.entries, + ...v.object({ + method: v.literal(getRunesBalanceMethodName), + params: getRunesBalanceParamsSchema, + id: v.string(), + }).entries, +}); +export type GetRunesBalanceRequestMessage = v.InferOutput< + typeof getRunesBalanceRequestMessageSchema +>; +export type GetRunesBalance = MethodParamsAndResult< + v.InferOutput, + v.InferOutput +>; diff --git a/src/request/types/stxMethods.ts b/src/request/types/stxMethods.ts index 2f31186..2309d99 100644 --- a/src/request/types/stxMethods.ts +++ b/src/request/types/stxMethods.ts @@ -1,4 +1,6 @@ -import { MethodParamsAndResult } from '../../types'; +import { addressSchema } from 'src/addresses'; +import { MethodParamsAndResult, rpcRequestMessageSchema } from '../../types'; +import * as v from 'valibot'; interface Pubkey { /** @@ -247,17 +249,43 @@ export type DeployContractResult = TxId & Transaction; export type StxDeployContract = MethodParamsAndResult; // Types for `stx_getAccounts` request -export type GetAccountsResult = { +export type StxGetAccountsResult = { addresses: Array
; }; -export type StxGetAccounts = MethodParamsAndResult<{}, GetAccountsResult>; - -// Types for `stx_getAddresses` request -export type GetAddressesParams = undefined | null; -export type GetAddressesResult = { - addresses: Array
; -}; -export type StxGetAddresses = MethodParamsAndResult; +export type StxGetAccounts = MethodParamsAndResult<{}, StxGetAccountsResult>; + +export const stxGetAddressesMethodName = 'stx_getAddresses'; +export const stxGetAddressesParamsSchema = v.nullish( + v.object({ + /** + * A message to be displayed to the user in the request prompt. + */ + message: v.optional(v.string()), + }) +); +export type StxGetAddressesParams = v.InferOutput; +export const stxGetAddressesResultSchema = v.object({ + /** + * The addresses generated for the given purposes. + */ + addresses: v.array(addressSchema), +}); +export type StxGetAddressesResult = v.InferOutput; +export const stxGetAddressesRequestMessageSchema = v.object({ + ...rpcRequestMessageSchema.entries, + ...v.object({ + method: v.literal(stxGetAddressesMethodName), + params: stxGetAddressesParamsSchema, + id: v.string(), + }).entries, +}); +export type StxGetAddressesRequestMessage = v.InferOutput< + typeof stxGetAddressesRequestMessageSchema +>; +export type StxGetAddresses = MethodParamsAndResult< + v.InferOutput, + v.InferOutput +>; // Types for `stx_signTransaction` request export type SignTransactionParams = Transaction & Partial; diff --git a/src/request/types/walletMethods.ts b/src/request/types/walletMethods.ts new file mode 100644 index 0000000..5fffc50 --- /dev/null +++ b/src/request/types/walletMethods.ts @@ -0,0 +1,34 @@ +import { MethodParamsAndResult, rpcRequestMessageSchema } from '../../types'; +import * as v from 'valibot'; + +export const requestPermissionsMethodName = 'wallet_requestPermissions'; +export const requestPermissionsParamsSchema = v.undefined(); +export const requestPermissionsResultSchema = v.literal(true); +export const requestPermissionsRequestMessageSchema = v.object({ + ...rpcRequestMessageSchema.entries, + ...v.object({ + method: v.literal(requestPermissionsMethodName), + params: requestPermissionsParamsSchema, + id: v.string(), + }).entries, +}); +export type RequestPermissions = MethodParamsAndResult< + v.InferOutput, + v.InferOutput +>; + +export const renouncePermissionsMethodName = 'wallet_renouncePermissions'; +export const renouncePermissionsParamsSchema = v.undefined(); +export const renouncePermissionsResultSchema = v.literal(true); +export const renouncePermissionsRequestMessageSchema = v.object({ + ...rpcRequestMessageSchema.entries, + ...v.object({ + method: v.literal(renouncePermissionsMethodName), + params: renouncePermissionsParamsSchema, + id: v.string(), + }).entries, +}); +export type RenouncePermissions = MethodParamsAndResult< + v.InferOutput, + v.InferOutput +>; diff --git a/src/types.ts b/src/types.ts index 2b97a2e..176487f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,3 +1,4 @@ +import * as v from 'valibot'; import type { BitcoinProvider } from './provider'; import { Requests, Return } from './request'; @@ -25,7 +26,25 @@ export interface RequestOptions { // RPC Request and Response types -export type RpcId = string | null; +export const RpcIdSchema = v.optional(v.union([v.string(), v.number(), v.null()])); +export type RpcId = v.InferOutput; +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: RpcIdSchema, +}); +export type RpcRequestMessage = v.InferOutput; export interface RpcBase { jsonrpc: '2.0'; @@ -76,8 +95,31 @@ export enum RpcErrorCode { * method is not supported for the address provided */ METHOD_NOT_SUPPORTED = -32001, + /** + * The client does not have permission to access the requested resource. + */ + ACCESS_DENIED = -32002, } +export const rpcSuccessResponseMessageSchema = v.object({ + jsonrpc: v.literal('2.0'), + result: v.nonOptional(v.unknown()), + id: RpcIdSchema, +}); +export type RpcSuccessResponseMessage = v.InferOutput; + +export const rpcErrorResponseMessageSchema = v.object({ + jsonrpc: v.literal('2.0'), + error: v.nonOptional(v.unknown()), + id: RpcIdSchema, +}); +export type RpcErrorResponseMessage = v.InferOutput; +export const rpcResponseMessageSchema = v.union([ + rpcSuccessResponseMessageSchema, + rpcErrorResponseMessageSchema, +]); +export type RpcResponseMessage = v.InferOutput; + export interface RpcError { code: number | RpcErrorCode; message: string;