@sip-protocol/sdk 0.7.2 → 0.7.4
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/LICENSE +21 -0
- package/README.md +267 -0
- package/dist/{TransportWebUSB-TQ7WZ4LE.mjs → TransportWebUSB-YQMAGJAJ.mjs} +12 -9
- package/dist/browser.d.mts +10 -4
- package/dist/browser.d.ts +10 -4
- package/dist/browser.js +48874 -18336
- package/dist/browser.mjs +674 -48
- package/dist/chunk-4GRJ5MAW.mjs +152 -0
- package/dist/chunk-5D7A3L3W.mjs +717 -0
- package/dist/chunk-64AYA5F5.mjs +7834 -0
- package/dist/chunk-GMDGB22A.mjs +379 -0
- package/dist/chunk-I534WKN7.mjs +328 -0
- package/dist/chunk-IBZVA5Y7.mjs +1003 -0
- package/dist/chunk-PRRZAWJE.mjs +223 -0
- package/dist/{chunk-UJCSKKID.mjs → chunk-XGB3TDIC.mjs} +13 -1
- package/dist/chunk-YWGJ77A2.mjs +33806 -0
- package/dist/{chunk-6WGN57S2.mjs → chunk-Z3K7W5S3.mjs} +48 -0
- package/dist/constants-LHAAUC2T.mjs +51 -0
- package/dist/dist-2OGQ7FED.mjs +3957 -0
- package/dist/dist-IFHPYLDX.mjs +254 -0
- package/dist/fulfillment_proof-ANHVPKTB.mjs +21 -0
- package/dist/funding_proof-ICFZ5LHY.mjs +21 -0
- package/dist/index-DXh2IGkz.d.ts +24681 -0
- package/dist/index-DeE1ZzA4.d.mts +24681 -0
- package/dist/index.d.mts +9 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.js +48676 -17318
- package/dist/index.mjs +583 -19
- package/dist/interface-Bf7w1PLW.d.mts +679 -0
- package/dist/interface-Bf7w1PLW.d.ts +679 -0
- package/dist/{noir-DKfEzWy9.d.mts → noir-kzbLVTei.d.mts} +31 -21
- package/dist/{noir-DKfEzWy9.d.ts → noir-kzbLVTei.d.ts} +31 -21
- package/dist/proofs/halo2.d.mts +151 -0
- package/dist/proofs/halo2.d.ts +151 -0
- package/dist/proofs/halo2.js +350 -0
- package/dist/proofs/halo2.mjs +11 -0
- package/dist/proofs/kimchi.d.mts +160 -0
- package/dist/proofs/kimchi.d.ts +160 -0
- package/dist/proofs/kimchi.js +431 -0
- package/dist/proofs/kimchi.mjs +13 -0
- package/dist/proofs/noir.d.mts +1 -1
- package/dist/proofs/noir.d.ts +1 -1
- package/dist/proofs/noir.js +74 -18
- package/dist/proofs/noir.mjs +84 -24
- package/dist/solana-U3MEGU7W.mjs +280 -0
- package/dist/validity_proof-3POXLPNY.mjs +21 -0
- package/package.json +54 -21
- package/src/adapters/index.ts +41 -0
- package/src/adapters/jupiter.ts +571 -0
- package/src/adapters/near-intents.ts +135 -0
- package/src/advisor/advisor.ts +653 -0
- package/src/advisor/index.ts +54 -0
- package/src/advisor/tools.ts +303 -0
- package/src/advisor/types.ts +164 -0
- package/src/chains/ethereum/announcement.ts +536 -0
- package/src/chains/ethereum/bnb-optimizations.ts +474 -0
- package/src/chains/ethereum/commitment.ts +522 -0
- package/src/chains/ethereum/constants.ts +462 -0
- package/src/chains/ethereum/deployment.ts +596 -0
- package/src/chains/ethereum/gas-estimation.ts +538 -0
- package/src/chains/ethereum/index.ts +268 -0
- package/src/chains/ethereum/optimizations.ts +614 -0
- package/src/chains/ethereum/privacy-adapter.ts +855 -0
- package/src/chains/ethereum/registry.ts +584 -0
- package/src/chains/ethereum/rpc.ts +905 -0
- package/src/chains/ethereum/stealth.ts +491 -0
- package/src/chains/ethereum/token.ts +790 -0
- package/src/chains/ethereum/transfer.ts +637 -0
- package/src/chains/ethereum/types.ts +456 -0
- package/src/chains/ethereum/viewing-key.ts +455 -0
- package/src/chains/near/commitment.ts +608 -0
- package/src/chains/near/constants.ts +284 -0
- package/src/chains/near/function-call.ts +871 -0
- package/src/chains/near/history.ts +654 -0
- package/src/chains/near/implicit-account.ts +840 -0
- package/src/chains/near/index.ts +393 -0
- package/src/chains/near/native-transfer.ts +658 -0
- package/src/chains/near/nep141.ts +775 -0
- package/src/chains/near/privacy-adapter.ts +889 -0
- package/src/chains/near/resolver.ts +971 -0
- package/src/chains/near/rpc.ts +1016 -0
- package/src/chains/near/stealth.ts +419 -0
- package/src/chains/near/types.ts +317 -0
- package/src/chains/near/viewing-key.ts +876 -0
- package/src/chains/solana/anchor-transfer.ts +386 -0
- package/src/chains/solana/commitment.ts +577 -0
- package/src/chains/solana/constants.ts +126 -12
- package/src/chains/solana/ephemeral-keys.ts +543 -0
- package/src/chains/solana/index.ts +276 -1
- package/src/chains/solana/key-derivation.ts +418 -0
- package/src/chains/solana/kit-compat.ts +334 -0
- package/src/chains/solana/optimizations.ts +560 -0
- package/src/chains/solana/privacy-adapter.ts +605 -0
- package/src/chains/solana/providers/generic.ts +201 -0
- package/src/chains/solana/providers/helius-enhanced-types.ts +336 -0
- package/src/chains/solana/providers/helius-enhanced.ts +623 -0
- package/src/chains/solana/providers/helius.ts +402 -0
- package/src/chains/solana/providers/index.ts +85 -0
- package/src/chains/solana/providers/interface.ts +221 -0
- package/src/chains/solana/providers/quicknode.ts +409 -0
- package/src/chains/solana/providers/triton.ts +426 -0
- package/src/chains/solana/providers/webhook.ts +790 -0
- package/src/chains/solana/rpc-client.ts +1150 -0
- package/src/chains/solana/scan.ts +170 -73
- package/src/chains/solana/sol-transfer.ts +732 -0
- package/src/chains/solana/spl-transfer.ts +886 -0
- package/src/chains/solana/stealth-scanner.ts +703 -0
- package/src/chains/solana/sunspot-verifier.ts +453 -0
- package/src/chains/solana/transaction-builder.ts +755 -0
- package/src/chains/solana/transfer.ts +74 -5
- package/src/chains/solana/types.ts +77 -7
- package/src/chains/solana/utils.ts +110 -0
- package/src/chains/solana/viewing-key.ts +807 -0
- package/src/compliance/fireblocks.ts +921 -0
- package/src/compliance/index.ts +37 -0
- package/src/compliance/range-sas.ts +956 -0
- package/src/config/endpoints.ts +100 -0
- package/src/crypto.ts +11 -8
- package/src/errors.ts +82 -0
- package/src/evm/erc4337-relayer.ts +830 -0
- package/src/evm/index.ts +47 -0
- package/src/fees/calculator.ts +396 -0
- package/src/fees/index.ts +87 -0
- package/src/fees/near-contract.ts +429 -0
- package/src/fees/types.ts +268 -0
- package/src/index.ts +785 -1
- package/src/intent.ts +6 -3
- package/src/logger.ts +324 -0
- package/src/network/index.ts +80 -0
- package/src/network/proxy.ts +691 -0
- package/src/optimizations/index.ts +541 -0
- package/src/oracle/types.ts +1 -0
- package/src/privacy-backends/arcium-types.ts +727 -0
- package/src/privacy-backends/arcium.ts +719 -0
- package/src/privacy-backends/combined-privacy.ts +866 -0
- package/src/privacy-backends/cspl-token.ts +595 -0
- package/src/privacy-backends/cspl-types.ts +512 -0
- package/src/privacy-backends/cspl.ts +907 -0
- package/src/privacy-backends/health.ts +488 -0
- package/src/privacy-backends/inco-types.ts +323 -0
- package/src/privacy-backends/inco.ts +616 -0
- package/src/privacy-backends/index.ts +336 -0
- package/src/privacy-backends/interface.ts +906 -0
- package/src/privacy-backends/lru-cache.ts +343 -0
- package/src/privacy-backends/magicblock.ts +458 -0
- package/src/privacy-backends/mock.ts +258 -0
- package/src/privacy-backends/privacycash-types.ts +278 -0
- package/src/privacy-backends/privacycash.ts +456 -0
- package/src/privacy-backends/private-swap.ts +570 -0
- package/src/privacy-backends/rate-limiter.ts +683 -0
- package/src/privacy-backends/registry.ts +690 -0
- package/src/privacy-backends/router.ts +626 -0
- package/src/privacy-backends/shadowwire.ts +449 -0
- package/src/privacy-backends/sip-native.ts +256 -0
- package/src/privacy-logger.ts +191 -0
- package/src/production-safety.ts +373 -0
- package/src/proofs/aggregator.ts +1029 -0
- package/src/proofs/browser-composer.ts +1150 -0
- package/src/proofs/browser.ts +113 -25
- package/src/proofs/cache/index.ts +127 -0
- package/src/proofs/cache/interface.ts +545 -0
- package/src/proofs/cache/key-generator.ts +188 -0
- package/src/proofs/cache/lru-cache.ts +481 -0
- package/src/proofs/cache/multi-tier-cache.ts +575 -0
- package/src/proofs/cache/persistent-cache.ts +788 -0
- package/src/proofs/compliance-proof.ts +872 -0
- package/src/proofs/composer/base.ts +923 -0
- package/src/proofs/composer/index.ts +25 -0
- package/src/proofs/composer/interface.ts +518 -0
- package/src/proofs/composer/types.ts +383 -0
- package/src/proofs/converters/halo2.ts +452 -0
- package/src/proofs/converters/index.ts +208 -0
- package/src/proofs/converters/interface.ts +363 -0
- package/src/proofs/converters/kimchi.ts +462 -0
- package/src/proofs/converters/noir.ts +451 -0
- package/src/proofs/fallback.ts +888 -0
- package/src/proofs/halo2.ts +42 -0
- package/src/proofs/index.ts +471 -0
- package/src/proofs/interface.ts +13 -0
- package/src/proofs/kimchi.ts +42 -0
- package/src/proofs/lazy.ts +1004 -0
- package/src/proofs/mock.ts +25 -1
- package/src/proofs/noir.ts +111 -30
- package/src/proofs/orchestrator.ts +960 -0
- package/src/proofs/parallel/concurrency.ts +297 -0
- package/src/proofs/parallel/dependency-graph.ts +602 -0
- package/src/proofs/parallel/executor.ts +420 -0
- package/src/proofs/parallel/index.ts +131 -0
- package/src/proofs/parallel/interface.ts +685 -0
- package/src/proofs/parallel/worker-pool.ts +644 -0
- package/src/proofs/providers/halo2.ts +560 -0
- package/src/proofs/providers/index.ts +34 -0
- package/src/proofs/providers/kimchi.ts +641 -0
- package/src/proofs/validator.ts +881 -0
- package/src/proofs/verifier.ts +867 -0
- package/src/quantum/index.ts +112 -0
- package/src/quantum/winternitz-vault.ts +639 -0
- package/src/quantum/wots.ts +611 -0
- package/src/settlement/backends/direct-chain.ts +1 -0
- package/src/settlement/index.ts +9 -0
- package/src/settlement/router.ts +732 -46
- package/src/solana/index.ts +72 -0
- package/src/solana/jito-relayer.ts +687 -0
- package/src/solana/noir-verifier-types.ts +430 -0
- package/src/solana/noir-verifier.ts +816 -0
- package/src/stealth/address-derivation.ts +193 -0
- package/src/stealth/ed25519.ts +431 -0
- package/src/stealth/index.ts +233 -0
- package/src/stealth/meta-address.ts +221 -0
- package/src/stealth/secp256k1.ts +368 -0
- package/src/stealth/utils.ts +194 -0
- package/src/stealth.ts +50 -1504
- package/src/surveillance/algorithms/address-reuse.ts +143 -0
- package/src/surveillance/algorithms/cluster.ts +247 -0
- package/src/surveillance/algorithms/exchange.ts +295 -0
- package/src/surveillance/algorithms/temporal.ts +337 -0
- package/src/surveillance/analyzer.ts +442 -0
- package/src/surveillance/index.ts +64 -0
- package/src/surveillance/scoring.ts +372 -0
- package/src/surveillance/types.ts +264 -0
- package/src/sync/index.ts +106 -0
- package/src/sync/manager.ts +504 -0
- package/src/sync/mock-provider.ts +318 -0
- package/src/sync/oblivious.ts +625 -0
- package/src/tokens/index.ts +15 -0
- package/src/tokens/registry.ts +301 -0
- package/src/utils/deprecation.ts +94 -0
- package/src/utils/index.ts +9 -0
- package/src/wallet/ethereum/index.ts +68 -0
- package/src/wallet/ethereum/metamask-privacy.ts +420 -0
- package/src/wallet/ethereum/multi-wallet.ts +646 -0
- package/src/wallet/ethereum/privacy-adapter.ts +700 -0
- package/src/wallet/ethereum/types.ts +3 -1
- package/src/wallet/ethereum/walletconnect-adapter.ts +675 -0
- package/src/wallet/hardware/index.ts +10 -0
- package/src/wallet/hardware/ledger-privacy.ts +414 -0
- package/src/wallet/index.ts +71 -0
- package/src/wallet/near/adapter.ts +626 -0
- package/src/wallet/near/index.ts +86 -0
- package/src/wallet/near/meteor-wallet.ts +1153 -0
- package/src/wallet/near/my-near-wallet.ts +790 -0
- package/src/wallet/near/wallet-selector.ts +702 -0
- package/src/wallet/solana/adapter.ts +6 -4
- package/src/wallet/solana/index.ts +13 -0
- package/src/wallet/solana/privacy-adapter.ts +567 -0
- package/src/wallet/sui/types.ts +6 -4
- package/src/zcash/rpc-client.ts +13 -6
- package/dist/chunk-3INS3PR5.mjs +0 -884
- package/dist/chunk-3OVABDRH.mjs +0 -17096
- package/dist/chunk-DLDWZFYC.mjs +0 -1495
- package/dist/chunk-E6SZWREQ.mjs +0 -57
- package/dist/chunk-G33LB27A.mjs +0 -16166
- package/dist/chunk-HGU6HZRC.mjs +0 -231
- package/dist/chunk-L2K34JCU.mjs +0 -1496
- package/dist/chunk-SN4ZDTVW.mjs +0 -16166
- package/dist/constants-VOI7BSLK.mjs +0 -27
- package/dist/index-BYZbDjal.d.ts +0 -11390
- package/dist/index-CHB3KuOB.d.mts +0 -11859
- package/dist/index-CzWPI6Le.d.ts +0 -11859
- package/dist/index-xbWjohNq.d.mts +0 -11390
- package/dist/solana-5EMCTPTS.mjs +0 -46
- package/dist/solana-Q4NAVBTS.mjs +0 -46
|
@@ -0,0 +1,840 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NEAR Implicit Account Privacy Support
|
|
3
|
+
*
|
|
4
|
+
* Provides privacy operations for NEAR implicit accounts using stealth addresses.
|
|
5
|
+
* Implicit accounts are ideal for stealth addresses as they:
|
|
6
|
+
* - Don't require on-chain registration
|
|
7
|
+
* - Are created automatically on first transfer
|
|
8
|
+
* - Account ID = lowercase hex of ed25519 public key
|
|
9
|
+
*
|
|
10
|
+
* @example Send private transfer
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { buildPrivateTransfer } from '@sip-protocol/sdk'
|
|
13
|
+
*
|
|
14
|
+
* const { transaction, stealthAddress } = await buildPrivateTransfer({
|
|
15
|
+
* recipientMetaAddress: 'sip:near:0x...:0x...',
|
|
16
|
+
* amount: 1_000_000_000_000_000_000_000_000n, // 1 NEAR
|
|
17
|
+
* })
|
|
18
|
+
*
|
|
19
|
+
* // Sign and send transaction using your NEAR wallet
|
|
20
|
+
* await wallet.signAndSendTransaction(transaction)
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @example Claim from stealth account
|
|
24
|
+
* ```typescript
|
|
25
|
+
* import { buildClaimTransaction, deriveStealthAccountKeyPair } from '@sip-protocol/sdk'
|
|
26
|
+
*
|
|
27
|
+
* // Derive keypair for the stealth account
|
|
28
|
+
* const keypair = deriveStealthAccountKeyPair({
|
|
29
|
+
* stealthAddress: detectedPayment.stealthAddress,
|
|
30
|
+
* spendingPrivateKey: mySpendingKey,
|
|
31
|
+
* viewingPrivateKey: myViewingKey,
|
|
32
|
+
* })
|
|
33
|
+
*
|
|
34
|
+
* // Build claim transaction
|
|
35
|
+
* const tx = buildClaimTransaction({
|
|
36
|
+
* stealthAccountId: detectedPayment.stealthAccountId,
|
|
37
|
+
* destinationAccountId: 'alice.near',
|
|
38
|
+
* amount: detectedPayment.amount,
|
|
39
|
+
* })
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @packageDocumentation
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
import { ed25519 } from '@noble/curves/ed25519'
|
|
46
|
+
import { bytesToHex, hexToBytes } from '@noble/hashes/utils'
|
|
47
|
+
import type { HexString, StealthAddress, StealthMetaAddress } from '@sip-protocol/types'
|
|
48
|
+
import { ValidationError } from '../../errors'
|
|
49
|
+
import { isValidPrivateKey } from '../../validation'
|
|
50
|
+
import {
|
|
51
|
+
generateNEARStealthAddress,
|
|
52
|
+
deriveNEARStealthPrivateKey,
|
|
53
|
+
ed25519PublicKeyToImplicitAccount,
|
|
54
|
+
parseNEARStealthMetaAddress,
|
|
55
|
+
} from './stealth'
|
|
56
|
+
import { createAnnouncementMemo } from './types'
|
|
57
|
+
import {
|
|
58
|
+
isImplicitAccount,
|
|
59
|
+
isValidAccountId,
|
|
60
|
+
ONE_YOCTO,
|
|
61
|
+
DEFAULT_GAS,
|
|
62
|
+
STORAGE_DEPOSIT_DEFAULT,
|
|
63
|
+
} from './constants'
|
|
64
|
+
|
|
65
|
+
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Result of building a private transfer
|
|
69
|
+
*/
|
|
70
|
+
export interface NEARPrivateTransferBuild {
|
|
71
|
+
/** The stealth address generated for the recipient */
|
|
72
|
+
stealthAddress: StealthAddress
|
|
73
|
+
/** NEAR implicit account ID (64 hex chars) */
|
|
74
|
+
stealthAccountId: string
|
|
75
|
+
/** Announcement memo to include in transaction */
|
|
76
|
+
announcementMemo: string
|
|
77
|
+
/** Transaction actions to execute */
|
|
78
|
+
actions: NEARAction[]
|
|
79
|
+
/** Receiver ID for the transaction (the stealth implicit account) */
|
|
80
|
+
receiverId: string
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* NEAR transaction action
|
|
85
|
+
*/
|
|
86
|
+
export interface NEARAction {
|
|
87
|
+
type: 'Transfer' | 'FunctionCall' | 'AddKey' | 'DeleteKey' | 'DeleteAccount'
|
|
88
|
+
params: NEARTransferAction | NEARFunctionCallAction | NEARAddKeyAction | NEARDeleteKeyAction | NEARDeleteAccountAction
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Native NEAR transfer action
|
|
93
|
+
*/
|
|
94
|
+
export interface NEARTransferAction {
|
|
95
|
+
deposit: bigint
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Function call action (for NEP-141 tokens or contracts)
|
|
100
|
+
*/
|
|
101
|
+
export interface NEARFunctionCallAction {
|
|
102
|
+
methodName: string
|
|
103
|
+
args: string | Uint8Array
|
|
104
|
+
gas: bigint
|
|
105
|
+
deposit: bigint
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Add access key action
|
|
110
|
+
*/
|
|
111
|
+
export interface NEARAddKeyAction {
|
|
112
|
+
publicKey: string
|
|
113
|
+
accessKey: {
|
|
114
|
+
permission: 'FullAccess' | {
|
|
115
|
+
FunctionCall: {
|
|
116
|
+
allowance?: bigint
|
|
117
|
+
receiverId: string
|
|
118
|
+
methodNames: string[]
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Delete access key action
|
|
126
|
+
*/
|
|
127
|
+
export interface NEARDeleteKeyAction {
|
|
128
|
+
publicKey: string
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Delete account action (sends remaining balance to beneficiary)
|
|
133
|
+
*/
|
|
134
|
+
export interface NEARDeleteAccountAction {
|
|
135
|
+
beneficiaryId: string
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Derived stealth account keypair
|
|
140
|
+
*/
|
|
141
|
+
export interface NEARStealthKeyPair {
|
|
142
|
+
/** Ed25519 public key (0x-prefixed hex) */
|
|
143
|
+
publicKey: HexString
|
|
144
|
+
/** Ed25519 private key scalar (0x-prefixed hex) */
|
|
145
|
+
privateKey: HexString
|
|
146
|
+
/** NEAR implicit account ID */
|
|
147
|
+
accountId: string
|
|
148
|
+
/** NEAR-formatted public key string (ed25519:base58) */
|
|
149
|
+
nearPublicKey: string
|
|
150
|
+
/** Raw public key bytes */
|
|
151
|
+
publicKeyBytes: Uint8Array
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Parameters for deriving a stealth account keypair
|
|
156
|
+
*/
|
|
157
|
+
export interface DeriveStealthKeyPairParams {
|
|
158
|
+
/** The stealth address to derive keys for */
|
|
159
|
+
stealthAddress: StealthAddress
|
|
160
|
+
/** Recipient's spending private key */
|
|
161
|
+
spendingPrivateKey: HexString
|
|
162
|
+
/** Recipient's viewing private key */
|
|
163
|
+
viewingPrivateKey: HexString
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Parameters for building a claim transaction
|
|
168
|
+
*/
|
|
169
|
+
export interface NEARClaimTransactionParams {
|
|
170
|
+
/** Stealth implicit account ID to claim from */
|
|
171
|
+
stealthAccountId: string
|
|
172
|
+
/** Destination account ID to receive funds */
|
|
173
|
+
destinationAccountId: string
|
|
174
|
+
/** Amount to claim in yoctoNEAR (defaults to full balance minus storage) */
|
|
175
|
+
amount?: bigint
|
|
176
|
+
/** Token contract for NEP-141 claims (omit for native NEAR) */
|
|
177
|
+
tokenContract?: string
|
|
178
|
+
/** Leave minimum balance for storage (default: true) */
|
|
179
|
+
keepStorageDeposit?: boolean
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Result of building a claim transaction
|
|
184
|
+
*/
|
|
185
|
+
export interface NEARClaimTransactionBuild {
|
|
186
|
+
/** Transaction actions */
|
|
187
|
+
actions: NEARAction[]
|
|
188
|
+
/** Receiver ID (destination for native NEAR, token contract for NEP-141) */
|
|
189
|
+
receiverId: string
|
|
190
|
+
/** Whether this is a token transfer */
|
|
191
|
+
isTokenTransfer: boolean
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Parameters for adding an access key to a stealth account
|
|
196
|
+
*/
|
|
197
|
+
export interface NEARAddAccessKeyParams {
|
|
198
|
+
/** New public key to add (ed25519:base58 format) */
|
|
199
|
+
newPublicKey: string
|
|
200
|
+
/** Permission type */
|
|
201
|
+
permission: 'FullAccess' | {
|
|
202
|
+
allowance?: bigint
|
|
203
|
+
receiverId: string
|
|
204
|
+
methodNames: string[]
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Parameters for key rotation
|
|
210
|
+
*/
|
|
211
|
+
export interface NEARKeyRotationParams {
|
|
212
|
+
/** Current stealth account ID */
|
|
213
|
+
stealthAccountId: string
|
|
214
|
+
/** New public key (ed25519:base58 format) */
|
|
215
|
+
newPublicKey: string
|
|
216
|
+
/** Old public key to delete (ed25519:base58 format) */
|
|
217
|
+
oldPublicKey: string
|
|
218
|
+
/** Permission for new key */
|
|
219
|
+
permission?: 'FullAccess' | {
|
|
220
|
+
allowance?: bigint
|
|
221
|
+
receiverId: string
|
|
222
|
+
methodNames: string[]
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ─── Private Transfer Building ────────────────────────────────────────────────
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Build a private NEAR transfer to a stealth address
|
|
230
|
+
*
|
|
231
|
+
* Generates a one-time stealth address and builds the transaction
|
|
232
|
+
* actions needed to send NEAR privately.
|
|
233
|
+
*
|
|
234
|
+
* @param recipientMetaAddress - Recipient's stealth meta-address
|
|
235
|
+
* @param amount - Amount in yoctoNEAR
|
|
236
|
+
* @returns Transfer build with actions and stealth address
|
|
237
|
+
*
|
|
238
|
+
* @example Native NEAR transfer
|
|
239
|
+
* ```typescript
|
|
240
|
+
* const { actions, receiverId, stealthAddress, announcementMemo } =
|
|
241
|
+
* buildPrivateTransfer(
|
|
242
|
+
* 'sip:near:0x...:0x...',
|
|
243
|
+
* 1_000_000_000_000_000_000_000_000n // 1 NEAR
|
|
244
|
+
* )
|
|
245
|
+
*
|
|
246
|
+
* // Create transaction with these actions
|
|
247
|
+
* // Include announcementMemo in transaction logs/memo
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
250
|
+
export function buildPrivateTransfer(
|
|
251
|
+
recipientMetaAddress: StealthMetaAddress | string,
|
|
252
|
+
amount: bigint
|
|
253
|
+
): NEARPrivateTransferBuild {
|
|
254
|
+
// Parse meta-address if string
|
|
255
|
+
const metaAddr = typeof recipientMetaAddress === 'string'
|
|
256
|
+
? parseNEARStealthMetaAddress(recipientMetaAddress)
|
|
257
|
+
: recipientMetaAddress
|
|
258
|
+
|
|
259
|
+
// Validate chain
|
|
260
|
+
if (metaAddr.chain !== 'near') {
|
|
261
|
+
throw new ValidationError(
|
|
262
|
+
`Expected NEAR meta-address, got chain '${metaAddr.chain}'`,
|
|
263
|
+
'recipientMetaAddress'
|
|
264
|
+
)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Validate amount
|
|
268
|
+
if (amount <= 0n) {
|
|
269
|
+
throw new ValidationError('amount must be greater than 0', 'amount')
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Generate stealth address
|
|
273
|
+
const { stealthAddress, implicitAccountId } = generateNEARStealthAddress(metaAddr)
|
|
274
|
+
|
|
275
|
+
// Create announcement memo
|
|
276
|
+
const announcementMemo = createAnnouncementMemo(
|
|
277
|
+
stealthAddress.ephemeralPublicKey,
|
|
278
|
+
stealthAddress.viewTag
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
// Build transfer action
|
|
282
|
+
const actions: NEARAction[] = [
|
|
283
|
+
{
|
|
284
|
+
type: 'Transfer',
|
|
285
|
+
params: {
|
|
286
|
+
deposit: amount,
|
|
287
|
+
} as NEARTransferAction,
|
|
288
|
+
},
|
|
289
|
+
]
|
|
290
|
+
|
|
291
|
+
return {
|
|
292
|
+
stealthAddress,
|
|
293
|
+
stealthAccountId: implicitAccountId,
|
|
294
|
+
announcementMemo,
|
|
295
|
+
actions,
|
|
296
|
+
receiverId: implicitAccountId,
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Build a private NEP-141 token transfer to a stealth address
|
|
302
|
+
*
|
|
303
|
+
* @param recipientMetaAddress - Recipient's stealth meta-address
|
|
304
|
+
* @param tokenContract - NEP-141 token contract address
|
|
305
|
+
* @param amount - Amount in token's smallest units
|
|
306
|
+
* @param memo - Optional memo for the transfer
|
|
307
|
+
* @returns Transfer build with actions and stealth address
|
|
308
|
+
*
|
|
309
|
+
* @example NEP-141 token transfer
|
|
310
|
+
* ```typescript
|
|
311
|
+
* const { actions, receiverId, stealthAddress, announcementMemo } =
|
|
312
|
+
* buildPrivateTokenTransfer(
|
|
313
|
+
* 'sip:near:0x...:0x...',
|
|
314
|
+
* 'usdt.tether-token.near',
|
|
315
|
+
* 1_000_000n, // 1 USDT (6 decimals)
|
|
316
|
+
* )
|
|
317
|
+
*
|
|
318
|
+
* // receiverId is the token contract
|
|
319
|
+
* // ft_transfer to stealthAccountId
|
|
320
|
+
* ```
|
|
321
|
+
*/
|
|
322
|
+
export function buildPrivateTokenTransfer(
|
|
323
|
+
recipientMetaAddress: StealthMetaAddress | string,
|
|
324
|
+
tokenContract: string,
|
|
325
|
+
amount: bigint,
|
|
326
|
+
memo?: string
|
|
327
|
+
): NEARPrivateTransferBuild {
|
|
328
|
+
// Parse meta-address if string
|
|
329
|
+
const metaAddr = typeof recipientMetaAddress === 'string'
|
|
330
|
+
? parseNEARStealthMetaAddress(recipientMetaAddress)
|
|
331
|
+
: recipientMetaAddress
|
|
332
|
+
|
|
333
|
+
// Validate chain
|
|
334
|
+
if (metaAddr.chain !== 'near') {
|
|
335
|
+
throw new ValidationError(
|
|
336
|
+
`Expected NEAR meta-address, got chain '${metaAddr.chain}'`,
|
|
337
|
+
'recipientMetaAddress'
|
|
338
|
+
)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Validate token contract
|
|
342
|
+
if (!isValidAccountId(tokenContract)) {
|
|
343
|
+
throw new ValidationError('Invalid token contract account ID', 'tokenContract')
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Validate amount
|
|
347
|
+
if (amount <= 0n) {
|
|
348
|
+
throw new ValidationError('amount must be greater than 0', 'amount')
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Generate stealth address
|
|
352
|
+
const { stealthAddress, implicitAccountId } = generateNEARStealthAddress(metaAddr)
|
|
353
|
+
|
|
354
|
+
// Create announcement memo (include in the ft_transfer memo)
|
|
355
|
+
const announcementMemo = createAnnouncementMemo(
|
|
356
|
+
stealthAddress.ephemeralPublicKey,
|
|
357
|
+
stealthAddress.viewTag
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
// Build ft_transfer_call args
|
|
361
|
+
const transferMemo = memo ? `${announcementMemo}|${memo}` : announcementMemo
|
|
362
|
+
const args = JSON.stringify({
|
|
363
|
+
receiver_id: implicitAccountId,
|
|
364
|
+
amount: amount.toString(),
|
|
365
|
+
memo: transferMemo,
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
// Build function call action
|
|
369
|
+
const actions: NEARAction[] = [
|
|
370
|
+
{
|
|
371
|
+
type: 'FunctionCall',
|
|
372
|
+
params: {
|
|
373
|
+
methodName: 'ft_transfer',
|
|
374
|
+
args,
|
|
375
|
+
gas: DEFAULT_GAS,
|
|
376
|
+
deposit: ONE_YOCTO, // NEP-141 requires 1 yoctoNEAR deposit
|
|
377
|
+
} as NEARFunctionCallAction,
|
|
378
|
+
},
|
|
379
|
+
]
|
|
380
|
+
|
|
381
|
+
return {
|
|
382
|
+
stealthAddress,
|
|
383
|
+
stealthAccountId: implicitAccountId,
|
|
384
|
+
announcementMemo,
|
|
385
|
+
actions,
|
|
386
|
+
receiverId: tokenContract,
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Build storage deposit for a stealth account on a token contract
|
|
392
|
+
*
|
|
393
|
+
* Many NEP-141 tokens require storage deposit before receiving tokens.
|
|
394
|
+
*
|
|
395
|
+
* @param stealthAccountId - Stealth implicit account ID
|
|
396
|
+
* @param tokenContract - Token contract address
|
|
397
|
+
* @param amount - Storage deposit amount (defaults to standard amount)
|
|
398
|
+
* @returns Actions for storage deposit
|
|
399
|
+
*/
|
|
400
|
+
export function buildStorageDeposit(
|
|
401
|
+
stealthAccountId: string,
|
|
402
|
+
tokenContract: string,
|
|
403
|
+
amount: bigint = STORAGE_DEPOSIT_DEFAULT
|
|
404
|
+
): NEARAction[] {
|
|
405
|
+
if (!isImplicitAccount(stealthAccountId)) {
|
|
406
|
+
throw new ValidationError(
|
|
407
|
+
'stealthAccountId must be a valid implicit account',
|
|
408
|
+
'stealthAccountId'
|
|
409
|
+
)
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (!isValidAccountId(tokenContract)) {
|
|
413
|
+
throw new ValidationError('Invalid token contract account ID', 'tokenContract')
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const args = JSON.stringify({
|
|
417
|
+
account_id: stealthAccountId,
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
return [
|
|
421
|
+
{
|
|
422
|
+
type: 'FunctionCall',
|
|
423
|
+
params: {
|
|
424
|
+
methodName: 'storage_deposit',
|
|
425
|
+
args,
|
|
426
|
+
gas: DEFAULT_GAS,
|
|
427
|
+
deposit: amount,
|
|
428
|
+
} as NEARFunctionCallAction,
|
|
429
|
+
},
|
|
430
|
+
]
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// ─── Key Derivation ───────────────────────────────────────────────────────────
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Derive the keypair for a stealth implicit account
|
|
437
|
+
*
|
|
438
|
+
* Uses the DKSAP (Dual-Key Stealth Address Protocol) to derive
|
|
439
|
+
* the private key that controls the stealth account.
|
|
440
|
+
*
|
|
441
|
+
* @param params - Derivation parameters
|
|
442
|
+
* @returns Derived keypair with NEAR-formatted public key
|
|
443
|
+
*
|
|
444
|
+
* @example
|
|
445
|
+
* ```typescript
|
|
446
|
+
* const keypair = deriveStealthAccountKeyPair({
|
|
447
|
+
* stealthAddress: detectedPayment.stealthAddress,
|
|
448
|
+
* spendingPrivateKey: mySpendingKey,
|
|
449
|
+
* viewingPrivateKey: myViewingKey,
|
|
450
|
+
* })
|
|
451
|
+
*
|
|
452
|
+
* // keypair.nearPublicKey can be used to sign transactions
|
|
453
|
+
* // keypair.accountId is the implicit account ID
|
|
454
|
+
* ```
|
|
455
|
+
*/
|
|
456
|
+
export function deriveStealthAccountKeyPair(
|
|
457
|
+
params: DeriveStealthKeyPairParams
|
|
458
|
+
): NEARStealthKeyPair {
|
|
459
|
+
const { stealthAddress, spendingPrivateKey, viewingPrivateKey } = params
|
|
460
|
+
|
|
461
|
+
// Validate inputs
|
|
462
|
+
if (!stealthAddress) {
|
|
463
|
+
throw new ValidationError('stealthAddress is required', 'stealthAddress')
|
|
464
|
+
}
|
|
465
|
+
if (!isValidPrivateKey(spendingPrivateKey)) {
|
|
466
|
+
throw new ValidationError('Invalid spendingPrivateKey', 'spendingPrivateKey')
|
|
467
|
+
}
|
|
468
|
+
if (!isValidPrivateKey(viewingPrivateKey)) {
|
|
469
|
+
throw new ValidationError('Invalid viewingPrivateKey', 'viewingPrivateKey')
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Derive private key using DKSAP
|
|
473
|
+
const recovery = deriveNEARStealthPrivateKey(
|
|
474
|
+
stealthAddress,
|
|
475
|
+
spendingPrivateKey,
|
|
476
|
+
viewingPrivateKey
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
// The derived private key is a scalar (not a seed)
|
|
480
|
+
// Compute public key from scalar
|
|
481
|
+
const privateKeyBytes = hexToBytes(recovery.privateKey.slice(2))
|
|
482
|
+
const scalar = bytesToBigIntLE(privateKeyBytes)
|
|
483
|
+
const publicPoint = ed25519.ExtendedPoint.BASE.multiply(scalar)
|
|
484
|
+
const publicKeyBytes = publicPoint.toRawBytes()
|
|
485
|
+
|
|
486
|
+
// Convert to NEAR-formatted public key (ed25519:base58)
|
|
487
|
+
const nearPublicKey = `ed25519:${bytesToBase58(publicKeyBytes)}`
|
|
488
|
+
|
|
489
|
+
// Get implicit account ID
|
|
490
|
+
const accountId = ed25519PublicKeyToImplicitAccount(
|
|
491
|
+
`0x${bytesToHex(publicKeyBytes)}` as HexString
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
return {
|
|
495
|
+
publicKey: `0x${bytesToHex(publicKeyBytes)}` as HexString,
|
|
496
|
+
privateKey: recovery.privateKey,
|
|
497
|
+
accountId,
|
|
498
|
+
nearPublicKey,
|
|
499
|
+
publicKeyBytes,
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// ─── Claim Transactions ───────────────────────────────────────────────────────
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Build a transaction to claim funds from a stealth account
|
|
507
|
+
*
|
|
508
|
+
* @param params - Claim parameters
|
|
509
|
+
* @returns Transaction build with actions
|
|
510
|
+
*
|
|
511
|
+
* @example Claim native NEAR
|
|
512
|
+
* ```typescript
|
|
513
|
+
* const { actions, receiverId } = buildClaimTransaction({
|
|
514
|
+
* stealthAccountId: '1234...abcd',
|
|
515
|
+
* destinationAccountId: 'alice.near',
|
|
516
|
+
* amount: 1_000_000_000_000_000_000_000_000n,
|
|
517
|
+
* })
|
|
518
|
+
*
|
|
519
|
+
* // Sign with derived stealth keypair and send
|
|
520
|
+
* ```
|
|
521
|
+
*/
|
|
522
|
+
export function buildClaimTransaction(
|
|
523
|
+
params: NEARClaimTransactionParams
|
|
524
|
+
): NEARClaimTransactionBuild {
|
|
525
|
+
const {
|
|
526
|
+
stealthAccountId,
|
|
527
|
+
destinationAccountId,
|
|
528
|
+
amount,
|
|
529
|
+
tokenContract,
|
|
530
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
531
|
+
keepStorageDeposit: _keepStorageDeposit = true, // Reserved for future balance calculation
|
|
532
|
+
} = params
|
|
533
|
+
|
|
534
|
+
// Validate stealth account
|
|
535
|
+
if (!isImplicitAccount(stealthAccountId)) {
|
|
536
|
+
throw new ValidationError(
|
|
537
|
+
'stealthAccountId must be a valid implicit account',
|
|
538
|
+
'stealthAccountId'
|
|
539
|
+
)
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// Validate destination
|
|
543
|
+
if (!isValidAccountId(destinationAccountId)) {
|
|
544
|
+
throw new ValidationError(
|
|
545
|
+
'Invalid destinationAccountId',
|
|
546
|
+
'destinationAccountId'
|
|
547
|
+
)
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// Token transfer
|
|
551
|
+
if (tokenContract) {
|
|
552
|
+
if (!isValidAccountId(tokenContract)) {
|
|
553
|
+
throw new ValidationError('Invalid tokenContract', 'tokenContract')
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const args = JSON.stringify({
|
|
557
|
+
receiver_id: destinationAccountId,
|
|
558
|
+
amount: amount?.toString() ?? '0', // Will need to query balance if not specified
|
|
559
|
+
memo: null,
|
|
560
|
+
})
|
|
561
|
+
|
|
562
|
+
return {
|
|
563
|
+
actions: [
|
|
564
|
+
{
|
|
565
|
+
type: 'FunctionCall',
|
|
566
|
+
params: {
|
|
567
|
+
methodName: 'ft_transfer',
|
|
568
|
+
args,
|
|
569
|
+
gas: DEFAULT_GAS,
|
|
570
|
+
deposit: ONE_YOCTO,
|
|
571
|
+
} as NEARFunctionCallAction,
|
|
572
|
+
},
|
|
573
|
+
],
|
|
574
|
+
receiverId: tokenContract,
|
|
575
|
+
isTokenTransfer: true,
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Native NEAR transfer
|
|
580
|
+
const transferAmount = amount ?? 0n
|
|
581
|
+
|
|
582
|
+
return {
|
|
583
|
+
actions: [
|
|
584
|
+
{
|
|
585
|
+
type: 'Transfer',
|
|
586
|
+
params: {
|
|
587
|
+
deposit: transferAmount,
|
|
588
|
+
} as NEARTransferAction,
|
|
589
|
+
},
|
|
590
|
+
],
|
|
591
|
+
receiverId: destinationAccountId,
|
|
592
|
+
isTokenTransfer: false,
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Build a transaction to delete a stealth account and claim all funds
|
|
598
|
+
*
|
|
599
|
+
* This is more gas-efficient than regular transfer as it reclaims
|
|
600
|
+
* storage deposit. Use when you want to fully drain the account.
|
|
601
|
+
*
|
|
602
|
+
* @param stealthAccountId - Stealth implicit account to delete
|
|
603
|
+
* @param beneficiaryId - Account to receive all remaining funds
|
|
604
|
+
* @returns Delete account action
|
|
605
|
+
*/
|
|
606
|
+
export function buildDeleteStealthAccount(
|
|
607
|
+
stealthAccountId: string,
|
|
608
|
+
beneficiaryId: string
|
|
609
|
+
): NEARAction[] {
|
|
610
|
+
if (!isImplicitAccount(stealthAccountId)) {
|
|
611
|
+
throw new ValidationError(
|
|
612
|
+
'stealthAccountId must be a valid implicit account',
|
|
613
|
+
'stealthAccountId'
|
|
614
|
+
)
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
if (!isValidAccountId(beneficiaryId)) {
|
|
618
|
+
throw new ValidationError('Invalid beneficiaryId', 'beneficiaryId')
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
return [
|
|
622
|
+
{
|
|
623
|
+
type: 'DeleteAccount',
|
|
624
|
+
params: {
|
|
625
|
+
beneficiaryId,
|
|
626
|
+
} as NEARDeleteAccountAction,
|
|
627
|
+
},
|
|
628
|
+
]
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// ─── Access Key Management ────────────────────────────────────────────────────
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Build actions to add an access key to a stealth account
|
|
635
|
+
*
|
|
636
|
+
* This allows spending from the stealth account with a different key
|
|
637
|
+
* (e.g., a hardware wallet key) without revealing the stealth private key.
|
|
638
|
+
*
|
|
639
|
+
* @param params - Add access key parameters
|
|
640
|
+
* @returns Add key action
|
|
641
|
+
*
|
|
642
|
+
* @example Add full access key
|
|
643
|
+
* ```typescript
|
|
644
|
+
* const actions = buildAddAccessKey({
|
|
645
|
+
* newPublicKey: 'ed25519:...',
|
|
646
|
+
* permission: 'FullAccess',
|
|
647
|
+
* })
|
|
648
|
+
*
|
|
649
|
+
* // Sign with stealth keypair and send
|
|
650
|
+
* ```
|
|
651
|
+
*/
|
|
652
|
+
export function buildAddAccessKey(params: NEARAddAccessKeyParams): NEARAction[] {
|
|
653
|
+
const { newPublicKey, permission } = params
|
|
654
|
+
|
|
655
|
+
// Validate public key format
|
|
656
|
+
if (!newPublicKey.startsWith('ed25519:')) {
|
|
657
|
+
throw new ValidationError(
|
|
658
|
+
'newPublicKey must be in ed25519:base58 format',
|
|
659
|
+
'newPublicKey'
|
|
660
|
+
)
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
const accessKey = permission === 'FullAccess'
|
|
664
|
+
? { permission: 'FullAccess' as const }
|
|
665
|
+
: {
|
|
666
|
+
permission: {
|
|
667
|
+
FunctionCall: {
|
|
668
|
+
allowance: permission.allowance,
|
|
669
|
+
receiverId: permission.receiverId,
|
|
670
|
+
methodNames: permission.methodNames,
|
|
671
|
+
},
|
|
672
|
+
},
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
return [
|
|
676
|
+
{
|
|
677
|
+
type: 'AddKey',
|
|
678
|
+
params: {
|
|
679
|
+
publicKey: newPublicKey,
|
|
680
|
+
accessKey,
|
|
681
|
+
} as NEARAddKeyAction,
|
|
682
|
+
},
|
|
683
|
+
]
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Build actions for key rotation on a stealth account
|
|
688
|
+
*
|
|
689
|
+
* Atomically adds a new key and removes the old key in a single transaction.
|
|
690
|
+
* Useful for migrating to a hardware wallet key.
|
|
691
|
+
*
|
|
692
|
+
* @param params - Key rotation parameters
|
|
693
|
+
* @returns Actions for key rotation
|
|
694
|
+
*/
|
|
695
|
+
export function buildKeyRotation(params: NEARKeyRotationParams): NEARAction[] {
|
|
696
|
+
const { newPublicKey, oldPublicKey, permission = 'FullAccess' } = params
|
|
697
|
+
|
|
698
|
+
// Validate public key formats
|
|
699
|
+
if (!newPublicKey.startsWith('ed25519:')) {
|
|
700
|
+
throw new ValidationError(
|
|
701
|
+
'newPublicKey must be in ed25519:base58 format',
|
|
702
|
+
'newPublicKey'
|
|
703
|
+
)
|
|
704
|
+
}
|
|
705
|
+
if (!oldPublicKey.startsWith('ed25519:')) {
|
|
706
|
+
throw new ValidationError(
|
|
707
|
+
'oldPublicKey must be in ed25519:base58 format',
|
|
708
|
+
'oldPublicKey'
|
|
709
|
+
)
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
const accessKey = permission === 'FullAccess'
|
|
713
|
+
? { permission: 'FullAccess' as const }
|
|
714
|
+
: {
|
|
715
|
+
permission: {
|
|
716
|
+
FunctionCall: {
|
|
717
|
+
allowance: permission.allowance,
|
|
718
|
+
receiverId: permission.receiverId,
|
|
719
|
+
methodNames: permission.methodNames,
|
|
720
|
+
},
|
|
721
|
+
},
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// Add new key first, then delete old key
|
|
725
|
+
return [
|
|
726
|
+
{
|
|
727
|
+
type: 'AddKey',
|
|
728
|
+
params: {
|
|
729
|
+
publicKey: newPublicKey,
|
|
730
|
+
accessKey,
|
|
731
|
+
} as NEARAddKeyAction,
|
|
732
|
+
},
|
|
733
|
+
{
|
|
734
|
+
type: 'DeleteKey',
|
|
735
|
+
params: {
|
|
736
|
+
publicKey: oldPublicKey,
|
|
737
|
+
} as NEARDeleteKeyAction,
|
|
738
|
+
},
|
|
739
|
+
]
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// ─── Account State Helpers ────────────────────────────────────────────────────
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* Check if an implicit account ID is valid and follows stealth address format
|
|
746
|
+
*
|
|
747
|
+
* @param accountId - Account ID to check
|
|
748
|
+
* @returns True if it's a valid stealth-compatible implicit account
|
|
749
|
+
*/
|
|
750
|
+
export function isStealthCompatibleAccount(accountId: string): boolean {
|
|
751
|
+
return isImplicitAccount(accountId)
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* Get the public key for an implicit account
|
|
756
|
+
*
|
|
757
|
+
* @param accountId - Implicit account ID (64 hex chars)
|
|
758
|
+
* @returns Ed25519 public key in NEAR format (ed25519:base58)
|
|
759
|
+
*/
|
|
760
|
+
export function getImplicitAccountPublicKey(accountId: string): string {
|
|
761
|
+
if (!isImplicitAccount(accountId)) {
|
|
762
|
+
throw new ValidationError(
|
|
763
|
+
'accountId must be a valid implicit account',
|
|
764
|
+
'accountId'
|
|
765
|
+
)
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// Convert hex to bytes
|
|
769
|
+
const publicKeyBytes = hexToBytes(accountId)
|
|
770
|
+
|
|
771
|
+
// Convert to base58 and format
|
|
772
|
+
return `ed25519:${bytesToBase58(publicKeyBytes)}`
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Verify that an implicit account matches a stealth address
|
|
777
|
+
*
|
|
778
|
+
* @param accountId - Implicit account ID to check
|
|
779
|
+
* @param stealthAddress - Expected stealth address
|
|
780
|
+
* @returns True if the account matches the stealth address
|
|
781
|
+
*/
|
|
782
|
+
export function verifyImplicitAccountMatch(
|
|
783
|
+
accountId: string,
|
|
784
|
+
stealthAddress: StealthAddress
|
|
785
|
+
): boolean {
|
|
786
|
+
if (!isImplicitAccount(accountId)) {
|
|
787
|
+
return false
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// Get expected account ID from stealth address
|
|
791
|
+
const expectedAccountId = ed25519PublicKeyToImplicitAccount(stealthAddress.address)
|
|
792
|
+
|
|
793
|
+
return accountId.toLowerCase() === expectedAccountId.toLowerCase()
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// ─── Utility Functions ────────────────────────────────────────────────────────
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* Convert bytes to little-endian BigInt
|
|
800
|
+
*/
|
|
801
|
+
function bytesToBigIntLE(bytes: Uint8Array): bigint {
|
|
802
|
+
let result = 0n
|
|
803
|
+
for (let i = bytes.length - 1; i >= 0; i--) {
|
|
804
|
+
result = (result << 8n) | BigInt(bytes[i])
|
|
805
|
+
}
|
|
806
|
+
return result
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* Convert bytes to base58
|
|
811
|
+
*/
|
|
812
|
+
function bytesToBase58(bytes: Uint8Array): string {
|
|
813
|
+
const ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
|
814
|
+
|
|
815
|
+
// Count leading zeros
|
|
816
|
+
let zeros = 0
|
|
817
|
+
for (let i = 0; i < bytes.length && bytes[i] === 0; i++) {
|
|
818
|
+
zeros++
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// Convert to base58
|
|
822
|
+
const result: number[] = []
|
|
823
|
+
let num = 0n
|
|
824
|
+
for (const byte of bytes) {
|
|
825
|
+
num = num * 256n + BigInt(byte)
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
while (num > 0n) {
|
|
829
|
+
const remainder = Number(num % 58n)
|
|
830
|
+
num = num / 58n
|
|
831
|
+
result.unshift(remainder)
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// Add leading '1's for each leading zero byte
|
|
835
|
+
for (let i = 0; i < zeros; i++) {
|
|
836
|
+
result.unshift(0)
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
return result.map(i => ALPHABET[i]).join('')
|
|
840
|
+
}
|