@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.
- package/README.md +92 -0
- package/dist/auditBundle.d.ts +28 -0
- package/dist/auditBundle.js +20 -0
- package/dist/backend.d.ts +288 -0
- package/dist/backend.js +107 -0
- package/dist/bitcoinIntent.d.ts +104 -0
- package/dist/bitcoinIntent.js +126 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +237 -0
- package/dist/client.d.ts +186 -0
- package/dist/client.js +767 -0
- package/dist/evm.d.ts +19 -0
- package/dist/evm.js +53 -0
- package/dist/evmIntent.d.ts +11 -0
- package/dist/evmIntent.js +12 -0
- package/dist/ika-protocol.d.ts +85 -0
- package/dist/ika-protocol.js +156 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +13 -0
- package/dist/keystore.d.ts +29 -0
- package/dist/keystore.js +53 -0
- package/dist/skill-templates.d.ts +9 -0
- package/dist/skill-templates.js +252 -0
- package/dist/solanaIntent.d.ts +128 -0
- package/dist/solanaIntent.js +214 -0
- package/dist/suiCustody.d.ts +14 -0
- package/dist/suiCustody.js +183 -0
- package/dist/suiReceipts.d.ts +27 -0
- package/dist/suiReceipts.js +203 -0
- package/dist/suiResult.d.ts +8 -0
- package/dist/suiResult.js +12 -0
- package/dist/suiTxBuilders.d.ts +15 -0
- package/dist/suiTxBuilders.js +38 -0
- package/dist/types.d.ts +38 -0
- package/dist/types.js +1 -0
- package/package.json +29 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedded skill file templates shipped with the SDK.
|
|
3
|
+
* Generated by `kairo init` into .cursor/skills/kairo/.
|
|
4
|
+
* All backend URLs are intentionally omitted -- the SDK and CLI
|
|
5
|
+
* resolve the endpoint internally.
|
|
6
|
+
*/
|
|
7
|
+
export const SKILL_MD = `---
|
|
8
|
+
name: kairo
|
|
9
|
+
description: Manage Kairo policy-enforced agent wallets. Use when creating wallets, setting transaction policies, checking vault status, minting policy receipts, or signing transactions through the Kairo SDK/CLI. Supports full wallet lifecycle: register API key -> create wallet (DKG) -> create policy -> bind -> vault provision -> mint receipt -> sign. Uses @kairo/sdk for non-custodial wallet creation (agent keeps secret share locally).
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Kairo — Agent Wallet Management
|
|
13
|
+
|
|
14
|
+
## Quick Reference
|
|
15
|
+
|
|
16
|
+
CLI: \`npx kairo <command>\`
|
|
17
|
+
SDK reference: \`.cursor/skills/kairo/references/sdk.md\`
|
|
18
|
+
API reference: \`.cursor/skills/kairo/references/api.md\`
|
|
19
|
+
|
|
20
|
+
## Setup
|
|
21
|
+
|
|
22
|
+
Run the one-line installer (already done if you see this file):
|
|
23
|
+
\`\`\`bash
|
|
24
|
+
npx @kairo/sdk init <YOUR_API_KEY>
|
|
25
|
+
\`\`\`
|
|
26
|
+
|
|
27
|
+
The API key is stored in \`~/.kairo/config.json\`. All CLI commands read it automatically.
|
|
28
|
+
|
|
29
|
+
## Common Workflows
|
|
30
|
+
|
|
31
|
+
### Check API Health
|
|
32
|
+
\`\`\`bash
|
|
33
|
+
npx kairo health
|
|
34
|
+
\`\`\`
|
|
35
|
+
|
|
36
|
+
### Create a Policy
|
|
37
|
+
\`\`\`bash
|
|
38
|
+
npx kairo policy-create --stable-id "my-policy" --allow "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18"
|
|
39
|
+
\`\`\`
|
|
40
|
+
Then register the version:
|
|
41
|
+
\`\`\`bash
|
|
42
|
+
npx kairo policy-register --policy-id "0x..."
|
|
43
|
+
\`\`\`
|
|
44
|
+
|
|
45
|
+
### Create Wallet (via SDK)
|
|
46
|
+
For wallet creation, use the Node.js SDK (handles DKG client-side):
|
|
47
|
+
\`\`\`typescript
|
|
48
|
+
import { KairoClient } from "@kairo/sdk";
|
|
49
|
+
const kairo = new KairoClient({ apiKey: process.env.KAIRO_API_KEY! });
|
|
50
|
+
const wallet = await kairo.createWallet({ curve: "secp256k1" });
|
|
51
|
+
\`\`\`
|
|
52
|
+
See \`.cursor/skills/kairo/references/sdk.md\` for full SDK docs.
|
|
53
|
+
|
|
54
|
+
### Provision Wallet into Vault
|
|
55
|
+
Requires: policy version registered first.
|
|
56
|
+
\`\`\`bash
|
|
57
|
+
npx kairo vault-provision --wallet-id "0x..." --policy-id "0x..." --stable-id "my-policy"
|
|
58
|
+
\`\`\`
|
|
59
|
+
|
|
60
|
+
### Mint Policy Receipt
|
|
61
|
+
\`\`\`bash
|
|
62
|
+
npx kairo receipt-mint --policy-id "0x..." --binding-id "0x..." --destination "0x742d35Cc..." --intent-hash "0xabab..."
|
|
63
|
+
\`\`\`
|
|
64
|
+
|
|
65
|
+
### Check Vault Status
|
|
66
|
+
\`\`\`bash
|
|
67
|
+
npx kairo vault-status --wallet-id "0x..."
|
|
68
|
+
\`\`\`
|
|
69
|
+
|
|
70
|
+
### View Audit Events
|
|
71
|
+
\`\`\`bash
|
|
72
|
+
npx kairo audit --limit 20
|
|
73
|
+
\`\`\`
|
|
74
|
+
|
|
75
|
+
## Full Agent Flow (End to End)
|
|
76
|
+
|
|
77
|
+
1. \`npx @kairo/sdk init <key>\` — store API key, install skill
|
|
78
|
+
2. \`npx kairo policy-create\` — create transaction policy with allowed addresses
|
|
79
|
+
3. \`npx kairo policy-register\` — register version in on-chain registry
|
|
80
|
+
4. Create wallet via SDK \`createWallet()\` — runs DKG locally, secret share stays on agent
|
|
81
|
+
5. \`npx kairo vault-provision\` — bind policy + register wallet in vault (atomic)
|
|
82
|
+
6. \`npx kairo receipt-mint\` — request policy check for a transaction
|
|
83
|
+
7. Sign via SDK — both shares combine, only if policy allows
|
|
84
|
+
|
|
85
|
+
## Trust Model
|
|
86
|
+
|
|
87
|
+
- Agent's key share stays local (\`~/.kairo/keys/\`)
|
|
88
|
+
- Server's key share stays on Kairo backend
|
|
89
|
+
- Neither party can sign alone
|
|
90
|
+
- Policy engine gates every transaction before server releases its share
|
|
91
|
+
- All policy decisions are on-chain (Sui) and verifiable
|
|
92
|
+
|
|
93
|
+
## Troubleshooting
|
|
94
|
+
|
|
95
|
+
- **401 Unauthorized**: API key missing/invalid or not registered in backend key store. Re-run \`npx @kairo/sdk init <key>\` with a valid key.
|
|
96
|
+
- **403 Forbidden: key does not own wallet**: Wallet wasn't created/provisioned with this API key (ownership mismatch).
|
|
97
|
+
- **429 Rate limit**: Public Sui RPC throttled — use Shinami or own RPC provider.
|
|
98
|
+
- **MoveAbort code 102**: Policy version not registered — call \`npx kairo policy-register\` before \`vault-provision\`.
|
|
99
|
+
- **\`nonce too low\` / \`already known\`**: Rapid reruns or duplicate raw tx; wait for pending tx, then re-sign and rebroadcast.
|
|
100
|
+
- **AwaitingKeyHolderSignature**: Wallet needs activation after DKG — SDK activation flow required.
|
|
101
|
+
`;
|
|
102
|
+
export const API_REFERENCE_MD = `# Kairo API Reference
|
|
103
|
+
|
|
104
|
+
## Authentication
|
|
105
|
+
All write endpoints require \`X-Kairo-Api-Key\` header.
|
|
106
|
+
The CLI reads the key from \`~/.kairo/config.json\` automatically.
|
|
107
|
+
Open endpoints: \`/health\`, \`/api/vault/info\`, \`/api/vault/status/:id\`, \`/api/audit/events\`
|
|
108
|
+
|
|
109
|
+
## Key Registration
|
|
110
|
+
\`\`\`bash
|
|
111
|
+
npx kairo register --label "my-agent"
|
|
112
|
+
\`\`\`
|
|
113
|
+
|
|
114
|
+
## Wallet Creation (via SDK)
|
|
115
|
+
The SDK handles DKG client-side. Agent keeps their secret share locally.
|
|
116
|
+
\`\`\`typescript
|
|
117
|
+
import { KairoClient } from "@kairo/sdk";
|
|
118
|
+
const kairo = new KairoClient({ apiKey: process.env.KAIRO_API_KEY! });
|
|
119
|
+
const wallet = await kairo.createWallet({ curve: "secp256k1" });
|
|
120
|
+
// wallet.walletId, wallet.address
|
|
121
|
+
\`\`\`
|
|
122
|
+
|
|
123
|
+
## Policy Management
|
|
124
|
+
|
|
125
|
+
### Create Policy
|
|
126
|
+
\`\`\`bash
|
|
127
|
+
npx kairo policy-create --stable-id "my-policy" --version "1.0.0" --allow "0x<address>"
|
|
128
|
+
\`\`\`
|
|
129
|
+
|
|
130
|
+
Rule types:
|
|
131
|
+
- \`1\` = MaxNativeValue (max single transaction value)
|
|
132
|
+
- \`10\` = PeriodLimit (cumulative spend limit per time window)
|
|
133
|
+
|
|
134
|
+
### Register Policy Version
|
|
135
|
+
\`\`\`bash
|
|
136
|
+
npx kairo policy-register --policy-id "0x..."
|
|
137
|
+
\`\`\`
|
|
138
|
+
|
|
139
|
+
### Get Policy Details
|
|
140
|
+
\`\`\`bash
|
|
141
|
+
npx kairo policy-details --policy-id "0x..."
|
|
142
|
+
\`\`\`
|
|
143
|
+
|
|
144
|
+
## Vault
|
|
145
|
+
|
|
146
|
+
### Provision (atomic binding + vault registration)
|
|
147
|
+
\`\`\`bash
|
|
148
|
+
npx kairo vault-provision --wallet-id "0x..." --policy-id "0x..." --stable-id "my-policy"
|
|
149
|
+
\`\`\`
|
|
150
|
+
Note: Register policy version BEFORE calling provision.
|
|
151
|
+
|
|
152
|
+
### Check Status
|
|
153
|
+
\`\`\`bash
|
|
154
|
+
npx kairo vault-status --wallet-id "0x..."
|
|
155
|
+
\`\`\`
|
|
156
|
+
|
|
157
|
+
## Receipt Minting
|
|
158
|
+
\`\`\`bash
|
|
159
|
+
npx kairo receipt-mint --policy-id "0x..." --binding-id "0x..." --destination "0x..." --intent-hash "0x..."
|
|
160
|
+
\`\`\`
|
|
161
|
+
Namespace: 1=EVM, 2=Bitcoin, 3=Solana
|
|
162
|
+
|
|
163
|
+
## Utility
|
|
164
|
+
\`\`\`bash
|
|
165
|
+
npx kairo health # Server health
|
|
166
|
+
npx kairo audit --limit 20 # Recent audit events
|
|
167
|
+
\`\`\`
|
|
168
|
+
`;
|
|
169
|
+
export const SDK_REFERENCE_MD = `# Kairo SDK Reference
|
|
170
|
+
|
|
171
|
+
## Installation
|
|
172
|
+
\`\`\`bash
|
|
173
|
+
npm install @kairo/sdk
|
|
174
|
+
\`\`\`
|
|
175
|
+
|
|
176
|
+
Requires: \`@ika.xyz/sdk\`, \`@mysten/sui\`
|
|
177
|
+
|
|
178
|
+
## KairoClient
|
|
179
|
+
|
|
180
|
+
\`\`\`typescript
|
|
181
|
+
import { KairoClient } from "@kairo/sdk";
|
|
182
|
+
|
|
183
|
+
const kairo = new KairoClient({
|
|
184
|
+
apiKey: process.env.KAIRO_API_KEY!,
|
|
185
|
+
storePath: "~/.kairo/keys", // local secret share storage (default)
|
|
186
|
+
network: "testnet", // or "mainnet"
|
|
187
|
+
suiRpcUrl: "https://...", // optional, defaults to public testnet
|
|
188
|
+
});
|
|
189
|
+
\`\`\`
|
|
190
|
+
|
|
191
|
+
### createWallet(opts?)
|
|
192
|
+
Creates a dWallet via client-side DKG. Secret share stays local.
|
|
193
|
+
|
|
194
|
+
\`\`\`typescript
|
|
195
|
+
const wallet = await kairo.createWallet({
|
|
196
|
+
curve: "secp256k1", // or "ed25519" for Solana
|
|
197
|
+
policyObjectId: "0x...", // optional: auto-provision into vault
|
|
198
|
+
stableId: "my-policy", // optional: binding label
|
|
199
|
+
});
|
|
200
|
+
// Returns: { walletId, address, curve, bindingObjectId?, createdAt }
|
|
201
|
+
\`\`\`
|
|
202
|
+
|
|
203
|
+
**Important:** If providing \`policyObjectId\`, register the policy version first.
|
|
204
|
+
|
|
205
|
+
### listWallets()
|
|
206
|
+
Lists all wallets in local key store.
|
|
207
|
+
\`\`\`typescript
|
|
208
|
+
const wallets = kairo.listWallets();
|
|
209
|
+
\`\`\`
|
|
210
|
+
|
|
211
|
+
### getWallet(walletId)
|
|
212
|
+
Gets a specific wallet from local store.
|
|
213
|
+
\`\`\`typescript
|
|
214
|
+
const w = kairo.getWallet("0x...");
|
|
215
|
+
\`\`\`
|
|
216
|
+
|
|
217
|
+
## BackendClient (HTTP wrapper)
|
|
218
|
+
Lower-level HTTP client for direct API calls.
|
|
219
|
+
|
|
220
|
+
\`\`\`typescript
|
|
221
|
+
import { BackendClient } from "@kairo/sdk";
|
|
222
|
+
|
|
223
|
+
const client = new BackendClient({ apiKey: "your-key" });
|
|
224
|
+
|
|
225
|
+
await client.register("my-agent");
|
|
226
|
+
await client.getHealth();
|
|
227
|
+
await client.submitDKG({...});
|
|
228
|
+
await client.getDKGStatus(requestId);
|
|
229
|
+
await client.provision({...});
|
|
230
|
+
await client.mintReceipt({...});
|
|
231
|
+
\`\`\`
|
|
232
|
+
|
|
233
|
+
## KeyStore (local storage)
|
|
234
|
+
File-based secret share storage at \`~/.kairo/keys/\`.
|
|
235
|
+
|
|
236
|
+
\`\`\`typescript
|
|
237
|
+
import { KeyStore } from "@kairo/sdk";
|
|
238
|
+
|
|
239
|
+
const store = new KeyStore("~/.kairo/keys");
|
|
240
|
+
store.save(record);
|
|
241
|
+
store.load("0x...");
|
|
242
|
+
store.list();
|
|
243
|
+
store.delete("0x...");
|
|
244
|
+
\`\`\`
|
|
245
|
+
|
|
246
|
+
## Trust Model
|
|
247
|
+
- Agent's secret share -> stored locally (KeyStore), never sent to server
|
|
248
|
+
- Server's share -> held by Kairo backend
|
|
249
|
+
- Full signing -> requires BOTH shares + policy approval
|
|
250
|
+
- Kairo alone cannot sign (missing agent share)
|
|
251
|
+
- Agent alone cannot sign (missing server share)
|
|
252
|
+
`;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solana Intent Utilities
|
|
3
|
+
*
|
|
4
|
+
* Client-side helpers for Solana transaction intent hashing and validation.
|
|
5
|
+
*/
|
|
6
|
+
import type { Hex } from "./types.js";
|
|
7
|
+
/**
|
|
8
|
+
* Solana cluster types.
|
|
9
|
+
*/
|
|
10
|
+
export type SolanaCluster = "mainnet-beta" | "devnet" | "testnet";
|
|
11
|
+
/**
|
|
12
|
+
* Lamports per SOL.
|
|
13
|
+
*/
|
|
14
|
+
export declare const LAMPORTS_PER_SOL = 1000000000n;
|
|
15
|
+
/**
|
|
16
|
+
* Parsed Solana instruction.
|
|
17
|
+
*/
|
|
18
|
+
export interface ParsedInstruction {
|
|
19
|
+
programId: string;
|
|
20
|
+
accounts: string[];
|
|
21
|
+
data: Uint8Array;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Parsed Solana transaction.
|
|
25
|
+
*/
|
|
26
|
+
export interface ParsedSolanaTransaction {
|
|
27
|
+
feePayer: string;
|
|
28
|
+
recentBlockhash: string;
|
|
29
|
+
instructions: ParsedInstruction[];
|
|
30
|
+
programIds: string[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Solana intent for policy verification.
|
|
34
|
+
*/
|
|
35
|
+
export interface SolanaIntent {
|
|
36
|
+
cluster: SolanaCluster;
|
|
37
|
+
/** 32-byte intent hash (message hash) */
|
|
38
|
+
intentHash: Hex;
|
|
39
|
+
/** Fee payer (signer) */
|
|
40
|
+
feePayer: string;
|
|
41
|
+
/** Destination addresses (extracted from transfer instructions) */
|
|
42
|
+
destinations: string[];
|
|
43
|
+
/** Amounts in lamports */
|
|
44
|
+
amounts: bigint[];
|
|
45
|
+
/** Program IDs involved */
|
|
46
|
+
programIds: string[];
|
|
47
|
+
/** Raw transaction bytes (base58) */
|
|
48
|
+
transactionBase58: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Common Solana program IDs.
|
|
52
|
+
*/
|
|
53
|
+
export declare const PROGRAM_IDS: {
|
|
54
|
+
readonly SYSTEM: "11111111111111111111111111111111";
|
|
55
|
+
readonly TOKEN: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
|
|
56
|
+
readonly TOKEN_2022: "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb";
|
|
57
|
+
readonly ASSOCIATED_TOKEN: "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL";
|
|
58
|
+
readonly MEMO: "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr";
|
|
59
|
+
readonly COMPUTE_BUDGET: "ComputeBudget111111111111111111111111111111";
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* System instruction types.
|
|
63
|
+
*/
|
|
64
|
+
export declare enum SystemInstructionType {
|
|
65
|
+
CreateAccount = 0,
|
|
66
|
+
Assign = 1,
|
|
67
|
+
Transfer = 2,
|
|
68
|
+
CreateAccountWithSeed = 3,
|
|
69
|
+
AdvanceNonceAccount = 4,
|
|
70
|
+
WithdrawNonceAccount = 5,
|
|
71
|
+
InitializeNonceAccount = 6,
|
|
72
|
+
AuthorizeNonceAccount = 7,
|
|
73
|
+
Allocate = 8,
|
|
74
|
+
AllocateWithSeed = 9,
|
|
75
|
+
AssignWithSeed = 10,
|
|
76
|
+
TransferWithSeed = 11
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Convert hex string to bytes.
|
|
80
|
+
*/
|
|
81
|
+
export declare function hexToBytes(hex: string): Uint8Array;
|
|
82
|
+
/**
|
|
83
|
+
* Convert bytes to hex string.
|
|
84
|
+
*/
|
|
85
|
+
export declare function bytesToHex(bytes: Uint8Array): Hex;
|
|
86
|
+
/**
|
|
87
|
+
* Decode base58 string to bytes.
|
|
88
|
+
*/
|
|
89
|
+
export declare function base58Decode(str: string): Uint8Array;
|
|
90
|
+
/**
|
|
91
|
+
* Encode bytes to base58.
|
|
92
|
+
*/
|
|
93
|
+
export declare function base58Encode(data: Uint8Array): string;
|
|
94
|
+
/**
|
|
95
|
+
* Validate a Solana address (base58 encoded Ed25519 public key).
|
|
96
|
+
*/
|
|
97
|
+
export declare function validateSolanaAddress(address: string): boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Compute intent hash from transaction message bytes.
|
|
100
|
+
*/
|
|
101
|
+
export declare function computeSolanaIntentHash(messageBytes: Uint8Array): Uint8Array;
|
|
102
|
+
/**
|
|
103
|
+
* Check if a program ID is a known safe program.
|
|
104
|
+
*/
|
|
105
|
+
export declare function isKnownSafeProgram(programId: string): boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Check if a program ID is a token program.
|
|
108
|
+
*/
|
|
109
|
+
export declare function isTokenProgram(programId: string): boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Get human-readable name for a program.
|
|
112
|
+
*/
|
|
113
|
+
export declare function getProgramName(programId: string): string;
|
|
114
|
+
/**
|
|
115
|
+
* Format lamports as SOL string.
|
|
116
|
+
*/
|
|
117
|
+
export declare function lamportsToSOL(lamports: bigint): string;
|
|
118
|
+
/**
|
|
119
|
+
* Parse SOL string to lamports.
|
|
120
|
+
*/
|
|
121
|
+
export declare function solToLamports(sol: string | number): bigint;
|
|
122
|
+
/**
|
|
123
|
+
* Extract transfer destinations and amounts from System Program instructions.
|
|
124
|
+
*/
|
|
125
|
+
export declare function extractSystemTransfers(instructions: ParsedInstruction[]): {
|
|
126
|
+
destinations: string[];
|
|
127
|
+
amounts: bigint[];
|
|
128
|
+
};
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solana Intent Utilities
|
|
3
|
+
*
|
|
4
|
+
* Client-side helpers for Solana transaction intent hashing and validation.
|
|
5
|
+
*/
|
|
6
|
+
import { sha256 } from "@noble/hashes/sha256";
|
|
7
|
+
/**
|
|
8
|
+
* Lamports per SOL.
|
|
9
|
+
*/
|
|
10
|
+
export const LAMPORTS_PER_SOL = 1000000000n;
|
|
11
|
+
/**
|
|
12
|
+
* Common Solana program IDs.
|
|
13
|
+
*/
|
|
14
|
+
export const PROGRAM_IDS = {
|
|
15
|
+
SYSTEM: "11111111111111111111111111111111",
|
|
16
|
+
TOKEN: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
|
|
17
|
+
TOKEN_2022: "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
|
|
18
|
+
ASSOCIATED_TOKEN: "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL",
|
|
19
|
+
MEMO: "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr",
|
|
20
|
+
COMPUTE_BUDGET: "ComputeBudget111111111111111111111111111111",
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* System instruction types.
|
|
24
|
+
*/
|
|
25
|
+
export var SystemInstructionType;
|
|
26
|
+
(function (SystemInstructionType) {
|
|
27
|
+
SystemInstructionType[SystemInstructionType["CreateAccount"] = 0] = "CreateAccount";
|
|
28
|
+
SystemInstructionType[SystemInstructionType["Assign"] = 1] = "Assign";
|
|
29
|
+
SystemInstructionType[SystemInstructionType["Transfer"] = 2] = "Transfer";
|
|
30
|
+
SystemInstructionType[SystemInstructionType["CreateAccountWithSeed"] = 3] = "CreateAccountWithSeed";
|
|
31
|
+
SystemInstructionType[SystemInstructionType["AdvanceNonceAccount"] = 4] = "AdvanceNonceAccount";
|
|
32
|
+
SystemInstructionType[SystemInstructionType["WithdrawNonceAccount"] = 5] = "WithdrawNonceAccount";
|
|
33
|
+
SystemInstructionType[SystemInstructionType["InitializeNonceAccount"] = 6] = "InitializeNonceAccount";
|
|
34
|
+
SystemInstructionType[SystemInstructionType["AuthorizeNonceAccount"] = 7] = "AuthorizeNonceAccount";
|
|
35
|
+
SystemInstructionType[SystemInstructionType["Allocate"] = 8] = "Allocate";
|
|
36
|
+
SystemInstructionType[SystemInstructionType["AllocateWithSeed"] = 9] = "AllocateWithSeed";
|
|
37
|
+
SystemInstructionType[SystemInstructionType["AssignWithSeed"] = 10] = "AssignWithSeed";
|
|
38
|
+
SystemInstructionType[SystemInstructionType["TransferWithSeed"] = 11] = "TransferWithSeed";
|
|
39
|
+
})(SystemInstructionType || (SystemInstructionType = {}));
|
|
40
|
+
/**
|
|
41
|
+
* Convert hex string to bytes.
|
|
42
|
+
*/
|
|
43
|
+
export function hexToBytes(hex) {
|
|
44
|
+
const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
45
|
+
const bytes = new Uint8Array(cleanHex.length / 2);
|
|
46
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
47
|
+
bytes[i] = parseInt(cleanHex.slice(i * 2, i * 2 + 2), 16);
|
|
48
|
+
}
|
|
49
|
+
return bytes;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Convert bytes to hex string.
|
|
53
|
+
*/
|
|
54
|
+
export function bytesToHex(bytes) {
|
|
55
|
+
return `0x${Array.from(bytes)
|
|
56
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
57
|
+
.join("")}`;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Base58 alphabet (Bitcoin/Solana variant).
|
|
61
|
+
*/
|
|
62
|
+
const BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
63
|
+
/**
|
|
64
|
+
* Decode base58 string to bytes.
|
|
65
|
+
*/
|
|
66
|
+
export function base58Decode(str) {
|
|
67
|
+
const bytes = [];
|
|
68
|
+
for (const char of str) {
|
|
69
|
+
const index = BASE58_ALPHABET.indexOf(char);
|
|
70
|
+
if (index === -1)
|
|
71
|
+
throw new Error(`Invalid base58 character: ${char}`);
|
|
72
|
+
let carry = index;
|
|
73
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
74
|
+
carry += bytes[i] * 58;
|
|
75
|
+
bytes[i] = carry & 0xff;
|
|
76
|
+
carry >>= 8;
|
|
77
|
+
}
|
|
78
|
+
while (carry > 0) {
|
|
79
|
+
bytes.push(carry & 0xff);
|
|
80
|
+
carry >>= 8;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
for (const char of str) {
|
|
84
|
+
if (char !== "1")
|
|
85
|
+
break;
|
|
86
|
+
bytes.push(0);
|
|
87
|
+
}
|
|
88
|
+
return Uint8Array.from(bytes.reverse());
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Encode bytes to base58.
|
|
92
|
+
*/
|
|
93
|
+
export function base58Encode(data) {
|
|
94
|
+
const digits = [0];
|
|
95
|
+
for (const byte of data) {
|
|
96
|
+
let carry = byte;
|
|
97
|
+
for (let j = 0; j < digits.length; j++) {
|
|
98
|
+
carry += digits[j] << 8;
|
|
99
|
+
digits[j] = carry % 58;
|
|
100
|
+
carry = Math.floor(carry / 58);
|
|
101
|
+
}
|
|
102
|
+
while (carry > 0) {
|
|
103
|
+
digits.push(carry % 58);
|
|
104
|
+
carry = Math.floor(carry / 58);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
let result = "";
|
|
108
|
+
for (const byte of data) {
|
|
109
|
+
if (byte !== 0)
|
|
110
|
+
break;
|
|
111
|
+
result += "1";
|
|
112
|
+
}
|
|
113
|
+
for (let i = digits.length - 1; i >= 0; i--) {
|
|
114
|
+
result += BASE58_ALPHABET[digits[i]];
|
|
115
|
+
}
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Validate a Solana address (base58 encoded Ed25519 public key).
|
|
120
|
+
*/
|
|
121
|
+
export function validateSolanaAddress(address) {
|
|
122
|
+
try {
|
|
123
|
+
const decoded = base58Decode(address);
|
|
124
|
+
return decoded.length === 32;
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Compute intent hash from transaction message bytes.
|
|
132
|
+
*/
|
|
133
|
+
export function computeSolanaIntentHash(messageBytes) {
|
|
134
|
+
return sha256(messageBytes);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Check if a program ID is a known safe program.
|
|
138
|
+
*/
|
|
139
|
+
export function isKnownSafeProgram(programId) {
|
|
140
|
+
return (programId === PROGRAM_IDS.SYSTEM ||
|
|
141
|
+
programId === PROGRAM_IDS.MEMO ||
|
|
142
|
+
programId === PROGRAM_IDS.COMPUTE_BUDGET);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Check if a program ID is a token program.
|
|
146
|
+
*/
|
|
147
|
+
export function isTokenProgram(programId) {
|
|
148
|
+
return (programId === PROGRAM_IDS.TOKEN ||
|
|
149
|
+
programId === PROGRAM_IDS.TOKEN_2022 ||
|
|
150
|
+
programId === PROGRAM_IDS.ASSOCIATED_TOKEN);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get human-readable name for a program.
|
|
154
|
+
*/
|
|
155
|
+
export function getProgramName(programId) {
|
|
156
|
+
switch (programId) {
|
|
157
|
+
case PROGRAM_IDS.SYSTEM:
|
|
158
|
+
return "System Program";
|
|
159
|
+
case PROGRAM_IDS.TOKEN:
|
|
160
|
+
return "Token Program";
|
|
161
|
+
case PROGRAM_IDS.TOKEN_2022:
|
|
162
|
+
return "Token 2022 Program";
|
|
163
|
+
case PROGRAM_IDS.ASSOCIATED_TOKEN:
|
|
164
|
+
return "Associated Token Program";
|
|
165
|
+
case PROGRAM_IDS.MEMO:
|
|
166
|
+
return "Memo Program";
|
|
167
|
+
case PROGRAM_IDS.COMPUTE_BUDGET:
|
|
168
|
+
return "Compute Budget Program";
|
|
169
|
+
default:
|
|
170
|
+
return `Unknown (${programId.slice(0, 8)}...)`;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Format lamports as SOL string.
|
|
175
|
+
*/
|
|
176
|
+
export function lamportsToSOL(lamports) {
|
|
177
|
+
const sol = Number(lamports) / Number(LAMPORTS_PER_SOL);
|
|
178
|
+
return sol.toFixed(9);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Parse SOL string to lamports.
|
|
182
|
+
*/
|
|
183
|
+
export function solToLamports(sol) {
|
|
184
|
+
const value = typeof sol === "string" ? parseFloat(sol) : sol;
|
|
185
|
+
return BigInt(Math.round(value * Number(LAMPORTS_PER_SOL)));
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Extract transfer destinations and amounts from System Program instructions.
|
|
189
|
+
*/
|
|
190
|
+
export function extractSystemTransfers(instructions) {
|
|
191
|
+
const destinations = [];
|
|
192
|
+
const amounts = [];
|
|
193
|
+
for (const ix of instructions) {
|
|
194
|
+
if (ix.programId !== PROGRAM_IDS.SYSTEM)
|
|
195
|
+
continue;
|
|
196
|
+
if (ix.data.length < 12)
|
|
197
|
+
continue;
|
|
198
|
+
// Check instruction type (first 4 bytes, little-endian)
|
|
199
|
+
const instructionType = ix.data[0] | (ix.data[1] << 8) | (ix.data[2] << 16) | (ix.data[3] << 24);
|
|
200
|
+
if (instructionType === SystemInstructionType.Transfer) {
|
|
201
|
+
// Next 8 bytes are lamports (little-endian u64)
|
|
202
|
+
let lamports = 0n;
|
|
203
|
+
for (let i = 0; i < 8; i++) {
|
|
204
|
+
lamports |= BigInt(ix.data[4 + i]) << BigInt(i * 8);
|
|
205
|
+
}
|
|
206
|
+
// Destination is the second account
|
|
207
|
+
if (ix.accounts.length >= 2) {
|
|
208
|
+
destinations.push(ix.accounts[1]);
|
|
209
|
+
amounts.push(lamports);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return { destinations, amounts };
|
|
214
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verify a custody event hash by recomputing the canonical BCS encoding (v2/v3 hashing).
|
|
3
|
+
*
|
|
4
|
+
* Note: This does NOT discover events. You must provide a specific `CustodyEvent` object id.
|
|
5
|
+
*/
|
|
6
|
+
export declare function fetchAndVerifyCustodyEvent(args: {
|
|
7
|
+
suiRpcUrl: string;
|
|
8
|
+
custodyEventObjectId: string;
|
|
9
|
+
}): Promise<{
|
|
10
|
+
ok: true;
|
|
11
|
+
} | {
|
|
12
|
+
ok: false;
|
|
13
|
+
error: string;
|
|
14
|
+
}>;
|