@bigmi/client
Version:
Reactive primitives for Bitcoin apps.
128 lines (127 loc) • 5.83 kB
JavaScript
import { ConnectorChainIdDetectionError } from "../errors/connectors.js";
import { createConnector } from "../factories/createConnector.js";
import { MethodNotSupportedRpcError, ProviderNotFoundError, UserRejectedRequestError, getAddressChainId } from "@bigmi/core";
//#region src/connectors/leather.ts
function leather(parameters = {}) {
const { chainId, shimDisconnect = true } = parameters;
return createConnector((config) => ({
id: "LeatherProvider",
name: "Leather",
type: leather.type,
icon: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiBmaWxsPSJub25lIj4KICAgIDxwYXRoIGZpbGw9IiMxMjEwMEYiIGQ9Ik0wIDBoMTI4djEyOEgweiIvPgogICAgPHBhdGggZmlsbD0iI0Y1RjFFRCIgZD0iTTc0LjkxNyA1Mi43MTFjNy41Ni0xLjE3IDE4LjQ5Mi05LjEzIDE4LjQ5Mi0xNS4zMzUgMC0xLjg3My0xLjUxMi0zLjE2LTMuNzIyLTMuMTYtNC4xODcgMC0xMS4yOCA2LjMyLTE0Ljc3IDE4LjQ5NU0zOS45MTEgODMuNWMtOS44ODUgMC0xMC43IDkuODMzLS44MTQgOS44MzMgNC40MiAwIDkuNzctMS43NTYgMTIuNTYtNC45MTYtNC4wNy0zLjUxMi03LjQ0My00LjkxNy0xMS43NDYtNC45MTdtNjIuOTE4LTQuMjE0Yy41ODEgMTYuNTA2LTcuNzkyIDI1Ljc1NC0yMS45OCAyNS43NTQtOC4zNzQgMC0xMi41Ni0zLjE2MS0yMS41MTYtOS4wMTQtNC42NTIgNS4xNTEtMTMuNDkgOS4wMTQtMjAuODE4IDkuMDE0LTI1LjIzNiAwLTI0LjE5LTMyLjE5MyAxLjUxMi0zMi4xOTMgNS4zNSAwIDkuODg2IDEuNDA1IDE1LjcgNS4wMzRsMy44MzktMTMuNDYyQzQzLjc0OSA2MC4wODYgMzUuODQgNDcuOTEyIDQzLjYzMyAzMC40NjloMTIuNTZjLTYuOTc4IDExLjU5LTIuMjEgMjEuMTg5IDYuNjI5IDIyLjI0MkM2Ny41OSAzNS43MzcgNzcuODI1IDIyLjUxIDkxLjQzMiAyMi41MWM3LjY3NSAwIDEzLjcyMyA1LjAzNCAxMy43MjMgMTQuMTY1IDAgMTQuNjMzLTE5LjA3MyAyNi41NzMtMzMuNDk0IDI3Ljc0NEw2NS43MyA4NS4zNzJjNi43NDUgNy44NDMgMjUuNDY5IDE1LjQ1MiAyNS40NjktNi4wODd6Ii8+Cjwvc3ZnPg==",
async setup() {},
async getInternalProvider() {
if (typeof window === "undefined") return;
if ("LeatherProvider" in window) return window.LeatherProvider;
},
async getProvider() {
const internalProvider = await this.getInternalProvider();
if (!internalProvider) return;
return { request: this.request.bind(internalProvider) };
},
async request({ method, params }) {
switch (method) {
case "signPsbt": {
const { psbt, ...options } = params;
const signedPsbt = await this.request("signPsbt", {
hex: psbt,
broadcast: options.finalize
});
if (signedPsbt?.error) throw signedPsbt?.error;
if (!signedPsbt?.result?.hex) throw new Error("Missing hex result from signed PSBT");
return signedPsbt.result.hex;
}
default: throw new MethodNotSupportedRpcError(method);
}
},
async connect() {
if (!await this.getInternalProvider()) throw new ProviderNotFoundError();
try {
const accounts = await this.getAccounts();
const chainId = getAddressChainId(accounts[0].address);
if (shimDisconnect) await Promise.all([
config.storage?.setItem(`${this.id}.connected`, true),
config.storage?.setItem(`${this.id}.lastConnected`, Date.now().toString()),
config.storage?.removeItem(`${this.id}.disconnected`)
]);
return {
accounts,
chainId
};
} catch (error) {
throw new UserRejectedRequestError(error.message);
}
},
async disconnect() {
if (!await this.getInternalProvider()) throw new ProviderNotFoundError();
if (shimDisconnect) await Promise.all([
config.storage?.setItem(`${this.id}.disconnected`, true),
config.storage?.removeItem(`${this.id}.connected`),
config.storage?.removeItem(`${this.id}.accounts`),
config.storage?.removeItem(`${this.id}.lastConnected`)
]);
},
async getAccounts() {
const provider = await this.getInternalProvider();
if (!provider) throw new ProviderNotFoundError();
if (shimDisconnect && await this.isAuthorized()) {
const accounts = await config.storage?.getItem(`${this.id}.accounts`);
if (accounts && Array.isArray(accounts) && accounts.length > 0) return accounts;
}
const accounts = await provider.request("getAddresses");
if (!accounts.result) throw new UserRejectedRequestError(accounts.error?.message ?? "Unknown error");
if (shimDisconnect) await config.storage?.setItem(`${this.id}.accounts`, accounts.result.addresses);
return accounts.result.addresses;
},
async getChainId() {
if (chainId) return chainId;
const accounts = await this.getAccounts();
if (accounts.length === 0) throw new ConnectorChainIdDetectionError({ connector: this.name });
return getAddressChainId(accounts[0].address);
},
async isAuthorized() {
try {
if (!shimDisconnect) return false;
const lastConnected = await config.storage?.getItem(`${this.id}.lastConnected`);
if (!lastConnected) return false;
const timestamp = parseInt(lastConnected, 10);
if (Number.isNaN(timestamp) || timestamp <= 0) {
await Promise.all([
config.storage?.setItem(`${this.id}.disconnected`, true),
config.storage?.removeItem(`${this.id}.connected`),
config.storage?.removeItem(`${this.id}.accounts`),
config.storage?.removeItem(`${this.id}.lastConnected`)
]);
return false;
}
const isExpired = Date.now() - timestamp > 1440 * 60 * 1e3;
if (isExpired) {
await Promise.all([
config.storage?.setItem(`${this.id}.disconnected`, true),
config.storage?.removeItem(`${this.id}.connected`),
config.storage?.removeItem(`${this.id}.accounts`),
config.storage?.removeItem(`${this.id}.lastConnected`)
]);
return false;
}
return !isExpired;
} catch {
return false;
}
},
async onAccountsChanged() {
const { accounts } = await this.connect();
config.emitter.emit("change", { accounts });
},
onChainChanged(chainId) {
config.emitter.emit("change", { chainId });
},
async onDisconnect(_error) {
config.emitter.emit("disconnect");
}
}));
}
leather.type = "UTXO";
//#endregion
export { leather };
//# sourceMappingURL=leather.js.map