@bigmi/client
Version:
Reactive primitives for Bitcoin apps.
148 lines • 6.87 kB
JavaScript
import { MethodNotSupportedRpcError, ProviderNotFoundError, UserRejectedRequestError, base64ToHex, hexToBase64, } from '@bigmi/core';
import { createConnector } from '../factories/createConnector.js';
xverse.type = 'UTXO';
export function xverse(parameters = {}) {
const { chainId, shimDisconnect = true } = parameters;
let accountChange;
return createConnector((config) => ({
id: 'XverseProviders.BitcoinProvider',
name: 'Xverse Wallet',
type: xverse.type,
icon: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MDAiIGhlaWdodD0iNjAwIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGZpbGw9IiMxNzE3MTciIGQ9Ik0wIDBoNjAwdjYwMEgweiIvPjxwYXRoIGZpbGw9IiNGRkYiIGZpbGwtcnVsZT0ibm9uemVybyIgZD0iTTQ0MCA0MzUuNHYtNTFjMC0yLS44LTMuOS0yLjItNS4zTDIyMCAxNjIuMmE3LjYgNy42IDAgMCAwLTUuNC0yLjJoLTUxLjFjLTIuNSAwLTQuNiAyLTQuNiA0LjZ2NDcuM2MwIDIgLjggNCAyLjIgNS40bDc4LjIgNzcuOGE0LjYgNC42IDAgMCAxIDAgNi41bC03OSA3OC43Yy0xIC45LTEuNCAyLTEuNCAzLjJ2NTJjMCAyLjQgMiA0LjUgNC42IDQuNUgyNDljMi42IDAgNC42LTIgNC42LTQuNlY0MDVjMC0xLjIuNS0yLjQgMS40LTMuM2w0Mi40LTQyLjJhNC42IDQuNiAwIDAgMSA2LjQgMGw3OC43IDc4LjRhNy42IDcuNiAwIDAgMCA1LjQgMi4yaDQ3LjVjMi41IDAgNC42LTIgNC42LTQuNloiLz48cGF0aCBmaWxsPSIjRUU3QTMwIiBmaWxsLXJ1bGU9Im5vbnplcm8iIGQ9Ik0zMjUuNiAyMjcuMmg0Mi44YzIuNiAwIDQuNiAyLjEgNC42IDQuNnY0Mi42YzAgNCA1IDYuMSA4IDMuMmw1OC43LTU4LjVjLjgtLjggMS4zLTIgMS4zLTMuMnYtNTEuMmMwLTIuNi0yLTQuNi00LjYtNC42TDM4NCAxNjBjLTEuMiAwLTIuNC41LTMuMyAxLjNsLTU4LjQgNTguMWE0LjYgNC42IDAgMCAwIDMuMiA3LjhaIi8+PC9nPjwvc3ZnPg==',
async setup() {
//
},
async getInternalProvider() {
if (typeof window === 'undefined') {
return undefined;
}
if ('XverseProviders' in window) {
const anyWindow = window;
const provider = anyWindow.XverseProviders.BitcoinProvider;
return provider;
}
},
async getProvider() {
const internalProvider = await this.getInternalProvider();
if (!internalProvider) {
return;
}
const provider = {
request: this.request.bind(internalProvider),
};
return provider;
},
async request({ method, params }) {
switch (method) {
case 'signPsbt': {
const { psbt, ...options } = params;
const psbtBase64 = hexToBase64(psbt);
const signInputs = options.inputsToSign.reduce((signInputs, input) => {
if (!signInputs[input.address]) {
signInputs[input.address] = [];
}
signInputs[input.address].push(...input.signingIndexes);
return signInputs;
}, {});
const signedPsbt = await this.request('signPsbt', {
psbt: psbtBase64,
allowedSignHash: 1, // Default to Transaction.SIGHASH_ALL - 1
signInputs: signInputs,
broadcast: options.finalize,
});
if (signedPsbt?.error) {
throw signedPsbt?.error;
}
return base64ToHex(signedPsbt?.result?.psbt);
}
default:
throw new MethodNotSupportedRpcError(method);
}
},
async connect({ isReconnecting } = {}) {
const provider = await this.getInternalProvider();
if (!provider) {
throw new ProviderNotFoundError();
}
if (!isReconnecting) {
const connected = await provider.request('wallet_requestPermissions');
if (connected.error) {
throw new UserRejectedRequestError(connected.error.message);
}
}
const accounts = await this.getAccounts();
const chainId = await this.getChainId();
if (!accountChange) {
accountChange = this.onAccountsChanged.bind(this);
provider.addListener('accountChange', accountChange);
}
// Remove disconnected shim if it exists
if (shimDisconnect) {
await Promise.all([
config.storage?.setItem(`${this.id}.connected`, true),
config.storage?.removeItem(`${this.id}.disconnected`),
]);
}
return { accounts, chainId };
},
async disconnect() {
const provider = await this.getInternalProvider();
if (!provider) {
throw new ProviderNotFoundError();
}
if (accountChange) {
provider.removeListener?.('accountChange', accountChange);
accountChange = undefined;
}
// await provider.request('wallet_renouncePermissions').catch();
// Add shim signalling connector is disconnected
if (shimDisconnect) {
await Promise.all([
config.storage?.setItem(`${this.id}.disconnected`, true),
config.storage?.removeItem(`${this.id}.connected`),
]);
}
},
async getAccounts() {
const provider = await this.getInternalProvider();
if (!provider) {
throw new ProviderNotFoundError();
}
const accounts = await provider.request('getAddresses', {
purposes: ['payment'],
});
if (!accounts.result) {
throw new UserRejectedRequestError(accounts.error?.message);
}
return accounts.result.addresses.map((account) => account.address);
},
async getChainId() {
return chainId;
},
async isAuthorized() {
try {
const isConnected = shimDisconnect &&
// If shim exists in storage, connector is disconnected
Boolean(await config.storage?.getItem(`${this.id}.connected`));
return isConnected;
}
catch {
return false;
}
},
async onAccountsChanged() {
const { accounts } = await this.connect();
config.emitter.emit('change', {
accounts,
});
},
onChainChanged(chain) {
const chainId = Number(chain);
config.emitter.emit('change', { chainId });
},
async onDisconnect(_error) {
config.emitter.emit('disconnect');
},
}));
}
//# sourceMappingURL=xverse.js.map