@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,40 +1,38 @@
1
- import type { Hex } from "viem"
1
+ import type { Address, Hex } from "viem"
2
2
 
3
3
  export type OnEntityCreatedEvent = {
4
4
  entityKey: Hex
5
- owner: Hex
6
- expirationBlock: number
7
- cost: bigint
5
+ owner: Address
6
+ expiresAt: number
7
+ entityHash: Hex
8
8
  }
9
9
 
10
10
  export type OnEntityUpdatedEvent = {
11
11
  entityKey: Hex
12
- owner: Hex
13
- oldExpirationBlock: number
14
- newExpirationBlock: number
15
- cost: bigint
12
+ owner: Address
13
+ expiresAt: number
14
+ entityHash: Hex
16
15
  }
17
16
 
18
17
  export type OnEntityDeletedEvent = {
19
18
  entityKey: Hex
20
- owner: Hex
19
+ owner: Address
21
20
  }
22
21
 
23
22
  export type OnEntityExpiredEvent = {
24
23
  entityKey: Hex
25
- owner: Hex
24
+ owner: Address
26
25
  }
27
26
 
28
27
  export type OnEntityExpiresInExtendedEvent = {
29
28
  entityKey: Hex
30
- owner: Hex
31
- oldExpirationBlock: number
32
- newExpirationBlock: number
33
- cost: bigint
29
+ owner: Address
30
+ expiresAt: number
31
+ entityHash: Hex
34
32
  }
35
33
 
36
34
  export type OnEntityOwnerChangedEvent = {
37
35
  entityKey: Hex
38
- oldOwner: Hex
39
- newOwner: Hex
36
+ owner: Address
37
+ entityHash: Hex
40
38
  }
@@ -19,6 +19,7 @@ export type {
19
19
  export type { UpdateEntityParameters, UpdateEntityReturnType } from "../actions/wallet/updateEntity"
20
20
  export type { Attribute } from "./attributes"
21
21
  export type { Entity } from "./entity"
22
+ export { EntityOperationType } from "./entity"
22
23
  export type {
23
24
  OnEntityCreatedEvent,
24
25
  OnEntityDeletedEvent,
@@ -12,8 +12,13 @@ export type RpcEntity = {
12
12
  operationIndexInTransaction: Hex
13
13
  owner: Hex
14
14
  creator: Hex
15
- stringAttributes?: [{ key: string; value: string }]
16
- numericAttributes?: [{ key: string; value: Hex }]
15
+ attributes?: { key: string; value: string; valueType: RpcAttributeValueType }[]
16
+ }
17
+
18
+ export enum RpcAttributeValueType {
19
+ Uint = 1,
20
+ String = 2,
21
+ EntityKey = 3,
17
22
  }
18
23
 
19
24
  export type RpcOrderByAttribute = {
@@ -1,6 +1,57 @@
1
- import { describe, expect, it } from "bun:test"
2
- import { InvalidAttributeError, InvalidExpirationError } from "../errors"
3
- import { opsToTxData } from "./arkivTransactions"
1
+ import { describe, expect, it, vi } from "bun:test"
2
+ import { stringToBytes, toBytes, toHex } from "viem"
3
+ import type { ArkivClient } from "../clients/baseClient"
4
+ import { DuplicateAttributeError, InvalidAttributeError, InvalidExpirationError } from "../errors"
5
+ import { sendArkivTransaction } from "./arkivTransactions"
6
+
7
+ const ZERO_32 = `0x${"00".repeat(32)}`
8
+
9
+ function encodeStringTo128(str: string): string[] {
10
+ const padded = new Uint8Array(128)
11
+ // stringToBytes, not toBytes — mirrors encodeAttribute: string values are always
12
+ // UTF-8 encoded, never hex-decoded, even when they look like hex
13
+ padded.set(stringToBytes(str).slice(0, 128))
14
+ return [
15
+ toHex(padded.slice(0, 32)),
16
+ toHex(padded.slice(32, 64)),
17
+ toHex(padded.slice(64, 96)),
18
+ toHex(padded.slice(96, 128)),
19
+ ]
20
+ }
21
+
22
+ function makeClient() {
23
+ const writeContract = vi.fn().mockResolvedValue("0xdeadbeef")
24
+ const waitForTransactionReceipt = vi.fn().mockResolvedValue({
25
+ status: "success",
26
+ transactionHash: "0xdeadbeef",
27
+ })
28
+ return {
29
+ client: {
30
+ account: { address: "0x1111111111111111111111111111111111111111" },
31
+ chain: { id: 1 },
32
+ getBlockNumber: vi.fn().mockResolvedValue(100n),
33
+ readContract: vi.fn().mockResolvedValue(0),
34
+ writeContract,
35
+ waitForTransactionReceipt,
36
+ } as unknown as ArkivClient,
37
+ writeContract,
38
+ }
39
+ }
40
+
41
+ const BASE_CREATE = {
42
+ payload: new Uint8Array([1, 2, 3]),
43
+ contentType: "application/octet-stream",
44
+ expiresIn: 3600,
45
+ }
46
+
47
+ async function captureAttributes(attributes: { key: string; value: string | number }[]) {
48
+ const { client, writeContract } = makeClient()
49
+ await sendArkivTransaction(client, {
50
+ creates: [{ ...BASE_CREATE, attributes }],
51
+ })
52
+ const callArgs = writeContract.mock.calls[0][0]
53
+ return callArgs.args[0][0].attributes as { name: string; valueType: number; value: string[] }[]
54
+ }
4
55
 
5
56
  const validCreate = {
6
57
  payload: new Uint8Array([1, 2, 3]),
@@ -9,54 +60,201 @@ const validCreate = {
9
60
  expiresIn: 1000,
10
61
  }
11
62
 
12
- describe("opsToTxData expiration validation", () => {
13
- it("builds tx data for a valid create", () => {
14
- expect(typeof opsToTxData({ creates: [validCreate] })).toBe("string")
63
+ describe("sendArkivTransaction expiration validation", () => {
64
+ it("sends a transaction for a valid create", async () => {
65
+ const { client, writeContract } = makeClient()
66
+ const result = await sendArkivTransaction(client, { creates: [validCreate] })
67
+ expect(writeContract).toHaveBeenCalledTimes(1)
68
+ expect(result.receipt.status).toBe("success")
15
69
  })
16
70
 
17
- it("throws when a create expiresIn is not a multiple of the block time", () => {
18
- expect(() => opsToTxData({ creates: [{ ...validCreate, expiresIn: 51 }] })).toThrow(
19
- InvalidExpirationError,
20
- )
71
+ it("rejects when a create expiresIn is not a multiple of the block time", async () => {
72
+ const { client } = makeClient()
73
+ await expect(
74
+ sendArkivTransaction(client, { creates: [{ ...validCreate, expiresIn: 51 }] }),
75
+ ).rejects.toThrow(InvalidExpirationError)
21
76
  })
22
77
 
23
- it("throws when an update expiresIn is not an integer", () => {
24
- expect(() =>
25
- opsToTxData({
26
- updates: [{ ...validCreate, entityKey: "0x123", expiresIn: 51.5 }],
27
- }),
28
- ).toThrow(InvalidExpirationError)
78
+ it("rejects when a create expiresIn is not an integer", async () => {
79
+ const { client } = makeClient()
80
+ await expect(
81
+ sendArkivTransaction(client, { creates: [{ ...validCreate, expiresIn: 51.5 }] }),
82
+ ).rejects.toThrow(InvalidExpirationError)
29
83
  })
30
84
 
31
- it("throws when an extension expiresIn is not a multiple of the block time", () => {
32
- expect(() => opsToTxData({ extensions: [{ entityKey: "0x123", expiresIn: 999 }] })).toThrow(
33
- InvalidExpirationError,
34
- )
85
+ it("rejects when an extension expiresIn is not a multiple of the block time", async () => {
86
+ const { client } = makeClient()
87
+ await expect(
88
+ sendArkivTransaction(client, { extensions: [{ entityKey: "0x123", expiresIn: 999 }] }),
89
+ ).rejects.toThrow(InvalidExpirationError)
35
90
  })
36
91
  })
37
92
 
38
- describe("opsToTxData attribute validation", () => {
39
- it("throws when a numeric attribute value is not an integer", () => {
40
- expect(() =>
41
- opsToTxData({
93
+ describe("sendArkivTransaction attribute validation", () => {
94
+ it("rejects when a numeric attribute value is not an integer", async () => {
95
+ const { client } = makeClient()
96
+ await expect(
97
+ sendArkivTransaction(client, {
42
98
  creates: [{ ...validCreate, attributes: [{ key: "n", value: 1.5 }] }],
43
99
  }),
44
- ).toThrow(InvalidAttributeError)
100
+ ).rejects.toThrow(InvalidAttributeError)
45
101
  })
46
102
 
47
- it("accepts integer numeric attribute values", () => {
48
- expect(
49
- typeof opsToTxData({
50
- creates: [{ ...validCreate, attributes: [{ key: "n", value: 123 }] }],
51
- }),
52
- ).toBe("string")
103
+ it("accepts integer numeric attribute values", async () => {
104
+ const { client, writeContract } = makeClient()
105
+ await sendArkivTransaction(client, {
106
+ creates: [{ ...validCreate, attributes: [{ key: "n", value: 123 }] }],
107
+ })
108
+ expect(writeContract).toHaveBeenCalledTimes(1)
109
+ })
110
+
111
+ it("accepts string attribute values that look like non-integers", async () => {
112
+ const { client, writeContract } = makeClient()
113
+ await sendArkivTransaction(client, {
114
+ creates: [{ ...validCreate, attributes: [{ key: "n", value: "1.5" }] }],
115
+ })
116
+ expect(writeContract).toHaveBeenCalledTimes(1)
117
+ })
118
+ })
119
+
120
+ describe("encodeAttribute", () => {
121
+ it("encodes a plain string as STRING (valueType 2) left-aligned across 128 bytes", async () => {
122
+ const [attr] = await captureAttributes([{ key: "name", value: "hello world" }])
123
+
124
+ expect(attr.name).toBe(toHex("name", { size: 32 }))
125
+ expect(attr.valueType).toBe(2)
126
+ expect(attr.value).toEqual(encodeStringTo128("hello world"))
127
+ })
128
+
129
+ it("encodes an empty string as STRING with all-zero value", async () => {
130
+ const [attr] = await captureAttributes([{ key: "tag", value: "" }])
131
+
132
+ expect(attr.valueType).toBe(2)
133
+ expect(attr.value).toEqual([ZERO_32, ZERO_32, ZERO_32, ZERO_32])
134
+ })
135
+
136
+ it("encodes a hex string as ENTITY_KEY (valueType 3) in first slot, rest zero", async () => {
137
+ const entityKey = "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
138
+ const [attr] = await captureAttributes([{ key: "ref", value: entityKey }])
139
+
140
+ expect(attr.name).toBe(toHex("ref", { size: 32 }))
141
+ expect(attr.valueType).toBe(3)
142
+ expect(attr.value).toEqual([entityKey, ZERO_32, ZERO_32, ZERO_32])
143
+ })
144
+
145
+ it("treats hex strings that are not 32 bytes as STRING, not ENTITY_KEY", async () => {
146
+ // only exact entity keys get the hex type — everything else must round-trip
147
+ // as a string and stay matchable by quoted string queries
148
+ for (const value of ["0x", "0xab", "0x6186b0dba9652262942d5a465d49686eb560834c"]) {
149
+ const [attr] = await captureAttributes([{ key: "x", value }])
150
+ expect(attr.valueType).toBe(2)
151
+ expect(attr.value).toEqual(encodeStringTo128(value))
152
+ }
153
+
154
+ // the encoded bytes are the UTF-8 text, not a hex-decode: "0xab" is the
155
+ // four characters 0, x, a, b
156
+ const [attr] = await captureAttributes([{ key: "x", value: "0xab" }])
157
+ expect(attr.value[0].startsWith("0x30786162")).toBe(true)
158
+ })
159
+
160
+ it("encodes a number as UINT (valueType 1) big-endian in first slot, rest zero", async () => {
161
+ const [attr] = await captureAttributes([{ key: "count", value: 42 }])
162
+
163
+ expect(attr.name).toBe(toHex("count", { size: 32 }))
164
+ expect(attr.valueType).toBe(1)
165
+ expect(attr.value).toEqual([toHex(42n, { size: 32 }), ZERO_32, ZERO_32, ZERO_32])
166
+ })
167
+
168
+ it("encodes zero as UINT", async () => {
169
+ const [attr] = await captureAttributes([{ key: "n", value: 0 }])
170
+
171
+ expect(attr.valueType).toBe(1)
172
+ expect(attr.value).toEqual([ZERO_32, ZERO_32, ZERO_32, ZERO_32])
173
+ })
174
+
175
+ it("encodes a large number as UINT", async () => {
176
+ const large = Number.MAX_SAFE_INTEGER
177
+ const [attr] = await captureAttributes([{ key: "big", value: large }])
178
+
179
+ expect(attr.valueType).toBe(1)
180
+ expect(attr.value[0]).toBe(toHex(BigInt(large), { size: 32 }))
181
+ })
182
+
183
+ it("encodes multiple mixed-type attributes preserving order", async () => {
184
+ const entityKey = "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
185
+ const attrs = await captureAttributes([
186
+ { key: "count", value: 7 },
187
+ { key: "label", value: "test" },
188
+ { key: "ref", value: entityKey },
189
+ ])
190
+
191
+ expect(attrs).toHaveLength(3)
192
+ expect(attrs[0]).toMatchObject({ valueType: 1, name: toHex("count", { size: 32 }) })
193
+ expect(attrs[1]).toMatchObject({ valueType: 2, name: toHex("label", { size: 32 }) })
194
+ expect(attrs[2]).toMatchObject({ valueType: 3, name: toHex("ref", { size: 32 }) })
195
+ })
196
+
197
+ it("encodes a UTF-8 string longer than 32 bytes spanning multiple chunks", async () => {
198
+ const long = "a".repeat(40)
199
+ const [attr] = await captureAttributes([{ key: "body", value: long }])
200
+
201
+ expect(attr.valueType).toBe(2)
202
+ const [chunk0, chunk1] = attr.value
203
+ expect(chunk0).toBe(toHex(new Uint8Array(32).fill(0x61))) // 32 × 'a'
204
+ expect(chunk1.startsWith("0x6161616161616161")).toBe(true) // next 8 × 'a', then zeros
205
+ })
206
+ })
207
+
208
+ describe("attribute sorting", () => {
209
+ it("sorts attributes ascending by their bytes32 name before encoding", async () => {
210
+ const attrs = await captureAttributes([
211
+ { key: "tag", value: "zz" },
212
+ { key: "status", value: "active" },
213
+ { key: "score", value: 30 },
214
+ ])
215
+
216
+ expect(attrs.map((a) => a.name)).toEqual([
217
+ toHex("score", { size: 32 }),
218
+ toHex("status", { size: 32 }),
219
+ toHex("tag", { size: 32 }),
220
+ ])
221
+ })
222
+
223
+ it("sorts a shorter key before a longer key sharing its prefix", async () => {
224
+ const attrs = await captureAttributes([
225
+ { key: "abc", value: 1 },
226
+ { key: "ab", value: 2 },
227
+ ])
228
+
229
+ expect(attrs.map((a) => a.name)).toEqual([
230
+ toHex("ab", { size: 32 }),
231
+ toHex("abc", { size: 32 }),
232
+ ])
233
+ })
234
+
235
+ it("does not mutate the caller's attributes array", async () => {
236
+ const input = [
237
+ { key: "b", value: 1 },
238
+ { key: "a", value: 2 },
239
+ ]
240
+ await captureAttributes(input)
241
+ expect(input.map((a) => a.key)).toEqual(["b", "a"])
53
242
  })
54
243
 
55
- it("accepts string attribute values that look like non-integers", () => {
56
- expect(
57
- typeof opsToTxData({
58
- creates: [{ ...validCreate, attributes: [{ key: "n", value: "1.5" }] }],
244
+ it("rejects duplicate attribute keys", async () => {
245
+ const { client } = makeClient()
246
+ await expect(
247
+ sendArkivTransaction(client, {
248
+ creates: [
249
+ {
250
+ ...BASE_CREATE,
251
+ attributes: [
252
+ { key: "tag", value: "one" },
253
+ { key: "tag", value: "two" },
254
+ ],
255
+ },
256
+ ],
59
257
  }),
60
- ).toBe("string")
258
+ ).rejects.toThrow(DuplicateAttributeError)
61
259
  })
62
260
  })