@layr-labs/ecloud-sdk 1.0.0-devep7 → 1.0.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.
@@ -1,5 +1,5 @@
1
1
  import { Address, Hex, WalletClient, PublicClient } from 'viem';
2
- import { a6 as SubscriptionOpts, a4 as SubscribeResponse, a0 as ProductSubscriptionResponse, g as CancelResponse, M as PaymentMethodsResponse, m as CreditPurchaseResponse } from './index-XhjPvHIY.cjs';
2
+ import { ai as SubscriptionOpts, ag as SubscribeResponse, ab as ProductSubscriptionResponse, k as CancelResponse, Z as PaymentMethodsResponse, s as CreditPurchaseResponse, ac as RedeemCouponResponse } from './index-BVnxNfqb.cjs';
3
3
 
4
4
  /**
5
5
  * Main Billing namespace entry point
@@ -8,11 +8,14 @@ import { a6 as SubscriptionOpts, a4 as SubscribeResponse, a0 as ProductSubscript
8
8
  * (privateKeyToAccount) and external signers (MetaMask, etc.).
9
9
  */
10
10
 
11
+ type BillingChain = "ethereum" | "base";
11
12
  interface TopUpOpts {
12
13
  /** Amount in raw USDC units (6 decimals, e.g. 50_000_000n = 50 USDC) */
13
14
  amount: bigint;
14
15
  /** Target account for purchaseCreditsFor (defaults to wallet address) */
15
16
  account?: Address;
17
+ /** Which blockchain to transact on (defaults to "ethereum") */
18
+ chain?: BillingChain;
16
19
  }
17
20
  interface TopUpResult {
18
21
  txHash: Hex;
@@ -30,11 +33,16 @@ interface BillingModule {
30
33
  getStatus: (opts?: SubscriptionOpts) => Promise<ProductSubscriptionResponse>;
31
34
  cancel: (opts?: SubscriptionOpts) => Promise<CancelResponse>;
32
35
  /** Read on-chain state needed for top-up */
33
- getTopUpInfo: () => Promise<TopUpInfo>;
36
+ getTopUpInfo: (opts?: {
37
+ chain?: BillingChain;
38
+ }) => Promise<TopUpInfo>;
34
39
  /** Purchase credits with USDC on-chain */
35
40
  topUp: (opts: TopUpOpts) => Promise<TopUpResult>;
36
41
  getPaymentMethods: () => Promise<PaymentMethodsResponse>;
37
42
  purchaseCredits: (amountCents: number, paymentMethodId?: string) => Promise<CreditPurchaseResponse>;
43
+ /** Check if Base chain is configured for this environment */
44
+ hasBaseSupport: () => boolean;
45
+ redeemCoupon: (code: string) => Promise<RedeemCouponResponse>;
38
46
  }
39
47
  interface BillingModuleConfig {
40
48
  verbose?: boolean;
@@ -42,7 +50,8 @@ interface BillingModuleConfig {
42
50
  skipTelemetry?: boolean;
43
51
  publicClient: PublicClient;
44
52
  environment: string;
53
+ privateKey?: Hex;
45
54
  }
46
55
  declare function createBillingModule(config: BillingModuleConfig): BillingModule;
47
56
 
48
- export { type BillingModule, type BillingModuleConfig, type TopUpInfo, type TopUpOpts, type TopUpResult, createBillingModule };
57
+ export { type BillingChain, type BillingModule, type BillingModuleConfig, type TopUpInfo, type TopUpOpts, type TopUpResult, createBillingModule };
package/dist/billing.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Address, Hex, WalletClient, PublicClient } from 'viem';
2
- import { a6 as SubscriptionOpts, a4 as SubscribeResponse, a0 as ProductSubscriptionResponse, g as CancelResponse, M as PaymentMethodsResponse, m as CreditPurchaseResponse } from './index-XhjPvHIY.js';
2
+ import { ai as SubscriptionOpts, ag as SubscribeResponse, ab as ProductSubscriptionResponse, k as CancelResponse, Z as PaymentMethodsResponse, s as CreditPurchaseResponse, ac as RedeemCouponResponse } from './index-BVnxNfqb.js';
3
3
 
4
4
  /**
5
5
  * Main Billing namespace entry point
@@ -8,11 +8,14 @@ import { a6 as SubscriptionOpts, a4 as SubscribeResponse, a0 as ProductSubscript
8
8
  * (privateKeyToAccount) and external signers (MetaMask, etc.).
9
9
  */
10
10
 
11
+ type BillingChain = "ethereum" | "base";
11
12
  interface TopUpOpts {
12
13
  /** Amount in raw USDC units (6 decimals, e.g. 50_000_000n = 50 USDC) */
13
14
  amount: bigint;
14
15
  /** Target account for purchaseCreditsFor (defaults to wallet address) */
15
16
  account?: Address;
17
+ /** Which blockchain to transact on (defaults to "ethereum") */
18
+ chain?: BillingChain;
16
19
  }
17
20
  interface TopUpResult {
18
21
  txHash: Hex;
@@ -30,11 +33,16 @@ interface BillingModule {
30
33
  getStatus: (opts?: SubscriptionOpts) => Promise<ProductSubscriptionResponse>;
31
34
  cancel: (opts?: SubscriptionOpts) => Promise<CancelResponse>;
32
35
  /** Read on-chain state needed for top-up */
33
- getTopUpInfo: () => Promise<TopUpInfo>;
36
+ getTopUpInfo: (opts?: {
37
+ chain?: BillingChain;
38
+ }) => Promise<TopUpInfo>;
34
39
  /** Purchase credits with USDC on-chain */
35
40
  topUp: (opts: TopUpOpts) => Promise<TopUpResult>;
36
41
  getPaymentMethods: () => Promise<PaymentMethodsResponse>;
37
42
  purchaseCredits: (amountCents: number, paymentMethodId?: string) => Promise<CreditPurchaseResponse>;
43
+ /** Check if Base chain is configured for this environment */
44
+ hasBaseSupport: () => boolean;
45
+ redeemCoupon: (code: string) => Promise<RedeemCouponResponse>;
38
46
  }
39
47
  interface BillingModuleConfig {
40
48
  verbose?: boolean;
@@ -42,7 +50,8 @@ interface BillingModuleConfig {
42
50
  skipTelemetry?: boolean;
43
51
  publicClient: PublicClient;
44
52
  environment: string;
53
+ privateKey?: Hex;
45
54
  }
46
55
  declare function createBillingModule(config: BillingModuleConfig): BillingModule;
47
56
 
48
- export { type BillingModule, type BillingModuleConfig, type TopUpInfo, type TopUpOpts, type TopUpResult, createBillingModule };
57
+ export { type BillingChain, type BillingModule, type BillingModuleConfig, type TopUpInfo, type TopUpOpts, type TopUpResult, createBillingModule };
package/dist/billing.js CHANGED
@@ -298,6 +298,63 @@ var BillingApiClient = class {
298
298
  return resp.json();
299
299
  }
300
300
  // ==========================================================================
301
+ // Admin - Coupon Methods
302
+ // ==========================================================================
303
+ async createCoupon(amountCents) {
304
+ const endpoint = `${this.config.billingApiServerURL}/admin/coupons`;
305
+ const resp = await this.makeAuthenticatedRequest(endpoint, "POST", "compute", { amountCents });
306
+ return resp.json();
307
+ }
308
+ async listCoupons(opts) {
309
+ const params = new URLSearchParams();
310
+ if (opts?.offset !== void 0) params.set("offset", opts.offset.toString());
311
+ if (opts?.limit !== void 0) params.set("limit", opts.limit.toString());
312
+ if (opts?.active !== void 0) params.set("active", opts.active.toString());
313
+ if (opts?.redeemed !== void 0) params.set("redeemed", opts.redeemed.toString());
314
+ const qs = params.toString();
315
+ const endpoint = `${this.config.billingApiServerURL}/admin/coupons${qs ? `?${qs}` : ""}`;
316
+ const resp = await this.makeAuthenticatedRequest(endpoint, "GET", "compute");
317
+ return resp.json();
318
+ }
319
+ async getCoupon(id) {
320
+ const endpoint = `${this.config.billingApiServerURL}/admin/coupons/${id}`;
321
+ const resp = await this.makeAuthenticatedRequest(endpoint, "GET", "compute");
322
+ return resp.json();
323
+ }
324
+ async deactivateCoupon(id) {
325
+ const endpoint = `${this.config.billingApiServerURL}/admin/coupons/${id}/deactivate`;
326
+ await this.makeAuthenticatedRequest(endpoint, "POST", "compute");
327
+ }
328
+ async redeemCouponForUser(id, address) {
329
+ const endpoint = `${this.config.billingApiServerURL}/admin/coupons/${id}/redeem`;
330
+ await this.makeAuthenticatedRequest(endpoint, "POST", "compute", { address });
331
+ }
332
+ // ==========================================================================
333
+ // Admin - Admin Management Methods
334
+ // ==========================================================================
335
+ async addAdmin(address) {
336
+ const endpoint = `${this.config.billingApiServerURL}/admin/admins`;
337
+ const resp = await this.makeAuthenticatedRequest(endpoint, "POST", "compute", { address });
338
+ return resp.json();
339
+ }
340
+ async removeAdmin(address) {
341
+ const endpoint = `${this.config.billingApiServerURL}/admin/admins/${address}`;
342
+ await this.makeAuthenticatedRequest(endpoint, "DELETE", "compute");
343
+ }
344
+ async listAdmins() {
345
+ const endpoint = `${this.config.billingApiServerURL}/admin/admins`;
346
+ const resp = await this.makeAuthenticatedRequest(endpoint, "GET", "compute");
347
+ return resp.json();
348
+ }
349
+ // ==========================================================================
350
+ // User - Coupon Redemption
351
+ // ==========================================================================
352
+ async redeemCoupon(code) {
353
+ const endpoint = `${this.config.billingApiServerURL}/v1/coupons/redeem`;
354
+ const resp = await this.makeAuthenticatedRequest(endpoint, "POST", "compute", { code });
355
+ return resp.json();
356
+ }
357
+ // ==========================================================================
301
358
  // Internal Methods
302
359
  // ==========================================================================
303
360
  /**
@@ -429,6 +486,7 @@ Please check:
429
486
  // src/client/common/config/environment.ts
430
487
  var SEPOLIA_CHAIN_ID = 11155111;
431
488
  var MAINNET_CHAIN_ID = 1;
489
+ var BASE_SEPOLIA_CHAIN_ID = 84532;
432
490
  var CommonAddresses = {
433
491
  ERC7702Delegator: "0x63c0c19a282a1b52b07dd5a65b58948a07dae32b"
434
492
  };
@@ -448,27 +506,28 @@ var BILLING_ENVIRONMENTS = {
448
506
  billingApiServerURL: "https://billingapi.eigencloud.xyz"
449
507
  }
450
508
  };
451
- var PLATFORM_ENV_TESTNET_SEPOLIA = "testnet-sepolia";
452
- var PLATFORM_ENV_MAINNET_ETHEREUM = "mainnet-ethereum";
453
- var DEFAULT_APP_BASE_DOMAIN = "eigencloud.xyz";
454
509
  var ENVIRONMENTS = {
455
510
  "sepolia-dev": {
456
511
  name: "sepolia",
457
512
  build: "dev",
458
513
  appControllerAddress: "0xa86DC1C47cb2518327fB4f9A1627F51966c83B92",
514
+ releaseAbiVersion: "v1.5",
515
+ // AppController upgraded to v1.5.x (containerPolicy)
459
516
  permissionControllerAddress: ChainAddresses[SEPOLIA_CHAIN_ID].PermissionController,
460
517
  erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
461
518
  kmsServerURL: "http://10.128.0.57:8080",
462
519
  userApiServerURL: "https://userapi-compute-sepolia-dev.eigencloud.xyz",
463
520
  defaultRPCURL: "https://ethereum-sepolia-rpc.publicnode.com",
464
521
  usdcCreditsAddress: "0xbdA3897c3A428763B59015C64AB766c288C97376",
465
- platformEnv: PLATFORM_ENV_TESTNET_SEPOLIA,
466
- appBaseDomain: DEFAULT_APP_BASE_DOMAIN
522
+ baseUsdcCreditsAddress: "0x7673a47463F80c6a3553Db9E54c8cDcd5313d0ac",
523
+ baseRPCURL: "https://base-sepolia-rpc.publicnode.com"
467
524
  },
468
525
  sepolia: {
469
526
  name: "sepolia",
470
527
  build: "prod",
471
528
  appControllerAddress: "0x0dd810a6ffba6a9820a10d97b659f07d8d23d4E2",
529
+ releaseAbiVersion: "v1.4",
530
+ // prod still on AppController v1.4.0 (3-field Release)
472
531
  permissionControllerAddress: ChainAddresses[SEPOLIA_CHAIN_ID].PermissionController,
473
532
  erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
474
533
  kmsServerURL: "http://10.128.15.203:8080",
@@ -476,21 +535,21 @@ var ENVIRONMENTS = {
476
535
  defaultRPCURL: "https://ethereum-sepolia-rpc.publicnode.com",
477
536
  billingRPCURL: "https://ethereum-rpc.publicnode.com",
478
537
  usdcCreditsAddress: "0xed9c88640ca9149Bd9f7ee6620074af10F2E145d",
479
- platformEnv: PLATFORM_ENV_TESTNET_SEPOLIA,
480
- appBaseDomain: DEFAULT_APP_BASE_DOMAIN
538
+ baseUsdcCreditsAddress: "0x7673a47463F80c6a3553Db9E54c8cDcd5313d0ac",
539
+ baseRPCURL: "https://base-sepolia-rpc.publicnode.com"
481
540
  },
482
541
  "mainnet-alpha": {
483
542
  name: "mainnet-alpha",
484
543
  build: "prod",
485
544
  appControllerAddress: "0xc38d35Fc995e75342A21CBd6D770305b142Fbe67",
545
+ releaseAbiVersion: "v1.4",
546
+ // prod still on AppController v1.4.0 (3-field Release)
486
547
  permissionControllerAddress: ChainAddresses[MAINNET_CHAIN_ID].PermissionController,
487
548
  erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
488
549
  kmsServerURL: "http://10.128.0.2:8080",
489
550
  userApiServerURL: "https://userapi-compute.eigencloud.xyz",
490
551
  defaultRPCURL: "https://ethereum-rpc.publicnode.com",
491
- usdcCreditsAddress: "0xed9c88640ca9149Bd9f7ee6620074af10F2E145d",
492
- platformEnv: PLATFORM_ENV_MAINNET_ETHEREUM,
493
- appBaseDomain: DEFAULT_APP_BASE_DOMAIN
552
+ usdcCreditsAddress: "0xed9c88640ca9149Bd9f7ee6620074af10F2E145d"
494
553
  }
495
554
  };
496
555
  var CHAIN_ID_TO_ENVIRONMENT = {
@@ -550,7 +609,7 @@ function getBillingEnvironmentConfig(build) {
550
609
  };
551
610
  }
552
611
  function getBuildType() {
553
- const buildTimeType = true ? "dev"?.toLowerCase() : void 0;
612
+ const buildTimeType = true ? "prod"?.toLowerCase() : void 0;
554
613
  const runtimeType = process.env.BUILD_TYPE?.toLowerCase();
555
614
  const buildType = buildTimeType || runtimeType;
556
615
  if (buildType === "dev") {
@@ -569,6 +628,41 @@ function isEnvironmentAvailable(environment) {
569
628
  return getAvailableEnvironments().includes(environment);
570
629
  }
571
630
 
631
+ // src/client/common/utils/helpers.ts
632
+ import { extractChain, createPublicClient, createWalletClient, http, fallback } from "viem";
633
+ import { sepolia as sepolia2 } from "viem/chains";
634
+ import { privateKeyToAccount } from "viem/accounts";
635
+
636
+ // src/client/common/constants.ts
637
+ import { sepolia, mainnet, baseSepolia } from "viem/chains";
638
+ var SUPPORTED_CHAINS = [mainnet, sepolia, baseSepolia];
639
+
640
+ // src/client/common/utils/helpers.ts
641
+ function getChainFromID(chainID, fallback2 = sepolia2) {
642
+ const id = Number(chainID);
643
+ return extractChain({ chains: SUPPORTED_CHAINS, id }) || fallback2;
644
+ }
645
+ function createClients(options) {
646
+ const { privateKey, rpcUrl, chainId } = options;
647
+ const privateKeyHex = addHexPrefix(privateKey);
648
+ const account = privateKeyToAccount(privateKeyHex);
649
+ const chain = getChainFromID(chainId);
650
+ const transport = typeof rpcUrl === "string" ? http(rpcUrl) : fallback(rpcUrl.map((url) => http(url)));
651
+ const publicClient = createPublicClient({
652
+ chain,
653
+ transport
654
+ });
655
+ const walletClient = createWalletClient({
656
+ account,
657
+ chain,
658
+ transport
659
+ });
660
+ return { walletClient, publicClient };
661
+ }
662
+ function addHexPrefix(value) {
663
+ return value.startsWith("0x") ? value : `0x${value}`;
664
+ }
665
+
572
666
  // src/client/common/utils/logger.ts
573
667
  var getLogger = (verbose) => ({
574
668
  info: (...args) => console.info(...args),
@@ -580,14 +674,6 @@ var getLogger = (verbose) => ({
580
674
  // src/client/common/utils/userapi.ts
581
675
  import axios3 from "axios";
582
676
 
583
- // src/client/common/utils/helpers.ts
584
- import { extractChain, createPublicClient, createWalletClient, http, fallback } from "viem";
585
- import { sepolia as sepolia2 } from "viem/chains";
586
- import { privateKeyToAccount } from "viem/accounts";
587
-
588
- // src/client/common/constants.ts
589
- import { sepolia, mainnet } from "viem/chains";
590
-
591
677
  // src/client/common/utils/retry.ts
592
678
  import axios2 from "axios";
593
679
 
@@ -2083,7 +2169,7 @@ var ERC20_default = [
2083
2169
 
2084
2170
  // src/client/modules/billing/index.ts
2085
2171
  function createBillingModule(config) {
2086
- const { verbose = false, skipTelemetry = false, walletClient, publicClient, environment } = config;
2172
+ const { verbose = false, skipTelemetry = false, walletClient, publicClient, environment, privateKey } = config;
2087
2173
  if (!walletClient.account) {
2088
2174
  throw new Error("WalletClient must have an account attached");
2089
2175
  }
@@ -2092,35 +2178,69 @@ function createBillingModule(config) {
2092
2178
  const billingEnvConfig = getBillingEnvironmentConfig(getBuildType());
2093
2179
  const billingApi = new BillingApiClient(billingEnvConfig, walletClient, { verbose });
2094
2180
  const environmentConfig = getEnvironmentConfig(environment);
2095
- const usdcCreditsAddress = environmentConfig.usdcCreditsAddress;
2096
- if (!usdcCreditsAddress) {
2181
+ if (!environmentConfig.usdcCreditsAddress) {
2097
2182
  throw new Error(`USDCCredits contract address not configured for environment "${environment}"`);
2098
2183
  }
2184
+ const usdcCreditsAddress = environmentConfig.usdcCreditsAddress;
2185
+ const baseUsdcCreditsAddress = environmentConfig.baseUsdcCreditsAddress;
2186
+ const baseRPCURL = environmentConfig.baseRPCURL;
2187
+ function resolveChainConfig(chain) {
2188
+ if (chain === "base") {
2189
+ if (!baseUsdcCreditsAddress || !baseRPCURL) {
2190
+ throw new Error(`Base chain not configured for environment "${environment}"`);
2191
+ }
2192
+ if (!privateKey) {
2193
+ throw new Error("Private key required for Base chain transactions");
2194
+ }
2195
+ const baseClients = createClients({
2196
+ privateKey,
2197
+ rpcUrl: baseRPCURL,
2198
+ chainId: BigInt(BASE_SEPOLIA_CHAIN_ID)
2199
+ });
2200
+ return {
2201
+ pub: baseClients.publicClient,
2202
+ wallet: baseClients.walletClient,
2203
+ creditsAddress: baseUsdcCreditsAddress,
2204
+ envConfig: {
2205
+ ...environmentConfig,
2206
+ chainID: BigInt(BASE_SEPOLIA_CHAIN_ID),
2207
+ defaultRPCURL: baseRPCURL
2208
+ }
2209
+ };
2210
+ }
2211
+ return {
2212
+ pub: publicClient,
2213
+ wallet: walletClient,
2214
+ creditsAddress: usdcCreditsAddress,
2215
+ envConfig: environmentConfig
2216
+ };
2217
+ }
2099
2218
  const module = {
2100
2219
  address,
2101
- async getTopUpInfo() {
2102
- const usdcAddress = await publicClient.readContract({
2103
- address: usdcCreditsAddress,
2220
+ async getTopUpInfo(opts) {
2221
+ const { pub, creditsAddress } = resolveChainConfig(opts?.chain);
2222
+ const usdcAddress = await pub.readContract({
2223
+ address: creditsAddress,
2104
2224
  abi: USDCCredits_default,
2105
2225
  functionName: "usdc"
2106
2226
  });
2107
2227
  const [minimumPurchase, usdcBalance, currentAllowance] = await Promise.all([
2108
- publicClient.readContract({
2109
- address: usdcCreditsAddress,
2228
+ pub.readContract({
2229
+ address: creditsAddress,
2110
2230
  abi: USDCCredits_default,
2111
2231
  functionName: "minimumPurchase"
2112
2232
  }),
2113
- publicClient.readContract({
2233
+ pub.readContract({
2114
2234
  address: usdcAddress,
2115
2235
  abi: ERC20_default,
2116
2236
  functionName: "balanceOf",
2117
2237
  args: [address]
2118
2238
  }),
2119
- publicClient.readContract({
2239
+ pub.readContract({
2120
2240
  address: usdcAddress,
2121
2241
  abi: ERC20_default,
2122
2242
  functionName: "allowance",
2123
- args: [address, usdcCreditsAddress]
2243
+ args: [address, creditsAddress]
2124
2244
  })
2125
2245
  ]);
2126
2246
  return { usdcAddress, minimumPurchase, usdcBalance, currentAllowance };
@@ -2130,11 +2250,12 @@ function createBillingModule(config) {
2130
2250
  {
2131
2251
  functionName: "topUp",
2132
2252
  skipTelemetry,
2133
- properties: { amount: opts.amount.toString() }
2253
+ properties: { amount: opts.amount.toString(), chain: opts.chain || "ethereum" }
2134
2254
  },
2135
2255
  async () => {
2136
2256
  const targetAccount = opts.account ?? address;
2137
- const { usdcAddress, currentAllowance } = await module.getTopUpInfo();
2257
+ const { pub, wallet, creditsAddress, envConfig } = resolveChainConfig(opts.chain);
2258
+ const { usdcAddress, currentAllowance } = await module.getTopUpInfo({ chain: opts.chain });
2138
2259
  const executions = [];
2139
2260
  if (currentAllowance < opts.amount) {
2140
2261
  executions.push({
@@ -2143,12 +2264,12 @@ function createBillingModule(config) {
2143
2264
  callData: encodeFunctionData3({
2144
2265
  abi: ERC20_default,
2145
2266
  functionName: "approve",
2146
- args: [usdcCreditsAddress, opts.amount]
2267
+ args: [creditsAddress, opts.amount]
2147
2268
  })
2148
2269
  });
2149
2270
  }
2150
2271
  executions.push({
2151
- target: usdcCreditsAddress,
2272
+ target: creditsAddress,
2152
2273
  value: 0n,
2153
2274
  callData: encodeFunctionData3({
2154
2275
  abi: USDCCredits_default,
@@ -2158,9 +2279,9 @@ function createBillingModule(config) {
2158
2279
  });
2159
2280
  const txHash = await executeBatch(
2160
2281
  {
2161
- walletClient,
2162
- publicClient,
2163
- environmentConfig,
2282
+ walletClient: wallet,
2283
+ publicClient: pub,
2284
+ environmentConfig: envConfig,
2164
2285
  executions,
2165
2286
  pendingMessage: "Submitting credit purchase..."
2166
2287
  },
@@ -2260,6 +2381,12 @@ function createBillingModule(config) {
2260
2381
  },
2261
2382
  async purchaseCredits(amountCents, paymentMethodId) {
2262
2383
  return billingApi.purchaseCredits(amountCents, paymentMethodId);
2384
+ },
2385
+ hasBaseSupport() {
2386
+ return !!baseUsdcCreditsAddress && !!baseRPCURL;
2387
+ },
2388
+ async redeemCoupon(code) {
2389
+ return billingApi.redeemCoupon(code);
2263
2390
  }
2264
2391
  };
2265
2392
  return module;