@kairoguard/sdk 0.0.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.
@@ -0,0 +1,183 @@
1
+ import { SuiClient } from "@mysten/sui/client";
2
+ import { bcs } from "@mysten/sui/bcs";
3
+ import { keccak256, toBytes } from "viem";
4
+ const VecU8 = bcs.vector(bcs.u8());
5
+ // Matches `kairo_policy_engine::custody_ledger::EventV2Canonical`
6
+ const EventV2CanonicalBcs = bcs.struct("EventV2Canonical", {
7
+ chain_id: bcs.Address,
8
+ seq: bcs.u64(),
9
+ kind: bcs.u8(),
10
+ recorded_at_ms: bcs.u64(),
11
+ prev_hash: VecU8,
12
+ src_namespace: bcs.u8(),
13
+ src_chain_id: bcs.u64(),
14
+ src_tx_hash: VecU8,
15
+ to_addr: VecU8,
16
+ policy_object_id: bcs.Address,
17
+ policy_version: VecU8,
18
+ intent_hash: VecU8,
19
+ receipt_object_id: bcs.Address,
20
+ payload: VecU8,
21
+ });
22
+ /**
23
+ * Verify a custody event hash by recomputing the canonical BCS encoding (v2/v3 hashing).
24
+ *
25
+ * Note: This does NOT discover events. You must provide a specific `CustodyEvent` object id.
26
+ */
27
+ export async function fetchAndVerifyCustodyEvent(args) {
28
+ try {
29
+ const client = new SuiClient({ url: args.suiRpcUrl });
30
+ const obj = await client.getObject({
31
+ id: args.custodyEventObjectId,
32
+ options: { showType: true, showContent: true },
33
+ });
34
+ if (!obj.data)
35
+ throw new Error("CustodyEvent object not found");
36
+ const typeStr = String(obj.data.type ?? "");
37
+ if (!typeStr.endsWith("::custody_ledger::CustodyEvent")) {
38
+ throw new Error(`Object is not a CustodyEvent (type=${typeStr})`);
39
+ }
40
+ const content = obj.data.content;
41
+ if (!content || content.dataType !== "moveObject") {
42
+ throw new Error("CustodyEvent object has no Move content");
43
+ }
44
+ const f = content.fields ?? {};
45
+ const chainId = String(f["chain_id"] ?? "").trim();
46
+ const receiptId = String(f["receipt_object_id"] ?? "").trim();
47
+ const policyObjectId = String(f["policy_object_id"] ?? "").trim();
48
+ if (!chainId.startsWith("0x") || !receiptId.startsWith("0x") || !policyObjectId.startsWith("0x")) {
49
+ throw new Error("CustodyEvent missing required object id fields");
50
+ }
51
+ const seq = toBigInt(f["seq"]);
52
+ const kind = toNumberU8(f["kind"]);
53
+ const recordedAt = toBigInt(f["recorded_at_ms"]);
54
+ const prevHash = coerceBytes(f["prev_hash"]);
55
+ const srcNs = toNumberU8(f["src_namespace"]);
56
+ const srcChainId = toBigInt(f["src_chain_id"]);
57
+ const srcTxHash = coerceBytes(f["src_tx_hash"]) ?? new Uint8Array();
58
+ const toAddr = coerceBytes(f["to_addr"]) ?? new Uint8Array();
59
+ const policyVersion = coerceBytes(f["policy_version"]) ?? new Uint8Array();
60
+ const intentHash = coerceBytes(f["intent_hash"]);
61
+ const payload = coerceBytes(f["payload"]) ?? new Uint8Array();
62
+ const eventHashOnChain = coerceBytes(f["event_hash"]);
63
+ if (seq == null ||
64
+ kind == null ||
65
+ recordedAt == null ||
66
+ srcNs == null ||
67
+ srcChainId == null ||
68
+ !prevHash ||
69
+ !intentHash ||
70
+ !eventHashOnChain) {
71
+ throw new Error("CustodyEvent missing required scalar/hash fields");
72
+ }
73
+ if (prevHash.length !== 32)
74
+ throw new Error("prev_hash must be 32 bytes");
75
+ if (intentHash.length !== 32)
76
+ throw new Error("intent_hash must be 32 bytes");
77
+ if (eventHashOnChain.length !== 32)
78
+ throw new Error("event_hash must be 32 bytes");
79
+ const canonBytes = EventV2CanonicalBcs.serialize({
80
+ chain_id: chainId,
81
+ seq,
82
+ kind,
83
+ recorded_at_ms: recordedAt,
84
+ prev_hash: Array.from(prevHash),
85
+ src_namespace: srcNs,
86
+ src_chain_id: srcChainId,
87
+ src_tx_hash: Array.from(srcTxHash),
88
+ to_addr: Array.from(toAddr),
89
+ policy_object_id: policyObjectId,
90
+ policy_version: Array.from(policyVersion),
91
+ intent_hash: Array.from(intentHash),
92
+ receipt_object_id: receiptId,
93
+ payload: Array.from(payload),
94
+ }).toBytes();
95
+ const computed = toBytes(keccak256(canonBytes));
96
+ if (!bytesEq(computed, eventHashOnChain)) {
97
+ throw new Error(`CustodyEvent hash mismatch (computed=${toHex(computed)}, onchain=${toHex(eventHashOnChain)})`);
98
+ }
99
+ return { ok: true };
100
+ }
101
+ catch (e) {
102
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
103
+ }
104
+ }
105
+ function toBigInt(v) {
106
+ if (v == null)
107
+ return null;
108
+ if (typeof v === "bigint")
109
+ return v;
110
+ if (typeof v === "number" && Number.isFinite(v))
111
+ return BigInt(v);
112
+ if (typeof v === "string") {
113
+ const s = v.trim();
114
+ if (!s)
115
+ return null;
116
+ if (s.startsWith("0x") || s.startsWith("0X"))
117
+ return BigInt(s);
118
+ if (/^\\d+$/.test(s))
119
+ return BigInt(s);
120
+ }
121
+ return null;
122
+ }
123
+ function toNumberU8(v) {
124
+ if (v == null)
125
+ return null;
126
+ const n = typeof v === "number" ? v : Number(String(v));
127
+ if (!Number.isFinite(n))
128
+ return null;
129
+ const i = Math.floor(n);
130
+ if (i < 0 || i > 255)
131
+ return null;
132
+ return i;
133
+ }
134
+ function coerceBytes(v) {
135
+ if (v instanceof Uint8Array)
136
+ return v;
137
+ if (Array.isArray(v) && v.every((x) => Number.isInteger(x) && x >= 0 && x <= 255)) {
138
+ return Uint8Array.from(v);
139
+ }
140
+ if (typeof v === "string") {
141
+ const s = v.trim();
142
+ if (!s)
143
+ return null;
144
+ if (/^0x[0-9a-fA-F]*$/.test(s)) {
145
+ try {
146
+ return toBytes(s);
147
+ }
148
+ catch {
149
+ return null;
150
+ }
151
+ }
152
+ // base64
153
+ try {
154
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
155
+ if (typeof Buffer !== "undefined")
156
+ return Uint8Array.from(Buffer.from(s, "base64"));
157
+ }
158
+ catch {
159
+ return null;
160
+ }
161
+ }
162
+ if (v && typeof v === "object") {
163
+ const o = v;
164
+ if (o.bytes != null)
165
+ return coerceBytes(o.bytes);
166
+ if (o.data != null)
167
+ return coerceBytes(o.data);
168
+ if (o.value != null)
169
+ return coerceBytes(o.value);
170
+ }
171
+ return null;
172
+ }
173
+ function bytesEq(a, b) {
174
+ if (a.length !== b.length)
175
+ return false;
176
+ for (let i = 0; i < a.length; i++)
177
+ if (a[i] !== b[i])
178
+ return false;
179
+ return true;
180
+ }
181
+ function toHex(bytes) {
182
+ return `0x${Buffer.from(bytes).toString("hex")}`;
183
+ }
@@ -0,0 +1,27 @@
1
+ import type { Hex } from "./types.js";
2
+ /**
3
+ * Minimal "hard gate" receipt verification helper.
4
+ *
5
+ * We verify:
6
+ * - receipt object exists
7
+ * - receipt.allowed === true
8
+ * - receipt fields match the expected commitment (policyId, policyVersion, chainId, intent hash, optional destination)
9
+ *
10
+ * The exact object type + field names are defined in the Move package under `sui/kairo_policy_engine`.
11
+ */
12
+ export declare function fetchAndValidatePolicyReceipt(params: {
13
+ suiRpcUrl: string;
14
+ receiptObjectId: string;
15
+ expected: {
16
+ policyId: string;
17
+ policyVersion: string;
18
+ evmChainId: number;
19
+ intentHash: Hex;
20
+ toEvm?: Hex;
21
+ policyStableId?: string;
22
+ policyRoot?: Hex;
23
+ policyVersionId?: string;
24
+ evmSelector?: Hex;
25
+ erc20Amount?: Hex;
26
+ };
27
+ }): Promise<void>;
@@ -0,0 +1,203 @@
1
+ import { SuiClient } from "@mysten/sui/client";
2
+ /**
3
+ * Minimal "hard gate" receipt verification helper.
4
+ *
5
+ * We verify:
6
+ * - receipt object exists
7
+ * - receipt.allowed === true
8
+ * - receipt fields match the expected commitment (policyId, policyVersion, chainId, intent hash, optional destination)
9
+ *
10
+ * The exact object type + field names are defined in the Move package under `sui/kairo_policy_engine`.
11
+ */
12
+ export async function fetchAndValidatePolicyReceipt(params) {
13
+ const suiClient = new SuiClient({ url: params.suiRpcUrl });
14
+ const obj = await suiClient.getObject({
15
+ id: params.receiptObjectId,
16
+ options: { showContent: true, showType: true },
17
+ });
18
+ if (!obj.data)
19
+ throw new Error("Receipt object not found");
20
+ const content = obj.data.content;
21
+ const typeStr = String(obj.data.type ?? "");
22
+ if (!content || content.dataType !== "moveObject") {
23
+ throw new Error("Receipt object has no Move content");
24
+ }
25
+ const fields = content.fields ?? {};
26
+ // PolicyReceiptV2
27
+ if (typeStr.endsWith("::policy_registry::PolicyReceiptV2")) {
28
+ const allowed = Boolean(fields["allowed"]);
29
+ if (!allowed) {
30
+ const denial = fields["denial_reason"];
31
+ const denialReason = typeof denial === "bigint" ? denial.toString() : String(denial ?? "");
32
+ throw new Error(`Receipt is denied (denial_reason=${denialReason})`);
33
+ }
34
+ const policyObjectId = String(fields["policy_object_id"] ?? "");
35
+ const policyVersion = bytesFieldToUtf8(fields["policy_version"]);
36
+ const stableId = bytesFieldToUtf8(fields["policy_stable_id"]);
37
+ const policyRoot = normalizeBytesFieldToHex(fields["policy_root"]);
38
+ const policyVersionId = String(fields["policy_version_id"] ?? "");
39
+ const evmChainId = Number(String(fields["evm_chain_id"] ?? ""));
40
+ const intentHash = normalizeBytesFieldToHex(fields["intent_hash"]);
41
+ const toEvm = normalizeBytesFieldToHex(fields["to_evm"]);
42
+ const selector = normalizeBytesFieldToHex(fields["evm_selector"]);
43
+ const amount = normalizeBytesFieldToHex(fields["erc20_amount"]);
44
+ if (!policyObjectId || !policyVersion || !Number.isFinite(evmChainId) || !intentHash || !toEvm) {
45
+ throw new Error("ReceiptV2 missing required fields");
46
+ }
47
+ if (!policyRoot || coerceBytes(fields["policy_root"])?.length !== 32) {
48
+ throw new Error("ReceiptV2 policy_root missing/invalid (expected 32 bytes)");
49
+ }
50
+ if (!policyVersionId.startsWith("0x")) {
51
+ throw new Error("ReceiptV2 policy_version_id missing/invalid");
52
+ }
53
+ if (selector && coerceBytes(fields["evm_selector"])?.length !== 4) {
54
+ throw new Error("ReceiptV2 evm_selector invalid (expected 4 bytes or empty)");
55
+ }
56
+ if (amount && coerceBytes(fields["erc20_amount"])?.length !== 32) {
57
+ throw new Error("ReceiptV2 erc20_amount invalid (expected 32 bytes or empty)");
58
+ }
59
+ if (policyObjectId.toLowerCase() !== params.expected.policyId.toLowerCase()) {
60
+ throw new Error("ReceiptV2 policy_object_id mismatch");
61
+ }
62
+ if (policyVersion !== params.expected.policyVersion) {
63
+ throw new Error("ReceiptV2 policy_version mismatch");
64
+ }
65
+ if (evmChainId !== params.expected.evmChainId) {
66
+ throw new Error("ReceiptV2 evm_chain_id mismatch");
67
+ }
68
+ if (intentHash.toLowerCase() !== params.expected.intentHash.toLowerCase()) {
69
+ throw new Error("ReceiptV2 intent_hash mismatch");
70
+ }
71
+ if (params.expected.toEvm && toEvm.toLowerCase() !== params.expected.toEvm.toLowerCase()) {
72
+ throw new Error("ReceiptV2 destination mismatch");
73
+ }
74
+ // Optional stronger checks (if caller provides expectations)
75
+ if (params.expected.policyStableId && stableId && stableId !== params.expected.policyStableId) {
76
+ throw new Error("ReceiptV2 policy_stable_id mismatch");
77
+ }
78
+ if (params.expected.policyRoot && policyRoot.toLowerCase() !== params.expected.policyRoot.toLowerCase()) {
79
+ throw new Error("ReceiptV2 policy_root mismatch");
80
+ }
81
+ if (params.expected.policyVersionId &&
82
+ policyVersionId.toLowerCase() !== params.expected.policyVersionId.toLowerCase()) {
83
+ throw new Error("ReceiptV2 policy_version_id mismatch");
84
+ }
85
+ if (params.expected.evmSelector && (!selector || selector.toLowerCase() !== params.expected.evmSelector.toLowerCase())) {
86
+ throw new Error("ReceiptV2 evm_selector mismatch");
87
+ }
88
+ if (params.expected.erc20Amount && (!amount || amount.toLowerCase() !== params.expected.erc20Amount.toLowerCase())) {
89
+ throw new Error("ReceiptV2 erc20_amount mismatch");
90
+ }
91
+ return;
92
+ }
93
+ // Legacy PolicyReceipt (MVP)
94
+ const policyId = String(fields["policy_id"] ?? "");
95
+ const policyVersion = bytesFieldToUtf8(fields["policy_version"]);
96
+ const evmChainId = Number(String(fields["evm_chain_id"] ?? ""));
97
+ const allowed = Boolean(fields["allowed"]);
98
+ if (!allowed) {
99
+ const denial = fields["denial_reason"];
100
+ const denialReason = typeof denial === "bigint" ? denial.toString() : String(denial ?? "");
101
+ throw new Error(`Receipt is denied (denial_reason=${denialReason})`);
102
+ }
103
+ const intentHash = normalizeBytesFieldToHex(fields["intent_hash"]);
104
+ if (!intentHash)
105
+ throw new Error("Receipt intent_hash missing/invalid");
106
+ if (!policyId || !policyVersion || !Number.isFinite(evmChainId)) {
107
+ throw new Error("Receipt missing required fields");
108
+ }
109
+ if (policyId.toLowerCase() !== params.expected.policyId.toLowerCase()) {
110
+ throw new Error("Receipt policy_id mismatch");
111
+ }
112
+ if (policyVersion !== params.expected.policyVersion) {
113
+ throw new Error("Receipt policy_version mismatch");
114
+ }
115
+ if (evmChainId !== params.expected.evmChainId) {
116
+ throw new Error("Receipt evm_chain_id mismatch");
117
+ }
118
+ if (intentHash.toLowerCase() !== params.expected.intentHash.toLowerCase()) {
119
+ throw new Error("Receipt intent_hash mismatch");
120
+ }
121
+ const expectedTo = params.expected.toEvm;
122
+ if (expectedTo) {
123
+ const receiptTo = normalizeBytesFieldToHex(fields["to_evm"]);
124
+ if (!receiptTo)
125
+ throw new Error("Receipt to_evm missing/invalid");
126
+ if (receiptTo.toLowerCase() !== expectedTo.toLowerCase()) {
127
+ throw new Error("Receipt destination mismatch");
128
+ }
129
+ }
130
+ }
131
+ function normalizeBytesFieldToHex(v) {
132
+ const bytes = coerceBytes(v);
133
+ if (!bytes)
134
+ return null;
135
+ return `0x${bytesToHex(bytes)}`;
136
+ }
137
+ function bytesFieldToUtf8(v) {
138
+ const bytes = coerceBytes(v);
139
+ if (!bytes)
140
+ return "";
141
+ return new TextDecoder().decode(bytes);
142
+ }
143
+ function bytesToHex(bytes) {
144
+ return [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
145
+ }
146
+ function coerceBytes(v) {
147
+ if (v instanceof Uint8Array)
148
+ return v;
149
+ if (Array.isArray(v) && v.every((x) => Number.isInteger(x) && x >= 0 && x <= 255)) {
150
+ return Uint8Array.from(v);
151
+ }
152
+ if (typeof v === "string") {
153
+ if (/^0x[0-9a-fA-F]*$/.test(v)) {
154
+ const raw = v.slice(2);
155
+ if (raw.length % 2 !== 0)
156
+ return null;
157
+ const out = new Uint8Array(raw.length / 2);
158
+ for (let i = 0; i < out.length; i++)
159
+ out[i] = parseInt(raw.slice(i * 2, i * 2 + 2), 16);
160
+ return out;
161
+ }
162
+ return base64ToBytes(v);
163
+ }
164
+ if (v && typeof v === "object") {
165
+ const o = v;
166
+ if (typeof o.bytes === "string")
167
+ return coerceBytes(o.bytes);
168
+ if (typeof o.data === "string")
169
+ return coerceBytes(o.data);
170
+ if (typeof o.value === "string")
171
+ return coerceBytes(o.value);
172
+ }
173
+ return null;
174
+ }
175
+ function base64ToBytes(s) {
176
+ // Node
177
+ try {
178
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
179
+ if (typeof Buffer !== "undefined") {
180
+ const buf = Buffer.from(s, "base64");
181
+ if (buf.length === 0 && s.length > 0)
182
+ return null;
183
+ return Uint8Array.from(buf);
184
+ }
185
+ }
186
+ catch {
187
+ // ignore
188
+ }
189
+ // Browser
190
+ try {
191
+ const atobFn = globalThis.atob;
192
+ if (typeof atobFn !== "function")
193
+ return null;
194
+ const bin = atobFn(s);
195
+ const out = new Uint8Array(bin.length);
196
+ for (let i = 0; i < bin.length; i++)
197
+ out[i] = bin.charCodeAt(i);
198
+ return out;
199
+ }
200
+ catch {
201
+ return null;
202
+ }
203
+ }
@@ -0,0 +1,8 @@
1
+ import type { SuiTransactionBlockResponse } from "@mysten/sui/client";
2
+ /**
3
+ * Extract created object IDs from a Sui transaction response.
4
+ *
5
+ * Wallet adapters differ in what they return; for hard-gating we want to reliably
6
+ * recover the newly created `PolicyReceipt` object id.
7
+ */
8
+ export declare function getCreatedObjectIds(result: SuiTransactionBlockResponse): string[];
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Extract created object IDs from a Sui transaction response.
3
+ *
4
+ * Wallet adapters differ in what they return; for hard-gating we want to reliably
5
+ * recover the newly created `PolicyReceipt` object id.
6
+ */
7
+ export function getCreatedObjectIds(result) {
8
+ const created = result.effects?.created ?? [];
9
+ return created
10
+ .map((c) => c.reference?.objectId)
11
+ .filter((x) => typeof x === "string" && x.length > 0);
12
+ }
@@ -0,0 +1,15 @@
1
+ import { Transaction } from "@mysten/sui/transactions";
2
+ import type { Hex, KairoPolicyId } from "./types.js";
3
+ /**
4
+ * Build a Sui Transaction that calls the Move policy engine to mint a hard-gating receipt.
5
+ *
6
+ * You can have the user sign this tx with their Sui wallet, then pass the resulting receipt object id
7
+ * into the EVM signing flow.
8
+ */
9
+ export declare function buildMintEvmReceiptTx(params: {
10
+ packageId: string;
11
+ policyObjectId: KairoPolicyId;
12
+ evmChainId: number;
13
+ intentHash: Hex;
14
+ toEvm: Hex;
15
+ }): Transaction;
@@ -0,0 +1,38 @@
1
+ import { Transaction } from "@mysten/sui/transactions";
2
+ /**
3
+ * Build a Sui Transaction that calls the Move policy engine to mint a hard-gating receipt.
4
+ *
5
+ * You can have the user sign this tx with their Sui wallet, then pass the resulting receipt object id
6
+ * into the EVM signing flow.
7
+ */
8
+ export function buildMintEvmReceiptTx(params) {
9
+ const tx = new Transaction();
10
+ const intentBytes = hexToBytes(params.intentHash, 32);
11
+ const toBytes = hexToBytes(params.toEvm, 20);
12
+ tx.moveCall({
13
+ // Use *_to_sender to avoid UnusedValueWithoutDrop in Sui PTB semantics.
14
+ // This function transfers the created PolicyReceipt to the tx sender and returns its id (droppable).
15
+ target: `${params.packageId}::policy_registry::mint_receipt_evm_to_sender`,
16
+ arguments: [
17
+ tx.object(params.policyObjectId),
18
+ tx.object("0x6"), // Clock object (standard)
19
+ tx.pure.u64(BigInt(params.evmChainId)),
20
+ tx.pure.vector("u8", [...intentBytes]),
21
+ tx.pure.vector("u8", [...toBytes]),
22
+ ],
23
+ });
24
+ // The receipt is returned as a newly created object; callers should inspect execution effects
25
+ // and extract the created object id of type `PolicyReceipt`.
26
+ return tx;
27
+ }
28
+ function hexToBytes(hex, expectedLen) {
29
+ const raw = hex.startsWith("0x") ? hex.slice(2) : hex;
30
+ if (raw.length !== expectedLen * 2) {
31
+ throw new Error(`Expected ${expectedLen} bytes, got ${raw.length / 2}`);
32
+ }
33
+ const out = new Uint8Array(expectedLen);
34
+ for (let i = 0; i < expectedLen; i++) {
35
+ out[i] = parseInt(raw.slice(i * 2, i * 2 + 2), 16);
36
+ }
37
+ return out;
38
+ }
@@ -0,0 +1,38 @@
1
+ export type Hex = `0x${string}`;
2
+ export type EvmChainId = number;
3
+ export type KairoPolicyId = string;
4
+ export type KairoPolicyReceiptId = string;
5
+ export interface EvmIntent {
6
+ chainId: EvmChainId;
7
+ /**
8
+ * Keccak256 hash of the unsigned tx bytes (EIP-1559 serialized unsigned tx).
9
+ * 32 bytes, hex with 0x prefix.
10
+ */
11
+ intentHash: Hex;
12
+ }
13
+ export interface PolicyReceiptCommitment {
14
+ policyId: KairoPolicyId;
15
+ policyVersion: string;
16
+ evmChainId: EvmChainId;
17
+ intentHash: Hex;
18
+ /**
19
+ * Optional EVM destination address if the policy checks destination gating.
20
+ * 20 bytes, hex with 0x prefix.
21
+ */
22
+ to?: Hex;
23
+ }
24
+ /**
25
+ * Expected commitment for PolicyReceiptV2.
26
+ *
27
+ * Note:
28
+ * - `policyId` remains the on-chain *policy object id* (receipt field: `policy_object_id`).
29
+ * - `policyStableId` is the human-readable stable id (receipt field: `policy_stable_id`).
30
+ * - Selector/amount are optional and depend on the policy + intent.
31
+ */
32
+ export interface PolicyReceiptV2Commitment extends PolicyReceiptCommitment {
33
+ policyStableId?: string;
34
+ policyRoot?: Hex;
35
+ policyVersionId?: string;
36
+ evmSelector?: Hex;
37
+ erc20Amount?: Hex;
38
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@kairoguard/sdk",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "bin": {
11
+ "kairo": "dist/cli.js",
12
+ "kairo-audit": "dist/cli.js"
13
+ },
14
+ "scripts": {
15
+ "build": "tsc -p tsconfig.json",
16
+ "test": "node --test"
17
+ },
18
+ "dependencies": {
19
+ "@ika.xyz/sdk": "^0.2.7",
20
+ "@mysten/sui": "^1.44.0",
21
+ "@noble/hashes": "^1.7.2",
22
+ "viem": "^2.23.10"
23
+ },
24
+ "devDependencies": {
25
+ "@types/node": "^20.11.30",
26
+ "typescript": "^5.4.5"
27
+ }
28
+ }
29
+