@xswap-link/sdk 0.8.6 → 0.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.
Files changed (41) hide show
  1. package/.github/workflows/main.yml +2 -1
  2. package/.github/workflows/pr_agent.yml +22 -0
  3. package/CHANGELOG.md +12 -0
  4. package/babel.config.cjs +8 -0
  5. package/dist/index.global.js +130 -133
  6. package/dist/index.js +10 -10
  7. package/dist/index.mjs +10 -10
  8. package/jest.config.ts +37 -0
  9. package/package.json +17 -5
  10. package/src/components/Modal/index.tsx +1 -1
  11. package/src/components/Swap/Header/index.tsx +1 -1
  12. package/src/components/Swap/ReorderButton/ReorderButton.tsx +12 -10
  13. package/src/components/Swap/SwapView/FeesPanel/index.tsx +1 -1
  14. package/src/components/Swap/SwapView/SwapButton/index.tsx +23 -21
  15. package/src/components/Swap/SwapView/SwapPanel/AmountPanel/index.tsx +4 -2
  16. package/src/components/Swap/SwapView/SwapPanel/ChainPanel/ChainPicker/ChainItem/index.tsx +5 -7
  17. package/src/components/Swap/SwapView/SwapPanel/ChainPanel/ChainPicker/index.tsx +18 -2
  18. package/src/components/Swap/SwapView/SwapPanel/ChainPanel/index.tsx +1 -24
  19. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/QuickPickTokenItem/index.tsx +2 -11
  20. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/TokenItem/index.tsx +12 -3
  21. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/index.tsx +124 -34
  22. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/index.tsx +1 -1
  23. package/src/components/Swap/SwapView/index.tsx +1 -1
  24. package/src/constants/index.ts +1 -1
  25. package/src/context/HistoryProvider.tsx +2 -2
  26. package/src/context/SwapProvider.tsx +163 -53
  27. package/src/context/TransactionProvider.tsx +1 -1
  28. package/src/utils/validation.ts +239 -186
  29. package/tailwind.config.js +1 -0
  30. package/test/context/SwapProvider.test.tsx +851 -0
  31. package/test/fileMock.ts +1 -0
  32. package/test/fixtures/bridgeTokens.mock.ts +1318 -0
  33. package/test/fixtures/bridgeTokensDictionary.mock.ts +1272 -0
  34. package/test/fixtures/integrationConfig.mock.ts +10 -0
  35. package/test/fixtures/supportedChains.mock.ts +32950 -0
  36. package/test/{api.test.ts → services/getChains.test.ts} +6 -5
  37. package/test/setup.ts +13 -0
  38. package/test/styleMock.ts +1 -0
  39. package/jest.config.json +0 -8
  40. package/test/api.mock.ts +0 -106
  41. package/test/setupTests.ts +0 -3
@@ -1,10 +1,12 @@
1
1
  import {
2
2
  DEFAULT_DESTINATION_CHAIN_ID,
3
+ DEFAULT_DESTINATION_TOKEN_ADDR,
3
4
  DEFAULT_SOURCE_CHAIN_ID,
5
+ DEFAULT_SOURCE_TOKEN_ADDR,
4
6
  } from "@src/constants";
5
7
  import { BridgeTokensDictionary, Chain, Token } from "@src/models";
6
8
 
7
- const getDefaultSourceChain = ({
9
+ export const getDefaultSwapSourceChain = ({
8
10
  supportedChains,
9
11
  }: {
10
12
  supportedChains: Chain[];
@@ -23,7 +25,7 @@ const getDefaultSourceChain = ({
23
25
  return defaultSourceChain;
24
26
  };
25
27
 
26
- const getDefaultDestinationChain = ({
28
+ export const getDefaultDestinationChain = ({
27
29
  supportedChains,
28
30
  }: {
29
31
  supportedChains: Chain[];
@@ -42,15 +44,26 @@ const getDefaultDestinationChain = ({
42
44
  return defaultTargetChain;
43
45
  };
44
46
 
45
- const getDefaultSwapTokenForChain = (
47
+ export const getDefaultSwapTokenForChain = (
46
48
  chain: Chain,
49
+ defaultTokenAddress: string,
47
50
  sameChain?: boolean,
48
51
  sourceTokenId?: string,
49
52
  ) => {
50
- const xswapToken = chain.tokens.find((token) => token.tokenId === "XSWAP");
53
+ // First check if there are any supported tokens at all
54
+ const hasAnySupportedTokens = chain.tokens.some((token) => token.supported);
55
+ if (!hasAnySupportedTokens) {
56
+ return undefined;
57
+ }
51
58
 
52
- if (xswapToken) {
53
- return xswapToken;
59
+ const defaultToken = chain.tokens.find(
60
+ (token) =>
61
+ token.address.toLowerCase() === defaultTokenAddress.toLowerCase() &&
62
+ token.supported,
63
+ );
64
+
65
+ if (defaultToken) {
66
+ return defaultToken;
54
67
  }
55
68
 
56
69
  const fallbackToken = chain.tokens.find((token) =>
@@ -63,16 +76,20 @@ const getDefaultSwapTokenForChain = (
63
76
  return fallbackToken;
64
77
  }
65
78
 
66
- return chain.tokens[0];
79
+ return undefined;
67
80
  };
68
81
 
69
- const getDefaultBridgeTokenForChain = ({
82
+ export const getDefaultBridgeTokenForChain = ({
70
83
  chain,
71
84
  bridgeTokensDictionary,
72
85
  }: {
73
- chain: Chain;
86
+ chain: Chain | undefined;
74
87
  bridgeTokensDictionary: BridgeTokensDictionary;
75
88
  }) => {
89
+ if (!chain) {
90
+ return undefined;
91
+ }
92
+
76
93
  const xswapToken = chain.tokens.find((token) => token.tokenId === "XSWAP");
77
94
 
78
95
  if (xswapToken) {
@@ -108,7 +125,8 @@ const isSwapTargetChainValid = ({
108
125
  );
109
126
  };
110
127
 
111
- const isBridgeSourceValid = (sourceChain: Chain) => sourceChain.bridgeSupported;
128
+ const isBridgeSourceValid = (sourceChain: Chain | undefined) =>
129
+ !sourceChain || sourceChain.bridgeSupported;
112
130
 
113
131
  const isBridgeTargetValid = ({
114
132
  sourceChain,
@@ -116,71 +134,57 @@ const isBridgeTargetValid = ({
116
134
  sourceToken,
117
135
  }: {
118
136
  sourceChain: Chain | undefined;
119
- targetChain: Chain;
137
+ targetChain: Chain | undefined;
120
138
  sourceToken: Token | undefined;
121
139
  }) => {
122
- if (!sourceChain) {
123
- throw new Error(`isBridgeTargetValid > sourceChain should be defined.`);
124
- }
140
+ // We set it to a valid state initially.
141
+ let isSameToken = true;
142
+ let isSameChain = false;
125
143
 
126
- if (
127
- sourceToken &&
128
- !targetChain.tokens.find((token) => token.tokenId === sourceToken!.tokenId)
129
- ) {
144
+ if (sourceToken && targetChain) {
130
145
  // Bridge target chain is invalid when it doesn't have the same token available as selected in the source.
131
- return false;
146
+ isSameToken = !!targetChain.tokens.find(
147
+ (token) => token.tokenId === sourceToken.tokenId,
148
+ );
132
149
  }
133
150
 
134
- return (
135
- targetChain.bridgeSupported &&
136
- // Source and target chains must be different for bridging.
137
- sourceChain.chainId !== targetChain.chainId
138
- );
151
+ if (sourceChain && targetChain) {
152
+ // @TODO use bridge_tokens collection
153
+ // See: https://github.com/xswap-link/xswap-sdk/pull/200#discussion_r1905586346
154
+ isSameChain = sourceChain.chainId === targetChain.chainId;
155
+ }
156
+
157
+ return isSameToken && !isSameChain;
139
158
  };
140
159
 
141
- // It takes chain and token for source and target and returns them.
142
- // If provided data is valid, it returns the same.
143
- // If data is not valid, it returns other available chain/token.
144
- export const mapToValidPath = ({
145
- source,
146
- target,
147
- type,
148
- supportedChains,
149
- bridgeTokensDictionary,
150
- }: {
160
+ type MapToValidPathParams = {
151
161
  source: { chain: Chain | undefined; token: Token | undefined };
152
162
  target: { chain: Chain | undefined; token: Token | undefined };
153
163
  type: "SWAP" | "BRIDGE";
154
164
  supportedChains: Chain[];
155
165
  bridgeTokensDictionary: BridgeTokensDictionary;
156
- }): {
157
- source: { chain: Chain | undefined; token: Token | undefined };
158
- target: { chain: Chain | undefined; token: Token | undefined };
159
- } => {
166
+ };
167
+
168
+ const mapToValidPathSwap = ({
169
+ source,
170
+ target,
171
+ supportedChains,
172
+ }: Omit<MapToValidPathParams, "type">) => {
160
173
  let newSourceChain: Chain | undefined =
161
- source.chain || getDefaultSourceChain({ supportedChains });
174
+ source.chain || getDefaultSwapSourceChain({ supportedChains });
162
175
  let newTargetChain: Chain | undefined =
163
176
  target.chain || getDefaultDestinationChain({ supportedChains });
164
177
  let newSourceToken: Token | undefined =
165
178
  source.token ||
166
- (type === "SWAP"
167
- ? getDefaultSwapTokenForChain(newSourceChain)
168
- : getDefaultBridgeTokenForChain({
169
- chain: newSourceChain,
170
- bridgeTokensDictionary,
171
- }));
179
+ getDefaultSwapTokenForChain(newSourceChain, DEFAULT_SOURCE_TOKEN_ADDR);
172
180
  let newTargetToken: Token | undefined =
173
181
  target.token ||
174
- (type === "SWAP"
175
- ? getDefaultSwapTokenForChain(
176
- newTargetChain,
177
- newSourceChain.chainId === newTargetChain.chainId,
178
- newSourceToken?.tokenId,
179
- )
180
- : getDefaultBridgeTokenForChain({
181
- chain: newTargetChain,
182
- bridgeTokensDictionary,
183
- }));
182
+ getDefaultSwapTokenForChain(
183
+ newTargetChain,
184
+ DEFAULT_DESTINATION_TOKEN_ADDR,
185
+ newSourceChain.chainId === newTargetChain.chainId,
186
+ newSourceToken?.tokenId,
187
+ );
184
188
 
185
189
  if (
186
190
  !newSourceChain ||
@@ -195,28 +199,36 @@ export const mapToValidPath = ({
195
199
  };
196
200
  }
197
201
 
198
- // Setting chains
199
- if (type === "SWAP") {
200
- if (!isSwapSourceValid(newSourceChain)) {
201
- newSourceChain = getDefaultSourceChain({ supportedChains });
202
- // We can't have dangling token from a different chain.
203
- newSourceToken = undefined;
202
+ if (!isSwapSourceValid(newSourceChain)) {
203
+ newSourceChain = getDefaultSwapSourceChain({ supportedChains });
204
+ // We can't have dangling token from a different chain.
205
+ newSourceToken = undefined;
204
206
 
205
- if (!isSwapSourceValid(newSourceChain)) {
206
- // This should never occur if our app is configured properly.
207
- console.error("[WRONG CONFIG] default swap source chain is invalid.");
207
+ if (!isSwapSourceValid(newSourceChain)) {
208
+ // This should never occur if our app is configured properly.
209
+ console.error("[WRONG CONFIG] default swap source chain is invalid.");
208
210
 
209
- // Find any chain that is valid.
210
- newSourceChain = supportedChains.find((chain) =>
211
- isSwapSourceValid(chain),
211
+ // Find any chain that is valid.
212
+ newSourceChain = supportedChains.find((chain) =>
213
+ isSwapSourceValid(chain),
214
+ );
215
+ if (!newSourceChain) {
216
+ throw new Error(
217
+ "mapToValidSwap > None of the chains supports swap. Couldn't set source chain.",
212
218
  );
213
- if (!newSourceChain) {
214
- throw new Error(
215
- "mapToValidSwap > None of the chains supports swap. Couldn't set source chain.",
216
- );
217
- }
218
219
  }
219
220
  }
221
+ }
222
+
223
+ if (
224
+ !isSwapTargetChainValid({
225
+ sourceChain: newSourceChain,
226
+ targetChain: newTargetChain,
227
+ })
228
+ ) {
229
+ newTargetChain = getDefaultDestinationChain({ supportedChains });
230
+ // We can't have dangling token from a different chain.
231
+ newTargetToken = undefined;
220
232
 
221
233
  if (
222
234
  !isSwapTargetChainValid({
@@ -224,54 +236,88 @@ export const mapToValidPath = ({
224
236
  targetChain: newTargetChain,
225
237
  })
226
238
  ) {
227
- newTargetChain = getDefaultDestinationChain({ supportedChains });
228
- // We can't have dangling token from a different chain.
229
- newTargetToken = undefined;
239
+ // This should never occur if our app is configured properly.
240
+ console.error("[WRONG CONFIG] default swap target chain is invalid.");
230
241
 
231
- if (
232
- !isSwapTargetChainValid({
242
+ // Find any chain that is valid.
243
+ newTargetChain = supportedChains.find((chain) =>
244
+ isSwapTargetChainValid({
233
245
  sourceChain: newSourceChain,
234
- targetChain: newTargetChain,
235
- })
236
- ) {
237
- // This should never occur if our app is configured properly.
238
- console.error("[WRONG CONFIG] default swap target chain is invalid.");
239
-
240
- // Find any chain that is valid.
241
- newTargetChain = supportedChains.find((chain) =>
242
- isSwapTargetChainValid({
243
- sourceChain: newSourceChain,
244
- targetChain: chain,
245
- }),
246
+ targetChain: chain,
247
+ }),
248
+ );
249
+ if (!newTargetChain) {
250
+ throw new Error(
251
+ "mapToValidSwap > None of the chains supports swap. Couldn't set target chain.",
246
252
  );
247
- if (!newTargetChain) {
248
- throw new Error(
249
- "mapToValidSwap > None of the chains supports swap. Couldn't set target chain.",
250
- );
251
- }
252
253
  }
253
254
  }
254
- } else {
255
- if (!isBridgeSourceValid(newSourceChain)) {
256
- newSourceChain = getDefaultSourceChain({ supportedChains });
257
- // We can't have dangling token from a different chain.
258
- newSourceToken = undefined;
255
+ }
259
256
 
260
- if (!isBridgeSourceValid(newSourceChain)) {
261
- // This should never occur if our app is configured properly.
262
- console.error("[WRONG CONFIG] default bridge source chain is invalid.");
257
+ // Setting tokens
263
258
 
264
- // Find any chain that is valid.
265
- newSourceChain = supportedChains.find((chain) =>
266
- isBridgeSourceValid(chain),
259
+ if (!newSourceToken) {
260
+ newSourceToken = getDefaultSwapTokenForChain(
261
+ newSourceChain,
262
+ DEFAULT_SOURCE_TOKEN_ADDR,
263
+ );
264
+ }
265
+ if (!newTargetToken || newSourceToken === newTargetToken) {
266
+ newTargetToken = getDefaultSwapTokenForChain(
267
+ newTargetChain,
268
+ DEFAULT_DESTINATION_TOKEN_ADDR,
269
+ newSourceChain.chainId === newTargetChain.chainId,
270
+ newSourceToken?.tokenId,
271
+ );
272
+ }
273
+
274
+ return {
275
+ source: { chain: newSourceChain, token: newSourceToken },
276
+ target: { chain: newTargetChain, token: newTargetToken },
277
+ };
278
+ };
279
+
280
+ const mapToValidPathBridge = ({
281
+ source,
282
+ target,
283
+ supportedChains,
284
+ bridgeTokensDictionary,
285
+ }: Omit<MapToValidPathParams, "type">) => {
286
+ let newSourceChain = source.chain;
287
+ let newTargetChain = target.chain;
288
+ let newSourceToken = source.token;
289
+ let newTargetToken = target.token;
290
+
291
+ if (!isBridgeSourceValid(newSourceChain)) {
292
+ newSourceChain = undefined;
293
+ newSourceToken = undefined;
294
+
295
+ if (!isBridgeSourceValid(newSourceChain)) {
296
+ // This should never occur if our app is configured properly.
297
+ console.error("[WRONG CONFIG] default bridge source chain is invalid.");
298
+
299
+ // Find any chain that is valid.
300
+ newSourceChain = supportedChains.find((chain) =>
301
+ isBridgeSourceValid(chain),
302
+ );
303
+ if (!newSourceChain) {
304
+ throw new Error(
305
+ "mapToValidSwap > None of the chains supports bridge. Couldn't set source chain.",
267
306
  );
268
- if (!newSourceChain) {
269
- throw new Error(
270
- "mapToValidSwap > None of the chains supports bridge. Couldn't set source chain.",
271
- );
272
- }
273
307
  }
274
308
  }
309
+ }
310
+
311
+ if (
312
+ !isBridgeTargetValid({
313
+ sourceChain: newSourceChain,
314
+ targetChain: newTargetChain,
315
+ sourceToken: newSourceToken,
316
+ })
317
+ ) {
318
+ newTargetChain = undefined;
319
+ // We can't have dangling token from a different chain.
320
+ newTargetToken = undefined;
275
321
 
276
322
  if (
277
323
  !isBridgeTargetValid({
@@ -280,100 +326,90 @@ export const mapToValidPath = ({
280
326
  sourceToken: newSourceToken,
281
327
  })
282
328
  ) {
283
- newTargetChain = getDefaultDestinationChain({ supportedChains });
284
- // We can't have dangling token from a different chain.
285
- newTargetToken = undefined;
329
+ // This should never occur if our app is configured properly.
330
+ console.error("[WRONG CONFIG] default bridge target chain is invalid.");
286
331
 
287
- if (
288
- !isBridgeTargetValid({
332
+ // Find any chain that is valid.
333
+ newTargetChain = supportedChains.find((chain) =>
334
+ isBridgeTargetValid({
289
335
  sourceChain: newSourceChain,
290
- targetChain: newTargetChain,
336
+ targetChain: chain,
291
337
  sourceToken: newSourceToken,
292
- })
293
- ) {
294
- // This should never occur if our app is configured properly.
295
- console.error("[WRONG CONFIG] default bridge target chain is invalid.");
296
-
297
- // Find any chain that is valid.
298
- newTargetChain = supportedChains.find((chain) =>
299
- isBridgeTargetValid({
300
- sourceChain: newSourceChain,
301
- targetChain: chain,
302
- sourceToken: newSourceToken,
303
- }),
338
+ }),
339
+ );
340
+ if (!newTargetChain) {
341
+ throw new Error(
342
+ "mapToValidSwap > None of the chains supports bridge. Couldn't set target chain.",
304
343
  );
305
- if (!newTargetChain) {
306
- throw new Error(
307
- "mapToValidSwap > None of the chains supports swap. Couldn't set target chain.",
308
- );
309
- }
310
344
  }
311
345
  }
312
346
  }
313
347
 
314
348
  // Setting tokens
315
- if (type === "SWAP") {
316
- if (!newSourceToken) {
317
- newSourceToken = getDefaultSwapTokenForChain(newSourceChain);
318
- }
319
- if (!newTargetToken) {
320
- newTargetToken = getDefaultSwapTokenForChain(
321
- newTargetChain,
322
- newSourceChain.chainId === newTargetChain.chainId,
323
- newSourceToken?.tokenId,
324
- );
349
+
350
+ // check if the source token is bridgeable
351
+ if (newSourceToken && newSourceChain && newTargetChain) {
352
+ const isBridgeable =
353
+ !!bridgeTokensDictionary[newSourceChain.chainId]?.[
354
+ newSourceToken.address.toLowerCase()
355
+ ]?.[newTargetChain.chainId];
356
+
357
+ if (!isBridgeable) {
358
+ newSourceToken = undefined;
325
359
  }
326
- } else {
327
- if (!newSourceToken) {
328
- const defaultToken = getDefaultBridgeTokenForChain({
329
- chain: newSourceChain,
330
- bridgeTokensDictionary,
331
- });
332
-
333
- if (defaultToken) {
334
- const isDefaultTokenBridgeable =
335
- !!bridgeTokensDictionary[newSourceChain.chainId]?.[
336
- defaultToken.address.toLowerCase()
337
- ]?.[newTargetChain.chainId];
338
-
339
- if (isDefaultTokenBridgeable) {
340
- newSourceToken = defaultToken;
341
- } else {
342
- // This should never occur if our app is configured properly.
343
- console.error(
344
- "[WRONG CONFIG] default bridge source token is invalid.",
345
- );
346
- // Default is invalid, so let's keep it unset.
347
- newSourceToken = undefined;
348
- }
360
+ }
361
+
362
+ if (!newSourceToken) {
363
+ const defaultToken = getDefaultBridgeTokenForChain({
364
+ chain: newSourceChain,
365
+ bridgeTokensDictionary,
366
+ });
367
+
368
+ if (defaultToken && newSourceChain && newTargetChain) {
369
+ const isDefaultTokenBridgeable =
370
+ !!bridgeTokensDictionary[newSourceChain.chainId]?.[
371
+ defaultToken.address.toLowerCase()
372
+ ]?.[newTargetChain.chainId];
373
+
374
+ if (isDefaultTokenBridgeable) {
375
+ newSourceToken = defaultToken;
349
376
  } else {
350
- // Default was not found, so let's keep it unset.
377
+ // This should never occur if our app is configured properly.
378
+ console.error("[WRONG CONFIG] default bridge source token is invalid.");
379
+ // Default is invalid, so let's keep it unset.
351
380
  newSourceToken = undefined;
352
381
  }
382
+ } else {
383
+ // Default was not found, so let's keep it unset.
384
+ newSourceToken = undefined;
353
385
  }
386
+ }
354
387
 
355
- if (newSourceToken?.tokenId !== newTargetToken?.tokenId) {
388
+ if (newSourceToken?.tokenId !== newTargetToken?.tokenId) {
389
+ newTargetToken = undefined;
390
+ }
391
+
392
+ if (!newTargetToken) {
393
+ if (newSourceToken && newSourceChain && newTargetChain) {
394
+ const correspondingTokenAddress =
395
+ bridgeTokensDictionary[newSourceChain.chainId]?.[
396
+ newSourceToken.address.toLowerCase()
397
+ ]?.[newTargetChain.chainId];
398
+
399
+ const correspondingToken = newTargetChain.tokens.find(
400
+ (token) =>
401
+ token.address.toLowerCase() ===
402
+ correspondingTokenAddress?.toLowerCase(),
403
+ );
404
+ newTargetToken = correspondingToken;
405
+ } else {
406
+ // If source token is not defined, destination token can't be defined as well.
356
407
  newTargetToken = undefined;
357
408
  }
409
+ }
358
410
 
359
- if (!newTargetToken) {
360
- if (newSourceToken) {
361
- const correspondingTokenAddress =
362
- bridgeTokensDictionary[newSourceChain.chainId]?.[
363
- newSourceToken.address.toLowerCase()
364
- ]?.[newTargetChain.chainId];
365
-
366
- const correspondingToken = newTargetChain.tokens.find(
367
- (token) =>
368
- token.address.toLowerCase() ===
369
- correspondingTokenAddress?.toLowerCase(),
370
- );
371
- newTargetToken = correspondingToken;
372
- } else {
373
- // If source token is not defined, destination token can't be defined as well.
374
- newTargetToken = undefined;
375
- }
376
- }
411
+ if (!newTargetToken) {
412
+ newTargetChain = undefined;
377
413
  }
378
414
 
379
415
  return {
@@ -381,3 +417,20 @@ export const mapToValidPath = ({
381
417
  target: { chain: newTargetChain, token: newTargetToken },
382
418
  };
383
419
  };
420
+
421
+ // It takes chain and token for source and target and returns them.
422
+ // If provided data is valid, it returns the same.
423
+ // If data is not valid, it returns other available chain/token.
424
+ export const mapToValidPath = ({
425
+ type,
426
+ ...params
427
+ }: MapToValidPathParams): {
428
+ source: { chain: Chain | undefined; token: Token | undefined };
429
+ target: { chain: Chain | undefined; token: Token | undefined };
430
+ } => {
431
+ if (type === "SWAP") {
432
+ return mapToValidPathSwap(params);
433
+ } else {
434
+ return mapToValidPathBridge(params);
435
+ }
436
+ };
@@ -6,6 +6,7 @@ export default {
6
6
  fontFamily: {
7
7
  body: ["'Satoshi-Medium'", "Tahoma", "Verdana", "ui-sans-serif"],
8
8
  sans: ["Satoshi-Medium", "Tahoma", "Verdana", "ui-sans-serif"],
9
+ "sans-bold": ["Satoshi-Bold", "Tahoma", "Verdana", "ui-sans-serif"],
9
10
  },
10
11
  extend: {
11
12
  colors: {