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