@mycelium-sdk/core 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/dist/index.js ADDED
@@ -0,0 +1,2481 @@
1
+ // src/wallet/DefaultSmartWallet.ts
2
+ import {
3
+ encodeFunctionData,
4
+ erc20Abi as erc20Abi2,
5
+ pad
6
+ } from "viem";
7
+ import { toCoinbaseSmartAccount } from "viem/account-abstraction";
8
+
9
+ // src/abis/smartWalletFactory.ts
10
+ var smartWalletFactoryAbi = [
11
+ {
12
+ type: "constructor",
13
+ inputs: [
14
+ { name: "implementation_", type: "address", internalType: "address" }
15
+ ],
16
+ stateMutability: "payable"
17
+ },
18
+ {
19
+ type: "function",
20
+ name: "createAccount",
21
+ inputs: [
22
+ { name: "owners", type: "bytes[]", internalType: "bytes[]" },
23
+ { name: "nonce", type: "uint256", internalType: "uint256" }
24
+ ],
25
+ outputs: [
26
+ {
27
+ name: "account",
28
+ type: "address",
29
+ internalType: "contract CoinbaseSmartWallet"
30
+ }
31
+ ],
32
+ stateMutability: "payable"
33
+ },
34
+ {
35
+ type: "function",
36
+ name: "getAddress",
37
+ inputs: [
38
+ { name: "owners", type: "bytes[]", internalType: "bytes[]" },
39
+ { name: "nonce", type: "uint256", internalType: "uint256" }
40
+ ],
41
+ outputs: [{ name: "", type: "address", internalType: "address" }],
42
+ stateMutability: "view"
43
+ },
44
+ {
45
+ type: "function",
46
+ name: "implementation",
47
+ inputs: [],
48
+ outputs: [{ name: "", type: "address", internalType: "address" }],
49
+ stateMutability: "view"
50
+ },
51
+ {
52
+ type: "function",
53
+ name: "initCodeHash",
54
+ inputs: [],
55
+ outputs: [{ name: "", type: "bytes32", internalType: "bytes32" }],
56
+ stateMutability: "view"
57
+ },
58
+ {
59
+ type: "event",
60
+ name: "AccountCreated",
61
+ inputs: [
62
+ {
63
+ name: "account",
64
+ type: "address",
65
+ indexed: true,
66
+ internalType: "address"
67
+ },
68
+ {
69
+ name: "owners",
70
+ type: "bytes[]",
71
+ indexed: false,
72
+ internalType: "bytes[]"
73
+ },
74
+ {
75
+ name: "nonce",
76
+ type: "uint256",
77
+ indexed: false,
78
+ internalType: "uint256"
79
+ }
80
+ ],
81
+ anonymous: false
82
+ },
83
+ { type: "error", name: "ImplementationUndeployed", inputs: [] },
84
+ { type: "error", name: "OwnerRequired", inputs: [] }
85
+ ];
86
+
87
+ // src/constants/addresses.ts
88
+ var smartWalletFactoryAddress = "0xBA5ED110eFDBa3D005bfC882d75358ACBbB85842";
89
+
90
+ // src/tools/TokenBalance.ts
91
+ import { erc20Abi, formatEther, formatUnits } from "viem";
92
+
93
+ // src/constants/tokens.ts
94
+ import { mainnet, unichain, base, baseSepolia, sepolia } from "viem/chains";
95
+ var SUPPORTED_TOKENS = {
96
+ ETH: {
97
+ symbol: "ETH",
98
+ name: "Ethereum",
99
+ decimals: 18,
100
+ addresses: {
101
+ [mainnet.id]: "0x0000000000000000000000000000000000000000",
102
+ [unichain.id]: "0x0000000000000000000000000000000000000000",
103
+ [base.id]: "0x0000000000000000000000000000000000000000",
104
+ [baseSepolia.id]: "0x0000000000000000000000000000000000000000",
105
+ [sepolia.id]: "0x0000000000000000000000000000000000000000"
106
+ }
107
+ },
108
+ USDC: {
109
+ symbol: "USDC",
110
+ name: "USDC",
111
+ decimals: 6,
112
+ addresses: {
113
+ [mainnet.id]: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
114
+ [unichain.id]: "0x078d782b760474a361dda0af3839290b0ef57ad6",
115
+ [baseSepolia.id]: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
116
+ [sepolia.id]: "0xf08A50178dfcDe18524640EA6618a1f965821715",
117
+ [base.id]: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
118
+ }
119
+ }
120
+ };
121
+
122
+ // src/utils/tokens.ts
123
+ function getTokenAddress(symbol, chainId) {
124
+ const token = SUPPORTED_TOKENS[symbol];
125
+ return token?.addresses[chainId] || null;
126
+ }
127
+
128
+ // src/tools/TokenBalance.ts
129
+ async function fetchETHBalance(chainManager, walletAddress) {
130
+ const supportedChain = chainManager.getSupportedChain();
131
+ const publicClient = chainManager.getPublicClient(supportedChain);
132
+ const balance = await publicClient.getBalance({
133
+ address: walletAddress
134
+ });
135
+ return {
136
+ symbol: "ETH",
137
+ totalBalance: balance,
138
+ totalFormattedBalance: formatEther(balance),
139
+ chainBalances: [
140
+ {
141
+ chainId: supportedChain,
142
+ balance,
143
+ formattedBalance: formatEther(balance)
144
+ }
145
+ ]
146
+ };
147
+ }
148
+ async function fetchERC20Balance(chainManager, walletAddress, token) {
149
+ const supportedChain = chainManager.getSupportedChain();
150
+ const balance = await fetchERC20BalanceForChain(
151
+ token,
152
+ supportedChain,
153
+ walletAddress,
154
+ chainManager
155
+ );
156
+ return {
157
+ symbol: token.symbol,
158
+ totalBalance: balance,
159
+ totalFormattedBalance: formatUnits(balance, token.decimals),
160
+ chainBalances: [
161
+ {
162
+ chainId: supportedChain,
163
+ balance,
164
+ formattedBalance: formatUnits(balance, token.decimals)
165
+ }
166
+ ]
167
+ };
168
+ }
169
+ async function fetchERC20BalanceForChain(token, chainId, walletAddress, chainManager) {
170
+ const tokenAddress = getTokenAddress(token.symbol, chainId);
171
+ if (!tokenAddress) {
172
+ throw new Error(`${token.symbol} not supported on chain ${chainId}`);
173
+ }
174
+ const publicClient = chainManager.getPublicClient(chainId);
175
+ if (token.symbol === "ETH") {
176
+ return publicClient.getBalance({
177
+ address: walletAddress
178
+ });
179
+ }
180
+ return publicClient.readContract({
181
+ address: tokenAddress,
182
+ abi: erc20Abi,
183
+ functionName: "balanceOf",
184
+ args: [walletAddress]
185
+ });
186
+ }
187
+
188
+ // src/utils/assets.ts
189
+ import { parseUnits } from "viem";
190
+ function resolveAsset(asset, chainId) {
191
+ if (asset.startsWith("0x")) {
192
+ const address2 = asset;
193
+ for (const [, tokenInfo2] of Object.entries(SUPPORTED_TOKENS)) {
194
+ const tokenAddress = tokenInfo2.addresses[chainId];
195
+ if (tokenAddress && tokenAddress.toLowerCase() === address2.toLowerCase()) {
196
+ return {
197
+ address: tokenAddress,
198
+ symbol: tokenInfo2.symbol,
199
+ decimals: tokenInfo2.decimals
200
+ };
201
+ }
202
+ }
203
+ throw new Error(
204
+ `Unknown asset address: ${address2}. Please use a supported asset symbol like 'usdc' or add the token to SUPPORTED_TOKENS.`
205
+ );
206
+ }
207
+ const normalizedSymbol = asset.toUpperCase();
208
+ const tokenInfo = SUPPORTED_TOKENS[normalizedSymbol];
209
+ if (!tokenInfo) {
210
+ const availableSymbols = Object.keys(SUPPORTED_TOKENS).join(", ");
211
+ throw new Error(`Unsupported asset symbol: ${asset}. Supported assets: ${availableSymbols}`);
212
+ }
213
+ const address = getTokenAddress(normalizedSymbol, chainId);
214
+ if (!address) {
215
+ throw new Error(
216
+ `Asset ${asset} is not supported on chain ${chainId}. Available chains: ${Object.keys(tokenInfo.addresses).join(", ")}`
217
+ );
218
+ }
219
+ return {
220
+ address,
221
+ symbol: tokenInfo.symbol,
222
+ decimals: tokenInfo.decimals
223
+ };
224
+ }
225
+ function parseAssetAmount(amount, decimals) {
226
+ const amountStr = amount.toString();
227
+ return parseUnits(amountStr, decimals);
228
+ }
229
+
230
+ // src/wallet/base/wallets/SmartWallet.ts
231
+ var SmartWallet = class {
232
+ };
233
+
234
+ // src/wallet/DefaultSmartWallet.ts
235
+ var DefaultSmartWallet = class extends SmartWallet {
236
+ /**
237
+ * Creates a smart wallet instance
238
+ *
239
+ * @internal
240
+ * @param owners Owners (addresses or WebAuthn accounts)
241
+ * @param signer Local account used to sign
242
+ * @param chainManager Chain/client manager
243
+ * @param protocolProvider Protocol provider instance (selected upstream)
244
+ * @param deploymentAddress Optional known deployment address
245
+ * @param signerOwnerIndex Optional index of `signer` in owners (default 0)
246
+ * @param nonce Optional salt for deterministic address calc (default 0)
247
+ */
248
+ constructor(owners, signer, chainManager, protocolProvider, coinbaseCDP, deploymentAddress, signerOwnerIndex, nonce) {
249
+ super();
250
+ this.owners = owners;
251
+ this._signer = signer;
252
+ this.signerOwnerIndex = signerOwnerIndex;
253
+ this.deploymentAddress = deploymentAddress;
254
+ this.chainManager = chainManager;
255
+ this.nonce = nonce;
256
+ this.protocolProvider = protocolProvider;
257
+ this.coinbaseCDP = coinbaseCDP;
258
+ }
259
+ /**
260
+ * Returns the signer account for this smart wallet
261
+ *
262
+ * @public
263
+ * @category Account
264
+ * @remarks
265
+ * Used to authorize UserOperations and on-chain transactions
266
+ */
267
+ get signer() {
268
+ return this._signer;
269
+ }
270
+ /**
271
+ * Resolves the smart wallet address
272
+ *
273
+ * @public
274
+ * @category Account
275
+ * @remarks
276
+ * If `deploymentAddress` is known, returns it. Otherwise derives a deterministic address
277
+ * via the factory (`getAddress`) using owners and `nonce` (CREATE2-style)
278
+ *
279
+ * @returns Promise that resolves to the wallet address
280
+ * @throws Error if no supported chains are configured
281
+ * @throws Error if an owner has an invalid type
282
+ */
283
+ async getAddress() {
284
+ if (this.deploymentAddress) {
285
+ return this.deploymentAddress;
286
+ }
287
+ const owners_bytes = this.owners.map((owner) => {
288
+ if (typeof owner === "string") {
289
+ return pad(owner);
290
+ }
291
+ if (owner.type === "webAuthn") {
292
+ return owner.publicKey;
293
+ }
294
+ throw new Error("invalid owner type");
295
+ });
296
+ const supportedChain = this.chainManager.getSupportedChain();
297
+ if (!supportedChain) {
298
+ throw new Error("No supported chains configured");
299
+ }
300
+ const publicClient = this.chainManager.getPublicClient(supportedChain);
301
+ const smartWalletAddress = await publicClient.readContract({
302
+ abi: smartWalletFactoryAbi,
303
+ address: smartWalletFactoryAddress,
304
+ functionName: "getAddress",
305
+ args: [owners_bytes, this.nonce || 0n]
306
+ });
307
+ return smartWalletAddress;
308
+ }
309
+ /**
310
+ * Builds a Coinbase Smart Account for a specific chain
311
+ *
312
+ * @internal
313
+ * @category Account
314
+ * @param chainId Target chain ID
315
+ * @returns Viem Coinbase Smart Account for the given chain
316
+ */
317
+ async getCoinbaseSmartAccount(chainId) {
318
+ return toCoinbaseSmartAccount({
319
+ address: this.deploymentAddress,
320
+ ownerIndex: this.signerOwnerIndex,
321
+ client: this.chainManager.getPublicClient(chainId),
322
+ owners: [this.signer],
323
+ nonce: this.nonce,
324
+ version: "1.1"
325
+ });
326
+ }
327
+ /**
328
+ * Fetches balances (ETH + ERC-20) for a smart account across supported chains
329
+ *
330
+ * @public
331
+ * @category Account
332
+ * @returns Promise resolving to a list of {@link TokenBalance}
333
+ */
334
+ async getBalance() {
335
+ const address = await this.getAddress();
336
+ const tokenBalancePromises = Object.values(SUPPORTED_TOKENS).map(async (token) => {
337
+ return fetchERC20Balance(this.chainManager, address, token);
338
+ });
339
+ const ethBalancePromise = fetchETHBalance(this.chainManager, address);
340
+ return Promise.all([ethBalancePromise, ...tokenBalancePromises]);
341
+ }
342
+ /**
343
+ * Deposits into the selected protocol’s vault to start earning yield
344
+ * @public
345
+ * @category Earn
346
+ * @remarks
347
+ * The protocol is selected on the SDK initialization step
348
+ * @param amount Human-readable amount string
349
+ * @returns Transaction result for the deposit
350
+ */
351
+ async earn(amount) {
352
+ this.chainManager.getSupportedChain();
353
+ const depositTransactionResult = this.protocolProvider.deposit(amount, this);
354
+ return depositTransactionResult;
355
+ }
356
+ /**
357
+ * Reads current deposit balance from the selected protocol’s vault
358
+ * Method to read the current deposit balance from the selected protocol’s vault for a smart account
359
+ *
360
+ * @public
361
+ * @category Earn
362
+ * @returns Vault balance or `null` if nothing deposited
363
+ */
364
+ async getEarnBalance() {
365
+ const depositedVault = await this.protocolProvider.fetchDepositedVaults(this);
366
+ if (!depositedVault) {
367
+ return null;
368
+ }
369
+ const userAddress = await this.getAddress();
370
+ return this.protocolProvider.getBalance(depositedVault, userAddress);
371
+ }
372
+ /**
373
+ * Withdraws from the selected protocol’s vault
374
+ * @public
375
+ * @category Earn
376
+ * @param amount Human-readable amount string
377
+ * @returns Transaction result for the withdrawal
378
+ * @throws Error if the withdrawal fails
379
+ * @throws Error a user didn't deposit anything
380
+ */
381
+ async withdraw(amount) {
382
+ const withdrawTransactionResult = await this.protocolProvider.withdraw(amount, this);
383
+ return withdrawTransactionResult;
384
+ }
385
+ /**
386
+ * Builds a UserOperation and submits via the bundler, then waits for inclusion
387
+
388
+ *
389
+ * @public
390
+ * @category Transactions
391
+ *
392
+ * @param transactionData Transaction details (`to`, `value`, `data`)
393
+ * @param chainId Target chain ID
394
+ * @returns Promise that resolves to the UserOperation hash
395
+ * @throws Error with a readable message if submission or inclusion fails
396
+ */
397
+ async send(transactionData, chainId) {
398
+ try {
399
+ const account = await this.getCoinbaseSmartAccount(chainId);
400
+ const bundlerClient = this.chainManager.getBundlerClient(chainId, account);
401
+ const bump = (x, pct = 40n) => x + x * pct / 100n;
402
+ const gas = await bundlerClient.estimateUserOperationGas({
403
+ account,
404
+ calls: [transactionData]
405
+ });
406
+ const hash = await bundlerClient.sendUserOperation({
407
+ account,
408
+ calls: [transactionData],
409
+ callGasLimit: bump(gas.callGasLimit),
410
+ verificationGasLimit: bump(gas.verificationGasLimit),
411
+ preVerificationGas: bump(gas.preVerificationGas)
412
+ });
413
+ await bundlerClient.waitForUserOperationReceipt({
414
+ hash
415
+ });
416
+ return hash;
417
+ } catch (error) {
418
+ throw new Error(
419
+ `Failed to send transaction: ${error instanceof Error ? error.message : "Unknown error"}`
420
+ );
421
+ }
422
+ }
423
+ /**
424
+ * Builds a UserOperation from several onchain transactions and submits via the bundler, then waits for inclusion
425
+ *
426
+ * @public
427
+ * @category Transactions
428
+ *
429
+ * @param transactionData An array of calls to execute
430
+ * @param chainId Target chain ID
431
+ * @returns Promise that resolves to the UserOperation hash for the batch
432
+ * @throws Error with a readable message if submission or inclusion fails
433
+ */
434
+ async sendBatch(transactionData, chainId) {
435
+ try {
436
+ const account = await this.getCoinbaseSmartAccount(chainId);
437
+ const bundlerClient = this.chainManager.getBundlerClient(chainId, account);
438
+ const bump = (x, pct = 40n) => x + x * pct / 100n;
439
+ const gas = await bundlerClient.estimateUserOperationGas({
440
+ account,
441
+ calls: transactionData
442
+ });
443
+ const hash = await bundlerClient.sendUserOperation({
444
+ account,
445
+ calls: transactionData,
446
+ callGasLimit: bump(gas.callGasLimit),
447
+ verificationGasLimit: bump(gas.verificationGasLimit),
448
+ preVerificationGas: bump(gas.preVerificationGas)
449
+ });
450
+ await bundlerClient.waitForUserOperationReceipt({
451
+ hash
452
+ });
453
+ return hash;
454
+ } catch (error) {
455
+ throw new Error(
456
+ `Failed to send transaction: ${error instanceof Error ? error.message : "Unknown error"}`
457
+ );
458
+ }
459
+ }
460
+ /**
461
+ * Funds the smart wallet with the specified amount of the specified token via Coinbase CDP on-ramp service
462
+ *
463
+ * @public
464
+ * @category Ramp
465
+ *
466
+ * @remarks
467
+ * If Coinbase CDP is not initialized, the method will throw an error. For more details, visit @see {@link https://docs.cdp.coinbase.com/api-reference/v2/rest-api/onramp/create-an-onramp-session}
468
+ *
469
+ * @param amount Amount of token that a user wants to purchase and top up his account with (e.g., `"100"`, `"1.5"`)
470
+ * @param redirectUrl URL to redirect to after the on-ramp is complete. It's required to be a valid URL
471
+ * @param purchaseCurrency Purchase currency (e.g., `"USDC"`, `"ETH"`)
472
+ * @param paymentCurrency Payment currency (e.g., `"USD"`, `"EUR"`)
473
+ * @param paymentMethod Payment method (e.g., `"CARD"`)
474
+ * @param chain Chain name (e.g., `"base"`)
475
+ * @param country Country code (e.g., `"US"`)
476
+ *
477
+ *
478
+ * @returns @see {@link OnRampUrlResponse}
479
+ */
480
+ async topUp(amount, redirectUrl, purchaseCurrency, paymentCurrency, paymentMethod, country) {
481
+ if (!this.coinbaseCDP) {
482
+ throw new Error(
483
+ "Coinbase CDP is not initialized. Please, provide the configuration in the SDK initialization"
484
+ );
485
+ }
486
+ const address = await this.getAddress();
487
+ const onRampLink = await this.coinbaseCDP.getOnRampLink(
488
+ address,
489
+ redirectUrl,
490
+ amount,
491
+ purchaseCurrency,
492
+ paymentCurrency,
493
+ paymentMethod,
494
+ country
495
+ );
496
+ return onRampLink;
497
+ }
498
+ /**
499
+ * Cashout token from smart wallet to fiat currency via Coinbase CDP off-ramp service
500
+ *
501
+ * @public
502
+ * @category Ramp
503
+ *
504
+ * @remarks
505
+ * If Coinbase CDP is not initialized, the method will throw an error. For more details, visit @see {@link https://docs.cdp.coinbase.com/api-reference/rest-api/onramp-offramp/create-sell-quote}
506
+ *
507
+ * @param country Country code (e.g., `"US"`)
508
+ * @param paymentMethod Payment method (e.g., `"CARD"`). To get the ful list, visit ""
509
+ * @param redirectUrl URL to redirect to after the off-ramp is complete. It's required to be a valid URL
510
+ * @param sellAmount Amount of token that a user wants to sell (e.g., `"100"`, `"1.5"`)
511
+ * @param cashoutCurrency Cashout currency (e.g., `"USD"`, `"EUR"`). To get the ful list, visit ""
512
+ * @param sellCurrency Sell currency (e.g., `"USDC"`, `"ETH"`). To get the ful list, visit ""
513
+ *
514
+ *
515
+ * @returns @see {@link OffRampUrlResponse}
516
+ */
517
+ async cashOut(country, paymentMethod, redirectUrl, sellAmount, cashoutCurrency, sellCurrency) {
518
+ if (!this.coinbaseCDP) {
519
+ throw new Error(
520
+ "Coinbase CDP is not initialized. Please, provide the configuration in the SDK initialization"
521
+ );
522
+ }
523
+ const address = await this.getAddress();
524
+ const offRampLink = await this.coinbaseCDP.getOffRampLink(
525
+ address,
526
+ country,
527
+ paymentMethod,
528
+ redirectUrl,
529
+ sellAmount,
530
+ cashoutCurrency,
531
+ sellCurrency
532
+ );
533
+ return offRampLink;
534
+ }
535
+ /**
536
+ * Send tokens from a smart account to another address
537
+ *
538
+ * @public
539
+ * @category Transactions
540
+ *
541
+ * @param amount Human-readable amount (e.g., `1.5`)
542
+ * @param asset Asset symbol (e.g., `"usdc"`, `"eth"`) or token address
543
+ * @param recipientAddress Destination address
544
+ * @returns Transaction data suitable for inclusion in a UserOperation/call
545
+ * @throws Error if `recipientAddress` is missing, `amount` ≤ 0, or asset cannot be resolved
546
+ */
547
+ async sendTokens(amount, asset, recipientAddress) {
548
+ if (!recipientAddress) {
549
+ throw new Error("Recipient address is required");
550
+ }
551
+ if (amount <= 0) {
552
+ throw new Error("Amount must be greater than 0");
553
+ }
554
+ const chainId = this.chainManager.getSupportedChain();
555
+ if (asset.toLowerCase() === "eth") {
556
+ const parsedAmount2 = parseAssetAmount(amount, 18);
557
+ return {
558
+ to: recipientAddress,
559
+ value: parsedAmount2,
560
+ data: "0x"
561
+ };
562
+ }
563
+ const resolvedAsset = resolveAsset(asset, chainId);
564
+ const parsedAmount = parseAssetAmount(amount, resolvedAsset.decimals);
565
+ const transferData = encodeFunctionData({
566
+ abi: erc20Abi2,
567
+ functionName: "transfer",
568
+ args: [recipientAddress, parsedAmount]
569
+ });
570
+ return {
571
+ to: resolvedAsset.address,
572
+ value: 0n,
573
+ data: transferData
574
+ };
575
+ }
576
+ };
577
+
578
+ // src/tools/ChainManager.ts
579
+ import { createPublicClient, http } from "viem";
580
+ import {
581
+ createBundlerClient
582
+ } from "viem/account-abstraction";
583
+
584
+ // src/constants/chains.ts
585
+ import { base as base2, baseSepolia as baseSepolia2 } from "viem/chains";
586
+ var CHAINS_MAP = {
587
+ base: base2,
588
+ baseSepolia: baseSepolia2
589
+ };
590
+ var SUPPORTED_CHAIN_IDS = Object.values(CHAINS_MAP).map((c) => c.id);
591
+
592
+ // src/utils/chains.ts
593
+ import "viem";
594
+ import * as viemChains from "viem/chains";
595
+ var chainById = Object.values(viemChains).reduce(
596
+ (acc, maybeChain) => {
597
+ if (maybeChain && typeof maybeChain === "object" && "id" in maybeChain && typeof maybeChain.id === "number" && "name" in maybeChain) {
598
+ const chain = maybeChain;
599
+ acc[chain.id] = chain;
600
+ }
601
+ return acc;
602
+ },
603
+ {}
604
+ );
605
+
606
+ // src/types/logger.ts
607
+ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
608
+ LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
609
+ LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
610
+ LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
611
+ LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
612
+ return LogLevel2;
613
+ })(LogLevel || {});
614
+
615
+ // src/tools/Logger.ts
616
+ var Logger = class _Logger {
617
+ /**
618
+ * Create a new logger instance
619
+ * @param logLevel Initial log level, defaults to DEBUG
620
+ */
621
+ constructor(logLevel = 0 /* DEBUG */) {
622
+ this.logs = [];
623
+ this.maxLogs = 1e3;
624
+ this.logLevel = logLevel;
625
+ }
626
+ /**
627
+ * Get singleton instance of the logger
628
+ * @param logLevel Optional log level to initialize if instance not yet created
629
+ * @returns Logger instance
630
+ */
631
+ static getInstance(logLevel) {
632
+ if (!_Logger.instance) {
633
+ _Logger.instance = new _Logger(logLevel);
634
+ }
635
+ return _Logger.instance;
636
+ }
637
+ /** Set the log level */
638
+ setLogLevel(level) {
639
+ this.logLevel = level;
640
+ }
641
+ /** Get the current log level */
642
+ getLogLevel() {
643
+ return this.logLevel;
644
+ }
645
+ /** Internal check if a message should be logged */
646
+ shouldLog(level) {
647
+ return level >= this.logLevel;
648
+ }
649
+ /** Format log message into a readable string */
650
+ formatMessage(level, message, data, context) {
651
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
652
+ const levelName = LogLevel[level];
653
+ const contextStr = context ? `[${context}]` : "";
654
+ return `${timestamp} ${levelName}${contextStr}: ${message}`;
655
+ }
656
+ /** Add a log entry and output to console */
657
+ addLog(level, message, data, context) {
658
+ if (!this.shouldLog(level)) {
659
+ return;
660
+ }
661
+ const logEntry = {
662
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
663
+ level,
664
+ message,
665
+ data,
666
+ context
667
+ };
668
+ this.logs.push(logEntry);
669
+ if (this.logs.length > this.maxLogs) {
670
+ this.logs = this.logs.slice(-this.maxLogs);
671
+ }
672
+ const formattedMessage = this.formatMessage(level, message, data, context);
673
+ switch (level) {
674
+ case 0 /* DEBUG */:
675
+ console.debug(`\u{1F50D} ${formattedMessage}`, data || "");
676
+ break;
677
+ case 1 /* INFO */:
678
+ console.info(`\u2139\uFE0F ${formattedMessage}`, data || "");
679
+ break;
680
+ case 2 /* WARN */:
681
+ console.warn(`\u26A0\uFE0F ${formattedMessage}`, data || "");
682
+ break;
683
+ case 3 /* ERROR */:
684
+ console.error(`\u274C ${formattedMessage}`, data || "");
685
+ break;
686
+ }
687
+ }
688
+ /** Log a debug message */
689
+ debug(message, data, context) {
690
+ this.addLog(0 /* DEBUG */, message, data, context);
691
+ }
692
+ /** Log an info message */
693
+ info(message, data, context) {
694
+ this.addLog(1 /* INFO */, message, data, context);
695
+ }
696
+ /** Log a warning message */
697
+ warn(message, data, context) {
698
+ this.addLog(2 /* WARN */, message, data, context);
699
+ }
700
+ /** Log an error message */
701
+ error(message, data, context) {
702
+ this.addLog(3 /* ERROR */, message, data, context);
703
+ }
704
+ /** Get all logs */
705
+ getLogs() {
706
+ return [...this.logs];
707
+ }
708
+ /** Get logs by level */
709
+ getLogsByLevel(level) {
710
+ return this.logs.filter((log) => log.level === level);
711
+ }
712
+ /** Clear all logs */
713
+ clearLogs() {
714
+ this.logs = [];
715
+ }
716
+ /** Export logs as a JSON string */
717
+ exportLogs() {
718
+ return JSON.stringify(this.logs, null, 2);
719
+ }
720
+ /** Set maximum number of logs to retain in memory */
721
+ setMaxLogs(max) {
722
+ this.maxLogs = max;
723
+ }
724
+ };
725
+ var logger = Logger.getInstance();
726
+
727
+ // src/tools/ChainManager.ts
728
+ var ChainManager = class {
729
+ /**
730
+ * Initializes the chain manager with the given configuration
731
+ *
732
+ * @internal
733
+ * @param chains Configuration object for a supported chain
734
+ */
735
+ constructor(chains) {
736
+ this.chainConfigs = chains;
737
+ this.publicClient = this.createPublicClient(chains);
738
+ this.chainNames = CHAINS_MAP;
739
+ }
740
+ /**
741
+ * Utility to validate if a string is a valid HTTP(S) URL
742
+ *
743
+ * @internal
744
+ * @param url Candidate URL
745
+ * @returns True if valid, false otherwise
746
+ */
747
+ isValidUrl(url) {
748
+ return /^https?:\/\/.+$/.test(url);
749
+ }
750
+ /**
751
+ * Returns a {@link PublicClient} for the given chain ID
752
+ *
753
+ * @internal
754
+ * @category Clients
755
+ * @param chainId Target chain ID
756
+ * @returns {@link PublicClient} instance
757
+ * @throws Error if client is not configured
758
+ */
759
+ getPublicClient(chainId) {
760
+ const client = this.publicClient;
761
+ if (!client) {
762
+ throw new Error(`No public client configured for chain ID: ${chainId}`);
763
+ }
764
+ return client;
765
+ }
766
+ /**
767
+ * Returns a {@link BundlerClient} for the given chain ID
768
+ *
769
+ * @internal
770
+ * @category Clients
771
+ * @param chainId Target chain ID
772
+ * @param account SmartAccount to bind to the bundler client
773
+ * @returns {@link BundlerClient} instance
774
+ * @throws Error if no bundler URL is configured
775
+ */
776
+ getBundlerClient(chainId, account) {
777
+ const rpcUrl = this.getRpcUrl(chainId);
778
+ const bundlerUrl = this.getBundlerUrl(chainId);
779
+ if (!bundlerUrl) {
780
+ throw new Error(`No bundler URL configured for chain ID: ${chainId}`);
781
+ }
782
+ logger.info("Public client setup:", { bundlerUrl, chainId }, "ChainManager");
783
+ const client = createPublicClient({
784
+ chain: this.getChain(chainId),
785
+ transport: http(rpcUrl)
786
+ });
787
+ return createBundlerClient({
788
+ account,
789
+ client,
790
+ transport: http(bundlerUrl),
791
+ chain: this.getChain(chainId)
792
+ });
793
+ }
794
+ /**
795
+ * Returns the RPC URL for the given chain ID
796
+ *
797
+ * @internal
798
+ * @category URLs
799
+ * @param chainId Target chain ID
800
+ * @returns RPC URL string
801
+ * @throws Error if chain config is missing or URL is invalid
802
+ */
803
+ getRpcUrl(chainId) {
804
+ const chainConfig = this.chainConfigs;
805
+ if (!chainConfig) {
806
+ throw new Error(`No chain config found for chain ID: ${chainId}`);
807
+ }
808
+ if (!this.isValidUrl(chainConfig.rpcUrl)) {
809
+ throw new Error(`Invalid RPC URL for chain ID: ${chainId}`);
810
+ }
811
+ return chainConfig.rpcUrl;
812
+ }
813
+ /**
814
+ * Returns the bundler URL for the given chain ID
815
+ *
816
+ * @internal
817
+ * @category URLs
818
+ * @param chainId Target chain ID
819
+ * @returns Bundler URL string
820
+ * @throws Error if chain config is missing or URL is invalid
821
+ */
822
+ getBundlerUrl(chainId) {
823
+ const chainConfig = this.chainConfigs;
824
+ if (!chainConfig) {
825
+ throw new Error(`No chain config found for chain ID: ${chainId}`);
826
+ }
827
+ if (!this.isValidUrl(chainConfig.bundlerUrl)) {
828
+ throw new Error(`Invalid bundler URL for chain ID: ${chainId}`);
829
+ }
830
+ return chainConfig.bundlerUrl;
831
+ }
832
+ /**
833
+ * Returns the {@link Chain} object for the given chain ID
834
+ *
835
+ * @internal
836
+ * @category Info
837
+ * @param chainId Target chain ID
838
+ * @returns Chain metadata
839
+ * @throws Error if chain is not found
840
+ */
841
+ getChain(chainId) {
842
+ const chain = chainById[chainId];
843
+ if (!chain) {
844
+ throw new Error(`Chain not found for ID: ${chainId}`);
845
+ }
846
+ return chain;
847
+ }
848
+ /**
849
+ * Returns the currently configured supported chain ID
850
+ *
851
+ * @internal
852
+ * @category Info
853
+ * @returns Supported chain ID
854
+ */
855
+ getSupportedChain() {
856
+ return this.chainConfigs.chainId;
857
+ }
858
+ /**
859
+ * Creates a {@link PublicClient} for a chain
860
+ *
861
+ * @internal
862
+ * @category Clients
863
+ * @param chain Chain configuration
864
+ * @returns PublicClient instance
865
+ */
866
+ createPublicClient(chain) {
867
+ const chainObject = chainById[chain.chainId];
868
+ const client = createPublicClient({
869
+ chain: chainObject,
870
+ transport: http(chain.rpcUrl)
871
+ });
872
+ return client;
873
+ }
874
+ };
875
+
876
+ // src/wallet/WalletNamespace.ts
877
+ var WalletNamespace = class {
878
+ /**
879
+ * Creates a wallet namespace to manage embedded and smart wallets
880
+ * @param provider Unified provider that composes embedded & smart providers
881
+ */
882
+ constructor(provider) {
883
+ this.provider = provider;
884
+ }
885
+ /**
886
+ * Direct access to the underlying embedded wallet provider
887
+ *
888
+ * @public
889
+ * @category Providers
890
+ * @remarks
891
+ * Useful when you need advanced functionality beyond the unified namespace. By default, you should use the unified namespace
892
+ *
893
+ * @returns The configured embedded wallet provider instance
894
+ */
895
+ get embeddedWalletProvider() {
896
+ return this.provider.embeddedWalletProvider;
897
+ }
898
+ /**
899
+ * Direct access to the underlying smart wallet provider
900
+ *
901
+ * @public
902
+ * @category Providers
903
+ * @remarks
904
+ * Useful when you need advanced functionality beyond the unified namespace. By default, you should use the unified namespace
905
+ *
906
+ * @returns The configured smart wallet provider instance
907
+ */
908
+ get smartWalletProvider() {
909
+ return this.provider.smartWalletProvider;
910
+ }
911
+ /**
912
+ * Creates an embedded wallet
913
+ *
914
+ * @public
915
+ * @category Creation
916
+ * @remarks
917
+ * Thin wrapper around the embedded wallet provider’s `createWallet`
918
+ *
919
+ * @returns Promise that resolves to the newly created {@link EmbeddedWallet}
920
+ */
921
+ async createEmbeddedWallet() {
922
+ return this.provider.createEmbeddedWallet();
923
+ }
924
+ /**
925
+ * Creates a smart wallet (you provide signer and owners)
926
+ *
927
+ * @public
928
+ * @category Creation
929
+ * @remarks
930
+ * Use this when you already control a signer (e.g., `LocalAccount`) and want to
931
+ * create a smart wallet without creating an embedded wallet
932
+ *
933
+ * @param params Smart wallet creation parameters
934
+ * @param params.owners Owners for the smart wallet (addresses or WebAuthn public keys)
935
+ * @param params.signer Local account used for signing transactions
936
+ * @param params.nonce Optional nonce/salt for deterministic address generation (defaults to 0)
937
+ * @returns Promise that resolves to the created {@link SmartWallet}
938
+ */
939
+ async createSmartWallet(params) {
940
+ return this.provider.createSmartWallet(params);
941
+ }
942
+ /**
943
+ * Creates a smart wallet with an embedded wallet as signer
944
+ *
945
+ * @public
946
+ * @category Creation
947
+ * @remarks
948
+ * Creates an embedded wallet first, inserts its address into the owners array,
949
+ * and uses its account as the signer for the smart wallet
950
+ *
951
+ * @param params Optional creation parameters
952
+ * @param params.owners Optional additional owners. The embedded wallet address is inserted at the specified index
953
+ * @param params.embeddedWalletIndex Optional index at which to insert the embedded wallet address (defaults to end)
954
+ * @param params.nonce Optional nonce/salt for deterministic address generation (defaults to 0)
955
+ * @returns Promise that resolves to the created {@link SmartWallet}
956
+ */
957
+ async createWalletWithEmbeddedSigner(params) {
958
+ return this.provider.createWalletWithEmbeddedSigner(params);
959
+ }
960
+ /**
961
+ * Gets a smart wallet using an embedded wallet as the signer
962
+ *
963
+ * @public
964
+ * @category Retrieval
965
+ * @remarks
966
+ * Looks up an embedded wallet by `walletId` and uses it as signer
967
+ * If neither `walletAddress` nor `deploymentOwners` is provided, defaults to using
968
+ * the embedded wallet as the single owner for deterministic address calculation
969
+ *
970
+ * @param params Retrieval parameters
971
+ * @param params.walletId Embedded wallet ID used to locate the signer wallet
972
+ * @param params.deploymentOwners Optional original deployment owners used for address calculation
973
+ * @param params.signerOwnerIndex Index of the signer within the **current** owners set (defaults to 0)
974
+ * @param params.walletAddress Optional explicit smart wallet address (skips calculation)
975
+ * @param params.nonce Optional nonce used during original creation
976
+ * @returns Promise that resolves to the {@link SmartWallet}
977
+ * @throws Error if the embedded wallet cannot be found
978
+ */
979
+ async getSmartWalletWithEmbeddedSigner(params) {
980
+ return this.provider.getSmartWalletWithEmbeddedSigner(params);
981
+ }
982
+ /**
983
+ * Gets an existing embedded wallet by ID
984
+ *
985
+ * @public
986
+ * @category Retrieval
987
+ * @param params Retrieval parameters
988
+ * @param params.walletId Embedded wallet ID to retrieve
989
+ * @returns Promise that resolves to the {@link EmbeddedWallet} (or `null/undefined` per provider contract)
990
+ */
991
+ async getEmbeddedWallet(params) {
992
+ return this.provider.getEmbeddedWallet(params);
993
+ }
994
+ /**
995
+ * Gets a smart wallet using a provided signer
996
+ *
997
+ * @public
998
+ * @category Retrieval
999
+ * @remarks
1000
+ * Use when you already control a signer. Requires either:
1001
+ * - `walletAddress`, or
1002
+ * - `deploymentOwners` (+ optional `nonce`) to derive the address
1003
+ *
1004
+ * @param params Retrieval parameters
1005
+ * @param params.signer Signer (local account)
1006
+ * @param params.deploymentOwners Original deployment owners (required if `walletAddress` is not provided)
1007
+ * @param params.signerOwnerIndex Index of the signer within the **current** owners set (defaults to 0)
1008
+ * @param params.walletAddress Explicit smart wallet address (skips calculation)
1009
+ * @param params.nonce Optional nonce used during original creation
1010
+ * @returns Promise that resolves to the {@link SmartWallet}
1011
+ * @throws Error if neither `walletAddress` nor `deploymentOwners` is provided
1012
+ */
1013
+ async getSmartWallet(params) {
1014
+ return this.provider.getSmartWallet(params);
1015
+ }
1016
+ };
1017
+
1018
+ // src/index.ts
1019
+ import { base as base3 } from "viem/chains";
1020
+
1021
+ // src/wallet/providers/DefaultSmartWalletProvider.ts
1022
+ import { pad as pad2 } from "viem";
1023
+ import "viem/account-abstraction";
1024
+
1025
+ // src/wallet/base/providers/SmartWalletProvider.ts
1026
+ var SmartWalletProvider = class {
1027
+ };
1028
+
1029
+ // src/wallet/providers/DefaultSmartWalletProvider.ts
1030
+ var DefaultSmartWalletProvider = class extends SmartWalletProvider {
1031
+ /**
1032
+ * Initializes the smart wallet provider
1033
+ *
1034
+ * @internal
1035
+ * @param chainManager Manager for chains and viem clients
1036
+ * @param protocol Selected protocol descriptor that exposes an initialized instance
1037
+ */
1038
+ constructor(chainManager, protocol, coinbaseCDP) {
1039
+ super();
1040
+ this.chainManager = chainManager;
1041
+ this.protocolProvider = protocol.instance;
1042
+ this.coinbaseCDP = coinbaseCDP;
1043
+ }
1044
+ /**
1045
+ * Creates a new smart wallet instance that deploys on first use
1046
+ *
1047
+ * @internal
1048
+ * @category Creation
1049
+ * @remarks
1050
+ * Address is derived deterministically from `owners` and `nonce`
1051
+ *
1052
+ * @param params Parameters for wallet creation
1053
+ * @param params.owners Owners as EVM addresses or WebAuthn owners
1054
+ * @param params.signer Local account used to sign UserOperations and transactions
1055
+ * @param params.nonce Optional salt for deterministic address calculation, default 0
1056
+ * @returns Promise that resolves to a {@link DefaultSmartWallet} instance
1057
+ */
1058
+ async createWallet(params) {
1059
+ const { owners, signer, nonce } = params;
1060
+ return new DefaultSmartWallet(
1061
+ owners,
1062
+ signer,
1063
+ this.chainManager,
1064
+ this.protocolProvider,
1065
+ this.coinbaseCDP,
1066
+ void 0,
1067
+ void 0,
1068
+ nonce
1069
+ );
1070
+ }
1071
+ /**
1072
+ * Predicts the deterministic smart wallet address for the given owners and nonce
1073
+ *
1074
+ * @internal
1075
+ * @category Addressing
1076
+ * @remarks
1077
+ * Uses the smart wallet factory `getAddress` to compute the CREATE2 address
1078
+ *
1079
+ * @param params Address prediction parameters
1080
+ * @param params.owners Owners as EVM addresses or WebAuthn owners
1081
+ * @param params.nonce Optional salt for deterministic address calculation, default 0
1082
+ * @returns Promise that resolves to the predicted wallet address
1083
+ * @throws Error if no supported chains are configured
1084
+ * @throws Error if an owner has an invalid type
1085
+ */
1086
+ async getWalletAddress(params) {
1087
+ const { owners, nonce = 0n } = params;
1088
+ const owners_bytes = owners.map((owner) => {
1089
+ if (typeof owner === "string") {
1090
+ return pad2(owner);
1091
+ }
1092
+ if (owner.type === "webAuthn") {
1093
+ return owner.publicKey;
1094
+ }
1095
+ throw new Error("invalid owner type");
1096
+ });
1097
+ const supportedChain = this.chainManager.getSupportedChain();
1098
+ if (!supportedChain) {
1099
+ throw new Error("No supported chains configured");
1100
+ }
1101
+ const publicClient = this.chainManager.getPublicClient(supportedChain);
1102
+ const smartWalletAddress = await publicClient.readContract({
1103
+ abi: smartWalletFactoryAbi,
1104
+ address: smartWalletFactoryAddress,
1105
+ functionName: "getAddress",
1106
+ args: [owners_bytes, nonce]
1107
+ });
1108
+ return smartWalletAddress;
1109
+ }
1110
+ /**
1111
+ * Returns a smart wallet instance for an already deployed address
1112
+ *
1113
+ * @internal
1114
+ * @category Retrieval
1115
+ * @remarks
1116
+ * Use when you already know the deployment address and want an instance bound to a signer
1117
+ *
1118
+ * @param params Retrieval parameters
1119
+ * @param params.walletAddress Deployed smart wallet address
1120
+ * @param params.signer Local account to operate the wallet
1121
+ * @param params.ownerIndex Optional index of `signer` within the current owners set, default 0
1122
+ * @returns A {@link DefaultSmartWallet} instance
1123
+ */
1124
+ getWallet(params) {
1125
+ const { walletAddress, signer, ownerIndex } = params;
1126
+ return new DefaultSmartWallet(
1127
+ [signer.address],
1128
+ signer,
1129
+ this.chainManager,
1130
+ this.protocolProvider,
1131
+ this.coinbaseCDP,
1132
+ walletAddress,
1133
+ ownerIndex
1134
+ );
1135
+ }
1136
+ /**
1137
+ * Funds a wallet via a faucet if supported by the selected chain
1138
+ *
1139
+ * @internal
1140
+ * @category Funding
1141
+ * @remarks
1142
+ * Placeholder for testnet faucet integration
1143
+ *
1144
+ * @returns Future transaction hash or provider response
1145
+ */
1146
+ fundViaFaucet() {
1147
+ }
1148
+ };
1149
+
1150
+ // src/wallet/WalletProvider.ts
1151
+ var WalletProvider = class {
1152
+ /**
1153
+ * Creates a unified wallet provider
1154
+ *
1155
+ * @internal
1156
+ * @param embeddedWalletProvider Provider for embedded wallet operations
1157
+ * @param smartWalletProvider Provider for smart wallet operations
1158
+ */
1159
+ constructor(embeddedWalletProvider, smartWalletProvider) {
1160
+ this.embeddedWalletProvider = embeddedWalletProvider;
1161
+ this.smartWalletProvider = smartWalletProvider;
1162
+ }
1163
+ /**
1164
+ * Creates an embedded wallet
1165
+ *
1166
+ * @internal
1167
+ * @remarks
1168
+ * Thin wrapper around the embedded wallet provider’s `createWallet`
1169
+ *
1170
+ * @returns Promise that resolves to the newly created {@link EmbeddedWallet}
1171
+ */
1172
+ async createEmbeddedWallet() {
1173
+ return this.embeddedWalletProvider.createWallet();
1174
+ }
1175
+ /**
1176
+ * Creates a smart wallet (you provide signer and owners)
1177
+ *
1178
+ * @internal
1179
+ * @remarks
1180
+ * Use when you already control a signer and want to create a smart
1181
+ * wallet without creating an embedded wallet
1182
+ *
1183
+ * @param params Smart wallet creation parameters
1184
+ * @param params.owners Owners array for the smart wallet (EVM addresses or WebAuthn owners)
1185
+ * @param params.signer Signer (local account) used for transactions
1186
+ * @param params.nonce Optional salt/nonce for deterministic address calculation (defaults to 0)
1187
+ * @returns Promise that resolves to the created {@link SmartWallet}
1188
+ */
1189
+ async createSmartWallet(params) {
1190
+ const { owners, signer, nonce } = params;
1191
+ return this.smartWalletProvider.createWallet({
1192
+ owners,
1193
+ signer,
1194
+ nonce
1195
+ });
1196
+ }
1197
+ /**
1198
+ * Creates a smart wallet with an embedded wallet as signer
1199
+ *
1200
+ * @internal
1201
+ * @remarks
1202
+ * Creates an embedded wallet first, then inserts its address into the owners array
1203
+ * and uses its account as the signer for the smart wallet. Default SDK option, embedded wallets manager is necessary to be provided
1204
+ *
1205
+ * @param params Optional creation parameters
1206
+ * @param params.owners Optional additional owners. The embedded wallet address is inserted at the specified index
1207
+ * @param params.embeddedWalletIndex Optional index where the embedded wallet address should be inserted (defaults to the end)
1208
+ * @param params.nonce Optional salt/nonce for deterministic address calculation (defaults to 0)
1209
+ * @returns Promise that resolves to the created {@link SmartWallet}
1210
+ */
1211
+ async createWalletWithEmbeddedSigner(params) {
1212
+ const { owners: ownersParam, embeddedWalletIndex, nonce } = params || {};
1213
+ const embeddedWallet = await this.embeddedWalletProvider.createWallet();
1214
+ const account = await embeddedWallet.account();
1215
+ let owners;
1216
+ if (ownersParam) {
1217
+ owners = [...ownersParam];
1218
+ const insertIndex = embeddedWalletIndex ?? owners.length;
1219
+ owners.splice(insertIndex, 0, embeddedWallet.address);
1220
+ } else {
1221
+ owners = [embeddedWallet.address];
1222
+ }
1223
+ return this.smartWalletProvider.createWallet({
1224
+ owners,
1225
+ signer: account,
1226
+ nonce
1227
+ });
1228
+ }
1229
+ /**
1230
+ * Gets a smart wallet using an embedded wallet as the signer
1231
+ *
1232
+ * @internal
1233
+ * @remarks
1234
+ * Fetches an embedded wallet by `walletId` and uses it as signer.
1235
+ * If neither `walletAddress` nor `deploymentOwners` is provided, defaults to using
1236
+ * the embedded wallet as the single owner for deterministic address calculation
1237
+ *
1238
+ * @param params Retrieval parameters
1239
+ * @param params.walletId Embedded wallet ID used to locate the signer wallet
1240
+ * @param params.deploymentOwners Optional original deployment owners used for address calculation
1241
+ * @param params.signerOwnerIndex Index of the signer in the **current** owners set (defaults to 0)
1242
+ * @param params.walletAddress Optional explicit smart wallet address (skips calculation)
1243
+ * @param params.nonce Optional nonce used during original creation
1244
+ * @returns Promise that resolves to the {@link SmartWallet}
1245
+ * @throws Error if the embedded wallet cannot be found
1246
+ */
1247
+ async getSmartWalletWithEmbeddedSigner(params) {
1248
+ const { walletId, deploymentOwners, walletAddress } = params;
1249
+ const embeddedWallet = await this.embeddedWalletProvider.getWallet({
1250
+ walletId
1251
+ });
1252
+ if (!embeddedWallet) {
1253
+ throw new Error("Embedded wallet not found");
1254
+ }
1255
+ const account = await embeddedWallet.account();
1256
+ const finalDeploymentOwners = deploymentOwners || (walletAddress ? void 0 : [embeddedWallet.address]);
1257
+ return this.getSmartWallet({
1258
+ signer: account,
1259
+ ...params,
1260
+ deploymentOwners: finalDeploymentOwners
1261
+ });
1262
+ }
1263
+ /**
1264
+ * Gets an existing embedded wallet by ID
1265
+ *
1266
+ * @internal
1267
+ * @param params Retrieval parameters
1268
+ * @param params.walletId Embedded wallet ID
1269
+ * @returns Promise that resolves to the {@link EmbeddedWallet}, or `null/undefined` per provider’s contract
1270
+ */
1271
+ async getEmbeddedWallet(params) {
1272
+ const { walletId } = params;
1273
+ return this.embeddedWalletProvider.getWallet({
1274
+ walletId
1275
+ });
1276
+ }
1277
+ /**
1278
+ * Gets a smart wallet using a provided signer
1279
+ *
1280
+ * @internal
1281
+ * @remarks
1282
+ * Use when you already control a `LocalAccount` signer
1283
+ * Requires either:
1284
+ * - `walletAddress`, or
1285
+ * - `deploymentOwners` (+ optional `nonce`) to deterministically derive the address
1286
+ *
1287
+ * @param params Retrieval parameters
1288
+ * @param params.signer Signer (local account)
1289
+ * @param params.deploymentOwners Original deployment owners (required if `walletAddress` is not provided)
1290
+ * @param params.signerOwnerIndex Index of `signer` within the **current** owners set (defaults to 0)
1291
+ * @param params.walletAddress Explicit smart wallet address (skips calculation)
1292
+ * @param params.nonce Optional nonce used during original creation
1293
+ * @returns Promise that resolves to the {@link SmartWallet}
1294
+ * @throws Error if neither `walletAddress` nor `deploymentOwners` is provided
1295
+ */
1296
+ async getSmartWallet(params) {
1297
+ const {
1298
+ signer,
1299
+ deploymentOwners,
1300
+ signerOwnerIndex,
1301
+ walletAddress: walletAddressParam,
1302
+ nonce
1303
+ } = params;
1304
+ if (!walletAddressParam && !deploymentOwners) {
1305
+ try {
1306
+ throw new Error(
1307
+ "Either walletAddress or deploymentOwners array must be provided to locate the smart wallet"
1308
+ );
1309
+ } catch (error) {
1310
+ logger.error("Error getting smart wallet", error, "WalletProvider");
1311
+ throw new Error(
1312
+ "Either walletAddress or deploymentOwners array must be provided to locate the smart wallet"
1313
+ );
1314
+ }
1315
+ }
1316
+ const ownerIndex = signerOwnerIndex ?? 0;
1317
+ const walletAddress = walletAddressParam || await this.smartWalletProvider.getWalletAddress({
1318
+ // Safe to use ! since we validated above
1319
+ owners: deploymentOwners,
1320
+ nonce
1321
+ });
1322
+ return this.smartWalletProvider.getWallet({
1323
+ walletAddress,
1324
+ signer,
1325
+ ownerIndex
1326
+ });
1327
+ }
1328
+ };
1329
+
1330
+ // src/wallet/providers/PrivyEmbeddedWalletProvider.ts
1331
+ import { getAddress } from "viem";
1332
+
1333
+ // src/wallet/PrivyWallet.ts
1334
+ import { createViemAccount } from "@privy-io/server-auth/viem";
1335
+ import {
1336
+ createWalletClient,
1337
+ http as http2
1338
+ } from "viem";
1339
+ import { unichain as unichain2 } from "viem/chains";
1340
+
1341
+ // src/wallet/base/wallets/EmbeddedWallet.ts
1342
+ var EmbeddedWallet = class {
1343
+ /**
1344
+ * Creates an embedded wallet instance
1345
+ *
1346
+ * @internal
1347
+ * @param address Ethereum address of the wallet
1348
+ * @param walletId Optional provider-specific identifier
1349
+ */
1350
+ constructor(address, walletId) {
1351
+ this.address = address;
1352
+ this.walletId = walletId;
1353
+ }
1354
+ };
1355
+
1356
+ // src/wallet/PrivyWallet.ts
1357
+ var PrivyWallet = class extends EmbeddedWallet {
1358
+ /**
1359
+ * Creates a Privy-backed embedded wallet
1360
+ *
1361
+ * @internal
1362
+ * @param privyClient Privy client used to access wallet and signing APIs
1363
+ * @param walletId Privy wallet identifier
1364
+ * @param address Wallet EVM address
1365
+ * @param chainManager Chain and client manager
1366
+ */
1367
+ constructor(privyClient, walletId, address, chainManager) {
1368
+ super(address, walletId);
1369
+ this.privyClient = privyClient;
1370
+ this.walletId = walletId;
1371
+ this.chainManager = chainManager;
1372
+ }
1373
+ /**
1374
+ * Returns a viem-compatible {@link LocalAccount} for this Privy wallet
1375
+ *
1376
+ * @internal
1377
+ * @category Accounts
1378
+ * @remarks
1379
+ * Uses Privy’s signing infra under the hood while exposing the standard viem interface
1380
+ *
1381
+ * @returns Promise that resolves to a {@link LocalAccount}
1382
+ * @throws Error if wallet retrieval or account construction fails
1383
+ */
1384
+ async account() {
1385
+ const account = await createViemAccount({
1386
+ walletId: this.walletId,
1387
+ address: this.address,
1388
+ privy: this.privyClient
1389
+ });
1390
+ return account;
1391
+ }
1392
+ /**
1393
+ * Creates a viem {@link WalletClient} for a given chain
1394
+ *
1395
+ * @internal
1396
+ * @category Accounts
1397
+ * @param chainId Target chain ID
1398
+ * @returns Promise that resolves to a {@link WalletClient}
1399
+ * @throws Error if the chain is unsupported or client creation fails
1400
+ */
1401
+ async walletClient(chainId) {
1402
+ const account = await this.account();
1403
+ return createWalletClient({
1404
+ account,
1405
+ chain: this.chainManager.getChain(chainId),
1406
+ transport: http2(this.chainManager.getRpcUrl(chainId))
1407
+ });
1408
+ }
1409
+ /**
1410
+ * Signs a transaction without broadcasting it
1411
+ *
1412
+ * @internal
1413
+ * @category Signing
1414
+ * @param transactionData Transaction payload to sign
1415
+ * @returns Promise that resolves to a signed transaction hex string
1416
+ */
1417
+ async sign(transactionData) {
1418
+ return await this.signOnly(transactionData);
1419
+ }
1420
+ /**
1421
+ * Produces a signed transaction using Privy’s wallet API without sending it
1422
+ *
1423
+ * @internal
1424
+ * @category Signing
1425
+ * @remarks
1426
+ * Estimates gas, fees, and nonce to build a complete EIP-1559 transaction
1427
+ * Per Privy docs, if any gas field is set, all must be set
1428
+ *
1429
+ * @param transactionData Transaction payload to sign
1430
+ * @returns Promise that resolves to a signed transaction string
1431
+ * @throws Error if signing fails
1432
+ */
1433
+ async signOnly(transactionData) {
1434
+ try {
1435
+ const privyWallet = await this.privyClient.walletApi.getWallet({
1436
+ id: this.walletId
1437
+ });
1438
+ const publicClient = this.chainManager.getPublicClient(unichain2.id);
1439
+ const gasLimit = await publicClient.estimateGas({
1440
+ account: privyWallet.address,
1441
+ to: transactionData.to,
1442
+ data: transactionData.data,
1443
+ value: BigInt(transactionData.value || 0)
1444
+ });
1445
+ const feeData = await publicClient.estimateFeesPerGas();
1446
+ const nonce = await publicClient.getTransactionCount({
1447
+ address: privyWallet.address,
1448
+ blockTag: "pending"
1449
+ // Use pending to get the next nonce including any pending txs
1450
+ });
1451
+ const txParams = {
1452
+ to: transactionData.to,
1453
+ data: transactionData.data,
1454
+ value: transactionData.value,
1455
+ chainId: 130,
1456
+ // Unichain
1457
+ type: 2,
1458
+ // EIP-1559
1459
+ gasLimit: `0x${gasLimit.toString(16)}`,
1460
+ maxFeePerGas: `0x${(feeData.maxFeePerGas || BigInt(1e9)).toString(16)}`,
1461
+ // fallback to 1 gwei
1462
+ maxPriorityFeePerGas: `0x${(feeData.maxPriorityFeePerGas || BigInt(1e8)).toString(16)}`,
1463
+ // fallback to 0.1 gwei
1464
+ nonce: `0x${nonce.toString(16)}`
1465
+ // Explicitly provide the correct nonce
1466
+ };
1467
+ logger.info(
1468
+ "Complete tx params: ",
1469
+ {
1470
+ txParamsType: txParams.type,
1471
+ txParamsNonce: nonce,
1472
+ txParamsLimit: gasLimit,
1473
+ txParamsMaxFee: feeData.maxFeePerGas || "fallback",
1474
+ txParamsPriority: feeData.maxPriorityFeePerGas || "fallback"
1475
+ },
1476
+ "PrivyWallet"
1477
+ );
1478
+ const response = await this.privyClient.walletApi.ethereum.signTransaction({
1479
+ walletId: this.walletId,
1480
+ transaction: txParams
1481
+ });
1482
+ return response.signedTransaction;
1483
+ } catch (error) {
1484
+ throw new Error(
1485
+ `Failed to sign transaction for wallet ${this.walletId}: ${error instanceof Error ? error.message : "Unknown error"}`
1486
+ );
1487
+ }
1488
+ }
1489
+ /**
1490
+ * Broadcasts a previously-signed transaction
1491
+ *
1492
+ * @internal
1493
+ * @category Sending
1494
+ * @param signedTransaction Signed transaction hex
1495
+ * @param publicClient Viem public client to send the transaction
1496
+ * @returns Promise that resolves to the transaction {@link Hash}
1497
+ * @throws Error if submission fails
1498
+ */
1499
+ async send(signedTransaction, publicClient) {
1500
+ try {
1501
+ const hash = await publicClient.sendRawTransaction({
1502
+ serializedTransaction: signedTransaction
1503
+ });
1504
+ return hash;
1505
+ } catch (error) {
1506
+ throw new Error(
1507
+ `Failed to send transaction: ${error instanceof Error ? error.message : "Unknown error"}`
1508
+ );
1509
+ }
1510
+ }
1511
+ };
1512
+
1513
+ // src/wallet/base/providers/EmbeddedWalletProvider.ts
1514
+ var EmbeddedWalletProvider = class {
1515
+ };
1516
+
1517
+ // src/wallet/providers/PrivyEmbeddedWalletProvider.ts
1518
+ var PrivyEmbeddedWalletProvider = class extends EmbeddedWalletProvider {
1519
+ /**
1520
+ * Creates a new Privy-backed embedded wallet provider
1521
+ *
1522
+ * @internal
1523
+ * @param privyClient Privy client instance
1524
+ * @param chainManager Chain and client manager
1525
+ */
1526
+ constructor(privyClient, chainManager) {
1527
+ super();
1528
+ this.privy = privyClient;
1529
+ this.chainManager = chainManager;
1530
+ }
1531
+ /**
1532
+ * Creates a new wallet using Privy’s wallet API
1533
+ *
1534
+ * @internal
1535
+ * @category Creation
1536
+ * @returns Promise that resolves to a new {@link PrivyWallet} instance
1537
+ * @throws Error if wallet creation fails
1538
+ */
1539
+ async createWallet() {
1540
+ try {
1541
+ const wallet = await this.privy.walletApi.createWallet({
1542
+ chainType: "ethereum"
1543
+ });
1544
+ const walletInstance = new PrivyWallet(
1545
+ this.privy,
1546
+ wallet.id,
1547
+ getAddress(wallet.address),
1548
+ this.chainManager
1549
+ );
1550
+ return walletInstance;
1551
+ } catch (error) {
1552
+ logger.error("Failed to create wallet: ", error, "PrivyEmbeddedWalletProvider");
1553
+ throw new Error(`Failed to create wallet: ${error}`);
1554
+ }
1555
+ }
1556
+ /**
1557
+ * Retrieves a wallet by its ID via Privy
1558
+ *
1559
+ * @internal
1560
+ * @category Retrieval
1561
+ * @param params Parameters containing walletId
1562
+ * @returns Promise that resolves to a {@link PrivyWallet} instance
1563
+ * @throws Error if the wallet cannot be retrieved
1564
+ */
1565
+ async getWallet(params) {
1566
+ try {
1567
+ const wallet = await this.privy.walletApi.getWallet({
1568
+ id: params.walletId
1569
+ });
1570
+ const walletInstance = new PrivyWallet(
1571
+ this.privy,
1572
+ wallet.id,
1573
+ getAddress(wallet.address),
1574
+ this.chainManager
1575
+ );
1576
+ return walletInstance;
1577
+ } catch {
1578
+ throw new Error(`Failed to get wallet with id: ${params.walletId}`);
1579
+ }
1580
+ }
1581
+ /**
1582
+ * Retrieves all wallets from Privy with optional filtering and pagination
1583
+ *
1584
+ * @internal
1585
+ * @category Retrieval
1586
+ * @param options Optional filtering and pagination parameters
1587
+ * @returns Promise that resolves to an array of {@link PrivyWallet} instances
1588
+ * @throws Error if wallets cannot be retrieved
1589
+ */
1590
+ async getAllWallets(options) {
1591
+ try {
1592
+ const response = await this.privy.walletApi.getWallets({
1593
+ limit: options?.limit,
1594
+ cursor: options?.cursor
1595
+ });
1596
+ return response.data.map((wallet) => {
1597
+ const walletInstance = new PrivyWallet(
1598
+ this.privy,
1599
+ wallet.id,
1600
+ getAddress(wallet.address),
1601
+ this.chainManager
1602
+ );
1603
+ return walletInstance;
1604
+ });
1605
+ } catch {
1606
+ throw new Error("Failed to retrieve wallets");
1607
+ }
1608
+ }
1609
+ };
1610
+
1611
+ // src/index.ts
1612
+ import { PrivyClient } from "@privy-io/server-auth";
1613
+
1614
+ // src/protocols/base/BaseProtocol.ts
1615
+ import {
1616
+ erc20Abi as erc20Abi3,
1617
+ createWalletClient as createWalletClient2,
1618
+ http as http3,
1619
+ parseGwei
1620
+ } from "viem";
1621
+ var BaseProtocol = class {
1622
+ /**
1623
+ * Ensure the protocol has been initialized
1624
+ * @throws Error if `init()` has not been called
1625
+ */
1626
+ ensureInitialized() {
1627
+ if (!this.chainManager) {
1628
+ throw new Error("Protocol must be initialized before use. Call init() first.");
1629
+ }
1630
+ }
1631
+ /**
1632
+ * Approve a token for protocol use
1633
+ * @param tokenAddress Token address
1634
+ * @param spenderAddress Spender address
1635
+ * @param amount Allowance amount in wei
1636
+ * @param chainId Target chain ID
1637
+ * @param account Account authorizing the approval
1638
+ * @returns Transaction hash
1639
+ */
1640
+ async approveToken(tokenAddress, spenderAddress, amount, chainId, account) {
1641
+ this.ensureInitialized();
1642
+ const walletClient = createWalletClient2({
1643
+ account,
1644
+ chain: this.chainManager.getChain(chainId),
1645
+ transport: http3(this.chainManager.getRpcUrl(chainId))
1646
+ });
1647
+ const hash = await walletClient.writeContract({
1648
+ address: tokenAddress,
1649
+ abi: erc20Abi3,
1650
+ functionName: "approve",
1651
+ args: [spenderAddress, amount],
1652
+ gas: 100000n,
1653
+ maxFeePerGas: parseGwei("20"),
1654
+ maxPriorityFeePerGas: parseGwei("2")
1655
+ });
1656
+ return hash;
1657
+ }
1658
+ /**
1659
+ * Check token allowance for a spender
1660
+ * @param tokenAddress Token address
1661
+ * @param spenderAddress Spender address
1662
+ * @param walletAddress Wallet address granting allowance
1663
+ * @param chainId Target chain ID
1664
+ * @returns Current allowance amount
1665
+ */
1666
+ async checkAllowance(tokenAddress, spenderAddress, walletAddress, chainId) {
1667
+ this.ensureInitialized();
1668
+ const publicClient = this.chainManager.getPublicClient(chainId);
1669
+ return await publicClient.readContract({
1670
+ address: tokenAddress,
1671
+ abi: erc20Abi3,
1672
+ functionName: "allowance",
1673
+ args: [walletAddress, spenderAddress]
1674
+ });
1675
+ }
1676
+ };
1677
+
1678
+ // src/protocols/implementations/SparkProtocol.ts
1679
+ import {
1680
+ encodeFunctionData as encodeFunctionData2,
1681
+ erc20Abi as erc20Abi4,
1682
+ formatUnits as formatUnits2,
1683
+ parseUnits as parseUnits2
1684
+ } from "viem";
1685
+
1686
+ // src/abis/protocols/spark.ts
1687
+ var SPARK_VAULT_ABI = [
1688
+ { type: "function", name: "asset", stateMutability: "view", inputs: [], outputs: [{ type: "address" }] },
1689
+ { type: "function", name: "totalAssets", stateMutability: "view", inputs: [], outputs: [{ type: "uint256" }] },
1690
+ { type: "function", name: "convertToShares", stateMutability: "view", inputs: [{ name: "assets", type: "uint256" }], outputs: [{ type: "uint256" }] },
1691
+ { type: "function", name: "convertToAssets", stateMutability: "view", inputs: [{ name: "shares", type: "uint256" }], outputs: [{ type: "uint256" }] },
1692
+ { type: "function", name: "balanceOf", stateMutability: "view", inputs: [{ name: "owner", type: "address" }], outputs: [{ type: "uint256" }] },
1693
+ { type: "function", name: "deposit", stateMutability: "nonpayable", inputs: [
1694
+ { name: "assets", type: "uint256" },
1695
+ { name: "receiver", type: "address" }
1696
+ ], outputs: [{ type: "uint256" }] },
1697
+ { type: "function", name: "withdraw", stateMutability: "nonpayable", inputs: [
1698
+ { name: "assets", type: "uint256" },
1699
+ { name: "receiver", type: "address" },
1700
+ { name: "owner", type: "address" }
1701
+ ], outputs: [{ type: "uint256" }] },
1702
+ { type: "function", name: "redeem", stateMutability: "nonpayable", inputs: [
1703
+ { name: "shares", type: "uint256" },
1704
+ { name: "receiver", type: "address" },
1705
+ { name: "owner", type: "address" }
1706
+ ], outputs: [{ type: "uint256" }] }
1707
+ ];
1708
+ var SPARK_SSR_ORACLE_ABI = [
1709
+ { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" },
1710
+ { "inputs": [], "name": "AccessControlBadConfirmation", "type": "error" },
1711
+ { "inputs": [{ "internalType": "address", "name": "account", "type": "address" }, { "internalType": "bytes32", "name": "neededRole", "type": "bytes32" }], "name": "AccessControlUnauthorizedAccount", "type": "error" },
1712
+ { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "bytes32", "name": "role", "type": "bytes32" }, { "indexed": true, "internalType": "bytes32", "name": "previousAdminRole", "type": "bytes32" }, { "indexed": true, "internalType": "bytes32", "name": "newAdminRole", "type": "bytes32" }], "name": "RoleAdminChanged", "type": "event" },
1713
+ { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "bytes32", "name": "role", "type": "bytes32" }, { "indexed": true, "internalType": "address", "name": "account", "type": "address" }, { "indexed": true, "internalType": "address", "name": "sender", "type": "address" }], "name": "RoleGranted", "type": "event" },
1714
+ { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "bytes32", "name": "role", "type": "bytes32" }, { "indexed": true, "internalType": "address", "name": "account", "type": "address" }, { "indexed": true, "internalType": "address", "name": "sender", "type": "address" }], "name": "RoleRevoked", "type": "event" },
1715
+ { "anonymous": false, "inputs": [{ "indexed": false, "internalType": "uint256", "name": "maxSSR", "type": "uint256" }], "name": "SetMaxSSR", "type": "event" },
1716
+ { "anonymous": false, "inputs": [{ "components": [{ "internalType": "uint96", "name": "ssr", "type": "uint96" }, { "internalType": "uint120", "name": "chi", "type": "uint120" }, { "internalType": "uint40", "name": "rho", "type": "uint40" }], "indexed": false, "internalType": "struct ISSROracle.SUSDSData", "name": "nextData", "type": "tuple" }], "name": "SetSUSDSData", "type": "event" },
1717
+ { "inputs": [], "name": "DATA_PROVIDER_ROLE", "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], "stateMutability": "view", "type": "function" },
1718
+ { "inputs": [], "name": "DEFAULT_ADMIN_ROLE", "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], "stateMutability": "view", "type": "function" },
1719
+ { "inputs": [], "name": "getAPR", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" },
1720
+ { "inputs": [], "name": "getChi", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" },
1721
+ { "inputs": [{ "internalType": "uint256", "name": "timestamp", "type": "uint256" }], "name": "getConversionRate", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" },
1722
+ { "inputs": [], "name": "getConversionRate", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" },
1723
+ { "inputs": [{ "internalType": "uint256", "name": "timestamp", "type": "uint256" }], "name": "getConversionRateBinomialApprox", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" },
1724
+ { "inputs": [], "name": "getConversionRateBinomialApprox", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" },
1725
+ { "inputs": [{ "internalType": "uint256", "name": "timestamp", "type": "uint256" }], "name": "getConversionRateLinearApprox", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" },
1726
+ { "inputs": [], "name": "getConversionRateLinearApprox", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" },
1727
+ { "inputs": [], "name": "getRho", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" },
1728
+ { "inputs": [{ "internalType": "bytes32", "name": "role", "type": "bytes32" }], "name": "getRoleAdmin", "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], "stateMutability": "view", "type": "function" },
1729
+ { "inputs": [], "name": "getSSR", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" },
1730
+ { "inputs": [], "name": "getSUSDSData", "outputs": [{ "components": [{ "internalType": "uint96", "name": "ssr", "type": "uint96" }, { "internalType": "uint120", "name": "chi", "type": "uint120" }, { "internalType": "uint40", "name": "rho", "type": "uint40" }], "internalType": "struct ISSROracle.SUSDSData", "name": "", "type": "tuple" }], "stateMutability": "view", "type": "function" },
1731
+ { "inputs": [{ "internalType": "bytes32", "name": "role", "type": "bytes32" }, { "internalType": "address", "name": "account", "type": "address" }], "name": "grantRole", "outputs": [], "stateMutability": "nonpayable", "type": "function" },
1732
+ { "inputs": [{ "internalType": "bytes32", "name": "role", "type": "bytes32" }, { "internalType": "address", "name": "account", "type": "address" }], "name": "hasRole", "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "view", "type": "function" },
1733
+ { "inputs": [], "name": "maxSSR", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" },
1734
+ { "inputs": [{ "internalType": "bytes32", "name": "role", "type": "bytes32" }, { "internalType": "address", "name": "callerConfirmation", "type": "address" }], "name": "renounceRole", "outputs": [], "stateMutability": "nonpayable", "type": "function" },
1735
+ { "inputs": [{ "internalType": "bytes32", "name": "role", "type": "bytes32" }, { "internalType": "address", "name": "account", "type": "address" }], "name": "revokeRole", "outputs": [], "stateMutability": "nonpayable", "type": "function" },
1736
+ { "inputs": [{ "internalType": "uint256", "name": "_maxSSR", "type": "uint256" }], "name": "setMaxSSR", "outputs": [], "stateMutability": "nonpayable", "type": "function" },
1737
+ { "inputs": [{ "components": [{ "internalType": "uint96", "name": "ssr", "type": "uint96" }, { "internalType": "uint120", "name": "chi", "type": "uint120" }, { "internalType": "uint40", "name": "rho", "type": "uint40" }], "internalType": "struct ISSROracle.SUSDSData", "name": "nextData", "type": "tuple" }], "name": "setSUSDSData", "outputs": [], "stateMutability": "nonpayable", "type": "function" },
1738
+ { "inputs": [{ "internalType": "bytes4", "name": "interfaceId", "type": "bytes4" }], "name": "supportsInterface", "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "view", "type": "function" }
1739
+ ];
1740
+
1741
+ // src/protocols/constants/spark.ts
1742
+ var SPARK_SSR_ORACLE_ADDRESS = "0x65d946e533748A998B1f0E430803e39A6388f7a1";
1743
+ var SPARK_VAULT = [
1744
+ {
1745
+ id: "sUSDC",
1746
+ chain: "base",
1747
+ vaultAddress: "0x3128a0f7f0ea68e7b7c9b00afa7e41045828e858",
1748
+ depositTokenAddress: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
1749
+ depositTokenDecimals: 6,
1750
+ depositTokenSymbol: "USDC",
1751
+ earnTokenAddress: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
1752
+ earnTokenDecimals: 18,
1753
+ earnTokenSymbol: "sUSDC",
1754
+ metadata: {}
1755
+ }
1756
+ ];
1757
+ var SECONDS_PER_YEAR = 60 * 60 * 24 * 365;
1758
+ var RAY = BigInt("1000000000000000000000000000");
1759
+
1760
+ // src/protocols/implementations/SparkProtocol.ts
1761
+ var SparkProtocol = class extends BaseProtocol {
1762
+ constructor() {
1763
+ super(...arguments);
1764
+ this.allVaults = [];
1765
+ }
1766
+ /**
1767
+ * Initialize the Spark protocol with the provided chain manager
1768
+ * @param chainManager Chain manager instance used for network operations
1769
+ */
1770
+ async init(chainManager) {
1771
+ this.chainManager = chainManager;
1772
+ this.selectedChainId = chainManager.getSupportedChain();
1773
+ this.publicClient = chainManager.getPublicClient(this.selectedChainId);
1774
+ this.allVaults = this.getVaults();
1775
+ }
1776
+ /**
1777
+ * Get the SSR (Sky Saving Rate) of the Spark protocol
1778
+ * @remarks
1779
+ * The parameter ius necessary to calculate the APY of a vault
1780
+ * @returns
1781
+ */
1782
+ async getSSR() {
1783
+ if (!this.publicClient) {
1784
+ throw new Error("Public client not initialized");
1785
+ }
1786
+ const ssrRaw = await this.publicClient.readContract({
1787
+ address: SPARK_SSR_ORACLE_ADDRESS,
1788
+ abi: SPARK_SSR_ORACLE_ABI,
1789
+ functionName: "getSSR"
1790
+ });
1791
+ const ssr = Number(ssrRaw) / Number(RAY);
1792
+ return ssr;
1793
+ }
1794
+ /**
1795
+ * Get the APY of the Spark protocol
1796
+ * @remarks
1797
+ * Calculation based on the formula from the documentation:
1798
+ * https://docs.spark.fi/dev/integration-guides/susds-lending-market#rates
1799
+ * @returns The APY of the Spark protocol in percentage
1800
+ */
1801
+ async getAPY() {
1802
+ const ssr = await this.getSSR();
1803
+ const apy = Math.exp(Math.log(ssr) * SECONDS_PER_YEAR) - 1;
1804
+ return Number((apy * 100).toFixed(2));
1805
+ }
1806
+ /**
1807
+ *
1808
+ * Get all vault info from a Spark protocol
1809
+ * @returns The list of vaults
1810
+ */
1811
+ getVaults() {
1812
+ return SPARK_VAULT;
1813
+ }
1814
+ /**
1815
+ * Get the best available Spark vault
1816
+ * @returns The top-ranked Spark vault
1817
+ * @throws Error if no vaults found
1818
+ */
1819
+ async getBestVault() {
1820
+ if (this.allVaults.length === 0) {
1821
+ throw new Error("No vaults found");
1822
+ }
1823
+ const selectedVault = this.allVaults[0];
1824
+ selectedVault.metadata.apy = await this.getAPY();
1825
+ return selectedVault;
1826
+ }
1827
+ /**
1828
+ * Fetch a vault where the user previously deposited funds
1829
+ * @param smartWallet Smart wallet to inspect
1830
+ * @returns The vault with user deposits, or null if none found
1831
+ */
1832
+ async fetchDepositedVaults(smartWallet) {
1833
+ let depositedVault = void 0;
1834
+ const userAddress = await smartWallet.getAddress();
1835
+ for (const vault of this.allVaults) {
1836
+ const balance = await this.getBalance(vault, userAddress);
1837
+ if (parseInt(balance.depositedAmount) > 0) {
1838
+ depositedVault = vault;
1839
+ }
1840
+ }
1841
+ if (depositedVault) {
1842
+ depositedVault.metadata.apy = await this.getAPY();
1843
+ }
1844
+ logger.info("Deposited vaults:", { depositedVault }, "SparkProtocol");
1845
+ return depositedVault || null;
1846
+ }
1847
+ /**
1848
+ * Deposit funds into a Spark vault
1849
+ * @param amount Amount to deposit (human-readable)
1850
+ * @param smartWallet Smart wallet instance to use
1851
+ * @returns Transaction result with hash
1852
+ */
1853
+ async deposit(amount, smartWallet) {
1854
+ const depositedVault = await this.fetchDepositedVaults(smartWallet);
1855
+ let vaultInfoToDeposit;
1856
+ logger.info("Previously deposited vault:", { depositedVault }, "SparkProtocol");
1857
+ if (depositedVault) {
1858
+ vaultInfoToDeposit = depositedVault;
1859
+ } else {
1860
+ vaultInfoToDeposit = await this.getBestVault();
1861
+ logger.info("Best vault that found:", { bestVault: vaultInfoToDeposit }, "SparkProtocol");
1862
+ }
1863
+ const owner = await smartWallet.getAddress();
1864
+ const assets = parseUnits2(amount, vaultInfoToDeposit.depositTokenDecimals);
1865
+ logger.info("Raw deposit amount:", { amount, assets }, "SparkProtocol");
1866
+ const allowance = await this.checkAllowance(
1867
+ vaultInfoToDeposit.depositTokenAddress,
1868
+ vaultInfoToDeposit.vaultAddress,
1869
+ owner,
1870
+ this.selectedChainId
1871
+ );
1872
+ logger.info("Current vault contract allowance:", { allowance }, "SparkProtocol");
1873
+ const ops = [];
1874
+ if (allowance < assets) {
1875
+ ops.push({
1876
+ to: vaultInfoToDeposit.depositTokenAddress,
1877
+ data: encodeFunctionData2({
1878
+ abi: erc20Abi4,
1879
+ functionName: "approve",
1880
+ args: [vaultInfoToDeposit.vaultAddress, assets]
1881
+ })
1882
+ });
1883
+ }
1884
+ ops.push({
1885
+ to: vaultInfoToDeposit.vaultAddress,
1886
+ data: encodeFunctionData2({
1887
+ abi: SPARK_VAULT_ABI,
1888
+ functionName: "deposit",
1889
+ args: [assets, owner]
1890
+ })
1891
+ });
1892
+ const hash = await smartWallet.sendBatch(ops, this.selectedChainId);
1893
+ return { success: true, hash };
1894
+ }
1895
+ /**
1896
+ * Withdraw funds from a Spark vault
1897
+ * @param amountInUnderlying Amount in base token units (or undefined to withdraw all)
1898
+ * @param smartWallet Smart wallet instance to withdraw from
1899
+ * @returns Transaction result with hash
1900
+ * @throws Error if no deposited vault found
1901
+ */
1902
+ async withdraw(amountInUnderlying, smartWallet) {
1903
+ const depositedVault = await this.fetchDepositedVaults(smartWallet);
1904
+ if (!depositedVault) {
1905
+ throw new Error("No vault found to withdraw from");
1906
+ }
1907
+ const owner = await smartWallet.getAddress();
1908
+ let withdrawData;
1909
+ if (amountInUnderlying) {
1910
+ const assets = parseUnits2(amountInUnderlying, depositedVault.depositTokenDecimals);
1911
+ logger.info("Withdraw amount:", { amountInUnderlying, assets }, "SparkProtocol");
1912
+ withdrawData = {
1913
+ to: depositedVault.vaultAddress,
1914
+ data: encodeFunctionData2({
1915
+ abi: SPARK_VAULT_ABI,
1916
+ functionName: "withdraw",
1917
+ args: [assets, owner, owner]
1918
+ })
1919
+ };
1920
+ } else {
1921
+ const maxShares = await this.getMaxRedeemableShares(depositedVault, owner);
1922
+ logger.info("Withdrawing all funds:", { maxShares }, "SparkProtocol");
1923
+ withdrawData = {
1924
+ to: depositedVault.vaultAddress,
1925
+ data: encodeFunctionData2({
1926
+ abi: SPARK_VAULT_ABI,
1927
+ functionName: "redeem",
1928
+ args: [maxShares, owner, owner]
1929
+ })
1930
+ };
1931
+ }
1932
+ const hash = await smartWallet.send(withdrawData, this.selectedChainId);
1933
+ logger.info("Withdraw transaction sent:", { hash }, "SparkProtocol");
1934
+ return { success: true, hash };
1935
+ }
1936
+ /**
1937
+ * Get the maximum redeemable shares for a wallet
1938
+ * @param vaultInfo Vault information
1939
+ * @param walletAddress Wallet address to check
1940
+ * @returns Maximum redeemable shares as bigint
1941
+ */
1942
+ async getMaxRedeemableShares(vaultInfo, walletAddress) {
1943
+ if (!this.publicClient) {
1944
+ throw new Error("Public client not initialized");
1945
+ }
1946
+ const shares = await this.publicClient.readContract({
1947
+ address: vaultInfo.vaultAddress,
1948
+ abi: SPARK_VAULT_ABI,
1949
+ functionName: "balanceOf",
1950
+ args: [walletAddress]
1951
+ });
1952
+ return shares;
1953
+ }
1954
+ /**
1955
+ * Get amount that a wallet has deposited in a vault
1956
+ * @param vaultInfo Vault information
1957
+ * @param walletAddress Wallet address to check
1958
+ * @returns Object containing shares and deposited amount
1959
+ */
1960
+ async getBalance(vaultInfo, walletAddress) {
1961
+ if (!this.publicClient) {
1962
+ throw new Error("Public client not initialized");
1963
+ }
1964
+ const shares = await this.publicClient.readContract({
1965
+ address: vaultInfo.vaultAddress,
1966
+ abi: SPARK_VAULT_ABI,
1967
+ functionName: "balanceOf",
1968
+ args: [walletAddress]
1969
+ });
1970
+ if (shares === 0n) {
1971
+ return { shares: "0", depositedAmount: "0", vaultInfo };
1972
+ }
1973
+ const assets = await this.publicClient.readContract({
1974
+ address: vaultInfo.vaultAddress,
1975
+ abi: SPARK_VAULT_ABI,
1976
+ functionName: "convertToAssets",
1977
+ args: [shares]
1978
+ });
1979
+ return {
1980
+ shares: formatUnits2(shares, vaultInfo.earnTokenDecimals),
1981
+ depositedAmount: formatUnits2(assets, vaultInfo.depositTokenDecimals),
1982
+ vaultInfo
1983
+ };
1984
+ }
1985
+ };
1986
+
1987
+ // src/constants/protocols.ts
1988
+ var availableProtocols = [
1989
+ {
1990
+ info: {
1991
+ id: "spark",
1992
+ name: "Spark",
1993
+ website: "https://spark.fi/",
1994
+ logo: "/logos/spark.png",
1995
+ supportedChains: [1, 8453, 42161],
1996
+ riskLevel: "low",
1997
+ isPremium: false
1998
+ },
1999
+ instance: new SparkProtocol()
2000
+ }
2001
+ ];
2002
+
2003
+ // src/tools/ApiKeysValidator.ts
2004
+ var ApiKeysValidator = class {
2005
+ // TODO: Implement the validation logic
2006
+ /**
2007
+ * Validates whether the provided API key is valid
2008
+ *
2009
+ * @internal
2010
+ * @param apiKey API key from {@link ProtocolsRouterConfig}
2011
+ * @returns True if the API key is considered valid
2012
+ */
2013
+ validate(apiKey) {
2014
+ logger.info("Validating api key...", apiKey, "ApiKeysValidator");
2015
+ return true;
2016
+ }
2017
+ };
2018
+
2019
+ // src/router/base/ProtocolRouterBase.ts
2020
+ var ProtocolRouterBase = class {
2021
+ /**
2022
+ * Initialize a base protocol router
2023
+ * @param riskLevel Risk level required by the integrator
2024
+ * @param chainManager Chain manager instance for network operations
2025
+ * @param minApy Optional minimum APY filter
2026
+ * @param apiKey Optional API key for premium protocol access
2027
+ */
2028
+ constructor(riskLevel, chainManager, minApy, apiKey) {
2029
+ // TODO: Add an API key validation
2030
+ /** API key validator instance */
2031
+ this.apiKeyValidator = new ApiKeysValidator();
2032
+ this.riskLevel = riskLevel;
2033
+ this.minApy = minApy;
2034
+ this.apiKey = apiKey;
2035
+ this.chainManager = chainManager;
2036
+ }
2037
+ };
2038
+
2039
+ // src/router/ProtocolRouter.ts
2040
+ var ProtocolRouter = class extends ProtocolRouterBase {
2041
+ /**
2042
+ * Initialize the protocol router
2043
+ * @param config Router configuration including risk level, min APY, and optional API key
2044
+ * @param chainManager Chain manager instance for network validation
2045
+ */
2046
+ constructor(config, chainManager) {
2047
+ super(config.riskLevel, chainManager, config.minApy, config.apiKey);
2048
+ }
2049
+ /**
2050
+ * Get all protocols available for the current configuration
2051
+ *
2052
+ * Includes all non-premium protocols and premium protocols if the API key is valid
2053
+ * @returns Array of available protocol definitions
2054
+ */
2055
+ getProtocols() {
2056
+ const isKeyValid = this.apiKeyValidator.validate(this.apiKey);
2057
+ const allAvailableProtocols = availableProtocols.filter((protocol) => {
2058
+ if (!protocol.info.isPremium) {
2059
+ return true;
2060
+ }
2061
+ return protocol.info.isPremium && isKeyValid;
2062
+ });
2063
+ return allAvailableProtocols;
2064
+ }
2065
+ /**
2066
+ * Check if any protocol supports a given set of chains
2067
+ * @param chainIds List of chain IDs to validate
2068
+ * @returns True if at least one chain is supported by the router
2069
+ */
2070
+ isProtocolSupportedChain(chainIds) {
2071
+ return chainIds.some((chainId) => this.chainManager.getSupportedChain() === chainId);
2072
+ }
2073
+ /**
2074
+ * Recommend the best protocol for the current router configuration
2075
+ *
2076
+ * Filters available protocols by risk level and supported chains. More criteria will be added later on
2077
+ *
2078
+ *
2079
+ * @remarks
2080
+ * Currently returns the first match. Future improvements will add
2081
+ * smarter sorting and pool-based APY checks
2082
+ *
2083
+ * @throws Error if no protocols are available for the current risk level
2084
+ * @returns Protocol instance considered the best match
2085
+ */
2086
+ recommend() {
2087
+ const protocols = this.getProtocols();
2088
+ const eligibleProtocols = protocols.filter((protocol) => {
2089
+ const riskMatches = protocol.info.riskLevel === this.riskLevel;
2090
+ const isSupportedChain = this.isProtocolSupportedChain(protocol.info.supportedChains);
2091
+ return riskMatches && isSupportedChain;
2092
+ });
2093
+ if (eligibleProtocols.length === 0) {
2094
+ throw new Error(`No protocols available for risk level: ${this.riskLevel}`);
2095
+ }
2096
+ const bestProtocol = eligibleProtocols[0];
2097
+ return bestProtocol;
2098
+ }
2099
+ };
2100
+
2101
+ // src/tools/CoinbaseCDP.ts
2102
+ import { generateJwt } from "@coinbase/cdp-sdk/auth";
2103
+ import axios from "axios";
2104
+
2105
+ // src/utils/urls.ts
2106
+ var checkValidUrl = (url) => {
2107
+ return /^https?:\/\/.+$/.test(url);
2108
+ };
2109
+
2110
+ // src/tools/CoinbaseCDP.ts
2111
+ var CoinbaseCDP = class {
2112
+ constructor(apiKeyId, apiKeySecret, integratorId, chainManager) {
2113
+ this.coinbaseCdpV2Hostname = "https://api.cdp.coinbase.com";
2114
+ this.coinbaseCdpV1Hostname = "https://api.developer.coinbase.com";
2115
+ this.onRampUrlAuthParams = {
2116
+ requestMethod: "POST",
2117
+ requestHost: "api.cdp.coinbase.com",
2118
+ requestPath: "/platform/v2/onramp/sessions",
2119
+ expiresIn: 120
2120
+ };
2121
+ this.onRampConfigAuthParams = {
2122
+ requestMethod: "GET",
2123
+ requestHost: "api.developer.coinbase.com",
2124
+ requestPath: "/onramp/v1/buy/config",
2125
+ expiresIn: 120
2126
+ };
2127
+ this.offRampUrlAuthParams = {
2128
+ requestMethod: "POST",
2129
+ requestHost: "api.developer.coinbase.com",
2130
+ requestPath: "/onramp/v1/sell/quote",
2131
+ expiresIn: 120
2132
+ };
2133
+ this.offRampConfigAuthParams = {
2134
+ requestMethod: "GET",
2135
+ requestHost: "api.developer.coinbase.com",
2136
+ requestPath: "/onramp/v1/sell/config",
2137
+ expiresIn: 120
2138
+ };
2139
+ this.clientV2 = axios.create({
2140
+ baseURL: this.coinbaseCdpV2Hostname
2141
+ });
2142
+ this.clientV1 = axios.create({
2143
+ baseURL: this.coinbaseCdpV1Hostname
2144
+ });
2145
+ this.apiKeyId = apiKeyId;
2146
+ this.apiKeySecret = apiKeySecret;
2147
+ this.chainManager = chainManager;
2148
+ this.integratorId = integratorId;
2149
+ this.verifyParameters();
2150
+ }
2151
+ verifyParameters() {
2152
+ if (!this.apiKeyId || !this.apiKeySecret || !this.integratorId) {
2153
+ throw new Error("API key ID, secret and integrator ID are required");
2154
+ }
2155
+ }
2156
+ /**
2157
+ * @internal
2158
+ * Generate a JWT token for a provided API endpoint and hostname to make a request after
2159
+ * @category Ramp
2160
+ *
2161
+ * @param authParams Authentication parameters
2162
+ * @returns JWT token
2163
+ * @throws Error if the JWT token generation fails
2164
+ */
2165
+ async auth(authParams) {
2166
+ const jwtToken = await generateJwt({
2167
+ apiKeyId: this.apiKeyId,
2168
+ apiKeySecret: this.apiKeySecret,
2169
+ ...authParams
2170
+ });
2171
+ return jwtToken;
2172
+ }
2173
+ /**
2174
+ * @internal
2175
+ * Return a on-ramp URL for the given parameters via Coinbase CDP V2 API
2176
+ * @category Ramp
2177
+ *
2178
+ * @param receiverAddress
2179
+ * @param redirectUrl
2180
+ * @param amount
2181
+ * @param purchaseCurrency
2182
+ * @param paymentCurrency
2183
+ * @param paymentMethod
2184
+ * @param country
2185
+ * @returns OnRampUrlResponse
2186
+ * @throws Error if redirect URL is not a valid URL
2187
+ * @throws CoinbaseCDPError if the request fails
2188
+ */
2189
+ async getOnRampLink(receiverAddress, redirectUrl, amount, purchaseCurrency = "USDC", paymentCurrency = "USD", paymentMethod = "CARD", country) {
2190
+ if (!checkValidUrl(redirectUrl)) {
2191
+ throw new Error("Redirect URL is not a valid URL");
2192
+ }
2193
+ const chainId = this.chainManager.getSupportedChain();
2194
+ const chainName = chainById[chainId]?.name.toLowerCase();
2195
+ const authJwtToken = await this.auth(this.onRampUrlAuthParams);
2196
+ const response = await this.clientV2.post(
2197
+ this.onRampUrlAuthParams.requestPath,
2198
+ {
2199
+ destinationAddress: receiverAddress,
2200
+ destinationNetwork: chainName,
2201
+ redirectUrl,
2202
+ paymentAmount: amount,
2203
+ purchaseCurrency,
2204
+ paymentCurrency,
2205
+ paymentMethod,
2206
+ country
2207
+ },
2208
+ {
2209
+ method: this.onRampUrlAuthParams.requestMethod,
2210
+ headers: { Authorization: `Bearer ${authJwtToken}`, "Content-Type": "application/json" }
2211
+ }
2212
+ );
2213
+ if (response.status !== 200 && response.status !== 201) {
2214
+ const error = response.data;
2215
+ throw error;
2216
+ }
2217
+ const onRampResponse = response.data;
2218
+ return onRampResponse;
2219
+ }
2220
+ /**
2221
+ * @internal
2222
+ * Current method return all supported countries and payment methods for on-ramp by Coinbase CDP
2223
+ * @category Ramp
2224
+ *
2225
+ * @returns Config with supported countries and payment methods for on-ramp
2226
+ * @throws If API returned an error
2227
+ */
2228
+ async getOnRampConfig() {
2229
+ const authJwtToken = await this.auth(this.onRampConfigAuthParams);
2230
+ const response = await this.clientV1.get(
2231
+ this.onRampConfigAuthParams.requestPath,
2232
+ {
2233
+ headers: { Authorization: `Bearer ${authJwtToken}` }
2234
+ }
2235
+ );
2236
+ if (response.status !== 200) {
2237
+ const error = response.data;
2238
+ throw error;
2239
+ }
2240
+ const onRampConfigResponse = response.data;
2241
+ return onRampConfigResponse;
2242
+ }
2243
+ /**
2244
+ * @internal
2245
+ * Return a off-ramp URL for the given parameters via Coinbase CDP V1 API
2246
+ * @remarks
2247
+ * Use an integratorId as a partnerUserId in Coinbase CDP API
2248
+ * @category Ramp
2249
+ *
2250
+ * @param address
2251
+ * @param country
2252
+ * @param paymentMethod
2253
+ * @param redirectUrl
2254
+ * @param sellAmount
2255
+ * @param cashoutCurrency
2256
+ * @param sellCurrency
2257
+ * @returns OffRampUrlResponse
2258
+ * @throws Error if redirect URL is not a valid URL
2259
+ * @throws CoinbaseCDPError if the request fails
2260
+ */
2261
+ async getOffRampLink(address, country, paymentMethod, redirectUrl, sellAmount, cashoutCurrency = "USD", sellCurrency = "USDC") {
2262
+ if (!checkValidUrl(redirectUrl)) {
2263
+ throw new Error("Redirect URL is not a valid URL");
2264
+ }
2265
+ const chainId = this.chainManager.getSupportedChain();
2266
+ const chainName = chainById[chainId]?.name.toLowerCase();
2267
+ const authJwtToken = await this.auth(this.offRampUrlAuthParams);
2268
+ const response = await this.clientV1.post(
2269
+ this.offRampUrlAuthParams.requestPath,
2270
+ {
2271
+ sourceAddress: address,
2272
+ country,
2273
+ paymentMethod,
2274
+ partnerUserId: this.integratorId,
2275
+ redirectUrl,
2276
+ sellAmount,
2277
+ sellNetwork: chainName,
2278
+ cashoutCurrency,
2279
+ sellCurrency
2280
+ },
2281
+ {
2282
+ method: this.offRampUrlAuthParams.requestMethod,
2283
+ headers: { Authorization: `Bearer ${authJwtToken}`, "Content-Type": "application/json" }
2284
+ }
2285
+ );
2286
+ if (response.status !== 200 && response.status !== 201) {
2287
+ const error = response.data;
2288
+ throw error;
2289
+ }
2290
+ const offRampResponse = response.data;
2291
+ return offRampResponse;
2292
+ }
2293
+ /**
2294
+ * @internal
2295
+ * Current method return all supported countries and payment methods for off-ramp by Coinbase CDP
2296
+ * @category Ramp
2297
+ * @returns Config with supported countries and payment methods for off-ramp
2298
+ * @throws If API returned an error
2299
+ */
2300
+ async getOffRampConfig() {
2301
+ const authJwtToken = await this.auth(this.offRampConfigAuthParams);
2302
+ const response = await this.clientV1.get(
2303
+ this.offRampConfigAuthParams.requestPath,
2304
+ {
2305
+ headers: { Authorization: `Bearer ${authJwtToken}` }
2306
+ }
2307
+ );
2308
+ if (response.status !== 200) {
2309
+ const error = response.data;
2310
+ throw error;
2311
+ }
2312
+ const offRampConfigResponse = response.data;
2313
+ return offRampConfigResponse;
2314
+ }
2315
+ };
2316
+
2317
+ // src/index.ts
2318
+ var MyceliumSDK = class {
2319
+ /**
2320
+ * Creates a new SDK instance
2321
+ *
2322
+ * @param config SDK configuration (networks, wallets, protocol router settings)
2323
+ * @throws Throws if an unsupported wallet provider is given
2324
+ * @see MyceliumSDKConfig
2325
+ */
2326
+ constructor(config) {
2327
+ /**
2328
+ * Coinbase CDP instance to Coinbase related and onchain operations using Coinbase CDP API
2329
+ * @remarks
2330
+ * If the configuration is not provided, the Coinbase CDP functionality will be disabled.
2331
+ * Calling the respective method will throw an error.
2332
+ * @internal
2333
+ */
2334
+ this.coinbaseCDP = null;
2335
+ /**
2336
+ * Coinbase CDP configuration methods for ramp operations
2337
+ * @public
2338
+ * @category Tools
2339
+ */
2340
+ this.rampConfig = {
2341
+ /**
2342
+ * Return all supported countries and payment methods for on-ramp by Coinbase CDP
2343
+ * @public
2344
+ * @category Ramp
2345
+ *
2346
+ * @returns @see {@link RampConfigResponse} with supported countries and payment methods for top-up
2347
+ * @throws If API returned an error
2348
+ */
2349
+ getTopUpConfig: async () => {
2350
+ if (!this.coinbaseCDP) {
2351
+ throw new Error(
2352
+ "Coinbase CDP is not initialized. Please, provide the configuration in the SDK initialization"
2353
+ );
2354
+ }
2355
+ return await this.coinbaseCDP.getOnRampConfig();
2356
+ },
2357
+ /**
2358
+ * Return all supported countries and payment methods for off-ramp by Coinbase CDP
2359
+ * @public
2360
+ * @category Ramp
2361
+ *
2362
+ *
2363
+ * @returns @see {@link RampConfigResponse} with supported countries and payment methods for cash out
2364
+ * @throws If API returned an error
2365
+ */
2366
+ getCashOutConfig: async () => {
2367
+ if (!this.coinbaseCDP) {
2368
+ throw new Error(
2369
+ "Coinbase CDP is not initialized. Please, provide the configuration in the SDK initialization"
2370
+ );
2371
+ }
2372
+ return await this.coinbaseCDP.getOffRampConfig();
2373
+ }
2374
+ };
2375
+ this._chainManager = new ChainManager(
2376
+ config.chain || {
2377
+ chainId: base3.id,
2378
+ rpcUrl: base3.rpcUrls.default.http[0],
2379
+ bundlerUrl: "https://public.pimlico.io/v2/8453/rpc"
2380
+ }
2381
+ );
2382
+ if (!config.chain) {
2383
+ logger.warn(
2384
+ "No chain config provided, using default public RPC and Bundler URLs",
2385
+ "MyceliumSDK"
2386
+ );
2387
+ }
2388
+ if (config.coinbaseCDPConfig) {
2389
+ this.coinbaseCDP = new CoinbaseCDP(
2390
+ config.coinbaseCDPConfig.apiKeyId,
2391
+ config.coinbaseCDPConfig.apiKeySecret,
2392
+ config.integratorId,
2393
+ this.chainManager
2394
+ );
2395
+ }
2396
+ const protocolsRouterConfig = config.protocolsRouterConfig || {
2397
+ riskLevel: "low"
2398
+ };
2399
+ this.protocol = this.findProtocol(protocolsRouterConfig);
2400
+ this.wallet = this.createWalletNamespace(config.walletsConfig);
2401
+ }
2402
+ /**
2403
+ * Returns the chain manager instance for multi-chain operations
2404
+ * @public
2405
+ * @category Tools
2406
+ *
2407
+ * @returns ChainManager instance of the type {@link ChainManager}
2408
+ */
2409
+ get chainManager() {
2410
+ return this._chainManager;
2411
+ }
2412
+ /**
2413
+ * Recommends and initializes a protocol based on router settings
2414
+ *
2415
+ * @internal
2416
+ * @param config Protocol router configuration (e.g. risk level)
2417
+ * @returns Selected protocol object of the type {@link Protocol}
2418
+ */
2419
+ findProtocol(config) {
2420
+ const protocolRouter = new ProtocolRouter(config, this.chainManager);
2421
+ const protocol = protocolRouter.recommend();
2422
+ protocol.instance.init(this.chainManager);
2423
+ return protocol;
2424
+ }
2425
+ /**
2426
+ * Creates a wallet provider (embedded + smart) and combines them
2427
+ *
2428
+ * @internal
2429
+ * @param config Wallet configuration
2430
+ * @returns Configured {@link WalletProvider}
2431
+ * @throws If an unsupported wallet provider type is specified
2432
+ */
2433
+ createWalletProvider(config) {
2434
+ if (config.embeddedWalletConfig.provider.type === "privy") {
2435
+ const privyClient = new PrivyClient(
2436
+ config.embeddedWalletConfig.provider.providerConfig.appId,
2437
+ config.embeddedWalletConfig.provider.providerConfig.appSecret
2438
+ );
2439
+ this.embeddedWalletProvider = new PrivyEmbeddedWalletProvider(
2440
+ privyClient,
2441
+ this._chainManager
2442
+ );
2443
+ } else {
2444
+ throw new Error(
2445
+ `Unsupported embedded wallet provider: ${config.embeddedWalletConfig.provider.type}`
2446
+ );
2447
+ }
2448
+ if (!config.smartWalletConfig || config.smartWalletConfig.provider.type === "default") {
2449
+ this.smartWalletProvider = new DefaultSmartWalletProvider(
2450
+ this.chainManager,
2451
+ this.protocol,
2452
+ this.coinbaseCDP
2453
+ );
2454
+ } else {
2455
+ throw new Error(
2456
+ `Unsupported smart wallet provider: ${config.smartWalletConfig.provider.type}`
2457
+ );
2458
+ }
2459
+ const walletProvider = new WalletProvider(
2460
+ this.embeddedWalletProvider,
2461
+ this.smartWalletProvider
2462
+ );
2463
+ return walletProvider;
2464
+ }
2465
+ /**
2466
+ * Creates the public wallet namespace
2467
+ *
2468
+ * @internal
2469
+ * @param config Wallet configuration.
2470
+ * @returns A {@link WalletNamespace} instance
2471
+ */
2472
+ createWalletNamespace(config) {
2473
+ const walletProvider = this.createWalletProvider(config);
2474
+ return new WalletNamespace(walletProvider);
2475
+ }
2476
+ };
2477
+ export {
2478
+ DefaultSmartWallet,
2479
+ MyceliumSDK
2480
+ };
2481
+ //# sourceMappingURL=index.js.map