@earthworm/wallet-sdk 0.1.0
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/README.md +37 -0
- package/dist/index.d.ts +328 -0
- package/dist/index.js +887 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# @earthworm/wallet-sdk
|
|
2
|
+
|
|
3
|
+
Browser EVM wallet SDK for DApps (MetaMask, Coinbase, TronLink, EIP-6963, contract reads).
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @earthworm/wallet-sdk ethers
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { initWalletSdk, getWalletSdkConfig, walletEvents } from "@earthworm/wallet-sdk";
|
|
15
|
+
|
|
16
|
+
initWalletSdk({
|
|
17
|
+
isMainnet: true,
|
|
18
|
+
contract: { address: "0x...", abi: [] },
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
walletEvents.on("accountsChanged", (accounts) => {
|
|
22
|
+
console.log(accounts);
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Develop
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pnpm install
|
|
30
|
+
pnpm run build
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Publish
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pnpm publish
|
|
37
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import * as ethers from 'ethers';
|
|
2
|
+
import { InterfaceAbi, Eip1193Provider, BrowserProvider, JsonRpcProvider, JsonRpcSigner } from 'ethers';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 将 EVM 十六进制 chainId(如 0x61)转换为十进制 chainId(如 97)。
|
|
6
|
+
* 钱包 `chainChanged` / `eth_chainId` 返回的通常是 0x 前缀 hex 字符串。
|
|
7
|
+
*/
|
|
8
|
+
declare function toChainIdDecimal(chainId: string, fallback?: number): number;
|
|
9
|
+
|
|
10
|
+
type EthereumEvent = "accountsChanged" | "chainChanged" | "connect" | "disconnect" | string;
|
|
11
|
+
type EthereumListener = (...args: unknown[]) => void;
|
|
12
|
+
type WalletEventProvider = Eip1193Provider & {
|
|
13
|
+
on(event: EthereumEvent, listener: EthereumListener): void;
|
|
14
|
+
removeListener(event: EthereumEvent, listener: EthereumListener): void;
|
|
15
|
+
removeAllListeners?(event?: EthereumEvent): void;
|
|
16
|
+
};
|
|
17
|
+
type InjectedEthereumProvider = WalletEventProvider & {
|
|
18
|
+
isMetaMask?: boolean;
|
|
19
|
+
isCoinbaseWallet?: boolean;
|
|
20
|
+
isTronLink?: boolean;
|
|
21
|
+
isTronLinkWallet?: boolean;
|
|
22
|
+
providers?: InjectedEthereumProvider[];
|
|
23
|
+
providerMap?: Map<string, InjectedEthereumProvider> | Record<string, InjectedEthereumProvider>;
|
|
24
|
+
};
|
|
25
|
+
type EvmReadProvider = BrowserProvider | JsonRpcProvider;
|
|
26
|
+
type MutableRef<T> = {
|
|
27
|
+
value: T;
|
|
28
|
+
};
|
|
29
|
+
type EthereumRequestParams = unknown[] | Record<string, unknown>;
|
|
30
|
+
type ChainConfig = {
|
|
31
|
+
chainId: string;
|
|
32
|
+
chainName: string;
|
|
33
|
+
nativeCurrencyName: string;
|
|
34
|
+
nativeCurrencySymbol: string;
|
|
35
|
+
nativeCurrencyDecimals: string | number;
|
|
36
|
+
rpcUrl: string;
|
|
37
|
+
blockExplorerUrl: string;
|
|
38
|
+
};
|
|
39
|
+
type Eip6963ProviderInfo = {
|
|
40
|
+
uuid: string;
|
|
41
|
+
name: string;
|
|
42
|
+
icon: string;
|
|
43
|
+
rdns: string;
|
|
44
|
+
};
|
|
45
|
+
type Eip6963ProviderDetail = {
|
|
46
|
+
info: Eip6963ProviderInfo;
|
|
47
|
+
provider: Eip1193Provider;
|
|
48
|
+
};
|
|
49
|
+
type ContractConfig = {
|
|
50
|
+
address: string;
|
|
51
|
+
abi: InterfaceAbi;
|
|
52
|
+
};
|
|
53
|
+
type WalletSdkInitOptions = {
|
|
54
|
+
/** `true` BSC 主网,`false` BSC 测试网 */
|
|
55
|
+
isMainnet: boolean;
|
|
56
|
+
contract: ContractConfig;
|
|
57
|
+
};
|
|
58
|
+
type WalletSdkConfig = {
|
|
59
|
+
isMainnet: boolean;
|
|
60
|
+
chainId: string;
|
|
61
|
+
chain: ChainConfig;
|
|
62
|
+
contract: ContractConfig;
|
|
63
|
+
usdt: {
|
|
64
|
+
address: string;
|
|
65
|
+
abi: InterfaceAbi;
|
|
66
|
+
symbol: string;
|
|
67
|
+
name: string;
|
|
68
|
+
decimals: number;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
declare function discoverWalletProviders(timeoutMs?: number): Promise<Eip6963ProviderDetail[]>;
|
|
73
|
+
declare function findDiscoveredProvider(match: (detail: Eip6963ProviderDetail) => boolean): ethers.Eip1193Provider | undefined;
|
|
74
|
+
declare function findMetaMaskEip6963Provider(): ethers.Eip1193Provider | undefined;
|
|
75
|
+
declare function findCoinbaseEip6963Provider(): ethers.Eip1193Provider | undefined;
|
|
76
|
+
declare function hasMetaMaskEip6963Provider(): boolean;
|
|
77
|
+
declare function hasCoinbaseEip6963Provider(): boolean;
|
|
78
|
+
|
|
79
|
+
declare function getSharedBrowserProvider(ethereum: Eip1193Provider): BrowserProvider;
|
|
80
|
+
|
|
81
|
+
declare function getInjectedEthereumProviders(): InjectedEthereumProvider[];
|
|
82
|
+
declare function resolveMetaMaskInjectedProvider(): WalletEventProvider | undefined;
|
|
83
|
+
declare function resolveCoinbaseInjectedProvider(): WalletEventProvider | undefined;
|
|
84
|
+
declare function hasMetaMaskInjectedProvider(): boolean;
|
|
85
|
+
declare function hasCoinbaseInjectedProvider(): boolean;
|
|
86
|
+
declare function rememberAggregatorProvider(): void;
|
|
87
|
+
declare function hasRecoverableAggregatorProvider(): boolean;
|
|
88
|
+
declare function getAggregatedInjectedProvider(): WalletEventProvider | undefined;
|
|
89
|
+
declare function hasAggregatedInjectedProvider(): boolean;
|
|
90
|
+
|
|
91
|
+
declare const ERC20_DECIMALS_ABI: readonly ["function decimals() view returns (uint8)"];
|
|
92
|
+
/** 检查地址在链上是否已部署合约(有 bytecode 才算合约地址) */
|
|
93
|
+
declare function hasDeployedContract(readProvider: EvmReadProvider, address: string): Promise<boolean>;
|
|
94
|
+
/** 读取 ERC20 代币 decimals;合约未部署直接报错,调用失败时返回 fallback */
|
|
95
|
+
declare function readErc20Decimals(readProvider: EvmReadProvider, tokenAddress: string, erc20Abi?: InterfaceAbi, fallback?: 18): Promise<number>;
|
|
96
|
+
|
|
97
|
+
type GetContractReadOptions = {
|
|
98
|
+
abi: InterfaceAbi;
|
|
99
|
+
method: string;
|
|
100
|
+
};
|
|
101
|
+
/** 设置钱包 BrowserProvider,链上读取优先走钱包 */
|
|
102
|
+
declare function setWalletReadProvider(provider: BrowserProvider | null): void;
|
|
103
|
+
declare function getConfiguredReadProvider(): EvmReadProvider;
|
|
104
|
+
/** 检查合约是否已部署 */
|
|
105
|
+
declare function isContractDeployed(contractAddress: string): Promise<boolean>;
|
|
106
|
+
/** 读取合约 decimals */
|
|
107
|
+
declare function getContractDecimals(contractAddress: string, options: GetContractReadOptions): Promise<number>;
|
|
108
|
+
/** 读取合约价格 */
|
|
109
|
+
declare function getContractPrice(contractAddress: string, options: GetContractReadOptions): Promise<string>;
|
|
110
|
+
|
|
111
|
+
declare function buildEthereumChainParams(config: ChainConfig): {
|
|
112
|
+
chainId: string;
|
|
113
|
+
chainName: string;
|
|
114
|
+
nativeCurrency: {
|
|
115
|
+
name: string;
|
|
116
|
+
symbol: string;
|
|
117
|
+
decimals: number;
|
|
118
|
+
};
|
|
119
|
+
rpcUrls: string[];
|
|
120
|
+
blockExplorerUrls: string[];
|
|
121
|
+
};
|
|
122
|
+
/** 确保钱包切换到目标网络(不存在时自动 addEthereumChain) */
|
|
123
|
+
declare function ensureTargetNetwork(options: {
|
|
124
|
+
targetChainId: string;
|
|
125
|
+
getCurrentChainId: () => Promise<string>;
|
|
126
|
+
switchChain: (chainId: string) => Promise<unknown>;
|
|
127
|
+
addChain: (chainParams: Record<string, unknown>) => Promise<unknown>;
|
|
128
|
+
chainParams: Record<string, unknown>;
|
|
129
|
+
}): Promise<void>;
|
|
130
|
+
|
|
131
|
+
declare function createJsonRpcReadProvider(rpcUrl: string, chainIdHex: string, cache?: {
|
|
132
|
+
current: JsonRpcProvider | null;
|
|
133
|
+
}): JsonRpcProvider;
|
|
134
|
+
/** 优先使用钱包 BrowserProvider,否则回退到 JsonRpcProvider */
|
|
135
|
+
declare function getReadProviderWithFallback(walletProvider: BrowserProvider | null, rpcConfig: {
|
|
136
|
+
rpcUrl: string;
|
|
137
|
+
chainId: string;
|
|
138
|
+
}, fallbackCache?: {
|
|
139
|
+
current: JsonRpcProvider | null;
|
|
140
|
+
}): EvmReadProvider;
|
|
141
|
+
|
|
142
|
+
declare function bindBrowserProvider(eip1193: WalletEventProvider, cache: MutableRef<BrowserProvider | null>): BrowserProvider;
|
|
143
|
+
declare function requestWallet(eip1193: WalletEventProvider, method: string, params?: EthereumRequestParams): Promise<any>;
|
|
144
|
+
declare function connectWalletAccount(requestAccounts: () => Promise<unknown>, getProvider: () => BrowserProvider, setSigner: (signer: JsonRpcSigner) => void, markConnected: () => void): Promise<{
|
|
145
|
+
address: string;
|
|
146
|
+
signer: JsonRpcSigner;
|
|
147
|
+
}>;
|
|
148
|
+
declare function syncWalletAccountAddress(getProvider: () => BrowserProvider, setSigner: (signer: JsonRpcSigner) => void, markConnected: () => void, nextAddress?: string): Promise<{
|
|
149
|
+
address: string;
|
|
150
|
+
signer: JsonRpcSigner;
|
|
151
|
+
}>;
|
|
152
|
+
declare function clearWalletRuntime(cache: {
|
|
153
|
+
provider: MutableRef<BrowserProvider | null>;
|
|
154
|
+
signer: MutableRef<JsonRpcSigner | null>;
|
|
155
|
+
selectedProvider?: MutableRef<WalletEventProvider | null>;
|
|
156
|
+
}): void;
|
|
157
|
+
|
|
158
|
+
type WalletProviderEventMap = {
|
|
159
|
+
accountsChanged: string[];
|
|
160
|
+
chainChanged: string;
|
|
161
|
+
};
|
|
162
|
+
type WalletProviderEventName = keyof WalletProviderEventMap;
|
|
163
|
+
type WalletProviderEventListener<K extends WalletProviderEventName = WalletProviderEventName> = (payload: WalletProviderEventMap[K]) => void | Promise<void>;
|
|
164
|
+
/** 设置当前钱包 EIP-1193 provider,供事件监听使用 */
|
|
165
|
+
declare function setWalletEventProvider(provider: WalletEventProvider | null): void;
|
|
166
|
+
declare function getWalletEventProvider(): WalletEventProvider;
|
|
167
|
+
declare class WalletEventsEmitter {
|
|
168
|
+
on(event: "accountsChanged", listener: WalletProviderEventListener<"accountsChanged">): void;
|
|
169
|
+
on(event: "chainChanged", listener: WalletProviderEventListener<"chainChanged">): void;
|
|
170
|
+
on(event: WalletProviderEventName, listener: (...args: any[]) => void | Promise<void>): void;
|
|
171
|
+
off(event: "accountsChanged", listener: WalletProviderEventListener<"accountsChanged">): void;
|
|
172
|
+
off(event: "chainChanged", listener: WalletProviderEventListener<"chainChanged">): void;
|
|
173
|
+
off(event: WalletProviderEventName, listener?: (...args: any[]) => void | Promise<void>): void;
|
|
174
|
+
clear(): void;
|
|
175
|
+
}
|
|
176
|
+
type WalletEvents = WalletEventsEmitter;
|
|
177
|
+
declare const walletEvents: WalletEventsEmitter;
|
|
178
|
+
|
|
179
|
+
/** 是否在浏览器环境(模块加载时判定一次) */
|
|
180
|
+
declare const isBrowser: boolean;
|
|
181
|
+
/** 仅浏览器可用的操作入口;检测类 API 请用 isBrowser 后 return false */
|
|
182
|
+
declare function assertBrowser(apiName: string): void;
|
|
183
|
+
|
|
184
|
+
/** 是否检测到 MetaMask */
|
|
185
|
+
declare function isEthereumProvider(): boolean;
|
|
186
|
+
/** 是否检测到 Coinbase Wallet */
|
|
187
|
+
declare function isCoinbaseProvider(): boolean;
|
|
188
|
+
/** 是否检测到通用 Injected 钱包 */
|
|
189
|
+
declare function isInjectedProvider(): boolean;
|
|
190
|
+
/** 是否检测到 TronLink */
|
|
191
|
+
declare function isTronLinkProvider(): boolean;
|
|
192
|
+
|
|
193
|
+
/** 初始化并持久化配置,任意模块均可通过 getWalletSdkConfig 读取 */
|
|
194
|
+
declare function initWalletSdk(options: WalletSdkInitOptions): WalletSdkConfig;
|
|
195
|
+
declare function getWalletSdkConfig(): WalletSdkConfig;
|
|
196
|
+
declare function isWalletSdkInitialized(): boolean;
|
|
197
|
+
declare function resetWalletSdkConfig(): void;
|
|
198
|
+
|
|
199
|
+
declare const BSC_NETWORK: {
|
|
200
|
+
chainId: string;
|
|
201
|
+
chainName: string;
|
|
202
|
+
nativeCurrencyName: string;
|
|
203
|
+
nativeCurrencySymbol: string;
|
|
204
|
+
nativeCurrencyDecimals: string;
|
|
205
|
+
rpcUrl: string;
|
|
206
|
+
blockExplorerUrl: string;
|
|
207
|
+
};
|
|
208
|
+
declare const BSC_TESTNET_NETWORK: {
|
|
209
|
+
chainId: string;
|
|
210
|
+
chainName: string;
|
|
211
|
+
nativeCurrencyName: string;
|
|
212
|
+
nativeCurrencySymbol: string;
|
|
213
|
+
nativeCurrencyDecimals: string;
|
|
214
|
+
rpcUrl: string;
|
|
215
|
+
blockExplorerUrl: string;
|
|
216
|
+
};
|
|
217
|
+
declare function getDefaultBscConfig(isProd?: boolean): {
|
|
218
|
+
chainId: string;
|
|
219
|
+
chainName: string;
|
|
220
|
+
nativeCurrencyName: string;
|
|
221
|
+
nativeCurrencySymbol: string;
|
|
222
|
+
nativeCurrencyDecimals: string;
|
|
223
|
+
rpcUrl: string;
|
|
224
|
+
blockExplorerUrl: string;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
/** BSC USDT 合约地址 */
|
|
228
|
+
declare const USDT_CONTRACT_ADDRESSES: {
|
|
229
|
+
readonly [BSC_NETWORK.chainId]: "0x55d398326f99059fF775485246999027B3197955";
|
|
230
|
+
readonly [BSC_TESTNET_NETWORK.chainId]: "0x66E972502A34A625828C544a1914E8D8cc2A9dE5";
|
|
231
|
+
};
|
|
232
|
+
declare function resolveUsdtContractAddress(chainId: string): "0x55d398326f99059fF775485246999027B3197955" | "0x66E972502A34A625828C544a1914E8D8cc2A9dE5";
|
|
233
|
+
declare function resolveChainByMainnet(isMainnet: boolean): ChainConfig;
|
|
234
|
+
declare function resolveUsdtByMainnet(isMainnet: boolean): "0x55d398326f99059fF775485246999027B3197955" | "0x66E972502A34A625828C544a1914E8D8cc2A9dE5";
|
|
235
|
+
declare function resolveChainConfig(chainId: string): ChainConfig;
|
|
236
|
+
|
|
237
|
+
declare const USDT_CONFIG: {
|
|
238
|
+
readonly symbol: "USDT";
|
|
239
|
+
readonly name: "Tether USD";
|
|
240
|
+
readonly decimals: 18;
|
|
241
|
+
readonly contractAbi: ({
|
|
242
|
+
constant: boolean;
|
|
243
|
+
inputs: {
|
|
244
|
+
name: string;
|
|
245
|
+
type: string;
|
|
246
|
+
}[];
|
|
247
|
+
name: string;
|
|
248
|
+
outputs: {
|
|
249
|
+
name: string;
|
|
250
|
+
type: string;
|
|
251
|
+
}[];
|
|
252
|
+
payable: boolean;
|
|
253
|
+
stateMutability: string;
|
|
254
|
+
type: string;
|
|
255
|
+
anonymous?: undefined;
|
|
256
|
+
} | {
|
|
257
|
+
inputs: {
|
|
258
|
+
name: string;
|
|
259
|
+
type: string;
|
|
260
|
+
}[];
|
|
261
|
+
payable: boolean;
|
|
262
|
+
stateMutability: string;
|
|
263
|
+
type: string;
|
|
264
|
+
constant?: undefined;
|
|
265
|
+
name?: undefined;
|
|
266
|
+
outputs?: undefined;
|
|
267
|
+
anonymous?: undefined;
|
|
268
|
+
} | {
|
|
269
|
+
anonymous: boolean;
|
|
270
|
+
inputs: {
|
|
271
|
+
indexed: boolean;
|
|
272
|
+
name: string;
|
|
273
|
+
type: string;
|
|
274
|
+
}[];
|
|
275
|
+
name: string;
|
|
276
|
+
type: string;
|
|
277
|
+
constant?: undefined;
|
|
278
|
+
outputs?: undefined;
|
|
279
|
+
payable?: undefined;
|
|
280
|
+
stateMutability?: undefined;
|
|
281
|
+
})[];
|
|
282
|
+
};
|
|
283
|
+
/** BSC 上 ERC20 代币统一 decimals(如 USDT 为 18) */
|
|
284
|
+
declare const ERC20_DECIMALS: 18;
|
|
285
|
+
|
|
286
|
+
declare const ERC20_ABI: ({
|
|
287
|
+
constant: boolean;
|
|
288
|
+
inputs: {
|
|
289
|
+
name: string;
|
|
290
|
+
type: string;
|
|
291
|
+
}[];
|
|
292
|
+
name: string;
|
|
293
|
+
outputs: {
|
|
294
|
+
name: string;
|
|
295
|
+
type: string;
|
|
296
|
+
}[];
|
|
297
|
+
payable: boolean;
|
|
298
|
+
stateMutability: string;
|
|
299
|
+
type: string;
|
|
300
|
+
anonymous?: undefined;
|
|
301
|
+
} | {
|
|
302
|
+
inputs: {
|
|
303
|
+
name: string;
|
|
304
|
+
type: string;
|
|
305
|
+
}[];
|
|
306
|
+
payable: boolean;
|
|
307
|
+
stateMutability: string;
|
|
308
|
+
type: string;
|
|
309
|
+
constant?: undefined;
|
|
310
|
+
name?: undefined;
|
|
311
|
+
outputs?: undefined;
|
|
312
|
+
anonymous?: undefined;
|
|
313
|
+
} | {
|
|
314
|
+
anonymous: boolean;
|
|
315
|
+
inputs: {
|
|
316
|
+
indexed: boolean;
|
|
317
|
+
name: string;
|
|
318
|
+
type: string;
|
|
319
|
+
}[];
|
|
320
|
+
name: string;
|
|
321
|
+
type: string;
|
|
322
|
+
constant?: undefined;
|
|
323
|
+
outputs?: undefined;
|
|
324
|
+
payable?: undefined;
|
|
325
|
+
stateMutability?: undefined;
|
|
326
|
+
})[];
|
|
327
|
+
|
|
328
|
+
export { BSC_NETWORK, BSC_TESTNET_NETWORK, type ChainConfig, type ContractConfig, ERC20_ABI, ERC20_DECIMALS, ERC20_DECIMALS_ABI, type Eip6963ProviderDetail, type Eip6963ProviderInfo, type EthereumRequestParams, type EvmReadProvider, type GetContractReadOptions, type InjectedEthereumProvider, type MutableRef, USDT_CONFIG, USDT_CONTRACT_ADDRESSES, type WalletEventProvider, type WalletEvents, type WalletProviderEventListener, type WalletProviderEventMap, type WalletProviderEventName, type WalletSdkConfig, type WalletSdkInitOptions, assertBrowser, bindBrowserProvider, buildEthereumChainParams, clearWalletRuntime, connectWalletAccount, createJsonRpcReadProvider, discoverWalletProviders, ensureTargetNetwork, findCoinbaseEip6963Provider, findDiscoveredProvider, findMetaMaskEip6963Provider, getAggregatedInjectedProvider, getConfiguredReadProvider, getContractDecimals, getContractPrice, getDefaultBscConfig, getInjectedEthereumProviders, getReadProviderWithFallback, getSharedBrowserProvider, getWalletEventProvider, getWalletSdkConfig, hasAggregatedInjectedProvider, hasCoinbaseEip6963Provider, hasCoinbaseInjectedProvider, hasDeployedContract, hasMetaMaskEip6963Provider, hasMetaMaskInjectedProvider, hasRecoverableAggregatorProvider, initWalletSdk, isBrowser, isCoinbaseProvider, isContractDeployed, isEthereumProvider, isInjectedProvider, isTronLinkProvider, isWalletSdkInitialized, readErc20Decimals, rememberAggregatorProvider, requestWallet, resetWalletSdkConfig, resolveChainByMainnet, resolveChainConfig, resolveCoinbaseInjectedProvider, resolveMetaMaskInjectedProvider, resolveUsdtByMainnet, resolveUsdtContractAddress, setWalletEventProvider, setWalletReadProvider, syncWalletAccountAddress, toChainIdDecimal, walletEvents };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,887 @@
|
|
|
1
|
+
import { BrowserProvider, Contract, JsonRpcProvider, formatUnits } from 'ethers';
|
|
2
|
+
|
|
3
|
+
// src/chain.ts
|
|
4
|
+
function toChainIdDecimal(chainId, fallback) {
|
|
5
|
+
const decimal = Number.parseInt(chainId, 16);
|
|
6
|
+
if (Number.isFinite(decimal)) return decimal;
|
|
7
|
+
if (fallback !== void 0) return fallback;
|
|
8
|
+
return Number.NaN;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// src/env.ts
|
|
12
|
+
var isBrowser = typeof window !== "undefined";
|
|
13
|
+
function assertBrowser(apiName) {
|
|
14
|
+
if (!isBrowser) {
|
|
15
|
+
throw new Error(`[wallet-sdk] ${apiName} is only available in the browser`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// src/eip6963.ts
|
|
20
|
+
var METAMASK_RDNS = "io.metamask";
|
|
21
|
+
var COINBASE_RDNS = "com.coinbase.wallet";
|
|
22
|
+
var discoveredProviders = [];
|
|
23
|
+
var discoveryPromise = null;
|
|
24
|
+
function discoverWalletProviders(timeoutMs = 500) {
|
|
25
|
+
if (!isBrowser) {
|
|
26
|
+
return Promise.resolve([]);
|
|
27
|
+
}
|
|
28
|
+
if (discoveryPromise) {
|
|
29
|
+
return discoveryPromise;
|
|
30
|
+
}
|
|
31
|
+
discoveryPromise = new Promise((resolve) => {
|
|
32
|
+
const providers = [];
|
|
33
|
+
const onAnnounce = (event) => {
|
|
34
|
+
const detail = event.detail;
|
|
35
|
+
if (!detail?.provider) return;
|
|
36
|
+
if (providers.some((item) => item.info.uuid === detail.info.uuid)) return;
|
|
37
|
+
providers.push(detail);
|
|
38
|
+
};
|
|
39
|
+
window.addEventListener("eip6963:announceProvider", onAnnounce);
|
|
40
|
+
window.dispatchEvent(new Event("eip6963:requestProvider"));
|
|
41
|
+
window.setTimeout(() => {
|
|
42
|
+
window.removeEventListener("eip6963:announceProvider", onAnnounce);
|
|
43
|
+
discoveredProviders = providers;
|
|
44
|
+
resolve(providers);
|
|
45
|
+
}, timeoutMs);
|
|
46
|
+
});
|
|
47
|
+
return discoveryPromise;
|
|
48
|
+
}
|
|
49
|
+
function findDiscoveredProvider(match) {
|
|
50
|
+
return discoveredProviders.find(match)?.provider;
|
|
51
|
+
}
|
|
52
|
+
function findMetaMaskEip6963Provider() {
|
|
53
|
+
return findDiscoveredProvider(
|
|
54
|
+
(detail) => detail.info.rdns === METAMASK_RDNS || detail.info.name.toLowerCase() === "metamask"
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
function findCoinbaseEip6963Provider() {
|
|
58
|
+
return findDiscoveredProvider(
|
|
59
|
+
(detail) => detail.info.rdns === COINBASE_RDNS || detail.info.name.toLowerCase().includes("coinbase")
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
function hasMetaMaskEip6963Provider() {
|
|
63
|
+
return Boolean(findMetaMaskEip6963Provider());
|
|
64
|
+
}
|
|
65
|
+
function hasCoinbaseEip6963Provider() {
|
|
66
|
+
return Boolean(findCoinbaseEip6963Provider());
|
|
67
|
+
}
|
|
68
|
+
var providerCache = /* @__PURE__ */ new WeakMap();
|
|
69
|
+
function getSharedBrowserProvider(ethereum) {
|
|
70
|
+
const cached = providerCache.get(ethereum);
|
|
71
|
+
if (cached) {
|
|
72
|
+
return cached;
|
|
73
|
+
}
|
|
74
|
+
const provider = new BrowserProvider(ethereum);
|
|
75
|
+
providerCache.set(ethereum, provider);
|
|
76
|
+
return provider;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/injected-providers.ts
|
|
80
|
+
var isRequestableProvider = (provider) => {
|
|
81
|
+
return Boolean(provider && typeof provider.request === "function");
|
|
82
|
+
};
|
|
83
|
+
function getInjectedEthereumProviders() {
|
|
84
|
+
const ethereum = window.ethereum;
|
|
85
|
+
if (!ethereum) return [];
|
|
86
|
+
if (Array.isArray(ethereum.providers) && ethereum.providers.length > 0) {
|
|
87
|
+
return ethereum.providers.filter(isRequestableProvider);
|
|
88
|
+
}
|
|
89
|
+
return isRequestableProvider(ethereum) ? [ethereum] : [];
|
|
90
|
+
}
|
|
91
|
+
var isMetaMaskLikeProvider = (provider) => {
|
|
92
|
+
return Boolean(
|
|
93
|
+
provider.isMetaMask && !provider.isCoinbaseWallet && !provider.isTronLink && !provider.isTronLinkWallet
|
|
94
|
+
);
|
|
95
|
+
};
|
|
96
|
+
var isCoinbaseLikeProvider = (provider) => {
|
|
97
|
+
return Boolean(provider.isCoinbaseWallet && !provider.isTronLink && !provider.isTronLinkWallet);
|
|
98
|
+
};
|
|
99
|
+
var isTronLinkLikeProvider = (provider) => {
|
|
100
|
+
return Boolean(provider.isTronLink || provider.isTronLinkWallet);
|
|
101
|
+
};
|
|
102
|
+
function getMetaMaskFromProviderMap(ethereum) {
|
|
103
|
+
const providerMap = ethereum.providerMap;
|
|
104
|
+
if (!providerMap) return void 0;
|
|
105
|
+
if (providerMap instanceof Map) {
|
|
106
|
+
const metamask2 = providerMap.get("MetaMask") || providerMap.get("metamask");
|
|
107
|
+
return metamask2 && isMetaMaskLikeProvider(metamask2) ? metamask2 : void 0;
|
|
108
|
+
}
|
|
109
|
+
const metamask = providerMap.MetaMask || providerMap.metamask;
|
|
110
|
+
return metamask && isMetaMaskLikeProvider(metamask) ? metamask : void 0;
|
|
111
|
+
}
|
|
112
|
+
function getCoinbaseFromProviderMap(ethereum) {
|
|
113
|
+
const providerMap = ethereum.providerMap;
|
|
114
|
+
if (!providerMap) return void 0;
|
|
115
|
+
if (providerMap instanceof Map) {
|
|
116
|
+
const coinbase2 = providerMap.get("Coinbase Wallet") || providerMap.get("CoinbaseWallet");
|
|
117
|
+
return coinbase2 && isCoinbaseLikeProvider(coinbase2) ? coinbase2 : void 0;
|
|
118
|
+
}
|
|
119
|
+
const coinbase = providerMap["Coinbase Wallet"] || providerMap.CoinbaseWallet;
|
|
120
|
+
return coinbase && isCoinbaseLikeProvider(coinbase) ? coinbase : void 0;
|
|
121
|
+
}
|
|
122
|
+
function resolveMetaMaskInjectedProvider() {
|
|
123
|
+
const fromEip6963 = findMetaMaskEip6963Provider();
|
|
124
|
+
if (fromEip6963 && isRequestableProvider(fromEip6963)) {
|
|
125
|
+
return fromEip6963;
|
|
126
|
+
}
|
|
127
|
+
const ethereum = window.ethereum;
|
|
128
|
+
const fromMap = ethereum ? getMetaMaskFromProviderMap(ethereum) : void 0;
|
|
129
|
+
if (fromMap) {
|
|
130
|
+
return fromMap;
|
|
131
|
+
}
|
|
132
|
+
const fromProviders = getInjectedEthereumProviders().find(isMetaMaskLikeProvider);
|
|
133
|
+
if (fromProviders) {
|
|
134
|
+
return fromProviders;
|
|
135
|
+
}
|
|
136
|
+
if (ethereum && isMetaMaskLikeProvider(ethereum)) {
|
|
137
|
+
return ethereum;
|
|
138
|
+
}
|
|
139
|
+
return void 0;
|
|
140
|
+
}
|
|
141
|
+
function resolveCoinbaseInjectedProvider() {
|
|
142
|
+
if (isRequestableProvider(window.coinbaseWalletExtension)) {
|
|
143
|
+
return window.coinbaseWalletExtension;
|
|
144
|
+
}
|
|
145
|
+
if (isRequestableProvider(window.coinbaseWalletRequestProvider)) {
|
|
146
|
+
return window.coinbaseWalletRequestProvider;
|
|
147
|
+
}
|
|
148
|
+
const fromEip6963 = findCoinbaseEip6963Provider();
|
|
149
|
+
if (fromEip6963 && isRequestableProvider(fromEip6963)) {
|
|
150
|
+
return fromEip6963;
|
|
151
|
+
}
|
|
152
|
+
const ethereum = window.ethereum;
|
|
153
|
+
const fromMap = ethereum ? getCoinbaseFromProviderMap(ethereum) : void 0;
|
|
154
|
+
if (fromMap) {
|
|
155
|
+
return fromMap;
|
|
156
|
+
}
|
|
157
|
+
const fromProviders = getInjectedEthereumProviders().find(isCoinbaseLikeProvider);
|
|
158
|
+
if (fromProviders) {
|
|
159
|
+
return fromProviders;
|
|
160
|
+
}
|
|
161
|
+
if (ethereum && isCoinbaseLikeProvider(ethereum)) {
|
|
162
|
+
return ethereum;
|
|
163
|
+
}
|
|
164
|
+
return void 0;
|
|
165
|
+
}
|
|
166
|
+
function hasMetaMaskInjectedProvider() {
|
|
167
|
+
return Boolean(resolveMetaMaskInjectedProvider());
|
|
168
|
+
}
|
|
169
|
+
function hasCoinbaseInjectedProvider() {
|
|
170
|
+
return Boolean(
|
|
171
|
+
window.coinbaseWalletExtension || window.coinbaseWalletRequestProvider || window.coinbaseSolana || resolveCoinbaseInjectedProvider()
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
var cachedAggregatorProvider;
|
|
175
|
+
var cachedAggregatorProviderCount = 0;
|
|
176
|
+
var isMultiWalletAggregator = (provider) => {
|
|
177
|
+
return Array.isArray(provider.providers) && provider.providers.length > 1;
|
|
178
|
+
};
|
|
179
|
+
var collectAggregatorCandidates = () => {
|
|
180
|
+
const seen = /* @__PURE__ */ new Set();
|
|
181
|
+
const candidates = [];
|
|
182
|
+
const add = (provider) => {
|
|
183
|
+
if (!provider || seen.has(provider) || !isRequestableProvider(provider)) return;
|
|
184
|
+
seen.add(provider);
|
|
185
|
+
candidates.push(provider);
|
|
186
|
+
};
|
|
187
|
+
add(window.ethereum);
|
|
188
|
+
add(window.coinbaseWalletExtension);
|
|
189
|
+
add(window.coinbaseWalletRequestProvider);
|
|
190
|
+
add(cachedAggregatorProvider);
|
|
191
|
+
return candidates;
|
|
192
|
+
};
|
|
193
|
+
var findLiveMultiWalletRoot = () => {
|
|
194
|
+
return collectAggregatorCandidates().find(isMultiWalletAggregator);
|
|
195
|
+
};
|
|
196
|
+
function rememberAggregatorProvider() {
|
|
197
|
+
if (!isBrowser) return;
|
|
198
|
+
const live = findLiveMultiWalletRoot();
|
|
199
|
+
if (!live) return;
|
|
200
|
+
const count = live.providers?.length ?? 0;
|
|
201
|
+
if (!cachedAggregatorProvider || count >= cachedAggregatorProviderCount) {
|
|
202
|
+
cachedAggregatorProvider = live;
|
|
203
|
+
cachedAggregatorProviderCount = count;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function hasRecoverableAggregatorProvider() {
|
|
207
|
+
return Boolean(findLiveMultiWalletRoot() || cachedAggregatorProviderCount > 1);
|
|
208
|
+
}
|
|
209
|
+
function getAggregatedInjectedProvider() {
|
|
210
|
+
const live = findLiveMultiWalletRoot();
|
|
211
|
+
if (live) {
|
|
212
|
+
rememberAggregatorProvider();
|
|
213
|
+
return live;
|
|
214
|
+
}
|
|
215
|
+
if (cachedAggregatorProvider && cachedAggregatorProviderCount > 1) {
|
|
216
|
+
return cachedAggregatorProvider;
|
|
217
|
+
}
|
|
218
|
+
const ethereum = window.ethereum;
|
|
219
|
+
if (ethereum && isTronLinkLikeProvider(ethereum) && !isMultiWalletAggregator(ethereum)) {
|
|
220
|
+
const coinbaseRequest = window.coinbaseWalletRequestProvider ?? window.coinbaseWalletExtension;
|
|
221
|
+
if (isRequestableProvider(coinbaseRequest)) {
|
|
222
|
+
return coinbaseRequest;
|
|
223
|
+
}
|
|
224
|
+
return cachedAggregatorProvider ?? ethereum;
|
|
225
|
+
}
|
|
226
|
+
return ethereum;
|
|
227
|
+
}
|
|
228
|
+
function hasAggregatedInjectedProvider() {
|
|
229
|
+
if (!isBrowser) return false;
|
|
230
|
+
if (getAggregatedInjectedProvider()) return true;
|
|
231
|
+
if (hasRecoverableAggregatorProvider()) return true;
|
|
232
|
+
return isRequestableProvider(window.ethereum);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// src/abi/erc20.ts
|
|
236
|
+
var ERC20_ABI = [
|
|
237
|
+
{
|
|
238
|
+
constant: true,
|
|
239
|
+
inputs: [],
|
|
240
|
+
name: "name",
|
|
241
|
+
outputs: [
|
|
242
|
+
{
|
|
243
|
+
name: "",
|
|
244
|
+
type: "string"
|
|
245
|
+
}
|
|
246
|
+
],
|
|
247
|
+
payable: false,
|
|
248
|
+
stateMutability: "view",
|
|
249
|
+
type: "function"
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
constant: false,
|
|
253
|
+
inputs: [
|
|
254
|
+
{
|
|
255
|
+
name: "_spender",
|
|
256
|
+
type: "address"
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
name: "_value",
|
|
260
|
+
type: "uint256"
|
|
261
|
+
}
|
|
262
|
+
],
|
|
263
|
+
name: "approve",
|
|
264
|
+
outputs: [
|
|
265
|
+
{
|
|
266
|
+
name: "success",
|
|
267
|
+
type: "bool"
|
|
268
|
+
}
|
|
269
|
+
],
|
|
270
|
+
payable: false,
|
|
271
|
+
stateMutability: "nonpayable",
|
|
272
|
+
type: "function"
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
constant: true,
|
|
276
|
+
inputs: [],
|
|
277
|
+
name: "totalSupply",
|
|
278
|
+
outputs: [
|
|
279
|
+
{
|
|
280
|
+
name: "",
|
|
281
|
+
type: "uint256"
|
|
282
|
+
}
|
|
283
|
+
],
|
|
284
|
+
payable: false,
|
|
285
|
+
stateMutability: "view",
|
|
286
|
+
type: "function"
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
constant: false,
|
|
290
|
+
inputs: [
|
|
291
|
+
{
|
|
292
|
+
name: "_from",
|
|
293
|
+
type: "address"
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
name: "_to",
|
|
297
|
+
type: "address"
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
name: "_value",
|
|
301
|
+
type: "uint256"
|
|
302
|
+
}
|
|
303
|
+
],
|
|
304
|
+
name: "transferFrom",
|
|
305
|
+
outputs: [
|
|
306
|
+
{
|
|
307
|
+
name: "success",
|
|
308
|
+
type: "bool"
|
|
309
|
+
}
|
|
310
|
+
],
|
|
311
|
+
payable: false,
|
|
312
|
+
stateMutability: "nonpayable",
|
|
313
|
+
type: "function"
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
constant: true,
|
|
317
|
+
inputs: [],
|
|
318
|
+
name: "decimals",
|
|
319
|
+
outputs: [
|
|
320
|
+
{
|
|
321
|
+
name: "",
|
|
322
|
+
type: "uint8"
|
|
323
|
+
}
|
|
324
|
+
],
|
|
325
|
+
payable: false,
|
|
326
|
+
stateMutability: "view",
|
|
327
|
+
type: "function"
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
constant: false,
|
|
331
|
+
inputs: [
|
|
332
|
+
{
|
|
333
|
+
name: "_value",
|
|
334
|
+
type: "uint256"
|
|
335
|
+
}
|
|
336
|
+
],
|
|
337
|
+
name: "burn",
|
|
338
|
+
outputs: [],
|
|
339
|
+
payable: false,
|
|
340
|
+
stateMutability: "nonpayable",
|
|
341
|
+
type: "function"
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
constant: true,
|
|
345
|
+
inputs: [
|
|
346
|
+
{
|
|
347
|
+
name: "_owner",
|
|
348
|
+
type: "address"
|
|
349
|
+
}
|
|
350
|
+
],
|
|
351
|
+
name: "balanceOf",
|
|
352
|
+
outputs: [
|
|
353
|
+
{
|
|
354
|
+
name: "balance",
|
|
355
|
+
type: "uint256"
|
|
356
|
+
}
|
|
357
|
+
],
|
|
358
|
+
payable: false,
|
|
359
|
+
stateMutability: "view",
|
|
360
|
+
type: "function"
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
constant: false,
|
|
364
|
+
inputs: [
|
|
365
|
+
{
|
|
366
|
+
name: "_from",
|
|
367
|
+
type: "address"
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
name: "_value",
|
|
371
|
+
type: "uint256"
|
|
372
|
+
}
|
|
373
|
+
],
|
|
374
|
+
name: "burnFrom",
|
|
375
|
+
outputs: [],
|
|
376
|
+
payable: false,
|
|
377
|
+
stateMutability: "nonpayable",
|
|
378
|
+
type: "function"
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
constant: false,
|
|
382
|
+
inputs: [
|
|
383
|
+
{
|
|
384
|
+
name: "_to",
|
|
385
|
+
type: "address[]"
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
name: "_value",
|
|
389
|
+
type: "uint256[]"
|
|
390
|
+
}
|
|
391
|
+
],
|
|
392
|
+
name: "transferArray",
|
|
393
|
+
outputs: [],
|
|
394
|
+
payable: false,
|
|
395
|
+
stateMutability: "nonpayable",
|
|
396
|
+
type: "function"
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
constant: true,
|
|
400
|
+
inputs: [],
|
|
401
|
+
name: "symbol",
|
|
402
|
+
outputs: [
|
|
403
|
+
{
|
|
404
|
+
name: "",
|
|
405
|
+
type: "string"
|
|
406
|
+
}
|
|
407
|
+
],
|
|
408
|
+
payable: false,
|
|
409
|
+
stateMutability: "view",
|
|
410
|
+
type: "function"
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
constant: false,
|
|
414
|
+
inputs: [
|
|
415
|
+
{
|
|
416
|
+
name: "_to",
|
|
417
|
+
type: "address"
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
name: "_value",
|
|
421
|
+
type: "uint256"
|
|
422
|
+
}
|
|
423
|
+
],
|
|
424
|
+
name: "transfer",
|
|
425
|
+
outputs: [
|
|
426
|
+
{
|
|
427
|
+
name: "success",
|
|
428
|
+
type: "bool"
|
|
429
|
+
}
|
|
430
|
+
],
|
|
431
|
+
payable: false,
|
|
432
|
+
stateMutability: "nonpayable",
|
|
433
|
+
type: "function"
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
constant: true,
|
|
437
|
+
inputs: [
|
|
438
|
+
{
|
|
439
|
+
name: "_owner",
|
|
440
|
+
type: "address"
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
name: "_spender",
|
|
444
|
+
type: "address"
|
|
445
|
+
}
|
|
446
|
+
],
|
|
447
|
+
name: "allowance",
|
|
448
|
+
outputs: [
|
|
449
|
+
{
|
|
450
|
+
name: "remaining",
|
|
451
|
+
type: "uint256"
|
|
452
|
+
}
|
|
453
|
+
],
|
|
454
|
+
payable: false,
|
|
455
|
+
stateMutability: "view",
|
|
456
|
+
type: "function"
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
inputs: [
|
|
460
|
+
{
|
|
461
|
+
name: "_tokenName",
|
|
462
|
+
type: "string"
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
name: "_tokenSymbol",
|
|
466
|
+
type: "string"
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
name: "_decimalUnits",
|
|
470
|
+
type: "uint8"
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
name: "_initialAmount",
|
|
474
|
+
type: "uint256"
|
|
475
|
+
}
|
|
476
|
+
],
|
|
477
|
+
payable: false,
|
|
478
|
+
stateMutability: "nonpayable",
|
|
479
|
+
type: "constructor"
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
anonymous: false,
|
|
483
|
+
inputs: [
|
|
484
|
+
{
|
|
485
|
+
indexed: true,
|
|
486
|
+
name: "from",
|
|
487
|
+
type: "address"
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
indexed: true,
|
|
491
|
+
name: "to",
|
|
492
|
+
type: "address"
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
indexed: false,
|
|
496
|
+
name: "value",
|
|
497
|
+
type: "uint256"
|
|
498
|
+
}
|
|
499
|
+
],
|
|
500
|
+
name: "Transfer",
|
|
501
|
+
type: "event"
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
anonymous: false,
|
|
505
|
+
inputs: [
|
|
506
|
+
{
|
|
507
|
+
indexed: true,
|
|
508
|
+
name: "owner",
|
|
509
|
+
type: "address"
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
indexed: true,
|
|
513
|
+
name: "spender",
|
|
514
|
+
type: "address"
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
indexed: false,
|
|
518
|
+
name: "value",
|
|
519
|
+
type: "uint256"
|
|
520
|
+
}
|
|
521
|
+
],
|
|
522
|
+
name: "Approval",
|
|
523
|
+
type: "event"
|
|
524
|
+
}
|
|
525
|
+
];
|
|
526
|
+
|
|
527
|
+
// src/config/networks.ts
|
|
528
|
+
var BSC_NETWORK = {
|
|
529
|
+
chainId: "0x38",
|
|
530
|
+
chainName: "BNB Smart Chain",
|
|
531
|
+
nativeCurrencyName: "BNB",
|
|
532
|
+
nativeCurrencySymbol: "BNB",
|
|
533
|
+
nativeCurrencyDecimals: "18",
|
|
534
|
+
rpcUrl: "https://bsc-dataseed.binance.org/",
|
|
535
|
+
blockExplorerUrl: "https://bscscan.com"
|
|
536
|
+
};
|
|
537
|
+
var BSC_TESTNET_NETWORK = {
|
|
538
|
+
chainId: "0x61",
|
|
539
|
+
chainName: "BNB Smart Chain Testnet",
|
|
540
|
+
nativeCurrencyName: "tBNB",
|
|
541
|
+
nativeCurrencySymbol: "tBNB",
|
|
542
|
+
nativeCurrencyDecimals: "18",
|
|
543
|
+
rpcUrl: "https://data-seed-prebsc-1-s1.bnbchain.org:8545/",
|
|
544
|
+
blockExplorerUrl: "https://testnet.bscscan.com"
|
|
545
|
+
};
|
|
546
|
+
function getDefaultBscConfig(isProd = typeof process !== "undefined" && process.env.NODE_ENV === "production") {
|
|
547
|
+
return isProd ? BSC_NETWORK : BSC_TESTNET_NETWORK;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// src/config/resolve-chain.ts
|
|
551
|
+
var USDT_CONTRACT_ADDRESSES = {
|
|
552
|
+
[BSC_NETWORK.chainId]: "0x55d398326f99059fF775485246999027B3197955",
|
|
553
|
+
[BSC_TESTNET_NETWORK.chainId]: "0x66E972502A34A625828C544a1914E8D8cc2A9dE5"
|
|
554
|
+
};
|
|
555
|
+
function resolveUsdtContractAddress(chainId) {
|
|
556
|
+
const address = USDT_CONTRACT_ADDRESSES[chainId];
|
|
557
|
+
if (!address) {
|
|
558
|
+
throw new Error(`[wallet-sdk] Unsupported chainId for USDT: ${chainId}`);
|
|
559
|
+
}
|
|
560
|
+
return address;
|
|
561
|
+
}
|
|
562
|
+
function resolveChainByMainnet(isMainnet) {
|
|
563
|
+
return isMainnet ? BSC_NETWORK : BSC_TESTNET_NETWORK;
|
|
564
|
+
}
|
|
565
|
+
function resolveUsdtByMainnet(isMainnet) {
|
|
566
|
+
return resolveUsdtContractAddress(resolveChainByMainnet(isMainnet).chainId);
|
|
567
|
+
}
|
|
568
|
+
function resolveChainConfig(chainId) {
|
|
569
|
+
if (chainId === BSC_NETWORK.chainId) {
|
|
570
|
+
return BSC_NETWORK;
|
|
571
|
+
}
|
|
572
|
+
if (chainId === BSC_TESTNET_NETWORK.chainId) {
|
|
573
|
+
return BSC_TESTNET_NETWORK;
|
|
574
|
+
}
|
|
575
|
+
throw new Error(`[wallet-sdk] Unsupported chainId: ${chainId}`);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// src/config/usdt.ts
|
|
579
|
+
var USDT_CONFIG = {
|
|
580
|
+
symbol: "USDT",
|
|
581
|
+
name: "Tether USD",
|
|
582
|
+
decimals: 18,
|
|
583
|
+
contractAbi: ERC20_ABI
|
|
584
|
+
};
|
|
585
|
+
var ERC20_DECIMALS = USDT_CONFIG.decimals;
|
|
586
|
+
|
|
587
|
+
// src/contract.ts
|
|
588
|
+
var ERC20_DECIMALS_ABI = ["function decimals() view returns (uint8)"];
|
|
589
|
+
async function hasDeployedContract(readProvider, address) {
|
|
590
|
+
const code = await readProvider.getCode(address);
|
|
591
|
+
return Boolean(code && code !== "0x");
|
|
592
|
+
}
|
|
593
|
+
async function readErc20Decimals(readProvider, tokenAddress, erc20Abi = ERC20_DECIMALS_ABI, fallback = ERC20_DECIMALS) {
|
|
594
|
+
if (!await hasDeployedContract(readProvider, tokenAddress)) {
|
|
595
|
+
throw new Error(`[wallet-sdk] Token contract not deployed: ${tokenAddress}`);
|
|
596
|
+
}
|
|
597
|
+
try {
|
|
598
|
+
const tokenContract = new Contract(tokenAddress, erc20Abi, readProvider);
|
|
599
|
+
const decimals = Number(await tokenContract.getFunction("decimals").staticCall());
|
|
600
|
+
return Number.isFinite(decimals) && decimals >= 0 ? decimals : fallback;
|
|
601
|
+
} catch {
|
|
602
|
+
return fallback;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// src/init.ts
|
|
607
|
+
var CONFIG_KEY = "__WALLET_SDK_CONFIG__";
|
|
608
|
+
function getStoredConfig() {
|
|
609
|
+
return globalThis[CONFIG_KEY] ?? null;
|
|
610
|
+
}
|
|
611
|
+
function storeConfig(config) {
|
|
612
|
+
globalThis[CONFIG_KEY] = config;
|
|
613
|
+
}
|
|
614
|
+
function buildConfig(options) {
|
|
615
|
+
const chain = resolveChainByMainnet(options.isMainnet);
|
|
616
|
+
return {
|
|
617
|
+
isMainnet: options.isMainnet,
|
|
618
|
+
chainId: chain.chainId,
|
|
619
|
+
chain,
|
|
620
|
+
contract: {
|
|
621
|
+
address: options.contract.address,
|
|
622
|
+
abi: options.contract.abi
|
|
623
|
+
},
|
|
624
|
+
usdt: {
|
|
625
|
+
address: resolveUsdtByMainnet(options.isMainnet),
|
|
626
|
+
abi: USDT_CONFIG.contractAbi,
|
|
627
|
+
symbol: USDT_CONFIG.symbol,
|
|
628
|
+
name: USDT_CONFIG.name,
|
|
629
|
+
decimals: USDT_CONFIG.decimals
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
function initWalletSdk(options) {
|
|
634
|
+
const config = buildConfig(options);
|
|
635
|
+
storeConfig(config);
|
|
636
|
+
return config;
|
|
637
|
+
}
|
|
638
|
+
function getWalletSdkConfig() {
|
|
639
|
+
const config = getStoredConfig();
|
|
640
|
+
if (!config) {
|
|
641
|
+
throw new Error("[wallet-sdk] initWalletSdk() must be called before using the library");
|
|
642
|
+
}
|
|
643
|
+
return config;
|
|
644
|
+
}
|
|
645
|
+
function isWalletSdkInitialized() {
|
|
646
|
+
return getStoredConfig() !== null;
|
|
647
|
+
}
|
|
648
|
+
function resetWalletSdkConfig() {
|
|
649
|
+
delete globalThis[CONFIG_KEY];
|
|
650
|
+
}
|
|
651
|
+
function createJsonRpcReadProvider(rpcUrl, chainIdHex, cache) {
|
|
652
|
+
if (cache?.current) return cache.current;
|
|
653
|
+
const provider = new JsonRpcProvider(rpcUrl, toChainIdDecimal(chainIdHex), {
|
|
654
|
+
staticNetwork: true
|
|
655
|
+
});
|
|
656
|
+
if (cache) {
|
|
657
|
+
cache.current = provider;
|
|
658
|
+
}
|
|
659
|
+
return provider;
|
|
660
|
+
}
|
|
661
|
+
function getReadProviderWithFallback(walletProvider, rpcConfig, fallbackCache) {
|
|
662
|
+
if (walletProvider) return walletProvider;
|
|
663
|
+
if (!rpcConfig.rpcUrl || !rpcConfig.chainId) {
|
|
664
|
+
throw new Error("RPC config is missing");
|
|
665
|
+
}
|
|
666
|
+
return createJsonRpcReadProvider(rpcConfig.rpcUrl, rpcConfig.chainId, fallbackCache);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// src/contract-read.ts
|
|
670
|
+
var readProviderCache = { current: null };
|
|
671
|
+
var walletReadProvider = null;
|
|
672
|
+
function setWalletReadProvider(provider) {
|
|
673
|
+
walletReadProvider = provider;
|
|
674
|
+
}
|
|
675
|
+
function getConfiguredReadProvider() {
|
|
676
|
+
const { chain } = getWalletSdkConfig();
|
|
677
|
+
return getReadProviderWithFallback(walletReadProvider, chain, readProviderCache);
|
|
678
|
+
}
|
|
679
|
+
async function isContractDeployed(contractAddress) {
|
|
680
|
+
return hasDeployedContract(getConfiguredReadProvider(), contractAddress);
|
|
681
|
+
}
|
|
682
|
+
async function getContractDecimals(contractAddress, options) {
|
|
683
|
+
const readProvider = getConfiguredReadProvider();
|
|
684
|
+
if (!await hasDeployedContract(readProvider, contractAddress)) {
|
|
685
|
+
throw new Error(`[wallet-sdk] Contract not deployed: ${contractAddress}`);
|
|
686
|
+
}
|
|
687
|
+
const tokenContract = new Contract(contractAddress, options.abi, readProvider);
|
|
688
|
+
const decimals = await tokenContract.getFunction(options.method).staticCall();
|
|
689
|
+
return Number(decimals);
|
|
690
|
+
}
|
|
691
|
+
async function getContractPrice(contractAddress, options) {
|
|
692
|
+
const readProvider = getConfiguredReadProvider();
|
|
693
|
+
if (!await hasDeployedContract(readProvider, contractAddress)) {
|
|
694
|
+
throw new Error(`[wallet-sdk] Contract not deployed: ${contractAddress}`);
|
|
695
|
+
}
|
|
696
|
+
const tokenContract = new Contract(contractAddress, options.abi, readProvider);
|
|
697
|
+
const price = await tokenContract.getFunction(options.method).staticCall();
|
|
698
|
+
const decimals = await getContractDecimals(contractAddress, { abi: options.abi, method: "decimals" });
|
|
699
|
+
return formatUnits(price, decimals);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// src/network.ts
|
|
703
|
+
function buildEthereumChainParams(config) {
|
|
704
|
+
return {
|
|
705
|
+
chainId: config.chainId,
|
|
706
|
+
chainName: config.chainName,
|
|
707
|
+
nativeCurrency: {
|
|
708
|
+
name: config.nativeCurrencyName,
|
|
709
|
+
symbol: config.nativeCurrencySymbol,
|
|
710
|
+
decimals: Number(config.nativeCurrencyDecimals) || 18
|
|
711
|
+
},
|
|
712
|
+
rpcUrls: [config.rpcUrl],
|
|
713
|
+
blockExplorerUrls: [config.blockExplorerUrl]
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
async function ensureTargetNetwork(options) {
|
|
717
|
+
const currentChainId = await options.getCurrentChainId();
|
|
718
|
+
if (currentChainId.toLowerCase() === options.targetChainId.toLowerCase()) return;
|
|
719
|
+
try {
|
|
720
|
+
await options.switchChain(options.targetChainId);
|
|
721
|
+
} catch (err) {
|
|
722
|
+
const error = err;
|
|
723
|
+
if (error?.code !== 4902 && error?.info?.error?.code !== 4902) {
|
|
724
|
+
throw err;
|
|
725
|
+
}
|
|
726
|
+
await options.addChain(options.chainParams);
|
|
727
|
+
await options.switchChain(options.targetChainId);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// src/wallet/common.ts
|
|
732
|
+
function bindBrowserProvider(eip1193, cache) {
|
|
733
|
+
const nextProvider = getSharedBrowserProvider(eip1193);
|
|
734
|
+
if (cache.value !== nextProvider) {
|
|
735
|
+
cache.value = nextProvider;
|
|
736
|
+
}
|
|
737
|
+
return nextProvider;
|
|
738
|
+
}
|
|
739
|
+
async function requestWallet(eip1193, method, params) {
|
|
740
|
+
return eip1193.request({
|
|
741
|
+
method,
|
|
742
|
+
...params === void 0 ? {} : { params }
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
async function connectWalletAccount(requestAccounts, getProvider, setSigner, markConnected) {
|
|
746
|
+
const accounts = await requestAccounts() || [];
|
|
747
|
+
if (!accounts.length) {
|
|
748
|
+
throw new Error("No accounts returned");
|
|
749
|
+
}
|
|
750
|
+
const sourceProvider = getProvider();
|
|
751
|
+
const nextSigner = await sourceProvider.getSigner();
|
|
752
|
+
const address = await nextSigner.getAddress();
|
|
753
|
+
setSigner(nextSigner);
|
|
754
|
+
markConnected();
|
|
755
|
+
return { address, signer: nextSigner };
|
|
756
|
+
}
|
|
757
|
+
async function syncWalletAccountAddress(getProvider, setSigner, markConnected, nextAddress) {
|
|
758
|
+
const sourceProvider = getProvider();
|
|
759
|
+
const nextSigner = nextAddress ? await sourceProvider.getSigner(nextAddress) : await sourceProvider.getSigner();
|
|
760
|
+
const address = nextAddress || await nextSigner.getAddress();
|
|
761
|
+
setSigner(nextSigner);
|
|
762
|
+
markConnected();
|
|
763
|
+
return { address, signer: nextSigner };
|
|
764
|
+
}
|
|
765
|
+
function clearWalletRuntime(cache) {
|
|
766
|
+
cache.provider.value = null;
|
|
767
|
+
cache.signer.value = null;
|
|
768
|
+
if (cache.selectedProvider) {
|
|
769
|
+
cache.selectedProvider.value = null;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// src/wallet-events.ts
|
|
774
|
+
var eventHandlers = {};
|
|
775
|
+
var providerBridges = /* @__PURE__ */ new WeakMap();
|
|
776
|
+
var walletEventProvider = null;
|
|
777
|
+
var boundProvider = null;
|
|
778
|
+
function hasRegisteredHandlers() {
|
|
779
|
+
return Object.values(eventHandlers).some((handlers) => handlers && handlers.size > 0);
|
|
780
|
+
}
|
|
781
|
+
function dispatch(event, payload) {
|
|
782
|
+
const handlers = eventHandlers[event];
|
|
783
|
+
if (!handlers) return;
|
|
784
|
+
for (const handler of handlers) {
|
|
785
|
+
void handler(payload);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
function unbindProvider(provider) {
|
|
789
|
+
const bridgeHandlers = providerBridges.get(provider);
|
|
790
|
+
if (!bridgeHandlers) return;
|
|
791
|
+
for (const [event, handler] of Object.entries(bridgeHandlers)) {
|
|
792
|
+
provider.removeListener(event, handler);
|
|
793
|
+
}
|
|
794
|
+
providerBridges.delete(provider);
|
|
795
|
+
}
|
|
796
|
+
function syncProviderBinding() {
|
|
797
|
+
if (boundProvider) {
|
|
798
|
+
unbindProvider(boundProvider);
|
|
799
|
+
boundProvider = null;
|
|
800
|
+
}
|
|
801
|
+
if (!walletEventProvider || !hasRegisteredHandlers()) {
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
const bridgeHandlers = {};
|
|
805
|
+
if (eventHandlers.accountsChanged?.size) {
|
|
806
|
+
bridgeHandlers.accountsChanged = (...args) => {
|
|
807
|
+
dispatch("accountsChanged", args[0] ?? []);
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
if (eventHandlers.chainChanged?.size) {
|
|
811
|
+
bridgeHandlers.chainChanged = (...args) => {
|
|
812
|
+
dispatch("chainChanged", String(args[0] ?? ""));
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
for (const [event, handler] of Object.entries(bridgeHandlers)) {
|
|
816
|
+
walletEventProvider.removeListener(event, handler);
|
|
817
|
+
walletEventProvider.on(event, handler);
|
|
818
|
+
}
|
|
819
|
+
providerBridges.set(walletEventProvider, bridgeHandlers);
|
|
820
|
+
boundProvider = walletEventProvider;
|
|
821
|
+
}
|
|
822
|
+
function setWalletEventProvider(provider) {
|
|
823
|
+
walletEventProvider = provider;
|
|
824
|
+
syncProviderBinding();
|
|
825
|
+
}
|
|
826
|
+
function getWalletEventProvider() {
|
|
827
|
+
if (!walletEventProvider) {
|
|
828
|
+
throw new Error("[wallet-sdk] Wallet event provider is not set");
|
|
829
|
+
}
|
|
830
|
+
return walletEventProvider;
|
|
831
|
+
}
|
|
832
|
+
function registerEvent(event, listener) {
|
|
833
|
+
assertBrowser("walletEvents.on");
|
|
834
|
+
if (!eventHandlers[event]) {
|
|
835
|
+
eventHandlers[event] = /* @__PURE__ */ new Set();
|
|
836
|
+
}
|
|
837
|
+
eventHandlers[event].add(listener);
|
|
838
|
+
syncProviderBinding();
|
|
839
|
+
}
|
|
840
|
+
function unregisterEvent(event, listener) {
|
|
841
|
+
if (!isBrowser) return;
|
|
842
|
+
if (listener) {
|
|
843
|
+
eventHandlers[event]?.delete(listener);
|
|
844
|
+
} else {
|
|
845
|
+
eventHandlers[event]?.clear();
|
|
846
|
+
}
|
|
847
|
+
syncProviderBinding();
|
|
848
|
+
}
|
|
849
|
+
function clearEvents() {
|
|
850
|
+
if (!isBrowser) return;
|
|
851
|
+
for (const event of Object.keys(eventHandlers)) {
|
|
852
|
+
eventHandlers[event]?.clear();
|
|
853
|
+
}
|
|
854
|
+
syncProviderBinding();
|
|
855
|
+
}
|
|
856
|
+
var WalletEventsEmitter = class {
|
|
857
|
+
on(event, listener) {
|
|
858
|
+
registerEvent(event, listener);
|
|
859
|
+
}
|
|
860
|
+
off(event, listener) {
|
|
861
|
+
unregisterEvent(event, listener);
|
|
862
|
+
}
|
|
863
|
+
clear() {
|
|
864
|
+
clearEvents();
|
|
865
|
+
}
|
|
866
|
+
};
|
|
867
|
+
var walletEvents = new WalletEventsEmitter();
|
|
868
|
+
|
|
869
|
+
// src/wallet-detection.ts
|
|
870
|
+
function isEthereumProvider() {
|
|
871
|
+
if (!isBrowser) return false;
|
|
872
|
+
return hasMetaMaskInjectedProvider() || hasMetaMaskEip6963Provider();
|
|
873
|
+
}
|
|
874
|
+
function isCoinbaseProvider() {
|
|
875
|
+
if (!isBrowser) return false;
|
|
876
|
+
return hasCoinbaseInjectedProvider();
|
|
877
|
+
}
|
|
878
|
+
function isInjectedProvider() {
|
|
879
|
+
if (!isBrowser) return false;
|
|
880
|
+
return hasAggregatedInjectedProvider();
|
|
881
|
+
}
|
|
882
|
+
function isTronLinkProvider() {
|
|
883
|
+
if (!isBrowser) return false;
|
|
884
|
+
return Boolean(window.tron?.request || window.tronLink);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
export { BSC_NETWORK, BSC_TESTNET_NETWORK, ERC20_ABI, ERC20_DECIMALS, ERC20_DECIMALS_ABI, USDT_CONFIG, USDT_CONTRACT_ADDRESSES, assertBrowser, bindBrowserProvider, buildEthereumChainParams, clearWalletRuntime, connectWalletAccount, createJsonRpcReadProvider, discoverWalletProviders, ensureTargetNetwork, findCoinbaseEip6963Provider, findDiscoveredProvider, findMetaMaskEip6963Provider, getAggregatedInjectedProvider, getConfiguredReadProvider, getContractDecimals, getContractPrice, getDefaultBscConfig, getInjectedEthereumProviders, getReadProviderWithFallback, getSharedBrowserProvider, getWalletEventProvider, getWalletSdkConfig, hasAggregatedInjectedProvider, hasCoinbaseEip6963Provider, hasCoinbaseInjectedProvider, hasDeployedContract, hasMetaMaskEip6963Provider, hasMetaMaskInjectedProvider, hasRecoverableAggregatorProvider, initWalletSdk, isBrowser, isCoinbaseProvider, isContractDeployed, isEthereumProvider, isInjectedProvider, isTronLinkProvider, isWalletSdkInitialized, readErc20Decimals, rememberAggregatorProvider, requestWallet, resetWalletSdkConfig, resolveChainByMainnet, resolveChainConfig, resolveCoinbaseInjectedProvider, resolveMetaMaskInjectedProvider, resolveUsdtByMainnet, resolveUsdtContractAddress, setWalletEventProvider, setWalletReadProvider, syncWalletAccountAddress, toChainIdDecimal, walletEvents };
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@earthworm/wallet-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "EVM wallet utilities for browser DApps",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"main": "./dist/index.js",
|
|
14
|
+
"module": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"ethereum",
|
|
24
|
+
"wallet",
|
|
25
|
+
"eip6963",
|
|
26
|
+
"ethers",
|
|
27
|
+
"dapp"
|
|
28
|
+
],
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"author": "Alex",
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"ethers": "^6.0.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"ethers": "^6.17.0",
|
|
36
|
+
"tsup": "^8.4.0",
|
|
37
|
+
"typescript": "^6.0.3"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsup",
|
|
41
|
+
"dev": "tsup --watch"
|
|
42
|
+
}
|
|
43
|
+
}
|