near-kit 0.4.4 → 0.5.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.
@@ -0,0 +1,208 @@
1
+ /**
2
+ * NEP-616 StateInit utilities for deterministic account IDs
3
+ *
4
+ * This module provides utilities for working with NEP-616 Deterministic AccountIds.
5
+ * These account IDs are derived from the contract initialization state using the formula:
6
+ * `"0s" + hex(keccak256(borsh(state_init))[12..32])`
7
+ *
8
+ * @see https://github.com/near/NEPs/pull/616
9
+ */
10
+ import { keccak_256 } from "@noble/hashes/sha3.js";
11
+ import { base58, hex } from "@scure/base";
12
+ import { b } from "@zorsh/zorsh";
13
+ // ==================== Helper Functions ====================
14
+ /**
15
+ * Parse and validate a code hash from either base58 string or Uint8Array
16
+ *
17
+ * @param codeHash - Code hash as base58 string or Uint8Array
18
+ * @returns Validated 32-byte hash as Uint8Array
19
+ * @throws Error if base58 is invalid or hash is not 32 bytes
20
+ */
21
+ export function parseCodeHash(codeHash) {
22
+ let hashBytes;
23
+ if (typeof codeHash === "string") {
24
+ try {
25
+ hashBytes = base58.decode(codeHash);
26
+ }
27
+ catch {
28
+ throw new Error(`Invalid base58 code hash: ${codeHash}`);
29
+ }
30
+ }
31
+ else {
32
+ hashBytes = codeHash;
33
+ }
34
+ if (hashBytes.length !== 32) {
35
+ throw new Error(`Code hash must be 32 bytes, got ${hashBytes.length} bytes`);
36
+ }
37
+ return hashBytes;
38
+ }
39
+ // ==================== Borsh Schemas for StateInit ====================
40
+ /**
41
+ * GlobalContractIdentifier enum for Borsh serialization
42
+ * 0 = CodeHash (32-byte hash)
43
+ * 1 = AccountId (string)
44
+ */
45
+ const GlobalContractIdentifierSchema = b.enum({
46
+ CodeHash: b.array(b.u8(), 32),
47
+ AccountId: b.string(),
48
+ });
49
+ /**
50
+ * StateInitV1 struct for Borsh serialization
51
+ */
52
+ const StateInitV1Schema = b.struct({
53
+ code: GlobalContractIdentifierSchema,
54
+ data: b.hashMap(b.bytes(), b.bytes()),
55
+ });
56
+ /**
57
+ * StateInit enum (versioned) for Borsh serialization
58
+ */
59
+ const StateInitSchema = b.enum({
60
+ V1: StateInitV1Schema,
61
+ });
62
+ /**
63
+ * Create a StateInit object from options
64
+ *
65
+ * @param options - StateInit configuration
66
+ * @returns StateInit object
67
+ */
68
+ export function createStateInit(options) {
69
+ let code;
70
+ if ("accountId" in options.code) {
71
+ code = { type: "accountId", accountId: options.code.accountId };
72
+ }
73
+ else {
74
+ const hashBytes = parseCodeHash(options.code.codeHash);
75
+ code = { type: "codeHash", hash: hashBytes };
76
+ }
77
+ return {
78
+ code,
79
+ data: options.data ?? new Map(),
80
+ };
81
+ }
82
+ /**
83
+ * Serialize a StateInit to Borsh bytes
84
+ *
85
+ * @param stateInit - StateInit object to serialize
86
+ * @returns Borsh-serialized bytes
87
+ */
88
+ export function serializeStateInit(stateInit) {
89
+ let codeIdentifier;
90
+ if (stateInit.code.type === "accountId") {
91
+ codeIdentifier = { AccountId: stateInit.code.accountId };
92
+ }
93
+ else {
94
+ codeIdentifier = { CodeHash: Array.from(stateInit.code.hash) };
95
+ }
96
+ // Sort the data map by keys to ensure deterministic serialization
97
+ // NEP-616 specifies BTreeMap which has sorted order, but JavaScript Map
98
+ // maintains insertion order. We must sort to ensure the same key-value pairs
99
+ // produce the same account ID regardless of insertion order.
100
+ const sortedData = new Map(Array.from(stateInit.data.entries()).sort((a, b) => {
101
+ const [keyA] = a;
102
+ const [keyB] = b;
103
+ // Compare byte-by-byte
104
+ for (let i = 0; i < Math.min(keyA.length, keyB.length); i++) {
105
+ const byteA = keyA[i];
106
+ const byteB = keyB[i];
107
+ if (byteA !== undefined && byteB !== undefined && byteA !== byteB) {
108
+ return byteA - byteB;
109
+ }
110
+ }
111
+ // If all bytes match, shorter key comes first
112
+ return keyA.length - keyB.length;
113
+ }));
114
+ return StateInitSchema.serialize({
115
+ V1: {
116
+ code: codeIdentifier,
117
+ data: sortedData,
118
+ },
119
+ });
120
+ }
121
+ /**
122
+ * Derive a deterministic account ID from a StateInit according to NEP-616.
123
+ *
124
+ * The account ID is derived as: `"0s" + hex(keccak256(borsh(state_init))[12..32])`
125
+ *
126
+ * This produces a 42-character account ID that:
127
+ * - Starts with "0s" prefix (distinguishes from Ethereum implicit accounts "0x")
128
+ * - Followed by 40 hex characters (20 bytes from the keccak256 hash)
129
+ *
130
+ * @param stateInit - StateInit object or options to create one
131
+ * @returns The deterministically derived account ID
132
+ *
133
+ * @example
134
+ * ```typescript
135
+ * // From a global contract by account ID
136
+ * const accountId = deriveAccountId({
137
+ * code: { accountId: "publisher.near" },
138
+ * })
139
+ * // => "0s1234567890abcdef1234567890abcdef12345678"
140
+ *
141
+ * // From a global contract by code hash
142
+ * const accountId = deriveAccountId({
143
+ * code: { codeHash: hashBytes },
144
+ * })
145
+ *
146
+ * // With initial storage data
147
+ * const accountId = deriveAccountId({
148
+ * code: { accountId: "publisher.near" },
149
+ * data: new Map([[key1, value1], [key2, value2]]),
150
+ * })
151
+ * ```
152
+ */
153
+ export function deriveAccountId(options) {
154
+ const stateInit = createStateInit(options);
155
+ const serialized = serializeStateInit(stateInit);
156
+ const hash = keccak_256(serialized);
157
+ // Take last 20 bytes (indices 12-32) of the hash
158
+ const suffix = hash.slice(12, 32);
159
+ return `0s${hex.encode(suffix)}`;
160
+ }
161
+ /**
162
+ * Verify that an account ID matches the expected deterministic derivation from a StateInit.
163
+ *
164
+ * @param accountId - The account ID to verify
165
+ * @param options - StateInit options to derive the expected account ID from
166
+ * @returns true if the account ID matches the derivation, false otherwise
167
+ *
168
+ * @example
169
+ * ```typescript
170
+ * const isValid = verifyDeterministicAccountId(
171
+ * "0s1234567890abcdef1234567890abcdef12345678",
172
+ * { code: { accountId: "publisher.near" } }
173
+ * )
174
+ * ```
175
+ */
176
+ export function verifyDeterministicAccountId(accountId, options) {
177
+ const expected = deriveAccountId(options);
178
+ return accountId === expected;
179
+ }
180
+ /**
181
+ * Check if an account ID is a deterministic account ID (NEP-616).
182
+ *
183
+ * Deterministic account IDs start with "0s" followed by 40 hex characters.
184
+ *
185
+ * @param accountId - The account ID to check
186
+ * @returns true if it's a deterministic account ID, false otherwise
187
+ *
188
+ * @example
189
+ * ```typescript
190
+ * isDeterministicAccountId("0s1234567890abcdef1234567890abcdef12345678") // true
191
+ * isDeterministicAccountId("alice.near") // false
192
+ * isDeterministicAccountId("0x1234567890abcdef1234567890abcdef12345678") // false (Ethereum)
193
+ * ```
194
+ */
195
+ export function isDeterministicAccountId(accountId) {
196
+ // Must be exactly 42 characters: "0s" + 40 hex chars
197
+ if (accountId.length !== 42) {
198
+ return false;
199
+ }
200
+ // Must start with "0s"
201
+ if (!accountId.startsWith("0s")) {
202
+ return false;
203
+ }
204
+ // Rest must be valid hex
205
+ const hexPart = accountId.slice(2);
206
+ return /^[0-9a-f]+$/.test(hexPart);
207
+ }
208
+ //# sourceMappingURL=state-init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-init.js","sourceRoot":"","sources":["../../src/utils/state-init.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "near-kit",
3
- "version": "0.4.4",
3
+ "version": "0.5.0",
4
4
  "description": "A simple, intuitive TypeScript library for interacting with NEAR Protocol",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -34,7 +34,11 @@
34
34
  "scripts": {
35
35
  "build": "tsc",
36
36
  "dev": "tsc --watch",
37
- "test": "bun test",
37
+ "test": "vitest run",
38
+ "test:ui": "vitest --ui",
39
+ "test:unit": "vitest run tests/unit",
40
+ "test:integration": "vitest run tests/integration",
41
+ "test:wallets": "vitest run tests/wallets",
38
42
  "lint": "biome check . --write",
39
43
  "typecheck": "tsc --noEmit --project tsconfig.typecheck.json",
40
44
  "prepublishOnly": "bun run build",
@@ -63,7 +67,7 @@
63
67
  }
64
68
  },
65
69
  "devDependencies": {
66
- "@biomejs/biome": "2.3.5",
70
+ "@biomejs/biome": "2.3.8",
67
71
  "@changesets/cli": "^2.29.7",
68
72
  "@hot-labs/near-connect": "^0.6.9",
69
73
  "@near-js/transactions": "^2.5.1",
@@ -71,8 +75,11 @@
71
75
  "@near-wallet-selector/core": "^10.1.1",
72
76
  "@types/bun": "^1.3.2",
73
77
  "@types/tar": "^6.1.13",
78
+ "@vitest/coverage-v8": "^4.0.10",
79
+ "@vitest/ui": "^4.0.10",
74
80
  "lefthook": "^2.0.4",
75
- "typescript": "^5.9.3"
81
+ "typescript": "^5.9.3",
82
+ "vitest": "^4.0.10"
76
83
  },
77
84
  "dependencies": {
78
85
  "@napi-rs/keyring": "^1.2.0",
@@ -81,7 +88,7 @@
81
88
  "@scure/base": "^2.0.0",
82
89
  "@scure/bip32": "^2.0.1",
83
90
  "@scure/bip39": "^2.0.1",
84
- "@zorsh/zorsh": "^0.3.3",
91
+ "@zorsh/zorsh": "^0.4.0",
85
92
  "tar": "^7.5.2",
86
93
  "zod": "^4.1.12"
87
94
  }