@sip-protocol/sdk 0.7.3 → 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 +47556 -19603
- package/dist/browser.mjs +628 -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-3M3HNQCW.mjs → chunk-YWGJ77A2.mjs} +28656 -13103
- 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-DIBZHOOQ.d.ts → index-DXh2IGkz.d.ts} +21239 -10304
- package/dist/{index-8MQz13eJ.d.mts → index-DeE1ZzA4.d.mts} +21239 -10304
- package/dist/index.d.mts +9 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.js +48396 -19623
- package/dist/index.mjs +537 -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 +252 -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 +47 -6
- 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 +186 -33
- package/src/chains/solana/providers/index.ts +31 -0
- package/src/chains/solana/providers/interface.ts +61 -18
- 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 +338 -67
- package/src/chains/solana/rpc-client.ts +1150 -0
- package/src/chains/solana/scan.ts +83 -66
- 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 +57 -6
- 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 +23 -0
- package/src/compliance/range-sas.ts +398 -33
- 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 +686 -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 +254 -4
- package/src/privacy-backends/interface.ts +649 -6
- 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.ts +13 -17
- package/src/privacy-backends/private-swap.ts +570 -0
- package/src/privacy-backends/rate-limiter.ts +683 -0
- package/src/privacy-backends/registry.ts +414 -2
- package/src/privacy-backends/router.ts +283 -3
- package/src/privacy-backends/shadowwire.ts +449 -0
- package/src/privacy-backends/sip-native.ts +3 -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 +110 -29
- 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/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-2XIVXWHA.mjs +0 -1930
- package/dist/chunk-3INS3PR5.mjs +0 -884
- package/dist/chunk-3OVABDRH.mjs +0 -17096
- package/dist/chunk-7RFRWDCW.mjs +0 -1504
- package/dist/chunk-DLDWZFYC.mjs +0 -1495
- package/dist/chunk-E6SZWREQ.mjs +0 -57
- package/dist/chunk-F6F73W35.mjs +0 -16166
- package/dist/chunk-G33LB27A.mjs +0 -16166
- package/dist/chunk-HGU6HZRC.mjs +0 -231
- package/dist/chunk-L2K34JCU.mjs +0 -1496
- package/dist/chunk-OFDBEIEK.mjs +0 -16166
- package/dist/chunk-SF7YSLF5.mjs +0 -1515
- package/dist/chunk-SN4ZDTVW.mjs +0 -16166
- package/dist/chunk-WWUSGOXE.mjs +0 -17129
- package/dist/constants-VOI7BSLK.mjs +0 -27
- package/dist/index-B71aXVzk.d.ts +0 -13264
- 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-pOIIuwfV.d.mts +0 -13264
- package/dist/index-xbWjohNq.d.mts +0 -11390
- package/dist/solana-4O4K45VU.mjs +0 -46
- package/dist/solana-5EMCTPTS.mjs +0 -46
- package/dist/solana-NDABAZ6P.mjs +0 -56
- package/dist/solana-Q4NAVBTS.mjs +0 -46
- package/dist/solana-ZYO63LY5.mjs +0 -46
|
@@ -0,0 +1,790 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ERC-20 Token Helper for Privacy Transfers
|
|
3
|
+
*
|
|
4
|
+
* Handles token allowances, EIP-2612 permit signatures,
|
|
5
|
+
* and metadata fetching for privacy-preserving token transfers.
|
|
6
|
+
*
|
|
7
|
+
* @module chains/ethereum/token
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { HexString } from '@sip-protocol/types'
|
|
11
|
+
import { EthereumRpcClient } from './rpc'
|
|
12
|
+
import { estimateTokenTransferGas, type DetailedGasEstimate } from './gas-estimation'
|
|
13
|
+
import type { EthereumNetwork } from './constants'
|
|
14
|
+
import { DEFAULT_GAS_LIMITS } from './constants'
|
|
15
|
+
|
|
16
|
+
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* ERC-20 token metadata
|
|
20
|
+
*/
|
|
21
|
+
export interface TokenMetadata {
|
|
22
|
+
/**
|
|
23
|
+
* Token name
|
|
24
|
+
*/
|
|
25
|
+
name: string
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Token symbol
|
|
29
|
+
*/
|
|
30
|
+
symbol: string
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Token decimals
|
|
34
|
+
*/
|
|
35
|
+
decimals: number
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Token contract address
|
|
39
|
+
*/
|
|
40
|
+
address: HexString
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Token allowance info
|
|
45
|
+
*/
|
|
46
|
+
export interface TokenAllowance {
|
|
47
|
+
/**
|
|
48
|
+
* Owner address
|
|
49
|
+
*/
|
|
50
|
+
owner: HexString
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Spender address
|
|
54
|
+
*/
|
|
55
|
+
spender: HexString
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Current allowance
|
|
59
|
+
*/
|
|
60
|
+
allowance: bigint
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Token address
|
|
64
|
+
*/
|
|
65
|
+
tokenAddress: HexString
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* EIP-2612 permit data
|
|
70
|
+
*/
|
|
71
|
+
export interface PermitData {
|
|
72
|
+
/**
|
|
73
|
+
* Owner (signer) address
|
|
74
|
+
*/
|
|
75
|
+
owner: HexString
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Spender address
|
|
79
|
+
*/
|
|
80
|
+
spender: HexString
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Permit value
|
|
84
|
+
*/
|
|
85
|
+
value: bigint
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Permit nonce
|
|
89
|
+
*/
|
|
90
|
+
nonce: bigint
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Permit deadline (Unix timestamp)
|
|
94
|
+
*/
|
|
95
|
+
deadline: bigint
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Token address
|
|
99
|
+
*/
|
|
100
|
+
tokenAddress: HexString
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* EIP-712 typed data for permit signing
|
|
105
|
+
*/
|
|
106
|
+
export interface PermitTypedData {
|
|
107
|
+
/**
|
|
108
|
+
* Domain separator data
|
|
109
|
+
*/
|
|
110
|
+
domain: {
|
|
111
|
+
name: string
|
|
112
|
+
version: string
|
|
113
|
+
chainId: number
|
|
114
|
+
verifyingContract: HexString
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Types
|
|
119
|
+
*/
|
|
120
|
+
types: {
|
|
121
|
+
Permit: Array<{
|
|
122
|
+
name: string
|
|
123
|
+
type: string
|
|
124
|
+
}>
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Primary type
|
|
129
|
+
*/
|
|
130
|
+
primaryType: 'Permit'
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Message to sign
|
|
134
|
+
*/
|
|
135
|
+
message: {
|
|
136
|
+
owner: HexString
|
|
137
|
+
spender: HexString
|
|
138
|
+
value: string
|
|
139
|
+
nonce: string
|
|
140
|
+
deadline: string
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Prepared approval transaction
|
|
146
|
+
*/
|
|
147
|
+
export interface PreparedApproval {
|
|
148
|
+
/**
|
|
149
|
+
* Transaction data
|
|
150
|
+
*/
|
|
151
|
+
tx: {
|
|
152
|
+
to: HexString
|
|
153
|
+
data: HexString
|
|
154
|
+
value: bigint
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Token address
|
|
159
|
+
*/
|
|
160
|
+
tokenAddress: HexString
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Spender address
|
|
164
|
+
*/
|
|
165
|
+
spender: HexString
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Approval amount
|
|
169
|
+
*/
|
|
170
|
+
amount: bigint
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Estimated gas
|
|
174
|
+
*/
|
|
175
|
+
estimatedGas: bigint
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Token transfer check result
|
|
180
|
+
*/
|
|
181
|
+
export interface TransferCheck {
|
|
182
|
+
/**
|
|
183
|
+
* Whether transfer is possible
|
|
184
|
+
*/
|
|
185
|
+
canTransfer: boolean
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Current token balance
|
|
189
|
+
*/
|
|
190
|
+
balance: bigint
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Current allowance for spender
|
|
194
|
+
*/
|
|
195
|
+
allowance: bigint
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Whether approval is needed
|
|
199
|
+
*/
|
|
200
|
+
needsApproval: boolean
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Amount of additional approval needed
|
|
204
|
+
*/
|
|
205
|
+
additionalApprovalNeeded: bigint
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Transfer amount
|
|
209
|
+
*/
|
|
210
|
+
amount: bigint
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Estimated gas for transfer (including approval if needed)
|
|
214
|
+
*/
|
|
215
|
+
gasEstimate: DetailedGasEstimate
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* ERC-20 function selectors
|
|
222
|
+
*/
|
|
223
|
+
const ERC20_SELECTORS = {
|
|
224
|
+
/**
|
|
225
|
+
* name() returns (string)
|
|
226
|
+
*/
|
|
227
|
+
name: '0x06fdde03',
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* symbol() returns (string)
|
|
231
|
+
*/
|
|
232
|
+
symbol: '0x95d89b41',
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* decimals() returns (uint8)
|
|
236
|
+
*/
|
|
237
|
+
decimals: '0x313ce567',
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* totalSupply() returns (uint256)
|
|
241
|
+
*/
|
|
242
|
+
totalSupply: '0x18160ddd',
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* balanceOf(address) returns (uint256)
|
|
246
|
+
*/
|
|
247
|
+
balanceOf: '0x70a08231',
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* allowance(address,address) returns (uint256)
|
|
251
|
+
*/
|
|
252
|
+
allowance: '0xdd62ed3e',
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* approve(address,uint256) returns (bool)
|
|
256
|
+
*/
|
|
257
|
+
approve: '0x095ea7b3',
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* transfer(address,uint256) returns (bool)
|
|
261
|
+
*/
|
|
262
|
+
transfer: '0xa9059cbb',
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* transferFrom(address,address,uint256) returns (bool)
|
|
266
|
+
*/
|
|
267
|
+
transferFrom: '0x23b872dd',
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* permit(address,address,uint256,uint256,uint8,bytes32,bytes32)
|
|
271
|
+
*/
|
|
272
|
+
permit: '0xd505accf',
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* nonces(address) returns (uint256)
|
|
276
|
+
*/
|
|
277
|
+
nonces: '0x7ecebe00',
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* DOMAIN_SEPARATOR() returns (bytes32)
|
|
281
|
+
*/
|
|
282
|
+
domainSeparator: '0x3644e515',
|
|
283
|
+
} as const
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Maximum uint256 value for unlimited approval
|
|
287
|
+
*/
|
|
288
|
+
export const MAX_UINT256 = 2n ** 256n - 1n
|
|
289
|
+
|
|
290
|
+
// ─── Token Helper Class ──────────────────────────────────────────────────────
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* ERC-20 Token Helper
|
|
294
|
+
*
|
|
295
|
+
* Provides utilities for working with ERC-20 tokens in privacy transfers:
|
|
296
|
+
* - Metadata fetching (name, symbol, decimals)
|
|
297
|
+
* - Balance and allowance checking
|
|
298
|
+
* - Approval transaction building
|
|
299
|
+
* - EIP-2612 permit support
|
|
300
|
+
*
|
|
301
|
+
* @example Basic usage
|
|
302
|
+
* ```typescript
|
|
303
|
+
* const helper = new TokenHelper('mainnet')
|
|
304
|
+
*
|
|
305
|
+
* // Get token info
|
|
306
|
+
* const metadata = await helper.getTokenMetadata(usdcAddress)
|
|
307
|
+
* console.log(metadata.symbol) // 'USDC'
|
|
308
|
+
*
|
|
309
|
+
* // Check if transfer is possible
|
|
310
|
+
* const check = await helper.checkTransfer({
|
|
311
|
+
* owner: senderAddress,
|
|
312
|
+
* spender: routerAddress,
|
|
313
|
+
* tokenAddress: usdcAddress,
|
|
314
|
+
* amount: 100_000_000n,
|
|
315
|
+
* })
|
|
316
|
+
*
|
|
317
|
+
* if (check.needsApproval) {
|
|
318
|
+
* const approval = helper.buildApproval(...)
|
|
319
|
+
* // Sign and submit approval
|
|
320
|
+
* }
|
|
321
|
+
* ```
|
|
322
|
+
*/
|
|
323
|
+
export class TokenHelper {
|
|
324
|
+
private rpc: EthereumRpcClient
|
|
325
|
+
private network: EthereumNetwork
|
|
326
|
+
private metadataCache: Map<string, TokenMetadata> = new Map()
|
|
327
|
+
|
|
328
|
+
constructor(
|
|
329
|
+
network: EthereumNetwork = 'mainnet',
|
|
330
|
+
options?: {
|
|
331
|
+
rpcUrl?: string
|
|
332
|
+
}
|
|
333
|
+
) {
|
|
334
|
+
this.network = network
|
|
335
|
+
this.rpc = new EthereumRpcClient(network, options)
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// ─── Metadata Methods ────────────────────────────────────────────────────────
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Get token metadata (name, symbol, decimals)
|
|
342
|
+
*
|
|
343
|
+
* Results are cached for performance.
|
|
344
|
+
*
|
|
345
|
+
* @param tokenAddress - Token contract address
|
|
346
|
+
* @returns Token metadata
|
|
347
|
+
*/
|
|
348
|
+
async getTokenMetadata(tokenAddress: HexString): Promise<TokenMetadata> {
|
|
349
|
+
const cached = this.metadataCache.get(tokenAddress.toLowerCase())
|
|
350
|
+
if (cached) {
|
|
351
|
+
return cached
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Fetch name, symbol, decimals in parallel
|
|
355
|
+
const [name, symbol, decimals] = await Promise.all([
|
|
356
|
+
this.getTokenName(tokenAddress),
|
|
357
|
+
this.getTokenSymbol(tokenAddress),
|
|
358
|
+
this.getTokenDecimals(tokenAddress),
|
|
359
|
+
])
|
|
360
|
+
|
|
361
|
+
const metadata: TokenMetadata = {
|
|
362
|
+
name,
|
|
363
|
+
symbol,
|
|
364
|
+
decimals,
|
|
365
|
+
address: tokenAddress,
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
this.metadataCache.set(tokenAddress.toLowerCase(), metadata)
|
|
369
|
+
return metadata
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Get token name
|
|
374
|
+
*
|
|
375
|
+
* @param tokenAddress - Token contract address
|
|
376
|
+
* @returns Token name
|
|
377
|
+
*/
|
|
378
|
+
async getTokenName(tokenAddress: HexString): Promise<string> {
|
|
379
|
+
try {
|
|
380
|
+
const result = await this.rpc.call({
|
|
381
|
+
to: tokenAddress,
|
|
382
|
+
data: ERC20_SELECTORS.name as HexString,
|
|
383
|
+
})
|
|
384
|
+
return this.decodeString(result)
|
|
385
|
+
} catch {
|
|
386
|
+
return 'Unknown Token'
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Get token symbol
|
|
392
|
+
*
|
|
393
|
+
* @param tokenAddress - Token contract address
|
|
394
|
+
* @returns Token symbol
|
|
395
|
+
*/
|
|
396
|
+
async getTokenSymbol(tokenAddress: HexString): Promise<string> {
|
|
397
|
+
try {
|
|
398
|
+
const result = await this.rpc.call({
|
|
399
|
+
to: tokenAddress,
|
|
400
|
+
data: ERC20_SELECTORS.symbol as HexString,
|
|
401
|
+
})
|
|
402
|
+
return this.decodeString(result)
|
|
403
|
+
} catch {
|
|
404
|
+
return 'UNKNOWN'
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Get token decimals
|
|
410
|
+
*
|
|
411
|
+
* @param tokenAddress - Token contract address
|
|
412
|
+
* @returns Token decimals (default: 18)
|
|
413
|
+
*/
|
|
414
|
+
async getTokenDecimals(tokenAddress: HexString): Promise<number> {
|
|
415
|
+
try {
|
|
416
|
+
const result = await this.rpc.call({
|
|
417
|
+
to: tokenAddress,
|
|
418
|
+
data: ERC20_SELECTORS.decimals as HexString,
|
|
419
|
+
})
|
|
420
|
+
return parseInt(result, 16)
|
|
421
|
+
} catch {
|
|
422
|
+
return 18 // Default to 18 if call fails
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// ─── Balance Methods ─────────────────────────────────────────────────────────
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Get token balance
|
|
430
|
+
*
|
|
431
|
+
* @param tokenAddress - Token contract address
|
|
432
|
+
* @param owner - Address to check balance of
|
|
433
|
+
* @returns Token balance
|
|
434
|
+
*/
|
|
435
|
+
async getBalance(tokenAddress: HexString, owner: HexString): Promise<bigint> {
|
|
436
|
+
const data = `${ERC20_SELECTORS.balanceOf}${owner.slice(2).padStart(64, '0')}` as HexString
|
|
437
|
+
|
|
438
|
+
const result = await this.rpc.call({
|
|
439
|
+
to: tokenAddress,
|
|
440
|
+
data,
|
|
441
|
+
})
|
|
442
|
+
|
|
443
|
+
return BigInt(result)
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// ─── Allowance Methods ───────────────────────────────────────────────────────
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Get token allowance
|
|
450
|
+
*
|
|
451
|
+
* @param tokenAddress - Token contract address
|
|
452
|
+
* @param owner - Token owner address
|
|
453
|
+
* @param spender - Spender address
|
|
454
|
+
* @returns Current allowance
|
|
455
|
+
*/
|
|
456
|
+
async getAllowance(
|
|
457
|
+
tokenAddress: HexString,
|
|
458
|
+
owner: HexString,
|
|
459
|
+
spender: HexString
|
|
460
|
+
): Promise<bigint> {
|
|
461
|
+
const ownerParam = owner.slice(2).padStart(64, '0')
|
|
462
|
+
const spenderParam = spender.slice(2).padStart(64, '0')
|
|
463
|
+
const data = `${ERC20_SELECTORS.allowance}${ownerParam}${spenderParam}` as HexString
|
|
464
|
+
|
|
465
|
+
const result = await this.rpc.call({
|
|
466
|
+
to: tokenAddress,
|
|
467
|
+
data,
|
|
468
|
+
})
|
|
469
|
+
|
|
470
|
+
return BigInt(result)
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Build an approval transaction
|
|
475
|
+
*
|
|
476
|
+
* @param tokenAddress - Token contract address
|
|
477
|
+
* @param spender - Spender address to approve
|
|
478
|
+
* @param amount - Amount to approve (use MAX_UINT256 for unlimited)
|
|
479
|
+
* @returns Prepared approval transaction
|
|
480
|
+
*/
|
|
481
|
+
buildApproval(
|
|
482
|
+
tokenAddress: HexString,
|
|
483
|
+
spender: HexString,
|
|
484
|
+
amount: bigint = MAX_UINT256
|
|
485
|
+
): PreparedApproval {
|
|
486
|
+
const spenderParam = spender.slice(2).padStart(64, '0')
|
|
487
|
+
const amountParam = amount.toString(16).padStart(64, '0')
|
|
488
|
+
const data = `${ERC20_SELECTORS.approve}${spenderParam}${amountParam}` as HexString
|
|
489
|
+
|
|
490
|
+
return {
|
|
491
|
+
tx: {
|
|
492
|
+
to: tokenAddress,
|
|
493
|
+
data,
|
|
494
|
+
value: 0n,
|
|
495
|
+
},
|
|
496
|
+
tokenAddress,
|
|
497
|
+
spender,
|
|
498
|
+
amount,
|
|
499
|
+
estimatedGas: DEFAULT_GAS_LIMITS.erc20Approve,
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Check if transfer is possible and what approvals are needed
|
|
505
|
+
*
|
|
506
|
+
* @param params - Check parameters
|
|
507
|
+
* @returns Transfer check result
|
|
508
|
+
*/
|
|
509
|
+
async checkTransfer(params: {
|
|
510
|
+
owner: HexString
|
|
511
|
+
spender: HexString
|
|
512
|
+
tokenAddress: HexString
|
|
513
|
+
amount: bigint
|
|
514
|
+
}): Promise<TransferCheck> {
|
|
515
|
+
// Fetch balance and allowance in parallel
|
|
516
|
+
const [balance, allowance] = await Promise.all([
|
|
517
|
+
this.getBalance(params.tokenAddress, params.owner),
|
|
518
|
+
this.getAllowance(params.tokenAddress, params.owner, params.spender),
|
|
519
|
+
])
|
|
520
|
+
|
|
521
|
+
const needsApproval = allowance < params.amount
|
|
522
|
+
const additionalApprovalNeeded = needsApproval
|
|
523
|
+
? params.amount - allowance
|
|
524
|
+
: 0n
|
|
525
|
+
|
|
526
|
+
const canTransfer = balance >= params.amount
|
|
527
|
+
|
|
528
|
+
// Estimate gas
|
|
529
|
+
const gasEstimate = estimateTokenTransferGas(this.network, needsApproval)
|
|
530
|
+
|
|
531
|
+
return {
|
|
532
|
+
canTransfer,
|
|
533
|
+
balance,
|
|
534
|
+
allowance,
|
|
535
|
+
needsApproval,
|
|
536
|
+
additionalApprovalNeeded,
|
|
537
|
+
amount: params.amount,
|
|
538
|
+
gasEstimate,
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// ─── Permit Methods ──────────────────────────────────────────────────────────
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Check if token supports EIP-2612 permit
|
|
546
|
+
*
|
|
547
|
+
* @param tokenAddress - Token contract address
|
|
548
|
+
* @returns True if permit is supported
|
|
549
|
+
*/
|
|
550
|
+
async supportsPermit(tokenAddress: HexString): Promise<boolean> {
|
|
551
|
+
try {
|
|
552
|
+
// Try to call nonces() - if it exists, permit is likely supported
|
|
553
|
+
await this.rpc.call({
|
|
554
|
+
to: tokenAddress,
|
|
555
|
+
data: `${ERC20_SELECTORS.nonces}${'0'.padStart(64, '0')}` as HexString,
|
|
556
|
+
})
|
|
557
|
+
return true
|
|
558
|
+
} catch {
|
|
559
|
+
return false
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Get permit nonce for an address
|
|
565
|
+
*
|
|
566
|
+
* @param tokenAddress - Token contract address
|
|
567
|
+
* @param owner - Address to get nonce for
|
|
568
|
+
* @returns Permit nonce
|
|
569
|
+
*/
|
|
570
|
+
async getPermitNonce(tokenAddress: HexString, owner: HexString): Promise<bigint> {
|
|
571
|
+
const ownerParam = owner.slice(2).padStart(64, '0')
|
|
572
|
+
const data = `${ERC20_SELECTORS.nonces}${ownerParam}` as HexString
|
|
573
|
+
|
|
574
|
+
const result = await this.rpc.call({
|
|
575
|
+
to: tokenAddress,
|
|
576
|
+
data,
|
|
577
|
+
})
|
|
578
|
+
|
|
579
|
+
return BigInt(result)
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* Build EIP-712 typed data for permit signing
|
|
584
|
+
*
|
|
585
|
+
* @param params - Permit parameters
|
|
586
|
+
* @returns Typed data for signing
|
|
587
|
+
*/
|
|
588
|
+
async buildPermitTypedData(params: {
|
|
589
|
+
tokenAddress: HexString
|
|
590
|
+
owner: HexString
|
|
591
|
+
spender: HexString
|
|
592
|
+
value: bigint
|
|
593
|
+
deadline?: bigint
|
|
594
|
+
}): Promise<PermitTypedData> {
|
|
595
|
+
// Get token metadata and nonce
|
|
596
|
+
const [metadata, nonce] = await Promise.all([
|
|
597
|
+
this.getTokenMetadata(params.tokenAddress),
|
|
598
|
+
this.getPermitNonce(params.tokenAddress, params.owner),
|
|
599
|
+
])
|
|
600
|
+
|
|
601
|
+
// Default deadline: 1 hour from now
|
|
602
|
+
const deadline = params.deadline ?? BigInt(Math.floor(Date.now() / 1000) + 3600)
|
|
603
|
+
|
|
604
|
+
return {
|
|
605
|
+
domain: {
|
|
606
|
+
name: metadata.name,
|
|
607
|
+
version: '1',
|
|
608
|
+
chainId: this.rpc.getChainId(),
|
|
609
|
+
verifyingContract: params.tokenAddress,
|
|
610
|
+
},
|
|
611
|
+
types: {
|
|
612
|
+
Permit: [
|
|
613
|
+
{ name: 'owner', type: 'address' },
|
|
614
|
+
{ name: 'spender', type: 'address' },
|
|
615
|
+
{ name: 'value', type: 'uint256' },
|
|
616
|
+
{ name: 'nonce', type: 'uint256' },
|
|
617
|
+
{ name: 'deadline', type: 'uint256' },
|
|
618
|
+
],
|
|
619
|
+
},
|
|
620
|
+
primaryType: 'Permit',
|
|
621
|
+
message: {
|
|
622
|
+
owner: params.owner,
|
|
623
|
+
spender: params.spender,
|
|
624
|
+
value: params.value.toString(),
|
|
625
|
+
nonce: nonce.toString(),
|
|
626
|
+
deadline: deadline.toString(),
|
|
627
|
+
},
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* Build permit call data from signature
|
|
633
|
+
*
|
|
634
|
+
* @param params - Permit parameters with signature
|
|
635
|
+
* @returns Call data for permit function
|
|
636
|
+
*/
|
|
637
|
+
buildPermitCallData(params: {
|
|
638
|
+
owner: HexString
|
|
639
|
+
spender: HexString
|
|
640
|
+
value: bigint
|
|
641
|
+
deadline: bigint
|
|
642
|
+
v: number
|
|
643
|
+
r: HexString
|
|
644
|
+
s: HexString
|
|
645
|
+
}): HexString {
|
|
646
|
+
const ownerParam = params.owner.slice(2).padStart(64, '0')
|
|
647
|
+
const spenderParam = params.spender.slice(2).padStart(64, '0')
|
|
648
|
+
const valueParam = params.value.toString(16).padStart(64, '0')
|
|
649
|
+
const deadlineParam = params.deadline.toString(16).padStart(64, '0')
|
|
650
|
+
const vParam = params.v.toString(16).padStart(64, '0')
|
|
651
|
+
const rParam = params.r.slice(2).padStart(64, '0')
|
|
652
|
+
const sParam = params.s.slice(2).padStart(64, '0')
|
|
653
|
+
|
|
654
|
+
return `${ERC20_SELECTORS.permit}${ownerParam}${spenderParam}${valueParam}${deadlineParam}${vParam}${rParam}${sParam}` as HexString
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// ─── Utility Methods ─────────────────────────────────────────────────────────
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Format token amount with decimals
|
|
661
|
+
*
|
|
662
|
+
* @param amount - Raw amount
|
|
663
|
+
* @param decimals - Token decimals
|
|
664
|
+
* @returns Formatted string
|
|
665
|
+
*/
|
|
666
|
+
formatAmount(amount: bigint, decimals: number): string {
|
|
667
|
+
const divisor = 10n ** BigInt(decimals)
|
|
668
|
+
const whole = amount / divisor
|
|
669
|
+
const fraction = amount % divisor
|
|
670
|
+
|
|
671
|
+
if (fraction === 0n) {
|
|
672
|
+
return whole.toString()
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
const fractionStr = fraction.toString().padStart(decimals, '0')
|
|
676
|
+
const trimmed = fractionStr.replace(/0+$/, '')
|
|
677
|
+
|
|
678
|
+
return `${whole}.${trimmed}`
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Parse token amount from string
|
|
683
|
+
*
|
|
684
|
+
* @param amount - Amount string (e.g., "100.5")
|
|
685
|
+
* @param decimals - Token decimals
|
|
686
|
+
* @returns Raw amount
|
|
687
|
+
*/
|
|
688
|
+
parseAmount(amount: string, decimals: number): bigint {
|
|
689
|
+
const [whole, fraction = ''] = amount.split('.')
|
|
690
|
+
const paddedFraction = fraction.padEnd(decimals, '0').slice(0, decimals)
|
|
691
|
+
const combined = `${whole}${paddedFraction}`
|
|
692
|
+
|
|
693
|
+
return BigInt(combined)
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* Get network
|
|
698
|
+
*/
|
|
699
|
+
getNetwork(): EthereumNetwork {
|
|
700
|
+
return this.network
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Clear metadata cache
|
|
705
|
+
*/
|
|
706
|
+
clearCache(): void {
|
|
707
|
+
this.metadataCache.clear()
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// ─── Private Methods ─────────────────────────────────────────────────────────
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Decode ABI-encoded string
|
|
714
|
+
*/
|
|
715
|
+
private decodeString(data: HexString): string {
|
|
716
|
+
// Remove 0x prefix
|
|
717
|
+
const hex = data.slice(2)
|
|
718
|
+
|
|
719
|
+
// ABI-encoded string: offset (32 bytes) + length (32 bytes) + data
|
|
720
|
+
if (hex.length < 128) {
|
|
721
|
+
// Might be raw bytes32 string (some older tokens)
|
|
722
|
+
return this.decodeBytesString(hex)
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// Skip offset (first 32 bytes)
|
|
726
|
+
// Get length (next 32 bytes)
|
|
727
|
+
const lengthHex = hex.slice(64, 128)
|
|
728
|
+
const length = parseInt(lengthHex, 16)
|
|
729
|
+
|
|
730
|
+
if (length === 0 || length > 256) {
|
|
731
|
+
return this.decodeBytesString(hex)
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// Get string data
|
|
735
|
+
const stringHex = hex.slice(128, 128 + length * 2)
|
|
736
|
+
|
|
737
|
+
// Decode hex to string
|
|
738
|
+
let result = ''
|
|
739
|
+
for (let i = 0; i < stringHex.length; i += 2) {
|
|
740
|
+
const charCode = parseInt(stringHex.substr(i, 2), 16)
|
|
741
|
+
if (charCode === 0) break
|
|
742
|
+
result += String.fromCharCode(charCode)
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
return result
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* Decode bytes32 string (for non-standard tokens)
|
|
750
|
+
*/
|
|
751
|
+
private decodeBytesString(hex: string): string {
|
|
752
|
+
let result = ''
|
|
753
|
+
for (let i = 0; i < Math.min(hex.length, 64); i += 2) {
|
|
754
|
+
const charCode = parseInt(hex.substr(i, 2), 16)
|
|
755
|
+
if (charCode === 0) break
|
|
756
|
+
result += String.fromCharCode(charCode)
|
|
757
|
+
}
|
|
758
|
+
return result.trim() || 'Unknown'
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// ─── Factory Functions ────────────────────────────────────────────────────────
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* Create a token helper for a network
|
|
766
|
+
*
|
|
767
|
+
* @param network - Target network
|
|
768
|
+
* @param rpcUrl - Optional custom RPC URL
|
|
769
|
+
* @returns Token helper
|
|
770
|
+
*/
|
|
771
|
+
export function createTokenHelper(
|
|
772
|
+
network: EthereumNetwork = 'mainnet',
|
|
773
|
+
rpcUrl?: string
|
|
774
|
+
): TokenHelper {
|
|
775
|
+
return new TokenHelper(network, { rpcUrl })
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* Create a mainnet token helper
|
|
780
|
+
*/
|
|
781
|
+
export function createMainnetTokenHelper(rpcUrl?: string): TokenHelper {
|
|
782
|
+
return new TokenHelper('mainnet', { rpcUrl })
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* Create a Sepolia testnet token helper
|
|
787
|
+
*/
|
|
788
|
+
export function createSepoliaTokenHelper(rpcUrl?: string): TokenHelper {
|
|
789
|
+
return new TokenHelper('sepolia', { rpcUrl })
|
|
790
|
+
}
|