@nfid/identitykit
Version:
A React library for adding wallet connections to dApps.
1,105 lines (1,060 loc) • 77.9 kB
JavaScript
'use client';
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
import { createContext, useState, useCallback, useMemo, useEffect, useContext, useRef } from 'react';
import { createContext as createContext$1, useContextSelector, useContext as useContext$1 } from 'use-context-selector';
import { SignerAgent } from '@slide-computer/signer-agent';
import { Principal } from '@dfinity/principal';
import { AnonymousIdentity, HttpAgent } from '@dfinity/agent';
import { Ed25519KeyIdentity, ECDSAKeyIdentity, isDelegationValid, PartialIdentity, PartialDelegationIdentity, DelegationIdentity, DelegationChain, Delegation } from '@dfinity/identity';
import { IdbStorage, getIdentity, getDelegationChain, setIdentity, setDelegationChain, removeIdentity, removeDelegationChain } from '@slide-computer/signer-storage';
import { SubAccount, LedgerCanister, AccountIdentifier } from '@dfinity/ledger-icp';
import { toBase64, fromBase64, Signer } from '@slide-computer/signer';
import { PostMessageTransport } from '@slide-computer/signer-web';
import { BrowserExtensionTransport } from '@slide-computer/signer-extension';
import { AuthClientTransport } from '@slide-computer/signer-transport-auth-client';
import { StoicTransport } from '@slide-computer/signer-transport-stoic';
import { useAsyncMemo } from 'use-async-memo';
import clsx from 'clsx';
import { MenuItems, Menu as Menu$1, MenuButton } from '@headlessui/react';
import colors from 'tailwindcss/colors';
import * as RadixTooltip from '@radix-ui/react-tooltip';
import { TooltipProvider } from '@radix-ui/react-tooltip';
import * as Dialog from '@radix-ui/react-dialog';
var IdentityKitTheme;
(function (IdentityKitTheme) {
IdentityKitTheme["LIGHT"] = "light";
IdentityKitTheme["DARK"] = "dark";
IdentityKitTheme["SYSTEM"] = "system";
})(IdentityKitTheme || (IdentityKitTheme = {}));
const DEFAULT_SIZES = {
width: 450,
height: 640,
};
const ThemeContext = createContext(IdentityKitTheme.SYSTEM);
const Context = createContext$1(null);
class Agent {
signerAgentStrategy;
agentStrategy;
delegation;
constructor(signerAgentStrategy, agentStrategy, delegation) {
this.signerAgentStrategy = signerAgentStrategy;
this.agentStrategy = agentStrategy;
this.delegation = delegation;
}
static async create({ delegation, signerAgent, agent }) {
return new Agent(signerAgent, agent, delegation);
}
async call(...params) {
const delegationTargets = this.delegation?.targets;
const strategy = this.delegation &&
(!delegationTargets?.length ||
delegationTargets?.find((t) => t.compareTo(Principal.from(params[0])) === "eq"))
? this.agentStrategy
: this.signerAgentStrategy;
return strategy.call(...params);
}
async query(...params) {
const delegationTargets = this.delegation?.targets;
const strategy = this.delegation &&
(!delegationTargets?.length ||
delegationTargets?.find((t) => t.compareTo(Principal.from(params[0])) === "eq"))
? this.agentStrategy
: this.signerAgentStrategy;
return strategy.query(...params);
}
get rootKey() {
return this.agentStrategy.rootKey;
}
async fetchRootKey() {
return this.agentStrategy.fetchRootKey();
}
async getPrincipal() {
return this.agentStrategy.getPrincipal();
}
async status() {
return this.agentStrategy.status();
}
async readState(...params) {
const delegationTargets = this.delegation?.targets;
const strategy = this.delegation &&
(!delegationTargets?.length ||
delegationTargets?.find((t) => t.compareTo(Principal.from(params[0])) === "eq"))
? this.agentStrategy
: this.signerAgentStrategy;
return strategy.readState(...params);
}
async createReadStateRequest(...params) {
return this.agentStrategy.createReadStateRequest?.(...params);
}
}
const DEFAULT_MAX_TIME_TO_LIVE = BigInt(1.8e12); // 30 min
const DEFAULT_IDLE_TIMEOUT = 14_400_000; // 4 hours
const TIMEOUT_MAX_DELAY = 2147483647;
/**
* Detects if the `timeout` ms is over, and calls `onTimeout` and registered callbacks.
* To override these defaults, you can pass an `onTimeout` callback, or configure a custom `timeout` in milliseconds
*/
class TimeoutManager {
callbacks = [];
timeout;
timeoutID = undefined;
/**
* @param options {@link IdleManagerOptions}
*/
constructor(options) {
const { onTimeout, timeout } = options || {};
this.callbacks = onTimeout ? [onTimeout] : [];
this.timeout = timeout > TIMEOUT_MAX_DELAY ? TIMEOUT_MAX_DELAY : timeout;
const _resetTimer = this._resetTimer.bind(this);
window.addEventListener("load", _resetTimer, true);
_resetTimer();
}
/**
* @param {TimeoutCB} callback function to be called on timeout
*/
registerCallback(callback) {
this.callbacks.push(callback);
}
/**
* Cleans up the timeout manager and its listeners
*/
exit() {
clearTimeout(this.timeoutID);
window.removeEventListener("load", this._resetTimer, true);
}
/**
* Resets the timeouts during cleanup
*/
_resetTimer() {
const exit = this.exit.bind(this);
window.clearTimeout(this.timeoutID);
this.timeoutID = window.setTimeout(() => {
exit();
this.callbacks.forEach((cb) => cb());
}, this.timeout);
}
}
const events = ["mousedown", "mousemove", "keydown", "touchstart", "wheel"];
/**
* Detects if the user has been idle for a duration of `idleTimeout` ms, and calls `onIdle` and registered callbacks.
* By default, the IdleManager will log a user out after 10 minutes of inactivity.
* To override these defaults, you can pass an `onIdle` callback, or configure a custom `idleTimeout` in milliseconds
*/
class IdleManager extends TimeoutManager {
constructor(options = {}) {
super({ timeout: options.idleTimeout || DEFAULT_IDLE_TIMEOUT, onTimeout: options.onIdle });
const _resetTimer = this._resetTimer.bind(this);
events.forEach(function (name) {
document.addEventListener(name, _resetTimer, true);
});
const debounce = (func, wait) => {
let timeout;
return (...args) => {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const context = this;
const later = function () {
timeout = undefined;
func.apply(context, args);
};
clearTimeout(timeout);
timeout = window.setTimeout(later, wait);
};
};
if (options?.captureScroll) {
// debounce scroll events
const scroll = debounce(_resetTimer, options?.scrollDebounce ?? 100);
window.addEventListener("scroll", scroll, true);
}
}
}
const STORAGE_KEY = "client";
const STORAGE_CONNECTED_OWNER_KEY = "connected-owner";
const STORAGE_CONNECTED_SUBACCOUNT_KEY = "connected-subaccount";
class SignerClient {
options;
idleManager;
storage = new IdbStorage();
connectedUser;
constructor(options) {
this.options = options;
if (!options?.idleOptions?.disableIdle) {
this.idleManager = new IdleManager(options.idleOptions);
this.registerDefaultIdleCallback();
}
if (options.storage)
this.storage = options.storage;
}
registerDefaultIdleCallback() {
/**
* Default behavior is to clear stored identity and reload the page.
* By either setting the disableDefaultIdleCallback flag or passing in a custom idle callback, we will ignore this config
*/
if (!this.options?.idleOptions?.disableDefaultIdleCallback) {
this.idleManager?.registerCallback(async () => {
await this.logout();
});
}
}
async logout(options) {
await this.setConnectedUserToStorage(undefined);
this.idleManager?.exit();
this.idleManager = undefined;
await this.options.onLogout?.();
if (options?.returnTo) {
try {
window.history.pushState({}, "", options.returnTo);
}
catch (e) {
window.location.href = options.returnTo;
}
}
}
async setConnectedUser(user) {
if (!user)
this.connectedUser = undefined;
else {
let subAccount;
if (user.subAccount) {
const subAccountOrError = SubAccount.fromBytes(new Uint8Array(user.subAccount));
if (typeof subAccountOrError === typeof Error) {
throw subAccount;
}
subAccount = subAccountOrError;
}
this.connectedUser = {
principal: Principal.from(user.owner),
subAccount,
};
}
}
async setConnectedUserToStorage(user) {
if (!user) {
await this.storage.remove(STORAGE_CONNECTED_OWNER_KEY);
await this.storage.remove(STORAGE_CONNECTED_SUBACCOUNT_KEY);
localStorage.removeItem("connected");
this.setConnectedUser(undefined);
return;
}
await this.storage.set(STORAGE_CONNECTED_OWNER_KEY, user.owner);
localStorage.setItem("connected", "1");
if (user.subAccount)
await this.storage.set(STORAGE_CONNECTED_SUBACCOUNT_KEY, new TextDecoder().decode(user.subAccount));
this.setConnectedUser({
owner: user.owner,
subAccount: user.subAccount,
});
}
// sync method to check if it's needed to check authorization reading from async storage
static shouldCheckIsUserConnected() {
return !!localStorage.getItem("connected");
}
async getConnectedUserFromStorage() {
const owner = await this.storage.get(STORAGE_CONNECTED_OWNER_KEY);
if (!owner)
return;
const subAccount = await this.storage.get(STORAGE_CONNECTED_SUBACCOUNT_KEY);
return {
owner: owner.toString(),
subAccount: subAccount
? new TextEncoder().encode(subAccount.toString()).buffer
: undefined,
};
}
get crypto() {
return this.options.crypto ?? globalThis.crypto;
}
}
const ED25519_KEY_LABEL = "Ed25519";
var DelegationType;
(function (DelegationType) {
DelegationType["ACCOUNT"] = "ACCOUNT";
DelegationType["RELYING_PARTY"] = "RELYING_PARTY";
})(DelegationType || (DelegationType = {}));
const NANOS_IN_MILLIS = BigInt(1000000);
class DelegationSignerClient extends SignerClient {
identity;
baseIdentity;
targets;
maxTimeToLive;
expirationManager;
constructor(options, identity, baseIdentity, targets, maxTimeToLive = BigInt(DEFAULT_MAX_TIME_TO_LIVE)) {
// TODO for delegation use delegation expiration as idle timeout
super(options);
this.identity = identity;
this.baseIdentity = baseIdentity;
this.targets = targets;
this.maxTimeToLive = maxTimeToLive;
}
static async create(options) {
const storage = options.storage ?? new IdbStorage();
let baseIdentity = options.identity;
let identity = new AnonymousIdentity();
if (this.shouldCheckIsUserConnected() && !baseIdentity) {
baseIdentity = await getIdentity(STORAGE_KEY, storage);
}
if (!baseIdentity) {
const createdBaseIdentity = await (!options?.keyType || options?.keyType === ED25519_KEY_LABEL
? Ed25519KeyIdentity.generate(crypto.getRandomValues(new Uint8Array(32)))
: ECDSAKeyIdentity.generate());
baseIdentity = createdBaseIdentity;
}
if (this.shouldCheckIsUserConnected()) {
const delegationChain = await getDelegationChain(STORAGE_KEY, storage);
const delegationValid = baseIdentity && delegationChain && isDelegationValid(delegationChain);
identity = delegationValid
? DelegationSignerClient.createIdentity(baseIdentity, delegationChain)
: new AnonymousIdentity();
const signerClient = new DelegationSignerClient(options, identity, baseIdentity, options.targets, options.maxTimeToLive);
if (delegationValid) {
signerClient.initExpirationManager(delegationChain);
}
const storageConnectedUser = await signerClient.getConnectedUserFromStorage();
await signerClient.setConnectedUser(storageConnectedUser);
return signerClient;
}
const signerClient = new DelegationSignerClient(options, identity, baseIdentity, options.targets, options.maxTimeToLive);
return signerClient;
}
static createIdentity(baseIdentity, delegationChain) {
if (baseIdentity instanceof PartialIdentity) {
return PartialDelegationIdentity.fromDelegation(baseIdentity, delegationChain);
}
return DelegationIdentity.fromDelegation(baseIdentity, delegationChain);
}
async login() {
const params = this.options.derivationOrigin
? {
icrc95DerivationOrigin: this.options.derivationOrigin,
}
: {};
const delegationChainResponse = await this.options.signer.sendRequest({
id: this.crypto.randomUUID(),
jsonrpc: "2.0",
method: "icrc34_delegation",
params: {
...params,
publicKey: toBase64(this.baseIdentity.getPublicKey().toDer()),
targets: this.targets,
maxTimeToLive: this.maxTimeToLive === undefined ? undefined : String(this.maxTimeToLive),
},
});
if ("error" in delegationChainResponse) {
throw Error(delegationChainResponse.error.message);
}
const delegationChain = DelegationChain.fromDelegations(delegationChainResponse.result.signerDelegation.map((delegation) => {
return {
delegation: new Delegation(fromBase64(delegation.delegation.pubkey), BigInt(delegation.delegation.expiration), delegation.delegation.targets?.map((principal) => Principal.fromText(principal))),
signature: fromBase64(delegation.signature),
};
}), fromBase64(delegationChainResponse.result.publicKey));
if (this.baseIdentity instanceof Ed25519KeyIdentity ||
this.baseIdentity instanceof ECDSAKeyIdentity) {
await setIdentity(STORAGE_KEY, this.baseIdentity, this.storage);
}
await setDelegationChain(STORAGE_KEY, delegationChain, this.storage);
this.identity = DelegationSignerClient.createIdentity(this.baseIdentity, delegationChain);
await this.setConnectedUserToStorage({ owner: this.identity.getPrincipal().toString() });
if (!this.options?.idleOptions?.disableIdle && !this.idleManager) {
this.idleManager = new IdleManager(this.options.idleOptions);
this.registerDefaultIdleCallback();
}
return this.initExpirationManager(delegationChain);
}
initExpirationManager(delegationChain) {
if (!this.expirationManager) {
const delegationExpirationInMillis = Number(delegationChain.delegations.reduce((acc, value) => {
const bigIntValue = BigInt(value.delegation.expiration) / NANOS_IN_MILLIS;
return bigIntValue > acc ? bigIntValue : acc;
}, BigInt(delegationChain.delegations[0].delegation.expiration) / NANOS_IN_MILLIS)) - Date.now();
this.expirationManager = new TimeoutManager({ timeout: delegationExpirationInMillis });
this.expirationManager?.registerCallback(async () => {
await this.logout();
});
}
}
async logout(options) {
await Promise.all([
removeIdentity(STORAGE_KEY, this.storage),
removeDelegationChain(STORAGE_KEY, this.storage),
]);
this.identity = new AnonymousIdentity();
return super.logout(options);
}
getIdentity() {
return this.identity;
}
async getDelegationType() {
if (!this.connectedUser)
throw new Error("Not authorized");
const delegationChain = await getDelegationChain(STORAGE_KEY, this.storage);
if (!delegationChain)
throw new Error("Not authorized");
return delegationChain.delegations[0].delegation.targets?.length
? DelegationType.ACCOUNT
: DelegationType.RELYING_PARTY;
}
async getDelegation() {
const chain = await getDelegationChain(STORAGE_KEY, this.storage);
return chain?.delegations[0];
}
}
class AccountsSignerClient extends SignerClient {
static async create(options) {
const signerClient = new AccountsSignerClient(options);
if (SignerClient.shouldCheckIsUserConnected()) {
const storageConnectedUser = await signerClient.getConnectedUserFromStorage();
await signerClient.setConnectedUser(storageConnectedUser);
}
return signerClient;
}
async login() {
// get and transform accounts from signer
const params = this.options.derivationOrigin
? {
params: {
icrc95DerivationOrigin: this.options.derivationOrigin,
},
}
: {};
const accountsResponse = await this.options.signer.sendRequest({
method: "icrc27_accounts",
id: this.crypto.randomUUID(),
jsonrpc: "2.0",
...params,
});
if ("error" in accountsResponse) {
throw Error(accountsResponse.error.message);
}
const accounts = accountsResponse.result.accounts.map(({ owner, subaccount }) => {
return {
owner: Principal.fromText(owner),
subaccount: subaccount === undefined ? undefined : fromBase64(subaccount),
};
});
await this.setAccounts(accounts);
const account = accounts[0];
if (!account.subaccount) {
if (!this.options?.idleOptions?.disableIdle && !this.idleManager) {
this.idleManager = new IdleManager(this.options.idleOptions);
this.registerDefaultIdleCallback();
}
await this.setConnectedUserToStorage({ owner: account.owner.toString() });
return;
}
await this.setConnectedUserToStorage({
owner: account.owner.toString(),
subAccount: account.subaccount,
});
if (!this.options?.idleOptions?.disableIdle && !this.idleManager) {
this.idleManager = new IdleManager(this.options.idleOptions);
this.registerDefaultIdleCallback();
}
}
async logout(options) {
await this.storage.remove(`accounts-${STORAGE_KEY}`);
return super.logout(options);
}
async setAccounts(accounts) {
return this.storage.set(`accounts-${STORAGE_KEY}`, JSON.stringify(accounts.map((acc) => ({
owner: acc.owner.toString(),
subaccount: acc.subaccount && new TextDecoder().decode(acc.subaccount),
}))));
}
async getAccounts() {
const storageData = await this.storage.get(`accounts-${STORAGE_KEY}`);
if (!storageData || typeof storageData !== "string")
return;
return JSON.parse(storageData).map(({ owner, subaccount }) => {
let subAccount;
if (subaccount) {
const subAccountOrError = SubAccount.fromBytes(new Uint8Array(new TextEncoder().encode(subaccount)));
if (typeof subAccountOrError === typeof Error) {
throw subAccount;
}
subAccount = subAccountOrError;
}
return {
principal: Principal.from(owner),
subAccount,
};
});
}
}
const IdentityKitAuthType = {
DELEGATION: "DELEGATION",
ACCOUNTS: "ACCOUNTS",
};
class IdentityKit {
signerClient;
constructor(signerClient) {
this.signerClient = signerClient;
}
async getIcpBalance() {
const connectedUser = await this.signerClient.connectedUser;
if (!connectedUser)
throw new Error("Not authenticated");
const balance = (await LedgerCanister.create().accountBalance({
accountIdentifier: AccountIdentifier.fromPrincipal({
principal: connectedUser.principal,
subAccount: connectedUser.subAccount,
}),
certified: false,
})).toString();
return Number(balance) / 10 ** 8;
}
static async create({ signerClientOptions, authType }) {
if (authType === IdentityKitAuthType.DELEGATION) {
const signerClient = await DelegationSignerClient.create(signerClientOptions);
return new this(signerClient);
}
else {
const signerClient = await AccountsSignerClient.create(signerClientOptions);
return new this(signerClient);
}
}
}
var TransportType;
(function (TransportType) {
TransportType[TransportType["NEW_TAB"] = 0] = "NEW_TAB";
TransportType[TransportType["EXTENSION"] = 1] = "EXTENSION";
TransportType[TransportType["INTERNET_IDENTITY"] = 2] = "INTERNET_IDENTITY";
TransportType[TransportType["STOIC"] = 3] = "STOIC";
})(TransportType || (TransportType = {}));
const NFIDW = {
id: "NFIDW",
description: "Quickly sign in or create an anonymous, self-sovereign wallet with your email address or passkey.",
providerUrl: "https://nfid.one/rpc",
transportType: TransportType.NEW_TAB,
label: "NFID Wallet",
icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjQiIGhlaWdodD0iNjQiIHZpZXdCb3g9IjAgMCA2NCA2NCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNS45MDIyIDMuMTU2MjlDMTYuNzcxNCAzLjA2MzYzIDE3LjQwMDggMi4yODM5OCAxNy4zMDgxIDEuNDE0ODlDMTcuMjE1NSAwLjU0NTgwOSAxNi40MzU4IC0wLjA4MzYwOTkgMTUuNTY2NyAwLjAwOTA0OTQ2TDEyLjMzNDUgMC4zNTM2MzNDMTAuNjc4NiAwLjUzMDE2NSA5LjM1MDQ2IDAuNjcxNzUxIDguMjcxOCAwLjg2NzMwNkM3LjE2MDYgMS4wNjg3NiA2LjIwODI3IDEuMzQ0MTQgNS4zMjM3OCAxLjgzOTgxQzMuODg3NzMgMi42NDQ1NyAyLjY5NzE5IDMuODIzODUgMS44Nzk0NCA1LjI1MTk2QzEuMzc1NjggNi4xMzE3MyAxLjA5MjA0IDcuMDgxMTIgMC44ODExODUgOC4xODk2MUMwLjY3NjUzNSA5LjI2NTQ5IDAuNTIzODkzIDEwLjU5MSAwLjMzMzYyMSAxMi4yNDMyTDAuMzI1NDI0IDEyLjMxNDRMMC4wMDgwNjAwNiAxNS40NzA0Qy0wLjA3OTM4NzUgMTYuMzQgMC41NTQ3MjYgMTcuMTE1OSAxLjQyNDM5IDE3LjIwMzRDMi4yOTQwNiAxNy4yOTA4IDMuMDY5OTUgMTYuNjU2NyAzLjE1NzQgMTUuNzg3MUwzLjQ3MjQ4IDEyLjY1MzhDMy42NzA1NCAxMC45MzQgMy44MTA2NiA5LjcyNzMgMy45OTA2NiA4Ljc4MTAzQzQuMTY3MzEgNy44NTIzNSA0LjM2NDQ5IDcuMjgxODEgNC42MjYyNiA2LjgyNDY3QzUuMTU5MDEgNS44OTQyNyA1LjkzNDg3IDUuMTI1NTkgNi44NzEyMSA0LjYwMDg2QzcuMzMxNTEgNC4zNDI5MSA3LjkwNDg2IDQuMTUwNTEgOC44MzY0NiAzLjk4MTYxQzkuNzg3NjYgMy44MDkxNiAxMS4wMDA1IDMuNjc4ODggMTIuNzI5OSAzLjQ5NDVMMTUuOTAyMiAzLjE1NjI5Wk00Ni42OTE5IDEuNDE0ODlDNDYuNTk5MiAyLjI4Mzk4IDQ3LjIyODYgMy4wNjM2MyA0OC4wOTc4IDMuMTU2MjlMNTEuMjcwMSAzLjQ5NDVDNTIuOTk5NSAzLjY3ODg4IDU0LjIxMjMgMy44MDkxNiA1NS4xNjM1IDMuOTgxNjFDNTYuMDk1MSA0LjE1MDUxIDU2LjY2ODUgNC4zNDI5MSA1Ny4xMjg4IDQuNjAwODZDNTguMDY1MSA1LjEyNTU5IDU4Ljg0MSA1Ljg5NDI3IDU5LjM3MzcgNi44MjQ2N0M1OS42MzU1IDcuMjgxODEgNTkuODMyNyA3Ljg1MjM1IDYwLjAwOTMgOC43ODEwM0M2MC4xODkzIDkuNzI3MyA2MC4zMjk1IDEwLjkzNCA2MC41Mjc1IDEyLjY1MzhMNjAuODQyNiAxNS43ODcxQzYwLjkzIDE2LjY1NjcgNjEuNzA1OSAxNy4yOTA4IDYyLjU3NTYgMTcuMjAzNEM2My40NDUzIDE3LjExNTkgNjQuMDc5NCAxNi4zNCA2My45OTE5IDE1LjQ3MDRMNjMuNjc0NiAxMi4zMTQ0TDYzLjY2NjQgMTIuMjQzMkM2My40NzYxIDEwLjU5MSA2My4zMjM1IDkuMjY1NDkgNjMuMTE4OCA4LjE4OTYxQzYyLjkwOCA3LjA4MTEyIDYyLjYyNDMgNi4xMzE3MyA2Mi4xMjA2IDUuMjUxOTZDNjEuMzAyOCAzLjgyMzg1IDYwLjExMjMgMi42NDQ1NyA1OC42NzYyIDEuODM5ODFDNTcuNzkxNyAxLjM0NDE0IDU2LjgzOTQgMS4wNjg3NiA1NS43MjgyIDAuODY3MzA2QzU0LjY0OTUgMC42NzE3NTEgNTMuMzIxNCAwLjUzMDE2NSA1MS42NjU1IDAuMzUzNjMzTDQ4LjQzMzMgMC4wMDkwNDk0NkM0Ny41NjQyIC0wLjA4MzYwOTkgNDYuNzg0NSAwLjU0NTgwOSA0Ni42OTE5IDEuNDE0ODlaTTQ2LjY5MTkgNjIuNTg1MUM0Ni41OTkyIDYxLjcxNiA0Ny4yMjg2IDYwLjkzNjQgNDguMDk3OCA2MC44NDM3TDUxLjI3MDEgNjAuNTA1NUM1Mi45OTk1IDYwLjMyMTEgNTQuMjEyMyA2MC4xOTA4IDU1LjE2MzUgNjAuMDE4NEM1Ni4wOTUxIDU5Ljg0OTUgNTYuNjY4NSA1OS42NTcxIDU3LjEyODggNTkuMzk5MUM1OC4wNjUxIDU4Ljg3NDQgNTguODQxIDU4LjEwNTcgNTkuMzczNyA1Ny4xNzUzQzU5LjYzNTUgNTYuNzE4MiA1OS44MzI3IDU2LjE0NzYgNjAuMDA5MyA1NS4yMTlDNjAuMTg5MyA1NC4yNzI3IDYwLjMyOTUgNTMuMDY2IDYwLjUyNzUgNTEuMzQ2Mkw2MC44NDI2IDQ4LjIxMjlDNjAuOTMgNDcuMzQzMyA2MS43MDU5IDQ2LjcwOTIgNjIuNTc1NiA0Ni43OTY2QzYzLjQ0NTMgNDYuODg0MSA2NC4wNzk0IDQ3LjY2IDYzLjk5MTkgNDguNTI5Nkw2My42NzQ2IDUxLjY4NTZMNjMuNjY2NCA1MS43NTY4QzYzLjQ3NjEgNTMuNDA5IDYzLjMyMzUgNTQuNzM0NSA2My4xMTg4IDU1LjgxMDRDNjIuOTA4IDU2LjkxODkgNjIuNjI0MyA1Ny44NjgzIDYyLjEyMDYgNTguNzQ4QzYxLjMwMjggNjAuMTc2MiA2MC4xMTIzIDYxLjM1NTQgNTguNjc2MiA2Mi4xNjAyQzU3Ljc5MTcgNjIuNjU1OSA1Ni44Mzk0IDYyLjkzMTIgNTUuNzI4MiA2My4xMzI3QzU0LjY0OTYgNjMuMzI4MiA1My4zMjE2IDYzLjQ2OTggNTEuNjY1OCA2My42NDYzTDUxLjY2NTYgNjMuNjQ2M0w1MS42NjU1IDYzLjY0NjRMNTEuNjY1NSA2My42NDY0TDQ4LjQzMzMgNjMuOTkxQzQ3LjU2NDIgNjQuMDgzNiA0Ni43ODQ1IDYzLjQ1NDIgNDYuNjkxOSA2Mi41ODUxWk0xNy4zMDgxIDYyLjU4NTZDMTcuNDAwOCA2MS43MTY1IDE2Ljc3MTQgNjAuOTM2OSAxNS45MDIyIDYwLjg0NDJMMTIuNzI5OSA2MC41MDZDMTEuMDAwNSA2MC4zMjE2IDkuNzg3NjYgNjAuMTkxMyA4LjgzNjQ2IDYwLjAxODlDNy45MDQ4NiA1OS44NSA3LjMzMTUxIDU5LjY1NzYgNi44NzEyMSA1OS4zOTk2QzUuOTM0ODcgNTguODc0OSA1LjE1OTAxIDU4LjEwNjIgNC42MjYyNiA1Ny4xNzU4QzQuMzY0NDkgNTYuNzE4NyA0LjE2NzMxIDU2LjE0ODEgMy45OTA2NiA1NS4yMTk1QzMuODEwNjYgNTQuMjczMiAzLjY3MDU0IDUzLjA2NjUgMy40NzI0OCA1MS4zNDY3TDMuMTU3NCA0OC4yMTM0QzMuMDY5OTUgNDcuMzQzOCAyLjI5NDA2IDQ2LjcwOTcgMS40MjQzOSA0Ni43OTcxQzAuNTU0NzI2IDQ2Ljg4NDYgLTAuMDc5Mzg3NSA0Ny42NjA0IDAuMDA4MDYwMDYgNDguNTMwMUwwLjMyNTQyNCA1MS42ODYxTDAuMzMzNjIxIDUxLjc1NzNDMC41MjM4OTMgNTMuNDA5NSAwLjY3NjUzNSA1NC43MzUgMC44ODExODUgNTUuODEwOUMxLjA5MjA0IDU2LjkxOTQgMS4zNzU2OCA1Ny44Njg4IDEuODc5NDQgNTguNzQ4NUMyLjY5NzE5IDYwLjE3NjYgMy44ODc3MyA2MS4zNTU5IDUuMzIzNzggNjIuMTYwN0M2LjIwODI3IDYyLjY1NjMgNy4xNjA2IDYyLjkzMTcgOC4yNzE4IDYzLjEzMzJDOS4zNTA0NCA2My4zMjg3IDEwLjY3ODYgNjMuNDcwMyAxMi4zMzQ1IDYzLjY0NjhMMTIuMzM0NSA2My42NDY5TDE1LjU2NjcgNjMuOTkxNEMxNi40MzU4IDY0LjA4NDEgMTcuMjE1NSA2My40NTQ3IDE3LjMwODEgNjIuNTg1NlpNMTYuNDUxMiAxOS43OTU0QzE1LjY5NTUgMTkuNzk1NCAxNS4wODI4IDIwLjQwODEgMTUuMDgyOCAyMS4xNjM4VjQzLjI5NzJDMTUuMDgyOCA0NC4wNTMgMTUuNjk1NSA0NC42NjU2IDE2LjQ1MTIgNDQuNjY1NkgyMC43NDk0QzIxLjUwNTIgNDQuNjY1NiAyMi4xMTc4IDQ0LjA1MyAyMi4xMTc4IDQzLjI5NzJWMjEuMTYzOEMyMi4xMTc4IDIwLjQwODEgMjEuNTA1MiAxOS43OTU0IDIwLjc0OTQgMTkuNzk1NEgxNi40NTEyWk0yOC4xMDgyIDE5Ljc5NTRDMjcuMzUyNSAxOS43OTU0IDI2LjczOTggMjAuNDA4MSAyNi43Mzk4IDIxLjE2MzhWNDMuMjk3MkMyNi43Mzk4IDQ0LjA1MyAyNy4zNTI1IDQ0LjY2NTYgMjguMTA4MiA0NC42NjU2SDM4LjUwMDRDNDEuMjI0NCA0NC42NjU2IDQzLjY0MDUgNDQuMTU2NCA0NS43NDg3IDQzLjEzNzlDNDcuODU2OCA0Mi4xMTk0IDQ5LjQ5MTIgNDAuNjc0NiA1MC42NTE5IDM4LjgwMzRDNTEuODEyNSAzNi45MzIyIDUyLjM5MjkgMzQuNzQxMiA1Mi4zOTI5IDMyLjIzMDVDNTIuMzkyOSAyOS43MTk4IDUxLjgxMjUgMjcuNTI4OSA1MC42NTE5IDI1LjY1NzdDNDkuNDkxMiAyMy43ODY1IDQ3Ljg1NjggMjIuMzQxNyA0NS43NDg3IDIxLjMyMzJDNDMuNjQwNSAyMC4zMDQ3IDQxLjIyNDQgMTkuNzk1NCAzOC41MDA0IDE5Ljc5NTRIMjguMTA4MlpNNDMuMzMyNiAzNy4yNDAxQzQyLjA1MzUgMzguNDQ4MSA0MC4zNDggMzkuMDUyMSAzOC4yMTYyIDM5LjA1MjFIMzQuMTg1NEMzMy45NTg3IDM5LjA1MjEgMzMuNzc0OSAzOC44NjgzIDMzLjc3NDkgMzguNjQxNlYyNS44MTk1QzMzLjc3NDkgMjUuNTkyOCAzMy45NTg3IDI1LjQwOSAzNC4xODU0IDI1LjQwOUgzOC4yMTYyQzQwLjM0OCAyNS40MDkgNDIuMDUzNSAyNi4wMTMgNDMuMzMyNiAyNy4yMjFDNDQuNjM1NCAyOC40Mjg5IDQ1LjI4NjggMzAuMDk4OCA0NS4yODY4IDMyLjIzMDVDNDUuMjg2OCAzNC4zNjIzIDQ0LjYzNTQgMzYuMDMyMSA0My4zMzI2IDM3LjI0MDFaIiBmaWxsPSJ1cmwoI3BhaW50MF9saW5lYXJfODAzODBfNTQpIi8+CjxkZWZzPgo8bGluZWFyR3JhZGllbnQgaWQ9InBhaW50MF9saW5lYXJfODAzODBfNTQiIHgxPSIwLjc2NTgxMiIgeTE9Ii0yLjcwNDk2ZS0wNyIgeDI9IjYzLjU0MTciIHkyPSI2NC4yNzkzIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiMwMEE2OTUiLz4KPHN0b3Agb2Zmc2V0PSIwLjUiIHN0b3AtY29sb3I9IiMwMTg0NzciLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMDA2RjY0Ii8+CjwvbGluZWFyR3JhZGllbnQ+CjwvZGVmcz4KPC9zdmc+Cg==",
};
({
transportType: TransportType.NEW_TAB});
const InternetIdentity = {
id: "InternetIdentity",
providerUrl: "",
transportType: TransportType.INTERNET_IDENTITY,
label: "Internet Identity",
icon: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAzNTguOCAxNzkuOCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMzU4LjggMTc5Ljg7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCS5zdDB7ZmlsbDp1cmwoI1NWR0lEXzFfKTt9Cgkuc3Qxe2ZpbGw6dXJsKCNTVkdJRF8yXyk7fQoJLnN0MntmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtmaWxsOiMyOUFCRTI7fQo8L3N0eWxlPgo8bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzFfIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjIyNC43ODUzIiB5MT0iMjU3Ljc1MzYiIHgyPSIzNDguMDY2MyIgeTI9IjEzMy40NTgxIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIDAgMjcyKSI+Cgk8c3RvcCAgb2Zmc2V0PSIwLjIxIiBzdHlsZT0ic3RvcC1jb2xvcjojRjE1QTI0Ii8+Cgk8c3RvcCAgb2Zmc2V0PSIwLjY4NDEiIHN0eWxlPSJzdG9wLWNvbG9yOiNGQkIwM0IiLz4KPC9saW5lYXJHcmFkaWVudD4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTI3MS42LDBjLTIwLDAtNDEuOSwxMC45LTY1LDMyLjRjLTEwLjksMTAuMS0yMC41LDIxLjEtMjcuNSwyOS44YzAsMCwxMS4yLDEyLjksMjMuNSwyNi44CgljNi43LTguNCwxNi4yLTE5LjgsMjcuMy0zMC4xYzIwLjUtMTkuMiwzMy45LTIzLjEsNDEuNi0yMy4xYzI4LjgsMCw1Mi4yLDI0LjIsNTIuMiw1NC4xYzAsMjkuNi0yMy40LDUzLjgtNTIuMiw1NC4xCgljLTEuNCwwLTMtMC4yLTUtMC42YzguNCwzLjksMTcuNSw2LjcsMjYsNi43YzUyLjgsMCw2My4yLTM2LjUsNjMuOC0zOS4xYzEuNS02LjcsMi40LTEzLjcsMi40LTIwLjlDMzU4LjYsNDAuNCwzMTkuNiwwLDI3MS42LDB6Ii8+CjxsaW5lYXJHcmFkaWVudCBpZD0iU1ZHSURfMl8iIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iMTMzLjk0NjEiIHkxPSIxMDYuNDI2MiIgeDI9IjEwLjY2NTMiIHkyPSIyMzAuNzIxNSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSAwIDI3MikiPgoJPHN0b3AgIG9mZnNldD0iMC4yMSIgc3R5bGU9InN0b3AtY29sb3I6I0VEMUU3OSIvPgoJPHN0b3AgIG9mZnNldD0iMC44OTI5IiBzdHlsZT0ic3RvcC1jb2xvcjojNTIyNzg1Ii8+CjwvbGluZWFyR3JhZGllbnQ+CjxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik04Ny4xLDE3OS44YzIwLDAsNDEuOS0xMC45LDY1LTMyLjRjMTAuOS0xMC4xLDIwLjUtMjEuMSwyNy41LTI5LjhjMCwwLTExLjItMTIuOS0yMy41LTI2LjgKCWMtNi43LDguNC0xNi4yLDE5LjgtMjcuMywzMC4xYy0yMC41LDE5LTM0LDIzLjEtNDEuNiwyMy4xYy0yOC44LDAtNTIuMi0yNC4yLTUyLjItNTQuMWMwLTI5LjYsMjMuNC01My44LDUyLjItNTQuMQoJYzEuNCwwLDMsMC4yLDUsMC42Yy04LjQtMy45LTE3LjUtNi43LTI2LTYuN0MxMy40LDI5LjYsMyw2Ni4xLDIuNCw2OC44QzAuOSw3NS41LDAsODIuNSwwLDg5LjdDMCwxMzkuNCwzOSwxNzkuOCw4Ny4xLDE3OS44eiIvPgo8cGF0aCBjbGFzcz0ic3QyIiBkPSJNMTI3LjMsNTkuN2MtNS44LTUuNi0zNC0yOC41LTYxLTI5LjNDMTguMSwyOS4yLDQsNjQuMiwyLjcsNjguN0MxMiwyOS41LDQ2LjQsMC4yLDg3LjIsMAoJYzMzLjMsMCw2NywzMi43LDkxLjksNjIuMmMwLDAsMC4xLTAuMSwwLjEtMC4xYzAsMCwxMS4yLDEyLjksMjMuNSwyNi44YzAsMCwxNCwxNi41LDI4LjgsMzFjNS44LDUuNiwzMy45LDI4LjIsNjAuOSwyOQoJYzQ5LjUsMS40LDYzLjItMzUuNiw2My45LTM4LjRjLTkuMSwzOS41LTQzLjYsNjguOS04NC42LDY5LjFjLTMzLjMsMC02Ny0zMi43LTkyLTYyLjJjMCwwLjEtMC4xLDAuMS0wLjEsMC4yCgljMCwwLTExLjItMTIuOS0yMy41LTI2LjhDMTU2LjIsOTAuOCwxNDIuMiw3NC4yLDEyNy4zLDU5Ljd6IE0yLjcsNjkuMWMwLTAuMSwwLTAuMiwwLjEtMC4zQzIuNyw2OC45LDIuNyw2OSwyLjcsNjkuMXoiLz4KPC9zdmc+Cg==",
};
({
transportType: TransportType.STOIC});
({
transportType: TransportType.NEW_TAB});
function useCreateIdentityKit({ selectedSigner, clearSigner, signerClientOptions = {}, authType, onConnectFailure, onConnectSuccess, realConnectDisabled, ...props }) {
const [ik, setIk] = useState(null);
const [user, setUser] = useState();
const [icpBalance, setIcpBalance] = useState();
const onDisconnect = useCallback(async () => {
setIk(null);
setUser(undefined);
setIcpBalance(undefined);
await selectedSigner?.value.transport.connection?.disconnect();
await clearSigner();
props.onDisconnect?.();
if (selectedSigner?.id === InternetIdentity.id)
window.location.reload();
}, [ik?.signerClient, clearSigner, props.onDisconnect, selectedSigner]);
// create disconnect func
const disconnect = useCallback(async () => {
return await ik?.signerClient?.logout();
}, [ik?.signerClient]);
// create fetchBalance func
const fetchIcpBalance = useMemo(() => {
if (!user || !ik)
return;
return () => ik.getIcpBalance().then(setIcpBalance);
}, [ik, user, setIcpBalance]);
useEffect(() => {
setIk(null);
// when signer is selected, but user is not connected, create indetity kit and trigger login
if (selectedSigner && !ik?.signerClient) {
IdentityKit.create({
authType,
signerClientOptions: {
...signerClientOptions,
crypto,
signer: selectedSigner.value,
onLogout: onDisconnect,
},
}).then(async (instance) => {
if (!realConnectDisabled) {
if (!instance.signerClient.connectedUser) {
try {
await instance.signerClient.login();
setUser(instance.signerClient.connectedUser);
onConnectSuccess?.();
}
catch (e) {
await selectedSigner.value.transport.connection?.disconnect();
await clearSigner();
onConnectFailure?.(e);
}
}
else {
if (instance.signerClient.getIdentity?.() instanceof AnonymousIdentity) {
await instance.signerClient.logout();
await disconnect();
return;
}
setUser(instance.signerClient.connectedUser);
}
}
else {
onConnectSuccess?.();
}
setIk(instance);
});
}
}, [selectedSigner, realConnectDisabled]);
// fetch balance when user connected
useEffect(() => {
if (icpBalance === undefined) {
fetchIcpBalance?.();
}
}, [icpBalance, user, fetchIcpBalance]);
return {
user,
disconnect,
icpBalance,
signerClient: ik?.signerClient,
fetchIcpBalance,
};
}
function useProceedTheme(theme = IdentityKitTheme.SYSTEM) {
const [finalTheme, setFinalTheme] = useState(theme);
useEffect(() => {
if (!theme || theme === IdentityKitTheme.SYSTEM) {
setFinalTheme(window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches
? IdentityKitTheme.DARK
: IdentityKitTheme.LIGHT);
}
else {
setFinalTheme(theme);
}
}, [theme]);
return finalTheme;
}
async function getPopupTransportBuilder(options) {
return new PostMessageTransport({
...options,
detectNonClickEstablishment: false,
});
}
async function getExtensionTransportBuilder({ uuid }) {
return BrowserExtensionTransport.findTransport({ uuid });
}
async function getAuthClientTransportBuilder(options) {
return await AuthClientTransport.create({
...options,
authClientCreateOptions: {
...options.authClientCreateOptions,
idleOptions: {
disableIdle: true,
},
},
});
}
async function getStoicTransportBuilder({ maxTimeToLive, }) {
return await StoicTransport.create({ maxTimeToLive });
}
class TransportBuilder {
static builders = {
[TransportType.NEW_TAB]: ({ url, crypto, window, windowOpenerFeatures }) => getPopupTransportBuilder({
url,
crypto,
window,
windowOpenerFeatures,
}),
[TransportType.EXTENSION]: ({ id }) => {
if (!id) {
throw Error("Id is required to find the ICRC-94 specific wallet");
}
return getExtensionTransportBuilder({ uuid: id });
},
[TransportType.INTERNET_IDENTITY]: ({ maxTimeToLive, derivationOrigin, identity, keyType, storage, allowInternetIdentityPinAuthentication, url, }) => getAuthClientTransportBuilder({
authClientCreateOptions: {
identity,
keyType,
storage,
},
authClientLoginOptions: {
maxTimeToLive,
derivationOrigin,
allowPinAuthentication: allowInternetIdentityPinAuthentication,
identityProvider: url,
},
}),
[TransportType.STOIC]: ({ maxTimeToLive }) => getStoicTransportBuilder({ maxTimeToLive }),
};
static async build(request) {
return await TransportBuilder.builders[request.transportType]({
...request,
maxTimeToLive: request.maxTimeToLive || DEFAULT_MAX_TIME_TO_LIVE,
});
}
}
function useProceedSigner({ signers, transports, closeModal, crypto, window, windowOpenerFeatures, onConnectFailure, }) {
// saved to local storage for next js (localStorage is not defined during server render)
const [localStorageSigner, setLocalStorageSigner] = useState((typeof window !== "undefined" && localStorage.getItem("signerId")) || "");
const [selectedSigner, setSelectedSigner] = useState(undefined);
const [isSignerBeingSelected, setIsSignerBeingSelected] = useState(false);
const selectSigner = useCallback(async (signerId) => {
if (!signerId) {
localStorage.removeItem("signerId");
setLocalStorageSigner(undefined);
return setSelectedSigner(undefined);
}
try {
setIsSignerBeingSelected(true);
closeModal();
const signerConfig = signers.find((s) => s.id === signerId);
if (!signerConfig)
throw new Error(`Signer with id ${signerId} not found`);
const transport = transports?.find((t) => t.signerId === signerId)?.value;
if (!transport)
throw new Error("Transport was not found");
if (!transport.connection?.connected && !localStorageSigner) {
await transport.connection?.connect();
}
const createdSigner = new Signer({
crypto,
transport,
});
setSelectedSigner({ value: createdSigner, id: signerId });
setIsSignerBeingSelected(false);
}
catch (e) {
setIsSignerBeingSelected(false);
onConnectFailure?.(e);
return;
}
}, [signers, crypto, closeModal, transports, localStorageSigner]);
const selectCustomSigner = useCallback(async (url) => {
const transport = await TransportBuilder.build({
transportType: TransportType.NEW_TAB,
url,
crypto,
window,
windowOpenerFeatures,
});
const signer = new Signer({ crypto, transport });
setSelectedSigner({ value: signer, id: url });
closeModal();
}, [crypto, window, closeModal, windowOpenerFeatures]);
// default selected signer from local storage
useEffect(() => {
// for next.js, where localStorage is not available during ssr
const lsSigner = localStorage.getItem("signerId");
if (!selectedSigner && lsSigner && transports) {
selectSigner(lsSigner);
}
}, [selectedSigner, selectSigner, transports]);
const setSelectedSignerToLocalStorage = useCallback(() => {
if (selectedSigner && selectedSigner?.id) {
localStorage.setItem("signerId", selectedSigner?.id);
}
}, [selectedSigner]);
return {
selectSigner,
setSelectedSignerToLocalStorage,
// clears both local state and local storage
clearSigner: () => selectSigner(),
selectCustomSigner,
// selected signer is local storage signer by default (in case authenticated user)
selectedSigner,
// signer id in localStorage (used on connected user page reload)
localStorageSigner,
isSignerBeingSelected,
};
}
class ContextNotInitializedError extends Error {
constructor() {
super("IdentityKit context not initialized");
}
}
function useBalance() {
return useContextSelector(Context, (ctx) => {
if (!ctx)
throw new ContextNotInitializedError();
return {
balance: ctx.icpBalance,
fetchBalance: ctx.fetchIcpBalance,
};
});
}
function useSigner() {
return useContextSelector(Context, (ctx) => {
if (!ctx)
throw new ContextNotInitializedError();
return ctx.selectedSigner?.value;
});
}
function useModal() {
return useContextSelector(Context, (ctx) => {
if (!ctx)
throw new ContextNotInitializedError();
return {
isModalOpen: ctx.isModalOpen,
toggleModal: ctx.toggleModal,
featuredSigner: ctx.featuredSigner,
signers: ctx.signers,
selectCustomSigner: ctx.selectCustomSigner,
selectSigner: ctx.selectSigner,
};
});
}
function useTheme() {
return useContext(ThemeContext);
}
function useAuthType() {
return useContextSelector(Context, (ctx) => {
if (!ctx)
throw new ContextNotInitializedError();
return ctx.authType;
});
}
function useUser() {
return useContextSelector(Context, (ctx) => {
if (!ctx)
throw new ContextNotInitializedError();
return ctx.user;
});
}
function useSignerClient() {
return useContextSelector(Context, (ctx) => {
if (!ctx)
throw new ContextNotInitializedError();
return ctx.signerClient;
});
}
function useIsInitializing() {
return useContextSelector(Context, (ctx) => {
if (!ctx)
throw new ContextNotInitializedError();
return ctx.isInitializing;
});
}
function useIsUserConnecting() {
return useContextSelector(Context, (ctx) => {
if (!ctx)
throw new ContextNotInitializedError();
return ctx.isUserConnecting;
});
}
function useConnect() {
return useContextSelector(Context, (ctx) => {
if (!ctx)
throw new ContextNotInitializedError();
return ctx.connect;
});
}
function useDisconnect() {
return useContextSelector(Context, (ctx) => {
if (!ctx)
throw new ContextNotInitializedError();
return ctx.disconnect;
});
}
function useSignerConfig() {
return useContextSelector(Context, (ctx) => {
if (!ctx)
throw new ContextNotInitializedError();
return ctx.selectedSigner?.id
? (ctx.signers.find((s) => s.id === ctx.selectedSigner?.id) ?? {
id: ctx.selectedSigner?.id,
label: ctx.selectedSigner?.id,
transportType: TransportType.NEW_TAB,
providerUrl: ctx.selectedSigner?.id,
})
: undefined;
});
}
function useIdentity() {
const signerClient = useSignerClient();
const authType = useAuthType();
const identity = useMemo(() => authType === IdentityKitAuthType.ACCOUNTS || signerClient instanceof AccountsSignerClient
? undefined
: signerClient?.getIdentity(), [authType, signerClient]);
return identity;
}
function useAccounts() {
const authType = useAuthType();
const signerClient = useSignerClient();
const user = useUser();
const [accounts, setAccounts] = useState();
useEffect(() => {
if (!user) {
setAccounts(undefined);
}
else {
if (authType === IdentityKitAuthType.ACCOUNTS &&
!accounts &&
signerClient instanceof AccountsSignerClient) {
signerClient?.getAccounts().then(setAccounts);
}
}
}, [user, authType, signerClient]);
return accounts;
}
function useDelegationType() {
const signerClient = useSignerClient();
const authType = useAuthType();
const user = useUser();
const delegationType = useAsyncMemo(() => {
if (!user ||
!signerClient ||
authType !== IdentityKitAuthType.DELEGATION ||
signerClient instanceof AccountsSignerClient)
return undefined;
return signerClient.getDelegationType();
}, [user, authType, signerClient]);
return delegationType;
}
function useAgent(agentOptions = {}) {
const identity = useIdentity();
const selectedSigner = useSigner();
const user = useUser();
const signerClient = useSignerClient();
const authType = useAuthType();
const ikAgent = useAsyncMemo(async () => {
if (!selectedSigner || !user || !signerClient)
return undefined;
const isAccountsAuth = authType === IdentityKitAuthType.ACCOUNTS;
if (!isAccountsAuth && !identity)
return undefined;
const delegation = isAccountsAuth
? undefined
: await signerClient.getDelegation();
const defaultAgent = await HttpAgent.create({
identity,
...agentOptions,
});
const signerAgent = await SignerAgent.create({
signer: selectedSigner,
account: user.principal,
agent: defaultAgent,
});
return Agent.create({
signerAgent,
agent: defaultAgent,
identity,
delegation: delegation?.delegation,
});
}, [selectedSigner, signerClient, authType, identity]);
return ikAgent;
}
function useClickOutside(handler) {
const ref = useRef(null);
useEffect(() => {
const listener = (event) => {
const el = ref?.current;
// Do nothing if clicking ref's element or descendent elements
if (!el || el.contains(event.target))
return;
handler(event);
};
document.addEventListener(`mousedown`, listener);
document.addEventListener(`touchstart`, listener);
return () => {
document.removeEventListener(`mousedown`, listener);
document.removeEventListener(`touchstart`, listener);
};
}, [ref, handler]);
return ref;
}
function useAuth() {
const user = useUser();
const isConnecting = useIsUserConnecting();
const connect = useConnect();
const disconnect = useDisconnect();
return {
user,
isConnecting,
connect,
disconnect,
};
}
function useCreatePromise() {
// Internal state to force re-renders when the promise resolves
const [, setResolvedValue] = useState();
// Store the promise and its resolve/reject handlers in a ref
const promiseRef = useRef({
promise: null,
resolve: null,
reject: null,
});
// Create the promise
const createPromise = useCallback(() => {
promiseRef.current.promise = new Promise((resolve, reject) => {
promiseRef.current.resolve = resolve;
promiseRef.current.reject = reject;
});
return promiseRef.current.promise;
}, []);
// Resolve the promise
const resolve = useCallback((value) => {
if (promiseRef.current.resolve) {
promiseRef.current.resolve(value);
setResolvedValue(value); // Trigger re-render
}
}, []);
// Reject the promise
const reject = useCallback((error) => {
if (promiseRef.current.reject) {
promiseRef.current.reject(error);
setResolvedValue(undefined); // Trigger re-render (optional)
}
}, []);
return { createPromise, resolve, reject, promise: promiseRef.current.promise };
}
/**
* @deprecated This function is deprecated. Please use separate hooks instead (useUser, useBalance etc).
*/
function useIdentityKit() {
const ctx = useContext$1(Context);
if (!ctx) {
throw new ContextNotInitializedError();
}
const { selectedSigner, user, icpBalance, authType, isInitializing, isUserConnecting, connect, disconnect, fetchIcpBalance, } = ctx;
const identity = useIdentity();
const delegationType = useDelegationType();
const accounts = useAccounts();
return {
signer: selectedSigner?.value,
user,
icpBalance,
authType,
accounts,
delegationType,
identity,
isInitializing,
isUserConnecting,
connect,
disconnect,
fetchIcpBalance,
};
}
function ThemeProvider({ children, ...props }) {
const theme = useProceedTheme(props.theme);
return jsx(ThemeContext.Provider, { value: theme, children: children });
}
const validateUrl = (url) => {
const urlPattern = new RegExp("^(https?:\\/\\/)?" +
"((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|" +
"((\\d{1,3}\\.){3}\\d{1,3}))" +
"(\\:\\d+)?" +
"(\\/[-a-z\\d%_.~+]*)*" +
"(\\?[;&a-z\\d%_.~+=-]*)?" +
"(\\#[-a-z\\d_]*)?$", "i");
return urlPattern.test(url);
};
const ICP_DECIMALS = 8;
function countDecimals(value) {
if (Math.floor(value) === value)
return 0;
const str = value.toString();
if (str.indexOf(".") !== -1 && str.indexOf("-") !== -1) {
return Number(str.split("-")[1]);
}
else if (