@qorechain/sdk 0.1.0
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 +152 -0
- package/dist/index.cjs +1252 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1532 -0
- package/dist/index.d.ts +1532 -0
- package/dist/index.js +1191 -0
- package/dist/index.js.map +1 -0
- package/package.json +65 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1252 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var stargate = require('@cosmjs/stargate');
|
|
4
|
+
var protoSigning = require('@cosmjs/proto-signing');
|
|
5
|
+
var tx = require('cosmjs-types/cosmos/bank/v1beta1/tx');
|
|
6
|
+
var cosmwasmStargate = require('@cosmjs/cosmwasm-stargate');
|
|
7
|
+
var bech32 = require('bech32');
|
|
8
|
+
var bip39 = require('@scure/bip39');
|
|
9
|
+
var english = require('@scure/bip39/wordlists/english');
|
|
10
|
+
var bip32 = require('@scure/bip32');
|
|
11
|
+
var slip10_js = require('micro-key-producer/slip10.js');
|
|
12
|
+
var secp256k1 = require('@noble/curves/secp256k1');
|
|
13
|
+
var sha256 = require('@noble/hashes/sha256');
|
|
14
|
+
var ripemd160 = require('@noble/hashes/ripemd160');
|
|
15
|
+
var sha3 = require('@noble/hashes/sha3');
|
|
16
|
+
var base = require('@scure/base');
|
|
17
|
+
var mlDsa_js = require('@noble/post-quantum/ml-dsa.js');
|
|
18
|
+
var utils = require('@noble/hashes/utils');
|
|
19
|
+
var any = require('cosmjs-types/google/protobuf/any');
|
|
20
|
+
var tx$1 = require('cosmjs-types/cosmos/tx/v1beta1/tx');
|
|
21
|
+
var amino = require('@cosmjs/amino');
|
|
22
|
+
var signing = require('cosmjs-types/cosmos/tx/signing/v1beta1/signing');
|
|
23
|
+
|
|
24
|
+
// src/config/networks.ts
|
|
25
|
+
var BECH32 = {
|
|
26
|
+
account: "qor",
|
|
27
|
+
validator: "qorvaloper",
|
|
28
|
+
consensus: "qorvalcons"
|
|
29
|
+
};
|
|
30
|
+
var COIN = {
|
|
31
|
+
display: "QOR",
|
|
32
|
+
base: "uqor",
|
|
33
|
+
exponent: 6
|
|
34
|
+
};
|
|
35
|
+
var NETWORKS = {
|
|
36
|
+
testnet: {
|
|
37
|
+
name: "testnet",
|
|
38
|
+
live: true,
|
|
39
|
+
chainId: "qorechain-diana",
|
|
40
|
+
bech32: BECH32,
|
|
41
|
+
coin: COIN,
|
|
42
|
+
endpoints: {
|
|
43
|
+
rest: "http://localhost:1317",
|
|
44
|
+
grpc: "http://localhost:9090",
|
|
45
|
+
rpc: "http://localhost:26657",
|
|
46
|
+
evmRpc: "http://localhost:8545",
|
|
47
|
+
evmWs: "ws://localhost:8546",
|
|
48
|
+
svmRpc: "http://localhost:8899"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
mainnet: {
|
|
52
|
+
name: "mainnet",
|
|
53
|
+
live: true,
|
|
54
|
+
chainId: "qorechain-vladi",
|
|
55
|
+
bech32: BECH32,
|
|
56
|
+
coin: COIN,
|
|
57
|
+
endpoints: {
|
|
58
|
+
rest: "http://localhost:1317",
|
|
59
|
+
grpc: "http://localhost:9090",
|
|
60
|
+
rpc: "http://localhost:26657",
|
|
61
|
+
evmRpc: "http://localhost:8545",
|
|
62
|
+
evmWs: "ws://localhost:8546",
|
|
63
|
+
svmRpc: "http://localhost:8899"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
function getNetwork(name) {
|
|
68
|
+
return NETWORKS[name];
|
|
69
|
+
}
|
|
70
|
+
function listNetworks() {
|
|
71
|
+
return Object.keys(NETWORKS);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/query/http.ts
|
|
75
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
76
|
+
var DEFAULT_RETRIES = 2;
|
|
77
|
+
var DEFAULT_RETRY_DELAY_MS = 250;
|
|
78
|
+
var QoreHttpError = class _QoreHttpError extends Error {
|
|
79
|
+
constructor(status, url, body) {
|
|
80
|
+
super(`HTTP ${status} for ${url}`);
|
|
81
|
+
this.name = "QoreHttpError";
|
|
82
|
+
this.status = status;
|
|
83
|
+
this.url = url;
|
|
84
|
+
this.body = body;
|
|
85
|
+
Object.setPrototypeOf(this, _QoreHttpError.prototype);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
function resolveFetch(opts) {
|
|
89
|
+
const f = opts?.fetch ?? globalThis.fetch;
|
|
90
|
+
if (!f) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
"no fetch implementation available \u2014 pass `fetch` in the client options"
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
return f;
|
|
96
|
+
}
|
|
97
|
+
function buildUrl(base, query) {
|
|
98
|
+
if (!query) return base;
|
|
99
|
+
const parts = [];
|
|
100
|
+
for (const [key, value] of Object.entries(query)) {
|
|
101
|
+
if (value === void 0 || value === null) continue;
|
|
102
|
+
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
|
|
103
|
+
}
|
|
104
|
+
if (parts.length === 0) return base;
|
|
105
|
+
const sep = base.includes("?") ? "&" : "?";
|
|
106
|
+
return `${base}${sep}${parts.join("&")}`;
|
|
107
|
+
}
|
|
108
|
+
function joinUrl(base, path) {
|
|
109
|
+
const left = base.replace(/\/+$/, "");
|
|
110
|
+
const right = path.replace(/^\/+/, "");
|
|
111
|
+
return `${left}/${right}`;
|
|
112
|
+
}
|
|
113
|
+
function delay(ms) {
|
|
114
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
115
|
+
}
|
|
116
|
+
function isRetryableStatus(status) {
|
|
117
|
+
return status >= 500 && status < 600;
|
|
118
|
+
}
|
|
119
|
+
async function attempt(url, init, fetchImpl, timeoutMs) {
|
|
120
|
+
const controller = timeoutMs > 0 ? new AbortController() : void 0;
|
|
121
|
+
const timer = controller && timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : void 0;
|
|
122
|
+
try {
|
|
123
|
+
const res = await fetchImpl(url, {
|
|
124
|
+
...init,
|
|
125
|
+
signal: controller?.signal
|
|
126
|
+
});
|
|
127
|
+
if (!res.ok) {
|
|
128
|
+
let body;
|
|
129
|
+
try {
|
|
130
|
+
body = await res.text();
|
|
131
|
+
} catch {
|
|
132
|
+
body = void 0;
|
|
133
|
+
}
|
|
134
|
+
throw new QoreHttpError(res.status, url, body);
|
|
135
|
+
}
|
|
136
|
+
return await res.json();
|
|
137
|
+
} finally {
|
|
138
|
+
if (timer) clearTimeout(timer);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async function withRetry(url, init, opts) {
|
|
142
|
+
const fetchImpl = resolveFetch(opts);
|
|
143
|
+
const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
144
|
+
const retries = opts?.retries ?? DEFAULT_RETRIES;
|
|
145
|
+
const retryDelayMs = opts?.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;
|
|
146
|
+
let lastError;
|
|
147
|
+
for (let i = 0; i <= retries; i++) {
|
|
148
|
+
try {
|
|
149
|
+
return await attempt(url, init, fetchImpl, timeoutMs);
|
|
150
|
+
} catch (err) {
|
|
151
|
+
lastError = err;
|
|
152
|
+
const retryable = err instanceof QoreHttpError ? isRetryableStatus(err.status) : true;
|
|
153
|
+
if (!retryable || i === retries) throw err;
|
|
154
|
+
if (retryDelayMs > 0) await delay(retryDelayMs);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
throw lastError;
|
|
158
|
+
}
|
|
159
|
+
function getJson(url, opts) {
|
|
160
|
+
const fullUrl = buildUrl(url, opts?.query);
|
|
161
|
+
const init = {
|
|
162
|
+
method: "GET",
|
|
163
|
+
headers: { accept: "application/json", ...opts?.headers ?? {} }
|
|
164
|
+
};
|
|
165
|
+
return withRetry(fullUrl, init, opts);
|
|
166
|
+
}
|
|
167
|
+
function postJsonRpc(url, body, opts) {
|
|
168
|
+
const init = {
|
|
169
|
+
method: "POST",
|
|
170
|
+
headers: {
|
|
171
|
+
"content-type": "application/json",
|
|
172
|
+
accept: "application/json",
|
|
173
|
+
...opts?.headers ?? {}
|
|
174
|
+
},
|
|
175
|
+
body: JSON.stringify(body)
|
|
176
|
+
};
|
|
177
|
+
return withRetry(url, init, opts);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// src/query/rest.ts
|
|
181
|
+
function paginationQuery(p) {
|
|
182
|
+
const q = {};
|
|
183
|
+
if (p?.key !== void 0) q["pagination.key"] = p.key;
|
|
184
|
+
if (p?.limit !== void 0) q["pagination.limit"] = p.limit;
|
|
185
|
+
return q;
|
|
186
|
+
}
|
|
187
|
+
var RestClient = class {
|
|
188
|
+
/**
|
|
189
|
+
* @param baseUrl - The network's REST endpoint (e.g. `endpoints.rest`).
|
|
190
|
+
* @param opts - Injectable `fetch`, timeout, and retry settings.
|
|
191
|
+
*/
|
|
192
|
+
constructor(baseUrl, opts = {}) {
|
|
193
|
+
this.baseUrl = baseUrl;
|
|
194
|
+
this.opts = opts;
|
|
195
|
+
}
|
|
196
|
+
/** Injectable fetch passthrough so the escape hatch reuses the same transport. */
|
|
197
|
+
get fetchImpl() {
|
|
198
|
+
return this.opts.fetch;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Generic GET escape hatch for any documented REST route.
|
|
202
|
+
*
|
|
203
|
+
* @param path - Path beginning with `/` (e.g. `/qorechain/foo/v1/bar`).
|
|
204
|
+
* Embedded path params must be URL-encoded by the caller.
|
|
205
|
+
* @param query - Optional query parameters.
|
|
206
|
+
*/
|
|
207
|
+
get(path, query) {
|
|
208
|
+
return getJson(joinUrl(this.baseUrl, path), {
|
|
209
|
+
...this.opts,
|
|
210
|
+
query
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
// --- Standard Cosmos bank ------------------------------------------------
|
|
214
|
+
/** All balances of `address` (`/cosmos/bank/v1beta1/balances/{address}`). */
|
|
215
|
+
getAllBalances(address, opts) {
|
|
216
|
+
return this.get(
|
|
217
|
+
`/cosmos/bank/v1beta1/balances/${encodeURIComponent(address)}`,
|
|
218
|
+
paginationQuery(opts?.pagination)
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
/** Balance of `address` in `denom` (`.../balances/{address}/by_denom`). */
|
|
222
|
+
getBalance(address, denom) {
|
|
223
|
+
return this.get(
|
|
224
|
+
`/cosmos/bank/v1beta1/balances/${encodeURIComponent(address)}/by_denom`,
|
|
225
|
+
{ denom }
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
// --- Custom QoreChain module reads ---------------------------------------
|
|
229
|
+
/** QCAI fee/network stats (`/qorechain/ai/v1/stats`). */
|
|
230
|
+
getAiStats() {
|
|
231
|
+
return this.get("/qorechain/ai/v1/stats");
|
|
232
|
+
}
|
|
233
|
+
/** QCAI fee estimate for an urgency level (`/qorechain/ai/v1/fee-estimate`). */
|
|
234
|
+
getFeeEstimate(urgency) {
|
|
235
|
+
return this.get("/qorechain/ai/v1/fee-estimate", { urgency });
|
|
236
|
+
}
|
|
237
|
+
/** Supported bridge chains (`/qorechain/bridge/v1/chains`). */
|
|
238
|
+
getBridgeChains() {
|
|
239
|
+
return this.get("/qorechain/bridge/v1/chains");
|
|
240
|
+
}
|
|
241
|
+
/** PQC account record (`/qorechain/pqc/v1/accounts/{address}`). */
|
|
242
|
+
getPqcAccount(address) {
|
|
243
|
+
return this.get(`/qorechain/pqc/v1/accounts/${encodeURIComponent(address)}`);
|
|
244
|
+
}
|
|
245
|
+
/** Validator reputation (`/qorechain/reputation/v1/validators/{address}`). */
|
|
246
|
+
getReputation(validatorAddress) {
|
|
247
|
+
return this.get(
|
|
248
|
+
`/qorechain/reputation/v1/validators/${encodeURIComponent(validatorAddress)}`
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
/** Token burn statistics (`/qorechain/burn/v1/stats`). */
|
|
252
|
+
getBurnStats() {
|
|
253
|
+
return this.get("/qorechain/burn/v1/stats");
|
|
254
|
+
}
|
|
255
|
+
/** xQORE staking position (`/qorechain/xqore/v1/position/{address}`). */
|
|
256
|
+
getXqorePosition(address) {
|
|
257
|
+
return this.get(`/qorechain/xqore/v1/position/${encodeURIComponent(address)}`);
|
|
258
|
+
}
|
|
259
|
+
/** Current inflation rate (`/qorechain/inflation/v1/rate`). */
|
|
260
|
+
getInflationRate() {
|
|
261
|
+
return this.get("/qorechain/inflation/v1/rate");
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
// src/query/jsonrpc.ts
|
|
266
|
+
var JsonRpcError = class _JsonRpcError extends Error {
|
|
267
|
+
constructor(code, message, data) {
|
|
268
|
+
super(message);
|
|
269
|
+
this.name = "JsonRpcError";
|
|
270
|
+
this.code = code;
|
|
271
|
+
this.data = data;
|
|
272
|
+
Object.setPrototypeOf(this, _JsonRpcError.prototype);
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
var JsonRpcClient = class {
|
|
276
|
+
constructor(url, opts = {}) {
|
|
277
|
+
this.nextId = 1;
|
|
278
|
+
this.url = url;
|
|
279
|
+
this.opts = opts;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Invoke a JSON-RPC method and return its `result`.
|
|
283
|
+
*
|
|
284
|
+
* @param method - Wire method name (e.g. `"eth_chainId"`).
|
|
285
|
+
* @param params - Positional params; defaults to an empty array.
|
|
286
|
+
* @throws {@link JsonRpcError} when the response contains an `error`.
|
|
287
|
+
* @throws {@link QoreHttpError} on a non-2xx transport response.
|
|
288
|
+
*/
|
|
289
|
+
async call(method, params = []) {
|
|
290
|
+
const id = this.nextId++;
|
|
291
|
+
const body = { jsonrpc: "2.0", id, method, params };
|
|
292
|
+
const res = await postJsonRpc(this.url, body, this.opts);
|
|
293
|
+
if (res.error) {
|
|
294
|
+
throw new JsonRpcError(res.error.code, res.error.message, res.error.data);
|
|
295
|
+
}
|
|
296
|
+
return res.result;
|
|
297
|
+
}
|
|
298
|
+
/** `eth_chainId` — the chain id as a hex quantity string. */
|
|
299
|
+
ethChainId() {
|
|
300
|
+
return this.call("eth_chainId", []);
|
|
301
|
+
}
|
|
302
|
+
/** `eth_blockNumber` — the latest block height as a hex quantity string. */
|
|
303
|
+
ethBlockNumber() {
|
|
304
|
+
return this.call("eth_blockNumber", []);
|
|
305
|
+
}
|
|
306
|
+
/** `eth_getBalance` — wei balance of `address` as a hex quantity string. */
|
|
307
|
+
ethGetBalance(address, block = "latest") {
|
|
308
|
+
return this.call("eth_getBalance", [address, block]);
|
|
309
|
+
}
|
|
310
|
+
/** `net_version` — the network id as a decimal string. */
|
|
311
|
+
netVersion() {
|
|
312
|
+
return this.call("net_version", []);
|
|
313
|
+
}
|
|
314
|
+
/** `web3_clientVersion` — the node client version string. */
|
|
315
|
+
web3ClientVersion() {
|
|
316
|
+
return this.call("web3_clientVersion", []);
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
// src/query/qor.ts
|
|
321
|
+
var QorClient = class extends JsonRpcClient {
|
|
322
|
+
constructor(url, opts = {}) {
|
|
323
|
+
super(url, opts);
|
|
324
|
+
}
|
|
325
|
+
/** `qor_getPQCKeyStatus` — PQC key registration status for an address. */
|
|
326
|
+
getPqcKeyStatus(address) {
|
|
327
|
+
return this.call("qor_getPQCKeyStatus", [address]);
|
|
328
|
+
}
|
|
329
|
+
/** `qor_getHybridSignatureMode` — active hybrid-signature policy. */
|
|
330
|
+
getHybridSignatureMode() {
|
|
331
|
+
return this.call("qor_getHybridSignatureMode", []);
|
|
332
|
+
}
|
|
333
|
+
/** `qor_getAIStats` — QCAI engine statistics. */
|
|
334
|
+
getAiStats() {
|
|
335
|
+
return this.call("qor_getAIStats", []);
|
|
336
|
+
}
|
|
337
|
+
/** `qor_getCrossVMMessage` — a cross-VM message by id. */
|
|
338
|
+
getCrossVmMessage(messageId) {
|
|
339
|
+
return this.call("qor_getCrossVMMessage", [messageId]);
|
|
340
|
+
}
|
|
341
|
+
/** `qor_getReputationScore` — reputation score for a validator. */
|
|
342
|
+
getReputationScore(validator) {
|
|
343
|
+
return this.call("qor_getReputationScore", [validator]);
|
|
344
|
+
}
|
|
345
|
+
/** `qor_getLayerInfo` — info about a chain layer. */
|
|
346
|
+
getLayerInfo(layerId) {
|
|
347
|
+
return this.call("qor_getLayerInfo", [layerId]);
|
|
348
|
+
}
|
|
349
|
+
/** `qor_getBridgeStatus` — bridge status for a remote chain id. */
|
|
350
|
+
getBridgeStatus(chainId) {
|
|
351
|
+
return this.call("qor_getBridgeStatus", [chainId]);
|
|
352
|
+
}
|
|
353
|
+
/** `qor_getRLAgentStatus` — reinforcement-learning agent status. */
|
|
354
|
+
getRlAgentStatus() {
|
|
355
|
+
return this.call("qor_getRLAgentStatus", []);
|
|
356
|
+
}
|
|
357
|
+
/** `qor_getRLObservation` — latest RL observation vector. */
|
|
358
|
+
getRlObservation() {
|
|
359
|
+
return this.call("qor_getRLObservation", []);
|
|
360
|
+
}
|
|
361
|
+
/** `qor_getRLReward` — latest RL reward signal. */
|
|
362
|
+
getRlReward() {
|
|
363
|
+
return this.call("qor_getRLReward", []);
|
|
364
|
+
}
|
|
365
|
+
/** `qor_getPoolClassification` — validator pool classification. */
|
|
366
|
+
getPoolClassification(validator) {
|
|
367
|
+
return this.call("qor_getPoolClassification", [validator]);
|
|
368
|
+
}
|
|
369
|
+
/** `qor_getBurnStats` — token burn statistics. */
|
|
370
|
+
getBurnStats() {
|
|
371
|
+
return this.call("qor_getBurnStats", []);
|
|
372
|
+
}
|
|
373
|
+
/** `qor_getXQOREPosition` — xQORE staking position for an address. */
|
|
374
|
+
getXqorePosition(address) {
|
|
375
|
+
return this.call("qor_getXQOREPosition", [address]);
|
|
376
|
+
}
|
|
377
|
+
/** `qor_getInflationRate` — current inflation rate. */
|
|
378
|
+
getInflationRate() {
|
|
379
|
+
return this.call("qor_getInflationRate", []);
|
|
380
|
+
}
|
|
381
|
+
/** `qor_getTokenomicsOverview` — aggregate tokenomics snapshot. */
|
|
382
|
+
getTokenomicsOverview() {
|
|
383
|
+
return this.call("qor_getTokenomicsOverview", []);
|
|
384
|
+
}
|
|
385
|
+
/** `qor_getRollupStatus` — status of a rollup. */
|
|
386
|
+
getRollupStatus(rollupId) {
|
|
387
|
+
return this.call("qor_getRollupStatus", [rollupId]);
|
|
388
|
+
}
|
|
389
|
+
/** `qor_listRollups` — all known rollups. */
|
|
390
|
+
listRollups() {
|
|
391
|
+
return this.call("qor_listRollups", []);
|
|
392
|
+
}
|
|
393
|
+
/** `qor_getSettlementBatch` — a settlement batch by rollup and index. */
|
|
394
|
+
getSettlementBatch(rollupId, batchIndex) {
|
|
395
|
+
return this.call("qor_getSettlementBatch", [rollupId, batchIndex]);
|
|
396
|
+
}
|
|
397
|
+
/** `qor_suggestRollupProfile` — a suggested rollup profile for a use case. */
|
|
398
|
+
suggestRollupProfile(useCase) {
|
|
399
|
+
return this.call("qor_suggestRollupProfile", [useCase]);
|
|
400
|
+
}
|
|
401
|
+
/** `qor_getDABlobStatus` — data-availability blob status by rollup and index. */
|
|
402
|
+
getDaBlobStatus(rollupId, blobIndex) {
|
|
403
|
+
return this.call("qor_getDABlobStatus", [rollupId, blobIndex]);
|
|
404
|
+
}
|
|
405
|
+
/** `qor_getBTCStakingPosition` — BTC staking position for an address. */
|
|
406
|
+
getBtcStakingPosition(address) {
|
|
407
|
+
return this.call("qor_getBTCStakingPosition", [address]);
|
|
408
|
+
}
|
|
409
|
+
/** `qor_getAbstractAccount` — account-abstraction record for an address. */
|
|
410
|
+
getAbstractAccount(address) {
|
|
411
|
+
return this.call("qor_getAbstractAccount", [address]);
|
|
412
|
+
}
|
|
413
|
+
/** `qor_getFairBlockStatus` — fair-ordering / fair-block status. */
|
|
414
|
+
getFairBlockStatus() {
|
|
415
|
+
return this.call("qor_getFairBlockStatus", []);
|
|
416
|
+
}
|
|
417
|
+
/** `qor_getGasAbstractionConfig` — gas-abstraction configuration. */
|
|
418
|
+
getGasAbstractionConfig() {
|
|
419
|
+
return this.call("qor_getGasAbstractionConfig", []);
|
|
420
|
+
}
|
|
421
|
+
/** `qor_getLaneConfiguration` — mempool lane configuration. */
|
|
422
|
+
getLaneConfiguration() {
|
|
423
|
+
return this.call("qor_getLaneConfiguration", []);
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
// src/tx/fees.ts
|
|
428
|
+
var STATIC_FALLBACK = {
|
|
429
|
+
/** Fallback gas price, in base denom per unit of gas. */
|
|
430
|
+
gasPrice: "0.025",
|
|
431
|
+
/** Base denomination fees are paid in. */
|
|
432
|
+
denom: "uqor",
|
|
433
|
+
/** Default gas limit when the caller does not supply one. */
|
|
434
|
+
gas: "200000"
|
|
435
|
+
};
|
|
436
|
+
function gasString(gas) {
|
|
437
|
+
if (gas === void 0) return STATIC_FALLBACK.gas;
|
|
438
|
+
return typeof gas === "number" ? Math.ceil(gas).toString() : gas;
|
|
439
|
+
}
|
|
440
|
+
function staticFee(gas, gasPrice, denom) {
|
|
441
|
+
const gasUnits = BigInt(gas);
|
|
442
|
+
const [intPart, fracPart = ""] = gasPrice.split(".");
|
|
443
|
+
const scale = 10n ** BigInt(fracPart.length);
|
|
444
|
+
const numerator = BigInt(intPart || "0") * scale + BigInt(fracPart || "0");
|
|
445
|
+
const raw = gasUnits * numerator;
|
|
446
|
+
const amount = (raw + scale - 1n) / scale;
|
|
447
|
+
return { amount: [{ denom, amount: amount.toString() }], gas };
|
|
448
|
+
}
|
|
449
|
+
async function estimateFee(rest, opts = {}) {
|
|
450
|
+
const urgency = opts.urgency ?? "normal";
|
|
451
|
+
const gas = gasString(opts.gas);
|
|
452
|
+
const denom = opts.denom ?? STATIC_FALLBACK.denom;
|
|
453
|
+
const fallbackGasPrice = opts.fallbackGasPrice ?? STATIC_FALLBACK.gasPrice;
|
|
454
|
+
try {
|
|
455
|
+
const res = await rest.getFeeEstimate(urgency);
|
|
456
|
+
const suggested = res?.suggested_fee_uqor;
|
|
457
|
+
if (suggested !== void 0 && suggested !== null) {
|
|
458
|
+
const amount = typeof suggested === "number" ? Math.ceil(suggested).toString() : suggested;
|
|
459
|
+
if (amount !== "" && amount !== "0") {
|
|
460
|
+
return { amount: [{ denom, amount }], gas };
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
} catch {
|
|
464
|
+
}
|
|
465
|
+
return staticFee(gas, fallbackGasPrice, denom);
|
|
466
|
+
}
|
|
467
|
+
var MSG_SEND_TYPE_URL = "/cosmos.bank.v1beta1.MsgSend";
|
|
468
|
+
var TxClient = class _TxClient {
|
|
469
|
+
constructor(opts) {
|
|
470
|
+
this.client = opts.signingClient;
|
|
471
|
+
this.senderAddress = opts.senderAddress;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Connect to a network's consensus RPC and build a {@link TxClient}.
|
|
475
|
+
*
|
|
476
|
+
* Resolves the signer's first account as the sender address and merges any
|
|
477
|
+
* `registryTypes` into cosmjs's default message registry.
|
|
478
|
+
*/
|
|
479
|
+
static async connect(opts) {
|
|
480
|
+
const registry = new protoSigning.Registry(stargate.defaultRegistryTypes);
|
|
481
|
+
for (const [typeUrl, type] of opts.registryTypes ?? []) {
|
|
482
|
+
registry.register(typeUrl, type);
|
|
483
|
+
}
|
|
484
|
+
const client = await stargate.SigningStargateClient.connectWithSigner(
|
|
485
|
+
opts.rpcEndpoint,
|
|
486
|
+
opts.signer,
|
|
487
|
+
{ registry, ...opts.clientOptions }
|
|
488
|
+
);
|
|
489
|
+
const accounts = await opts.signer.getAccounts();
|
|
490
|
+
if (accounts.length === 0) {
|
|
491
|
+
throw new Error("signer exposes no accounts");
|
|
492
|
+
}
|
|
493
|
+
return new _TxClient({
|
|
494
|
+
signingClient: client,
|
|
495
|
+
senderAddress: accounts[0].address
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Simulate the given messages and return the estimated gas units.
|
|
500
|
+
*
|
|
501
|
+
* Use the result (with a safety multiplier) as the `gas` for `estimateFee`.
|
|
502
|
+
*/
|
|
503
|
+
simulate(messages, opts = {}) {
|
|
504
|
+
return this.client.simulate(this.senderAddress, messages, opts.memo);
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Sign and broadcast the given messages with an explicit fee.
|
|
508
|
+
*
|
|
509
|
+
* Broadcast mode maps onto cosmjs transports:
|
|
510
|
+
* - `commit` (default): polls until the tx lands in a block; returns the full
|
|
511
|
+
* result and throws on a non-zero delivery code.
|
|
512
|
+
* - `sync` / `async`: returns after mempool submission with just the tx hash.
|
|
513
|
+
*/
|
|
514
|
+
async signAndBroadcast(messages, fee, memo = "", opts = {}) {
|
|
515
|
+
const mode = opts.mode ?? "commit";
|
|
516
|
+
if (mode === "commit") {
|
|
517
|
+
const res = await this.client.signAndBroadcast(
|
|
518
|
+
this.senderAddress,
|
|
519
|
+
messages,
|
|
520
|
+
fee,
|
|
521
|
+
memo
|
|
522
|
+
);
|
|
523
|
+
if (res.code !== 0) {
|
|
524
|
+
throw new Error(
|
|
525
|
+
`transaction failed with code ${res.code}: ${res.rawLog ?? "(no log)"} (hash ${res.transactionHash})`
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
return {
|
|
529
|
+
transactionHash: res.transactionHash,
|
|
530
|
+
code: res.code,
|
|
531
|
+
height: res.height,
|
|
532
|
+
gasUsed: res.gasUsed,
|
|
533
|
+
gasWanted: res.gasWanted,
|
|
534
|
+
rawLog: res.rawLog
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
const hash = await this.client.signAndBroadcastSync(
|
|
538
|
+
this.senderAddress,
|
|
539
|
+
messages,
|
|
540
|
+
fee,
|
|
541
|
+
memo
|
|
542
|
+
);
|
|
543
|
+
return { transactionHash: hash, code: 0 };
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Send `amount` to `toAddress` via a bank `MsgSend`, then broadcast.
|
|
547
|
+
*
|
|
548
|
+
* Constructs `/cosmos.bank.v1beta1.MsgSend` from this client's sender address.
|
|
549
|
+
*/
|
|
550
|
+
bankSend(toAddress, amount, opts) {
|
|
551
|
+
const msg = {
|
|
552
|
+
typeUrl: MSG_SEND_TYPE_URL,
|
|
553
|
+
value: tx.MsgSend.fromPartial({
|
|
554
|
+
fromAddress: this.senderAddress,
|
|
555
|
+
toAddress,
|
|
556
|
+
amount
|
|
557
|
+
})
|
|
558
|
+
};
|
|
559
|
+
return this.signAndBroadcast([msg], opts.fee, opts.memo ?? "", {
|
|
560
|
+
mode: opts.mode
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
/** Disconnect the underlying client's transport, if it supports it. */
|
|
564
|
+
disconnect() {
|
|
565
|
+
this.client.disconnect?.();
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
function createCosmWasmClient(rpcUrl) {
|
|
569
|
+
return cosmwasmStargate.CosmWasmClient.connect(rpcUrl);
|
|
570
|
+
}
|
|
571
|
+
function connectCosmWasmSigner(rpcUrl, signer) {
|
|
572
|
+
return cosmwasmStargate.SigningCosmWasmClient.connectWithSigner(rpcUrl, signer);
|
|
573
|
+
}
|
|
574
|
+
function queryContractSmart(client, contractAddress, queryMsg) {
|
|
575
|
+
return client.queryContractSmart(contractAddress, queryMsg);
|
|
576
|
+
}
|
|
577
|
+
function getContractInfo(client, contractAddress) {
|
|
578
|
+
return client.getContract(contractAddress);
|
|
579
|
+
}
|
|
580
|
+
function instantiate(client, sender, codeId, initMsg, label, opts = {}) {
|
|
581
|
+
const { fee = "auto", memo, funds, admin } = opts;
|
|
582
|
+
const options = {};
|
|
583
|
+
if (funds !== void 0) options.funds = funds;
|
|
584
|
+
if (admin !== void 0) options.admin = admin;
|
|
585
|
+
if (memo !== void 0) options.memo = memo;
|
|
586
|
+
return client.instantiate(
|
|
587
|
+
sender,
|
|
588
|
+
codeId,
|
|
589
|
+
initMsg,
|
|
590
|
+
label,
|
|
591
|
+
fee,
|
|
592
|
+
options
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
function execute(client, sender, contractAddress, msg, fee, funds) {
|
|
596
|
+
return client.execute(
|
|
597
|
+
sender,
|
|
598
|
+
contractAddress,
|
|
599
|
+
msg,
|
|
600
|
+
fee,
|
|
601
|
+
void 0,
|
|
602
|
+
funds
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
function uploadCode(client, sender, wasmBytes, fee) {
|
|
606
|
+
return client.upload(sender, wasmBytes, fee);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// src/query/crossvm.ts
|
|
610
|
+
function getCrossVmMessage(rest, id) {
|
|
611
|
+
return rest.get(
|
|
612
|
+
`/qorechain/crossvm/v1/message/${encodeURIComponent(id)}`
|
|
613
|
+
);
|
|
614
|
+
}
|
|
615
|
+
function getPendingCrossVmMessages(rest) {
|
|
616
|
+
return rest.get("/qorechain/crossvm/v1/pending");
|
|
617
|
+
}
|
|
618
|
+
function getCrossVmParams(rest) {
|
|
619
|
+
return rest.get("/qorechain/crossvm/v1/params");
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// src/client.ts
|
|
623
|
+
function resolveNetwork(opts) {
|
|
624
|
+
const name = opts.network ?? "testnet";
|
|
625
|
+
const overrides = opts.endpoints ?? {};
|
|
626
|
+
const base = getNetwork(name);
|
|
627
|
+
return {
|
|
628
|
+
...base,
|
|
629
|
+
chainId: opts.chainId ?? base.chainId,
|
|
630
|
+
endpoints: { ...base.endpoints, ...overrides }
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
function requireEndpoint(endpoints, key) {
|
|
634
|
+
const value = endpoints?.[key];
|
|
635
|
+
if (!value) {
|
|
636
|
+
throw new Error(
|
|
637
|
+
`endpoint "${key}" is not configured \u2014 pass it via createClient({ endpoints: { ${key}: "..." } })`
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
return value;
|
|
641
|
+
}
|
|
642
|
+
function createClient(opts = {}) {
|
|
643
|
+
const network = resolveNetwork(opts);
|
|
644
|
+
const httpOptions = { ...opts.http, fetch: opts.fetch };
|
|
645
|
+
let restClient;
|
|
646
|
+
let evmClient;
|
|
647
|
+
let qorClient;
|
|
648
|
+
const getRest = () => {
|
|
649
|
+
if (!restClient) {
|
|
650
|
+
restClient = new RestClient(
|
|
651
|
+
requireEndpoint(network.endpoints, "rest"),
|
|
652
|
+
httpOptions
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
return restClient;
|
|
656
|
+
};
|
|
657
|
+
const getEvm = () => {
|
|
658
|
+
if (!evmClient) {
|
|
659
|
+
evmClient = new JsonRpcClient(
|
|
660
|
+
requireEndpoint(network.endpoints, "evmRpc"),
|
|
661
|
+
httpOptions
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
return evmClient;
|
|
665
|
+
};
|
|
666
|
+
const getQor = () => {
|
|
667
|
+
if (!qorClient) {
|
|
668
|
+
qorClient = new QorClient(
|
|
669
|
+
requireEndpoint(network.endpoints, "evmRpc"),
|
|
670
|
+
httpOptions
|
|
671
|
+
);
|
|
672
|
+
}
|
|
673
|
+
return qorClient;
|
|
674
|
+
};
|
|
675
|
+
const fees = {
|
|
676
|
+
estimate: (urgency) => estimateFee(getRest(), { urgency })
|
|
677
|
+
};
|
|
678
|
+
const crossvm = {
|
|
679
|
+
message: (id) => getCrossVmMessage(getRest(), id),
|
|
680
|
+
pending: () => getPendingCrossVmMessages(getRest()),
|
|
681
|
+
params: () => getCrossVmParams(getRest())
|
|
682
|
+
};
|
|
683
|
+
let cosmWasmClient;
|
|
684
|
+
return {
|
|
685
|
+
network,
|
|
686
|
+
get rest() {
|
|
687
|
+
return getRest();
|
|
688
|
+
},
|
|
689
|
+
get evm() {
|
|
690
|
+
return getEvm();
|
|
691
|
+
},
|
|
692
|
+
get qor() {
|
|
693
|
+
return getQor();
|
|
694
|
+
},
|
|
695
|
+
fees,
|
|
696
|
+
crossvm,
|
|
697
|
+
cosmwasm() {
|
|
698
|
+
if (!cosmWasmClient) {
|
|
699
|
+
cosmWasmClient = createCosmWasmClient(
|
|
700
|
+
requireEndpoint(network.endpoints, "rpc")
|
|
701
|
+
);
|
|
702
|
+
}
|
|
703
|
+
return cosmWasmClient;
|
|
704
|
+
},
|
|
705
|
+
connectTx(signer, txOpts = {}) {
|
|
706
|
+
return TxClient.connect({
|
|
707
|
+
rpcEndpoint: requireEndpoint(network.endpoints, "rpc"),
|
|
708
|
+
signer,
|
|
709
|
+
registryTypes: txOpts.registryTypes
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// src/utils/denom.ts
|
|
716
|
+
var DEFAULT_EXPONENT = 6;
|
|
717
|
+
function resolveExponent(opts) {
|
|
718
|
+
const exponent = opts?.exponent ?? DEFAULT_EXPONENT;
|
|
719
|
+
if (!Number.isInteger(exponent) || exponent < 0) {
|
|
720
|
+
throw new Error(`invalid exponent: ${exponent} (must be a non-negative integer)`);
|
|
721
|
+
}
|
|
722
|
+
return exponent;
|
|
723
|
+
}
|
|
724
|
+
function toBase(amount, opts) {
|
|
725
|
+
const exponent = resolveExponent(opts);
|
|
726
|
+
const trimmed = amount.trim();
|
|
727
|
+
let body = trimmed;
|
|
728
|
+
if (body.startsWith("-")) {
|
|
729
|
+
throw new Error(`negative amounts are not supported: ${amount}`);
|
|
730
|
+
}
|
|
731
|
+
if (body.startsWith("+")) {
|
|
732
|
+
body = body.slice(1);
|
|
733
|
+
}
|
|
734
|
+
if (!/^\d+(\.\d+)?$/.test(body)) {
|
|
735
|
+
throw new Error(`invalid decimal amount: ${amount}`);
|
|
736
|
+
}
|
|
737
|
+
const [intPart, fracPart = ""] = body.split(".");
|
|
738
|
+
if (fracPart.length > exponent) {
|
|
739
|
+
throw new Error(
|
|
740
|
+
`too many decimal places in ${amount}: ${fracPart.length} > exponent ${exponent}`
|
|
741
|
+
);
|
|
742
|
+
}
|
|
743
|
+
const padded = fracPart.padEnd(exponent, "0");
|
|
744
|
+
const base = BigInt(intPart + padded);
|
|
745
|
+
return base.toString();
|
|
746
|
+
}
|
|
747
|
+
function fromBase(base, opts) {
|
|
748
|
+
const exponent = resolveExponent(opts);
|
|
749
|
+
const trimmed = base.trim();
|
|
750
|
+
if (trimmed.startsWith("-")) {
|
|
751
|
+
throw new Error(`negative amounts are not supported: ${base}`);
|
|
752
|
+
}
|
|
753
|
+
if (!/^\d+$/.test(trimmed)) {
|
|
754
|
+
throw new Error(`invalid base amount: ${base}`);
|
|
755
|
+
}
|
|
756
|
+
if (exponent === 0) {
|
|
757
|
+
return BigInt(trimmed).toString();
|
|
758
|
+
}
|
|
759
|
+
const padded = trimmed.padStart(exponent + 1, "0");
|
|
760
|
+
const intPart = padded.slice(0, padded.length - exponent);
|
|
761
|
+
const fracPart = padded.slice(padded.length - exponent);
|
|
762
|
+
const normalizedInt = BigInt(intPart).toString();
|
|
763
|
+
const trimmedFrac = fracPart.replace(/0+$/, "");
|
|
764
|
+
return trimmedFrac.length > 0 ? `${normalizedInt}.${trimmedFrac}` : normalizedInt;
|
|
765
|
+
}
|
|
766
|
+
var DEFAULT_PREFIX = "qor";
|
|
767
|
+
var LIMIT = 1023;
|
|
768
|
+
function stripHexPrefix(hex) {
|
|
769
|
+
return hex.startsWith("0x") || hex.startsWith("0X") ? hex.slice(2) : hex;
|
|
770
|
+
}
|
|
771
|
+
function hexToBytes(hex) {
|
|
772
|
+
const body = stripHexPrefix(hex);
|
|
773
|
+
if (body.length === 0 || body.length % 2 !== 0 || !/^[0-9a-fA-F]+$/.test(body)) {
|
|
774
|
+
throw new Error(`invalid hex string: ${hex}`);
|
|
775
|
+
}
|
|
776
|
+
const bytes = new Uint8Array(body.length / 2);
|
|
777
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
778
|
+
bytes[i] = parseInt(body.slice(i * 2, i * 2 + 2), 16);
|
|
779
|
+
}
|
|
780
|
+
return bytes;
|
|
781
|
+
}
|
|
782
|
+
function bytesToHex(bytes) {
|
|
783
|
+
let out = "0x";
|
|
784
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
785
|
+
out += bytes[i].toString(16).padStart(2, "0");
|
|
786
|
+
}
|
|
787
|
+
return out;
|
|
788
|
+
}
|
|
789
|
+
function bech32ToHex(addr) {
|
|
790
|
+
const { words } = bech32.bech32.decode(addr, LIMIT);
|
|
791
|
+
const bytes = bech32.bech32.fromWords(words);
|
|
792
|
+
return bytesToHex(bytes);
|
|
793
|
+
}
|
|
794
|
+
function bytesToBech32(bytes, prefix = DEFAULT_PREFIX) {
|
|
795
|
+
const words = bech32.bech32.toWords(bytes);
|
|
796
|
+
return bech32.bech32.encode(prefix, words, LIMIT);
|
|
797
|
+
}
|
|
798
|
+
function hexToBech32(hex, prefix = DEFAULT_PREFIX) {
|
|
799
|
+
return bytesToBech32(hexToBytes(hex), prefix);
|
|
800
|
+
}
|
|
801
|
+
function isValidBech32(addr, prefix) {
|
|
802
|
+
const decoded = bech32.bech32.decodeUnsafe(addr, LIMIT);
|
|
803
|
+
if (!decoded) {
|
|
804
|
+
return false;
|
|
805
|
+
}
|
|
806
|
+
return prefix === void 0 ? true : decoded.prefix === prefix;
|
|
807
|
+
}
|
|
808
|
+
var NATIVE_PREFIX = "qor";
|
|
809
|
+
var COIN_TYPE_NATIVE = 118;
|
|
810
|
+
var COIN_TYPE_EVM = 60;
|
|
811
|
+
var COIN_TYPE_SVM = 501;
|
|
812
|
+
function generateMnemonic(strength = 128) {
|
|
813
|
+
return bip39.generateMnemonic(english.wordlist, strength);
|
|
814
|
+
}
|
|
815
|
+
function validateMnemonic(mnemonic) {
|
|
816
|
+
return bip39.validateMnemonic(mnemonic, english.wordlist);
|
|
817
|
+
}
|
|
818
|
+
function addressIndex(opts) {
|
|
819
|
+
const index = opts?.accountIndex ?? 0;
|
|
820
|
+
if (!Number.isInteger(index) || index < 0) {
|
|
821
|
+
throw new Error(`accountIndex must be a non-negative integer, got ${index}`);
|
|
822
|
+
}
|
|
823
|
+
return index;
|
|
824
|
+
}
|
|
825
|
+
async function seedFromMnemonic(mnemonic) {
|
|
826
|
+
if (!validateMnemonic(mnemonic)) {
|
|
827
|
+
throw new Error("invalid mnemonic");
|
|
828
|
+
}
|
|
829
|
+
return bip39.mnemonicToSeed(mnemonic);
|
|
830
|
+
}
|
|
831
|
+
async function deriveSecp256k1(mnemonic, coinType, index) {
|
|
832
|
+
const seed = await seedFromMnemonic(mnemonic);
|
|
833
|
+
const node = bip32.HDKey.fromMasterSeed(seed).derive(
|
|
834
|
+
`m/44'/${coinType}'/0'/0/${index}`
|
|
835
|
+
);
|
|
836
|
+
if (!node.privateKey || !node.publicKey) {
|
|
837
|
+
throw new Error("failed to derive secp256k1 key from mnemonic");
|
|
838
|
+
}
|
|
839
|
+
return { privateKey: node.privateKey, publicKey: node.publicKey };
|
|
840
|
+
}
|
|
841
|
+
function toEip55Checksum(addressBytes) {
|
|
842
|
+
const lowerHex = Array.from(addressBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
843
|
+
const hashHex = Array.from(sha3.keccak_256(new TextEncoder().encode(lowerHex))).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
844
|
+
let out = "0x";
|
|
845
|
+
for (let i = 0; i < lowerHex.length; i++) {
|
|
846
|
+
const c = lowerHex[i];
|
|
847
|
+
out += parseInt(hashHex[i], 16) >= 8 ? c.toUpperCase() : c;
|
|
848
|
+
}
|
|
849
|
+
return out;
|
|
850
|
+
}
|
|
851
|
+
async function deriveNativeAccount(mnemonic, opts) {
|
|
852
|
+
const index = addressIndex(opts);
|
|
853
|
+
const { privateKey, publicKey } = await deriveSecp256k1(
|
|
854
|
+
mnemonic,
|
|
855
|
+
COIN_TYPE_NATIVE,
|
|
856
|
+
index
|
|
857
|
+
);
|
|
858
|
+
const digest = ripemd160.ripemd160(sha256.sha256(publicKey));
|
|
859
|
+
const address = bytesToBech32(digest, NATIVE_PREFIX);
|
|
860
|
+
return { type: "native", address, publicKey, privateKey };
|
|
861
|
+
}
|
|
862
|
+
async function deriveEvmAccount(mnemonic, opts) {
|
|
863
|
+
const index = addressIndex(opts);
|
|
864
|
+
const { privateKey, publicKey } = await deriveSecp256k1(
|
|
865
|
+
mnemonic,
|
|
866
|
+
COIN_TYPE_EVM,
|
|
867
|
+
index
|
|
868
|
+
);
|
|
869
|
+
const uncompressed = secp256k1.secp256k1.ProjectivePoint.fromHex(publicKey).toRawBytes(
|
|
870
|
+
false
|
|
871
|
+
);
|
|
872
|
+
const hash = sha3.keccak_256(uncompressed.slice(1));
|
|
873
|
+
const addressBytes = hash.slice(-20);
|
|
874
|
+
const address = toEip55Checksum(addressBytes);
|
|
875
|
+
return { type: "evm", address, publicKey, privateKey };
|
|
876
|
+
}
|
|
877
|
+
async function deriveSvmAccount(mnemonic, opts) {
|
|
878
|
+
const index = addressIndex(opts);
|
|
879
|
+
const seed = await seedFromMnemonic(mnemonic);
|
|
880
|
+
const node = slip10_js.HDKey.fromMasterSeed(seed).derive(
|
|
881
|
+
`m/44'/${COIN_TYPE_SVM}'/${index}'/0'`
|
|
882
|
+
);
|
|
883
|
+
const publicKey = node.publicKeyRaw;
|
|
884
|
+
const secretKey = new Uint8Array(64);
|
|
885
|
+
secretKey.set(node.privateKey, 0);
|
|
886
|
+
secretKey.set(publicKey, 32);
|
|
887
|
+
const address = base.base58.encode(publicKey);
|
|
888
|
+
return { type: "svm", address, publicKey, secretKey };
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
// src/accounts/pqc-algorithm.ts
|
|
892
|
+
var AlgorithmUnspecified = 0;
|
|
893
|
+
var AlgorithmDilithium5 = 1;
|
|
894
|
+
var AlgorithmMLKEM1024 = 2;
|
|
895
|
+
function algorithmName(id) {
|
|
896
|
+
switch (id) {
|
|
897
|
+
case AlgorithmUnspecified:
|
|
898
|
+
return "unspecified";
|
|
899
|
+
case AlgorithmDilithium5:
|
|
900
|
+
return "dilithium5";
|
|
901
|
+
case AlgorithmMLKEM1024:
|
|
902
|
+
return "mlkem1024";
|
|
903
|
+
default:
|
|
904
|
+
return `algorithm_${id}`;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
function isSignatureAlgorithm(id) {
|
|
908
|
+
return id === AlgorithmDilithium5;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// src/accounts/pqc.ts
|
|
912
|
+
var ML_DSA_87_PUBLIC_KEY_LENGTH = 2592;
|
|
913
|
+
var ML_DSA_87_SECRET_KEY_LENGTH = 4896;
|
|
914
|
+
var ML_DSA_87_SIGNATURE_LENGTH = 4627;
|
|
915
|
+
var ML_DSA_87_SEED_LENGTH = 32;
|
|
916
|
+
var HYBRID_SIG_TYPE_URL = "/qorechain.pqc.v1.PQCHybridSignature";
|
|
917
|
+
function generatePqcKeypair(seed) {
|
|
918
|
+
if (seed !== void 0 && seed.length !== ML_DSA_87_SEED_LENGTH) {
|
|
919
|
+
throw new Error(
|
|
920
|
+
`ML-DSA-87 seed must be ${ML_DSA_87_SEED_LENGTH} bytes, got ${seed.length}`
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
const xi = seed ?? utils.randomBytes(ML_DSA_87_SEED_LENGTH);
|
|
924
|
+
const kp = mlDsa_js.ml_dsa87.keygen(xi);
|
|
925
|
+
return { publicKey: kp.publicKey, secretKey: kp.secretKey };
|
|
926
|
+
}
|
|
927
|
+
function pqcSign(secretKey, message) {
|
|
928
|
+
return mlDsa_js.ml_dsa87.sign(secretKey, message);
|
|
929
|
+
}
|
|
930
|
+
function pqcVerify(publicKey, message, signature) {
|
|
931
|
+
return mlDsa_js.ml_dsa87.verify(publicKey, message, signature);
|
|
932
|
+
}
|
|
933
|
+
function buildHybridSignatureExtension(args) {
|
|
934
|
+
const { algorithmId, signature, publicKey } = args;
|
|
935
|
+
if (!isSignatureAlgorithm(algorithmId)) {
|
|
936
|
+
throw new Error(
|
|
937
|
+
`algorithm ${algorithmName(algorithmId)} is not a PQC signature algorithm`
|
|
938
|
+
);
|
|
939
|
+
}
|
|
940
|
+
if (signature.length === 0) {
|
|
941
|
+
throw new Error("PQC signature cannot be empty");
|
|
942
|
+
}
|
|
943
|
+
if (algorithmId === AlgorithmDilithium5) {
|
|
944
|
+
if (signature.length !== ML_DSA_87_SIGNATURE_LENGTH) {
|
|
945
|
+
throw new Error(
|
|
946
|
+
`dilithium5 signature must be ${ML_DSA_87_SIGNATURE_LENGTH} bytes, got ${signature.length}`
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
if (publicKey !== void 0 && publicKey.length !== ML_DSA_87_PUBLIC_KEY_LENGTH) {
|
|
950
|
+
throw new Error(
|
|
951
|
+
`dilithium5 public key must be ${ML_DSA_87_PUBLIC_KEY_LENGTH} bytes, got ${publicKey.length}`
|
|
952
|
+
);
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
const ext = {
|
|
956
|
+
algorithm_id: algorithmId,
|
|
957
|
+
pqc_signature: signature
|
|
958
|
+
};
|
|
959
|
+
if (publicKey !== void 0) {
|
|
960
|
+
ext.pqc_public_key = publicKey;
|
|
961
|
+
}
|
|
962
|
+
return ext;
|
|
963
|
+
}
|
|
964
|
+
var PqcSigner = class {
|
|
965
|
+
constructor(keypair, algorithmId = AlgorithmDilithium5) {
|
|
966
|
+
this.mode = "pqc";
|
|
967
|
+
this.keypair = keypair;
|
|
968
|
+
this.algorithmId = algorithmId;
|
|
969
|
+
}
|
|
970
|
+
/** The PQC public key (the signing identity in pqc-only mode). */
|
|
971
|
+
publicKey() {
|
|
972
|
+
return this.keypair.publicKey;
|
|
973
|
+
}
|
|
974
|
+
async sign(message) {
|
|
975
|
+
const part = {
|
|
976
|
+
algorithmId: this.algorithmId,
|
|
977
|
+
signature: pqcSign(this.keypair.secretKey, message),
|
|
978
|
+
publicKey: this.keypair.publicKey
|
|
979
|
+
};
|
|
980
|
+
return { pqc: part };
|
|
981
|
+
}
|
|
982
|
+
};
|
|
983
|
+
var HybridSigner = class {
|
|
984
|
+
/**
|
|
985
|
+
* @param classical The wrapped classical signer. Must be a `classical`-mode
|
|
986
|
+
* signer; its `classicalSignature` output is passed through unchanged.
|
|
987
|
+
* @param keypair The ML-DSA-87 keypair used for the PQC half.
|
|
988
|
+
* @throws if `classical` is not a `classical`-mode signer.
|
|
989
|
+
*/
|
|
990
|
+
constructor(classical, keypair, algorithmId = AlgorithmDilithium5) {
|
|
991
|
+
this.mode = "hybrid";
|
|
992
|
+
if (classical.mode !== "classical") {
|
|
993
|
+
throw new Error("HybridSigner requires a classical-mode signer");
|
|
994
|
+
}
|
|
995
|
+
this.classical = classical;
|
|
996
|
+
this.keypair = keypair;
|
|
997
|
+
this.algorithmId = algorithmId;
|
|
998
|
+
}
|
|
999
|
+
/** The classical public key — the on-chain account identity. */
|
|
1000
|
+
publicKey() {
|
|
1001
|
+
return this.classical.publicKey();
|
|
1002
|
+
}
|
|
1003
|
+
async sign(message) {
|
|
1004
|
+
const classicalOut = await this.classical.sign(message);
|
|
1005
|
+
if (classicalOut.classicalSignature === void 0) {
|
|
1006
|
+
throw new Error(
|
|
1007
|
+
"wrapped classical signer did not produce a classical signature"
|
|
1008
|
+
);
|
|
1009
|
+
}
|
|
1010
|
+
const part = {
|
|
1011
|
+
algorithmId: this.algorithmId,
|
|
1012
|
+
signature: pqcSign(this.keypair.secretKey, message),
|
|
1013
|
+
publicKey: this.keypair.publicKey
|
|
1014
|
+
};
|
|
1015
|
+
return { classicalSignature: classicalOut.classicalSignature, pqc: part };
|
|
1016
|
+
}
|
|
1017
|
+
};
|
|
1018
|
+
async function directSignerFromPrivateKey(privateKey, prefix) {
|
|
1019
|
+
if (privateKey.length !== 32) {
|
|
1020
|
+
throw new Error(
|
|
1021
|
+
`secp256k1 private key must be 32 bytes, got ${privateKey.length}`
|
|
1022
|
+
);
|
|
1023
|
+
}
|
|
1024
|
+
return protoSigning.DirectSecp256k1Wallet.fromKey(privateKey, prefix);
|
|
1025
|
+
}
|
|
1026
|
+
function toGoJson(ext) {
|
|
1027
|
+
const obj = {
|
|
1028
|
+
algorithm_id: ext.algorithm_id,
|
|
1029
|
+
pqc_signature: base64(ext.pqc_signature)
|
|
1030
|
+
};
|
|
1031
|
+
if (ext.pqc_public_key !== void 0 && ext.pqc_public_key.length > 0) {
|
|
1032
|
+
obj.pqc_public_key = base64(ext.pqc_public_key);
|
|
1033
|
+
}
|
|
1034
|
+
return JSON.stringify(obj);
|
|
1035
|
+
}
|
|
1036
|
+
function base64(bytes) {
|
|
1037
|
+
let binary = "";
|
|
1038
|
+
for (const b of bytes) binary += String.fromCharCode(b);
|
|
1039
|
+
return btoa(binary);
|
|
1040
|
+
}
|
|
1041
|
+
function encodeHybridExtension(ext) {
|
|
1042
|
+
const value = new TextEncoder().encode(toGoJson(ext));
|
|
1043
|
+
return any.Any.fromPartial({ typeUrl: HYBRID_SIG_TYPE_URL, value });
|
|
1044
|
+
}
|
|
1045
|
+
function attachHybridExtension(body, ext, opts = {}) {
|
|
1046
|
+
const anyExt = encodeHybridExtension(ext);
|
|
1047
|
+
const placement = opts.placement ?? "non_critical_extension_options";
|
|
1048
|
+
const next = tx$1.TxBody.fromPartial({
|
|
1049
|
+
messages: body.messages,
|
|
1050
|
+
memo: body.memo,
|
|
1051
|
+
timeoutHeight: body.timeoutHeight,
|
|
1052
|
+
extensionOptions: [...body.extensionOptions],
|
|
1053
|
+
nonCriticalExtensionOptions: [...body.nonCriticalExtensionOptions]
|
|
1054
|
+
});
|
|
1055
|
+
if (placement === "extension_options") {
|
|
1056
|
+
next.extensionOptions.push(anyExt);
|
|
1057
|
+
} else {
|
|
1058
|
+
next.nonCriticalExtensionOptions.push(anyExt);
|
|
1059
|
+
}
|
|
1060
|
+
return next;
|
|
1061
|
+
}
|
|
1062
|
+
function be32(n) {
|
|
1063
|
+
const b = new Uint8Array(4);
|
|
1064
|
+
b[0] = n >>> 24 & 255;
|
|
1065
|
+
b[1] = n >>> 16 & 255;
|
|
1066
|
+
b[2] = n >>> 8 & 255;
|
|
1067
|
+
b[3] = n & 255;
|
|
1068
|
+
return b;
|
|
1069
|
+
}
|
|
1070
|
+
function fromBase64(b64) {
|
|
1071
|
+
const binary = atob(b64);
|
|
1072
|
+
const out = new Uint8Array(binary.length);
|
|
1073
|
+
for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
|
|
1074
|
+
return out;
|
|
1075
|
+
}
|
|
1076
|
+
function concatBytes(...parts) {
|
|
1077
|
+
let total = 0;
|
|
1078
|
+
for (const p of parts) total += p.length;
|
|
1079
|
+
const out = new Uint8Array(total);
|
|
1080
|
+
let off = 0;
|
|
1081
|
+
for (const p of parts) {
|
|
1082
|
+
out.set(p, off);
|
|
1083
|
+
off += p.length;
|
|
1084
|
+
}
|
|
1085
|
+
return out;
|
|
1086
|
+
}
|
|
1087
|
+
async function buildHybridTx(opts) {
|
|
1088
|
+
const {
|
|
1089
|
+
registry,
|
|
1090
|
+
signer,
|
|
1091
|
+
pqcKeypair,
|
|
1092
|
+
messages,
|
|
1093
|
+
fee,
|
|
1094
|
+
chainId,
|
|
1095
|
+
accountNumber,
|
|
1096
|
+
sequence
|
|
1097
|
+
} = opts;
|
|
1098
|
+
const memo = opts.memo ?? "";
|
|
1099
|
+
const timeoutHeight = opts.timeoutHeight ?? 0n;
|
|
1100
|
+
const accounts = await signer.getAccounts();
|
|
1101
|
+
if (accounts.length === 0) {
|
|
1102
|
+
throw new Error("signer exposes no accounts");
|
|
1103
|
+
}
|
|
1104
|
+
const account = accounts[0];
|
|
1105
|
+
const encodedMessages = messages.map((m) => registry.encodeAsAny(m));
|
|
1106
|
+
const baseBody = tx$1.TxBody.fromPartial({
|
|
1107
|
+
messages: encodedMessages,
|
|
1108
|
+
memo,
|
|
1109
|
+
timeoutHeight
|
|
1110
|
+
});
|
|
1111
|
+
const b0 = tx$1.TxBody.encode(baseBody).finish();
|
|
1112
|
+
const pubkeyAny = protoSigning.encodePubkey(amino.encodeSecp256k1Pubkey(account.pubkey));
|
|
1113
|
+
const gasLimit = Number(fee.gas);
|
|
1114
|
+
const authInfoBytes = protoSigning.makeAuthInfoBytes(
|
|
1115
|
+
[{ pubkey: pubkeyAny, sequence: BigInt(sequence) }],
|
|
1116
|
+
fee.amount,
|
|
1117
|
+
gasLimit,
|
|
1118
|
+
fee.granter,
|
|
1119
|
+
fee.payer,
|
|
1120
|
+
signing.SignMode.SIGN_MODE_DIRECT
|
|
1121
|
+
);
|
|
1122
|
+
const pqcSignedMessage = concatBytes(
|
|
1123
|
+
be32(b0.length),
|
|
1124
|
+
b0,
|
|
1125
|
+
be32(authInfoBytes.length),
|
|
1126
|
+
authInfoBytes
|
|
1127
|
+
);
|
|
1128
|
+
const pqcSignature = pqcSign(pqcKeypair.secretKey, pqcSignedMessage);
|
|
1129
|
+
const ext = buildHybridSignatureExtension({
|
|
1130
|
+
algorithmId: AlgorithmDilithium5,
|
|
1131
|
+
signature: pqcSignature,
|
|
1132
|
+
publicKey: opts.includePqcPublicKey ? pqcKeypair.publicKey : void 0
|
|
1133
|
+
});
|
|
1134
|
+
const extAny = encodeHybridExtension(ext);
|
|
1135
|
+
const finalBody = tx$1.TxBody.fromPartial({
|
|
1136
|
+
messages: encodedMessages,
|
|
1137
|
+
memo,
|
|
1138
|
+
timeoutHeight,
|
|
1139
|
+
extensionOptions: [extAny]
|
|
1140
|
+
});
|
|
1141
|
+
const bodyBytesFinal = tx$1.TxBody.encode(finalBody).finish();
|
|
1142
|
+
const signDoc = tx$1.SignDoc.fromPartial({
|
|
1143
|
+
bodyBytes: bodyBytesFinal,
|
|
1144
|
+
authInfoBytes,
|
|
1145
|
+
chainId,
|
|
1146
|
+
accountNumber: BigInt(accountNumber)
|
|
1147
|
+
});
|
|
1148
|
+
const { signature } = await signer.signDirect(account.address, signDoc);
|
|
1149
|
+
const classicalSig = fromBase64(signature.signature);
|
|
1150
|
+
const txRaw = tx$1.TxRaw.fromPartial({
|
|
1151
|
+
bodyBytes: bodyBytesFinal,
|
|
1152
|
+
authInfoBytes,
|
|
1153
|
+
signatures: [classicalSig]
|
|
1154
|
+
});
|
|
1155
|
+
const txRawBytes = tx$1.TxRaw.encode(txRaw).finish();
|
|
1156
|
+
return {
|
|
1157
|
+
txRaw,
|
|
1158
|
+
txRawBytes,
|
|
1159
|
+
authInfoBytes,
|
|
1160
|
+
pqcSignedMessage,
|
|
1161
|
+
pqcSignature
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
async function signAndBroadcastHybrid(opts) {
|
|
1165
|
+
const { transport } = opts;
|
|
1166
|
+
const mode = opts.mode ?? "commit";
|
|
1167
|
+
const built = await buildHybridTx(opts);
|
|
1168
|
+
if (mode === "commit") {
|
|
1169
|
+
const res = await transport.broadcastTx(built.txRawBytes);
|
|
1170
|
+
if (res.code !== 0) {
|
|
1171
|
+
throw new Error(
|
|
1172
|
+
`hybrid transaction failed with code ${res.code}: ${res.rawLog ?? "(no log)"} (hash ${res.transactionHash})`
|
|
1173
|
+
);
|
|
1174
|
+
}
|
|
1175
|
+
return {
|
|
1176
|
+
transactionHash: res.transactionHash,
|
|
1177
|
+
code: res.code,
|
|
1178
|
+
height: res.height,
|
|
1179
|
+
gasUsed: res.gasUsed,
|
|
1180
|
+
gasWanted: res.gasWanted,
|
|
1181
|
+
rawLog: res.rawLog
|
|
1182
|
+
};
|
|
1183
|
+
}
|
|
1184
|
+
const hash = await transport.broadcastTxSync(built.txRawBytes);
|
|
1185
|
+
return { transactionHash: hash, code: 0 };
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
// src/index.ts
|
|
1189
|
+
var VERSION = "0.1.0";
|
|
1190
|
+
|
|
1191
|
+
exports.AlgorithmDilithium5 = AlgorithmDilithium5;
|
|
1192
|
+
exports.AlgorithmMLKEM1024 = AlgorithmMLKEM1024;
|
|
1193
|
+
exports.AlgorithmUnspecified = AlgorithmUnspecified;
|
|
1194
|
+
exports.HYBRID_SIG_TYPE_URL = HYBRID_SIG_TYPE_URL;
|
|
1195
|
+
exports.HybridSigner = HybridSigner;
|
|
1196
|
+
exports.JsonRpcClient = JsonRpcClient;
|
|
1197
|
+
exports.JsonRpcError = JsonRpcError;
|
|
1198
|
+
exports.ML_DSA_87_PUBLIC_KEY_LENGTH = ML_DSA_87_PUBLIC_KEY_LENGTH;
|
|
1199
|
+
exports.ML_DSA_87_SECRET_KEY_LENGTH = ML_DSA_87_SECRET_KEY_LENGTH;
|
|
1200
|
+
exports.ML_DSA_87_SEED_LENGTH = ML_DSA_87_SEED_LENGTH;
|
|
1201
|
+
exports.ML_DSA_87_SIGNATURE_LENGTH = ML_DSA_87_SIGNATURE_LENGTH;
|
|
1202
|
+
exports.MSG_SEND_TYPE_URL = MSG_SEND_TYPE_URL;
|
|
1203
|
+
exports.NETWORKS = NETWORKS;
|
|
1204
|
+
exports.PqcSigner = PqcSigner;
|
|
1205
|
+
exports.QorClient = QorClient;
|
|
1206
|
+
exports.QoreHttpError = QoreHttpError;
|
|
1207
|
+
exports.RestClient = RestClient;
|
|
1208
|
+
exports.STATIC_FALLBACK = STATIC_FALLBACK;
|
|
1209
|
+
exports.TxClient = TxClient;
|
|
1210
|
+
exports.VERSION = VERSION;
|
|
1211
|
+
exports.algorithmName = algorithmName;
|
|
1212
|
+
exports.attachHybridExtension = attachHybridExtension;
|
|
1213
|
+
exports.bech32ToHex = bech32ToHex;
|
|
1214
|
+
exports.buildHybridSignatureExtension = buildHybridSignatureExtension;
|
|
1215
|
+
exports.buildHybridTx = buildHybridTx;
|
|
1216
|
+
exports.buildUrl = buildUrl;
|
|
1217
|
+
exports.bytesToBech32 = bytesToBech32;
|
|
1218
|
+
exports.connectCosmWasmSigner = connectCosmWasmSigner;
|
|
1219
|
+
exports.createClient = createClient;
|
|
1220
|
+
exports.createCosmWasmClient = createCosmWasmClient;
|
|
1221
|
+
exports.deriveEvmAccount = deriveEvmAccount;
|
|
1222
|
+
exports.deriveNativeAccount = deriveNativeAccount;
|
|
1223
|
+
exports.deriveSvmAccount = deriveSvmAccount;
|
|
1224
|
+
exports.directSignerFromPrivateKey = directSignerFromPrivateKey;
|
|
1225
|
+
exports.encodeHybridExtension = encodeHybridExtension;
|
|
1226
|
+
exports.estimateFee = estimateFee;
|
|
1227
|
+
exports.execute = execute;
|
|
1228
|
+
exports.fromBase = fromBase;
|
|
1229
|
+
exports.generateMnemonic = generateMnemonic;
|
|
1230
|
+
exports.generatePqcKeypair = generatePqcKeypair;
|
|
1231
|
+
exports.getContractInfo = getContractInfo;
|
|
1232
|
+
exports.getCrossVmMessage = getCrossVmMessage;
|
|
1233
|
+
exports.getCrossVmParams = getCrossVmParams;
|
|
1234
|
+
exports.getJson = getJson;
|
|
1235
|
+
exports.getNetwork = getNetwork;
|
|
1236
|
+
exports.getPendingCrossVmMessages = getPendingCrossVmMessages;
|
|
1237
|
+
exports.hexToBech32 = hexToBech32;
|
|
1238
|
+
exports.instantiate = instantiate;
|
|
1239
|
+
exports.isSignatureAlgorithm = isSignatureAlgorithm;
|
|
1240
|
+
exports.isValidBech32 = isValidBech32;
|
|
1241
|
+
exports.joinUrl = joinUrl;
|
|
1242
|
+
exports.listNetworks = listNetworks;
|
|
1243
|
+
exports.postJsonRpc = postJsonRpc;
|
|
1244
|
+
exports.pqcSign = pqcSign;
|
|
1245
|
+
exports.pqcVerify = pqcVerify;
|
|
1246
|
+
exports.queryContractSmart = queryContractSmart;
|
|
1247
|
+
exports.signAndBroadcastHybrid = signAndBroadcastHybrid;
|
|
1248
|
+
exports.toBase = toBase;
|
|
1249
|
+
exports.uploadCode = uploadCode;
|
|
1250
|
+
exports.validateMnemonic = validateMnemonic;
|
|
1251
|
+
//# sourceMappingURL=index.cjs.map
|
|
1252
|
+
//# sourceMappingURL=index.cjs.map
|