@kalp_studio/tresori-sdk-js
v1.4.3
Published
The TreSori SDK provides a unified, high-level API for blockchain wallet operations. It supports three distinct wallet types to accommodate various security models and use cases:
Readme
TreSori SDK
Overview
The TreSori SDK provides a unified, high-level API for blockchain wallet operations. It supports three distinct wallet types to accommodate various security models and use cases:
- Self-Custodial: Full user control with local key management
- Custodial: Service-managed wallets linked to user accounts
- MPC: Enhanced security through distributed key shards with verification flows
Table of Contents
Features
Self-Custodial Wallets
| Feature | Description | | -------------------------- | ---------------------------------------------------------------- | | Mnemonic Generation | Generate BIP39-compliant mnemonic phrases (12/15/18/21/24 words) | | Wallet Creation | Create wallets from mnemonic with blockchain registration | | Mnemonic Import | Restore wallets from existing mnemonic phrases | | Private Key Import | Import wallets directly from private keys | | Token Transfers | Transfer native tokens and ERC-20 tokens | | Smart Contract Interaction | Sign and send contract transactions |
Custodial Wallets
| Feature | Description | | --------------------------- | ------------------------------------------------ | | Managed Wallet Creation | Create server-managed wallets linked to user IDs | | Token Transfers | Transfer native and ERC-20 tokens | | Smart Contract Transactions | Execute contract write operations |
MPC Wallets
| Feature | Description |
| -------------------------- | ------------------------------------------------------------------------------------------------------------- |
| Email / phone verification | OTP flows; after verify you receive session data for MPC signing |
| Native & ERC-20 transfers | Sign and broadcast via chain RPC using clientShare, sessionId, rpcUrl |
| Gasless ERC-20 (optional) | When the project has gasless enabled, transferMpcTokens with a token address uses permit + relay (EIP-2612) |
| Smart contract writes | Encode locally, MPC sign-simple, broadcast raw tx on your rpcUrl; returns rawResult + decodedResult |
| Smart contract reads | View/pure calls via relayer (readMpcSmartContractTransaction); JSON ABI; rpcUrl required |
Multi-Chain Support
- Dynamic chain configuration loaded at runtime
- Support for multiple EVM-compatible networks
- Chain-specific parameters (chain ID, explorer URL, currency)
Installation
Published package name (see package.json):
npm install @kalp_studio/tresori-sdk-jsOr with yarn:
yarn add @kalp_studio/tresori-sdk-jsThe package is ESM-only ("type": "module"). Use in Node.js (14+) or in bundlers (Vite, webpack, Metro, etc.).
The SDK lists @noble/hashes, @noble/ciphers, and related @noble/* packages for MPC DKG (ECDH, SHA-256, AES-256-GCM in pure JS, so React Native does not need crypto.subtle for that path).
The example and example-rn apps in this repo depend on the local package as tresori-sdk-js (file:..); their imports use that name. Published apps should import @kalp_studio/tresori-sdk-js as shown above.
React Native
The SDK works in React Native (iOS and Android). The library does not use DOM-only APIs in its published code.
MPC email flow (new vs returning user)
For sendEmailVerificationRequest and verifyEmail, the SDK calls POST /auth/is-mpc-exist with blockchain, network (mapped from your selected chain), and walletIdentifier (email is trimmed and lowercased before server calls). It sets the internal new-user flag to !isMPCExists, then sends/verifies the OTP with the correct backend shape. You do not pass isNewUser on these methods. If the wallet already exists, verify follows the returning-user path (no DKG). If DKG runs for a new user and fails (e.g. missing crypto.getRandomValues on React Native), the SDK returns the raw verify body and logs a console.warn with the error.
Cryptography (MPC DKG on React Native)
MPC DKG: ECDH and SHA-256 use @noble/curves / @noble/hashes. AES-256-GCM uses @noble/ciphers inside the SDK (pure JavaScript) — you do not need react-native-webcrypto or crypto.subtle. UTF-8 for MPC payloads uses ethers’s toUtf8Bytes / toUtf8String so you do not need global TextDecoder / TextEncoder (Hermes often omits TextDecoder, which previously broke dkg-init after email verify).
Secure random: AES-GCM IVs use crypto.getRandomValues. On React Native you must load react-native-get-random-values once at app entry (Android and iOS). That is the standard, maintained polyfill used across the ecosystem (including with ethers).
Dependencies in your React Native app
npm install react-native-get-random-valuesPolyfills at app entry (before any other imports)
In your very first entry file (e.g. index.js), run:
import 'react-native-get-random-values';This repo’s example-rn does this in polyfills.ts, imported first from index.js.
(Optional) Register ethers secure random
If you use react-native-quick-crypto for performance, you can register it with ethers so mnemonic/key generation uses native bytes (unrelated to MPC AES, which stays in @noble/ciphers):
import crypto from 'react-native-quick-crypto';
import { randomBytes } from 'ethers';
randomBytes.register((length) => new Uint8Array(crypto.randomBytes(length)));Use the SDK as on web
import { TreSori, ChainListInstance } from '@kalp_studio/tresori-sdk-js';
const tresori = TreSori();
await tresori.initialize(API_KEY);
// Chains are loaded by initialize()
const chain = ChainListInstance.all[0];
// Self-custodial, custodial, MPC APIs are unchanged.React Native summary
- Load
react-native-get-random-valuesat entry on Android and iOS socrypto.getRandomValuesexists. - No
react-native-webcryptois required for TreSori MPC DKG. - Phone MPC still requires an explicit
isNewUserflag (nois-mpc-existwiring in the SDK for phone yet).
Quick Start
import { TreSori, ChainListInstance } from '@kalp_studio/tresori-sdk-js';
async function main() {
const tresori = TreSori();
// 1. Initialize the SDK with your API key
await tresori.initialize('YOUR_API_KEY');
// 2. Generate a mnemonic phrase
const mnemonic = tresori.generateMnemonic();
console.log('Mnemonic:', mnemonic);
// 3. Get a chain (after initialize, chains are loaded)
const selectedChain = ChainListInstance.all[0];
// 4. Create a wallet
const wallet = await tresori.createWallet({
chain: selectedChain,
mnemonic,
userId: 'user_123',
});
console.log('Address:', wallet.address);
console.log('Private Key:', wallet.privateKey);
}
main().catch(console.error);API Reference
Create the SDK singleton once, then reuse it for all calls:
import { TreSori } from '@kalp_studio/tresori-sdk-js';
const tresori = TreSori();
await tresori.initialize('YOUR_API_KEY');The examples below assume tresori is already initialized unless noted otherwise.
Initialization
initialize(apiKey: string): Promise<void>
Initializes the SDK with your API key. Must be called before any other operations. Loads supported chains from the backend.
await tresori.initialize('YOUR_API_KEY');| Parameter | Type | Required | Description | | --------- | ------ | -------- | -------------------- | | apiKey | string | ✅ | Your TreSori API key |
Throws: Error if the API key is empty.
get mpcGaslessEnabled(): boolean
Read-only flag: true when the backend enabled-chains response includes gasless for the project (cached when initialize runs, and refreshed via Config when available). Use it to show or hide gasless ERC-20 UI. Actual gasless transfers still require an EIP-2612–compatible token and the arguments described under transferMpcTokens below.
if (tresori.mpcGaslessEnabled) {
// Offer gasless ERC-20 path (see transferMpcTokens)
}Self-Custodial Wallet
generateMnemonic(opts?: { wordCount?: number }): string
Generates a BIP39-compliant mnemonic phrase.
// Generate 12-word mnemonic (default)
const mnemonic12 = tresori.generateMnemonic();
// Generate 24-word mnemonic
const mnemonic24 = tresori.generateMnemonic({ wordCount: 24 });| Parameter | Type | Default | Description | | -------------- | ------ | ------- | --------------------------------------- | | opts.wordCount | number | 12 | Number of words (12, 15, 18, 21, or 24) |
Returns: string — Space-separated mnemonic phrase
createWallet(args: { chain, mnemonic, userId }): Promise<object>
Creates a new wallet and registers it with the backend.
const result = await tresori.createWallet({
chain: selectedChain,
mnemonic: 'word1 word2 word3 ... word12',
userId: 'unique_user_id',
});
console.log('Address:', result.address);
console.log('Private Key:', result.privateKey);
console.log('Mnemonic:', result.mnemonic);| Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------------------- | | chain | Chain | ✅ | Target blockchain network | | mnemonic | string | ✅ | BIP39 mnemonic phrase | | userId | string | ✅ | Unique user identifier |
Returns: Promise<object>
{
mnemonic: string,
privateKey: string,
address: string,
userId: string,
}importWalletFromMnemonic(mnemonic: string): object
Imports an existing wallet from a mnemonic phrase.
const result = tresori.importWalletFromMnemonic(
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
);
console.log('Address:', result.address);
console.log('Private Key:', result.privateKey);| Parameter | Type | Required | Description | | --------- | ------ | -------- | --------------------------- | | mnemonic | string | ✅ | Valid BIP39 mnemonic phrase |
Returns: object
{
privateKey: string,
address: string,
}importWalletFromPrivateKey(privateKey: string): object
Imports a wallet from a private key.
const result = tresori.importWalletFromPrivateKey(
'0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318',
);
console.log('Address:', result.address);| Parameter | Type | Required | Description | | ---------- | ------ | -------- | --------------------------------------------------- | | privateKey | string | ✅ | Hex-encoded private key (with or without 0x prefix) |
Returns: object
{
privateKey: string,
address: string,
}transferTokens(args): Promise<string>
Transfers native tokens (ETH, MATIC, etc.) or ERC-20 tokens to a recipient address.
// Native token transfer
const nativeTxHash = await tresori.transferTokens({
chain: selectedChain,
privateKey: '0x...',
toAddress: '0xRecipientAddress',
amount: '1', // in ether (e.g. 1 ETH); uses ethers parseEther
rpcUrl: 'https://rpc.example.com',
});
console.log('Native Tx Hash:', nativeTxHash);
// ERC-20 token transfer
const erc20TxHash = await tresori.transferTokens({
chain: selectedChain,
privateKey: '0x...',
toAddress: '0xRecipientAddress',
amount: '10', // in token units (e.g. 10 tokens); uses decimals
rpcUrl: 'https://rpc.example.com',
tokenAddress: '0xTokenContractAddress',
decimals: 18, // Optional: defaults to 18
});
console.log('ERC-20 Tx Hash:', erc20TxHash);| Parameter | Type | Required | Description |
| ------------ | ------ | -------- | --------------------------------------------------------------------------- |
| chain | Chain | ✅ | Target blockchain network |
| privateKey | string | ✅ | Sender's private key |
| toAddress | string | ✅ | Recipient's wallet address |
| amount | string | ✅ | Amount in ether/token units (e.g. "1") |
| rpcUrl | string | ✅ | RPC endpoint URL |
| tokenAddress | string | No | Omit or empty for native; required for ERC-20 token transfers |
| decimals | number | No | Token decimals (default 18) when tokenAddress is set |
Returns: Promise<string> — Transaction hash
signContractTransaction(args): Promise<string>
Signs and sends a smart contract transaction (self-custodial).
const txHash = await tresori.signContractTransaction({
contractAddress: '0xContractAddress',
functionName: 'transfer',
abi: ['function transfer(address to, uint256 amount) returns (bool)'],
params: ['0xRecipient', '1000000000000000000'],
privateKey: '0x...',
chain: selectedChain,
rpcUrl: 'https://rpc.example.com',
value: '0', // Optional: wei sent with the call (payable functions)
transaction: { maxGas: 100000n }, // Optional: extra transaction parameters
});| Parameter | Type | Required | Description |
| --------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------- |
| contractAddress | string | ✅ | Deployed contract address |
| functionName | string | ✅ | Contract function to call |
| abi | string[] | ✅ | Human-readable ethers fragments only (e.g. 'function foo(uint256) external') — not { type, name, … } JSON |
| params | any[] | ✅ | Function arguments (ethers-compatible) |
| privateKey | string | ✅ | Sender's private key |
| chain | Chain | ✅ | Target blockchain network |
| rpcUrl | string | ✅ | RPC endpoint URL |
| value | string | No | Wei sent with tx (payable functions) |
| transaction | object | No | Optional Ethers TransactionRequest fields (e.g., maxGas, gasPrice, nonce) |
Returns: Promise<string> — Transaction hash
Custodial Wallet
createCustodialWallet(args: { chain, userId }): Promise<object>
Creates a service-managed custodial wallet.
const result = await tresori.createCustodialWallet({
chain: selectedChain,
userId: 'unique_user_id',
});
console.log('Custodial Address:', result.address);| Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------------------- | | chain | Chain | ✅ | Target blockchain network | | userId | string | ✅ | Unique user identifier |
Returns: Promise<object> — Wallet details including address
transferCustodialTokens(args): Promise<object>
Transfers tokens from a custodial wallet.
const result = await tresori.transferCustodialTokens({
fromAddress: '0xCustodialWalletAddress',
toAddress: '0xRecipientAddress',
amount: '1000000000000000000', // 1 token in wei
chain: selectedChain,
tokenAddress: '0xTokenContractAddress', // Optional: for ERC-20 tokens
});| Parameter | Type | Required | Description | | ------------ | ------ | -------- | ----------------------------------------------- | | fromAddress | string | ✅ | Custodial wallet address | | toAddress | string | ✅ | Recipient address | | amount | string | ✅ | Amount in wei (as string) | | chain | Chain | ✅ | Target blockchain network | | tokenAddress | string | No | ERC-20 token contract address (omit for native) |
Returns: Promise<object> — Transaction response
writeCustodialSmartContractTransaction(args): Promise<object>
Executes a smart contract write operation from a custodial wallet.
const result = await tresori.writeCustodialSmartContractTransaction({
contractAddress: '0xContractAddress',
functionName: 'transfer',
parameters: ['0xRecipient', 1000],
contractAbi: [...],
fromAddress: '0xCustodialWalletAddress',
chain: selectedChain,
});| Parameter | Type | Required | Description | | --------------- | ------ | -------- | ------------------------- | | contractAddress | string | ✅ | Deployed contract address | | functionName | string | ✅ | Contract function to call | | parameters | any[] | ✅ | Function parameters | | contractAbi | any[] | ✅ | Contract ABI as array | | fromAddress | string | ✅ | Custodial wallet address | | chain | Chain | ✅ | Target blockchain network |
Returns: Promise<object> — Transaction response
MPC Wallet
isMpcExist(args): Promise<object>
Checks whether an MPC wallet already exists for a chain + identifier (typically the same email you use for OTP).
const { isMPCExists, mpcExistRequest } = await tresori.isMpcExist({
chain: selectedChain,
walletIdentifier: '[email protected]',
});| Parameter | Type | Required | Description |
| ---------------- | ------ | -------- | ---------------------------------------------------------- |
| chain | Chain | ✅ | Target chain (blockchain / network mapped for the API) |
| walletIdentifier | string | ✅ | e.g. email address |
Returns: isMPCExists (boolean) and mpcExistRequest — the { blockchain, network, walletIdentifier } body sent to /auth/is-mpc-exist (useful for logging).
Email Verification
sendEmailVerificationRequest(args): Promise<object>
Calls isMpcExist with walletIdentifier = trimmed email, then sends the OTP with the correct new-user vs returning-user payload. chain selects blockchain / network for that check and for DKG when applicable.
const result = await tresori.sendEmailVerificationRequest({
chain: selectedChain,
email: '[email protected]',
userId: 'unique_user_id',
});| Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------------------- | | chain | Chain | ✅ | Target blockchain network | | email | string | ✅ | User's email address | | userId | string | ✅ | Unique user identifier |
Returns: Promise<object> — Backend OTP response. On a normal JSON object response, the SDK also merges isMPCExists and mpcExistRequest for client logging.
verifyEmail(args): Promise<object>
Calls isMpcExist again (same rules as send), then verifies the OTP with the matching new-user vs returning-user behavior.
const result = await tresori.verifyEmail({
chain: selectedChain,
email: '[email protected]',
userId: 'unique_user_id',
otp: '123456',
});| Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------------------- | | chain | Chain | ✅ | Target blockchain network | | email | string | ✅ | User's email address | | userId | string | ✅ | Unique user identifier | | otp | string | ✅ | OTP code from email |
Returns: Promise<object> — Returning users (MPC already exists for that email/chain): response often includes walletAddress, sessionId, and clientShare without DKG. New users: the SDK runs initDKG (dkg-init then dkg-contribute) and returns those fields on success; on failure it returns the raw email-verify payload (see the React Native section). Merged fields isMPCExists and mpcExistRequest are included when the payload is a plain object.
Phone Verification
sendPhoneVerificationCode(args): Promise<object>
Sends an OTP verification code via SMS.
const result = await tresori.sendPhoneVerificationCode({
chain: selectedChain,
countryCode: '+1',
phone: '5551234567',
userId: 'unique_user_id',
isNewUser: true,
});| Parameter | Type | Required | Description | | ----------- | ------- | -------- | --------------------------------------- | | chain | Chain | ✅ | Target blockchain network | | countryCode | string | ✅ | Country code (e.g. '+1', '+44') | | phone | string | ✅ | Phone number without country code | | userId | string | ✅ | Unique user identifier | | isNewUser | boolean | ✅ | Whether this is a new user registration |
Returns: Promise<object> — Response indicating OTP was sent
verifyPhone(args): Promise<object>
Verifies the phone number using the OTP received.
const result = await tresori.verifyPhone({
chain: selectedChain,
countryCode: '+1',
phone: '5551234567',
userId: 'unique_user_id',
otp: '123456',
isNewUser: true,
});| Parameter | Type | Required | Description | | ----------- | ------- | -------- | --------------------------------------- | | chain | Chain | ✅ | Target blockchain network | | countryCode | string | ✅ | Country code | | phone | string | ✅ | Phone number | | userId | string | ✅ | Unique user identifier | | otp | string | ✅ | OTP code from SMS | | isNewUser | boolean | ✅ | Whether this is a new user registration |
Returns: Promise<object> — Verification result with wallet details
MPC token transfers
transferMpcTokens(args): Promise<Record<string, any>>
Single entry point for native, ERC-20 (user pays gas), and gasless ERC-20 MPC transfers. You must complete email (or phone) verification first and persist clientShare, sessionId, and the MPC wallet fromAddress. All paths need a working rpcUrl to the same chain as chain.
Modes
- Native (chain currency) — Omit
tokenAddressor pass an empty string. Amount is parsed withdecimals(default 18), e.g."1"for one ETH-style unit. The wallet pays gas. - ERC-20 (user pays gas) — Set
tokenAddressto the token contract. Setdecimalsif not 18. Amount is a human-readable token string in those decimals (e.g."1.5"). You can explicitly passisGasless: falseto guarantee bypassing gasless mode (useful ifmpcGaslessEnabledis globally enabled for the project). - Gasless ERC-20 — When
mpcGaslessEnabledistrue,tokenAddressis non-empty, andisGaslessis not explicitly set tofalse, the implementation routes to the gasless pipeline (permit + relayer). UseplatformFeefor the fee in token units (same decimals); defaults apply per backend/facilitator rules. Token must support EIP-2612 permits.
When gasless is used via this method, TreSoriImpl supplies built-in sponsor and relayer addresses for the relay flow. To use your own relayer/sponsor, call gaslessTransferTokens instead.
// Native (default 18 decimals → "1" = 1 ETH-style unit)
const native = await tresori.transferMpcTokens({
fromAddress: mpcAddress,
toAddress: '0xRecipient',
amount: '1',
chain: selectedChain,
clientShare,
sessionId,
userIdentity: 'email',
rpcUrl: 'https://sepolia.drpc.org',
});
// ERC-20 (payer pays gas)
const erc20 = await tresori.transferMpcTokens({
fromAddress: mpcAddress,
toAddress: '0xRecipient',
amount: '10',
chain: selectedChain,
clientShare,
sessionId,
userIdentity: 'email',
rpcUrl: 'https://sepolia.drpc.org',
tokenAddress: '0xToken',
decimals: 18,
});
// Gasless ERC-20 (only when mpcGaslessEnabled && tokenAddress set)
const gasless = await tresori.transferMpcTokens({
fromAddress: mpcAddress,
toAddress: '0xRecipient',
amount: '10',
chain: selectedChain,
clientShare,
sessionId,
userIdentity: 'email',
rpcUrl: 'https://sepolia.drpc.org',
tokenAddress: '0xEip2612Token',
decimals: 18,
platformFee: '1',
});| Parameter | Type | Required | Description |
| ------------ | ------ | -------- | ---------------------------------------------------------------------------- |
| fromAddress | string | ✅ | MPC wallet address |
| toAddress | string | ✅ | Recipient address |
| amount | string | ✅ | Human amount; decimals applies (default 18) for both native and ERC-20 |
| chain | Chain | ✅ | Target chain |
| clientShare | string | ✅ | Client share from verify / DKG |
| sessionId | string | ✅ | MPC session id |
| userIdentity | string | ✅ | Identity channel, e.g. 'email' |
| rpcUrl | string | ✅ | Chain JSON-RPC URL |
| tokenAddress | string | No | Omit or empty for native; required for ERC-20 and gasless |
| decimals | number | No | Token decimals (default 18) when tokenAddress is set |
| platformFee | string | No | Gasless: fee in token units (default '1' when gasless path is used) |
| isGasless | boolean| No | Pass false to explicitly bypass gasless transfer and use standard gas path|
Returns: Promise<Record<string, any>> — Backend/RPC payload; often includes txHash / transactionHash (shape may vary by path).
gaslessTransferTokens(args): Promise<Record<string, any>>
Lower-level gasless transfer with explicit relayerAddress and sponsorAddress. The userShards argument is the same secret material as clientShare in other MPC APIs (naming follows the abstract API).
await tresori.gaslessTransferTokens({
fromAddress: mpcAddress,
toAddress: '0xRecipient',
amount: '10',
chain: selectedChain,
userShards: clientShare,
userIdentity: 'email',
rpcUrl: 'https://...',
sessionId,
tokenAddress: '0xToken',
relayerAddress: '0x...',
sponsorAddress: '0x...',
decimals: 18,
platformFee: '1',
});| Parameter | Type | Required | Description |
| -------------- | ------ | -------- | ---------------------------------------------- |
| fromAddress | string | ✅ | MPC wallet address |
| toAddress | string | ✅ | Recipient |
| amount | string | ✅ | Human amount (token decimals) |
| chain | Chain | ✅ | Target chain |
| userShards | string | ✅ | Client share (same as clientShare elsewhere) |
| userIdentity | string | ✅ | e.g. 'email' |
| rpcUrl | string | ✅ | Chain RPC |
| sessionId | string | ✅ | MPC session |
| tokenAddress | string | ✅ | EIP-2612 token |
| relayerAddress | string | ✅ | Relayer contract / service address |
| sponsorAddress | string | ✅ | Sponsor address |
| decimals | number | No | Default 18 |
| platformFee | string | No | Fee in token units |
writeMpcSmartContractTransaction(args): Promise<object>
Encodes the call locally, pre-simulates with eth_call (to decode the return value and catch reverts before spending gas), signs with MPC (/v2/wallet/mpc/sign-simple), and broadcasts via your chain RPC (no relayer write-transaction).
const result = await tresori.writeMpcSmartContractTransaction({
contractAddress: '0xContractAddress',
functionName: 'transfer',
abi: ['function transfer(address to, uint256 amount) returns (bool)'],
params: ['0xRecipient', '1000000000000000000'],
fromAddress: '0xMpcWalletAddress',
chain: selectedChain,
clientShare: '...', // from verify / DKG
sessionId: '...',
rpcUrl: 'https://...',
value: '0', // optional: wei sent with call (payable)
});
console.log('Tx:', result.txHash);
console.log('Decoded return:', result.decodedResult); // e.g. true for returns (bool)
console.log('Raw hex:', result.rawResult); // eth_call simulation output| Parameter | Type | Required | Description |
| --------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------- |
| contractAddress | string | ✅ | Contract address |
| functionName | string | ✅ | Function to call |
| abi | string[] | ✅ | Human-readable ethers fragments only (e.g. 'function foo(uint256) external') — not { type, name, … } JSON |
| params | any[] | ✅ | Function arguments (ethers-compatible) |
| fromAddress | string | ✅ | MPC wallet address |
| chain | Chain | ✅ | Target blockchain network |
| clientShare | string | ✅ | Client share from verify / DKG |
| sessionId | string | ✅ | MPC session id |
| rpcUrl | string | ✅ | Chain JSON-RPC URL |
| value | string | No | Wei sent with tx (payable functions) |
Returns: Promise<object>
{
txHash: string, // from eth_sendRawTransaction
rawResult: string, // raw hex from eth_call pre-simulation
decodedResult: unknown, // decoded Solidity return value (see below)
}decodedResult follows the same rules as readMpcSmartContractTransaction: no outputs → null; one output → unwrapped value; multiple outputs → array. bigint values are converted to strings for JSON safety. If pre-simulation reverts, the transaction is not sent and the promise rejects.
writeGaslessMpcSmartContractTransaction(args): Promise<object>
Executes a gasless EIP-2771 smart contract write transaction by encoding the call locally, signing it via the MPC backend, and dispatching it through the Kalp Relayer.
SDK automatically injects default relayer and sponsor addresses via internal Gasless Constants if they are not provided. The relayer execution response includes the contract return data, which the SDK decodes into decodedResult.
const result = await tresori.writeGaslessMpcSmartContractTransaction({
contractAddress: '0xContractAddress',
functionName: 'transfer',
abi: ['function transfer(address to, uint256 amount) returns (bool)'],
params: ['0xRecipient', '1000000000000000000'],
fromAddress: '0xMpcWalletAddress',
chain: selectedChain,
clientShare: '...', // from verify / DKG
sessionId: '...',
rpcUrl: 'https://...',
// relayerAddress: '0x...', // Optional: defaults to internal Gasless Constants for the chain
// sponsorAddress: '0x...', // Optional: defaults to internal Gasless Constants
});
console.log('Tx:', result.txHash);
console.log('Decoded return:', result.decodedResult);
console.log('Raw hex:', result.rawResult);| Parameter | Type | Required | Description |
| --------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------- |
| contractAddress | string | ✅ | Contract address |
| functionName | string | ✅ | Function to call |
| abi | string[] | ✅ | Human-readable ethers fragments only (e.g. 'function foo(uint256) external') — not { type, name, … } JSON |
| params | any[] | ✅ | Function arguments (ethers-compatible) |
| fromAddress | string | ✅ | MPC wallet address |
| chain | Chain | ✅ | Target blockchain network |
| clientShare | string | ✅ | Client share from verify / DKG |
| sessionId | string | ✅ | MPC session id |
| rpcUrl | string | ✅ | Chain JSON-RPC URL |
| relayerAddress | string | No | Relayer contract / service address (defaults to SDK gasless constants) |
| sponsorAddress | string | No | Sponsor address (defaults to SDK gasless constants) |
Returns: Promise<object>
{
txHash: string,
blockNumber?: string | number,
gasUsed?: string | number,
message: string,
rawResult: string, // raw hex from relayer execution (defaults to '0x' if absent)
decodedResult: unknown, // decoded Solidity return value (same rules as writeMpcSmartContractTransaction)
}readMpcSmartContractTransaction(args): Promise<object>
Read-only smart contract call via backend relayer (/relayer/read-contract). No MPC signing or session
required. rpcUrl is required so the backend executes the read against your chain endpoint.
const result = await tresori.readMpcSmartContractTransaction({
contractAddress: '0xContractAddress',
functionName: 'getRequestStatus',
params: ['12'],
contractAbi: [
{
inputs: [{ internalType: 'uint256', name: 'requestId', type: 'uint256' }],
name: 'getRequestStatus',
outputs: [{ internalType: 'uint8', name: '', type: 'uint8' }],
stateMutability: 'view',
type: 'function',
},
],
chain: selectedChain,
rpcUrl: 'https://polygon-amoy.drpc.org',
});| Parameter | Type | Required | Description |
| --------------- | ------ | -------- | -------------------------------------------------------------------- |
| contractAddress | string | ✅ | Contract address |
| functionName | string | ✅ | View/pure function to call |
| params | any[] | ✅ | Function arguments (use [] when none) |
| contractAbi | any[] | ✅ | JSON ABI objects — not human-readable fragment strings |
| chain | Chain | ✅ | Target blockchain network |
| rpcUrl | string | ✅ | Chain JSON-RPC URL for the relayer read |
Returns: Promise<unknown> — decoded return value from the relayer. Type matches the Solidity return type, for example:
bool→boolean(e.g.true)uint8/uint256→stringornumber(backend-dependent)bytes32,address→ hexstring- struct / tuple →
object - multiple outputs →
array
If the backend wraps the payload (e.g. { result: … }), the SDK unwraps result and returns the
inner value directly.
Wallet Balance
getWalletBalance(args: { address, chain }): Promise<object>
Retrieves the balance of a wallet.
const result = await tresori.getWalletBalance({
address: '0xWalletAddress',
chain: selectedChain,
});
console.log('Balance:', result.balance);| Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------------------- | | address | string | ✅ | Wallet address to query | | chain | Chain | ✅ | Target blockchain network |
Returns: Promise<object> — Balance information
Chain Management
After initialize() is called, supported chains are loaded from the backend. Use ChainListInstance or the chain proxy to obtain a Chain for API calls.
import { TreSori, ChainListInstance, chain } from '@kalp_studio/tresori-sdk-js';
const tresori = TreSori();
await tresori.initialize('YOUR_API_KEY');
// All available chains
const allChains = ChainListInstance.all;
for (const c of ChainListInstance.all) {
console.log(`${c.blockchain} - ${c.network}`);
console.log(' Chain ID:', c.chainId);
console.log(' Currency:', c.currency);
console.log(' Explorer:', c.explorerUrl);
}
// Get chain by blockchain and network (e.g. Ethereum Mainnet)
const ethereumMainnet = chain.ethereum.mainnet;
// Get chain by chainId
const chainById = ChainListInstance.ofChainId('1');Chain shape
Chains are plain objects with the following fields:
| Property | Type | Description | | ----------- | ------ | -------------------------- | | id | number | Internal ID | | blockchain | string | e.g. "Ethereum", "Polygon" | | network | string | e.g. "Mainnet", "Amoy" | | chainId | string | e.g. "1", "80002" | | explorerUrl | string | Block explorer URL | | currency | string | e.g. "ETH", "MATIC" | | logo | string | Logo URL | | createdAt | string | Creation timestamp |
Error Handling
The SDK throws errors for invalid or failed operations. Wrap calls in try/catch (or handle promise rejections).
try {
const tresori = TreSori();
await tresori.initialize('YOUR_API_KEY');
const wallet = await tresori.createWallet({
chain: selectedChain,
mnemonic,
userId,
});
} catch (err) {
console.error('Error:', err.message);
}Common errors
| Error | Cause |
| --------------------------------------------------------------------- | --------------------------------------------------------------------- |
| Wallet not initialized. Call initialize() first. | SDK methods called before initialize() |
| API key is required. Call initialize() first. | Empty API key passed to initialize() |
| ChainList not loaded. Call "await ChainList.instance.load()" first. | Chains accessed before initialize() (load runs inside initialize) |
| Network/HTTP errors | API connectivity or backend failures |
Response Formats
Successful wallet creation
{
mnemonic: 'word1 word2 ... word12',
privateKey: '4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318',
address: '0x742d35Cc6634C0532925a3b844Bc9e7595f8fE56',
userId: 'user_123',
}Wallet import (mnemonic / private key)
{
privateKey: '4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318',
address: '0x742d35Cc6634C0532925a3b844Bc9e7595f8fE56',
}Example apps
Web (/example)
React + Vite demo: initialization, self-custodial and custodial flows, MPC email/phone verification, native and ERC-20 MPC transfers (including gasless when enabled), MPC contract writes with fragment ABI, MPC contract reads (JSON ABI), and chain selection.
cd example
npm install
npm run devReact Native (/example-rn)
Expo-based (SDK 55, React Native with New Architecture) Android/iOS demo with the same TreSori APIs. The checked-in app targets Android in app.json. Entry index.js loads ./polyfills first so react-native-get-random-values runs before other imports (Android and iOS). MPC DKG uses @noble/ciphers in the SDK, not react-native-webcrypto. Use npm run android:dev / expo run:android or expo run:ios when you need a full native project; after major Expo upgrades run npm run prebuild:android (or npx expo prebuild --platform android) so android/ stays in sync. See React Native above and example-rn/MPC_FLOW_RN.md. On the MPC tab, new vs returning email flow is determined by /auth/is-mpc-exist; the demo logs MPC: New user or MPC: User exists after Send OTP / Verify.
# from repo root
npm run build
cd example-rn
npm install
npm run android:dev
# iOS: npm run iosBuild the SDK from the repo root first when using a local file:.. dependency:
npm run build
cd example && npm install && npm run devExports
The package exports:
TreSori— Function that returns the singleton SDK instance. Typical usage:const tresori = TreSori();thentresori.initialize(),tresori.createWallet(), etc.TreSoriImpl— Concrete class withTreSoriImpl.instance(same singleton asTreSori(); optional if you prefer a static entry point).ChainListInstance— Chain list:load(),all,get,ofChainId,isLoaded, and related helpers.chain— Proxy:chain.<blockchain>.<network>(e.g.chain.ethereum.mainnet).parseMpcAbiFragments/parseMpcAbiFragmentsFromText— Normalize MPC contractabitostring[]fragments (human-readable ethers fragments only; rejects JSON ABI objects). The text helper accepts one fragment per line, strict JSONstring[], or bracketed lists (including common single-quote paste formats).- Abstract
TreSoritype and re-exports from./corewhere applicable for advanced integration.
License
See LICENSE for details.
Support
For issues, feature requests, or questions, please contact the TreSori team.
