@kaleidorg/mind 0.6.4 → 0.8.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/README.md +345 -0
- package/dist/autonomy/risk.js.map +1 -1
- package/dist/autonomy/run-state.d.ts.map +1 -1
- package/dist/autonomy/run-state.js.map +1 -1
- package/dist/autonomy/task-store.d.ts.map +1 -1
- package/dist/autonomy/task-store.js.map +1 -1
- package/dist/bitrefill/contract.js.map +1 -1
- package/dist/context/budget.js.map +1 -1
- package/dist/context/builder.d.ts.map +1 -1
- package/dist/context/builder.js.map +1 -1
- package/dist/context/compress.d.ts.map +1 -1
- package/dist/context/compress.js +1 -0
- package/dist/context/compress.js.map +1 -1
- package/dist/context/rgb-units.d.ts +18 -0
- package/dist/context/rgb-units.d.ts.map +1 -0
- package/dist/context/rgb-units.js +86 -0
- package/dist/context/rgb-units.js.map +1 -0
- package/dist/engine.d.ts +53 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +179 -17
- package/dist/engine.js.map +1 -1
- package/dist/evidence.d.ts +1 -1
- package/dist/evidence.d.ts.map +1 -1
- package/dist/evidence.js.map +1 -1
- package/dist/fastpath/fastpath.d.ts.map +1 -1
- package/dist/fastpath/fastpath.js.map +1 -1
- package/dist/flashnet/contract.js.map +1 -1
- package/dist/funnel.d.ts.map +1 -1
- package/dist/funnel.js +23 -4
- package/dist/funnel.js.map +1 -1
- package/dist/guards.d.ts +63 -0
- package/dist/guards.d.ts.map +1 -0
- package/dist/guards.js +284 -0
- package/dist/guards.js.map +1 -0
- package/dist/index.d.ts +10 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/kaleidoswap/contract.d.ts +3 -4
- package/dist/kaleidoswap/contract.d.ts.map +1 -1
- package/dist/kaleidoswap/contract.js +3 -17
- package/dist/kaleidoswap/contract.js.map +1 -1
- package/dist/knowledge/btc-map.js.map +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js.map +1 -1
- package/dist/lsps1/contract.js.map +1 -1
- package/dist/memory/store.d.ts.map +1 -1
- package/dist/memory/store.js.map +1 -1
- package/dist/providers/openai.d.ts +64 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +233 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/types.d.ts +20 -0
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/qvac/assistant.js.map +1 -1
- package/dist/qvac/config.d.ts +7 -7
- package/dist/qvac/config.d.ts.map +1 -1
- package/dist/qvac/config.js +1 -1
- package/dist/qvac/delegate.d.ts +2 -0
- package/dist/qvac/delegate.d.ts.map +1 -1
- package/dist/qvac/delegate.js +2 -0
- package/dist/qvac/delegate.js.map +1 -1
- package/dist/qvac/index.d.ts +2 -0
- package/dist/qvac/index.d.ts.map +1 -1
- package/dist/qvac/index.js +2 -0
- package/dist/qvac/index.js.map +1 -1
- package/dist/qvac/models.d.ts +31 -0
- package/dist/qvac/models.d.ts.map +1 -0
- package/dist/qvac/models.js +80 -0
- package/dist/qvac/models.js.map +1 -0
- package/dist/qvac/parse.d.ts +16 -2
- package/dist/qvac/parse.d.ts.map +1 -1
- package/dist/qvac/parse.js +29 -7
- package/dist/qvac/parse.js.map +1 -1
- package/dist/qvac/provider.d.ts +5 -4
- package/dist/qvac/provider.d.ts.map +1 -1
- package/dist/qvac/provider.js +30 -21
- package/dist/qvac/provider.js.map +1 -1
- package/dist/qvac/stream.d.ts +3 -5
- package/dist/qvac/stream.d.ts.map +1 -1
- package/dist/qvac/stream.js +29 -1
- package/dist/qvac/stream.js.map +1 -1
- package/dist/qvac/tools.d.ts +37 -0
- package/dist/qvac/tools.d.ts.map +1 -0
- package/dist/qvac/tools.js +94 -0
- package/dist/qvac/tools.js.map +1 -0
- package/dist/qvac/voice.js.map +1 -1
- package/dist/rag/retriever.d.ts.map +1 -1
- package/dist/rag/tool.js.map +1 -1
- package/dist/rag/vector-store.d.ts.map +1 -1
- package/dist/rag/vector-store.js.map +1 -1
- package/dist/recipe/issue-asset.d.ts +16 -0
- package/dist/recipe/issue-asset.d.ts.map +1 -0
- package/dist/recipe/issue-asset.js +120 -0
- package/dist/recipe/issue-asset.js.map +1 -0
- package/dist/recipe/runner.d.ts.map +1 -1
- package/dist/recipe/runner.js +1 -0
- package/dist/recipe/runner.js.map +1 -1
- package/dist/recipe/submarine-pay.d.ts +17 -0
- package/dist/recipe/submarine-pay.d.ts.map +1 -0
- package/dist/recipe/submarine-pay.js +79 -0
- package/dist/recipe/submarine-pay.js.map +1 -0
- package/dist/skills/loader.js.map +1 -1
- package/dist/skills/registry.d.ts.map +1 -1
- package/dist/skills/registry.js +1 -1
- package/dist/skills/registry.js.map +1 -1
- package/dist/skills/select.d.ts +20 -0
- package/dist/skills/select.d.ts.map +1 -0
- package/dist/skills/select.js +32 -0
- package/dist/skills/select.js.map +1 -0
- package/dist/submarine/contract.d.ts +42 -0
- package/dist/submarine/contract.d.ts.map +1 -0
- package/dist/submarine/contract.js +73 -0
- package/dist/submarine/contract.js.map +1 -0
- package/dist/testing/index.d.ts +14 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +12 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/mock-wallet.d.ts +103 -0
- package/dist/testing/mock-wallet.d.ts.map +1 -0
- package/dist/testing/mock-wallet.js +249 -0
- package/dist/testing/mock-wallet.js.map +1 -0
- package/dist/testing/scripted-provider.d.ts +17 -0
- package/dist/testing/scripted-provider.d.ts.map +1 -0
- package/dist/testing/scripted-provider.js +26 -0
- package/dist/testing/scripted-provider.js.map +1 -0
- package/dist/tools/in-process.d.ts.map +1 -1
- package/dist/tools/mcp.d.ts.map +1 -1
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js.map +1 -1
- package/dist/wallet/confirm.d.ts.map +1 -1
- package/dist/wallet/confirm.js +43 -5
- package/dist/wallet/confirm.js.map +1 -1
- package/dist/wallet/contract.d.ts.map +1 -1
- package/dist/wallet/contract.js +36 -0
- package/dist/wallet/contract.js.map +1 -1
- package/package.json +15 -5
- package/skills/README.md +2 -1
- package/skills/kaleido-node/SKILL.md +63 -0
- package/skills/kaleido-trading/SKILL.md +34 -27
- package/skills/kaleido-trading/references/api.md +61 -0
- package/skills/kaleido-trading/references/assets.md +57 -0
- package/skills/kaleido-trading/references/atomic.md +93 -0
- package/skills/paid-data/SKILL.md +59 -6
- package/skills/rgb-lightning-node/SKILL.md +142 -17
- package/skills/spark-wallet/SKILL.md +1 -0
- package/skills/submarine-swaps/SKILL.md +48 -0
- package/src/context/compress.ts +1 -0
- package/src/context/rgb-units.test.ts +42 -0
- package/src/context/rgb-units.ts +89 -0
- package/src/engine.test.ts +94 -3
- package/src/engine.ts +234 -19
- package/src/funnel.mind.test.ts +4 -2
- package/src/funnel.ts +23 -4
- package/src/guards.test.ts +399 -0
- package/src/guards.ts +299 -0
- package/src/index.ts +40 -1
- package/src/kaleidoswap/contract.test.ts +8 -16
- package/src/kaleidoswap/contract.ts +4 -32
- package/src/providers/openai.test.ts +127 -0
- package/src/providers/openai.ts +282 -0
- package/src/providers/types.ts +22 -0
- package/src/qvac/config.ts +1 -1
- package/src/qvac/delegate.ts +2 -0
- package/src/qvac/index.ts +12 -0
- package/src/qvac/models.ts +98 -0
- package/src/qvac/parse.test.ts +7 -0
- package/src/qvac/parse.ts +38 -3
- package/src/qvac/provider.test.ts +72 -1
- package/src/qvac/provider.ts +40 -27
- package/src/qvac/stream.test.ts +28 -0
- package/src/qvac/stream.ts +33 -6
- package/src/qvac/tools.test.ts +86 -0
- package/src/qvac/tools.ts +116 -0
- package/src/recipe/issue-asset.test.ts +131 -0
- package/src/recipe/issue-asset.ts +123 -0
- package/src/recipe/runner.ts +1 -0
- package/src/recipe/submarine-pay.test.ts +87 -0
- package/src/recipe/submarine-pay.ts +78 -0
- package/src/skills/registry.ts +1 -1
- package/src/skills/select.ts +41 -0
- package/src/submarine/contract.ts +112 -0
- package/src/testing/index.ts +14 -0
- package/src/testing/mock-wallet.ts +278 -0
- package/src/testing/scripted-provider.ts +37 -0
- package/src/wallet/confirm.test.ts +16 -0
- package/src/wallet/confirm.ts +43 -5
- package/src/wallet/contract.test.ts +38 -0
- package/src/wallet/contract.ts +36 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Atomic swap over kaleido-mcp
|
|
2
|
+
|
|
3
|
+
Every KaleidoSwap swap settles as an atomic HTLC swap between the maker and
|
|
4
|
+
the user's RGB Lightning Node (RLN). There is no deposit or order flow on
|
|
5
|
+
kaleido-mcp 0.3.0+. If either side fails, nothing settles. Typical end-to-end
|
|
6
|
+
time: 2–15 s.
|
|
7
|
+
|
|
8
|
+
These are kaleido-mcp's argument names. In-app wallets that bind the
|
|
9
|
+
`@kaleidorg/mind` KaleidoSwap contract use the simpler `from_asset` /
|
|
10
|
+
`to_asset` / `amount` quote and run the chain through a recipe instead.
|
|
11
|
+
|
|
12
|
+
## Steps
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
0. kaleidoswap_get_pairs() → pairs + routes[{ from_layer, to_layer, min_amount, max_amount }]
|
|
16
|
+
kaleidoswap_get_assets() → asset_id + precision per ticker (ids differ per network)
|
|
17
|
+
|
|
18
|
+
1. kaleidoswap_get_quote({
|
|
19
|
+
from_asset_id: "BTC", from_layer: "BTC_LN", from_amount: 0.001, // display units
|
|
20
|
+
to_asset_id: "<USDT asset_id>", to_layer: "RGB_LN"
|
|
21
|
+
})
|
|
22
|
+
→ { rfq_id, expires_at, from_asset: { amount_raw, amount_display },
|
|
23
|
+
to_asset: { amount_raw, amount_display }, price }
|
|
24
|
+
Pass exactly one of from_amount (sell a fixed input) or to_amount (buy a fixed output).
|
|
25
|
+
Show the user amount in → amount out → rate, and wait for a yes.
|
|
26
|
+
|
|
27
|
+
2. kaleidoswap_atomic_init({ rfq_id,
|
|
28
|
+
from_asset_id, from_amount_raw: quote.from_asset.amount_raw,
|
|
29
|
+
to_asset_id, to_amount_raw: quote.to_asset.amount_raw })
|
|
30
|
+
→ { swapstring, payment_hash, access_token }
|
|
31
|
+
access_token is returned only here; keep it for status.
|
|
32
|
+
|
|
33
|
+
3. rln_atomic_taker({ swapstring }) ← whitelist the incoming HTLC. MUST precede execute.
|
|
34
|
+
rln_get_node_info() → { pubkey } (the taker_pubkey)
|
|
35
|
+
|
|
36
|
+
4. kaleidoswap_atomic_execute({ swapstring, taker_pubkey, payment_hash })
|
|
37
|
+
|
|
38
|
+
5. kaleidoswap_atomic_status({ payment_hash, access_token }) ← poll every 2 s
|
|
39
|
+
Waiting → Pending → Succeeded | Failed | Expired
|
|
40
|
+
|
|
41
|
+
6. rln_refresh_transfers() ← after an RGB leg, so balances update
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`amount_raw` values are already in the maker's raw units (msat for BTC, atomic
|
|
45
|
+
units for RGB). Never convert them again. `payment_hash` identifies the swap
|
|
46
|
+
and is not an order id.
|
|
47
|
+
|
|
48
|
+
## Errors
|
|
49
|
+
|
|
50
|
+
| Situation | What to do |
|
|
51
|
+
|---|---|
|
|
52
|
+
| Quote expired (~60 s) | Re-quote. Never reuse an rfq_id or swapstring. |
|
|
53
|
+
| `rln_atomic_taker` fails | Do NOT execute. Re-quote and restart. |
|
|
54
|
+
| Status `Expired` / `Failed` | Nothing settled. Check liquidity on both legs (`rln_list_channels`), then re-quote. |
|
|
55
|
+
| Polling > 120 s | Check `rln_get_swap({ payment_hash, taker: true })` before retrying. Never start a second swap for the same trade while one may still settle. |
|
|
56
|
+
|
|
57
|
+
## Liquidity prerequisites
|
|
58
|
+
|
|
59
|
+
The swap needs outbound capacity on the asset you send and inbound capacity on
|
|
60
|
+
the asset you receive. If either side is short, `atomic_execute` fails. Buy a
|
|
61
|
+
channel first (skill: `kaleido-lsps`).
|
|
62
|
+
|
|
63
|
+
## Reading the result
|
|
64
|
+
|
|
65
|
+
An RGB asset received into a Lightning channel shows up in
|
|
66
|
+
`rln_get_asset_balance({ asset_id }).offchain_outbound`, not in `spendable`
|
|
67
|
+
(which is on-chain only). `offchain_outbound + offchain_inbound` is the
|
|
68
|
+
channel's capacity for that asset. The node's own view of swaps:
|
|
69
|
+
`rln_list_swaps()` / `rln_get_swap({ payment_hash, taker: true })`.
|
|
70
|
+
|
|
71
|
+
## Cross-layer moves
|
|
72
|
+
|
|
73
|
+
| From | To | How |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| BTC_LN | RGB_LN (USDT/XAUT) | atomic swap |
|
|
76
|
+
| RGB_LN | BTC_LN | atomic swap |
|
|
77
|
+
| RGB_LN | RGB_LN (e.g. XAUT → USDT) | atomic swap |
|
|
78
|
+
| BTC_LN | BTC_SPARK | not a swap: create a Spark Lightning invoice, pay it from the node (`rln_pay_invoice`) |
|
|
79
|
+
| BTC_SPARK | BTC_LN | create an invoice on the node (`rln_create_ln_invoice`), pay it from Spark |
|
|
80
|
+
| BTC_LN | BTC_L1 | close a channel (`rln_close_channel`), slow |
|
|
81
|
+
|
|
82
|
+
Other layers can appear in `kaleidoswap_get_pairs()` routes but are not
|
|
83
|
+
executable through these tools.
|
|
84
|
+
|
|
85
|
+
## Safety
|
|
86
|
+
|
|
87
|
+
1. Confirm from / to / amount / rate before `atomic_init`.
|
|
88
|
+
2. Never hard-code asset ids or layers; read them from the API.
|
|
89
|
+
3. Re-quote if execution would start more than ~30 s after the quote.
|
|
90
|
+
4. Check `rln_list_swaps()` for a pending taker swap before starting another.
|
|
91
|
+
5. Dry run: when asked for a dry run, quote and report only; never call
|
|
92
|
+
`kaleidoswap_atomic_init`, `rln_atomic_taker`, `kaleidoswap_atomic_execute`,
|
|
93
|
+
`rln_send_asset` or `rln_pay_invoice`.
|
|
@@ -1,12 +1,65 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: paid-data
|
|
3
|
-
description: Fetch premium or paywalled data
|
|
4
|
-
tools: fetch_paid_resource
|
|
5
|
-
triggers: premium, paid, l402, feed, subscription, unlock, paywall
|
|
3
|
+
description: Fetch premium or paywalled data behind an HTTP 402 Lightning payment (L402 or MPP, the Machine Payments Protocol) — paid feeds, pay-per-call APIs, unlockable resources — without signing up or holding an API key. Also finds paid APIs in the public 402index registry. Triggers when the user wants premium, paid, gated or unlockable data, or asks for a paid API.
|
|
4
|
+
tools: fetch_paid_resource, search_paid_apis, mpp_request_challenge, mpp_parse_challenge_header, mpp_submit_credential, rln_mpp_pay, spark_mpp_pay, l402_request_challenge, l402_fetch_resource
|
|
5
|
+
triggers: premium, paid, l402, mpp, 402, feed, subscription, unlock, paywall, pay per call, paid api, gated
|
|
6
|
+
metadata:
|
|
7
|
+
author: kaleidoswap
|
|
8
|
+
version: "0.2.0"
|
|
6
9
|
---
|
|
7
10
|
|
|
8
11
|
# Paid data
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
Servers gate a resource behind an HTTP 402 Lightning challenge; you pay the
|
|
14
|
+
invoice and present the proof. L402 is the Lightning-only subset of MPP. Tell
|
|
15
|
+
the user what was paid and what came back.
|
|
16
|
+
|
|
17
|
+
Use whichever tools you have:
|
|
18
|
+
|
|
19
|
+
## In-app: one call
|
|
20
|
+
|
|
21
|
+
`fetch_paid_resource({ url })` fetches, pays small invoices automatically
|
|
22
|
+
(capped by the host) and returns the data. Anything above the cap is declined;
|
|
23
|
+
report that instead of retrying.
|
|
24
|
+
|
|
25
|
+
## Over kaleido-mcp: three steps
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
1. mpp_request_challenge({ url })
|
|
29
|
+
→ { challenge_id, invoice, amount_sats, intent, expires_at, macaroon? }
|
|
30
|
+
|
|
31
|
+
2. rln_mpp_pay({ invoice, challenge_id, macaroon? }) ← pays from the RGB Lightning Node
|
|
32
|
+
(or spark_mpp_pay with the same arguments, from the Spark wallet; better for
|
|
33
|
+
tiny amounts where Lightning routing may fail)
|
|
34
|
+
→ { paid, payment_hash, preimage?, credential: "<JSON string>" }
|
|
35
|
+
|
|
36
|
+
3. mpp_submit_credential({ url, credential }) ← pass credential verbatim
|
|
37
|
+
→ { ok, status, data, receipt }
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Finish all three before `expires_at` (~60 s); a credential is single-use, so
|
|
41
|
+
restart from step 1 if it expires. Keep the `receipt` as proof of payment.
|
|
42
|
+
|
|
43
|
+
- **Discovery:** `search_paid_apis({ query, protocol?, health: "healthy" })`
|
|
44
|
+
lists registered endpoints with their price; pick a healthy one, then run the
|
|
45
|
+
flow on its `url`.
|
|
46
|
+
- **Own fetch:** with a `WWW-Authenticate` header already in hand, use
|
|
47
|
+
`mpp_parse_challenge_header({ url, www_authenticate })` instead of step 1.
|
|
48
|
+
- **Legacy L402-only servers:** `l402_request_challenge` /
|
|
49
|
+
`l402_fetch_resource`. Prefer the `mpp_*` tools; they handle both.
|
|
50
|
+
- **Sessions:** `intent: "session"` challenges allow pay-once, then cheap
|
|
51
|
+
repeat calls. Not every server offers them; fall back to `charge`.
|
|
52
|
+
|
|
53
|
+
| Error | Meaning | Fix |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| `Expected HTTP 402` | URL is not payment-gated | Fetch it normally |
|
|
56
|
+
| `payment failed` | Route or balance problem | Check balances; try `spark_mpp_pay` |
|
|
57
|
+
| `401 after submit` | Bad or reused credential | Redo steps 1–3 |
|
|
58
|
+
| `challenge expired` | Too slow between steps | Restart from step 1 |
|
|
59
|
+
|
|
60
|
+
## Rules
|
|
61
|
+
|
|
62
|
+
1. Show `amount_sats` before paying; ask for a yes above 1,000 sats.
|
|
63
|
+
2. Only pay challenges for URLs the user asked for.
|
|
64
|
+
3. Stop after two failures in a row and report; don't loop.
|
|
65
|
+
4. Dry run: report the price and what you would fetch; never pay.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: rgb-lightning-node
|
|
3
|
-
description: "Drive the user's local RGB Lightning Node (RLN) — read its pubkey/status, list channels and their capacities, check RGB asset balances, manage channels/peers, whitelist a swap, or create Lightning/RGB receive invoices. Triggers when the user asks about the node, their channels or capacities, needs an invoice, or is mid-atomic-swap and the maker needs the node pubkey or a swapstring whitelisted."
|
|
4
|
-
tools: rln_get_node_info, rln_get_balances, rln_list_channels, rln_list_assets, rln_get_asset_balance, rln_open_channel, rln_close_channel, rln_connect_peer, rln_get_channel_id,
|
|
5
|
-
triggers: node, nodeinfo, pubkey, peer, channels, channel capacity, list channels, inbound, capacity, asset balance, whitelist, taker, swapstring, invoice, receive, rgb invoice, ln invoice
|
|
3
|
+
description: "Drive the user's local RGB Lightning Node (RLN) — read its pubkey/status, list channels and their capacities, check RGB asset balances, manage channels/peers, whitelist a swap, or create Lightning/RGB receive invoices. Triggers when the user asks about the node, their channels or capacities, needs an invoice, wants to issue/mint a new RGB token or NFT, or is mid-atomic-swap and the maker needs the node pubkey or a swapstring whitelisted."
|
|
4
|
+
tools: rln_get_node_info, rln_get_balances, rln_list_channels, rln_list_assets, rln_get_asset_balance, rln_refresh_transfers, rln_get_address, rln_send_btc, rln_send_asset, rln_pay_invoice, rln_open_channel, rln_close_channel, rln_connect_peer, rln_get_channel_id, rln_atomic_taker, rln_list_swaps, rln_get_swap, rln_list_payments, rln_create_ln_invoice, rln_create_rgb_invoice, rln_list_transfers, rln_create_utxos, rln_issue_asset
|
|
5
|
+
triggers: node, nodeinfo, pubkey, peer, channels, channel capacity, list channels, open channel, close channel, inbound, capacity, asset balance, whitelist, taker, swapstring, swaps, payments, invoice, receive, send asset, send rgb, on-chain address, deposit, rgb invoice, ln invoice, issue, mint, new token, nft, utxos, transfers
|
|
6
6
|
metadata:
|
|
7
7
|
author: kaleidoswap
|
|
8
|
-
version: "0.
|
|
8
|
+
version: "0.3.3"
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# RGB Lightning Node (taker-side)
|
|
@@ -70,14 +70,79 @@ ASYNCHRONOUSLY (seconds to minutes after payment). If the new channel isn't
|
|
|
70
70
|
listed yet, say it's still opening and suggest checking again — don't claim
|
|
71
71
|
failure.
|
|
72
72
|
|
|
73
|
-
### `rln_list_assets` —
|
|
74
|
-
Lists RGB assets known to the node
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
### `rln_list_assets` — { schemas? }
|
|
74
|
+
Lists RGB assets known to the node: `asset_id`, `ticker`, `name`, `precision`
|
|
75
|
+
(in-app wallets also return balances). `schemas` is an optional filter — an
|
|
76
|
+
array of `"Nia"`, `"Uda"`, `"Cfa"`; call it with `{}` to list everything.
|
|
77
|
+
Use for "what assets do I hold / what's my USDT balance".
|
|
78
|
+
|
|
79
|
+
Call it **once** per question. An empty list is a real answer: the node holds
|
|
80
|
+
no RGB assets yet — tell the user that (and offer to issue one) instead of
|
|
81
|
+
calling it again.
|
|
82
|
+
|
|
83
|
+
### Tickers are not asset ids
|
|
84
|
+
`asset_id` arguments take the full RGB id (`rgb:…`), never a ticker like
|
|
85
|
+
`USDT` or `HCK`. When the user names an asset by ticker or name, first call
|
|
86
|
+
`rln_list_assets`, find the entry whose `ticker`/`name` matches, and pass its
|
|
87
|
+
`asset_id`. If nothing matches, say the node has no such asset — do not guess
|
|
88
|
+
an id.
|
|
77
89
|
|
|
78
90
|
### `rln_get_asset_balance` — { asset_id }
|
|
79
|
-
Balance for one RGB asset by id.
|
|
80
|
-
|
|
91
|
+
Balance for one RGB asset by id. Resolve a ticker to its `asset_id` with
|
|
92
|
+
`rln_list_assets` first.
|
|
93
|
+
|
|
94
|
+
| Field | Meaning |
|
|
95
|
+
|---|---|
|
|
96
|
+
| `settled` / `spendable` | On-chain (RGB_L1), UTXO-bound |
|
|
97
|
+
| `future` | Pending settlement |
|
|
98
|
+
| `offchain_outbound` | In a Lightning channel: what you can **send** |
|
|
99
|
+
| `offchain_inbound` | Channel receive capacity for this asset |
|
|
100
|
+
|
|
101
|
+
For USDT/XAUT held in an RGB channel, report `offchain_outbound`; `spendable`
|
|
102
|
+
is 0 and that is normal.
|
|
103
|
+
|
|
104
|
+
### `rln_refresh_transfers` — no args
|
|
105
|
+
Syncs pending RGB transfers. Call it before re-reading a balance or transfer
|
|
106
|
+
status that looks stale (e.g. right after an invoice was paid or a swap filled).
|
|
107
|
+
|
|
108
|
+
### `rln_get_address` — no args
|
|
109
|
+
An on-chain BTC address of the node, for funding it. Not an RGB invoice and not
|
|
110
|
+
a Lightning invoice.
|
|
111
|
+
|
|
112
|
+
### `rln_send_btc` — { address, amount_sat, fee_rate? } — 🔒 confirm-gated
|
|
113
|
+
Sends on-chain BTC from the node. Only when the user gave both the address and
|
|
114
|
+
the amount.
|
|
115
|
+
|
|
116
|
+
### `rln_send_asset` — 🔒 confirm-gated
|
|
117
|
+
Sends an RGB asset to the recipient encoded in an RGB invoice. Use the argument
|
|
118
|
+
names of the schema you were given: in-app wallets take `{ asset, amount, to }`
|
|
119
|
+
(ticker or asset_id, units, the invoice); kaleido-mcp takes
|
|
120
|
+
`{ asset_id, amount, recipient_id }`, where `asset_id` is the `rgb:…` id from
|
|
121
|
+
`rln_list_assets`. Never invent a recipient.
|
|
122
|
+
|
|
123
|
+
### `rln_pay_invoice` — { invoice } — 🔒 confirm-gated
|
|
124
|
+
Pays a BOLT11 Lightning invoice from the node. Pass the full invoice string.
|
|
125
|
+
|
|
126
|
+
### Channels and peers
|
|
127
|
+
- `rln_connect_peer { peer_pubkey_and_addr }` — `pubkey@host:port`. Needed
|
|
128
|
+
before opening a channel to a peer the node has never seen.
|
|
129
|
+
- `rln_open_channel { peer_pubkey_and_addr, capacity_sat, asset_id?, asset_amount?, push_msat?, is_public? }`
|
|
130
|
+
— 🔒 confirm-gated. Locks on-chain BTC (and optionally an RGB asset) into a
|
|
131
|
+
channel. Opening is asynchronous: report the `temporary_channel_id` and
|
|
132
|
+
suggest checking `rln_list_channels`.
|
|
133
|
+
- `rln_get_channel_id { temporary_channel_id }` — the final `channel_id` once
|
|
134
|
+
the channel is established.
|
|
135
|
+
- `rln_close_channel { channel_id, peer_pubkey, force? }` — 🔒 confirm-gated.
|
|
136
|
+
Both values come from `rln_list_channels`. `force` only for an unresponsive
|
|
137
|
+
peer.
|
|
138
|
+
|
|
139
|
+
### `rln_list_payments` — { limit? }
|
|
140
|
+
Recent Lightning payments, sent and received. Use for "did I get paid" /
|
|
141
|
+
"what did I pay" over Lightning.
|
|
142
|
+
|
|
143
|
+
### `rln_list_swaps` / `rln_get_swap { payment_hash, taker? }`
|
|
144
|
+
Atomic swaps as the node sees them (HTLC status). For the maker-side status use
|
|
145
|
+
`kaleidoswap_atomic_status`.
|
|
81
146
|
|
|
82
147
|
### `rln_atomic_taker` — { swapstring } — 🔒 confirm-gated
|
|
83
148
|
Tell the node "I accept this swap." Args: the `swapstring` returned by
|
|
@@ -92,22 +157,74 @@ Call this **after** `kaleidoswap_atomic_init` and **before**
|
|
|
92
157
|
### `rln_create_ln_invoice` — Lightning invoice for receiving sats
|
|
93
158
|
Args:
|
|
94
159
|
- `amount_sats` (optional) — omit for an amountless invoice.
|
|
95
|
-
- `
|
|
96
|
-
- `
|
|
160
|
+
- `description` (optional, kaleido-mcp) — memo shown to the payer.
|
|
161
|
+
- `expiry_sec` (default 3600, kaleido-mcp only) — invoice TTL in seconds.
|
|
162
|
+
|
|
163
|
+
Reply with the full `invoice` string from the result — never write an invoice
|
|
164
|
+
yourself.
|
|
97
165
|
|
|
98
166
|
Use when the user wants to **receive** a Lightning payment. Do NOT call inside
|
|
99
167
|
an atomic swap flow unless the user explicitly asked to invoice someone.
|
|
100
168
|
|
|
101
169
|
### `rln_create_rgb_invoice` — on-chain RGB receive invoice
|
|
102
|
-
Args:
|
|
103
|
-
- `
|
|
104
|
-
- `
|
|
105
|
-
|
|
106
|
-
|
|
170
|
+
Args (use the names in your schema):
|
|
171
|
+
- in-app wallets: `asset` (ticker or asset_id) + `amount`.
|
|
172
|
+
- kaleido-mcp: `asset_id` (the `rgb:…` id from `rln_list_assets` — never a
|
|
173
|
+
ticker; omit for an any-asset invoice), `amount` (display units),
|
|
174
|
+
`duration_seconds` (default 86400).
|
|
175
|
+
|
|
176
|
+
Reply with the full `invoice` string; the payer needs all of it.
|
|
107
177
|
|
|
108
178
|
Use when the user wants to **receive** an RGB asset directly (not over
|
|
109
179
|
Lightning). Outside the atomic swap flow.
|
|
110
180
|
|
|
181
|
+
### `rln_list_transfers` — { asset_id }
|
|
182
|
+
Transfers for one asset (issuance, sends, receives) with `status`
|
|
183
|
+
(`WaitingCounterparty`, `WaitingConfirmations`, `Settled`, `Failed`). Use it to
|
|
184
|
+
answer "has my RGB invoice been paid?". Pass the `asset_id` (`rgb:…`) from
|
|
185
|
+
`rln_list_assets` or `rln_issue_asset`; in-app wallets also accept a ticker.
|
|
186
|
+
|
|
187
|
+
### `rln_create_utxos` — { num?, size?, up_to?, fee_rate? } — 🔒 confirm-gated
|
|
188
|
+
Creates colorable UTXOs (spends a little on-chain BTC). A fresh node needs
|
|
189
|
+
these before it can **issue** or **receive** RGB assets. Call it when an RGB
|
|
190
|
+
call fails with "no available UTXOs", then retry the original action. The
|
|
191
|
+
defaults (`num` 5, `fee_rate` 1) are fine; set `up_to: true` to only top up to
|
|
192
|
+
`num` free UTXOs.
|
|
193
|
+
|
|
194
|
+
### `rln_issue_asset` — { name, ticker?, amount?, precision?, schema?, details? } — 🔒 confirm-gated
|
|
195
|
+
Creates a **new** RGB asset owned by this node. `schema`: `NIA` fungible token
|
|
196
|
+
(default), `CFA` collectible, `UDA` unique asset / NFT (supply 1).
|
|
197
|
+
- `ticker` (uppercase, 1–8 letters/digits, nothing else) is required for `NIA`
|
|
198
|
+
and `UDA`.
|
|
199
|
+
- `amount` is the total supply in display units (a number), required for `NIA`
|
|
200
|
+
and `CFA` — if the user did not say how many, ask before calling;
|
|
201
|
+
the raw supply is `amount × 10^precision` (`precision` defaults to 0).
|
|
202
|
+
- `details` is an optional description for `CFA` and `UDA`.
|
|
203
|
+
|
|
204
|
+
Reply with the returned `asset_id` — the user needs it to invoice or send the
|
|
205
|
+
new asset.
|
|
206
|
+
|
|
207
|
+
Examples:
|
|
208
|
+
- "issue 1000 TICKET tokens called Hackathon Ticket" →
|
|
209
|
+
`rln_issue_asset { name: "Hackathon Ticket", ticker: "TICKET", amount: 1000 }`
|
|
210
|
+
- "mint an NFT called Genesis Badge" →
|
|
211
|
+
`rln_issue_asset { name: "Genesis Badge", ticker: "GENESISB", schema: "UDA" }`
|
|
212
|
+
- "has anyone paid my TICKET invoice?" → `rln_refresh_transfers` →
|
|
213
|
+
`rln_list_transfers { asset_id: "<TICKET asset_id from rln_list_assets>" }`
|
|
214
|
+
|
|
215
|
+
kaleido-mcp exposes these three as `wdk_issue_asset`, `wdk_create_utxos` and
|
|
216
|
+
`wdk_list_transfers`, with the same arguments and `rln_*` aliases.
|
|
217
|
+
|
|
218
|
+
### Recipe: issue your own RGB asset
|
|
219
|
+
1. `rln_get_node_info` — the node is reachable and unlocked.
|
|
220
|
+
2. `rln_create_utxos {}` — skip if the node already has free colored UTXOs;
|
|
221
|
+
wait for the funding tx to confirm before issuing.
|
|
222
|
+
3. `rln_issue_asset { name, ticker, amount }` — confirm with the user first.
|
|
223
|
+
4. `rln_create_rgb_invoice` on the receiver's node, then `rln_send_asset` with
|
|
224
|
+
the new `asset_id` to distribute it.
|
|
225
|
+
5. `rln_refresh_transfers` → `rln_list_transfers { asset_id }` until the
|
|
226
|
+
transfer is `Settled`.
|
|
227
|
+
|
|
111
228
|
## The maker / node split
|
|
112
229
|
|
|
113
230
|
A user-driven swap on KaleidoSwap is a two-service flow. Keep them straight:
|
|
@@ -125,6 +242,14 @@ The node's two contributions to the swap are the **pubkey** and the
|
|
|
125
242
|
**whitelist ack** — nothing more. Don't reach for `/makerinit` or
|
|
126
243
|
`/makerexecute`; those are for nodes that act AS the maker, which is not us.
|
|
127
244
|
|
|
245
|
+
## Safety
|
|
246
|
+
|
|
247
|
+
- Show amount, recipient and asset before any send; the engine also asks for
|
|
248
|
+
confirmation on every 🔒 tool.
|
|
249
|
+
- Flag a send above half of the available balance.
|
|
250
|
+
- If the node is unreachable or the wallet is locked, use the `kaleido-node`
|
|
251
|
+
skill (start / unlock) before retrying.
|
|
252
|
+
|
|
128
253
|
## Reply style
|
|
129
254
|
|
|
130
255
|
- One short sentence built from the tool result.
|
|
@@ -3,6 +3,7 @@ name: spark-wallet
|
|
|
3
3
|
description: "Operate the user's Spark BTC wallet on this device — check Spark balance, get a Spark deposit address, create a Spark Lightning invoice to receive, pay any BOLT11 Lightning invoice with Spark, or send BTC on-chain from Spark. Use this when the user names Spark explicitly OR when paying a Lightning invoice on a phone where Spark is the connected layer. Pairs with the bitrefill skill: a Bitrefill purchase that returns a Lightning invoice is paid with `spark_pay_invoice`."
|
|
4
4
|
tools: spark_get_balance, spark_get_address, spark_get_onchain_address, spark_create_invoice, spark_pay_invoice, spark_send, get_price, fiat_to_sats, bitrefill_search, bitrefill_get_product, bitrefill_get_balance, bitrefill_create_invoice, bitrefill_get_invoice, bitrefill_get_order
|
|
5
5
|
triggers: spark, sprak, spakr, spark wallet, pay with spark, send with spark, spark balance, spark address, spark invoice, lightning invoice, pay invoice, bolt11, ln invoice, pay this invoice, on-chain address, onchain address, deposit address, deposit btc, fund spark
|
|
6
|
+
requires-tools: spark_get_balance
|
|
6
7
|
metadata:
|
|
7
8
|
author: kaleidoswap
|
|
8
9
|
version: "1.0.0"
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: submarine-swaps
|
|
3
|
+
description: "Pay a Lightning invoice from Liquid funds (L-USDT or L-BTC) through a KaleidoSwap submarine swap on the /v2 maker, and follow its status. Triggers when the user wants to pay a Lightning invoice with Liquid USDT / L-USDT / L-BTC, asks what Liquid assets can pay Lightning, or asks about a submarine swap."
|
|
4
|
+
tools: kaleidoswap_submarine_pairs, kaleidoswap_submarine_create, kaleidoswap_submarine_fund, kaleidoswap_submarine_status
|
|
5
|
+
triggers: l-usdt, liquid usdt, usdt on liquid, l-btc, liquid bitcoin, submarine, submarine swap, pay invoice with liquid, swap status
|
|
6
|
+
metadata:
|
|
7
|
+
author: kaleidoswap
|
|
8
|
+
version: "0.1.0"
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Submarine swaps — pay Lightning from Liquid
|
|
12
|
+
|
|
13
|
+
A submarine swap pays a Lightning (BOLT11) invoice with funds the user locks on
|
|
14
|
+
Liquid. The KaleidoSwap maker pays the invoice, then claims the lockup. It is
|
|
15
|
+
atomic: if the maker can't pay, the lockup is refunded to the user.
|
|
16
|
+
|
|
17
|
+
## Critical rules
|
|
18
|
+
|
|
19
|
+
- Every amount, fee and status in your reply MUST come from a tool result in the
|
|
20
|
+
CURRENT turn. Never guess the amount to lock — `kaleidoswap_submarine_create`
|
|
21
|
+
returns it.
|
|
22
|
+
- `kaleidoswap_submarine_fund` moves money. Call it ONLY after the user has
|
|
23
|
+
confirmed the exact `expected_amount` returned by `kaleidoswap_submarine_create`.
|
|
24
|
+
- `kaleidoswap_submarine_fund` takes only `swap_id`. Never try to pass an amount
|
|
25
|
+
or an address.
|
|
26
|
+
- A bare "USDT" means RGB USDT on the Lightning node, which uses the atomic swap
|
|
27
|
+
tools, not this skill. Use this skill only for Liquid assets (L-USDT, L-BTC).
|
|
28
|
+
|
|
29
|
+
## Flow
|
|
30
|
+
|
|
31
|
+
1. `kaleidoswap_submarine_create { invoice, from_asset }` — from_asset is
|
|
32
|
+
`L-USDT` (default) or `L-BTC`. Returns `swap_id`, `expected_amount` (smallest
|
|
33
|
+
unit: L-USDT has 8 decimals, L-BTC is sats) and the fees.
|
|
34
|
+
2. Tell the user the amount and ask to confirm.
|
|
35
|
+
3. `kaleidoswap_submarine_fund { swap_id }`.
|
|
36
|
+
4. `kaleidoswap_submarine_status { swap_id }` — `transaction.claimed` means the
|
|
37
|
+
invoice was paid. `failed: true` on a funded swap means the funds need a refund;
|
|
38
|
+
say so and quote the `refund` field.
|
|
39
|
+
|
|
40
|
+
Use `kaleidoswap_submarine_pairs` when the user asks what can pay a Lightning
|
|
41
|
+
invoice, or for the limits and fees.
|
|
42
|
+
|
|
43
|
+
## Examples
|
|
44
|
+
|
|
45
|
+
- "pay lntbs10u1p… with L-USDT" →
|
|
46
|
+
`kaleidoswap_submarine_create { invoice: "lntbs10u1p…", from_asset: "L-USDT" }`
|
|
47
|
+
- "did my Liquid payment go through? swap sub1" →
|
|
48
|
+
`kaleidoswap_submarine_status { swap_id: "sub1" }`
|
package/src/context/compress.ts
CHANGED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { annotateRgbBalances, fixRgbBalanceUnits, formatRgbAmount } from './rgb-units.js';
|
|
3
|
+
|
|
4
|
+
const LIST = {
|
|
5
|
+
assets: [
|
|
6
|
+
{ asset_id: 'rgb:usdt', ticker: 'USDT', name: 'Tether USD', precision: 0, balance: { settled: 1000, spendable: 1000 } },
|
|
7
|
+
{ asset_id: 'rgb:xaut', ticker: 'XAUT', name: 'Tether Gold', precision: 0, balance: { spendable: 2 } },
|
|
8
|
+
{ asset_id: 'rgb:dec', ticker: 'DEC', precision: 6, balance: { spendable: 1_500_000 } },
|
|
9
|
+
],
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
describe('RGB balance units', () => {
|
|
13
|
+
it('formats raw amounts with the asset precision', () => {
|
|
14
|
+
expect(formatRgbAmount(1_500_000, 6)).toBe('1.5');
|
|
15
|
+
expect(formatRgbAmount(1_000_000, 0)).toBe('1,000,000');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('adds balance_display next to each asset balance without touching the rest', () => {
|
|
19
|
+
const out = annotateRgbBalances(LIST) as typeof LIST & { assets: Array<{ balance_display: Record<string, string> }> };
|
|
20
|
+
expect(out.assets[0]!.balance_display).toEqual({ settled: '1,000 USDT', spendable: '1,000 USDT' });
|
|
21
|
+
expect(out.assets[2]!.balance_display).toEqual({ spendable: '1.5 DEC' });
|
|
22
|
+
expect(out.assets[0]!.balance).toEqual({ settled: 1000, spendable: 1000 });
|
|
23
|
+
expect(annotateRgbBalances({ vanilla: { spendable: 4277 } })).toEqual({ vanilla: { spendable: 4277 } });
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('relabels the asset balances the 2B model called satoshis', () => {
|
|
27
|
+
const answer =
|
|
28
|
+
'You hold two RGB assets:\n1. **USDT** (Tether USD)\n - Balance: 1,000 satoshis (spendable)\n' +
|
|
29
|
+
'2. **XAUT** (Tether Gold)\n - Balance: 2 satoshis (spendable)';
|
|
30
|
+
expect(fixRgbBalanceUnits(answer, [LIST])).toBe(
|
|
31
|
+
'You hold two RGB assets:\n1. **USDT** (Tether USD)\n - Balance: 1,000 USDT (spendable)\n' +
|
|
32
|
+
'2. **XAUT** (Tether Gold)\n - Balance: 2 XAUT (spendable)',
|
|
33
|
+
);
|
|
34
|
+
expect(fixRgbBalanceUnits('DEC balance: 1,500,000 sats', [LIST])).toBe('DEC balance: 1.5 DEC');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('leaves real sats and unmatched numbers alone', () => {
|
|
38
|
+
const btc = { btc: { vanilla: { spendable: 4277 } } };
|
|
39
|
+
expect(fixRgbBalanceUnits('Your on-chain balance is 4,277 sats.', [btc, LIST])).toBe('Your on-chain balance is 4,277 sats.');
|
|
40
|
+
expect(fixRgbBalanceUnits('USDT channel fee: 300 sats', [LIST])).toBe('USDT channel fee: 300 sats');
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RGB asset balances are raw integers in the asset's own unit, scaled by its
|
|
3
|
+
* `precision`. Nothing in the tool result says so, and small models fill the
|
|
4
|
+
* gap with "satoshis" ("USDT — Balance: 1,000 satoshis").
|
|
5
|
+
*
|
|
6
|
+
* `annotateRgbBalances` adds a `balance_display` next to each asset's
|
|
7
|
+
* `balance` before the result reaches the model ("1,000 USDT").
|
|
8
|
+
* `fixRgbBalanceUnits` corrects a final answer that still labels an asset
|
|
9
|
+
* balance as sats: the number must equal one of that asset's balances and the
|
|
10
|
+
* nearest ticker before it must be that asset's.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
type Obj = Record<string, unknown>;
|
|
14
|
+
|
|
15
|
+
interface RgbAsset {
|
|
16
|
+
ticker: string;
|
|
17
|
+
precision: number;
|
|
18
|
+
amounts: number[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const BALANCE_FIELDS = ['settled', 'spendable', 'future'] as const;
|
|
22
|
+
|
|
23
|
+
const isObj = (v: unknown): v is Obj => !!v && typeof v === 'object' && !Array.isArray(v);
|
|
24
|
+
|
|
25
|
+
function asAsset(v: Obj): RgbAsset | null {
|
|
26
|
+
if (typeof v.ticker !== 'string' || !v.ticker || !isObj(v.balance)) return null;
|
|
27
|
+
const amounts = BALANCE_FIELDS.map((k) => (v.balance as Obj)[k]).filter((n): n is number => typeof n === 'number');
|
|
28
|
+
if (!amounts.length) return null;
|
|
29
|
+
const precision = typeof v.precision === 'number' && v.precision > 0 ? v.precision : 0;
|
|
30
|
+
return { ticker: v.ticker, precision, amounts };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** A raw RGB amount in display units, e.g. 1500000 at precision 6 → "1.5". */
|
|
34
|
+
export function formatRgbAmount(raw: number, precision: number): string {
|
|
35
|
+
const value = raw / 10 ** precision;
|
|
36
|
+
return value.toLocaleString('en-US', { maximumFractionDigits: precision });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Return a copy of `result` where every RGB asset carries `balance_display`. */
|
|
40
|
+
export function annotateRgbBalances(result: unknown): unknown {
|
|
41
|
+
if (Array.isArray(result)) return result.map(annotateRgbBalances);
|
|
42
|
+
if (!isObj(result)) return result;
|
|
43
|
+
const out: Obj = {};
|
|
44
|
+
for (const [k, v] of Object.entries(result)) out[k] = annotateRgbBalances(v);
|
|
45
|
+
const asset = asAsset(result);
|
|
46
|
+
if (asset) {
|
|
47
|
+
const balance = result.balance as Obj;
|
|
48
|
+
const display: Obj = {};
|
|
49
|
+
for (const k of BALANCE_FIELDS) {
|
|
50
|
+
if (typeof balance[k] === 'number') display[k] = `${formatRgbAmount(balance[k] as number, asset.precision)} ${asset.ticker}`;
|
|
51
|
+
}
|
|
52
|
+
out.balance_display = display;
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function collectAssets(value: unknown, into: RgbAsset[]): void {
|
|
58
|
+
if (Array.isArray(value)) {
|
|
59
|
+
for (const v of value) collectAssets(v, into);
|
|
60
|
+
} else if (isObj(value)) {
|
|
61
|
+
const asset = asAsset(value);
|
|
62
|
+
if (asset) into.push(asset);
|
|
63
|
+
for (const v of Object.values(value)) collectAssets(v, into);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const AMOUNT_IN_SATS = /(\d[\d,]*(?:\.\d+)?)\s*(?:satoshis|sats?)\b/gi;
|
|
68
|
+
const escapeRe = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
69
|
+
|
|
70
|
+
/** Relabel asset balances that the answer calls sats, using the tool results it was built from. */
|
|
71
|
+
export function fixRgbBalanceUnits(text: string, toolResults: unknown[]): string {
|
|
72
|
+
const assets: RgbAsset[] = [];
|
|
73
|
+
for (const r of toolResults) collectAssets(r, assets);
|
|
74
|
+
if (!assets.length) return text;
|
|
75
|
+
const tickerRe = new RegExp(`\\b(${[...new Set(assets.map((a) => escapeRe(a.ticker)))].join('|')})\\b`, 'g');
|
|
76
|
+
|
|
77
|
+
return text.replace(AMOUNT_IN_SATS, (match: string, num: string, offset: number) => {
|
|
78
|
+
let nearest: string | undefined;
|
|
79
|
+
for (const m of text.slice(Math.max(0, offset - 300), offset).matchAll(tickerRe)) nearest = m[1];
|
|
80
|
+
if (!nearest) return match;
|
|
81
|
+
const value = Number(num.replace(/,/g, ''));
|
|
82
|
+
const asset = assets.find(
|
|
83
|
+
(a) => a.ticker === nearest && a.amounts.some((raw) => raw === value || raw / 10 ** a.precision === value),
|
|
84
|
+
);
|
|
85
|
+
if (!asset) return match;
|
|
86
|
+
const raw = asset.amounts.find((r) => r === value) ?? value * 10 ** asset.precision;
|
|
87
|
+
return `${formatRgbAmount(raw, asset.precision)} ${asset.ticker}`;
|
|
88
|
+
});
|
|
89
|
+
}
|