@gibs/bridge-sdk 1.6.27 → 1.6.33
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/src/abis.d.ts +356 -0
- package/dist/src/abis.js +54 -0
- package/dist/src/chain-info.d.ts +78 -0
- package/dist/src/chain-info.js +312 -0
- package/dist/src/chains.d.ts +6 -0
- package/dist/src/chains.js +86 -0
- package/dist/src/config.d.ts +51 -0
- package/dist/src/config.js +313 -0
- package/dist/src/fee-type.d.ts +7 -0
- package/dist/src/fee-type.js +7 -0
- package/dist/src/image-links.d.ts +12 -0
- package/dist/src/image-links.js +17 -0
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.js +6 -0
- package/dist/src/types.d.ts +35 -0
- package/dist/src/types.js +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import { zeroAddress } from "viem";
|
|
2
|
+
import _ from 'lodash';
|
|
3
|
+
import * as imageLinks from '@gibs/bridge-sdk/image-links';
|
|
4
|
+
/**
|
|
5
|
+
* Add here your chain id as hex, be sure to add your chain in the chainsMetadata object too
|
|
6
|
+
*/
|
|
7
|
+
export var Chains;
|
|
8
|
+
(function (Chains) {
|
|
9
|
+
Chains["PLS"] = "0x171";
|
|
10
|
+
Chains["ETH"] = "0x1";
|
|
11
|
+
Chains["BNB"] = "0x38";
|
|
12
|
+
// testnets
|
|
13
|
+
Chains["SEP"] = "0xaa36a7";
|
|
14
|
+
Chains["V4PLS"] = "0x3af";
|
|
15
|
+
// TBNB = '0x61',
|
|
16
|
+
})(Chains || (Chains = {}));
|
|
17
|
+
export const chainIdToKey = new Map(Object.entries(Chains).map(([key, chain]) => [chain, key]));
|
|
18
|
+
export const toChain = (chainId) => `0x${Number(chainId).toString(16)}`;
|
|
19
|
+
export const toChainKey = (chainId) => chainIdToKey.get(toChain(chainId));
|
|
20
|
+
export var Provider;
|
|
21
|
+
(function (Provider) {
|
|
22
|
+
Provider["PULSECHAIN"] = "pulsechain";
|
|
23
|
+
Provider["TOKENSEX"] = "tokensex";
|
|
24
|
+
})(Provider || (Provider = {}));
|
|
25
|
+
export const nativeAssetOut = {
|
|
26
|
+
[Chains.ETH]: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
|
|
27
|
+
[Chains.BNB]: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c',
|
|
28
|
+
[Chains.PLS]: '0xA1077a294dDE1B09bB078844df40758a5D0f9a27',
|
|
29
|
+
[Chains.SEP]: '0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9',
|
|
30
|
+
[Chains.V4PLS]: '0x70499adEBB11Efd915E3b69E700c331778628707',
|
|
31
|
+
};
|
|
32
|
+
export const uniV2Routers = {
|
|
33
|
+
[Chains.PLS]: [
|
|
34
|
+
'0x98bf93ebf5c380C0e6Ae8e192A7e2AE08edAcc02',
|
|
35
|
+
'0x165C3410fC91EF562C50559f7d2289fEbed552d9',
|
|
36
|
+
],
|
|
37
|
+
[Chains.ETH]: ['0x7a250d5630b4cf539739df2c5dacb4c659f2488d'],
|
|
38
|
+
[Chains.BNB]: ['0x05fF2B0DB69458A0750badebc4f9e13aDd608C7F'],
|
|
39
|
+
[Chains.SEP]: ['0xeE567Fe1712Faf6149d80dA1E6934E354124CfE3'],
|
|
40
|
+
[Chains.V4PLS]: [
|
|
41
|
+
'0xDaE9dd3d1A52CfCe9d5F2fAC7fDe164D500E50f7',
|
|
42
|
+
'0x636f6407B90661b73b1C0F7e24F4C79f624d0738',
|
|
43
|
+
],
|
|
44
|
+
};
|
|
45
|
+
export const nativeTokenSymbol = {
|
|
46
|
+
[Chains.PLS]: 'PLS',
|
|
47
|
+
[Chains.V4PLS]: 'V4PLS',
|
|
48
|
+
[Chains.ETH]: 'ETH',
|
|
49
|
+
[Chains.SEP]: 'sepETH',
|
|
50
|
+
[Chains.BNB]: 'BNB',
|
|
51
|
+
};
|
|
52
|
+
export const nativeTokenName = {
|
|
53
|
+
[Chains.PLS]: 'Pulse',
|
|
54
|
+
[Chains.V4PLS]: 'V4 Pulse',
|
|
55
|
+
[Chains.ETH]: 'Ether',
|
|
56
|
+
[Chains.SEP]: 'Sepolia Ether',
|
|
57
|
+
[Chains.BNB]: 'BNB',
|
|
58
|
+
};
|
|
59
|
+
export const pathways = {
|
|
60
|
+
[Provider.PULSECHAIN]: {
|
|
61
|
+
[Chains.PLS]: {
|
|
62
|
+
[Chains.ETH]: {
|
|
63
|
+
from: '0x4fD0aaa7506f3d9cB8274bdB946Ec42A1b8751Ef',
|
|
64
|
+
to: '0x1715a3E4A142d8b698131108995174F37aEBA10D',
|
|
65
|
+
destinationRouter: '0x2Ed02372C3efF1F239B22a901ceA8Cf6fbf82d8d',
|
|
66
|
+
nativeRouter: '0x612Da68aA7BcA625dD2F27f252cddB573674991A',
|
|
67
|
+
validator: '0x5ECfE77502317F3677f23C3b8Ab17929ACE3D74E',
|
|
68
|
+
usesExtraParam: false,
|
|
69
|
+
feeManager: 'from',
|
|
70
|
+
toHome: false,
|
|
71
|
+
requiresDelivery: true,
|
|
72
|
+
defaultAssetIn: {
|
|
73
|
+
symbol: 'WETH',
|
|
74
|
+
name: 'Wrapped Ether from Ethereum',
|
|
75
|
+
address: '0x02DcdD04e3F455D838cd1249292C58f3B79e3C3C',
|
|
76
|
+
decimals: 18,
|
|
77
|
+
chainId: 369,
|
|
78
|
+
logoURI: imageLinks.network(369),
|
|
79
|
+
extensions: {
|
|
80
|
+
bridgeInfo: {
|
|
81
|
+
'1': {
|
|
82
|
+
tokenAddress: nativeAssetOut[Chains.ETH],
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
[Chains.ETH]: {
|
|
90
|
+
[Chains.PLS]: {
|
|
91
|
+
from: '0x1715a3E4A142d8b698131108995174F37aEBA10D',
|
|
92
|
+
to: '0x4fD0aaa7506f3d9cB8274bdB946Ec42A1b8751Ef',
|
|
93
|
+
nativeRouter: '0x2Ed02372C3efF1F239B22a901ceA8Cf6fbf82d8d',
|
|
94
|
+
destinationRouter: '0x612Da68aA7BcA625dD2F27f252cddB573674991A',
|
|
95
|
+
validator: '0x5ECfE77502317F3677f23C3b8Ab17929ACE3D74E',
|
|
96
|
+
feeManager: 'to',
|
|
97
|
+
usesExtraParam: false,
|
|
98
|
+
toHome: true,
|
|
99
|
+
requiresDelivery: false,
|
|
100
|
+
defaultAssetIn: {
|
|
101
|
+
chainId: 1,
|
|
102
|
+
address: nativeAssetOut[Chains.ETH],
|
|
103
|
+
name: 'Wrapped Ether',
|
|
104
|
+
symbol: 'WETH',
|
|
105
|
+
decimals: 18,
|
|
106
|
+
logoURI: imageLinks.network(1),
|
|
107
|
+
extensions: {
|
|
108
|
+
bridgeInfo: {
|
|
109
|
+
'369': {
|
|
110
|
+
tokenAddress: '0x02DcdD04e3F455D838cd1249292C58f3B79e3C3C',
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
[Provider.TOKENSEX]: {
|
|
119
|
+
[Chains.PLS]: {
|
|
120
|
+
[Chains.BNB]: {
|
|
121
|
+
from: '0xf1DFc63e10fF01b8c3d307529b47AefaD2154C0e',
|
|
122
|
+
to: '0xb4005881e81a6ecd2c1f75d58e8e41f28d59c6b1',
|
|
123
|
+
destinationRouter: '0x9c50EfD36dF2DBD310fBC981d6D8478cF3484B85',
|
|
124
|
+
nativeRouter: '0xB80B80EBB375BD83a9d6c0761b197d16a8670f81',
|
|
125
|
+
validator: '0xc3c3d5d3ba946a2eb3906878ebe187418b0b524e',
|
|
126
|
+
feeManager: 'from',
|
|
127
|
+
usesExtraParam: true,
|
|
128
|
+
toHome: false,
|
|
129
|
+
requiresDelivery: true,
|
|
130
|
+
defaultAssetIn: {
|
|
131
|
+
symbol: 'WBNB',
|
|
132
|
+
name: 'Wrapped BNB',
|
|
133
|
+
address: '0x518076CCE3729eF1a3877EA3647a26e278e764FE',
|
|
134
|
+
decimals: 18,
|
|
135
|
+
chainId: 369,
|
|
136
|
+
logoURI: imageLinks.network(369),
|
|
137
|
+
extensions: {
|
|
138
|
+
bridgeInfo: {
|
|
139
|
+
'56': {
|
|
140
|
+
tokenAddress: nativeAssetOut[Chains.BNB],
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
[Chains.BNB]: {
|
|
148
|
+
[Chains.PLS]: {
|
|
149
|
+
from: '0xb4005881e81a6ecd2c1f75d58e8e41f28d59c6b1',
|
|
150
|
+
to: '0xf1DFc63e10fF01b8c3d307529b47AefaD2154C0e',
|
|
151
|
+
nativeRouter: '0x9c50EfD36dF2DBD310fBC981d6D8478cF3484B85',
|
|
152
|
+
destinationRouter: '0xB80B80EBB375BD83a9d6c0761b197d16a8670f81',
|
|
153
|
+
validator: '0xc3c3d5d3ba946a2eb3906878ebe187418b0b524e',
|
|
154
|
+
feeManager: 'to',
|
|
155
|
+
usesExtraParam: true,
|
|
156
|
+
toHome: true,
|
|
157
|
+
requiresDelivery: false,
|
|
158
|
+
defaultAssetIn: {
|
|
159
|
+
chainId: 56,
|
|
160
|
+
address: nativeAssetOut[Chains.BNB],
|
|
161
|
+
name: 'Wrapped BNB from BSC',
|
|
162
|
+
symbol: 'WBNB',
|
|
163
|
+
decimals: 18,
|
|
164
|
+
logoURI: imageLinks.network(56),
|
|
165
|
+
extensions: {
|
|
166
|
+
bridgeInfo: {
|
|
167
|
+
'369': {
|
|
168
|
+
tokenAddress: '0x518076CCE3729eF1a3877EA3647a26e278e764FE',
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
export const testnetPathways = {
|
|
178
|
+
[Provider.TOKENSEX]: {
|
|
179
|
+
// plsv4 to bnb home omnibridge: 0xfd0c07C376D27EA0E9fA64Ac5D2481439e36CF9C
|
|
180
|
+
// [Chains.BNB]: {
|
|
181
|
+
// [Chains.V4PLS]: {
|
|
182
|
+
// from: '0xDDf1b0CD7fb7AAA310c703C17995c6671F9DC806',
|
|
183
|
+
// to: '0xfd0c07C376D27EA0E9fA64Ac5D2481439e36CF9C',
|
|
184
|
+
// usesExtraParam: false,
|
|
185
|
+
// requiresDelivery: false,
|
|
186
|
+
// toHome: true,
|
|
187
|
+
// feeManager: 'to',
|
|
188
|
+
// defaultAssetIn: {
|
|
189
|
+
// chainId: 56,
|
|
190
|
+
// address: nativeAssetOut[Chains.TBNB],
|
|
191
|
+
// },
|
|
192
|
+
// },
|
|
193
|
+
// },
|
|
194
|
+
// [Chains.V4PLS]: {
|
|
195
|
+
// [Chains.BNB]: {
|
|
196
|
+
// from: '0xfd0c07C376D27EA0E9fA64Ac5D2481439e36CF9C',
|
|
197
|
+
// to: '0xDDf1b0CD7fb7AAA310c703C17995c6671F9DC806',
|
|
198
|
+
// },
|
|
199
|
+
// },
|
|
200
|
+
},
|
|
201
|
+
[Provider.PULSECHAIN]: {
|
|
202
|
+
[Chains.V4PLS]: {
|
|
203
|
+
[Chains.SEP]: {
|
|
204
|
+
from: '0x6B08a50865aDeCe6e3869D9AfbB316d0a0436B6c',
|
|
205
|
+
to: '0x546e37DAA15cdb82fd1a717E5dEEa4AF08D4349A',
|
|
206
|
+
nativeRouter: '0xCA1BDc038909038C4B3cbab0EA0ec3cC2839F106',
|
|
207
|
+
destinationRouter: '0xCb4057d7DA4CE521B93b71186DfE11f9FBC3e3b1',
|
|
208
|
+
validator: '0xDcd4D88552c86114b2ca029F7F8d4e1a7951d051',
|
|
209
|
+
usesExtraParam: false,
|
|
210
|
+
requiresDelivery: true,
|
|
211
|
+
toHome: false,
|
|
212
|
+
feeManager: 'from',
|
|
213
|
+
defaultAssetIn: {
|
|
214
|
+
chainId: 943,
|
|
215
|
+
address: '0x3677bd78CCf4d299328ECFBa61790cf8dBfcF686',
|
|
216
|
+
name: 'Wrapped Ether from Sepolia',
|
|
217
|
+
symbol: 'WsepETH',
|
|
218
|
+
decimals: 18,
|
|
219
|
+
logoURI: imageLinks.network(943),
|
|
220
|
+
extensions: {
|
|
221
|
+
bridgeInfo: {
|
|
222
|
+
'11155111': {
|
|
223
|
+
tokenAddress: nativeAssetOut[Chains.SEP],
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
[Chains.SEP]: {
|
|
231
|
+
[Chains.V4PLS]: {
|
|
232
|
+
from: '0x546e37DAA15cdb82fd1a717E5dEEa4AF08D4349A',
|
|
233
|
+
to: '0x6B08a50865aDeCe6e3869D9AfbB316d0a0436B6c',
|
|
234
|
+
nativeRouter: '0xCb4057d7DA4CE521B93b71186DfE11f9FBC3e3b1',
|
|
235
|
+
destinationRouter: '0xCA1BDc038909038C4B3cbab0EA0ec3cC2839F106',
|
|
236
|
+
validator: '0xDcd4D88552c86114b2ca029F7F8d4e1a7951d051',
|
|
237
|
+
usesExtraParam: false,
|
|
238
|
+
requiresDelivery: false,
|
|
239
|
+
toHome: true,
|
|
240
|
+
feeManager: 'to',
|
|
241
|
+
defaultAssetIn: {
|
|
242
|
+
chainId: 11_155_111,
|
|
243
|
+
address: nativeAssetOut[Chains.SEP],
|
|
244
|
+
name: 'Wrapped Ether',
|
|
245
|
+
symbol: 'sepWETH',
|
|
246
|
+
decimals: 18,
|
|
247
|
+
logoURI: imageLinks.network(11_155_111),
|
|
248
|
+
extensions: {
|
|
249
|
+
bridgeInfo: {
|
|
250
|
+
'943': {
|
|
251
|
+
tokenAddress: '0x3677bd78CCf4d299328ECFBa61790cf8dBfcF686',
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
const mainnetBridgeKeys = [
|
|
261
|
+
[Provider.PULSECHAIN, Chains.PLS, Chains.ETH],
|
|
262
|
+
[Provider.PULSECHAIN, Chains.ETH, Chains.PLS],
|
|
263
|
+
[Provider.TOKENSEX, Chains.PLS, Chains.BNB],
|
|
264
|
+
[Provider.TOKENSEX, Chains.BNB, Chains.PLS],
|
|
265
|
+
];
|
|
266
|
+
const testnetBridgeKeys = [
|
|
267
|
+
[Provider.PULSECHAIN, Chains.SEP, Chains.V4PLS],
|
|
268
|
+
[Provider.PULSECHAIN, Chains.V4PLS, Chains.SEP],
|
|
269
|
+
];
|
|
270
|
+
export const validBridgeKeys = (isProd) => [
|
|
271
|
+
...mainnetBridgeKeys,
|
|
272
|
+
...(isProd ? [] : testnetBridgeKeys),
|
|
273
|
+
];
|
|
274
|
+
export const inferBridgeKey = ({ currentKey, provider, fromChain, toChain, }) => {
|
|
275
|
+
const [, fromChainKey, toChainKey] = currentKey;
|
|
276
|
+
const from = fromChain ?? fromChainKey;
|
|
277
|
+
const to = toChain ?? toChainKey;
|
|
278
|
+
const currentFromChainIsValid = !!_.get(pathways, [provider, from]);
|
|
279
|
+
const nextFromChain = currentFromChainIsValid
|
|
280
|
+
? from
|
|
281
|
+
: Object.keys(pathways[provider])[0];
|
|
282
|
+
const currentToChainIsValid = !!_.get(pathways, [provider, nextFromChain, to]);
|
|
283
|
+
const nextToChain = currentToChainIsValid
|
|
284
|
+
? to
|
|
285
|
+
: Object.keys(pathways[provider][nextFromChain])[0];
|
|
286
|
+
return [provider, nextFromChain, nextToChain];
|
|
287
|
+
};
|
|
288
|
+
export const pathway = (bridgeKey, isProd) => {
|
|
289
|
+
if (!bridgeKey)
|
|
290
|
+
return;
|
|
291
|
+
return _.get(pathways, bridgeKey) || (!isProd ? _.get(testnetPathways, bridgeKey) : undefined);
|
|
292
|
+
};
|
|
293
|
+
export const defaultAssetIn = ($bridgeKey, isProd) => {
|
|
294
|
+
const conf = pathway($bridgeKey, isProd);
|
|
295
|
+
const defaultAssetIn = _.get(conf, ['defaultAssetIn']);
|
|
296
|
+
return defaultAssetIn;
|
|
297
|
+
};
|
|
298
|
+
export const isNative = (asset, bridgeKey) => {
|
|
299
|
+
if (!bridgeKey || !asset) {
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
return ((asset.address === zeroAddress ||
|
|
303
|
+
nativeAssetOut[toChain(asset.chainId)]?.toLowerCase() === asset.address?.toLowerCase()) &&
|
|
304
|
+
!asset.name.includes(' from Pulsechain'));
|
|
305
|
+
};
|
|
306
|
+
export const isUnwrappable = (asset, bridgeKey) => {
|
|
307
|
+
if (!bridgeKey || !asset) {
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
const [, , toChain] = bridgeKey;
|
|
311
|
+
return nativeAssetOut[toChain] === asset.extensions?.bridgeInfo?.[Number(toChain)]?.tokenAddress;
|
|
312
|
+
};
|
|
313
|
+
export const canChangeUnwrap = (bridgeKey, assetIn) => !!assetIn && isUnwrappable(assetIn, bridgeKey);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const setImageRoot: (root: string) => void;
|
|
2
|
+
export declare const getImageRoot: () => string;
|
|
3
|
+
interface MinimalTokenInfo {
|
|
4
|
+
chainId: number;
|
|
5
|
+
address: string;
|
|
6
|
+
logoURI?: string | null;
|
|
7
|
+
}
|
|
8
|
+
export declare const list: (path: string) => string;
|
|
9
|
+
export declare const network: (chainId: number) => string;
|
|
10
|
+
export declare const image: (t: MinimalTokenInfo) => string;
|
|
11
|
+
export declare const images: (sources: string[]) => string;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getAddress } from 'viem';
|
|
2
|
+
let imageRoot = 'https://gib.show';
|
|
3
|
+
export const setImageRoot = (root) => {
|
|
4
|
+
imageRoot = root;
|
|
5
|
+
};
|
|
6
|
+
export const getImageRoot = () => imageRoot;
|
|
7
|
+
export const list = (path) => {
|
|
8
|
+
return `${imageRoot}/list${path}`;
|
|
9
|
+
};
|
|
10
|
+
export const network = (chainId) => {
|
|
11
|
+
if (!chainId) {
|
|
12
|
+
console.trace('network image');
|
|
13
|
+
}
|
|
14
|
+
return `${imageRoot}/image/${Number(chainId)}`;
|
|
15
|
+
};
|
|
16
|
+
export const image = (t) => t.logoURI || `${network(t.chainId)}/${getAddress(t.address)}`;
|
|
17
|
+
export const images = (sources) => `${imageRoot}/image/?${sources.map((s) => `i=${s}`).join('&')}`;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Hex, Chain } from 'viem';
|
|
2
|
+
import type { Chains, Provider } from './config.js';
|
|
3
|
+
export type PerNetworkBridgeLink = {
|
|
4
|
+
tokenAddress: Hex;
|
|
5
|
+
};
|
|
6
|
+
export type Extensions = {
|
|
7
|
+
wrapped?: {
|
|
8
|
+
address: Hex;
|
|
9
|
+
};
|
|
10
|
+
bridgeInfo?: Record<number, PerNetworkBridgeLink>;
|
|
11
|
+
};
|
|
12
|
+
export type TokenMetadata = {
|
|
13
|
+
name: string;
|
|
14
|
+
symbol: string;
|
|
15
|
+
decimals: number;
|
|
16
|
+
};
|
|
17
|
+
export type Token = TokenMetadata & {
|
|
18
|
+
address: string;
|
|
19
|
+
chainId: number;
|
|
20
|
+
logoURI: string | null;
|
|
21
|
+
extensions?: Extensions;
|
|
22
|
+
};
|
|
23
|
+
export type TokenOut = Omit<Token, 'address'> & {
|
|
24
|
+
address: string | null;
|
|
25
|
+
};
|
|
26
|
+
export type TokenList = {
|
|
27
|
+
tokens: Token[];
|
|
28
|
+
};
|
|
29
|
+
export type BridgeKey = [Provider, Chains, Chains];
|
|
30
|
+
export type VisualChain = Chain & {
|
|
31
|
+
chainId: Chains;
|
|
32
|
+
logoURI: string;
|
|
33
|
+
alt: string;
|
|
34
|
+
};
|
|
35
|
+
export type ChainsMetadata = Record<Chains, VisualChain>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|