@defuse-protocol/intents-sdk 0.81.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 +21 -39
- package/dist/src/bridges/omni-bridge/omni-bridge-utils.js +23 -40
- package/dist/src/bridges/omni-bridge/omni-bridge.cjs +14 -42
- package/dist/src/bridges/omni-bridge/omni-bridge.js +7 -35
- package/dist/src/bridges/omni-bridge/omni-withdraw-params.cjs +72 -0
- package/dist/src/bridges/omni-bridge/omni-withdraw-params.d.cts +60 -0
- package/dist/src/bridges/omni-bridge/omni-withdraw-params.d.ts +60 -0
- package/dist/src/bridges/omni-bridge/omni-withdraw-params.js +69 -0
- 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 +13 -3
|
@@ -6,39 +6,28 @@ let _defuse_protocol_internal_utils = require("@defuse-protocol/internal-utils")
|
|
|
6
6
|
let valibot = require("valibot");
|
|
7
7
|
valibot = require_rolldown_runtime.__toESM(valibot);
|
|
8
8
|
let _omni_bridge_core = require("@omni-bridge/core");
|
|
9
|
-
let _omni_bridge_near = require("@omni-bridge/near");
|
|
10
9
|
|
|
11
10
|
//#region src/bridges/omni-bridge/omni-bridge-utils.ts
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Lays a calculated withdrawal out as intents.
|
|
13
|
+
*
|
|
14
|
+
* It takes the result of `deriveOmniWithdrawIntentParams` rather than the inputs to it, so
|
|
15
|
+
* there is one calculation and no way to hand it a value that was worked out some other way.
|
|
16
|
+
* That matters most for the amount: a UTXO chain has already added its fees to it, and doing
|
|
17
|
+
* that twice would send more than the caller asked for.
|
|
18
|
+
*/
|
|
19
|
+
function createWithdrawIntentsPrimitive({ tokenAccountId, recipient, msg, externalId, storageDepositAccountId, amount, nativeFee, storageDepositAmount }) {
|
|
17
20
|
const ftWithdrawPayload = {
|
|
18
21
|
recipient,
|
|
19
22
|
fee: "0",
|
|
20
|
-
native_token_fee:
|
|
21
|
-
external_id:
|
|
23
|
+
native_token_fee: nativeFee.toString(),
|
|
24
|
+
external_id: externalId
|
|
22
25
|
};
|
|
23
|
-
if (
|
|
24
|
-
(0, _defuse_protocol_internal_utils.assert)(params.utxoMaxGasFee !== null && params.utxoMaxGasFee > 0n, `Invalid utxo max gas fee: expected > 0, got ${params.utxoMaxGasFee}`);
|
|
25
|
-
msg = JSON.stringify({ MaxGasFee: params.utxoMaxGasFee.toString() });
|
|
26
|
-
ftWithdrawPayload.msg = msg;
|
|
27
|
-
}
|
|
26
|
+
if (msg !== "") ftWithdrawPayload.msg = msg;
|
|
28
27
|
const intents = [];
|
|
29
|
-
if (
|
|
30
|
-
deposit_for_account_id:
|
|
31
|
-
|
|
32
|
-
amount: params.amount,
|
|
33
|
-
recipient,
|
|
34
|
-
fee: {
|
|
35
|
-
fee: 0n,
|
|
36
|
-
native_fee: params.nativeFee
|
|
37
|
-
},
|
|
38
|
-
sender: `near:${params.intentsContract}`,
|
|
39
|
-
msg
|
|
40
|
-
}, ftWithdrawPayload.external_id),
|
|
41
|
-
amount: params.nativeFee.toString(),
|
|
28
|
+
if (storageDepositAccountId !== null) intents.push({
|
|
29
|
+
deposit_for_account_id: storageDepositAccountId,
|
|
30
|
+
amount: nativeFee.toString(),
|
|
42
31
|
contract_id: require_omni_bridge_constants.OMNI_BRIDGE_CONTRACT,
|
|
43
32
|
intent: "storage_deposit"
|
|
44
33
|
});
|
|
@@ -46,8 +35,8 @@ function createWithdrawIntentsPrimitive(params) {
|
|
|
46
35
|
intent: "ft_withdraw",
|
|
47
36
|
token: tokenAccountId,
|
|
48
37
|
receiver_id: require_omni_bridge_constants.OMNI_BRIDGE_CONTRACT,
|
|
49
|
-
amount:
|
|
50
|
-
storage_deposit:
|
|
38
|
+
amount: amount.toString(),
|
|
39
|
+
storage_deposit: storageDepositAmount > 0n ? storageDepositAmount.toString() : void 0,
|
|
51
40
|
msg: JSON.stringify(ftWithdrawPayload),
|
|
52
41
|
min_gas: require_omni_bridge_constants.MIN_GAS_AMOUNT
|
|
53
42
|
});
|
|
@@ -69,18 +58,12 @@ const CHAIN_MAPPINGS = [
|
|
|
69
58
|
[require_caip2.Chains.Fogo, _omni_bridge_core.ChainKind.Fogo],
|
|
70
59
|
[require_caip2.Chains.Polygon, _omni_bridge_core.ChainKind.Pol],
|
|
71
60
|
[require_caip2.Chains.Aptos, _omni_bridge_core.ChainKind.Aptos],
|
|
72
|
-
[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]
|
|
73
63
|
];
|
|
74
|
-
function caip2ToChainKind(network) {
|
|
75
|
-
return CHAIN_MAPPINGS.find(([chain]) => chain === network)?.[1] ?? null;
|
|
76
|
-
}
|
|
77
64
|
function chainKindToCaip2(network) {
|
|
78
65
|
return CHAIN_MAPPINGS.find(([_, kind]) => kind === network)?.[0] ?? null;
|
|
79
66
|
}
|
|
80
|
-
const UTXO_CHAINS = [_omni_bridge_core.ChainKind.Btc];
|
|
81
|
-
function isUtxoChain(network) {
|
|
82
|
-
return UTXO_CHAINS.includes(network);
|
|
83
|
-
}
|
|
84
67
|
function poaContractIdToChainKind(contractId) {
|
|
85
68
|
return require_poa_tokens_migrated_to_omni_bridge.POA_TOKENS_MIGRATED_TO_OMNI_BRIDGE[contractId] ?? null;
|
|
86
69
|
}
|
|
@@ -101,7 +84,7 @@ async function getAccountOmniStorageBalance(nearProvider, accountId) {
|
|
|
101
84
|
})])
|
|
102
85
|
});
|
|
103
86
|
}
|
|
104
|
-
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");
|
|
105
88
|
/**
|
|
106
89
|
* Converts a token address from one chain to its equivalent on another chain.
|
|
107
90
|
* @param nearProvider Near provider used for querying the contract
|
|
@@ -149,12 +132,11 @@ async function getTokenDecimals(nearProvider, tokenAddress) {
|
|
|
149
132
|
}
|
|
150
133
|
|
|
151
134
|
//#endregion
|
|
152
|
-
exports.
|
|
135
|
+
exports.CHAIN_MAPPINGS = CHAIN_MAPPINGS;
|
|
153
136
|
exports.chainKindToCaip2 = chainKindToCaip2;
|
|
154
137
|
exports.createWithdrawIntentsPrimitive = createWithdrawIntentsPrimitive;
|
|
155
138
|
exports.getAccountOmniStorageBalance = getAccountOmniStorageBalance;
|
|
156
139
|
exports.getBridgedToken = getBridgedToken;
|
|
157
140
|
exports.getTokenDecimals = getTokenDecimals;
|
|
158
|
-
exports.isUtxoChain = isUtxoChain;
|
|
159
141
|
exports.poaContractIdToChainKind = poaContractIdToChainKind;
|
|
160
142
|
exports.validateOmniToken = validateOmniToken;
|
|
@@ -1,42 +1,31 @@
|
|
|
1
1
|
import { Chains } from "../../lib/caip2.js";
|
|
2
2
|
import { MIN_GAS_AMOUNT, OMNI_BRIDGE_CONTRACT } from "./omni-bridge-constants.js";
|
|
3
3
|
import { POA_TOKENS_MIGRATED_TO_OMNI_BRIDGE } from "../../constants/poa-tokens-migrated-to-omni-bridge.js";
|
|
4
|
-
import {
|
|
4
|
+
import { utils } from "@defuse-protocol/internal-utils";
|
|
5
5
|
import * as v from "valibot";
|
|
6
|
-
import { ChainKind, getChain, isBridgeToken
|
|
7
|
-
import { calculateStorageAccountId } from "@omni-bridge/near";
|
|
6
|
+
import { ChainKind, getChain, isBridgeToken } from "@omni-bridge/core";
|
|
8
7
|
|
|
9
8
|
//#region src/bridges/omni-bridge/omni-bridge-utils.ts
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Lays a calculated withdrawal out as intents.
|
|
11
|
+
*
|
|
12
|
+
* It takes the result of `deriveOmniWithdrawIntentParams` rather than the inputs to it, so
|
|
13
|
+
* there is one calculation and no way to hand it a value that was worked out some other way.
|
|
14
|
+
* That matters most for the amount: a UTXO chain has already added its fees to it, and doing
|
|
15
|
+
* that twice would send more than the caller asked for.
|
|
16
|
+
*/
|
|
17
|
+
function createWithdrawIntentsPrimitive({ tokenAccountId, recipient, msg, externalId, storageDepositAccountId, amount, nativeFee, storageDepositAmount }) {
|
|
15
18
|
const ftWithdrawPayload = {
|
|
16
19
|
recipient,
|
|
17
20
|
fee: "0",
|
|
18
|
-
native_token_fee:
|
|
19
|
-
external_id:
|
|
21
|
+
native_token_fee: nativeFee.toString(),
|
|
22
|
+
external_id: externalId
|
|
20
23
|
};
|
|
21
|
-
if (
|
|
22
|
-
assert(params.utxoMaxGasFee !== null && params.utxoMaxGasFee > 0n, `Invalid utxo max gas fee: expected > 0, got ${params.utxoMaxGasFee}`);
|
|
23
|
-
msg = JSON.stringify({ MaxGasFee: params.utxoMaxGasFee.toString() });
|
|
24
|
-
ftWithdrawPayload.msg = msg;
|
|
25
|
-
}
|
|
24
|
+
if (msg !== "") ftWithdrawPayload.msg = msg;
|
|
26
25
|
const intents = [];
|
|
27
|
-
if (
|
|
28
|
-
deposit_for_account_id:
|
|
29
|
-
|
|
30
|
-
amount: params.amount,
|
|
31
|
-
recipient,
|
|
32
|
-
fee: {
|
|
33
|
-
fee: 0n,
|
|
34
|
-
native_fee: params.nativeFee
|
|
35
|
-
},
|
|
36
|
-
sender: `near:${params.intentsContract}`,
|
|
37
|
-
msg
|
|
38
|
-
}, ftWithdrawPayload.external_id),
|
|
39
|
-
amount: params.nativeFee.toString(),
|
|
26
|
+
if (storageDepositAccountId !== null) intents.push({
|
|
27
|
+
deposit_for_account_id: storageDepositAccountId,
|
|
28
|
+
amount: nativeFee.toString(),
|
|
40
29
|
contract_id: OMNI_BRIDGE_CONTRACT,
|
|
41
30
|
intent: "storage_deposit"
|
|
42
31
|
});
|
|
@@ -44,8 +33,8 @@ function createWithdrawIntentsPrimitive(params) {
|
|
|
44
33
|
intent: "ft_withdraw",
|
|
45
34
|
token: tokenAccountId,
|
|
46
35
|
receiver_id: OMNI_BRIDGE_CONTRACT,
|
|
47
|
-
amount:
|
|
48
|
-
storage_deposit:
|
|
36
|
+
amount: amount.toString(),
|
|
37
|
+
storage_deposit: storageDepositAmount > 0n ? storageDepositAmount.toString() : void 0,
|
|
49
38
|
msg: JSON.stringify(ftWithdrawPayload),
|
|
50
39
|
min_gas: MIN_GAS_AMOUNT
|
|
51
40
|
});
|
|
@@ -67,18 +56,12 @@ const CHAIN_MAPPINGS = [
|
|
|
67
56
|
[Chains.Fogo, ChainKind.Fogo],
|
|
68
57
|
[Chains.Polygon, ChainKind.Pol],
|
|
69
58
|
[Chains.Aptos, ChainKind.Aptos],
|
|
70
|
-
[Chains.HyperEvm, ChainKind.HlEvm]
|
|
59
|
+
[Chains.HyperEvm, ChainKind.HlEvm],
|
|
60
|
+
[Chains.Zcash, ChainKind.Zcash]
|
|
71
61
|
];
|
|
72
|
-
function caip2ToChainKind(network) {
|
|
73
|
-
return CHAIN_MAPPINGS.find(([chain]) => chain === network)?.[1] ?? null;
|
|
74
|
-
}
|
|
75
62
|
function chainKindToCaip2(network) {
|
|
76
63
|
return CHAIN_MAPPINGS.find(([_, kind]) => kind === network)?.[0] ?? null;
|
|
77
64
|
}
|
|
78
|
-
const UTXO_CHAINS = [ChainKind.Btc];
|
|
79
|
-
function isUtxoChain(network) {
|
|
80
|
-
return UTXO_CHAINS.includes(network);
|
|
81
|
-
}
|
|
82
65
|
function poaContractIdToChainKind(contractId) {
|
|
83
66
|
return POA_TOKENS_MIGRATED_TO_OMNI_BRIDGE[contractId] ?? null;
|
|
84
67
|
}
|
|
@@ -99,7 +82,7 @@ async function getAccountOmniStorageBalance(nearProvider, accountId) {
|
|
|
99
82
|
})])
|
|
100
83
|
});
|
|
101
84
|
}
|
|
102
|
-
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");
|
|
103
86
|
/**
|
|
104
87
|
* Converts a token address from one chain to its equivalent on another chain.
|
|
105
88
|
* @param nearProvider Near provider used for querying the contract
|
|
@@ -147,4 +130,4 @@ async function getTokenDecimals(nearProvider, tokenAddress) {
|
|
|
147
130
|
}
|
|
148
131
|
|
|
149
132
|
//#endregion
|
|
150
|
-
export {
|
|
133
|
+
export { CHAIN_MAPPINGS, chainKindToCaip2, createWithdrawIntentsPrimitive, getAccountOmniStorageBalance, getBridgedToken, getTokenDecimals, poaContractIdToChainKind, validateOmniToken };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const require_rolldown_runtime = require('../../../_virtual/rolldown_runtime.cjs');
|
|
2
2
|
const require_errors = require('../../classes/errors.cjs');
|
|
3
3
|
const require_route_enum = require('../../constants/route-enum.cjs');
|
|
4
|
-
const require_caip2 = require('../../lib/caip2.cjs');
|
|
5
4
|
const require_estimate_fee = require('../../lib/estimate-fee.cjs');
|
|
6
5
|
const require_parse_defuse_asset_id = require('../../lib/parse-defuse-asset-id.cjs');
|
|
7
6
|
const require_validateAddress = require('../../lib/validateAddress.cjs');
|
|
@@ -11,6 +10,7 @@ const require_error = require('./error.cjs');
|
|
|
11
10
|
const require_omni_bridge_constants = require('./omni-bridge-constants.cjs');
|
|
12
11
|
const require_poa_tokens_migrated_to_omni_bridge = require('../../constants/poa-tokens-migrated-to-omni-bridge.cjs');
|
|
13
12
|
const require_omni_bridge_utils = require('./omni-bridge-utils.cjs');
|
|
13
|
+
const require_src_bridges_omni_bridge_omni_withdraw_params = require('./omni-withdraw-params.cjs');
|
|
14
14
|
let _defuse_protocol_internal_utils = require("@defuse-protocol/internal-utils");
|
|
15
15
|
let lru_cache = require("lru-cache");
|
|
16
16
|
let _isaacs_ttlcache = require("@isaacs/ttlcache");
|
|
@@ -55,7 +55,7 @@ var OmniBridge = class {
|
|
|
55
55
|
let omniChainKind = null;
|
|
56
56
|
let caip2Chain = null;
|
|
57
57
|
if (this.targetChainSpecified(params.routeConfig)) {
|
|
58
|
-
omniChainKind =
|
|
58
|
+
omniChainKind = require_src_bridges_omni_bridge_omni_withdraw_params.caip2ToChainKind(params.routeConfig.chain);
|
|
59
59
|
if (omniChainKind === null) throw new require_errors.UnsupportedAssetIdError(params.assetId, `Chain ${params.routeConfig.chain} is not supported in Omni Bridge.`);
|
|
60
60
|
caip2Chain = params.routeConfig.chain;
|
|
61
61
|
} else {
|
|
@@ -89,7 +89,7 @@ var OmniBridge = class {
|
|
|
89
89
|
let omniChainKind = null;
|
|
90
90
|
let blockchain = null;
|
|
91
91
|
if (this.targetChainSpecified(routeConfig)) {
|
|
92
|
-
omniChainKind =
|
|
92
|
+
omniChainKind = require_src_bridges_omni_bridge_omni_withdraw_params.caip2ToChainKind(routeConfig.chain);
|
|
93
93
|
blockchain = routeConfig.chain;
|
|
94
94
|
} else {
|
|
95
95
|
omniChainKind = this.isPoaTokenMigratedToOmniBridge(parsed.contractId) ? require_omni_bridge_utils.poaContractIdToChainKind(parsed.contractId) : (0, _omni_bridge_core.parseOriginChain)(parsed.contractId);
|
|
@@ -106,7 +106,7 @@ var OmniBridge = class {
|
|
|
106
106
|
async createWithdrawalIntents(args) {
|
|
107
107
|
const assetInfo = this.makeAssetInfo(args.withdrawalParams.assetId, args.withdrawalParams.routeConfig);
|
|
108
108
|
(0, _defuse_protocol_internal_utils.assert)(assetInfo !== null, `Asset ${args.withdrawalParams.assetId} is not supported by Omni Bridge`);
|
|
109
|
-
const omniChainKind =
|
|
109
|
+
const omniChainKind = require_src_bridges_omni_bridge_omni_withdraw_params.caip2ToChainKind(assetInfo.blockchain);
|
|
110
110
|
(0, _defuse_protocol_internal_utils.assert)(omniChainKind !== null, `Chain ${assetInfo.blockchain} is not supported by Omni Bridge`);
|
|
111
111
|
const intents = [];
|
|
112
112
|
if (args.feeEstimation.quote !== null) intents.push({
|
|
@@ -117,42 +117,14 @@ var OmniBridge = class {
|
|
|
117
117
|
},
|
|
118
118
|
referral: args.referral
|
|
119
119
|
});
|
|
120
|
-
|
|
121
|
-
(0, _defuse_protocol_internal_utils.assert)(relayerFee >= 0n, `Invalid Omni bridge relayer fee: expected >= 0, got ${relayerFee}`);
|
|
122
|
-
let amount = args.withdrawalParams.amount;
|
|
123
|
-
let utxoMaxGasFee = null;
|
|
124
|
-
/**
|
|
125
|
-
* UTXO withdrawals add protocol + max gas fees to the intent amount since they're paid
|
|
126
|
-
* from the withdrawn asset, not wrap.near.
|
|
127
|
-
*
|
|
128
|
-
* Example with nep141:nbtc.bridge.near (made-up values):
|
|
129
|
-
* utxoFees = 50 + 50 = 100, relayerFee = 2 (excluded; paid in wrap.near)
|
|
130
|
-
*
|
|
131
|
-
* feeInclusive=false:
|
|
132
|
-
* - amount = 4000 → intent = 4000 + 100 = 4100 → user receives 4000
|
|
133
|
-
*
|
|
134
|
-
* feeInclusive=true:
|
|
135
|
-
* - amount = 3898 (4000 − 102) → intent = 3898 + 100 = 3998 → user receives 3898
|
|
136
|
-
**/
|
|
137
|
-
if (require_omni_bridge_utils.isUtxoChain(omniChainKind)) {
|
|
138
|
-
utxoMaxGasFee = require_estimate_fee.getUnderlyingFee(args.feeEstimation, require_route_enum.RouteEnum.OmniBridge, "utxoMaxGasFee");
|
|
139
|
-
const utxoProtocolFee = require_estimate_fee.getUnderlyingFee(args.feeEstimation, require_route_enum.RouteEnum.OmniBridge, "utxoProtocolFee");
|
|
140
|
-
(0, _defuse_protocol_internal_utils.assert)(utxoMaxGasFee !== void 0 && utxoMaxGasFee > 0n, `Invalid Omni Bridge utxo max gas fee: expected > 0, got ${utxoMaxGasFee}`);
|
|
141
|
-
(0, _defuse_protocol_internal_utils.assert)(utxoProtocolFee !== void 0 && utxoProtocolFee > 0n, `Invalid Omni Bridge utxo protocol fee: expected > 0, got ${utxoProtocolFee}`);
|
|
142
|
-
amount += utxoMaxGasFee + utxoProtocolFee;
|
|
143
|
-
}
|
|
144
|
-
let destinationAddress = args.withdrawalParams.destinationAddress;
|
|
145
|
-
if (assetInfo.blockchain === require_caip2.Chains.Bitcoin && /^bc1/i.test(destinationAddress)) destinationAddress = destinationAddress.toLowerCase();
|
|
146
|
-
intents.push(...require_omni_bridge_utils.createWithdrawIntentsPrimitive({
|
|
120
|
+
intents.push(...require_omni_bridge_utils.createWithdrawIntentsPrimitive(require_src_bridges_omni_bridge_omni_withdraw_params.deriveOmniWithdrawIntentParams({
|
|
147
121
|
assetId: args.withdrawalParams.assetId,
|
|
148
|
-
destinationAddress,
|
|
149
|
-
amount,
|
|
122
|
+
destinationAddress: args.withdrawalParams.destinationAddress,
|
|
123
|
+
actualAmount: args.withdrawalParams.amount,
|
|
150
124
|
omniChainKind,
|
|
151
125
|
intentsContract: this.envConfig.contractID,
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
utxoMaxGasFee
|
|
155
|
-
}));
|
|
126
|
+
feeEstimation: args.feeEstimation
|
|
127
|
+
})));
|
|
156
128
|
return Promise.resolve(intents);
|
|
157
129
|
}
|
|
158
130
|
async validateWithdrawal(args) {
|
|
@@ -162,7 +134,7 @@ var OmniBridge = class {
|
|
|
162
134
|
const assetInfo = this.makeAssetInfo(args.assetId, args.routeConfig);
|
|
163
135
|
(0, _defuse_protocol_internal_utils.assert)(assetInfo !== null, `Asset ${args.assetId} is not supported by Omni Bridge`);
|
|
164
136
|
if (require_validateAddress.validateAddress(args.destinationAddress, assetInfo.blockchain) === false) throw new require_errors.InvalidDestinationAddressForWithdrawalError(args.destinationAddress, assetInfo.blockchain);
|
|
165
|
-
const omniChainKind =
|
|
137
|
+
const omniChainKind = require_src_bridges_omni_bridge_omni_withdraw_params.caip2ToChainKind(assetInfo.blockchain);
|
|
166
138
|
(0, _defuse_protocol_internal_utils.assert)(omniChainKind !== null, `Chain ${assetInfo.blockchain} is not supported by Omni Bridge`);
|
|
167
139
|
const destTokenOmniAddress = await this.getCachedDestinationTokenAddress(assetInfo.contractId, omniChainKind);
|
|
168
140
|
if (destTokenOmniAddress === null) throw new require_error.TokenNotFoundInDestinationChainError(args.assetId, assetInfo.blockchain);
|
|
@@ -176,7 +148,7 @@ var OmniBridge = class {
|
|
|
176
148
|
}
|
|
177
149
|
const intentsStorageBalance = await this.getCachedIntentsStorageBalance();
|
|
178
150
|
if (intentsStorageBalance <= require_omni_bridge_constants.MIN_STORAGE_BALANCE_FOR_INTENTS_NEAR) throw new require_error.IntentsNearOmniAvailableBalanceTooLowError(intentsStorageBalance.toString());
|
|
179
|
-
const utxoChainWithdrawal =
|
|
151
|
+
const utxoChainWithdrawal = require_src_bridges_omni_bridge_omni_withdraw_params.isUtxoChain(omniChainKind);
|
|
180
152
|
if (!utxoChainWithdrawal && !isFeeSubsidized) {
|
|
181
153
|
const relayerFee = require_estimate_fee.getUnderlyingFee(args.feeEstimation, require_route_enum.RouteEnum.OmniBridge, "relayerFee");
|
|
182
154
|
(0, _defuse_protocol_internal_utils.assert)(require_estimate_fee.getUnderlyingFee(args.feeEstimation, require_route_enum.RouteEnum.OmniBridge, "relayerFee") > 0n, `Invalid Omni Bridge relayer fee for non UTXO chain withdrawal: expected > 0, got ${relayerFee}`);
|
|
@@ -202,7 +174,7 @@ var OmniBridge = class {
|
|
|
202
174
|
async estimateWithdrawalFee(args) {
|
|
203
175
|
const assetInfo = this.makeAssetInfo(args.withdrawalParams.assetId, args.withdrawalParams.routeConfig);
|
|
204
176
|
(0, _defuse_protocol_internal_utils.assert)(assetInfo !== null, `Asset ${args.withdrawalParams.assetId} is not supported by Omni Bridge`);
|
|
205
|
-
const omniChainKind =
|
|
177
|
+
const omniChainKind = require_src_bridges_omni_bridge_omni_withdraw_params.caip2ToChainKind(assetInfo.blockchain);
|
|
206
178
|
(0, _defuse_protocol_internal_utils.assert)(omniChainKind !== null, `Chain ${assetInfo.blockchain} is not supported by Omni Bridge`);
|
|
207
179
|
const fee = await (0, _defuse_protocol_internal_utils.withTimeout)(() => this.omniBridgeAPI.getFee((0, _omni_bridge_core.omniAddress)(_omni_bridge_core.ChainKind.Near, this.envConfig.contractID), (0, _omni_bridge_core.omniAddress)(omniChainKind, args.withdrawalParams.destinationAddress), (0, _omni_bridge_core.omniAddress)(_omni_bridge_core.ChainKind.Near, assetInfo.contractId), args.withdrawalParams.amount), {
|
|
208
180
|
timeout: typeof window !== "undefined" ? 1e4 : 3e3,
|
|
@@ -240,7 +212,7 @@ var OmniBridge = class {
|
|
|
240
212
|
});
|
|
241
213
|
amount += BigInt(quote.amount_in);
|
|
242
214
|
}
|
|
243
|
-
if (
|
|
215
|
+
if (require_src_bridges_omni_bridge_omni_withdraw_params.isUtxoChain(omniChainKind)) {
|
|
244
216
|
(0, _defuse_protocol_internal_utils.assert)(fee.gas_fee !== null && fee.gas_fee !== void 0 && fee.gas_fee > 0n, `Invalid Omni Bridge utxo gas fee: expected > 0, got ${fee.gas_fee}`);
|
|
245
217
|
(0, _defuse_protocol_internal_utils.assert)(fee.protocol_fee !== null && fee.protocol_fee !== void 0 && fee.protocol_fee > 0n, `Invalid Omni Bridge utxo protocol fee: expected > 0, got ${fee.protocol_fee}`);
|
|
246
218
|
amount += fee.gas_fee + fee.protocol_fee;
|
|
@@ -269,7 +241,7 @@ var OmniBridge = class {
|
|
|
269
241
|
const destinationChain = (0, _omni_bridge_core.getChain)(transfer.recipient);
|
|
270
242
|
let txHash = null;
|
|
271
243
|
if ((0, _omni_bridge_core.isEvmChain)(destinationChain) || destinationChain === _omni_bridge_core.ChainKind.Sol || destinationChain === _omni_bridge_core.ChainKind.Fogo || destinationChain === _omni_bridge_core.ChainKind.Strk || destinationChain === _omni_bridge_core.ChainKind.Aptos) txHash = transfer.finalised?.transaction_hash;
|
|
272
|
-
else if (
|
|
244
|
+
else if (require_src_bridges_omni_bridge_omni_withdraw_params.isUtxoChain(destinationChain)) txHash = typeof window !== "undefined" ? transfer.utxo_meta?.pending_sign_id : transfer.finalised?.transaction_hash;
|
|
273
245
|
else return {
|
|
274
246
|
status: "completed",
|
|
275
247
|
txHash: null
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { DestinationAddressMatchesTokenAddressError, InvalidDestinationAddressForWithdrawalError, MinWithdrawalAmountError, UnsupportedAssetIdError } from "../../classes/errors.js";
|
|
2
2
|
import { RouteEnum } from "../../constants/route-enum.js";
|
|
3
|
-
import { Chains } from "../../lib/caip2.js";
|
|
4
3
|
import { getFeeQuote, getUnderlyingFee } from "../../lib/estimate-fee.js";
|
|
5
4
|
import { parseDefuseAssetId } from "../../lib/parse-defuse-asset-id.js";
|
|
6
5
|
import { validateAddress } from "../../lib/validateAddress.js";
|
|
@@ -9,7 +8,8 @@ import { compareAddresses } from "../../lib/compareAddresses.js";
|
|
|
9
8
|
import { InsufficientUtxoForOmniBridgeWithdrawalError, IntentsNearOmniAvailableBalanceTooLowError, InvalidFeeValueError, OmniWithdrawalApiFeeRequestTimeoutError, TokenNotFoundInDestinationChainError } from "./error.js";
|
|
10
9
|
import { FEE_SUBSIDIZED_TOKENS, INTENTS_STORAGE_BALANCE_CACHE_KEY, MIN_AMOUNT_SOL_OMNI_WITHDRAWAL, MIN_STORAGE_BALANCE_FOR_INTENTS_NEAR, NEAR_NATIVE_ASSET_ID, OMNI_BRIDGE_CONTRACT, SOL_OMNI_CONTRACT_ID } from "./omni-bridge-constants.js";
|
|
11
10
|
import { POA_TOKENS_MIGRATED_TO_OMNI_BRIDGE } from "../../constants/poa-tokens-migrated-to-omni-bridge.js";
|
|
12
|
-
import {
|
|
11
|
+
import { chainKindToCaip2, createWithdrawIntentsPrimitive, getAccountOmniStorageBalance, getBridgedToken, getTokenDecimals, poaContractIdToChainKind, validateOmniToken } from "./omni-bridge-utils.js";
|
|
12
|
+
import { caip2ToChainKind, deriveOmniWithdrawIntentParams, isUtxoChain } from "./omni-withdraw-params.js";
|
|
13
13
|
import { assert, getNearNep141MinStorageBalance, getNearNep141StorageBalance, withTimeout } from "@defuse-protocol/internal-utils";
|
|
14
14
|
import { LRUCache } from "lru-cache";
|
|
15
15
|
import TTLCache from "@isaacs/ttlcache";
|
|
@@ -115,42 +115,14 @@ var OmniBridge = class {
|
|
|
115
115
|
},
|
|
116
116
|
referral: args.referral
|
|
117
117
|
});
|
|
118
|
-
|
|
119
|
-
assert(relayerFee >= 0n, `Invalid Omni bridge relayer fee: expected >= 0, got ${relayerFee}`);
|
|
120
|
-
let amount = args.withdrawalParams.amount;
|
|
121
|
-
let utxoMaxGasFee = null;
|
|
122
|
-
/**
|
|
123
|
-
* UTXO withdrawals add protocol + max gas fees to the intent amount since they're paid
|
|
124
|
-
* from the withdrawn asset, not wrap.near.
|
|
125
|
-
*
|
|
126
|
-
* Example with nep141:nbtc.bridge.near (made-up values):
|
|
127
|
-
* utxoFees = 50 + 50 = 100, relayerFee = 2 (excluded; paid in wrap.near)
|
|
128
|
-
*
|
|
129
|
-
* feeInclusive=false:
|
|
130
|
-
* - amount = 4000 → intent = 4000 + 100 = 4100 → user receives 4000
|
|
131
|
-
*
|
|
132
|
-
* feeInclusive=true:
|
|
133
|
-
* - amount = 3898 (4000 − 102) → intent = 3898 + 100 = 3998 → user receives 3898
|
|
134
|
-
**/
|
|
135
|
-
if (isUtxoChain(omniChainKind)) {
|
|
136
|
-
utxoMaxGasFee = getUnderlyingFee(args.feeEstimation, RouteEnum.OmniBridge, "utxoMaxGasFee");
|
|
137
|
-
const utxoProtocolFee = getUnderlyingFee(args.feeEstimation, RouteEnum.OmniBridge, "utxoProtocolFee");
|
|
138
|
-
assert(utxoMaxGasFee !== void 0 && utxoMaxGasFee > 0n, `Invalid Omni Bridge utxo max gas fee: expected > 0, got ${utxoMaxGasFee}`);
|
|
139
|
-
assert(utxoProtocolFee !== void 0 && utxoProtocolFee > 0n, `Invalid Omni Bridge utxo protocol fee: expected > 0, got ${utxoProtocolFee}`);
|
|
140
|
-
amount += utxoMaxGasFee + utxoProtocolFee;
|
|
141
|
-
}
|
|
142
|
-
let destinationAddress = args.withdrawalParams.destinationAddress;
|
|
143
|
-
if (assetInfo.blockchain === Chains.Bitcoin && /^bc1/i.test(destinationAddress)) destinationAddress = destinationAddress.toLowerCase();
|
|
144
|
-
intents.push(...createWithdrawIntentsPrimitive({
|
|
118
|
+
intents.push(...createWithdrawIntentsPrimitive(deriveOmniWithdrawIntentParams({
|
|
145
119
|
assetId: args.withdrawalParams.assetId,
|
|
146
|
-
destinationAddress,
|
|
147
|
-
amount,
|
|
120
|
+
destinationAddress: args.withdrawalParams.destinationAddress,
|
|
121
|
+
actualAmount: args.withdrawalParams.amount,
|
|
148
122
|
omniChainKind,
|
|
149
123
|
intentsContract: this.envConfig.contractID,
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
utxoMaxGasFee
|
|
153
|
-
}));
|
|
124
|
+
feeEstimation: args.feeEstimation
|
|
125
|
+
})));
|
|
154
126
|
return Promise.resolve(intents);
|
|
155
127
|
}
|
|
156
128
|
async validateWithdrawal(args) {
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const require_rolldown_runtime = require('../../../_virtual/rolldown_runtime.cjs');
|
|
2
|
+
const require_route_enum = require('../../constants/route-enum.cjs');
|
|
3
|
+
const require_estimate_fee = require('../../lib/estimate-fee.cjs');
|
|
4
|
+
const require_omni_bridge_utils = require('./omni-bridge-utils.cjs');
|
|
5
|
+
let _defuse_protocol_internal_utils = require("@defuse-protocol/internal-utils");
|
|
6
|
+
let _omni_bridge_core = require("@omni-bridge/core");
|
|
7
|
+
let _omni_bridge_near = require("@omni-bridge/near");
|
|
8
|
+
|
|
9
|
+
//#region src/bridges/omni-bridge/omni-withdraw-params.ts
|
|
10
|
+
/**
|
|
11
|
+
* Computes the values of an Omni withdrawal without building the intents. `OmniBridge` calls
|
|
12
|
+
* it too, so a caller that signs the payload elsewhere gets the same numbers.
|
|
13
|
+
* @param params.actualAmount Net of `feeEstimation.amount`. `IntentsSDK` subtracts it for a
|
|
14
|
+
* fee-inclusive withdrawal, so a caller that builds the payload itself has to do the same
|
|
15
|
+
* @param params.feeEstimation Read for every fee of the route, one of which raises the amount
|
|
16
|
+
* @param params.externalId Supply one to repeat an earlier call.
|
|
17
|
+
* `calculateStorageAccountId` throws above 64 UTF-8 bytes. Omit it and the function draws a
|
|
18
|
+
* random one
|
|
19
|
+
* @returns The values the `ft_withdraw` carries
|
|
20
|
+
*/
|
|
21
|
+
function deriveOmniWithdrawIntentParams(params) {
|
|
22
|
+
const { contractId: tokenAccountId, standard } = _defuse_protocol_internal_utils.utils.parseDefuseAssetId(params.assetId);
|
|
23
|
+
(0, _defuse_protocol_internal_utils.assert)(standard === "nep141", "Only NEP-141 is supported");
|
|
24
|
+
const nativeFee = require_estimate_fee.getUnderlyingFee(params.feeEstimation, require_route_enum.RouteEnum.OmniBridge, "relayerFee");
|
|
25
|
+
(0, _defuse_protocol_internal_utils.assert)(nativeFee >= 0n, `Invalid Omni bridge relayer fee: expected >= 0, got ${nativeFee}`);
|
|
26
|
+
const storageDepositAmount = require_estimate_fee.getUnderlyingFee(params.feeEstimation, require_route_enum.RouteEnum.OmniBridge, "storageDepositFee");
|
|
27
|
+
let amount = params.actualAmount;
|
|
28
|
+
let msg = "";
|
|
29
|
+
if (isUtxoChain(params.omniChainKind)) {
|
|
30
|
+
const utxoMaxGasFee = require_estimate_fee.getUnderlyingFee(params.feeEstimation, require_route_enum.RouteEnum.OmniBridge, "utxoMaxGasFee");
|
|
31
|
+
const utxoProtocolFee = require_estimate_fee.getUnderlyingFee(params.feeEstimation, require_route_enum.RouteEnum.OmniBridge, "utxoProtocolFee");
|
|
32
|
+
(0, _defuse_protocol_internal_utils.assert)(utxoMaxGasFee !== void 0 && utxoMaxGasFee > 0n, `Invalid Omni Bridge utxo max gas fee: expected > 0, got ${utxoMaxGasFee}`);
|
|
33
|
+
(0, _defuse_protocol_internal_utils.assert)(utxoProtocolFee !== void 0 && utxoProtocolFee > 0n, `Invalid Omni Bridge utxo protocol fee: expected > 0, got ${utxoProtocolFee}`);
|
|
34
|
+
amount += utxoMaxGasFee + utxoProtocolFee;
|
|
35
|
+
msg = JSON.stringify({ MaxGasFee: utxoMaxGasFee.toString() });
|
|
36
|
+
}
|
|
37
|
+
const destinationAddress = params.omniChainKind === _omni_bridge_core.ChainKind.Btc && /^bc1/i.test(params.destinationAddress) ? params.destinationAddress.toLowerCase() : params.destinationAddress;
|
|
38
|
+
const recipient = (0, _omni_bridge_core.omniAddress)(params.omniChainKind, destinationAddress);
|
|
39
|
+
const externalId = params.externalId ?? crypto.randomUUID();
|
|
40
|
+
return {
|
|
41
|
+
tokenAccountId,
|
|
42
|
+
recipient,
|
|
43
|
+
msg,
|
|
44
|
+
externalId,
|
|
45
|
+
amount,
|
|
46
|
+
nativeFee,
|
|
47
|
+
storageDepositAmount,
|
|
48
|
+
storageDepositAccountId: nativeFee > 0n ? (0, _omni_bridge_near.calculateStorageAccountId)({
|
|
49
|
+
token: `near:${tokenAccountId}`,
|
|
50
|
+
amount,
|
|
51
|
+
recipient,
|
|
52
|
+
fee: {
|
|
53
|
+
fee: 0n,
|
|
54
|
+
native_fee: nativeFee
|
|
55
|
+
},
|
|
56
|
+
sender: `near:${params.intentsContract}`,
|
|
57
|
+
msg
|
|
58
|
+
}, externalId) : null
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function caip2ToChainKind(network) {
|
|
62
|
+
return require_omni_bridge_utils.CHAIN_MAPPINGS.find(([chain]) => chain === network)?.[1] ?? null;
|
|
63
|
+
}
|
|
64
|
+
const UTXO_CHAINS = [_omni_bridge_core.ChainKind.Btc, _omni_bridge_core.ChainKind.Zcash];
|
|
65
|
+
function isUtxoChain(network) {
|
|
66
|
+
return UTXO_CHAINS.includes(network);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
//#endregion
|
|
70
|
+
exports.caip2ToChainKind = caip2ToChainKind;
|
|
71
|
+
exports.deriveOmniWithdrawIntentParams = deriveOmniWithdrawIntentParams;
|
|
72
|
+
exports.isUtxoChain = isUtxoChain;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Chain } from "../../lib/caip2.cjs";
|
|
2
|
+
import { FeeEstimation } from "../../shared-types.cjs";
|
|
3
|
+
import { ChainKind, OmniAddress } from "@omni-bridge/core";
|
|
4
|
+
|
|
5
|
+
//#region src/bridges/omni-bridge/omni-withdraw-params.d.ts
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The values an Omni withdrawal computes before it builds the intents.
|
|
9
|
+
*
|
|
10
|
+
* `storageDepositAccountId` is derived from `externalId`, and letter case takes part in that
|
|
11
|
+
* derivation, so recomputing any of these sends the native fee to a different account.
|
|
12
|
+
*/
|
|
13
|
+
type OmniWithdrawIntentParams = {
|
|
14
|
+
/** The NEP-141 account of the asset. */
|
|
15
|
+
tokenAccountId: string;
|
|
16
|
+
recipient: OmniAddress;
|
|
17
|
+
/** Empty, except for a UTXO chain. Then it contains `MaxGasFee`. */
|
|
18
|
+
msg: string;
|
|
19
|
+
/**
|
|
20
|
+
* This value makes the storage account unique. The hash covers the rest of the transfer,
|
|
21
|
+
* so two withdrawals that match on every field would otherwise land on one account.
|
|
22
|
+
* `calculateStorageAccountId` throws above 64 UTF-8 bytes.
|
|
23
|
+
*/
|
|
24
|
+
externalId: string;
|
|
25
|
+
/** The account that gets the native fee. Null if there is no native fee. */
|
|
26
|
+
storageDepositAccountId: string | null;
|
|
27
|
+
/**
|
|
28
|
+
* The amount the `ft_withdraw` carries. A UTXO chain adds its own fees to the amount the
|
|
29
|
+
* caller asked for, because they are paid from the asset and not from wrap.near.
|
|
30
|
+
*/
|
|
31
|
+
amount: bigint;
|
|
32
|
+
/** The relayer fee. It goes in `native_token_fee`, and the storage deposit transfers it. */
|
|
33
|
+
nativeFee: bigint;
|
|
34
|
+
/** The `storage_deposit` of the `ft_withdraw`. Zero leaves the field out. */
|
|
35
|
+
storageDepositAmount: bigint;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Computes the values of an Omni withdrawal without building the intents. `OmniBridge` calls
|
|
39
|
+
* it too, so a caller that signs the payload elsewhere gets the same numbers.
|
|
40
|
+
* @param params.actualAmount Net of `feeEstimation.amount`. `IntentsSDK` subtracts it for a
|
|
41
|
+
* fee-inclusive withdrawal, so a caller that builds the payload itself has to do the same
|
|
42
|
+
* @param params.feeEstimation Read for every fee of the route, one of which raises the amount
|
|
43
|
+
* @param params.externalId Supply one to repeat an earlier call.
|
|
44
|
+
* `calculateStorageAccountId` throws above 64 UTF-8 bytes. Omit it and the function draws a
|
|
45
|
+
* random one
|
|
46
|
+
* @returns The values the `ft_withdraw` carries
|
|
47
|
+
*/
|
|
48
|
+
declare function deriveOmniWithdrawIntentParams(params: {
|
|
49
|
+
assetId: string;
|
|
50
|
+
destinationAddress: string;
|
|
51
|
+
actualAmount: bigint;
|
|
52
|
+
omniChainKind: ChainKind;
|
|
53
|
+
intentsContract: string;
|
|
54
|
+
feeEstimation: FeeEstimation;
|
|
55
|
+
externalId?: string;
|
|
56
|
+
}): OmniWithdrawIntentParams;
|
|
57
|
+
declare function caip2ToChainKind(network: Chain): ChainKind | null;
|
|
58
|
+
declare function isUtxoChain(network: ChainKind): boolean;
|
|
59
|
+
//#endregion
|
|
60
|
+
export { OmniWithdrawIntentParams, caip2ToChainKind, deriveOmniWithdrawIntentParams, isUtxoChain };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Chain } from "../../lib/caip2.js";
|
|
2
|
+
import { FeeEstimation } from "../../shared-types.js";
|
|
3
|
+
import { ChainKind, OmniAddress } from "@omni-bridge/core";
|
|
4
|
+
|
|
5
|
+
//#region src/bridges/omni-bridge/omni-withdraw-params.d.ts
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The values an Omni withdrawal computes before it builds the intents.
|
|
9
|
+
*
|
|
10
|
+
* `storageDepositAccountId` is derived from `externalId`, and letter case takes part in that
|
|
11
|
+
* derivation, so recomputing any of these sends the native fee to a different account.
|
|
12
|
+
*/
|
|
13
|
+
type OmniWithdrawIntentParams = {
|
|
14
|
+
/** The NEP-141 account of the asset. */
|
|
15
|
+
tokenAccountId: string;
|
|
16
|
+
recipient: OmniAddress;
|
|
17
|
+
/** Empty, except for a UTXO chain. Then it contains `MaxGasFee`. */
|
|
18
|
+
msg: string;
|
|
19
|
+
/**
|
|
20
|
+
* This value makes the storage account unique. The hash covers the rest of the transfer,
|
|
21
|
+
* so two withdrawals that match on every field would otherwise land on one account.
|
|
22
|
+
* `calculateStorageAccountId` throws above 64 UTF-8 bytes.
|
|
23
|
+
*/
|
|
24
|
+
externalId: string;
|
|
25
|
+
/** The account that gets the native fee. Null if there is no native fee. */
|
|
26
|
+
storageDepositAccountId: string | null;
|
|
27
|
+
/**
|
|
28
|
+
* The amount the `ft_withdraw` carries. A UTXO chain adds its own fees to the amount the
|
|
29
|
+
* caller asked for, because they are paid from the asset and not from wrap.near.
|
|
30
|
+
*/
|
|
31
|
+
amount: bigint;
|
|
32
|
+
/** The relayer fee. It goes in `native_token_fee`, and the storage deposit transfers it. */
|
|
33
|
+
nativeFee: bigint;
|
|
34
|
+
/** The `storage_deposit` of the `ft_withdraw`. Zero leaves the field out. */
|
|
35
|
+
storageDepositAmount: bigint;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Computes the values of an Omni withdrawal without building the intents. `OmniBridge` calls
|
|
39
|
+
* it too, so a caller that signs the payload elsewhere gets the same numbers.
|
|
40
|
+
* @param params.actualAmount Net of `feeEstimation.amount`. `IntentsSDK` subtracts it for a
|
|
41
|
+
* fee-inclusive withdrawal, so a caller that builds the payload itself has to do the same
|
|
42
|
+
* @param params.feeEstimation Read for every fee of the route, one of which raises the amount
|
|
43
|
+
* @param params.externalId Supply one to repeat an earlier call.
|
|
44
|
+
* `calculateStorageAccountId` throws above 64 UTF-8 bytes. Omit it and the function draws a
|
|
45
|
+
* random one
|
|
46
|
+
* @returns The values the `ft_withdraw` carries
|
|
47
|
+
*/
|
|
48
|
+
declare function deriveOmniWithdrawIntentParams(params: {
|
|
49
|
+
assetId: string;
|
|
50
|
+
destinationAddress: string;
|
|
51
|
+
actualAmount: bigint;
|
|
52
|
+
omniChainKind: ChainKind;
|
|
53
|
+
intentsContract: string;
|
|
54
|
+
feeEstimation: FeeEstimation;
|
|
55
|
+
externalId?: string;
|
|
56
|
+
}): OmniWithdrawIntentParams;
|
|
57
|
+
declare function caip2ToChainKind(network: Chain): ChainKind | null;
|
|
58
|
+
declare function isUtxoChain(network: ChainKind): boolean;
|
|
59
|
+
//#endregion
|
|
60
|
+
export { OmniWithdrawIntentParams, caip2ToChainKind, deriveOmniWithdrawIntentParams, isUtxoChain };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { RouteEnum } from "../../constants/route-enum.js";
|
|
2
|
+
import { getUnderlyingFee } from "../../lib/estimate-fee.js";
|
|
3
|
+
import { CHAIN_MAPPINGS } from "./omni-bridge-utils.js";
|
|
4
|
+
import { assert, utils } from "@defuse-protocol/internal-utils";
|
|
5
|
+
import { ChainKind, omniAddress } from "@omni-bridge/core";
|
|
6
|
+
import { calculateStorageAccountId } from "@omni-bridge/near";
|
|
7
|
+
|
|
8
|
+
//#region src/bridges/omni-bridge/omni-withdraw-params.ts
|
|
9
|
+
/**
|
|
10
|
+
* Computes the values of an Omni withdrawal without building the intents. `OmniBridge` calls
|
|
11
|
+
* it too, so a caller that signs the payload elsewhere gets the same numbers.
|
|
12
|
+
* @param params.actualAmount Net of `feeEstimation.amount`. `IntentsSDK` subtracts it for a
|
|
13
|
+
* fee-inclusive withdrawal, so a caller that builds the payload itself has to do the same
|
|
14
|
+
* @param params.feeEstimation Read for every fee of the route, one of which raises the amount
|
|
15
|
+
* @param params.externalId Supply one to repeat an earlier call.
|
|
16
|
+
* `calculateStorageAccountId` throws above 64 UTF-8 bytes. Omit it and the function draws a
|
|
17
|
+
* random one
|
|
18
|
+
* @returns The values the `ft_withdraw` carries
|
|
19
|
+
*/
|
|
20
|
+
function deriveOmniWithdrawIntentParams(params) {
|
|
21
|
+
const { contractId: tokenAccountId, standard } = utils.parseDefuseAssetId(params.assetId);
|
|
22
|
+
assert(standard === "nep141", "Only NEP-141 is supported");
|
|
23
|
+
const nativeFee = getUnderlyingFee(params.feeEstimation, RouteEnum.OmniBridge, "relayerFee");
|
|
24
|
+
assert(nativeFee >= 0n, `Invalid Omni bridge relayer fee: expected >= 0, got ${nativeFee}`);
|
|
25
|
+
const storageDepositAmount = getUnderlyingFee(params.feeEstimation, RouteEnum.OmniBridge, "storageDepositFee");
|
|
26
|
+
let amount = params.actualAmount;
|
|
27
|
+
let msg = "";
|
|
28
|
+
if (isUtxoChain(params.omniChainKind)) {
|
|
29
|
+
const utxoMaxGasFee = getUnderlyingFee(params.feeEstimation, RouteEnum.OmniBridge, "utxoMaxGasFee");
|
|
30
|
+
const utxoProtocolFee = getUnderlyingFee(params.feeEstimation, RouteEnum.OmniBridge, "utxoProtocolFee");
|
|
31
|
+
assert(utxoMaxGasFee !== void 0 && utxoMaxGasFee > 0n, `Invalid Omni Bridge utxo max gas fee: expected > 0, got ${utxoMaxGasFee}`);
|
|
32
|
+
assert(utxoProtocolFee !== void 0 && utxoProtocolFee > 0n, `Invalid Omni Bridge utxo protocol fee: expected > 0, got ${utxoProtocolFee}`);
|
|
33
|
+
amount += utxoMaxGasFee + utxoProtocolFee;
|
|
34
|
+
msg = JSON.stringify({ MaxGasFee: utxoMaxGasFee.toString() });
|
|
35
|
+
}
|
|
36
|
+
const destinationAddress = params.omniChainKind === ChainKind.Btc && /^bc1/i.test(params.destinationAddress) ? params.destinationAddress.toLowerCase() : params.destinationAddress;
|
|
37
|
+
const recipient = omniAddress(params.omniChainKind, destinationAddress);
|
|
38
|
+
const externalId = params.externalId ?? crypto.randomUUID();
|
|
39
|
+
return {
|
|
40
|
+
tokenAccountId,
|
|
41
|
+
recipient,
|
|
42
|
+
msg,
|
|
43
|
+
externalId,
|
|
44
|
+
amount,
|
|
45
|
+
nativeFee,
|
|
46
|
+
storageDepositAmount,
|
|
47
|
+
storageDepositAccountId: nativeFee > 0n ? calculateStorageAccountId({
|
|
48
|
+
token: `near:${tokenAccountId}`,
|
|
49
|
+
amount,
|
|
50
|
+
recipient,
|
|
51
|
+
fee: {
|
|
52
|
+
fee: 0n,
|
|
53
|
+
native_fee: nativeFee
|
|
54
|
+
},
|
|
55
|
+
sender: `near:${params.intentsContract}`,
|
|
56
|
+
msg
|
|
57
|
+
}, externalId) : null
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function caip2ToChainKind(network) {
|
|
61
|
+
return CHAIN_MAPPINGS.find(([chain]) => chain === network)?.[1] ?? null;
|
|
62
|
+
}
|
|
63
|
+
const UTXO_CHAINS = [ChainKind.Btc, ChainKind.Zcash];
|
|
64
|
+
function isUtxoChain(network) {
|
|
65
|
+
return UTXO_CHAINS.includes(network);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
//#endregion
|
|
69
|
+
export { caip2ToChainKind, deriveOmniWithdrawIntentParams, isUtxoChain };
|
|
@@ -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": {
|
|
@@ -26,6 +26,16 @@
|
|
|
26
26
|
"types": "./dist/index.d.cts",
|
|
27
27
|
"default": "./dist/index.cjs"
|
|
28
28
|
}
|
|
29
|
+
},
|
|
30
|
+
"./omni-bridge": {
|
|
31
|
+
"import": {
|
|
32
|
+
"types": "./dist/src/bridges/omni-bridge/omni-withdraw-params.d.ts",
|
|
33
|
+
"default": "./dist/src/bridges/omni-bridge/omni-withdraw-params.js"
|
|
34
|
+
},
|
|
35
|
+
"require": {
|
|
36
|
+
"types": "./dist/src/bridges/omni-bridge/omni-withdraw-params.d.cts",
|
|
37
|
+
"default": "./dist/src/bridges/omni-bridge/omni-withdraw-params.cjs"
|
|
38
|
+
}
|
|
29
39
|
}
|
|
30
40
|
},
|
|
31
41
|
"files": [
|
|
@@ -38,8 +48,8 @@
|
|
|
38
48
|
"@hot-labs/omni-sdk": "2.25.5",
|
|
39
49
|
"@isaacs/ttlcache": "^1.0.0",
|
|
40
50
|
"@lifeomic/attempt": "^3.0.0",
|
|
41
|
-
"@omni-bridge/core": "0.
|
|
42
|
-
"@omni-bridge/near": "0.
|
|
51
|
+
"@omni-bridge/core": "0.19.0",
|
|
52
|
+
"@omni-bridge/near": "0.19.0",
|
|
43
53
|
"@noble/hashes": "^1.7.1",
|
|
44
54
|
"@scure/base": "^1.0.0",
|
|
45
55
|
"borsher": "^4.0.0",
|