@toon-protocol/client 0.14.4 → 0.14.5
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 +31 -0
- package/dist/index.js +161 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,17 @@ import { NostrEvent, EventTemplate } from 'nostr-tools/pure';
|
|
|
5
5
|
import { PrivateKeyAccount } from 'viem/accounts';
|
|
6
6
|
export { A2uiDecision, ConsentDecision, ConsentRequest, DispatchGuardInfo, DispatchInput, GenerateContext, GeneratedRenderer, GenerativeDecision, GenerativeFallbackOptions, GenerativeFallbackRenderer, GenerativeFallbackResult, GuardedDispatchInput, IntentClassification, KindRegistry, MIME_A2UI, MIME_MCP_APP, McpUiDecision, NativeDecision, PublishBackOptions, RenderBranch, RenderDecision, RenderTrust, RendererGenerator, RendererPin, RendererPinStore, RendererPublisher, RendererSigner, ResolvedCoordinate, SwapApproval, SwapDecision, SwapRejection, SwapRejectionReason, UiResource, VerifyRendererInput, WidgetIntent, buildConsentRequest, buildRendererEventTemplate, classifyIntent, deterministicGenerator, extractUiResource, guardedRenderDispatch, isTrustDowngrade, publishBackCoordinate, renderDeterministicHtml, renderDispatch, resolveRendererMime, resolveUiCoordinate, resolveUiRenderer, verifyRendererTrust } from './render/index.js';
|
|
7
7
|
|
|
8
|
+
/** One on-chain wallet token balance. `amount` is base-unit integer, decimal. */
|
|
9
|
+
interface WalletBalance {
|
|
10
|
+
chain: 'evm' | 'solana' | 'mina';
|
|
11
|
+
address: string;
|
|
12
|
+
amount: string;
|
|
13
|
+
/** Token symbol, when resolved (e.g. `'USDC'`, `'MINA'`). */
|
|
14
|
+
asset?: string;
|
|
15
|
+
/** Token decimals, when resolved. */
|
|
16
|
+
assetScale?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
8
19
|
/**
|
|
9
20
|
* Solana payment-channel parameters supplied via `ToonClientConfig.solanaChannel`.
|
|
10
21
|
*
|
|
@@ -1210,6 +1221,20 @@ declare class ToonClient {
|
|
|
1210
1221
|
* Gets the cumulative transferred amount for a tracked channel.
|
|
1211
1222
|
*/
|
|
1212
1223
|
getChannelCumulativeAmount(channelId: string): bigint;
|
|
1224
|
+
/**
|
|
1225
|
+
* Gets the on-chain deposit total (locked collateral) for a tracked channel.
|
|
1226
|
+
* The available (spendable) balance is this minus the cumulative spent amount.
|
|
1227
|
+
*/
|
|
1228
|
+
getChannelDepositTotal(channelId: string): bigint;
|
|
1229
|
+
/**
|
|
1230
|
+
* Read the on-chain settlement-token balance of this client's OWN wallet on
|
|
1231
|
+
* each configured chain (EVM token, Solana SPL, native MINA). A free read — no
|
|
1232
|
+
* signing, no payment. Best-effort per chain: a chain whose config is absent or
|
|
1233
|
+
* whose RPC read fails is omitted rather than failing the whole result, so the
|
|
1234
|
+
* wallet view degrades gracefully. Available after `start()` (Solana/Mina keys
|
|
1235
|
+
* are derived there).
|
|
1236
|
+
*/
|
|
1237
|
+
getBalances(): Promise<WalletBalance[]>;
|
|
1213
1238
|
/**
|
|
1214
1239
|
* Resolves an ILP destination address to a peer ID.
|
|
1215
1240
|
* Convention: destination "g.toon.peer1" → peerId "peer1" (last segment).
|
|
@@ -2274,6 +2299,12 @@ declare class ChannelManager {
|
|
|
2274
2299
|
* Gets the cumulative transferred amount for a tracked channel.
|
|
2275
2300
|
*/
|
|
2276
2301
|
getCumulativeAmount(channelId: string): bigint;
|
|
2302
|
+
/**
|
|
2303
|
+
* Gets the on-chain deposit total (collateral locked at open / via deposits)
|
|
2304
|
+
* for a tracked channel, or `0n` when none was captured. The available
|
|
2305
|
+
* (spendable) balance is `depositTotal - cumulativeAmount`.
|
|
2306
|
+
*/
|
|
2307
|
+
getDepositTotal(channelId: string): bigint;
|
|
2277
2308
|
/**
|
|
2278
2309
|
* Gets all tracked channel IDs.
|
|
2279
2310
|
*/
|
package/dist/index.js
CHANGED
|
@@ -1640,17 +1640,17 @@ function readDiscoveredIlpPeer(peer) {
|
|
|
1640
1640
|
}
|
|
1641
1641
|
function selectIlpTransport(peer, options = {}) {
|
|
1642
1642
|
const needsDuplex = options.needsDuplex ?? false;
|
|
1643
|
-
const
|
|
1643
|
+
const http3 = peer.httpEndpoint?.trim() || void 0;
|
|
1644
1644
|
const btp = peer.btpEndpoint?.trim() || void 0;
|
|
1645
1645
|
const canUpgrade = peer.supportsUpgrade === true;
|
|
1646
1646
|
if (needsDuplex) {
|
|
1647
1647
|
if (btp) return { kind: "btp", btpEndpoint: btp };
|
|
1648
|
-
if (
|
|
1648
|
+
if (http3 && canUpgrade) return { kind: "http-upgradable", httpEndpoint: http3 };
|
|
1649
1649
|
throw new Error(
|
|
1650
1650
|
"Duplex transport required but peer exposes neither a btpEndpoint nor an upgradable httpEndpoint"
|
|
1651
1651
|
);
|
|
1652
1652
|
}
|
|
1653
|
-
if (
|
|
1653
|
+
if (http3) return { kind: "http", httpEndpoint: http3, canUpgrade };
|
|
1654
1654
|
if (btp) return { kind: "btp", btpEndpoint: btp };
|
|
1655
1655
|
throw new Error("Peer exposes neither an httpEndpoint nor a btpEndpoint");
|
|
1656
1656
|
}
|
|
@@ -3489,6 +3489,18 @@ var ChannelManager = class {
|
|
|
3489
3489
|
}
|
|
3490
3490
|
return tracking.cumulativeAmount;
|
|
3491
3491
|
}
|
|
3492
|
+
/**
|
|
3493
|
+
* Gets the on-chain deposit total (collateral locked at open / via deposits)
|
|
3494
|
+
* for a tracked channel, or `0n` when none was captured. The available
|
|
3495
|
+
* (spendable) balance is `depositTotal - cumulativeAmount`.
|
|
3496
|
+
*/
|
|
3497
|
+
getDepositTotal(channelId) {
|
|
3498
|
+
const tracking = this.channels.get(channelId);
|
|
3499
|
+
if (!tracking) {
|
|
3500
|
+
throw new Error(`Channel "${channelId}" is not being tracked.`);
|
|
3501
|
+
}
|
|
3502
|
+
return tracking.depositTotal ?? 0n;
|
|
3503
|
+
}
|
|
3492
3504
|
/**
|
|
3493
3505
|
* Gets all tracked channel IDs.
|
|
3494
3506
|
*/
|
|
@@ -3547,6 +3559,91 @@ var JsonFileChannelStore = class {
|
|
|
3547
3559
|
}
|
|
3548
3560
|
};
|
|
3549
3561
|
|
|
3562
|
+
// src/balance/WalletBalanceReader.ts
|
|
3563
|
+
import { createPublicClient as createPublicClient2, http as http2, defineChain as defineChain2 } from "viem";
|
|
3564
|
+
var ERC20_READ_ABI = [
|
|
3565
|
+
{ name: "balanceOf", type: "function", stateMutability: "view", inputs: [{ name: "account", type: "address" }], outputs: [{ type: "uint256" }] },
|
|
3566
|
+
{ name: "decimals", type: "function", stateMutability: "view", inputs: [], outputs: [{ type: "uint8" }] },
|
|
3567
|
+
{ name: "symbol", type: "function", stateMutability: "view", inputs: [], outputs: [{ type: "string" }] }
|
|
3568
|
+
];
|
|
3569
|
+
function parseEvmChainId(chainKey) {
|
|
3570
|
+
const parts = chainKey.split(":");
|
|
3571
|
+
const idStr = parts.length >= 3 ? parts[2] : parts[1];
|
|
3572
|
+
const id = Number.parseInt(idStr ?? "", 10);
|
|
3573
|
+
if (!Number.isFinite(id)) throw new Error(`Invalid EVM chain key "${chainKey}".`);
|
|
3574
|
+
return id;
|
|
3575
|
+
}
|
|
3576
|
+
async function readEvmTokenBalance(opts) {
|
|
3577
|
+
const chainId = parseEvmChainId(opts.chainKey);
|
|
3578
|
+
const client = createPublicClient2({
|
|
3579
|
+
transport: http2(opts.rpcUrl),
|
|
3580
|
+
chain: defineChain2({
|
|
3581
|
+
id: chainId,
|
|
3582
|
+
name: opts.chainKey,
|
|
3583
|
+
nativeCurrency: { name: "ETH", symbol: "ETH", decimals: 18 },
|
|
3584
|
+
rpcUrls: { default: { http: [opts.rpcUrl] } }
|
|
3585
|
+
})
|
|
3586
|
+
});
|
|
3587
|
+
const token = opts.tokenAddress;
|
|
3588
|
+
const owner = opts.owner;
|
|
3589
|
+
const [amount, decimals, symbol] = await Promise.all([
|
|
3590
|
+
client.readContract({ address: token, abi: ERC20_READ_ABI, functionName: "balanceOf", args: [owner] }),
|
|
3591
|
+
client.readContract({ address: token, abi: ERC20_READ_ABI, functionName: "decimals" }).catch(() => void 0),
|
|
3592
|
+
client.readContract({ address: token, abi: ERC20_READ_ABI, functionName: "symbol" }).catch(() => void 0)
|
|
3593
|
+
]);
|
|
3594
|
+
const out = { chain: "evm", address: opts.owner, amount: amount.toString() };
|
|
3595
|
+
if (typeof symbol === "string" && symbol) out.asset = symbol;
|
|
3596
|
+
if (decimals !== void 0) out.assetScale = Number(decimals);
|
|
3597
|
+
return out;
|
|
3598
|
+
}
|
|
3599
|
+
async function readSolanaTokenBalance(opts) {
|
|
3600
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
3601
|
+
const res = await fetchImpl(opts.rpcUrl, {
|
|
3602
|
+
method: "POST",
|
|
3603
|
+
headers: { "content-type": "application/json" },
|
|
3604
|
+
body: JSON.stringify({
|
|
3605
|
+
jsonrpc: "2.0",
|
|
3606
|
+
id: 1,
|
|
3607
|
+
method: "getTokenAccountsByOwner",
|
|
3608
|
+
params: [opts.owner, { mint: opts.mint }, { encoding: "jsonParsed", commitment: "confirmed" }]
|
|
3609
|
+
})
|
|
3610
|
+
});
|
|
3611
|
+
if (!res.ok) throw new Error(`Solana RPC request failed: HTTP ${res.status}`);
|
|
3612
|
+
const json = await res.json();
|
|
3613
|
+
if (json.error) throw new Error(`Solana RPC error: ${json.error.message ?? "unknown"}`);
|
|
3614
|
+
let amount = 0n;
|
|
3615
|
+
let decimals;
|
|
3616
|
+
for (const acc of json.result?.value ?? []) {
|
|
3617
|
+
const ta = acc.account?.data?.parsed?.info?.tokenAmount;
|
|
3618
|
+
if (ta?.amount) amount += BigInt(ta.amount);
|
|
3619
|
+
if (ta?.decimals !== void 0) decimals = ta.decimals;
|
|
3620
|
+
}
|
|
3621
|
+
const out = { chain: "solana", address: opts.owner, amount: amount.toString() };
|
|
3622
|
+
if (decimals !== void 0) out.assetScale = decimals;
|
|
3623
|
+
return out;
|
|
3624
|
+
}
|
|
3625
|
+
async function readMinaBalance(opts) {
|
|
3626
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
3627
|
+
const query = "query($pk:String!){account(publicKey:$pk){balance{total}}}";
|
|
3628
|
+
const res = await fetchImpl(opts.graphqlUrl, {
|
|
3629
|
+
method: "POST",
|
|
3630
|
+
headers: { "content-type": "application/json" },
|
|
3631
|
+
body: JSON.stringify({ query, variables: { pk: opts.owner } })
|
|
3632
|
+
});
|
|
3633
|
+
if (!res.ok) throw new Error(`Mina GraphQL request failed: HTTP ${res.status}`);
|
|
3634
|
+
const json = await res.json();
|
|
3635
|
+
if (json.errors && json.errors.length > 0) {
|
|
3636
|
+
throw new Error(`Mina GraphQL error: ${json.errors[0]?.message ?? "unknown"}`);
|
|
3637
|
+
}
|
|
3638
|
+
return {
|
|
3639
|
+
chain: "mina",
|
|
3640
|
+
address: opts.owner,
|
|
3641
|
+
amount: String(json.data?.account?.balance?.total ?? "0"),
|
|
3642
|
+
asset: "MINA",
|
|
3643
|
+
assetScale: 9
|
|
3644
|
+
};
|
|
3645
|
+
}
|
|
3646
|
+
|
|
3550
3647
|
// src/blob-storage.ts
|
|
3551
3648
|
import { buildBlobStorageRequest } from "@toon-protocol/core";
|
|
3552
3649
|
var ARWEAVE_TX_ID_REGEX = /^[A-Za-z0-9_-]{43}$/;
|
|
@@ -3617,8 +3714,8 @@ async function requestBlobStorage(client, secretKey, params) {
|
|
|
3617
3714
|
};
|
|
3618
3715
|
}
|
|
3619
3716
|
function extractArweaveTxId(base64Data) {
|
|
3620
|
-
const
|
|
3621
|
-
if (!
|
|
3717
|
+
const http3 = parseFulfillHttp(base64Data);
|
|
3718
|
+
if (!http3.isHttp) {
|
|
3622
3719
|
const legacy = decodeUtf8(fromBase64(base64Data));
|
|
3623
3720
|
if (!ARWEAVE_TX_ID_REGEX.test(legacy)) {
|
|
3624
3721
|
throw new Error(
|
|
@@ -3627,18 +3724,18 @@ function extractArweaveTxId(base64Data) {
|
|
|
3627
3724
|
}
|
|
3628
3725
|
return legacy;
|
|
3629
3726
|
}
|
|
3630
|
-
if (
|
|
3631
|
-
const detail =
|
|
3727
|
+
if (http3.status < 200 || http3.status >= 300) {
|
|
3728
|
+
const detail = http3.body ? ` - ${http3.body}` : "";
|
|
3632
3729
|
throw new Error(
|
|
3633
|
-
`Blob upload failed: DVM returned HTTP ${
|
|
3730
|
+
`Blob upload failed: DVM returned HTTP ${http3.status} ${http3.statusText}`.trimEnd() + detail
|
|
3634
3731
|
);
|
|
3635
3732
|
}
|
|
3636
3733
|
let parsed;
|
|
3637
3734
|
try {
|
|
3638
|
-
parsed = JSON.parse(
|
|
3735
|
+
parsed = JSON.parse(http3.body);
|
|
3639
3736
|
} catch {
|
|
3640
3737
|
throw new Error(
|
|
3641
|
-
`Blob upload response body was not valid JSON: "${
|
|
3738
|
+
`Blob upload response body was not valid JSON: "${http3.body}"`
|
|
3642
3739
|
);
|
|
3643
3740
|
}
|
|
3644
3741
|
const body = parsed;
|
|
@@ -3656,7 +3753,7 @@ function extractArweaveTxId(base64Data) {
|
|
|
3656
3753
|
}
|
|
3657
3754
|
}
|
|
3658
3755
|
throw new Error(
|
|
3659
|
-
`Blob upload response did not contain a valid Arweave tx ID: "${
|
|
3756
|
+
`Blob upload response did not contain a valid Arweave tx ID: "${http3.body}"`
|
|
3660
3757
|
);
|
|
3661
3758
|
}
|
|
3662
3759
|
|
|
@@ -4533,6 +4630,59 @@ var ToonClient = class {
|
|
|
4533
4630
|
if (!this.channelManager) throw new Error("ChannelManager not initialized");
|
|
4534
4631
|
return this.channelManager.getCumulativeAmount(channelId);
|
|
4535
4632
|
}
|
|
4633
|
+
/**
|
|
4634
|
+
* Gets the on-chain deposit total (locked collateral) for a tracked channel.
|
|
4635
|
+
* The available (spendable) balance is this minus the cumulative spent amount.
|
|
4636
|
+
*/
|
|
4637
|
+
getChannelDepositTotal(channelId) {
|
|
4638
|
+
if (!this.channelManager) throw new Error("ChannelManager not initialized");
|
|
4639
|
+
return this.channelManager.getDepositTotal(channelId);
|
|
4640
|
+
}
|
|
4641
|
+
/**
|
|
4642
|
+
* Read the on-chain settlement-token balance of this client's OWN wallet on
|
|
4643
|
+
* each configured chain (EVM token, Solana SPL, native MINA). A free read — no
|
|
4644
|
+
* signing, no payment. Best-effort per chain: a chain whose config is absent or
|
|
4645
|
+
* whose RPC read fails is omitted rather than failing the whole result, so the
|
|
4646
|
+
* wallet view degrades gracefully. Available after `start()` (Solana/Mina keys
|
|
4647
|
+
* are derived there).
|
|
4648
|
+
*/
|
|
4649
|
+
async getBalances() {
|
|
4650
|
+
const out = [];
|
|
4651
|
+
const evmAddress = this.getEvmAddress();
|
|
4652
|
+
const rpcUrls = this.config.chainRpcUrls;
|
|
4653
|
+
const tokens = this.config.preferredTokens;
|
|
4654
|
+
if (evmAddress && rpcUrls && tokens) {
|
|
4655
|
+
const chainKeys = this.config.supportedChains ?? Object.keys(rpcUrls);
|
|
4656
|
+
const chainKey = chainKeys.find((c) => c.startsWith("evm") && rpcUrls[c] && tokens[c]);
|
|
4657
|
+
const rpcUrl = chainKey ? rpcUrls[chainKey] : void 0;
|
|
4658
|
+
const tokenAddress = chainKey ? tokens[chainKey] : void 0;
|
|
4659
|
+
if (chainKey && rpcUrl && tokenAddress) {
|
|
4660
|
+
try {
|
|
4661
|
+
out.push(await readEvmTokenBalance({ rpcUrl, chainKey, tokenAddress, owner: evmAddress }));
|
|
4662
|
+
} catch {
|
|
4663
|
+
}
|
|
4664
|
+
}
|
|
4665
|
+
}
|
|
4666
|
+
const solAddress = this.getSolanaAddress();
|
|
4667
|
+
const sol = this.config.solanaChannel;
|
|
4668
|
+
if (solAddress && sol?.rpcUrl && sol.tokenMint) {
|
|
4669
|
+
try {
|
|
4670
|
+
out.push(
|
|
4671
|
+
await readSolanaTokenBalance({ rpcUrl: sol.rpcUrl, mint: sol.tokenMint, owner: solAddress })
|
|
4672
|
+
);
|
|
4673
|
+
} catch {
|
|
4674
|
+
}
|
|
4675
|
+
}
|
|
4676
|
+
const minaAddress = this.getMinaAddress();
|
|
4677
|
+
const mina = this.config.minaChannel;
|
|
4678
|
+
if (minaAddress && mina?.graphqlUrl) {
|
|
4679
|
+
try {
|
|
4680
|
+
out.push(await readMinaBalance({ graphqlUrl: mina.graphqlUrl, owner: minaAddress }));
|
|
4681
|
+
} catch {
|
|
4682
|
+
}
|
|
4683
|
+
}
|
|
4684
|
+
return out;
|
|
4685
|
+
}
|
|
4536
4686
|
/**
|
|
4537
4687
|
* Resolves an ILP destination address to a peer ID.
|
|
4538
4688
|
* Convention: destination "g.toon.peer1" → peerId "peer1" (last segment).
|