@defuse-protocol/intents-sdk 0.51.0 → 0.53.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/dist/src/bridges/hot-bridge/hot-bridge.cjs +66 -15
- package/dist/src/bridges/hot-bridge/hot-bridge.js +67 -16
- package/dist/src/bridges/poa-bridge/poa-bridge-utils.cjs +4 -2
- package/dist/src/bridges/poa-bridge/poa-bridge-utils.js +4 -2
- package/dist/src/lib/caip2.cjs +2 -1
- package/dist/src/lib/caip2.d.cts +1 -0
- package/dist/src/lib/caip2.d.ts +1 -0
- package/dist/src/lib/caip2.js +2 -1
- package/dist/src/lib/validateAddress.cjs +149 -0
- package/dist/src/lib/validateAddress.js +149 -0
- package/package.json +2 -2
|
@@ -183,30 +183,81 @@ var HotBridge = class HotBridge {
|
|
|
183
183
|
status: "failed",
|
|
184
184
|
reason: "Withdrawal was cancelled"
|
|
185
185
|
};
|
|
186
|
-
if (
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
186
|
+
if (args.landingChain.startsWith("eip155:")) try {
|
|
187
|
+
args.logger?.info(`Using bridge indexer to retrieve HOT withdrawal hash with nearTxHash=${args.tx.hash} nonce=${nonce.toString()}`);
|
|
188
|
+
const bridgeIndexerHash = await this.fetchWithdrawalHashBridgeIndexer(args.tx.hash, nonce.toString(), args.logger);
|
|
189
|
+
if (bridgeIndexerHash !== null) {
|
|
190
|
+
args.logger?.info(`Bridge indexer returned withdrawHash=${bridgeIndexerHash} for nearTxHash=${args.tx.hash} nonce=${nonce.toString()}`);
|
|
191
|
+
return {
|
|
192
|
+
status: "completed",
|
|
193
|
+
txHash: bridgeIndexerHash
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
} catch (error) {
|
|
197
|
+
args.logger?.error("Bridge indexer failed unexpectedly, trying HOT API fallback", {
|
|
198
|
+
nearTxHash: args.tx.hash,
|
|
199
|
+
nonce: nonce.toString(),
|
|
200
|
+
error
|
|
201
|
+
});
|
|
202
|
+
const apiHash = await this.fetchWithdrawalHashFromApi(args.tx.hash, nonce, args.logger);
|
|
203
|
+
if (apiHash != null) {
|
|
204
|
+
args.logger?.info(`Hot Api returned withdrawHash=${apiHash} for nearTxHash=${args.tx.hash} nonce=${nonce.toString()}`);
|
|
193
205
|
return {
|
|
194
206
|
status: "completed",
|
|
195
|
-
txHash:
|
|
207
|
+
txHash: require_hot_bridge_utils.formatTxHash(apiHash, args.landingChain)
|
|
196
208
|
};
|
|
197
209
|
}
|
|
198
|
-
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
if (status === require_hot_bridge_constants.HotWithdrawStatus.Completed) return {
|
|
199
213
|
status: "completed",
|
|
200
|
-
txHash:
|
|
214
|
+
txHash: null
|
|
215
|
+
};
|
|
216
|
+
if (typeof status === "string") {
|
|
217
|
+
if (!require_hex.default(status)) {
|
|
218
|
+
args.logger?.warn("HOT Bridge incorrect destination tx hash detected", { value: status });
|
|
219
|
+
return {
|
|
220
|
+
status: "completed",
|
|
221
|
+
txHash: null
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
status: "completed",
|
|
226
|
+
txHash: require_hot_bridge_utils.formatTxHash(status, args.landingChain)
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
const apiHash = await this.fetchWithdrawalHashFromApi(args.tx.hash, nonce, args.logger);
|
|
230
|
+
if (apiHash != null) return {
|
|
231
|
+
status: "completed",
|
|
232
|
+
txHash: require_hot_bridge_utils.formatTxHash(apiHash, args.landingChain)
|
|
201
233
|
};
|
|
202
234
|
}
|
|
203
|
-
const apiHash = await this.fetchWithdrawalHashFromApi(args.tx.hash, nonce, args.logger);
|
|
204
|
-
if (apiHash != null) return {
|
|
205
|
-
status: "completed",
|
|
206
|
-
txHash: require_hot_bridge_utils.formatTxHash(apiHash, args.landingChain)
|
|
207
|
-
};
|
|
208
235
|
return { status: "pending" };
|
|
209
236
|
}
|
|
237
|
+
async fetchWithdrawalHashBridgeIndexer(nearTxHash, nonce, logger) {
|
|
238
|
+
const { withdrawals } = await _defuse_protocol_internal_utils.bridgeIndexer.httpClient.withdrawalsByNearTxHash(nearTxHash, {
|
|
239
|
+
timeout: typeof window !== "undefined" ? 1e4 : 3e3,
|
|
240
|
+
logger
|
|
241
|
+
});
|
|
242
|
+
const withdrawal = withdrawals.find((withdrawal$1) => {
|
|
243
|
+
return withdrawal$1.nonce === nonce;
|
|
244
|
+
});
|
|
245
|
+
if (withdrawal === void 0) {
|
|
246
|
+
logger?.info("HOT Bridge indexer withdrawal hash not found", {
|
|
247
|
+
nearTxHash,
|
|
248
|
+
nonce: nonce.toString()
|
|
249
|
+
});
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
if (withdrawal.hash === null || withdrawal.hash === "") {
|
|
253
|
+
logger?.info(`HOT Bridge returned invalid hash, expected a non empty string, got ${withdrawal.hash}`, {
|
|
254
|
+
nearTxHash,
|
|
255
|
+
nonce: nonce.toString()
|
|
256
|
+
});
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
return withdrawal.hash;
|
|
260
|
+
}
|
|
210
261
|
async fetchWithdrawalHashFromApi(nearTxHash, nonce, logger) {
|
|
211
262
|
try {
|
|
212
263
|
const data = await (await (0, _defuse_protocol_internal_utils.withTimeout)(() => this.hotSdk.api.requestApi(`/api/v1/evm/bridge_withdrawal_hash?near_trx=${nearTxHash}`, { method: "GET" }), { timeout: HotBridge.API_FALLBACK_TIMEOUT_MS })).json();
|
|
@@ -9,7 +9,7 @@ import { HotWithdrawalApiFeeRequestTimeoutError, HotWithdrawalNotFoundError } fr
|
|
|
9
9
|
import { HotWithdrawStatus, MIN_GAS_AMOUNT } from "./hot-bridge-constants.js";
|
|
10
10
|
import { formatTxHash, getFeeAssetIdForChain, hotBlockchainInvariant, hotNetworkIdToCAIP2, toHotNetworkId } from "./hot-bridge-utils.js";
|
|
11
11
|
import isHex from "../../lib/hex.js";
|
|
12
|
-
import { assert, withTimeout } from "@defuse-protocol/internal-utils";
|
|
12
|
+
import { assert, bridgeIndexer, withTimeout } from "@defuse-protocol/internal-utils";
|
|
13
13
|
import { OMNI_HOT_V2, utils as utils$1 } from "@hot-labs/omni-sdk";
|
|
14
14
|
import { LRUCache } from "lru-cache";
|
|
15
15
|
import * as v from "valibot";
|
|
@@ -181,30 +181,81 @@ var HotBridge$1 = class HotBridge$1 {
|
|
|
181
181
|
status: "failed",
|
|
182
182
|
reason: "Withdrawal was cancelled"
|
|
183
183
|
};
|
|
184
|
-
if (
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
184
|
+
if (args.landingChain.startsWith("eip155:")) try {
|
|
185
|
+
args.logger?.info(`Using bridge indexer to retrieve HOT withdrawal hash with nearTxHash=${args.tx.hash} nonce=${nonce.toString()}`);
|
|
186
|
+
const bridgeIndexerHash = await this.fetchWithdrawalHashBridgeIndexer(args.tx.hash, nonce.toString(), args.logger);
|
|
187
|
+
if (bridgeIndexerHash !== null) {
|
|
188
|
+
args.logger?.info(`Bridge indexer returned withdrawHash=${bridgeIndexerHash} for nearTxHash=${args.tx.hash} nonce=${nonce.toString()}`);
|
|
189
|
+
return {
|
|
190
|
+
status: "completed",
|
|
191
|
+
txHash: bridgeIndexerHash
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
} catch (error) {
|
|
195
|
+
args.logger?.error("Bridge indexer failed unexpectedly, trying HOT API fallback", {
|
|
196
|
+
nearTxHash: args.tx.hash,
|
|
197
|
+
nonce: nonce.toString(),
|
|
198
|
+
error
|
|
199
|
+
});
|
|
200
|
+
const apiHash = await this.fetchWithdrawalHashFromApi(args.tx.hash, nonce, args.logger);
|
|
201
|
+
if (apiHash != null) {
|
|
202
|
+
args.logger?.info(`Hot Api returned withdrawHash=${apiHash} for nearTxHash=${args.tx.hash} nonce=${nonce.toString()}`);
|
|
191
203
|
return {
|
|
192
204
|
status: "completed",
|
|
193
|
-
txHash:
|
|
205
|
+
txHash: formatTxHash(apiHash, args.landingChain)
|
|
194
206
|
};
|
|
195
207
|
}
|
|
196
|
-
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
if (status === HotWithdrawStatus.Completed) return {
|
|
197
211
|
status: "completed",
|
|
198
|
-
txHash:
|
|
212
|
+
txHash: null
|
|
213
|
+
};
|
|
214
|
+
if (typeof status === "string") {
|
|
215
|
+
if (!isHex(status)) {
|
|
216
|
+
args.logger?.warn("HOT Bridge incorrect destination tx hash detected", { value: status });
|
|
217
|
+
return {
|
|
218
|
+
status: "completed",
|
|
219
|
+
txHash: null
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
status: "completed",
|
|
224
|
+
txHash: formatTxHash(status, args.landingChain)
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
const apiHash = await this.fetchWithdrawalHashFromApi(args.tx.hash, nonce, args.logger);
|
|
228
|
+
if (apiHash != null) return {
|
|
229
|
+
status: "completed",
|
|
230
|
+
txHash: formatTxHash(apiHash, args.landingChain)
|
|
199
231
|
};
|
|
200
232
|
}
|
|
201
|
-
const apiHash = await this.fetchWithdrawalHashFromApi(args.tx.hash, nonce, args.logger);
|
|
202
|
-
if (apiHash != null) return {
|
|
203
|
-
status: "completed",
|
|
204
|
-
txHash: formatTxHash(apiHash, args.landingChain)
|
|
205
|
-
};
|
|
206
233
|
return { status: "pending" };
|
|
207
234
|
}
|
|
235
|
+
async fetchWithdrawalHashBridgeIndexer(nearTxHash, nonce, logger) {
|
|
236
|
+
const { withdrawals } = await bridgeIndexer.httpClient.withdrawalsByNearTxHash(nearTxHash, {
|
|
237
|
+
timeout: typeof window !== "undefined" ? 1e4 : 3e3,
|
|
238
|
+
logger
|
|
239
|
+
});
|
|
240
|
+
const withdrawal = withdrawals.find((withdrawal$1) => {
|
|
241
|
+
return withdrawal$1.nonce === nonce;
|
|
242
|
+
});
|
|
243
|
+
if (withdrawal === void 0) {
|
|
244
|
+
logger?.info("HOT Bridge indexer withdrawal hash not found", {
|
|
245
|
+
nearTxHash,
|
|
246
|
+
nonce: nonce.toString()
|
|
247
|
+
});
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
if (withdrawal.hash === null || withdrawal.hash === "") {
|
|
251
|
+
logger?.info(`HOT Bridge returned invalid hash, expected a non empty string, got ${withdrawal.hash}`, {
|
|
252
|
+
nearTxHash,
|
|
253
|
+
nonce: nonce.toString()
|
|
254
|
+
});
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
return withdrawal.hash;
|
|
258
|
+
}
|
|
208
259
|
async fetchWithdrawalHashFromApi(nearTxHash, nonce, logger) {
|
|
209
260
|
try {
|
|
210
261
|
const data = await (await withTimeout(() => this.hotSdk.api.requestApi(`/api/v1/evm/bridge_withdrawal_hash?near_trx=${nearTxHash}`, { method: "GET" }), { timeout: HotBridge$1.API_FALLBACK_TIMEOUT_MS })).json();
|
|
@@ -40,7 +40,8 @@ const caip2Mapping = {
|
|
|
40
40
|
[require_caip2.Chains.Aptos]: "aptos:mainnet",
|
|
41
41
|
[require_caip2.Chains.Cardano]: "cardano:mainnet",
|
|
42
42
|
[require_caip2.Chains.Litecoin]: "ltc:mainnet",
|
|
43
|
-
[require_caip2.Chains.Starknet]: "starknet:mainnet"
|
|
43
|
+
[require_caip2.Chains.Starknet]: "starknet:mainnet",
|
|
44
|
+
[require_caip2.Chains.Aleo]: "aleo:mainnet"
|
|
44
45
|
};
|
|
45
46
|
function toPoaNetwork(caip2) {
|
|
46
47
|
if (caip2Mapping[caip2] == null) throw new Error(`Unsupported POA Bridge chain = ${caip2}`);
|
|
@@ -63,7 +64,8 @@ const tokenPrefixMapping = {
|
|
|
63
64
|
aptos: require_caip2.Chains.Aptos,
|
|
64
65
|
cardano: require_caip2.Chains.Cardano,
|
|
65
66
|
ltc: require_caip2.Chains.Litecoin,
|
|
66
|
-
starknet: require_caip2.Chains.Starknet
|
|
67
|
+
starknet: require_caip2.Chains.Starknet,
|
|
68
|
+
aleo: require_caip2.Chains.Aleo
|
|
67
69
|
};
|
|
68
70
|
function contractIdToCaip2(contractId) {
|
|
69
71
|
for (const [prefix, caip2] of Object.entries(tokenPrefixMapping)) if (contractId.startsWith(`${prefix}.`) || contractId.startsWith(`${prefix}-`)) return caip2;
|
|
@@ -39,7 +39,8 @@ const caip2Mapping = {
|
|
|
39
39
|
[Chains.Aptos]: "aptos:mainnet",
|
|
40
40
|
[Chains.Cardano]: "cardano:mainnet",
|
|
41
41
|
[Chains.Litecoin]: "ltc:mainnet",
|
|
42
|
-
[Chains.Starknet]: "starknet:mainnet"
|
|
42
|
+
[Chains.Starknet]: "starknet:mainnet",
|
|
43
|
+
[Chains.Aleo]: "aleo:mainnet"
|
|
43
44
|
};
|
|
44
45
|
function toPoaNetwork(caip2) {
|
|
45
46
|
if (caip2Mapping[caip2] == null) throw new Error(`Unsupported POA Bridge chain = ${caip2}`);
|
|
@@ -62,7 +63,8 @@ const tokenPrefixMapping = {
|
|
|
62
63
|
aptos: Chains.Aptos,
|
|
63
64
|
cardano: Chains.Cardano,
|
|
64
65
|
ltc: Chains.Litecoin,
|
|
65
|
-
starknet: Chains.Starknet
|
|
66
|
+
starknet: Chains.Starknet,
|
|
67
|
+
aleo: Chains.Aleo
|
|
66
68
|
};
|
|
67
69
|
function contractIdToCaip2(contractId) {
|
|
68
70
|
for (const [prefix, caip2] of Object.entries(tokenPrefixMapping)) if (contractId.startsWith(`${prefix}.`) || contractId.startsWith(`${prefix}-`)) return caip2;
|
package/dist/src/lib/caip2.cjs
CHANGED
|
@@ -34,7 +34,8 @@ const Chains = {
|
|
|
34
34
|
Cardano: "cip34:1-764824073",
|
|
35
35
|
Starknet: "starknet:SN_MAIN",
|
|
36
36
|
Plasma: "eip155:9745",
|
|
37
|
-
Scroll: "eip155:534352"
|
|
37
|
+
Scroll: "eip155:534352",
|
|
38
|
+
Aleo: "aleo:0"
|
|
38
39
|
};
|
|
39
40
|
function getEIP155ChainId(chain) {
|
|
40
41
|
(0, _defuse_protocol_internal_utils.assert)(chain.startsWith("eip155:"), "Chain is not an EIP-155 chain");
|
package/dist/src/lib/caip2.d.cts
CHANGED
package/dist/src/lib/caip2.d.ts
CHANGED
package/dist/src/lib/caip2.js
CHANGED
|
@@ -33,7 +33,8 @@ const Chains = {
|
|
|
33
33
|
Cardano: "cip34:1-764824073",
|
|
34
34
|
Starknet: "starknet:SN_MAIN",
|
|
35
35
|
Plasma: "eip155:9745",
|
|
36
|
-
Scroll: "eip155:534352"
|
|
36
|
+
Scroll: "eip155:534352",
|
|
37
|
+
Aleo: "aleo:0"
|
|
37
38
|
};
|
|
38
39
|
function getEIP155ChainId(chain) {
|
|
39
40
|
assert(chain.startsWith("eip155:"), "Chain is not an EIP-155 chain");
|
|
@@ -46,6 +46,7 @@ function validateAddress(address, blockchain) {
|
|
|
46
46
|
case require_caip2.Chains.Berachain:
|
|
47
47
|
case require_caip2.Chains.Plasma:
|
|
48
48
|
case require_caip2.Chains.Scroll: return validateEthAddress(address);
|
|
49
|
+
case require_caip2.Chains.Aleo: return validateAleoAddress(address);
|
|
49
50
|
default: return false;
|
|
50
51
|
}
|
|
51
52
|
}
|
|
@@ -266,6 +267,154 @@ function validateLitecoinBech32Address(address) {
|
|
|
266
267
|
}
|
|
267
268
|
return false;
|
|
268
269
|
}
|
|
270
|
+
/** Base field modulus (= scalar field order of BLS12-377). */
|
|
271
|
+
const P = 8444461749428370424248824938781546531375899335154063827935233455917409239041n;
|
|
272
|
+
/** Edwards curve coefficient d. Curve: -x² + y² = 1 + d·x²·y² (a = -1). */
|
|
273
|
+
const D = 3021n;
|
|
274
|
+
/** Prime-order subgroup order (= scalar field of Edwards BLS12-377, cofactor = 4). */
|
|
275
|
+
const SUBGROUP_ORDER = 2111115437357092606062206234695386632838870926408408195193685246394721360383n;
|
|
276
|
+
function modPow(base, exp, m) {
|
|
277
|
+
let result = 1n;
|
|
278
|
+
base = (base % m + m) % m;
|
|
279
|
+
while (exp > 0n) {
|
|
280
|
+
if (exp & 1n) result = result * base % m;
|
|
281
|
+
exp >>= 1n;
|
|
282
|
+
base = base * base % m;
|
|
283
|
+
}
|
|
284
|
+
return result;
|
|
285
|
+
}
|
|
286
|
+
/** Tonelli-Shanks square root in F_P. Returns null when n is a non-residue. */
|
|
287
|
+
function modSqrt(n) {
|
|
288
|
+
n = (n % P + P) % P;
|
|
289
|
+
if (n === 0n) return 0n;
|
|
290
|
+
if (modPow(n, (P - 1n) / 2n, P) !== 1n) return null;
|
|
291
|
+
let s = 0;
|
|
292
|
+
let q = P - 1n;
|
|
293
|
+
while ((q & 1n) === 0n) {
|
|
294
|
+
s++;
|
|
295
|
+
q >>= 1n;
|
|
296
|
+
}
|
|
297
|
+
let z = 2n;
|
|
298
|
+
while (modPow(z, (P - 1n) / 2n, P) !== P - 1n) z++;
|
|
299
|
+
let m = s;
|
|
300
|
+
let c = modPow(z, q, P);
|
|
301
|
+
let t = modPow(n, q, P);
|
|
302
|
+
let r = modPow(n, (q + 1n) / 2n, P);
|
|
303
|
+
for (;;) {
|
|
304
|
+
if (t === 1n) return r;
|
|
305
|
+
let i = 1;
|
|
306
|
+
let tmp = t * t % P;
|
|
307
|
+
while (tmp !== 1n) {
|
|
308
|
+
tmp = tmp * tmp % P;
|
|
309
|
+
i++;
|
|
310
|
+
}
|
|
311
|
+
let b = c;
|
|
312
|
+
for (let j = 0; j < m - i - 1; j++) b = b * b % P;
|
|
313
|
+
m = i;
|
|
314
|
+
c = b * b % P;
|
|
315
|
+
t = t * c % P;
|
|
316
|
+
r = r * b % P;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
const EXT_ZERO = [
|
|
320
|
+
0n,
|
|
321
|
+
1n,
|
|
322
|
+
0n,
|
|
323
|
+
1n
|
|
324
|
+
];
|
|
325
|
+
function extAdd(a, b) {
|
|
326
|
+
const [X1, Y1, T1, Z1] = a;
|
|
327
|
+
const [X2, Y2, T2, Z2] = b;
|
|
328
|
+
const A = X1 * X2 % P;
|
|
329
|
+
const B = Y1 * Y2 % P;
|
|
330
|
+
const C = T1 * D % P * T2 % P;
|
|
331
|
+
const Dd = Z1 * Z2 % P;
|
|
332
|
+
const E = ((X1 + Y1) % P * ((X2 + Y2) % P) % P - A - B + 2n * P) % P;
|
|
333
|
+
const F = ((Dd - C) % P + P) % P;
|
|
334
|
+
const G = (Dd + C) % P;
|
|
335
|
+
const H = (B + A) % P;
|
|
336
|
+
return [
|
|
337
|
+
E * F % P,
|
|
338
|
+
G * H % P,
|
|
339
|
+
E * H % P,
|
|
340
|
+
F * G % P
|
|
341
|
+
];
|
|
342
|
+
}
|
|
343
|
+
function extDouble(a) {
|
|
344
|
+
const [X, Y, _T, Z] = a;
|
|
345
|
+
const A = X * X % P;
|
|
346
|
+
const B = Y * Y % P;
|
|
347
|
+
const C = 2n * (Z * Z % P) % P;
|
|
348
|
+
const Dd = (P - A) % P;
|
|
349
|
+
const E = ((X + Y) % P * ((X + Y) % P) % P - A - B + 2n * P) % P;
|
|
350
|
+
const G = (Dd + B) % P;
|
|
351
|
+
const F = ((G - C) % P + P) % P;
|
|
352
|
+
const H = ((Dd - B) % P + P) % P;
|
|
353
|
+
return [
|
|
354
|
+
E * F % P,
|
|
355
|
+
G * H % P,
|
|
356
|
+
E * H % P,
|
|
357
|
+
F * G % P
|
|
358
|
+
];
|
|
359
|
+
}
|
|
360
|
+
function scalarMul(pt, scalar) {
|
|
361
|
+
let result = EXT_ZERO;
|
|
362
|
+
let base = pt;
|
|
363
|
+
let s = scalar;
|
|
364
|
+
while (s > 0n) {
|
|
365
|
+
if (s & 1n) result = extAdd(result, base);
|
|
366
|
+
base = extDouble(base);
|
|
367
|
+
s >>= 1n;
|
|
368
|
+
}
|
|
369
|
+
return result;
|
|
370
|
+
}
|
|
371
|
+
/** Check whether an extended point is the identity (0, 1). */
|
|
372
|
+
function isIdentity(pt) {
|
|
373
|
+
return pt[0] === 0n && pt[1] === pt[3];
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Validates an Aleo address by decoding the bech32m payload, decompressing
|
|
377
|
+
* the Edwards BLS12-377 point, and verifying it lies in the prime-order
|
|
378
|
+
* subgroup — matching snarkVM's `Group::from_x_coordinate` exactly.
|
|
379
|
+
*
|
|
380
|
+
* Compressed format: x-coordinate (little-endian, 32 bytes) with y-sign
|
|
381
|
+
* flag in bit 7 of byte 31.
|
|
382
|
+
*/
|
|
383
|
+
function validateAleoAddress(address) {
|
|
384
|
+
try {
|
|
385
|
+
const decoded = _scure_base.bech32m.decodeToBytes(address);
|
|
386
|
+
if (decoded.prefix !== "aleo") return false;
|
|
387
|
+
if (decoded.bytes.length !== 32) return false;
|
|
388
|
+
const bytes = new Uint8Array(decoded.bytes);
|
|
389
|
+
bytes[31] = bytes[31] & 127;
|
|
390
|
+
let x = 0n;
|
|
391
|
+
for (let i = 31; i >= 0; i--) x = x << 8n | BigInt(bytes[i]);
|
|
392
|
+
if (x >= P) return false;
|
|
393
|
+
const x2 = x * x % P;
|
|
394
|
+
const num = (1n + x2) % P;
|
|
395
|
+
const den = ((1n - D * x2 % P) % P + P) % P;
|
|
396
|
+
if (den === 0n) return false;
|
|
397
|
+
const y = modSqrt(num * modPow(den, P - 2n, P) % P);
|
|
398
|
+
if (y === null) return false;
|
|
399
|
+
const negY = y === 0n ? 0n : P - y;
|
|
400
|
+
const p1 = [
|
|
401
|
+
x,
|
|
402
|
+
y,
|
|
403
|
+
x * y % P,
|
|
404
|
+
1n
|
|
405
|
+
];
|
|
406
|
+
const p2 = [
|
|
407
|
+
x,
|
|
408
|
+
negY,
|
|
409
|
+
x * negY % P,
|
|
410
|
+
1n
|
|
411
|
+
];
|
|
412
|
+
for (const pt of [p1, p2]) if (isIdentity(scalarMul(pt, SUBGROUP_ORDER))) return true;
|
|
413
|
+
return false;
|
|
414
|
+
} catch {
|
|
415
|
+
return false;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
269
418
|
|
|
270
419
|
//#endregion
|
|
271
420
|
exports.validateAddress = validateAddress;
|
|
@@ -45,6 +45,7 @@ function validateAddress(address, blockchain) {
|
|
|
45
45
|
case Chains.Berachain:
|
|
46
46
|
case Chains.Plasma:
|
|
47
47
|
case Chains.Scroll: return validateEthAddress(address);
|
|
48
|
+
case Chains.Aleo: return validateAleoAddress(address);
|
|
48
49
|
default: return false;
|
|
49
50
|
}
|
|
50
51
|
}
|
|
@@ -265,6 +266,154 @@ function validateLitecoinBech32Address(address) {
|
|
|
265
266
|
}
|
|
266
267
|
return false;
|
|
267
268
|
}
|
|
269
|
+
/** Base field modulus (= scalar field order of BLS12-377). */
|
|
270
|
+
const P = 8444461749428370424248824938781546531375899335154063827935233455917409239041n;
|
|
271
|
+
/** Edwards curve coefficient d. Curve: -x² + y² = 1 + d·x²·y² (a = -1). */
|
|
272
|
+
const D = 3021n;
|
|
273
|
+
/** Prime-order subgroup order (= scalar field of Edwards BLS12-377, cofactor = 4). */
|
|
274
|
+
const SUBGROUP_ORDER = 2111115437357092606062206234695386632838870926408408195193685246394721360383n;
|
|
275
|
+
function modPow(base, exp, m) {
|
|
276
|
+
let result = 1n;
|
|
277
|
+
base = (base % m + m) % m;
|
|
278
|
+
while (exp > 0n) {
|
|
279
|
+
if (exp & 1n) result = result * base % m;
|
|
280
|
+
exp >>= 1n;
|
|
281
|
+
base = base * base % m;
|
|
282
|
+
}
|
|
283
|
+
return result;
|
|
284
|
+
}
|
|
285
|
+
/** Tonelli-Shanks square root in F_P. Returns null when n is a non-residue. */
|
|
286
|
+
function modSqrt(n) {
|
|
287
|
+
n = (n % P + P) % P;
|
|
288
|
+
if (n === 0n) return 0n;
|
|
289
|
+
if (modPow(n, (P - 1n) / 2n, P) !== 1n) return null;
|
|
290
|
+
let s = 0;
|
|
291
|
+
let q = P - 1n;
|
|
292
|
+
while ((q & 1n) === 0n) {
|
|
293
|
+
s++;
|
|
294
|
+
q >>= 1n;
|
|
295
|
+
}
|
|
296
|
+
let z = 2n;
|
|
297
|
+
while (modPow(z, (P - 1n) / 2n, P) !== P - 1n) z++;
|
|
298
|
+
let m = s;
|
|
299
|
+
let c = modPow(z, q, P);
|
|
300
|
+
let t = modPow(n, q, P);
|
|
301
|
+
let r = modPow(n, (q + 1n) / 2n, P);
|
|
302
|
+
for (;;) {
|
|
303
|
+
if (t === 1n) return r;
|
|
304
|
+
let i = 1;
|
|
305
|
+
let tmp = t * t % P;
|
|
306
|
+
while (tmp !== 1n) {
|
|
307
|
+
tmp = tmp * tmp % P;
|
|
308
|
+
i++;
|
|
309
|
+
}
|
|
310
|
+
let b = c;
|
|
311
|
+
for (let j = 0; j < m - i - 1; j++) b = b * b % P;
|
|
312
|
+
m = i;
|
|
313
|
+
c = b * b % P;
|
|
314
|
+
t = t * c % P;
|
|
315
|
+
r = r * b % P;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
const EXT_ZERO = [
|
|
319
|
+
0n,
|
|
320
|
+
1n,
|
|
321
|
+
0n,
|
|
322
|
+
1n
|
|
323
|
+
];
|
|
324
|
+
function extAdd(a, b) {
|
|
325
|
+
const [X1, Y1, T1, Z1] = a;
|
|
326
|
+
const [X2, Y2, T2, Z2] = b;
|
|
327
|
+
const A = X1 * X2 % P;
|
|
328
|
+
const B = Y1 * Y2 % P;
|
|
329
|
+
const C = T1 * D % P * T2 % P;
|
|
330
|
+
const Dd = Z1 * Z2 % P;
|
|
331
|
+
const E = ((X1 + Y1) % P * ((X2 + Y2) % P) % P - A - B + 2n * P) % P;
|
|
332
|
+
const F = ((Dd - C) % P + P) % P;
|
|
333
|
+
const G = (Dd + C) % P;
|
|
334
|
+
const H = (B + A) % P;
|
|
335
|
+
return [
|
|
336
|
+
E * F % P,
|
|
337
|
+
G * H % P,
|
|
338
|
+
E * H % P,
|
|
339
|
+
F * G % P
|
|
340
|
+
];
|
|
341
|
+
}
|
|
342
|
+
function extDouble(a) {
|
|
343
|
+
const [X, Y, _T, Z] = a;
|
|
344
|
+
const A = X * X % P;
|
|
345
|
+
const B = Y * Y % P;
|
|
346
|
+
const C = 2n * (Z * Z % P) % P;
|
|
347
|
+
const Dd = (P - A) % P;
|
|
348
|
+
const E = ((X + Y) % P * ((X + Y) % P) % P - A - B + 2n * P) % P;
|
|
349
|
+
const G = (Dd + B) % P;
|
|
350
|
+
const F = ((G - C) % P + P) % P;
|
|
351
|
+
const H = ((Dd - B) % P + P) % P;
|
|
352
|
+
return [
|
|
353
|
+
E * F % P,
|
|
354
|
+
G * H % P,
|
|
355
|
+
E * H % P,
|
|
356
|
+
F * G % P
|
|
357
|
+
];
|
|
358
|
+
}
|
|
359
|
+
function scalarMul(pt, scalar) {
|
|
360
|
+
let result = EXT_ZERO;
|
|
361
|
+
let base = pt;
|
|
362
|
+
let s = scalar;
|
|
363
|
+
while (s > 0n) {
|
|
364
|
+
if (s & 1n) result = extAdd(result, base);
|
|
365
|
+
base = extDouble(base);
|
|
366
|
+
s >>= 1n;
|
|
367
|
+
}
|
|
368
|
+
return result;
|
|
369
|
+
}
|
|
370
|
+
/** Check whether an extended point is the identity (0, 1). */
|
|
371
|
+
function isIdentity(pt) {
|
|
372
|
+
return pt[0] === 0n && pt[1] === pt[3];
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Validates an Aleo address by decoding the bech32m payload, decompressing
|
|
376
|
+
* the Edwards BLS12-377 point, and verifying it lies in the prime-order
|
|
377
|
+
* subgroup — matching snarkVM's `Group::from_x_coordinate` exactly.
|
|
378
|
+
*
|
|
379
|
+
* Compressed format: x-coordinate (little-endian, 32 bytes) with y-sign
|
|
380
|
+
* flag in bit 7 of byte 31.
|
|
381
|
+
*/
|
|
382
|
+
function validateAleoAddress(address) {
|
|
383
|
+
try {
|
|
384
|
+
const decoded = bech32m.decodeToBytes(address);
|
|
385
|
+
if (decoded.prefix !== "aleo") return false;
|
|
386
|
+
if (decoded.bytes.length !== 32) return false;
|
|
387
|
+
const bytes = new Uint8Array(decoded.bytes);
|
|
388
|
+
bytes[31] = bytes[31] & 127;
|
|
389
|
+
let x = 0n;
|
|
390
|
+
for (let i = 31; i >= 0; i--) x = x << 8n | BigInt(bytes[i]);
|
|
391
|
+
if (x >= P) return false;
|
|
392
|
+
const x2 = x * x % P;
|
|
393
|
+
const num = (1n + x2) % P;
|
|
394
|
+
const den = ((1n - D * x2 % P) % P + P) % P;
|
|
395
|
+
if (den === 0n) return false;
|
|
396
|
+
const y = modSqrt(num * modPow(den, P - 2n, P) % P);
|
|
397
|
+
if (y === null) return false;
|
|
398
|
+
const negY = y === 0n ? 0n : P - y;
|
|
399
|
+
const p1 = [
|
|
400
|
+
x,
|
|
401
|
+
y,
|
|
402
|
+
x * y % P,
|
|
403
|
+
1n
|
|
404
|
+
];
|
|
405
|
+
const p2 = [
|
|
406
|
+
x,
|
|
407
|
+
negY,
|
|
408
|
+
x * negY % P,
|
|
409
|
+
1n
|
|
410
|
+
];
|
|
411
|
+
for (const pt of [p1, p2]) if (isIdentity(scalarMul(pt, SUBGROUP_ORDER))) return true;
|
|
412
|
+
return false;
|
|
413
|
+
} catch {
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
268
417
|
|
|
269
418
|
//#endregion
|
|
270
419
|
export { validateAddress };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@defuse-protocol/intents-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.53.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"valibot": "^1.0.0",
|
|
47
47
|
"viem": "^2.0.0",
|
|
48
48
|
"@defuse-protocol/contract-types": "0.6.0",
|
|
49
|
-
"@defuse-protocol/internal-utils": "0.
|
|
49
|
+
"@defuse-protocol/internal-utils": "0.28.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"tsdown": "0.19.0"
|