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

mahmoud/eng-3688-detecting-the-wallet-api-bitcoin-provider #63

Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
f6dd768
inject webbtc providersArray and export utils
m-aboelenein Feb 7, 2024
3f4588f
init listen util and update getProvider to return the provider object
m-aboelenein Feb 9, 2024
d3be775
init request method
m-aboelenein Feb 19, 2024
cc6476a
fix provider imports
m-aboelenein Feb 20, 2024
0df093d
export request method types
m-aboelenein Feb 20, 2024
d1ca5fa
Update Stacks request types
aryzing Feb 22, 2024
039f8d4
Update types
aryzing Feb 22, 2024
c433e3e
Add more stx request types
aryzing Feb 22, 2024
4edc8ab
Remove placeholder code
aryzing Feb 22, 2024
fc13128
Update Stacks types
aryzing Feb 23, 2024
5876741
init btc methods
m-aboelenein Feb 26, 2024
6fa19e9
Finalize btc request types
m-aboelenein Feb 26, 2024
aab4c9e
Simplify types
aryzing Feb 26, 2024
fe5ebb8
re-arrange request types
m-aboelenein Feb 26, 2024
5b88679
Add stx getAddress and getAccounts types
aryzing Feb 26, 2024
72123ad
Prepend Stx types to avoid naming collisions
aryzing Feb 26, 2024
52544c4
fix error type
m-aboelenein Feb 26, 2024
d9ce234
Set default type for Request
aryzing Feb 27, 2024
c503e1b
update signPsbt types and added success response type
m-aboelenein Feb 28, 2024
5df44d5
Fix type inferrence
aryzing Feb 29, 2024
cde3670
update rpc success response
m-aboelenein Feb 29, 2024
5ccafbd
updated btc methods types and added jsdocs
m-aboelenein Feb 29, 2024
8be8750
Added JSDocs for error types
m-aboelenein Mar 1, 2024
a217a6b
Construct Request type from request types for each network
aryzing Mar 1, 2024
bd411f8
Add type for method names
aryzing Mar 1, 2024
632d2f7
Set return and params to never for unknown methods
aryzing Mar 1, 2024
23c9461
Update contract call & deploy method names
aryzing Mar 4, 2024
42c5251
Start adding zod schemas
aryzing Mar 4, 2024
73d7f3a
Remove zod
aryzing Mar 4, 2024
a55b452
code review fixes
m-aboelenein Mar 4, 2024
ddc8fa4
Add exports
aryzing Mar 5, 2024
348e67c
remove listen request and update rpcid type
m-aboelenein Mar 6, 2024
74dd724
added response type check util
m-aboelenein Mar 6, 2024
0b16384
Proposed typing for the result (#75)
victorkirov Mar 8, 2024
79287a2
Merge branch 'develop' into mahmoud/eng-3688-detecting-the-wallet-api…
victorkirov Mar 8, 2024
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
25 changes: 0 additions & 25 deletions src/call/index.ts

This file was deleted.

10 changes: 0 additions & 10 deletions src/call/types.ts

This file was deleted.

3 changes: 2 additions & 1 deletion src/capabilities/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ const extractOrValidateCapabilities = (
};

const capabilityMap: CapabilityMap = {
call: validateCapability('call'),
request: validateCapability('request'),
listen: validateCapability('listen'),
connect: validateCapability('connect'),
signMessage: validateCapability('signMessage'),
signTransaction: validateCapability('signTransaction'),
Expand Down
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
export * from './addresses';
export * from './call';
export * from './request';
export * from './capabilities';
export * from './inscriptions';
export * from './messages';
export * from './provider';
export * from './transactions';
export * from './types';
export * from './listen';
15 changes: 15 additions & 0 deletions src/listen/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { getProviderOrThrow } from '../provider';

export const listen = async (method: string, options: Record<string, any>) => {
teebszet marked this conversation as resolved.
Show resolved Hide resolved
const provider = await getProviderOrThrow(options.getProvider);

if (!method) {
throw new Error('A wallet method is required');
}

try {
return provider.listen(method, options.callback);
} catch (error) {
console.error('[Connect] Error during listen request', error);
victorkirov marked this conversation as resolved.
Show resolved Hide resolved
}
};
20 changes: 18 additions & 2 deletions src/provider/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { BitcoinProvider } from './types';
import { type BitcoinProvider, type WebbtcProvider } from './types';

export async function getProviderOrThrow(
getProvider?: () => Promise<BitcoinProvider | undefined>
): Promise<BitcoinProvider> {
const provider = (await getProvider?.()) || window.XverseProviders?.BitcoinProvider || window.BitcoinProvider;
const provider =
(await getProvider?.()) || window.XverseProviders?.BitcoinProvider || window.BitcoinProvider;

if (!provider) {
throw new Error('No Bitcoin wallet installed');
Expand All @@ -12,4 +13,19 @@ export async function getProviderOrThrow(
return provider;
}

export function getProviders(): WebbtcProvider[] {
if (!window.webbtc_providers) window.webbtc_providers = [];
return window.webbtc_providers;
}

export function getProviderById(providerId: string) {
if (Array.isArray(window.webbtc_providers)) {
const provider = window.webbtc_providers.find((provider) => provider.id === providerId);
return provider?.id?.split('.').reduce((acc: any, part) => acc?.[part], window);
} else {
victorkirov marked this conversation as resolved.
Show resolved Hide resolved
console.error('window.webbtc_providers is not defined or not an array');
return null;
}
victorkirov marked this conversation as resolved.
Show resolved Hide resolved
}

export * from './types';
26 changes: 22 additions & 4 deletions src/provider/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Params, Requests } from '../request';
import type { GetAddressResponse } from '../addresses';
import type { CallWalletResponse } from '../call';
import type { GetCapabilitiesResponse } from '../capabilities';
import type { CreateInscriptionResponse, CreateRepeatInscriptionsResponse } from '../inscriptions';
import type { SignMessageResponse } from '../messages';
Expand All @@ -8,9 +8,15 @@ import type {
SignMultipleTransactionsResponse,
SignTransactionResponse,
} from '../transactions';
import { RpcResponse } from '../types';

interface BaseBitcoinProvider {
call: (request: string) => Promise<CallWalletResponse>;
request: <Method extends keyof Requests>(
method: Method,
options: Params<Method>,
providerId?: string
) => Promise<RpcResponse<Method>>;
listen: (method: string, callback: () => void) => void;
victorkirov marked this conversation as resolved.
Show resolved Hide resolved
connect: (request: string) => Promise<GetAddressResponse>;
signMessage: (request: string) => Promise<SignMessageResponse>;
signTransaction: (request: string) => Promise<SignTransactionResponse>;
Expand All @@ -26,13 +32,25 @@ export interface BitcoinProvider extends BaseBitcoinProvider {
getCapabilities?: (request: string) => Promise<GetCapabilitiesResponse>;
}

export interface WebbtcProvider {
id: string;
name: string;
icon: string;
webUrl?: string;
chromeWebStoreUrl?: string;
mozillaAddOnsUrl?: string;
googlePlayStoreUrl?: string;
iOSAppStoreUrl?: string;
methods?: string[];
}

declare global {
interface XverseProviders {
BitcoinProvider?: BitcoinProvider;
}

interface Window {
BitcoinProvider?: BitcoinProvider;
XverseProviders?: XverseProviders
XverseProviders?: XverseProviders;
webbtc_providers: WebbtcProvider[];
victorkirov marked this conversation as resolved.
Show resolved Hide resolved
}
}
24 changes: 24 additions & 0 deletions src/request/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { RpcResponse } from '../types';
import { getProviderById } from '../provider';
import { Params, Requests } from './types';

export const request = async <Method extends keyof Requests>(
method: Method,
params: Params<Method>,
providerId?: string
): Promise<RpcResponse<Method>> => {
let provider = window.XverseProviders?.BitcoinProvider;
victorkirov marked this conversation as resolved.
Show resolved Hide resolved
if (providerId) {
provider = await getProviderById(providerId);
}
if (!provider) {
throw new Error('no wallet provider was found');
}
if (!method) {
throw new Error('A wallet method is required');
}

return provider.request(method, params);
};

export * from './types';
125 changes: 125 additions & 0 deletions src/request/types/btcMethods.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/**
* Represents the types and interfaces related to BTC methods.
*/

import { Address, AddressPurpose } from '../../addresses';
import { MethodParamsAndResult } from '../../types';

type GetInfoResult = {
version: number | string;
methods?: Array<string>;
supports?: Array<string>;
};

export type GetInfo = MethodParamsAndResult<null, GetInfoResult>;

type GetAddressesParams = {
/**
* 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.
*/
message?: string;
};

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

export type GetAddresses = MethodParamsAndResult<GetAddressesParams, GetAddressesResult>;

type SignMessageParams = {
/**
* The address used for signing.
**/
address: string;
/**
* The message to sign.
**/
message: string;
};

type SignMessageResult = {
/**
* The signature of the message.
*/
signature: string;
/**
* hash of the message.
*/
messageHash: string;
/**
* The address used for signing.
*/
address: string;
};

export type SignMessage = MethodParamsAndResult<SignMessageParams, SignMessageResult>;

type Recipient = {
/**
* The recipient's address.
**/
address: string;
/**
* The amount to send to the recipient in satoshis.
*/
amount: number;
};

type SendTransferParams = {
/**
* Array of recipients to send to.
* The amount to send to each recipient is in satoshis.
*/
recipients: Array<Recipient>;
};
type SendTransferResult = {
/**
* The transaction id as a hex-encoded string.
*/
txid: string;
};

export type SendTransfer = MethodParamsAndResult<SendTransferParams, SendTransferResult>;

export type SignPsbtParams = {
/**
* The base64 encoded PSBT to sign.
*/
psbt: string;
/**
* The inputs to sign.
* The key is the address and the value is an array of indexes of the inputs to sign.
*/
signInputs: Record<string, number[]>;
/**
* the sigHash type to use for signing.
* will default to the sighash type of the input if not provided.
**/
allowedSignHash?: number;
/**
* Whether to broadcast the transaction after signing.
**/
broadcast?: boolean;
};

export type SignPsbtResult = {
/**
* The base64 encoded PSBT after signing.
*/
psbt: string;
/**
* The transaction id as a hex-encoded string.
* This is only returned if the transaction was broadcast.
**/
txid?: string;
};

export type SignPsbt = MethodParamsAndResult<SignPsbtParams, SignPsbtResult>;
48 changes: 48 additions & 0 deletions src/request/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { RpcSuccessResponse } from 'src/types';
import { GetAddresses, GetInfo, SendTransfer, SignMessage, SignPsbt } from './btcMethods';
import {
StxContractCall,
StxContractDeploy,
StxGetAccounts,
StxSignStructuredMessage,
StxSignStxMessage,
StxSignTransaction,
StxTransferStx,
StxGetAddresses,
} from './stxMethods';

export interface StxRequests {
stx_contractCall: StxContractCall;
stx_transferStx: StxTransferStx;
stx_signMessage: StxSignStxMessage;
stx_signStructuredMessage: StxSignStructuredMessage;
stx_contractDeploy: StxContractDeploy;
stx_signTransaction: StxSignTransaction;
stx_getAccounts: StxGetAccounts;
stx_getAddresses: StxGetAddresses;
}

export type StxRequestMethod = keyof StxRequests;

export interface BtcRequests {
getInfo: GetInfo;
getAddresses: GetAddresses;
signMessage: SignMessage;
sendTransfer: SendTransfer;
signPsbt: SignPsbt;
}

export type BtcRequestMethod = keyof BtcRequests;
teebszet marked this conversation as resolved.
Show resolved Hide resolved

export type Requests = BtcRequests & StxRequests;

export type Return<Method> = Method extends keyof Requests ? Requests[Method]['result'] : unknown;
export type Params<Method> = Method extends keyof Requests ? Requests[Method]['params'] : unknown;
aryzing marked this conversation as resolved.
Show resolved Hide resolved

export type Request<Method extends keyof Requests> = (
requestMethod: Method,
param?: Params<Method>
) => Promise<RpcSuccessResponse<Method>>;
victorkirov marked this conversation as resolved.
Show resolved Hide resolved

export * from './stxMethods';
export * from './btcMethods';
Loading
Loading