@toon-protocol/client 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -15,6 +15,108 @@ interface WalletBalance {
15
15
  /** Token decimals, when resolved. */
16
16
  assetScale?: number;
17
17
  }
18
+ /**
19
+ * One asset amount within a chain's wallet view — the native coin or one token.
20
+ * `amount` is a base-unit integer, decimal string.
21
+ */
22
+ interface WalletTokenAmount {
23
+ /** Asset symbol (e.g. `'ETH'`, `'SOL'`, `'MINA'`, `'USDC'`), when known. */
24
+ symbol?: string;
25
+ /** Base-unit integer, decimal string. */
26
+ amount: string;
27
+ /** Decimals for formatting (ETH 18, SOL 9, MINA 9, USDC 6). */
28
+ decimals?: number;
29
+ /** Token contract / SPL mint address. Absent for the native coin. */
30
+ address?: string;
31
+ }
32
+ /**
33
+ * The full wallet view for ONE chain the identity is configured for: the native
34
+ * coin plus every configured token, keyed by the chain's full key (e.g.
35
+ * `'evm:31337'`). `unreadable` marks a chain whose RPC could not be reached at
36
+ * all — the caller renders a per-chain notice rather than crashing.
37
+ */
38
+ interface WalletChainBalances {
39
+ chain: 'evm' | 'solana' | 'mina';
40
+ /** Full chain key, e.g. `'evm:31337'`, `'solana'`, `'mina'`. */
41
+ chainKey: string;
42
+ address: string;
43
+ /** Native-coin balance, when readable. */
44
+ native?: WalletTokenAmount;
45
+ /** Configured token balances (e.g. USDC), each best-effort. */
46
+ tokens: WalletTokenAmount[];
47
+ /** True when nothing on this chain could be read (RPC unreachable). */
48
+ unreadable?: boolean;
49
+ /** First read error, when any read failed (for diagnostics). */
50
+ error?: string;
51
+ }
52
+ /** Read an ERC-20 token balance (balance + decimals + symbol) for `owner`. */
53
+ declare function readEvmTokenBalance(opts: {
54
+ rpcUrl: string;
55
+ chainKey: string;
56
+ tokenAddress: string;
57
+ owner: string;
58
+ }): Promise<WalletBalance>;
59
+ /** Read the native ETH balance (wei) for `owner` via `eth_getBalance`. */
60
+ declare function readEvmNativeBalance(opts: {
61
+ rpcUrl: string;
62
+ chainKey: string;
63
+ owner: string;
64
+ }): Promise<WalletTokenAmount>;
65
+ /** Read the native SOL balance (lamports) for `owner` via the `getBalance` RPC. */
66
+ declare function readSolanaNativeBalance(opts: {
67
+ rpcUrl: string;
68
+ owner: string;
69
+ fetchImpl?: typeof fetch;
70
+ }): Promise<WalletTokenAmount>;
71
+ /** Read the SPL-token balance for `owner`'s token account(s) of `mint`. */
72
+ declare function readSolanaTokenBalance(opts: {
73
+ rpcUrl: string;
74
+ mint: string;
75
+ owner: string;
76
+ fetchImpl?: typeof fetch;
77
+ }): Promise<WalletBalance>;
78
+ /** Read the native MINA balance (nanomina) for `owner` via the Mina GraphQL API. */
79
+ declare function readMinaBalance(opts: {
80
+ graphqlUrl: string;
81
+ owner: string;
82
+ fetchImpl?: typeof fetch;
83
+ }): Promise<WalletBalance>;
84
+ /**
85
+ * Per-chain inputs for {@link readWalletBalances}: the resolved RPC URL, the
86
+ * identity's address on that chain, and the configured token (USDC) — sourced by
87
+ * the caller from the network topology / presets, never hardcoded here. A chain
88
+ * key absent from the object is simply not read.
89
+ */
90
+ interface WalletBalanceSources {
91
+ evm?: {
92
+ chainKey: string;
93
+ rpcUrl: string;
94
+ owner: string;
95
+ tokenAddress?: string;
96
+ };
97
+ solana?: {
98
+ chainKey?: string;
99
+ rpcUrl: string;
100
+ owner: string;
101
+ tokenMint?: string;
102
+ };
103
+ mina?: {
104
+ chainKey?: string;
105
+ graphqlUrl: string;
106
+ owner: string;
107
+ };
108
+ /** Injectable fetch (Solana/Mina JSON-RPC & GraphQL) for tests. */
109
+ fetchImpl?: typeof fetch;
110
+ }
111
+ /**
112
+ * Read the full wallet view — native coin + configured tokens — for every chain
113
+ * in `sources`, keyed per chain. FREE: read-only RPC, no signing. Each chain is
114
+ * read independently and in parallel; a chain whose RPC is unreachable degrades
115
+ * to `{ unreadable: true, error }` instead of failing the others. Within a chain
116
+ * the native and token reads are independent, so a native read can succeed even
117
+ * if the token read fails (and vice versa).
118
+ */
119
+ declare function readWalletBalances(sources: WalletBalanceSources): Promise<WalletChainBalances[]>;
18
120
 
19
121
  /**
20
122
  * Solana payment-channel parameters supplied via `ToonClientConfig.solanaChannel`.
@@ -1317,6 +1419,22 @@ declare class ToonClient {
1317
1419
  * are derived there).
1318
1420
  */
1319
1421
  getBalances(): Promise<WalletBalance[]>;
1422
+ /**
1423
+ * The FULL multi-chain wallet view (#299): for every chain the identity is
1424
+ * configured for, the native coin (ETH / SOL / MINA) AND every configured
1425
+ * token (USDC), grouped per chain with the identity's address on that chain.
1426
+ * A superset of {@link getBalances} — which stays scoped to the channel's
1427
+ * settlement token — kept as a separate reader so channel-settlement callers
1428
+ * are unaffected.
1429
+ *
1430
+ * FREE: read-only RPC, no signing, no payment. Works on an UNSTARTED client:
1431
+ * the Solana/Mina addresses (which the signers only register during
1432
+ * `start()`) are derived on demand from the retained mnemonic — the SAME keys
1433
+ * `start()` would register and that `rig fund` prints — so all configured
1434
+ * chains appear even before a start. Best-effort per chain: an unreachable
1435
+ * RPC yields `{ unreadable: true }` for that chain, never failing the others.
1436
+ */
1437
+ getWalletBalances(): Promise<WalletChainBalances[]>;
1320
1438
  /**
1321
1439
  * Resolves an ILP destination address to a peer ID.
1322
1440
  * Convention: destination "g.toon.peer1" → peerId "peer1" (last segment).
@@ -3208,4 +3326,4 @@ declare function loadKeystore(path: string, password: string): string;
3208
3326
  */
3209
3327
  declare function writeKeystoreFile(path: string, keystore: EncryptedKeystore): void;
3210
3328
 
3211
- export { type BackupPayload, type BalanceProofParams, BtpRuntimeClient, type BtpRuntimeClientConfig, type ChainMetadata, type ChainSigner, ChannelFundingError, ChannelManager, type ClaimMessage, type ClaimResolver, ConnectorError, type DiscoveredIlpPeer, type EVMClaimMessage, type EncryptedKeystore, EvmSigner, type FaucetChain, type FundWalletOptions, type FundWalletResult, type H402FetchOptions, Http402Client, type Http402ClientConfig, HttpConnectorAdmin, type HttpConnectorAdminConfig, HttpIlpClient, type HttpIlpClientConfig, type HttpIlpClientFactory, HttpRuntimeClient, type HttpRuntimeClientConfig, ILP_CLAIM_HEADER, ILP_CLAIM_WRAPPED_HEADER, ILP_PEER_ID_HEADER, type IlpTransportChoice, KeyManager, type KeyManagerConfig, type MinaClaimMessage, type MinaDepositReader, MinaSigner, type MinaSignerOptions, NetworkError, OnChainChannelClient, type OnChainChannelClientConfig, type ParsedFulfillHttp, type ParsedX402Challenge, type PasskeyInfo, type PublishEventResult, type RequestBlobStorageParams, type RequestBlobStorageResult, type RetryOptions, type SelectIlpTransportOptions, type SignedBalanceProof, type SolanaChannelClientOptions, type SolanaClaimMessage, SolanaSigner, type ToonChannelAccept, ToonClient, type ToonClientConfig, ToonClientError, type ToonIdentity, type ToonSigners, type ToonStartResult, ValidationError, type VaultData, applyDefaults, applyNetworkPresets, buildBackupEvent, buildBackupFilter, buildSettlementInfo, buildStoreWriteEnvelope, decryptMnemonic, defaultFaucetTimeout, deriveFromNsec, deriveFullIdentity, deriveNostrKeyFromMnemonic, encryptMnemonic, extractArweaveTxId, fundWallet, generateKeystore, generateMnemonic, generateRandomIdentity, getNetworkStatus, httpEndpointToBtpUrl, importKeystore, isInsufficientGasError, isPrfSupported, loadKeystore, parseBackupPayload, parseFulfillHttp, parseFulfillHttpBytes, parseHttpResponse, parseX402Body, parseX402Challenge, proxyIlpEndpoint, readDiscoveredIlpPeer, readMinaDepositTotal, requestBlobStorage, selectIlpTransport, serializeHttpRequest, validateConfig, validateMnemonic, withRetry, writeKeystoreFile };
3329
+ export { type BackupPayload, type BalanceProofParams, BtpRuntimeClient, type BtpRuntimeClientConfig, type ChainMetadata, type ChainSigner, ChannelFundingError, ChannelManager, type ClaimMessage, type ClaimResolver, ConnectorError, type DiscoveredIlpPeer, type EVMClaimMessage, type EncryptedKeystore, EvmSigner, type FaucetChain, type FundWalletOptions, type FundWalletResult, type H402FetchOptions, Http402Client, type Http402ClientConfig, HttpConnectorAdmin, type HttpConnectorAdminConfig, HttpIlpClient, type HttpIlpClientConfig, type HttpIlpClientFactory, HttpRuntimeClient, type HttpRuntimeClientConfig, ILP_CLAIM_HEADER, ILP_CLAIM_WRAPPED_HEADER, ILP_PEER_ID_HEADER, type IlpTransportChoice, KeyManager, type KeyManagerConfig, type MinaClaimMessage, type MinaDepositReader, MinaSigner, type MinaSignerOptions, NetworkError, OnChainChannelClient, type OnChainChannelClientConfig, type ParsedFulfillHttp, type ParsedX402Challenge, type PasskeyInfo, type PublishEventResult, type RequestBlobStorageParams, type RequestBlobStorageResult, type RetryOptions, type SelectIlpTransportOptions, type SignedBalanceProof, type SolanaChannelClientOptions, type SolanaClaimMessage, SolanaSigner, type ToonChannelAccept, ToonClient, type ToonClientConfig, ToonClientError, type ToonIdentity, type ToonSigners, type ToonStartResult, ValidationError, type VaultData, type WalletBalance, type WalletBalanceSources, type WalletChainBalances, type WalletTokenAmount, applyDefaults, applyNetworkPresets, buildBackupEvent, buildBackupFilter, buildSettlementInfo, buildStoreWriteEnvelope, decryptMnemonic, defaultFaucetTimeout, deriveFromNsec, deriveFullIdentity, deriveNostrKeyFromMnemonic, encryptMnemonic, extractArweaveTxId, fundWallet, generateKeystore, generateMnemonic, generateRandomIdentity, getNetworkStatus, httpEndpointToBtpUrl, importKeystore, isInsufficientGasError, isPrfSupported, loadKeystore, parseBackupPayload, parseFulfillHttp, parseFulfillHttpBytes, parseHttpResponse, parseX402Body, parseX402Challenge, proxyIlpEndpoint, readDiscoveredIlpPeer, readEvmNativeBalance, readEvmTokenBalance, readMinaBalance, readMinaDepositTotal, readSolanaNativeBalance, readSolanaTokenBalance, readWalletBalances, requestBlobStorage, selectIlpTransport, serializeHttpRequest, validateConfig, validateMnemonic, withRetry, writeKeystoreFile };
package/dist/index.js CHANGED
@@ -3957,6 +3957,38 @@ async function readEvmTokenBalance(opts) {
3957
3957
  if (decimals !== void 0) out.assetScale = Number(decimals);
3958
3958
  return out;
3959
3959
  }
3960
+ async function readEvmNativeBalance(opts) {
3961
+ const chainId = parseEvmChainId(opts.chainKey);
3962
+ const client = createPublicClient2({
3963
+ transport: http2(opts.rpcUrl),
3964
+ chain: defineChain2({
3965
+ id: chainId,
3966
+ name: opts.chainKey,
3967
+ nativeCurrency: { name: "ETH", symbol: "ETH", decimals: 18 },
3968
+ rpcUrls: { default: { http: [opts.rpcUrl] } }
3969
+ })
3970
+ });
3971
+ const wei = await client.getBalance({ address: opts.owner });
3972
+ return { symbol: "ETH", amount: wei.toString(), decimals: 18 };
3973
+ }
3974
+ async function readSolanaNativeBalance(opts) {
3975
+ const fetchImpl = opts.fetchImpl ?? fetch;
3976
+ const res = await fetchImpl(opts.rpcUrl, {
3977
+ method: "POST",
3978
+ headers: { "content-type": "application/json" },
3979
+ body: JSON.stringify({
3980
+ jsonrpc: "2.0",
3981
+ id: 1,
3982
+ method: "getBalance",
3983
+ params: [opts.owner, { commitment: "confirmed" }]
3984
+ })
3985
+ });
3986
+ if (!res.ok) throw new Error(`Solana RPC request failed: HTTP ${res.status}`);
3987
+ const json = await res.json();
3988
+ if (json.error) throw new Error(`Solana RPC error: ${json.error.message ?? "unknown"}`);
3989
+ const lamports = BigInt(json.result?.value ?? 0);
3990
+ return { symbol: "SOL", amount: lamports.toString(), decimals: 9 };
3991
+ }
3960
3992
  async function readSolanaTokenBalance(opts) {
3961
3993
  const fetchImpl = opts.fetchImpl ?? fetch;
3962
3994
  const res = await fetchImpl(opts.rpcUrl, {
@@ -4004,6 +4036,95 @@ async function readMinaBalance(opts) {
4004
4036
  assetScale: 9
4005
4037
  };
4006
4038
  }
4039
+ var errText = (e) => e instanceof Error ? e.message : String(e);
4040
+ function foldNative(out, settled, errors) {
4041
+ if (settled.status === "fulfilled") out.native = settled.value;
4042
+ else errors.push(errText(settled.reason));
4043
+ }
4044
+ function foldToken(out, settled, tokenAddress, errors) {
4045
+ if (settled.status === "rejected") {
4046
+ errors.push(errText(settled.reason));
4047
+ return;
4048
+ }
4049
+ const bal = settled.value;
4050
+ if (!bal) return;
4051
+ out.tokens.push({
4052
+ symbol: bal.asset,
4053
+ amount: bal.amount,
4054
+ decimals: bal.assetScale,
4055
+ ...tokenAddress ? { address: tokenAddress } : {}
4056
+ });
4057
+ }
4058
+ function finalizeChain(out, errors) {
4059
+ if (errors.length > 0) out.error = errors[0];
4060
+ if (out.native === void 0 && out.tokens.length === 0) out.unreadable = true;
4061
+ }
4062
+ async function readWalletBalances(sources) {
4063
+ const { fetchImpl } = sources;
4064
+ const tasks = [];
4065
+ if (sources.evm) {
4066
+ const { chainKey, rpcUrl, owner, tokenAddress } = sources.evm;
4067
+ tasks.push(
4068
+ (async () => {
4069
+ const out = { chain: "evm", chainKey, address: owner, tokens: [] };
4070
+ const errors = [];
4071
+ const [nativeR, tokenR] = await Promise.allSettled([
4072
+ readEvmNativeBalance({ rpcUrl, chainKey, owner }),
4073
+ tokenAddress ? readEvmTokenBalance({ rpcUrl, chainKey, tokenAddress, owner }) : Promise.resolve(void 0)
4074
+ ]);
4075
+ foldNative(out, nativeR, errors);
4076
+ foldToken(out, tokenR, tokenAddress, errors);
4077
+ finalizeChain(out, errors);
4078
+ return out;
4079
+ })()
4080
+ );
4081
+ }
4082
+ if (sources.solana) {
4083
+ const { chainKey = "solana", rpcUrl, owner, tokenMint } = sources.solana;
4084
+ tasks.push(
4085
+ (async () => {
4086
+ const out = { chain: "solana", chainKey, address: owner, tokens: [] };
4087
+ const errors = [];
4088
+ const [nativeR, tokenR] = await Promise.allSettled([
4089
+ readSolanaNativeBalance({ rpcUrl, owner, fetchImpl }),
4090
+ tokenMint ? readSolanaTokenBalance({ rpcUrl, mint: tokenMint, owner, fetchImpl }) : Promise.resolve(void 0)
4091
+ ]);
4092
+ foldNative(out, nativeR, errors);
4093
+ if (tokenR.status === "fulfilled" && tokenR.value && tokenR.value.asset === void 0) {
4094
+ tokenR.value.asset = "USDC";
4095
+ }
4096
+ foldToken(out, tokenR, tokenMint, errors);
4097
+ finalizeChain(out, errors);
4098
+ return out;
4099
+ })()
4100
+ );
4101
+ }
4102
+ if (sources.mina) {
4103
+ const { chainKey = "mina", graphqlUrl, owner } = sources.mina;
4104
+ tasks.push(
4105
+ (async () => {
4106
+ const out = { chain: "mina", chainKey, address: owner, tokens: [] };
4107
+ const errors = [];
4108
+ const nativeR = await Promise.allSettled([readMinaBalance({ graphqlUrl, owner, fetchImpl })]);
4109
+ foldNative(
4110
+ out,
4111
+ nativeR[0].status === "fulfilled" ? {
4112
+ status: "fulfilled",
4113
+ value: {
4114
+ symbol: nativeR[0].value.asset,
4115
+ amount: nativeR[0].value.amount,
4116
+ decimals: nativeR[0].value.assetScale
4117
+ }
4118
+ } : nativeR[0],
4119
+ errors
4120
+ );
4121
+ finalizeChain(out, errors);
4122
+ return out;
4123
+ })()
4124
+ );
4125
+ }
4126
+ return Promise.all(tasks);
4127
+ }
4007
4128
 
4008
4129
  // src/blob-storage.ts
4009
4130
  import { buildBlobStorageRequest } from "@toon-protocol/core";
@@ -5154,6 +5275,74 @@ var ToonClient = class {
5154
5275
  }
5155
5276
  return out;
5156
5277
  }
5278
+ /**
5279
+ * The FULL multi-chain wallet view (#299): for every chain the identity is
5280
+ * configured for, the native coin (ETH / SOL / MINA) AND every configured
5281
+ * token (USDC), grouped per chain with the identity's address on that chain.
5282
+ * A superset of {@link getBalances} — which stays scoped to the channel's
5283
+ * settlement token — kept as a separate reader so channel-settlement callers
5284
+ * are unaffected.
5285
+ *
5286
+ * FREE: read-only RPC, no signing, no payment. Works on an UNSTARTED client:
5287
+ * the Solana/Mina addresses (which the signers only register during
5288
+ * `start()`) are derived on demand from the retained mnemonic — the SAME keys
5289
+ * `start()` would register and that `rig fund` prints — so all configured
5290
+ * chains appear even before a start. Best-effort per chain: an unreachable
5291
+ * RPC yields `{ unreadable: true }` for that chain, never failing the others.
5292
+ */
5293
+ async getWalletBalances() {
5294
+ const sources = {};
5295
+ let derived;
5296
+ let derivedTried = false;
5297
+ const ensureDerived = async () => {
5298
+ if (derivedTried) return derived;
5299
+ derivedTried = true;
5300
+ if (this.config.mnemonic) {
5301
+ derived = await deriveFullIdentity(
5302
+ this.config.mnemonic,
5303
+ this.config.mnemonicAccountIndex ?? 0
5304
+ );
5305
+ }
5306
+ return derived;
5307
+ };
5308
+ const evmAddress = this.getEvmAddress();
5309
+ const rpcUrls = this.config.chainRpcUrls;
5310
+ const tokens = this.config.preferredTokens;
5311
+ if (evmAddress && rpcUrls) {
5312
+ const usableEvm = (c) => c.startsWith("evm") && Boolean(rpcUrls[c]);
5313
+ const settlementKeys = Object.keys(this.config.settlementAddresses ?? {});
5314
+ const chainKeys = this.config.supportedChains ?? Object.keys(rpcUrls);
5315
+ const chainKey = settlementKeys.find(usableEvm) ?? chainKeys.find(usableEvm);
5316
+ if (chainKey && rpcUrls[chainKey]) {
5317
+ sources.evm = {
5318
+ chainKey,
5319
+ rpcUrl: rpcUrls[chainKey],
5320
+ owner: evmAddress,
5321
+ ...tokens?.[chainKey] ? { tokenAddress: tokens[chainKey] } : {}
5322
+ };
5323
+ }
5324
+ }
5325
+ const sol = this.config.solanaChannel;
5326
+ if (sol?.rpcUrl) {
5327
+ const solAddress = this.getSolanaAddress() ?? (await ensureDerived())?.solana.publicKey;
5328
+ if (solAddress) {
5329
+ sources.solana = {
5330
+ chainKey: "solana",
5331
+ rpcUrl: sol.rpcUrl,
5332
+ owner: solAddress,
5333
+ ...sol.tokenMint ? { tokenMint: sol.tokenMint } : {}
5334
+ };
5335
+ }
5336
+ }
5337
+ const mina = this.config.minaChannel;
5338
+ if (mina?.graphqlUrl) {
5339
+ const minaAddress = this.getMinaAddress() ?? (await ensureDerived())?.mina.publicKey;
5340
+ if (minaAddress) {
5341
+ sources.mina = { chainKey: "mina", graphqlUrl: mina.graphqlUrl, owner: minaAddress };
5342
+ }
5343
+ }
5344
+ return readWalletBalances(sources);
5345
+ }
5157
5346
  /**
5158
5347
  * Resolves an ILP destination address to a peer ID.
5159
5348
  * Convention: destination "g.toon.peer1" → peerId "peer1" (last segment).
@@ -6781,7 +6970,13 @@ export {
6781
6970
  proxyIlpEndpoint,
6782
6971
  publishBackCoordinate,
6783
6972
  readDiscoveredIlpPeer,
6973
+ readEvmNativeBalance,
6974
+ readEvmTokenBalance,
6975
+ readMinaBalance,
6784
6976
  readMinaDepositTotal,
6977
+ readSolanaNativeBalance,
6978
+ readSolanaTokenBalance,
6979
+ readWalletBalances,
6785
6980
  renderDeterministicHtml,
6786
6981
  renderDispatch,
6787
6982
  requestBlobStorage,