@defuse-protocol/intents-sdk 0.82.0 → 0.83.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/omni-bridge/omni-bridge-utils.cjs +3 -2
- package/dist/src/bridges/omni-bridge/omni-bridge-utils.js +3 -2
- package/dist/src/bridges/omni-bridge/omni-withdraw-params.cjs +1 -1
- package/dist/src/bridges/omni-bridge/omni-withdraw-params.js +1 -1
- package/dist/src/bridges/poa-bridge/poa-bridge-utils.cjs +0 -2
- package/dist/src/bridges/poa-bridge/poa-bridge-utils.js +0 -2
- package/dist/src/constants/poa-tokens-migrated-to-omni-bridge.cjs +4 -2
- package/dist/src/constants/poa-tokens-migrated-to-omni-bridge.d.cts +2 -1
- package/dist/src/constants/poa-tokens-migrated-to-omni-bridge.d.ts +2 -1
- package/dist/src/constants/poa-tokens-migrated-to-omni-bridge.js +4 -2
- package/dist/src/lib/validateAddress.cjs +3 -6
- package/dist/src/lib/validateAddress.js +3 -6
- package/dist/src/lib/zcash-unified-address.cjs +192 -0
- package/dist/src/lib/zcash-unified-address.js +191 -0
- package/package.json +3 -3
|
@@ -58,7 +58,8 @@ const CHAIN_MAPPINGS = [
|
|
|
58
58
|
[require_caip2.Chains.Fogo, _omni_bridge_core.ChainKind.Fogo],
|
|
59
59
|
[require_caip2.Chains.Polygon, _omni_bridge_core.ChainKind.Pol],
|
|
60
60
|
[require_caip2.Chains.Aptos, _omni_bridge_core.ChainKind.Aptos],
|
|
61
|
-
[require_caip2.Chains.HyperEvm, _omni_bridge_core.ChainKind.HlEvm]
|
|
61
|
+
[require_caip2.Chains.HyperEvm, _omni_bridge_core.ChainKind.HlEvm],
|
|
62
|
+
[require_caip2.Chains.Zcash, _omni_bridge_core.ChainKind.Zcash]
|
|
62
63
|
];
|
|
63
64
|
function chainKindToCaip2(network) {
|
|
64
65
|
return CHAIN_MAPPINGS.find(([_, kind]) => kind === network)?.[0] ?? null;
|
|
@@ -83,7 +84,7 @@ async function getAccountOmniStorageBalance(nearProvider, accountId) {
|
|
|
83
84
|
})])
|
|
84
85
|
});
|
|
85
86
|
}
|
|
86
|
-
const OmniAddressSchema = valibot.custom((input) => typeof input === "string" && (input.startsWith("eth:") || input.startsWith("near:") || input.startsWith("sol:") || input.startsWith("arb:") || input.startsWith("base:") || input.startsWith("btc:") || input.startsWith("bnb:") || input.startsWith("abs:") || input.startsWith("strk:") || input.startsWith("fogo:") || input.startsWith("pol:") || input.startsWith("aptos:") || input.startsWith("hlevm:")), "Must comply with omni address schema");
|
|
87
|
+
const OmniAddressSchema = valibot.custom((input) => typeof input === "string" && (input.startsWith("eth:") || input.startsWith("near:") || input.startsWith("sol:") || input.startsWith("arb:") || input.startsWith("base:") || input.startsWith("btc:") || input.startsWith("bnb:") || input.startsWith("abs:") || input.startsWith("strk:") || input.startsWith("fogo:") || input.startsWith("pol:") || input.startsWith("aptos:") || input.startsWith("hlevm:") || input.startsWith("zcash:")), "Must comply with omni address schema");
|
|
87
88
|
/**
|
|
88
89
|
* Converts a token address from one chain to its equivalent on another chain.
|
|
89
90
|
* @param nearProvider Near provider used for querying the contract
|
|
@@ -56,7 +56,8 @@ const CHAIN_MAPPINGS = [
|
|
|
56
56
|
[Chains.Fogo, ChainKind.Fogo],
|
|
57
57
|
[Chains.Polygon, ChainKind.Pol],
|
|
58
58
|
[Chains.Aptos, ChainKind.Aptos],
|
|
59
|
-
[Chains.HyperEvm, ChainKind.HlEvm]
|
|
59
|
+
[Chains.HyperEvm, ChainKind.HlEvm],
|
|
60
|
+
[Chains.Zcash, ChainKind.Zcash]
|
|
60
61
|
];
|
|
61
62
|
function chainKindToCaip2(network) {
|
|
62
63
|
return CHAIN_MAPPINGS.find(([_, kind]) => kind === network)?.[0] ?? null;
|
|
@@ -81,7 +82,7 @@ async function getAccountOmniStorageBalance(nearProvider, accountId) {
|
|
|
81
82
|
})])
|
|
82
83
|
});
|
|
83
84
|
}
|
|
84
|
-
const OmniAddressSchema = v.custom((input) => typeof input === "string" && (input.startsWith("eth:") || input.startsWith("near:") || input.startsWith("sol:") || input.startsWith("arb:") || input.startsWith("base:") || input.startsWith("btc:") || input.startsWith("bnb:") || input.startsWith("abs:") || input.startsWith("strk:") || input.startsWith("fogo:") || input.startsWith("pol:") || input.startsWith("aptos:") || input.startsWith("hlevm:")), "Must comply with omni address schema");
|
|
85
|
+
const OmniAddressSchema = v.custom((input) => typeof input === "string" && (input.startsWith("eth:") || input.startsWith("near:") || input.startsWith("sol:") || input.startsWith("arb:") || input.startsWith("base:") || input.startsWith("btc:") || input.startsWith("bnb:") || input.startsWith("abs:") || input.startsWith("strk:") || input.startsWith("fogo:") || input.startsWith("pol:") || input.startsWith("aptos:") || input.startsWith("hlevm:") || input.startsWith("zcash:")), "Must comply with omni address schema");
|
|
85
86
|
/**
|
|
86
87
|
* Converts a token address from one chain to its equivalent on another chain.
|
|
87
88
|
* @param nearProvider Near provider used for querying the contract
|
|
@@ -61,7 +61,7 @@ function deriveOmniWithdrawIntentParams(params) {
|
|
|
61
61
|
function caip2ToChainKind(network) {
|
|
62
62
|
return require_omni_bridge_utils.CHAIN_MAPPINGS.find(([chain]) => chain === network)?.[1] ?? null;
|
|
63
63
|
}
|
|
64
|
-
const UTXO_CHAINS = [_omni_bridge_core.ChainKind.Btc];
|
|
64
|
+
const UTXO_CHAINS = [_omni_bridge_core.ChainKind.Btc, _omni_bridge_core.ChainKind.Zcash];
|
|
65
65
|
function isUtxoChain(network) {
|
|
66
66
|
return UTXO_CHAINS.includes(network);
|
|
67
67
|
}
|
|
@@ -60,7 +60,7 @@ function deriveOmniWithdrawIntentParams(params) {
|
|
|
60
60
|
function caip2ToChainKind(network) {
|
|
61
61
|
return CHAIN_MAPPINGS.find(([chain]) => chain === network)?.[1] ?? null;
|
|
62
62
|
}
|
|
63
|
-
const UTXO_CHAINS = [ChainKind.Btc];
|
|
63
|
+
const UTXO_CHAINS = [ChainKind.Btc, ChainKind.Zcash];
|
|
64
64
|
function isUtxoChain(network) {
|
|
65
65
|
return UTXO_CHAINS.includes(network);
|
|
66
66
|
}
|
|
@@ -31,7 +31,6 @@ const caip2Mapping = {
|
|
|
31
31
|
[require_caip2.Chains.BitcoinCash]: "bch:mainnet",
|
|
32
32
|
[require_caip2.Chains.Dogecoin]: "doge:mainnet",
|
|
33
33
|
[require_caip2.Chains.XRPL]: "xrp:mainnet",
|
|
34
|
-
[require_caip2.Chains.Zcash]: "zec:mainnet",
|
|
35
34
|
[require_caip2.Chains.Gnosis]: "eth:100",
|
|
36
35
|
[require_caip2.Chains.Berachain]: "eth:80094",
|
|
37
36
|
[require_caip2.Chains.Tron]: "tron:mainnet",
|
|
@@ -56,7 +55,6 @@ const tokenPrefixMapping = {
|
|
|
56
55
|
bch: require_caip2.Chains.BitcoinCash,
|
|
57
56
|
doge: require_caip2.Chains.Dogecoin,
|
|
58
57
|
xrp: require_caip2.Chains.XRPL,
|
|
59
|
-
zec: require_caip2.Chains.Zcash,
|
|
60
58
|
gnosis: require_caip2.Chains.Gnosis,
|
|
61
59
|
bera: require_caip2.Chains.Berachain,
|
|
62
60
|
tron: require_caip2.Chains.Tron,
|
|
@@ -30,7 +30,6 @@ const caip2Mapping = {
|
|
|
30
30
|
[Chains.BitcoinCash]: "bch:mainnet",
|
|
31
31
|
[Chains.Dogecoin]: "doge:mainnet",
|
|
32
32
|
[Chains.XRPL]: "xrp:mainnet",
|
|
33
|
-
[Chains.Zcash]: "zec:mainnet",
|
|
34
33
|
[Chains.Gnosis]: "eth:100",
|
|
35
34
|
[Chains.Berachain]: "eth:80094",
|
|
36
35
|
[Chains.Tron]: "tron:mainnet",
|
|
@@ -55,7 +54,6 @@ const tokenPrefixMapping = {
|
|
|
55
54
|
bch: Chains.BitcoinCash,
|
|
56
55
|
doge: Chains.Dogecoin,
|
|
57
56
|
xrp: Chains.XRPL,
|
|
58
|
-
zec: Chains.Zcash,
|
|
59
57
|
gnosis: Chains.Gnosis,
|
|
60
58
|
bera: Chains.Berachain,
|
|
61
59
|
tron: Chains.Tron,
|
|
@@ -3,7 +3,8 @@ let _omni_bridge_core = require("@omni-bridge/core");
|
|
|
3
3
|
|
|
4
4
|
//#region src/constants/poa-tokens-migrated-to-omni-bridge.ts
|
|
5
5
|
/**
|
|
6
|
-
* These tokens
|
|
6
|
+
* These tokens originally were PoA Bridge tokens but have since been migrated to Omni Bridge.
|
|
7
|
+
* Every migrated token must be listed here, otherwise the PoA bridge class (supports, parseAssetId methods) will keep treating it as a PoA token.
|
|
7
8
|
*/
|
|
8
9
|
const POA_TOKENS_MIGRATED_TO_OMNI_BRIDGE = {
|
|
9
10
|
"sol-57d087fd8c460f612f8701f5499ad8b2eec5ab68.omft.near": _omni_bridge_core.ChainKind.Sol,
|
|
@@ -21,7 +22,8 @@ const POA_TOKENS_MIGRATED_TO_OMNI_BRIDGE = {
|
|
|
21
22
|
"aptos.omft.near": _omni_bridge_core.ChainKind.Aptos,
|
|
22
23
|
"aptos-34ee497f210c5a511e8d5b53bc56d75b63612bb5.omft.near": _omni_bridge_core.ChainKind.Aptos,
|
|
23
24
|
"aptos-88cb7619440a914fe6400149a12b443c3ac21d59.omft.near": _omni_bridge_core.ChainKind.Aptos,
|
|
24
|
-
"starknet.omft.near": _omni_bridge_core.ChainKind.Strk
|
|
25
|
+
"starknet.omft.near": _omni_bridge_core.ChainKind.Strk,
|
|
26
|
+
"zec.omft.near": _omni_bridge_core.ChainKind.Zcash
|
|
25
27
|
};
|
|
26
28
|
|
|
27
29
|
//#endregion
|
|
@@ -3,7 +3,8 @@ import { ChainKind } from "@omni-bridge/core";
|
|
|
3
3
|
//#region src/constants/poa-tokens-migrated-to-omni-bridge.d.ts
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* These tokens
|
|
6
|
+
* These tokens originally were PoA Bridge tokens but have since been migrated to Omni Bridge.
|
|
7
|
+
* Every migrated token must be listed here, otherwise the PoA bridge class (supports, parseAssetId methods) will keep treating it as a PoA token.
|
|
7
8
|
*/
|
|
8
9
|
declare const POA_TOKENS_MIGRATED_TO_OMNI_BRIDGE: Record<string, ChainKind>;
|
|
9
10
|
//#endregion
|
|
@@ -3,7 +3,8 @@ import { ChainKind } from "@omni-bridge/core";
|
|
|
3
3
|
//#region src/constants/poa-tokens-migrated-to-omni-bridge.d.ts
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* These tokens
|
|
6
|
+
* These tokens originally were PoA Bridge tokens but have since been migrated to Omni Bridge.
|
|
7
|
+
* Every migrated token must be listed here, otherwise the PoA bridge class (supports, parseAssetId methods) will keep treating it as a PoA token.
|
|
7
8
|
*/
|
|
8
9
|
declare const POA_TOKENS_MIGRATED_TO_OMNI_BRIDGE: Record<string, ChainKind>;
|
|
9
10
|
//#endregion
|
|
@@ -2,7 +2,8 @@ import { ChainKind } from "@omni-bridge/core";
|
|
|
2
2
|
|
|
3
3
|
//#region src/constants/poa-tokens-migrated-to-omni-bridge.ts
|
|
4
4
|
/**
|
|
5
|
-
* These tokens
|
|
5
|
+
* These tokens originally were PoA Bridge tokens but have since been migrated to Omni Bridge.
|
|
6
|
+
* Every migrated token must be listed here, otherwise the PoA bridge class (supports, parseAssetId methods) will keep treating it as a PoA token.
|
|
6
7
|
*/
|
|
7
8
|
const POA_TOKENS_MIGRATED_TO_OMNI_BRIDGE = {
|
|
8
9
|
"sol-57d087fd8c460f612f8701f5499ad8b2eec5ab68.omft.near": ChainKind.Sol,
|
|
@@ -20,7 +21,8 @@ const POA_TOKENS_MIGRATED_TO_OMNI_BRIDGE = {
|
|
|
20
21
|
"aptos.omft.near": ChainKind.Aptos,
|
|
21
22
|
"aptos-34ee497f210c5a511e8d5b53bc56d75b63612bb5.omft.near": ChainKind.Aptos,
|
|
22
23
|
"aptos-88cb7619440a914fe6400149a12b443c3ac21d59.omft.near": ChainKind.Aptos,
|
|
23
|
-
"starknet.omft.near": ChainKind.Strk
|
|
24
|
+
"starknet.omft.near": ChainKind.Strk,
|
|
25
|
+
"zec.omft.near": ChainKind.Zcash
|
|
24
26
|
};
|
|
25
27
|
|
|
26
28
|
//#endregion
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const require_rolldown_runtime = require('../../_virtual/rolldown_runtime.cjs');
|
|
2
2
|
const require_caip2 = require('./caip2.cjs');
|
|
3
3
|
const require_ton_address = require('./ton-address.cjs');
|
|
4
|
+
const require_zcash_unified_address = require('./zcash-unified-address.cjs');
|
|
4
5
|
let _defuse_protocol_internal_utils = require("@defuse-protocol/internal-utils");
|
|
5
6
|
let viem = require("viem");
|
|
6
7
|
let _noble_hashes_sha2 = require("@noble/hashes/sha2");
|
|
@@ -199,6 +200,7 @@ function validateXrpAddress(address) {
|
|
|
199
200
|
* Supports:
|
|
200
201
|
* - Transparent addresses (t1, t3)
|
|
201
202
|
* - TEX addresses (tex1)
|
|
203
|
+
* - Unified Orchard / Unified Addresses (UA)
|
|
202
204
|
*/
|
|
203
205
|
function validateZcashAddress(address) {
|
|
204
206
|
if (address.startsWith("t1") || address.startsWith("t3")) return /^t[13][a-km-zA-HJ-NP-Z1-9]{33}$/.test(address);
|
|
@@ -210,12 +212,7 @@ function validateZcashAddress(address) {
|
|
|
210
212
|
} catch {
|
|
211
213
|
return false;
|
|
212
214
|
}
|
|
213
|
-
|
|
214
|
-
if (address.startsWith(`${uaHrp}1`)) try {
|
|
215
|
-
return _scure_base.bech32m.decodeToBytes(address).prefix === uaHrp;
|
|
216
|
-
} catch {
|
|
217
|
-
return false;
|
|
218
|
-
}
|
|
215
|
+
if (address.startsWith("u1")) return require_zcash_unified_address.validateZcashUnifiedAddress(address);
|
|
219
216
|
return false;
|
|
220
217
|
}
|
|
221
218
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Chains } from "./caip2.js";
|
|
2
2
|
import { TON_ADDRESS_TAG_BOUNCEABLE_MAINNET, TON_ADDRESS_TAG_NON_BOUNCEABLE_MAINNET, TON_WORKCHAIN_BASECHAIN, TON_WORKCHAIN_MASTERCHAIN, tryParseTonAddress } from "./ton-address.js";
|
|
3
|
+
import { validateZcashUnifiedAddress } from "./zcash-unified-address.js";
|
|
3
4
|
import { utils } from "@defuse-protocol/internal-utils";
|
|
4
5
|
import { isAddress } from "viem";
|
|
5
6
|
import { sha256 } from "@noble/hashes/sha2";
|
|
@@ -198,6 +199,7 @@ function validateXrpAddress(address) {
|
|
|
198
199
|
* Supports:
|
|
199
200
|
* - Transparent addresses (t1, t3)
|
|
200
201
|
* - TEX addresses (tex1)
|
|
202
|
+
* - Unified Orchard / Unified Addresses (UA)
|
|
201
203
|
*/
|
|
202
204
|
function validateZcashAddress(address) {
|
|
203
205
|
if (address.startsWith("t1") || address.startsWith("t3")) return /^t[13][a-km-zA-HJ-NP-Z1-9]{33}$/.test(address);
|
|
@@ -209,12 +211,7 @@ function validateZcashAddress(address) {
|
|
|
209
211
|
} catch {
|
|
210
212
|
return false;
|
|
211
213
|
}
|
|
212
|
-
|
|
213
|
-
if (address.startsWith(`${uaHrp}1`)) try {
|
|
214
|
-
return bech32m.decodeToBytes(address).prefix === uaHrp;
|
|
215
|
-
} catch {
|
|
216
|
-
return false;
|
|
217
|
-
}
|
|
214
|
+
if (address.startsWith("u1")) return validateZcashUnifiedAddress(address);
|
|
218
215
|
return false;
|
|
219
216
|
}
|
|
220
217
|
/**
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
const require_rolldown_runtime = require('../../_virtual/rolldown_runtime.cjs');
|
|
2
|
+
let _scure_base = require("@scure/base");
|
|
3
|
+
let _noble_hashes_blake2 = require("@noble/hashes/blake2");
|
|
4
|
+
|
|
5
|
+
//#region src/lib/zcash-unified-address.ts
|
|
6
|
+
/**
|
|
7
|
+
* Zcash Unified Address (ZIP 316) validation.
|
|
8
|
+
*
|
|
9
|
+
* Reference implementation used to cross-check this code:
|
|
10
|
+
* - crate: `zcash_address` https://crates.io/crates/zcash_address
|
|
11
|
+
* - crate: `f4jumble` https://crates.io/crates/f4jumble
|
|
12
|
+
* - source: https://github.com/zcash/librustzcash/tree/main/components/zcash_address
|
|
13
|
+
* https://github.com/zcash/librustzcash/tree/main/components/f4jumble
|
|
14
|
+
*
|
|
15
|
+
* Spec: https://zips.z.cash/zip-0316
|
|
16
|
+
*/
|
|
17
|
+
const ZCASH_UA_MAINNET_HRP = "u";
|
|
18
|
+
const F4_MIN_LEN = 48;
|
|
19
|
+
const F4_MAX_LEN = 4194368;
|
|
20
|
+
const F4_BLAKE2B_LEN = 64;
|
|
21
|
+
const G_PREFIX = asciiBytes("UA_F4Jumble_G");
|
|
22
|
+
const H_PREFIX = asciiBytes("UA_F4Jumble_H");
|
|
23
|
+
const RECEIVER_TYPECODE = {
|
|
24
|
+
P2PKH: 0,
|
|
25
|
+
P2SH: 1,
|
|
26
|
+
SAPLING: 2,
|
|
27
|
+
ORCHARD: 3
|
|
28
|
+
};
|
|
29
|
+
const MAX_TYPECODE_OR_LENGTH = 33554432;
|
|
30
|
+
const MUST_UNDERSTAND_METADATA_MIN = 224;
|
|
31
|
+
const MUST_UNDERSTAND_METADATA_MAX = 252;
|
|
32
|
+
const KNOWN_RECEIVER_LENGTH = {
|
|
33
|
+
[RECEIVER_TYPECODE.P2PKH]: 20,
|
|
34
|
+
[RECEIVER_TYPECODE.P2SH]: 20,
|
|
35
|
+
[RECEIVER_TYPECODE.SAPLING]: 43,
|
|
36
|
+
[RECEIVER_TYPECODE.ORCHARD]: 43
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Validates a Zcash Unified Address (ZIP 316) and requires that it contains
|
|
40
|
+
* at least one Orchard, P2PKH, or P2SH receiver. Sapling-only UAs are rejected.
|
|
41
|
+
*
|
|
42
|
+
* The parse pipeline (bech32m → F4Jumble⁻¹ → padding check → receiver list)
|
|
43
|
+
* mirrors `Encoding::parse_items` in
|
|
44
|
+
* https://github.com/zcash/librustzcash/blob/main/components/zcash_address/src/kind/unified.rs
|
|
45
|
+
*/
|
|
46
|
+
function validateZcashUnifiedAddress(address) {
|
|
47
|
+
try {
|
|
48
|
+
const decoded = _scure_base.bech32m.decodeToBytes(address);
|
|
49
|
+
if (decoded.prefix !== ZCASH_UA_MAINNET_HRP) return false;
|
|
50
|
+
const payload = decoded.bytes;
|
|
51
|
+
if (payload.length < F4_MIN_LEN || payload.length > F4_MAX_LEN) return false;
|
|
52
|
+
const unjumbled = f4JumbleInverse(payload);
|
|
53
|
+
const paddingLen = 16;
|
|
54
|
+
if (unjumbled.length <= paddingLen) return false;
|
|
55
|
+
const paddingStart = unjumbled.length - paddingLen;
|
|
56
|
+
if (unjumbled[paddingStart] !== ZCASH_UA_MAINNET_HRP.charCodeAt(0)) return false;
|
|
57
|
+
for (let i = paddingStart + 1; i < unjumbled.length; i++) if (unjumbled[i] !== 0) return false;
|
|
58
|
+
let offset = 0;
|
|
59
|
+
let lastTypecode = -1;
|
|
60
|
+
let hasOrchardOrTransparent = false;
|
|
61
|
+
let hasP2pkh = false;
|
|
62
|
+
let hasP2sh = false;
|
|
63
|
+
while (offset < paddingStart) {
|
|
64
|
+
const typeRead = readCompactSize(unjumbled, offset, paddingStart);
|
|
65
|
+
if (typeRead === null) return false;
|
|
66
|
+
const typecode = typeRead.value;
|
|
67
|
+
offset = typeRead.next;
|
|
68
|
+
if (typecode > MAX_TYPECODE_OR_LENGTH) return false;
|
|
69
|
+
if (typecode <= lastTypecode) return false;
|
|
70
|
+
lastTypecode = typecode;
|
|
71
|
+
const lenRead = readCompactSize(unjumbled, offset, paddingStart);
|
|
72
|
+
if (lenRead === null) return false;
|
|
73
|
+
const len = lenRead.value;
|
|
74
|
+
offset = lenRead.next;
|
|
75
|
+
if (len === 0 || len > MAX_TYPECODE_OR_LENGTH) return false;
|
|
76
|
+
if (offset + len > paddingStart) return false;
|
|
77
|
+
const expectedLen = KNOWN_RECEIVER_LENGTH[typecode];
|
|
78
|
+
if (expectedLen !== void 0 && len !== expectedLen) return false;
|
|
79
|
+
if (expectedLen === void 0 && typecode >= MUST_UNDERSTAND_METADATA_MIN && typecode <= MUST_UNDERSTAND_METADATA_MAX) return false;
|
|
80
|
+
if (typecode === RECEIVER_TYPECODE.P2PKH) hasP2pkh = true;
|
|
81
|
+
if (typecode === RECEIVER_TYPECODE.P2SH) hasP2sh = true;
|
|
82
|
+
if (typecode === RECEIVER_TYPECODE.P2PKH || typecode === RECEIVER_TYPECODE.P2SH || typecode === RECEIVER_TYPECODE.ORCHARD) hasOrchardOrTransparent = true;
|
|
83
|
+
offset += len;
|
|
84
|
+
}
|
|
85
|
+
if (offset !== paddingStart) return false;
|
|
86
|
+
if (hasP2pkh && hasP2sh) return false;
|
|
87
|
+
return hasOrchardOrTransparent;
|
|
88
|
+
} catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Bitcoin-style CompactSize decoder, canonical form only.
|
|
94
|
+
* Reference: `CompactSize::read` in
|
|
95
|
+
* https://github.com/zcash/librustzcash/blob/main/components/zcash_encoding/src/lib.rs
|
|
96
|
+
*/
|
|
97
|
+
function readCompactSize(buf, offset, end) {
|
|
98
|
+
if (offset >= end) return null;
|
|
99
|
+
const first = buf[offset];
|
|
100
|
+
if (first === void 0) return null;
|
|
101
|
+
if (first < 253) return {
|
|
102
|
+
value: first,
|
|
103
|
+
next: offset + 1
|
|
104
|
+
};
|
|
105
|
+
if (first === 253) {
|
|
106
|
+
if (offset + 3 > end) return null;
|
|
107
|
+
const b1 = buf[offset + 1];
|
|
108
|
+
const b2 = buf[offset + 2];
|
|
109
|
+
if (b1 === void 0 || b2 === void 0) return null;
|
|
110
|
+
const value = b1 | b2 << 8;
|
|
111
|
+
if (value < 253) return null;
|
|
112
|
+
return {
|
|
113
|
+
value,
|
|
114
|
+
next: offset + 3
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
if (first === 254) {
|
|
118
|
+
if (offset + 5 > end) return null;
|
|
119
|
+
const b1 = buf[offset + 1];
|
|
120
|
+
const b2 = buf[offset + 2];
|
|
121
|
+
const b3 = buf[offset + 3];
|
|
122
|
+
const b4 = buf[offset + 4];
|
|
123
|
+
if (b1 === void 0 || b2 === void 0 || b3 === void 0 || b4 === void 0) return null;
|
|
124
|
+
const value = (b1 | b2 << 8 | b3 << 16 | b4 << 24) >>> 0;
|
|
125
|
+
if (value <= 65535) return null;
|
|
126
|
+
return {
|
|
127
|
+
value,
|
|
128
|
+
next: offset + 5
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Inverse of ZIP 316 F4Jumble: a 4-round unkeyed Feistel over BLAKE2b.
|
|
135
|
+
* Forward: x = b ⊕ G_0(a); y = a ⊕ H_0(x); d = x ⊕ G_1(y); c = y ⊕ H_1(d)
|
|
136
|
+
* Inverse undoes each XOR in reverse order.
|
|
137
|
+
*
|
|
138
|
+
* Reference: `State::apply_f4jumble_inv` in
|
|
139
|
+
* https://github.com/zcash/librustzcash/blob/main/components/f4jumble/src/lib.rs
|
|
140
|
+
*/
|
|
141
|
+
function f4JumbleInverse(msg) {
|
|
142
|
+
const len = msg.length;
|
|
143
|
+
const lLen = Math.min(F4_BLAKE2B_LEN, Math.floor(len / 2));
|
|
144
|
+
const rLen = len - lLen;
|
|
145
|
+
const left = msg.slice(0, lLen);
|
|
146
|
+
const right = msg.slice(lLen);
|
|
147
|
+
xorInto(left, hI(1, right, lLen));
|
|
148
|
+
xorInto(right, gI(1, left, rLen));
|
|
149
|
+
xorInto(left, hI(0, right, lLen));
|
|
150
|
+
xorInto(right, gI(0, left, rLen));
|
|
151
|
+
const out = new Uint8Array(len);
|
|
152
|
+
out.set(left, 0);
|
|
153
|
+
out.set(right, lLen);
|
|
154
|
+
return out;
|
|
155
|
+
}
|
|
156
|
+
function hI(i, u, lLen) {
|
|
157
|
+
return (0, _noble_hashes_blake2.blake2b)(u, {
|
|
158
|
+
personalization: buildPersonalization(H_PREFIX, i, 0),
|
|
159
|
+
dkLen: lLen
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
function gI(i, u, rLen) {
|
|
163
|
+
const chunks = Math.ceil(rLen / F4_BLAKE2B_LEN);
|
|
164
|
+
const out = new Uint8Array(chunks * F4_BLAKE2B_LEN);
|
|
165
|
+
for (let j = 0; j < chunks; j++) {
|
|
166
|
+
const digest = (0, _noble_hashes_blake2.blake2b)(u, {
|
|
167
|
+
personalization: buildPersonalization(G_PREFIX, i, j),
|
|
168
|
+
dkLen: F4_BLAKE2B_LEN
|
|
169
|
+
});
|
|
170
|
+
out.set(digest, j * F4_BLAKE2B_LEN);
|
|
171
|
+
}
|
|
172
|
+
return out.subarray(0, rLen);
|
|
173
|
+
}
|
|
174
|
+
function buildPersonalization(prefix, i, j) {
|
|
175
|
+
const p = new Uint8Array(16);
|
|
176
|
+
p.set(prefix, 0);
|
|
177
|
+
p[13] = i & 255;
|
|
178
|
+
p[14] = j & 255;
|
|
179
|
+
p[15] = j >> 8 & 255;
|
|
180
|
+
return p;
|
|
181
|
+
}
|
|
182
|
+
function asciiBytes(s) {
|
|
183
|
+
const out = new Uint8Array(s.length);
|
|
184
|
+
for (let i = 0; i < s.length; i++) out[i] = s.charCodeAt(i);
|
|
185
|
+
return out;
|
|
186
|
+
}
|
|
187
|
+
function xorInto(target, other) {
|
|
188
|
+
for (let i = 0; i < target.length; i++) target[i] = target[i] ^ other[i];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
//#endregion
|
|
192
|
+
exports.validateZcashUnifiedAddress = validateZcashUnifiedAddress;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { bech32m } from "@scure/base";
|
|
2
|
+
import { blake2b } from "@noble/hashes/blake2";
|
|
3
|
+
|
|
4
|
+
//#region src/lib/zcash-unified-address.ts
|
|
5
|
+
/**
|
|
6
|
+
* Zcash Unified Address (ZIP 316) validation.
|
|
7
|
+
*
|
|
8
|
+
* Reference implementation used to cross-check this code:
|
|
9
|
+
* - crate: `zcash_address` https://crates.io/crates/zcash_address
|
|
10
|
+
* - crate: `f4jumble` https://crates.io/crates/f4jumble
|
|
11
|
+
* - source: https://github.com/zcash/librustzcash/tree/main/components/zcash_address
|
|
12
|
+
* https://github.com/zcash/librustzcash/tree/main/components/f4jumble
|
|
13
|
+
*
|
|
14
|
+
* Spec: https://zips.z.cash/zip-0316
|
|
15
|
+
*/
|
|
16
|
+
const ZCASH_UA_MAINNET_HRP = "u";
|
|
17
|
+
const F4_MIN_LEN = 48;
|
|
18
|
+
const F4_MAX_LEN = 4194368;
|
|
19
|
+
const F4_BLAKE2B_LEN = 64;
|
|
20
|
+
const G_PREFIX = asciiBytes("UA_F4Jumble_G");
|
|
21
|
+
const H_PREFIX = asciiBytes("UA_F4Jumble_H");
|
|
22
|
+
const RECEIVER_TYPECODE = {
|
|
23
|
+
P2PKH: 0,
|
|
24
|
+
P2SH: 1,
|
|
25
|
+
SAPLING: 2,
|
|
26
|
+
ORCHARD: 3
|
|
27
|
+
};
|
|
28
|
+
const MAX_TYPECODE_OR_LENGTH = 33554432;
|
|
29
|
+
const MUST_UNDERSTAND_METADATA_MIN = 224;
|
|
30
|
+
const MUST_UNDERSTAND_METADATA_MAX = 252;
|
|
31
|
+
const KNOWN_RECEIVER_LENGTH = {
|
|
32
|
+
[RECEIVER_TYPECODE.P2PKH]: 20,
|
|
33
|
+
[RECEIVER_TYPECODE.P2SH]: 20,
|
|
34
|
+
[RECEIVER_TYPECODE.SAPLING]: 43,
|
|
35
|
+
[RECEIVER_TYPECODE.ORCHARD]: 43
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Validates a Zcash Unified Address (ZIP 316) and requires that it contains
|
|
39
|
+
* at least one Orchard, P2PKH, or P2SH receiver. Sapling-only UAs are rejected.
|
|
40
|
+
*
|
|
41
|
+
* The parse pipeline (bech32m → F4Jumble⁻¹ → padding check → receiver list)
|
|
42
|
+
* mirrors `Encoding::parse_items` in
|
|
43
|
+
* https://github.com/zcash/librustzcash/blob/main/components/zcash_address/src/kind/unified.rs
|
|
44
|
+
*/
|
|
45
|
+
function validateZcashUnifiedAddress(address) {
|
|
46
|
+
try {
|
|
47
|
+
const decoded = bech32m.decodeToBytes(address);
|
|
48
|
+
if (decoded.prefix !== ZCASH_UA_MAINNET_HRP) return false;
|
|
49
|
+
const payload = decoded.bytes;
|
|
50
|
+
if (payload.length < F4_MIN_LEN || payload.length > F4_MAX_LEN) return false;
|
|
51
|
+
const unjumbled = f4JumbleInverse(payload);
|
|
52
|
+
const paddingLen = 16;
|
|
53
|
+
if (unjumbled.length <= paddingLen) return false;
|
|
54
|
+
const paddingStart = unjumbled.length - paddingLen;
|
|
55
|
+
if (unjumbled[paddingStart] !== ZCASH_UA_MAINNET_HRP.charCodeAt(0)) return false;
|
|
56
|
+
for (let i = paddingStart + 1; i < unjumbled.length; i++) if (unjumbled[i] !== 0) return false;
|
|
57
|
+
let offset = 0;
|
|
58
|
+
let lastTypecode = -1;
|
|
59
|
+
let hasOrchardOrTransparent = false;
|
|
60
|
+
let hasP2pkh = false;
|
|
61
|
+
let hasP2sh = false;
|
|
62
|
+
while (offset < paddingStart) {
|
|
63
|
+
const typeRead = readCompactSize(unjumbled, offset, paddingStart);
|
|
64
|
+
if (typeRead === null) return false;
|
|
65
|
+
const typecode = typeRead.value;
|
|
66
|
+
offset = typeRead.next;
|
|
67
|
+
if (typecode > MAX_TYPECODE_OR_LENGTH) return false;
|
|
68
|
+
if (typecode <= lastTypecode) return false;
|
|
69
|
+
lastTypecode = typecode;
|
|
70
|
+
const lenRead = readCompactSize(unjumbled, offset, paddingStart);
|
|
71
|
+
if (lenRead === null) return false;
|
|
72
|
+
const len = lenRead.value;
|
|
73
|
+
offset = lenRead.next;
|
|
74
|
+
if (len === 0 || len > MAX_TYPECODE_OR_LENGTH) return false;
|
|
75
|
+
if (offset + len > paddingStart) return false;
|
|
76
|
+
const expectedLen = KNOWN_RECEIVER_LENGTH[typecode];
|
|
77
|
+
if (expectedLen !== void 0 && len !== expectedLen) return false;
|
|
78
|
+
if (expectedLen === void 0 && typecode >= MUST_UNDERSTAND_METADATA_MIN && typecode <= MUST_UNDERSTAND_METADATA_MAX) return false;
|
|
79
|
+
if (typecode === RECEIVER_TYPECODE.P2PKH) hasP2pkh = true;
|
|
80
|
+
if (typecode === RECEIVER_TYPECODE.P2SH) hasP2sh = true;
|
|
81
|
+
if (typecode === RECEIVER_TYPECODE.P2PKH || typecode === RECEIVER_TYPECODE.P2SH || typecode === RECEIVER_TYPECODE.ORCHARD) hasOrchardOrTransparent = true;
|
|
82
|
+
offset += len;
|
|
83
|
+
}
|
|
84
|
+
if (offset !== paddingStart) return false;
|
|
85
|
+
if (hasP2pkh && hasP2sh) return false;
|
|
86
|
+
return hasOrchardOrTransparent;
|
|
87
|
+
} catch {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Bitcoin-style CompactSize decoder, canonical form only.
|
|
93
|
+
* Reference: `CompactSize::read` in
|
|
94
|
+
* https://github.com/zcash/librustzcash/blob/main/components/zcash_encoding/src/lib.rs
|
|
95
|
+
*/
|
|
96
|
+
function readCompactSize(buf, offset, end) {
|
|
97
|
+
if (offset >= end) return null;
|
|
98
|
+
const first = buf[offset];
|
|
99
|
+
if (first === void 0) return null;
|
|
100
|
+
if (first < 253) return {
|
|
101
|
+
value: first,
|
|
102
|
+
next: offset + 1
|
|
103
|
+
};
|
|
104
|
+
if (first === 253) {
|
|
105
|
+
if (offset + 3 > end) return null;
|
|
106
|
+
const b1 = buf[offset + 1];
|
|
107
|
+
const b2 = buf[offset + 2];
|
|
108
|
+
if (b1 === void 0 || b2 === void 0) return null;
|
|
109
|
+
const value = b1 | b2 << 8;
|
|
110
|
+
if (value < 253) return null;
|
|
111
|
+
return {
|
|
112
|
+
value,
|
|
113
|
+
next: offset + 3
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
if (first === 254) {
|
|
117
|
+
if (offset + 5 > end) return null;
|
|
118
|
+
const b1 = buf[offset + 1];
|
|
119
|
+
const b2 = buf[offset + 2];
|
|
120
|
+
const b3 = buf[offset + 3];
|
|
121
|
+
const b4 = buf[offset + 4];
|
|
122
|
+
if (b1 === void 0 || b2 === void 0 || b3 === void 0 || b4 === void 0) return null;
|
|
123
|
+
const value = (b1 | b2 << 8 | b3 << 16 | b4 << 24) >>> 0;
|
|
124
|
+
if (value <= 65535) return null;
|
|
125
|
+
return {
|
|
126
|
+
value,
|
|
127
|
+
next: offset + 5
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Inverse of ZIP 316 F4Jumble: a 4-round unkeyed Feistel over BLAKE2b.
|
|
134
|
+
* Forward: x = b ⊕ G_0(a); y = a ⊕ H_0(x); d = x ⊕ G_1(y); c = y ⊕ H_1(d)
|
|
135
|
+
* Inverse undoes each XOR in reverse order.
|
|
136
|
+
*
|
|
137
|
+
* Reference: `State::apply_f4jumble_inv` in
|
|
138
|
+
* https://github.com/zcash/librustzcash/blob/main/components/f4jumble/src/lib.rs
|
|
139
|
+
*/
|
|
140
|
+
function f4JumbleInverse(msg) {
|
|
141
|
+
const len = msg.length;
|
|
142
|
+
const lLen = Math.min(F4_BLAKE2B_LEN, Math.floor(len / 2));
|
|
143
|
+
const rLen = len - lLen;
|
|
144
|
+
const left = msg.slice(0, lLen);
|
|
145
|
+
const right = msg.slice(lLen);
|
|
146
|
+
xorInto(left, hI(1, right, lLen));
|
|
147
|
+
xorInto(right, gI(1, left, rLen));
|
|
148
|
+
xorInto(left, hI(0, right, lLen));
|
|
149
|
+
xorInto(right, gI(0, left, rLen));
|
|
150
|
+
const out = new Uint8Array(len);
|
|
151
|
+
out.set(left, 0);
|
|
152
|
+
out.set(right, lLen);
|
|
153
|
+
return out;
|
|
154
|
+
}
|
|
155
|
+
function hI(i, u, lLen) {
|
|
156
|
+
return blake2b(u, {
|
|
157
|
+
personalization: buildPersonalization(H_PREFIX, i, 0),
|
|
158
|
+
dkLen: lLen
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
function gI(i, u, rLen) {
|
|
162
|
+
const chunks = Math.ceil(rLen / F4_BLAKE2B_LEN);
|
|
163
|
+
const out = new Uint8Array(chunks * F4_BLAKE2B_LEN);
|
|
164
|
+
for (let j = 0; j < chunks; j++) {
|
|
165
|
+
const digest = blake2b(u, {
|
|
166
|
+
personalization: buildPersonalization(G_PREFIX, i, j),
|
|
167
|
+
dkLen: F4_BLAKE2B_LEN
|
|
168
|
+
});
|
|
169
|
+
out.set(digest, j * F4_BLAKE2B_LEN);
|
|
170
|
+
}
|
|
171
|
+
return out.subarray(0, rLen);
|
|
172
|
+
}
|
|
173
|
+
function buildPersonalization(prefix, i, j) {
|
|
174
|
+
const p = new Uint8Array(16);
|
|
175
|
+
p.set(prefix, 0);
|
|
176
|
+
p[13] = i & 255;
|
|
177
|
+
p[14] = j & 255;
|
|
178
|
+
p[15] = j >> 8 & 255;
|
|
179
|
+
return p;
|
|
180
|
+
}
|
|
181
|
+
function asciiBytes(s) {
|
|
182
|
+
const out = new Uint8Array(s.length);
|
|
183
|
+
for (let i = 0; i < s.length; i++) out[i] = s.charCodeAt(i);
|
|
184
|
+
return out;
|
|
185
|
+
}
|
|
186
|
+
function xorInto(target, other) {
|
|
187
|
+
for (let i = 0; i < target.length; i++) target[i] = target[i] ^ other[i];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
//#endregion
|
|
191
|
+
export { validateZcashUnifiedAddress };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@defuse-protocol/intents-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.83.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -48,8 +48,8 @@
|
|
|
48
48
|
"@hot-labs/omni-sdk": "2.25.5",
|
|
49
49
|
"@isaacs/ttlcache": "^1.0.0",
|
|
50
50
|
"@lifeomic/attempt": "^3.0.0",
|
|
51
|
-
"@omni-bridge/core": "0.
|
|
52
|
-
"@omni-bridge/near": "0.
|
|
51
|
+
"@omni-bridge/core": "0.19.0",
|
|
52
|
+
"@omni-bridge/near": "0.19.0",
|
|
53
53
|
"@noble/hashes": "^1.7.1",
|
|
54
54
|
"@scure/base": "^1.0.0",
|
|
55
55
|
"borsher": "^4.0.0",
|