@getpara/shared 1.7.0 → 1.9.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.
@@ -0,0 +1,26 @@
1
+ export const TRANSACTION_ERROR_CODES = [
2
+ // RPC issues
3
+ 'INVALID_RPC_URL',
4
+ 'INVALID_CHAIN_ID',
5
+ 'RPC_UNAVAILABLE',
6
+ 'UNSUPPORTED_TRANSACTION_TYPE',
7
+ // Address validation
8
+ 'INVALID_DESTINATION_ADDRESS',
9
+ 'INVALID_SOURCE_ADDRESS',
10
+ 'INVALID_CONTRACT_ADDRESS',
11
+ // Balance issues
12
+ 'INSUFFICIENT_NATIVE_BALANCE', // Not enough ETH/SOL for send amount + fees
13
+ 'INSUFFICIENT_TOKEN_BALANCE', // Not enough ERC-20/SPL tokens
14
+ 'INSUFFICIENT_FEE', // Not enough native token for fees only (Solana)
15
+ // Token approvals (EVM only)
16
+ 'INSUFFICIENT_ALLOWANCE', // ERC-20 approval needed
17
+ // Account issues (Solana only)
18
+ 'RECIPIENT_ACCOUNT_REQUIRED', // SPL token account needs to be created
19
+ // Execution failures
20
+ 'CONTRACT_EXECUTION_REVERTED', // Smart contract rejected the transaction
21
+ 'GAS_ESTIMATION_FAILED', // Cannot estimate gas (EVM)
22
+ 'PROGRAM_ERROR', // Solana program error
23
+ 'COMPUTE_BUDGET_EXCEEDED', // Solana compute units exceeded
24
+ 'TRANSACTION_EXPIRED', // Solana blockhash expired
25
+ 'EXECUTION_FAILED', // Generic execution failure
26
+ ];
@@ -25,22 +25,129 @@ export function formatCurrency(value, { fallback = '' } = {}) {
25
25
  }
26
26
  export function formatAssetQuantity({ quantity, symbol = '', decimals, fallback = '', }) {
27
27
  try {
28
- if (quantity == null) {
28
+ if (quantity === undefined || isNaN(Number(quantity))) {
29
29
  return fallback;
30
30
  }
31
+ const num = Number(quantity);
31
32
  const formatter = new Intl.NumberFormat('en-US', {
32
33
  style: 'decimal',
33
- maximumFractionDigits: decimals ?? (Math.abs(quantity) < 0.001 ? 6 : 3),
34
- minimumFractionDigits: decimals ?? 3,
34
+ maximumFractionDigits: decimals ?? (Math.abs(num) < 0.001 ? 6 : 3),
35
+ minimumFractionDigits: 0,
35
36
  });
36
- const formattedQuantity = quantity === 0
37
+ let formattedQuantity = quantity === 0
37
38
  ? '0'
38
- : Math.abs(quantity) < 10 ** (-1 * (decimals ?? 6))
39
+ : Math.abs(num) < 10 ** (-1 * (decimals ?? 6))
39
40
  ? zeroAssetFormatter.format(0)
40
- : formatter.format(quantity);
41
+ : formatter.format(num);
42
+ // Trim trailing zeros and decimal point if needed (e.g., "0.000000657000" -> "0.000000657")
43
+ // Only trim if there's a decimal point and the result isn't just "0"
44
+ if (formattedQuantity.includes('.') && formattedQuantity !== '0') {
45
+ formattedQuantity = formattedQuantity.replace(/\.?0+$/, '');
46
+ }
41
47
  return `${formattedQuantity}${symbol && symbol.length > 0 ? ` ${symbol}` : ''}`;
42
48
  }
43
49
  catch (_e) {
44
50
  return fallback;
45
51
  }
46
52
  }
53
+ const buildCustomAssetId = (symbol) => `custom_${symbol}`;
54
+ const isNonNullable = (value) => value !== null;
55
+ export function filterWalletBalance(balance, config) {
56
+ if (config.isComprehensive) {
57
+ return balance;
58
+ }
59
+ const requestType = config.requestType ?? 'MAINNET';
60
+ let filteredAssets = balance.assets;
61
+ let filteredNetworks = balance.networks;
62
+ if (requestType !== 'MAINNET_AND_TESTNET') {
63
+ const includeTestnet = requestType === 'TESTNET';
64
+ filteredAssets = balance.assets
65
+ .map((asset) => {
66
+ const filteredAssetNetworks = asset.networks.filter((network) => {
67
+ const networkIsTestnet = network.metadata?.isTestnet ?? false;
68
+ return includeTestnet ? networkIsTestnet : !networkIsTestnet;
69
+ });
70
+ if (filteredAssetNetworks.length === 0) {
71
+ return null;
72
+ }
73
+ const quantity = filteredAssetNetworks.reduce((sum, n) => sum + n.quantity, 0);
74
+ const value = filteredAssetNetworks.reduce((sum, n) => sum + (n.value?.value ?? 0), 0);
75
+ return {
76
+ ...asset,
77
+ quantity,
78
+ networks: filteredAssetNetworks,
79
+ ...(asset.value ? { value: { value, currency: asset.value.currency } } : {}),
80
+ };
81
+ })
82
+ .filter(isNonNullable);
83
+ filteredNetworks = balance.networks
84
+ .map((network) => {
85
+ const networkIsTestnet = network.metadata?.isTestnet ?? false;
86
+ if (includeTestnet !== networkIsTestnet) {
87
+ return null;
88
+ }
89
+ const value = network.assets.reduce((sum, a) => sum + (a.value?.value ?? 0), 0);
90
+ return {
91
+ ...network,
92
+ assets: network.assets,
93
+ ...(network.value ? { value: { value, currency: network.value.currency } } : {}),
94
+ };
95
+ })
96
+ .filter(isNonNullable);
97
+ }
98
+ if (config.displayType === 'AGGREGATED' && config.excludeStandardAssets) {
99
+ filteredAssets = filteredAssets.filter((asset) => asset.metadata?.customAssetId !== undefined);
100
+ filteredNetworks = filteredNetworks
101
+ .map((network) => {
102
+ const filteredNetworkAssets = network.assets.filter((asset) => asset.metadata?.customAssetId !== undefined);
103
+ if (filteredNetworkAssets.length === 0) {
104
+ return null;
105
+ }
106
+ const value = filteredNetworkAssets.reduce((sum, a) => sum + (a.value?.value ?? 0), 0);
107
+ return {
108
+ ...network,
109
+ assets: filteredNetworkAssets,
110
+ ...(network.value ? { value: { value, currency: network.value.currency } } : {}),
111
+ };
112
+ })
113
+ .filter(isNonNullable);
114
+ }
115
+ if (config.displayType === 'CUSTOM_ASSET') {
116
+ const targetCustomAssetId = buildCustomAssetId(config.asset.symbol);
117
+ const matchesCustomAsset = (metadata) => {
118
+ if (metadata?.customAssetId) {
119
+ return metadata.customAssetId === targetCustomAssetId;
120
+ }
121
+ return metadata?.symbol === config.asset.symbol;
122
+ };
123
+ filteredAssets = filteredAssets.filter((asset) => matchesCustomAsset(asset.metadata));
124
+ filteredNetworks = filteredNetworks
125
+ .map((network) => {
126
+ const matchingAssets = network.assets.filter((asset) => matchesCustomAsset(asset.metadata));
127
+ if (matchingAssets.length === 0) {
128
+ return null;
129
+ }
130
+ return {
131
+ ...network,
132
+ assets: matchingAssets,
133
+ };
134
+ })
135
+ .filter(isNonNullable);
136
+ }
137
+ const totalValue = filteredAssets.reduce((sum, asset) => sum + (asset.value?.value ?? 0), 0);
138
+ return {
139
+ ...balance,
140
+ assets: filteredAssets,
141
+ networks: filteredNetworks,
142
+ ...(balance.value ? { value: { value: totalValue, currency: balance.value.currency } } : {}),
143
+ };
144
+ }
145
+ export function filterProfileBalance(balance, config) {
146
+ const filteredWallets = balance.wallets.map((wallet) => filterWalletBalance(wallet, config));
147
+ const totalValue = filteredWallets.reduce((sum, wallet) => sum + (wallet.value?.value ?? 0), 0);
148
+ return {
149
+ ...balance,
150
+ wallets: filteredWallets,
151
+ ...(balance.value ? { value: { value: totalValue, currency: balance.value.currency } } : {}),
152
+ };
153
+ }
@@ -1,15 +1,433 @@
1
1
  export const NETWORK_METADATA = [
2
- { evmChainId: '11155111', internalId: 'SEPOLIA', zerionId: 'ethereum-sepolia', name: 'Sepolia' },
3
- { evmChainId: '1', internalId: 'ETHEREUM', zerionId: 'ethereum', name: 'Ethereum' },
4
- { evmChainId: '137', internalId: 'POLYGON', zerionId: 'polygon', name: 'Polygon' },
5
- { evmChainId: '42220', internalId: 'CELO', zerionId: 'celo', name: 'Celo' },
6
- { evmChainId: '8453', internalId: 'BASE', zerionId: 'base', name: 'Base' },
7
- { evmChainId: '42161', internalId: 'ARBITRUM', zerionId: 'arbitrum', name: 'Arbitrum' },
8
- { evmChainId: '10', internalId: 'OPTIMISM', zerionId: 'optimism', name: 'Optimism' },
9
- { evmChainId: '80094', internalId: 'BERACHAIN', zerionId: 'berachain', name: 'Berachain' },
10
- { internalId: 'SOLANA', name: 'Solana' },
11
- { internalId: 'SOLANA_DEVNET', name: 'Solana Devnet' },
12
- { internalId: 'COSMOS', name: 'Cosmos' },
2
+ {
3
+ type: 'EVM',
4
+ evmChainId: '11155111',
5
+ internalId: 'SEPOLIA',
6
+ zerionId: 'ethereum-sepolia',
7
+ name: 'Ethereum Sepolia',
8
+ alchemySlug: 'eth-sepolia',
9
+ },
10
+ {
11
+ type: 'EVM',
12
+ evmChainId: '1',
13
+ internalId: 'ETHEREUM',
14
+ zerionId: 'ethereum',
15
+ name: 'Ethereum',
16
+ alchemySlug: 'eth-mainnet',
17
+ },
18
+ {
19
+ type: 'EVM',
20
+ evmChainId: '137',
21
+ internalId: 'POLYGON',
22
+ zerionId: 'polygon',
23
+ name: 'Polygon',
24
+ alchemySlug: 'polygon-mainnet',
25
+ nativeTokenSymbol: 'POL',
26
+ },
27
+ {
28
+ type: 'EVM',
29
+ evmChainId: '42220',
30
+ internalId: 'CELO',
31
+ zerionId: 'celo',
32
+ name: 'Celo',
33
+ alchemySlug: 'celo-mainnet',
34
+ nativeTokenSymbol: 'CELO',
35
+ },
36
+ {
37
+ type: 'EVM',
38
+ evmChainId: '8453',
39
+ internalId: 'BASE',
40
+ zerionId: 'base',
41
+ name: 'Base',
42
+ alchemySlug: 'base-mainnet',
43
+ },
44
+ {
45
+ type: 'EVM',
46
+ evmChainId: '42161',
47
+ internalId: 'ARBITRUM',
48
+ zerionId: 'arbitrum',
49
+ name: 'Arbitrum',
50
+ alchemySlug: 'arb-mainnet',
51
+ },
52
+ {
53
+ type: 'EVM',
54
+ evmChainId: '10',
55
+ internalId: 'OPTIMISM',
56
+ zerionId: 'optimism',
57
+ name: 'Optimism',
58
+ alchemySlug: 'opt-mainnet',
59
+ },
60
+ {
61
+ type: 'EVM',
62
+ evmChainId: '80094',
63
+ internalId: 'BERACHAIN',
64
+ zerionId: 'berachain',
65
+ name: 'Berachain',
66
+ alchemySlug: 'bera-mainnet',
67
+ },
68
+ {
69
+ type: 'EVM',
70
+ zerionId: 'binance-smart-chain',
71
+ evmChainId: '56',
72
+ name: 'Binance Smart Chain',
73
+ alchemySlug: 'bsc-mainnet',
74
+ nativeTokenSymbol: 'BNB',
75
+ },
76
+ {
77
+ type: 'EVM',
78
+ zerionId: 'tron',
79
+ evmChainId: '728126428',
80
+ name: 'Tron',
81
+ alchemySlug: 'tron-mainnet',
82
+ nativeTokenSymbol: 'TRX',
83
+ },
84
+ {
85
+ type: 'EVM',
86
+ zerionId: 'blast',
87
+ evmChainId: '81457',
88
+ name: 'Blast',
89
+ alchemySlug: 'blast-mainnet',
90
+ },
91
+ {
92
+ type: 'EVM',
93
+ zerionId: 'ape',
94
+ evmChainId: '33139',
95
+ name: 'Ape',
96
+ alchemySlug: 'ape-mainnet',
97
+ },
98
+ {
99
+ type: 'EVM',
100
+ zerionId: 'cronos-zkevm',
101
+ evmChainId: '388',
102
+ name: 'Cronos ZK-EVM',
103
+ nativeTokenSymbol: 'CRO',
104
+ },
105
+ {
106
+ type: 'EVM',
107
+ zerionId: '0g',
108
+ evmChainId: '16661',
109
+ name: 'Harmony',
110
+ nativeTokenSymbol: 'ONE',
111
+ },
112
+ {
113
+ type: 'EVM',
114
+ zerionId: 'opbnb',
115
+ evmChainId: '204',
116
+ alchemySlug: 'opbnb-mainnet',
117
+ name: 'Optimism Nova',
118
+ },
119
+ {
120
+ type: 'EVM',
121
+ zerionId: 'abstract',
122
+ evmChainId: '2741',
123
+ alchemySlug: 'abstract-mainnet',
124
+ name: 'Abstract',
125
+ },
126
+ {
127
+ type: 'EVM',
128
+ zerionId: 'avalanche',
129
+ evmChainId: '43114',
130
+ alchemySlug: 'avax-mainnet',
131
+ name: 'Avalanche',
132
+ nativeTokenSymbol: 'AVAX',
133
+ },
134
+ {
135
+ type: 'EVM',
136
+ zerionId: 'zksync-era',
137
+ evmChainId: '324',
138
+ alchemySlug: 'zksync-mainnet',
139
+ name: 'ZKSync Era',
140
+ },
141
+ {
142
+ type: 'EVM',
143
+ zerionId: 'mantle',
144
+ evmChainId: '5000',
145
+ alchemySlug: 'mantle-mainnet',
146
+ name: 'Mantle',
147
+ },
148
+ {
149
+ type: 'EVM',
150
+ zerionId: 'bob',
151
+ evmChainId: '60808',
152
+ alchemySlug: 'bob-mainnet',
153
+ name: 'Bob',
154
+ },
155
+ { type: 'EVM', zerionId: 'cyber', evmChainId: '7560' },
156
+ {
157
+ type: 'EVM',
158
+ zerionId: 'degen',
159
+ evmChainId: '666666666',
160
+ alchemySlug: 'degen-mainnet',
161
+ name: 'Degen',
162
+ },
163
+ {
164
+ type: 'EVM',
165
+ zerionId: 'manta-pacific',
166
+ evmChainId: '169',
167
+ name: 'Manta Pacific',
168
+ },
169
+ { type: 'EVM', zerionId: 'fantom', evmChainId: '250', name: 'Fantom', nativeTokenSymbol: 'FTM' },
170
+ {
171
+ type: 'EVM',
172
+ zerionId: 'ronin',
173
+ evmChainId: '2020',
174
+ alchemySlug: 'ronin-mainnet',
175
+ name: 'Ronin',
176
+ },
177
+ {
178
+ type: 'EVM',
179
+ zerionId: 'linea',
180
+ evmChainId: '59144',
181
+ alchemySlug: 'linea-mainnet',
182
+ name: 'Linea',
183
+ },
184
+ {
185
+ type: 'EVM',
186
+ zerionId: 'metis-andromeda',
187
+ evmChainId: '1088',
188
+ alchemySlug: 'metis-mainnet',
189
+ name: 'Metis Andromeda',
190
+ nativeTokenSymbol: 'METIS',
191
+ },
192
+ {
193
+ type: 'EVM',
194
+ zerionId: 'aurora',
195
+ evmChainId: '1313161554',
196
+ name: 'Aurora',
197
+ },
198
+ { type: 'EVM', zerionId: 'xinfin-xdc', evmChainId: '50', nativeTokenSymbol: 'XDC' },
199
+ {
200
+ type: 'EVM',
201
+ zerionId: 'fraxtal',
202
+ evmChainId: '252',
203
+ alchemySlug: 'frax-mainnet',
204
+ name: 'Frax',
205
+ },
206
+ {
207
+ type: 'EVM',
208
+ zerionId: 'gravity-alpha',
209
+ evmChainId: '1625',
210
+ name: 'Gravity Alpha',
211
+ },
212
+ {
213
+ type: 'EVM',
214
+ zerionId: 'hyperevm',
215
+ evmChainId: '999',
216
+ alchemySlug: 'hyperliquid-mainnet',
217
+ name: 'Hyperliquid',
218
+ },
219
+ {
220
+ type: 'EVM',
221
+ zerionId: 'ink',
222
+ evmChainId: '57073',
223
+ alchemySlug: 'ink-mainnet',
224
+ name: 'Ink',
225
+ },
226
+ {
227
+ type: 'EVM',
228
+ zerionId: 'lens',
229
+ evmChainId: '232',
230
+ alchemySlug: 'lens-mainnet',
231
+ name: 'Lens',
232
+ },
233
+ {
234
+ type: 'EVM',
235
+ zerionId: 'lisk',
236
+ evmChainId: '1135',
237
+ name: 'Lisk',
238
+ },
239
+ {
240
+ type: 'EVM',
241
+ zerionId: 'mode',
242
+ evmChainId: '34443',
243
+ alchemySlug: 'mode-mainnet',
244
+ name: 'Mode',
245
+ },
246
+ {
247
+ type: 'EVM',
248
+ zerionId: 'plasma',
249
+ evmChainId: '9745',
250
+ alchemySlug: 'plasma-mainnet',
251
+ name: 'Plasma',
252
+ },
253
+ {
254
+ type: 'EVM',
255
+ zerionId: 'polygon-zkevm',
256
+ evmChainId: '1101',
257
+ alchemySlug: 'polygonzkevm-mainnet',
258
+ name: 'Polygon ZK-EVM',
259
+ },
260
+ {
261
+ type: 'EVM',
262
+ zerionId: 'polynomial',
263
+ evmChainId: '8008',
264
+ name: 'Polynomial',
265
+ },
266
+ {
267
+ type: 'EVM',
268
+ zerionId: 'rari',
269
+ evmChainId: '1380012617',
270
+ name: 'Rari',
271
+ },
272
+ {
273
+ type: 'EVM',
274
+ zerionId: 'redstone',
275
+ evmChainId: '690',
276
+ name: 'Redstone',
277
+ },
278
+ {
279
+ type: 'EVM',
280
+ zerionId: 'scroll',
281
+ evmChainId: '534352',
282
+ alchemySlug: 'scroll-mainnet',
283
+ name: 'Scroll',
284
+ },
285
+ {
286
+ type: 'EVM',
287
+ zerionId: 'sei',
288
+ evmChainId: '1329',
289
+ alchemySlug: 'sei-mainnet',
290
+ name: 'Sei',
291
+ },
292
+ {
293
+ type: 'EVM',
294
+ zerionId: 'somnia',
295
+ evmChainId: '5031',
296
+ },
297
+ {
298
+ type: 'EVM',
299
+ zerionId: 'soneium',
300
+ evmChainId: '1868',
301
+ alchemySlug: 'soneium-mainnet',
302
+ name: 'Soneium',
303
+ },
304
+ {
305
+ type: 'EVM',
306
+ zerionId: 'sonic',
307
+ evmChainId: '146',
308
+ alchemySlug: 'sonic-mainnet',
309
+ name: 'Sonic',
310
+ },
311
+ {
312
+ type: 'EVM',
313
+ zerionId: 'swellchain',
314
+ evmChainId: '1923',
315
+ name: 'Swellchain',
316
+ },
317
+ {
318
+ type: 'EVM',
319
+ zerionId: 'taiko',
320
+ evmChainId: '167000',
321
+ alchemySlug: 'taiko-mainnet',
322
+ name: 'Taiko',
323
+ },
324
+ {
325
+ type: 'EVM',
326
+ zerionId: 'unichain',
327
+ evmChainId: '130',
328
+ alchemySlug: 'uni-mainnet',
329
+ name: 'Unichain',
330
+ },
331
+ {
332
+ type: 'EVM',
333
+ zerionId: 'wonder',
334
+ evmChainId: '9637',
335
+ name: 'Wonder',
336
+ },
337
+ {
338
+ type: 'EVM',
339
+ zerionId: 'tomochain',
340
+ evmChainId: '88',
341
+ name: 'Tomochain',
342
+ },
343
+ {
344
+ type: 'EVM',
345
+ zerionId: 'world',
346
+ evmChainId: '480',
347
+ alchemySlug: 'world-mainnet',
348
+ name: 'Worldchain',
349
+ },
350
+ {
351
+ type: 'EVM',
352
+ zerionId: 'zero',
353
+ evmChainId: '543210',
354
+ name: 'Zero',
355
+ },
356
+ {
357
+ type: 'EVM',
358
+ zerionId: 'okbchain',
359
+ evmChainId: '196',
360
+ name: 'OKBChain',
361
+ },
362
+ {
363
+ type: 'EVM',
364
+ zerionId: 'zkcandy',
365
+ evmChainId: '320',
366
+ name: 'ZKCandy',
367
+ },
368
+ {
369
+ type: 'EVM',
370
+ zerionId: 'zklink-nova',
371
+ evmChainId: '810180',
372
+ name: 'ZKLink Nova',
373
+ },
374
+ {
375
+ type: 'EVM',
376
+ zerionId: 'zora',
377
+ evmChainId: '7777777',
378
+ alchemySlug: 'zora-mainnet',
379
+ name: 'Zora',
380
+ },
381
+ {
382
+ type: 'EVM',
383
+ zerionId: 're-al',
384
+ evmChainId: '111188',
385
+ name: 'Re-Al',
386
+ },
387
+ {
388
+ type: 'EVM',
389
+ zerionId: 'katana',
390
+ evmChainId: '747474',
391
+ name: 'Katana',
392
+ },
393
+ {
394
+ type: 'EVM',
395
+ zerionId: 'monad-test-v2',
396
+ evmChainId: '10143',
397
+ name: 'Monad Testnet',
398
+ },
399
+ {
400
+ type: 'EVM',
401
+ zerionId: 'unichain-sepolia',
402
+ evmChainId: '1301',
403
+ name: 'Unichain Sepolia',
404
+ },
405
+ {
406
+ type: 'EVM',
407
+ zerionId: 'zero-sepolia-v2',
408
+ evmChainId: '43210',
409
+ name: 'Zero Sepolia V2',
410
+ },
411
+ {
412
+ type: 'SOLANA',
413
+ internalId: 'SOLANA',
414
+ zerionId: 'solana',
415
+ alchemySlug: 'solana-mainnet',
416
+ name: 'Solana',
417
+ },
418
+ {
419
+ type: 'SOLANA',
420
+ internalId: 'SOLANA_DEVNET',
421
+ alchemySlug: 'solana-devnet',
422
+ logoUrl: 'https://cdn.zerion.io/11111111111111111111111111111111.png',
423
+ explorer: {
424
+ name: 'Solana Explorer',
425
+ url: 'https://explorer.solana.com/?cluster=devnet',
426
+ txUrlFormat: 'https://explorer.solana.com/tx/{HASH}?cluster=devnet',
427
+ },
428
+ name: 'Solana Devnet',
429
+ },
430
+ { type: 'COSMOS', internalId: 'COSMOS', name: 'Cosmos' },
13
431
  ];
14
432
  export function getChainId(network) {
15
433
  return NETWORK_METADATA.find(({ internalId: n }) => n === network)?.evmChainId ?? undefined;
@@ -1,3 +1,5 @@
1
1
  export * from './types/index.js';
2
2
  export * from './utils/index.js';
3
+ import * as transactionTypes from './types/transaction.js';
4
+ export { transactionTypes };
3
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AAEjC,OAAO,KAAK,gBAAgB,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAE,gBAAgB,EAAE,CAAC"}