UNPKG

@keyban/sdk-base

Version:

Keyban Javascript SDK provides core functionalities for the MPC wallet solution, supporting web and Node.js apps with TypeScript, custom storage, and Ethereum blockchain integration.

582 lines (571 loc) 24.1 kB
import { Address, Hex, Hash, Network, Prettify } from '@keyban/types'; export * from '@keyban/types'; export { ApplicationFeature, ApplicationThemeMode, Currency, DppProductStatus, Network } from '@keyban/types'; import { GqlKeybanClient_TokenContractFragment, GqlKeybanClient_OrderFragment } from './graphql.js'; export { GqlKeybanClient_AssetTransferFragment as KeybanAssetTransfer, GqlKeybanClient_NftFragment as KeybanNft, GqlKeybanClient_NftBalanceFragment as KeybanNftBalance, GqlKeybanClient_TokenBalanceFragment as KeybanTokenBalance } from './graphql.js'; import { IKeybanApi, KeybanApiStatus } from './api.js'; export { ApiClient, ApiServer, AuthSendOtpArgs, AuthSignInArgs, AuthSignUpArgs, AuthUpdateUserArgs, IAccountService, IApplicationService, IAuthService, IClientShareStorageService, IDppService, ILoyaltyService, ISignerService } from './api.js'; import { ApolloClient } from '@apollo/client/core'; export { CryptoError, CryptoErrorType, IFrameApiError, IFrameApiErrorType, JwtError, JwtErrorType, KeybanBaseError, SdkError, SdkErrorTypes } from './errors.js'; import '@graphql-typed-document-node/core'; /** * EIP‑1559 fee parameters for EVM transactions. * Used by EVM account transfer methods to override gas pricing when needed. * @example * const fees: EvmFeeDetails = { maxFeePerGas: 30n * 10n ** 9n, maxPriorityFeePerGas: 2n * 10n ** 9n }; */ type EvmFeeDetails = { /** The maximum fee per unit of gas. */ maxFeePerGas: bigint; /** The maximum priority fee per unit of gas. */ maxPriorityFeePerGas: bigint; }; /** * Stellar fee per operation represented as a string in stroops. * In congestion, Horizon exposes a higher mode fee; otherwise it's the base fee (100 stroops). * @example * const fee: StellarFeeDetails = "100"; // 100 stroops per operation */ type StellarFeeDetails = string; /** * @module Account * Public account and transfer primitives used by chain-specific clients. */ type FeeDetails = EvmFeeDetails | StellarFeeDetails; /** * Represents a transaction fee estimation returned by estimation methods. * The concrete shape of the `details` field depends on the underlying network: * - EVM chains: {@link EvmFeeDetails} (EIP-1559 fields) * - Stellar: {@link StellarFeeDetails} (fee per operation as string) * @see {@link KeybanAccount.estimateTransfer} * @see {@link KeybanAccount.estimateERC20Transfer} * @see {@link KeybanAccount.estimateNftTransfer} */ type FeesEstimation = { /** * The total maximum fees for the transaction. */ maxFees: bigint; /** * Parameters to set when constructing a transaction to ensure the estimation maxFees. */ details: FeeDetails; }; /** * Parameters for transferring ERC‑20-like fungible tokens. * @template TFeeDetails Network-specific fee type (defaults to {@link FeeDetails}). * @property contractAddress The token contract address. * @property to The recipient wallet address. * @property value Amount to transfer, in the token's smallest unit. * @property [fees] Optional network-specific fee parameters. * @see {@link KeybanAccount.transferERC20} * @see {@link SdkErrorTypes} */ type TransferERC20Params<TFeeDetails = FeeDetails> = { contractAddress: Address; to: Address; value: bigint; fees?: TFeeDetails; }; /** * Parameters for transferring NFTs (ERC‑721 / ERC‑1155 on EVM chains). * @template TFeeDetails Network-specific fee type (defaults to {@link FeeDetails}). * @property contractAddress The NFT contract address. * @property tokenId The token identifier. * @property to The recipient wallet address. * @property [value] Amount to transfer for ERC‑1155 (required and > 0), ignored for ERC‑721. * @property standard NFT standard to use: "ERC721" | "ERC1155". * @property [fees] Optional network-specific fee parameters. * @see {@link KeybanAccount.transferNft} * @see {@link SdkErrorTypes} */ type TransferNftParams<TFeeDetails = FeeDetails> = { contractAddress: Address; tokenId: bigint; to: Address; value?: bigint; standard: "ERC721" | "ERC1155"; fees?: TFeeDetails; }; /** * Parameters to estimate an ERC‑20 transfer, without fees. * Derived from {@link TransferERC20Params} by omitting `fees`. */ type EstimateERC20TransferParams = Omit<TransferERC20Params, "fees">; /** * Parameters to estimate an NFT transfer, without fees. * Derived from {@link TransferNftParams} by omitting `fees`. */ type EstimateNftTransferParams = Omit<TransferNftParams, "fees">; /** * Abstract account bound to a specific chain implementation (EVM, Starknet, Stellar). * Provides methods to sign messages and to transfer/estimate fees for native currency, * ERC‑20-like tokens, and NFTs, when supported by the target network. * Obtain an instance via {@link KeybanClient.initialize}. * @see {@link KeybanClient} */ declare abstract class KeybanAccount { /** * The blockchain address associated with the account. */ abstract address: Address; /** * The public key associated with the account. */ abstract publicKey: string; protected api: IKeybanApi; /** * Internal constructor used by chain-specific clients. * @param api - Keyban API facade for remote operations. * @internal */ constructor(api: IKeybanApi); /** * Sign a message with the account's key material. * @param message - The message to sign. * @returns Promise resolving to the signature. EVM returns hex, Stellar returns base64 string. * @throws {Error} On signing failures. * @see {@link KeybanClient} * @example * import { KeybanClient, Network } from "@keyban/sdk-base"; * * const client = new KeybanClient({ apiUrl: "https://api.prod.keyban.io", appId: "YOUR_APP_ID", network: Network.EthereumAnvil }); * const account = await client.initialize(); * const signature = await account.signMessage("Hello Keyban"); * console.log(signature); */ abstract signMessage(message: string): Promise<Hex | string | string[]>; /** * Transfer native currency to a recipient. * @param to - Recipient address. * @param value - Amount to transfer in the smallest unit (e.g., wei, stroop). * @param [fees] - Optional network-specific fee parameters. * @returns Promise resolving to the transaction hash/string. * @throws {SdkError} {@link SdkErrorTypes.AddressInvalid} | {@link SdkErrorTypes.AmountInvalid} * @example * import { KeybanClient, Network, type Address } from "@keyban/sdk-base"; * * const client = new KeybanClient({ * apiUrl: "https://api.prod.keyban.io", * appId: "YOUR_APP_ID", * network: Network.EthereumAnvil, * }); * const account = await client.initialize(); * * const recipient: Address = "0x0000000000000000000000000000000000000001"; * const value = 1_000_000_000_000_000_000n; // 1 ETH in wei * const txHash = await account.transfer(recipient, value); * console.log(txHash); */ abstract transfer(to: Address, value: bigint, fees?: FeeDetails): Promise<Hash | string>; /** * Estimate maximum fees to transfer native currency. * @param to - Recipient address. * @returns Promise resolving to {@link FeesEstimation}. * @throws {Error} On estimation failure. * @example * import { KeybanClient, Network, type Address } from "@keyban/sdk-base"; * const client = new KeybanClient({ apiUrl: "https://api.prod.keyban.io", appId: "YOUR_APP_ID", network: Network.EthereumAnvil }); * const account = await client.initialize(); * const recipient: Address = "0x0000000000000000000000000000000000000001"; * const estimation = await account.estimateTransfer(recipient); * console.log(estimation.maxFees.toString()); */ abstract estimateTransfer(to: Address): Promise<FeesEstimation>; /** * Transfer ERC‑20-like fungible tokens. * @param params - Transfer parameters. * @returns Promise resolving to the transaction hash/string. * @throws {SdkError} {@link SdkErrorTypes.AddressInvalid} | {@link SdkErrorTypes.AmountInvalid} | {@link SdkErrorTypes.RecipientAddressEqualsSender} * @example * import { KeybanClient, Network, type Address } from "@keyban/sdk-base"; * * const client = new KeybanClient({ apiUrl: "https://api.prod.keyban.io", appId: "YOUR_APP_ID", network: Network.EthereumAnvil }); * * const txHash = await account.transferERC20({ * contractAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" as Address, // USDC * to: "0x0000000000000000000000000000000000000001" as Address, * value: 1_000_000n, // 1 USDC (6 decimals) * }); * console.log(txHash); */ abstract transferERC20(params: TransferERC20Params): Promise<Hash | string>; /** * Estimate maximum fees for an ERC‑20 token transfer. * @param params - Transfer parameters without explicit fees. * @returns Promise resolving to {@link FeesEstimation}. * @throws {Error} On estimation failure. * @example * import { KeybanClient, Network, type Address } from "@keyban/sdk-base"; * const client = new KeybanClient({ apiUrl: "https://api.prod.keyban.io", appId: "YOUR_APP_ID", network: Network.EthereumAnvil }); * const account = await client.initialize(); * const estimation = await account.estimateERC20Transfer({ * contractAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" as Address, * to: "0x0000000000000000000000000000000000000001" as Address, * value: 1_000_000n, * }); * console.log(estimation.details); */ abstract estimateERC20Transfer(params: EstimateERC20TransferParams): Promise<FeesEstimation>; /** * Transfer an NFT (ERC‑721 / ERC‑1155). * @param params - NFT transfer parameters. * @returns Promise resolving to the transaction hash. * @throws {SdkError} {@link SdkErrorTypes.AddressInvalid} | {@link SdkErrorTypes.RecipientAddressEqualsSender} | {@link SdkErrorTypes.AmountRequired} | {@link SdkErrorTypes.AmountInvalid} | {@link SdkErrorTypes.AmountIrrelevant} | {@link SdkErrorTypes.InvalidNftStandard} * @example * import { KeybanClient, Network, type Address } from "@keyban/sdk-base"; * * const client = new KeybanClient({ apiUrl: "https://api.prod.keyban.io", appId: "YOUR_APP_ID", network: Network.EthereumAnvil }); * const account = await client.initialize(); * * // ERC-721 * await account.transferNft({ * contractAddress: "0xABC..." as Address, * tokenId: 123n, * to: "0x0000000000000000000000000000000000000001" as Address, * standard: "ERC721", * }); * * // ERC-1155 * await account.transferNft({ * contractAddress: "0xDEF..." as Address, * tokenId: 456n, * to: "0x0000000000000000000000000000000000000001" as Address, * value: 5n, * standard: "ERC1155", * }); */ abstract transferNft(params: TransferNftParams): Promise<Hash>; /** * Estimate maximum fees for an NFT transfer (ERC‑721 / ERC‑1155). * @param params - NFT estimation parameters (without explicit fees). * @returns Promise resolving to {@link FeesEstimation}. * @throws {Error} On estimation failure or invalid parameters. * @example * import { KeybanClient, Network, type Address } from "@keyban/sdk-base"; * const client = new KeybanClient({ apiUrl: "https://api.prod.keyban.io", appId: "YOUR_APP_ID", network: Network.EthereumAnvil }); * const account = await client.initialize(); * const estimation = await account.estimateNftTransfer({ * contractAddress: "0xDEF..." as Address, * tokenId: 456n, * to: "0x0000000000000000000000000000000000000001" as Address, * value: 5n, * standard: "ERC1155", * }); * console.log(estimation.maxFees); */ abstract estimateNftTransfer(params: EstimateNftTransferParams): Promise<FeesEstimation>; } /** * Represents a storage provider for the client share. * * The purpose of `ClientShareProvider` is to provide an interface that allows integrators * to save and restore the client share of their customers. The integrator has the * responsibility to securely store the client share. * @remarks * The client share is not considered sensitive data because only the client * can use it, and its usage is secured by strong authentication between the client * and the Keyban services. * * ### Example Implementation * * Below is a basic implementation of a `CustomClientShareProvider` using a fetch-based provider: * * ```typescript * class CustomClientShareProvider implements ClientShareProvider { * // Retrieves the client share data. * // @returns - A promise resolving to the client share string or `null` if unavailable. * async get(): Promise<string | null> { * try { * const response = await fetch("/api/clientShare", { * method: "GET", * headers: { "Content-Type": "application/json" }, * }); * * if (!response.ok) { * console.error("Failed to fetch client share:", response.statusText); * return null; * } * * return response.text(); * } catch (error) { * console.error("Error retrieving client share:", error); * return null; * } * } * * * // Saves the client share data. * // @param clientShare - The client share string to store. * // @returns - A promise that resolves when the operation is complete. * async set(clientShare: string): Promise<void> { * try { * const response = await fetch("/api/clientShare", { * method: "POST", * headers: { "Content-Type": "application/json" }, * body: JSON.stringify({ clientShare }), * }); * * if (!response.ok) { * throw new Error(`Failed to save client share: ${response.statusText}`); * } * } catch (error) { * console.error("Error saving client share:", error); * throw error; * } * } * } * ``` * * This implementation assumes the existence of an API endpoint `/api/clientShare` * to manage the client share on the server side. The integrator should ensure that * the endpoint is appropriately secured. */ interface ClientShareProvider { /** * Retrieves the client share information. * @param key - The key associated with the client share. * @returns - A promise that resolves to a string containing the client share, or null if not available. */ get(key: string): Promise<string | null>; /** * Sets the client share information. * @param key - The key associated with the client share. * @param clientShare - The client share string to set. * @returns - A promise that resolves when the client share has been set. */ set(key: string, clientShare: string): Promise<unknown>; } /** * @module Chains */ /** * Represents the unit of fees in a specific blockchain. * @property {string} symbol - The symbol of the fee unit (e.g., "gwei"). * @property {number} decimals - The number of decimal places for the fee unit. */ type FeesUnit = { symbol: string; decimals: number; }; /** * Represents the native currency of a blockchain network. * @property {string} name - The name of the native currency (e.g., "Ether"). * @property {string} symbol - The symbol of the native currency (e.g., "ETH"). * @property {number} decimals - The number of decimal places the currency can be divided into. * @example * const nativeCurrency = { * name: "Ether", * symbol: "ETH", * decimals: 18 * }; * @example * const nativeCurrency = { * name: "Bitcoin", * symbol: "BTC", * decimals: 8 * }; */ type NativeCurrency = { name: string; symbol: string; decimals: number; }; /** * Configuration options for initializing the Keyban client. */ type KeybanClientConfig = { /** * The base URL of the API. Optional. Defaults to "https://api.prod.keyban.io" if not provided. */ apiUrl?: URL | string; /** * The application ID. */ appId: string; /** * The blockchain configuration for Keyban. */ network: Network; /** * The client share provider. */ clientShareProvider?: ClientShareProvider; }; type MetadataConfig = { network: { rpcUrl: string; indexerUrl: string; horizonUrl?: string; }; }; declare abstract class KeybanClientBase { /** * The Keyban API URL, defaulting to "https://api.prod.keyban.io". */ apiUrl: URL; /** * The application ID used for authentication with the Keyban API. */ appId: string; /** * The blockchain used by Keyban. */ network: Network; /** * The Apollo GraphQL client used for making API requests. */ apolloClient: ApolloClient; protected clientShareProvider: ClientShareProvider; protected metadataConfig: Promise<MetadataConfig>; constructor(config: KeybanClientConfig, metadataConfig?: Promise<MetadataConfig>); get api(): IKeybanApi; /** * Retrieves the native currency details associated with the current network. * * Returns an object that includes the native currency's name, symbol, and decimal precision * for the active {@link KeybanClientBase.network}. * @returns The native currency information for the current network. */ get nativeCurrency(): NativeCurrency; /** * Retrieves the fee unit configuration based on the current network. * * Maps supported networks to their fee unit details, including symbol and number of decimals. * Supported configurations include: * - EthereumAnvil & PolygonAmoy: symbol "gwei", decimals 9 * - StarknetDevnet, StarknetSepolia & StarknetMainnet: symbol "FRI", decimals 18 * - StellarQuickstart, StellarTestnet & StellarMainnet: symbol "stroop", decimals 7 * @returns The fee unit configuration for the active network. */ get feesUnit(): FeesUnit; /** * Performs a health check on the Keyban API to determine its operational status. * @returns - A promise resolving to the API status, either `"operational"` or `"down"`. * @example * ```typescript * const status = await client.apiStatus(); * console.log(`API Status: ${status}`); * ``` * @throws {Error} Throws an error if the health check request fails. * @see {@link KeybanApiStatus} */ apiStatus(): Promise<KeybanApiStatus>; /** * Initializes a `KeybanAccount` associated with the current client. * This method sets up the account by retrieving or generating the client share, * and prepares the account for transactions and other operations. * @returns - A promise that resolves to an instance of `KeybanAccount`. * @throws {SdkError} If initialization fails due to signing errors. * @example * ```typescript * const account = await client.initialize(); * console.log(`Account address: ${account.address}`); * ``` * @see {@link KeybanAccount} */ abstract initialize(): Promise<KeybanAccount>; } /** * Main client for interacting with the Keyban API and associated services. * This class provides methods to initialize accounts, retrieve balances, query NFTs, * and interact with the Keyban blockchain. * @remarks * The `KeybanClient` serves as the primary interface for developers to interact with * the Keyban ecosystem. It handles authentication, communication with the Keyban API, * and provides utility methods for common tasks. * @example * ```typescript * // Initialize the client * const client = new KeybanClient({ * apiUrl: "https://api.prod.keyban.io", * appId: "your-app-id", * network: Network.EthereumAnvil, * }); * * // Initialize an account * const account = await client.initialize(); * ``` * @see {@link KeybanAccount} */ declare class KeybanClient extends KeybanClientBase { #private; /** * Creates a new instance of `KeybanClient`. * @param config - The configuration object to initialize the client. * @throws {SdkError} If the configuration is invalid. * @example * ```typescript * const client = new KeybanClient({ * apiUrl: "https://api.prod.keyban.io", * appId: "your-app-id", * network: Network.EthereumAnvil, * }); * ``` */ constructor(config: KeybanClientConfig); initialize(): Promise<KeybanAccount>; } /** * Represents a balance with optional metadata. * @example * const balance: Balance = { raw: 1_000_000_000_000_000_000n, decimals: 18, symbol: "ETH", isNative: true }; * const tokenBalance: Balance = { raw: 500_000_000_000_000_000n, decimals: 18, symbol: "DAI" }; */ type Balance = { /** The raw balance value. */ raw: string | bigint; /** The number of decimal places for the balance. */ decimals?: number; /** The symbol of the currency. */ symbol?: string; /** Indicates if the balance is in the native currency. */ isNative?: boolean; /** Indicates if the balance is used for fees. */ isFees?: boolean; }; /** * Format a balance using the client's native/fee units or the token metadata. * @param client - The Keyban client providing network native currency and fee units. * @param balance - Raw amount and optional hints: decimals/symbol/isNative/isFees. * @param [token] - Optional token metadata used when `isNative`/`isFees` are false. * @returns A formatted string with decimals applied and symbol when available. * @example * import { KeybanClient, Network, formatBalance } from "@keyban/sdk-base"; * const client = new KeybanClient({ apiUrl: "https://api.prod.keyban.io", appId: "APP", network: Network.EthereumAnvil }); * const native = { raw: 1_000_000_000_000_000_000n, isNative: true }; * console.log(formatBalance(client, native)); // "1 ETH" * const token = { id: "1", type: "ERC20", name: "Tether", symbol: "USDT", decimals: 6 } as KeybanToken; * console.log(formatBalance(client, { raw: 1_000_000n }, token)); // "1 USDT" * console.log(formatBalance(client, { raw: 1_000n, isFees: true })); // e.g. "0.000001 gwei" */ declare function formatBalance(client: KeybanClient, balance: Balance, token?: GqlKeybanClient_TokenContractFragment): string; type OrderItem = { id: number; itemId: number; name: string; price: number; }; type Order = Prettify<Omit<GqlKeybanClient_OrderFragment, "items"> & { items: OrderItem[]; }>; /** * Arguments for paginating a collection. * @property {number} first - The maximum number of items to retrieve in the current page. */ type PaginationArgs = { /** The maximum number of items to retrieve in the current page. */ first?: number; /** A cursor representing the starting point for the next page */ after?: string; }; /** * Represents the types of authentication connections available. * @type {("username-password" | "google-oauth2")} * @property {"username-password"} username-password - Standard username and password authentication. * @property {"email"} email - One time password send through email. * @property {"sms"} sms - One time password send through sms. * @property {"google-oauth2"} google-oauth2 - Google OAuth 2.0 authentication. */ type AuthConnection = "username-password" | "email" | "sms" | "google-oauth2"; export { type AuthConnection, type Balance, type ClientShareProvider, type EstimateERC20TransferParams, type EstimateNftTransferParams, type EvmFeeDetails, type FeeDetails, type FeesEstimation, type FeesUnit, IKeybanApi, KeybanAccount, KeybanApiStatus, KeybanClient, type KeybanClientConfig, GqlKeybanClient_OrderFragment as KeybanOrder, GqlKeybanClient_TokenContractFragment as KeybanToken, type NativeCurrency, type Order, type OrderItem, type PaginationArgs, type StellarFeeDetails, type TransferERC20Params, type TransferNftParams, formatBalance };