@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
TypeScript
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 };