@arkiv-network/sdk 0.6.8-dev.5 → 0.7.0-dev.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.
Files changed (75) hide show
  1. package/dist/chains/index.d.ts +25 -25
  2. package/dist/consts-Bz0LZQZY.js +7 -0
  3. package/dist/consts-Bz0LZQZY.js.map +1 -0
  4. package/dist/{consts-AUoL-v42.cjs → consts-CxT1-RXU.cjs} +1 -1
  5. package/dist/entity-BhSXaB23.cjs +81 -0
  6. package/dist/{entity-B6g_EPm1.d.cts → entity-CN41IOrq.d.cts} +53 -34
  7. package/dist/entity-CN41IOrq.d.cts.map +1 -0
  8. package/dist/entity-DGfVyB-a.js +71 -0
  9. package/dist/entity-DGfVyB-a.js.map +1 -0
  10. package/dist/{entity-qoLMAuuk.d.ts → entity-NnEVlePN.d.ts} +53 -34
  11. package/dist/entity-NnEVlePN.d.ts.map +1 -0
  12. package/dist/{index-DdspxNAA.d.ts → index-ClkpS6s_.d.ts} +2 -2
  13. package/dist/{index-DdspxNAA.d.ts.map → index-ClkpS6s_.d.ts.map} +1 -1
  14. package/dist/{index-Ds9dwqb5.d.cts → index-w4qop0Pe.d.cts} +2 -2
  15. package/dist/{index-Ds9dwqb5.d.cts.map → index-w4qop0Pe.d.cts.map} +1 -1
  16. package/dist/index.cjs +281 -264
  17. package/dist/index.d.cts +13 -4
  18. package/dist/index.d.cts.map +1 -1
  19. package/dist/index.d.ts +14 -5
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +279 -266
  22. package/dist/index.js.map +1 -1
  23. package/dist/{payload-Claf2jUO.js → payload-BpNUPOnY.js} +3 -3
  24. package/dist/payload-BpNUPOnY.js.map +1 -0
  25. package/dist/{payload-B2kFJyah.d.ts → payload-C2KHOUoj.d.ts} +1 -1
  26. package/dist/{payload-B2kFJyah.d.ts.map → payload-C2KHOUoj.d.ts.map} +1 -1
  27. package/dist/{payload-DpA5PA-w.cjs → payload-D8YLKcJj.cjs} +1 -1
  28. package/dist/query/index.cjs +3 -2
  29. package/dist/query/index.d.cts +2 -2
  30. package/dist/query/index.d.ts +2 -2
  31. package/dist/query/index.js +3 -2
  32. package/dist/query/index.js.map +1 -1
  33. package/dist/{queryBuilder-D2uwP9jI.js → queryBuilder-BE_XhLLa.js} +98 -67
  34. package/dist/queryBuilder-BE_XhLLa.js.map +1 -0
  35. package/dist/{queryBuilder-UW2MCaX4.cjs → queryBuilder-J37buGCi.cjs} +137 -64
  36. package/dist/types/index.cjs +4 -1
  37. package/dist/types/index.d.cts +2 -2
  38. package/dist/types/index.d.ts +2 -2
  39. package/dist/types/index.js +4 -1
  40. package/dist/utils/index.cjs +2 -2
  41. package/dist/utils/index.d.ts +1 -1
  42. package/dist/utils/index.js +2 -2
  43. package/package.json +1 -1
  44. package/src/actions/public/subscribeEntityEvents.ts +25 -51
  45. package/src/actions/wallet/changeOwnership.ts +6 -6
  46. package/src/actions/wallet/createEntity.ts +7 -4
  47. package/src/actions/wallet/deleteEntity.ts +2 -3
  48. package/src/actions/wallet/extendEntity.ts +3 -4
  49. package/src/actions/wallet/mutateEntities.ts +11 -54
  50. package/src/actions/wallet/updateEntity.ts +3 -4
  51. package/src/clients/decorators/arkivWallet.ts +5 -1
  52. package/src/consts.ts +2 -1
  53. package/src/errors.ts +29 -1
  54. package/src/query/engine.test.ts +45 -0
  55. package/src/query/engine.ts +6 -1
  56. package/src/query/selectQueryBuilder.test.ts +5 -2
  57. package/src/types/attributes.ts +17 -5
  58. package/src/types/entity.ts +12 -2
  59. package/src/types/events.ts +14 -16
  60. package/src/types/index.ts +1 -0
  61. package/src/types/rpcSchema.ts +7 -2
  62. package/src/utils/arkivTransactions.test.ts +234 -36
  63. package/src/utils/arkivTransactions.ts +255 -101
  64. package/src/utils/entities.ts +17 -7
  65. package/src/utils/payload.ts +4 -2
  66. package/src/utils/validation.test.ts +52 -6
  67. package/src/utils/validation.ts +54 -6
  68. package/dist/consts-BV4PR-de.js +0 -7
  69. package/dist/consts-BV4PR-de.js.map +0 -1
  70. package/dist/entity-B6g_EPm1.d.cts.map +0 -1
  71. package/dist/entity-qoLMAuuk.d.ts.map +0 -1
  72. package/dist/payload-Claf2jUO.js.map +0 -1
  73. package/dist/queryBuilder-D2uwP9jI.js.map +0 -1
  74. /package/dist/{types-H-TSmRpm.js → types-Ci4BRClw.js} +0 -0
  75. /package/dist/{types-o3zWarRp.cjs → types-a8gm_IaQ.cjs} +0 -0
@@ -1,4 +1,17 @@
1
- import { type Hex, hexToString, TransactionExecutionError, toBytes, toHex, toRlp } from "viem"
1
+ import {
2
+ type Address,
3
+ ContractFunctionExecutionError,
4
+ ContractFunctionRevertedError,
5
+ encodePacked,
6
+ type Hex,
7
+ keccak256,
8
+ parseAbi,
9
+ stringToBytes,
10
+ TransactionExecutionError,
11
+ type TransactionReceipt,
12
+ toBytes,
13
+ toHex,
14
+ } from "viem"
2
15
  import type { ChangeOwnershipParameters } from "../actions/wallet/changeOwnership"
3
16
  import type { CreateEntityParameters } from "../actions/wallet/createEntity"
4
17
  import type { DeleteEntityParameters } from "../actions/wallet/deleteEntity"
@@ -7,149 +20,290 @@ import type { UpdateEntityParameters } from "../actions/wallet/updateEntity"
7
20
  import type { ArkivClient } from "../clients/baseClient"
8
21
  import type { WalletArkivClient } from "../clients/createWalletClient"
9
22
  import { ARKIV_ADDRESS, BLOCK_TIME } from "../consts"
10
- import { EntityMutationError } from "../errors"
23
+ import { DuplicateAttributeError, EntityMutationError, InvalidContentTypeError } from "../errors"
11
24
  import type { TxParams } from "../types"
12
- import { compress } from "./compression"
25
+ import { EntityOperationType } from "../types/entity"
26
+ import { RpcAttributeValueType as AttributeValueType } from "../types/rpcSchema"
27
+
13
28
  import { getLogger } from "./logger"
14
- import { validateAttribute, validateExpiresIn } from "./validation"
29
+ import { isEntityKey, validateAttribute, validateExpiresIn } from "./validation"
15
30
 
16
31
  const logger = getLogger("utils:arkiv-transactions")
17
32
 
18
- export function opsToTxData({
19
- creates,
20
- updates,
21
- deletes,
22
- extensions,
23
- ownershipChanges,
24
- }: {
25
- creates?: CreateEntityParameters[]
26
- updates?: UpdateEntityParameters[]
27
- deletes?: DeleteEntityParameters[]
28
- extensions?: ExtendEntityParameters[]
29
- ownershipChanges?: ChangeOwnershipParameters[]
30
- }) {
31
- function formatAttributes<T extends string | number | bigint | boolean>(attribute: {
32
- key: string
33
- value: T
34
- }): [Hex, Hex] {
35
- return [
36
- toHex(attribute.key),
37
- toHex(typeof attribute.value === "number" && attribute.value === 0 ? "" : attribute.value),
38
- ]
33
+ // RFC 2045 token chars, lowercase only. Rejects uppercase to prevent text/plain vs Text/Plain ambiguity.
34
+ const MIME_REGEX = /^[a-z][a-z0-9!#$&\-^_]*\/[a-z0-9][a-z0-9!#$&\-^_.+]*$/
35
+
36
+ function validateContentType(contentType: string): void {
37
+ if (!MIME_REGEX.test(contentType)) {
38
+ throw new InvalidContentTypeError(contentType)
39
39
  }
40
+ }
41
+
42
+ // Mime128 = struct { bytes32[4] data } (128-byte fixed MIME type container)
43
+ // Attribute = struct { bytes32 name, uint8 valueType, bytes32[4] value }
44
+ // BlockNumber = type BlockNumber is uint32
45
+ export const ENTITY_EXECUTE_ABI = parseAbi([
46
+ "function execute((uint8 operationType, bytes32 entityKey, bytes payload, (bytes32[4] data) contentType, (bytes32 name, uint8 valueType, bytes32[4] value)[] attributes, uint32 expiresIn, address newOwner)[] ops) external",
47
+ ])
48
+
49
+ export const ENTITY_OPERATION_EVENT_ABI = parseAbi([
50
+ "event EntityOperation(bytes32 indexed entityKey, uint8 indexed operationType, address indexed owner, uint32 expiresAt, bytes32 entityHash)",
51
+ ])
40
52
 
41
- const payload = [
42
- //creates
43
- (creates ?? []).map((item) => {
44
- validateExpiresIn(item.expiresIn)
45
- item.attributes.forEach(validateAttribute)
46
- return [
47
- toHex(item.expiresIn / BLOCK_TIME),
48
- toHex(item.contentType),
49
- toHex(item.payload),
50
- item.attributes
51
- .filter((attribute) => typeof attribute.value === "string")
52
- .map(formatAttributes),
53
- item.attributes
54
- .filter((attribute) => typeof attribute.value === "number")
55
- .map(formatAttributes),
56
- ]
57
- }),
58
- //updates
59
- (updates ?? []).map((item) => {
60
- validateExpiresIn(item.expiresIn)
61
- item.attributes.forEach(validateAttribute)
62
- return [
63
- item.entityKey,
64
- toHex(item.contentType),
65
- toHex(item.expiresIn / BLOCK_TIME),
66
- toHex(item.payload),
67
- item.attributes
68
- .filter((attribute) => typeof attribute.value === "string")
69
- .map(formatAttributes),
70
- item.attributes
71
- .filter((attribute) => typeof attribute.value === "number")
72
- .map(formatAttributes),
73
- ]
74
- }),
75
- //deletes
76
- (deletes ?? []).map((item) => item.entityKey),
77
- //extends
78
- (extensions ?? []).map((item) => {
79
- validateExpiresIn(item.expiresIn)
80
- return [item.entityKey, toHex(item.expiresIn / BLOCK_TIME)]
81
- }),
82
- //ownershipChanges TODO
83
- (ownershipChanges ?? []).map((item) => [item.entityKey, item.newOwner]),
53
+ export const ENTITY_ERRORS_ABI = parseAbi([
54
+ "error EmptyBatch()",
55
+ "error AttributesNotSorted()",
56
+ "error InvalidValueType(bytes32 name, uint8 valueType)",
57
+ "error InvalidOpType(uint8 operationType)",
58
+ "error ExpiryInPast(uint32 expiresAt, uint32 currentBlock)",
59
+ "error TooManyAttributes(uint256 count, uint256 maxCount)",
60
+ "error EntityNotFound(bytes32 entityKey)",
61
+ "error NotOwner(bytes32 entityKey, address caller, address owner)",
62
+ "error EntityExpired(bytes32 entityKey, uint32 expiresAt)",
63
+ "error ExpiryNotExtended(bytes32 entityKey, uint32 newExpiresAt, uint32 currentExpiresAt)",
64
+ "error TransferToZeroAddress(bytes32 entityKey)",
65
+ "error TransferToSelf(bytes32 entityKey)",
66
+ "error EntityNotExpired(bytes32 entityKey, uint32 expiresAt)",
67
+ "error Ident32Empty()",
68
+ "error Ident32TooLong(uint256 length)",
69
+ "error Ident32InvalidByte(uint256 position, bytes1 value)",
70
+ "error MimeEmpty()",
71
+ "error MimeTooLong(uint256 length, uint256 maxLength)",
72
+ "error MimeInvalidByte(uint256 position, bytes1 value)",
73
+ "error MimeIncomplete()",
74
+ ])
75
+
76
+ const EXECUTE_ABI = [...ENTITY_EXECUTE_ABI, ...ENTITY_ERRORS_ABI]
77
+
78
+ const ENTITY_NONCE_ABI = parseAbi(["function nonces(address owner) view returns (uint32)"])
79
+
80
+ const ZERO_ADDRESS: Address = "0x0000000000000000000000000000000000000000"
81
+ const ZERO_32 = `0x${"00".repeat(32)}` as Hex
82
+ const EMPTY_BYTES128 = [ZERO_32, ZERO_32, ZERO_32, ZERO_32] as const
83
+
84
+ // Encode a string or bytes into a bytes32[4] (128-byte) container, left-aligned.
85
+ function encodeBytes128(data: Uint8Array): readonly [Hex, Hex, Hex, Hex] {
86
+ const padded = new Uint8Array(128)
87
+ padded.set(data.slice(0, 128))
88
+ return [
89
+ toHex(padded.slice(0, 32)),
90
+ toHex(padded.slice(32, 64)),
91
+ toHex(padded.slice(64, 96)),
92
+ toHex(padded.slice(96, 128)),
84
93
  ]
94
+ }
85
95
 
86
- logger("txData to send as RLP %o length %d", payload, payload.length)
96
+ // Mime128 struct: bytes32[4] data, string packed left-aligned into 128 bytes.
97
+ function encodeMime128(contentType: string): { data: readonly [Hex, Hex, Hex, Hex] } {
98
+ return { data: contentType ? encodeBytes128(stringToBytes(contentType)) : EMPTY_BYTES128 }
99
+ }
100
+
101
+ function encodeAttribute(attr: { key: string; value: Hex | string | number }) {
102
+ validateAttribute(attr)
103
+ const name = toHex(attr.key, { size: 32 })
104
+
105
+ if (typeof attr.value === "string") {
106
+ // Only exact 32-byte hex (an entity key) gets the EntityKey type; shorter or
107
+ // longer hex-looking strings are stored as plain strings so they round-trip
108
+ // unchanged and stay matchable by quoted string queries.
109
+ if (isEntityKey(attr.value)) {
110
+ return {
111
+ name,
112
+ valueType: AttributeValueType.EntityKey,
113
+ value: [toHex(toBytes(attr.value), { size: 32 }), ZERO_32, ZERO_32, ZERO_32] as const,
114
+ }
115
+ }
116
+ return {
117
+ name,
118
+ valueType: AttributeValueType.String,
119
+ // stringToBytes, not toBytes: toBytes hex-decodes hex-looking strings, which
120
+ // would corrupt string values like "0xab" instead of UTF-8 encoding them
121
+ value: encodeBytes128(stringToBytes(attr.value)),
122
+ }
123
+ }
87
124
 
88
- return toRlp(payload)
125
+ return {
126
+ name,
127
+ valueType: AttributeValueType.Uint,
128
+ value: [toHex(BigInt(attr.value), { size: 32 }), ZERO_32, ZERO_32, ZERO_32] as const,
129
+ }
89
130
  }
90
131
 
91
- export async function sendArkivTransaction(client: ArkivClient, data: Hex, txParams?: TxParams) {
132
+ // The registry requires attributes strictly ascending by their bytes32 name — the
133
+ // canonical serialization hashes into the state root, and strict ordering doubles as
134
+ // the uniqueness check (AttributesNotSorted). Sort here so callers can pass any order;
135
+ // duplicates would revert on-chain, so reject them client-side with a clear error.
136
+ function encodeAttributes(attrs: { key: string; value: Hex | string | number }[]) {
137
+ const encoded = attrs
138
+ .map((attr) => ({ key: attr.key, ...encodeAttribute(attr) }))
139
+ .sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0))
140
+ for (let i = 1; i < encoded.length; i++) {
141
+ if (encoded[i].name === encoded[i - 1].name) {
142
+ throw new DuplicateAttributeError(encoded[i].key)
143
+ }
144
+ }
145
+ return encoded.map(({ key: _, ...attr }) => attr)
146
+ }
147
+
148
+ function toBTL(expiresIn: number): number {
149
+ validateExpiresIn(expiresIn)
150
+ return Math.ceil(expiresIn / BLOCK_TIME)
151
+ }
152
+
153
+ // entityKey = keccak256(chainId || registryAddress || ownerAddress || nonce)
154
+ function deriveEntityKey(chainId: number, owner: Address, nonce: bigint): Hex {
155
+ return keccak256(
156
+ encodePacked(
157
+ ["uint256", "address", "address", "uint32"],
158
+ [BigInt(chainId), ARKIV_ADDRESS, owner, Number(nonce)],
159
+ ),
160
+ )
161
+ }
162
+
163
+ export type SendArkivTransactionResult = {
164
+ receipt: TransactionReceipt
165
+ createdEntityKeys: Hex[]
166
+ }
167
+
168
+ export async function sendArkivTransaction(
169
+ client: ArkivClient,
170
+ ops: {
171
+ creates?: CreateEntityParameters[]
172
+ updates?: UpdateEntityParameters[]
173
+ deletes?: DeleteEntityParameters[]
174
+ extensions?: ExtendEntityParameters[]
175
+ ownershipChanges?: ChangeOwnershipParameters[]
176
+ },
177
+ txParams?: TxParams,
178
+ ): Promise<SendArkivTransactionResult> {
92
179
  if (!client.account) throw new Error("Account required")
180
+ if (!client.chain) throw new Error("Chain required")
93
181
  const walletClient = client as WalletArkivClient
94
182
 
95
- logger("Sending transaction %o", {
96
- account: client.account,
97
- chain: client.chain,
98
- to: ARKIV_ADDRESS,
99
- value: 0n,
100
- data,
101
- ...txParams,
102
- })
183
+ const { creates, updates, deletes, extensions, ownershipChanges } = ops
184
+ const owner = client.account.address as Address
185
+
186
+ const ownerNonce: bigint = creates?.length
187
+ ? BigInt(
188
+ await walletClient.readContract({
189
+ address: ARKIV_ADDRESS,
190
+ abi: ENTITY_NONCE_ABI,
191
+ functionName: "nonces",
192
+ args: [owner],
193
+ }),
194
+ )
195
+ : 0n
196
+
197
+ const createdEntityKeys: Hex[] = (creates ?? []).map((_, i) =>
198
+ deriveEntityKey(client.chain!.id, owner, ownerNonce + BigInt(i)),
199
+ )
200
+
201
+ for (const item of creates ?? []) validateContentType(item.contentType)
202
+ for (const item of updates ?? []) validateContentType(item.contentType)
203
+
204
+ const operations = [
205
+ ...(creates ?? []).map((item, i) => ({
206
+ operationType: EntityOperationType.Create,
207
+ entityKey: createdEntityKeys[i],
208
+ payload: toHex(item.payload),
209
+ contentType: encodeMime128(item.contentType),
210
+ attributes: encodeAttributes(item.attributes),
211
+ expiresIn: toBTL(item.expiresIn),
212
+ newOwner: ZERO_ADDRESS,
213
+ })),
214
+ ...(updates ?? []).map((item) => ({
215
+ operationType: EntityOperationType.Update,
216
+ entityKey: item.entityKey,
217
+ payload: toHex(item.payload),
218
+ contentType: encodeMime128(item.contentType),
219
+ attributes: encodeAttributes(item.attributes),
220
+ expiresIn: 0, // contract ignores expiresAt on UPDATE
221
+ newOwner: ZERO_ADDRESS,
222
+ })),
223
+ ...(deletes ?? []).map((item) => ({
224
+ operationType: EntityOperationType.Delete,
225
+ entityKey: item.entityKey,
226
+ payload: "0x" as Hex,
227
+ contentType: encodeMime128(""),
228
+ attributes: [] as never[],
229
+ expiresIn: 0,
230
+ newOwner: ZERO_ADDRESS,
231
+ })),
232
+ ...(extensions ?? []).map((item) => ({
233
+ operationType: EntityOperationType.Extend,
234
+ entityKey: item.entityKey,
235
+ payload: "0x" as Hex,
236
+ contentType: encodeMime128(""),
237
+ attributes: [] as never[],
238
+ expiresIn: toBTL(item.expiresIn),
239
+ newOwner: ZERO_ADDRESS,
240
+ })),
241
+ ...(ownershipChanges ?? []).map((item) => ({
242
+ operationType: EntityOperationType.Transfer,
243
+ entityKey: item.entityKey,
244
+ payload: "0x" as Hex,
245
+ contentType: encodeMime128(""),
246
+ attributes: [] as never[],
247
+ expiresIn: 0,
248
+ newOwner: item.newOwner as Address,
249
+ })),
250
+ ]
251
+
252
+ logger("Sending execute with %d operations %s", operations.length, JSON.stringify(operations))
103
253
 
104
254
  try {
105
- const compressed = await compress(toBytes(data))
106
- const txHash = await walletClient.sendTransaction({
255
+ const txHash = await walletClient.writeContract({
256
+ address: ARKIV_ADDRESS,
257
+ abi: EXECUTE_ABI,
258
+ functionName: "execute",
259
+ args: [operations],
107
260
  account: client.account,
108
261
  chain: client.chain,
109
- to: ARKIV_ADDRESS,
110
- value: 0n,
111
- data: toHex(compressed),
112
262
  ...txParams,
113
263
  })
114
264
 
115
265
  const receipt = await walletClient.waitForTransactionReceipt({ hash: txHash })
116
266
  logger("Tx receipt %o", receipt)
267
+
117
268
  if (receipt.status === "reverted") {
118
269
  try {
119
- const callResult = await walletClient.call({
270
+ await walletClient.simulateContract({
271
+ address: ARKIV_ADDRESS,
272
+ abi: EXECUTE_ABI,
273
+ functionName: "execute",
274
+ args: [operations],
120
275
  account: client.account,
121
- to: ARKIV_ADDRESS,
122
- value: 0n,
123
- data: toHex(compressed),
124
- ...txParams,
276
+ chain: client.chain,
125
277
  })
126
- throw new EntityMutationError(
127
- `Transaction ${receipt.transactionHash} reverted. Please make sure the data for entities doesn't contain invalid characters or invalid data types.
128
- Reason: ${callResult.data ? hexToString(callResult.data) : "No reason provided by backend."}`,
129
- )
130
278
  } catch (err) {
131
- const error = err as { cause?: { details?: string } }
132
- const reason = error.cause?.details ?? "No reason provided by backend."
133
- logger(
134
- "Error while calling to get more details about reverted transaction. Reason: %s",
135
- reason,
136
- )
279
+ const error = err as { shortMessage?: string; cause?: { details?: string } }
280
+ const reason =
281
+ error.shortMessage ?? error.cause?.details ?? "No reason provided by backend."
137
282
  throw new EntityMutationError(
138
283
  `Transaction ${receipt.transactionHash} reverted. Reason: ${reason}`,
139
284
  )
140
285
  }
286
+ throw new EntityMutationError(
287
+ `Transaction ${receipt.transactionHash} reverted. No reason provided by backend.`,
288
+ )
141
289
  }
142
290
 
143
- return receipt
291
+ return { receipt, createdEntityKeys }
144
292
  } catch (error) {
145
293
  let message = "Transaction failed"
146
294
  if (error instanceof TransactionExecutionError) {
147
295
  message += `: ${error.details}`
296
+ } else if (error instanceof ContractFunctionExecutionError) {
297
+ logger("Contract function execution error data:", error.shortMessage)
298
+ if (error.cause instanceof ContractFunctionRevertedError) {
299
+ message += `: ${error.cause.message}`
300
+ } else {
301
+ message += ": Execution error without revert data"
302
+ }
148
303
  } else if (error instanceof EntityMutationError) {
149
304
  throw error
150
305
  }
151
306
 
152
- logger("%s Detailed error stack: %o", message, error)
153
307
  throw new EntityMutationError(message)
154
308
  }
155
309
  }
@@ -1,8 +1,22 @@
1
- import { toBytes } from "viem"
1
+ import { type Hex, toBytes } from "viem"
2
+ import type { Attribute } from "../types/attributes"
2
3
  import { Entity } from "../types/entity"
3
4
  import type { RpcEntity } from "../types/rpcSchema"
5
+ import { RpcAttributeValueType } from "../types/rpcSchema"
4
6
  import { getLogger } from "./logger"
5
7
 
8
+ function decodeAttributeValue(value: string, valueType: RpcAttributeValueType): Attribute["value"] {
9
+ switch (valueType) {
10
+ case RpcAttributeValueType.Uint:
11
+ return Number(BigInt(value as Hex))
12
+ case RpcAttributeValueType.EntityKey:
13
+ return value as Hex
14
+ case RpcAttributeValueType.String:
15
+ default:
16
+ return value
17
+ }
18
+ }
19
+
6
20
  const logger = getLogger("utils:entities")
7
21
 
8
22
  export async function entityFromRpcResult(rpcEntity: RpcEntity) {
@@ -24,13 +38,9 @@ export async function entityFromRpcResult(rpcEntity: RpcEntity) {
24
38
  : undefined,
25
39
  rpcEntity.value !== undefined ? toBytes(rpcEntity.value) : undefined,
26
40
  [
27
- ...(rpcEntity.stringAttributes ?? []).map(({ key, value }) => ({
28
- key,
29
- value,
30
- })),
31
- ...(rpcEntity.numericAttributes ?? []).map(({ key, value }) => ({
41
+ ...(rpcEntity.attributes ?? []).map(({ key, value, valueType }) => ({
32
42
  key,
33
- value: Number(value),
43
+ value: decodeAttributeValue(value, valueType),
34
44
  })),
35
45
  ],
36
46
  )
@@ -1,9 +1,11 @@
1
- import { toBytes } from "viem"
1
+ import { stringToBytes, toBytes } from "viem"
2
2
 
3
3
  export function jsonToPayload(json: object): Uint8Array {
4
4
  return toBytes(JSON.stringify(json))
5
5
  }
6
6
 
7
7
  export function stringToPayload(data: string): Uint8Array {
8
- return toBytes(data)
8
+ // stringToBytes, not toBytes: toBytes hex-decodes hex-looking strings, which
9
+ // would corrupt payloads like "0xab" instead of UTF-8 encoding them
10
+ return stringToBytes(data)
9
11
  }
@@ -1,7 +1,7 @@
1
1
  import { describe, expect, it } from "bun:test"
2
2
  import { BLOCK_TIME } from "../consts"
3
- import { InvalidAttributeError, InvalidExpirationError } from "../errors"
4
- import { validateAttribute, validateExpiresIn } from "./validation"
3
+ import { InvalidAttributeError, InvalidAttributeKeyError, InvalidExpirationError } from "../errors"
4
+ import { validateAttribute, validateAttributeKey, validateExpiresIn } from "./validation"
5
5
 
6
6
  describe("validateExpiresIn", () => {
7
7
  it("accepts positive integers that are a multiple of the block time", () => {
@@ -42,10 +42,9 @@ describe("validateAttribute", () => {
42
42
  expect(() => validateAttribute({ key: "k", value: "anything" })).not.toThrow()
43
43
  })
44
44
 
45
- it("accepts integer numeric values", () => {
45
+ it("accepts non-negative integer numeric values", () => {
46
46
  expect(() => validateAttribute({ key: "k", value: 0 })).not.toThrow()
47
47
  expect(() => validateAttribute({ key: "k", value: 123 })).not.toThrow()
48
- expect(() => validateAttribute({ key: "k", value: -5 })).not.toThrow()
49
48
  })
50
49
 
51
50
  it("throws for non-integer numeric values", () => {
@@ -53,8 +52,55 @@ describe("validateAttribute", () => {
53
52
  expect(() => validateAttribute({ key: "k", value: Number.NaN })).toThrow(InvalidAttributeError)
54
53
  })
55
54
 
55
+ it("throws for negative numeric values", () => {
56
+ expect(() => validateAttribute({ key: "k", value: -5 })).toThrow(InvalidAttributeError)
57
+ expect(() => validateAttribute({ key: "k", value: -0.5 })).toThrow(InvalidAttributeError)
58
+ })
59
+
56
60
  it("includes the attribute key and value in the error message", () => {
57
- expect(() => validateAttribute({ key: "myKey", value: 1.5 })).toThrow(/myKey/)
58
- expect(() => validateAttribute({ key: "myKey", value: 1.5 })).toThrow(/1\.5/)
61
+ expect(() => validateAttribute({ key: "my_key", value: 1.5 })).toThrow(/my_key/)
62
+ expect(() => validateAttribute({ key: "my_key", value: 1.5 })).toThrow(/1\.5/)
63
+ })
64
+ })
65
+
66
+ describe("validateAttributeKey", () => {
67
+ it("accepts valid Ident32 keys", () => {
68
+ expect(() => validateAttributeKey("a")).not.toThrow()
69
+ expect(() => validateAttributeKey("tag")).not.toThrow()
70
+ expect(() => validateAttributeKey("num_attr")).not.toThrow()
71
+ expect(() => validateAttributeKey("a.b-c_d9")).not.toThrow()
72
+ expect(() => validateAttributeKey("a".repeat(32))).not.toThrow()
73
+ })
74
+
75
+ it("throws for an empty key", () => {
76
+ expect(() => validateAttributeKey("")).toThrow(InvalidAttributeKeyError)
77
+ })
78
+
79
+ it("throws when the key does not start with a lowercase letter", () => {
80
+ expect(() => validateAttributeKey("Tag")).toThrow(InvalidAttributeKeyError)
81
+ expect(() => validateAttributeKey("1tag")).toThrow(InvalidAttributeKeyError)
82
+ expect(() => validateAttributeKey("_tag")).toThrow(InvalidAttributeKeyError)
83
+ expect(() => validateAttributeKey(".tag")).toThrow(InvalidAttributeKeyError)
84
+ })
85
+
86
+ it("throws for invalid characters after the first", () => {
87
+ expect(() => validateAttributeKey("strAttr")).toThrow(InvalidAttributeKeyError)
88
+ expect(() => validateAttributeKey("has space")).toThrow(InvalidAttributeKeyError)
89
+ expect(() => validateAttributeKey("has:colon")).toThrow(InvalidAttributeKeyError)
90
+ expect(() => validateAttributeKey("héllo")).toThrow(InvalidAttributeKeyError)
91
+ })
92
+
93
+ it("throws for keys longer than 32 bytes", () => {
94
+ expect(() => validateAttributeKey("a".repeat(33))).toThrow(InvalidAttributeKeyError)
95
+ })
96
+
97
+ it("names the offending character and position", () => {
98
+ expect(() => validateAttributeKey("strAttr")).toThrow(/"A" at position 3/)
99
+ })
100
+
101
+ it("is applied by validateAttribute", () => {
102
+ expect(() => validateAttribute({ key: "strAttr", value: "x" })).toThrow(
103
+ InvalidAttributeKeyError,
104
+ )
59
105
  })
60
106
  })
@@ -1,7 +1,20 @@
1
+ import type { Hex } from "viem"
2
+ import { isHex } from "viem"
1
3
  import { BLOCK_TIME } from "../consts"
2
- import { InvalidAttributeError, InvalidExpirationError } from "../errors"
4
+ import { InvalidAttributeError, InvalidAttributeKeyError, InvalidExpirationError } from "../errors"
3
5
  import type { Attribute } from "../types"
4
6
 
7
+ /**
8
+ * True only for hex strings that are exactly 32 bytes (an entity key). This is
9
+ * the single rule deciding whether an attribute string value is stored as the
10
+ * on-chain `EntityKey` type or as a plain string — the query engine applies
11
+ * the same rule, so equality comparisons agree with storage on both paths.
12
+ * Hex-looking strings of any other length are treated as ordinary strings.
13
+ */
14
+ export function isEntityKey(value: string): value is Hex {
15
+ return isHex(value) && value.length === 66
16
+ }
17
+
5
18
  /**
6
19
  * Expiration on Arkiv is measured in blocks (1 block =
7
20
  * 2 seconds), so the value must be a positive integer that is
@@ -18,15 +31,50 @@ export function validateExpiresIn(expiresIn: number): void {
18
31
  }
19
32
 
20
33
  /**
21
- * Validates a single attribute. Arkiv supports string and number attribute
22
- * values, but numeric values must be integers.
34
+ * Validates an attribute key against the on-chain `Ident32` rules (mirrors
35
+ * `Ident32.sol` / `validate_ident32_bytes` in the node): non-empty, at most
36
+ * 32 bytes, leading character `a-z`, remaining characters `a-z 0-9 . - _`.
37
+ *
38
+ * @param key - The attribute key to validate.
39
+ * @throws {InvalidAttributeKeyError} If the key violates the `Ident32` rules.
40
+ */
41
+ export function validateAttributeKey(key: string): void {
42
+ if (key.length === 0) {
43
+ throw new InvalidAttributeKeyError(key, "key is empty")
44
+ }
45
+ if (!/^[a-z]/.test(key)) {
46
+ throw new InvalidAttributeKeyError(key, `key starts with "${key[0]}"`)
47
+ }
48
+ const invalid = key.match(/[^a-z0-9._-]/)
49
+ if (invalid) {
50
+ throw new InvalidAttributeKeyError(
51
+ key,
52
+ `key contains "${invalid[0]}" at position ${invalid.index}`,
53
+ )
54
+ }
55
+ // The charset is ASCII-only, so string length equals byte length here.
56
+ if (key.length > 32) {
57
+ throw new InvalidAttributeKeyError(key, `key is ${key.length} bytes long`)
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Validates a single attribute. Keys must follow the on-chain `Ident32` rules
63
+ * (see {@link validateAttributeKey}). Arkiv supports string and number
64
+ * attribute values, but numeric values must be non-negative integers — they
65
+ * are stored on-chain as unsigned 256-bit values.
23
66
  *
24
67
  * @param attribute - The attribute to validate.
25
- * @throws {InvalidAttributeError} If the attribute has a non-integer numeric
26
- * value.
68
+ * @throws {InvalidAttributeKeyError} If the key violates the `Ident32` rules.
69
+ * @throws {InvalidAttributeError} If the attribute has a non-integer or
70
+ * negative numeric value.
27
71
  */
28
72
  export function validateAttribute(attribute: Attribute): void {
29
- if (typeof attribute.value === "number" && !Number.isInteger(attribute.value)) {
73
+ validateAttributeKey(attribute.key)
74
+ if (
75
+ typeof attribute.value === "number" &&
76
+ (!Number.isInteger(attribute.value) || attribute.value < 0)
77
+ ) {
30
78
  throw new InvalidAttributeError(attribute.key, attribute.value)
31
79
  }
32
80
  }
@@ -1,7 +0,0 @@
1
- //#region src/consts.ts
2
- const ARKIV_ADDRESS = "0x00000000000000000000000000000061726b6976";
3
- const BLOCK_TIME = 2;
4
-
5
- //#endregion
6
- export { BLOCK_TIME as n, ARKIV_ADDRESS as t };
7
- //# sourceMappingURL=consts-BV4PR-de.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"consts-BV4PR-de.js","names":[],"sources":["../src/consts.ts"],"sourcesContent":["export const ARKIV_ADDRESS = \"0x00000000000000000000000000000061726b6976\" as `0x${string}`\nexport const BLOCK_TIME = 2 // 2 seconds\n"],"mappings":";AAAA,MAAa,gBAAgB;AAC7B,MAAa,aAAa"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"entity-B6g_EPm1.d.cts","names":[],"sources":["../src/types/mimeTypes.ts","../src/types/rpcSchema.ts","../src/clients/baseClient.ts","../src/actions/public/getBlockTiming.ts","../src/actions/public/query.ts","../src/actions/wallet/changeOwnership.ts","../src/actions/wallet/createEntity.ts","../src/actions/wallet/deleteEntity.ts","../src/actions/wallet/extendEntity.ts","../src/actions/wallet/updateEntity.ts","../src/actions/wallet/mutateEntities.ts","../src/types/attributes.ts","../src/types/events.ts","../src/types/txParams.ts","../src/types/entity.ts"],"sourcesContent":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"entity-qoLMAuuk.d.ts","names":[],"sources":["../src/types/mimeTypes.ts","../src/types/rpcSchema.ts","../src/clients/baseClient.ts","../src/actions/public/getBlockTiming.ts","../src/actions/public/query.ts","../src/actions/wallet/changeOwnership.ts","../src/actions/wallet/createEntity.ts","../src/actions/wallet/deleteEntity.ts","../src/actions/wallet/extendEntity.ts","../src/actions/wallet/updateEntity.ts","../src/actions/wallet/mutateEntities.ts","../src/types/attributes.ts","../src/types/events.ts","../src/types/txParams.ts","../src/types/entity.ts"],"sourcesContent":[],"mappings":"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"}