@veridex/sdk 1.0.0-beta.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/CHANGELOG.md +73 -0
- package/LICENSE +21 -0
- package/README.md +212 -0
- package/dist/chains/aptos/index.d.mts +140 -0
- package/dist/chains/aptos/index.d.ts +140 -0
- package/dist/chains/aptos/index.js +563 -0
- package/dist/chains/aptos/index.js.map +1 -0
- package/dist/chains/aptos/index.mjs +536 -0
- package/dist/chains/aptos/index.mjs.map +1 -0
- package/dist/chains/evm/index.d.mts +5 -0
- package/dist/chains/evm/index.d.ts +5 -0
- package/dist/chains/evm/index.js +1233 -0
- package/dist/chains/evm/index.js.map +1 -0
- package/dist/chains/evm/index.mjs +1205 -0
- package/dist/chains/evm/index.mjs.map +1 -0
- package/dist/chains/solana/index.d.mts +116 -0
- package/dist/chains/solana/index.d.ts +116 -0
- package/dist/chains/solana/index.js +513 -0
- package/dist/chains/solana/index.js.map +1 -0
- package/dist/chains/solana/index.mjs +491 -0
- package/dist/chains/solana/index.mjs.map +1 -0
- package/dist/chains/starknet/index.d.mts +172 -0
- package/dist/chains/starknet/index.d.ts +172 -0
- package/dist/chains/starknet/index.js +534 -0
- package/dist/chains/starknet/index.js.map +1 -0
- package/dist/chains/starknet/index.mjs +507 -0
- package/dist/chains/starknet/index.mjs.map +1 -0
- package/dist/chains/sui/index.d.mts +182 -0
- package/dist/chains/sui/index.d.ts +182 -0
- package/dist/chains/sui/index.js +560 -0
- package/dist/chains/sui/index.js.map +1 -0
- package/dist/chains/sui/index.mjs +533 -0
- package/dist/chains/sui/index.mjs.map +1 -0
- package/dist/constants.d.mts +150 -0
- package/dist/constants.d.ts +150 -0
- package/dist/constants.js +430 -0
- package/dist/constants.js.map +1 -0
- package/dist/constants.mjs +392 -0
- package/dist/constants.mjs.map +1 -0
- package/dist/index-0NXfbk0z.d.ts +637 -0
- package/dist/index-D0dLVjTA.d.mts +637 -0
- package/dist/index.d.mts +3101 -0
- package/dist/index.d.ts +3101 -0
- package/dist/index.js +13186 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +13011 -0
- package/dist/index.mjs.map +1 -0
- package/dist/payload.d.mts +125 -0
- package/dist/payload.d.ts +125 -0
- package/dist/payload.js +315 -0
- package/dist/payload.js.map +1 -0
- package/dist/payload.mjs +269 -0
- package/dist/payload.mjs.map +1 -0
- package/dist/queries/index.d.mts +148 -0
- package/dist/queries/index.d.ts +148 -0
- package/dist/queries/index.js +1533 -0
- package/dist/queries/index.js.map +1 -0
- package/dist/queries/index.mjs +1508 -0
- package/dist/queries/index.mjs.map +1 -0
- package/dist/types-ChIsqCiw.d.mts +565 -0
- package/dist/types-ChIsqCiw.d.ts +565 -0
- package/dist/types-FJL7j6gQ.d.mts +172 -0
- package/dist/types-FJL7j6gQ.d.ts +172 -0
- package/dist/types.d.mts +407 -0
- package/dist/types.d.ts +407 -0
- package/dist/types.js +19 -0
- package/dist/types.js.map +1 -0
- package/dist/types.mjs +1 -0
- package/dist/types.mjs.map +1 -0
- package/dist/utils.d.mts +81 -0
- package/dist/utils.d.ts +81 -0
- package/dist/utils.js +430 -0
- package/dist/utils.js.map +1 -0
- package/dist/utils.mjs +390 -0
- package/dist/utils.mjs.map +1 -0
- package/dist/wormhole.d.mts +167 -0
- package/dist/wormhole.d.ts +167 -0
- package/dist/wormhole.js +468 -0
- package/dist/wormhole.js.map +1 -0
- package/dist/wormhole.mjs +422 -0
- package/dist/wormhole.mjs.map +1 -0
- package/package.json +151 -0
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
// src/chains/sui/SuiClient.ts
|
|
2
|
+
import { SuiClient as MystenSuiClient } from "@mysten/sui/client";
|
|
3
|
+
import { createHash } from "crypto";
|
|
4
|
+
|
|
5
|
+
// src/payload.ts
|
|
6
|
+
import { ethers } from "ethers";
|
|
7
|
+
|
|
8
|
+
// src/constants.ts
|
|
9
|
+
var ACTION_TRANSFER = 1;
|
|
10
|
+
var ACTION_EXECUTE = 2;
|
|
11
|
+
var ACTION_BRIDGE = 4;
|
|
12
|
+
|
|
13
|
+
// src/payload.ts
|
|
14
|
+
function encodeTransferAction(token, recipient, amount) {
|
|
15
|
+
const tokenPadded = padTo32Bytes(token);
|
|
16
|
+
const recipientPadded = padTo32Bytes(recipient);
|
|
17
|
+
const amountBytes = ethers.zeroPadValue(ethers.toBeHex(amount), 32);
|
|
18
|
+
return ethers.concat([
|
|
19
|
+
ethers.toBeHex(ACTION_TRANSFER, 1),
|
|
20
|
+
tokenPadded,
|
|
21
|
+
recipientPadded,
|
|
22
|
+
amountBytes
|
|
23
|
+
]);
|
|
24
|
+
}
|
|
25
|
+
function encodeBridgeAction(token, amount, targetChain, recipient) {
|
|
26
|
+
const tokenPadded = padTo32Bytes(token);
|
|
27
|
+
const amountBytes = ethers.zeroPadValue(ethers.toBeHex(amount), 32);
|
|
28
|
+
const targetChainBytes = ethers.toBeHex(targetChain, 2);
|
|
29
|
+
const recipientPadded = padTo32Bytes(recipient);
|
|
30
|
+
return ethers.concat([
|
|
31
|
+
ethers.toBeHex(ACTION_BRIDGE, 1),
|
|
32
|
+
tokenPadded,
|
|
33
|
+
amountBytes,
|
|
34
|
+
targetChainBytes,
|
|
35
|
+
recipientPadded
|
|
36
|
+
]);
|
|
37
|
+
}
|
|
38
|
+
function encodeExecuteAction(target, value, data) {
|
|
39
|
+
const targetPadded = padTo32Bytes(target);
|
|
40
|
+
const valueBytes = ethers.zeroPadValue(ethers.toBeHex(value), 32);
|
|
41
|
+
const dataBytes = ethers.getBytes(data);
|
|
42
|
+
const dataLengthBytes = ethers.toBeHex(dataBytes.length, 2);
|
|
43
|
+
return ethers.concat([
|
|
44
|
+
ethers.toBeHex(ACTION_EXECUTE, 1),
|
|
45
|
+
targetPadded,
|
|
46
|
+
valueBytes,
|
|
47
|
+
dataLengthBytes,
|
|
48
|
+
data
|
|
49
|
+
]);
|
|
50
|
+
}
|
|
51
|
+
function padTo32Bytes(address) {
|
|
52
|
+
if (address.toLowerCase() === "native") {
|
|
53
|
+
return "0x" + "0".repeat(64);
|
|
54
|
+
}
|
|
55
|
+
if (address.startsWith("0x")) {
|
|
56
|
+
const hex2 = address.replace("0x", "");
|
|
57
|
+
if (!/^[0-9a-fA-F]*$/.test(hex2)) {
|
|
58
|
+
throw new Error(`Invalid address: ${address}. Expected hex string or 'native'.`);
|
|
59
|
+
}
|
|
60
|
+
return "0x" + hex2.padStart(64, "0");
|
|
61
|
+
}
|
|
62
|
+
const base58Chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
63
|
+
for (const char of address) {
|
|
64
|
+
if (!base58Chars.includes(char)) {
|
|
65
|
+
throw new Error(`Invalid address: ${address}. Contains invalid base58 character '${char}'.`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
let value = BigInt(0);
|
|
69
|
+
for (const char of address) {
|
|
70
|
+
value = value * 58n + BigInt(base58Chars.indexOf(char));
|
|
71
|
+
}
|
|
72
|
+
let hex = value.toString(16);
|
|
73
|
+
if (hex.length > 64) {
|
|
74
|
+
throw new Error(`Invalid address: ${address}. Decoded value too large for 32 bytes.`);
|
|
75
|
+
}
|
|
76
|
+
return "0x" + hex.padStart(64, "0");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/chains/sui/SuiClient.ts
|
|
80
|
+
var SuiClient = class {
|
|
81
|
+
config;
|
|
82
|
+
client;
|
|
83
|
+
packageId;
|
|
84
|
+
hubRpcUrl;
|
|
85
|
+
hubContractAddress;
|
|
86
|
+
constructor(config) {
|
|
87
|
+
this.config = {
|
|
88
|
+
name: `Sui ${config.network || "mainnet"}`,
|
|
89
|
+
chainId: 0,
|
|
90
|
+
wormholeChainId: config.wormholeChainId,
|
|
91
|
+
rpcUrl: config.rpcUrl,
|
|
92
|
+
explorerUrl: config.network === "testnet" ? "https://suiscan.xyz/testnet" : config.network === "devnet" ? "https://suiscan.xyz/devnet" : "https://suiscan.xyz/mainnet",
|
|
93
|
+
isEvm: false,
|
|
94
|
+
contracts: {
|
|
95
|
+
hub: config.packageId,
|
|
96
|
+
wormholeCoreBridge: config.wormholeCoreBridge,
|
|
97
|
+
tokenBridge: config.tokenBridge
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
this.client = new MystenSuiClient({ url: config.rpcUrl });
|
|
101
|
+
this.packageId = config.packageId;
|
|
102
|
+
this.hubRpcUrl = config.hubRpcUrl;
|
|
103
|
+
this.hubContractAddress = config.hubContractAddress;
|
|
104
|
+
}
|
|
105
|
+
getConfig() {
|
|
106
|
+
return this.config;
|
|
107
|
+
}
|
|
108
|
+
async getNonce(_userKeyHash) {
|
|
109
|
+
return 0n;
|
|
110
|
+
}
|
|
111
|
+
async getMessageFee() {
|
|
112
|
+
return 0n;
|
|
113
|
+
}
|
|
114
|
+
async buildTransferPayload(params) {
|
|
115
|
+
return encodeTransferAction(params.token, params.recipient, params.amount);
|
|
116
|
+
}
|
|
117
|
+
async buildExecutePayload(params) {
|
|
118
|
+
return encodeExecuteAction(params.target, params.value, params.data);
|
|
119
|
+
}
|
|
120
|
+
async buildBridgePayload(params) {
|
|
121
|
+
return encodeBridgeAction(params.token, params.amount, params.destinationChain, params.recipient);
|
|
122
|
+
}
|
|
123
|
+
async dispatch(signature, publicKeyX, publicKeyY, targetChain, actionPayload, nonce, signer) {
|
|
124
|
+
void signature;
|
|
125
|
+
void publicKeyX;
|
|
126
|
+
void publicKeyY;
|
|
127
|
+
void targetChain;
|
|
128
|
+
void actionPayload;
|
|
129
|
+
void nonce;
|
|
130
|
+
void signer;
|
|
131
|
+
throw new Error(
|
|
132
|
+
"Direct dispatch not supported on Sui spoke chains. Actions must be dispatched from the Hub (EVM) chain. "
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
async dispatchGasless(signature, publicKeyX, publicKeyY, targetChain, actionPayload, nonce, relayerUrl) {
|
|
136
|
+
const keyHash = this.computeKeyHash(publicKeyX, publicKeyY);
|
|
137
|
+
const messageHash = this.buildMessageHash(keyHash, targetChain, actionPayload, nonce);
|
|
138
|
+
const request = {
|
|
139
|
+
messageHash,
|
|
140
|
+
r: "0x" + signature.r.toString(16).padStart(64, "0"),
|
|
141
|
+
s: "0x" + signature.s.toString(16).padStart(64, "0"),
|
|
142
|
+
publicKeyX: "0x" + publicKeyX.toString(16).padStart(64, "0"),
|
|
143
|
+
publicKeyY: "0x" + publicKeyY.toString(16).padStart(64, "0"),
|
|
144
|
+
targetChain,
|
|
145
|
+
actionPayload,
|
|
146
|
+
nonce: Number(nonce)
|
|
147
|
+
};
|
|
148
|
+
const response = await fetch(`${relayerUrl}/api/v1/submit`, {
|
|
149
|
+
method: "POST",
|
|
150
|
+
headers: { "Content-Type": "application/json" },
|
|
151
|
+
body: JSON.stringify(request)
|
|
152
|
+
});
|
|
153
|
+
if (!response.ok) {
|
|
154
|
+
throw new Error(`Relayer submission failed: ${response.status} ${response.statusText}`);
|
|
155
|
+
}
|
|
156
|
+
const result = await response.json();
|
|
157
|
+
return {
|
|
158
|
+
transactionHash: result.transactionHash ?? result.txHash,
|
|
159
|
+
sequence: BigInt(result.sequence || 0),
|
|
160
|
+
userKeyHash: keyHash,
|
|
161
|
+
targetChain
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
async getVaultAddress(userKeyHash) {
|
|
165
|
+
return this.computeVaultAddress(userKeyHash);
|
|
166
|
+
}
|
|
167
|
+
computeVaultAddress(userKeyHash) {
|
|
168
|
+
const clean = userKeyHash.replace(/^0x/, "").padStart(64, "0");
|
|
169
|
+
return "0x" + clean;
|
|
170
|
+
}
|
|
171
|
+
async vaultExists(_userKeyHash) {
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
async createVault(userKeyHash, signer) {
|
|
175
|
+
void signer;
|
|
176
|
+
throw new Error(
|
|
177
|
+
`Vault creation on Sui must be done via cross-chain message from Hub. Use the Hub client to dispatch a vault creation action targeting Sui (chain ${this.config.wormholeChainId}). KeyHash=${userKeyHash}`
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
async createVaultSponsored(userKeyHash, sponsorPrivateKey, rpcUrl) {
|
|
181
|
+
void userKeyHash;
|
|
182
|
+
void sponsorPrivateKey;
|
|
183
|
+
void rpcUrl;
|
|
184
|
+
throw new Error(
|
|
185
|
+
"Vault creation on Sui must be done via cross-chain message from Hub. Use relayer gasless submission to create vault."
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Create a vault via the relayer (sponsored/gasless)
|
|
190
|
+
* This is the recommended way to create Sui vaults
|
|
191
|
+
*
|
|
192
|
+
* The relayer will dispatch a vault creation action from Hub to Sui spoke
|
|
193
|
+
*/
|
|
194
|
+
async createVaultViaRelayer(userKeyHash, relayerUrl) {
|
|
195
|
+
const response = await fetch(`${relayerUrl}/api/v1/sui/vault`, {
|
|
196
|
+
method: "POST",
|
|
197
|
+
headers: {
|
|
198
|
+
"Content-Type": "application/json"
|
|
199
|
+
},
|
|
200
|
+
body: JSON.stringify({
|
|
201
|
+
userKeyHash,
|
|
202
|
+
chainId: this.config.wormholeChainId
|
|
203
|
+
})
|
|
204
|
+
});
|
|
205
|
+
const result = await response.json();
|
|
206
|
+
if (!response.ok || !result.success) {
|
|
207
|
+
throw new Error(result.error || "Failed to create vault via relayer");
|
|
208
|
+
}
|
|
209
|
+
return {
|
|
210
|
+
address: result.vaultAddress,
|
|
211
|
+
transactionHash: result.transactionHash || "",
|
|
212
|
+
blockNumber: 0,
|
|
213
|
+
gasUsed: 0n,
|
|
214
|
+
alreadyExisted: result.alreadyExists || false,
|
|
215
|
+
sponsoredBy: "relayer"
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Get vault info via relayer (includes existence check)
|
|
220
|
+
*/
|
|
221
|
+
async getVaultViaRelayer(userKeyHash, relayerUrl) {
|
|
222
|
+
const response = await fetch(
|
|
223
|
+
`${relayerUrl}/api/v1/sui/vault/${userKeyHash}?chainId=${this.config.wormholeChainId}`
|
|
224
|
+
);
|
|
225
|
+
if (!response.ok) {
|
|
226
|
+
throw new Error("Failed to get vault info from relayer");
|
|
227
|
+
}
|
|
228
|
+
const result = await response.json();
|
|
229
|
+
return {
|
|
230
|
+
vaultAddress: result.vaultAddress,
|
|
231
|
+
exists: result.exists
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
async estimateVaultCreationGas(_userKeyHash) {
|
|
235
|
+
return 5000n;
|
|
236
|
+
}
|
|
237
|
+
getFactoryAddress() {
|
|
238
|
+
return void 0;
|
|
239
|
+
}
|
|
240
|
+
getImplementationAddress() {
|
|
241
|
+
return void 0;
|
|
242
|
+
}
|
|
243
|
+
// ========================================================================
|
|
244
|
+
// Balance utilities (used by VeridexSDK multichain)
|
|
245
|
+
// ========================================================================
|
|
246
|
+
async getNativeBalance(address) {
|
|
247
|
+
const balance = await this.client.getBalance({ owner: address });
|
|
248
|
+
return BigInt(balance.totalBalance);
|
|
249
|
+
}
|
|
250
|
+
async getTokenBalance(coinType, ownerAddress) {
|
|
251
|
+
const balance = await this.client.getBalance({ owner: ownerAddress, coinType });
|
|
252
|
+
return BigInt(balance.totalBalance);
|
|
253
|
+
}
|
|
254
|
+
getClient() {
|
|
255
|
+
return this.client;
|
|
256
|
+
}
|
|
257
|
+
getPackageId() {
|
|
258
|
+
return this.packageId;
|
|
259
|
+
}
|
|
260
|
+
// ========================================================================
|
|
261
|
+
// Session Management (Issue #13)
|
|
262
|
+
// ========================================================================
|
|
263
|
+
/**
|
|
264
|
+
* Register a session key on the Hub (must be called via Hub client)
|
|
265
|
+
* Sui spokes validate sessions via CCQ, but registration happens on Hub
|
|
266
|
+
*
|
|
267
|
+
* @throws Error - Session management must be done via Hub chain
|
|
268
|
+
*/
|
|
269
|
+
async registerSession(_params) {
|
|
270
|
+
throw new Error(
|
|
271
|
+
"Session registration must be performed on the Hub chain (Base). Use EVMClient connected to the Hub to call registerSession()."
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Revoke a session key on the Hub (must be called via Hub client)
|
|
276
|
+
*
|
|
277
|
+
* @throws Error - Session management must be done via Hub chain
|
|
278
|
+
*/
|
|
279
|
+
async revokeSession(_params) {
|
|
280
|
+
throw new Error(
|
|
281
|
+
"Session revocation must be performed on the Hub chain (Base). Use EVMClient connected to the Hub to call revokeSession()."
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Check if a session is active by querying the Hub
|
|
286
|
+
* This method queries the Hub contract directly for session validation
|
|
287
|
+
*
|
|
288
|
+
* @param userKeyHash - Hash of user's Passkey public key
|
|
289
|
+
* @param sessionKeyHash - Hash of session key to validate
|
|
290
|
+
* @returns Session validation result with expiry and limits
|
|
291
|
+
*/
|
|
292
|
+
async isSessionActive(_userKeyHash, _sessionKeyHash) {
|
|
293
|
+
if (!this.hubRpcUrl || !this.hubContractAddress) {
|
|
294
|
+
throw new Error(
|
|
295
|
+
"Hub configuration required for session validation. Provide hubRpcUrl and hubContractAddress in SuiClientConfig."
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
throw new Error(
|
|
299
|
+
"isSessionActive requires Hub client integration. Use EVMClient.isSessionActive() on the Hub chain, then pass the result to session execution on Sui."
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Get all sessions for a user from the Hub
|
|
304
|
+
*
|
|
305
|
+
* @param userKeyHash - Hash of user's Passkey public key
|
|
306
|
+
* @returns Array of all sessions (active and expired/revoked)
|
|
307
|
+
*/
|
|
308
|
+
async getUserSessions(userKeyHash) {
|
|
309
|
+
if (!this.hubRpcUrl || !this.hubContractAddress) {
|
|
310
|
+
throw new Error(
|
|
311
|
+
"Hub configuration required for session queries. Provide hubRpcUrl and hubContractAddress in SuiClientConfig."
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
throw new Error(
|
|
315
|
+
`getUserSessions requires Hub client integration. Use EVMClient.getUserSessions() on the Hub chain. User: ${userKeyHash}`
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
// ========================================================================
|
|
319
|
+
// Query-Based Execution (Issue #9/#10)
|
|
320
|
+
// ========================================================================
|
|
321
|
+
/**
|
|
322
|
+
* Get user state from Hub (comprehensive state query)
|
|
323
|
+
* Returns key hash, nonce, and last action hash for CCQ validation
|
|
324
|
+
*
|
|
325
|
+
* @param userKeyHash - Hash of user's Passkey public key
|
|
326
|
+
* @returns User state with nonce and last action hash
|
|
327
|
+
*/
|
|
328
|
+
async getUserState(userKeyHash) {
|
|
329
|
+
if (!this.hubRpcUrl || !this.hubContractAddress) {
|
|
330
|
+
throw new Error(
|
|
331
|
+
"Hub configuration required for state queries. Provide hubRpcUrl and hubContractAddress in SuiClientConfig."
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
throw new Error(
|
|
335
|
+
`getUserState requires Hub client integration. Use EVMClient.getUserState() on the Hub chain. User: ${userKeyHash}`
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Get user's last action hash from Hub
|
|
340
|
+
* Used for optimistic execution and nonce validation
|
|
341
|
+
*
|
|
342
|
+
* @param userKeyHash - Hash of user's Passkey public key
|
|
343
|
+
* @returns Last action hash (zero hash if no actions)
|
|
344
|
+
*/
|
|
345
|
+
async getUserLastActionHash(userKeyHash) {
|
|
346
|
+
if (!this.hubRpcUrl || !this.hubContractAddress) {
|
|
347
|
+
throw new Error(
|
|
348
|
+
"Hub configuration required for action hash queries. Provide hubRpcUrl and hubContractAddress in SuiClientConfig."
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
throw new Error(
|
|
352
|
+
`getUserLastActionHash requires Hub client integration. Use EVMClient.getUserLastActionHash() on the Hub chain. User: ${userKeyHash}`
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Execute with query-based validation (faster than VAA, ~23s vs 60-90s)
|
|
357
|
+
* Uses Wormhole CCQ to validate Hub state, then executes on Sui
|
|
358
|
+
*
|
|
359
|
+
* @param params Query execution parameters with CCQ response
|
|
360
|
+
* @returns Dispatch result with transaction hash
|
|
361
|
+
*
|
|
362
|
+
* @remarks
|
|
363
|
+
* Query-based execution flow:
|
|
364
|
+
* 1. Query Hub state via Wormhole CCQ
|
|
365
|
+
* 2. Validate Guardian signatures on query response
|
|
366
|
+
* 3. Execute on Sui with validated state
|
|
367
|
+
* 4. Hub state must be < 60s stale (enforced by QueryVerifier)
|
|
368
|
+
*/
|
|
369
|
+
async executeWithQuery(_params) {
|
|
370
|
+
throw new Error(
|
|
371
|
+
"Query-based execution on Sui requires relayer integration. Use relayer API to submit query-validated transactions. Relayer will call veridex_spoke::execute_with_query on Sui."
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
// ========================================================================
|
|
375
|
+
// Internal helpers
|
|
376
|
+
// ========================================================================
|
|
377
|
+
computeKeyHash(publicKeyX, publicKeyY) {
|
|
378
|
+
const xHex = publicKeyX.toString(16).padStart(64, "0");
|
|
379
|
+
const yHex = publicKeyY.toString(16).padStart(64, "0");
|
|
380
|
+
const combined = Buffer.from(xHex + yHex, "hex");
|
|
381
|
+
const hash = createHash("sha256").update(combined).digest("hex");
|
|
382
|
+
return "0x" + hash;
|
|
383
|
+
}
|
|
384
|
+
buildMessageHash(keyHash, targetChain, actionPayload, nonce) {
|
|
385
|
+
const keyHashBuffer = Buffer.from(keyHash.replace(/^0x/, ""), "hex");
|
|
386
|
+
const targetChainBuffer = Buffer.alloc(2);
|
|
387
|
+
targetChainBuffer.writeUInt16BE(targetChain);
|
|
388
|
+
const payloadBuffer = Buffer.from(actionPayload.replace(/^0x/, ""), "hex");
|
|
389
|
+
const nonceHex = nonce.toString(16).padStart(64, "0");
|
|
390
|
+
const nonceBuffer = Buffer.from(nonceHex, "hex");
|
|
391
|
+
const combined = Buffer.concat([keyHashBuffer, targetChainBuffer, payloadBuffer, nonceBuffer]);
|
|
392
|
+
const hash = createHash("sha256").update(combined).digest("hex");
|
|
393
|
+
return "0x" + hash;
|
|
394
|
+
}
|
|
395
|
+
// ============================================================================
|
|
396
|
+
// Social Recovery Methods (Issue #23)
|
|
397
|
+
// ============================================================================
|
|
398
|
+
//
|
|
399
|
+
// Note: Social recovery is managed on the Hub chain (EVM).
|
|
400
|
+
// Sui spokes receive and execute recovery VAAs broadcast from the Hub.
|
|
401
|
+
// The relayer service handles submitting recovery transactions to Sui.
|
|
402
|
+
//
|
|
403
|
+
// SDK users should use EVMClient methods for guardian management and
|
|
404
|
+
// recovery initiation on the Hub chain.
|
|
405
|
+
// ============================================================================
|
|
406
|
+
/**
|
|
407
|
+
* Get vault object ID by owner key hash
|
|
408
|
+
*
|
|
409
|
+
* @param ownerKeyHash - Owner's passkey hash (32 bytes as hex)
|
|
410
|
+
* @param configObjectId - Shared Config object ID
|
|
411
|
+
* @param registryObjectId - Shared VaultRegistry object ID
|
|
412
|
+
* @returns Vault object ID or null if not found
|
|
413
|
+
*/
|
|
414
|
+
async getVaultId(ownerKeyHash, registryObjectId) {
|
|
415
|
+
try {
|
|
416
|
+
const registryObject = await this.client.getObject({
|
|
417
|
+
id: registryObjectId,
|
|
418
|
+
options: { showContent: true }
|
|
419
|
+
});
|
|
420
|
+
if (!registryObject.data?.content) {
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
console.warn("getVaultId requires dynamic field query - use Move view function");
|
|
424
|
+
return null;
|
|
425
|
+
} catch (error) {
|
|
426
|
+
console.error("Error getting vault ID:", error);
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Get vault owner key hash from vault object
|
|
432
|
+
*
|
|
433
|
+
* @param vaultObjectId - Vault object ID
|
|
434
|
+
* @returns Owner key hash as hex string
|
|
435
|
+
*/
|
|
436
|
+
async getVaultOwner(vaultObjectId) {
|
|
437
|
+
try {
|
|
438
|
+
const vaultObject = await this.client.getObject({
|
|
439
|
+
id: vaultObjectId,
|
|
440
|
+
options: { showContent: true }
|
|
441
|
+
});
|
|
442
|
+
if (!vaultObject.data?.content || vaultObject.data.content.dataType !== "moveObject") {
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
const fields = vaultObject.data.content.fields;
|
|
446
|
+
const ownerKeyHash = fields["owner_key_hash"];
|
|
447
|
+
if (!ownerKeyHash) {
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
return "0x" + Buffer.from(ownerKeyHash).toString("hex");
|
|
451
|
+
} catch (error) {
|
|
452
|
+
console.error("Error getting vault owner:", error);
|
|
453
|
+
return null;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Get authorized signers for a vault
|
|
458
|
+
*
|
|
459
|
+
* @param vaultObjectId - Vault object ID
|
|
460
|
+
* @returns Array of authorized signer key hashes
|
|
461
|
+
*/
|
|
462
|
+
async getAuthorizedSigners(vaultObjectId) {
|
|
463
|
+
try {
|
|
464
|
+
const vaultObject = await this.client.getObject({
|
|
465
|
+
id: vaultObjectId,
|
|
466
|
+
options: { showContent: true }
|
|
467
|
+
});
|
|
468
|
+
if (!vaultObject.data?.content || vaultObject.data.content.dataType !== "moveObject") {
|
|
469
|
+
return [];
|
|
470
|
+
}
|
|
471
|
+
const fields = vaultObject.data.content.fields;
|
|
472
|
+
const authorizedSigners = fields["authorized_signers"];
|
|
473
|
+
if (!authorizedSigners) {
|
|
474
|
+
return [];
|
|
475
|
+
}
|
|
476
|
+
return authorizedSigners.map(
|
|
477
|
+
(signer) => "0x" + Buffer.from(signer).toString("hex")
|
|
478
|
+
);
|
|
479
|
+
} catch (error) {
|
|
480
|
+
console.error("Error getting authorized signers:", error);
|
|
481
|
+
return [];
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Check if a VAA has been processed (for replay protection)
|
|
486
|
+
*
|
|
487
|
+
* @param vaaHash - VAA hash as hex string
|
|
488
|
+
* @param processedVaasObjectId - ProcessedVAAs shared object ID
|
|
489
|
+
* @returns Whether the VAA has been processed
|
|
490
|
+
*/
|
|
491
|
+
async isVaaProcessed(vaaHash, processedVaasObjectId) {
|
|
492
|
+
try {
|
|
493
|
+
const processedObject = await this.client.getObject({
|
|
494
|
+
id: processedVaasObjectId,
|
|
495
|
+
options: { showContent: true }
|
|
496
|
+
});
|
|
497
|
+
if (!processedObject.data?.content) {
|
|
498
|
+
return false;
|
|
499
|
+
}
|
|
500
|
+
console.warn("isVaaProcessed requires dynamic field query");
|
|
501
|
+
return false;
|
|
502
|
+
} catch (error) {
|
|
503
|
+
console.error("Error checking VAA status:", error);
|
|
504
|
+
return false;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Check if protocol is paused
|
|
509
|
+
*
|
|
510
|
+
* @param configObjectId - Config shared object ID
|
|
511
|
+
* @returns Whether the protocol is paused
|
|
512
|
+
*/
|
|
513
|
+
async isProtocolPaused(configObjectId) {
|
|
514
|
+
try {
|
|
515
|
+
const configObject = await this.client.getObject({
|
|
516
|
+
id: configObjectId,
|
|
517
|
+
options: { showContent: true }
|
|
518
|
+
});
|
|
519
|
+
if (!configObject.data?.content || configObject.data.content.dataType !== "moveObject") {
|
|
520
|
+
return false;
|
|
521
|
+
}
|
|
522
|
+
const fields = configObject.data.content.fields;
|
|
523
|
+
return fields["paused"] === true;
|
|
524
|
+
} catch (error) {
|
|
525
|
+
console.error("Error checking pause status:", error);
|
|
526
|
+
return false;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
export {
|
|
531
|
+
SuiClient
|
|
532
|
+
};
|
|
533
|
+
//# sourceMappingURL=index.mjs.map
|