@leather.io/utils 0.48.2 → 0.49.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.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@leather.io/utils",
3
3
  "author": "Leather.io contact@leather.io",
4
4
  "description": "Shared bitcoin utilities",
5
- "version": "0.48.2",
5
+ "version": "0.49.0",
6
6
  "license": "MIT",
7
7
  "homepage": "https://github.com/leather-io/mono/tree/dev/packages/utils",
8
8
  "repository": {
@@ -19,8 +19,8 @@
19
19
  "dependencies": {
20
20
  "bignumber.js": "9.1.2",
21
21
  "dompurify": "3.2.4",
22
- "@leather.io/constants": "0.27.4",
23
- "@leather.io/models": "0.47.0"
22
+ "@leather.io/constants": "0.28.0",
23
+ "@leather.io/models": "0.49.0"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/dompurify": "3.0.5",
@@ -28,8 +28,8 @@
28
28
  "tsup": "8.4.0",
29
29
  "typescript": "5.8.3",
30
30
  "vitest": "2.1.9",
31
- "@leather.io/prettier-config": "0.9.0",
32
- "@leather.io/tsconfig-config": "0.11.0"
31
+ "@leather.io/tsconfig-config": "0.11.0",
32
+ "@leather.io/prettier-config": "0.9.0"
33
33
  },
34
34
  "keywords": [
35
35
  "leather",
@@ -4,22 +4,13 @@ import {
4
4
  CryptoAssetProtocol,
5
5
  CryptoAssetProtocols,
6
6
  Sip9Asset,
7
- isSip9Asset,
8
7
  } from '@leather.io/models';
9
8
 
10
9
  import { assertUnreachable } from '../index';
11
10
 
12
11
  export function matchesAssetId(asset: CryptoAsset, assetId: CryptoAssetId) {
13
- const protocol = getAssetId(asset).protocol;
14
- const id = getAssetId(asset).id;
15
- if (isSip9Asset(asset)) {
16
- return (
17
- protocol === assetId.protocol &&
18
- assetId.id === asset.assetId &&
19
- assetId.tokenId === asset.tokenId
20
- );
21
- }
22
- return protocol === assetId.protocol && id === assetId.id;
12
+ const assetIdentifier = getAssetId(asset);
13
+ return isSameAssetId(assetIdentifier, assetId);
23
14
  }
24
15
 
25
16
  export function isSameAssetId(assetId1: CryptoAssetId, assetId2: CryptoAssetId) {
@@ -87,10 +78,19 @@ export function getAssetId(asset: CryptoAsset): CryptoAssetId {
87
78
  export function createSip9AssetId(asset: Sip9Asset): CryptoAssetId {
88
79
  return {
89
80
  protocol: asset.protocol,
90
- id: `${asset.assetId}|${asset.tokenId}`,
81
+ id: formatSip9IdField(asset),
91
82
  };
92
83
  }
93
84
 
85
+ export function formatSip9IdField(asset: Sip9Asset) {
86
+ return `${asset.assetId}|${asset.tokenId}`;
87
+ }
88
+
89
+ export function parseSip9IdField(id: string) {
90
+ const [assetId, tokenId] = id.split('|');
91
+ return { assetId, tokenId: Number(tokenId) };
92
+ }
93
+
94
94
  export type SerializedCryptoAssetId = `${string}|${string}`;
95
95
 
96
96
  export function serializeAssetId(assetId: CryptoAssetId): SerializedCryptoAssetId {
@@ -98,7 +98,8 @@ export function serializeAssetId(assetId: CryptoAssetId): SerializedCryptoAssetI
98
98
  }
99
99
 
100
100
  export function deserializeAssetId(serializedAssetId: SerializedCryptoAssetId): CryptoAssetId {
101
- const [protocol, id, tokenId] = serializedAssetId.split('|');
101
+ const [protocol, ...idParts] = serializedAssetId.split('|');
102
+ const id = idParts.join('|');
102
103
  if (!Object.keys(CryptoAssetProtocols).includes(protocol)) {
103
104
  throw new Error(`Unrecognized Asset Protocol: ${protocol}`);
104
105
  }
@@ -106,6 +107,5 @@ export function deserializeAssetId(serializedAssetId: SerializedCryptoAssetId):
106
107
  return {
107
108
  protocol: protocol as CryptoAssetProtocol,
108
109
  id,
109
- tokenId: tokenId ? parseInt(tokenId, 10) : undefined,
110
110
  };
111
111
  }
@@ -14,6 +14,7 @@ describe(createInscriptionAsset.name, () => {
14
14
  genesisBlockHeight: 100,
15
15
  genesisTimestamp: '2025-01-01T12:00:00.000Z',
16
16
  outputValue: '1000',
17
+ thumbnailSrc: 'https://example.com/thumb.png',
17
18
  };
18
19
 
19
20
  it('populates inscription data on asset as expected', () => {
@@ -35,5 +36,29 @@ describe(createInscriptionAsset.name, () => {
35
36
  expect(inscription.genesisTimestamp).toEqual(1735732800);
36
37
  expect(inscription.value).toEqual(mockCreateInscriptionData.outputValue);
37
38
  expect(inscription.mimeType).toEqual('svg');
39
+ expect(inscription.thumbnailSrc).toEqual(mockCreateInscriptionData.thumbnailSrc);
40
+ });
41
+
42
+ it('falls back to ordinal preview when no content source is provided', () => {
43
+ const inscription = createInscriptionAsset({
44
+ ...mockCreateInscriptionData,
45
+ contentSrc: '',
46
+ });
47
+
48
+ expect(inscription.src).toContain('https://ordinals.com/preview/');
49
+ });
50
+
51
+ it.each([
52
+ { mimeType: 'text/html', expectedMimeType: 'html' },
53
+ { mimeType: 'model/gltf+json', expectedMimeType: 'gltf' },
54
+ ])('uses contentSrc for %s inscriptions', ({ mimeType, expectedMimeType }) => {
55
+ const inscription = createInscriptionAsset({
56
+ ...mockCreateInscriptionData,
57
+ mimeType,
58
+ contentSrc: 'https://content.bestinslot.xyz/example',
59
+ });
60
+
61
+ expect(inscription.mimeType).toEqual(expectedMimeType);
62
+ expect(inscription.src).toEqual('https://content.bestinslot.xyz/example');
38
63
  });
39
64
  });
@@ -1,3 +1,4 @@
1
+ import { ORD_IO_URL } from '@leather.io/constants';
1
2
  import {
2
3
  CryptoAssetCategories,
3
4
  CryptoAssetChains,
@@ -49,10 +50,14 @@ export interface CreateInscriptionData {
49
50
  readonly genesisTimestamp: string | number;
50
51
  readonly genesisBlockHeight: number;
51
52
  readonly outputValue: string;
53
+ readonly thumbnailSrc?: string;
52
54
  }
53
55
 
54
56
  export function createInscriptionAsset(data: CreateInscriptionData): InscriptionAsset {
55
- const iframeSrc = `https://ordinals.com/preview/${data.id}`;
57
+ const ordinalPreviewSrc = `https://ordinals.com/preview/${data.id}`;
58
+ const ordIoSrc = `${ORD_IO_URL}/content/${data.id}`;
59
+ const thumbnailSrc = data.thumbnailSrc ?? ordinalPreviewSrc;
60
+ const primarySrc = data.contentSrc || ordinalPreviewSrc;
56
61
  const preview = `https://ordinals.hiro.so/inscription/${data.id}`;
57
62
  const title = `Inscription ${data.number}`;
58
63
  const [txid, output, offset] = data.satPoint.split(':');
@@ -73,6 +78,7 @@ export function createInscriptionAsset(data: CreateInscriptionData): Inscription
73
78
  genesisBlockHash: data.genesisBlockHash,
74
79
  genesisTimestamp: dateToUnixTimestamp(new Date(data.genesisTimestamp)),
75
80
  value: data.outputValue,
81
+ thumbnailSrc,
76
82
  };
77
83
 
78
84
  if (!data.mimeType) {
@@ -88,31 +94,33 @@ export function createInscriptionAsset(data: CreateInscriptionData): Inscription
88
94
  ...sharedInfo,
89
95
  mimeType: 'audio',
90
96
  name: 'inscription',
91
- src: iframeSrc,
97
+ src: primarySrc,
92
98
  }),
93
99
  gltf: () => ({
94
100
  ...sharedInfo,
95
101
  mimeType: 'gltf',
96
102
  name: 'inscription',
97
- src: iframeSrc,
103
+ src: primarySrc,
98
104
  }),
99
105
  html: () => ({
100
106
  ...sharedInfo,
101
107
  mimeType: 'html',
102
108
  name: 'inscription',
103
- src: iframeSrc,
109
+ thumbnailSrc: ordIoSrc,
110
+ src: primarySrc,
104
111
  }),
105
112
  image: () => ({
106
113
  ...sharedInfo,
107
114
  mimeType: 'image',
108
115
  name: 'inscription',
109
- src: data.contentSrc,
116
+ thumbnailSrc: ordIoSrc,
117
+ src: primarySrc,
110
118
  }),
111
119
  svg: () => ({
112
120
  ...sharedInfo,
113
121
  mimeType: 'svg',
114
122
  name: 'inscription',
115
- src: iframeSrc,
123
+ src: primarySrc,
116
124
  }),
117
125
  text: () => ({
118
126
  ...sharedInfo,
@@ -124,13 +132,13 @@ export function createInscriptionAsset(data: CreateInscriptionData): Inscription
124
132
  ...sharedInfo,
125
133
  mimeType: 'video',
126
134
  name: 'inscription',
127
- src: iframeSrc,
135
+ src: primarySrc,
128
136
  }),
129
137
  other: () => ({
130
138
  ...sharedInfo,
131
139
  mimeType: 'other',
132
140
  name: 'inscription',
133
- src: '',
141
+ src: data.contentSrc ?? '',
134
142
  }),
135
143
  });
136
144
  }
package/src/index.ts CHANGED
@@ -17,6 +17,7 @@ export * from './time';
17
17
  export * from './market-data';
18
18
  export * from './currency-formatter/currency-formatter';
19
19
  export * from './flatten-object';
20
+ export * from './special-char';
20
21
 
21
22
  export { spamFilter } from './spam-filter/spam-filter';
22
23
  export { extractPhraseFromString } from './extract-phrase-from-string/extract-phrase-from-string';
@@ -0,0 +1,3 @@
1
+ // A minus sign is a wider character than a hyphen, and should be used in
2
+ // numerical contexts
3
+ export const minusSign = '−';
@@ -0,0 +1,11 @@
1
+ import { describe, expect, test } from 'vitest';
2
+
3
+ import { minusSign } from './special-char';
4
+
5
+ describe('minusSign', () => {
6
+ test('should be the Unicode minus sign character (U+2212)', () => {
7
+ expect(minusSign).toBe('−');
8
+ expect(minusSign.charCodeAt(0)).toBe(0x2212);
9
+ expect(minusSign).not.toBe('-');
10
+ });
11
+ });
package/src/swap.ts ADDED
@@ -0,0 +1,14 @@
1
+ import BigNumber from 'bignumber.js';
2
+
3
+ import type { Money } from '@leather.io/models';
4
+
5
+ import { createMoney } from './money';
6
+
7
+ export function calculateMinToReceiveAmount(quoteAmount: Money, slippage: number): Money {
8
+ const minReceiveAmount = quoteAmount.amount.times(BigNumber(1).minus(slippage));
9
+ return createMoney(
10
+ minReceiveAmount.integerValue(BigNumber.ROUND_DOWN),
11
+ quoteAmount.symbol,
12
+ quoteAmount.decimals
13
+ );
14
+ }