@arkiv-network/sdk 0.1.1-dev.0 → 0.1.3-dev.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.
- package/README.md +22 -10
- package/dist/chains/index.d.ts +25 -25
- package/dist/chains/index.d.ts.map +1 -1
- package/dist/index.cjs +30 -9
- package/dist/index.js +30 -16
- package/dist/index.js.map +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/package.json +22 -8
- package/src/accounts/index.ts +1 -0
- package/src/actions/public/buildQuery.ts +6 -0
- package/src/actions/public/getBlockTiming.ts +20 -0
- package/src/actions/public/getEntity.ts +18 -0
- package/src/actions/public/getEntityCount.ts +10 -0
- package/src/actions/public/query.ts +13 -0
- package/src/actions/public/subscribeEntityEvents.ts +105 -0
- package/src/actions/wallet/changeOwnership.ts +40 -0
- package/src/actions/wallet/createEntity.ts +45 -0
- package/src/actions/wallet/deleteEntity.ts +39 -0
- package/src/actions/wallet/extendEntity.ts +41 -0
- package/src/actions/wallet/mutateEntities.ts +100 -0
- package/src/actions/wallet/updateEntity.ts +47 -0
- package/src/chains/index.ts +3 -0
- package/src/chains/kaolin.ts +26 -0
- package/src/chains/localhost.ts +14 -0
- package/src/chains/marketplace.ts +26 -0
- package/src/clients/baseClient.ts +9 -0
- package/src/clients/createPublicClient.test.ts +22 -0
- package/src/clients/createPublicClient.ts +65 -0
- package/src/clients/createWalletClient.test.ts +23 -0
- package/src/clients/createWalletClient.ts +70 -0
- package/src/clients/decorators/arkivPublic.ts +203 -0
- package/src/clients/decorators/arkivWallet.ts +250 -0
- package/src/consts.ts +2 -0
- package/src/index.ts +21 -0
- package/src/query/engine.test.ts +288 -0
- package/src/query/engine.ts +112 -0
- package/src/query/errors.ts +23 -0
- package/src/query/index.ts +4 -0
- package/src/query/predicate.test.ts +418 -0
- package/src/query/predicate.ts +140 -0
- package/src/query/queryBuilder.test.ts +537 -0
- package/src/query/queryBuilder.ts +209 -0
- package/src/query/queryResult.test.ts +270 -0
- package/src/query/queryResult.ts +61 -0
- package/src/types/attributes.ts +4 -0
- package/src/types/entity.ts +36 -0
- package/src/types/events.ts +34 -0
- package/src/types/index.ts +4 -0
- package/src/types/mimeTypes.ts +27 -0
- package/src/types/rpcSchema.ts +61 -0
- package/src/types/txParams.ts +15 -0
- package/src/utils/arkivTransactions.ts +100 -0
- package/src/utils/compression.test.ts +32 -0
- package/src/utils/compression.ts +99 -0
- package/src/utils/entities.ts +23 -0
- package/src/utils/expirationTime.ts +13 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/payload.ts +9 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Hex, PublicRpcSchema } from "viem"
|
|
2
|
+
import type { MimeType } from "./mimeTypes"
|
|
3
|
+
|
|
4
|
+
export type RpcEntity = {
|
|
5
|
+
key: Hex
|
|
6
|
+
contentType: MimeType
|
|
7
|
+
value: string
|
|
8
|
+
expiresAt: bigint
|
|
9
|
+
owner: Hex
|
|
10
|
+
stringAttributes?: [{ key: string; value: string }]
|
|
11
|
+
numericAttributes?: [{ key: string; value: number }]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type RpcQueryOptions = {
|
|
15
|
+
atBlock?: number
|
|
16
|
+
includeData?: RpcIncludeData
|
|
17
|
+
resultsPerPage?: number
|
|
18
|
+
cursor?: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type RpcIncludeData = {
|
|
22
|
+
key: boolean
|
|
23
|
+
attributes: boolean
|
|
24
|
+
payload: boolean
|
|
25
|
+
contentType: boolean
|
|
26
|
+
expiration: boolean
|
|
27
|
+
owner: boolean
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type ArkivRpcSchema = [
|
|
31
|
+
{
|
|
32
|
+
Method: "golembase_getStorageValue"
|
|
33
|
+
Parameters?: [entityId: Hex]
|
|
34
|
+
ReturnType: string
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
Method: "arkiv_query"
|
|
38
|
+
Parameters?: [query: string, queryOptions?: RpcQueryOptions]
|
|
39
|
+
ReturnType: {
|
|
40
|
+
data: [RpcEntity]
|
|
41
|
+
blockNumber: bigint
|
|
42
|
+
cursor: string
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
Method: "arkiv_getBlockTiming"
|
|
47
|
+
Parameters?: []
|
|
48
|
+
ReturnType: {
|
|
49
|
+
current_block: bigint
|
|
50
|
+
current_block_time: number
|
|
51
|
+
duration: number
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
Method: "arkiv_getEntityCount"
|
|
56
|
+
Parameters?: []
|
|
57
|
+
ReturnType: number
|
|
58
|
+
},
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
export type PublicArkivRpcSchema = [...PublicRpcSchema, ...ArkivRpcSchema]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type TxParams =
|
|
2
|
+
| {
|
|
3
|
+
gas?: bigint
|
|
4
|
+
nonce?: number
|
|
5
|
+
gasPrice?: bigint
|
|
6
|
+
maxFeePerGas?: never
|
|
7
|
+
maxPriorityFeePerGas?: never
|
|
8
|
+
}
|
|
9
|
+
| {
|
|
10
|
+
gas?: bigint
|
|
11
|
+
nonce?: number
|
|
12
|
+
maxFeePerGas?: bigint
|
|
13
|
+
maxPriorityFeePerGas?: bigint
|
|
14
|
+
gasPrice?: never
|
|
15
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { type Hex, toBytes, toHex, toRlp } from "viem"
|
|
2
|
+
import type { ChangeOwnershipParameters } from "../actions/wallet/changeOwnership"
|
|
3
|
+
import type { CreateEntityParameters } from "../actions/wallet/createEntity"
|
|
4
|
+
import type { DeleteEntityParameters } from "../actions/wallet/deleteEntity"
|
|
5
|
+
import type { ExtendEntityParameters } from "../actions/wallet/extendEntity"
|
|
6
|
+
import type { UpdateEntityParameters } from "../actions/wallet/updateEntity"
|
|
7
|
+
import type { ArkivClient } from "../clients/baseClient"
|
|
8
|
+
import type { WalletArkivClient } from "../clients/createWalletClient"
|
|
9
|
+
import { ARKIV_ADDRESS, BLOCK_TIME } from "../consts"
|
|
10
|
+
import type { TxParams } from "../types"
|
|
11
|
+
import { compress } from "./compression"
|
|
12
|
+
|
|
13
|
+
export function opsToTxData({
|
|
14
|
+
creates,
|
|
15
|
+
updates,
|
|
16
|
+
deletes,
|
|
17
|
+
extensions,
|
|
18
|
+
ownershipChanges,
|
|
19
|
+
}: {
|
|
20
|
+
creates?: CreateEntityParameters[]
|
|
21
|
+
updates?: UpdateEntityParameters[]
|
|
22
|
+
deletes?: DeleteEntityParameters[]
|
|
23
|
+
extensions?: ExtendEntityParameters[]
|
|
24
|
+
ownershipChanges?: ChangeOwnershipParameters[]
|
|
25
|
+
}) {
|
|
26
|
+
function formatAttributes<T extends string | number | bigint | boolean>(attribute: {
|
|
27
|
+
key: string
|
|
28
|
+
value: T
|
|
29
|
+
}): [Hex, Hex] {
|
|
30
|
+
return [toHex(attribute.key), toHex(attribute.value)]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const payload = [
|
|
34
|
+
//creates
|
|
35
|
+
(creates ?? []).map((item) => [
|
|
36
|
+
toHex(item.expiresIn / BLOCK_TIME),
|
|
37
|
+
toHex(item.contentType),
|
|
38
|
+
toHex(item.payload),
|
|
39
|
+
item.attributes
|
|
40
|
+
.filter((attribute) => typeof attribute.value === "string")
|
|
41
|
+
.map(formatAttributes),
|
|
42
|
+
item.attributes
|
|
43
|
+
.filter((attribute) => typeof attribute.value === "number")
|
|
44
|
+
.map(formatAttributes),
|
|
45
|
+
]),
|
|
46
|
+
//updates
|
|
47
|
+
(updates ?? []).map((item) => [
|
|
48
|
+
item.entityKey,
|
|
49
|
+
toHex(item.contentType),
|
|
50
|
+
toHex(item.expiresIn / BLOCK_TIME),
|
|
51
|
+
toHex(item.payload),
|
|
52
|
+
item.attributes
|
|
53
|
+
.filter((attribute) => typeof attribute.value === "string")
|
|
54
|
+
.map(formatAttributes),
|
|
55
|
+
item.attributes
|
|
56
|
+
.filter((attribute) => typeof attribute.value === "number")
|
|
57
|
+
.map(formatAttributes),
|
|
58
|
+
]),
|
|
59
|
+
//deletes
|
|
60
|
+
(deletes ?? []).map((item) => item.entityKey),
|
|
61
|
+
//extends
|
|
62
|
+
(extensions ?? []).map((item) => [item.entityKey, toHex(item.expiresIn / BLOCK_TIME)]),
|
|
63
|
+
//ownershipChanges TODO
|
|
64
|
+
(ownershipChanges ?? []).map((item) => [item.entityKey, item.newOwner]),
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
console.debug("txData to send as RLP", payload, payload.length)
|
|
68
|
+
|
|
69
|
+
return toRlp(payload)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function sendArkivTransaction(client: ArkivClient, data: Hex, txParams?: TxParams) {
|
|
73
|
+
if (!client.account) throw new Error("Account required")
|
|
74
|
+
const walletClient = client as WalletArkivClient
|
|
75
|
+
|
|
76
|
+
console.debug("Sending transaction", {
|
|
77
|
+
account: client.account,
|
|
78
|
+
chain: client.chain,
|
|
79
|
+
to: ARKIV_ADDRESS,
|
|
80
|
+
value: 0n,
|
|
81
|
+
data,
|
|
82
|
+
...txParams,
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
const compressed = await compress(toBytes(data))
|
|
86
|
+
const txHash = await walletClient.sendTransaction({
|
|
87
|
+
account: client.account,
|
|
88
|
+
chain: client.chain,
|
|
89
|
+
to: ARKIV_ADDRESS,
|
|
90
|
+
value: 0n,
|
|
91
|
+
data: toHex(compressed),
|
|
92
|
+
...txParams,
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
const receipt = await walletClient.waitForTransactionReceipt({ hash: txHash })
|
|
96
|
+
|
|
97
|
+
console.debug("Tx receipt", receipt)
|
|
98
|
+
|
|
99
|
+
return receipt
|
|
100
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test"
|
|
2
|
+
import { compress, decompress } from "./compression"
|
|
3
|
+
|
|
4
|
+
describe("compression", () => {
|
|
5
|
+
it("should compress data", async () => {
|
|
6
|
+
const originalData = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
|
7
|
+
const compressed = await compress(originalData)
|
|
8
|
+
|
|
9
|
+
expect(compressed).toBeInstanceOf(Uint8Array)
|
|
10
|
+
expect(compressed.length).toBeGreaterThan(0)
|
|
11
|
+
// Compressed data should be different from original
|
|
12
|
+
expect(compressed).not.toEqual(originalData)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it("should compress data with large payload into smaller size", async () => {
|
|
16
|
+
const originalData = new Uint8Array(1000).fill(1)
|
|
17
|
+
const compressed = await compress(originalData)
|
|
18
|
+
|
|
19
|
+
expect(compressed).toBeInstanceOf(Uint8Array)
|
|
20
|
+
expect(compressed.length).toBeGreaterThan(0)
|
|
21
|
+
expect(compressed.length).toBeLessThan(originalData.length)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it("should decompress data", async () => {
|
|
25
|
+
const originalData = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
|
26
|
+
const compressed = await compress(originalData)
|
|
27
|
+
const decompressed = await decompress(compressed)
|
|
28
|
+
|
|
29
|
+
expect(decompressed).toBeInstanceOf(Uint8Array)
|
|
30
|
+
expect(decompressed).toEqual(originalData)
|
|
31
|
+
})
|
|
32
|
+
})
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// src/utils/compression.ts
|
|
2
|
+
|
|
3
|
+
import type { BrotliWasmType } from "brotli-wasm"
|
|
4
|
+
|
|
5
|
+
// Detect if we're in Node.js environment
|
|
6
|
+
const isNode = typeof process !== "undefined" && process.versions?.node != null
|
|
7
|
+
|
|
8
|
+
let zlib: typeof import("zlib") | null = null
|
|
9
|
+
let brotli: BrotliWasmType | null = null
|
|
10
|
+
|
|
11
|
+
// Lazy load zlib (Node.js only)
|
|
12
|
+
async function getZlib(): Promise<typeof import("zlib") | null> {
|
|
13
|
+
if (!isNode) return null
|
|
14
|
+
|
|
15
|
+
if (!zlib) {
|
|
16
|
+
try {
|
|
17
|
+
zlib = await import("zlib")
|
|
18
|
+
} catch (error) {
|
|
19
|
+
console.error("Error importing zlib", error)
|
|
20
|
+
return null
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return zlib
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Lazy load brotli-wasm (browser/fallback)
|
|
27
|
+
async function getBrotli(): Promise<BrotliWasmType | null> {
|
|
28
|
+
if (brotli) return brotli
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const brotliModule = await import("brotli-wasm")
|
|
32
|
+
brotli = brotliModule.default || brotliModule
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error("Error importing brotli-wasm", error)
|
|
35
|
+
return null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return brotli
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function compress(data: Uint8Array): Promise<Uint8Array> {
|
|
42
|
+
console.debug(`Compressing data (size: ${data.length})`)
|
|
43
|
+
|
|
44
|
+
// Try Node.js zlib first (if available)
|
|
45
|
+
if (isNode) {
|
|
46
|
+
const zlibModule = await getZlib()
|
|
47
|
+
if (zlibModule) {
|
|
48
|
+
try {
|
|
49
|
+
// zlib.brotliCompress expects Buffer, returns Buffer
|
|
50
|
+
const buffer = Buffer.from(data)
|
|
51
|
+
const compressed = zlibModule.brotliCompressSync(buffer)
|
|
52
|
+
const result = new Uint8Array(compressed)
|
|
53
|
+
console.debug(`Brotli compressed with zlib (size: ${result.length})`)
|
|
54
|
+
return result
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.warn("zlib compression failed, falling back to brotli-wasm", error)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Fallback to brotli-wasm (browser or if zlib fails)
|
|
62
|
+
const brotliInstance = await getBrotli()
|
|
63
|
+
if (!brotliInstance) {
|
|
64
|
+
console.warn("Brotli instance not found, not compressing data")
|
|
65
|
+
return data
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const brotliCompressed = brotliInstance.compress(data)
|
|
69
|
+
console.debug(`Brotli compressed with wasm (size: ${brotliCompressed.length})`)
|
|
70
|
+
return brotliCompressed
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function decompress(data: Uint8Array): Promise<Uint8Array> {
|
|
74
|
+
// Try Node.js zlib first (if available)
|
|
75
|
+
if (isNode) {
|
|
76
|
+
const zlibModule = await getZlib()
|
|
77
|
+
if (zlibModule) {
|
|
78
|
+
try {
|
|
79
|
+
// zlib.brotliDecompress expects Buffer, returns Buffer
|
|
80
|
+
const buffer = Buffer.from(data)
|
|
81
|
+
const decompressed = zlibModule.brotliDecompressSync(buffer)
|
|
82
|
+
const result = new Uint8Array(decompressed)
|
|
83
|
+
console.debug(`Brotli decompressed with zlib (size: ${result.length})`)
|
|
84
|
+
return result
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.warn("zlib decompression failed, falling back to brotli-wasm", error)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Fallback to brotli-wasm (browser or if zlib fails)
|
|
92
|
+
const brotliInstance = await getBrotli()
|
|
93
|
+
if (!brotliInstance) {
|
|
94
|
+
console.warn("Brotli instance not found, not decompressing data")
|
|
95
|
+
return data
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return brotliInstance.decompress(data)
|
|
99
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { toBytes } from "viem"
|
|
2
|
+
import { Entity } from "../types/entity"
|
|
3
|
+
import type { RpcEntity } from "../types/rpcSchema"
|
|
4
|
+
|
|
5
|
+
export async function entityFromRpcResult(rpcEntity: RpcEntity) {
|
|
6
|
+
return new Entity(
|
|
7
|
+
rpcEntity.key,
|
|
8
|
+
rpcEntity.contentType,
|
|
9
|
+
rpcEntity.owner,
|
|
10
|
+
rpcEntity.expiresAt,
|
|
11
|
+
toBytes(rpcEntity.value),
|
|
12
|
+
[
|
|
13
|
+
...(rpcEntity.stringAttributes ?? []).map(({ key, value }) => ({
|
|
14
|
+
key,
|
|
15
|
+
value,
|
|
16
|
+
})),
|
|
17
|
+
...(rpcEntity.numericAttributes ?? []).map(({ key, value }) => ({
|
|
18
|
+
key,
|
|
19
|
+
value: Number(value),
|
|
20
|
+
})),
|
|
21
|
+
],
|
|
22
|
+
)
|
|
23
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BLOCK_TIME } from "../consts"
|
|
2
|
+
|
|
3
|
+
export const ExpirationTime = {
|
|
4
|
+
fromSeconds: (seconds: number) => seconds,
|
|
5
|
+
fromMinutes: (minutes: number) => minutes * 60,
|
|
6
|
+
fromHours: (hours: number) => hours * 60 * 60,
|
|
7
|
+
fromDays: (days: number) => days * 24 * 60 * 60,
|
|
8
|
+
fromWeeks: (weeks: number) => weeks * 7 * 24 * 60 * 60,
|
|
9
|
+
fromMonths: (months: number) => months * 30 * 24 * 60 * 60,
|
|
10
|
+
fromYears: (years: number) => years * 365 * 24 * 60 * 60,
|
|
11
|
+
fromBlocks: (blocks: number) => blocks * BLOCK_TIME,
|
|
12
|
+
fromDate: (date: Date) => Math.floor(date.getTime() / 1000) - Math.floor(Date.now() / 1000),
|
|
13
|
+
}
|