@defuse-protocol/intents-sdk 0.82.0 → 0.84.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.
@@ -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",
@@ -42,7 +41,8 @@ const caip2Mapping = {
42
41
  [require_caip2.Chains.Aleo]: "aleo:mainnet",
43
42
  [require_caip2.Chains.Dash]: "dash:mainnet",
44
43
  [require_caip2.Chains.Plasma]: "eth:9745",
45
- [require_caip2.Chains.Adi]: "eth:36900"
44
+ [require_caip2.Chains.Adi]: "eth:36900",
45
+ [require_caip2.Chains.Robinhood]: "eth:4663"
46
46
  };
47
47
  function toPoaNetwork(caip2) {
48
48
  if (caip2Mapping[caip2] == null) throw new Error(`Unsupported POA Bridge chain = ${caip2}`);
@@ -56,7 +56,6 @@ const tokenPrefixMapping = {
56
56
  bch: require_caip2.Chains.BitcoinCash,
57
57
  doge: require_caip2.Chains.Dogecoin,
58
58
  xrp: require_caip2.Chains.XRPL,
59
- zec: require_caip2.Chains.Zcash,
60
59
  gnosis: require_caip2.Chains.Gnosis,
61
60
  bera: require_caip2.Chains.Berachain,
62
61
  tron: require_caip2.Chains.Tron,
@@ -67,7 +66,8 @@ const tokenPrefixMapping = {
67
66
  aleo: require_caip2.Chains.Aleo,
68
67
  dash: require_caip2.Chains.Dash,
69
68
  plasma: require_caip2.Chains.Plasma,
70
- adi: require_caip2.Chains.Adi
69
+ adi: require_caip2.Chains.Adi,
70
+ hood: require_caip2.Chains.Robinhood
71
71
  };
72
72
  function contractIdToCaip2(contractId) {
73
73
  for (const [prefix, caip2] of Object.entries(tokenPrefixMapping)) if (contractId.startsWith(`${prefix}.`) || contractId.startsWith(`${prefix}-`)) return caip2;
@@ -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",
@@ -41,7 +40,8 @@ const caip2Mapping = {
41
40
  [Chains.Aleo]: "aleo:mainnet",
42
41
  [Chains.Dash]: "dash:mainnet",
43
42
  [Chains.Plasma]: "eth:9745",
44
- [Chains.Adi]: "eth:36900"
43
+ [Chains.Adi]: "eth:36900",
44
+ [Chains.Robinhood]: "eth:4663"
45
45
  };
46
46
  function toPoaNetwork(caip2) {
47
47
  if (caip2Mapping[caip2] == null) throw new Error(`Unsupported POA Bridge chain = ${caip2}`);
@@ -55,7 +55,6 @@ const tokenPrefixMapping = {
55
55
  bch: Chains.BitcoinCash,
56
56
  doge: Chains.Dogecoin,
57
57
  xrp: Chains.XRPL,
58
- zec: Chains.Zcash,
59
58
  gnosis: Chains.Gnosis,
60
59
  bera: Chains.Berachain,
61
60
  tron: Chains.Tron,
@@ -66,7 +65,8 @@ const tokenPrefixMapping = {
66
65
  aleo: Chains.Aleo,
67
66
  dash: Chains.Dash,
68
67
  plasma: Chains.Plasma,
69
- adi: Chains.Adi
68
+ adi: Chains.Adi,
69
+ hood: Chains.Robinhood
70
70
  };
71
71
  function contractIdToCaip2(contractId) {
72
72
  for (const [prefix, caip2] of Object.entries(tokenPrefixMapping)) if (contractId.startsWith(`${prefix}.`) || contractId.startsWith(`${prefix}-`)) return caip2;
@@ -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 originaly have been PoA Bridge tokens but have been migrated to Omni bridge.
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 originaly have been PoA Bridge tokens but have been migrated to Omni bridge.
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 originaly have been PoA Bridge tokens but have been migrated to Omni bridge.
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 originaly have been PoA Bridge tokens but have been migrated to Omni bridge.
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
@@ -41,7 +41,8 @@ const Chains = {
41
41
  Dash: "bip122:00000ffd590b1485b3caadc19b22e637",
42
42
  Abstract: "eip155:2741",
43
43
  HyperCore: "hlcore:mainnet",
44
- HyperEvm: "eip155:999"
44
+ HyperEvm: "eip155:999",
45
+ Robinhood: "eip155:4663"
45
46
  };
46
47
  function getEIP155ChainId(chain) {
47
48
  (0, _defuse_protocol_internal_utils.assert)(chain.startsWith("eip155:"), "Chain is not an EIP-155 chain");
@@ -39,6 +39,7 @@ declare const Chains: {
39
39
  readonly Abstract: "eip155:2741";
40
40
  readonly HyperCore: "hlcore:mainnet";
41
41
  readonly HyperEvm: "eip155:999";
42
+ readonly Robinhood: "eip155:4663";
42
43
  };
43
44
  type Chain = (typeof Chains)[keyof typeof Chains];
44
45
  //#endregion
@@ -39,6 +39,7 @@ declare const Chains: {
39
39
  readonly Abstract: "eip155:2741";
40
40
  readonly HyperCore: "hlcore:mainnet";
41
41
  readonly HyperEvm: "eip155:999";
42
+ readonly Robinhood: "eip155:4663";
42
43
  };
43
44
  type Chain = (typeof Chains)[keyof typeof Chains];
44
45
  //#endregion
@@ -40,7 +40,8 @@ const Chains = {
40
40
  Dash: "bip122:00000ffd590b1485b3caadc19b22e637",
41
41
  Abstract: "eip155:2741",
42
42
  HyperCore: "hlcore:mainnet",
43
- HyperEvm: "eip155:999"
43
+ HyperEvm: "eip155:999",
44
+ Robinhood: "eip155:4663"
44
45
  };
45
46
  function getEIP155ChainId(chain) {
46
47
  assert(chain.startsWith("eip155:"), "Chain is not an EIP-155 chain");
@@ -30,7 +30,8 @@ function compareAddresses(a, b, blockchain) {
30
30
  case require_caip2.Chains.Scroll:
31
31
  case require_caip2.Chains.Abstract:
32
32
  case require_caip2.Chains.HyperCore:
33
- case require_caip2.Chains.HyperEvm: return (0, viem.getAddress)(a) === (0, viem.getAddress)(b);
33
+ case require_caip2.Chains.HyperEvm:
34
+ case require_caip2.Chains.Robinhood: return (0, viem.getAddress)(a) === (0, viem.getAddress)(b);
34
35
  case require_caip2.Chains.Aptos:
35
36
  case require_caip2.Chains.Movement:
36
37
  case require_caip2.Chains.Sui:
@@ -29,7 +29,8 @@ function compareAddresses(a, b, blockchain) {
29
29
  case Chains.Scroll:
30
30
  case Chains.Abstract:
31
31
  case Chains.HyperCore:
32
- case Chains.HyperEvm: return getAddress(a) === getAddress(b);
32
+ case Chains.HyperEvm:
33
+ case Chains.Robinhood: return getAddress(a) === getAddress(b);
33
34
  case Chains.Aptos:
34
35
  case Chains.Movement:
35
36
  case Chains.Sui:
@@ -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");
@@ -50,7 +51,8 @@ function validateAddress(address, blockchain) {
50
51
  case require_caip2.Chains.Scroll:
51
52
  case require_caip2.Chains.Abstract:
52
53
  case require_caip2.Chains.HyperCore:
53
- case require_caip2.Chains.HyperEvm: return validateEthAddress(address);
54
+ case require_caip2.Chains.HyperEvm:
55
+ case require_caip2.Chains.Robinhood: return validateEthAddress(address);
54
56
  case require_caip2.Chains.Aleo: return validateAleoAddress(address);
55
57
  case require_caip2.Chains.Dash: return validateDashAddress(address);
56
58
  default: return false;
@@ -199,6 +201,7 @@ function validateXrpAddress(address) {
199
201
  * Supports:
200
202
  * - Transparent addresses (t1, t3)
201
203
  * - TEX addresses (tex1)
204
+ * - Unified Orchard / Unified Addresses (UA)
202
205
  */
203
206
  function validateZcashAddress(address) {
204
207
  if (address.startsWith("t1") || address.startsWith("t3")) return /^t[13][a-km-zA-HJ-NP-Z1-9]{33}$/.test(address);
@@ -210,12 +213,7 @@ function validateZcashAddress(address) {
210
213
  } catch {
211
214
  return false;
212
215
  }
213
- const uaHrp = "u";
214
- if (address.startsWith(`${uaHrp}1`)) try {
215
- return _scure_base.bech32m.decodeToBytes(address).prefix === uaHrp;
216
- } catch {
217
- return false;
218
- }
216
+ if (address.startsWith("u1")) return require_zcash_unified_address.validateZcashUnifiedAddress(address);
219
217
  return false;
220
218
  }
221
219
  /**
@@ -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";
@@ -49,7 +50,8 @@ function validateAddress(address, blockchain) {
49
50
  case Chains.Scroll:
50
51
  case Chains.Abstract:
51
52
  case Chains.HyperCore:
52
- case Chains.HyperEvm: return validateEthAddress(address);
53
+ case Chains.HyperEvm:
54
+ case Chains.Robinhood: return validateEthAddress(address);
53
55
  case Chains.Aleo: return validateAleoAddress(address);
54
56
  case Chains.Dash: return validateDashAddress(address);
55
57
  default: return false;
@@ -198,6 +200,7 @@ function validateXrpAddress(address) {
198
200
  * Supports:
199
201
  * - Transparent addresses (t1, t3)
200
202
  * - TEX addresses (tex1)
203
+ * - Unified Orchard / Unified Addresses (UA)
201
204
  */
202
205
  function validateZcashAddress(address) {
203
206
  if (address.startsWith("t1") || address.startsWith("t3")) return /^t[13][a-km-zA-HJ-NP-Z1-9]{33}$/.test(address);
@@ -209,12 +212,7 @@ function validateZcashAddress(address) {
209
212
  } catch {
210
213
  return false;
211
214
  }
212
- const uaHrp = "u";
213
- if (address.startsWith(`${uaHrp}1`)) try {
214
- return bech32m.decodeToBytes(address).prefix === uaHrp;
215
- } catch {
216
- return false;
217
- }
215
+ if (address.startsWith("u1")) return validateZcashUnifiedAddress(address);
218
216
  return false;
219
217
  }
220
218
  /**
@@ -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.82.0",
3
+ "version": "0.84.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.18.0",
52
- "@omni-bridge/near": "0.18.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",
@@ -59,7 +59,7 @@
59
59
  "valibot": "^1.0.0",
60
60
  "viem": "^2.0.0",
61
61
  "@defuse-protocol/contract-types": "0.6.4",
62
- "@defuse-protocol/internal-utils": "0.37.0"
62
+ "@defuse-protocol/internal-utils": "0.38.0"
63
63
  },
64
64
  "devDependencies": {
65
65
  "@ton/core": "^0.60.0",