@needmoretruth/nmts-cli 0.17.2 → 0.34.1
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 +124 -516
- package/CHANGELOG.md +98 -0
- package/README.ko.md +374 -467
- package/README.md +384 -465
- 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,116 @@
|
|
|
1
|
+
// The rules of sending SUI or WAL out of the wallet — address shape, exact amounts, what can be
|
|
2
|
+
// sent, and the gas budget a person may set. ⚠ PUBLISHED — copied byte-for-byte into the `nmts`
|
|
3
|
+
// command-line package; keep comments self-contained English.
|
|
4
|
+
//
|
|
5
|
+
// CONTRACT: every function is pure — no I/O, no side effects, no network, no dates. Validators
|
|
6
|
+
// return a discriminated result carrying a STABLE error CODE (a screen maps codes to sentences);
|
|
7
|
+
// they never throw on bad input and never format for a locale. Base units are 1e9
|
|
8
|
+
// (1 SUI = 1e9 MIST, 1 WAL = 1e9 FROST).
|
|
9
|
+
// NON-CUSTODIAL: these helpers only DECIDE amounts and addresses that the person's own wallet then
|
|
10
|
+
// signs; nothing here holds, routes or moves value.
|
|
11
|
+
/** Base-unit scale for both tokens (1 SUI = 1e9 MIST, 1 WAL = 1e9 FROST). */
|
|
12
|
+
export const TOKEN_DECIMALS = 9;
|
|
13
|
+
/**
|
|
14
|
+
* SUI kept back so a send or exchange can pay its own gas (0.05 SUI). A ceiling, not the charged
|
|
15
|
+
* amount — a real fee is a few million MIST; this is generous so a send never strands the wallet
|
|
16
|
+
* without gas.
|
|
17
|
+
*/
|
|
18
|
+
export const SUI_GAS_RESERVE_MIST = 50000000n;
|
|
19
|
+
/** A full Sui address: `0x` + 64 lowercase hex characters. */
|
|
20
|
+
const SUI_ADDRESS_RE = /^0x[0-9a-f]{64}$/;
|
|
21
|
+
/** Trim and lowercase an address (hex is case-insensitive; lowercase is the shape kept). */
|
|
22
|
+
export function normalizeSuiAddress(input) {
|
|
23
|
+
return input.trim().toLowerCase();
|
|
24
|
+
}
|
|
25
|
+
/** True when `input`, normalised, is a full 0x + 64-hex Sui address. */
|
|
26
|
+
export function isValidSuiAddress(input) {
|
|
27
|
+
return SUI_ADDRESS_RE.test(normalizeSuiAddress(input));
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A decimal amount ("0.5", "12", ".25") as base units, EXACTLY — no floating point, so 0.1 never
|
|
31
|
+
* drifts. Null for anything malformed or finer than the chain keeps (never silently rounded). Does
|
|
32
|
+
* not judge whether the amount is affordable; that is the validator's job.
|
|
33
|
+
*/
|
|
34
|
+
export function parseTokenAmountToBaseUnits(input, decimals = TOKEN_DECIMALS) {
|
|
35
|
+
const s = input.trim();
|
|
36
|
+
if (s === "" || s === "." || !/^\d*\.?\d*$/.test(s))
|
|
37
|
+
return null;
|
|
38
|
+
const [intPart = "", fracPart = ""] = s.split(".");
|
|
39
|
+
if (intPart === "" && fracPart === "")
|
|
40
|
+
return null;
|
|
41
|
+
if (fracPart.length > decimals)
|
|
42
|
+
return null;
|
|
43
|
+
const frac = fracPart.padEnd(decimals, "0");
|
|
44
|
+
try {
|
|
45
|
+
return BigInt(`${intPart === "" ? "0" : intPart}${frac}`);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The most that can be sent, in base units. SUI keeps the gas reserve back; WAL is fully sendable
|
|
53
|
+
* (a WAL transfer pays its gas in SUI, which the validator checks separately). Never negative.
|
|
54
|
+
*/
|
|
55
|
+
export function maxSendableBaseUnits(params) {
|
|
56
|
+
const reserve = params.gasReserve ?? SUI_GAS_RESERVE_MIST;
|
|
57
|
+
if (params.coin === "SUI") {
|
|
58
|
+
const max = params.suiBalance - reserve;
|
|
59
|
+
return max > 0n ? max : 0n;
|
|
60
|
+
}
|
|
61
|
+
return params.walBalance > 0n ? params.walBalance : 0n;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Validate a send. The address must be a full Sui address; the amount must be above zero and within
|
|
65
|
+
* what can be sent; a WAL send also needs the gas reserve in SUI — else `noGasForWal`.
|
|
66
|
+
*/
|
|
67
|
+
export function validateSendForm(params) {
|
|
68
|
+
const reserve = params.gasReserve ?? SUI_GAS_RESERVE_MIST;
|
|
69
|
+
const address = normalizeSuiAddress(params.addressInput);
|
|
70
|
+
if (!isValidSuiAddress(address))
|
|
71
|
+
return { ok: false, error: "invalidAddress" };
|
|
72
|
+
const amount = parseTokenAmountToBaseUnits(params.amountInput);
|
|
73
|
+
if (amount === null || amount <= 0n)
|
|
74
|
+
return { ok: false, error: "invalidAmount" };
|
|
75
|
+
if (params.coin === "SUI") {
|
|
76
|
+
const max = maxSendableBaseUnits({
|
|
77
|
+
coin: "SUI",
|
|
78
|
+
suiBalance: params.suiBalance,
|
|
79
|
+
walBalance: params.walBalance,
|
|
80
|
+
gasReserve: reserve,
|
|
81
|
+
});
|
|
82
|
+
if (max <= 0n || amount > max)
|
|
83
|
+
return { ok: false, error: "insufficientBalance" };
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
if (params.suiBalance < reserve)
|
|
87
|
+
return { ok: false, error: "noGasForWal" };
|
|
88
|
+
if (amount > params.walBalance)
|
|
89
|
+
return { ok: false, error: "insufficientBalance" };
|
|
90
|
+
}
|
|
91
|
+
return { ok: true, amountBaseUnits: amount, address };
|
|
92
|
+
}
|
|
93
|
+
/** The lowest gas budget a person may set: 0.002 SUI. Below it no transaction goes through. */
|
|
94
|
+
export const GAS_BUDGET_MIN_MIST = 2000000n;
|
|
95
|
+
/** The absolute ceiling: 1 SUI. A slipped extra zero stops here. */
|
|
96
|
+
export const GAS_BUDGET_ABS_MAX_MIST = 1000000000n;
|
|
97
|
+
/**
|
|
98
|
+
* The RECOMMENDED gas budget from a measured (dry-run) fee: twice the measurement, at least
|
|
99
|
+
* 0.005 SUI, at most 1 SUI.
|
|
100
|
+
*
|
|
101
|
+
* Why twice: the measurement is taken against this moment's object versions and reference gas
|
|
102
|
+
* price, and both move a little between signing and broadcast. A budget below the real cost makes
|
|
103
|
+
* the transaction FAIL FOR WANT OF GAS, and that gas is gone; the doubling is what buys out that
|
|
104
|
+
* failure. A budget is a ceiling, not a charge — what is not used stays in the wallet.
|
|
105
|
+
*/
|
|
106
|
+
export function recommendedGasBudgetMist(estimateMist) {
|
|
107
|
+
const doubled = estimateMist * 2n;
|
|
108
|
+
const floored = doubled > 5000000n ? doubled : 5000000n;
|
|
109
|
+
return floored < GAS_BUDGET_ABS_MAX_MIST ? floored : GAS_BUDGET_ABS_MAX_MIST;
|
|
110
|
+
}
|
|
111
|
+
/** Keep a typed gas budget inside the usable range — clamped, not refused. */
|
|
112
|
+
export function clampGasBudgetMist(n) {
|
|
113
|
+
if (n < GAS_BUDGET_MIN_MIST)
|
|
114
|
+
return GAS_BUDGET_MIN_MIST;
|
|
115
|
+
return n > GAS_BUDGET_ABS_MAX_MIST ? GAS_BUDGET_ABS_MAX_MIST : n;
|
|
116
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/** Which way the trade runs. Every screen and every command uses these two words only. */
|
|
2
|
+
export type SwapDirection = "SUI_TO_WAL" | "WAL_TO_SUI";
|
|
3
|
+
/** Where the trade runs, on mainnet. */
|
|
4
|
+
export type SwapVenue = "deepbook" | "bluefin";
|
|
5
|
+
/**
|
|
6
|
+
* The order the two venues are listed in. NOT a ranking: neither is a default and nobody
|
|
7
|
+
* recommends one — at some amounts one pays more, at others the other. The order is fixed only so
|
|
8
|
+
* a person finds the same venue in the same place every time.
|
|
9
|
+
*/
|
|
10
|
+
export declare const SWAP_VENUES: readonly SwapVenue[];
|
|
11
|
+
/** The lowest slippage allowance: 1 bps (0.01%). Zero would refuse every price movement. */
|
|
12
|
+
export declare const SLIPPAGE_BPS_MIN = 1;
|
|
13
|
+
/** The highest: 5,000 bps (50%). Above that it is not protection, it is protection switched off. */
|
|
14
|
+
export declare const SLIPPAGE_BPS_MAX = 5000;
|
|
15
|
+
/**
|
|
16
|
+
* The default: 50 bps (0.5%). The two real mainnet swaps the treasury made came out 0.2% and 0.6%
|
|
17
|
+
* from their quotes (treasury ledger rows 2 and 3); this sits between those measurements.
|
|
18
|
+
*/
|
|
19
|
+
export declare const SLIPPAGE_BPS_DEFAULT = 50;
|
|
20
|
+
/** Preset buttons. A free number beside them is mandatory — presets alone are never enough. */
|
|
21
|
+
export declare const SLIPPAGE_PRESETS_BPS: readonly number[];
|
|
22
|
+
/**
|
|
23
|
+
* A typed slippage as a whole number of bps inside the usable range — clamped, not refused: a
|
|
24
|
+
* person who types 99999 means "as loose as it goes". Not a number, or infinite, goes to the
|
|
25
|
+
* DEFAULT rather than the ceiling: a broken input must not read as "protection at its loosest".
|
|
26
|
+
*/
|
|
27
|
+
export declare function clampSlippageBps(n: number): number;
|
|
28
|
+
/** Which band a slippage sits in. The warning on every surface reads this one verdict. */
|
|
29
|
+
export type SlippageBand = "low" | "ok" | "high";
|
|
30
|
+
/**
|
|
31
|
+
* Too low / fine / too high. Below 10 bps the trade fails on the smallest price movement (gas
|
|
32
|
+
* spent, nothing bought); above 200 bps a thin order book is handed that much for free. Both
|
|
33
|
+
* edges (10 and 200) are "ok", so the 10 bps preset never warns about itself.
|
|
34
|
+
*/
|
|
35
|
+
export declare function slippageBand(bps: number): SlippageBand;
|
|
36
|
+
/**
|
|
37
|
+
* The minimum to accept, from a quote: floor(quote × (10000 − bps) / 10000), never negative. Floor,
|
|
38
|
+
* because rounding up would demand more than the quote and fail a trade that fills exactly as
|
|
39
|
+
* quoted. A bps outside 0..10000 is folded to that range here; validation is `clampSlippageBps`.
|
|
40
|
+
*/
|
|
41
|
+
export declare function minOutFromQuote(quoteUnits: bigint, slippageBps: number): bigint;
|
|
42
|
+
/** Which band a gas budget sits in — the same three words as slippage, so one warning part serves. */
|
|
43
|
+
export type FeeBand = "low" | "ok" | "high";
|
|
44
|
+
/**
|
|
45
|
+
* Too low / fine / too high, judged against a measured fee when there is one: low below 1.2× the
|
|
46
|
+
* measurement (a budget cut to the measurement often falls short by the time it executes), high
|
|
47
|
+
* above 10× AND above 0.05 SUI (ten times a tiny fee is still small change). With no measurement,
|
|
48
|
+
* absolute bounds only: above 0.5 SUI is high, below the floor is low. A missing measurement is
|
|
49
|
+
* never replaced by a plausible one.
|
|
50
|
+
*/
|
|
51
|
+
export declare function feeBand(budgetMist: bigint, estimateMist: bigint | null): FeeBand;
|
|
52
|
+
/**
|
|
53
|
+
* How far a quote sits from the market rate, in bps, sign-free (the denominator is the market).
|
|
54
|
+
* Null when either side is not a finite positive number. Unrounded — where to cut digits is the
|
|
55
|
+
* surface's decision. This is what stops a thin or manipulated order book taking the money: a quote
|
|
56
|
+
* on its own makes one order book the truth, and an empty one is exactly when a person loses.
|
|
57
|
+
*/
|
|
58
|
+
export declare function priceDeviationBps(impliedRate: number, marketRate: number): number | null;
|
|
59
|
+
/** Beyond this distance from the market rate a quote is an extreme (3%). */
|
|
60
|
+
export declare const DEVIATION_WARN_BPS = 300;
|
|
61
|
+
/** Output per unit of input, as a number for the market comparison. Null when either side is 0. */
|
|
62
|
+
export declare function impliedRate(outUnits: bigint, amountInUnits: bigint): number | null;
|
|
63
|
+
/**
|
|
64
|
+
* The market rate in the DIRECTION'S OWN unit — WAL per SUI going one way, SUI per WAL the other —
|
|
65
|
+
* so the two sides of the ratio are never swapped. Null unless both prices are positive numbers.
|
|
66
|
+
*/
|
|
67
|
+
export declare function marketRate(direction: SwapDirection, suiUsd: number | null | undefined, walUsd: number | null | undefined): number | null;
|
|
68
|
+
/**
|
|
69
|
+
* The most SUI a swap may take in: balance minus the gas budget the person set, never negative.
|
|
70
|
+
* Not a fixed reserve — the person chose the budget, so that exact figure is what is kept back.
|
|
71
|
+
*/
|
|
72
|
+
export declare function maxSwappableSuiMist(suiBalanceMist: bigint, gasBudgetMist: bigint): bigint;
|
|
73
|
+
/** One reason a swap is outside the ordinary bands. Each maps to one sentence on each surface. */
|
|
74
|
+
export type SwapExtreme = "lowSlippage" | "highSlippage" | "lowFee" | "highFee" | "deviation";
|
|
75
|
+
/**
|
|
76
|
+
* Every extreme in one trade, in reading order. Empty means ordinary. A budget of null means the
|
|
77
|
+
* person set none (the SDK will), so there is no fee band to judge; a deviation of null means no
|
|
78
|
+
* market price was available, which is reported as unknown by the surface, never as "fine".
|
|
79
|
+
*/
|
|
80
|
+
export declare function swapExtremes(input: {
|
|
81
|
+
slippageBps: number;
|
|
82
|
+
budgetMist: bigint | null;
|
|
83
|
+
estimateMist: bigint | null;
|
|
84
|
+
deviationBps: number | null;
|
|
85
|
+
}): SwapExtreme[];
|
|
86
|
+
/**
|
|
87
|
+
* A fee in base units as a rate of the amount put in, in bps with two decimals. Bluefin's quote
|
|
88
|
+
* reports the fee outright (`fee_amount` + `protocol_fee`); both parts count, or the rate reads
|
|
89
|
+
* smaller than what is charged. Null when nothing went in.
|
|
90
|
+
*/
|
|
91
|
+
export declare function feeRateBpsOf(feeUnits: bigint, amountInUnits: bigint): number | null;
|
|
92
|
+
/** One DeepBook quote row: what comes out, and what went in but was not used. */
|
|
93
|
+
export interface DeepbookQuoteRow {
|
|
94
|
+
outUnits: bigint;
|
|
95
|
+
leftoverInUnits: bigint;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The three values a DeepBook quote returns are always (base side, quote side, DEEP needed); which
|
|
99
|
+
* of the first two is "received" and which is "left over" depends on the direction. Base = WAL,
|
|
100
|
+
* quote = SUI on the WAL_SUI book.
|
|
101
|
+
*/
|
|
102
|
+
export declare function deepbookRowFrom(direction: SwapDirection, base: bigint, quote: bigint): DeepbookQuoteRow;
|
|
103
|
+
/**
|
|
104
|
+
* DeepBook's EFFECTIVE fee rate, measured — there is no constant to read. The same amount is
|
|
105
|
+
* quoted in two modes: fee taken from the input coin (the mode used, needs no DEEP) and fee paid in
|
|
106
|
+
* DEEP; the difference is what the input mode kept. ⛔ Outputs alone must not be subtracted:
|
|
107
|
+
* DeepBook rounds to its lot size and each mode leaves a different remainder, so output per unit
|
|
108
|
+
* ACTUALLY SPENT is compared (25 WAL measured 0.40% the naive way and 0.127% this way, matching a
|
|
109
|
+
* 100 SUI trade where rounding vanishes). Integer arithmetic throughout.
|
|
110
|
+
*
|
|
111
|
+
* Null, never 0, when it cannot be measured — including a non-positive result, which means the
|
|
112
|
+
* method broke down (the two modes walked the book to different depths), not that the fee is 0.
|
|
113
|
+
* DeepBook always charges; "0%" would be a lie.
|
|
114
|
+
*/
|
|
115
|
+
export declare function measureDeepbookFeeBps(amountInUnits: bigint, inputMode: DeepbookQuoteRow, deepMode: DeepbookQuoteRow | null): number | null;
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// The rules of an in-app swap between SUI and WAL — the slippage a person may set, the minimum a
|
|
2
|
+
// quote turns into, the gas-budget bands, the market cross-check, the venue fee arithmetic and the
|
|
3
|
+
// extremes gate. ⚠ PUBLISHED — copied byte-for-byte into the `nmts` command-line package; keep
|
|
4
|
+
// comments self-contained English.
|
|
5
|
+
//
|
|
6
|
+
// CONTRACT: every function is pure — no I/O, no side effects, no network, no dates. Slippage is in
|
|
7
|
+
// bps (1 bps = 0.01%) and is a number because a person picks it; amounts are base units
|
|
8
|
+
// (1e9 = one coin) and are bigint because money is never a float.
|
|
9
|
+
// NON-CUSTODIAL: these helpers only DECIDE the numbers a person's own wallet then signs. NMTS is
|
|
10
|
+
// not a party to the trade and takes nothing from it: no fee argument, no NMTS address.
|
|
11
|
+
import { GAS_BUDGET_MIN_MIST } from "./send-rules.js";
|
|
12
|
+
/**
|
|
13
|
+
* The order the two venues are listed in. NOT a ranking: neither is a default and nobody
|
|
14
|
+
* recommends one — at some amounts one pays more, at others the other. The order is fixed only so
|
|
15
|
+
* a person finds the same venue in the same place every time.
|
|
16
|
+
*/
|
|
17
|
+
export const SWAP_VENUES = ["deepbook", "bluefin"];
|
|
18
|
+
/** The lowest slippage allowance: 1 bps (0.01%). Zero would refuse every price movement. */
|
|
19
|
+
export const SLIPPAGE_BPS_MIN = 1;
|
|
20
|
+
/** The highest: 5,000 bps (50%). Above that it is not protection, it is protection switched off. */
|
|
21
|
+
export const SLIPPAGE_BPS_MAX = 5000;
|
|
22
|
+
/**
|
|
23
|
+
* The default: 50 bps (0.5%). The two real mainnet swaps the treasury made came out 0.2% and 0.6%
|
|
24
|
+
* from their quotes (treasury ledger rows 2 and 3); this sits between those measurements.
|
|
25
|
+
*/
|
|
26
|
+
export const SLIPPAGE_BPS_DEFAULT = 50;
|
|
27
|
+
/** Preset buttons. A free number beside them is mandatory — presets alone are never enough. */
|
|
28
|
+
export const SLIPPAGE_PRESETS_BPS = [10, 50, 100];
|
|
29
|
+
/**
|
|
30
|
+
* A typed slippage as a whole number of bps inside the usable range — clamped, not refused: a
|
|
31
|
+
* person who types 99999 means "as loose as it goes". Not a number, or infinite, goes to the
|
|
32
|
+
* DEFAULT rather than the ceiling: a broken input must not read as "protection at its loosest".
|
|
33
|
+
*/
|
|
34
|
+
export function clampSlippageBps(n) {
|
|
35
|
+
if (!Number.isFinite(n))
|
|
36
|
+
return SLIPPAGE_BPS_DEFAULT;
|
|
37
|
+
const rounded = Math.round(n);
|
|
38
|
+
return Math.min(SLIPPAGE_BPS_MAX, Math.max(SLIPPAGE_BPS_MIN, rounded));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Too low / fine / too high. Below 10 bps the trade fails on the smallest price movement (gas
|
|
42
|
+
* spent, nothing bought); above 200 bps a thin order book is handed that much for free. Both
|
|
43
|
+
* edges (10 and 200) are "ok", so the 10 bps preset never warns about itself.
|
|
44
|
+
*/
|
|
45
|
+
export function slippageBand(bps) {
|
|
46
|
+
if (bps < 10)
|
|
47
|
+
return "low";
|
|
48
|
+
if (bps > 200)
|
|
49
|
+
return "high";
|
|
50
|
+
return "ok";
|
|
51
|
+
}
|
|
52
|
+
const BPS_DENOMINATOR = 10000n;
|
|
53
|
+
/**
|
|
54
|
+
* The minimum to accept, from a quote: floor(quote × (10000 − bps) / 10000), never negative. Floor,
|
|
55
|
+
* because rounding up would demand more than the quote and fail a trade that fills exactly as
|
|
56
|
+
* quoted. A bps outside 0..10000 is folded to that range here; validation is `clampSlippageBps`.
|
|
57
|
+
*/
|
|
58
|
+
export function minOutFromQuote(quoteUnits, slippageBps) {
|
|
59
|
+
if (quoteUnits <= 0n)
|
|
60
|
+
return 0n;
|
|
61
|
+
const rounded = Number.isFinite(slippageBps) ? Math.round(slippageBps) : SLIPPAGE_BPS_DEFAULT;
|
|
62
|
+
const bounded = BigInt(Math.min(10_000, Math.max(0, rounded)));
|
|
63
|
+
const out = (quoteUnits * (BPS_DENOMINATOR - bounded)) / BPS_DENOMINATOR;
|
|
64
|
+
return out > 0n ? out : 0n;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Too low / fine / too high, judged against a measured fee when there is one: low below 1.2× the
|
|
68
|
+
* measurement (a budget cut to the measurement often falls short by the time it executes), high
|
|
69
|
+
* above 10× AND above 0.05 SUI (ten times a tiny fee is still small change). With no measurement,
|
|
70
|
+
* absolute bounds only: above 0.5 SUI is high, below the floor is low. A missing measurement is
|
|
71
|
+
* never replaced by a plausible one.
|
|
72
|
+
*/
|
|
73
|
+
export function feeBand(budgetMist, estimateMist) {
|
|
74
|
+
if (estimateMist === null) {
|
|
75
|
+
if (budgetMist > 500000000n)
|
|
76
|
+
return "high";
|
|
77
|
+
return budgetMist < GAS_BUDGET_MIN_MIST ? "low" : "ok";
|
|
78
|
+
}
|
|
79
|
+
if (budgetMist < (estimateMist * 12n) / 10n)
|
|
80
|
+
return "low";
|
|
81
|
+
if (budgetMist > estimateMist * 10n && budgetMist > 50000000n)
|
|
82
|
+
return "high";
|
|
83
|
+
return "ok";
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* How far a quote sits from the market rate, in bps, sign-free (the denominator is the market).
|
|
87
|
+
* Null when either side is not a finite positive number. Unrounded — where to cut digits is the
|
|
88
|
+
* surface's decision. This is what stops a thin or manipulated order book taking the money: a quote
|
|
89
|
+
* on its own makes one order book the truth, and an empty one is exactly when a person loses.
|
|
90
|
+
*/
|
|
91
|
+
export function priceDeviationBps(impliedRate, marketRate) {
|
|
92
|
+
if (!Number.isFinite(impliedRate) || impliedRate <= 0)
|
|
93
|
+
return null;
|
|
94
|
+
if (!Number.isFinite(marketRate) || marketRate <= 0)
|
|
95
|
+
return null;
|
|
96
|
+
return (Math.abs(impliedRate - marketRate) / marketRate) * 10_000;
|
|
97
|
+
}
|
|
98
|
+
/** Beyond this distance from the market rate a quote is an extreme (3%). */
|
|
99
|
+
export const DEVIATION_WARN_BPS = 300;
|
|
100
|
+
/** Output per unit of input, as a number for the market comparison. Null when either side is 0. */
|
|
101
|
+
export function impliedRate(outUnits, amountInUnits) {
|
|
102
|
+
if (amountInUnits <= 0n || outUnits <= 0n)
|
|
103
|
+
return null;
|
|
104
|
+
const r = Number(outUnits) / Number(amountInUnits);
|
|
105
|
+
return Number.isFinite(r) && r > 0 ? r : null;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The market rate in the DIRECTION'S OWN unit — WAL per SUI going one way, SUI per WAL the other —
|
|
109
|
+
* so the two sides of the ratio are never swapped. Null unless both prices are positive numbers.
|
|
110
|
+
*/
|
|
111
|
+
export function marketRate(direction, suiUsd, walUsd) {
|
|
112
|
+
if (suiUsd == null || walUsd == null || suiUsd <= 0 || walUsd <= 0)
|
|
113
|
+
return null;
|
|
114
|
+
if (!Number.isFinite(suiUsd) || !Number.isFinite(walUsd))
|
|
115
|
+
return null;
|
|
116
|
+
return direction === "SUI_TO_WAL" ? suiUsd / walUsd : walUsd / suiUsd;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The most SUI a swap may take in: balance minus the gas budget the person set, never negative.
|
|
120
|
+
* Not a fixed reserve — the person chose the budget, so that exact figure is what is kept back.
|
|
121
|
+
*/
|
|
122
|
+
export function maxSwappableSuiMist(suiBalanceMist, gasBudgetMist) {
|
|
123
|
+
const max = suiBalanceMist - gasBudgetMist;
|
|
124
|
+
return max > 0n ? max : 0n;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Every extreme in one trade, in reading order. Empty means ordinary. A budget of null means the
|
|
128
|
+
* person set none (the SDK will), so there is no fee band to judge; a deviation of null means no
|
|
129
|
+
* market price was available, which is reported as unknown by the surface, never as "fine".
|
|
130
|
+
*/
|
|
131
|
+
export function swapExtremes(input) {
|
|
132
|
+
const out = [];
|
|
133
|
+
const slip = slippageBand(input.slippageBps);
|
|
134
|
+
if (slip === "low")
|
|
135
|
+
out.push("lowSlippage");
|
|
136
|
+
if (slip === "high")
|
|
137
|
+
out.push("highSlippage");
|
|
138
|
+
if (input.budgetMist !== null) {
|
|
139
|
+
const gas = feeBand(input.budgetMist, input.estimateMist);
|
|
140
|
+
if (gas === "low")
|
|
141
|
+
out.push("lowFee");
|
|
142
|
+
if (gas === "high")
|
|
143
|
+
out.push("highFee");
|
|
144
|
+
}
|
|
145
|
+
if (input.deviationBps !== null && Math.abs(input.deviationBps) > DEVIATION_WARN_BPS) {
|
|
146
|
+
out.push("deviation");
|
|
147
|
+
}
|
|
148
|
+
return out;
|
|
149
|
+
}
|
|
150
|
+
// ── Venue fee arithmetic — what a quote's own numbers say the venue charged ────────────────────
|
|
151
|
+
/** Scale that keeps two decimals of a bps figure through integer arithmetic (10,000 bps × 100). */
|
|
152
|
+
const BPS_SCALE = 1000000n;
|
|
153
|
+
/**
|
|
154
|
+
* A fee in base units as a rate of the amount put in, in bps with two decimals. Bluefin's quote
|
|
155
|
+
* reports the fee outright (`fee_amount` + `protocol_fee`); both parts count, or the rate reads
|
|
156
|
+
* smaller than what is charged. Null when nothing went in.
|
|
157
|
+
*/
|
|
158
|
+
export function feeRateBpsOf(feeUnits, amountInUnits) {
|
|
159
|
+
if (amountInUnits <= 0n || feeUnits < 0n)
|
|
160
|
+
return null;
|
|
161
|
+
return Number((feeUnits * BPS_SCALE) / amountInUnits) / 100;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* The three values a DeepBook quote returns are always (base side, quote side, DEEP needed); which
|
|
165
|
+
* of the first two is "received" and which is "left over" depends on the direction. Base = WAL,
|
|
166
|
+
* quote = SUI on the WAL_SUI book.
|
|
167
|
+
*/
|
|
168
|
+
export function deepbookRowFrom(direction, base, quote) {
|
|
169
|
+
return direction === "SUI_TO_WAL"
|
|
170
|
+
? { outUnits: base, leftoverInUnits: quote }
|
|
171
|
+
: { outUnits: quote, leftoverInUnits: base };
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* DeepBook's EFFECTIVE fee rate, measured — there is no constant to read. The same amount is
|
|
175
|
+
* quoted in two modes: fee taken from the input coin (the mode used, needs no DEEP) and fee paid in
|
|
176
|
+
* DEEP; the difference is what the input mode kept. ⛔ Outputs alone must not be subtracted:
|
|
177
|
+
* DeepBook rounds to its lot size and each mode leaves a different remainder, so output per unit
|
|
178
|
+
* ACTUALLY SPENT is compared (25 WAL measured 0.40% the naive way and 0.127% this way, matching a
|
|
179
|
+
* 100 SUI trade where rounding vanishes). Integer arithmetic throughout.
|
|
180
|
+
*
|
|
181
|
+
* Null, never 0, when it cannot be measured — including a non-positive result, which means the
|
|
182
|
+
* method broke down (the two modes walked the book to different depths), not that the fee is 0.
|
|
183
|
+
* DeepBook always charges; "0%" would be a lie.
|
|
184
|
+
*/
|
|
185
|
+
export function measureDeepbookFeeBps(amountInUnits, inputMode, deepMode) {
|
|
186
|
+
if (deepMode === null)
|
|
187
|
+
return null;
|
|
188
|
+
const spentInput = amountInUnits - inputMode.leftoverInUnits;
|
|
189
|
+
const spentDeep = amountInUnits - deepMode.leftoverInUnits;
|
|
190
|
+
if (spentInput <= 0n || spentDeep <= 0n)
|
|
191
|
+
return null;
|
|
192
|
+
const denominator = deepMode.outUnits * spentInput;
|
|
193
|
+
if (denominator <= 0n)
|
|
194
|
+
return null;
|
|
195
|
+
const numerator = denominator - inputMode.outUnits * spentDeep;
|
|
196
|
+
if (numerator <= 0n)
|
|
197
|
+
return null;
|
|
198
|
+
return Number((numerator * BPS_SCALE) / denominator) / 100;
|
|
199
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** The dial's right end: 10 %. Above it the value is typed, and confirmed once more. */
|
|
2
|
+
export declare const TIP_DIAL_MAX_TENTHS = 100;
|
|
3
|
+
/** No input goes above this: the whole paid amount (100 %). More than that is a separate gift. */
|
|
4
|
+
export declare const TIP_MAX_TENTHS = 1000;
|
|
5
|
+
/** Above this the person confirms "this share, permanently" once more. Same as the dial's end. */
|
|
6
|
+
export declare const TIP_CONFIRM_ABOVE_TENTHS = 100;
|
|
7
|
+
/** A typed percent (decimals allowed) to integer tenths 0..1000. Not a number → 0 (send nothing), not an error. */
|
|
8
|
+
export declare function tenthsFromPercent(input: number): number;
|
|
9
|
+
/** Tenths → the percent a person reads: 25 → "2.5", 100 → "10". */
|
|
10
|
+
export declare function percentText(tenths: number): string;
|
|
11
|
+
/** `tenths`/10 % of the paid amount (base units), rounded down. 0n when nothing was paid or the share is 0. */
|
|
12
|
+
export declare function tipFromTenths(paidBaseUnits: bigint, tenths: number): bigint;
|
|
13
|
+
/** Does this share need the extra confirmation (a value past the dial's end)? */
|
|
14
|
+
export declare function needsExtraConfirm(tenths: number): boolean;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Tip arithmetic: what share of a storage payment goes to the developer as a gift. The screen's
|
|
2
|
+
// dial and the CLI's setting use the same scale — tenths of a percent — with the dial ending at
|
|
3
|
+
// 10 % and anything above typed in and confirmed once more. The payment is in WAL, so the tip is.
|
|
4
|
+
//
|
|
5
|
+
// The default is 0, and 0 sends nothing. Division rounds down, so nothing ever signs one unit
|
|
6
|
+
// more than the screen showed. No network, no React, no translation; `test/tip-math.test.ts`.
|
|
7
|
+
/** The dial's right end: 10 %. Above it the value is typed, and confirmed once more. */
|
|
8
|
+
export const TIP_DIAL_MAX_TENTHS = 100;
|
|
9
|
+
/** No input goes above this: the whole paid amount (100 %). More than that is a separate gift. */
|
|
10
|
+
export const TIP_MAX_TENTHS = 1000;
|
|
11
|
+
/** Above this the person confirms "this share, permanently" once more. Same as the dial's end. */
|
|
12
|
+
export const TIP_CONFIRM_ABOVE_TENTHS = TIP_DIAL_MAX_TENTHS;
|
|
13
|
+
/** A typed percent (decimals allowed) to integer tenths 0..1000. Not a number → 0 (send nothing), not an error. */
|
|
14
|
+
export function tenthsFromPercent(input) {
|
|
15
|
+
if (!Number.isFinite(input))
|
|
16
|
+
return 0;
|
|
17
|
+
return Math.min(TIP_MAX_TENTHS, Math.max(0, Math.round(input * 10)));
|
|
18
|
+
}
|
|
19
|
+
/** Tenths → the percent a person reads: 25 → "2.5", 100 → "10". */
|
|
20
|
+
export function percentText(tenths) {
|
|
21
|
+
const t = Math.min(TIP_MAX_TENTHS, Math.max(0, Math.round(tenths)));
|
|
22
|
+
return t % 10 === 0 ? String(t / 10) : (t / 10).toFixed(1);
|
|
23
|
+
}
|
|
24
|
+
/** `tenths`/10 % of the paid amount (base units), rounded down. 0n when nothing was paid or the share is 0. */
|
|
25
|
+
export function tipFromTenths(paidBaseUnits, tenths) {
|
|
26
|
+
const t = Math.min(TIP_MAX_TENTHS, Math.max(0, Math.round(tenths)));
|
|
27
|
+
if (paidBaseUnits <= 0n || t === 0 || !Number.isFinite(t))
|
|
28
|
+
return 0n;
|
|
29
|
+
return (paidBaseUnits * BigInt(t)) / 1000n;
|
|
30
|
+
}
|
|
31
|
+
/** Does this share need the extra confirmation (a value past the dial's end)? */
|
|
32
|
+
export function needsExtraConfirm(tenths) {
|
|
33
|
+
return tenths > TIP_CONFIRM_ABOVE_TENTHS;
|
|
34
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** DeepBook v3's package, per network. Read from `@mysten/deepbook-v3` 1.6.2; the mainnet value is confirmed by real trades. */
|
|
2
|
+
export declare const DEEPBOOK_PACKAGE_IDS: Readonly<Record<"mainnet" | "testnet", string>>;
|
|
3
|
+
/** Bluefin Spot's CURRENT package (mainnet only). A fallback for the browser, a name for the list. */
|
|
4
|
+
export declare const BLUEFIN_PACKAGE_IDS: Readonly<Record<"mainnet" | "testnet", string | null>>;
|
|
5
|
+
/** DeepBook's WAL_SUI order book (base = WAL, quote = SUI). ⚠ The testnet book trades a different WAL. */
|
|
6
|
+
export declare const DEEPBOOK_WAL_SUI_POOLS: Readonly<Record<"mainnet" | "testnet", string>>;
|
|
7
|
+
/** DEEP, DeepBook's own coin — an empty coin of it is passed so the fee comes off the input coin. */
|
|
8
|
+
export declare const DEEP_COIN_TYPES: Readonly<Record<"mainnet" | "testnet", string>>;
|
|
9
|
+
/** Bluefin's UpgradeCap — where the CURRENT package is read from before any quote or swap. */
|
|
10
|
+
export declare const BLUEFIN_UPGRADE_CAP_IDS: Readonly<Record<"mainnet" | "testnet", string | null>>;
|
|
11
|
+
/** Bluefin's GlobalConfig shared object — a swap argument, and what the version check reads. */
|
|
12
|
+
export declare const BLUEFIN_GLOBAL_CONFIG_IDS: Readonly<Record<"mainnet" | "testnet", string | null>>;
|
|
13
|
+
/** Bluefin's WAL/SUI pool, `Pool<WAL, SUI>` (coin_a = WAL, coin_b = SUI). Pinned by address: same-pair empty pools exist. */
|
|
14
|
+
export declare const BLUEFIN_WAL_SUI_POOLS: Readonly<Record<"mainnet" | "testnet", string | null>>;
|
|
15
|
+
/**
|
|
16
|
+
* Bluefin's sqrt-price limits, ONE STEP INSIDE the protocol's tick range (`tick_math.move`). The
|
|
17
|
+
* range ends themselves abort a swap (1009) while the quote function accepts them, so a swap built
|
|
18
|
+
* on the ends shows a good quote and fails on chain. Measured on mainnet, both directions,
|
|
19
|
+
* 2026-08-03. WAL→SUI passes the minimum, SUI→WAL the maximum; the minimum-out argument is what
|
|
20
|
+
* protects the person, not this.
|
|
21
|
+
*/
|
|
22
|
+
export declare const BLUEFIN_MIN_SQRT_PRICE: bigint;
|
|
23
|
+
export declare const BLUEFIN_MAX_SQRT_PRICE: bigint;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// The two exchange packages the wallet's activity list recognises, per network. ⚠ PUBLISHED —
|
|
2
|
+
// copied byte-for-byte into the `nmts` command-line package; keep comments self-contained English.
|
|
3
|
+
//
|
|
4
|
+
// ⛔ VALUES ONLY. Their provenance, what makes them stale and what notices it are written beside
|
|
5
|
+
// the browser's wallet configuration, which re-exports these and is the one place to read about
|
|
6
|
+
// them. This file exists so the command-line tool can name an exchange transaction with the SAME
|
|
7
|
+
// addresses the browser uses, without a second copy that drifts.
|
|
8
|
+
/** DeepBook v3's package, per network. Read from `@mysten/deepbook-v3` 1.6.2; the mainnet value is confirmed by real trades. */
|
|
9
|
+
export const DEEPBOOK_PACKAGE_IDS = {
|
|
10
|
+
testnet: "0xd874d2417a55bfa6479bffa06ad950fea144ef93a94cc6c49f32b03e386bbb24",
|
|
11
|
+
mainnet: "0x0e735f8c93a95722efd73521aca7a7652c0bb71ed1daf41b26dfd7d1ff71f748",
|
|
12
|
+
};
|
|
13
|
+
/** Bluefin Spot's CURRENT package (mainnet only). A fallback for the browser, a name for the list. */
|
|
14
|
+
export const BLUEFIN_PACKAGE_IDS = {
|
|
15
|
+
testnet: null,
|
|
16
|
+
mainnet: "0xd075338d105482f1527cbfd363d6413558f184dec36d9138a70261e87f486e9c",
|
|
17
|
+
};
|
|
18
|
+
// ── The rest of what a swap needs on chain, per network. Same rule: values only, provenance beside
|
|
19
|
+
// the browser's wallet configuration. Null = that network has no such thing, and nothing is guessed.
|
|
20
|
+
/** DeepBook's WAL_SUI order book (base = WAL, quote = SUI). ⚠ The testnet book trades a different WAL. */
|
|
21
|
+
export const DEEPBOOK_WAL_SUI_POOLS = {
|
|
22
|
+
testnet: "0x8c1c1b186c4fddab1ebd53e0895a36c1d1b3b9a77cd34e607bef49a38af0150a",
|
|
23
|
+
mainnet: "0x81f5339934c83ea19dd6bcc75c52e83509629a5f71d3257428c2ce47cc94d08b",
|
|
24
|
+
};
|
|
25
|
+
/** DEEP, DeepBook's own coin — an empty coin of it is passed so the fee comes off the input coin. */
|
|
26
|
+
export const DEEP_COIN_TYPES = {
|
|
27
|
+
testnet: "0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP",
|
|
28
|
+
mainnet: "0xdeeb7a4662eec9f2f3def03fb937a663dddaa2e215b8078a284d026b7946c270::deep::DEEP",
|
|
29
|
+
};
|
|
30
|
+
/** Bluefin's UpgradeCap — where the CURRENT package is read from before any quote or swap. */
|
|
31
|
+
export const BLUEFIN_UPGRADE_CAP_IDS = {
|
|
32
|
+
testnet: null,
|
|
33
|
+
mainnet: "0xd5b2d2159a78030e6f07e028eb75236693ed7f2f32fecbdc1edb32d3a2079c0d",
|
|
34
|
+
};
|
|
35
|
+
/** Bluefin's GlobalConfig shared object — a swap argument, and what the version check reads. */
|
|
36
|
+
export const BLUEFIN_GLOBAL_CONFIG_IDS = {
|
|
37
|
+
testnet: null,
|
|
38
|
+
mainnet: "0x03db251ba509a8d5d8777b6338836082335d93eecbdd09a11e190a1cff51c352",
|
|
39
|
+
};
|
|
40
|
+
/** Bluefin's WAL/SUI pool, `Pool<WAL, SUI>` (coin_a = WAL, coin_b = SUI). Pinned by address: same-pair empty pools exist. */
|
|
41
|
+
export const BLUEFIN_WAL_SUI_POOLS = {
|
|
42
|
+
testnet: null,
|
|
43
|
+
mainnet: "0xe60bc7ade245b9f35b49686dfab0a18e5ca9176d49bef1b90f60d67d06315ff0",
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Bluefin's sqrt-price limits, ONE STEP INSIDE the protocol's tick range (`tick_math.move`). The
|
|
47
|
+
* range ends themselves abort a swap (1009) while the quote function accepts them, so a swap built
|
|
48
|
+
* on the ends shows a good quote and fails on chain. Measured on mainnet, both directions,
|
|
49
|
+
* 2026-08-03. WAL→SUI passes the minimum, SUI→WAL the maximum; the minimum-out argument is what
|
|
50
|
+
* protects the person, not this.
|
|
51
|
+
*/
|
|
52
|
+
export const BLUEFIN_MIN_SQRT_PRICE = 4295048016n + 1n;
|
|
53
|
+
export const BLUEFIN_MAX_SQRT_PRICE = 79226673515401279992447579055n - 1n;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type DonationConfig } from "./commands/wallet-donate.ts";
|
|
2
|
+
import type { Network } from "./network.ts";
|
|
3
|
+
import type { SignTransfer } from "./wallet-sign.ts";
|
|
4
|
+
/** The owner's thank-you, in both languages, printed after every gift whatever its size. */
|
|
5
|
+
export declare const THANKS_EN = "Thank you for your gift. Honestly, I did not know anyone would. Thank you.";
|
|
6
|
+
export declare const THANKS_KO = "\uD6C4\uC6D0\uD574 \uC8FC\uC154\uC11C \uC815\uB9D0 \uAC10\uC0AC\uD569\uB2C8\uB2E4. \uC0AC\uC2E4 \uC800\uB294 \uD6C4\uC6D0\uD574 \uC904 \uC0AC\uB78C\uC774 \uC788\uC744 \uAC70\uB77C\uACE0\uB3C4 \uBAB0\uB790\uC2B5\uB2C8\uB2E4. \uC815\uB9D0 \uAC10\uC0AC\uD569\uB2C8\uB2E4.";
|
|
7
|
+
export interface StandingTipInput {
|
|
8
|
+
server: string;
|
|
9
|
+
network: Network;
|
|
10
|
+
code: string;
|
|
11
|
+
/** The sealed list's settings, as read for the payment. */
|
|
12
|
+
settings: {
|
|
13
|
+
tipTenths?: number;
|
|
14
|
+
tipConsentAt?: number;
|
|
15
|
+
} | undefined;
|
|
16
|
+
/** What was just paid for storage, in WAL base units. */
|
|
17
|
+
paidWalFrost: bigint;
|
|
18
|
+
say: (line: string) => void;
|
|
19
|
+
/** Seams for tests. */
|
|
20
|
+
readDonation?: (server: string) => Promise<DonationConfig>;
|
|
21
|
+
sign?: SignTransfer;
|
|
22
|
+
}
|
|
23
|
+
/** Send the standing share, if there is one. Returns what happened; never throws past a payment. */
|
|
24
|
+
export declare function standingTipAfter(input: StandingTipInput): Promise<"none" | "sent" | "failed">;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// The standing tip: the share of a storage payment the person set once, sent to the developer
|
|
2
|
+
// after every wallet payment without a question (owner 2026-09-06 · the tier table calls it none).
|
|
3
|
+
//
|
|
4
|
+
// ⛔ IT IS THE PERSON'S STANDING CHOICE, READ FROM THE SEALED FILE LIST — the same place the
|
|
5
|
+
// browser writes it — and it exists only where two things are both true: a share above 0, and
|
|
6
|
+
// the gift terms agreed to. Nothing here asks; `nmts tip` is where the asking happened.
|
|
7
|
+
//
|
|
8
|
+
// ⛔ A SECOND, SEPARATE TRANSACTION, after the payment is done, never inside it: a gift that fails
|
|
9
|
+
// must not fail the storage purchase. A failure here is said and does not change the exit code
|
|
10
|
+
// of the payment — the storage is bought either way.
|
|
11
|
+
//
|
|
12
|
+
// ⛔ OUTSIDE THE WALLET UNLOCK AND ITS CEILING, like every gift (`wallet donate`): the unlock is what
|
|
13
|
+
// lets a PROGRAM sign for storage; a tip is the person's own standing instruction.
|
|
14
|
+
import { request } from "./api.js";
|
|
15
|
+
import { asDonationConfig } from "./commands/wallet-donate.js";
|
|
16
|
+
import { isValidSuiAddress } from "./shared/lib/wallet/send-rules.js";
|
|
17
|
+
import { percentText, tipFromTenths } from "./shared/lib/wallet/tip.js";
|
|
18
|
+
import { coinAmount, walCoinType } from "./wallet.js";
|
|
19
|
+
/** The owner's thank-you, in both languages, printed after every gift whatever its size. */
|
|
20
|
+
export const THANKS_EN = "Thank you for your gift. Honestly, I did not know anyone would. Thank you.";
|
|
21
|
+
export const THANKS_KO = "후원해 주셔서 정말 감사합니다. 사실 저는 후원해 줄 사람이 있을 거라고도 몰랐습니다. 정말 감사합니다."; // shown in Korean
|
|
22
|
+
/** Send the standing share, if there is one. Returns what happened; never throws past a payment. */
|
|
23
|
+
export async function standingTipAfter(input) {
|
|
24
|
+
const tenths = input.settings?.tipTenths ?? 0;
|
|
25
|
+
if (tenths <= 0 || input.settings?.tipConsentAt === undefined || input.paidWalFrost <= 0n)
|
|
26
|
+
return "none";
|
|
27
|
+
const amount = tipFromTenths(input.paidWalFrost, tenths);
|
|
28
|
+
if (amount <= 0n)
|
|
29
|
+
return "none";
|
|
30
|
+
try {
|
|
31
|
+
const config = await (input.readDonation ?? (async (base) => asDonationConfig(await request(base, "/api/donation"))))(input.server);
|
|
32
|
+
if (!config.sendEnabled || !config.walEnabled || !isValidSuiAddress(config.devAddress)) {
|
|
33
|
+
input.say(` Your standing ${percentText(tenths)} % gift was not sent: gifts in WAL are not open right now.`);
|
|
34
|
+
return "failed";
|
|
35
|
+
}
|
|
36
|
+
const sign = input.sign ?? (await import("./wallet-sign.js")).signTransfer;
|
|
37
|
+
const digest = await sign({
|
|
38
|
+
network: input.network,
|
|
39
|
+
code: input.code,
|
|
40
|
+
shape: { coin: "WAL", amountBaseUnits: amount, destination: config.devAddress, walType: walCoinType(input.network) },
|
|
41
|
+
});
|
|
42
|
+
input.say(` Your standing ${percentText(tenths)} % gift — ${coinAmount(amount)} WAL — went to the developer. Transaction ${digest}`);
|
|
43
|
+
input.say(` ${THANKS_EN}`);
|
|
44
|
+
input.say(` ${THANKS_KO}`);
|
|
45
|
+
return "sent";
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
input.say(` Your standing ${percentText(tenths)} % gift was not sent: ${error instanceof Error ? error.message : String(error)}`);
|
|
49
|
+
input.say(` The storage payment above is unaffected.`);
|
|
50
|
+
return "failed";
|
|
51
|
+
}
|
|
52
|
+
}
|