@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 +11 -0
- package/dist/backend.js +3 -0
- package/dist/cli.js +58 -6
- package/dist/client.d.ts +15 -0
- package/dist/client.js +131 -48
- package/dist/ika-protocol.d.ts +8 -0
- package/dist/ika-protocol.js +12 -0
- package/dist/policy-templates.d.ts +16 -0
- package/dist/policy-templates.js +56 -0
- package/package.json +1 -1
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:
|
|
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 =
|
|
145
|
-
const
|
|
146
|
-
const
|
|
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.
|
|
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
|
|
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:
|
|
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
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
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
|
-
|
|
559
|
-
|
|
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
|
|
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 = {
|
package/dist/ika-protocol.d.ts
CHANGED
|
@@ -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;
|
package/dist/ika-protocol.js
CHANGED
|
@@ -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
|
+
}
|