@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/.turbo/turbo-build.log +6 -6
- package/CHANGELOG.md +64 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.js +36 -21
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/assets/asset-id.ts +14 -14
- package/src/assets/inscription-helpers.spec.ts +25 -0
- package/src/assets/inscription-helpers.ts +16 -8
- package/src/index.ts +1 -0
- package/src/special-char.ts +3 -0
- package/src/special-chars.spec.ts +11 -0
- package/src/swap.ts +14 -0
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.
|
|
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.
|
|
23
|
-
"@leather.io/models": "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/
|
|
32
|
-
"@leather.io/
|
|
31
|
+
"@leather.io/tsconfig-config": "0.11.0",
|
|
32
|
+
"@leather.io/prettier-config": "0.9.0"
|
|
33
33
|
},
|
|
34
34
|
"keywords": [
|
|
35
35
|
"leather",
|
package/src/assets/asset-id.ts
CHANGED
|
@@ -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
|
|
14
|
-
|
|
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:
|
|
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,
|
|
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
|
|
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:
|
|
97
|
+
src: primarySrc,
|
|
92
98
|
}),
|
|
93
99
|
gltf: () => ({
|
|
94
100
|
...sharedInfo,
|
|
95
101
|
mimeType: 'gltf',
|
|
96
102
|
name: 'inscription',
|
|
97
|
-
src:
|
|
103
|
+
src: primarySrc,
|
|
98
104
|
}),
|
|
99
105
|
html: () => ({
|
|
100
106
|
...sharedInfo,
|
|
101
107
|
mimeType: 'html',
|
|
102
108
|
name: 'inscription',
|
|
103
|
-
|
|
109
|
+
thumbnailSrc: ordIoSrc,
|
|
110
|
+
src: primarySrc,
|
|
104
111
|
}),
|
|
105
112
|
image: () => ({
|
|
106
113
|
...sharedInfo,
|
|
107
114
|
mimeType: 'image',
|
|
108
115
|
name: 'inscription',
|
|
109
|
-
|
|
116
|
+
thumbnailSrc: ordIoSrc,
|
|
117
|
+
src: primarySrc,
|
|
110
118
|
}),
|
|
111
119
|
svg: () => ({
|
|
112
120
|
...sharedInfo,
|
|
113
121
|
mimeType: 'svg',
|
|
114
122
|
name: 'inscription',
|
|
115
|
-
src:
|
|
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:
|
|
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,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
|
+
}
|