@kairoguard/sdk 0.0.6 → 0.0.8

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/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";
@@ -22,7 +22,10 @@ async function testRpcEndpoint(url) {
22
22
  headers: { "Content-Type": "application/json" },
23
23
  body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "suix_getLatestSuiSystemState", params: [] }),
24
24
  });
25
- return res.ok;
25
+ if (!res.ok)
26
+ return false;
27
+ const json = await res.json();
28
+ return json.jsonrpc === "2.0" && "result" in json;
26
29
  }
27
30
  catch {
28
31
  return false;
@@ -61,6 +64,8 @@ const PRESIGN_POLL_INTERVAL_MS = 2_000;
61
64
  const PRESIGN_POLL_TIMEOUT_MS = 120_000;
62
65
  const SIGN_POLL_INTERVAL_MS = 2_000;
63
66
  const SIGN_POLL_TIMEOUT_MS = 180_000;
67
+ const ACTIVATION_POLL_INTERVAL_MS = 3_000;
68
+ const ACTIVATION_POLL_TIMEOUT_MS = 90_000;
64
69
  function curveToNumber(curve) {
65
70
  return curve === "ed25519" ? 2 : 0;
66
71
  }
@@ -146,21 +151,7 @@ export class KairoClient {
146
151
  const walletId = dkgResult.dWalletObjectId;
147
152
  const address = (curve === "ed25519" ? dkgResult.solanaAddress : dkgResult.ethereumAddress) ?? "";
148
153
  const encryptedShareId = dkgResult.encryptedUserSecretKeyShareId ?? "";
149
- // 8. Activate the dWallet (sign to accept encrypted key share)
150
- if (encryptedShareId) {
151
- await this.activateWallet(walletId, encryptedShareId, encryptionKeys, dkgOutputs.userPublicOutput);
152
- }
153
- // 9. Provision into vault (binding + registration) if policy is provided
154
- let bindingObjectId;
155
- if (opts?.policyObjectId) {
156
- const provisionResult = await this.backend.provision({
157
- dwalletObjectId: walletId,
158
- policyObjectId: opts.policyObjectId,
159
- stableId: opts.stableId ?? `agent-wallet-${walletId.slice(0, 8)}`,
160
- });
161
- bindingObjectId = provisionResult.bindingObjectId ?? undefined;
162
- }
163
- // 10. Save secret share locally
154
+ // 8. Save secret share locally BEFORE activation so it is never lost
164
155
  const record = {
165
156
  walletId,
166
157
  dWalletCapId: dkgResult.dWalletCapObjectId,
@@ -170,11 +161,26 @@ export class KairoClient {
170
161
  userSecretKeyShare: dkgOutputs.userSecretKeyShare,
171
162
  userPublicOutput: dkgOutputs.userPublicOutput,
172
163
  encryptedUserSecretKeyShareId: encryptedShareId,
173
- bindingObjectId,
164
+ bindingObjectId: undefined,
174
165
  policyObjectId: opts?.policyObjectId,
175
166
  createdAt: Date.now(),
176
167
  };
177
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.backend.provision({
177
+ dwalletObjectId: walletId,
178
+ policyObjectId: opts.policyObjectId,
179
+ stableId: opts.stableId ?? `agent-wallet-${walletId.slice(0, 8)}`,
180
+ });
181
+ bindingObjectId = provisionResult.bindingObjectId ?? undefined;
182
+ this.store.save({ ...record, bindingObjectId });
183
+ }
178
184
  return {
179
185
  walletId,
180
186
  address,
@@ -531,38 +537,26 @@ export class KairoClient {
531
537
  return BigInt(balanceHex);
532
538
  }
533
539
  async activateWallet(walletId, encryptedShareId, encryptionKeys, userPublicOutput) {
534
- // Wait a moment for the dWallet state to propagate
535
- await new Promise((r) => setTimeout(r, 2000));
536
- const rpcUrlForActivation = await this.resolveSuiRpcUrl();
537
- const dWallet = await withRetry(() => fetchDWallet(rpcUrlForActivation, this.network, walletId), { label: "fetchDWallet(activate)" });
538
- // Check dWallet state - skip activation if already active
539
- const state = dWallet?.state;
540
- const isActive = Boolean(state?.Active) || state?.$kind === "Active";
541
- if (isActive) {
542
- // Already activated, nothing to do
543
- return;
544
- }
545
- const isAwaitingSignature = Boolean(state?.AwaitingKeyHolderSignature) ||
546
- state?.$kind === "AwaitingKeyHolderSignature";
547
- if (!isAwaitingSignature) {
548
- // Unknown state - wait and retry once
549
- await new Promise((r) => setTimeout(r, 3000));
550
- const dWallet2 = await withRetry(() => fetchDWallet(rpcUrlForActivation, this.network, walletId), { label: "fetchDWallet(activate-recheck)" });
551
- const state2 = dWallet2?.state;
552
- const isActive2 = Boolean(state2?.Active) || state2?.$kind === "Active";
553
- if (isActive2)
540
+ const rpcUrl = await this.resolveSuiRpcUrl();
541
+ // After DKG the on-chain dWallet goes through AwaitingNetworkDKGVerification
542
+ // (30-60s+ on testnet). Use the Ika SDK's native polling to wait for the
543
+ // correct state before attempting activation.
544
+ let dWallet;
545
+ try {
546
+ dWallet = await waitForDWalletState(rpcUrl, this.network, walletId, "AwaitingKeyHolderSignature", { timeout: ACTIVATION_POLL_TIMEOUT_MS, interval: ACTIVATION_POLL_INTERVAL_MS });
547
+ }
548
+ catch {
549
+ // May already be Active (idempotent retry after earlier partial success)
550
+ const current = await withRetry(() => fetchDWallet(rpcUrl, this.network, walletId), { label: "fetchDWallet(activate-fallback)" });
551
+ const kind = String(current?.state?.$kind ?? "");
552
+ if (kind === "Active")
554
553
  return;
555
- const isAwaiting2 = Boolean(state2?.AwaitingKeyHolderSignature) ||
556
- state2?.$kind === "AwaitingKeyHolderSignature";
557
- if (!isAwaiting2) {
558
- const stateKind = state2?.$kind ?? Object.keys(state2 ?? {})[0] ?? "Unknown";
559
- throw new Error(`dWallet is not ready for activation (state=${stateKind}). ` +
560
- `This can happen if the DKG is still processing. Wait a few seconds and retry.`);
561
- }
554
+ throw new Error(`dWallet activation timed out after ${ACTIVATION_POLL_TIMEOUT_MS / 1000}s ` +
555
+ `(state=${kind || "Unknown"}). The wallet record is saved locally — retry later.`);
562
556
  }
563
557
  const signature = await computeUserOutputSignature({
564
558
  encryptionKeys,
565
- dWallet,
559
+ dWallet: dWallet,
566
560
  userPublicOutput: new Uint8Array(userPublicOutput),
567
561
  });
568
562
  await this.backend.activateDWallet({
@@ -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
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kairoguard/sdk",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",