# ivem > Modern IOST SDK with lightweight, composable, and type-safe clients. ## Accounts `@ivem/core` provides local account helpers for modern IOST wallet workflows. ### Create Local Account ```ts import { accountFromPrivateKey } from '@ivem/core' const account = accountFromPrivateKey('alice', 'BASE58_PRIVATE_KEY') ``` * Creates a `LocalAccount` with `active` and `owner` key permissions. * Works with `createWalletClient({ account, ... })`. ### Generate Key Pair ```ts import { generateKeyPair, getB58PubKey, getB58SecKey } from '@ivem/core' const keyPair = generateKeyPair() const publicKey = getB58PubKey(keyPair) const privateKey = getB58SecKey(keyPair) ``` ### Restore Key Pair ```ts import { createKeyPair, keyPairFromB58SecKey } from '@ivem/core' const keyPairA = createKeyPair('BASE58_PRIVATE_KEY') const keyPairB = keyPairFromB58SecKey('BASE58_PRIVATE_KEY') ``` `createKeyPair` and `keyPairFromB58SecKey` both build a usable key pair from base58 private key text. ### Account Utilities ```ts import { addKeyPair, getAccountId, getKeyPair } from '@ivem/core' import { createKeyPair } from '@ivem/core' let account = accountFromPrivateKey('alice') const activeKeyPair = createKeyPair('BASE58_PRIVATE_KEY') account = addKeyPair(account, activeKeyPair, 'active') const accountId = getAccountId(account) const active = getKeyPair(account, 'active') ``` ### Sign Capabilities `LocalAccount` supports: * `signTx(tx, permission)` * `signPublishTx(tx)` * `signMessage(message, permission)` These are used internally by `walletClient.signTransaction` and `walletClient.signMessage`. ## Getting Started \[Get started with ivem in just a few lines of code.] ### Overview `ivem` is a modern TypeScript SDK for IOST that provides lightweight clients, explicit wallet actions, and focused transport primitives. * **Developer experience** Type-safe APIs, clear docs, and composable building blocks. * **Stability** Targeted test coverage across clients, wallet core, and polling behavior. * **Lightweight** Transport and action modules stay small and tree-shakable. * **Performance** Minimal request layers and efficient sign/broadcast paths. ### Installation :::code-group ```bash [pnpm] pnpm add @ivem/core ``` ```bash [npm] npm i @ivem/core ``` ```bash [yarn] yarn add @ivem/core ``` ```bash [bun] bun add @ivem/core ``` ::: ### Quick Start #### 1. Set up your Client & Transport ```ts import { createPublicClient, http, mainnet } from '@ivem/core' const publicClient = createPublicClient({ chain: mainnet, transport: http(), }) const chainInfo = await publicClient.getChainInfo() console.log(chainInfo) ``` `http()` defaults to `chain.rpcUrls[0]` (for `mainnet`, this is `https://api.iost.io`). You can still pass a custom URL when needed: `http('https://your-rpc.example')`. #### 2. Create a Wallet Client ```ts import { accountFromPrivateKey, createWalletClient, http, mainnet, } from '@ivem/core' const account = accountFromPrivateKey('your_account', 'your_private_key') const walletClient = createWalletClient({ account, chain: mainnet, transport: http(), }) ``` #### 3. Consume Actions ```ts const result = await walletClient.callContract({ contract: 'token.iost', action: 'transfer', args: ['iost', 'from', 'to', '1.00000000', 'memo'], gasRatio: 1, gasLimit: 2000000, }) console.log(result.hash) ``` ## Installation ### Package :::code-group ```bash [pnpm] pnpm add @ivem/core ``` ```bash [npm] npm i @ivem/core ``` ```bash [yarn] yarn add @ivem/core ``` ```bash [bun] bun add @ivem/core ``` ::: Use `@ivem/core` for public npm installs. ### Node.js Recommended: Node.js 18+ ### Local Development ```bash pnpm install pnpm build pnpm test ``` ## custom Transport `custom(provider)` connects a browser wallet provider that exposes `request({ method, params })`. ```ts import { custom } from '@ivem/core' const transport = custom(window.IWalletJS) ``` Useful for: * injected browser wallets * provider-driven signing flows * account access via `provider.request({ method: "accounts" })` * wallet actions such as `signMessage`, `sendTransaction`, and `callContract` ## http Transport `http(url?)` creates an HTTP transport. ```ts import { http } from '@ivem/core' const transport = http() ``` ### Default URL Behavior * `http()` with no argument: uses `chain.rpcUrls[0]` from client config. * `http('https://your-rpc.example')`: overrides the default URL. For `mainnet`, the default URL is `https://api.iost.io`, so passing a URL is optional. ### Request Injection ```ts transport.request = async (method, path, data) => { const res = await fetch(`https://rpc-proxy.example/${path}`, { method, body: data ? JSON.stringify(data) : undefined, headers: { 'Content-Type': 'application/json' }, }) if (!res.ok) throw new Error(await res.text()) return res.json() } ``` Resolution order: 1. use `transport.request` when provided 2. otherwise use SDK built-in fetcher with `url` or `chain.rpcUrls[0]` ## createPublicClient ```ts function createPublicClient(config: { chain: { id: string; name: string; rpcUrls: string[] } transport: ReturnType | ReturnType }) ``` Builds a query-focused client for chain reads and receipt polling. ## createWalletClient ```ts function createWalletClient(config: { account?: LocalAccount chain: { id: string; name: string; rpcUrls: string[] } transport: ReturnType | ReturnType }) ``` Builds a wallet client for tx build/sign/send. The client handles the full wallet transaction lifecycle with clear boundaries between preparation, authorization, and broadcast, while keeping the public API focused on actions like `callContract`, `writeContract`, and `sendTransaction`. ## Custom Transport Client Use `custom(provider)` with an injected wallet provider such as `window.IWalletJS`. ```ts import { createWalletClient, custom, mainnet } from '@ivem/core' const client = createWalletClient({ chain: mainnet, transport: custom(window.IWalletJS), }) ``` The provider should implement: ```ts provider.request({ method: 'accounts', }) ``` This transport is intended for wallet-provider flows. For direct IOST node HTTP RPC access, use `http()`. ## Public Client `createPublicClient` is used for chain read/query operations. ### Create ```ts import { createPublicClient, http, mainnet } from '@ivem/core' const publicClient = createPublicClient({ chain: mainnet, transport: http(), }) ``` `http()` uses `chain.rpcUrls[0]` by default. For `mainnet`, this is `https://api.iost.io`. You can override it via `http('https://your-rpc.example')`. ### Methods The following methods are aligned to IOST node APIs (reference: [IOST API](https://developers.iost.io/docs/en/6-reference/API.html)). #### Chain & Node * `getChainInfo()` -> `GET /getChainInfo` * `getNodeInfo()` -> `GET /getNodeInfo` * `getRAMInfo()` -> `GET /getRAMInfo` #### Account & Balance * `getAccount({ address, reversible? })` -> `GET /getAccount/{address}/{reversible}` * `getBalance({ address, token?, useLongestChain? })` -> `GET /getTokenBalance/{address}/{token}/{useLongestChain}` #### Block & Transaction * `getBlockByHash({ hash, complete? })` -> `GET /getBlockByHash/{hash}/{complete}` * `getBlockByNum({ blockNumber, complete? })` -> `GET /getBlockByNum/{blockNumber}/{complete}` * `getTxByHash({ hash })` -> `GET /getTxByHash/{hash}` * `getTxReceiptByHash({ hash })` -> `GET /getTxReceiptByHash/{hash}` * `getTxReceiptByTxHash({ hash })` -> `GET /getTxReceiptByTxHash/{hash}` #### Contract State * `getContract({ contractID, useLongestChain? })` -> `GET /getContract/{contractID}/{useLongestChain}` * `getContractStorage({ contractID, key, field?, byLongestChain? })` -> `POST /getContractStorage` * `getContractStorageFields({ contractID, key, byLongestChain? })` -> `POST /getContractStorageFields` #### NFT (Token721) * `getToken721Balance({ address, token?, useLongestChain? })` -> `GET /getToken721Balance/{address}/{token}/{useLongestChain}` * `getToken721Metadata({ token, tokenID, useLongestChain? })` -> `GET /getToken721Metadata/{token}/{tokenID}/{useLongestChain}` * `getToken721Owner({ token, tokenID, useLongestChain? })` -> `GET /getToken721Owner/{token}/{tokenID}/{useLongestChain}` ### Receipt Polling `waitForTransactionReceipt({ hash, retryCount?, retryDelay?, timeout?, stage? })` * `stage: "executed"` polls `getTxByHash` and reads `transaction.tx_receipt`. * `stage: "irreversible"` polls `getTxReceiptByTxHash`. * If `stage` is omitted, the default is `"executed"`. * Resolves when `status_code === "SUCCESS"`. * Rejects on explicit failure `status_code` or timeout/retry overflow. * Default timeout: `90_000ms`. ## Wallet Client `createWalletClient` is used for transaction build/sign/send flows. ### Create ```ts import { accountFromPrivateKey, createWalletClient, http, mainnet } from '@ivem/core' const account = accountFromPrivateKey('your_account', 'your_private_key') const walletClient = createWalletClient({ account, chain: mainnet, transport: http(), }) ``` `http()` defaults to `chain.rpcUrls[0]` (`https://api.iost.io` on `mainnet`). Custom URL is optional: `http('https://your-rpc.example')`. ### Core Methods * `getAccountId()`\ Returns current account id from local account or injected wallet provider. * `signMessage(message, permission?)`\ Signs arbitrary message with selected permission (default: `active`). * `prepareAccountCreation(params)`\ Builds account creation tx (`auth.iost/signUp`, optional `ram.iost/buy`, `gas.iost/pledge`). * `prepareTransactionRequest(params)`\ Builds generic ABI tx with gas, expiration, delay, chainID and approvals. * `signTransaction(transaction, permission?)`\ Signs tx (local account or injected wallet). * `sendRawTransaction(transaction)`\ Broadcasts signed tx (`POST /sendTx` for local account).\ If node returns time-expired error, client refreshes time diff and retries once. * `callContract(params)`\ Recommended unified contract call API.\ On local account path, this builds tx -> signs -> broadcasts. * `writeContract(params)`\ Direct write action for contract transactions. * `readContract(params)`\ Compatibility API. On IOST, contract invocation is ABI tx-oriented, so this method currently routes to `callContract`. * `transfer(params)`\ Convenience wrapper over `token.iost/transfer`. * `refreshServerTimeDiff({ force? })`\ Refreshes server time offset cache used to set tx time/expiration. ## callContract Recommended unified contract-call entry on wallet client. ```ts const result = await walletClient.callContract({ contract: 'token.iost', action: 'transfer', args: ['iost', 'from', 'to', '1', 'memo'], gasRatio: 1, gasLimit: 2000000, }) ``` Returns: ```ts { hash: string } ``` ## readContract `readContract(params)` is kept for compatibility. On IOST, contract interaction is ABI-call based, so this API may still represent on-chain behavior depending on execution flow. For pure read/query use cases, prefer `publicClient` read methods. ## refreshServerTimeDiff Manually refresh wallet server time diff state. ```ts await walletClient.refreshServerTimeDiff() await walletClient.refreshServerTimeDiff({ force: true }) ``` Behavior: * per-client cached offset * midpoint estimation * TTL refresh * force refresh support ## transfer Convenience method for `token.iost` transfer flow. ```ts await walletClient.transfer({ token: 'iost', from: 'fromAccount', to: 'toAccount', amount: '1.00000000', memo: 'test', gasRatio: 1, gasLimit: 100000, }) ``` ## writeContract `writeContract(params)` sends a direct contract write transaction. ```ts const result = await walletClient.writeContract({ contract: 'token.iost', action: 'transfer', args: ['iost', 'from', 'to', '1', 'memo'], }) ``` Returns: ```ts { hash: string } ```