@needmoretruth/nmts-cli 0.17.2 → 0.34.2
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/AGENTS.md +125 -517
- package/CHANGELOG.md +104 -0
- package/README.ko.md +377 -469
- package/README.md +394 -474
- package/dist/account-proof.d.ts +29 -0
- package/dist/account-proof.js +6 -6
- package/dist/account.d.ts +18 -0
- package/dist/account.js +5 -5
- package/dist/agent-host.d.ts +71 -0
- package/dist/agent-host.js +169 -0
- package/dist/api-advice.d.ts +2 -0
- package/dist/api-advice.js +210 -0
- package/dist/api-key.d.ts +79 -0
- package/dist/api-key.js +7 -7
- package/dist/api-refusal.d.ts +14 -0
- package/dist/api-refusal.js +8 -0
- package/dist/api.d.ts +114 -0
- package/dist/api.js +59 -162
- package/dist/args.d.ts +160 -0
- package/dist/args.js +118 -30
- package/dist/artifact-about.d.ts +49 -0
- package/dist/autonomy.d.ts +39 -0
- package/dist/autonomy.js +110 -32
- package/dist/code-access.d.ts +29 -0
- package/dist/code-access.js +3 -3
- package/dist/code-vault.d.ts +38 -0
- package/dist/code-vault.js +8 -8
- package/dist/collision.d.ts +67 -0
- package/dist/collision.js +1 -1
- package/dist/commands/accept-terms.d.ts +11 -0
- package/dist/commands/accept-terms.js +106 -0
- package/dist/commands/balance.d.ts +7 -0
- package/dist/commands/balance.js +54 -1
- package/dist/commands/create-code-file.d.ts +24 -0
- package/dist/commands/create-code-file.js +93 -0
- package/dist/commands/create-link.d.ts +10 -0
- package/dist/commands/create-link.js +232 -0
- package/dist/commands/create.d.ts +20 -0
- package/dist/commands/create.js +43 -102
- package/dist/commands/credits.d.ts +9 -0
- package/dist/commands/credits.js +110 -0
- package/dist/commands/delete-account.d.ts +11 -0
- package/dist/commands/delete-account.js +63 -0
- package/dist/commands/deposit.d.ts +7 -0
- package/dist/commands/deposit.js +85 -0
- package/dist/commands/devices.d.ts +12 -0
- package/dist/commands/devices.js +144 -0
- package/dist/commands/documents.d.ts +35 -0
- package/dist/commands/documents.js +212 -0
- package/dist/commands/env.d.ts +5 -0
- package/dist/commands/env.js +10 -2
- package/dist/commands/erase.d.ts +13 -0
- package/dist/commands/erase.js +193 -0
- package/dist/commands/expiring.d.ts +19 -0
- package/dist/commands/extend.d.ts +33 -0
- package/dist/commands/extend.js +54 -15
- package/dist/commands/get.d.ts +18 -0
- package/dist/commands/key-manage.d.ts +11 -0
- package/dist/commands/key-manage.js +162 -0
- package/dist/commands/key.d.ts +28 -0
- package/dist/commands/key.js +249 -0
- package/dist/commands/kit.d.ts +11 -0
- package/dist/commands/kit.js +7 -7
- package/dist/commands/listfile.d.ts +13 -0
- package/dist/commands/listfile.js +12 -5
- package/dist/commands/login.d.ts +18 -0
- package/dist/commands/login.js +19 -19
- package/dist/commands/logout.d.ts +1 -0
- package/dist/commands/logout.js +1 -1
- package/dist/commands/losses.d.ts +12 -0
- package/dist/commands/losses.js +180 -0
- package/dist/commands/ls.d.ts +23 -0
- package/dist/commands/marks-dispatch.d.ts +2 -0
- package/dist/commands/marks-dispatch.js +51 -0
- package/dist/commands/marks.d.ts +46 -0
- package/dist/commands/marks.js +80 -0
- package/dist/commands/mcp.d.ts +24 -0
- package/dist/commands/mcp.js +70 -5
- package/dist/commands/mode.d.ts +8 -0
- package/dist/commands/mode.js +60 -24
- package/dist/commands/on-collision.d.ts +6 -0
- package/dist/commands/organise.d.ts +46 -0
- package/dist/commands/padding.d.ts +7 -0
- package/dist/commands/padding.js +114 -0
- package/dist/commands/public-code.d.ts +9 -0
- package/dist/commands/public-code.js +8 -8
- package/dist/commands/pull.d.ts +27 -0
- package/dist/commands/push-wallet.d.ts +19 -0
- package/dist/commands/push-wallet.js +69 -0
- package/dist/commands/push.d.ts +47 -0
- package/dist/commands/push.js +37 -7
- package/dist/commands/put-wallet.d.ts +77 -0
- package/dist/commands/put-wallet.js +316 -0
- package/dist/commands/put.d.ts +56 -0
- package/dist/commands/put.js +53 -6
- package/dist/commands/rebuild.d.ts +18 -0
- package/dist/commands/rebuild.js +108 -10
- package/dist/commands/receive.d.ts +24 -0
- package/dist/commands/recovery-dispatch.d.ts +2 -0
- package/dist/commands/recovery-dispatch.js +34 -0
- package/dist/commands/recovery-list.d.ts +11 -0
- package/dist/commands/recovery-list.js +6 -6
- package/dist/commands/recovery.d.ts +25 -0
- package/dist/commands/recovery.js +2 -2
- package/dist/commands/rollback.d.ts +9 -0
- package/dist/commands/rollback.js +118 -0
- package/dist/commands/s3.d.ts +23 -0
- package/dist/commands/s3.js +6 -6
- package/dist/commands/settings.d.ts +5 -0
- package/dist/commands/settings.js +21 -6
- package/dist/commands/share-dispatch.d.ts +2 -0
- package/dist/commands/share-dispatch.js +46 -0
- package/dist/commands/share.d.ts +14 -0
- package/dist/commands/share.js +13 -3
- package/dist/commands/shares-sent.d.ts +18 -0
- package/dist/commands/shares-sent.js +95 -0
- package/dist/commands/support.d.ts +24 -0
- package/dist/commands/support.js +321 -0
- package/dist/commands/sweep.d.ts +11 -0
- package/dist/commands/tip.d.ts +11 -0
- package/dist/commands/tip.js +86 -0
- package/dist/commands/trash.d.ts +17 -0
- package/dist/commands/trash.js +1 -1
- package/dist/commands/trial.d.ts +7 -0
- package/dist/commands/trial.js +16 -18
- package/dist/commands/unlock.d.ts +16 -0
- package/dist/commands/unlock.js +159 -0
- package/dist/commands/update.d.ts +42 -0
- package/dist/commands/update.js +1 -1
- package/dist/commands/usage.d.ts +8 -0
- package/dist/commands/verify.d.ts +18 -0
- package/dist/commands/verify.js +10 -6
- package/dist/commands/wallet-activity.d.ts +26 -0
- package/dist/commands/wallet-activity.js +117 -0
- package/dist/commands/wallet-donate.d.ts +26 -0
- package/dist/commands/wallet-donate.js +163 -0
- package/dist/commands/wallet-hall.d.ts +35 -0
- package/dist/commands/wallet-hall.js +214 -0
- package/dist/commands/wallet-send.d.ts +22 -0
- package/dist/commands/wallet-send.js +153 -0
- package/dist/commands/wallet-storage-ops.d.ts +24 -0
- package/dist/commands/wallet-storage-ops.js +215 -0
- package/dist/commands/wallet-storage.d.ts +18 -0
- package/dist/commands/wallet-storage.js +95 -0
- package/dist/commands/wallet-swap.d.ts +32 -0
- package/dist/commands/wallet-swap.js +287 -0
- package/dist/commands/wallet.d.ts +36 -0
- package/dist/commands/wallet.js +57 -15
- package/dist/commands/whoami.d.ts +10 -0
- package/dist/commands/whoami.js +24 -6
- package/dist/consent.d.ts +140 -0
- package/dist/consent.js +89 -30
- package/dist/credentials.d.ts +146 -0
- package/dist/credentials.js +10 -10
- package/dist/crypto-surface.d.ts +197 -0
- package/dist/crypto.d.ts +81 -0
- package/dist/crypto.js +13 -0
- package/dist/deposit.d.ts +25 -0
- package/dist/deposit.js +78 -0
- package/dist/download-part.d.ts +40 -0
- package/dist/download-sink.d.ts +76 -0
- package/dist/download.d.ts +79 -0
- package/dist/download.js +3 -3
- package/dist/drive-paths.d.ts +73 -0
- package/dist/environment.d.ts +62 -0
- package/dist/environment.js +29 -9
- package/dist/errors.d.ts +22 -0
- package/dist/errors.js +3 -3
- package/dist/exit.d.ts +44 -0
- package/dist/expiry.d.ts +58 -0
- package/dist/extend-budget.d.ts +35 -0
- package/dist/extend-budget.js +81 -0
- package/dist/extend-chain.d.ts +43 -0
- package/dist/extend-chain.js +40 -3
- package/dist/extend-plan.d.ts +148 -0
- package/dist/extend-plan.js +1 -1
- package/dist/gate.d.ts +18 -0
- package/dist/gate.js +100 -0
- package/dist/guards.d.ts +2 -0
- package/dist/help.d.ts +20 -0
- package/dist/help.js +199 -33
- package/dist/human-check.d.ts +23 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +49 -0
- package/dist/item-trash.d.ts +1 -0
- package/dist/kit-file.d.ts +56 -0
- package/dist/kit-file.js +10 -10
- package/dist/list-file.d.ts +88 -0
- package/dist/list-file.js +90 -7
- package/dist/list-view-find.d.ts +15 -0
- package/dist/list-view-order.d.ts +32 -0
- package/dist/main.d.ts +12 -0
- package/dist/main.js +112 -113
- package/dist/manifest-chunk-cache.d.ts +14 -0
- package/dist/manifest-chunk-cache.js +103 -0
- package/dist/manifest-chunk-flow.d.ts +81 -0
- package/dist/manifest-chunk-flow.js +307 -0
- package/dist/manifest-create.d.ts +23 -0
- package/dist/manifest-create.js +26 -30
- package/dist/manifest-write.d.ts +161 -0
- package/dist/manifest-write.js +42 -33
- package/dist/manifest.d.ts +82 -0
- package/dist/manifest.js +69 -21
- package/dist/mark-render.d.ts +26 -0
- package/dist/mcp-args.d.ts +17 -0
- package/dist/mcp-ask.d.ts +43 -0
- package/dist/mcp-ask.js +100 -0
- package/dist/mcp-tools/context.d.ts +46 -0
- package/dist/mcp-tools/credits.d.ts +3 -0
- package/dist/mcp-tools/credits.js +61 -0
- package/dist/mcp-tools/files.d.ts +3 -0
- package/dist/mcp-tools/files.js +34 -0
- package/dist/mcp-tools/organise.d.ts +3 -0
- package/dist/mcp-tools/organise.js +82 -2
- package/dist/mcp-tools/reads.d.ts +3 -0
- package/dist/mcp-tools/reads.js +132 -4
- package/dist/mcp-tools/share.d.ts +3 -0
- package/dist/mcp-tools/share.js +26 -7
- package/dist/mcp-tools/support.d.ts +3 -0
- package/dist/mcp-tools/support.js +140 -0
- package/dist/mcp-tools/tiers.d.ts +31 -0
- package/dist/mcp-tools/tiers.js +147 -0
- package/dist/mcp.d.ts +80 -0
- package/dist/mcp.js +110 -9
- package/dist/net-retry.d.ts +38 -0
- package/dist/network.d.ts +10 -0
- package/dist/notice.d.ts +7 -0
- package/dist/notice.js +7 -7
- package/dist/product.d.ts +29 -0
- package/dist/product.js +1 -1
- package/dist/progress.d.ts +37 -0
- package/dist/prompt.d.ts +31 -0
- package/dist/prompt.js +1 -1
- package/dist/rebuild-key-check.d.ts +26 -0
- package/dist/rebuild-key-check.js +90 -0
- package/dist/rebuild.d.ts +87 -0
- package/dist/rebuild.js +26 -4
- package/dist/recovery-assemble.d.ts +40 -0
- package/dist/recovery-assemble.js +1 -1
- package/dist/recovery-build.d.ts +40 -0
- package/dist/recovery-map-file.d.ts +53 -0
- package/dist/recovery-map-file.js +4 -4
- package/dist/recovery-map.d.ts +197 -0
- package/dist/recovery-release.d.ts +104 -0
- package/dist/recovery-seq.d.ts +10 -0
- package/dist/recovery-source.d.ts +50 -0
- package/dist/redact.d.ts +70 -0
- package/dist/redact.js +212 -0
- package/dist/registration.d.ts +30 -0
- package/dist/registration.js +5 -5
- package/dist/risk.d.ts +380 -0
- package/dist/risk.js +206 -0
- package/dist/run-log.d.ts +78 -0
- package/dist/run-log.js +301 -0
- package/dist/s3/listing.d.ts +47 -0
- package/dist/s3/multipart.d.ts +21 -0
- package/dist/s3/response-sink.d.ts +8 -0
- package/dist/s3/same-file.d.ts +54 -0
- package/dist/s3/server.d.ts +58 -0
- package/dist/s3/server.js +2 -2
- package/dist/s3/sigv4.d.ts +46 -0
- package/dist/s3/sigv4.js +1 -1
- package/dist/s3/staging.d.ts +10 -0
- package/dist/s3/xml.d.ts +28 -0
- package/dist/safe-path.d.ts +41 -0
- package/dist/seal.d.ts +120 -0
- package/dist/secret-reader.d.ts +28 -0
- package/dist/server.d.ts +9 -0
- package/dist/session.d.ts +32 -0
- package/dist/session.js +1 -1
- package/dist/setup-questions.d.ts +7 -0
- package/dist/share.d.ts +108 -0
- package/dist/share.js +1 -1
- package/dist/shared/lib/crypto/size-padding.d.ts +55 -0
- package/dist/shared/lib/crypto/size-padding.js +5 -0
- package/dist/shared/lib/drive/manifest-chunks.d.ts +95 -0
- package/dist/shared/lib/drive/manifest-chunks.js +252 -0
- package/dist/shared/lib/drive/manifest-codec.d.ts +214 -0
- package/dist/shared/lib/drive/manifest-codec.js +7 -7
- package/dist/shared/lib/drive/manifest-index.d.ts +98 -0
- package/dist/shared/lib/drive/manifest-ops.d.ts +167 -0
- package/dist/shared/lib/drive/manifest-ops.js +9 -4
- package/dist/shared/lib/drive/manifest-pack.d.ts +65 -0
- package/dist/shared/lib/drive/manifest-pack.js +287 -0
- package/dist/shared/lib/drive/manifest-settings.d.ts +113 -0
- package/dist/shared/lib/drive/manifest-settings.js +87 -6
- package/dist/shared/lib/drive/name-conflict.d.ts +76 -0
- package/dist/shared/lib/drive/rebuild-verify.d.ts +73 -0
- package/dist/shared/lib/drive/rebuild-verify.js +112 -0
- package/dist/shared/lib/drive/unique-name.d.ts +9 -0
- package/dist/shared/lib/drive/zstd.d.ts +27 -0
- package/dist/shared/lib/drive/zstd.js +74 -0
- package/dist/shared/lib/extend/epochs.d.ts +205 -0
- package/dist/shared/lib/net/retry-budget.d.ts +79 -0
- package/dist/shared/lib/share/shared-file-info.d.ts +21 -0
- package/dist/shared/lib/storage-control/chain.d.ts +57 -0
- package/dist/shared/lib/storage-control/chain.js +82 -0
- package/dist/shared/lib/storage-control/plan.d.ts +40 -0
- package/dist/shared/lib/storage-control/plan.js +72 -0
- package/dist/shared/lib/storage-control/reuse.d.ts +72 -0
- package/dist/shared/lib/storage-control/reuse.js +95 -0
- package/dist/shared/lib/storage-network.d.ts +25 -0
- package/dist/shared/lib/upload/part-plan.d.ts +24 -0
- package/dist/shared/lib/wallet/activity.d.ts +138 -0
- package/dist/shared/lib/wallet/activity.js +208 -0
- package/dist/shared/lib/wallet/send-rules.d.ts +68 -0
- package/dist/shared/lib/wallet/send-rules.js +116 -0
- package/dist/shared/lib/wallet/swap-rules.d.ts +115 -0
- package/dist/shared/lib/wallet/swap-rules.js +199 -0
- package/dist/shared/lib/wallet/tip.d.ts +14 -0
- package/dist/shared/lib/wallet/tip.js +34 -0
- package/dist/shared/lib/wallet/venue-ids.d.ts +23 -0
- package/dist/shared/lib/wallet/venue-ids.js +53 -0
- package/dist/standing-tip.d.ts +24 -0
- package/dist/standing-tip.js +52 -0
- package/dist/stdout.d.ts +38 -0
- package/dist/storage-control-chain.d.ts +38 -0
- package/dist/storage-control-chain.js +78 -0
- package/dist/sui-rpc.d.ts +15 -0
- package/dist/sui-rpc.js +59 -0
- package/dist/support-attach.d.ts +24 -0
- package/dist/support-attach.js +72 -0
- package/dist/support-copy.d.ts +42 -0
- package/dist/support-copy.js +109 -0
- package/dist/support-wire.d.ts +49 -0
- package/dist/support-wire.js +194 -0
- package/dist/trash-sweep.d.ts +55 -0
- package/dist/units.d.ts +2 -0
- package/dist/update-check.d.ts +50 -0
- package/dist/update-source.d.ts +63 -0
- package/dist/upload-api.d.ts +3 -0
- package/dist/upload-file.d.ts +97 -0
- package/dist/upload-file.js +3 -2
- package/dist/upload-price.d.ts +50 -0
- package/dist/upload-steps.d.ts +26 -0
- package/dist/upload-steps.js +29 -9
- package/dist/upload-store.d.ts +182 -0
- package/dist/upload-wallet-chain.d.ts +54 -0
- package/dist/upload-wallet-chain.js +203 -0
- package/dist/upload-wallet-plan.d.ts +128 -0
- package/dist/upload-wallet-plan.js +184 -0
- package/dist/upload-wallet.d.ts +24 -0
- package/dist/upload-wallet.js +208 -0
- package/dist/upload-wire.d.ts +244 -0
- package/dist/upload.d.ts +18 -0
- package/dist/upload.js +10 -0
- package/dist/usage-report.d.ts +35 -0
- package/dist/wallet-activity-chain.d.ts +5 -0
- package/dist/wallet-activity-chain.js +31 -0
- package/dist/wallet-chain.d.ts +19 -0
- package/dist/wallet-chain.js +2 -2
- package/dist/wallet-grant.d.ts +50 -0
- package/dist/wallet-grant.js +218 -0
- package/dist/wallet-send-chain.d.ts +24 -0
- package/dist/wallet-send-chain.js +49 -0
- package/dist/wallet-sign.d.ts +133 -0
- package/dist/wallet-sign.js +317 -0
- package/dist/wallet-storage-chain.d.ts +3 -0
- package/dist/wallet-storage-chain.js +20 -0
- package/dist/wallet-swap-chain.d.ts +52 -0
- package/dist/wallet-swap-chain.js +186 -0
- package/dist/wallet-swap-quote.d.ts +22 -0
- package/dist/wallet-swap-quote.js +126 -0
- package/dist/wallet.d.ts +135 -0
- package/dist/wallet.js +5 -5
- package/dist/walrus-write.d.ts +56 -0
- package/dist/walrus-write.js +5 -4
- package/dist/walrus.d.ts +86 -0
- package/dist/walrus.js +48 -10
- package/dist/zstd-node.d.ts +11 -0
- package/dist/zstd-node.js +71 -0
- package/docs/commands/accept-terms.md +15 -0
- package/docs/commands/create.md +17 -0
- package/docs/commands/credits.md +34 -0
- package/docs/commands/delete-account.md +14 -0
- package/docs/commands/deposit.md +27 -0
- package/docs/commands/devices.md +9 -0
- package/docs/commands/documents.md +10 -0
- package/docs/commands/env.md +21 -0
- package/docs/commands/extend.md +27 -0
- package/docs/commands/folders.md +10 -0
- package/docs/commands/get.md +14 -0
- package/docs/commands/key.md +14 -0
- package/docs/commands/login.md +23 -0
- package/docs/commands/logout.md +11 -0
- package/docs/commands/losses.md +13 -0
- package/docs/commands/ls.md +16 -0
- package/docs/commands/marks.md +9 -0
- package/docs/commands/mcp.md +33 -0
- package/docs/commands/mode.md +29 -0
- package/docs/commands/on-collision.md +13 -0
- package/docs/commands/padding.md +13 -0
- package/docs/commands/public-code.md +9 -0
- package/docs/commands/pull.md +9 -0
- package/docs/commands/push.md +12 -0
- package/docs/commands/put.md +36 -0
- package/docs/commands/rebuild.md +13 -0
- package/docs/commands/recovery.md +18 -0
- package/docs/commands/s3.md +17 -0
- package/docs/commands/share.md +15 -0
- package/docs/commands/support.md +16 -0
- package/docs/commands/tip.md +21 -0
- package/docs/commands/trash.md +25 -0
- package/docs/commands/trial.md +12 -0
- package/docs/commands/unlock.md +25 -0
- package/docs/commands/update.md +14 -0
- package/docs/commands/usage.md +14 -0
- package/docs/commands/verify.md +18 -0
- package/docs/commands/wallet.md +67 -0
- package/docs/commands/whoami.md +12 -0
- package/package.json +67 -8
- package/server.json +28 -0
- package/vendor/nmts-crypto/nmts_crypto_wasm.d.ts +23 -2
- package/vendor/nmts-crypto/nmts_crypto_wasm.js +44 -2
- package/vendor/nmts-crypto/nmts_crypto_wasm_bg.wasm +0 -0
- package/vendor/nmts-crypto/nmts_crypto_wasm_bg.wasm.d.ts +1 -0
- package/vendor/nmts-crypto/package.json +1 -1
- package/dist/commands/consent.js +0 -77
- package/dist/extend-sign.js +0 -130
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
// ⛔ THE ONE FILE IN THIS TOOL THAT SIGNS. Everything it can do moves real assets and cannot be
|
|
2
|
+
// reversed by anybody, including NMTS. It signs FIVE SHAPES and no others: extending a file's
|
|
3
|
+
// storage (`signExtension`), sending SUI or WAL to an address (`signTransfer`), swapping one for
|
|
4
|
+
// the other on a venue (`signSwap`), and the two halves of a wallet-paid upload — registering one
|
|
5
|
+
// part's blob (`signBlobRegister`, which buys the storage or binds a held resource and pays the
|
|
6
|
+
// relay's tip) and certifying it (`signBlobCertify`, gas only). Another transaction shape is a
|
|
7
|
+
// decision made here, in the open — the test pins the export list.
|
|
8
|
+
//
|
|
9
|
+
// ⛔ AND ONE THING THAT IS NOT A TRANSACTION. `signMessage` signs a short text under the
|
|
10
|
+
// personal-message intent: it moves nothing, it cannot be replayed as a transaction, and it
|
|
11
|
+
// exists so somebody can prove an address is theirs — all the hall of fame asks for.
|
|
12
|
+
//
|
|
13
|
+
// ⛔ IT IS REACHED FROM `commands/extend.ts`, `commands/wallet-send.ts`, `commands/wallet-donate.ts`,
|
|
14
|
+
// `commands/wallet-swap.ts` and `upload-wallet.ts` (the wallet rail `commands/put-wallet.ts`
|
|
15
|
+
// drives), each after the price, the fee and the balances have been read and printed, and (a
|
|
16
|
+
// gift excepted) after `requireWalletGrant(…)`. Nothing else imports it, and it is loaded lazily
|
|
17
|
+
// so that a run which does not spend never even brings the code into memory.
|
|
18
|
+
//
|
|
19
|
+
// ⛔ NO KEY LEAVES THIS FILE. `wallet.ts` states the rule for the whole tool — no function returns
|
|
20
|
+
// a seed, a private key or a keypair — and this file is the one exception to the reason for it
|
|
21
|
+
// (something has to hold a key to sign) rather than to the rule itself: the keypair is built
|
|
22
|
+
// inside a function, asked one question, and dropped. ⚠ `@mysten/sui` keeps the secret inside
|
|
23
|
+
// its own object with no method that clears it, exactly as it does in the browser's worker; what
|
|
24
|
+
// is controlled here is lifetime. The buffers this file makes — the derived material, the wallet
|
|
25
|
+
// root, the seed — are wiped on every path out, failures included.
|
|
26
|
+
//
|
|
27
|
+
// ⛔ THE DERIVATION IS THE ENGINE'S, AT THE INDEX THE BROWSER USES. A second derivation written
|
|
28
|
+
// here would be free to drift from the one `nmts wallet address` prints, and the way that
|
|
29
|
+
// surfaces is a transaction signed by a wallet the person has never funded.
|
|
30
|
+
import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
|
|
31
|
+
import { Transaction } from "@mysten/sui/transactions";
|
|
32
|
+
import { assertUsableCode } from "./account.js";
|
|
33
|
+
import { DERIVED, loadCrypto } from "./crypto.js";
|
|
34
|
+
import { NmtsError } from "./errors.js";
|
|
35
|
+
import { walrusClient } from "./extend-chain.js";
|
|
36
|
+
import { transferTransaction } from "./wallet-send-chain.js";
|
|
37
|
+
import { storageOpTransaction } from "./storage-control-chain.js";
|
|
38
|
+
import { swapTransaction } from "./wallet-swap-chain.js";
|
|
39
|
+
import { isRecord } from "./guards.js";
|
|
40
|
+
import { certifyTransaction, createdBlob, payingClient, registerTransaction, } from "./upload-wallet-chain.js";
|
|
41
|
+
import { BUILT_IN_WALLET_INDEX } from "./wallet.js";
|
|
42
|
+
/**
|
|
43
|
+
* The keypair the NMTS key derives.
|
|
44
|
+
*
|
|
45
|
+
* ⛔ NOT EXPORTED. A caller that could hold this could sign anything, and the whole of this tool's
|
|
46
|
+
* story about the wallet is that one command signs one shape of transaction.
|
|
47
|
+
*/
|
|
48
|
+
async function keypairFor(code) {
|
|
49
|
+
// The one refusal text for a malformed code lives in `account.ts`, so a typo fails here the same
|
|
50
|
+
// way it fails everywhere else in this tool rather than as an engine error.
|
|
51
|
+
await assertUsableCode(code);
|
|
52
|
+
const glue = await loadCrypto();
|
|
53
|
+
let bytes = null;
|
|
54
|
+
let derived = null;
|
|
55
|
+
let root = null;
|
|
56
|
+
let seed = null;
|
|
57
|
+
try {
|
|
58
|
+
bytes = glue.account_code_parse(code);
|
|
59
|
+
// ⛔ THIS BUFFER IS EVERY KEY IN THE ACCOUNT, not just the wallet root: the sign-in secret, the
|
|
60
|
+
// key that opens the files, the key that opens the file list.
|
|
61
|
+
derived = glue.kdf_derive(bytes);
|
|
62
|
+
const [from, to] = DERIVED.walletRoot;
|
|
63
|
+
root = derived.slice(from, to);
|
|
64
|
+
seed = glue.wallet_seed_for(root, BUILT_IN_WALLET_INDEX);
|
|
65
|
+
return Ed25519Keypair.fromSecretKey(seed);
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (error instanceof NmtsError)
|
|
69
|
+
throw error;
|
|
70
|
+
// ⛔ An engine message about a code can carry the code (`errors.ts`), so it is never passed on.
|
|
71
|
+
throw new NmtsError("The NMTS key could not be read on this machine.", { exitCode: 1 });
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
seed?.fill(0);
|
|
75
|
+
root?.fill(0);
|
|
76
|
+
derived?.fill(0);
|
|
77
|
+
bytes?.fill(0);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The address this tool would sign as.
|
|
82
|
+
*
|
|
83
|
+
* ⛔ IT EXISTS TO BE HELD AGAINST `walletAddress`. That function is what `nmts wallet address`
|
|
84
|
+
* prints and what somebody funds; this one is what a transaction would actually be signed by.
|
|
85
|
+
* Nothing else in this package proves the two are the same wallet, and the failure if they ever
|
|
86
|
+
* part is silent — a signature from an address with nothing in it, or worse, money sent to an
|
|
87
|
+
* address that signs nothing. A test compares them, offline, for free.
|
|
88
|
+
*/
|
|
89
|
+
export async function signerAddress(code) {
|
|
90
|
+
return (await keypairFor(code)).toSuiAddress();
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Extend every listed blob by `epochs`, in ONE transaction, signed by the account's own wallet.
|
|
94
|
+
*
|
|
95
|
+
* ONE SIGNATURE FOR ALL OF IT: `extendBlob` returns a transaction fragment, so every blob a file
|
|
96
|
+
* sits on goes into the same transaction. A multi-part file is one payment and one gas fee, not
|
|
97
|
+
* five — and a partial extension would buy nothing, because one expired blob is enough to make the
|
|
98
|
+
* file unreadable.
|
|
99
|
+
*
|
|
100
|
+
* ⛔ THE IDS ARE DE-DUPLICATED. Naming the same blob twice pays for the same epochs twice.
|
|
101
|
+
*
|
|
102
|
+
* ⚠ A FAILURE HERE IS NOT PROOF THAT NOTHING HAPPENED. A refusal from the node, a timeout, a
|
|
103
|
+
* connection that dropped after the bytes went out — none of them say whether the transaction
|
|
104
|
+
* was executed. The caller re-reads the chain rather than offering a second attempt against
|
|
105
|
+
* numbers it read before.
|
|
106
|
+
*/
|
|
107
|
+
export const signExtension = async ({ network, code, objectIds, epochs }) => {
|
|
108
|
+
const unique = [...new Set(objectIds)];
|
|
109
|
+
if (unique.length === 0) {
|
|
110
|
+
throw new NmtsError("There is nothing on this file that can be extended.", { exitCode: 4 });
|
|
111
|
+
}
|
|
112
|
+
if (!Number.isSafeInteger(epochs) || epochs <= 0) {
|
|
113
|
+
throw new NmtsError("An extension must be a positive whole number of epochs.", { exitCode: 2 });
|
|
114
|
+
}
|
|
115
|
+
const client = walrusClient(network);
|
|
116
|
+
const keypair = await keypairFor(code);
|
|
117
|
+
const tx = new Transaction();
|
|
118
|
+
// The sender must be set before the fragments resolve: paying with the SDK's default coin
|
|
119
|
+
// selection picks the WAL coins from the sender's own address.
|
|
120
|
+
tx.setSender(keypair.toSuiAddress());
|
|
121
|
+
for (const blobObjectId of unique) {
|
|
122
|
+
tx.add(client.walrus.extendBlob({ blobObjectId, epochs }));
|
|
123
|
+
}
|
|
124
|
+
const result = await client.signAndExecuteTransaction({
|
|
125
|
+
transaction: tx,
|
|
126
|
+
signer: keypair,
|
|
127
|
+
options: { showEffects: true },
|
|
128
|
+
});
|
|
129
|
+
// ⛔ A DIGEST IS NOT A SUCCESS. A transaction that was executed and FAILED still has one, and the
|
|
130
|
+
// gas for it is still gone; recording it as an extension would move the drive's expiry date
|
|
131
|
+
// over storage nobody bought. The status is asked for above and read here.
|
|
132
|
+
const effects = result.effects;
|
|
133
|
+
const status = isRecord(effects) ? effects["status"] : undefined;
|
|
134
|
+
const outcome = isRecord(status) ? status["status"] : undefined;
|
|
135
|
+
if (outcome === "failure") {
|
|
136
|
+
const why = isRecord(status) ? status["error"] : undefined;
|
|
137
|
+
throw new NmtsError(`The storage network refused the extension: ${typeof why === "string" ? why : "no reason was given"}.`, {
|
|
138
|
+
exitCode: 1,
|
|
139
|
+
nextStep: `The storage was NOT extended and the file still ends when it did. The transaction fee ` +
|
|
140
|
+
`was spent. The usual causes are too little WAL or SUI in this account's wallet — ` +
|
|
141
|
+
`\`nmts wallet\` shows both — or a length the network will no longer sell.`,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
// ⛔ SUCCESS IS ALREADY KNOWN — the effects above said so. This wait is for what happens NEXT:
|
|
145
|
+
// the server is told the digest, `nmts wallet` is run, the browser's activity list opens —
|
|
146
|
+
// and a full node that has not indexed the transaction yet answers those as if nothing
|
|
147
|
+
// happened. Waiting until it is readable is what the browser's signers do. A wait that times
|
|
148
|
+
// out changes nothing about what was signed, so it is not an error of this extension.
|
|
149
|
+
await client.waitForTransaction({ digest: result.digest }).catch(() => undefined);
|
|
150
|
+
return result.digest;
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* Send SUI or WAL to one address, in ONE transaction, signed by the account's own wallet.
|
|
154
|
+
*
|
|
155
|
+
* ⛔ THE TRANSACTION IS BUILT BY `wallet-send-chain.ts`, the same builder the fee was measured
|
|
156
|
+
* with, so what is signed is what was priced. The destination is whatever the caller validated
|
|
157
|
+
* (`send-rules.ts`) — nothing here checks it again, and nothing can take it back.
|
|
158
|
+
*
|
|
159
|
+
* ⚠ A FAILURE HERE IS NOT PROOF THAT NOTHING HAPPENED — the same words as the extension above.
|
|
160
|
+
*/
|
|
161
|
+
export const signTransfer = async ({ network, code, shape }) => {
|
|
162
|
+
const client = walrusClient(network);
|
|
163
|
+
const keypair = await keypairFor(code);
|
|
164
|
+
const tx = transferTransaction({ ...shape, sender: keypair.toSuiAddress() });
|
|
165
|
+
const result = await client.signAndExecuteTransaction({
|
|
166
|
+
transaction: tx,
|
|
167
|
+
signer: keypair,
|
|
168
|
+
options: { showEffects: true },
|
|
169
|
+
});
|
|
170
|
+
const effects = result.effects;
|
|
171
|
+
const status = isRecord(effects) ? effects["status"] : undefined;
|
|
172
|
+
const outcome = isRecord(status) ? status["status"] : undefined;
|
|
173
|
+
if (outcome === "failure") {
|
|
174
|
+
const why = isRecord(status) ? status["error"] : undefined;
|
|
175
|
+
throw new NmtsError(`The chain refused the transfer: ${typeof why === "string" ? why : "no reason was given"}.`, {
|
|
176
|
+
exitCode: 1,
|
|
177
|
+
nextStep: `Nothing was sent. The transaction fee was spent. The usual cause is a balance that moved ` +
|
|
178
|
+
`between reading it and signing — \`nmts wallet\` shows both balances now.`,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
await client.waitForTransaction({ digest: result.digest }).catch(() => undefined);
|
|
182
|
+
return result.digest;
|
|
183
|
+
};
|
|
184
|
+
/**
|
|
185
|
+
* Sign one short message with the account's own wallet. NOT a transaction: nothing moves.
|
|
186
|
+
*
|
|
187
|
+
* ⛔ THE MESSAGE IS THE CALLER'S, WHOLE AND UNCHANGED — the server rebuilds the same bytes from
|
|
188
|
+
* the fields it was sent, so a byte added here would invalidate every signature this tool
|
|
189
|
+
* makes. `signPersonalMessage` and not `sign`: that intent is the domain separator which stops
|
|
190
|
+
* signed text from ever being read as a transaction this wallet authorised.
|
|
191
|
+
*/
|
|
192
|
+
export const signMessage = async ({ code, message }) => {
|
|
193
|
+
const keypair = await keypairFor(code);
|
|
194
|
+
const { signature } = await keypair.signPersonalMessage(new TextEncoder().encode(message));
|
|
195
|
+
return signature;
|
|
196
|
+
};
|
|
197
|
+
/**
|
|
198
|
+
* Swap SUI for WAL or WAL for SUI on the named venue, in ONE transaction, signed by the account's
|
|
199
|
+
* own wallet. Every output goes back to the signer — DeepBook's three coins are sent there by this
|
|
200
|
+
* transaction, Bluefin's by its own entry function — so there is no destination to get wrong.
|
|
201
|
+
*
|
|
202
|
+
* ⛔ THE TRANSACTION IS BUILT BY `wallet-swap-chain.ts`, the same builder the fee was measured with,
|
|
203
|
+
* on the same Bluefin package the quote used. The minimum-out in the shape is what protects the
|
|
204
|
+
* person: the chain refuses a swap that would give less, and the fee for that refusal is spent.
|
|
205
|
+
*
|
|
206
|
+
* ⚠ A FAILURE HERE IS NOT PROOF THAT NOTHING HAPPENED — the same words as the extension above.
|
|
207
|
+
*/
|
|
208
|
+
export const signSwap = async ({ network, code, shape }) => {
|
|
209
|
+
const client = walrusClient(network);
|
|
210
|
+
const keypair = await keypairFor(code);
|
|
211
|
+
const tx = swapTransaction({ ...shape, network, sender: keypair.toSuiAddress() });
|
|
212
|
+
const result = await client.signAndExecuteTransaction({
|
|
213
|
+
transaction: tx,
|
|
214
|
+
signer: keypair,
|
|
215
|
+
options: { showEffects: true },
|
|
216
|
+
});
|
|
217
|
+
const refused = refusedBecause(result);
|
|
218
|
+
if (refused !== null) {
|
|
219
|
+
throw new NmtsError(`The chain refused the swap: ${refused}.`, {
|
|
220
|
+
exitCode: 1,
|
|
221
|
+
nextStep: `Nothing was swapped. The transaction fee was spent. The usual causes are a price that moved ` +
|
|
222
|
+
`past the minimum between the quote and the signature — quote again, or allow more slippage — ` +
|
|
223
|
+
`or a balance that moved. \`nmts wallet\` shows both balances now.`,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
await client.waitForTransaction({ digest: result.digest }).catch(() => undefined);
|
|
227
|
+
return result.digest;
|
|
228
|
+
};
|
|
229
|
+
/** What a refused execution said, or null when the effects say it went through. */
|
|
230
|
+
function refusedBecause(result) {
|
|
231
|
+
const effects = isRecord(result) ? result["effects"] : undefined;
|
|
232
|
+
const status = isRecord(effects) ? effects["status"] : undefined;
|
|
233
|
+
if (!isRecord(status) || status["status"] !== "failure")
|
|
234
|
+
return null;
|
|
235
|
+
const why = status["error"];
|
|
236
|
+
return typeof why === "string" ? why : "no reason was given";
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Register ONE part's blob, signed by the account's own wallet: the relay's tip, then the storage
|
|
240
|
+
* — bought for `epochs`, or a resource the wallet already holds, cut to fit first if asked.
|
|
241
|
+
*
|
|
242
|
+
* ⛔ THE TRANSACTION IS BUILT BY `upload-wallet-chain.ts`, the same builder the fee was measured
|
|
243
|
+
* with, so what is signed is what was priced. What comes back is what the commit and the resume
|
|
244
|
+
* need: the digest the relay checks its tip in, the blob object, and the epoch the chain says
|
|
245
|
+
* the storage ends at.
|
|
246
|
+
*
|
|
247
|
+
* ⚠ A FAILURE HERE IS NOT PROOF THAT NOTHING HAPPENED — the same words as the extension above.
|
|
248
|
+
* The caller keeps its record and re-reads it rather than registering again.
|
|
249
|
+
*/
|
|
250
|
+
export const signBlobRegister = async ({ network, code, relayUrl, ...shape }) => {
|
|
251
|
+
const client = payingClient(network, relayUrl);
|
|
252
|
+
const keypair = await keypairFor(code);
|
|
253
|
+
const tx = await registerTransaction(client, network, { ...shape, sender: keypair.toSuiAddress() });
|
|
254
|
+
const result = await client.signAndExecuteTransaction({
|
|
255
|
+
transaction: tx,
|
|
256
|
+
signer: keypair,
|
|
257
|
+
options: { showEffects: true, showObjectChanges: true },
|
|
258
|
+
});
|
|
259
|
+
const refused = refusedBecause(result);
|
|
260
|
+
if (refused !== null) {
|
|
261
|
+
throw new NmtsError(`The storage network refused the registration: ${refused}.`, {
|
|
262
|
+
exitCode: 1,
|
|
263
|
+
nextStep: `Nothing is stored and no storage was bought. The transaction fee was spent. The usual causes ` +
|
|
264
|
+
`are too little WAL or SUI in this account's wallet — \`nmts wallet\` shows both — or a held ` +
|
|
265
|
+
`resource too small for the part once encoded.`,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
await client.waitForTransaction({ digest: result.digest }).catch(() => undefined);
|
|
269
|
+
const blob = await createdBlob(client, result.objectChanges);
|
|
270
|
+
return { digest: result.digest, ...blob };
|
|
271
|
+
};
|
|
272
|
+
/** Certify ONE registered part from the relay's certificate. Gas only; nothing else leaves the wallet. */
|
|
273
|
+
export const signBlobCertify = async ({ network, code, relayUrl, ...shape }) => {
|
|
274
|
+
const client = payingClient(network, relayUrl);
|
|
275
|
+
const keypair = await keypairFor(code);
|
|
276
|
+
const tx = certifyTransaction(client, shape);
|
|
277
|
+
tx.setSender(keypair.toSuiAddress());
|
|
278
|
+
const result = await client.signAndExecuteTransaction({
|
|
279
|
+
transaction: tx,
|
|
280
|
+
signer: keypair,
|
|
281
|
+
options: { showEffects: true },
|
|
282
|
+
});
|
|
283
|
+
const refused = refusedBecause(result);
|
|
284
|
+
if (refused !== null) {
|
|
285
|
+
throw new NmtsError(`The storage network refused the certification: ${refused}.`, {
|
|
286
|
+
exitCode: 1,
|
|
287
|
+
nextStep: `The storage is bought and the bytes are on the network; only the certification failed, and ` +
|
|
288
|
+
`its fee was spent. Running the same command again certifies again and buys nothing.`,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
await client.waitForTransaction({ digest: result.digest }).catch(() => undefined);
|
|
292
|
+
return result.digest;
|
|
293
|
+
};
|
|
294
|
+
/**
|
|
295
|
+
* Cut, join or hand over a storage resource, in ONE transaction, signed by the account's own wallet.
|
|
296
|
+
*
|
|
297
|
+
* ⛔ THE TRANSACTION IS BUILT BY `storage-control-chain.ts`, the same builder the dry run priced,
|
|
298
|
+
* so what is signed is what was reviewed. A failed execution still has a digest and still
|
|
299
|
+
* spent its gas, so the status is read and a failure is said as one.
|
|
300
|
+
*/
|
|
301
|
+
export const signStorageOp = async ({ network, code, shape, walrusPackageId }) => {
|
|
302
|
+
const client = walrusClient(network);
|
|
303
|
+
const keypair = await keypairFor(code);
|
|
304
|
+
const tx = storageOpTransaction(shape, { walrusPackageId, sender: keypair.toSuiAddress() });
|
|
305
|
+
const result = await client.signAndExecuteTransaction({ transaction: tx, signer: keypair, options: { showEffects: true } });
|
|
306
|
+
const effects = result.effects;
|
|
307
|
+
const status = isRecord(effects) ? effects["status"] : undefined;
|
|
308
|
+
if (isRecord(status) && status["status"] === "failure") {
|
|
309
|
+
const why = status["error"];
|
|
310
|
+
throw new NmtsError(`The chain refused it: ${typeof why === "string" ? why : "no reason was given"}.`, {
|
|
311
|
+
exitCode: 1,
|
|
312
|
+
nextStep: "Nothing changed on the resource. The transaction fee was spent.",
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
await client.waitForTransaction({ digest: result.digest }).catch(() => undefined);
|
|
316
|
+
return result.digest;
|
|
317
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// The live read behind `nmts wallet storage`: the resources one address holds, and the epoch.
|
|
2
|
+
//
|
|
3
|
+
// ⛔ READ ONLY, and the reader is the browser's (`shared/lib/storage-control/chain.ts`). The epoch
|
|
4
|
+
// comes from the same window read `nmts extend` prices with; when that read fails the list is
|
|
5
|
+
// still printed, without the "usable now" judgement, rather than not at all.
|
|
6
|
+
import { SuiJsonRpcClient } from "@mysten/sui/jsonRpc";
|
|
7
|
+
import { MAINNET_WALRUS_PACKAGE_CONFIG, TESTNET_WALRUS_PACKAGE_CONFIG } from "@mysten/walrus";
|
|
8
|
+
import { extendReads } from "./extend-chain.js";
|
|
9
|
+
import { readOwnedStorage, readStorageType } from "./shared/lib/storage-control/chain.js";
|
|
10
|
+
import { suiRpcTransport } from "./sui-rpc.js";
|
|
11
|
+
export async function readWalletStorage(network, address) {
|
|
12
|
+
const client = new SuiJsonRpcClient({ network, transport: suiRpcTransport(network) });
|
|
13
|
+
const walrus = network === "mainnet" ? MAINNET_WALRUS_PACKAGE_CONFIG : TESTNET_WALRUS_PACKAGE_CONFIG;
|
|
14
|
+
const [type, window] = await Promise.all([
|
|
15
|
+
readStorageType(client, walrus.systemObjectId),
|
|
16
|
+
extendReads(network).readWindow(),
|
|
17
|
+
]);
|
|
18
|
+
const items = await readOwnedStorage(client, address, type);
|
|
19
|
+
return { items, currentEpoch: window?.clock.current ?? null };
|
|
20
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Transaction } from "@mysten/sui/transactions";
|
|
2
|
+
import type { Network } from "./network.ts";
|
|
3
|
+
import type { SwapDirection, SwapVenue } from "./shared/lib/wallet/swap-rules.ts";
|
|
4
|
+
import { type WalletBalances } from "./wallet.ts";
|
|
5
|
+
import { type VenueQuote } from "./wallet-swap-quote.ts";
|
|
6
|
+
/** Where a swap runs: one of the two mainnet venues, or the official testnet facility. */
|
|
7
|
+
export type SwapRail = SwapVenue | "exchange";
|
|
8
|
+
/** Bluefin's addresses, with the package RESOLVED and version-checked on chain, never assumed. */
|
|
9
|
+
export interface BluefinBinding {
|
|
10
|
+
packageId: string;
|
|
11
|
+
globalConfigId: string;
|
|
12
|
+
poolId: string;
|
|
13
|
+
}
|
|
14
|
+
/** The testnet facility: its object, the package its type names, and its rate (WAL per SUI, as a fraction). */
|
|
15
|
+
export interface ExchangeFacility {
|
|
16
|
+
objectId: string;
|
|
17
|
+
packageId: string;
|
|
18
|
+
rateWal: bigint;
|
|
19
|
+
rateSui: bigint;
|
|
20
|
+
}
|
|
21
|
+
export interface SwapShape {
|
|
22
|
+
venue: SwapRail;
|
|
23
|
+
direction: SwapDirection;
|
|
24
|
+
amountInUnits: bigint;
|
|
25
|
+
/** The least to accept, or the chain refuses the swap. The facility has no such argument: 0n. */
|
|
26
|
+
minOutUnits: bigint;
|
|
27
|
+
/** A gas ceiling, or undefined to let the SDK set one from its own dry run. */
|
|
28
|
+
gasBudgetMist?: bigint | undefined;
|
|
29
|
+
/** Present exactly when `venue` is bluefin: the signature uses the package the quote used. */
|
|
30
|
+
bluefin?: BluefinBinding | undefined;
|
|
31
|
+
/** Present exactly when `venue` is exchange. */
|
|
32
|
+
exchange?: ExchangeFacility | undefined;
|
|
33
|
+
}
|
|
34
|
+
/** The rails a network has. Testnet's DeepBook book trades a different WAL, and Bluefin has none. */
|
|
35
|
+
export declare function railsFor(network: Network): readonly SwapRail[];
|
|
36
|
+
export declare function swapTransaction(input: SwapShape & {
|
|
37
|
+
network: Network;
|
|
38
|
+
sender: string;
|
|
39
|
+
}): Transaction;
|
|
40
|
+
/** What `commands/wallet-swap.ts` reads before it prints a review. */
|
|
41
|
+
export interface SwapReads {
|
|
42
|
+
readWallet(address: string): Promise<WalletBalances>;
|
|
43
|
+
/** Bluefin's current package, version-checked. Throws when no known package passes. */
|
|
44
|
+
resolveBluefin(): Promise<BluefinBinding>;
|
|
45
|
+
/** The testnet facility and its rate. Throws off testnet, or when the object cannot be read. */
|
|
46
|
+
readExchange(): Promise<ExchangeFacility>;
|
|
47
|
+
/** One venue's answer now. Throws when it does not answer — never a number in its place. */
|
|
48
|
+
quote(venue: SwapVenue, direction: SwapDirection, amountInUnits: bigint, bluefin: BluefinBinding | null): Promise<VenueQuote>;
|
|
49
|
+
/** The fee in MIST measured by dry-running this exact swap, or null when it could not be. */
|
|
50
|
+
estimateFee(shape: SwapShape, sender: string): Promise<bigint | null>;
|
|
51
|
+
}
|
|
52
|
+
export declare function swapReads(network: Network): SwapReads;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// The swap transaction and its live reads: the wallet's balances, Bluefin's current package, the
|
|
2
|
+
// testnet facility's rate, and the fee a dry run measures. The quotes live next door in
|
|
3
|
+
// `wallet-swap-quote.ts`; both files read, and only `wallet-sign.ts` signs.
|
|
4
|
+
//
|
|
5
|
+
// ⛔ ONE BUILDER FOR THE FEE AND THE SIGNATURE. `estimateFee` and `wallet-sign.ts` both call
|
|
6
|
+
// `swapTransaction`, so the fee printed is the fee of the transaction that is then signed.
|
|
7
|
+
//
|
|
8
|
+
// ⛔ THE SHAPES ARE THE BROWSER'S, CALL FOR CALL. DeepBook: `pool::swap_exact_quote_for_base` (SUI→WAL)
|
|
9
|
+
// or `swap_exact_base_for_quote` (WAL→SUI) with an EMPTY DEEP coin so the fee comes off the input
|
|
10
|
+
// coin, and all THREE outputs (base, quote, DEEP) sent back to the signer — Move cannot drop a
|
|
11
|
+
// coin, so a forgotten one aborts the whole transaction. Bluefin: `gateway::swap_assets`, which
|
|
12
|
+
// sends its outputs to the sender itself; type arguments always in pool order [WAL, SUI], the
|
|
13
|
+
// direction carried by the `a2b` flag, and a sqrt-price limit one step inside the tick range
|
|
14
|
+
// (the exact end aborts). Testnet: the official Walrus facility `wal_exchange::exchange_all_for_wal`,
|
|
15
|
+
// SUI→WAL only, whose package is read off the Exchange object's own type. The ids come from
|
|
16
|
+
// `shared/lib/wallet/venue-ids.ts`, copied byte-for-byte from the browser.
|
|
17
|
+
//
|
|
18
|
+
// ⛔ OUR SHARE IS ZERO. No NMTS address, no fee argument, no output of ours in any of the three.
|
|
19
|
+
import { coinWithBalance, Transaction } from "@mysten/sui/transactions";
|
|
20
|
+
import { TESTNET_WALRUS_PACKAGE_CONFIG } from "@mysten/walrus";
|
|
21
|
+
import { walrusClient, netGasFee } from "./extend-chain.js";
|
|
22
|
+
import { isRecord } from "./guards.js";
|
|
23
|
+
import { BLUEFIN_GLOBAL_CONFIG_IDS, BLUEFIN_MAX_SQRT_PRICE, BLUEFIN_MIN_SQRT_PRICE, BLUEFIN_PACKAGE_IDS, BLUEFIN_UPGRADE_CAP_IDS, BLUEFIN_WAL_SUI_POOLS, DEEP_COIN_TYPES, DEEPBOOK_PACKAGE_IDS, DEEPBOOK_WAL_SUI_POOLS, } from "./shared/lib/wallet/venue-ids.js";
|
|
24
|
+
import { readBalances, SUI_COIN_TYPE, walCoinType } from "./wallet.js";
|
|
25
|
+
import { chainReader } from "./wallet-chain.js";
|
|
26
|
+
import { quoteVenue, QUOTE_SENDER } from "./wallet-swap-quote.js";
|
|
27
|
+
/** The rails a network has. Testnet's DeepBook book trades a different WAL, and Bluefin has none. */
|
|
28
|
+
export function railsFor(network) {
|
|
29
|
+
return network === "mainnet" ? ["deepbook", "bluefin"] : ["exchange"];
|
|
30
|
+
}
|
|
31
|
+
export function swapTransaction(input) {
|
|
32
|
+
const tx = new Transaction();
|
|
33
|
+
tx.setSender(input.sender);
|
|
34
|
+
const walType = walCoinType(input.network);
|
|
35
|
+
const a2b = input.direction === "WAL_TO_SUI";
|
|
36
|
+
const coinIn = a2b
|
|
37
|
+
? tx.add(coinWithBalance({ balance: input.amountInUnits, type: walType }))
|
|
38
|
+
: tx.splitCoins(tx.gas, [input.amountInUnits])[0];
|
|
39
|
+
if (input.venue === "deepbook") {
|
|
40
|
+
const deepIn = tx.moveCall({ target: "0x2::coin::zero", typeArguments: [DEEP_COIN_TYPES[input.network]] });
|
|
41
|
+
const swapped = tx.moveCall({
|
|
42
|
+
target: `${DEEPBOOK_PACKAGE_IDS[input.network]}::pool::${a2b ? "swap_exact_base_for_quote" : "swap_exact_quote_for_base"}`,
|
|
43
|
+
typeArguments: [walType, SUI_COIN_TYPE],
|
|
44
|
+
arguments: [tx.object(DEEPBOOK_WAL_SUI_POOLS[input.network]), coinIn, deepIn, tx.pure.u64(input.minOutUnits), tx.object.clock()],
|
|
45
|
+
});
|
|
46
|
+
// All three outputs — base, quote, DEEP — go back to the signer. The result is indexed, and an
|
|
47
|
+
// index is typed as possibly absent; nothing below can build a transfer of fewer than three.
|
|
48
|
+
const outputs = [swapped[0], swapped[1], swapped[2]].filter((coin) => coin !== undefined);
|
|
49
|
+
if (outputs.length !== 3)
|
|
50
|
+
throw new Error("DeepBook's swap did not yield its three outputs.");
|
|
51
|
+
tx.transferObjects(outputs, input.sender);
|
|
52
|
+
}
|
|
53
|
+
else if (input.venue === "bluefin") {
|
|
54
|
+
if (input.bluefin === undefined)
|
|
55
|
+
throw new Error("A Bluefin swap needs its resolved package first.");
|
|
56
|
+
const coinZero = tx.moveCall({ target: "0x2::coin::zero", typeArguments: [a2b ? SUI_COIN_TYPE : walType] });
|
|
57
|
+
tx.moveCall({
|
|
58
|
+
target: `${input.bluefin.packageId}::gateway::swap_assets`,
|
|
59
|
+
typeArguments: [walType, SUI_COIN_TYPE],
|
|
60
|
+
arguments: [
|
|
61
|
+
tx.object.clock(),
|
|
62
|
+
tx.object(input.bluefin.globalConfigId),
|
|
63
|
+
tx.object(input.bluefin.poolId),
|
|
64
|
+
a2b ? coinIn : coinZero,
|
|
65
|
+
a2b ? coinZero : coinIn,
|
|
66
|
+
tx.pure.bool(a2b),
|
|
67
|
+
tx.pure.bool(true),
|
|
68
|
+
tx.pure.u64(input.amountInUnits),
|
|
69
|
+
tx.pure.u64(input.minOutUnits),
|
|
70
|
+
tx.pure.u128(a2b ? BLUEFIN_MIN_SQRT_PRICE : BLUEFIN_MAX_SQRT_PRICE),
|
|
71
|
+
],
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
if (input.exchange === undefined)
|
|
76
|
+
throw new Error("The testnet exchange needs its object first.");
|
|
77
|
+
if (a2b)
|
|
78
|
+
throw new Error("The testnet facility only turns SUI into WAL.");
|
|
79
|
+
const walOut = tx.moveCall({
|
|
80
|
+
target: `${input.exchange.packageId}::wal_exchange::exchange_all_for_wal`,
|
|
81
|
+
arguments: [tx.object(input.exchange.objectId), coinIn],
|
|
82
|
+
});
|
|
83
|
+
tx.transferObjects([walOut], input.sender);
|
|
84
|
+
}
|
|
85
|
+
if (input.gasBudgetMist !== undefined)
|
|
86
|
+
tx.setGasBudget(input.gasBudgetMist);
|
|
87
|
+
return tx;
|
|
88
|
+
}
|
|
89
|
+
const FULL_ADDRESS = /^0x[0-9a-f]{64}$/;
|
|
90
|
+
/** `fields.<name>` of a Move object's content, as a string, or null when the shape differs. */
|
|
91
|
+
function fieldOf(content, name) {
|
|
92
|
+
const fields = isRecord(content) ? content["fields"] : undefined;
|
|
93
|
+
return isRecord(fields) ? fields[name] : undefined;
|
|
94
|
+
}
|
|
95
|
+
export function swapReads(network) {
|
|
96
|
+
const client = walrusClient(network);
|
|
97
|
+
let bluefin = null;
|
|
98
|
+
async function livePackage(upgradeCapId) {
|
|
99
|
+
try {
|
|
100
|
+
const obj = await client.getObject({ id: upgradeCapId, options: { showContent: true, showType: true } });
|
|
101
|
+
if (obj.data?.type !== "0x2::package::UpgradeCap")
|
|
102
|
+
return null;
|
|
103
|
+
const pkg = fieldOf(obj.data.content, "package");
|
|
104
|
+
return typeof pkg === "string" && FULL_ADDRESS.test(pkg) ? pkg : null;
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async function versionPasses(packageId, globalConfigId) {
|
|
111
|
+
try {
|
|
112
|
+
const tx = new Transaction();
|
|
113
|
+
tx.moveCall({ target: `${packageId}::config::verify_version`, arguments: [tx.object(globalConfigId)] });
|
|
114
|
+
const res = await client.devInspectTransactionBlock({ sender: QUOTE_SENDER, transactionBlock: tx });
|
|
115
|
+
return !res.error;
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
async readWallet(address) {
|
|
123
|
+
return readBalances(chainReader(network, address), walCoinType(network));
|
|
124
|
+
},
|
|
125
|
+
async resolveBluefin() {
|
|
126
|
+
// Memoised for the run, and a failure is not memoised: one refusal must not become "no Bluefin".
|
|
127
|
+
if (bluefin !== null)
|
|
128
|
+
return bluefin;
|
|
129
|
+
const pending = (async () => {
|
|
130
|
+
const pinned = BLUEFIN_PACKAGE_IDS[network];
|
|
131
|
+
const capId = BLUEFIN_UPGRADE_CAP_IDS[network];
|
|
132
|
+
const globalConfigId = BLUEFIN_GLOBAL_CONFIG_IDS[network];
|
|
133
|
+
const poolId = BLUEFIN_WAL_SUI_POOLS[network];
|
|
134
|
+
if (pinned === null || capId === null || globalConfigId === null || poolId === null) {
|
|
135
|
+
throw new Error(`This tool knows no Bluefin WAL/SUI pool on ${network}.`);
|
|
136
|
+
}
|
|
137
|
+
const live = await livePackage(capId);
|
|
138
|
+
for (const packageId of live === null ? [pinned] : [live, pinned]) {
|
|
139
|
+
if (await versionPasses(packageId, globalConfigId))
|
|
140
|
+
return { packageId, globalConfigId, poolId };
|
|
141
|
+
}
|
|
142
|
+
throw new Error("Bluefin's on-chain version check refused every package address this tool knows.");
|
|
143
|
+
})();
|
|
144
|
+
bluefin = pending;
|
|
145
|
+
pending.catch(() => {
|
|
146
|
+
if (bluefin === pending)
|
|
147
|
+
bluefin = null;
|
|
148
|
+
});
|
|
149
|
+
return pending;
|
|
150
|
+
},
|
|
151
|
+
async readExchange() {
|
|
152
|
+
if (network !== "testnet")
|
|
153
|
+
throw new Error("The built-in SUI→WAL exchange is a testnet-only facility.");
|
|
154
|
+
const objectId = TESTNET_WALRUS_PACKAGE_CONFIG.exchangeIds[0];
|
|
155
|
+
if (objectId === undefined)
|
|
156
|
+
throw new Error("This build names no testnet exchange object.");
|
|
157
|
+
const obj = await client.getObject({ id: objectId, options: { showContent: true, showType: true } });
|
|
158
|
+
const type = obj.data?.type;
|
|
159
|
+
if (typeof type !== "string" || !type.includes("::wal_exchange::Exchange")) {
|
|
160
|
+
throw new Error(`Object ${objectId} is not a wal_exchange Exchange (type ${type ?? "unknown"}).`);
|
|
161
|
+
}
|
|
162
|
+
const rate = fieldOf(obj.data?.content, "rate");
|
|
163
|
+
const wal = fieldOf(rate, "wal");
|
|
164
|
+
const sui = fieldOf(rate, "sui");
|
|
165
|
+
if (typeof wal !== "string" || typeof sui !== "string" || !/^\d+$/.test(wal) || !/^\d+$/.test(sui) || BigInt(sui) === 0n) {
|
|
166
|
+
throw new Error("The exchange's rate could not be read off its object, so what it would give is unknown.");
|
|
167
|
+
}
|
|
168
|
+
return { objectId, packageId: type.split("::")[0] ?? "", rateWal: BigInt(wal), rateSui: BigInt(sui) };
|
|
169
|
+
},
|
|
170
|
+
async quote(venue, direction, amountInUnits, binding) {
|
|
171
|
+
return quoteVenue(client, network, venue, direction, amountInUnits, binding);
|
|
172
|
+
},
|
|
173
|
+
async estimateFee(shape, sender) {
|
|
174
|
+
try {
|
|
175
|
+
const bytes = await swapTransaction({ ...shape, network, sender }).build({ client });
|
|
176
|
+
const { effects } = await client.dryRunTransactionBlock({ transactionBlock: bytes });
|
|
177
|
+
if (effects.status.status !== "success")
|
|
178
|
+
return null;
|
|
179
|
+
return netGasFee(effects.gasUsed);
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { walrusClient } from "./extend-chain.ts";
|
|
2
|
+
import type { Network } from "./network.ts";
|
|
3
|
+
import { type SwapDirection, type SwapVenue } from "./shared/lib/wallet/swap-rules.ts";
|
|
4
|
+
/** The zero address: a quote is a read, and a read needs no wallet. */
|
|
5
|
+
export declare const QUOTE_SENDER: string;
|
|
6
|
+
/** One venue's answer, now. Every field was read from the chain this moment; none is a constant. */
|
|
7
|
+
export interface VenueQuote {
|
|
8
|
+
venue: SwapVenue;
|
|
9
|
+
/** What comes out AFTER the venue's fee, in base units. */
|
|
10
|
+
outUnits: bigint;
|
|
11
|
+
/** The fee this quote carries, in bps (two decimals). Null = could not be measured, not 0. */
|
|
12
|
+
feeRateBps: number | null;
|
|
13
|
+
/** Input that would come back unused (DeepBook rounds to its lot size). */
|
|
14
|
+
leftoverInUnits: bigint;
|
|
15
|
+
}
|
|
16
|
+
type Client = ReturnType<typeof walrusClient>;
|
|
17
|
+
/** One venue's answer now. Bluefin needs its resolved binding; DeepBook needs nothing resolved. */
|
|
18
|
+
export declare function quoteVenue(client: Client, network: Network, venue: SwapVenue, direction: SwapDirection, amountInUnits: bigint, bluefin: {
|
|
19
|
+
packageId: string;
|
|
20
|
+
poolId: string;
|
|
21
|
+
} | null): Promise<VenueQuote>;
|
|
22
|
+
export {};
|