@kairoguard/sdk 0.0.7 → 0.0.9

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/dist/backend.d.ts CHANGED
@@ -232,6 +232,16 @@ export interface PolicyDetailsResponse {
232
232
  policy?: Record<string, unknown>;
233
233
  error?: string;
234
234
  }
235
+ export interface ReaffirmPolicyBindingParams {
236
+ bindingObjectId: string;
237
+ registryObjectId?: string;
238
+ }
239
+ export interface ReaffirmPolicyBindingResponse {
240
+ success: boolean;
241
+ digest: string;
242
+ activeVersionObjectId?: string;
243
+ error?: string;
244
+ }
235
245
  export interface DWalletFullResponse {
236
246
  success: boolean;
237
247
  dWallet?: unknown;
@@ -273,6 +283,7 @@ export declare class BackendClient {
273
283
  getGovernance(governanceId: string): Promise<GovernanceGetResponse>;
274
284
  getGovernanceProposal(proposalId: string): Promise<GovernanceProposalGetResponse>;
275
285
  getPolicy(policyObjectId: string): Promise<PolicyDetailsResponse>;
286
+ reaffirmPolicyBinding(params: ReaffirmPolicyBindingParams): Promise<ReaffirmPolicyBindingResponse>;
276
287
  getDWalletFull(dWalletId: string): Promise<DWalletFullResponse>;
277
288
  getSuiObject(objectId: string): Promise<SuiObjectResponse>;
278
289
  policySign(params: Record<string, unknown>): Promise<Record<string, unknown>>;
package/dist/backend.js CHANGED
@@ -89,6 +89,9 @@ export class BackendClient {
89
89
  async getPolicy(policyObjectId) {
90
90
  return this.request("GET", `/api/policies/${policyObjectId}`);
91
91
  }
92
+ async reaffirmPolicyBinding(params) {
93
+ return this.request("POST", "/api/policy/binding/reaffirm", params);
94
+ }
92
95
  async getDWalletFull(dWalletId) {
93
96
  return this.request("GET", `/api/dwallet/full/${dWalletId}`);
94
97
  }
package/dist/cli.js CHANGED
@@ -2,9 +2,12 @@
2
2
  import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import { homedir } from "node:os";
5
+ import { createInterface } from "node:readline/promises";
6
+ import { stdin as input, stdout as output } from "node:process";
5
7
  import { verifyAuditBundle } from "./auditBundle.js";
6
8
  import { BackendClient, DEFAULT_BACKEND_URL } from "./backend.js";
7
9
  import { KairoClient } from "./client.js";
10
+ import { POLICY_TEMPLATE_PRESETS, buildPolicyTemplatePayload } from "./policy-templates.js";
8
11
  import { SKILL_MD, API_REFERENCE_MD, SDK_REFERENCE_MD } from "./skill-templates.js";
9
12
  const CONFIG_DIR = join(homedir(), ".kairo");
10
13
  const CONFIG_PATH = join(CONFIG_DIR, "config.json");
@@ -39,6 +42,9 @@ function flag(args, name) {
39
42
  return undefined;
40
43
  return args[idx + 1];
41
44
  }
45
+ function hasFlag(args, name) {
46
+ return args.includes(name);
47
+ }
42
48
  function requireFlag(args, name, label) {
43
49
  const v = flag(args, name);
44
50
  if (!v) {
@@ -47,6 +53,24 @@ function requireFlag(args, name, label) {
47
53
  }
48
54
  return v;
49
55
  }
56
+ async function promptPolicyTemplateId() {
57
+ const rl = createInterface({ input, output });
58
+ try {
59
+ console.log("Choose a default policy template:");
60
+ console.log(` 1) ${POLICY_TEMPLATE_PRESETS["tpl-1"].label}`);
61
+ console.log(` 2) ${POLICY_TEMPLATE_PRESETS["tpl-2"].label}`);
62
+ console.log(` 3) ${POLICY_TEMPLATE_PRESETS["tpl-3"].label}`);
63
+ const answer = (await rl.question("Template [1/2/3] (default 2): ")).trim();
64
+ if (answer === "1")
65
+ return "tpl-1";
66
+ if (answer === "3")
67
+ return "tpl-3";
68
+ return "tpl-2";
69
+ }
70
+ finally {
71
+ rl.close();
72
+ }
73
+ }
50
74
  // ── Commands ────────────────────────────────────────────────────────────────
51
75
  async function cmdInit(args) {
52
76
  const apiKey = args[0];
@@ -88,17 +112,41 @@ async function cmdWalletCreate(args) {
88
112
  }
89
113
  const policyId = flag(args, "--policy-id");
90
114
  const stableId = flag(args, "--stable-id");
115
+ const autoProvision = hasFlag(args, "--auto-provision");
91
116
  const cfg = requireConfig();
117
+ let resolvedPolicyId = policyId;
118
+ if (autoProvision && !resolvedPolicyId) {
119
+ const templateId = await promptPolicyTemplateId();
120
+ const templatePayload = buildPolicyTemplatePayload(templateId);
121
+ const stable = stableId ?? `agent-policy-${Date.now()}`;
122
+ const client = getClient();
123
+ const created = await client.createPolicyV4({
124
+ stableId: stable,
125
+ version: "1.0.0",
126
+ allowNamespaces: templatePayload.allowNamespaces,
127
+ rules: templatePayload.rules,
128
+ });
129
+ if (!created.success || !created.policyObjectId?.startsWith("0x")) {
130
+ throw new Error(created.error ?? "Failed to create default policy from template");
131
+ }
132
+ await client.registerPolicyVersionFromPolicy({ policyObjectId: created.policyObjectId });
133
+ resolvedPolicyId = created.policyObjectId;
134
+ console.log(`Created + registered policy ${resolvedPolicyId} using ${templatePayload.template.id}.`);
135
+ }
92
136
  const kairo = new KairoClient({
93
137
  apiKey: cfg.apiKey,
94
138
  backendUrl: cfg.backendUrl,
95
139
  });
96
140
  const wallet = await kairo.createWallet({
97
141
  curve: curveRaw,
98
- policyObjectId: policyId,
142
+ policyObjectId: resolvedPolicyId,
99
143
  stableId,
100
144
  });
101
145
  console.log(JSON.stringify(wallet, null, 2));
146
+ if (!resolvedPolicyId) {
147
+ console.log("Wallet created but not provisioned. To show it in dashboard policies, run: " +
148
+ `kairo vault-provision --wallet-id ${wallet.walletId} --policy-id <policyObjectId> [--stable-id <id>]`);
149
+ }
102
150
  }
103
151
  async function cmdRegister(args) {
104
152
  const label = requireFlag(args, "--label", "name");
@@ -141,9 +189,13 @@ async function cmdVaultStatus(args) {
141
189
  async function cmdVaultProvision(args) {
142
190
  const walletId = requireFlag(args, "--wallet-id", "dwalletId");
143
191
  const policyId = requireFlag(args, "--policy-id", "objectId");
144
- const stableId = requireFlag(args, "--stable-id", "id");
145
- const client = getClient();
146
- const res = await client.provision({ dwalletObjectId: walletId, policyObjectId: policyId, stableId });
192
+ const stableId = flag(args, "--stable-id");
193
+ const cfg = requireConfig();
194
+ const kairo = new KairoClient({
195
+ apiKey: cfg.apiKey,
196
+ backendUrl: cfg.backendUrl,
197
+ });
198
+ const res = await kairo.provision(walletId, policyId, stableId);
147
199
  console.log(JSON.stringify(res, null, 2));
148
200
  }
149
201
  async function cmdReceiptMint(args) {
@@ -204,14 +256,14 @@ Setup:
204
256
 
205
257
  Wallet & Policy:
206
258
  health Server health check
207
- wallet-create [--curve secp256k1|ed25519] [--policy-id <id>] [--stable-id <id>]
259
+ wallet-create [--curve secp256k1|ed25519] [--policy-id <id>] [--stable-id <id>] [--auto-provision]
208
260
  Create a new dWallet via SDK DKG flow
209
261
  register --label <name> Register new API key
210
262
  policy-create --stable-id <id> --allow <addrs> Create policy
211
263
  policy-register --policy-id <id> Register policy version
212
264
  policy-details --policy-id <id> Get policy details
213
265
  vault-status --wallet-id <id> Check vault registration
214
- vault-provision --wallet-id <id> --policy-id <id> --stable-id <id>
266
+ vault-provision --wallet-id <id> --policy-id <id> [--stable-id <id>]
215
267
  receipt-mint --policy-id <id> --binding-id <id> --destination <hex> --intent-hash <hex>
216
268
 
217
269
  Utility:
package/dist/client.d.ts CHANGED
@@ -153,6 +153,18 @@ export declare class KairoClient {
153
153
  listWallets(): WalletInfo[];
154
154
  /** Get a wallet from local key store by ID. */
155
155
  getWallet(walletId: string): WalletInfo | null;
156
+ /**
157
+ * Provision an existing local wallet into the policy vault.
158
+ * Persists binding/policy metadata to local keystore.
159
+ */
160
+ provision(walletId: string, policyObjectId: string, stableId?: string): Promise<{
161
+ bindingObjectId: string;
162
+ digest: string;
163
+ }>;
164
+ reaffirmBinding(walletId: string): Promise<{
165
+ digest: string;
166
+ activeVersionObjectId?: string;
167
+ }>;
156
168
  /**
157
169
  * Governance-first policy update: creates a new policy + version, then proposes
158
170
  * a change for approvers. This method does NOT execute/reaffirm directly.
@@ -182,6 +194,9 @@ export declare class KairoClient {
182
194
  private pollPresignStatus;
183
195
  private pollSignStatus;
184
196
  private mintPolicyReceipt;
197
+ private resolvePolicyVersion;
198
+ private isReaffirmRequiredError;
199
+ private requestSignWithReaffirmRetry;
185
200
  private computeUserSignMessageWithExtensionFallback;
186
201
  private rebuildSigningMaterialFromChain;
187
202
  private resolveEvmRpcUrl;
package/dist/client.js CHANGED
@@ -10,7 +10,7 @@
10
10
  */
11
11
  import { BackendClient, } from "./backend.js";
12
12
  import { KeyStore } from "./keystore.js";
13
- import { Curve, fetchProtocolParams, deriveEncryptionKeys, generateSeed, generateSessionIdentifier, runDKG, computeUserOutputSignature, fetchDWallet, } from "./ika-protocol.js";
13
+ import { Curve, fetchProtocolParams, deriveEncryptionKeys, generateSeed, generateSessionIdentifier, runDKG, computeUserOutputSignature, fetchDWallet, waitForDWalletState, } from "./ika-protocol.js";
14
14
  import { Hash, SignatureAlgorithm, createUserSignMessageWithPublicOutput } from "@ika.xyz/sdk";
15
15
  import { computeEvmIntentFromUnsignedTxBytes } from "./evmIntent.js";
16
16
  import { keccak256, recoverTransactionAddress, serializeTransaction, } from "viem";
@@ -64,6 +64,8 @@ const PRESIGN_POLL_INTERVAL_MS = 2_000;
64
64
  const PRESIGN_POLL_TIMEOUT_MS = 120_000;
65
65
  const SIGN_POLL_INTERVAL_MS = 2_000;
66
66
  const SIGN_POLL_TIMEOUT_MS = 180_000;
67
+ const ACTIVATION_POLL_INTERVAL_MS = 3_000;
68
+ const ACTIVATION_POLL_TIMEOUT_MS = 90_000;
67
69
  function curveToNumber(curve) {
68
70
  return curve === "ed25519" ? 2 : 0;
69
71
  }
@@ -149,21 +151,7 @@ export class KairoClient {
149
151
  const walletId = dkgResult.dWalletObjectId;
150
152
  const address = (curve === "ed25519" ? dkgResult.solanaAddress : dkgResult.ethereumAddress) ?? "";
151
153
  const encryptedShareId = dkgResult.encryptedUserSecretKeyShareId ?? "";
152
- // 8. Activate the dWallet (sign to accept encrypted key share)
153
- if (encryptedShareId) {
154
- await this.activateWallet(walletId, encryptedShareId, encryptionKeys, dkgOutputs.userPublicOutput);
155
- }
156
- // 9. Provision into vault (binding + registration) if policy is provided
157
- let bindingObjectId;
158
- if (opts?.policyObjectId) {
159
- const provisionResult = await this.backend.provision({
160
- dwalletObjectId: walletId,
161
- policyObjectId: opts.policyObjectId,
162
- stableId: opts.stableId ?? `agent-wallet-${walletId.slice(0, 8)}`,
163
- });
164
- bindingObjectId = provisionResult.bindingObjectId ?? undefined;
165
- }
166
- // 10. Save secret share locally
154
+ // 8. Save secret share locally BEFORE activation so it is never lost
167
155
  const record = {
168
156
  walletId,
169
157
  dWalletCapId: dkgResult.dWalletCapObjectId,
@@ -173,11 +161,21 @@ export class KairoClient {
173
161
  userSecretKeyShare: dkgOutputs.userSecretKeyShare,
174
162
  userPublicOutput: dkgOutputs.userPublicOutput,
175
163
  encryptedUserSecretKeyShareId: encryptedShareId,
176
- bindingObjectId,
164
+ bindingObjectId: undefined,
177
165
  policyObjectId: opts?.policyObjectId,
178
166
  createdAt: Date.now(),
179
167
  };
180
168
  this.store.save(record);
169
+ // 9. Activate the dWallet (sign to accept encrypted key share)
170
+ if (encryptedShareId) {
171
+ await this.activateWallet(walletId, encryptedShareId, encryptionKeys, dkgOutputs.userPublicOutput);
172
+ }
173
+ // 10. Provision into vault (binding + registration) if policy is provided
174
+ let bindingObjectId;
175
+ if (opts?.policyObjectId) {
176
+ const provisionResult = await this.provision(walletId, opts.policyObjectId, opts.stableId);
177
+ bindingObjectId = provisionResult.bindingObjectId;
178
+ }
181
179
  return {
182
180
  walletId,
183
181
  address,
@@ -209,6 +207,44 @@ export class KairoClient {
209
207
  createdAt: r.createdAt,
210
208
  };
211
209
  }
210
+ /**
211
+ * Provision an existing local wallet into the policy vault.
212
+ * Persists binding/policy metadata to local keystore.
213
+ */
214
+ async provision(walletId, policyObjectId, stableId) {
215
+ const wallet = this.requireWalletRecord(walletId);
216
+ if (!policyObjectId?.startsWith("0x")) {
217
+ throw new Error("policyObjectId must be a valid 0x object id");
218
+ }
219
+ const result = await this.backend.provision({
220
+ dwalletObjectId: walletId,
221
+ policyObjectId,
222
+ stableId: stableId ?? `agent-wallet-${walletId.slice(0, 8)}`,
223
+ });
224
+ const bindingObjectId = String(result.bindingObjectId ?? "");
225
+ if (!bindingObjectId.startsWith("0x")) {
226
+ throw new Error("Provision succeeded but no bindingObjectId was returned");
227
+ }
228
+ this.store.save({
229
+ ...wallet,
230
+ bindingObjectId,
231
+ policyObjectId,
232
+ });
233
+ return { bindingObjectId, digest: result.digest };
234
+ }
235
+ async reaffirmBinding(walletId) {
236
+ const wallet = this.requireWalletRecord(walletId);
237
+ if (!wallet.bindingObjectId?.startsWith("0x")) {
238
+ throw new Error("Wallet is missing bindingObjectId. Provision the wallet before reaffirming.");
239
+ }
240
+ const result = await this.backend.reaffirmPolicyBinding({
241
+ bindingObjectId: wallet.bindingObjectId,
242
+ });
243
+ return {
244
+ digest: result.digest,
245
+ activeVersionObjectId: result.activeVersionObjectId,
246
+ };
247
+ }
212
248
  /**
213
249
  * Governance-first policy update: creates a new policy + version, then proposes
214
250
  * a change for approvers. This method does NOT execute/reaffirm directly.
@@ -400,7 +436,8 @@ export class KairoClient {
400
436
  if (!dWalletCapId) {
401
437
  throw new Error("Wallet record is missing dWalletCapId. Recreate/provision this wallet before signing.");
402
438
  }
403
- const req = await this.backend.requestSign({
439
+ const resolvedPolicyVersion = await this.resolvePolicyVersion(wallet, opts?.policyVersion);
440
+ const req = await this.requestSignWithReaffirmRetry(wallet, {
404
441
  dWalletId: wallet.walletId,
405
442
  dWalletCapId,
406
443
  encryptedUserSecretKeyShareId: wallet.encryptedUserSecretKeyShareId ?? "",
@@ -411,7 +448,7 @@ export class KairoClient {
411
448
  policyReceiptId,
412
449
  policyBindingObjectId: wallet.bindingObjectId,
413
450
  policyObjectId: wallet.policyObjectId,
414
- policyVersion: opts?.policyVersion ?? "1.0.0",
451
+ policyVersion: resolvedPolicyVersion,
415
452
  ethTx: opts?.ethTx,
416
453
  });
417
454
  if (!req.success) {
@@ -534,38 +571,26 @@ export class KairoClient {
534
571
  return BigInt(balanceHex);
535
572
  }
536
573
  async activateWallet(walletId, encryptedShareId, encryptionKeys, userPublicOutput) {
537
- // Wait a moment for the dWallet state to propagate
538
- await new Promise((r) => setTimeout(r, 2000));
539
- const rpcUrlForActivation = await this.resolveSuiRpcUrl();
540
- const dWallet = await withRetry(() => fetchDWallet(rpcUrlForActivation, this.network, walletId), { label: "fetchDWallet(activate)" });
541
- // Check dWallet state - skip activation if already active
542
- const state = dWallet?.state;
543
- const isActive = Boolean(state?.Active) || state?.$kind === "Active";
544
- if (isActive) {
545
- // Already activated, nothing to do
546
- return;
547
- }
548
- const isAwaitingSignature = Boolean(state?.AwaitingKeyHolderSignature) ||
549
- state?.$kind === "AwaitingKeyHolderSignature";
550
- if (!isAwaitingSignature) {
551
- // Unknown state - wait and retry once
552
- await new Promise((r) => setTimeout(r, 3000));
553
- const dWallet2 = await withRetry(() => fetchDWallet(rpcUrlForActivation, this.network, walletId), { label: "fetchDWallet(activate-recheck)" });
554
- const state2 = dWallet2?.state;
555
- const isActive2 = Boolean(state2?.Active) || state2?.$kind === "Active";
556
- if (isActive2)
574
+ const rpcUrl = await this.resolveSuiRpcUrl();
575
+ // After DKG the on-chain dWallet goes through AwaitingNetworkDKGVerification
576
+ // (30-60s+ on testnet). Use the Ika SDK's native polling to wait for the
577
+ // correct state before attempting activation.
578
+ let dWallet;
579
+ try {
580
+ dWallet = await waitForDWalletState(rpcUrl, this.network, walletId, "AwaitingKeyHolderSignature", { timeout: ACTIVATION_POLL_TIMEOUT_MS, interval: ACTIVATION_POLL_INTERVAL_MS });
581
+ }
582
+ catch {
583
+ // May already be Active (idempotent retry after earlier partial success)
584
+ const current = await withRetry(() => fetchDWallet(rpcUrl, this.network, walletId), { label: "fetchDWallet(activate-fallback)" });
585
+ const kind = String(current?.state?.$kind ?? "");
586
+ if (kind === "Active")
557
587
  return;
558
- const isAwaiting2 = Boolean(state2?.AwaitingKeyHolderSignature) ||
559
- state2?.$kind === "AwaitingKeyHolderSignature";
560
- if (!isAwaiting2) {
561
- const stateKind = state2?.$kind ?? Object.keys(state2 ?? {})[0] ?? "Unknown";
562
- throw new Error(`dWallet is not ready for activation (state=${stateKind}). ` +
563
- `This can happen if the DKG is still processing. Wait a few seconds and retry.`);
564
- }
588
+ throw new Error(`dWallet activation timed out after ${ACTIVATION_POLL_TIMEOUT_MS / 1000}s ` +
589
+ `(state=${kind || "Unknown"}). The wallet record is saved locally — retry later.`);
565
590
  }
566
591
  const signature = await computeUserOutputSignature({
567
592
  encryptionKeys,
568
- dWallet,
593
+ dWallet: dWallet,
569
594
  userPublicOutput: new Uint8Array(userPublicOutput),
570
595
  });
571
596
  await this.backend.activateDWallet({
@@ -636,7 +661,7 @@ export class KairoClient {
636
661
  if (!wallet.policyObjectId || !wallet.bindingObjectId) {
637
662
  throw new Error("Wallet is missing policy binding metadata. Ensure it is provisioned with policyObjectId and bindingObjectId.");
638
663
  }
639
- const response = await this.backend.mintReceipt({
664
+ const mintOnce = () => this.backend.mintReceipt({
640
665
  policyObjectId: wallet.policyObjectId,
641
666
  bindingObjectId: wallet.bindingObjectId,
642
667
  namespace: ctx.namespace,
@@ -647,6 +672,12 @@ export class KairoClient {
647
672
  nativeValueHex: ctx.nativeValue.toString(16).padStart(64, "0"),
648
673
  contextDataHex: ctx.contextDataHex ? stripHexPrefix(ctx.contextDataHex) : undefined,
649
674
  });
675
+ let response = await mintOnce();
676
+ const initialError = String(response?.error ?? "");
677
+ if (response.success === false && this.isReaffirmRequiredError(initialError)) {
678
+ await this.reaffirmBinding(wallet.walletId);
679
+ response = await mintOnce();
680
+ }
650
681
  if (response.success === false) {
651
682
  throw new Error(String(response.error ?? "Failed to mint policy receipt"));
652
683
  }
@@ -659,6 +690,38 @@ export class KairoClient {
659
690
  }
660
691
  return receiptId;
661
692
  }
693
+ async resolvePolicyVersion(wallet, override) {
694
+ const explicit = String(override ?? "").trim();
695
+ if (explicit)
696
+ return explicit;
697
+ if (!wallet.policyObjectId?.startsWith("0x")) {
698
+ throw new Error("Wallet is missing policyObjectId. Provision the wallet before signing.");
699
+ }
700
+ const response = await this.backend.getPolicy(wallet.policyObjectId);
701
+ if (!response.success || !response.policy) {
702
+ throw new Error(response.error ?? "Failed to resolve policy details for signing");
703
+ }
704
+ const version = decodeMoveString(response.policy.version).trim();
705
+ if (!version) {
706
+ throw new Error("Policy version is missing on-chain. Pass policyVersion explicitly.");
707
+ }
708
+ return version;
709
+ }
710
+ isReaffirmRequiredError(err) {
711
+ const message = err instanceof Error ? err.message : String(err);
712
+ return /requires confirmation/i.test(message) || /reaffirm/i.test(message);
713
+ }
714
+ async requestSignWithReaffirmRetry(wallet, payload) {
715
+ try {
716
+ return await this.backend.requestSign(payload);
717
+ }
718
+ catch (error) {
719
+ if (!this.isReaffirmRequiredError(error))
720
+ throw error;
721
+ await this.reaffirmBinding(wallet.walletId);
722
+ return this.backend.requestSign(payload);
723
+ }
724
+ }
662
725
  async computeUserSignMessageWithExtensionFallback(wallet, protocolParams, presignBytes, messageBytes) {
663
726
  try {
664
727
  return await createUserSignMessageWithPublicOutput(protocolParams, new Uint8Array(wallet.userPublicOutput), new Uint8Array(wallet.userSecretKeyShare), presignBytes, messageBytes, Hash.KECCAK256, SignatureAlgorithm.ECDSASecp256k1, Curve.SECP256K1);
@@ -742,6 +805,26 @@ function toBigInt(value) {
742
805
  return BigInt(value);
743
806
  return value.startsWith("0x") ? BigInt(value) : BigInt(value);
744
807
  }
808
+ function decodeMoveString(value) {
809
+ if (typeof value === "string")
810
+ return value;
811
+ if (Array.isArray(value) && value.every((x) => Number.isInteger(x) && x >= 0 && x <= 255)) {
812
+ try {
813
+ return new TextDecoder().decode(Uint8Array.from(value));
814
+ }
815
+ catch {
816
+ return "";
817
+ }
818
+ }
819
+ if (value && typeof value === "object") {
820
+ const obj = value;
821
+ return (decodeMoveString(obj.bytes) ||
822
+ decodeMoveString(obj.data) ||
823
+ decodeMoveString(obj.value) ||
824
+ decodeMoveString(obj.fields));
825
+ }
826
+ return "";
827
+ }
745
828
  function buildNormalizedEncryptedShare(fields) {
746
829
  const { state } = normalizeMoveEnumState(fields.state);
747
830
  const candidate = {
@@ -54,6 +54,14 @@ export declare function computeUserOutputSignature(params: {
54
54
  * Fetch the dWallet object from Ika network for activation.
55
55
  */
56
56
  export declare function fetchDWallet(suiRpcUrl: string, network: "testnet" | "mainnet", dwalletId: string): Promise<any>;
57
+ /**
58
+ * Poll until the dWallet reaches `targetState` using the Ika SDK's native
59
+ * getDWalletInParticularState, which handles reindexing/lag gracefully.
60
+ */
61
+ export declare function waitForDWalletState(suiRpcUrl: string, network: "testnet" | "mainnet", dwalletId: string, targetState: "AwaitingKeyHolderSignature" | "Active", opts?: {
62
+ timeout?: number;
63
+ interval?: number;
64
+ }): Promise<any>;
57
65
  export interface ComputeUserSignMessageParams {
58
66
  protocolPublicParameters: Uint8Array;
59
67
  userPublicOutput: Uint8Array;
@@ -98,6 +98,18 @@ export async function fetchDWallet(suiRpcUrl, network, dwalletId) {
98
98
  await ready;
99
99
  return ikaClient.getDWallet(dwalletId);
100
100
  }
101
+ /**
102
+ * Poll until the dWallet reaches `targetState` using the Ika SDK's native
103
+ * getDWalletInParticularState, which handles reindexing/lag gracefully.
104
+ */
105
+ export async function waitForDWalletState(suiRpcUrl, network, dwalletId, targetState, opts) {
106
+ const { ikaClient, ready } = getOrCreateIkaClient(network, suiRpcUrl);
107
+ await ready;
108
+ return ikaClient.getDWalletInParticularState(dwalletId, targetState, {
109
+ timeout: opts?.timeout ?? 90_000,
110
+ interval: opts?.interval ?? 3_000,
111
+ });
112
+ }
101
113
  /**
102
114
  * Build the user-side sign message used by IKA MPC signing.
103
115
  */
@@ -0,0 +1,16 @@
1
+ export interface PolicyTemplatePreset {
2
+ id: "tpl-1" | "tpl-2" | "tpl-3";
3
+ label: string;
4
+ singleTxUsd: number;
5
+ dailyLimitUsd: number;
6
+ }
7
+ export declare const POLICY_TEMPLATE_PRESETS: Record<PolicyTemplatePreset["id"], PolicyTemplatePreset>;
8
+ export declare function buildPolicyTemplatePayload(templateId: string): {
9
+ template: PolicyTemplatePreset;
10
+ allowNamespaces: number[];
11
+ rules: Array<{
12
+ ruleType: number;
13
+ namespace?: number;
14
+ params: string;
15
+ }>;
16
+ };
@@ -0,0 +1,56 @@
1
+ export const POLICY_TEMPLATE_PRESETS = {
2
+ "tpl-1": {
3
+ id: "tpl-1",
4
+ label: "Conservative ($200/tx, $1k/day)",
5
+ singleTxUsd: 200,
6
+ dailyLimitUsd: 1_000,
7
+ },
8
+ "tpl-2": {
9
+ id: "tpl-2",
10
+ label: "Standard ($2k/tx, $10k/day)",
11
+ singleTxUsd: 2_000,
12
+ dailyLimitUsd: 10_000,
13
+ },
14
+ "tpl-3": {
15
+ id: "tpl-3",
16
+ label: "High-limit ($10k/tx, $50k/day)",
17
+ singleTxUsd: 10_000,
18
+ dailyLimitUsd: 50_000,
19
+ },
20
+ };
21
+ function stripHexPrefix(value) {
22
+ return value.startsWith("0x") ? value.slice(2) : value;
23
+ }
24
+ function encodeU256(value) {
25
+ const big = typeof value === "bigint" ? value : BigInt(value);
26
+ return `0x${big.toString(16).padStart(64, "0")}`;
27
+ }
28
+ function encodeMaxNativeRule(maxNativeBaseUnits) {
29
+ return encodeU256(maxNativeBaseUnits);
30
+ }
31
+ function encodeDailyPeriodLimitRule(maxDailyBaseUnits) {
32
+ const periodTypeDaily = 1;
33
+ return `0x${periodTypeDaily.toString(16).padStart(2, "0")}${stripHexPrefix(encodeU256(maxDailyBaseUnits))}`;
34
+ }
35
+ export function buildPolicyTemplatePayload(templateId) {
36
+ const template = POLICY_TEMPLATE_PRESETS[templateId] ?? POLICY_TEMPLATE_PRESETS["tpl-2"];
37
+ const maxNativeBaseUnits = BigInt(Math.round(template.singleTxUsd * 1_000_000));
38
+ const dailyLimitBaseUnits = BigInt(Math.round(template.dailyLimitUsd * 1_000_000));
39
+ return {
40
+ template,
41
+ // Default to EVM namespace so newly onboarded users can sign EVM txs immediately.
42
+ allowNamespaces: [1],
43
+ rules: [
44
+ {
45
+ ruleType: 1,
46
+ namespace: 0,
47
+ params: encodeMaxNativeRule(maxNativeBaseUnits),
48
+ },
49
+ {
50
+ ruleType: 10,
51
+ namespace: 0,
52
+ params: encodeDailyPeriodLimitRule(dailyLimitBaseUnits),
53
+ },
54
+ ],
55
+ };
56
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kairoguard/sdk",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",