@kairoguard/sdk 0.0.14 → 0.0.16

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
@@ -56,6 +56,16 @@ export interface ProvisionResponse {
56
56
  dwalletObjectId: string;
57
57
  walletState: string;
58
58
  }
59
+ export interface MintReceiptResponse {
60
+ success: boolean;
61
+ receiptId?: string;
62
+ receiptObjectId?: string;
63
+ allowed?: boolean;
64
+ denialReason?: number;
65
+ denialReasonName?: string;
66
+ digest?: string;
67
+ error?: string;
68
+ }
59
69
  export interface RegisterKeyRequest {
60
70
  label: string;
61
71
  email?: string;
@@ -307,7 +317,7 @@ export declare class BackendClient {
307
317
  submitDKG(data: DKGSubmitRequest): Promise<DKGSubmitResponse>;
308
318
  getDKGStatus(requestId: string): Promise<DKGStatusResponse>;
309
319
  provision(params: ProvisionRequest): Promise<ProvisionResponse>;
310
- mintReceipt(params: Record<string, unknown>): Promise<Record<string, unknown>>;
320
+ mintReceipt(params: Record<string, unknown>): Promise<MintReceiptResponse>;
311
321
  requestPresign(params: PresignRequestParams): Promise<PresignRequestResponse>;
312
322
  getPresignStatus(requestId: string): Promise<PresignStatusResponse>;
313
323
  requestSign(params: SignRequestParams): Promise<SignRequestResponse>;
package/dist/client.d.ts CHANGED
@@ -50,6 +50,11 @@ export interface SignResult {
50
50
  presignId: string;
51
51
  signatureHex: Hex;
52
52
  }
53
+ export declare class PolicyDeniedError extends Error {
54
+ readonly denialReason: number;
55
+ readonly denialReasonName: string;
56
+ constructor(denialReason: number, denialReasonName?: string);
57
+ }
53
58
  export interface SignEvmParams {
54
59
  walletId: string;
55
60
  to: Hex;
package/dist/client.js CHANGED
@@ -9,12 +9,23 @@
9
9
  * // { walletId: "0xabc...", address: "0x742d...", curve: "secp256k1" }
10
10
  */
11
11
  import { BackendClient, } from "./backend.js";
12
+ import { getDenialReasonName } from "./denialReasons.js";
12
13
  import { KeyStore } from "./keystore.js";
13
14
  import { Curve, fetchProtocolParams, deriveEncryptionKeys, generateSeed, generateSessionIdentifier, runDKG, computeUserOutputSignature, fetchDWallet, waitForDWalletState, } from "./ika-protocol.js";
14
15
  import { Hash, SignatureAlgorithm, createUserSignMessageWithPublicOutput } from "@ika.xyz/sdk";
15
16
  import { computeEvmIntentFromUnsignedTxBytes } from "./evmIntent.js";
16
17
  import { keccak256, recoverTransactionAddress, serializeTransaction, } from "viem";
17
- import { convertWeiToUsdMicros, fetchEthUsdMicros } from "./price.js";
18
+ export class PolicyDeniedError extends Error {
19
+ denialReason;
20
+ denialReasonName;
21
+ constructor(denialReason, denialReasonName) {
22
+ const resolvedName = denialReasonName?.trim() || getDenialReasonName(denialReason);
23
+ super(`Policy denied: ${resolvedName}`);
24
+ this.name = "PolicyDeniedError";
25
+ this.denialReason = denialReason;
26
+ this.denialReasonName = resolvedName;
27
+ }
28
+ }
18
29
  const FALLBACK_SUI_RPC = "https://fullnode.testnet.sui.io:443";
19
30
  async function testRpcEndpoint(url) {
20
31
  try {
@@ -753,30 +764,17 @@ export class KairoClient {
753
764
  if (!wallet.policyObjectId || !wallet.bindingObjectId) {
754
765
  throw new Error("Wallet is missing policy binding metadata. Provision the wallet via dashboard or SDK before signing.");
755
766
  }
756
- const mintOnce = async () => {
757
- let normalizedNativeValue = ctx.nativeValue;
758
- // MaxNativeValue rules are encoded in USD-micros; normalize EVM wei inputs.
759
- if (ctx.namespace === 1) {
760
- try {
761
- const ethUsdMicros = await fetchEthUsdMicros();
762
- normalizedNativeValue = convertWeiToUsdMicros(ctx.nativeValue, ethUsdMicros);
763
- }
764
- catch {
765
- // Keep raw value as fallback if price feed is unavailable.
766
- }
767
- }
768
- return this.backend.mintReceipt({
769
- policyObjectId: wallet.policyObjectId ?? undefined,
770
- bindingObjectId: wallet.bindingObjectId,
771
- namespace: ctx.namespace,
772
- // chainId is encoded as u64 bytes (16 hex chars)
773
- chainId: ctx.chainId.toString(16).padStart(16, "0"),
774
- intentHashHex: stripHexPrefix(ctx.intentHashHex),
775
- destinationHex: stripHexPrefix(ctx.destinationHex),
776
- nativeValueHex: normalizedNativeValue.toString(16).padStart(64, "0"),
777
- contextDataHex: ctx.contextDataHex ? stripHexPrefix(ctx.contextDataHex) : undefined,
778
- });
779
- };
767
+ const mintOnce = async () => this.backend.mintReceipt({
768
+ policyObjectId: wallet.policyObjectId ?? undefined,
769
+ bindingObjectId: wallet.bindingObjectId,
770
+ namespace: ctx.namespace,
771
+ // chainId is encoded as u64 bytes (16 hex chars)
772
+ chainId: ctx.chainId.toString(16).padStart(16, "0"),
773
+ intentHashHex: stripHexPrefix(ctx.intentHashHex),
774
+ destinationHex: stripHexPrefix(ctx.destinationHex),
775
+ nativeValueHex: ctx.nativeValue.toString(16).padStart(64, "0"),
776
+ contextDataHex: ctx.contextDataHex ? stripHexPrefix(ctx.contextDataHex) : undefined,
777
+ });
780
778
  let response = await mintOnce();
781
779
  const initialError = String(response?.error ?? "");
782
780
  if (response.success === false && this.isReaffirmRequiredError(initialError)) {
@@ -787,7 +785,8 @@ export class KairoClient {
787
785
  throw new Error(String(response.error ?? "Failed to mint policy receipt"));
788
786
  }
789
787
  if (response.allowed === false) {
790
- throw new Error("Policy denied this signing intent");
788
+ const denialReason = Number(response.denialReason ?? 0);
789
+ throw new PolicyDeniedError(denialReason, response.denialReasonName);
791
790
  }
792
791
  const receiptId = String(response.receiptId ?? response.receiptObjectId ?? "");
793
792
  if (!receiptId.startsWith("0x")) {
@@ -0,0 +1,5 @@
1
+ export declare const DENIAL_REASONS: Record<number, {
2
+ code: string;
3
+ message: string;
4
+ }>;
5
+ export declare function getDenialReasonName(code: number | undefined): string;
@@ -0,0 +1,28 @@
1
+ export const DENIAL_REASONS = {
2
+ 0: { code: "NONE", message: "No denial (allowed)" },
3
+ 1: { code: "EXPIRED", message: "Policy has expired" },
4
+ 2: { code: "DENYLIST", message: "Destination address is on the denylist" },
5
+ 3: { code: "NOT_IN_ALLOWLIST", message: "Destination address is not in the allowlist" },
6
+ 4: { code: "BAD_FORMAT", message: "Invalid intent format" },
7
+ 10: { code: "CHAIN_NOT_ALLOWED", message: "This chain/network is not allowed by policy" },
8
+ 11: { code: "BAD_SELECTOR_FORMAT", message: "Invalid EVM function selector format" },
9
+ 12: { code: "SELECTOR_DENYLIST", message: "This contract function is on the denylist" },
10
+ 13: { code: "SELECTOR_NOT_ALLOWED", message: "This contract function is not in the allowlist" },
11
+ 14: { code: "BAD_AMOUNT_FORMAT", message: "Invalid ERC20 amount format" },
12
+ 15: { code: "ERC20_AMOUNT_EXCEEDS_MAX", message: "ERC20 transfer amount exceeds policy limit" },
13
+ 16: { code: "NO_POLICY_VERSION", message: "No active policy version found" },
14
+ 20: { code: "NAMESPACE_NOT_ALLOWED", message: "This blockchain type (EVM/Bitcoin/Solana) is not allowed" },
15
+ 21: { code: "BTC_SCRIPT_TYPE_NOT_ALLOWED", message: "Bitcoin address type not allowed by policy" },
16
+ 22: { code: "BTC_FEE_RATE_EXCEEDED", message: "Bitcoin fee rate exceeds policy limit" },
17
+ 23: { code: "SOL_PROGRAM_DENYLISTED", message: "Solana program is on the denylist" },
18
+ 24: { code: "SOL_PROGRAM_NOT_ALLOWED", message: "Solana program is not in the allowlist" },
19
+ 30: { code: "NATIVE_VALUE_EXCEEDED", message: "Native value exceeds policy limit" },
20
+ 31: { code: "TIME_WINDOW_BLOCKED", message: "Transaction blocked by time window rule" },
21
+ 32: { code: "PERIOD_LIMIT_EXCEEDED", message: "Period spending limit exceeded" },
22
+ 33: { code: "RATE_LIMIT_EXCEEDED", message: "Rate limit exceeded" },
23
+ };
24
+ export function getDenialReasonName(code) {
25
+ if (code === undefined || code === null)
26
+ return "Unknown denial reason";
27
+ return DENIAL_REASONS[code]?.message ?? `Unknown denial reason (${code})`;
28
+ }
package/dist/index.d.ts CHANGED
@@ -8,6 +8,7 @@ export * from "./suiResult.js";
8
8
  export * from "./suiTxBuilders.js";
9
9
  export * from "./auditBundle.js";
10
10
  export * from "./suiCustody.js";
11
- export { KairoClient, type KairoClientOpts, type CreateWalletOpts, type WalletInfo, type ProposePolicyUpdateParams, type PolicyUpdateProposalResult, type ApprovePolicyUpdateParams, type ExecutePolicyUpdateParams, type PolicyUpdateStatus, } from "./client.js";
11
+ export * from "./denialReasons.js";
12
+ export { KairoClient, PolicyDeniedError, type KairoClientOpts, type CreateWalletOpts, type WalletInfo, type ProposePolicyUpdateParams, type PolicyUpdateProposalResult, type ApprovePolicyUpdateParams, type ExecutePolicyUpdateParams, type PolicyUpdateStatus, } from "./client.js";
12
13
  export { KeyStore, type WalletRecord } from "./keystore.js";
13
14
  export { BackendClient, DEFAULT_BACKEND_URL, type BackendClientOpts } from "./backend.js";
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ export * from "./suiResult.js";
8
8
  export * from "./suiTxBuilders.js";
9
9
  export * from "./auditBundle.js";
10
10
  export * from "./suiCustody.js";
11
- export { KairoClient, } from "./client.js";
11
+ export * from "./denialReasons.js";
12
+ export { KairoClient, PolicyDeniedError, } from "./client.js";
12
13
  export { KeyStore } from "./keystore.js";
13
14
  export { BackendClient, DEFAULT_BACKEND_URL } from "./backend.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kairoguard/sdk",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
4
4
  "description": "Kairo SDK for multi-chain policy-based transaction signing with dWallet support (EVM, Bitcoin, Solana, Sui)",
5
5
  "license": "MIT",
6
6
  "author": "Kairo <mehraab@thewidercollective.com>",
package/dist/price.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export declare function fetchEthUsdMicros(): Promise<bigint>;
2
- export declare function convertWeiToUsdMicros(weiValue: bigint, ethUsdMicros: bigint): bigint;
package/dist/price.js DELETED
@@ -1,65 +0,0 @@
1
- const WEI_PER_ETH = 1000000000000000000n;
2
- const USD_MICROS_SCALE = 1000000n;
3
- const CACHE_MS = 60_000;
4
- let cachedEthUsdMicros = null;
5
- function parseUsdToMicros(input) {
6
- const s = input.trim();
7
- if (!s)
8
- throw new Error("ETH/USD response is empty");
9
- if (!/^\d+(\.\d+)?$/.test(s)) {
10
- throw new Error(`Invalid ETH/USD amount: ${s}`);
11
- }
12
- const [wholeRaw, fracRaw = ""] = s.split(".");
13
- const whole = BigInt(wholeRaw);
14
- const frac = BigInt((fracRaw + "000000").slice(0, 6));
15
- return whole * USD_MICROS_SCALE + frac;
16
- }
17
- async function fetchJsonWithTimeout(url, timeoutMs) {
18
- const ctrl = new AbortController();
19
- const timer = setTimeout(() => ctrl.abort(), timeoutMs);
20
- try {
21
- const resp = await fetch(url, { signal: ctrl.signal });
22
- if (!resp.ok) {
23
- throw new Error(`HTTP ${resp.status} from ${url}`);
24
- }
25
- return await resp.json();
26
- }
27
- finally {
28
- clearTimeout(timer);
29
- }
30
- }
31
- export async function fetchEthUsdMicros() {
32
- const now = Date.now();
33
- if (cachedEthUsdMicros && now - cachedEthUsdMicros.atMs < CACHE_MS) {
34
- return cachedEthUsdMicros.value;
35
- }
36
- const providers = [
37
- async () => {
38
- const j = await fetchJsonWithTimeout("https://api.coinbase.com/v2/prices/ETH-USD/spot", 7000);
39
- const amount = String(j?.data?.amount ?? "").trim();
40
- return parseUsdToMicros(amount);
41
- },
42
- async () => {
43
- const j = await fetchJsonWithTimeout("https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=usd", 7000);
44
- const amount = String(j?.ethereum?.usd ?? "").trim();
45
- return parseUsdToMicros(amount);
46
- },
47
- ];
48
- let lastErr = null;
49
- for (const getPrice of providers) {
50
- try {
51
- const value = await getPrice();
52
- if (value > 0n) {
53
- cachedEthUsdMicros = { value, atMs: now };
54
- return value;
55
- }
56
- }
57
- catch (error) {
58
- lastErr = error;
59
- }
60
- }
61
- throw new Error(`Could not fetch ETH/USD price${lastErr ? `: ${lastErr instanceof Error ? lastErr.message : String(lastErr)}` : ""}`);
62
- }
63
- export function convertWeiToUsdMicros(weiValue, ethUsdMicros) {
64
- return (weiValue * ethUsdMicros) / WEI_PER_ETH;
65
- }