@talismn/orb 0.2.1 → 0.3.1
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/CHANGELOG.md +18 -1
- package/README.md +3 -0
- package/dist/declarations/src/components/TalismanOrb.d.ts +3 -1
- package/dist/declarations/src/util/lib/ethAddress.d.ts +2 -0
- package/dist/declarations/src/util/lib/index.d.ts +2 -0
- package/dist/declarations/src/util/lib/subAddress.d.ts +1 -0
- package/dist/declarations/src/util/normalizeAddress.d.ts +1 -0
- package/dist/talismn-orb.cjs.dev.js +203 -31
- package/dist/talismn-orb.cjs.prod.js +203 -31
- package/dist/talismn-orb.esm.js +202 -29
- package/package.json +16 -16
- package/src/components/TalismanOrb.tsx +110 -22
- package/src/util/lib/ethAddress.ts +29 -0
- package/src/util/lib/index.ts +2 -0
- package/src/util/lib/subAddress.ts +39 -0
- package/src/util/normalizeAddress.ts +5 -0
- package/dist/declarations/src/lib/encodeAnyAddress.d.ts +0 -1
- package/src/lib/encodeAnyAddress.ts +0 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,28 @@
|
|
|
1
1
|
# @talismn/orb
|
|
2
2
|
|
|
3
|
-
## 0.
|
|
3
|
+
## 0.3.1
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
7
|
+
- 78f3616: bump pjs and papi deps
|
|
8
|
+
|
|
9
|
+
## 0.3.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 59a150b: Show polkadot icon logo on polkadot account orbs
|
|
14
|
+
- a8f7c1e: remove @polkadot/util-crypto dependency
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- 96cd696: class on orb type svg
|
|
19
|
+
- 64e4344: bump deps
|
|
20
|
+
- a25771e: prettier fix
|
|
7
21
|
- c4d5967: bump typescript version
|
|
8
22
|
- 620b7eb: Dependency updates
|
|
23
|
+
- f78c909: Remove dependency on `nanoid`
|
|
24
|
+
- f44f560: feat: azns lookups
|
|
25
|
+
- 05ca588: feat: migrated to pnpm
|
|
9
26
|
|
|
10
27
|
## 0.2.0
|
|
11
28
|
|
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@ type TalismanOrbProps = {
|
|
|
5
5
|
height?: number;
|
|
6
6
|
className?: string;
|
|
7
7
|
};
|
|
8
|
+
type LogoIconType = "ethereum" | "substrate";
|
|
8
9
|
export declare const useTalismanOrb: (seed: string) => {
|
|
9
10
|
id: string;
|
|
10
11
|
bgColor1: string;
|
|
@@ -13,7 +14,8 @@ export declare const useTalismanOrb: (seed: string) => {
|
|
|
13
14
|
transform: string;
|
|
14
15
|
cx: number;
|
|
15
16
|
cy: number;
|
|
16
|
-
|
|
17
|
+
iconType: LogoIconType;
|
|
17
18
|
};
|
|
18
19
|
export declare const TalismanOrb: FC<TalismanOrbProps>;
|
|
20
|
+
export declare const TalismanOrbRectangle: FC<TalismanOrbProps>;
|
|
19
21
|
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const normalizeSubAddress: (address: string) => string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const normalizeAddress: (address: string) => string;
|
|
@@ -1,28 +1,75 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
3
|
var md5 = require('blueimp-md5');
|
|
6
4
|
var Color = require('color');
|
|
7
|
-
var nanoid = require('nanoid');
|
|
8
5
|
var react = require('react');
|
|
9
|
-
var
|
|
6
|
+
var sha3 = require('@noble/hashes/sha3');
|
|
7
|
+
var blake2b = require('@noble/hashes/blake2b');
|
|
8
|
+
var base = require('@scure/base');
|
|
10
9
|
var jsxRuntime = require('react/jsx-runtime');
|
|
11
10
|
|
|
12
|
-
function _interopDefault (e) { return e && e.__esModule ? e : {
|
|
11
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
12
|
|
|
14
13
|
var md5__default = /*#__PURE__*/_interopDefault(md5);
|
|
15
14
|
var Color__default = /*#__PURE__*/_interopDefault(Color);
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
// inspired from https://github.com/wevm/viem/blob/main/src/utils/address/getAddress.ts
|
|
17
|
+
|
|
18
|
+
const TEXT_ENCODER = new TextEncoder();
|
|
19
|
+
const isEthAddress = address => /^0x[a-fA-F0-9]{40}$/.test(address);
|
|
20
|
+
const normalizeEthAddress = address => {
|
|
21
|
+
if (!isEthAddress(address)) throw new Error(`Invalid Ethereum address ${address}`);
|
|
22
|
+
const rawAddress = address.toLowerCase().substring(2);
|
|
23
|
+
const bytes = TEXT_ENCODER.encode(rawAddress);
|
|
24
|
+
const hash = sha3.keccak_256(bytes);
|
|
25
|
+
|
|
26
|
+
// apply checksum
|
|
27
|
+
const csAddress = rawAddress.split("");
|
|
28
|
+
for (let i = 0; i < 40; i += 2) {
|
|
29
|
+
if (hash[i >> 1] >> 4 >= 8 && address[i]) {
|
|
30
|
+
csAddress[i] = csAddress[i].toUpperCase();
|
|
31
|
+
}
|
|
32
|
+
if ((hash[i >> 1] & 0x0f) >= 8 && address[i + 1]) {
|
|
33
|
+
csAddress[i + 1] = csAddress[i + 1].toUpperCase();
|
|
34
|
+
}
|
|
24
35
|
}
|
|
25
|
-
}
|
|
36
|
+
return `0x${csAddress.join("")}`;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// inspired from https://github.com/polkadot-api/polkadot-api/blob/main/packages/substrate-bindings/src/codecs/scale/AccountId.ts
|
|
40
|
+
|
|
41
|
+
const SS58_PREFIX = new TextEncoder().encode("SS58PRE");
|
|
42
|
+
const SS58_FORMAT = 42;
|
|
43
|
+
const CHECKSUM_LENGTH = 2;
|
|
44
|
+
const VALID_BYTES_LENGTH = [32, 33];
|
|
45
|
+
const encode = publicKey => {
|
|
46
|
+
const prefixBytes = Uint8Array.of(SS58_FORMAT);
|
|
47
|
+
const checksum = blake2b.blake2b(Uint8Array.of(...SS58_PREFIX, ...prefixBytes, ...publicKey), {
|
|
48
|
+
dkLen: 64
|
|
49
|
+
}).subarray(0, CHECKSUM_LENGTH);
|
|
50
|
+
return base.base58.encode(Uint8Array.of(...prefixBytes, ...publicKey, ...checksum));
|
|
51
|
+
};
|
|
52
|
+
const decode = address => {
|
|
53
|
+
const decoded = base.base58.decode(address);
|
|
54
|
+
const prefixBytes = decoded.subarray(0, decoded[0] & 0b0100_0000 ? 2 : 1);
|
|
55
|
+
const publicKey = decoded.subarray(prefixBytes.length, decoded.length - CHECKSUM_LENGTH);
|
|
56
|
+
if (!VALID_BYTES_LENGTH.includes(publicKey.length)) throw new Error("Invalid public key length");
|
|
57
|
+
const checksum = decoded.subarray(prefixBytes.length + publicKey.length);
|
|
58
|
+
const expectedChecksum = blake2b.blake2b(Uint8Array.of(...SS58_PREFIX, ...prefixBytes, ...publicKey), {
|
|
59
|
+
dkLen: 64
|
|
60
|
+
}).subarray(0, CHECKSUM_LENGTH);
|
|
61
|
+
if (checksum[0] !== expectedChecksum[0] || checksum[1] !== expectedChecksum[1]) throw new Error("Invalid checksum");
|
|
62
|
+
return publicKey.slice();
|
|
63
|
+
};
|
|
64
|
+
const normalizeSubAddress = address => {
|
|
65
|
+
// source address might be encoded with a different prefix than 42
|
|
66
|
+
// decode then reencode with prefix 42
|
|
67
|
+
return encode(decode(address));
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const normalizeAddress = address => {
|
|
71
|
+
return isEthAddress(address) ? normalizeEthAddress(address) : normalizeSubAddress(address);
|
|
72
|
+
};
|
|
26
73
|
|
|
27
74
|
const djb2 = str => {
|
|
28
75
|
let hash = 5381;
|
|
@@ -34,22 +81,23 @@ const valueFromHash = (hash, max) => {
|
|
|
34
81
|
};
|
|
35
82
|
const colorFromHash = hash => {
|
|
36
83
|
const hue = valueFromHash(hash, 360);
|
|
37
|
-
return Color__default
|
|
84
|
+
return Color__default.default.hsv(hue, 100, 100);
|
|
38
85
|
};
|
|
39
86
|
const rotateText = (text, nbChars = 0) => text.slice(nbChars) + text.slice(0, nbChars);
|
|
40
87
|
const useTalismanOrb = seed => {
|
|
88
|
+
const id = react.useId();
|
|
41
89
|
return react.useMemo(() => {
|
|
42
|
-
const
|
|
90
|
+
const iconType = seed?.startsWith("0x") ? "ethereum" : "substrate";
|
|
43
91
|
try {
|
|
44
92
|
// seed may be specific to a ss58 prefix, get the base address
|
|
45
93
|
// eslint-disable-next-line no-var
|
|
46
|
-
var address =
|
|
94
|
+
var address = normalizeAddress(seed);
|
|
47
95
|
} catch (err) {
|
|
48
96
|
address = seed;
|
|
49
97
|
}
|
|
50
98
|
|
|
51
99
|
// derive 3 hashs from the seed, used to generate the 3 colors
|
|
52
|
-
const hash1 = md5__default
|
|
100
|
+
const hash1 = md5__default.default(address);
|
|
53
101
|
const hash2 = rotateText(hash1, 1);
|
|
54
102
|
const hash3 = rotateText(hash1, 2);
|
|
55
103
|
|
|
@@ -64,17 +112,76 @@ const useTalismanOrb = seed => {
|
|
|
64
112
|
// global rotation
|
|
65
113
|
const rotation = valueFromHash(hash1, 360);
|
|
66
114
|
return {
|
|
67
|
-
id
|
|
68
|
-
//multiple avatars should cohabit on the same
|
|
115
|
+
id,
|
|
116
|
+
//multiple avatars should cohabit on the same page
|
|
69
117
|
bgColor1: colors[0].hex(),
|
|
70
118
|
bgColor2: colors[1].hex(),
|
|
71
119
|
glowColor: colors[2].hex(),
|
|
72
120
|
transform: `rotate(${rotation} 32 32)`,
|
|
73
121
|
cx: dotX,
|
|
74
122
|
cy: dotY,
|
|
75
|
-
|
|
123
|
+
iconType
|
|
76
124
|
};
|
|
77
|
-
}, [seed]);
|
|
125
|
+
}, [id, seed]);
|
|
126
|
+
};
|
|
127
|
+
const TalismanOrbLogo = ({
|
|
128
|
+
id,
|
|
129
|
+
iconType
|
|
130
|
+
}) => {
|
|
131
|
+
switch (iconType) {
|
|
132
|
+
case "ethereum":
|
|
133
|
+
return /*#__PURE__*/jsxRuntime.jsxs("g", {
|
|
134
|
+
opacity: "0.75",
|
|
135
|
+
transform: "scale(0.7) translate(14 14)",
|
|
136
|
+
className: "orb-type",
|
|
137
|
+
children: [/*#__PURE__*/jsxRuntime.jsx("path", {
|
|
138
|
+
d: "M12.8101 32.76L32.0001 44.62L51.1901 32.76L32.0001 -0.0699997L12.8101 32.76Z",
|
|
139
|
+
fill: "white"
|
|
140
|
+
}), /*#__PURE__*/jsxRuntime.jsx("path", {
|
|
141
|
+
d: "M12.8101 36.48L32.0001 48.43L51.1901 36.48L32.0001 63.93L12.8101 36.48Z",
|
|
142
|
+
fill: "white"
|
|
143
|
+
})]
|
|
144
|
+
});
|
|
145
|
+
case "substrate":
|
|
146
|
+
return /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
147
|
+
children: [/*#__PURE__*/jsxRuntime.jsxs("g", {
|
|
148
|
+
clipPath: `url(#${id}_1751_2030)`,
|
|
149
|
+
opacity: "0.75",
|
|
150
|
+
transform: "scale(2.2) translate(4.5 3.9)",
|
|
151
|
+
className: "orb-type",
|
|
152
|
+
children: [/*#__PURE__*/jsxRuntime.jsx("path", {
|
|
153
|
+
d: "M9.99937 4.4612C12.1176 4.4612 13.8347 3.46253 13.8347 2.2306C13.8347 0.998674 12.1176 0 9.99937 0C7.88119 0 6.16406 0.998674 6.16406 2.2306C6.16406 3.46253 7.88119 4.4612 9.99937 4.4612Z",
|
|
154
|
+
fill: "white"
|
|
155
|
+
}), /*#__PURE__*/jsxRuntime.jsx("path", {
|
|
156
|
+
d: "M9.99937 21.2683C12.1176 21.2683 13.8347 20.2697 13.8347 19.0377C13.8347 17.8058 12.1176 16.8071 9.99937 16.8071C7.88119 16.8071 6.16406 17.8058 6.16406 19.0377C6.16406 20.2697 7.88119 21.2683 9.99937 21.2683Z",
|
|
157
|
+
fill: "white"
|
|
158
|
+
}), /*#__PURE__*/jsxRuntime.jsx("path", {
|
|
159
|
+
d: "M4.65427 7.54892C5.71336 5.71457 5.70649 3.72787 4.63892 3.11149C3.57135 2.49511 1.84735 3.48246 0.788259 5.31681C-0.270832 7.15115 -0.263958 9.13786 0.803612 9.75424C1.87118 10.3706 3.59518 9.38326 4.65427 7.54892Z",
|
|
160
|
+
fill: "white"
|
|
161
|
+
}), /*#__PURE__*/jsxRuntime.jsx("path", {
|
|
162
|
+
d: "M19.2083 15.9515C20.2674 14.1171 20.2611 12.1307 19.1943 11.5148C18.1274 10.8988 16.404 11.8865 15.3449 13.7209C14.2858 15.5552 14.2921 17.5416 15.3589 18.1575C16.4258 18.7735 18.1492 17.7858 19.2083 15.9515Z",
|
|
163
|
+
fill: "white"
|
|
164
|
+
}), /*#__PURE__*/jsxRuntime.jsx("path", {
|
|
165
|
+
d: "M4.6399 18.1571C5.70747 17.5407 5.71434 15.554 4.65525 13.7196C3.59616 11.8853 1.87216 10.8979 0.804589 11.5143C-0.262981 12.1307 -0.269855 14.1174 0.789235 15.9517C1.84833 17.7861 3.57233 18.7734 4.6399 18.1571Z",
|
|
166
|
+
fill: "white"
|
|
167
|
+
}), /*#__PURE__*/jsxRuntime.jsx("path", {
|
|
168
|
+
d: "M19.1952 9.75475C20.2621 9.13878 20.2684 7.15241 19.2093 5.31807C18.1502 3.48372 16.4268 2.49603 15.3599 3.11199C14.2931 3.72796 14.2868 5.71433 15.3459 7.54867C16.405 9.38302 18.1284 10.3707 19.1952 9.75475Z",
|
|
169
|
+
fill: "white"
|
|
170
|
+
})]
|
|
171
|
+
}), /*#__PURE__*/jsxRuntime.jsx("defs", {
|
|
172
|
+
children: /*#__PURE__*/jsxRuntime.jsx("clipPath", {
|
|
173
|
+
id: `${id}_1751_2030`,
|
|
174
|
+
children: /*#__PURE__*/jsxRuntime.jsx("rect", {
|
|
175
|
+
width: "20",
|
|
176
|
+
height: "21.2699",
|
|
177
|
+
fill: "white"
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
})]
|
|
181
|
+
});
|
|
182
|
+
default:
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
78
185
|
};
|
|
79
186
|
const TalismanOrb = ({
|
|
80
187
|
seed,
|
|
@@ -90,7 +197,7 @@ const TalismanOrb = ({
|
|
|
90
197
|
glowColor,
|
|
91
198
|
cx,
|
|
92
199
|
cy,
|
|
93
|
-
|
|
200
|
+
iconType
|
|
94
201
|
} = useTalismanOrb(seed);
|
|
95
202
|
return /*#__PURE__*/jsxRuntime.jsxs("svg", {
|
|
96
203
|
width: width,
|
|
@@ -143,20 +250,85 @@ const TalismanOrb = ({
|
|
|
143
250
|
r: 45,
|
|
144
251
|
opacity: 0.7
|
|
145
252
|
})]
|
|
146
|
-
}),
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
253
|
+
}), /*#__PURE__*/jsxRuntime.jsx(TalismanOrbLogo, {
|
|
254
|
+
id: id,
|
|
255
|
+
iconType: iconType
|
|
256
|
+
})]
|
|
257
|
+
})]
|
|
258
|
+
});
|
|
259
|
+
};
|
|
260
|
+
const TalismanOrbRectangle = ({
|
|
261
|
+
width,
|
|
262
|
+
height,
|
|
263
|
+
seed,
|
|
264
|
+
className
|
|
265
|
+
}) => {
|
|
266
|
+
const {
|
|
267
|
+
id,
|
|
268
|
+
bgColor1,
|
|
269
|
+
bgColor2,
|
|
270
|
+
transform,
|
|
271
|
+
glowColor,
|
|
272
|
+
cx,
|
|
273
|
+
cy
|
|
274
|
+
} = useTalismanOrb(seed);
|
|
275
|
+
return /*#__PURE__*/jsxRuntime.jsxs("svg", {
|
|
276
|
+
width: width,
|
|
277
|
+
height: height,
|
|
278
|
+
viewBox: `0 0 64 64`,
|
|
279
|
+
preserveAspectRatio: "none",
|
|
280
|
+
className: className,
|
|
281
|
+
version: "1.1",
|
|
282
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
283
|
+
children: [/*#__PURE__*/jsxRuntime.jsxs("defs", {
|
|
284
|
+
children: [/*#__PURE__*/jsxRuntime.jsxs("linearGradient", {
|
|
285
|
+
id: `${id}-bg`,
|
|
286
|
+
children: [/*#__PURE__*/jsxRuntime.jsx("stop", {
|
|
287
|
+
offset: "20%",
|
|
288
|
+
stopColor: bgColor1
|
|
289
|
+
}), /*#__PURE__*/jsxRuntime.jsx("stop", {
|
|
290
|
+
offset: "100%",
|
|
291
|
+
stopColor: bgColor2
|
|
155
292
|
})]
|
|
293
|
+
}), /*#__PURE__*/jsxRuntime.jsxs("radialGradient", {
|
|
294
|
+
id: `${id}-circle`,
|
|
295
|
+
children: [/*#__PURE__*/jsxRuntime.jsx("stop", {
|
|
296
|
+
offset: "10%",
|
|
297
|
+
stopColor: glowColor
|
|
298
|
+
}), /*#__PURE__*/jsxRuntime.jsx("stop", {
|
|
299
|
+
offset: "100%",
|
|
300
|
+
stopColor: "transparent"
|
|
301
|
+
})]
|
|
302
|
+
}), /*#__PURE__*/jsxRuntime.jsx("clipPath", {
|
|
303
|
+
id: `${id}-clip`,
|
|
304
|
+
children: /*#__PURE__*/jsxRuntime.jsx("circle", {
|
|
305
|
+
cx: "32",
|
|
306
|
+
cy: "32",
|
|
307
|
+
r: "48"
|
|
308
|
+
})
|
|
156
309
|
})]
|
|
310
|
+
}), /*#__PURE__*/jsxRuntime.jsx("g", {
|
|
311
|
+
clipPath: `url(#${id}-clip)`,
|
|
312
|
+
children: /*#__PURE__*/jsxRuntime.jsxs("g", {
|
|
313
|
+
transform: transform,
|
|
314
|
+
children: [/*#__PURE__*/jsxRuntime.jsx("rect", {
|
|
315
|
+
fill: `url(#${id}-bg)`,
|
|
316
|
+
x: -16,
|
|
317
|
+
y: -16,
|
|
318
|
+
width: 96,
|
|
319
|
+
height: 96
|
|
320
|
+
}), /*#__PURE__*/jsxRuntime.jsx("circle", {
|
|
321
|
+
fill: `url(#${id}-circle)`,
|
|
322
|
+
cx: cx,
|
|
323
|
+
cy: cy,
|
|
324
|
+
r: 45,
|
|
325
|
+
opacity: 0.7
|
|
326
|
+
})]
|
|
327
|
+
})
|
|
157
328
|
})]
|
|
158
329
|
});
|
|
159
330
|
};
|
|
160
331
|
|
|
161
332
|
exports.TalismanOrb = TalismanOrb;
|
|
333
|
+
exports.TalismanOrbRectangle = TalismanOrbRectangle;
|
|
162
334
|
exports.useTalismanOrb = useTalismanOrb;
|
|
@@ -1,28 +1,75 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
3
|
var md5 = require('blueimp-md5');
|
|
6
4
|
var Color = require('color');
|
|
7
|
-
var nanoid = require('nanoid');
|
|
8
5
|
var react = require('react');
|
|
9
|
-
var
|
|
6
|
+
var sha3 = require('@noble/hashes/sha3');
|
|
7
|
+
var blake2b = require('@noble/hashes/blake2b');
|
|
8
|
+
var base = require('@scure/base');
|
|
10
9
|
var jsxRuntime = require('react/jsx-runtime');
|
|
11
10
|
|
|
12
|
-
function _interopDefault (e) { return e && e.__esModule ? e : {
|
|
11
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
12
|
|
|
14
13
|
var md5__default = /*#__PURE__*/_interopDefault(md5);
|
|
15
14
|
var Color__default = /*#__PURE__*/_interopDefault(Color);
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
// inspired from https://github.com/wevm/viem/blob/main/src/utils/address/getAddress.ts
|
|
17
|
+
|
|
18
|
+
const TEXT_ENCODER = new TextEncoder();
|
|
19
|
+
const isEthAddress = address => /^0x[a-fA-F0-9]{40}$/.test(address);
|
|
20
|
+
const normalizeEthAddress = address => {
|
|
21
|
+
if (!isEthAddress(address)) throw new Error(`Invalid Ethereum address ${address}`);
|
|
22
|
+
const rawAddress = address.toLowerCase().substring(2);
|
|
23
|
+
const bytes = TEXT_ENCODER.encode(rawAddress);
|
|
24
|
+
const hash = sha3.keccak_256(bytes);
|
|
25
|
+
|
|
26
|
+
// apply checksum
|
|
27
|
+
const csAddress = rawAddress.split("");
|
|
28
|
+
for (let i = 0; i < 40; i += 2) {
|
|
29
|
+
if (hash[i >> 1] >> 4 >= 8 && address[i]) {
|
|
30
|
+
csAddress[i] = csAddress[i].toUpperCase();
|
|
31
|
+
}
|
|
32
|
+
if ((hash[i >> 1] & 0x0f) >= 8 && address[i + 1]) {
|
|
33
|
+
csAddress[i + 1] = csAddress[i + 1].toUpperCase();
|
|
34
|
+
}
|
|
24
35
|
}
|
|
25
|
-
}
|
|
36
|
+
return `0x${csAddress.join("")}`;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// inspired from https://github.com/polkadot-api/polkadot-api/blob/main/packages/substrate-bindings/src/codecs/scale/AccountId.ts
|
|
40
|
+
|
|
41
|
+
const SS58_PREFIX = new TextEncoder().encode("SS58PRE");
|
|
42
|
+
const SS58_FORMAT = 42;
|
|
43
|
+
const CHECKSUM_LENGTH = 2;
|
|
44
|
+
const VALID_BYTES_LENGTH = [32, 33];
|
|
45
|
+
const encode = publicKey => {
|
|
46
|
+
const prefixBytes = Uint8Array.of(SS58_FORMAT);
|
|
47
|
+
const checksum = blake2b.blake2b(Uint8Array.of(...SS58_PREFIX, ...prefixBytes, ...publicKey), {
|
|
48
|
+
dkLen: 64
|
|
49
|
+
}).subarray(0, CHECKSUM_LENGTH);
|
|
50
|
+
return base.base58.encode(Uint8Array.of(...prefixBytes, ...publicKey, ...checksum));
|
|
51
|
+
};
|
|
52
|
+
const decode = address => {
|
|
53
|
+
const decoded = base.base58.decode(address);
|
|
54
|
+
const prefixBytes = decoded.subarray(0, decoded[0] & 0b0100_0000 ? 2 : 1);
|
|
55
|
+
const publicKey = decoded.subarray(prefixBytes.length, decoded.length - CHECKSUM_LENGTH);
|
|
56
|
+
if (!VALID_BYTES_LENGTH.includes(publicKey.length)) throw new Error("Invalid public key length");
|
|
57
|
+
const checksum = decoded.subarray(prefixBytes.length + publicKey.length);
|
|
58
|
+
const expectedChecksum = blake2b.blake2b(Uint8Array.of(...SS58_PREFIX, ...prefixBytes, ...publicKey), {
|
|
59
|
+
dkLen: 64
|
|
60
|
+
}).subarray(0, CHECKSUM_LENGTH);
|
|
61
|
+
if (checksum[0] !== expectedChecksum[0] || checksum[1] !== expectedChecksum[1]) throw new Error("Invalid checksum");
|
|
62
|
+
return publicKey.slice();
|
|
63
|
+
};
|
|
64
|
+
const normalizeSubAddress = address => {
|
|
65
|
+
// source address might be encoded with a different prefix than 42
|
|
66
|
+
// decode then reencode with prefix 42
|
|
67
|
+
return encode(decode(address));
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const normalizeAddress = address => {
|
|
71
|
+
return isEthAddress(address) ? normalizeEthAddress(address) : normalizeSubAddress(address);
|
|
72
|
+
};
|
|
26
73
|
|
|
27
74
|
const djb2 = str => {
|
|
28
75
|
let hash = 5381;
|
|
@@ -34,22 +81,23 @@ const valueFromHash = (hash, max) => {
|
|
|
34
81
|
};
|
|
35
82
|
const colorFromHash = hash => {
|
|
36
83
|
const hue = valueFromHash(hash, 360);
|
|
37
|
-
return Color__default
|
|
84
|
+
return Color__default.default.hsv(hue, 100, 100);
|
|
38
85
|
};
|
|
39
86
|
const rotateText = (text, nbChars = 0) => text.slice(nbChars) + text.slice(0, nbChars);
|
|
40
87
|
const useTalismanOrb = seed => {
|
|
88
|
+
const id = react.useId();
|
|
41
89
|
return react.useMemo(() => {
|
|
42
|
-
const
|
|
90
|
+
const iconType = seed?.startsWith("0x") ? "ethereum" : "substrate";
|
|
43
91
|
try {
|
|
44
92
|
// seed may be specific to a ss58 prefix, get the base address
|
|
45
93
|
// eslint-disable-next-line no-var
|
|
46
|
-
var address =
|
|
94
|
+
var address = normalizeAddress(seed);
|
|
47
95
|
} catch (err) {
|
|
48
96
|
address = seed;
|
|
49
97
|
}
|
|
50
98
|
|
|
51
99
|
// derive 3 hashs from the seed, used to generate the 3 colors
|
|
52
|
-
const hash1 = md5__default
|
|
100
|
+
const hash1 = md5__default.default(address);
|
|
53
101
|
const hash2 = rotateText(hash1, 1);
|
|
54
102
|
const hash3 = rotateText(hash1, 2);
|
|
55
103
|
|
|
@@ -64,17 +112,76 @@ const useTalismanOrb = seed => {
|
|
|
64
112
|
// global rotation
|
|
65
113
|
const rotation = valueFromHash(hash1, 360);
|
|
66
114
|
return {
|
|
67
|
-
id
|
|
68
|
-
//multiple avatars should cohabit on the same
|
|
115
|
+
id,
|
|
116
|
+
//multiple avatars should cohabit on the same page
|
|
69
117
|
bgColor1: colors[0].hex(),
|
|
70
118
|
bgColor2: colors[1].hex(),
|
|
71
119
|
glowColor: colors[2].hex(),
|
|
72
120
|
transform: `rotate(${rotation} 32 32)`,
|
|
73
121
|
cx: dotX,
|
|
74
122
|
cy: dotY,
|
|
75
|
-
|
|
123
|
+
iconType
|
|
76
124
|
};
|
|
77
|
-
}, [seed]);
|
|
125
|
+
}, [id, seed]);
|
|
126
|
+
};
|
|
127
|
+
const TalismanOrbLogo = ({
|
|
128
|
+
id,
|
|
129
|
+
iconType
|
|
130
|
+
}) => {
|
|
131
|
+
switch (iconType) {
|
|
132
|
+
case "ethereum":
|
|
133
|
+
return /*#__PURE__*/jsxRuntime.jsxs("g", {
|
|
134
|
+
opacity: "0.75",
|
|
135
|
+
transform: "scale(0.7) translate(14 14)",
|
|
136
|
+
className: "orb-type",
|
|
137
|
+
children: [/*#__PURE__*/jsxRuntime.jsx("path", {
|
|
138
|
+
d: "M12.8101 32.76L32.0001 44.62L51.1901 32.76L32.0001 -0.0699997L12.8101 32.76Z",
|
|
139
|
+
fill: "white"
|
|
140
|
+
}), /*#__PURE__*/jsxRuntime.jsx("path", {
|
|
141
|
+
d: "M12.8101 36.48L32.0001 48.43L51.1901 36.48L32.0001 63.93L12.8101 36.48Z",
|
|
142
|
+
fill: "white"
|
|
143
|
+
})]
|
|
144
|
+
});
|
|
145
|
+
case "substrate":
|
|
146
|
+
return /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
147
|
+
children: [/*#__PURE__*/jsxRuntime.jsxs("g", {
|
|
148
|
+
clipPath: `url(#${id}_1751_2030)`,
|
|
149
|
+
opacity: "0.75",
|
|
150
|
+
transform: "scale(2.2) translate(4.5 3.9)",
|
|
151
|
+
className: "orb-type",
|
|
152
|
+
children: [/*#__PURE__*/jsxRuntime.jsx("path", {
|
|
153
|
+
d: "M9.99937 4.4612C12.1176 4.4612 13.8347 3.46253 13.8347 2.2306C13.8347 0.998674 12.1176 0 9.99937 0C7.88119 0 6.16406 0.998674 6.16406 2.2306C6.16406 3.46253 7.88119 4.4612 9.99937 4.4612Z",
|
|
154
|
+
fill: "white"
|
|
155
|
+
}), /*#__PURE__*/jsxRuntime.jsx("path", {
|
|
156
|
+
d: "M9.99937 21.2683C12.1176 21.2683 13.8347 20.2697 13.8347 19.0377C13.8347 17.8058 12.1176 16.8071 9.99937 16.8071C7.88119 16.8071 6.16406 17.8058 6.16406 19.0377C6.16406 20.2697 7.88119 21.2683 9.99937 21.2683Z",
|
|
157
|
+
fill: "white"
|
|
158
|
+
}), /*#__PURE__*/jsxRuntime.jsx("path", {
|
|
159
|
+
d: "M4.65427 7.54892C5.71336 5.71457 5.70649 3.72787 4.63892 3.11149C3.57135 2.49511 1.84735 3.48246 0.788259 5.31681C-0.270832 7.15115 -0.263958 9.13786 0.803612 9.75424C1.87118 10.3706 3.59518 9.38326 4.65427 7.54892Z",
|
|
160
|
+
fill: "white"
|
|
161
|
+
}), /*#__PURE__*/jsxRuntime.jsx("path", {
|
|
162
|
+
d: "M19.2083 15.9515C20.2674 14.1171 20.2611 12.1307 19.1943 11.5148C18.1274 10.8988 16.404 11.8865 15.3449 13.7209C14.2858 15.5552 14.2921 17.5416 15.3589 18.1575C16.4258 18.7735 18.1492 17.7858 19.2083 15.9515Z",
|
|
163
|
+
fill: "white"
|
|
164
|
+
}), /*#__PURE__*/jsxRuntime.jsx("path", {
|
|
165
|
+
d: "M4.6399 18.1571C5.70747 17.5407 5.71434 15.554 4.65525 13.7196C3.59616 11.8853 1.87216 10.8979 0.804589 11.5143C-0.262981 12.1307 -0.269855 14.1174 0.789235 15.9517C1.84833 17.7861 3.57233 18.7734 4.6399 18.1571Z",
|
|
166
|
+
fill: "white"
|
|
167
|
+
}), /*#__PURE__*/jsxRuntime.jsx("path", {
|
|
168
|
+
d: "M19.1952 9.75475C20.2621 9.13878 20.2684 7.15241 19.2093 5.31807C18.1502 3.48372 16.4268 2.49603 15.3599 3.11199C14.2931 3.72796 14.2868 5.71433 15.3459 7.54867C16.405 9.38302 18.1284 10.3707 19.1952 9.75475Z",
|
|
169
|
+
fill: "white"
|
|
170
|
+
})]
|
|
171
|
+
}), /*#__PURE__*/jsxRuntime.jsx("defs", {
|
|
172
|
+
children: /*#__PURE__*/jsxRuntime.jsx("clipPath", {
|
|
173
|
+
id: `${id}_1751_2030`,
|
|
174
|
+
children: /*#__PURE__*/jsxRuntime.jsx("rect", {
|
|
175
|
+
width: "20",
|
|
176
|
+
height: "21.2699",
|
|
177
|
+
fill: "white"
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
})]
|
|
181
|
+
});
|
|
182
|
+
default:
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
78
185
|
};
|
|
79
186
|
const TalismanOrb = ({
|
|
80
187
|
seed,
|
|
@@ -90,7 +197,7 @@ const TalismanOrb = ({
|
|
|
90
197
|
glowColor,
|
|
91
198
|
cx,
|
|
92
199
|
cy,
|
|
93
|
-
|
|
200
|
+
iconType
|
|
94
201
|
} = useTalismanOrb(seed);
|
|
95
202
|
return /*#__PURE__*/jsxRuntime.jsxs("svg", {
|
|
96
203
|
width: width,
|
|
@@ -143,20 +250,85 @@ const TalismanOrb = ({
|
|
|
143
250
|
r: 45,
|
|
144
251
|
opacity: 0.7
|
|
145
252
|
})]
|
|
146
|
-
}),
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
253
|
+
}), /*#__PURE__*/jsxRuntime.jsx(TalismanOrbLogo, {
|
|
254
|
+
id: id,
|
|
255
|
+
iconType: iconType
|
|
256
|
+
})]
|
|
257
|
+
})]
|
|
258
|
+
});
|
|
259
|
+
};
|
|
260
|
+
const TalismanOrbRectangle = ({
|
|
261
|
+
width,
|
|
262
|
+
height,
|
|
263
|
+
seed,
|
|
264
|
+
className
|
|
265
|
+
}) => {
|
|
266
|
+
const {
|
|
267
|
+
id,
|
|
268
|
+
bgColor1,
|
|
269
|
+
bgColor2,
|
|
270
|
+
transform,
|
|
271
|
+
glowColor,
|
|
272
|
+
cx,
|
|
273
|
+
cy
|
|
274
|
+
} = useTalismanOrb(seed);
|
|
275
|
+
return /*#__PURE__*/jsxRuntime.jsxs("svg", {
|
|
276
|
+
width: width,
|
|
277
|
+
height: height,
|
|
278
|
+
viewBox: `0 0 64 64`,
|
|
279
|
+
preserveAspectRatio: "none",
|
|
280
|
+
className: className,
|
|
281
|
+
version: "1.1",
|
|
282
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
283
|
+
children: [/*#__PURE__*/jsxRuntime.jsxs("defs", {
|
|
284
|
+
children: [/*#__PURE__*/jsxRuntime.jsxs("linearGradient", {
|
|
285
|
+
id: `${id}-bg`,
|
|
286
|
+
children: [/*#__PURE__*/jsxRuntime.jsx("stop", {
|
|
287
|
+
offset: "20%",
|
|
288
|
+
stopColor: bgColor1
|
|
289
|
+
}), /*#__PURE__*/jsxRuntime.jsx("stop", {
|
|
290
|
+
offset: "100%",
|
|
291
|
+
stopColor: bgColor2
|
|
155
292
|
})]
|
|
293
|
+
}), /*#__PURE__*/jsxRuntime.jsxs("radialGradient", {
|
|
294
|
+
id: `${id}-circle`,
|
|
295
|
+
children: [/*#__PURE__*/jsxRuntime.jsx("stop", {
|
|
296
|
+
offset: "10%",
|
|
297
|
+
stopColor: glowColor
|
|
298
|
+
}), /*#__PURE__*/jsxRuntime.jsx("stop", {
|
|
299
|
+
offset: "100%",
|
|
300
|
+
stopColor: "transparent"
|
|
301
|
+
})]
|
|
302
|
+
}), /*#__PURE__*/jsxRuntime.jsx("clipPath", {
|
|
303
|
+
id: `${id}-clip`,
|
|
304
|
+
children: /*#__PURE__*/jsxRuntime.jsx("circle", {
|
|
305
|
+
cx: "32",
|
|
306
|
+
cy: "32",
|
|
307
|
+
r: "48"
|
|
308
|
+
})
|
|
156
309
|
})]
|
|
310
|
+
}), /*#__PURE__*/jsxRuntime.jsx("g", {
|
|
311
|
+
clipPath: `url(#${id}-clip)`,
|
|
312
|
+
children: /*#__PURE__*/jsxRuntime.jsxs("g", {
|
|
313
|
+
transform: transform,
|
|
314
|
+
children: [/*#__PURE__*/jsxRuntime.jsx("rect", {
|
|
315
|
+
fill: `url(#${id}-bg)`,
|
|
316
|
+
x: -16,
|
|
317
|
+
y: -16,
|
|
318
|
+
width: 96,
|
|
319
|
+
height: 96
|
|
320
|
+
}), /*#__PURE__*/jsxRuntime.jsx("circle", {
|
|
321
|
+
fill: `url(#${id}-circle)`,
|
|
322
|
+
cx: cx,
|
|
323
|
+
cy: cy,
|
|
324
|
+
r: 45,
|
|
325
|
+
opacity: 0.7
|
|
326
|
+
})]
|
|
327
|
+
})
|
|
157
328
|
})]
|
|
158
329
|
});
|
|
159
330
|
};
|
|
160
331
|
|
|
161
332
|
exports.TalismanOrb = TalismanOrb;
|
|
333
|
+
exports.TalismanOrbRectangle = TalismanOrbRectangle;
|
|
162
334
|
exports.useTalismanOrb = useTalismanOrb;
|
package/dist/talismn-orb.esm.js
CHANGED
|
@@ -1,19 +1,68 @@
|
|
|
1
1
|
import md5 from 'blueimp-md5';
|
|
2
2
|
import Color from 'color';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { useId, useMemo } from 'react';
|
|
4
|
+
import { keccak_256 } from '@noble/hashes/sha3';
|
|
5
|
+
import { blake2b } from '@noble/hashes/blake2b';
|
|
6
|
+
import { base58 } from '@scure/base';
|
|
7
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
// inspired from https://github.com/wevm/viem/blob/main/src/utils/address/getAddress.ts
|
|
10
|
+
|
|
11
|
+
const TEXT_ENCODER = new TextEncoder();
|
|
12
|
+
const isEthAddress = address => /^0x[a-fA-F0-9]{40}$/.test(address);
|
|
13
|
+
const normalizeEthAddress = address => {
|
|
14
|
+
if (!isEthAddress(address)) throw new Error(`Invalid Ethereum address ${address}`);
|
|
15
|
+
const rawAddress = address.toLowerCase().substring(2);
|
|
16
|
+
const bytes = TEXT_ENCODER.encode(rawAddress);
|
|
17
|
+
const hash = keccak_256(bytes);
|
|
18
|
+
|
|
19
|
+
// apply checksum
|
|
20
|
+
const csAddress = rawAddress.split("");
|
|
21
|
+
for (let i = 0; i < 40; i += 2) {
|
|
22
|
+
if (hash[i >> 1] >> 4 >= 8 && address[i]) {
|
|
23
|
+
csAddress[i] = csAddress[i].toUpperCase();
|
|
24
|
+
}
|
|
25
|
+
if ((hash[i >> 1] & 0x0f) >= 8 && address[i + 1]) {
|
|
26
|
+
csAddress[i + 1] = csAddress[i + 1].toUpperCase();
|
|
27
|
+
}
|
|
15
28
|
}
|
|
16
|
-
}
|
|
29
|
+
return `0x${csAddress.join("")}`;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// inspired from https://github.com/polkadot-api/polkadot-api/blob/main/packages/substrate-bindings/src/codecs/scale/AccountId.ts
|
|
33
|
+
|
|
34
|
+
const SS58_PREFIX = new TextEncoder().encode("SS58PRE");
|
|
35
|
+
const SS58_FORMAT = 42;
|
|
36
|
+
const CHECKSUM_LENGTH = 2;
|
|
37
|
+
const VALID_BYTES_LENGTH = [32, 33];
|
|
38
|
+
const encode = publicKey => {
|
|
39
|
+
const prefixBytes = Uint8Array.of(SS58_FORMAT);
|
|
40
|
+
const checksum = blake2b(Uint8Array.of(...SS58_PREFIX, ...prefixBytes, ...publicKey), {
|
|
41
|
+
dkLen: 64
|
|
42
|
+
}).subarray(0, CHECKSUM_LENGTH);
|
|
43
|
+
return base58.encode(Uint8Array.of(...prefixBytes, ...publicKey, ...checksum));
|
|
44
|
+
};
|
|
45
|
+
const decode = address => {
|
|
46
|
+
const decoded = base58.decode(address);
|
|
47
|
+
const prefixBytes = decoded.subarray(0, decoded[0] & 0b0100_0000 ? 2 : 1);
|
|
48
|
+
const publicKey = decoded.subarray(prefixBytes.length, decoded.length - CHECKSUM_LENGTH);
|
|
49
|
+
if (!VALID_BYTES_LENGTH.includes(publicKey.length)) throw new Error("Invalid public key length");
|
|
50
|
+
const checksum = decoded.subarray(prefixBytes.length + publicKey.length);
|
|
51
|
+
const expectedChecksum = blake2b(Uint8Array.of(...SS58_PREFIX, ...prefixBytes, ...publicKey), {
|
|
52
|
+
dkLen: 64
|
|
53
|
+
}).subarray(0, CHECKSUM_LENGTH);
|
|
54
|
+
if (checksum[0] !== expectedChecksum[0] || checksum[1] !== expectedChecksum[1]) throw new Error("Invalid checksum");
|
|
55
|
+
return publicKey.slice();
|
|
56
|
+
};
|
|
57
|
+
const normalizeSubAddress = address => {
|
|
58
|
+
// source address might be encoded with a different prefix than 42
|
|
59
|
+
// decode then reencode with prefix 42
|
|
60
|
+
return encode(decode(address));
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const normalizeAddress = address => {
|
|
64
|
+
return isEthAddress(address) ? normalizeEthAddress(address) : normalizeSubAddress(address);
|
|
65
|
+
};
|
|
17
66
|
|
|
18
67
|
const djb2 = str => {
|
|
19
68
|
let hash = 5381;
|
|
@@ -29,12 +78,13 @@ const colorFromHash = hash => {
|
|
|
29
78
|
};
|
|
30
79
|
const rotateText = (text, nbChars = 0) => text.slice(nbChars) + text.slice(0, nbChars);
|
|
31
80
|
const useTalismanOrb = seed => {
|
|
81
|
+
const id = useId();
|
|
32
82
|
return useMemo(() => {
|
|
33
|
-
const
|
|
83
|
+
const iconType = seed?.startsWith("0x") ? "ethereum" : "substrate";
|
|
34
84
|
try {
|
|
35
85
|
// seed may be specific to a ss58 prefix, get the base address
|
|
36
86
|
// eslint-disable-next-line no-var
|
|
37
|
-
var address =
|
|
87
|
+
var address = normalizeAddress(seed);
|
|
38
88
|
} catch (err) {
|
|
39
89
|
address = seed;
|
|
40
90
|
}
|
|
@@ -55,17 +105,76 @@ const useTalismanOrb = seed => {
|
|
|
55
105
|
// global rotation
|
|
56
106
|
const rotation = valueFromHash(hash1, 360);
|
|
57
107
|
return {
|
|
58
|
-
id
|
|
59
|
-
//multiple avatars should cohabit on the same
|
|
108
|
+
id,
|
|
109
|
+
//multiple avatars should cohabit on the same page
|
|
60
110
|
bgColor1: colors[0].hex(),
|
|
61
111
|
bgColor2: colors[1].hex(),
|
|
62
112
|
glowColor: colors[2].hex(),
|
|
63
113
|
transform: `rotate(${rotation} 32 32)`,
|
|
64
114
|
cx: dotX,
|
|
65
115
|
cy: dotY,
|
|
66
|
-
|
|
116
|
+
iconType
|
|
67
117
|
};
|
|
68
|
-
}, [seed]);
|
|
118
|
+
}, [id, seed]);
|
|
119
|
+
};
|
|
120
|
+
const TalismanOrbLogo = ({
|
|
121
|
+
id,
|
|
122
|
+
iconType
|
|
123
|
+
}) => {
|
|
124
|
+
switch (iconType) {
|
|
125
|
+
case "ethereum":
|
|
126
|
+
return /*#__PURE__*/jsxs("g", {
|
|
127
|
+
opacity: "0.75",
|
|
128
|
+
transform: "scale(0.7) translate(14 14)",
|
|
129
|
+
className: "orb-type",
|
|
130
|
+
children: [/*#__PURE__*/jsx("path", {
|
|
131
|
+
d: "M12.8101 32.76L32.0001 44.62L51.1901 32.76L32.0001 -0.0699997L12.8101 32.76Z",
|
|
132
|
+
fill: "white"
|
|
133
|
+
}), /*#__PURE__*/jsx("path", {
|
|
134
|
+
d: "M12.8101 36.48L32.0001 48.43L51.1901 36.48L32.0001 63.93L12.8101 36.48Z",
|
|
135
|
+
fill: "white"
|
|
136
|
+
})]
|
|
137
|
+
});
|
|
138
|
+
case "substrate":
|
|
139
|
+
return /*#__PURE__*/jsxs(Fragment, {
|
|
140
|
+
children: [/*#__PURE__*/jsxs("g", {
|
|
141
|
+
clipPath: `url(#${id}_1751_2030)`,
|
|
142
|
+
opacity: "0.75",
|
|
143
|
+
transform: "scale(2.2) translate(4.5 3.9)",
|
|
144
|
+
className: "orb-type",
|
|
145
|
+
children: [/*#__PURE__*/jsx("path", {
|
|
146
|
+
d: "M9.99937 4.4612C12.1176 4.4612 13.8347 3.46253 13.8347 2.2306C13.8347 0.998674 12.1176 0 9.99937 0C7.88119 0 6.16406 0.998674 6.16406 2.2306C6.16406 3.46253 7.88119 4.4612 9.99937 4.4612Z",
|
|
147
|
+
fill: "white"
|
|
148
|
+
}), /*#__PURE__*/jsx("path", {
|
|
149
|
+
d: "M9.99937 21.2683C12.1176 21.2683 13.8347 20.2697 13.8347 19.0377C13.8347 17.8058 12.1176 16.8071 9.99937 16.8071C7.88119 16.8071 6.16406 17.8058 6.16406 19.0377C6.16406 20.2697 7.88119 21.2683 9.99937 21.2683Z",
|
|
150
|
+
fill: "white"
|
|
151
|
+
}), /*#__PURE__*/jsx("path", {
|
|
152
|
+
d: "M4.65427 7.54892C5.71336 5.71457 5.70649 3.72787 4.63892 3.11149C3.57135 2.49511 1.84735 3.48246 0.788259 5.31681C-0.270832 7.15115 -0.263958 9.13786 0.803612 9.75424C1.87118 10.3706 3.59518 9.38326 4.65427 7.54892Z",
|
|
153
|
+
fill: "white"
|
|
154
|
+
}), /*#__PURE__*/jsx("path", {
|
|
155
|
+
d: "M19.2083 15.9515C20.2674 14.1171 20.2611 12.1307 19.1943 11.5148C18.1274 10.8988 16.404 11.8865 15.3449 13.7209C14.2858 15.5552 14.2921 17.5416 15.3589 18.1575C16.4258 18.7735 18.1492 17.7858 19.2083 15.9515Z",
|
|
156
|
+
fill: "white"
|
|
157
|
+
}), /*#__PURE__*/jsx("path", {
|
|
158
|
+
d: "M4.6399 18.1571C5.70747 17.5407 5.71434 15.554 4.65525 13.7196C3.59616 11.8853 1.87216 10.8979 0.804589 11.5143C-0.262981 12.1307 -0.269855 14.1174 0.789235 15.9517C1.84833 17.7861 3.57233 18.7734 4.6399 18.1571Z",
|
|
159
|
+
fill: "white"
|
|
160
|
+
}), /*#__PURE__*/jsx("path", {
|
|
161
|
+
d: "M19.1952 9.75475C20.2621 9.13878 20.2684 7.15241 19.2093 5.31807C18.1502 3.48372 16.4268 2.49603 15.3599 3.11199C14.2931 3.72796 14.2868 5.71433 15.3459 7.54867C16.405 9.38302 18.1284 10.3707 19.1952 9.75475Z",
|
|
162
|
+
fill: "white"
|
|
163
|
+
})]
|
|
164
|
+
}), /*#__PURE__*/jsx("defs", {
|
|
165
|
+
children: /*#__PURE__*/jsx("clipPath", {
|
|
166
|
+
id: `${id}_1751_2030`,
|
|
167
|
+
children: /*#__PURE__*/jsx("rect", {
|
|
168
|
+
width: "20",
|
|
169
|
+
height: "21.2699",
|
|
170
|
+
fill: "white"
|
|
171
|
+
})
|
|
172
|
+
})
|
|
173
|
+
})]
|
|
174
|
+
});
|
|
175
|
+
default:
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
69
178
|
};
|
|
70
179
|
const TalismanOrb = ({
|
|
71
180
|
seed,
|
|
@@ -81,7 +190,7 @@ const TalismanOrb = ({
|
|
|
81
190
|
glowColor,
|
|
82
191
|
cx,
|
|
83
192
|
cy,
|
|
84
|
-
|
|
193
|
+
iconType
|
|
85
194
|
} = useTalismanOrb(seed);
|
|
86
195
|
return /*#__PURE__*/jsxs("svg", {
|
|
87
196
|
width: width,
|
|
@@ -134,19 +243,83 @@ const TalismanOrb = ({
|
|
|
134
243
|
r: 45,
|
|
135
244
|
opacity: 0.7
|
|
136
245
|
})]
|
|
137
|
-
}),
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
246
|
+
}), /*#__PURE__*/jsx(TalismanOrbLogo, {
|
|
247
|
+
id: id,
|
|
248
|
+
iconType: iconType
|
|
249
|
+
})]
|
|
250
|
+
})]
|
|
251
|
+
});
|
|
252
|
+
};
|
|
253
|
+
const TalismanOrbRectangle = ({
|
|
254
|
+
width,
|
|
255
|
+
height,
|
|
256
|
+
seed,
|
|
257
|
+
className
|
|
258
|
+
}) => {
|
|
259
|
+
const {
|
|
260
|
+
id,
|
|
261
|
+
bgColor1,
|
|
262
|
+
bgColor2,
|
|
263
|
+
transform,
|
|
264
|
+
glowColor,
|
|
265
|
+
cx,
|
|
266
|
+
cy
|
|
267
|
+
} = useTalismanOrb(seed);
|
|
268
|
+
return /*#__PURE__*/jsxs("svg", {
|
|
269
|
+
width: width,
|
|
270
|
+
height: height,
|
|
271
|
+
viewBox: `0 0 64 64`,
|
|
272
|
+
preserveAspectRatio: "none",
|
|
273
|
+
className: className,
|
|
274
|
+
version: "1.1",
|
|
275
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
276
|
+
children: [/*#__PURE__*/jsxs("defs", {
|
|
277
|
+
children: [/*#__PURE__*/jsxs("linearGradient", {
|
|
278
|
+
id: `${id}-bg`,
|
|
279
|
+
children: [/*#__PURE__*/jsx("stop", {
|
|
280
|
+
offset: "20%",
|
|
281
|
+
stopColor: bgColor1
|
|
282
|
+
}), /*#__PURE__*/jsx("stop", {
|
|
283
|
+
offset: "100%",
|
|
284
|
+
stopColor: bgColor2
|
|
146
285
|
})]
|
|
286
|
+
}), /*#__PURE__*/jsxs("radialGradient", {
|
|
287
|
+
id: `${id}-circle`,
|
|
288
|
+
children: [/*#__PURE__*/jsx("stop", {
|
|
289
|
+
offset: "10%",
|
|
290
|
+
stopColor: glowColor
|
|
291
|
+
}), /*#__PURE__*/jsx("stop", {
|
|
292
|
+
offset: "100%",
|
|
293
|
+
stopColor: "transparent"
|
|
294
|
+
})]
|
|
295
|
+
}), /*#__PURE__*/jsx("clipPath", {
|
|
296
|
+
id: `${id}-clip`,
|
|
297
|
+
children: /*#__PURE__*/jsx("circle", {
|
|
298
|
+
cx: "32",
|
|
299
|
+
cy: "32",
|
|
300
|
+
r: "48"
|
|
301
|
+
})
|
|
147
302
|
})]
|
|
303
|
+
}), /*#__PURE__*/jsx("g", {
|
|
304
|
+
clipPath: `url(#${id}-clip)`,
|
|
305
|
+
children: /*#__PURE__*/jsxs("g", {
|
|
306
|
+
transform: transform,
|
|
307
|
+
children: [/*#__PURE__*/jsx("rect", {
|
|
308
|
+
fill: `url(#${id}-bg)`,
|
|
309
|
+
x: -16,
|
|
310
|
+
y: -16,
|
|
311
|
+
width: 96,
|
|
312
|
+
height: 96
|
|
313
|
+
}), /*#__PURE__*/jsx("circle", {
|
|
314
|
+
fill: `url(#${id}-circle)`,
|
|
315
|
+
cx: cx,
|
|
316
|
+
cy: cy,
|
|
317
|
+
r: 45,
|
|
318
|
+
opacity: 0.7
|
|
319
|
+
})]
|
|
320
|
+
})
|
|
148
321
|
})]
|
|
149
322
|
});
|
|
150
323
|
};
|
|
151
324
|
|
|
152
|
-
export { TalismanOrb, useTalismanOrb };
|
|
325
|
+
export { TalismanOrb, TalismanOrbRectangle, useTalismanOrb };
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@talismn/orb",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"author": "Talisman",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
8
8
|
},
|
|
9
|
+
"sideEffects": false,
|
|
9
10
|
"repository": {
|
|
10
11
|
"directory": "packages/orb",
|
|
11
12
|
"type": "git",
|
|
@@ -14,28 +15,23 @@
|
|
|
14
15
|
"type": "module",
|
|
15
16
|
"main": "dist/talismn-orb.cjs.js",
|
|
16
17
|
"module": "dist/talismn-orb.esm.js",
|
|
17
|
-
"scripts": {
|
|
18
|
-
"lint": "eslint src --max-warnings 0",
|
|
19
|
-
"clean": "rm -rf dist && rm -rf .turbo rm -rf node_modules"
|
|
20
|
-
},
|
|
21
18
|
"dependencies": {
|
|
19
|
+
"@noble/hashes": "1.8.0",
|
|
20
|
+
"@scure/base": "1.2.6",
|
|
22
21
|
"blueimp-md5": "2.19.0",
|
|
23
|
-
"color": "4.2.3"
|
|
24
|
-
"nanoid": "3.3.7"
|
|
22
|
+
"color": "4.2.3"
|
|
25
23
|
},
|
|
26
24
|
"devDependencies": {
|
|
27
|
-
"@
|
|
25
|
+
"@types/react": "18.3.12",
|
|
26
|
+
"@types/react-dom": "18.3.1",
|
|
27
|
+
"eslint": "^8.57.1",
|
|
28
|
+
"react": "18.3.1",
|
|
29
|
+
"react-dom": "18.3.1",
|
|
30
|
+
"typescript": "^5.6.3",
|
|
28
31
|
"@talismn/eslint-config": "0.0.3",
|
|
29
|
-
"@talismn/tsconfig": "0.0.2"
|
|
30
|
-
"@types/react": "18.0.14",
|
|
31
|
-
"@types/react-dom": "18.2.18",
|
|
32
|
-
"eslint": "^8.52.0",
|
|
33
|
-
"react": "18.2.0",
|
|
34
|
-
"react-dom": "18.2.0",
|
|
35
|
-
"typescript": "^5.2.2"
|
|
32
|
+
"@talismn/tsconfig": "0.0.2"
|
|
36
33
|
},
|
|
37
34
|
"peerDependencies": {
|
|
38
|
-
"@polkadot/util-crypto": "12.x",
|
|
39
35
|
"react": "*",
|
|
40
36
|
"react-dom": "*"
|
|
41
37
|
},
|
|
@@ -44,5 +40,9 @@
|
|
|
44
40
|
"react": "React",
|
|
45
41
|
"react-dom": "ReactDOM"
|
|
46
42
|
}
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"lint": "eslint src --max-warnings 0",
|
|
46
|
+
"clean": "rm -rf dist .turbo node_modules"
|
|
47
47
|
}
|
|
48
48
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import md5 from "blueimp-md5"
|
|
2
2
|
import Color from "color"
|
|
3
|
-
import {
|
|
4
|
-
import { FC, useMemo } from "react"
|
|
3
|
+
import { FC, useId, useMemo } from "react"
|
|
5
4
|
|
|
6
|
-
import {
|
|
5
|
+
import { normalizeAddress } from "../util/normalizeAddress"
|
|
7
6
|
|
|
8
7
|
const djb2 = (str: string) => {
|
|
9
8
|
let hash = 5381
|
|
@@ -23,14 +22,17 @@ const colorFromHash = (hash: string) => {
|
|
|
23
22
|
const rotateText = (text: string, nbChars = 0) => text.slice(nbChars) + text.slice(0, nbChars)
|
|
24
23
|
|
|
25
24
|
type TalismanOrbProps = { seed: string; width?: number; height?: number; className?: string }
|
|
25
|
+
type LogoIconType = "ethereum" | "substrate"
|
|
26
26
|
|
|
27
27
|
export const useTalismanOrb = (seed: string) => {
|
|
28
|
+
const id = useId()
|
|
29
|
+
|
|
28
30
|
return useMemo(() => {
|
|
29
|
-
const
|
|
31
|
+
const iconType: LogoIconType = seed?.startsWith("0x") ? "ethereum" : "substrate"
|
|
30
32
|
try {
|
|
31
33
|
// seed may be specific to a ss58 prefix, get the base address
|
|
32
34
|
// eslint-disable-next-line no-var
|
|
33
|
-
var address =
|
|
35
|
+
var address = normalizeAddress(seed)
|
|
34
36
|
} catch (err) {
|
|
35
37
|
address = seed
|
|
36
38
|
}
|
|
@@ -43,7 +45,7 @@ export const useTalismanOrb = (seed: string) => {
|
|
|
43
45
|
// the 2 darkest ones will be used as gradient BG
|
|
44
46
|
// the lightest one will be used as gradient circle, to mimic a 3D lighting effect
|
|
45
47
|
const colors = [colorFromHash(hash1), colorFromHash(hash2), colorFromHash(hash3)].sort(
|
|
46
|
-
(c1, c2) => c1.lightness() - c2.lightness()
|
|
48
|
+
(c1, c2) => c1.lightness() - c2.lightness(),
|
|
47
49
|
)
|
|
48
50
|
|
|
49
51
|
// random location in top left corner, avoid beeing to close from the center
|
|
@@ -54,16 +56,77 @@ export const useTalismanOrb = (seed: string) => {
|
|
|
54
56
|
const rotation = valueFromHash(hash1, 360)
|
|
55
57
|
|
|
56
58
|
return {
|
|
57
|
-
id
|
|
59
|
+
id, //multiple avatars should cohabit on the same page
|
|
58
60
|
bgColor1: colors[0].hex(),
|
|
59
61
|
bgColor2: colors[1].hex(),
|
|
60
62
|
glowColor: colors[2].hex(),
|
|
61
63
|
transform: `rotate(${rotation} 32 32)`,
|
|
62
64
|
cx: dotX,
|
|
63
65
|
cy: dotY,
|
|
64
|
-
|
|
66
|
+
iconType,
|
|
65
67
|
}
|
|
66
|
-
}, [seed])
|
|
68
|
+
}, [id, seed])
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const TalismanOrbLogo = ({ id, iconType }: { id: string; iconType?: LogoIconType }) => {
|
|
72
|
+
switch (iconType) {
|
|
73
|
+
case "ethereum":
|
|
74
|
+
return (
|
|
75
|
+
<g opacity="0.75" transform="scale(0.7) translate(14 14)" className="orb-type">
|
|
76
|
+
<path
|
|
77
|
+
d="M12.8101 32.76L32.0001 44.62L51.1901 32.76L32.0001 -0.0699997L12.8101 32.76Z"
|
|
78
|
+
fill="white"
|
|
79
|
+
/>
|
|
80
|
+
<path
|
|
81
|
+
d="M12.8101 36.48L32.0001 48.43L51.1901 36.48L32.0001 63.93L12.8101 36.48Z"
|
|
82
|
+
fill="white"
|
|
83
|
+
/>
|
|
84
|
+
</g>
|
|
85
|
+
)
|
|
86
|
+
case "substrate":
|
|
87
|
+
return (
|
|
88
|
+
<>
|
|
89
|
+
<g
|
|
90
|
+
clipPath={`url(#${id}_1751_2030)`}
|
|
91
|
+
opacity="0.75"
|
|
92
|
+
transform="scale(2.2) translate(4.5 3.9)"
|
|
93
|
+
className="orb-type"
|
|
94
|
+
>
|
|
95
|
+
<path
|
|
96
|
+
d="M9.99937 4.4612C12.1176 4.4612 13.8347 3.46253 13.8347 2.2306C13.8347 0.998674 12.1176 0 9.99937 0C7.88119 0 6.16406 0.998674 6.16406 2.2306C6.16406 3.46253 7.88119 4.4612 9.99937 4.4612Z"
|
|
97
|
+
fill="white"
|
|
98
|
+
/>
|
|
99
|
+
<path
|
|
100
|
+
d="M9.99937 21.2683C12.1176 21.2683 13.8347 20.2697 13.8347 19.0377C13.8347 17.8058 12.1176 16.8071 9.99937 16.8071C7.88119 16.8071 6.16406 17.8058 6.16406 19.0377C6.16406 20.2697 7.88119 21.2683 9.99937 21.2683Z"
|
|
101
|
+
fill="white"
|
|
102
|
+
/>
|
|
103
|
+
<path
|
|
104
|
+
d="M4.65427 7.54892C5.71336 5.71457 5.70649 3.72787 4.63892 3.11149C3.57135 2.49511 1.84735 3.48246 0.788259 5.31681C-0.270832 7.15115 -0.263958 9.13786 0.803612 9.75424C1.87118 10.3706 3.59518 9.38326 4.65427 7.54892Z"
|
|
105
|
+
fill="white"
|
|
106
|
+
/>
|
|
107
|
+
<path
|
|
108
|
+
d="M19.2083 15.9515C20.2674 14.1171 20.2611 12.1307 19.1943 11.5148C18.1274 10.8988 16.404 11.8865 15.3449 13.7209C14.2858 15.5552 14.2921 17.5416 15.3589 18.1575C16.4258 18.7735 18.1492 17.7858 19.2083 15.9515Z"
|
|
109
|
+
fill="white"
|
|
110
|
+
/>
|
|
111
|
+
<path
|
|
112
|
+
d="M4.6399 18.1571C5.70747 17.5407 5.71434 15.554 4.65525 13.7196C3.59616 11.8853 1.87216 10.8979 0.804589 11.5143C-0.262981 12.1307 -0.269855 14.1174 0.789235 15.9517C1.84833 17.7861 3.57233 18.7734 4.6399 18.1571Z"
|
|
113
|
+
fill="white"
|
|
114
|
+
/>
|
|
115
|
+
<path
|
|
116
|
+
d="M19.1952 9.75475C20.2621 9.13878 20.2684 7.15241 19.2093 5.31807C18.1502 3.48372 16.4268 2.49603 15.3599 3.11199C14.2931 3.72796 14.2868 5.71433 15.3459 7.54867C16.405 9.38302 18.1284 10.3707 19.1952 9.75475Z"
|
|
117
|
+
fill="white"
|
|
118
|
+
/>
|
|
119
|
+
</g>
|
|
120
|
+
<defs>
|
|
121
|
+
<clipPath id={`${id}_1751_2030`}>
|
|
122
|
+
<rect width="20" height="21.2699" fill="white" />
|
|
123
|
+
</clipPath>
|
|
124
|
+
</defs>
|
|
125
|
+
</>
|
|
126
|
+
)
|
|
127
|
+
default:
|
|
128
|
+
return null
|
|
129
|
+
}
|
|
67
130
|
}
|
|
68
131
|
|
|
69
132
|
export const TalismanOrb: FC<TalismanOrbProps> = ({
|
|
@@ -72,7 +135,7 @@ export const TalismanOrb: FC<TalismanOrbProps> = ({
|
|
|
72
135
|
height = "1em",
|
|
73
136
|
className,
|
|
74
137
|
}) => {
|
|
75
|
-
const { id, bgColor1, bgColor2, transform, glowColor, cx, cy,
|
|
138
|
+
const { id, bgColor1, bgColor2, transform, glowColor, cx, cy, iconType } = useTalismanOrb(seed)
|
|
76
139
|
|
|
77
140
|
return (
|
|
78
141
|
<svg
|
|
@@ -101,18 +164,43 @@ export const TalismanOrb: FC<TalismanOrbProps> = ({
|
|
|
101
164
|
<rect fill={`url(#${id}-bg)`} x={0} y={0} width={64} height={64} />
|
|
102
165
|
<circle fill={`url(#${id}-circle)`} cx={cx} cy={cy} r={45} opacity={0.7} />
|
|
103
166
|
</g>
|
|
104
|
-
{
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
167
|
+
<TalismanOrbLogo id={id} iconType={iconType} />
|
|
168
|
+
</g>
|
|
169
|
+
</svg>
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export const TalismanOrbRectangle: FC<TalismanOrbProps> = ({ width, height, seed, className }) => {
|
|
174
|
+
const { id, bgColor1, bgColor2, transform, glowColor, cx, cy } = useTalismanOrb(seed)
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<svg
|
|
178
|
+
width={width}
|
|
179
|
+
height={height}
|
|
180
|
+
viewBox={`0 0 64 64`}
|
|
181
|
+
preserveAspectRatio="none"
|
|
182
|
+
className={className}
|
|
183
|
+
version="1.1"
|
|
184
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
185
|
+
>
|
|
186
|
+
<defs>
|
|
187
|
+
<linearGradient id={`${id}-bg`}>
|
|
188
|
+
<stop offset="20%" stopColor={bgColor1} />
|
|
189
|
+
<stop offset="100%" stopColor={bgColor2} />
|
|
190
|
+
</linearGradient>
|
|
191
|
+
<radialGradient id={`${id}-circle`}>
|
|
192
|
+
<stop offset="10%" stopColor={glowColor} />
|
|
193
|
+
<stop offset="100%" stopColor="transparent" />
|
|
194
|
+
</radialGradient>
|
|
195
|
+
<clipPath id={`${id}-clip`}>
|
|
196
|
+
<circle cx="32" cy="32" r="48" />
|
|
197
|
+
</clipPath>
|
|
198
|
+
</defs>
|
|
199
|
+
<g clipPath={`url(#${id}-clip)`}>
|
|
200
|
+
<g transform={transform}>
|
|
201
|
+
<rect fill={`url(#${id}-bg)`} x={-16} y={-16} width={96} height={96} />
|
|
202
|
+
<circle fill={`url(#${id}-circle)`} cx={cx} cy={cy} r={45} opacity={0.7} />
|
|
203
|
+
</g>
|
|
116
204
|
</g>
|
|
117
205
|
</svg>
|
|
118
206
|
)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// inspired from https://github.com/wevm/viem/blob/main/src/utils/address/getAddress.ts
|
|
2
|
+
|
|
3
|
+
import { keccak_256 } from "@noble/hashes/sha3"
|
|
4
|
+
|
|
5
|
+
const TEXT_ENCODER = new TextEncoder()
|
|
6
|
+
|
|
7
|
+
export const isEthAddress = (address: string): address is `0x${string}` =>
|
|
8
|
+
/^0x[a-fA-F0-9]{40}$/.test(address)
|
|
9
|
+
|
|
10
|
+
export const normalizeEthAddress = (address: `0x${string}`): `0x${string}` => {
|
|
11
|
+
if (!isEthAddress(address)) throw new Error(`Invalid Ethereum address ${address}`)
|
|
12
|
+
|
|
13
|
+
const rawAddress = address.toLowerCase().substring(2)
|
|
14
|
+
const bytes = TEXT_ENCODER.encode(rawAddress)
|
|
15
|
+
const hash = keccak_256(bytes)
|
|
16
|
+
|
|
17
|
+
// apply checksum
|
|
18
|
+
const csAddress = rawAddress.split("")
|
|
19
|
+
for (let i = 0; i < 40; i += 2) {
|
|
20
|
+
if (hash[i >> 1] >> 4 >= 8 && address[i]) {
|
|
21
|
+
csAddress[i] = csAddress[i].toUpperCase()
|
|
22
|
+
}
|
|
23
|
+
if ((hash[i >> 1] & 0x0f) >= 8 && address[i + 1]) {
|
|
24
|
+
csAddress[i + 1] = csAddress[i + 1].toUpperCase()
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return `0x${csAddress.join("")}`
|
|
29
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// inspired from https://github.com/polkadot-api/polkadot-api/blob/main/packages/substrate-bindings/src/codecs/scale/AccountId.ts
|
|
2
|
+
|
|
3
|
+
import { blake2b } from "@noble/hashes/blake2b"
|
|
4
|
+
import { base58 } from "@scure/base"
|
|
5
|
+
|
|
6
|
+
const SS58_PREFIX = new TextEncoder().encode("SS58PRE")
|
|
7
|
+
const SS58_FORMAT = 42
|
|
8
|
+
const CHECKSUM_LENGTH = 2
|
|
9
|
+
const VALID_BYTES_LENGTH = [32, 33]
|
|
10
|
+
|
|
11
|
+
const encode = (publicKey: Uint8Array) => {
|
|
12
|
+
const prefixBytes = Uint8Array.of(SS58_FORMAT)
|
|
13
|
+
const checksum = blake2b(Uint8Array.of(...SS58_PREFIX, ...prefixBytes, ...publicKey), {
|
|
14
|
+
dkLen: 64,
|
|
15
|
+
}).subarray(0, CHECKSUM_LENGTH)
|
|
16
|
+
return base58.encode(Uint8Array.of(...prefixBytes, ...publicKey, ...checksum))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const decode = (address: string) => {
|
|
20
|
+
const decoded = base58.decode(address)
|
|
21
|
+
const prefixBytes = decoded.subarray(0, decoded[0] & 0b0100_0000 ? 2 : 1)
|
|
22
|
+
const publicKey = decoded.subarray(prefixBytes.length, decoded.length - CHECKSUM_LENGTH)
|
|
23
|
+
|
|
24
|
+
if (!VALID_BYTES_LENGTH.includes(publicKey.length)) throw new Error("Invalid public key length")
|
|
25
|
+
const checksum = decoded.subarray(prefixBytes.length + publicKey.length)
|
|
26
|
+
const expectedChecksum = blake2b(Uint8Array.of(...SS58_PREFIX, ...prefixBytes, ...publicKey), {
|
|
27
|
+
dkLen: 64,
|
|
28
|
+
}).subarray(0, CHECKSUM_LENGTH)
|
|
29
|
+
if (checksum[0] !== expectedChecksum[0] || checksum[1] !== expectedChecksum[1])
|
|
30
|
+
throw new Error("Invalid checksum")
|
|
31
|
+
|
|
32
|
+
return publicKey.slice()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const normalizeSubAddress = (address: string) => {
|
|
36
|
+
// source address might be encoded with a different prefix than 42
|
|
37
|
+
// decode then reencode with prefix 42
|
|
38
|
+
return encode(decode(address))
|
|
39
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function encodeAnyAddress(key: string | Uint8Array, ss58Format?: number | undefined): string;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { encodeAddress, ethereumEncode, isEthereumAddress } from "@polkadot/util-crypto"
|
|
2
|
-
|
|
3
|
-
export function encodeAnyAddress(
|
|
4
|
-
key: string | Uint8Array,
|
|
5
|
-
ss58Format?: number | undefined
|
|
6
|
-
): string {
|
|
7
|
-
try {
|
|
8
|
-
return encodeAddress(key, ss58Format)
|
|
9
|
-
} catch (error) {
|
|
10
|
-
if (typeof key !== "string") throw error
|
|
11
|
-
if (!isEthereumAddress(key)) throw error
|
|
12
|
-
|
|
13
|
-
return ethereumEncode(key)
|
|
14
|
-
}
|
|
15
|
-
}
|