@circle-fin/app-kit 1.11.0 → 1.12.1

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/swap.mjs CHANGED
@@ -38,6 +38,7 @@ import '@coral-xyz/anchor';
38
38
  import bs58 from 'bs58';
39
39
  import '@noble/curves/ed25519';
40
40
  import { formatUnits as formatUnits$1 } from '@ethersproject/units';
41
+ import 'viem';
41
42
  import { keccak256 } from '@ethersproject/keccak256';
42
43
 
43
44
  // Import global type declarations
@@ -3002,6 +3003,8 @@ class KitError extends Error {
3002
3003
  Blockchain["World_Chain_Sepolia"] = "World_Chain_Sepolia";
3003
3004
  Blockchain["XDC"] = "XDC";
3004
3005
  Blockchain["XDC_Apothem"] = "XDC_Apothem";
3006
+ Blockchain["X_Layer"] = "X_Layer";
3007
+ Blockchain["X_Layer_Testnet"] = "X_Layer_Testnet";
3005
3008
  Blockchain["ZKSync_Era"] = "ZKSync_Era";
3006
3009
  Blockchain["ZKSync_Sepolia"] = "ZKSync_Sepolia";
3007
3010
  })(Blockchain || (Blockchain = {}));
@@ -3055,6 +3058,7 @@ var BridgeChain;
3055
3058
  BridgeChain["Unichain"] = "Unichain";
3056
3059
  BridgeChain["World_Chain"] = "World_Chain";
3057
3060
  BridgeChain["XDC"] = "XDC";
3061
+ BridgeChain["X_Layer"] = "X_Layer";
3058
3062
  // Testnet chains with CCTPv2 support
3059
3063
  BridgeChain["Arc_Testnet"] = "Arc_Testnet";
3060
3064
  BridgeChain["Arbitrum_Sepolia"] = "Arbitrum_Sepolia";
@@ -3080,6 +3084,7 @@ var BridgeChain;
3080
3084
  BridgeChain["Unichain_Sepolia"] = "Unichain_Sepolia";
3081
3085
  BridgeChain["World_Chain_Sepolia"] = "World_Chain_Sepolia";
3082
3086
  BridgeChain["XDC_Apothem"] = "XDC_Apothem";
3087
+ BridgeChain["X_Layer_Testnet"] = "X_Layer_Testnet";
3083
3088
  })(BridgeChain || (BridgeChain = {}));
3084
3089
  var UnifiedBalanceChain;
3085
3090
  (function(UnifiedBalanceChain) {
@@ -5628,7 +5633,8 @@ var EarnChain;
5628
5633
  isTestnet: true,
5629
5634
  explorerUrl: 'https://amoy.polygonscan.com/tx/{hash}',
5630
5635
  rpcEndpoints: [
5631
- 'https://rpc-amoy.polygon.technology'
5636
+ 'https://polygon-amoy-bor-rpc.publicnode.com',
5637
+ 'https://polygon-amoy.drpc.org'
5632
5638
  ],
5633
5639
  eurcAddress: null,
5634
5640
  usdcAddress: '0x41e94eb019c0762f9bfcf9fb1e58725bfb0e7582',
@@ -6493,6 +6499,104 @@ var EarnChain;
6493
6499
  }
6494
6500
  });
6495
6501
 
6502
+ /**
6503
+ * X Layer Mainnet chain definition
6504
+ * @remarks
6505
+ * This represents the official production network for the X Layer blockchain.
6506
+ * X Layer is an EVM-compatible OP Stack Layer-2 blockchain built by OKX,
6507
+ * using OKB as its native gas token. (Migrated from Polygon zkEVM/CDK to the
6508
+ * OP Stack on 2025-10-27; older docs describing it as zkEVM are obsolete.)
6509
+ */ const XLayer = defineChain({
6510
+ type: 'evm',
6511
+ chain: Blockchain.X_Layer,
6512
+ name: 'X Layer',
6513
+ title: 'X Layer Mainnet',
6514
+ nativeCurrency: {
6515
+ name: 'OKB',
6516
+ symbol: 'OKB',
6517
+ decimals: 18
6518
+ },
6519
+ chainId: 196,
6520
+ isTestnet: false,
6521
+ explorerUrl: 'https://www.oklink.com/xlayer/tx/{hash}',
6522
+ rpcEndpoints: [
6523
+ 'https://xlayerrpc.okx.com'
6524
+ ],
6525
+ eurcAddress: null,
6526
+ usdcAddress: '0xB6CEceAB302E2E4948951eE7843FC24E92933061',
6527
+ usdtAddress: null,
6528
+ cctp: {
6529
+ domain: 37,
6530
+ contracts: {
6531
+ v2: {
6532
+ type: 'split',
6533
+ tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
6534
+ messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
6535
+ confirmations: 65,
6536
+ fastConfirmations: 1
6537
+ }
6538
+ },
6539
+ forwarderSupported: {
6540
+ source: false,
6541
+ destination: false
6542
+ }
6543
+ },
6544
+ kitContracts: {
6545
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET
6546
+ }
6547
+ });
6548
+
6549
+ /**
6550
+ * X Layer Testnet chain definition
6551
+ * @remarks
6552
+ * This represents the official test network for the X Layer blockchain.
6553
+ * X Layer is an EVM-compatible OP Stack Layer-2 blockchain built by OKX,
6554
+ * using OKB as its native gas token. (Migrated from Polygon zkEVM/CDK to the
6555
+ * OP Stack on 2025-10-27; older docs describing it as zkEVM are obsolete.)
6556
+ */ const XLayerTestnet = defineChain({
6557
+ type: 'evm',
6558
+ chain: Blockchain.X_Layer_Testnet,
6559
+ name: 'X Layer Testnet',
6560
+ title: 'X Layer Testnet',
6561
+ nativeCurrency: {
6562
+ name: 'OKB',
6563
+ symbol: 'OKB',
6564
+ decimals: 18
6565
+ },
6566
+ chainId: 1952,
6567
+ isTestnet: true,
6568
+ // Deliberately not oklink.com (used for mainnet): viem's bundled OKLink
6569
+ // testnet URL targets the deprecated pre-rebrand chain ID 195, not this
6570
+ // chain's ID (1952). Verified against the internal chain-expansion-scripts
6571
+ // config (`v2config.sandbox.yml`) — do not "normalize" this to match mainnet.
6572
+ explorerUrl: 'https://web3.okx.com/explorer/x-layer-testnet/tx/{hash}',
6573
+ rpcEndpoints: [
6574
+ 'https://testrpc.xlayer.tech'
6575
+ ],
6576
+ eurcAddress: null,
6577
+ usdcAddress: '0xDec90b78111Ba2fc6FC6d84d8B9ec159A2d4b9B3',
6578
+ usdtAddress: null,
6579
+ cctp: {
6580
+ domain: 37,
6581
+ contracts: {
6582
+ v2: {
6583
+ type: 'split',
6584
+ tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
6585
+ messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
6586
+ confirmations: 65,
6587
+ fastConfirmations: 1
6588
+ }
6589
+ },
6590
+ forwarderSupported: {
6591
+ source: false,
6592
+ destination: false
6593
+ }
6594
+ },
6595
+ kitContracts: {
6596
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET
6597
+ }
6598
+ });
6599
+
6496
6600
  /**
6497
6601
  * ZKSync Era Mainnet chain definition
6498
6602
  * @remarks
@@ -6612,6 +6716,8 @@ var Chains = /*#__PURE__*/Object.freeze({
6612
6716
  WorldChainSepolia: WorldChainSepolia,
6613
6717
  XDC: XDC,
6614
6718
  XDCApothem: XDCApothem,
6719
+ XLayer: XLayer,
6720
+ XLayerTestnet: XLayerTestnet,
6615
6721
  ZKSyncEra: ZKSyncEra,
6616
6722
  ZKSyncEraSepolia: ZKSyncEraSepolia
6617
6723
  });
@@ -8640,6 +8746,7 @@ const swapTokenEnumSchema = z.enum([
8640
8746
  [Blockchain.Unichain]: '0x078D782b760474a361dDA0AF3839290b0EF57AD6',
8641
8747
  [Blockchain.World_Chain]: '0x79A02482A880bCE3F13e09Da970dC34db4CD24d1',
8642
8748
  [Blockchain.XDC]: '0xfA2958CB79b0491CC627c1557F441eF849Ca8eb1',
8749
+ [Blockchain.X_Layer]: '0xB6CEceAB302E2E4948951eE7843FC24E92933061',
8643
8750
  [Blockchain.ZKSync_Era]: '0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4',
8644
8751
  // =========================================================================
8645
8752
  // Testnets (alphabetically sorted)
@@ -8648,6 +8755,7 @@ const swapTokenEnumSchema = z.enum([
8648
8755
  [Blockchain.Arbitrum_Sepolia]: '0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d',
8649
8756
  [Blockchain.Avalanche_Fuji]: '0x5425890298aed601595a70AB815c96711a31Bc65',
8650
8757
  [Blockchain.Base_Sepolia]: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
8758
+ [Blockchain.Celo_Alfajores_Testnet]: '0x2F25deB3848C207fc8E0c34035B3Ba7fC157602B',
8651
8759
  [Blockchain.Codex_Testnet]: '0x6d7f141b6819C2c9CC2f818e6ad549E7Ca090F8f',
8652
8760
  [Blockchain.Cronos_Testnet]: '0xEb33dc5fac03833e132593659e1dE7256aB59794',
8653
8761
  [Blockchain.Edge_Testnet]: '0x2d9F7CAD728051AA35Ecdc472a14cf8cDF5CFD6B',
@@ -8674,6 +8782,7 @@ const swapTokenEnumSchema = z.enum([
8674
8782
  [Blockchain.Unichain_Sepolia]: '0x31d0220469e10c4E71834a79b1f276d740d3768F',
8675
8783
  [Blockchain.World_Chain_Sepolia]: '0x66145f38cBAC35Ca6F1Dfb4914dF98F1614aeA88',
8676
8784
  [Blockchain.XDC_Apothem]: '0xb5AB69F7bBada22B28e79C8FFAECe55eF1c771D4',
8785
+ [Blockchain.X_Layer_Testnet]: '0xDec90b78111Ba2fc6FC6d84d8B9ec159A2d4b9B3',
8677
8786
  [Blockchain.ZKSync_Sepolia]: '0xAe045DE5638162fa134807Cb558E15A3F5A7F853'
8678
8787
  }
8679
8788
  };
@@ -9961,7 +10070,7 @@ const swapTokenEnumSchema = z.enum([
9961
10070
  }
9962
10071
 
9963
10072
  var name$2 = "@circle-fin/bridge-kit";
9964
- var version$2 = "1.12.2";
10073
+ var version$2 = "1.13.0";
9965
10074
  var pkg$2 = {
9966
10075
  name: name$2,
9967
10076
  version: version$2};
@@ -9997,15 +10106,61 @@ var pkg$2 = {
9997
10106
  computeFee: z.function().returns(z.string().or(z.promise(z.string()))).optional(),
9998
10107
  calculateFee: z.function().returns(z.string().or(z.promise(z.string()))).optional(),
9999
10108
  resolveFeeRecipientAddress: z.function().returns(z.string().or(z.promise(z.string())))
10000
- }).strict().refine((data)=>{
10109
+ }).strict().superRefine((data, ctx)=>{
10001
10110
  const hasComputeFee = data.computeFee !== undefined;
10002
10111
  const hasCalculateFee = data.calculateFee !== undefined;
10003
- // XOR: exactly one must be provided
10004
- return hasComputeFee !== hasCalculateFee;
10005
- }, {
10006
- message: 'Provide either computeFee or calculateFee, not both. Use computeFee (recommended) for human-readable amounts.'
10112
+ if (hasComputeFee && hasCalculateFee) {
10113
+ ctx.addIssue({
10114
+ code: z.ZodIssueCode.custom,
10115
+ message: 'Provide either computeFee or calculateFee, not both. Use computeFee (recommended) for human-readable amounts.'
10116
+ });
10117
+ }
10118
+ if (!hasComputeFee && !hasCalculateFee) {
10119
+ ctx.addIssue({
10120
+ code: z.ZodIssueCode.custom,
10121
+ message: 'Provide either computeFee or calculateFee. Use computeFee (recommended) for human-readable amounts.'
10122
+ });
10123
+ }
10007
10124
  });
10008
10125
 
10126
+ /**
10127
+ * Canonical list of actions that do not prepare or submit transactions.
10128
+ *
10129
+ * @internal
10130
+ */ const READ_ACTION_KEYS = [
10131
+ 'token.allowance',
10132
+ 'token.balanceOf',
10133
+ 'token.name',
10134
+ 'native.balanceOf',
10135
+ 'usdc.allowance',
10136
+ 'usdc.balanceOf',
10137
+ 'usdc.name',
10138
+ 'gateway.v1.isDelegate',
10139
+ 'gateway.v1.withdrawingBalance',
10140
+ 'gateway.v1.withdrawalBlock',
10141
+ 'gateway.v1.signBurnIntents'
10142
+ ];
10143
+ const READ_ACTION_KEY_SET = new Set(READ_ACTION_KEYS);
10144
+ /**
10145
+ * Check whether a runtime value identifies a read action.
10146
+ *
10147
+ * @param action - The value to classify.
10148
+ * @returns Whether the value is a registered read-action key.
10149
+ *
10150
+ * @example
10151
+ * ```typescript
10152
+ * import { isReadActionKey } from '@core/adapter'
10153
+ *
10154
+ * if (isReadActionKey(value)) {
10155
+ * await adapter.readAction(value, params, context)
10156
+ * }
10157
+ * ```
10158
+ *
10159
+ * @internal
10160
+ */ function isReadActionKey(action) {
10161
+ return typeof action === 'string' && READ_ACTION_KEY_SET.has(action);
10162
+ }
10163
+
10009
10164
  /**
10010
10165
  * Resolves an operation context into concrete chain and address values.
10011
10166
  *
@@ -10085,6 +10240,141 @@ var pkg$2 = {
10085
10240
  };
10086
10241
  }
10087
10242
 
10243
+ /**
10244
+ * Create the standard error for a missing or non-read action.
10245
+ *
10246
+ * @param action - The unsupported action value.
10247
+ * @returns A fatal unsupported-action error.
10248
+ *
10249
+ * @internal
10250
+ */ function createUnsupportedReadActionError(action) {
10251
+ return new KitError({
10252
+ ...InputError.UNSUPPORTED_ACTION,
10253
+ recoverability: 'FATAL',
10254
+ message: `Read action "${String(action)}" is not registered in this adapter.`
10255
+ });
10256
+ }
10257
+ /**
10258
+ * Execute a read through the adapter's dedicated read seam when available.
10259
+ *
10260
+ * @remarks
10261
+ * Fall back to the legacy `prepareAction().execute()` contract so providers
10262
+ * remain runtime-compatible with adapter versions released before `readAction`.
10263
+ * Consumers must upgrade their adapter package for reads to bypass custom
10264
+ * `prepareAction` wrappers.
10265
+ *
10266
+ * @typeParam TAdapterCapabilities - The adapter capabilities type.
10267
+ * @typeParam TActionKey - The read action key.
10268
+ * @param adapter - The adapter that owns the read action.
10269
+ * @param action - The read action to execute.
10270
+ * @param params - The parameters for the read action.
10271
+ * @param ctx - The operation context.
10272
+ * @returns The raw read-action result.
10273
+ * @throws {KitError} When `action` is not a supported read-action key.
10274
+ *
10275
+ * @example
10276
+ * ```typescript
10277
+ * import { executeAdapterReadAction } from '@core/adapter'
10278
+ * import { Ethereum } from '@core/chains'
10279
+ *
10280
+ * const allowance = await executeAdapterReadAction(
10281
+ * adapter,
10282
+ * 'token.allowance',
10283
+ * { tokenAddress, delegate },
10284
+ * { chain: Ethereum },
10285
+ * )
10286
+ * ```
10287
+ *
10288
+ * @internal
10289
+ */ async function executeAdapterReadAction(adapter, action, params, ctx) {
10290
+ if (!isReadActionKey(action)) {
10291
+ throw createUnsupportedReadActionError(action);
10292
+ }
10293
+ const runtimeAdapter = adapter;
10294
+ if (typeof runtimeAdapter.readAction === 'function') {
10295
+ return runtimeAdapter.readAction(action, params, ctx);
10296
+ }
10297
+ let request;
10298
+ try {
10299
+ request = await adapter.prepareAction(action, params, ctx);
10300
+ } catch (error) {
10301
+ if (error instanceof Error && error.message === `Action ${action} is not supported`) {
10302
+ throw createUnsupportedReadActionError(action);
10303
+ }
10304
+ throw error;
10305
+ }
10306
+ return request.execute();
10307
+ }
10308
+ /**
10309
+ * Read and parse a token allowance while supporting older adapter versions.
10310
+ *
10311
+ * @remarks
10312
+ * Prefer the adapter's dedicated read seam and fall back to the legacy
10313
+ * `prepareAction().execute()` contract when the runtime adapter predates it.
10314
+ *
10315
+ * @typeParam TAdapterCapabilities - The adapter capabilities type.
10316
+ * @param adapter - The adapter that owns the allowance action.
10317
+ * @param params - The token and delegate whose allowance is being read.
10318
+ * @param ctx - The operation context.
10319
+ * @returns The non-negative allowance in token base units.
10320
+ * @throws {KitError} When the action is unsupported or its response is malformed.
10321
+ *
10322
+ * @example
10323
+ * ```typescript
10324
+ * import { readTokenAllowance } from '@core/adapter'
10325
+ * import { Ethereum } from '@core/chains'
10326
+ *
10327
+ * const allowance = await readTokenAllowance(
10328
+ * adapter,
10329
+ * { tokenAddress, delegate },
10330
+ * { chain: Ethereum },
10331
+ * )
10332
+ * ```
10333
+ *
10334
+ * @internal
10335
+ */ async function readTokenAllowance(adapter, params, ctx) {
10336
+ return parseAllowanceResponse(await executeAdapterReadAction(adapter, 'token.allowance', params, ctx));
10337
+ }
10338
+ /**
10339
+ * Parse a raw token allowance response into base units.
10340
+ *
10341
+ * @param allowanceRaw - The adapter response, optionally wrapped as an Amount output.
10342
+ * @returns The non-negative allowance as a bigint, or zero for a missing value.
10343
+ * @throws {KitError} When the response cannot represent a non-negative bigint.
10344
+ *
10345
+ * @example
10346
+ * ```typescript
10347
+ * import { parseAllowanceResponse } from '@core/adapter'
10348
+ *
10349
+ * const allowance = parseAllowanceResponse({ amount: { raw: 1000000n } })
10350
+ * ```
10351
+ */ function parseAllowanceResponse(allowanceRaw) {
10352
+ let value = allowanceRaw;
10353
+ if (typeof value === 'object' && value !== null && 'amount' in value) {
10354
+ const amount = value.amount;
10355
+ if (typeof amount === 'object' && amount !== null && 'raw' in amount) {
10356
+ value = amount.raw;
10357
+ }
10358
+ }
10359
+ if (value === undefined || value === null) {
10360
+ return 0n;
10361
+ }
10362
+ let allowance;
10363
+ if (typeof value === 'bigint') {
10364
+ allowance = value;
10365
+ } else if (typeof value === 'string') {
10366
+ try {
10367
+ allowance = BigInt(value);
10368
+ } catch {
10369
+ allowance = undefined;
10370
+ }
10371
+ }
10372
+ if (allowance === undefined || allowance < 0n) {
10373
+ throw createValidationFailedError$1('token.allowance', value, 'token.allowance response must be a non-negative bigint-compatible string or bigint');
10374
+ }
10375
+ return allowance;
10376
+ }
10377
+
10088
10378
  /**
10089
10379
  * Schema for validating hexadecimal strings with '0x' prefix.
10090
10380
  *
@@ -10862,7 +11152,7 @@ var TransferSpeed;
10862
11152
  registerKit(`${pkg$2.name}/${pkg$2.version}`);
10863
11153
 
10864
11154
  var name$1 = "@circle-fin/swap-kit";
10865
- var version$1 = "1.5.0";
11155
+ var version$1 = "1.5.2";
10866
11156
  var pkg$1 = {
10867
11157
  name: name$1,
10868
11158
  version: version$1};
@@ -15182,14 +15472,13 @@ const TOKEN_REGISTRY$2 = createTokenRegistry();
15182
15472
  return;
15183
15473
  }
15184
15474
  // For non-native tokens (SPL tokens, ERC-20 tokens), check the token balance
15185
- const balancePrepared = await adapter.prepareAction('token.balanceOf', {
15475
+ const balance = await executeAdapterReadAction(adapter, 'token.balanceOf', {
15186
15476
  tokenAddress: tokenInAddress,
15187
15477
  walletAddress
15188
15478
  }, context);
15189
- const balance = await balancePrepared.execute();
15190
15479
  // Compare balances
15191
15480
  const requiredAmount = BigInt(amount);
15192
- const currentBalance = BigInt(balance);
15481
+ const currentBalance = BigInt(String(balance));
15193
15482
  if (currentBalance < requiredAmount) {
15194
15483
  throw new KitError({
15195
15484
  ...BalanceError.INSUFFICIENT_TOKEN,
@@ -16350,11 +16639,10 @@ async function fetchSameChainStatusSnapshot({ isCrossChainSwap, txHash, chain, a
16350
16639
  * @throws Error if the adapter fails to prepare or execute approval transactions
16351
16640
  * @throws Error if transaction confirmation fails or times out
16352
16641
  */ async handleUsdtApproval(adapter, chain, executionCtx, adapterContractAddress, resolvedContext, executedTransactions) {
16353
- const allowanceRequest = await adapter.prepareAction('token.allowance', {
16642
+ const current = await readTokenAllowance(adapter, {
16354
16643
  tokenAddress: executionCtx.tokenInAddress,
16355
16644
  delegate: adapterContractAddress
16356
16645
  }, resolvedContext);
16357
- const current = BigInt(await allowanceRequest.execute());
16358
16646
  const required = BigInt(executionCtx.amount);
16359
16647
  if (current >= required) {
16360
16648
  // Sufficient allowance - proceed to swap
@@ -21067,7 +21355,9 @@ registerKit(`${pkg$1.name}/${pkg$1.version}`);
21067
21355
  disableAnalytics: context.disableAnalytics
21068
21356
  }
21069
21357
  });
21070
- if (hasBoth) {
21358
+ if (context.customFeePolicy?.swap != null) {
21359
+ kit.setCustomFeePolicy(context.customFeePolicy.swap);
21360
+ } else if (hasBoth) {
21071
21361
  kit.setCustomFeePolicy({
21072
21362
  computeFee: async (params)=>{
21073
21363
  // Adapt provider-level params (with tokenIn/tokenOut)
@@ -21126,7 +21416,7 @@ registerKit(`${pkg$1.name}/${pkg$1.version}`);
21126
21416
  };
21127
21417
 
21128
21418
  var name = "@circle-fin/earn-kit";
21129
- var version = "1.4.0";
21419
+ var version = "1.5.1";
21130
21420
  var pkg = {
21131
21421
  name: name,
21132
21422
  version: version};
@@ -22137,11 +22427,16 @@ const sourceAdapterContextSchema = z.object({
22137
22427
  *
22138
22428
  * Validate the optional Kit Key field using the standard `apiKeySchema`
22139
22429
  * format (`KIT_KEY:<keyId>:<keySecret>`). When omitted, the SDK
22140
- * operates in permissionless mode.
22430
+ * operates in permissionless mode. `baseUrl` overrides the Earn Service
22431
+ * endpoint (e.g. staging); `batchTransactions: false` opts out of atomic
22432
+ * batched execution. Both are forwarded to the provider, so this `.strict()`
22433
+ * schema must accept them or a valid config object is rejected.
22141
22434
  *
22142
22435
  * @internal
22143
22436
  */ const earnConfigSchema = z.object({
22144
- kitKey: apiKeySchema.optional()
22437
+ kitKey: apiKeySchema.optional(),
22438
+ baseUrl: z.string().optional(),
22439
+ batchTransactions: z.boolean().optional()
22145
22440
  }).strict();
22146
22441
  /**
22147
22442
  * Canonical decimal form: a leading digit with no leading zeros (a single