@calimero-network/mero-js 13.4.0 → 15.0.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.
@@ -24,13 +24,12 @@
24
24
  /** What a warrant authorises. */
25
25
  export interface WarrantInput {
26
26
  /**
27
- * The context, base58 — the same string every other method here takes.
27
+ * The context, hex — the same string every other method here takes.
28
28
  *
29
- * Base58 while the two accounts below are hex, because that is how the node
30
- * spells them: `ContextId` and `PublicKey` are base58, `AccountId` and
31
- * `DeviceId` are hex. Mixing them up produces a valid-looking value that only
32
- * fails against real data, so this module decodes each by its own rule rather
33
- * than accepting "32 bytes, somehow".
29
+ * Hex like everything else on this interface. It was base58 while the node
30
+ * spelled `ContextId` and `PublicKey` that way and `AccountId` and `DeviceId`
31
+ * in hex; that split is gone, so this module no longer needs a rule per field
32
+ * and no longer needs a base58 decoder to enforce one.
34
33
  */
35
34
  context: string;
36
35
  /** The author's account, hex — whose consent this is. */
@@ -28,7 +28,6 @@ const PKCS8_ED25519_PREFIX = new Uint8Array([
28
28
  0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x04,
29
29
  0x22, 0x04, 0x20,
30
30
  ]);
31
- const B58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
32
31
  function hex(bytes) {
33
32
  return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
34
33
  }
@@ -46,58 +45,6 @@ function fromHex(value, label, expectedBytes) {
46
45
  }
47
46
  return bytes;
48
47
  }
49
- /**
50
- * Decode base58 to exactly 32 bytes.
51
- *
52
- * Hand-rolled rather than pulled in: it is fifteen lines, and a dependency here
53
- * would be this package's first.
54
- */
55
- function fromBase58(value, label) {
56
- const clean = value.trim();
57
- let n = 0n;
58
- for (const char of clean) {
59
- const digit = B58.indexOf(char);
60
- if (digit < 0) {
61
- throw new Error(`${label} is not base58: unexpected character '${char}'`);
62
- }
63
- n = n * 58n + BigInt(digit);
64
- }
65
- const out = new Uint8Array(32);
66
- for (let i = 31; i >= 0; i -= 1) {
67
- out[i] = Number(n & 0xffn);
68
- n >>= 8n;
69
- }
70
- if (n !== 0n) {
71
- throw new Error(`${label} decodes to more than 32 bytes`);
72
- }
73
- // Round-trip, because decoding alone accepts too much. Hex digits are mostly
74
- // valid base58 characters, so a context handed over in hex by mistake can
75
- // decode to a real-looking 32 bytes instead of failing — `'11'.repeat(32)`
76
- // decodes to all zeros. Re-encoding catches it: the canonical form of those
77
- // bytes is 32 '1's, not 64.
78
- if (toBase58(out) !== clean) {
79
- throw new Error(`${label} is not a canonical base58 32-byte id. If you have it in hex, ` +
80
- `note that ${label} is base58 here while accounts are hex`);
81
- }
82
- return out;
83
- }
84
- /** Encode 32 bytes as base58, for the canonical check above. */
85
- function toBase58(bytes) {
86
- let n = 0n;
87
- for (const byte of bytes) {
88
- n = (n << 8n) | BigInt(byte);
89
- }
90
- let out = '';
91
- while (n > 0n) {
92
- out = B58[Number(n % 58n)] + out;
93
- n /= 58n;
94
- }
95
- let leadingZeros = 0;
96
- while (leadingZeros < bytes.length && bytes[leadingZeros] === 0) {
97
- leadingZeros += 1;
98
- }
99
- return '1'.repeat(leadingZeros) + out;
100
- }
101
48
  function u64le(value) {
102
49
  const out = new Uint8Array(8);
103
50
  new DataView(out.buffer).setBigUint64(0, BigInt(value), true);
@@ -159,7 +106,7 @@ async function importSigningKey(secretHex) {
159
106
  * was signed.
160
107
  */
161
108
  export async function signWarrant(input) {
162
- const context = fromBase58(input.context, 'context');
109
+ const context = fromHex(input.context, 'context', 32);
163
110
  const authorAccount = fromHex(input.authorAccount, 'authorAccount', 32);
164
111
  const executor = fromHex(input.executor, 'executor', 32);
165
112
  const key = await importSigningKey(input.deviceSecret);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calimero-network/mero-js",
3
- "version": "13.4.0",
3
+ "version": "15.0.0",
4
4
  "description": "Pure JavaScript SDK for Calimero",
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,43 +0,0 @@
1
- /**
2
- * Account id encoding.
3
- *
4
- * Core names the same account in two encodings, and the split is deliberate:
5
- * the admin API serves and requires **64 hex characters**, while contract data
6
- * (`sender`, `get_profiles`, anything stamped by `env::account_id()`) carries
7
- * **base58**. They are rendered differently on purpose, so that handing one
8
- * where the other is expected fails loudly instead of resolving to the wrong
9
- * principal.
10
- *
11
- * The cost is that any client reading a member from the admin API and
12
- * comparing it against a message sender is comparing two spellings of the same
13
- * value. A raw `===` silently never matches, and passing a contract-sourced id
14
- * to an admin route answers `400 Invalid account format: expected 64 hex
15
- * characters (32 bytes)`. Both failure modes are quiet enough to ship.
16
- *
17
- * These helpers canonicalise at that boundary. They convert accounts and
18
- * nothing else: a device key, an alias or any other value is returned
19
- * unchanged, because converting is this module's job and destroying what it
20
- * cannot convert is not.
21
- */
22
- /**
23
- * Canonicalise an account id to hex — the form every admin route expects.
24
- *
25
- * Returns the input unchanged if it is not a 32-byte account, so a device key
26
- * or an alias survives a call to this function intact.
27
- */
28
- export declare function toAccountHex(id: string | null | undefined): string;
29
- /**
30
- * Canonicalise an account id to base58 — the form the contract stamps on
31
- * `sender` and keys `get_profiles` by.
32
- *
33
- * Returns the input unchanged if it is not a 32-byte account.
34
- */
35
- export declare function toAccountBase58(id: string | null | undefined): string;
36
- /**
37
- * True when both ids name the same account, whichever encoding each is in.
38
- *
39
- * This is the comparison to reach for when checking a contract-sourced
40
- * `sender` against an admin-sourced member id. Two ids that are not accounts
41
- * compare false rather than accidentally matching as raw strings.
42
- */
43
- export declare function sameAccount(a: string | null | undefined, b: string | null | undefined): boolean;
@@ -1,139 +0,0 @@
1
- /**
2
- * Account id encoding.
3
- *
4
- * Core names the same account in two encodings, and the split is deliberate:
5
- * the admin API serves and requires **64 hex characters**, while contract data
6
- * (`sender`, `get_profiles`, anything stamped by `env::account_id()`) carries
7
- * **base58**. They are rendered differently on purpose, so that handing one
8
- * where the other is expected fails loudly instead of resolving to the wrong
9
- * principal.
10
- *
11
- * The cost is that any client reading a member from the admin API and
12
- * comparing it against a message sender is comparing two spellings of the same
13
- * value. A raw `===` silently never matches, and passing a contract-sourced id
14
- * to an admin route answers `400 Invalid account format: expected 64 hex
15
- * characters (32 bytes)`. Both failure modes are quiet enough to ship.
16
- *
17
- * These helpers canonicalise at that boundary. They convert accounts and
18
- * nothing else: a device key, an alias or any other value is returned
19
- * unchanged, because converting is this module's job and destroying what it
20
- * cannot convert is not.
21
- */
22
- const BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
23
- /** A 32-byte account rendered as 64 hex characters. */
24
- const HEX_ACCOUNT = /^[0-9a-f]{64}$/i;
25
- /** Bytes → base58. Leading zero bytes become leading '1's, as in bitcoin base58. */
26
- function encodeBase58(bytes) {
27
- if (bytes.length === 0)
28
- return '';
29
- const digits = [0];
30
- for (const byte of bytes) {
31
- let carry = byte;
32
- for (let i = 0; i < digits.length; i++) {
33
- carry += digits[i] << 8;
34
- digits[i] = carry % 58;
35
- carry = (carry / 58) | 0;
36
- }
37
- while (carry > 0) {
38
- digits.push(carry % 58);
39
- carry = (carry / 58) | 0;
40
- }
41
- }
42
- let out = '';
43
- for (const byte of bytes) {
44
- if (byte !== 0)
45
- break;
46
- out += BASE58_ALPHABET[0];
47
- }
48
- for (let i = digits.length - 1; i >= 0; i--) {
49
- out += BASE58_ALPHABET[digits[i]];
50
- }
51
- return out;
52
- }
53
- /** base58 → bytes. Returns null when the input is not valid base58. */
54
- function decodeBase58(value) {
55
- if (value.length === 0)
56
- return null;
57
- const bytes = [0];
58
- for (const char of value) {
59
- const index = BASE58_ALPHABET.indexOf(char);
60
- if (index < 0)
61
- return null;
62
- let carry = index;
63
- for (let i = 0; i < bytes.length; i++) {
64
- carry += bytes[i] * 58;
65
- bytes[i] = carry & 0xff;
66
- carry >>= 8;
67
- }
68
- while (carry > 0) {
69
- bytes.push(carry & 0xff);
70
- carry >>= 8;
71
- }
72
- }
73
- let leadingZeros = 0;
74
- for (const char of value) {
75
- if (char !== BASE58_ALPHABET[0])
76
- break;
77
- leadingZeros++;
78
- }
79
- return Uint8Array.from([
80
- ...new Array(leadingZeros).fill(0),
81
- ...bytes.reverse(),
82
- ]);
83
- }
84
- function toHex(bytes) {
85
- let out = '';
86
- for (const byte of bytes)
87
- out += byte.toString(16).padStart(2, '0');
88
- return out;
89
- }
90
- function fromHex(hex) {
91
- const bytes = new Uint8Array(hex.length / 2);
92
- for (let i = 0; i < bytes.length; i++) {
93
- bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
94
- }
95
- return bytes;
96
- }
97
- /**
98
- * Canonicalise an account id to hex — the form every admin route expects.
99
- *
100
- * Returns the input unchanged if it is not a 32-byte account, so a device key
101
- * or an alias survives a call to this function intact.
102
- */
103
- export function toAccountHex(id) {
104
- const trimmed = (id ?? '').trim();
105
- if (!trimmed)
106
- return '';
107
- if (HEX_ACCOUNT.test(trimmed))
108
- return trimmed.toLowerCase();
109
- const decoded = decodeBase58(trimmed);
110
- if (!decoded || decoded.length !== 32)
111
- return trimmed;
112
- return toHex(decoded);
113
- }
114
- /**
115
- * Canonicalise an account id to base58 — the form the contract stamps on
116
- * `sender` and keys `get_profiles` by.
117
- *
118
- * Returns the input unchanged if it is not a 32-byte account.
119
- */
120
- export function toAccountBase58(id) {
121
- const trimmed = (id ?? '').trim();
122
- if (!trimmed)
123
- return '';
124
- if (!HEX_ACCOUNT.test(trimmed))
125
- return trimmed;
126
- return encodeBase58(fromHex(trimmed));
127
- }
128
- /**
129
- * True when both ids name the same account, whichever encoding each is in.
130
- *
131
- * This is the comparison to reach for when checking a contract-sourced
132
- * `sender` against an admin-sourced member id. Two ids that are not accounts
133
- * compare false rather than accidentally matching as raw strings.
134
- */
135
- export function sameAccount(a, b) {
136
- const left = toAccountHex(a);
137
- const right = toAccountHex(b);
138
- return HEX_ACCOUNT.test(left) && left === right;
139
- }