@nockchain/rose 0.1.4-nightly.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/artifacts.yml +33 -0
- package/.github/workflows/ci.yml +68 -0
- package/.github/workflows/publish-sdk.yml +35 -0
- package/.nvmrc +1 -0
- package/.prettierignore +5 -0
- package/.prettierrc +8 -0
- package/LICENSE +22 -0
- package/README.md +117 -0
- package/extension/background/index.ts +1500 -0
- package/extension/content/index.ts +59 -0
- package/extension/icons/rose.svg +27 -0
- package/extension/icons/rose128.png +0 -0
- package/extension/icons/rose16.png +0 -0
- package/extension/icons/rose256.png +0 -0
- package/extension/icons/rose32.png +0 -0
- package/extension/icons/rose48.png +0 -0
- package/extension/icons/rose512.png +0 -0
- package/extension/inpage/index.ts +86 -0
- package/extension/manifest.json +48 -0
- package/extension/popup/Popup.tsx +94 -0
- package/extension/popup/Router.tsx +121 -0
- package/extension/popup/assets/arrow-down-icon.svg +3 -0
- package/extension/popup/assets/arrow-left-icon.svg +3 -0
- package/extension/popup/assets/arrow-right-icon.svg +3 -0
- package/extension/popup/assets/arrow-up-icon.svg +3 -0
- package/extension/popup/assets/arrow-up-right-icon.svg +3 -0
- package/extension/popup/assets/checkmark-icon.svg +3 -0
- package/extension/popup/assets/checkmark-pencil-icon.svg +3 -0
- package/extension/popup/assets/checkmark-success-icon.svg +3 -0
- package/extension/popup/assets/clock-icon.svg +3 -0
- package/extension/popup/assets/close-x-icon.svg +3 -0
- package/extension/popup/assets/copy-icon.svg +6 -0
- package/extension/popup/assets/explorer-icon.svg +3 -0
- package/extension/popup/assets/eye-off-icon.svg +3 -0
- package/extension/popup/assets/eye-open-icon.svg +4 -0
- package/extension/popup/assets/feedback-icon.svg +3 -0
- package/extension/popup/assets/green-status-dot.svg +3 -0
- package/extension/popup/assets/info-icon.svg +3 -0
- package/extension/popup/assets/iris-logo-40.svg +27 -0
- package/extension/popup/assets/iris-logo-96.svg +27 -0
- package/extension/popup/assets/iris-logo-blue.svg +27 -0
- package/extension/popup/assets/iris-logo-no-eye.svg +27 -0
- package/extension/popup/assets/iris-logo-orange.svg +27 -0
- package/extension/popup/assets/iris-logo.svg +27 -0
- package/extension/popup/assets/key-icon.svg +3 -0
- package/extension/popup/assets/lock-icon-yellow.svg +3 -0
- package/extension/popup/assets/lock-icon.svg +3 -0
- package/extension/popup/assets/pencil-edit-icon.svg +3 -0
- package/extension/popup/assets/permissions-icon.svg +3 -0
- package/extension/popup/assets/receipt-icon.svg +5 -0
- package/extension/popup/assets/refresh-icon.svg +3 -0
- package/extension/popup/assets/settings-gear-icon.svg +8 -0
- package/extension/popup/assets/settings-icon.svg +3 -0
- package/extension/popup/assets/theme-icon.svg +3 -0
- package/extension/popup/assets/trash-bin-icon.svg +3 -0
- package/extension/popup/assets/trend-down-arrow.svg +5 -0
- package/extension/popup/assets/trend-up-arrow.svg +5 -0
- package/extension/popup/assets/user-account-icon.svg +3 -0
- package/extension/popup/assets/vector-bottom-left.svg +9 -0
- package/extension/popup/assets/vector-left.svg +9 -0
- package/extension/popup/assets/vector-right.svg +9 -0
- package/extension/popup/assets/vector-top-right-rotated.svg +8 -0
- package/extension/popup/assets/vector-top-right.svg +9 -0
- package/extension/popup/assets/wallet-dropdown-arrow.svg +5 -0
- package/extension/popup/assets/wallet-icon-style-1.svg +6 -0
- package/extension/popup/assets/wallet-icon-style-10.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-11.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-12.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-13.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-14.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-15.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-2.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-3.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-4.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-5.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-6.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-7.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-8.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-9.svg +8 -0
- package/extension/popup/components/AccountIcon.tsx +78 -0
- package/extension/popup/components/AccountSelector.tsx +246 -0
- package/extension/popup/components/Alert.tsx +48 -0
- package/extension/popup/components/ConfirmModal.tsx +81 -0
- package/extension/popup/components/PasswordInput.tsx +49 -0
- package/extension/popup/components/ScreenContainer.tsx +17 -0
- package/extension/popup/components/SiteIcon.tsx +60 -0
- package/extension/popup/components/ThemeToggle.tsx +44 -0
- package/extension/popup/components/icons/ArrowDownLeftIcon.tsx +20 -0
- package/extension/popup/components/icons/ArrowUpRightIcon.tsx +20 -0
- package/extension/popup/components/icons/CheckIcon.tsx +20 -0
- package/extension/popup/components/icons/ChevronDownIcon.tsx +15 -0
- package/extension/popup/components/icons/ChevronLeftIcon.tsx +15 -0
- package/extension/popup/components/icons/ChevronRightIcon.tsx +15 -0
- package/extension/popup/components/icons/ChevronUpIcon.tsx +15 -0
- package/extension/popup/components/icons/CloseIcon.tsx +26 -0
- package/extension/popup/components/icons/CopyIcon.tsx +20 -0
- package/extension/popup/components/icons/EditIcon.tsx +20 -0
- package/extension/popup/components/icons/EyeIcon.tsx +13 -0
- package/extension/popup/components/icons/EyeOffIcon.tsx +13 -0
- package/extension/popup/components/icons/InfoIcon.tsx +20 -0
- package/extension/popup/components/icons/LockIcon.tsx +20 -0
- package/extension/popup/components/icons/PlusIcon.tsx +15 -0
- package/extension/popup/components/icons/ReceiveArrowIcon.tsx +14 -0
- package/extension/popup/components/icons/ReceiveCircleIcon.tsx +20 -0
- package/extension/popup/components/icons/SendPaperPlaneIcon.tsx +18 -0
- package/extension/popup/components/icons/SentArrowIcon.tsx +21 -0
- package/extension/popup/components/icons/SettingsIcon.tsx +26 -0
- package/extension/popup/components/icons/ShieldIcon.tsx +20 -0
- package/extension/popup/components/icons/UploadIcon.tsx +20 -0
- package/extension/popup/components/icons/WalletIcon.tsx +20 -0
- package/extension/popup/contexts/ThemeContext.tsx +105 -0
- package/extension/popup/hooks/useApprovalDetection.ts +128 -0
- package/extension/popup/hooks/useAutoFocus.ts +36 -0
- package/extension/popup/hooks/useAutoRejectOnClose.ts +25 -0
- package/extension/popup/hooks/useClickOutside.ts +33 -0
- package/extension/popup/hooks/useCopyToClipboard.ts +33 -0
- package/extension/popup/hooks/useFavicon.ts +64 -0
- package/extension/popup/hooks/useNumericInput.ts +93 -0
- package/extension/popup/index.html +13 -0
- package/extension/popup/index.tsx +24 -0
- package/extension/popup/screens/AboutScreen.tsx +118 -0
- package/extension/popup/screens/HomeScreen.tailwind.css +85 -0
- package/extension/popup/screens/HomeScreen.tsx +902 -0
- package/extension/popup/screens/KeySettingsPasswordScreen.tsx +164 -0
- package/extension/popup/screens/LockTimeScreen.tsx +155 -0
- package/extension/popup/screens/ReceiveScreen.tsx +149 -0
- package/extension/popup/screens/RecoveryPhraseScreen.tsx +183 -0
- package/extension/popup/screens/SendReviewScreen.tsx +308 -0
- package/extension/popup/screens/SendScreen.tsx +825 -0
- package/extension/popup/screens/SendSubmittedScreen.tsx +193 -0
- package/extension/popup/screens/SettingsScreen.tsx +116 -0
- package/extension/popup/screens/ThemeSettingsScreen.tsx +107 -0
- package/extension/popup/screens/TransactionDetailsScreen.tsx +346 -0
- package/extension/popup/screens/ViewSecretPhraseScreen.tsx +212 -0
- package/extension/popup/screens/WalletPermissionsScreen.tsx +123 -0
- package/extension/popup/screens/WalletSettingsScreen.tsx +381 -0
- package/extension/popup/screens/WalletStylingScreen.tsx +306 -0
- package/extension/popup/screens/approvals/ConnectApprovalScreen.tsx +136 -0
- package/extension/popup/screens/approvals/SignMessageScreen.tsx +140 -0
- package/extension/popup/screens/approvals/SignRawTxScreen.tsx +320 -0
- package/extension/popup/screens/approvals/TransactionApprovalScreen.tsx +167 -0
- package/extension/popup/screens/onboarding/BackupScreen.tsx +254 -0
- package/extension/popup/screens/onboarding/CreateScreen.tsx +273 -0
- package/extension/popup/screens/onboarding/ImportScreen.tsx +676 -0
- package/extension/popup/screens/onboarding/ImportScreenV0.tsx +678 -0
- package/extension/popup/screens/onboarding/ImportSuccessScreen.tsx +236 -0
- package/extension/popup/screens/onboarding/ResumeBackupScreen.tsx +166 -0
- package/extension/popup/screens/onboarding/StartScreen.tsx +142 -0
- package/extension/popup/screens/onboarding/SuccessScreen.tsx +193 -0
- package/extension/popup/screens/onboarding/VerifyScreen.tsx +220 -0
- package/extension/popup/screens/system/LockedScreen.tsx +288 -0
- package/extension/popup/screens/transactions/ReceiveScreen.tsx +84 -0
- package/extension/popup/screens/transactions/SentScreen.tsx +138 -0
- package/extension/popup/store.ts +482 -0
- package/extension/popup/styles.css +246 -0
- package/extension/popup/utils/format.ts +58 -0
- package/extension/popup/utils/formatWalletError.ts +36 -0
- package/extension/popup/utils/memo.ts +299 -0
- package/extension/popup/utils/messaging.ts +16 -0
- package/extension/shared/address-encoding.ts +69 -0
- package/extension/shared/balance-query.ts +123 -0
- package/extension/shared/constants.ts +386 -0
- package/extension/shared/currency.ts +128 -0
- package/extension/shared/first-name-derivation.ts +128 -0
- package/extension/shared/keyfile.ts +58 -0
- package/extension/shared/onboarding.ts +78 -0
- package/extension/shared/price-api.ts +79 -0
- package/extension/shared/rpc-client-browser.ts +315 -0
- package/extension/shared/transaction-builder.ts +443 -0
- package/extension/shared/types.ts +450 -0
- package/extension/shared/utxo-diff.ts +212 -0
- package/extension/shared/utxo-store.ts +548 -0
- package/extension/shared/utxo-sync.ts +343 -0
- package/extension/shared/validators.ts +26 -0
- package/extension/shared/vault.ts +1580 -0
- package/extension/shared/wallet-crypto.ts +77 -0
- package/extension/shared/wasm-utils.ts +76 -0
- package/extension/shared/webcrypto.ts +67 -0
- package/extension/types/wasm.d.ts +13 -0
- package/package.json +39 -0
- package/postcss.config.js +6 -0
- package/rose-extension-dist.zip +0 -0
- package/sdk/README.md +88 -0
- package/sdk/examples/app.ts +166 -0
- package/sdk/examples/index.html +51 -0
- package/sdk/examples/tsconfig.json +15 -0
- package/sdk/examples/tx-builder.html +532 -0
- package/sdk/examples/tx-builder.ts +1766 -0
- package/sdk/package-lock.json +424 -0
- package/sdk/package.json +68 -0
- package/sdk/src/constants.ts +28 -0
- package/sdk/src/errors.ts +74 -0
- package/sdk/src/hooks/index.ts +1 -0
- package/sdk/src/hooks/use-rose.ts +94 -0
- package/sdk/src/index.ts +12 -0
- package/sdk/src/provider.ts +396 -0
- package/sdk/src/transaction.ts +163 -0
- package/sdk/src/types/rose-wasm.d.ts +14 -0
- package/sdk/src/types.ts +97 -0
- package/sdk/src/wasm.ts +13 -0
- package/sdk/tsconfig.json +20 -0
- package/sdk/vite.config.examples.ts +32 -0
- package/tailwind.config.ts +38 -0
- package/tsconfig.json +20 -0
- package/vite.config.ts +60 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Address encoding utilities for Nockchain V1
|
|
3
|
+
* Converts public keys to base58-encoded PKH addresses
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { base58 } from '@scure/base';
|
|
7
|
+
import { hashPublicKey } from '@nockchain/rose-wasm/rose_wasm.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Converts a public key to a Nockchain V1 PKH (Public Key Hash) address
|
|
11
|
+
* Uses proper Noun representation before hashing (matches CLI wallet)
|
|
12
|
+
*
|
|
13
|
+
* @param publicKey - The 97-byte public key from WASM
|
|
14
|
+
* @returns A ~60-character base58-encoded PKH address
|
|
15
|
+
*/
|
|
16
|
+
export function publicKeyToPKH(publicKey: Uint8Array): string {
|
|
17
|
+
if (publicKey.length !== 97) {
|
|
18
|
+
throw new Error(`Invalid public key length: ${publicKey.length}, expected 97`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Use proper Noun representation before hashing
|
|
22
|
+
const address = hashPublicKey(publicKey);
|
|
23
|
+
|
|
24
|
+
return address;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Converts digest bytes (40 bytes) to a base58-encoded digest string
|
|
29
|
+
* Used for communicating with WASM API which uses string-based digests
|
|
30
|
+
*
|
|
31
|
+
* @param digestBytes - The 40-byte digest (TIP5 hash)
|
|
32
|
+
* @returns Base58-encoded digest string
|
|
33
|
+
*/
|
|
34
|
+
export function digestBytesToString(digestBytes: Uint8Array): string {
|
|
35
|
+
if (digestBytes.length !== 40) {
|
|
36
|
+
throw new Error(`Invalid digest length: ${digestBytes.length}, expected 40`);
|
|
37
|
+
}
|
|
38
|
+
return base58.encode(digestBytes);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Converts a base58-encoded digest string to bytes
|
|
43
|
+
* Used for communicating with WASM API which uses string-based digests
|
|
44
|
+
*
|
|
45
|
+
* @param digestString - Base58-encoded digest string
|
|
46
|
+
* @returns The 40-byte digest
|
|
47
|
+
*/
|
|
48
|
+
export function digestStringToBytes(digestString: string): Uint8Array {
|
|
49
|
+
const bytes = base58.decode(digestString);
|
|
50
|
+
if (bytes.length !== 40) {
|
|
51
|
+
throw new Error(`Decoded digest has invalid length: ${bytes.length}, expected 40`);
|
|
52
|
+
}
|
|
53
|
+
return bytes;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Converts a public key to a PKH digest string (for WASM API)
|
|
58
|
+
* Uses proper Noun representation before hashing (matches CLI wallet)
|
|
59
|
+
*
|
|
60
|
+
* @param publicKey - The 97-byte public key
|
|
61
|
+
* @returns Base58-encoded PKH digest string
|
|
62
|
+
*/
|
|
63
|
+
export function publicKeyToPKHDigest(publicKey: Uint8Array): string {
|
|
64
|
+
if (publicKey.length !== 97) {
|
|
65
|
+
throw new Error(`Invalid public key length: ${publicKey.length}, expected 97`);
|
|
66
|
+
}
|
|
67
|
+
// Use proper Noun representation before hashing
|
|
68
|
+
return hashPublicKey(publicKey);
|
|
69
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* High-level balance query functions for Nockchain v1 addresses
|
|
3
|
+
*
|
|
4
|
+
* This module provides functions to query the balance of v1 PKH addresses
|
|
5
|
+
* by deriving the expected first-names for standard lock types and querying
|
|
6
|
+
* the gRPC API.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { NockchainBrowserRPCClient } from './rpc-client-browser';
|
|
10
|
+
import { getBothFirstNames } from './first-name-derivation';
|
|
11
|
+
import { NOCK_TO_NICKS } from './constants';
|
|
12
|
+
import type { Note } from './types';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Result of a balance query
|
|
16
|
+
*/
|
|
17
|
+
export interface BalanceResult {
|
|
18
|
+
/** Total balance in nicks (1 NOCK = 65,536 nicks) */
|
|
19
|
+
totalNicks: bigint;
|
|
20
|
+
/** Total balance in NOCK (for display) */
|
|
21
|
+
totalNock: number;
|
|
22
|
+
/** Simple (non-coinbase) notes */
|
|
23
|
+
simpleNotes: Note[];
|
|
24
|
+
/** Coinbase (mining reward) notes */
|
|
25
|
+
coinbaseNotes: Note[];
|
|
26
|
+
/** Total number of UTXOs/notes */
|
|
27
|
+
utxoCount: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Query the balance for a v1 PKH address
|
|
32
|
+
*
|
|
33
|
+
* This function:
|
|
34
|
+
* 1. Derives the expected first-names for both simple and coinbase notes
|
|
35
|
+
* 2. Queries the gRPC API for notes with each first-name
|
|
36
|
+
* 3. Combines and sums the results
|
|
37
|
+
*
|
|
38
|
+
* @param pkhBase58 - The base58-encoded v1 PKH address (~55 chars)
|
|
39
|
+
* @param rpcClient - The gRPC client to use for queries
|
|
40
|
+
* @returns Balance information including notes and totals
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* import { createBrowserClient } from './rpc-client-browser';
|
|
45
|
+
* import { queryV1Balance } from './balance-query';
|
|
46
|
+
*
|
|
47
|
+
* const client = createBrowserClient();
|
|
48
|
+
* const myPKH = "2R7Z8p..."; // Your v1 PKH address
|
|
49
|
+
* const balance = await queryV1Balance(myPKH, client);
|
|
50
|
+
*
|
|
51
|
+
* console.log(`Balance: ${balance.totalNock} NOCK`);
|
|
52
|
+
* console.log(`UTXOs: ${balance.utxoCount}`);
|
|
53
|
+
* console.log(`Simple notes: ${balance.simpleNotes.length}`);
|
|
54
|
+
* console.log(`Mining rewards: ${balance.coinbaseNotes.length}`);
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export async function queryV1Balance(
|
|
58
|
+
pkhBase58: string,
|
|
59
|
+
rpcClient: NockchainBrowserRPCClient
|
|
60
|
+
): Promise<BalanceResult> {
|
|
61
|
+
// Derive both first-names
|
|
62
|
+
const { simple, coinbase } = await getBothFirstNames(pkhBase58);
|
|
63
|
+
|
|
64
|
+
// Query both types of notes in parallel
|
|
65
|
+
const [simpleNotes, coinbaseNotes] = await Promise.all([
|
|
66
|
+
rpcClient.getNotesByFirstName(simple),
|
|
67
|
+
rpcClient.getNotesByFirstName(coinbase),
|
|
68
|
+
]);
|
|
69
|
+
|
|
70
|
+
// Sum the total value in nicks
|
|
71
|
+
const totalNicks = [...simpleNotes, ...coinbaseNotes].reduce(
|
|
72
|
+
(sum, note) => sum + BigInt(note.assets),
|
|
73
|
+
0n
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// Convert to NOCK for display (1 NOCK = 65,536 nicks)
|
|
77
|
+
const totalNock = Number(totalNicks) / NOCK_TO_NICKS;
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
totalNicks,
|
|
81
|
+
totalNock,
|
|
82
|
+
simpleNotes,
|
|
83
|
+
coinbaseNotes,
|
|
84
|
+
utxoCount: simpleNotes.length + coinbaseNotes.length,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Query only simple (non-coinbase) notes for a v1 PKH address
|
|
90
|
+
*
|
|
91
|
+
* Use this if you only care about regular transaction outputs.
|
|
92
|
+
*
|
|
93
|
+
* @param pkhBase58 - The base58-encoded v1 PKH address (~55 chars)
|
|
94
|
+
* @param rpcClient - The gRPC client to use for queries
|
|
95
|
+
* @returns Array of simple notes
|
|
96
|
+
*/
|
|
97
|
+
export async function querySimpleNotes(
|
|
98
|
+
pkhBase58: string,
|
|
99
|
+
rpcClient: NockchainBrowserRPCClient
|
|
100
|
+
): Promise<Note[]> {
|
|
101
|
+
const { simple } = await getBothFirstNames(pkhBase58);
|
|
102
|
+
return rpcClient.getNotesByFirstName(simple);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Query only coinbase (mining reward) notes for a v1 PKH address
|
|
107
|
+
*
|
|
108
|
+
* Use this if you only care about mining rewards.
|
|
109
|
+
*
|
|
110
|
+
* NOTE: The coinbase lock structure currently uses placeholder timelock parameters.
|
|
111
|
+
* This will need to be updated once we extract the exact coinbase timelock from Hoon.
|
|
112
|
+
*
|
|
113
|
+
* @param pkhBase58 - The base58-encoded v1 PKH address (~55 chars)
|
|
114
|
+
* @param rpcClient - The gRPC client to use for queries
|
|
115
|
+
* @returns Array of coinbase notes
|
|
116
|
+
*/
|
|
117
|
+
export async function queryCoinbaseNotes(
|
|
118
|
+
pkhBase58: string,
|
|
119
|
+
rpcClient: NockchainBrowserRPCClient
|
|
120
|
+
): Promise<Note[]> {
|
|
121
|
+
const { coinbase } = await getBothFirstNames(pkhBase58);
|
|
122
|
+
return rpcClient.getNotesByFirstName(coinbase);
|
|
123
|
+
}
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Application Constants
|
|
3
|
+
* Defines all method names, error codes, storage keys, and other constants
|
|
4
|
+
* for wallet provider API and internal extension communication
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Import provider methods from SDK
|
|
8
|
+
import { PROVIDER_METHODS } from '@nockchain/sdk';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Internal Extension Methods - Called by popup UI and other extension components
|
|
12
|
+
* Use 'wallet:' prefix to distinguish from public provider methods
|
|
13
|
+
*/
|
|
14
|
+
export const INTERNAL_METHODS = {
|
|
15
|
+
/** Get current wallet state */
|
|
16
|
+
GET_STATE: 'wallet:getState',
|
|
17
|
+
|
|
18
|
+
/** Unlock the wallet with password */
|
|
19
|
+
UNLOCK: 'wallet:unlock',
|
|
20
|
+
|
|
21
|
+
/** Lock the wallet */
|
|
22
|
+
LOCK: 'wallet:lock',
|
|
23
|
+
|
|
24
|
+
/** Reset/delete the wallet (clears all data) */
|
|
25
|
+
RESET_WALLET: 'wallet:resetWallet',
|
|
26
|
+
|
|
27
|
+
/** Setup/create a new wallet */
|
|
28
|
+
SETUP: 'wallet:setup',
|
|
29
|
+
|
|
30
|
+
/** Set auto-lock timeout in minutes */
|
|
31
|
+
SET_AUTO_LOCK: 'wallet:setAutoLock',
|
|
32
|
+
|
|
33
|
+
/** Create a new account */
|
|
34
|
+
CREATE_ACCOUNT: 'wallet:createAccount',
|
|
35
|
+
|
|
36
|
+
/** Switch to a different account */
|
|
37
|
+
SWITCH_ACCOUNT: 'wallet:switchAccount',
|
|
38
|
+
|
|
39
|
+
/** Get all accounts */
|
|
40
|
+
GET_ACCOUNTS: 'wallet:getAccounts',
|
|
41
|
+
|
|
42
|
+
/** Rename an account */
|
|
43
|
+
RENAME_ACCOUNT: 'wallet:renameAccount',
|
|
44
|
+
|
|
45
|
+
/** Update account styling (icon and color) */
|
|
46
|
+
UPDATE_ACCOUNT_STYLING: 'wallet:updateAccountStyling',
|
|
47
|
+
|
|
48
|
+
/** Hide an account from the UI */
|
|
49
|
+
HIDE_ACCOUNT: 'wallet:hideAccount',
|
|
50
|
+
|
|
51
|
+
/** Get mnemonic phrase (requires password verification) */
|
|
52
|
+
GET_MNEMONIC: 'wallet:getMnemonic',
|
|
53
|
+
|
|
54
|
+
/** Returns whether a v0 mnemonic is stored (requires unlocked) */
|
|
55
|
+
HAS_V0_MNEMONIC: 'wallet:hasV0Mnemonic',
|
|
56
|
+
|
|
57
|
+
/** Returns whether a v0 mnemonic is stored (requires unlocked) */
|
|
58
|
+
CLEAR_V0_MNEMONIC: 'wallet:clearV0Mnemonic',
|
|
59
|
+
|
|
60
|
+
/** Remove v0 mnemonic (requires password verification) */
|
|
61
|
+
SET_V0_MNEMONIC: 'wallet:setV0Mnemonic',
|
|
62
|
+
|
|
63
|
+
/** Get auto-lock timeout setting */
|
|
64
|
+
GET_AUTO_LOCK: 'wallet:getAutoLock',
|
|
65
|
+
|
|
66
|
+
/** Get balance from UTXO store (excludes in-flight notes) */
|
|
67
|
+
GET_BALANCE_FROM_STORE: 'wallet:getBalanceFromStore',
|
|
68
|
+
|
|
69
|
+
/** Get RPC connection status */
|
|
70
|
+
GET_CONNECTION_STATUS: 'wallet:getConnectionStatus',
|
|
71
|
+
|
|
72
|
+
/** Report RPC connection status from popup (where gRPC calls happen) */
|
|
73
|
+
REPORT_RPC_STATUS: 'wallet:reportRpcStatus',
|
|
74
|
+
|
|
75
|
+
/** Report user activity (e.g., popup opened) - resets auto-lock timer */
|
|
76
|
+
REPORT_ACTIVITY: 'wallet:reportActivity',
|
|
77
|
+
|
|
78
|
+
/** Get pending transaction request for approval */
|
|
79
|
+
GET_PENDING_TRANSACTION: 'wallet:getPendingTransaction',
|
|
80
|
+
|
|
81
|
+
/** Approve pending transaction request */
|
|
82
|
+
APPROVE_TRANSACTION: 'wallet:approveTransaction',
|
|
83
|
+
|
|
84
|
+
/** Reject pending transaction request */
|
|
85
|
+
REJECT_TRANSACTION: 'wallet:rejectTransaction',
|
|
86
|
+
|
|
87
|
+
/** Get pending sign message request for approval */
|
|
88
|
+
GET_PENDING_SIGN_REQUEST: 'wallet:getPendingSignRequest',
|
|
89
|
+
|
|
90
|
+
/** Get pending sign raw transaction request for approval */
|
|
91
|
+
GET_PENDING_SIGN_RAW_TX_REQUEST: 'wallet:getPendingSignRawTxRequest',
|
|
92
|
+
|
|
93
|
+
/** Approve pending sign message request */
|
|
94
|
+
APPROVE_SIGN_MESSAGE: 'wallet:approveSignMessage',
|
|
95
|
+
|
|
96
|
+
/** Reject pending sign message request */
|
|
97
|
+
REJECT_SIGN_MESSAGE: 'wallet:rejectSignMessage',
|
|
98
|
+
|
|
99
|
+
/** Get pending connection request for approval */
|
|
100
|
+
GET_PENDING_CONNECTION: 'wallet:getPendingConnection',
|
|
101
|
+
|
|
102
|
+
/** Approve pending connection request */
|
|
103
|
+
APPROVE_CONNECTION: 'wallet:approveConnection',
|
|
104
|
+
|
|
105
|
+
/** Reject pending connection request */
|
|
106
|
+
REJECT_CONNECTION: 'wallet:rejectConnection',
|
|
107
|
+
|
|
108
|
+
/** Revoke origin permissions */
|
|
109
|
+
REVOKE_ORIGIN: 'wallet:revokeOrigin',
|
|
110
|
+
|
|
111
|
+
/** Sign a transaction (internal popup-initiated transactions) */
|
|
112
|
+
SIGN_TRANSACTION: 'wallet:signTransaction',
|
|
113
|
+
|
|
114
|
+
/** Estimate transaction fee for a given recipient and amount */
|
|
115
|
+
ESTIMATE_TRANSACTION_FEE: 'wallet:estimateTransactionFee',
|
|
116
|
+
|
|
117
|
+
/** Estimate max sendable amount (for "send max" feature) */
|
|
118
|
+
ESTIMATE_MAX_SEND: 'wallet:estimateMaxSend',
|
|
119
|
+
|
|
120
|
+
/** Send a transaction (internal popup-initiated transactions - builds, signs, and broadcasts) */
|
|
121
|
+
SEND_TRANSACTION: 'wallet:sendTransaction',
|
|
122
|
+
|
|
123
|
+
/** Send transaction using UTXO store (build, lock, broadcast atomically) */
|
|
124
|
+
SEND_TRANSACTION_V2: 'wallet:sendTransactionV2',
|
|
125
|
+
|
|
126
|
+
/** Approve pending sign raw transaction request */
|
|
127
|
+
APPROVE_SIGN_RAW_TX: 'wallet:approveSignRawTx',
|
|
128
|
+
|
|
129
|
+
/** Reject pending sign raw transaction request */
|
|
130
|
+
REJECT_SIGN_RAW_TX: 'wallet:rejectSignRawTx',
|
|
131
|
+
|
|
132
|
+
/** Get pending sign raw transaction request */
|
|
133
|
+
GET_PENDING_RAW_TX_REQUEST: 'wallet:getPendingRawTxRequest',
|
|
134
|
+
} as const;
|
|
135
|
+
|
|
136
|
+
// Re-export PROVIDER_METHODS for other files
|
|
137
|
+
export { PROVIDER_METHODS };
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* All RPC methods (combined)
|
|
141
|
+
*/
|
|
142
|
+
export const RPC_METHODS = {
|
|
143
|
+
...PROVIDER_METHODS,
|
|
144
|
+
...INTERNAL_METHODS,
|
|
145
|
+
} as const;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Error Codes - Used in API error responses
|
|
149
|
+
*/
|
|
150
|
+
export const ERROR_CODES = {
|
|
151
|
+
/** Wallet is locked, user needs to unlock */
|
|
152
|
+
LOCKED: 'LOCKED',
|
|
153
|
+
|
|
154
|
+
/** No vault exists, user needs to create wallet */
|
|
155
|
+
NO_VAULT: 'NO_VAULT',
|
|
156
|
+
|
|
157
|
+
/** Incorrect password provided */
|
|
158
|
+
BAD_PASSWORD: 'BAD_PASSWORD',
|
|
159
|
+
|
|
160
|
+
/** Invalid address format */
|
|
161
|
+
BAD_ADDRESS: 'BAD_ADDRESS',
|
|
162
|
+
|
|
163
|
+
/** Invalid mnemonic phrase provided */
|
|
164
|
+
INVALID_MNEMONIC: 'INVALID_MNEMONIC',
|
|
165
|
+
INVALID_V0_MNEMONIC: 'INVALID_V0_MNEMONIC',
|
|
166
|
+
|
|
167
|
+
/** Invalid account index provided */
|
|
168
|
+
INVALID_ACCOUNT_INDEX: 'INVALID_ACCOUNT_INDEX',
|
|
169
|
+
|
|
170
|
+
/** No account selected */
|
|
171
|
+
NO_ACCOUNT: 'NO_ACCOUNT',
|
|
172
|
+
|
|
173
|
+
/** Cannot hide the last visible account */
|
|
174
|
+
CANNOT_HIDE_LAST_ACCOUNT: 'CANNOT_HIDE_LAST_ACCOUNT',
|
|
175
|
+
|
|
176
|
+
/** Unsupported RPC method requested */
|
|
177
|
+
METHOD_NOT_SUPPORTED: 'METHOD_NOT_SUPPORTED',
|
|
178
|
+
|
|
179
|
+
/** Unauthorized: internal methods can only be called from popup/extension pages */
|
|
180
|
+
UNAUTHORIZED: 'UNAUTHORIZED',
|
|
181
|
+
|
|
182
|
+
/** Requested resource not found (e.g., pending approval request) */
|
|
183
|
+
NOT_FOUND: 'NOT_FOUND',
|
|
184
|
+
|
|
185
|
+
/** Invalid parameters provided to method */
|
|
186
|
+
INVALID_PARAMS: 'INVALID_PARAMS',
|
|
187
|
+
} as const;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Chrome Storage Keys - Keys used for chrome.storage.local
|
|
191
|
+
*/
|
|
192
|
+
export const STORAGE_KEYS = {
|
|
193
|
+
/** Encrypted mnemonic data (iv, ct, salt) */
|
|
194
|
+
ENCRYPTED_VAULT: 'enc',
|
|
195
|
+
|
|
196
|
+
/** Current active account index */
|
|
197
|
+
CURRENT_ACCOUNT_INDEX: 'currentAccountIndex',
|
|
198
|
+
|
|
199
|
+
/** Auto-lock timeout in minutes */
|
|
200
|
+
AUTO_LOCK_MINUTES: 'autoLockMinutes',
|
|
201
|
+
|
|
202
|
+
/** Whether balance is hidden (privacy mode) */
|
|
203
|
+
BALANCE_HIDDEN: 'balanceHidden',
|
|
204
|
+
|
|
205
|
+
/** Onboarding state - tracks whether secret phrase backup is complete */
|
|
206
|
+
ONBOARDING_STATE: 'onboardingState',
|
|
207
|
+
|
|
208
|
+
/** Array of approved origins (websites that can access wallet) */
|
|
209
|
+
APPROVED_ORIGINS: 'approvedOrigins',
|
|
210
|
+
|
|
211
|
+
/** Cached balances per account address (persisted for offline access) */
|
|
212
|
+
CACHED_BALANCES: 'cachedBalances',
|
|
213
|
+
|
|
214
|
+
/** UTXO store per account - tracks note state (available, in_flight, spent) */
|
|
215
|
+
UTXO_STORE: 'utxoStore',
|
|
216
|
+
|
|
217
|
+
/** Wallet transactions per account - separate from UTXO lifecycle */
|
|
218
|
+
WALLET_TX_STORE: 'walletTxStore',
|
|
219
|
+
|
|
220
|
+
/** Per-account sync state (last synced block height) */
|
|
221
|
+
ACCOUNT_SYNC_STATE: 'accountSyncState',
|
|
222
|
+
|
|
223
|
+
/** Storage schema version for migrations */
|
|
224
|
+
SCHEMA_VERSION: 'schemaVersion',
|
|
225
|
+
|
|
226
|
+
/** Last user activity timestamp for auto-lock (survives SW restarts) */
|
|
227
|
+
LAST_ACTIVITY: 'lastActivity',
|
|
228
|
+
|
|
229
|
+
/** Whether the user manually locked the wallet (survives SW restarts) */
|
|
230
|
+
MANUALLY_LOCKED: 'manuallyLocked',
|
|
231
|
+
} as const;
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Chrome Session Storage Keys - ephemeral cache for unlocked session data
|
|
235
|
+
*/
|
|
236
|
+
export const SESSION_STORAGE_KEYS = {
|
|
237
|
+
/** Cached encryption key to restore unlock state after SW restarts */
|
|
238
|
+
UNLOCK_CACHE: 'unlockCache',
|
|
239
|
+
} as const;
|
|
240
|
+
|
|
241
|
+
/** Current storage schema version - increment when making breaking changes */
|
|
242
|
+
export const CURRENT_SCHEMA_VERSION = 1;
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Chrome Alarm Names - Named alarms for scheduled tasks
|
|
246
|
+
*/
|
|
247
|
+
export const ALARM_NAMES = {
|
|
248
|
+
/** Auto-lock timeout alarm */
|
|
249
|
+
AUTO_LOCK: 'autoLock',
|
|
250
|
+
} as const;
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Message Targets - Used for window.postMessage routing
|
|
254
|
+
*/
|
|
255
|
+
export const MESSAGE_TARGETS = {
|
|
256
|
+
/** Target identifier for wallet bridge messages */
|
|
257
|
+
WALLET_BRIDGE: 'ROSE',
|
|
258
|
+
} as const;
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Configuration - Default settings
|
|
262
|
+
*/
|
|
263
|
+
/** Default auto-lock timeout in minutes (0 = never) */
|
|
264
|
+
export const AUTOLOCK_MINUTES = 0;
|
|
265
|
+
|
|
266
|
+
/** Default RPC endpoint URL */
|
|
267
|
+
export const RPC_ENDPOINT = 'https://rpc.nockbox.org';
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Nockchain Currency Conversion
|
|
271
|
+
*/
|
|
272
|
+
/** Conversion rate: 1 NOCK = 65,536 nicks (2^16) */
|
|
273
|
+
export const NOCK_TO_NICKS = 65_536;
|
|
274
|
+
|
|
275
|
+
/** Default transaction fee in nicks (3,407,872 nicks = 52 NOCK)
|
|
276
|
+
* Used only for UI defaults in send form and approval screens.
|
|
277
|
+
* Actual fees are ALWAYS auto-calculated by WASM based on transaction size.
|
|
278
|
+
* This is just a reasonable starting point for the fee input field.
|
|
279
|
+
*/
|
|
280
|
+
export const DEFAULT_TRANSACTION_FEE = 3_407_872;
|
|
281
|
+
|
|
282
|
+
/** Fee per word (8-byte unit) for transaction size calculation in nicks */
|
|
283
|
+
export const DEFAULT_FEE_PER_WORD = 1 << 15; // 32,768 nicks = 0.5 NOCK per word
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* User Activity Methods - Methods that count as user activity for auto-lock timer
|
|
287
|
+
* Only these methods reset the lastActivity timestamp. Passive/polling methods
|
|
288
|
+
* (like GET_STATE, GET_ACCOUNTS, etc.) do NOT reset the timer.
|
|
289
|
+
*/
|
|
290
|
+
export const USER_ACTIVITY_METHODS = new Set([
|
|
291
|
+
// Provider methods (user-initiated actions from dApps)
|
|
292
|
+
PROVIDER_METHODS.CONNECT,
|
|
293
|
+
PROVIDER_METHODS.SIGN_MESSAGE,
|
|
294
|
+
PROVIDER_METHODS.SEND_TRANSACTION,
|
|
295
|
+
PROVIDER_METHODS.SIGN_RAW_TX,
|
|
296
|
+
|
|
297
|
+
// Internal methods (user actions in the UI)
|
|
298
|
+
INTERNAL_METHODS.UNLOCK,
|
|
299
|
+
INTERNAL_METHODS.SWITCH_ACCOUNT,
|
|
300
|
+
INTERNAL_METHODS.CREATE_ACCOUNT,
|
|
301
|
+
INTERNAL_METHODS.RENAME_ACCOUNT,
|
|
302
|
+
INTERNAL_METHODS.UPDATE_ACCOUNT_STYLING,
|
|
303
|
+
INTERNAL_METHODS.HIDE_ACCOUNT,
|
|
304
|
+
INTERNAL_METHODS.SET_AUTO_LOCK,
|
|
305
|
+
INTERNAL_METHODS.GET_MNEMONIC, // Viewing secret phrase is user activity
|
|
306
|
+
INTERNAL_METHODS.SEND_TRANSACTION_V2,
|
|
307
|
+
INTERNAL_METHODS.ESTIMATE_TRANSACTION_FEE,
|
|
308
|
+
INTERNAL_METHODS.ESTIMATE_MAX_SEND,
|
|
309
|
+
INTERNAL_METHODS.REPORT_ACTIVITY,
|
|
310
|
+
]);
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* UI Constants - Dimensions and constraints
|
|
314
|
+
*/
|
|
315
|
+
export const UI_CONSTANTS = {
|
|
316
|
+
/** Extension popup width in pixels */
|
|
317
|
+
POPUP_WIDTH: 357,
|
|
318
|
+
/** Extension popup height in pixels */
|
|
319
|
+
POPUP_HEIGHT: 600,
|
|
320
|
+
/** Approval popup top offset in pixels */
|
|
321
|
+
POPUP_TOP_OFFSET: 40,
|
|
322
|
+
/** Approval popup right offset in pixels */
|
|
323
|
+
POPUP_RIGHT_OFFSET: 20,
|
|
324
|
+
/** Wallet state polling interval in milliseconds */
|
|
325
|
+
STATE_POLL_INTERVAL: 2000,
|
|
326
|
+
/** Minimum password length */
|
|
327
|
+
MIN_PASSWORD_LENGTH: 8,
|
|
328
|
+
/** Number of words in BIP-39 mnemonic */
|
|
329
|
+
MNEMONIC_WORD_COUNT: 24,
|
|
330
|
+
} as const;
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Account Icon Colors - Available colors for account customization
|
|
334
|
+
*/
|
|
335
|
+
export const ACCOUNT_COLORS = [
|
|
336
|
+
'#2C9AEF', // blue
|
|
337
|
+
'#EF2C2F', // red
|
|
338
|
+
'#5968fb', // yellow (primary)
|
|
339
|
+
'#96B839', // green
|
|
340
|
+
'#3C2CEF', // purple
|
|
341
|
+
'#EF2CB1', // pink/magenta
|
|
342
|
+
'#2C6AEF', // darker blue
|
|
343
|
+
] as const;
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Preset Wallet Styles - Predetermined icon/color combinations for first 21 wallets
|
|
347
|
+
* Ensures visual variety without repeating until all presets are used
|
|
348
|
+
* After preset limit, random combinations are used
|
|
349
|
+
*/
|
|
350
|
+
export const PRESET_WALLET_STYLES = [
|
|
351
|
+
{ iconStyleId: 1, iconColor: '#5968fb' }, // Wallet 1: yellow, style 1
|
|
352
|
+
{ iconStyleId: 5, iconColor: '#2C9AEF' }, // Wallet 2: blue, style 5
|
|
353
|
+
{ iconStyleId: 9, iconColor: '#EF2C2F' }, // Wallet 3: red, style 9
|
|
354
|
+
{ iconStyleId: 3, iconColor: '#96B839' }, // Wallet 4: green, style 3
|
|
355
|
+
{ iconStyleId: 12, iconColor: '#3C2CEF' }, // Wallet 5: purple, style 12
|
|
356
|
+
{ iconStyleId: 7, iconColor: '#EF2CB1' }, // Wallet 6: pink, style 7
|
|
357
|
+
{ iconStyleId: 15, iconColor: '#2C6AEF' }, // Wallet 7: dark blue, style 15
|
|
358
|
+
{ iconStyleId: 2, iconColor: '#EF2C2F' }, // Wallet 8: red, style 2
|
|
359
|
+
{ iconStyleId: 6, iconColor: '#5968fb' }, // Wallet 9: yellow, style 6
|
|
360
|
+
{ iconStyleId: 10, iconColor: '#96B839' }, // Wallet 10: green, style 10
|
|
361
|
+
{ iconStyleId: 4, iconColor: '#2C9AEF' }, // Wallet 11: blue, style 4
|
|
362
|
+
{ iconStyleId: 13, iconColor: '#EF2CB1' }, // Wallet 12: pink, style 13
|
|
363
|
+
{ iconStyleId: 8, iconColor: '#3C2CEF' }, // Wallet 13: purple, style 8
|
|
364
|
+
{ iconStyleId: 14, iconColor: '#2C6AEF' }, // Wallet 14: dark blue, style 14
|
|
365
|
+
{ iconStyleId: 11, iconColor: '#EF2C2F' }, // Wallet 15: red, style 11
|
|
366
|
+
{ iconStyleId: 1, iconColor: '#96B839' }, // Wallet 16: green, style 1
|
|
367
|
+
{ iconStyleId: 5, iconColor: '#5968fb' }, // Wallet 17: yellow, style 5
|
|
368
|
+
{ iconStyleId: 9, iconColor: '#2C9AEF' }, // Wallet 18: blue, style 9
|
|
369
|
+
{ iconStyleId: 3, iconColor: '#3C2CEF' }, // Wallet 19: purple, style 3
|
|
370
|
+
{ iconStyleId: 12, iconColor: '#EF2CB1' }, // Wallet 20: pink, style 12
|
|
371
|
+
{ iconStyleId: 7, iconColor: '#2C6AEF' }, // Wallet 21: dark blue, style 7
|
|
372
|
+
] as const;
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Approval Request Constants - URL hash prefixes for approval flows
|
|
376
|
+
*/
|
|
377
|
+
export const APPROVAL_CONSTANTS = {
|
|
378
|
+
/** Hash prefix for connection approval requests */
|
|
379
|
+
CONNECT_HASH_PREFIX: 'connect-approval-',
|
|
380
|
+
/** Hash prefix for transaction approval requests */
|
|
381
|
+
TRANSACTION_HASH_PREFIX: 'transaction-approval-',
|
|
382
|
+
/** Hash prefix for sign message approval requests */
|
|
383
|
+
SIGN_MESSAGE_HASH_PREFIX: 'sign-message-approval-',
|
|
384
|
+
/** Hash prefix for sign raw transaction approval requests */
|
|
385
|
+
SIGN_RAW_TX_HASH_PREFIX: 'sign-raw-tx-approval-',
|
|
386
|
+
} as const;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NOCK/NICK Currency Conversion Utilities
|
|
3
|
+
*
|
|
4
|
+
* NOCK = Display unit (supports decimals)
|
|
5
|
+
* NICK = Base unit (whole numbers only, what blockchain accepts)
|
|
6
|
+
*
|
|
7
|
+
* 1 NOCK = 65,536 NICK (2^16)
|
|
8
|
+
* 1 NICK = 0.0000152587890625 NOCK
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { NOCK_TO_NICKS } from './constants';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Convert NOCK to whole NICK with proper rounding
|
|
15
|
+
*
|
|
16
|
+
* Uses Math.round() to minimize cumulative rounding errors
|
|
17
|
+
*
|
|
18
|
+
* @param nockAmount - Amount in NOCK (can have decimals)
|
|
19
|
+
* @returns Whole number of NICKS (what blockchain accepts)
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* nockToNick(1.5) // 98304 NICK
|
|
23
|
+
* nockToNick(100.12345) // 6561690 NICK
|
|
24
|
+
*/
|
|
25
|
+
export function nockToNick(nockAmount: number): number {
|
|
26
|
+
const exactNick = nockAmount * NOCK_TO_NICKS;
|
|
27
|
+
return Math.round(exactNick);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Convert NICK to NOCK
|
|
32
|
+
*
|
|
33
|
+
* @param nickAmount - Amount in NICK (whole number)
|
|
34
|
+
* @returns Amount in NOCK
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* nickToNock(65536) // 1.0 NOCK
|
|
38
|
+
* nickToNock(98304) // 1.5 NOCK
|
|
39
|
+
*/
|
|
40
|
+
export function nickToNock(nickAmount: number): number {
|
|
41
|
+
return nickAmount / NOCK_TO_NICKS;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Round NOCK amount to actual sendable amount
|
|
46
|
+
*
|
|
47
|
+
* This is what the user will actually send after blockchain rounding.
|
|
48
|
+
* Use this on input blur to show users the exact amount.
|
|
49
|
+
*
|
|
50
|
+
* @param nockAmount - Amount user entered in NOCK
|
|
51
|
+
* @returns Actual NOCK amount that will be sent (after NICK rounding)
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* roundNockToSendable(100.12345) // 100.12344360351562
|
|
55
|
+
* roundNockToSendable(1.23456789) // 1.2345886230468750
|
|
56
|
+
* roundNockToSendable(0.00001) // 0.0000152587890625 (1 NICK)
|
|
57
|
+
*/
|
|
58
|
+
export function roundNockToSendable(nockAmount: number): number {
|
|
59
|
+
if (nockAmount === 0) return 0;
|
|
60
|
+
|
|
61
|
+
const roundedNick = nockToNick(nockAmount);
|
|
62
|
+
return nickToNock(roundedNick);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Check if amount is too small to send (would round to 0 NICK)
|
|
67
|
+
*
|
|
68
|
+
* @param nockAmount - Amount in NOCK
|
|
69
|
+
* @returns true if amount rounds to 0 NICK (dust)
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* isDustAmount(0.000001) // true (rounds to 0 NICK)
|
|
73
|
+
* isDustAmount(0.00001) // false (rounds to 1 NICK)
|
|
74
|
+
*/
|
|
75
|
+
export function isDustAmount(nockAmount: number): boolean {
|
|
76
|
+
if (nockAmount <= 0) return false; // Zero/negative handled separately
|
|
77
|
+
const roundedNick = nockToNick(nockAmount);
|
|
78
|
+
return roundedNick === 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Format NOCK for display with smart decimal precision and thousands separators
|
|
83
|
+
*
|
|
84
|
+
* Shows minimum decimals needed (up to 5 max) with commas for readability:
|
|
85
|
+
* - 2.5 → "2.5" (not "2.50000")
|
|
86
|
+
* - 1000.12345 → "1,000.12345"
|
|
87
|
+
* - 100.1234567 → "100.12346" (rounded to 5)
|
|
88
|
+
*
|
|
89
|
+
* @param nockAmount - Amount in NOCK
|
|
90
|
+
* @param maxDecimals - Maximum decimal places (default: 5)
|
|
91
|
+
* @returns Formatted NOCK string with minimal decimals and thousands separators
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* formatNock(2.5) // "2.5"
|
|
95
|
+
* formatNock(1000.12345) // "1,000.12345"
|
|
96
|
+
* formatNock(100.00) // "100"
|
|
97
|
+
*/
|
|
98
|
+
export function formatNock(nockAmount: number, maxDecimals: number = 5): string {
|
|
99
|
+
// Round to max decimals first
|
|
100
|
+
const rounded = Number(nockAmount.toFixed(maxDecimals));
|
|
101
|
+
|
|
102
|
+
// Split into integer and decimal parts
|
|
103
|
+
const [integerPart, decimalPart] = rounded.toString().split('.');
|
|
104
|
+
|
|
105
|
+
// Add thousands separators to integer part
|
|
106
|
+
const formattedInteger = parseInt(integerPart).toLocaleString('en-US');
|
|
107
|
+
|
|
108
|
+
// Recombine with decimal part (if exists)
|
|
109
|
+
return decimalPart ? `${formattedInteger}.${decimalPart}` : formattedInteger;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Format NICK with thousands separators
|
|
114
|
+
*
|
|
115
|
+
* @param nickAmount - Amount in NICK
|
|
116
|
+
* @returns Formatted string with commas
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* formatNick(6561690) // "6,561,690"
|
|
120
|
+
*/
|
|
121
|
+
export function formatNick(nickAmount: number): string {
|
|
122
|
+
return Math.round(nickAmount).toLocaleString('en-US');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Minimum sendable amount (1 NICK in NOCK)
|
|
127
|
+
*/
|
|
128
|
+
export const MIN_SENDABLE_NOCK = 1 / NOCK_TO_NICKS; // 0.0000152587890625
|