@arkiv-network/sdk 0.0.36-dev.0 → 0.0.36-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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkiv-network/sdk",
3
- "version": "0.0.36-dev.0",
3
+ "version": "0.0.36-dev.1",
4
4
  "description": "TypeScript-only client library for Arkiv blockchain interactions",
5
5
  "type": "module",
6
6
  "main": "./dist/_cjs/index.js",
@@ -1,13 +1,18 @@
1
1
  import type { Hex } from "viem"
2
2
  import type { ArkivClient } from "../../clients/baseClient"
3
+ import { NoEntityFoundError } from "../../query/errors"
3
4
  import { entityFromRpcResult } from "../../utils/entities"
4
5
 
5
6
  export async function getEntity(client: ArkivClient, key: Hex) {
6
- const payload = await client.request({
7
- method: "golembase_getStorageValue",
8
- params: [key],
7
+ const result = await client.request({
8
+ method: "arkiv_query",
9
+ params: [`$key = ${key}`],
9
10
  })
10
- console.debug("Payload", payload)
11
+ console.debug("GetEntity result", result)
11
12
 
12
- return entityFromRpcResult(client, key, payload)
13
+ if (!result.data) {
14
+ throw new NoEntityFoundError()
15
+ }
16
+
17
+ return entityFromRpcResult(result.data[0])
13
18
  }
@@ -0,0 +1,10 @@
1
+ import type { ArkivClient } from "../../clients/baseClient"
2
+
3
+ export async function getEntityCount(client: ArkivClient) {
4
+ const count = await client.request({
5
+ method: "arkiv_getEntityCount",
6
+ params: [],
7
+ })
8
+
9
+ return count
10
+ }
@@ -3,13 +3,11 @@ import { entityFromRpcResult } from "../../utils/entities"
3
3
 
4
4
  export async function query(client: ArkivClient, query: string) {
5
5
  const result = await client.request({
6
- method: "golembase_queryEntities",
6
+ method: "arkiv_query",
7
7
  params: [query],
8
8
  })
9
9
 
10
- const entities = await Promise.all(
11
- result.map((entity) => entityFromRpcResult(client, entity.key, entity.value)),
12
- )
10
+ const entities = await Promise.all(result.data.map((entity) => entityFromRpcResult(entity)))
13
11
 
14
12
  return entities
15
13
  }
@@ -1,6 +1,7 @@
1
1
  import type { Account, Chain, Client, Hex, PublicActions, Transport } from "viem"
2
2
  import { getBlockTiming } from "../../actions/public/getBlockTiming"
3
3
  import { getEntity } from "../../actions/public/getEntity"
4
+ import { getEntityCount } from "../../actions/public/getEntityCount"
4
5
  import { query } from "../../actions/public/query"
5
6
  import { subscribeEntityEvents } from "../../actions/public/subscribeEntityEvents"
6
7
  import { QueryBuilder } from "../../query/queryBuilder"
@@ -95,6 +96,42 @@ export type PublicArkivActions<
95
96
  */
96
97
  query: (query: string) => Promise<Entity[]>
97
98
 
99
+ /**
100
+ * Returns the number of entities in the DBChain.
101
+ * @returns The number of entities in the DBChain. {@link number}
102
+ *
103
+ * @example
104
+ * import { createPublicClient, http } from 'arkiv'
105
+ * import { kaolin } from 'arkiv/chains'
106
+ *
107
+ * const client = createPublicClient({
108
+ * chain: kaolin,
109
+ * transport: http(),
110
+ * })
111
+ * const entityCount = await client.getEntityCount()
112
+ * // entityCount = 0
113
+ */
114
+ getEntityCount: () => Promise<number>
115
+
116
+ /**
117
+ * Returns the current block timing.
118
+ * @returns The current block timing. {@link GetBlockTimingReturnType}
119
+ *
120
+ * @example
121
+ * import { createPublicClient, http } from 'arkiv'
122
+ * import { kaolin } from 'arkiv/chains'
123
+ *
124
+ * const client = createPublicClient({
125
+ * chain: kaolin,
126
+ * transport: http(),
127
+ * })
128
+ * const blockTiming = await client.getBlockTiming()
129
+ * // {
130
+ * // currentBlock: 10n, // block number
131
+ * // currentBlockTime: 1234567890, // block timestamp
132
+ * // blockDuration: 2, // in seconds
133
+ * // }
134
+ */
98
135
  getBlockTiming: () => Promise<{
99
136
  currentBlock: bigint
100
137
  currentBlockTime: number
@@ -131,6 +168,7 @@ export function publicArkivActions<
131
168
  query: (rawQuery: string) => query(client, rawQuery),
132
169
  buildQuery: () => new QueryBuilder(client),
133
170
  getBlockTiming: () => getBlockTiming(client),
171
+ getEntityCount: () => getEntityCount(client),
134
172
  subscribeEntityEvents: (
135
173
  {
136
174
  onError,
@@ -1,5 +1,6 @@
1
1
  import type { Hex } from "viem"
2
2
  import type { ArkivClient } from "../clients/baseClient"
3
+ import type { RpcQueryOptions } from "../types/rpcSchema"
3
4
  import type { Predicate } from "./predicate"
4
5
 
5
6
  function processPredicates(predicates: Predicate[]): string {
@@ -42,31 +43,33 @@ export async function processQuery(
42
43
  queryParams: {
43
44
  predicates: Predicate[]
44
45
  limit: number | undefined
45
- offset: number | undefined
46
+ cursor: string | undefined
46
47
  ownedBy: Hex | undefined
47
- validBeforeBlock?: number | undefined
48
+ validAtBlock?: bigint | undefined
48
49
  withAnnotations?: boolean | undefined
49
50
  withMetadata?: boolean | undefined
51
+ withPayload?: boolean | undefined
50
52
  },
51
53
  ) {
52
- const { predicates, limit, offset, ownedBy, validBeforeBlock, withAnnotations, withMetadata } =
53
- queryParams
54
+ const {
55
+ predicates,
56
+ limit,
57
+ cursor,
58
+ ownedBy,
59
+ validAtBlock,
60
+ withAnnotations,
61
+ withMetadata,
62
+ withPayload,
63
+ } = queryParams
54
64
 
55
65
  console.debug(
56
- `Processing query with params: predicates: ${predicates}, limit: ${limit}, offset: ${offset}, ownedBy: ${ownedBy}, validBeforeBlock: ${validBeforeBlock}, withAnnotations: ${withAnnotations}, withMetadata: ${withMetadata}`,
66
+ `Processing query with params: predicates: ${predicates}, cursor: ${cursor}, limit: ${limit}, ownedBy: ${ownedBy}, validAtBlock: ${validAtBlock}, withAnnotations: ${withAnnotations}, withMetadata: ${withMetadata}, withPayload: ${withPayload}`,
57
67
  )
58
68
 
59
69
  let query = processPredicates(predicates)
60
70
  if (ownedBy) {
61
71
  query += ` && $owner=${ownedBy}`
62
72
  }
63
- if (limit) {
64
- query += ` limit=${limit}`
65
- }
66
-
67
- if (offset) {
68
- query += ` offset=${offset}`
69
- }
70
73
 
71
74
  // remove leading and trailing spaces and leading &&
72
75
  query = query.trim()
@@ -74,11 +77,31 @@ export async function processQuery(
74
77
  query = query.slice(3)
75
78
  }
76
79
 
77
- console.debug(`Built query to send: ${query}`)
80
+ const queryOptions: RpcQueryOptions = {
81
+ includeData: {
82
+ key: true,
83
+ annotations: withAnnotations ?? false,
84
+ payload: withPayload ?? false,
85
+ expiration: withMetadata ?? false,
86
+ owner: withMetadata ?? false,
87
+ },
88
+ }
89
+
90
+ if (validAtBlock !== undefined) {
91
+ queryOptions.atBlock = validAtBlock
92
+ }
93
+ if (limit !== undefined) {
94
+ queryOptions.resultsPerPage = limit
95
+ }
96
+ if (cursor !== undefined) {
97
+ queryOptions.cursor = cursor
98
+ }
99
+
100
+ console.debug(`Built query to send: ${query}, queryOptions: ${JSON.stringify(queryOptions)}`)
78
101
 
79
102
  const result = await client.request({
80
- method: "golembase_queryEntities",
81
- params: [query],
103
+ method: "arkiv_query",
104
+ params: [query, queryOptions],
82
105
  })
83
106
  console.debug("Raw result from query: ", result)
84
107
 
@@ -4,9 +4,9 @@ export class NoMoreResultsError extends Error {
4
4
  }
5
5
  }
6
6
 
7
- export class NoOffsetOrLimitError extends Error {
7
+ export class NoCursorOrLimitError extends Error {
8
8
  constructor() {
9
- super("Offset and limit must be defined to fetch next or previous")
9
+ super("Cursor and limit must be defined to fetch next")
10
10
  }
11
11
  }
12
12
 
@@ -15,3 +15,9 @@ export class OffsetCannotBeLessThanZeroError extends Error {
15
15
  super("Offset cannot be less than 0")
16
16
  }
17
17
  }
18
+
19
+ export class NoEntityFoundError extends Error {
20
+ constructor() {
21
+ super("No entity found")
22
+ }
23
+ }
@@ -14,11 +14,12 @@ import { QueryResult } from "./queryResult"
14
14
  export class QueryBuilder {
15
15
  private _client: ArkivClient
16
16
  private _ownedBy: Hex | undefined
17
- private _validBeforeBlock: number | undefined
17
+ private _validAtBlock: bigint | undefined
18
18
  private _withAnnotations: boolean | undefined
19
19
  private _withMetadata: boolean | undefined
20
+ private _withPayload: boolean | undefined
20
21
  private _limit: number | undefined
21
- private _offset: number | undefined
22
+ private _cursor: string | undefined
22
23
  private _predicates: Predicate[]
23
24
 
24
25
  constructor(client: ArkivClient) {
@@ -68,6 +69,20 @@ export class QueryBuilder {
68
69
  return this
69
70
  }
70
71
 
72
+ /**
73
+ * Sets the withPayload flag which will return the payload for the entities if true
74
+ * @param withPayload - The boolean value to set
75
+ * @returns The QueryBuilder instance
76
+ *
77
+ * @example
78
+ * const builder = new QueryBuilder(client)
79
+ * builder.withPayload(true)
80
+ */
81
+ withPayload(withPayload: boolean = true) {
82
+ this._withPayload = withPayload
83
+ return this
84
+ }
85
+
71
86
  /**
72
87
  * Sets the limit for the query
73
88
  * @param limit - The number of entities to return
@@ -83,16 +98,31 @@ export class QueryBuilder {
83
98
  }
84
99
 
85
100
  /**
86
- * Sets the offset for the query
87
- * @param offset - The number of entities to skip
101
+ * Sets the cursor for the query - it is advances setting which rather shouldn't be used manually but it is provided from query result if limit is used (pagination).
102
+ * @param cursor - The cursor to set which tells to RPC Query server where to start or continue the query.
88
103
  * @returns The QueryBuilder instance
89
104
  *
90
105
  * @example
91
106
  * const builder = new QueryBuilder(client)
92
107
  * builder.offset(10)
93
108
  */
94
- offset(offset: number) {
95
- this._offset = offset
109
+ cursor(cursor: string) {
110
+ this._cursor = cursor
111
+ return this
112
+ }
113
+
114
+ /**
115
+ * Sets the validAtBlock for the query which tells at which block height the state we are intested.
116
+ * If not set, the latest block is used.
117
+ * @param validAtBlock - The block number to set
118
+ * @returns The QueryBuilder instance
119
+ *
120
+ * @example
121
+ * const builder = new QueryBuilder(client)
122
+ * builder.validAtBlock(10000)
123
+ */
124
+ validAtBlock(validAtBlock: bigint) {
125
+ this._validAtBlock = validAtBlock
96
126
  return this
97
127
  }
98
128
 
@@ -135,18 +165,22 @@ export class QueryBuilder {
135
165
  const queryResult = await processQuery(this._client, {
136
166
  predicates: this._predicates,
137
167
  limit: this._limit,
138
- offset: this._offset,
168
+ cursor: this._cursor,
139
169
  ownedBy: this._ownedBy,
140
- validBeforeBlock: this._validBeforeBlock,
170
+ validAtBlock: this._validAtBlock,
141
171
  withAnnotations: this._withAnnotations,
142
172
  withMetadata: this._withMetadata,
173
+ withPayload: this._withPayload,
143
174
  })
144
175
 
145
176
  const entities = await Promise.all(
146
- queryResult.map((entity) => entityFromRpcResult(this._client, entity.key, entity.value)),
177
+ queryResult.data.map((entity) => entityFromRpcResult(entity)),
147
178
  )
148
179
 
149
- return new QueryResult(entities, this, this._limit, this._offset)
180
+ this.cursor(queryResult.cursor)
181
+ this.validAtBlock(queryResult.blockNumber)
182
+
183
+ return new QueryResult(entities, this, this._cursor, this._limit, this._validAtBlock)
150
184
  }
151
185
 
152
186
  /**
@@ -162,13 +196,14 @@ export class QueryBuilder {
162
196
  const queryResult = await processQuery(this._client, {
163
197
  predicates: this._predicates,
164
198
  limit: this._limit,
165
- offset: this._offset,
199
+ cursor: this._cursor,
166
200
  ownedBy: this._ownedBy,
167
- validBeforeBlock: this._validBeforeBlock,
168
- withAnnotations: this._withAnnotations,
169
- withMetadata: this._withMetadata,
201
+ validAtBlock: this._validAtBlock,
202
+ withAnnotations: false,
203
+ withMetadata: false,
204
+ withPayload: false,
170
205
  })
171
206
 
172
- return queryResult.length
207
+ return queryResult.data.length ?? 0
173
208
  }
174
209
  }
@@ -1,46 +1,57 @@
1
1
  import type { Entity } from "../types/entity"
2
- import { NoMoreResultsError, NoOffsetOrLimitError, OffsetCannotBeLessThanZeroError } from "./errors"
2
+ import { NoCursorOrLimitError, NoMoreResultsError } from "./errors"
3
3
  import type { QueryBuilder } from "./queryBuilder"
4
4
 
5
5
  export class QueryResult {
6
6
  entities: Entity[]
7
- private limit: number | undefined
8
- private offset: number | undefined
9
- private queryBuilder: QueryBuilder
7
+ private _endOfIteration: boolean
8
+ private _cursor: string | undefined
9
+ private _limit: number | undefined
10
+ private _validAtBlock: bigint | undefined
11
+ private _queryBuilder: QueryBuilder
12
+
13
+ // Public getters for internal state
14
+ get queryBuilder(): QueryBuilder {
15
+ return this._queryBuilder
16
+ }
17
+
18
+ get cursor(): string | undefined {
19
+ return this._cursor
20
+ }
10
21
 
11
22
  constructor(
12
23
  entities: Entity[],
13
24
  queryBuilder: QueryBuilder,
14
- limit: number | undefined = undefined,
15
- offset: number | undefined = undefined,
25
+ cursor: string | undefined,
26
+ limit: number | undefined,
27
+ validAtBlock: bigint | undefined,
16
28
  ) {
17
29
  this.entities = entities
18
- this.limit = limit
19
- this.offset = offset
20
- this.queryBuilder = queryBuilder
30
+ this._queryBuilder = queryBuilder
31
+ this._endOfIteration = !limit || entities.length < limit
32
+ this._cursor = cursor
33
+ this._limit = limit
34
+ this._validAtBlock = validAtBlock
21
35
  }
22
36
 
23
37
  async next() {
24
- if (this.offset === undefined || this.limit === undefined) {
25
- throw new NoOffsetOrLimitError()
38
+ if (this._cursor === undefined || this._limit === undefined) {
39
+ throw new NoCursorOrLimitError()
26
40
  }
27
- if (this.limit < this.entities.length) {
41
+ if (this._endOfIteration) {
28
42
  throw new NoMoreResultsError()
29
43
  }
30
- this.offset += this.limit
31
- const result = await this.queryBuilder.offset(this.offset).fetch()
44
+ this._queryBuilder.cursor(this._cursor)
45
+ const result = await this._queryBuilder.fetch()
32
46
  this.entities = result.entities
33
- }
47
+ // Update the query builder reference
48
+ this._queryBuilder = result.queryBuilder
49
+ // Check if we've reached the end (no more cursor or we got fewer entities than limit)
50
+ this._endOfIteration = !result.cursor || result.entities.length < this._limit
51
+ // Update the cursor
52
+ this._cursor = result.cursor
34
53
 
35
- async previous() {
36
- if (this.offset === undefined || this.limit === undefined) {
37
- throw new NoOffsetOrLimitError()
38
- }
39
- if (this.offset - this.limit < 0) {
40
- throw new OffsetCannotBeLessThanZeroError()
41
- }
42
- this.offset -= this.limit
43
- const result = await this.queryBuilder.offset(this.offset).fetch()
44
- this.entities = result.entities
54
+ // TODO check current block height and if it is not too old
55
+ console.debug("Current block height for next page: ", this._validAtBlock)
45
56
  }
46
57
  }
@@ -4,14 +4,14 @@ import type { Annotation } from "./annotation"
4
4
  export class Entity {
5
5
  key: Hex
6
6
  owner: Hex
7
- expiresAtBlock: number
7
+ expiresAtBlock: bigint
8
8
  payload: Uint8Array
9
9
  annotations: Annotation[]
10
10
 
11
11
  constructor(
12
12
  key: Hex,
13
13
  owner: Hex,
14
- expiresAtBlock: number,
14
+ expiresAtBlock: bigint,
15
15
  payload: Uint8Array,
16
16
  annotations: Annotation[],
17
17
  ) {
@@ -1,5 +1,29 @@
1
1
  import type { Hex, PublicRpcSchema } from "viem"
2
2
 
3
+ export type RpcEntity = {
4
+ key: Hex
5
+ value: string
6
+ expiresAt: bigint
7
+ owner: Hex
8
+ stringAnnotations?: [{ key: string; value: string }]
9
+ numericAnnotations?: [{ key: string; value: number }]
10
+ }
11
+
12
+ export type RpcQueryOptions = {
13
+ atBlock?: bigint
14
+ includeData?: RpcIncludeData
15
+ resultsPerPage?: number
16
+ cursor?: string
17
+ }
18
+
19
+ export type RpcIncludeData = {
20
+ key: boolean
21
+ annotations: boolean
22
+ payload: boolean
23
+ expiration: boolean
24
+ owner: boolean
25
+ }
26
+
3
27
  export type ArkivRpcSchema = [
4
28
  {
5
29
  Method: "golembase_getStorageValue"
@@ -7,9 +31,13 @@ export type ArkivRpcSchema = [
7
31
  ReturnType: string
8
32
  },
9
33
  {
10
- Method: "golembase_queryEntities"
11
- Parameters?: [query: string]
12
- ReturnType: [{ key: Hex; value: string }]
34
+ Method: "arkiv_query"
35
+ Parameters?: [query: string, queryOptions?: RpcQueryOptions]
36
+ ReturnType: {
37
+ data: [RpcEntity]
38
+ blockNumber: bigint
39
+ cursor: string
40
+ }
13
41
  },
14
42
  {
15
43
  Method: "golembase_getEntityMetaData"
@@ -30,6 +58,11 @@ export type ArkivRpcSchema = [
30
58
  duration: number
31
59
  }
32
60
  },
61
+ {
62
+ Method: "arkiv_getEntityCount"
63
+ Parameters?: []
64
+ ReturnType: number
65
+ },
33
66
  ]
34
67
 
35
68
  export type PublicArkivRpcSchema = [...PublicRpcSchema, ...ArkivRpcSchema]
@@ -1,20 +1,14 @@
1
- import type { Hex } from "viem"
2
- import type { ArkivClient } from "../clients/baseClient"
1
+ import { toBytes } from "viem"
3
2
  import { Entity } from "../types/entity"
3
+ import type { RpcEntity } from "../types/rpcSchema"
4
4
 
5
- export async function entityFromRpcResult(client: ArkivClient, key: Hex, payload: string) {
6
- const metadata = await client.request({
7
- method: "golembase_getEntityMetaData",
8
- params: [key],
9
- })
10
- console.debug("Metadata", metadata)
11
-
12
- return new Entity(key, metadata.owner, metadata.expiresAtBlock, Uint8Array.fromBase64(payload), [
13
- ...(metadata.stringAnnotations ?? []).map(({ key, value }) => ({
5
+ export async function entityFromRpcResult(rpcEntity: RpcEntity) {
6
+ return new Entity(rpcEntity.key, rpcEntity.owner, rpcEntity.expiresAt, toBytes(rpcEntity.value), [
7
+ ...(rpcEntity.stringAnnotations ?? []).map(({ key, value }) => ({
14
8
  key,
15
9
  value,
16
10
  })),
17
- ...(metadata.numericAnnotations ?? []).map(({ key, value }) => ({
11
+ ...(rpcEntity.numericAnnotations ?? []).map(({ key, value }) => ({
18
12
  key,
19
13
  value: value,
20
14
  })),