@mycelium-sdk/core 1.0.0 → 2.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,10 +1,9 @@
1
1
  // src/wallet/DefaultSmartWallet.ts
2
2
  import {
3
- encodeFunctionData,
4
- erc20Abi as erc20Abi2,
3
+ encodeFunctionData as encodeFunctionData2,
4
+ erc20Abi as erc20Abi3,
5
5
  pad
6
6
  } from "viem";
7
- import { toCoinbaseSmartAccount } from "viem/account-abstraction";
8
7
 
9
8
  // src/abis/smartWalletFactory.ts
10
9
  var smartWalletFactoryAbi = [
@@ -231,6 +230,159 @@ function parseAssetAmount(amount, decimals) {
231
230
  var SmartWallet = class {
232
231
  };
233
232
 
233
+ // src/wallet/DefaultSmartWallet.ts
234
+ import { toCoinbaseSmartAccount } from "viem/account-abstraction";
235
+
236
+ // src/tools/Paymaster.ts
237
+ import { encodeFunctionData, erc20Abi as erc20Abi2, http, maxUint256 } from "viem";
238
+ import "viem/account-abstraction";
239
+ import { createSmartAccountClient } from "permissionless";
240
+
241
+ // src/constants/paymaster.ts
242
+ var ERC20_PAYMASTER_ADDRESS = "0x6666666666667849c56f2850848ce1c4da65c68b";
243
+
244
+ // src/tools/Paymaster.ts
245
+ var Paymaster = class {
246
+ constructor(chainManager) {
247
+ this.chainManager = chainManager;
248
+ }
249
+ /**
250
+ * Gets paymaster client for the specified chain
251
+ *
252
+ * @internal
253
+ * @category Paymaster
254
+ * @param chainId Target chain ID
255
+ * @returns PimlicoClient instance
256
+ * @throws Error if no paymaster URL is configured
257
+ */
258
+ getPaymasterClient(chainId) {
259
+ return this.chainManager.getPaymasterClient(chainId);
260
+ }
261
+ /**
262
+ * Prepares approval transaction if needed for ERC-20 paymaster
263
+ *
264
+ * @internal
265
+ * @category Paymaster
266
+ * @param paymasterToken Paymaster token address
267
+ * @param walletAddress Wallet address
268
+ * @param chainId Target chain ID
269
+ * @returns Promise resolving to the approval transaction data or null if allowance exists
270
+ * @throws Error if no public client is available
271
+ */
272
+ async preparePaymasterApproval(paymasterToken, walletAddress, chainId) {
273
+ const publicClient = this.chainManager.getPublicClient(chainId);
274
+ const allowance = await publicClient.readContract({
275
+ address: paymasterToken,
276
+ abi: erc20Abi2,
277
+ functionName: "allowance",
278
+ args: [walletAddress, ERC20_PAYMASTER_ADDRESS]
279
+ });
280
+ if (allowance > 0n) {
281
+ return null;
282
+ }
283
+ return {
284
+ to: paymasterToken,
285
+ value: 0n,
286
+ data: encodeFunctionData({
287
+ abi: erc20Abi2,
288
+ functionName: "approve",
289
+ args: [ERC20_PAYMASTER_ADDRESS, maxUint256]
290
+ })
291
+ };
292
+ }
293
+ /**
294
+ * Prepares calls array with approval if needed for ERC-20 paymaster
295
+ *
296
+ * @internal
297
+ * @category Paymaster
298
+ * @param transactionData Transaction data to prepare
299
+ * @param paymasterToken Paymaster token address
300
+ * @param walletAddress Wallet address
301
+ * @param chainId Target chain ID
302
+ * @returns Prepared calls array with approval if needed
303
+ * @throws Error if no paymaster URL is configured
304
+ */
305
+ async prepareCallsWithApproval(transactionData, paymasterToken, walletAddress, chainId) {
306
+ const approvalTx = await this.preparePaymasterApproval(paymasterToken, walletAddress, chainId);
307
+ const calls = Array.isArray(transactionData) ? transactionData : [transactionData];
308
+ const callsWithApproval = approvalTx ? [approvalTx, ...calls] : calls;
309
+ return {
310
+ calls: callsWithApproval,
311
+ paymasterContext: {
312
+ token: paymasterToken
313
+ }
314
+ };
315
+ }
316
+ /**
317
+ * Creates a SmartAccountClient configured with ERC-20 paymaster
318
+ *
319
+ * @internal
320
+ * @category Paymaster
321
+ * @param account Coinbase smart account
322
+ * @param chainId Target chain ID
323
+ * @returns SmartAccountClient instance
324
+ * @throws Error if no bundler URL is configured
325
+ */
326
+ createSmartAccountClient(account, chainId) {
327
+ const pimlicoClient = this.getPaymasterClient(chainId);
328
+ if (!pimlicoClient) {
329
+ throw new Error(
330
+ "Paymaster client is not available. Probably the paymaster URL is not configured for this chain"
331
+ );
332
+ }
333
+ const chain = this.chainManager.getChain(chainId);
334
+ const bundlerUrl = this.chainManager.getBundlerUrl(chainId);
335
+ if (!bundlerUrl) {
336
+ throw new Error(`Bundler URL not configured for chain ID: ${chainId}`);
337
+ }
338
+ return createSmartAccountClient({
339
+ account,
340
+ chain,
341
+ bundlerTransport: http(bundlerUrl),
342
+ paymaster: pimlicoClient,
343
+ userOperation: {
344
+ estimateFeesPerGas: async () => {
345
+ const gasPrice = await pimlicoClient.getUserOperationGasPrice();
346
+ const bump = (value, pct = 20n) => value + value * pct / 100n;
347
+ return {
348
+ maxFeePerGas: bump(gasPrice.fast.maxFeePerGas),
349
+ maxPriorityFeePerGas: bump(gasPrice.fast.maxPriorityFeePerGas)
350
+ };
351
+ }
352
+ }
353
+ });
354
+ }
355
+ /**
356
+ * Sends transaction(s) with ERC-20 paymaster
357
+ *
358
+ * @internal
359
+ * @category Paymaster
360
+ * @param transactionData Transaction data to send
361
+ * @param account Coinbase smart account
362
+ * @param walletAddress Wallet address
363
+ * @param chainId Target chain ID
364
+ * @param paymasterToken Paymaster token address
365
+ * @returns Promise resolving to the transaction hash
366
+ */
367
+ async sendWithERC20Paymaster(transactionData, account, walletAddress, chainId, paymasterToken) {
368
+ const smartAccountClient = this.createSmartAccountClient(account, chainId);
369
+ const { calls, paymasterContext } = await this.prepareCallsWithApproval(
370
+ transactionData,
371
+ paymasterToken,
372
+ walletAddress,
373
+ chainId
374
+ );
375
+ return smartAccountClient.sendTransaction({
376
+ calls: calls.map((call) => ({
377
+ to: call.to,
378
+ data: call.data,
379
+ value: call.value ?? 0n
380
+ })),
381
+ paymasterContext
382
+ });
383
+ }
384
+ };
385
+
234
386
  // src/wallet/DefaultSmartWallet.ts
235
387
  var DefaultSmartWallet = class extends SmartWallet {
236
388
  /**
@@ -247,6 +399,7 @@ var DefaultSmartWallet = class extends SmartWallet {
247
399
  */
248
400
  constructor(owners, signer, chainManager, protocolProvider, coinbaseCDP, deploymentAddress, signerOwnerIndex, nonce) {
249
401
  super();
402
+ this.bumpGasLimits = (x, pct = 40n) => x + x * pct / 100n;
250
403
  this.owners = owners;
251
404
  this._signer = signer;
252
405
  this.signerOwnerIndex = signerOwnerIndex;
@@ -255,6 +408,7 @@ var DefaultSmartWallet = class extends SmartWallet {
255
408
  this.nonce = nonce;
256
409
  this.protocolProvider = protocolProvider;
257
410
  this.coinbaseCDP = coinbaseCDP;
411
+ this.paymaster = new Paymaster(this.chainManager);
258
412
  }
259
413
  /**
260
414
  * Returns the signer account for this smart wallet
@@ -387,14 +541,25 @@ var DefaultSmartWallet = class extends SmartWallet {
387
541
  *
388
542
  * @param transactionData Transaction details (`to`, `value`, `data`)
389
543
  * @param chainId Target chain ID
544
+ * @param options Optional parameters
545
+ * @param options.paymasterToken ERC-20 token address to use for gas payment (e.g., USDC)
390
546
  * @returns Promise that resolves to the UserOperation hash
391
547
  * @throws Error with a readable message if submission or inclusion fails
392
548
  */
393
- async send(transactionData, chainId) {
549
+ async send(transactionData, chainId, options) {
394
550
  try {
395
551
  const account = await this.getCoinbaseSmartAccount(chainId);
552
+ if (options?.paymasterToken) {
553
+ const walletAddress = await this.getAddress();
554
+ return this.paymaster.sendWithERC20Paymaster(
555
+ transactionData,
556
+ account,
557
+ walletAddress,
558
+ chainId,
559
+ options.paymasterToken
560
+ );
561
+ }
396
562
  const bundlerClient = this.chainManager.getBundlerClient(chainId, account);
397
- const bump = (x, pct = 40n) => x + x * pct / 100n;
398
563
  const gas = await bundlerClient.estimateUserOperationGas({
399
564
  account,
400
565
  calls: [transactionData]
@@ -402,9 +567,9 @@ var DefaultSmartWallet = class extends SmartWallet {
402
567
  const hash = await bundlerClient.sendUserOperation({
403
568
  account,
404
569
  calls: [transactionData],
405
- callGasLimit: bump(gas.callGasLimit),
406
- verificationGasLimit: bump(gas.verificationGasLimit),
407
- preVerificationGas: bump(gas.preVerificationGas)
570
+ callGasLimit: this.bumpGasLimits(gas.callGasLimit),
571
+ verificationGasLimit: this.bumpGasLimits(gas.verificationGasLimit),
572
+ preVerificationGas: this.bumpGasLimits(gas.preVerificationGas)
408
573
  });
409
574
  await bundlerClient.waitForUserOperationReceipt({
410
575
  hash
@@ -424,14 +589,25 @@ var DefaultSmartWallet = class extends SmartWallet {
424
589
  *
425
590
  * @param transactionData An array of calls to execute
426
591
  * @param chainId Target chain ID
592
+ * @param options Optional parameters
593
+ * @param options.paymasterToken ERC-20 token address to use for gas payment (e.g., USDC)
427
594
  * @returns Promise that resolves to the UserOperation hash for the batch
428
595
  * @throws Error with a readable message if submission or inclusion fails
429
596
  */
430
- async sendBatch(transactionData, chainId) {
597
+ async sendBatch(transactionData, chainId, options) {
431
598
  try {
432
599
  const account = await this.getCoinbaseSmartAccount(chainId);
600
+ if (options?.paymasterToken) {
601
+ const walletAddress = await this.getAddress();
602
+ return this.paymaster.sendWithERC20Paymaster(
603
+ transactionData,
604
+ account,
605
+ walletAddress,
606
+ chainId,
607
+ options.paymasterToken
608
+ );
609
+ }
433
610
  const bundlerClient = this.chainManager.getBundlerClient(chainId, account);
434
- const bump = (x, pct = 40n) => x + x * pct / 100n;
435
611
  const gas = await bundlerClient.estimateUserOperationGas({
436
612
  account,
437
613
  calls: transactionData
@@ -439,9 +615,9 @@ var DefaultSmartWallet = class extends SmartWallet {
439
615
  const hash = await bundlerClient.sendUserOperation({
440
616
  account,
441
617
  calls: transactionData,
442
- callGasLimit: bump(gas.callGasLimit),
443
- verificationGasLimit: bump(gas.verificationGasLimit),
444
- preVerificationGas: bump(gas.preVerificationGas)
618
+ callGasLimit: this.bumpGasLimits(gas.callGasLimit),
619
+ verificationGasLimit: this.bumpGasLimits(gas.verificationGasLimit),
620
+ preVerificationGas: this.bumpGasLimits(gas.preVerificationGas)
445
621
  });
446
622
  await bundlerClient.waitForUserOperationReceipt({
447
623
  hash
@@ -558,8 +734,8 @@ var DefaultSmartWallet = class extends SmartWallet {
558
734
  }
559
735
  const resolvedAsset = resolveAsset(asset, chainId);
560
736
  const parsedAmount = parseAssetAmount(amount, resolvedAsset.decimals);
561
- const transferData = encodeFunctionData({
562
- abi: erc20Abi2,
737
+ const transferData = encodeFunctionData2({
738
+ abi: erc20Abi3,
563
739
  functionName: "transfer",
564
740
  args: [recipientAddress, parsedAmount]
565
741
  });
@@ -608,10 +784,12 @@ var FundingNamespace = class {
608
784
  };
609
785
 
610
786
  // src/tools/ChainManager.ts
611
- import { createPublicClient, http } from "viem";
787
+ import { createPublicClient, http as http2 } from "viem";
612
788
  import {
613
- createBundlerClient
789
+ createBundlerClient,
790
+ entryPoint07Address
614
791
  } from "viem/account-abstraction";
792
+ import { createPimlicoClient } from "permissionless/clients/pimlico";
615
793
 
616
794
  // src/constants/chains.ts
617
795
  import { base as base2, baseSepolia as baseSepolia2 } from "viem/chains";
@@ -692,12 +870,12 @@ var ChainManager = class {
692
870
  }
693
871
  const client = createPublicClient({
694
872
  chain: this.getChain(chainId),
695
- transport: http(rpcUrl)
873
+ transport: http2(rpcUrl)
696
874
  });
697
875
  return createBundlerClient({
698
876
  account,
699
877
  client,
700
- transport: http(bundlerUrl),
878
+ transport: http2(bundlerUrl),
701
879
  chain: this.getChain(chainId)
702
880
  });
703
881
  }
@@ -777,10 +955,53 @@ var ChainManager = class {
777
955
  const chainObject = chainById[chain.chainId];
778
956
  const client = createPublicClient({
779
957
  chain: chainObject,
780
- transport: http(chain.rpcUrl)
958
+ transport: http2(chain.rpcUrl)
781
959
  });
782
960
  return client;
783
961
  }
962
+ /**
963
+ * Returns the paymaster URL for the given chain ID
964
+ *
965
+ * @internal
966
+ * @category URLs
967
+ * @param chainId Target chain ID
968
+ * @returns Paymaster URL string
969
+ * @throws Error if chain config is missing or URL is invalid
970
+ */
971
+ getPaymasterUrl(chainId) {
972
+ const chainConfig = this.chainConfigs;
973
+ if (!chainConfig) {
974
+ throw new Error(`No chain config found for chain ID: ${chainId}`);
975
+ }
976
+ if (chainConfig.paymasterUrl && !this.isValidUrl(chainConfig.paymasterUrl)) {
977
+ throw new Error(`Invalid paymaster URL for chain ID: ${chainId}`);
978
+ }
979
+ return chainConfig.paymasterUrl || "";
980
+ }
981
+ /**
982
+ * Creates a {@link PimlicoClient} for the given chain ID
983
+ *
984
+ * @internal
985
+ * @category Clients
986
+ * @param chainId Target chain ID
987
+ * @returns PimlicoClient instance
988
+ * @throws Error if no paymaster URL is configured
989
+ */
990
+ getPaymasterClient(chainId) {
991
+ const paymasterUrl = this.getPaymasterUrl(chainId);
992
+ if (!paymasterUrl) {
993
+ throw new Error(`No paymaster URL configured for chain ID: ${chainId}`);
994
+ }
995
+ const chain = this.getChain(chainId);
996
+ return createPimlicoClient({
997
+ chain,
998
+ transport: http2(paymasterUrl),
999
+ entryPoint: {
1000
+ address: entryPoint07Address,
1001
+ version: "0.7"
1002
+ }
1003
+ });
1004
+ }
784
1005
  };
785
1006
 
786
1007
  // src/wallet/WalletNamespace.ts
@@ -1212,7 +1433,7 @@ import { getAddress } from "viem";
1212
1433
  import { createViemAccount } from "@privy-io/server-auth/viem";
1213
1434
  import {
1214
1435
  createWalletClient,
1215
- http as http2
1436
+ http as http3
1216
1437
  } from "viem";
1217
1438
  import { unichain as unichain2 } from "viem/chains";
1218
1439
 
@@ -1402,7 +1623,7 @@ var PrivyWallet = class extends EmbeddedWallet {
1402
1623
  return createWalletClient({
1403
1624
  account,
1404
1625
  chain: this.chainManager.getChain(chainId),
1405
- transport: http2(this.chainManager.getRpcUrl(chainId))
1626
+ transport: http3(this.chainManager.getRpcUrl(chainId))
1406
1627
  });
1407
1628
  }
1408
1629
  /**
@@ -1612,9 +1833,9 @@ import { PrivyClient } from "@privy-io/server-auth";
1612
1833
 
1613
1834
  // src/protocols/base/BaseProtocol.ts
1614
1835
  import {
1615
- erc20Abi as erc20Abi3,
1836
+ erc20Abi as erc20Abi4,
1616
1837
  createWalletClient as createWalletClient2,
1617
- http as http3,
1838
+ http as http4,
1618
1839
  parseGwei
1619
1840
  } from "viem";
1620
1841
  var BaseProtocol = class {
@@ -1641,11 +1862,11 @@ var BaseProtocol = class {
1641
1862
  const walletClient = createWalletClient2({
1642
1863
  account,
1643
1864
  chain: this.chainManager.getChain(chainId),
1644
- transport: http3(this.chainManager.getRpcUrl(chainId))
1865
+ transport: http4(this.chainManager.getRpcUrl(chainId))
1645
1866
  });
1646
1867
  const hash = await walletClient.writeContract({
1647
1868
  address: tokenAddress,
1648
- abi: erc20Abi3,
1869
+ abi: erc20Abi4,
1649
1870
  functionName: "approve",
1650
1871
  args: [spenderAddress, amount],
1651
1872
  gas: 100000n,
@@ -1667,7 +1888,7 @@ var BaseProtocol = class {
1667
1888
  const publicClient = this.chainManager.getPublicClient(chainId);
1668
1889
  return await publicClient.readContract({
1669
1890
  address: tokenAddress,
1670
- abi: erc20Abi3,
1891
+ abi: erc20Abi4,
1671
1892
  functionName: "allowance",
1672
1893
  args: [walletAddress, spenderAddress]
1673
1894
  });
@@ -1675,7 +1896,7 @@ var BaseProtocol = class {
1675
1896
  };
1676
1897
 
1677
1898
  // src/protocols/implementations/SparkProtocol.ts
1678
- import { encodeFunctionData as encodeFunctionData2, erc20Abi as erc20Abi4, formatUnits as formatUnits2, parseUnits as parseUnits2 } from "viem";
1899
+ import { encodeFunctionData as encodeFunctionData3, erc20Abi as erc20Abi5, formatUnits as formatUnits2, parseUnits as parseUnits2 } from "viem";
1679
1900
 
1680
1901
  // src/abis/protocols/spark.ts
1681
1902
  var SPARK_VAULT_ABI = [
@@ -1835,8 +2056,8 @@ var SparkProtocol = class extends BaseProtocol {
1835
2056
  if (allowance < assets) {
1836
2057
  ops.push({
1837
2058
  to: vaultInfo.tokenAddress,
1838
- data: encodeFunctionData2({
1839
- abi: erc20Abi4,
2059
+ data: encodeFunctionData3({
2060
+ abi: erc20Abi5,
1840
2061
  functionName: "approve",
1841
2062
  args: [vaultInfo.vaultAddress, assets]
1842
2063
  })
@@ -1844,7 +2065,7 @@ var SparkProtocol = class extends BaseProtocol {
1844
2065
  }
1845
2066
  ops.push({
1846
2067
  to: vaultInfo.vaultAddress,
1847
- data: encodeFunctionData2({
2068
+ data: encodeFunctionData3({
1848
2069
  abi: SPARK_VAULT_ABI,
1849
2070
  functionName: "deposit",
1850
2071
  args: [assets, owner]
@@ -1868,7 +2089,7 @@ var SparkProtocol = class extends BaseProtocol {
1868
2089
  const assets = parseUnits2(amount, vaultInfo.tokenDecimals);
1869
2090
  withdrawData = {
1870
2091
  to: vaultInfo.vaultAddress,
1871
- data: encodeFunctionData2({
2092
+ data: encodeFunctionData3({
1872
2093
  abi: SPARK_VAULT_ABI,
1873
2094
  functionName: "withdraw",
1874
2095
  args: [assets, owner, owner]
@@ -1878,7 +2099,7 @@ var SparkProtocol = class extends BaseProtocol {
1878
2099
  const maxShares = await this.getMaxRedeemableShares(vaultInfo, owner);
1879
2100
  withdrawData = {
1880
2101
  to: vaultInfo.vaultAddress,
1881
- data: encodeFunctionData2({
2102
+ data: encodeFunctionData3({
1882
2103
  abi: SPARK_VAULT_ABI,
1883
2104
  functionName: "redeem",
1884
2105
  args: [maxShares, owner, owner]
@@ -1949,7 +2170,7 @@ var availableProtocols = [
1949
2170
  name: "Spark",
1950
2171
  website: "https://spark.fi/",
1951
2172
  logo: "/logos/spark.png",
1952
- supportedChains: [8453],
2173
+ supportedChains: [8453, 84532],
1953
2174
  riskLevel: "low",
1954
2175
  isActive: true
1955
2176
  },
@@ -1958,7 +2179,7 @@ var availableProtocols = [
1958
2179
  ];
1959
2180
 
1960
2181
  // src/protocols/implementations/ProxyProtocol.ts
1961
- import { encodeFunctionData as encodeFunctionData3, erc20Abi as erc20Abi5, parseUnits as parseUnits3 } from "viem";
2182
+ import { encodeFunctionData as encodeFunctionData4, erc20Abi as erc20Abi6, parseUnits as parseUnits3 } from "viem";
1962
2183
  var ProxyProtocol = class extends BaseProtocol {
1963
2184
  /**
1964
2185
  * Initialize the Spark protocol with the provided chain manager
@@ -2060,8 +2281,8 @@ var ProxyProtocol = class extends BaseProtocol {
2060
2281
  if (allowance < rawDepositAmount) {
2061
2282
  const approveData = {
2062
2283
  to: depositTokenAddress,
2063
- data: encodeFunctionData3({
2064
- abi: erc20Abi5,
2284
+ data: encodeFunctionData4({
2285
+ abi: erc20Abi6,
2065
2286
  functionName: "approve",
2066
2287
  args: [vaultAddress, rawDepositAmount]
2067
2288
  })
@@ -2459,7 +2680,7 @@ var ProtocolsNamespace = class {
2459
2680
  };
2460
2681
 
2461
2682
  // src/constants/general.ts
2462
- var BACKEND_HOSTNAME = process.env.NODE_ENV === "dev" ? "http://localhost:3000" : "https://mycelium-cloud-production.up.railway.app";
2683
+ var BACKEND_HOSTNAME = process.env.NODE_ENV === "dev" ? "http://localhost:3000" : "https://staging.mycelium.sh";
2463
2684
 
2464
2685
  // src/tools/ApiClient.ts
2465
2686
  import axios2 from "axios";
@@ -2689,7 +2910,8 @@ var MyceliumSDK = class _MyceliumSDK {
2689
2910
  chain: {
2690
2911
  chainId: config.chainId || backendConfig.chainId,
2691
2912
  rpcUrl: backendConfig.rpcUrl,
2692
- bundlerUrl: backendConfig.bundlerUrl
2913
+ bundlerUrl: backendConfig.bundlerUrl,
2914
+ paymasterUrl: backendConfig.paymasterUrl
2693
2915
  },
2694
2916
  protocolsSecurityConfig: config.protocolsSecurityConfig,
2695
2917
  coinbaseCDPConfig: {