@sodax/sdk 2.0.0-rc.11 → 2.0.0-rc.13
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/index.cjs +1482 -282
- package/dist/index.d.cts +692 -95
- package/dist/index.d.ts +692 -95
- package/dist/index.mjs +1464 -284
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -197,7 +197,8 @@ var SODAX_FEATURES = [
|
|
|
197
197
|
"migration",
|
|
198
198
|
"dex",
|
|
199
199
|
"partner",
|
|
200
|
-
"recovery"
|
|
200
|
+
"recovery",
|
|
201
|
+
"leverageYield"
|
|
201
202
|
];
|
|
202
203
|
|
|
203
204
|
// src/shared/utils/tiny-invariant.ts
|
|
@@ -559,6 +560,27 @@ var SodaTokens = {
|
|
|
559
560
|
vault: "0x243b0c26c8b38793908d7C64e8510f21B19B4613"
|
|
560
561
|
}
|
|
561
562
|
};
|
|
563
|
+
var LsodaSymbols = ["lsodaWEETH", "lsodaWSTETH"];
|
|
564
|
+
var LsodaTokens = {
|
|
565
|
+
lsodaWEETH: {
|
|
566
|
+
symbol: "lsodaWEETH",
|
|
567
|
+
name: "Leveraged Soda weETH",
|
|
568
|
+
decimals: 18,
|
|
569
|
+
address: "0xD09de2f5070699A909c0FD32fb5A909d3886701D",
|
|
570
|
+
chainKey: ChainKeys.SONIC_MAINNET,
|
|
571
|
+
hubAsset: "0xD09de2f5070699A909c0FD32fb5A909d3886701D",
|
|
572
|
+
vault: "0xD09de2f5070699A909c0FD32fb5A909d3886701D"
|
|
573
|
+
},
|
|
574
|
+
lsodaWSTETH: {
|
|
575
|
+
symbol: "lsodaWSTETH",
|
|
576
|
+
name: "Leveraged Soda wstETH",
|
|
577
|
+
decimals: 18,
|
|
578
|
+
address: "0x136E5D1CEC5db1829E24941Eddd9C8640E02Ce7a",
|
|
579
|
+
chainKey: ChainKeys.SONIC_MAINNET,
|
|
580
|
+
hubAsset: "0x136E5D1CEC5db1829E24941Eddd9C8640E02Ce7a",
|
|
581
|
+
vault: "0x136E5D1CEC5db1829E24941Eddd9C8640E02Ce7a"
|
|
582
|
+
}
|
|
583
|
+
};
|
|
562
584
|
var sonicSupportedTokens = {
|
|
563
585
|
S: {
|
|
564
586
|
symbol: "S",
|
|
@@ -623,7 +645,8 @@ var sonicSupportedTokens = {
|
|
|
623
645
|
hubAsset: "0x7c7d53EEcda37a87ce0D5bf8E0b24512A48dC963",
|
|
624
646
|
vault: SodaTokens.sodaSODA.address
|
|
625
647
|
},
|
|
626
|
-
...SodaTokens
|
|
648
|
+
...SodaTokens,
|
|
649
|
+
...LsodaTokens
|
|
627
650
|
};
|
|
628
651
|
var redbellySupportedTokens = {
|
|
629
652
|
RBNT: {
|
|
@@ -3052,9 +3075,9 @@ var spokeChainConfig = {
|
|
|
3052
3075
|
supportedTokens: bitcoinSupportedTokens,
|
|
3053
3076
|
radfi: {
|
|
3054
3077
|
walletMode: "TRADING",
|
|
3055
|
-
apiUrl: "https://api.
|
|
3078
|
+
apiUrl: "https://api.bound.exchange/api",
|
|
3056
3079
|
apiKey: "",
|
|
3057
|
-
umsUrl: "https://ums.
|
|
3080
|
+
umsUrl: "https://api.ums.bound.exchange/api",
|
|
3058
3081
|
accessToken: "",
|
|
3059
3082
|
refreshToken: ""
|
|
3060
3083
|
},
|
|
@@ -3624,7 +3647,8 @@ var swapSupportedTokens = {
|
|
|
3624
3647
|
spokeChainConfig[ChainKeys.SONIC_MAINNET].supportedTokens.USDT,
|
|
3625
3648
|
spokeChainConfig[ChainKeys.SONIC_MAINNET].supportedTokens.wS,
|
|
3626
3649
|
spokeChainConfig[ChainKeys.SONIC_MAINNET].supportedTokens.SODA,
|
|
3627
|
-
...Object.values(SodaTokens)
|
|
3650
|
+
...Object.values(SodaTokens),
|
|
3651
|
+
...Object.values(LsodaTokens)
|
|
3628
3652
|
],
|
|
3629
3653
|
[ChainKeys.AVALANCHE_MAINNET]: [
|
|
3630
3654
|
spokeChainConfig[ChainKeys.AVALANCHE_MAINNET].supportedTokens.AVAX,
|
|
@@ -3864,6 +3888,41 @@ var swapsConfig = {
|
|
|
3864
3888
|
var getSupportedSolverTokens = (chainId) => swapSupportedTokens[chainId];
|
|
3865
3889
|
var isSwapSupportedToken = (chainId, token) => swapSupportedTokens[chainId].some((t) => t.address.toLowerCase() === token.toLowerCase());
|
|
3866
3890
|
|
|
3891
|
+
// ../types/dist/leverageYield/leverageYield.js
|
|
3892
|
+
var leverageYieldVaults = [
|
|
3893
|
+
{
|
|
3894
|
+
name: LsodaTokens.lsodaWEETH.symbol,
|
|
3895
|
+
vault: LsodaTokens.lsodaWEETH.vault,
|
|
3896
|
+
asset: SodaTokens.sodaWEETH.address,
|
|
3897
|
+
// sodaWEETH on Sonic
|
|
3898
|
+
borrowToken: SodaTokens.sodaETH.address,
|
|
3899
|
+
// sodaETH on Sonic
|
|
3900
|
+
lsdSource: {
|
|
3901
|
+
// DefiLlama pool for EtherFi's weETH on Ethereum (project: 'ether.fi-stake').
|
|
3902
|
+
poolId: "46bd2bdf-6d92-4066-b482-e885ee172264",
|
|
3903
|
+
fallbackAprPct: 3,
|
|
3904
|
+
label: "EtherFi (weETH)"
|
|
3905
|
+
}
|
|
3906
|
+
},
|
|
3907
|
+
{
|
|
3908
|
+
name: LsodaTokens.lsodaWSTETH.symbol,
|
|
3909
|
+
vault: LsodaTokens.lsodaWSTETH.vault,
|
|
3910
|
+
asset: SodaTokens.sodaWSTETH.address,
|
|
3911
|
+
// sodaWSTETH on Sonic
|
|
3912
|
+
borrowToken: SodaTokens.sodaETH.address,
|
|
3913
|
+
// sodaETH on Sonic
|
|
3914
|
+
lsdSource: {
|
|
3915
|
+
// DefiLlama pool for Lido's stETH on Ethereum (wstETH inherits this rate via redeem).
|
|
3916
|
+
poolId: "747c1d2a-c668-4682-b9f9-296708a3dd90",
|
|
3917
|
+
fallbackAprPct: 2.4,
|
|
3918
|
+
label: "Lido (stETH)"
|
|
3919
|
+
}
|
|
3920
|
+
}
|
|
3921
|
+
];
|
|
3922
|
+
var leverageYieldConfig = {
|
|
3923
|
+
vaults: leverageYieldVaults
|
|
3924
|
+
};
|
|
3925
|
+
|
|
3867
3926
|
// ../types/dist/sodax-config/sodax-config.js
|
|
3868
3927
|
var bridgeConfig = {
|
|
3869
3928
|
partnerFee: void 0
|
|
@@ -3875,6 +3934,7 @@ var sodaxConfig = {
|
|
|
3875
3934
|
moneyMarket: moneyMarketConfig,
|
|
3876
3935
|
bridge: bridgeConfig,
|
|
3877
3936
|
dex: dexConfig,
|
|
3937
|
+
leverageYield: leverageYieldConfig,
|
|
3878
3938
|
hub: hubConfig,
|
|
3879
3939
|
api: apiConfig,
|
|
3880
3940
|
solver: solverConfig,
|
|
@@ -3896,6 +3956,9 @@ function detectBitcoinAddressType(address) {
|
|
|
3896
3956
|
return "P2PKH";
|
|
3897
3957
|
throw new Error(`Unknown Bitcoin address type: ${address}`);
|
|
3898
3958
|
}
|
|
3959
|
+
function usesBip322MessageSigning(addressType) {
|
|
3960
|
+
return addressType === "P2WPKH" || addressType === "P2TR";
|
|
3961
|
+
}
|
|
3899
3962
|
var BTC_WALLET_ADDRESS_TYPES = ["taproot", "segwit"];
|
|
3900
3963
|
var BTC_ADDRESS_TYPES = ["P2PKH", "P2SH", "P2WPKH", "P2TR"];
|
|
3901
3964
|
|
|
@@ -4013,7 +4076,7 @@ function isValidWalletProviderForChainKey(chainKey, walletProvider) {
|
|
|
4013
4076
|
}
|
|
4014
4077
|
|
|
4015
4078
|
// ../types/dist/index.js
|
|
4016
|
-
var CONFIG_VERSION =
|
|
4079
|
+
var CONFIG_VERSION = 211;
|
|
4017
4080
|
function isEvmSpokeChainConfig(value) {
|
|
4018
4081
|
return typeof value === "object" && value !== null && value.chain.type === "EVM" && value.chain.key !== HUB_CHAIN_KEY;
|
|
4019
4082
|
}
|
|
@@ -4440,7 +4503,7 @@ async function waitUntilIntentExecuted(payload) {
|
|
|
4440
4503
|
}
|
|
4441
4504
|
async function relayTxAndWaitPacket(params) {
|
|
4442
4505
|
try {
|
|
4443
|
-
const { srcTxHash, data, chainKey, relayerApiEndpoint, timeout = DEFAULT_RELAY_TX_TIMEOUT } = params;
|
|
4506
|
+
const { srcTxHash, data, chainKey, relayerApiEndpoint, timeout = DEFAULT_RELAY_TX_TIMEOUT, pollTxHash } = params;
|
|
4444
4507
|
const intentRelayChainId = getIntentRelayChainId(chainKey).toString();
|
|
4445
4508
|
const isSplitTxChain = isSolanaChainKeyType(chainKey) || isBitcoinChainKeyType(chainKey);
|
|
4446
4509
|
invariant(!isSplitTxChain || data !== void 0, "Data is required for Solana and Bitcoin chain keys");
|
|
@@ -4459,7 +4522,9 @@ async function relayTxAndWaitPacket(params) {
|
|
|
4459
4522
|
if (!submitResult.ok) return submitResult;
|
|
4460
4523
|
return await waitUntilIntentExecuted({
|
|
4461
4524
|
intentRelayChainId,
|
|
4462
|
-
|
|
4525
|
+
// The relay may track the packet under a different id than the submit tx_hash (Bitcoin
|
|
4526
|
+
// on-demand: submit "withdraw", poll the derived `od:<hash>`). Defaults to the submit id.
|
|
4527
|
+
srcTxHash: pollTxHash ?? srcTxHash,
|
|
4463
4528
|
timeout,
|
|
4464
4529
|
apiUrl: relayerApiEndpoint
|
|
4465
4530
|
});
|
|
@@ -13298,11 +13363,43 @@ function mergeSodaxConfig(base2, override) {
|
|
|
13298
13363
|
return merged;
|
|
13299
13364
|
}
|
|
13300
13365
|
|
|
13366
|
+
// src/shared/logger.ts
|
|
13367
|
+
var consoleLogger = {
|
|
13368
|
+
debug: (message, data) => data ? console.debug(message, data) : console.debug(message),
|
|
13369
|
+
info: (message, data) => data ? console.info(message, data) : console.info(message),
|
|
13370
|
+
warn: (message, data) => data ? console.warn(message, data) : console.warn(message),
|
|
13371
|
+
error: (message, error, data) => {
|
|
13372
|
+
if (error !== void 0 && data !== void 0) console.error(message, error, data);
|
|
13373
|
+
else if (error !== void 0) console.error(message, error);
|
|
13374
|
+
else console.error(message);
|
|
13375
|
+
}
|
|
13376
|
+
};
|
|
13377
|
+
var silentLogger = {
|
|
13378
|
+
debug: () => {
|
|
13379
|
+
},
|
|
13380
|
+
info: () => {
|
|
13381
|
+
},
|
|
13382
|
+
warn: () => {
|
|
13383
|
+
},
|
|
13384
|
+
error: () => {
|
|
13385
|
+
}
|
|
13386
|
+
};
|
|
13387
|
+
function resolveLogger(option) {
|
|
13388
|
+
if (option === void 0 || option === "console") return consoleLogger;
|
|
13389
|
+
if (option === "silent") return silentLogger;
|
|
13390
|
+
return option;
|
|
13391
|
+
}
|
|
13392
|
+
|
|
13301
13393
|
// src/shared/config/ConfigService.ts
|
|
13302
13394
|
var ConfigService = class {
|
|
13303
13395
|
sodax;
|
|
13304
13396
|
api;
|
|
13305
13397
|
userConfig;
|
|
13398
|
+
/**
|
|
13399
|
+
* SDK log sink. Resolved once at construction and kept independent of {@link sodax} so that
|
|
13400
|
+
* {@link initialize}'s dynamic-config swap never clobbers it. Read by services via `config.logger`.
|
|
13401
|
+
*/
|
|
13402
|
+
logger;
|
|
13306
13403
|
initialized = false;
|
|
13307
13404
|
// data structures for quick lookup
|
|
13308
13405
|
supportedHubAssetsSet;
|
|
@@ -13314,10 +13411,11 @@ var ConfigService = class {
|
|
|
13314
13411
|
stakedATokenAddressesSet;
|
|
13315
13412
|
chainToSupportedTokenAddressMap;
|
|
13316
13413
|
hubAssetToXTokenMap;
|
|
13317
|
-
constructor({ api, config, userConfig }) {
|
|
13414
|
+
constructor({ api, config, userConfig, logger }) {
|
|
13318
13415
|
this.api = api;
|
|
13319
13416
|
this.sodax = config;
|
|
13320
13417
|
this.userConfig = userConfig;
|
|
13418
|
+
this.logger = logger ?? resolveLogger(void 0);
|
|
13321
13419
|
this.loadSodaxConfigDataStructures(config);
|
|
13322
13420
|
}
|
|
13323
13421
|
async initialize() {
|
|
@@ -13326,7 +13424,7 @@ var ConfigService = class {
|
|
|
13326
13424
|
if (!result.ok) return result;
|
|
13327
13425
|
const response = result.value;
|
|
13328
13426
|
if (!response.version || response.version < CONFIG_VERSION) {
|
|
13329
|
-
|
|
13427
|
+
this.logger.warn(
|
|
13330
13428
|
`Dynamic config version is less than the current version, resorting to the default one. Current version: ${CONFIG_VERSION}, response version: ${response.version}`
|
|
13331
13429
|
);
|
|
13332
13430
|
} else {
|
|
@@ -14484,7 +14582,7 @@ var RadfiProvider = class {
|
|
|
14484
14582
|
}
|
|
14485
14583
|
}
|
|
14486
14584
|
/**
|
|
14487
|
-
* Authenticate with
|
|
14585
|
+
* Authenticate with Bound Exchange: BIP322-sign a login message, then call the Bound Exchange API.
|
|
14488
14586
|
* Returns accessToken, refreshToken, and tradingAddress.
|
|
14489
14587
|
*/
|
|
14490
14588
|
async authenticateWithWallet(walletProvider, cachedPublicKey) {
|
|
@@ -14500,15 +14598,14 @@ var RadfiProvider = class {
|
|
|
14500
14598
|
throw new Error("Failed to retrieve public key from wallet. Please unlock your wallet and try again.");
|
|
14501
14599
|
}
|
|
14502
14600
|
const message = `${Date.now()}`;
|
|
14503
|
-
const
|
|
14504
|
-
const signature = addressType === "P2WPKH" || addressType === "P2TR" ? await walletProvider.signBip322Message(message) : await walletProvider.signEcdsaMessage(message);
|
|
14601
|
+
const signature = usesBip322MessageSigning(detectBitcoinAddressType(address)) ? await walletProvider.signBip322Message(message) : await walletProvider.signEcdsaMessage(message);
|
|
14505
14602
|
const result = await this.authenticate({ message, signature, address, publicKey });
|
|
14506
14603
|
this.setRadfiAccessToken(result.accessToken, result.refreshToken);
|
|
14507
14604
|
return { ...result, publicKey };
|
|
14508
14605
|
}
|
|
14509
14606
|
/**
|
|
14510
|
-
* Ensure a valid
|
|
14511
|
-
* If a token exists, validates it via the
|
|
14607
|
+
* Ensure a valid Bound Exchange access token is set on this provider.
|
|
14608
|
+
* If a token exists, validates it via the Bound Exchange API.
|
|
14512
14609
|
* If invalid, tries refreshing with the refresh token first.
|
|
14513
14610
|
* If refresh also fails, falls back to full re-authentication (BIP322 sign).
|
|
14514
14611
|
*/
|
|
@@ -14541,7 +14638,7 @@ var RadfiProvider = class {
|
|
|
14541
14638
|
});
|
|
14542
14639
|
if (!res.ok) {
|
|
14543
14640
|
const err = await res.json();
|
|
14544
|
-
throw new RadfiApiError(res.status, err, "
|
|
14641
|
+
throw new RadfiApiError(res.status, err, "Bound Exchange authentication failed");
|
|
14545
14642
|
}
|
|
14546
14643
|
return res.json().then((r) => ({
|
|
14547
14644
|
accessToken: r.data?.accessToken ?? "",
|
|
@@ -14633,12 +14730,20 @@ var RadfiProvider = class {
|
|
|
14633
14730
|
}
|
|
14634
14731
|
})
|
|
14635
14732
|
});
|
|
14636
|
-
|
|
14637
|
-
|
|
14638
|
-
throw new RadfiApiError(res.status,
|
|
14733
|
+
const body = await res.json();
|
|
14734
|
+
if (!res.ok || !body?.data) {
|
|
14735
|
+
throw new RadfiApiError(res.status, body, "Bound Exchange transaction request failed");
|
|
14639
14736
|
}
|
|
14640
|
-
return
|
|
14737
|
+
return body.data;
|
|
14641
14738
|
}
|
|
14739
|
+
/**
|
|
14740
|
+
* Co-sign and broadcast a `sodax-withdraw` deposit via the Bound Exchange API.
|
|
14741
|
+
*
|
|
14742
|
+
* `relayData` ({ address, payload }) is the same `RelayExtraData` the SDK returns from
|
|
14743
|
+
* `createIntent()` / money-market supply etc. It is optional and non-breaking: when supplied,
|
|
14744
|
+
* the Bound Exchange backend persists it so it can auto-resubmit the intent relay if the relay
|
|
14745
|
+
* gets stuck (otherwise a stuck relay eventually refunds instead of completing the swap).
|
|
14746
|
+
*/
|
|
14642
14747
|
async requestRadfiSignature(params, accessToken) {
|
|
14643
14748
|
const res = await this.request("/sodax/transaction/sign", {
|
|
14644
14749
|
method: "POST",
|
|
@@ -14652,7 +14757,7 @@ var RadfiProvider = class {
|
|
|
14652
14757
|
});
|
|
14653
14758
|
if (!res.ok) {
|
|
14654
14759
|
const err = await res.json();
|
|
14655
|
-
throw new RadfiApiError(res.status, err, "
|
|
14760
|
+
throw new RadfiApiError(res.status, err, "Bound Exchange signature request failed");
|
|
14656
14761
|
}
|
|
14657
14762
|
return res.json().then((r) => r.data.txId);
|
|
14658
14763
|
}
|
|
@@ -14676,7 +14781,7 @@ var RadfiProvider = class {
|
|
|
14676
14781
|
return res.json();
|
|
14677
14782
|
}
|
|
14678
14783
|
/**
|
|
14679
|
-
* Build a renew-utxo transaction via the
|
|
14784
|
+
* Build a renew-utxo transaction via the Bound Exchange API.
|
|
14680
14785
|
* Returns a PSBT that needs to be signed by the user.
|
|
14681
14786
|
*/
|
|
14682
14787
|
async buildRenewUtxoTransaction(params, accessToken) {
|
|
@@ -14700,8 +14805,8 @@ var RadfiProvider = class {
|
|
|
14700
14805
|
return res.json().then((r) => r.data);
|
|
14701
14806
|
}
|
|
14702
14807
|
/**
|
|
14703
|
-
* Sign and broadcast a renew-utxo transaction via the
|
|
14704
|
-
* The user signs the PSBT first, then
|
|
14808
|
+
* Sign and broadcast a renew-utxo transaction via the Bound Exchange API.
|
|
14809
|
+
* The user signs the PSBT first, then Bound Exchange co-signs and broadcasts.
|
|
14705
14810
|
*/
|
|
14706
14811
|
async signAndBroadcastRenewUtxo(params, accessToken) {
|
|
14707
14812
|
const res = await this.request("/transactions/sign", {
|
|
@@ -14742,7 +14847,7 @@ var RadfiProvider = class {
|
|
|
14742
14847
|
return res.json().then((r) => r.data);
|
|
14743
14848
|
}
|
|
14744
14849
|
/**
|
|
14745
|
-
* Sign and broadcast a withdraw transaction via
|
|
14850
|
+
* Sign and broadcast a withdraw transaction via Bound Exchange.
|
|
14746
14851
|
*/
|
|
14747
14852
|
async signAndBroadcastWithdraw(params, accessToken) {
|
|
14748
14853
|
const res = await this.request("/transactions/sign", {
|
|
@@ -14796,7 +14901,20 @@ var RadfiProvider = class {
|
|
|
14796
14901
|
};
|
|
14797
14902
|
|
|
14798
14903
|
// src/shared/entities/btc/btc-utils.ts
|
|
14799
|
-
|
|
14904
|
+
var BITCOIN_FEE_SAFETY_VBYTES = 20;
|
|
14905
|
+
function calcOpReturnOutputVbytes(payloadByteLength) {
|
|
14906
|
+
let scriptSize;
|
|
14907
|
+
if (payloadByteLength <= 75) {
|
|
14908
|
+
scriptSize = 3 + payloadByteLength;
|
|
14909
|
+
} else if (payloadByteLength <= 255) {
|
|
14910
|
+
scriptSize = 4 + payloadByteLength;
|
|
14911
|
+
} else {
|
|
14912
|
+
scriptSize = 5 + payloadByteLength;
|
|
14913
|
+
}
|
|
14914
|
+
const scriptLenVarint = scriptSize <= 252 ? 1 : 3;
|
|
14915
|
+
return 8 + scriptLenVarint + scriptSize;
|
|
14916
|
+
}
|
|
14917
|
+
function estimateBitcoinTxSize(inputCount, outputCount, addressType, opReturnOutputVbytes = 44) {
|
|
14800
14918
|
let inputWeight;
|
|
14801
14919
|
switch (addressType) {
|
|
14802
14920
|
case "P2PKH":
|
|
@@ -14812,7 +14930,9 @@ function estimateBitcoinTxSize(inputCount, outputCount, addressType) {
|
|
|
14812
14930
|
inputWeight = 68;
|
|
14813
14931
|
break;
|
|
14814
14932
|
}
|
|
14815
|
-
return Math.ceil(
|
|
14933
|
+
return Math.ceil(
|
|
14934
|
+
10.5 + opReturnOutputVbytes + BITCOIN_FEE_SAFETY_VBYTES + inputCount * inputWeight + outputCount * 31
|
|
14935
|
+
);
|
|
14816
14936
|
}
|
|
14817
14937
|
function encodeBtcPayloadToBytes(payload) {
|
|
14818
14938
|
return JSON.stringify({
|
|
@@ -14829,6 +14949,10 @@ function normalizePsbtToBase64(signedPsbt) {
|
|
|
14829
14949
|
const isHex = /^[0-9a-fA-F]+$/.test(signedPsbt);
|
|
14830
14950
|
return isHex ? Buffer.from(signedPsbt, "hex").toString("base64") : signedPsbt;
|
|
14831
14951
|
}
|
|
14952
|
+
function normalizeSignatureToBase64(signature) {
|
|
14953
|
+
const isHex = /^[0-9a-fA-F]+$/.test(signature);
|
|
14954
|
+
return isHex ? Buffer.from(signature, "hex").toString("base64") : signature;
|
|
14955
|
+
}
|
|
14832
14956
|
|
|
14833
14957
|
// src/shared/services/spoke/BitcoinSpokeService.ts
|
|
14834
14958
|
bitcoinjsLib.initEccLib(ecc__namespace);
|
|
@@ -14937,7 +15061,7 @@ var BitcoinSpokeService = class {
|
|
|
14937
15061
|
return BigInt(totalBalance);
|
|
14938
15062
|
}
|
|
14939
15063
|
/**
|
|
14940
|
-
* Fund the
|
|
15064
|
+
* Fund the Bound Exchange trading wallet by sending BTC from the user's personal wallet
|
|
14941
15065
|
*
|
|
14942
15066
|
* @param {bigint} amount - Amount in satoshis to send
|
|
14943
15067
|
* @param {BitcoinSpokeProvider} spokeProvider - The Bitcoin spoke provider (must have signing capability)
|
|
@@ -14961,10 +15085,28 @@ var BitcoinSpokeService = class {
|
|
|
14961
15085
|
async sendMessage(params) {
|
|
14962
15086
|
return await this.encodeWithdrawalData(params);
|
|
14963
15087
|
}
|
|
15088
|
+
/**
|
|
15089
|
+
* Build the relay submit/poll identity for an on-demand action (borrow/withdraw).
|
|
15090
|
+
*
|
|
15091
|
+
* Bitcoin borrow/withdraw are on-demand: there is no broadcast transaction — the spoke result is
|
|
15092
|
+
* the signed payload JSON produced by {@link encodeWithdrawalData}/{@link sendMessage}. The relay
|
|
15093
|
+
* accepts the submit under the literal `withdraw` tx_hash with the signed payload (as a JSON object)
|
|
15094
|
+
* in `data`, then tracks the resulting packet under a derived id: `od:` + keccak256 of the ASCII
|
|
15095
|
+
* `payload_hex` string (hash the hex characters, not the decoded bytes). Polling must use that
|
|
15096
|
+
* derived id (`pollTxHash`), not `withdraw`.
|
|
15097
|
+
*
|
|
15098
|
+
* @param tx - The JSON-stringified signed payload returned by `sendMessage` / `encodeWithdrawalData`.
|
|
15099
|
+
*/
|
|
15100
|
+
getOnDemandRelayIdentity(tx) {
|
|
15101
|
+
const data = JSON.parse(tx);
|
|
15102
|
+
const payloadHex = data.payload_hex.startsWith("0x") ? data.payload_hex.slice(2) : data.payload_hex;
|
|
15103
|
+
const pollTxHash = `od:${viem.keccak256(viem.stringToBytes(payloadHex)).slice(2)}`;
|
|
15104
|
+
return { srcTxHash: "withdraw", data, pollTxHash };
|
|
15105
|
+
}
|
|
14964
15106
|
/**
|
|
14965
15107
|
* Build a priority Bitcoin transaction with proper fee calculation
|
|
14966
15108
|
*/
|
|
14967
|
-
async buildBitcoinTransaction(utxos, outputs, changeAddress, chainId, walletProvider, feeRate) {
|
|
15109
|
+
async buildBitcoinTransaction(utxos, outputs, changeAddress, chainId, walletProvider, feeRate, opReturnOutputVbytes) {
|
|
14968
15110
|
const psbt = new bitcoinjsLib.Psbt({ network: this.getBtcNetwork(chainId) });
|
|
14969
15111
|
const effectiveFeeRate = feeRate ?? await this.getFeeRateEstimate();
|
|
14970
15112
|
const walletAddress = await walletProvider.getWalletAddress();
|
|
@@ -15027,7 +15169,7 @@ var BitcoinSpokeService = class {
|
|
|
15027
15169
|
});
|
|
15028
15170
|
}
|
|
15029
15171
|
inputSum += utxo.value;
|
|
15030
|
-
const estimatedSize = estimateBitcoinTxSize(psbt.inputCount, outputs.length, addressType);
|
|
15172
|
+
const estimatedSize = estimateBitcoinTxSize(psbt.inputCount, outputs.length, addressType, opReturnOutputVbytes);
|
|
15031
15173
|
const estimatedFee = Math.ceil(effectiveFeeRate * estimatedSize);
|
|
15032
15174
|
if (inputSum >= outputSum + estimatedFee + DUST_THRESHOLD) {
|
|
15033
15175
|
break;
|
|
@@ -15039,8 +15181,13 @@ var BitcoinSpokeService = class {
|
|
|
15039
15181
|
value: output.value
|
|
15040
15182
|
});
|
|
15041
15183
|
}
|
|
15042
|
-
const sizeWithChange = estimateBitcoinTxSize(
|
|
15043
|
-
|
|
15184
|
+
const sizeWithChange = estimateBitcoinTxSize(
|
|
15185
|
+
psbt.inputCount,
|
|
15186
|
+
outputs.length + 1,
|
|
15187
|
+
addressType,
|
|
15188
|
+
opReturnOutputVbytes
|
|
15189
|
+
);
|
|
15190
|
+
const sizeWithoutChange = estimateBitcoinTxSize(psbt.inputCount, outputs.length, addressType, opReturnOutputVbytes);
|
|
15044
15191
|
const feeWithChange = Math.ceil(effectiveFeeRate * sizeWithChange);
|
|
15045
15192
|
const feeWithoutChange = Math.ceil(effectiveFeeRate * sizeWithoutChange);
|
|
15046
15193
|
let change = inputSum - outputSum - feeWithChange;
|
|
@@ -15109,7 +15256,12 @@ var BitcoinSpokeService = class {
|
|
|
15109
15256
|
return await this.radfi.requestRadfiSignature(
|
|
15110
15257
|
{
|
|
15111
15258
|
userAddress: from,
|
|
15112
|
-
signedBase64Tx
|
|
15259
|
+
signedBase64Tx,
|
|
15260
|
+
// Forward the relay identity ({ hub wallet address, full payload }) so the Bound Exchange
|
|
15261
|
+
// backend can auto-resubmit the intent relay if it gets stuck. `to` is the hub wallet
|
|
15262
|
+
// (relayData.address) and `data` is the full payload (relayData.payload) — the same pair
|
|
15263
|
+
// feature services return as `relayData` from createIntent()/supply().
|
|
15264
|
+
relayData: { address: params.to, payload: data }
|
|
15113
15265
|
},
|
|
15114
15266
|
accessToken
|
|
15115
15267
|
);
|
|
@@ -15119,7 +15271,11 @@ var BitcoinSpokeService = class {
|
|
|
15119
15271
|
"Raw mode is not supported for normal Bitcoin deposits. Use TRADING wallet mode for raw transactions."
|
|
15120
15272
|
);
|
|
15121
15273
|
}
|
|
15122
|
-
const
|
|
15274
|
+
const [allUtxos, mempoolSpent] = await Promise.all([
|
|
15275
|
+
this.fetchUTXOs(from),
|
|
15276
|
+
this.fetchMempoolSpentOutpoints(from)
|
|
15277
|
+
]);
|
|
15278
|
+
const utxos = allUtxos.filter((u) => !mempoolSpent.has(`${u.txid}:${u.vout}`));
|
|
15123
15279
|
if (!utxos?.length) {
|
|
15124
15280
|
throw new Error("No UTXOs available for deposit");
|
|
15125
15281
|
}
|
|
@@ -15134,7 +15290,7 @@ var BitcoinSpokeService = class {
|
|
|
15134
15290
|
);
|
|
15135
15291
|
return await this.signAndBroadcastTransaction(depositPsbt, params.walletProvider);
|
|
15136
15292
|
} catch (error) {
|
|
15137
|
-
|
|
15293
|
+
this.config.logger.error("Error during deposit", error);
|
|
15138
15294
|
throw error;
|
|
15139
15295
|
}
|
|
15140
15296
|
}
|
|
@@ -15142,22 +15298,37 @@ var BitcoinSpokeService = class {
|
|
|
15142
15298
|
* Build deposit PSBT with embedded cross-chain data
|
|
15143
15299
|
*/
|
|
15144
15300
|
async buildDepositPsbt(walletAddress, walletProvider, srcChainKey, token, amount, data, utxos) {
|
|
15145
|
-
const
|
|
15146
|
-
|
|
15301
|
+
const chainConfig = this.config.getChainConfig(srcChainKey);
|
|
15302
|
+
const assetManagerAddress = chainConfig.addresses.assetManager;
|
|
15303
|
+
const normalizedToken = token.toLocaleLowerCase();
|
|
15304
|
+
const nativeBtcTokens = new Set(
|
|
15305
|
+
["btc", chainConfig.nativeToken, chainConfig.supportedTokens.BTC?.address].filter((value) => !!value).map((value) => value.toLocaleLowerCase())
|
|
15306
|
+
);
|
|
15307
|
+
const isNativeBtc = nativeBtcTokens.has(normalizedToken);
|
|
15308
|
+
if (isNativeBtc) {
|
|
15309
|
+
const OP_RETURN = bitcoinjsLib.opcodes.OP_RETURN;
|
|
15310
|
+
const OP_12 = bitcoinjsLib.opcodes.OP_12;
|
|
15311
|
+
if (OP_RETURN === void 0 || OP_12 === void 0) {
|
|
15312
|
+
throw new Error("bitcoinjs-lib opcodes OP_RETURN or OP_12 are undefined");
|
|
15313
|
+
}
|
|
15314
|
+
const OP_RADFI_SODAX_DATA = 49;
|
|
15315
|
+
const payload = Buffer.concat([Buffer.from([OP_RADFI_SODAX_DATA]), Buffer.from(data.slice(2), "hex")]);
|
|
15316
|
+
const opReturnOutputVbytes = calcOpReturnOutputVbytes(payload.length);
|
|
15147
15317
|
const outputs = [
|
|
15148
15318
|
{
|
|
15149
15319
|
address: assetManagerAddress,
|
|
15150
15320
|
value: Number(amount)
|
|
15151
15321
|
}
|
|
15152
15322
|
];
|
|
15153
|
-
const psbt = await this.buildBitcoinTransaction(
|
|
15154
|
-
|
|
15155
|
-
|
|
15156
|
-
|
|
15157
|
-
|
|
15158
|
-
|
|
15159
|
-
|
|
15160
|
-
|
|
15323
|
+
const psbt = await this.buildBitcoinTransaction(
|
|
15324
|
+
utxos,
|
|
15325
|
+
outputs,
|
|
15326
|
+
walletAddress,
|
|
15327
|
+
srcChainKey,
|
|
15328
|
+
walletProvider,
|
|
15329
|
+
void 0,
|
|
15330
|
+
opReturnOutputVbytes
|
|
15331
|
+
);
|
|
15161
15332
|
const compiledScript = bitcoinjsLib.script.compile([OP_RETURN, OP_12, payload]);
|
|
15162
15333
|
psbt.addOutput({
|
|
15163
15334
|
script: compiledScript,
|
|
@@ -15177,6 +15348,27 @@ var BitcoinSpokeService = class {
|
|
|
15177
15348
|
}
|
|
15178
15349
|
return await response.json();
|
|
15179
15350
|
}
|
|
15351
|
+
/**
|
|
15352
|
+
* Returns the set of "txid:vout" outpoints currently being spent by
|
|
15353
|
+
* unconfirmed transactions in the mempool for the given address.
|
|
15354
|
+
* Used to prevent double-spend when building a new PSBT.
|
|
15355
|
+
*/
|
|
15356
|
+
async fetchMempoolSpentOutpoints(address) {
|
|
15357
|
+
try {
|
|
15358
|
+
const response = await fetch(`${this.rpcUrl}/address/${address}/txs/mempool`);
|
|
15359
|
+
if (!response.ok) return /* @__PURE__ */ new Set();
|
|
15360
|
+
const mempoolTxs = await response.json();
|
|
15361
|
+
const spent = /* @__PURE__ */ new Set();
|
|
15362
|
+
for (const tx of mempoolTxs) {
|
|
15363
|
+
for (const input of tx.vin) {
|
|
15364
|
+
spent.add(`${input.txid}:${input.vout}`);
|
|
15365
|
+
}
|
|
15366
|
+
}
|
|
15367
|
+
return spent;
|
|
15368
|
+
} catch {
|
|
15369
|
+
return /* @__PURE__ */ new Set();
|
|
15370
|
+
}
|
|
15371
|
+
}
|
|
15180
15372
|
/**
|
|
15181
15373
|
* Fetch raw transaction hex
|
|
15182
15374
|
*/
|
|
@@ -15192,7 +15384,7 @@ var BitcoinSpokeService = class {
|
|
|
15192
15384
|
let srcAddress = from;
|
|
15193
15385
|
const addressType = detectBitcoinAddressType(from);
|
|
15194
15386
|
if (walletMode === "TRADING") {
|
|
15195
|
-
srcAddress = await this.radfi.getTradingWallet(srcAddress)
|
|
15387
|
+
srcAddress = (await this.radfi.getTradingWallet(srcAddress)).tradingAddress;
|
|
15196
15388
|
}
|
|
15197
15389
|
const payload = {
|
|
15198
15390
|
src_address: srcAddress,
|
|
@@ -15211,8 +15403,12 @@ var BitcoinSpokeService = class {
|
|
|
15211
15403
|
if (params.raw === true) {
|
|
15212
15404
|
return JSON.stringify(onDemandWithdraw);
|
|
15213
15405
|
}
|
|
15214
|
-
|
|
15215
|
-
|
|
15406
|
+
if (!params.walletProvider.getPublicKey) {
|
|
15407
|
+
throw new Error("Wallet provider does not support getPublicKey");
|
|
15408
|
+
}
|
|
15409
|
+
const rawSignature = usesBip322MessageSigning(addressType) ? await params.walletProvider.signBip322Message(orderedPayload) : await params.walletProvider.signEcdsaMessage(orderedPayload);
|
|
15410
|
+
onDemandWithdraw.signature = normalizeSignatureToBase64(rawSignature);
|
|
15411
|
+
onDemandWithdraw.public_key = await params.walletProvider.getPublicKey();
|
|
15216
15412
|
return JSON.stringify(onDemandWithdraw);
|
|
15217
15413
|
}
|
|
15218
15414
|
/**
|
|
@@ -15220,8 +15416,14 @@ var BitcoinSpokeService = class {
|
|
|
15220
15416
|
*/
|
|
15221
15417
|
async signAndBroadcastTransaction(psbt, walletProvider) {
|
|
15222
15418
|
const psbtBase64 = typeof psbt === "string" ? psbt : psbt.toBase64();
|
|
15223
|
-
const
|
|
15224
|
-
const
|
|
15419
|
+
const signedRaw = await walletProvider.signTransaction(psbtBase64, false);
|
|
15420
|
+
const signedPsbt = bitcoinjsLib.Psbt.fromBase64(normalizePsbtToBase64(signedRaw));
|
|
15421
|
+
try {
|
|
15422
|
+
signedPsbt.finalizeAllInputs();
|
|
15423
|
+
} catch {
|
|
15424
|
+
}
|
|
15425
|
+
const txHex = signedPsbt.extractTransaction().toHex();
|
|
15426
|
+
const txHash = await this.broadcastTransaction(txHex);
|
|
15225
15427
|
return txHash;
|
|
15226
15428
|
}
|
|
15227
15429
|
/**
|
|
@@ -17460,7 +17662,7 @@ function base64Decode(str) {
|
|
|
17460
17662
|
}
|
|
17461
17663
|
var encoder = new TextEncoder();
|
|
17462
17664
|
var decoder = new TextDecoder();
|
|
17463
|
-
function
|
|
17665
|
+
function stringToBytes2(str) {
|
|
17464
17666
|
return encoder.encode(str);
|
|
17465
17667
|
}
|
|
17466
17668
|
function bytesToString(bytes) {
|
|
@@ -20857,7 +21059,7 @@ var JsonRpcProvider = class {
|
|
|
20857
21059
|
};
|
|
20858
21060
|
}
|
|
20859
21061
|
async viewContractState({ contractId, prefix: prefix2, blockQuery = { finality: DEFAULT_FINALITY } }) {
|
|
20860
|
-
const prefixBase64 = base64Encode(
|
|
21062
|
+
const prefixBase64 = base64Encode(stringToBytes2(prefix2 || ""));
|
|
20861
21063
|
let reference;
|
|
20862
21064
|
if ("blockId" in blockQuery) {
|
|
20863
21065
|
reference = { block_id: blockQuery.blockId };
|
|
@@ -20885,7 +21087,7 @@ var JsonRpcProvider = class {
|
|
|
20885
21087
|
}
|
|
20886
21088
|
}
|
|
20887
21089
|
async callFunctionRaw({ contractId, method, args, blockQuery = { finality: DEFAULT_FINALITY } }) {
|
|
20888
|
-
const argsBytes = args instanceof Uint8Array ? args :
|
|
21090
|
+
const argsBytes = args instanceof Uint8Array ? args : stringToBytes2(JSON.stringify(args));
|
|
20889
21091
|
const argsBase64 = base64Encode(argsBytes);
|
|
20890
21092
|
if ("blockId" in blockQuery) {
|
|
20891
21093
|
return this.query({
|
|
@@ -21151,6 +21353,7 @@ var JsonRpcProvider = class {
|
|
|
21151
21353
|
|
|
21152
21354
|
// src/shared/services/spoke/NearSpokeService.ts
|
|
21153
21355
|
var NEAR_DEFAULT_GAS = BigInt("300000000000000");
|
|
21356
|
+
var NEAR_STORAGE_DEPOSIT = BigInt("1250000000000000000000");
|
|
21154
21357
|
var NearSpokeService = class {
|
|
21155
21358
|
config;
|
|
21156
21359
|
rpcProvider;
|
|
@@ -21200,7 +21403,7 @@ var NearSpokeService = class {
|
|
|
21200
21403
|
token: Array.from(Buffer.from(fillData.token, "utf-8"))
|
|
21201
21404
|
};
|
|
21202
21405
|
}
|
|
21203
|
-
async fillIntent(fromInfo, fillData, deposit = BigInt("
|
|
21406
|
+
async fillIntent(fromInfo, fillData, deposit = BigInt("1"), gas = BigInt("300000000000000")) {
|
|
21204
21407
|
const intentFiller = this.config.getChainConfig(fromInfo.srcChainKey).addresses.intentFiller;
|
|
21205
21408
|
if (isNativeToken(fromInfo.srcChainKey, fillData.token)) {
|
|
21206
21409
|
deposit = BigInt(fillData.amount);
|
|
@@ -21274,7 +21477,8 @@ var NearSpokeService = class {
|
|
|
21274
21477
|
data: inputParams.data
|
|
21275
21478
|
})
|
|
21276
21479
|
},
|
|
21277
|
-
|
|
21480
|
+
// NEP-141 requires exactly 1 yoctoNEAR attached to ft_transfer_call.
|
|
21481
|
+
deposit: BigInt("1"),
|
|
21278
21482
|
gas: NEAR_DEFAULT_GAS
|
|
21279
21483
|
}
|
|
21280
21484
|
};
|
|
@@ -21305,6 +21509,52 @@ var NearSpokeService = class {
|
|
|
21305
21509
|
}
|
|
21306
21510
|
return BigInt(bal);
|
|
21307
21511
|
}
|
|
21512
|
+
/**
|
|
21513
|
+
* Whether `accountId` is storage-registered on a NEP-141 `token` contract. NEP-141 requires an
|
|
21514
|
+
* account to pay a one-time storage bond before it can receive (hold a balance of) the token, so
|
|
21515
|
+
* this gates any leg that delivers a token to a user on NEAR (swap output on NEAR, bridge into
|
|
21516
|
+
* NEAR, money-market borrow/withdraw to NEAR).
|
|
21517
|
+
*
|
|
21518
|
+
* Native NEAR is not a NEP-141 token and has no storage registration, so this returns `true` for
|
|
21519
|
+
* the native token. The view goes through {@link queryContract}, i.e. the configurable RPC from
|
|
21520
|
+
* chain config — a custom `rpcUrl` passed to the SDK is honoured.
|
|
21521
|
+
*/
|
|
21522
|
+
async isStorageRegistered(token, accountId) {
|
|
21523
|
+
if (isNativeToken(ChainKeys.NEAR_MAINNET, token)) {
|
|
21524
|
+
return true;
|
|
21525
|
+
}
|
|
21526
|
+
const balance = await this.queryContract(token, "storage_balance_of", { account_id: accountId });
|
|
21527
|
+
return balance != null;
|
|
21528
|
+
}
|
|
21529
|
+
/**
|
|
21530
|
+
* Build (and, unless `raw`, submit) a NEP-141 `storage_deposit` registration for `accountId` on
|
|
21531
|
+
* the `token` contract. Call this when {@link isStorageRegistered} is `false` before a token is
|
|
21532
|
+
* delivered to the account on NEAR.
|
|
21533
|
+
*
|
|
21534
|
+
* Native NEAR has no storage registration — passing the native token throws.
|
|
21535
|
+
*
|
|
21536
|
+
* @param params.deposit Storage bond to attach; defaults to {@link NEAR_STORAGE_DEPOSIT}
|
|
21537
|
+
* (0.00125 NEAR). Override per token if its `storage_balance_bounds.min` differs.
|
|
21538
|
+
*/
|
|
21539
|
+
async registerStorage(params) {
|
|
21540
|
+
if (isNativeToken(ChainKeys.NEAR_MAINNET, params.token)) {
|
|
21541
|
+
throw new Error("[NearSpokeService.registerStorage] Native NEAR has no NEP-141 storage registration.");
|
|
21542
|
+
}
|
|
21543
|
+
const tx = {
|
|
21544
|
+
signerId: params.accountId,
|
|
21545
|
+
params: {
|
|
21546
|
+
contractId: params.token,
|
|
21547
|
+
method: "storage_deposit",
|
|
21548
|
+
args: { account_id: params.accountId, registration_only: true },
|
|
21549
|
+
deposit: params.deposit ?? NEAR_STORAGE_DEPOSIT,
|
|
21550
|
+
gas: NEAR_DEFAULT_GAS
|
|
21551
|
+
}
|
|
21552
|
+
};
|
|
21553
|
+
if (params.raw === true) {
|
|
21554
|
+
return tx;
|
|
21555
|
+
}
|
|
21556
|
+
return params.walletProvider.signAndSubmitTxn(tx);
|
|
21557
|
+
}
|
|
21308
21558
|
/**
|
|
21309
21559
|
* Sends a message to the hub chain.
|
|
21310
21560
|
* @param {SendMessageParams} params - Includes dstChainKey, the chain key of the hub chain.
|
|
@@ -22105,17 +22355,11 @@ var SuiSpokeService = class {
|
|
|
22105
22355
|
]
|
|
22106
22356
|
});
|
|
22107
22357
|
if (params.raw === true) {
|
|
22108
|
-
tx.setSender(from);
|
|
22109
|
-
const transactionRaw = await tx.build({
|
|
22110
|
-
client: this.publicClient,
|
|
22111
|
-
onlyTransactionKind: true
|
|
22112
|
-
});
|
|
22113
|
-
const transactionRawBase64String = Buffer.from(transactionRaw).toString("base64");
|
|
22114
22358
|
return {
|
|
22115
22359
|
from,
|
|
22116
22360
|
to: `${assetManager.packageId}::${assetManager.moduleId}::transfer`,
|
|
22117
22361
|
value: amount,
|
|
22118
|
-
data:
|
|
22362
|
+
data: tx.serialize()
|
|
22119
22363
|
};
|
|
22120
22364
|
}
|
|
22121
22365
|
return params.walletProvider.signAndExecuteTxn(tx);
|
|
@@ -22135,17 +22379,11 @@ var SuiSpokeService = class {
|
|
|
22135
22379
|
]
|
|
22136
22380
|
});
|
|
22137
22381
|
if (params.raw === true) {
|
|
22138
|
-
txb.setSender(from);
|
|
22139
|
-
const transactionRaw = await txb.build({
|
|
22140
|
-
client: this.publicClient,
|
|
22141
|
-
onlyTransactionKind: true
|
|
22142
|
-
});
|
|
22143
|
-
const transactionRawBase64String = Buffer.from(transactionRaw).toString("base64");
|
|
22144
22382
|
return {
|
|
22145
22383
|
from,
|
|
22146
22384
|
to: `${connection.packageId}::${connection.moduleId}::send_message_ua`,
|
|
22147
22385
|
value: 0n,
|
|
22148
|
-
data:
|
|
22386
|
+
data: txb.serialize()
|
|
22149
22387
|
};
|
|
22150
22388
|
}
|
|
22151
22389
|
return params.walletProvider.signAndExecuteTxn(txb);
|
|
@@ -22156,7 +22394,7 @@ var SuiSpokeService = class {
|
|
|
22156
22394
|
* @returns {Promise<bigint>} The estimated computation cost.
|
|
22157
22395
|
*/
|
|
22158
22396
|
async estimateGas({ tx }) {
|
|
22159
|
-
const txb = transactions.Transaction.
|
|
22397
|
+
const txb = transactions.Transaction.from(tx.data);
|
|
22160
22398
|
const result = await this.publicClient.devInspectTransactionBlock({
|
|
22161
22399
|
sender: tx.from,
|
|
22162
22400
|
transactionBlock: txb
|
|
@@ -22387,6 +22625,7 @@ var CustomStellarAccount = class {
|
|
|
22387
22625
|
decrementSequenceNumber() {
|
|
22388
22626
|
if (this.sequenceNumber > this.startingSequenceNumber) {
|
|
22389
22627
|
this.sequenceNumber--;
|
|
22628
|
+
return;
|
|
22390
22629
|
}
|
|
22391
22630
|
throw new Error(
|
|
22392
22631
|
`Sequence number cannot be decremented below the starting sequence number: ${this.startingSequenceNumber}`
|
|
@@ -22493,7 +22732,7 @@ var StellarSpokeService = class {
|
|
|
22493
22732
|
);
|
|
22494
22733
|
const assembledPriorityTx = stellarSdk.rpc.assembleTransaction(rawPriorityTx, simulation).build();
|
|
22495
22734
|
if (params.raw) {
|
|
22496
|
-
const transactionXdr =
|
|
22735
|
+
const transactionXdr = assembledPriorityTx.toXDR();
|
|
22497
22736
|
return {
|
|
22498
22737
|
from,
|
|
22499
22738
|
to: this.config.getChainConfig(srcChainKey).addresses.assetManager,
|
|
@@ -22512,13 +22751,13 @@ var StellarSpokeService = class {
|
|
|
22512
22751
|
);
|
|
22513
22752
|
return `${hash}`;
|
|
22514
22753
|
} catch (error) {
|
|
22515
|
-
|
|
22754
|
+
this.config.logger.error("Error during sendMessage", error);
|
|
22516
22755
|
throw error;
|
|
22517
22756
|
}
|
|
22518
22757
|
}
|
|
22519
22758
|
handleSendTransactionError(response) {
|
|
22520
22759
|
if (response.status === "ERROR") {
|
|
22521
|
-
|
|
22760
|
+
this.config.logger.error(JSON.stringify(response, null, 2));
|
|
22522
22761
|
throw new Error(JSON.stringify(response, null, 2));
|
|
22523
22762
|
}
|
|
22524
22763
|
return response;
|
|
@@ -22610,7 +22849,7 @@ var StellarSpokeService = class {
|
|
|
22610
22849
|
);
|
|
22611
22850
|
const assembledPriorityTx = stellarSdk.rpc.assembleTransaction(rawPriorityTx, simulation).build();
|
|
22612
22851
|
if (params.raw) {
|
|
22613
|
-
const transactionXdr =
|
|
22852
|
+
const transactionXdr = assembledPriorityTx.toXDR();
|
|
22614
22853
|
return {
|
|
22615
22854
|
from,
|
|
22616
22855
|
to: this.config.getChainConfig(srcChainKey).addresses.assetManager,
|
|
@@ -22629,7 +22868,7 @@ var StellarSpokeService = class {
|
|
|
22629
22868
|
);
|
|
22630
22869
|
return `${hash}`;
|
|
22631
22870
|
} catch (error) {
|
|
22632
|
-
|
|
22871
|
+
this.config.logger.error("Error during deposit", error);
|
|
22633
22872
|
throw error;
|
|
22634
22873
|
}
|
|
22635
22874
|
}
|
|
@@ -22657,7 +22896,7 @@ var StellarSpokeService = class {
|
|
|
22657
22896
|
(balance2) => "limit" in balance2 && "balance" in balance2 && "asset_code" in balance2 && trustlineConfig.assetCode.toLowerCase() === balance2.asset_code?.toLowerCase() && "asset_issuer" in balance2 && trustlineConfig.assetIssuer.toLowerCase() === balance2.asset_issuer?.toLowerCase()
|
|
22658
22897
|
);
|
|
22659
22898
|
if (!tokenBalance) {
|
|
22660
|
-
|
|
22899
|
+
this.config.logger.error(`No token balances found for token: ${token}`);
|
|
22661
22900
|
return false;
|
|
22662
22901
|
}
|
|
22663
22902
|
const limit = parseToStroops(tokenBalance.limit);
|
|
@@ -22706,7 +22945,7 @@ var StellarSpokeService = class {
|
|
|
22706
22945
|
const hash = await this.signAndSendTransaction(walletProvider, transaction);
|
|
22707
22946
|
return `${hash}`;
|
|
22708
22947
|
} catch (error) {
|
|
22709
|
-
|
|
22948
|
+
this.config.logger.error("Error during requestTrustline", error);
|
|
22710
22949
|
throw error;
|
|
22711
22950
|
}
|
|
22712
22951
|
}
|
|
@@ -23565,7 +23804,7 @@ var SpokeService = class _SpokeService {
|
|
|
23565
23804
|
}
|
|
23566
23805
|
default: {
|
|
23567
23806
|
const exhaustiveCheck = chainType;
|
|
23568
|
-
|
|
23807
|
+
this.config.logger.debug("Unhandled exhaustive case", { value: exhaustiveCheck });
|
|
23569
23808
|
throw new Error(`[getSpokeService] Invalid chain type. Valid chain types: ${ChainTypeArr.join(", ")}`);
|
|
23570
23809
|
}
|
|
23571
23810
|
}
|
|
@@ -23738,7 +23977,7 @@ var SpokeService = class _SpokeService {
|
|
|
23738
23977
|
}
|
|
23739
23978
|
default: {
|
|
23740
23979
|
const exhaustiveCheck = chainType;
|
|
23741
|
-
|
|
23980
|
+
this.config.logger.debug("Unhandled exhaustive case", { value: exhaustiveCheck });
|
|
23742
23981
|
return {
|
|
23743
23982
|
ok: false,
|
|
23744
23983
|
error: new Error(`[estimateGas] Invalid chain type. Valid chain types: ${ChainTypeArr.join(", ")}`)
|
|
@@ -23845,17 +24084,17 @@ var SpokeService = class _SpokeService {
|
|
|
23845
24084
|
args: [params.srcChainId, params.srcAddress, params.payload]
|
|
23846
24085
|
})
|
|
23847
24086
|
});
|
|
23848
|
-
|
|
24087
|
+
this.config.logger.warn("simulateRecvMessage did not revert as expected", { result });
|
|
23849
24088
|
return {
|
|
23850
24089
|
ok: false,
|
|
23851
24090
|
error: new Error('Function should have reverted with "Simulation completed"')
|
|
23852
24091
|
};
|
|
23853
24092
|
} catch (error) {
|
|
23854
24093
|
if (error instanceof Error && error.message?.includes("Simulation completed")) {
|
|
23855
|
-
|
|
24094
|
+
this.config.logger.warn("simulateRecvMessage completed successfully with expected revert");
|
|
23856
24095
|
return { ok: true, value: true };
|
|
23857
24096
|
}
|
|
23858
|
-
|
|
24097
|
+
this.config.logger.error("simulateRecvMessage failed with unexpected error", error);
|
|
23859
24098
|
return { ok: false, error };
|
|
23860
24099
|
}
|
|
23861
24100
|
}
|
|
@@ -23940,7 +24179,7 @@ var SpokeService = class _SpokeService {
|
|
|
23940
24179
|
}
|
|
23941
24180
|
default: {
|
|
23942
24181
|
const exhaustiveCheck = chainType;
|
|
23943
|
-
|
|
24182
|
+
this.config.logger.debug("Unhandled exhaustive case", { value: exhaustiveCheck });
|
|
23944
24183
|
return {
|
|
23945
24184
|
ok: false,
|
|
23946
24185
|
error: new Error(`[deposit] Invalid chain type. Valid chain types: ${ChainTypeArr.join(", ")}`)
|
|
@@ -24017,7 +24256,7 @@ var SpokeService = class _SpokeService {
|
|
|
24017
24256
|
}
|
|
24018
24257
|
default: {
|
|
24019
24258
|
const exhaustiveCheck = chainType;
|
|
24020
|
-
|
|
24259
|
+
this.config.logger.debug("Unhandled exhaustive case", { value: exhaustiveCheck });
|
|
24021
24260
|
return {
|
|
24022
24261
|
ok: false,
|
|
24023
24262
|
error: new Error(`[getDeposit] Invalid chain type. Valid chain types: ${ChainTypeArr.join(", ")}`)
|
|
@@ -24126,7 +24365,7 @@ var SpokeService = class _SpokeService {
|
|
|
24126
24365
|
}
|
|
24127
24366
|
default: {
|
|
24128
24367
|
const exhaustiveCheck = chainType;
|
|
24129
|
-
|
|
24368
|
+
this.config.logger.debug("Unhandled exhaustive case", { value: exhaustiveCheck });
|
|
24130
24369
|
return {
|
|
24131
24370
|
ok: false,
|
|
24132
24371
|
error: new Error(`[sendMessage] Invalid chain type. Valid chain types: ${ChainTypeArr.join(", ")}`)
|
|
@@ -24205,10 +24444,10 @@ var SpokeService = class _SpokeService {
|
|
|
24205
24444
|
if (isSolanaChainKeyType(chainKey)) {
|
|
24206
24445
|
const result = await this.solana.waitForTransactionReceipt({ txHash, chainKey });
|
|
24207
24446
|
if (!result.ok || result.value.status !== "success") {
|
|
24208
|
-
|
|
24447
|
+
this.config.logger.warn(
|
|
24209
24448
|
`Solana verifyTxHash failed: ${!result.ok ? result.error : "error" in result.value ? result.value.error : "unknown"}`
|
|
24210
24449
|
);
|
|
24211
|
-
|
|
24450
|
+
this.config.logger.warn("Returning true to assume transaction exists on chain in future ");
|
|
24212
24451
|
return { ok: true, value: true };
|
|
24213
24452
|
}
|
|
24214
24453
|
return { ok: true, value: true };
|
|
@@ -24295,7 +24534,7 @@ var SpokeService = class _SpokeService {
|
|
|
24295
24534
|
}
|
|
24296
24535
|
default: {
|
|
24297
24536
|
const exhaustiveCheck = chainType;
|
|
24298
|
-
|
|
24537
|
+
this.config.logger.debug("Unhandled exhaustive case", { value: exhaustiveCheck });
|
|
24299
24538
|
return { ok: false, error: new Error(`waitForTransactionReceipt not supported for ${params.chainKey}`) };
|
|
24300
24539
|
}
|
|
24301
24540
|
}
|
|
@@ -25346,6 +25585,7 @@ var EvmHubProvider = class {
|
|
|
25346
25585
|
};
|
|
25347
25586
|
|
|
25348
25587
|
// src/swap/SolverApiService.ts
|
|
25588
|
+
var bigintReplacer = (_key, value) => typeof value === "bigint" ? value.toString() : value;
|
|
25349
25589
|
var SolverApiService = class {
|
|
25350
25590
|
constructor() {
|
|
25351
25591
|
}
|
|
@@ -25414,13 +25654,16 @@ var SolverApiService = class {
|
|
|
25414
25654
|
}
|
|
25415
25655
|
};
|
|
25416
25656
|
} catch (e) {
|
|
25417
|
-
|
|
25657
|
+
configService.logger.error(
|
|
25658
|
+
"[SolverApiService.getQuote] failed",
|
|
25659
|
+
e instanceof Error ? e : new Error(JSON.stringify(e, bigintReplacer))
|
|
25660
|
+
);
|
|
25418
25661
|
return {
|
|
25419
25662
|
ok: false,
|
|
25420
25663
|
error: {
|
|
25421
25664
|
detail: {
|
|
25422
25665
|
code: exports.SolverIntentErrorCode.UNKNOWN,
|
|
25423
|
-
message: e ? JSON.stringify(e) : "Unknown error"
|
|
25666
|
+
message: e ? JSON.stringify(e, bigintReplacer) : "Unknown error"
|
|
25424
25667
|
}
|
|
25425
25668
|
}
|
|
25426
25669
|
};
|
|
@@ -25437,7 +25680,7 @@ var SolverApiService = class {
|
|
|
25437
25680
|
* @param config - Solver endpoint configuration.
|
|
25438
25681
|
* @returns A `Result` containing `{ answer: 'OK', intent_hash: Hex }` on success.
|
|
25439
25682
|
*/
|
|
25440
|
-
static async postExecution(request, config) {
|
|
25683
|
+
static async postExecution(request, config, logger = silentLogger) {
|
|
25441
25684
|
try {
|
|
25442
25685
|
const response = await retry(
|
|
25443
25686
|
() => fetch(`${config.solverApiEndpoint}/execute`, {
|
|
@@ -25459,13 +25702,16 @@ var SolverApiService = class {
|
|
|
25459
25702
|
value: await response.json()
|
|
25460
25703
|
};
|
|
25461
25704
|
} catch (e) {
|
|
25462
|
-
|
|
25705
|
+
logger.error(
|
|
25706
|
+
"[SolverApiService.postExecution] failed",
|
|
25707
|
+
e instanceof Error ? e : new Error(JSON.stringify(e, bigintReplacer))
|
|
25708
|
+
);
|
|
25463
25709
|
return {
|
|
25464
25710
|
ok: false,
|
|
25465
25711
|
error: {
|
|
25466
25712
|
detail: {
|
|
25467
25713
|
code: exports.SolverIntentErrorCode.UNKNOWN,
|
|
25468
|
-
message: e ? JSON.stringify(e) : "Unknown error"
|
|
25714
|
+
message: e ? JSON.stringify(e, bigintReplacer) : "Unknown error"
|
|
25469
25715
|
}
|
|
25470
25716
|
}
|
|
25471
25717
|
};
|
|
@@ -25480,7 +25726,7 @@ var SolverApiService = class {
|
|
|
25480
25726
|
* `fill_tx_hash` is set only when `status === SolverIntentStatusCode.SOLVED (3)`.
|
|
25481
25727
|
* @throws Invariant error if `intent_tx_hash` is empty (thrown before the async request).
|
|
25482
25728
|
*/
|
|
25483
|
-
static async getStatus(request, config) {
|
|
25729
|
+
static async getStatus(request, config, logger = silentLogger) {
|
|
25484
25730
|
invariant(request.intent_tx_hash.length > 0, "Empty intent_tx_hash");
|
|
25485
25731
|
try {
|
|
25486
25732
|
const response = await fetch(`${config.solverApiEndpoint}/status`, {
|
|
@@ -25501,13 +25747,16 @@ var SolverApiService = class {
|
|
|
25501
25747
|
value: await response.json()
|
|
25502
25748
|
};
|
|
25503
25749
|
} catch (e) {
|
|
25504
|
-
|
|
25750
|
+
logger.error(
|
|
25751
|
+
"[SolverApiService.getStatus] failed",
|
|
25752
|
+
e instanceof Error ? e : new Error(JSON.stringify(e, bigintReplacer))
|
|
25753
|
+
);
|
|
25505
25754
|
return {
|
|
25506
25755
|
ok: false,
|
|
25507
25756
|
error: {
|
|
25508
25757
|
detail: {
|
|
25509
25758
|
code: exports.SolverIntentErrorCode.UNKNOWN,
|
|
25510
|
-
message: e ? JSON.stringify(e) : "Unknown error"
|
|
25759
|
+
message: e ? JSON.stringify(e, bigintReplacer) : "Unknown error"
|
|
25511
25760
|
}
|
|
25512
25761
|
}
|
|
25513
25762
|
};
|
|
@@ -25627,7 +25876,7 @@ var SwapService = class {
|
|
|
25627
25876
|
* `fill_tx_hash` is populated only when `status === SolverIntentStatusCode.SOLVED (3)`.
|
|
25628
25877
|
*/
|
|
25629
25878
|
async getStatus(request) {
|
|
25630
|
-
return SolverApiService.getStatus(request, this.solver);
|
|
25879
|
+
return SolverApiService.getStatus(request, this.solver, this.config.logger);
|
|
25631
25880
|
}
|
|
25632
25881
|
/**
|
|
25633
25882
|
* Notifies the solver API that an intent has been registered on the hub chain, triggering
|
|
@@ -25650,7 +25899,7 @@ var SwapService = class {
|
|
|
25650
25899
|
*/
|
|
25651
25900
|
async postExecution(request) {
|
|
25652
25901
|
try {
|
|
25653
|
-
const result = await SolverApiService.postExecution(request, this.solver);
|
|
25902
|
+
const result = await SolverApiService.postExecution(request, this.solver, this.config.logger);
|
|
25654
25903
|
if (result.ok) return result;
|
|
25655
25904
|
const detail = result.error?.detail ?? {
|
|
25656
25905
|
code: -999,
|
|
@@ -25923,7 +26172,7 @@ var SwapService = class {
|
|
|
25923
26172
|
* - `raw: false` — broadcasts the transaction; `walletProvider` is required and must match `K`.
|
|
25924
26173
|
*
|
|
25925
26174
|
* Validates tokens and chain keys against the active `ConfigService` before constructing the
|
|
25926
|
-
* intent. Bitcoin source chains require an additional
|
|
26175
|
+
* intent. Bitcoin source chains require an additional Bound Exchange access token step.
|
|
25927
26176
|
*
|
|
25928
26177
|
* @param _params - Intent parameters, source chain key, wallet provider (when `raw: false`),
|
|
25929
26178
|
* and optional `skipSimulation` flag.
|
|
@@ -28404,11 +28653,13 @@ var MigrationService = class {
|
|
|
28404
28653
|
|
|
28405
28654
|
// src/backendApi/BackendApiService.ts
|
|
28406
28655
|
var BackendApiService = class {
|
|
28407
|
-
constructor(config) {
|
|
28656
|
+
constructor(config, logger = consoleLogger) {
|
|
28408
28657
|
this.config = config;
|
|
28409
28658
|
this.headers = { ...config.headers };
|
|
28659
|
+
this.logger = logger;
|
|
28410
28660
|
}
|
|
28411
28661
|
headers;
|
|
28662
|
+
logger;
|
|
28412
28663
|
/**
|
|
28413
28664
|
* Execute a single HTTP request and return the parsed JSON body.
|
|
28414
28665
|
*
|
|
@@ -28447,10 +28698,10 @@ var BackendApiService = class {
|
|
|
28447
28698
|
if (error.name === "AbortError") {
|
|
28448
28699
|
throw new Error("REQUEST_TIMEOUT", { cause: new Error(`Request timeout after ${timeout}ms`) });
|
|
28449
28700
|
}
|
|
28450
|
-
|
|
28701
|
+
this.logger.error("[BackendApiService] Request error", error);
|
|
28451
28702
|
throw error;
|
|
28452
28703
|
}
|
|
28453
|
-
|
|
28704
|
+
this.logger.error("[BackendApiService] Unknown error", error);
|
|
28454
28705
|
throw new Error("UNKNOWN_REQUEST_ERROR", { cause: error });
|
|
28455
28706
|
}
|
|
28456
28707
|
}
|
|
@@ -29120,7 +29371,7 @@ var BridgeService = class {
|
|
|
29120
29371
|
* transaction simulation or batching). When `raw` is `false`, signs and submits the deposit
|
|
29121
29372
|
* transaction via the provided wallet provider.
|
|
29122
29373
|
*
|
|
29123
|
-
* Bitcoin is only supported with `raw: false` because it requires the
|
|
29374
|
+
* Bitcoin is only supported with `raw: false` because it requires the Bound Exchange trading wallet
|
|
29124
29375
|
* derivation flow.
|
|
29125
29376
|
*
|
|
29126
29377
|
* @param _params - Bridge parameters including source/destination chain keys, token addresses,
|
|
@@ -29153,9 +29404,12 @@ var BridgeService = class {
|
|
|
29153
29404
|
{ ...baseCtx, field: "walletProvider" }
|
|
29154
29405
|
);
|
|
29155
29406
|
walletAddress = await this.spoke.bitcoin.getEffectiveWalletAddress(personalAddress);
|
|
29156
|
-
|
|
29407
|
+
if (this.spoke.bitcoin.walletMode === "TRADING") {
|
|
29408
|
+
await this.spoke.bitcoin.radfi.ensureRadfiAccessToken(_params.walletProvider);
|
|
29409
|
+
}
|
|
29157
29410
|
}
|
|
29158
|
-
const hubWallet = await this.hubProvider.getUserHubWalletAddress(
|
|
29411
|
+
const hubWallet = await this.hubProvider.getUserHubWalletAddress(walletAddress, params.srcChainKey);
|
|
29412
|
+
const effectiveSkipSimulation = skipSimulation || isBitcoinChainKeyType(params.srcChainKey) && this.spoke.bitcoin.walletMode === "USER";
|
|
29159
29413
|
const data = this.buildBridgeData(params, srcToken, dstToken, this.config.bridge.partnerFee);
|
|
29160
29414
|
const coreParams = {
|
|
29161
29415
|
srcChainKey: params.srcChainKey,
|
|
@@ -29164,7 +29418,7 @@ var BridgeService = class {
|
|
|
29164
29418
|
token: params.srcToken,
|
|
29165
29419
|
amount: params.amount,
|
|
29166
29420
|
data,
|
|
29167
|
-
skipSimulation
|
|
29421
|
+
skipSimulation: effectiveSkipSimulation
|
|
29168
29422
|
};
|
|
29169
29423
|
const txResult = await this.spoke.deposit(
|
|
29170
29424
|
_params.raw ? {
|
|
@@ -29177,7 +29431,7 @@ var BridgeService = class {
|
|
|
29177
29431
|
}
|
|
29178
29432
|
);
|
|
29179
29433
|
if (!txResult.ok) {
|
|
29180
|
-
|
|
29434
|
+
this.config.logger.error("createBridgeIntent failed", txResult.error);
|
|
29181
29435
|
if (isBridgeCreateIntentError(txResult.error)) return { ok: false, error: txResult.error };
|
|
29182
29436
|
return {
|
|
29183
29437
|
ok: false,
|
|
@@ -29192,7 +29446,7 @@ var BridgeService = class {
|
|
|
29192
29446
|
}
|
|
29193
29447
|
};
|
|
29194
29448
|
} catch (error) {
|
|
29195
|
-
|
|
29449
|
+
this.config.logger.error("createBridgeIntent failed", error);
|
|
29196
29450
|
if (isBridgeCreateIntentError(error)) return { ok: false, error };
|
|
29197
29451
|
return {
|
|
29198
29452
|
ok: false,
|
|
@@ -29362,7 +29616,7 @@ var BridgeService = class {
|
|
|
29362
29616
|
value: availableDepositNormalised.isLessThan(assetManagerBalanceNormalised) ? { amount: availableDeposit, decimals: fromTokenInfo.decimals, type: "DEPOSIT_LIMIT" } : { amount: assetManagerBalance, decimals: toTokenInfo.decimals, type: "WITHDRAWAL_LIMIT" }
|
|
29363
29617
|
};
|
|
29364
29618
|
} catch (error) {
|
|
29365
|
-
|
|
29619
|
+
this.config.logger.error("getBridgeableAmount failed", error);
|
|
29366
29620
|
if (isBridgeLookupError(error)) return { ok: false, error };
|
|
29367
29621
|
return {
|
|
29368
29622
|
ok: false,
|
|
@@ -29409,7 +29663,7 @@ var BridgeService = class {
|
|
|
29409
29663
|
});
|
|
29410
29664
|
return srcToken.vault.toLowerCase() === dstToken.vault.toLowerCase();
|
|
29411
29665
|
} catch (error) {
|
|
29412
|
-
|
|
29666
|
+
this.config.logger.error("isBridgeable check failed", error);
|
|
29413
29667
|
return false;
|
|
29414
29668
|
}
|
|
29415
29669
|
}
|
|
@@ -31799,7 +32053,7 @@ var ClService = class _ClService {
|
|
|
31799
32053
|
};
|
|
31800
32054
|
const txResult = await this.spoke.sendMessage(sendMessageParams);
|
|
31801
32055
|
if (!txResult.ok) {
|
|
31802
|
-
|
|
32056
|
+
this.config.logger.error("executeSupplyLiquidity error", txResult.error);
|
|
31803
32057
|
return {
|
|
31804
32058
|
ok: false,
|
|
31805
32059
|
error: txResult.error
|
|
@@ -31813,7 +32067,7 @@ var ClService = class _ClService {
|
|
|
31813
32067
|
}
|
|
31814
32068
|
};
|
|
31815
32069
|
} catch (error) {
|
|
31816
|
-
|
|
32070
|
+
this.config.logger.error("executeSupplyLiquidity error", error);
|
|
31817
32071
|
return {
|
|
31818
32072
|
ok: false,
|
|
31819
32073
|
error
|
|
@@ -32086,7 +32340,7 @@ var ClService = class _ClService {
|
|
|
32086
32340
|
}
|
|
32087
32341
|
return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
|
|
32088
32342
|
} catch (error) {
|
|
32089
|
-
|
|
32343
|
+
this.config.logger.error("supplyLiquidity error", error);
|
|
32090
32344
|
return {
|
|
32091
32345
|
ok: false,
|
|
32092
32346
|
error
|
|
@@ -32230,7 +32484,7 @@ var ClService = class _ClService {
|
|
|
32230
32484
|
}
|
|
32231
32485
|
};
|
|
32232
32486
|
} catch (error) {
|
|
32233
|
-
|
|
32487
|
+
this.config.logger.error("getPoolRewardConfig error", error);
|
|
32234
32488
|
return {
|
|
32235
32489
|
ok: false,
|
|
32236
32490
|
error: lookupFailed("dex", "getPoolRewardConfig", error)
|
|
@@ -32291,7 +32545,7 @@ var ClService = class _ClService {
|
|
|
32291
32545
|
};
|
|
32292
32546
|
const txResult = await this.spoke.sendMessage(sendMessageParams);
|
|
32293
32547
|
if (!txResult.ok) {
|
|
32294
|
-
|
|
32548
|
+
this.config.logger.error("executeClaimRewards error", txResult.error);
|
|
32295
32549
|
return {
|
|
32296
32550
|
ok: false,
|
|
32297
32551
|
error: txResult.error
|
|
@@ -32305,7 +32559,7 @@ var ClService = class _ClService {
|
|
|
32305
32559
|
}
|
|
32306
32560
|
};
|
|
32307
32561
|
} catch (error) {
|
|
32308
|
-
|
|
32562
|
+
this.config.logger.error("executeClaimRewards error", error);
|
|
32309
32563
|
return {
|
|
32310
32564
|
ok: false,
|
|
32311
32565
|
error
|
|
@@ -32347,7 +32601,7 @@ var ClService = class _ClService {
|
|
|
32347
32601
|
}
|
|
32348
32602
|
return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
|
|
32349
32603
|
} catch (error) {
|
|
32350
|
-
|
|
32604
|
+
this.config.logger.error("claimRewards error", error);
|
|
32351
32605
|
return {
|
|
32352
32606
|
ok: false,
|
|
32353
32607
|
error
|
|
@@ -32395,7 +32649,7 @@ var ClService = class _ClService {
|
|
|
32395
32649
|
address: tokenAddress
|
|
32396
32650
|
};
|
|
32397
32651
|
} catch (error) {
|
|
32398
|
-
|
|
32652
|
+
this.config.logger.error(`Failed to fetch token info for ${tokenAddress}`, error);
|
|
32399
32653
|
return {
|
|
32400
32654
|
symbol: "UNKNOWN",
|
|
32401
32655
|
name: "Unknown Token",
|
|
@@ -32420,12 +32674,12 @@ var ClService = class _ClService {
|
|
|
32420
32674
|
const oneShare = BigInt(10 ** 18);
|
|
32421
32675
|
const result = await Erc4626Service.convertToAssets(statATokenAddress, oneShare, this.hubProvider.publicClient);
|
|
32422
32676
|
if (!result.ok) {
|
|
32423
|
-
|
|
32677
|
+
this.config.logger.error("[getStatATokenConversionRate] Failed to get conversion rate", result.error);
|
|
32424
32678
|
return oneShare;
|
|
32425
32679
|
}
|
|
32426
32680
|
return result.value;
|
|
32427
32681
|
} catch (error) {
|
|
32428
|
-
|
|
32682
|
+
this.config.logger.error("[getStatATokenConversionRate] Error", error);
|
|
32429
32683
|
return BigInt(10 ** 18);
|
|
32430
32684
|
}
|
|
32431
32685
|
}
|
|
@@ -32462,7 +32716,7 @@ var ClService = class _ClService {
|
|
|
32462
32716
|
underlyingToken
|
|
32463
32717
|
};
|
|
32464
32718
|
} catch (error) {
|
|
32465
|
-
|
|
32719
|
+
this.config.logger.error(`[getTokenEnrichmentData] Failed to enrich token ${token.address}`, error);
|
|
32466
32720
|
return {
|
|
32467
32721
|
token,
|
|
32468
32722
|
isStatAToken: true
|
|
@@ -32532,7 +32786,7 @@ var ClService = class _ClService {
|
|
|
32532
32786
|
rewardConfig = rewardConfigResult.value;
|
|
32533
32787
|
}
|
|
32534
32788
|
} catch (error) {
|
|
32535
|
-
|
|
32789
|
+
this.config.logger.warn("Failed to fetch reward config for pool", { error });
|
|
32536
32790
|
}
|
|
32537
32791
|
}
|
|
32538
32792
|
return {
|
|
@@ -32568,7 +32822,7 @@ var ClService = class _ClService {
|
|
|
32568
32822
|
}
|
|
32569
32823
|
};
|
|
32570
32824
|
} catch (error) {
|
|
32571
|
-
|
|
32825
|
+
this.config.logger.error("Failed to fetch pool data", error);
|
|
32572
32826
|
return {
|
|
32573
32827
|
ok: false,
|
|
32574
32828
|
error: lookupFailed("dex", "getPoolData", error)
|
|
@@ -35352,14 +35606,25 @@ var MoneyMarketService = class _MoneyMarketService {
|
|
|
35352
35606
|
);
|
|
35353
35607
|
const dstChainKey = params.dstChainKey ?? srcChainKey;
|
|
35354
35608
|
const dstAddress = params.dstAddress ?? params.srcAddress;
|
|
35355
|
-
const
|
|
35356
|
-
|
|
35357
|
-
|
|
35358
|
-
|
|
35609
|
+
const isBitcoinSrc = isBitcoinChainKeyType(srcChainKey);
|
|
35610
|
+
if (isBitcoinSrc && !_params.raw) {
|
|
35611
|
+
mmInvariant(
|
|
35612
|
+
walletProvider !== void 0 && isBitcoinWalletProviderType(walletProvider),
|
|
35613
|
+
`Invalid wallet provider for chain key: ${srcChainKey}. Expected bitcoin wallet provider.`,
|
|
35614
|
+
{ ...baseCtx, field: "walletProvider" }
|
|
35615
|
+
);
|
|
35616
|
+
await this.spoke.bitcoin.radfi.ensureRadfiAccessToken(walletProvider);
|
|
35617
|
+
}
|
|
35618
|
+
const srcEffectiveAddress = isBitcoinSrc ? await this.spoke.bitcoin.getEffectiveWalletAddress(params.srcAddress) : params.srcAddress;
|
|
35619
|
+
const fromHubWallet = await this.hubProvider.getUserHubWalletAddress(srcEffectiveAddress, srcChainKey);
|
|
35620
|
+
const toHubWallet = dstChainKey === srcChainKey && dstAddress === params.srcAddress ? fromHubWallet : await this.hubProvider.getUserHubWalletAddress(
|
|
35621
|
+
isBitcoinChainKeyType(dstChainKey) ? await this.spoke.bitcoin.getEffectiveWalletAddress(dstAddress) : dstAddress,
|
|
35622
|
+
dstChainKey
|
|
35623
|
+
);
|
|
35359
35624
|
const data = this.buildSupplyData(srcChainKey, params.token, params.amount, toHubWallet);
|
|
35360
35625
|
const coreParams = {
|
|
35361
35626
|
srcChainKey,
|
|
35362
|
-
srcAddress:
|
|
35627
|
+
srcAddress: srcEffectiveAddress,
|
|
35363
35628
|
to: fromHubWallet,
|
|
35364
35629
|
token: params.token,
|
|
35365
35630
|
amount: params.amount,
|
|
@@ -35399,6 +35664,17 @@ var MoneyMarketService = class _MoneyMarketService {
|
|
|
35399
35664
|
}
|
|
35400
35665
|
}
|
|
35401
35666
|
// ==== borrow ==========================================================================
|
|
35667
|
+
/**
|
|
35668
|
+
* Build the relay submit/poll identity for a money-market borrow/withdraw.
|
|
35669
|
+
*
|
|
35670
|
+
* Bitcoin borrow/withdraw are on-demand: the spoke result is a signed payload JSON that the relay
|
|
35671
|
+
* submits under the literal "withdraw" tx_hash and tracks under a derived `od:<hash>` poll id
|
|
35672
|
+
* (see {@link BitcoinSpokeService.getOnDemandRelayIdentity}). Every other chain relays and polls by
|
|
35673
|
+
* its real spoke tx hash, so `pollTxHash` is undefined and `srcChainTxHash` stays the spoke tx.
|
|
35674
|
+
*/
|
|
35675
|
+
buildRelayIdentity(srcChainKey, tx, relayData) {
|
|
35676
|
+
return isBitcoinChainKeyType(srcChainKey) ? this.spoke.bitcoin.getOnDemandRelayIdentity(tx) : { srcTxHash: tx, data: relayData, pollTxHash: void 0 };
|
|
35677
|
+
}
|
|
35402
35678
|
/**
|
|
35403
35679
|
* Borrow tokens from the money market lending pool and wait for the cross-chain relay to
|
|
35404
35680
|
* deliver the funds to the destination address.
|
|
@@ -35434,9 +35710,9 @@ var MoneyMarketService = class _MoneyMarketService {
|
|
|
35434
35710
|
value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: txResult.value.tx }
|
|
35435
35711
|
};
|
|
35436
35712
|
}
|
|
35713
|
+
const relayIdentity = this.buildRelayIdentity(srcChainKey, txResult.value.tx, txResult.value.relayData);
|
|
35437
35714
|
const packet = await relayTxAndWaitPacket({
|
|
35438
|
-
|
|
35439
|
-
data: txResult.value.relayData,
|
|
35715
|
+
...relayIdentity,
|
|
35440
35716
|
chainKey: srcChainKey,
|
|
35441
35717
|
relayerApiEndpoint: this.relayerApiEndpoint,
|
|
35442
35718
|
timeout
|
|
@@ -35451,7 +35727,13 @@ var MoneyMarketService = class _MoneyMarketService {
|
|
|
35451
35727
|
dstChainKey: baseCtx.dstChainKey
|
|
35452
35728
|
})
|
|
35453
35729
|
};
|
|
35454
|
-
return {
|
|
35730
|
+
return {
|
|
35731
|
+
ok: true,
|
|
35732
|
+
value: {
|
|
35733
|
+
srcChainTxHash: relayIdentity.pollTxHash ?? txResult.value.tx,
|
|
35734
|
+
dstChainTxHash: packet.value.dst_tx_hash
|
|
35735
|
+
}
|
|
35736
|
+
};
|
|
35455
35737
|
} catch (error) {
|
|
35456
35738
|
if (isMoneyMarketOrchestrationError(error)) return { ok: false, error };
|
|
35457
35739
|
return {
|
|
@@ -35493,7 +35775,8 @@ var MoneyMarketService = class _MoneyMarketService {
|
|
|
35493
35775
|
field: "token"
|
|
35494
35776
|
});
|
|
35495
35777
|
const encodedDstAddress = encodeAddress(dstChainKey, dstAddress);
|
|
35496
|
-
const
|
|
35778
|
+
const srcEffectiveAddress = isBitcoinChainKeyType(srcChainKey) ? await this.spoke.bitcoin.getEffectiveWalletAddress(params.srcAddress) : params.srcAddress;
|
|
35779
|
+
const fromHubWallet = await this.hubProvider.getUserHubWalletAddress(srcEffectiveAddress, srcChainKey);
|
|
35497
35780
|
const payload = this.buildBorrowData(
|
|
35498
35781
|
fromHubWallet,
|
|
35499
35782
|
encodedDstAddress,
|
|
@@ -35572,144 +35855,151 @@ var MoneyMarketService = class _MoneyMarketService {
|
|
|
35572
35855
|
}
|
|
35573
35856
|
const needsRelay = srcChainKey !== hubChainId || params.dstChainKey != null && params.dstAddress != null && params.dstChainKey !== hubChainId && params.dstAddress !== walletRouter;
|
|
35574
35857
|
if (!needsRelay) {
|
|
35575
|
-
return {
|
|
35576
|
-
ok: true,
|
|
35577
|
-
value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: txResult.value.tx }
|
|
35578
|
-
};
|
|
35579
|
-
}
|
|
35580
|
-
const
|
|
35581
|
-
|
|
35582
|
-
|
|
35583
|
-
chainKey: srcChainKey,
|
|
35584
|
-
relayerApiEndpoint: this.relayerApiEndpoint,
|
|
35585
|
-
timeout
|
|
35586
|
-
});
|
|
35587
|
-
if (!packet.ok)
|
|
35588
|
-
return {
|
|
35589
|
-
ok: false,
|
|
35590
|
-
error: mapRelayFailure(packet.error, {
|
|
35591
|
-
feature: "moneyMarket",
|
|
35592
|
-
action: baseCtx.action,
|
|
35593
|
-
srcChainKey: baseCtx.srcChainKey,
|
|
35594
|
-
dstChainKey: baseCtx.dstChainKey
|
|
35595
|
-
})
|
|
35596
|
-
};
|
|
35597
|
-
return {
|
|
35598
|
-
|
|
35599
|
-
|
|
35600
|
-
|
|
35601
|
-
|
|
35602
|
-
|
|
35603
|
-
};
|
|
35604
|
-
}
|
|
35605
|
-
|
|
35606
|
-
|
|
35607
|
-
|
|
35608
|
-
|
|
35609
|
-
|
|
35610
|
-
|
|
35611
|
-
|
|
35612
|
-
|
|
35613
|
-
*
|
|
35614
|
-
*
|
|
35615
|
-
*
|
|
35616
|
-
|
|
35617
|
-
|
|
35618
|
-
|
|
35619
|
-
|
|
35620
|
-
|
|
35621
|
-
|
|
35622
|
-
|
|
35623
|
-
|
|
35624
|
-
|
|
35625
|
-
|
|
35626
|
-
|
|
35627
|
-
|
|
35628
|
-
|
|
35629
|
-
|
|
35630
|
-
);
|
|
35631
|
-
|
|
35632
|
-
|
|
35633
|
-
|
|
35634
|
-
|
|
35635
|
-
|
|
35636
|
-
|
|
35637
|
-
|
|
35638
|
-
const
|
|
35639
|
-
|
|
35640
|
-
|
|
35641
|
-
|
|
35642
|
-
|
|
35643
|
-
|
|
35644
|
-
|
|
35645
|
-
|
|
35646
|
-
);
|
|
35647
|
-
const
|
|
35648
|
-
|
|
35649
|
-
|
|
35650
|
-
|
|
35651
|
-
|
|
35652
|
-
|
|
35653
|
-
|
|
35654
|
-
|
|
35655
|
-
|
|
35656
|
-
|
|
35657
|
-
|
|
35658
|
-
|
|
35659
|
-
|
|
35660
|
-
|
|
35661
|
-
|
|
35662
|
-
|
|
35663
|
-
|
|
35664
|
-
|
|
35665
|
-
|
|
35666
|
-
|
|
35667
|
-
|
|
35668
|
-
|
|
35669
|
-
|
|
35670
|
-
|
|
35671
|
-
|
|
35672
|
-
ok:
|
|
35673
|
-
|
|
35674
|
-
|
|
35675
|
-
|
|
35676
|
-
}
|
|
35677
|
-
}
|
|
35678
|
-
|
|
35679
|
-
|
|
35680
|
-
|
|
35681
|
-
|
|
35682
|
-
|
|
35683
|
-
|
|
35684
|
-
|
|
35685
|
-
|
|
35686
|
-
|
|
35687
|
-
|
|
35688
|
-
|
|
35689
|
-
|
|
35690
|
-
|
|
35691
|
-
|
|
35692
|
-
|
|
35693
|
-
|
|
35694
|
-
|
|
35695
|
-
*
|
|
35696
|
-
*
|
|
35697
|
-
|
|
35698
|
-
|
|
35699
|
-
|
|
35700
|
-
|
|
35701
|
-
|
|
35702
|
-
|
|
35703
|
-
|
|
35704
|
-
|
|
35705
|
-
|
|
35706
|
-
|
|
35707
|
-
|
|
35708
|
-
|
|
35709
|
-
|
|
35710
|
-
|
|
35711
|
-
}
|
|
35712
|
-
|
|
35858
|
+
return {
|
|
35859
|
+
ok: true,
|
|
35860
|
+
value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: txResult.value.tx }
|
|
35861
|
+
};
|
|
35862
|
+
}
|
|
35863
|
+
const relayIdentity = this.buildRelayIdentity(srcChainKey, txResult.value.tx, txResult.value.relayData);
|
|
35864
|
+
const packet = await relayTxAndWaitPacket({
|
|
35865
|
+
...relayIdentity,
|
|
35866
|
+
chainKey: srcChainKey,
|
|
35867
|
+
relayerApiEndpoint: this.relayerApiEndpoint,
|
|
35868
|
+
timeout
|
|
35869
|
+
});
|
|
35870
|
+
if (!packet.ok)
|
|
35871
|
+
return {
|
|
35872
|
+
ok: false,
|
|
35873
|
+
error: mapRelayFailure(packet.error, {
|
|
35874
|
+
feature: "moneyMarket",
|
|
35875
|
+
action: baseCtx.action,
|
|
35876
|
+
srcChainKey: baseCtx.srcChainKey,
|
|
35877
|
+
dstChainKey: baseCtx.dstChainKey
|
|
35878
|
+
})
|
|
35879
|
+
};
|
|
35880
|
+
return {
|
|
35881
|
+
ok: true,
|
|
35882
|
+
value: {
|
|
35883
|
+
srcChainTxHash: relayIdentity.pollTxHash ?? txResult.value.tx,
|
|
35884
|
+
dstChainTxHash: packet.value.dst_tx_hash
|
|
35885
|
+
}
|
|
35886
|
+
};
|
|
35887
|
+
} catch (error) {
|
|
35888
|
+
if (isMoneyMarketOrchestrationError(error)) return { ok: false, error };
|
|
35889
|
+
return {
|
|
35890
|
+
ok: false,
|
|
35891
|
+
error: executionFailed("moneyMarket", error, { ...baseCtx, phase: "intentCreation" })
|
|
35892
|
+
};
|
|
35893
|
+
}
|
|
35894
|
+
}
|
|
35895
|
+
/**
|
|
35896
|
+
* Build and optionally broadcast the spoke-side withdraw message without waiting for the
|
|
35897
|
+
* cross-chain relay to deliver funds.
|
|
35898
|
+
*
|
|
35899
|
+
* Use this when you need manual relay control or want to sign and broadcast the transaction
|
|
35900
|
+
* yourself. Pass `{ raw: true }` to receive unsigned calldata instead of executing.
|
|
35901
|
+
*
|
|
35902
|
+
* @param _params - Withdraw action params plus `raw` flag and optional `skipSimulation`.
|
|
35903
|
+
* @returns The spoke transaction result (hash or raw calldata) plus `relayData` required to
|
|
35904
|
+
* trigger the hub-side withdrawal via {@link relayTxAndWaitPacket}.
|
|
35905
|
+
*/
|
|
35906
|
+
async createWithdrawIntent(_params) {
|
|
35907
|
+
const { params, walletProvider } = _params;
|
|
35908
|
+
const srcChainKey = params.srcChainKey;
|
|
35909
|
+
const skipSimulation = _params.skipSimulation ?? false;
|
|
35910
|
+
const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey, action: "withdraw" };
|
|
35911
|
+
try {
|
|
35912
|
+
mmInvariant(params.action === "withdraw", "Invalid action", { ...baseCtx, field: "action" });
|
|
35913
|
+
mmInvariant(params.token.length > 0, "Token is required", { ...baseCtx, field: "token" });
|
|
35914
|
+
mmInvariant(params.amount > 0n, "Amount must be greater than 0", { ...baseCtx, field: "amount" });
|
|
35915
|
+
mmInvariant(
|
|
35916
|
+
isUndefinedOrValidWalletProviderForChainKey(srcChainKey, walletProvider),
|
|
35917
|
+
`Invalid wallet provider for chain key: ${srcChainKey}, walletProvider.chainType: ${walletProvider?.chainType}`,
|
|
35918
|
+
{ ...baseCtx, field: "walletProvider" }
|
|
35919
|
+
);
|
|
35920
|
+
const dstChainKey = params.dstChainKey ?? srcChainKey;
|
|
35921
|
+
const dstAddress = params.dstAddress ?? params.srcAddress;
|
|
35922
|
+
mmInvariant(
|
|
35923
|
+
this.config.isMoneyMarketSupportedToken(dstChainKey, params.token),
|
|
35924
|
+
`Unsupported spoke chain (${dstChainKey}) token: ${params.token}`,
|
|
35925
|
+
{ ...baseCtx, field: "token" }
|
|
35926
|
+
);
|
|
35927
|
+
const encodedDstAddress = encodeAddress(dstChainKey, dstAddress);
|
|
35928
|
+
const srcEffectiveAddress = isBitcoinChainKeyType(srcChainKey) ? await this.spoke.bitcoin.getEffectiveWalletAddress(params.srcAddress) : params.srcAddress;
|
|
35929
|
+
const fromHubWallet = await this.hubProvider.getUserHubWalletAddress(srcEffectiveAddress, srcChainKey);
|
|
35930
|
+
const payload = this.buildWithdrawData(
|
|
35931
|
+
fromHubWallet,
|
|
35932
|
+
encodedDstAddress,
|
|
35933
|
+
params.token,
|
|
35934
|
+
params.amount,
|
|
35935
|
+
dstChainKey
|
|
35936
|
+
);
|
|
35937
|
+
const coreParams = {
|
|
35938
|
+
srcChainKey,
|
|
35939
|
+
srcAddress: params.srcAddress,
|
|
35940
|
+
dstChainKey: HUB_CHAIN_KEY,
|
|
35941
|
+
dstAddress: fromHubWallet,
|
|
35942
|
+
payload,
|
|
35943
|
+
skipSimulation
|
|
35944
|
+
};
|
|
35945
|
+
const sendMessageParams = _params.raw ? {
|
|
35946
|
+
...coreParams,
|
|
35947
|
+
raw: true
|
|
35948
|
+
} : {
|
|
35949
|
+
...coreParams,
|
|
35950
|
+
raw: false,
|
|
35951
|
+
walletProvider: _params.walletProvider
|
|
35952
|
+
};
|
|
35953
|
+
const txResult = await this.spoke.sendMessage(sendMessageParams);
|
|
35954
|
+
if (!txResult.ok) {
|
|
35955
|
+
if (isMoneyMarketCreateIntentError(txResult.error)) return { ok: false, error: txResult.error };
|
|
35956
|
+
return {
|
|
35957
|
+
ok: false,
|
|
35958
|
+
error: intentCreationFailed("moneyMarket", txResult.error, baseCtx)
|
|
35959
|
+
};
|
|
35960
|
+
}
|
|
35961
|
+
return {
|
|
35962
|
+
ok: true,
|
|
35963
|
+
value: {
|
|
35964
|
+
tx: txResult.value,
|
|
35965
|
+
relayData: { address: fromHubWallet, payload }
|
|
35966
|
+
}
|
|
35967
|
+
};
|
|
35968
|
+
} catch (error) {
|
|
35969
|
+
if (isMoneyMarketCreateIntentError(error)) return { ok: false, error };
|
|
35970
|
+
return {
|
|
35971
|
+
ok: false,
|
|
35972
|
+
error: intentCreationFailed("moneyMarket", error, baseCtx)
|
|
35973
|
+
};
|
|
35974
|
+
}
|
|
35975
|
+
}
|
|
35976
|
+
// ==== repay ===========================================================================
|
|
35977
|
+
/**
|
|
35978
|
+
* Repay a borrowed position in the money market lending pool and wait for the cross-chain
|
|
35979
|
+
* relay to settle on the hub.
|
|
35980
|
+
*
|
|
35981
|
+
* Hub-chain callers skip the relay step. The repayment is credited to `dstAddress` on
|
|
35982
|
+
* `dstChainKey` (defaulting to the source address and chain when omitted).
|
|
35983
|
+
*
|
|
35984
|
+
* @param _params - Repay action params: `srcChainKey`, `srcAddress`, `token`, `amount`,
|
|
35985
|
+
* `walletProvider`, and optional `dstChainKey`/`dstAddress`.
|
|
35986
|
+
* @returns A pair of transaction hashes — `srcChainTxHash` (spoke) and `dstChainTxHash` (hub).
|
|
35987
|
+
*/
|
|
35988
|
+
async repay(_params) {
|
|
35989
|
+
const { params, timeout = DEFAULT_RELAY_TX_TIMEOUT } = _params;
|
|
35990
|
+
const srcChainKey = params.srcChainKey;
|
|
35991
|
+
const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey, action: "repay" };
|
|
35992
|
+
try {
|
|
35993
|
+
const txResult = await this.createRepayIntent(_params);
|
|
35994
|
+
if (!txResult.ok) return { ok: false, error: txResult.error };
|
|
35995
|
+
const verify = await this.spoke.verifyTxHash({ txHash: txResult.value.tx, chainKey: srcChainKey });
|
|
35996
|
+
if (!verify.ok) {
|
|
35997
|
+
return {
|
|
35998
|
+
ok: false,
|
|
35999
|
+
error: verifyFailed("moneyMarket", verify.error, baseCtx)
|
|
36000
|
+
};
|
|
36001
|
+
}
|
|
36002
|
+
if (isHubChainKeyType(srcChainKey)) {
|
|
35713
36003
|
return {
|
|
35714
36004
|
ok: true,
|
|
35715
36005
|
value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: txResult.value.tx }
|
|
@@ -35773,14 +36063,25 @@ var MoneyMarketService = class _MoneyMarketService {
|
|
|
35773
36063
|
);
|
|
35774
36064
|
const dstChainKey = params.dstChainKey ?? srcChainKey;
|
|
35775
36065
|
const dstAddress = params.dstAddress ?? params.srcAddress;
|
|
35776
|
-
const
|
|
35777
|
-
|
|
35778
|
-
|
|
35779
|
-
|
|
36066
|
+
const isBitcoinSrc = isBitcoinChainKeyType(srcChainKey);
|
|
36067
|
+
if (isBitcoinSrc && !_params.raw) {
|
|
36068
|
+
mmInvariant(
|
|
36069
|
+
walletProvider !== void 0 && isBitcoinWalletProviderType(walletProvider),
|
|
36070
|
+
`Invalid wallet provider for chain key: ${srcChainKey}. Expected bitcoin wallet provider.`,
|
|
36071
|
+
{ ...baseCtx, field: "walletProvider" }
|
|
36072
|
+
);
|
|
36073
|
+
await this.spoke.bitcoin.radfi.ensureRadfiAccessToken(walletProvider);
|
|
36074
|
+
}
|
|
36075
|
+
const srcEffectiveAddress = isBitcoinSrc ? await this.spoke.bitcoin.getEffectiveWalletAddress(params.srcAddress) : params.srcAddress;
|
|
36076
|
+
const fromHubWallet = await this.hubProvider.getUserHubWalletAddress(srcEffectiveAddress, srcChainKey);
|
|
36077
|
+
const toHubWallet = dstChainKey === srcChainKey && dstAddress === params.srcAddress ? fromHubWallet : await this.hubProvider.getUserHubWalletAddress(
|
|
36078
|
+
isBitcoinChainKeyType(dstChainKey) ? await this.spoke.bitcoin.getEffectiveWalletAddress(dstAddress) : dstAddress,
|
|
36079
|
+
dstChainKey
|
|
36080
|
+
);
|
|
35780
36081
|
const data = this.buildRepayData(srcChainKey, params.token, params.amount, toHubWallet);
|
|
35781
36082
|
const coreParams = {
|
|
35782
36083
|
srcChainKey,
|
|
35783
|
-
srcAddress:
|
|
36084
|
+
srcAddress: srcEffectiveAddress,
|
|
35784
36085
|
to: fromHubWallet,
|
|
35785
36086
|
token: params.token,
|
|
35786
36087
|
amount: params.amount,
|
|
@@ -36341,9 +36642,9 @@ var PartnerFeeClaimService = class {
|
|
|
36341
36642
|
if (typeof balanceResult === "bigint") {
|
|
36342
36643
|
balance = balanceResult;
|
|
36343
36644
|
} else {
|
|
36344
|
-
|
|
36345
|
-
`[PartnerFeeClaimService] Unexpected balance result format for ${entry.hubAsset.symbol} (${entry.assetAddress})
|
|
36346
|
-
balanceResult
|
|
36645
|
+
this.config.logger.warn(
|
|
36646
|
+
`[PartnerFeeClaimService] Unexpected balance result format for ${entry.hubAsset.symbol} (${entry.assetAddress})`,
|
|
36647
|
+
{ balanceResult }
|
|
36347
36648
|
);
|
|
36348
36649
|
balance = 0n;
|
|
36349
36650
|
}
|
|
@@ -36679,7 +36980,8 @@ var PartnerFeeClaimService = class {
|
|
|
36679
36980
|
}
|
|
36680
36981
|
const solverExecutionResponse = await SolverApiService.postExecution(
|
|
36681
36982
|
{ intent_tx_hash: intentTxHash },
|
|
36682
|
-
this.config.solver
|
|
36983
|
+
this.config.solver,
|
|
36984
|
+
this.config.logger
|
|
36683
36985
|
);
|
|
36684
36986
|
if (!solverExecutionResponse.ok) {
|
|
36685
36987
|
return solverExecutionResponse;
|
|
@@ -36760,7 +37062,12 @@ var RecoveryService = class {
|
|
|
36760
37062
|
srcChainKey: chainKey,
|
|
36761
37063
|
field: "chainKey"
|
|
36762
37064
|
});
|
|
36763
|
-
const
|
|
37065
|
+
const leverageVaultAddresses = new Set(
|
|
37066
|
+
this.config.sodaxConfig.leverageYield.vaults.map((vault) => vault.vault.toLowerCase())
|
|
37067
|
+
);
|
|
37068
|
+
const entries = Object.values(chainConfig.supportedTokens).filter(
|
|
37069
|
+
(token) => viem.isAddress(token.hubAsset) && !leverageVaultAddresses.has(token.hubAsset.toLowerCase())
|
|
37070
|
+
);
|
|
36764
37071
|
if (entries.length === 0) {
|
|
36765
37072
|
return { ok: true, value: [] };
|
|
36766
37073
|
}
|
|
@@ -36862,6 +37169,871 @@ var RecoveryService = class {
|
|
|
36862
37169
|
}
|
|
36863
37170
|
};
|
|
36864
37171
|
|
|
37172
|
+
// src/leverageYield/errors.ts
|
|
37173
|
+
var leverageYieldInvariant = createInvariant("leverageYield");
|
|
37174
|
+
var LEVERAGE_YIELD_CODES = /* @__PURE__ */ new Set([
|
|
37175
|
+
"VALIDATION_FAILED",
|
|
37176
|
+
"INTENT_CREATION_FAILED",
|
|
37177
|
+
"APPROVE_FAILED",
|
|
37178
|
+
"ALLOWANCE_CHECK_FAILED",
|
|
37179
|
+
"LOOKUP_FAILED",
|
|
37180
|
+
"TX_VERIFICATION_FAILED",
|
|
37181
|
+
"TX_SUBMIT_FAILED",
|
|
37182
|
+
"RELAY_TIMEOUT",
|
|
37183
|
+
"RELAY_FAILED",
|
|
37184
|
+
"EXECUTION_FAILED",
|
|
37185
|
+
"EXTERNAL_API_ERROR",
|
|
37186
|
+
"UNKNOWN"
|
|
37187
|
+
]);
|
|
37188
|
+
var LEVERAGE_YIELD_POST_EXECUTION_CODES = /* @__PURE__ */ new Set([
|
|
37189
|
+
"EXECUTION_FAILED",
|
|
37190
|
+
"EXTERNAL_API_ERROR",
|
|
37191
|
+
"UNKNOWN"
|
|
37192
|
+
]);
|
|
37193
|
+
var LEVERAGE_YIELD_SWAP_CODES = /* @__PURE__ */ new Set([
|
|
37194
|
+
"VALIDATION_FAILED",
|
|
37195
|
+
"INTENT_CREATION_FAILED",
|
|
37196
|
+
"TX_VERIFICATION_FAILED",
|
|
37197
|
+
"TX_SUBMIT_FAILED",
|
|
37198
|
+
"RELAY_TIMEOUT",
|
|
37199
|
+
"RELAY_FAILED",
|
|
37200
|
+
"EXECUTION_FAILED",
|
|
37201
|
+
"EXTERNAL_API_ERROR",
|
|
37202
|
+
"UNKNOWN"
|
|
37203
|
+
]);
|
|
37204
|
+
var isLeverageYieldCreateIntentError = isCodeMember(CREATE_INTENT_CODES);
|
|
37205
|
+
var isLeverageYieldApproveError = isCodeMember(APPROVE_CODES);
|
|
37206
|
+
var isLeverageYieldAllowanceCheckError = isCodeMember(ALLOWANCE_CHECK_CODES);
|
|
37207
|
+
var isLeverageYieldLookupError = isCodeMember(LOOKUP_CODES);
|
|
37208
|
+
var isLeverageYieldPostExecutionError = isCodeMember(
|
|
37209
|
+
LEVERAGE_YIELD_POST_EXECUTION_CODES
|
|
37210
|
+
);
|
|
37211
|
+
var isLeverageYieldSwapError = isCodeMember(LEVERAGE_YIELD_SWAP_CODES);
|
|
37212
|
+
var isLeverageYieldError = isCodeMember(LEVERAGE_YIELD_CODES);
|
|
37213
|
+
|
|
37214
|
+
// src/leverageYield/LeverageYieldService.ts
|
|
37215
|
+
var leverageYieldVaultAbi = viem.parseAbi([
|
|
37216
|
+
"function asset() view returns (address)",
|
|
37217
|
+
"function getPositionDetails() view returns (uint256 collateral, uint256 debt, uint256 ltv, uint256 healthFactor, uint256 idleAsset)",
|
|
37218
|
+
// Used by getApr() — read-only metadata that doesn't change per pass.
|
|
37219
|
+
"function pool() view returns (address)",
|
|
37220
|
+
"function borrowToken() view returns (address)",
|
|
37221
|
+
"function targetLTV() view returns (uint256)"
|
|
37222
|
+
]);
|
|
37223
|
+
var INTENT_DEADLINE_BUFFER_SECONDS = 5 * 60;
|
|
37224
|
+
var ANY_SOLVER_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
37225
|
+
var MAX_WITHDRAW_DUST_BUFFER = 1000n;
|
|
37226
|
+
function pctToRay(aprPct) {
|
|
37227
|
+
return BigInt(Math.round(aprPct * 1e9)) * 10n ** 16n;
|
|
37228
|
+
}
|
|
37229
|
+
var DEFILLAMA_FETCH_TIMEOUT_MS = 1e4;
|
|
37230
|
+
var DEFILLAMA_FETCH_MAX_ATTEMPTS = 2;
|
|
37231
|
+
async function fetchWithTimeout(url, timeoutMs) {
|
|
37232
|
+
const controller = new AbortController();
|
|
37233
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
37234
|
+
try {
|
|
37235
|
+
return await fetch(url, { signal: controller.signal });
|
|
37236
|
+
} finally {
|
|
37237
|
+
clearTimeout(timeoutId);
|
|
37238
|
+
}
|
|
37239
|
+
}
|
|
37240
|
+
async function fetchDefillamaApr(poolId) {
|
|
37241
|
+
return retry(async () => {
|
|
37242
|
+
const response = await fetchWithTimeout(`https://yields.llama.fi/chart/${poolId}`, DEFILLAMA_FETCH_TIMEOUT_MS);
|
|
37243
|
+
if (!response.ok) throw new Error(`DefiLlama APR fetch failed: HTTP ${response.status}`);
|
|
37244
|
+
const json = await response.json();
|
|
37245
|
+
const series = json?.data;
|
|
37246
|
+
if (!Array.isArray(series) || series.length === 0) {
|
|
37247
|
+
throw new Error("DefiLlama APR response: empty time series");
|
|
37248
|
+
}
|
|
37249
|
+
const latest = series[series.length - 1]?.apy;
|
|
37250
|
+
if (typeof latest !== "number") throw new Error("DefiLlama APR: latest entry missing numeric apy");
|
|
37251
|
+
return latest;
|
|
37252
|
+
}, DEFILLAMA_FETCH_MAX_ATTEMPTS);
|
|
37253
|
+
}
|
|
37254
|
+
var LeverageYieldService = class {
|
|
37255
|
+
hubProvider;
|
|
37256
|
+
config;
|
|
37257
|
+
spoke;
|
|
37258
|
+
constructor({ hubProvider, config, spoke }) {
|
|
37259
|
+
this.hubProvider = hubProvider;
|
|
37260
|
+
this.config = config;
|
|
37261
|
+
this.spoke = spoke;
|
|
37262
|
+
}
|
|
37263
|
+
// ─── Registry ──────────────────────────────────────────────────────────
|
|
37264
|
+
/** Returns the static registry of known leverage-yield vaults. */
|
|
37265
|
+
listVaults() {
|
|
37266
|
+
return this.config.sodaxConfig.leverageYield.vaults;
|
|
37267
|
+
}
|
|
37268
|
+
/** Looks up a vault by its `name` field. Returns `undefined` when not registered. */
|
|
37269
|
+
getVault(name) {
|
|
37270
|
+
return this.listVaults().find((v) => v.name === name);
|
|
37271
|
+
}
|
|
37272
|
+
/**
|
|
37273
|
+
* Looks up a registered vault by its on-chain proxy address (case-insensitive).
|
|
37274
|
+
* Returns `undefined` when the address isn't in the registry.
|
|
37275
|
+
*/
|
|
37276
|
+
getVaultByAddress(address) {
|
|
37277
|
+
const normalized = address.toLowerCase();
|
|
37278
|
+
return this.listVaults().find((v) => v.vault.toLowerCase() === normalized);
|
|
37279
|
+
}
|
|
37280
|
+
/**
|
|
37281
|
+
* Resolves the intent `deadline`: returns the caller-supplied value verbatim, otherwise
|
|
37282
|
+
* derives a default from the hub-chain (Sonic) block timestamp plus
|
|
37283
|
+
* {@link INTENT_DEADLINE_BUFFER_SECONDS}. The deadline is enforced on-chain against the hub
|
|
37284
|
+
* block time, so the default is anchored to that block — never the client clock, which can
|
|
37285
|
+
* drift and produce an already-expired or over-extended deadline.
|
|
37286
|
+
*
|
|
37287
|
+
* Returns a {@link Result}: a `getBlock` RPC outage is a read failure, surfaced as
|
|
37288
|
+
* `LOOKUP_FAILED` (`method: 'resolveDeadline'`) rather than masquerading as an intent-build
|
|
37289
|
+
* failure. The `deposit` / `withdraw` callers forward it verbatim.
|
|
37290
|
+
*/
|
|
37291
|
+
async resolveDeadline(deadline, srcChainKey) {
|
|
37292
|
+
if (deadline !== void 0) return { ok: true, value: deadline };
|
|
37293
|
+
try {
|
|
37294
|
+
const block = await this.hubProvider.publicClient.getBlock({
|
|
37295
|
+
includeTransactions: false,
|
|
37296
|
+
blockTag: "latest"
|
|
37297
|
+
});
|
|
37298
|
+
return { ok: true, value: block.timestamp + BigInt(INTENT_DEADLINE_BUFFER_SECONDS) };
|
|
37299
|
+
} catch (error) {
|
|
37300
|
+
return { ok: false, error: lookupFailed("leverageYield", "resolveDeadline", error, { srcChainKey }) };
|
|
37301
|
+
}
|
|
37302
|
+
}
|
|
37303
|
+
/**
|
|
37304
|
+
* Builds the {@link LeverageYieldSwapPayload} for a leverage-yield deposit (any token → lsoda*).
|
|
37305
|
+
* The lsoda* output is delivered to the user's hub wallet on Sonic so a later
|
|
37306
|
+
* {@link LeverageYieldService.withdraw} can swap it back. Spread the result into
|
|
37307
|
+
* {@link LeverageYieldService.vaultSwap}: `vaultSwap({ ...payload, walletProvider })`.
|
|
37308
|
+
* An optional `partnerFee` is forwarded on the payload as the per-intent fee override.
|
|
37309
|
+
*
|
|
37310
|
+
* For `minOutputAmount`, quote via `sodax.swaps.getQuote` with the vault address as the
|
|
37311
|
+
* destination token (`token_dst`) — vault shares are solver-tradeable, so the generic swap
|
|
37312
|
+
* quote applies; then subtract your slippage tolerance.
|
|
37313
|
+
*/
|
|
37314
|
+
async deposit(params) {
|
|
37315
|
+
const baseCtx = { srcChainKey: params.srcChainKey, action: "deposit" };
|
|
37316
|
+
try {
|
|
37317
|
+
leverageYieldInvariant(params.inputAmount > 0n, "inputAmount must be greater than 0", {
|
|
37318
|
+
...baseCtx,
|
|
37319
|
+
field: "inputAmount"
|
|
37320
|
+
});
|
|
37321
|
+
leverageYieldInvariant(params.vault.length > 0, "Vault address is required", { ...baseCtx, field: "vault" });
|
|
37322
|
+
leverageYieldInvariant(params.inputToken.length > 0, "inputToken is required", {
|
|
37323
|
+
...baseCtx,
|
|
37324
|
+
field: "inputToken"
|
|
37325
|
+
});
|
|
37326
|
+
const hubWallet = await this.hubProvider.getUserHubWalletAddress(params.srcAddress, params.srcChainKey);
|
|
37327
|
+
const deadlineResult = await this.resolveDeadline(params.deadline, params.srcChainKey);
|
|
37328
|
+
if (!deadlineResult.ok) return deadlineResult;
|
|
37329
|
+
const deadline = deadlineResult.value;
|
|
37330
|
+
return {
|
|
37331
|
+
ok: true,
|
|
37332
|
+
value: {
|
|
37333
|
+
params: {
|
|
37334
|
+
inputToken: params.inputToken,
|
|
37335
|
+
outputToken: params.vault,
|
|
37336
|
+
inputAmount: params.inputAmount,
|
|
37337
|
+
minOutputAmount: params.minOutputAmount,
|
|
37338
|
+
deadline,
|
|
37339
|
+
allowPartialFill: false,
|
|
37340
|
+
srcChainKey: params.srcChainKey,
|
|
37341
|
+
dstChainKey: this.hubProvider.chainConfig.chain.key,
|
|
37342
|
+
srcAddress: params.srcAddress,
|
|
37343
|
+
dstAddress: hubWallet,
|
|
37344
|
+
solver: params.solver ?? ANY_SOLVER_ADDRESS,
|
|
37345
|
+
data: "0x"
|
|
37346
|
+
},
|
|
37347
|
+
// Per-intent fee override — only included when the caller supplies one, so the
|
|
37348
|
+
// payload stays free of undefined-valued keys.
|
|
37349
|
+
...params.partnerFee !== void 0 && { partnerFee: params.partnerFee }
|
|
37350
|
+
}
|
|
37351
|
+
};
|
|
37352
|
+
} catch (error) {
|
|
37353
|
+
if (isLeverageYieldCreateIntentError(error)) return { ok: false, error };
|
|
37354
|
+
return { ok: false, error: intentCreationFailed("leverageYield", error, baseCtx) };
|
|
37355
|
+
}
|
|
37356
|
+
}
|
|
37357
|
+
/**
|
|
37358
|
+
* Builds the {@link LeverageYieldSwapPayload} for a leverage-yield withdraw (lsoda* → any
|
|
37359
|
+
* token). The payload carries `hubWalletSwap: true` — {@link LeverageYieldService.vaultSwap}
|
|
37360
|
+
* then spends the lsoda* held in the user's hub wallet by authorising it via a
|
|
37361
|
+
* `Connection.sendMessage` the user signs on `srcChainKey`. Wrapped in a {@link Result} for
|
|
37362
|
+
* a call shape uniform with {@link LeverageYieldService.deposit}; async because the default
|
|
37363
|
+
* `deadline` is read from the hub block timestamp.
|
|
37364
|
+
*
|
|
37365
|
+
* For `minOutputAmount`, quote via `sodax.swaps.getQuote` with the vault address as the
|
|
37366
|
+
* source token (`token_src`) — vault shares are solver-tradeable, so the generic swap quote
|
|
37367
|
+
* applies; then subtract your slippage tolerance.
|
|
37368
|
+
*/
|
|
37369
|
+
async withdraw(params) {
|
|
37370
|
+
const baseCtx = { srcChainKey: params.srcChainKey, action: "withdraw" };
|
|
37371
|
+
try {
|
|
37372
|
+
leverageYieldInvariant(params.inputAmount > 0n, "inputAmount must be greater than 0", {
|
|
37373
|
+
...baseCtx,
|
|
37374
|
+
field: "inputAmount"
|
|
37375
|
+
});
|
|
37376
|
+
leverageYieldInvariant(params.vault.length > 0, "Vault address is required", { ...baseCtx, field: "vault" });
|
|
37377
|
+
leverageYieldInvariant(params.outputToken.length > 0, "outputToken is required", {
|
|
37378
|
+
...baseCtx,
|
|
37379
|
+
field: "outputToken"
|
|
37380
|
+
});
|
|
37381
|
+
const deadlineResult = await this.resolveDeadline(params.deadline, params.srcChainKey);
|
|
37382
|
+
if (!deadlineResult.ok) return deadlineResult;
|
|
37383
|
+
const deadline = deadlineResult.value;
|
|
37384
|
+
return {
|
|
37385
|
+
ok: true,
|
|
37386
|
+
value: {
|
|
37387
|
+
params: {
|
|
37388
|
+
inputToken: params.vault,
|
|
37389
|
+
outputToken: params.outputToken,
|
|
37390
|
+
inputAmount: params.inputAmount,
|
|
37391
|
+
minOutputAmount: params.minOutputAmount,
|
|
37392
|
+
deadline,
|
|
37393
|
+
allowPartialFill: false,
|
|
37394
|
+
srcChainKey: params.srcChainKey,
|
|
37395
|
+
dstChainKey: params.dstChainKey,
|
|
37396
|
+
srcAddress: params.srcAddress,
|
|
37397
|
+
dstAddress: params.recipient ?? params.srcAddress,
|
|
37398
|
+
solver: params.solver ?? ANY_SOLVER_ADDRESS,
|
|
37399
|
+
data: "0x"
|
|
37400
|
+
},
|
|
37401
|
+
hubWalletSwap: true
|
|
37402
|
+
}
|
|
37403
|
+
};
|
|
37404
|
+
} catch (error) {
|
|
37405
|
+
if (isLeverageYieldCreateIntentError(error)) return { ok: false, error };
|
|
37406
|
+
return { ok: false, error: intentCreationFailed("leverageYield", error, baseCtx) };
|
|
37407
|
+
}
|
|
37408
|
+
}
|
|
37409
|
+
/**
|
|
37410
|
+
* Creates a vault swap intent on the user's source spoke chain without submitting it to
|
|
37411
|
+
* the solver. Leverage-yield copy of the swap domain's `createIntent`, specialised for
|
|
37412
|
+
* vault flows — duplicated deliberately so the vault-specific execution modifiers
|
|
37413
|
+
* (`hubWalletSwap`, per-intent `partnerFee`) stay off the generic swap surface.
|
|
37414
|
+
*
|
|
37415
|
+
* Use {@link LeverageYieldService.vaultSwap} for the full end-to-end flow
|
|
37416
|
+
* (create → relay → notify solver); use this directly when you need the raw transaction
|
|
37417
|
+
* or drive the relay yourself (e.g. the backend submit-tx path). To complete a manual flow,
|
|
37418
|
+
* relay the returned `relayData` (the shared `relayTxAndWaitPacket` helper) and then call
|
|
37419
|
+
* {@link LeverageYieldService.notifySolver} with the hub-side intent tx hash.
|
|
37420
|
+
*
|
|
37421
|
+
* @param _params - Intent parameters, source chain key, wallet provider (when `raw: false`),
|
|
37422
|
+
* and optional `skipSimulation` / `hubWalletSwap` / `partnerFee`.
|
|
37423
|
+
* @returns A `Result<CreateVaultIntentResult<K, Raw>, LeverageYieldCreateIntentError>`.
|
|
37424
|
+
* On success contains:
|
|
37425
|
+
* - `tx` — chain-specific tx hash (executed) or raw tx data (raw mode).
|
|
37426
|
+
* - `intent` — the fully constructed `Intent` object augmented with `feeAmount`.
|
|
37427
|
+
* - `relayData` — `{ address, payload }` needed to submit the intent to the relayer.
|
|
37428
|
+
*
|
|
37429
|
+
* On failure `result.error` is a SodaxError with `VALIDATION_FAILED` (invariant
|
|
37430
|
+
* precondition), `INTENT_CREATION_FAILED` (spoke-side creation/deposit failed) or
|
|
37431
|
+
* `UNKNOWN` (defensive fallback).
|
|
37432
|
+
*/
|
|
37433
|
+
async createVaultIntent(_params) {
|
|
37434
|
+
const { params, skipSimulation, hubWalletSwap, partnerFee = this.config.swaps.partnerFee } = _params;
|
|
37435
|
+
const baseCtx = { srcChainKey: params.srcChainKey, dstChainKey: params.dstChainKey };
|
|
37436
|
+
try {
|
|
37437
|
+
leverageYieldInvariant(
|
|
37438
|
+
isUndefinedOrValidWalletProviderForChainKey(params.srcChainKey, _params.walletProvider),
|
|
37439
|
+
`Invalid wallet provider for chain key: ${params.srcChainKey}`,
|
|
37440
|
+
baseCtx
|
|
37441
|
+
);
|
|
37442
|
+
const hubChainKey = this.hubProvider.chainConfig.chain.key;
|
|
37443
|
+
const inputTokenChainKey = hubWalletSwap ? hubChainKey : params.srcChainKey;
|
|
37444
|
+
leverageYieldInvariant(
|
|
37445
|
+
this.config.isValidOriginalAssetAddress(inputTokenChainKey, params.inputToken),
|
|
37446
|
+
`Unsupported spoke chain token (srcChainKey: ${inputTokenChainKey}, inputToken: ${params.inputToken})`,
|
|
37447
|
+
{ ...baseCtx, field: "inputToken" }
|
|
37448
|
+
);
|
|
37449
|
+
leverageYieldInvariant(
|
|
37450
|
+
this.config.isValidOriginalAssetAddress(params.dstChainKey, params.outputToken),
|
|
37451
|
+
`Unsupported spoke chain token (params.dstChain: ${params.dstChainKey}, params.outputToken: ${params.outputToken})`,
|
|
37452
|
+
{ ...baseCtx, field: "outputToken" }
|
|
37453
|
+
);
|
|
37454
|
+
leverageYieldInvariant(
|
|
37455
|
+
this.config.isValidSpokeChainKey(params.srcChainKey),
|
|
37456
|
+
`Invalid spoke chain (srcChainKey): ${params.srcChainKey}`,
|
|
37457
|
+
{ ...baseCtx, field: "srcChainKey" }
|
|
37458
|
+
);
|
|
37459
|
+
leverageYieldInvariant(
|
|
37460
|
+
this.config.isValidSpokeChainKey(params.dstChainKey),
|
|
37461
|
+
`Invalid spoke chain (params.dstChain): ${params.dstChainKey}`,
|
|
37462
|
+
{ ...baseCtx, field: "dstChainKey" }
|
|
37463
|
+
);
|
|
37464
|
+
if (isBitcoinChainKey(params.dstChainKey) && params.outputToken === "BTC") {
|
|
37465
|
+
leverageYieldInvariant(
|
|
37466
|
+
params.minOutputAmount >= 546n,
|
|
37467
|
+
`Invalid minOutputAmount (params.minOutputAmount): ${params.minOutputAmount}`,
|
|
37468
|
+
{ ...baseCtx, field: "minOutputAmount" }
|
|
37469
|
+
);
|
|
37470
|
+
}
|
|
37471
|
+
const personalAddress = params.srcAddress;
|
|
37472
|
+
let walletAddress = personalAddress;
|
|
37473
|
+
if (isBitcoinChainKeyType(params.srcChainKey) && _params.raw === false) {
|
|
37474
|
+
leverageYieldInvariant(
|
|
37475
|
+
isBitcoinWalletProviderType(_params.walletProvider),
|
|
37476
|
+
`Invalid wallet provider for chain key: ${params.srcChainKey}`,
|
|
37477
|
+
baseCtx
|
|
37478
|
+
);
|
|
37479
|
+
walletAddress = await this.spoke.bitcoin.getEffectiveWalletAddress(personalAddress);
|
|
37480
|
+
await this.spoke.bitcoin.radfi.ensureRadfiAccessToken(_params.walletProvider);
|
|
37481
|
+
}
|
|
37482
|
+
const creatorHubWalletAddress = await this.hubProvider.getUserHubWalletAddress(walletAddress, params.srcChainKey);
|
|
37483
|
+
if (hubWalletSwap) {
|
|
37484
|
+
const [data2, intent2, feeAmount2] = EvmSolverService.constructCreateIntentData(
|
|
37485
|
+
{ ...params, srcChainKey: hubChainKey, srcAddress: creatorHubWalletAddress },
|
|
37486
|
+
creatorHubWalletAddress,
|
|
37487
|
+
this.config,
|
|
37488
|
+
partnerFee
|
|
37489
|
+
);
|
|
37490
|
+
const coreSendMessageParams = {
|
|
37491
|
+
srcChainKey: params.srcChainKey,
|
|
37492
|
+
// Personal address — NOT the resolved trading `walletAddress`. SpokeService.sendMessage
|
|
37493
|
+
// re-resolves the effective (Bitcoin trading) address itself; passing the already-resolved
|
|
37494
|
+
// trading address here would double-resolve it (getTradingWallet(tradingAddress) →
|
|
37495
|
+
// "Trading wallet not found"). The trading address is used only for the hub-wallet
|
|
37496
|
+
// derivation above (creatorHubWalletAddress). Mirrors MoneyMarketService borrow/withdraw.
|
|
37497
|
+
srcAddress: personalAddress,
|
|
37498
|
+
dstChainKey: hubChainKey,
|
|
37499
|
+
dstAddress: creatorHubWalletAddress,
|
|
37500
|
+
payload: data2,
|
|
37501
|
+
skipSimulation
|
|
37502
|
+
};
|
|
37503
|
+
const txResult2 = await this.spoke.sendMessage(
|
|
37504
|
+
_params.raw ? { ...coreSendMessageParams, raw: true } : {
|
|
37505
|
+
...coreSendMessageParams,
|
|
37506
|
+
raw: false,
|
|
37507
|
+
walletProvider: _params.walletProvider
|
|
37508
|
+
}
|
|
37509
|
+
);
|
|
37510
|
+
if (!txResult2.ok) {
|
|
37511
|
+
if (isLeverageYieldCreateIntentError(txResult2.error)) {
|
|
37512
|
+
return { ok: false, error: txResult2.error };
|
|
37513
|
+
}
|
|
37514
|
+
return { ok: false, error: intentCreationFailed("leverageYield", txResult2.error, baseCtx) };
|
|
37515
|
+
}
|
|
37516
|
+
return {
|
|
37517
|
+
ok: true,
|
|
37518
|
+
value: {
|
|
37519
|
+
tx: txResult2.value,
|
|
37520
|
+
intent: { ...intent2, feeAmount: feeAmount2 },
|
|
37521
|
+
relayData: { address: creatorHubWalletAddress, payload: data2 }
|
|
37522
|
+
}
|
|
37523
|
+
};
|
|
37524
|
+
}
|
|
37525
|
+
if (isHubChainKeyType(params.srcChainKey) && isSonicChainKeyType(params.srcChainKey)) {
|
|
37526
|
+
const coreSonicParams = {
|
|
37527
|
+
createIntentParams: params,
|
|
37528
|
+
creatorHubWalletAddress,
|
|
37529
|
+
solverConfig: this.config.solver,
|
|
37530
|
+
fee: partnerFee,
|
|
37531
|
+
hubProvider: this.hubProvider
|
|
37532
|
+
};
|
|
37533
|
+
const [txResult2, intent2, feeAmount2, data2] = await SonicSpokeService.createSwapIntent(
|
|
37534
|
+
_params.raw ? { ...coreSonicParams, raw: true } : {
|
|
37535
|
+
...coreSonicParams,
|
|
37536
|
+
raw: false,
|
|
37537
|
+
walletProvider: _params.walletProvider
|
|
37538
|
+
}
|
|
37539
|
+
);
|
|
37540
|
+
return {
|
|
37541
|
+
ok: true,
|
|
37542
|
+
value: {
|
|
37543
|
+
tx: txResult2,
|
|
37544
|
+
intent: { ...intent2, feeAmount: feeAmount2 },
|
|
37545
|
+
relayData: { address: intent2.creator, payload: data2 }
|
|
37546
|
+
}
|
|
37547
|
+
};
|
|
37548
|
+
}
|
|
37549
|
+
const [data, intent, feeAmount] = EvmSolverService.constructCreateIntentData(
|
|
37550
|
+
{
|
|
37551
|
+
...params,
|
|
37552
|
+
srcAddress: walletAddress
|
|
37553
|
+
},
|
|
37554
|
+
creatorHubWalletAddress,
|
|
37555
|
+
this.config,
|
|
37556
|
+
partnerFee
|
|
37557
|
+
);
|
|
37558
|
+
const coreDepositParams = {
|
|
37559
|
+
srcChainKey: params.srcChainKey,
|
|
37560
|
+
srcAddress: walletAddress,
|
|
37561
|
+
to: creatorHubWalletAddress,
|
|
37562
|
+
token: params.inputToken,
|
|
37563
|
+
amount: params.inputAmount,
|
|
37564
|
+
data,
|
|
37565
|
+
skipSimulation
|
|
37566
|
+
};
|
|
37567
|
+
const txResult = await this.spoke.deposit(
|
|
37568
|
+
_params.raw ? {
|
|
37569
|
+
...coreDepositParams,
|
|
37570
|
+
raw: true
|
|
37571
|
+
} : {
|
|
37572
|
+
...coreDepositParams,
|
|
37573
|
+
raw: false,
|
|
37574
|
+
walletProvider: _params.walletProvider
|
|
37575
|
+
}
|
|
37576
|
+
);
|
|
37577
|
+
if (!txResult.ok) {
|
|
37578
|
+
if (isLeverageYieldCreateIntentError(txResult.error)) {
|
|
37579
|
+
return { ok: false, error: txResult.error };
|
|
37580
|
+
}
|
|
37581
|
+
return {
|
|
37582
|
+
ok: false,
|
|
37583
|
+
error: intentCreationFailed("leverageYield", txResult.error, baseCtx)
|
|
37584
|
+
};
|
|
37585
|
+
}
|
|
37586
|
+
return {
|
|
37587
|
+
ok: true,
|
|
37588
|
+
value: {
|
|
37589
|
+
tx: txResult.value,
|
|
37590
|
+
intent: { ...intent, feeAmount },
|
|
37591
|
+
relayData: { address: intent.creator, payload: data }
|
|
37592
|
+
}
|
|
37593
|
+
};
|
|
37594
|
+
} catch (error) {
|
|
37595
|
+
if (isLeverageYieldCreateIntentError(error)) return { ok: false, error };
|
|
37596
|
+
return {
|
|
37597
|
+
ok: false,
|
|
37598
|
+
error: intentCreationFailed("leverageYield", error, baseCtx)
|
|
37599
|
+
};
|
|
37600
|
+
}
|
|
37601
|
+
}
|
|
37602
|
+
/**
|
|
37603
|
+
* Executes a full end-to-end leverage-yield vault swap (deposit or withdraw).
|
|
37604
|
+
* Leverage-yield copy of the swap domain's `swap()` orchestrator:
|
|
37605
|
+
* 1. Calls {@link LeverageYieldService.createVaultIntent} to submit the intent
|
|
37606
|
+
* transaction on the source spoke chain.
|
|
37607
|
+
* 2. Verifies the spoke transaction landed on-chain.
|
|
37608
|
+
* 3. For non-hub source chains: submits the spoke tx to the relayer and waits for the
|
|
37609
|
+
* relay packet to land on the hub (Sonic). Skipped when `srcChainKey` is the hub.
|
|
37610
|
+
* 4. Notifies the solver, triggering it to fill the intent.
|
|
37611
|
+
*
|
|
37612
|
+
* Spread a {@link LeverageYieldSwapPayload} from `deposit` / `withdraw` into this method
|
|
37613
|
+
* alongside the wallet provider: `vaultSwap({ ...payload, walletProvider })`.
|
|
37614
|
+
*
|
|
37615
|
+
* @returns A `Result<VaultSwapResponse, LeverageYieldSwapError>`. On success:
|
|
37616
|
+
* - `solverExecutionResponse` — solver acknowledgement (`{ answer: 'OK', intent_hash }`).
|
|
37617
|
+
* - `intent` — the on-chain intent object that was created.
|
|
37618
|
+
* - `intentDeliveryInfo` — source/destination chain keys, tx hashes, and user addresses.
|
|
37619
|
+
*
|
|
37620
|
+
* On failure `result.error` carries one of the create-intent codes plus
|
|
37621
|
+
* `TX_VERIFICATION_FAILED`, `TX_SUBMIT_FAILED`, `RELAY_TIMEOUT`, `RELAY_FAILED`,
|
|
37622
|
+
* `EXECUTION_FAILED`, `EXTERNAL_API_ERROR` or `UNKNOWN`.
|
|
37623
|
+
*/
|
|
37624
|
+
async vaultSwap(_params) {
|
|
37625
|
+
const { params } = _params;
|
|
37626
|
+
const srcChainKey = params.srcChainKey;
|
|
37627
|
+
const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey, action: "vaultSwap" };
|
|
37628
|
+
try {
|
|
37629
|
+
const timeout = _params.timeout;
|
|
37630
|
+
const createIntentResult = await this.createVaultIntent(_params);
|
|
37631
|
+
if (!createIntentResult.ok) {
|
|
37632
|
+
return { ok: false, error: createIntentResult.error };
|
|
37633
|
+
}
|
|
37634
|
+
const { tx: spokeTxHash, intent, relayData } = createIntentResult.value;
|
|
37635
|
+
const verifyTxHashResult = await this.spoke.verifyTxHash({
|
|
37636
|
+
txHash: spokeTxHash,
|
|
37637
|
+
chainKey: srcChainKey
|
|
37638
|
+
});
|
|
37639
|
+
if (!verifyTxHashResult.ok) {
|
|
37640
|
+
return {
|
|
37641
|
+
ok: false,
|
|
37642
|
+
error: verifyFailed("leverageYield", verifyTxHashResult.error, baseCtx)
|
|
37643
|
+
};
|
|
37644
|
+
}
|
|
37645
|
+
let dstIntentTxHash;
|
|
37646
|
+
if (isHubChainKeyType(srcChainKey)) {
|
|
37647
|
+
dstIntentTxHash = spokeTxHash;
|
|
37648
|
+
} else {
|
|
37649
|
+
const packet = await relayTxAndWaitPacket({
|
|
37650
|
+
srcTxHash: spokeTxHash,
|
|
37651
|
+
data: relayData,
|
|
37652
|
+
chainKey: srcChainKey,
|
|
37653
|
+
relayerApiEndpoint: this.config.relay.relayerApiEndpoint,
|
|
37654
|
+
timeout
|
|
37655
|
+
});
|
|
37656
|
+
if (!packet.ok) {
|
|
37657
|
+
return {
|
|
37658
|
+
ok: false,
|
|
37659
|
+
error: mapRelayFailure(packet.error, { feature: "leverageYield", ...baseCtx })
|
|
37660
|
+
};
|
|
37661
|
+
}
|
|
37662
|
+
dstIntentTxHash = packet.value.dst_tx_hash;
|
|
37663
|
+
}
|
|
37664
|
+
const postExecResult = await this.notifySolver({
|
|
37665
|
+
intent_tx_hash: dstIntentTxHash
|
|
37666
|
+
});
|
|
37667
|
+
if (!postExecResult.ok) {
|
|
37668
|
+
return { ok: false, error: postExecResult.error };
|
|
37669
|
+
}
|
|
37670
|
+
return {
|
|
37671
|
+
ok: true,
|
|
37672
|
+
value: {
|
|
37673
|
+
solverExecutionResponse: postExecResult.value,
|
|
37674
|
+
intent,
|
|
37675
|
+
intentDeliveryInfo: {
|
|
37676
|
+
srcChainKey,
|
|
37677
|
+
srcTxHash: spokeTxHash,
|
|
37678
|
+
srcAddress: params.srcAddress,
|
|
37679
|
+
dstChainKey: params.dstChainKey,
|
|
37680
|
+
dstTxHash: dstIntentTxHash,
|
|
37681
|
+
dstAddress: params.dstAddress
|
|
37682
|
+
}
|
|
37683
|
+
}
|
|
37684
|
+
};
|
|
37685
|
+
} catch (error) {
|
|
37686
|
+
if (isLeverageYieldSwapError(error)) return { ok: false, error };
|
|
37687
|
+
return {
|
|
37688
|
+
ok: false,
|
|
37689
|
+
error: unknownFailed("leverageYield", error, baseCtx)
|
|
37690
|
+
};
|
|
37691
|
+
}
|
|
37692
|
+
}
|
|
37693
|
+
/**
|
|
37694
|
+
* Notifies the solver that the vault intent landed on the hub, triggering it to fill.
|
|
37695
|
+
* Leverage-yield copy of the swap domain's `postExecution` — emits only
|
|
37696
|
+
* `EXECUTION_FAILED` / `EXTERNAL_API_ERROR` / `UNKNOWN` (relay/verify codes appear only
|
|
37697
|
+
* on {@link LeverageYieldService.vaultSwap}, which owns the verify + relay steps).
|
|
37698
|
+
*
|
|
37699
|
+
* Called automatically by {@link LeverageYieldService.vaultSwap} after the relay packet
|
|
37700
|
+
* lands on the hub. Public so callers who created the intent via
|
|
37701
|
+
* {@link LeverageYieldService.createVaultIntent} and relayed it themselves can finish the
|
|
37702
|
+
* flow manually.
|
|
37703
|
+
*
|
|
37704
|
+
* @param request - `{ intent_tx_hash }` — the hub-chain (Sonic) tx hash where the intent
|
|
37705
|
+
* was registered (the relay packet's `dst_tx_hash`, or the spoke tx hash for hub-sourced
|
|
37706
|
+
* intents).
|
|
37707
|
+
*/
|
|
37708
|
+
async notifySolver(request) {
|
|
37709
|
+
try {
|
|
37710
|
+
const result = await SolverApiService.postExecution(request, this.config.solver, this.config.logger);
|
|
37711
|
+
if (result.ok) return result;
|
|
37712
|
+
const detail = result.error?.detail ?? {
|
|
37713
|
+
code: -999,
|
|
37714
|
+
// SolverIntentErrorCode.UNKNOWN
|
|
37715
|
+
message: "Solver returned malformed error response"
|
|
37716
|
+
};
|
|
37717
|
+
return {
|
|
37718
|
+
ok: false,
|
|
37719
|
+
error: new SodaxError("EXTERNAL_API_ERROR", detail.message, {
|
|
37720
|
+
feature: "leverageYield",
|
|
37721
|
+
context: {
|
|
37722
|
+
phase: "postExecution",
|
|
37723
|
+
api: "solver",
|
|
37724
|
+
solverCode: detail.code,
|
|
37725
|
+
solverDetail: detail
|
|
37726
|
+
}
|
|
37727
|
+
})
|
|
37728
|
+
};
|
|
37729
|
+
} catch (error) {
|
|
37730
|
+
if (isLeverageYieldPostExecutionError(error)) return { ok: false, error };
|
|
37731
|
+
return { ok: false, error: executionFailed("leverageYield", error, { phase: "postExecution" }) };
|
|
37732
|
+
}
|
|
37733
|
+
}
|
|
37734
|
+
/**
|
|
37735
|
+
* Approves the vault's underlying asset to the leverage vault on Sonic. For callers
|
|
37736
|
+
* interacting with the vault directly on the hub — the swap-style {@link
|
|
37737
|
+
* LeverageYieldService.deposit} flow handles its own approvals.
|
|
37738
|
+
*/
|
|
37739
|
+
async approve(params) {
|
|
37740
|
+
const baseCtx = { action: "approve" };
|
|
37741
|
+
try {
|
|
37742
|
+
leverageYieldInvariant(params.amount > 0n, "Amount must be greater than 0", { ...baseCtx, field: "amount" });
|
|
37743
|
+
leverageYieldInvariant(params.vault.length > 0, "Vault address is required", { ...baseCtx, field: "vault" });
|
|
37744
|
+
const assetResult = await this.getAsset(params.vault);
|
|
37745
|
+
if (!assetResult.ok) {
|
|
37746
|
+
return { ok: false, error: approveFailed("leverageYield", assetResult.error, baseCtx) };
|
|
37747
|
+
}
|
|
37748
|
+
const from = await params.walletProvider.getWalletAddress();
|
|
37749
|
+
const baseApprove = {
|
|
37750
|
+
token: assetResult.value,
|
|
37751
|
+
amount: params.amount,
|
|
37752
|
+
from,
|
|
37753
|
+
spender: params.vault
|
|
37754
|
+
};
|
|
37755
|
+
if (params.raw) {
|
|
37756
|
+
const tx2 = await Erc20Service.approve({ ...baseApprove, raw: true });
|
|
37757
|
+
return { ok: true, value: tx2 };
|
|
37758
|
+
}
|
|
37759
|
+
const tx = await Erc20Service.approve({
|
|
37760
|
+
...baseApprove,
|
|
37761
|
+
raw: false,
|
|
37762
|
+
walletProvider: params.walletProvider
|
|
37763
|
+
});
|
|
37764
|
+
return { ok: true, value: tx };
|
|
37765
|
+
} catch (error) {
|
|
37766
|
+
if (isLeverageYieldApproveError(error)) return { ok: false, error };
|
|
37767
|
+
return { ok: false, error: approveFailed("leverageYield", error, baseCtx) };
|
|
37768
|
+
}
|
|
37769
|
+
}
|
|
37770
|
+
/**
|
|
37771
|
+
* Reads on-chain allowance of the vault's underlying asset for `owner → vault`. Returns
|
|
37772
|
+
* `true` when the allowance covers `amount`. Use before a direct
|
|
37773
|
+
* {@link LeverageYieldService.deposit}.
|
|
37774
|
+
*/
|
|
37775
|
+
async isAllowanceValid(params) {
|
|
37776
|
+
const baseCtx = { action: "allowanceCheck" };
|
|
37777
|
+
try {
|
|
37778
|
+
leverageYieldInvariant(params.amount > 0n, "Amount must be greater than 0", { ...baseCtx, field: "amount" });
|
|
37779
|
+
const assetResult = await this.getAsset(params.vault);
|
|
37780
|
+
if (!assetResult.ok) {
|
|
37781
|
+
return { ok: false, error: allowanceCheckFailed("leverageYield", assetResult.error, baseCtx) };
|
|
37782
|
+
}
|
|
37783
|
+
const allowance = await this.hubProvider.publicClient.readContract({
|
|
37784
|
+
address: assetResult.value,
|
|
37785
|
+
abi: viem.erc20Abi,
|
|
37786
|
+
functionName: "allowance",
|
|
37787
|
+
args: [params.owner, params.vault]
|
|
37788
|
+
});
|
|
37789
|
+
return { ok: true, value: allowance >= params.amount };
|
|
37790
|
+
} catch (error) {
|
|
37791
|
+
if (isLeverageYieldAllowanceCheckError(error)) return { ok: false, error };
|
|
37792
|
+
return { ok: false, error: allowanceCheckFailed("leverageYield", error, baseCtx) };
|
|
37793
|
+
}
|
|
37794
|
+
}
|
|
37795
|
+
// ─── Reads ──────────────────────────────────────────────────────────────
|
|
37796
|
+
/** ERC-4626 `asset()` of the vault — the sodaWEETH-style underlying. */
|
|
37797
|
+
async getAsset(vault) {
|
|
37798
|
+
try {
|
|
37799
|
+
const value = await this.hubProvider.publicClient.readContract({
|
|
37800
|
+
address: vault,
|
|
37801
|
+
abi: leverageYieldVaultAbi,
|
|
37802
|
+
functionName: "asset"
|
|
37803
|
+
});
|
|
37804
|
+
return { ok: true, value };
|
|
37805
|
+
} catch (error) {
|
|
37806
|
+
if (isLeverageYieldLookupError(error)) return { ok: false, error };
|
|
37807
|
+
return { ok: false, error: lookupFailed("leverageYield", "getAsset", error) };
|
|
37808
|
+
}
|
|
37809
|
+
}
|
|
37810
|
+
/** Reads the vault's leveraged position snapshot via the non-standard `getPositionDetails`. */
|
|
37811
|
+
async getPosition(vault) {
|
|
37812
|
+
try {
|
|
37813
|
+
const [collateral, debt, ltv, healthFactor, idleAsset] = await this.hubProvider.publicClient.readContract({
|
|
37814
|
+
address: vault,
|
|
37815
|
+
abi: leverageYieldVaultAbi,
|
|
37816
|
+
functionName: "getPositionDetails"
|
|
37817
|
+
});
|
|
37818
|
+
return { ok: true, value: { collateral, debt, ltv, healthFactor, idleAsset } };
|
|
37819
|
+
} catch (error) {
|
|
37820
|
+
if (isLeverageYieldLookupError(error)) return { ok: false, error };
|
|
37821
|
+
return { ok: false, error: lookupFailed("leverageYield", "getPosition", error) };
|
|
37822
|
+
}
|
|
37823
|
+
}
|
|
37824
|
+
/**
|
|
37825
|
+
* Computes the steady-state APR of a leverage-yield vault from the AAVE supply/borrow
|
|
37826
|
+
* rates of its asset and borrowToken, scaled by the vault's target leverage. Matches the
|
|
37827
|
+
* convention used by leveraged-LSD vaults (Origami, Gearbox, etc.) — assumes constant
|
|
37828
|
+
* LTV at `targetLTV` and constant AAVE rates.
|
|
37829
|
+
*
|
|
37830
|
+
* Returns raw fields in RAY (1e27, AAVE's native rate scale) plus the leverage multiplier
|
|
37831
|
+
* in WAD (1e18). See {@link LeverageYieldApr} for the formula and caveats.
|
|
37832
|
+
*/
|
|
37833
|
+
async getApr(vault) {
|
|
37834
|
+
try {
|
|
37835
|
+
const [pool, asset, borrowToken, targetLtvBps] = await Promise.all([
|
|
37836
|
+
this.hubProvider.publicClient.readContract({
|
|
37837
|
+
address: vault,
|
|
37838
|
+
abi: leverageYieldVaultAbi,
|
|
37839
|
+
functionName: "pool"
|
|
37840
|
+
}),
|
|
37841
|
+
this.hubProvider.publicClient.readContract({
|
|
37842
|
+
address: vault,
|
|
37843
|
+
abi: leverageYieldVaultAbi,
|
|
37844
|
+
functionName: "asset"
|
|
37845
|
+
}),
|
|
37846
|
+
this.hubProvider.publicClient.readContract({
|
|
37847
|
+
address: vault,
|
|
37848
|
+
abi: leverageYieldVaultAbi,
|
|
37849
|
+
functionName: "borrowToken"
|
|
37850
|
+
}),
|
|
37851
|
+
this.hubProvider.publicClient.readContract({
|
|
37852
|
+
address: vault,
|
|
37853
|
+
abi: leverageYieldVaultAbi,
|
|
37854
|
+
functionName: "targetLTV"
|
|
37855
|
+
})
|
|
37856
|
+
]);
|
|
37857
|
+
const [assetReserve, borrowReserve] = await Promise.all([
|
|
37858
|
+
this.hubProvider.publicClient.readContract({
|
|
37859
|
+
address: pool,
|
|
37860
|
+
abi: poolAbi,
|
|
37861
|
+
functionName: "getReserveData",
|
|
37862
|
+
args: [asset]
|
|
37863
|
+
}),
|
|
37864
|
+
this.hubProvider.publicClient.readContract({
|
|
37865
|
+
address: pool,
|
|
37866
|
+
abi: poolAbi,
|
|
37867
|
+
functionName: "getReserveData",
|
|
37868
|
+
args: [borrowToken]
|
|
37869
|
+
})
|
|
37870
|
+
]);
|
|
37871
|
+
const supplyAprRay = assetReserve.currentLiquidityRate;
|
|
37872
|
+
const borrowAprRay = borrowReserve.currentVariableBorrowRate;
|
|
37873
|
+
const BPS = 10000n;
|
|
37874
|
+
const WAD2 = 1000000000000000000n;
|
|
37875
|
+
leverageYieldInvariant(targetLtvBps < BPS, `targetLTV (${targetLtvBps}) must be < 100% (10_000 bps)`, {
|
|
37876
|
+
method: "getApr",
|
|
37877
|
+
field: "targetLtvBps"
|
|
37878
|
+
});
|
|
37879
|
+
const leverageMultiplierWad = targetLtvBps * WAD2 / (BPS - targetLtvBps);
|
|
37880
|
+
const spreadRay = supplyAprRay - borrowAprRay;
|
|
37881
|
+
const netAprRay = supplyAprRay + spreadRay * leverageMultiplierWad / WAD2;
|
|
37882
|
+
return {
|
|
37883
|
+
ok: true,
|
|
37884
|
+
value: { supplyAprRay, borrowAprRay, targetLtvBps, leverageMultiplierWad, netAprRay }
|
|
37885
|
+
};
|
|
37886
|
+
} catch (error) {
|
|
37887
|
+
if (isLeverageYieldLookupError(error)) return { ok: false, error };
|
|
37888
|
+
return { ok: false, error: lookupFailed("leverageYield", "getApr", error) };
|
|
37889
|
+
}
|
|
37890
|
+
}
|
|
37891
|
+
/**
|
|
37892
|
+
* Off-chain LSD staking-APR for the vault's underlying asset. Looks the vault up in the
|
|
37893
|
+
* registry, hits DefiLlama's per-pool chart endpoint for the configured `poolId`, and on
|
|
37894
|
+
* any fetch failure returns the registry's hardcoded `fallbackAprPct` with `stale: true`.
|
|
37895
|
+
*
|
|
37896
|
+
* Always resolves to `{ ok: true, ... }` for a known vault — the fallback path replaces
|
|
37897
|
+
* the error, since a missing LSD APR shouldn't break the parent call. Returns
|
|
37898
|
+
* `{ aprRay: 0n, stale: true, label: 'no LSD source' }` for vaults without an
|
|
37899
|
+
* `lsdSource` configured (non-LSD strategies); callers can treat that as "skip LSD".
|
|
37900
|
+
*/
|
|
37901
|
+
async getLsdApr(vault) {
|
|
37902
|
+
try {
|
|
37903
|
+
const cfg = this.getVaultByAddress(vault);
|
|
37904
|
+
const source = cfg?.lsdSource;
|
|
37905
|
+
if (!source) {
|
|
37906
|
+
return { ok: true, value: { aprRay: 0n, label: "no LSD source", stale: true } };
|
|
37907
|
+
}
|
|
37908
|
+
try {
|
|
37909
|
+
const aprPct = await fetchDefillamaApr(source.poolId);
|
|
37910
|
+
return { ok: true, value: { aprRay: pctToRay(aprPct), label: source.label, stale: false } };
|
|
37911
|
+
} catch (fetchError) {
|
|
37912
|
+
this.config.logger.warn("[leverageYield] DefiLlama APR fetch failed; using fallbackAprPct", {
|
|
37913
|
+
vault,
|
|
37914
|
+
poolId: source.poolId,
|
|
37915
|
+
fallbackAprPct: source.fallbackAprPct,
|
|
37916
|
+
error: fetchError instanceof Error ? fetchError.message : String(fetchError)
|
|
37917
|
+
});
|
|
37918
|
+
return {
|
|
37919
|
+
ok: true,
|
|
37920
|
+
value: { aprRay: pctToRay(source.fallbackAprPct), label: `${source.label} (fallback)`, stale: true }
|
|
37921
|
+
};
|
|
37922
|
+
}
|
|
37923
|
+
} catch (error) {
|
|
37924
|
+
if (isLeverageYieldLookupError(error)) return { ok: false, error };
|
|
37925
|
+
return { ok: false, error: lookupFailed("leverageYield", "getLsdApr", error) };
|
|
37926
|
+
}
|
|
37927
|
+
}
|
|
37928
|
+
/**
|
|
37929
|
+
* Combined view of {@link getApr} + {@link getLsdApr}: re-applies the vault's leverage
|
|
37930
|
+
* formula with the LSD's native staking yield folded into the supply side, exposing the
|
|
37931
|
+
* **effective** net APR that LSD-backed strategies actually earn. The AAVE-only
|
|
37932
|
+
* `netAprRay` is preserved on the return value for callers who want to display both.
|
|
37933
|
+
*
|
|
37934
|
+
* effectiveSupply = supplyAprRay + lsdApr.aprRay
|
|
37935
|
+
* effectiveNet = effectiveSupply + leverage × (effectiveSupply − borrowAprRay)
|
|
37936
|
+
*
|
|
37937
|
+
* Fetches AAVE rates and the LSD APR in parallel for one round-trip's worth of latency.
|
|
37938
|
+
*/
|
|
37939
|
+
async getEffectiveApr(vault) {
|
|
37940
|
+
try {
|
|
37941
|
+
const [aprResult, lsdResult] = await Promise.all([this.getApr(vault), this.getLsdApr(vault)]);
|
|
37942
|
+
if (!aprResult.ok) return aprResult;
|
|
37943
|
+
if (!lsdResult.ok) return lsdResult;
|
|
37944
|
+
const apr = aprResult.value;
|
|
37945
|
+
const lsd = lsdResult.value;
|
|
37946
|
+
const effectiveSupplyAprRay = apr.supplyAprRay + lsd.aprRay;
|
|
37947
|
+
const spreadRay = effectiveSupplyAprRay - apr.borrowAprRay;
|
|
37948
|
+
const WAD2 = 1000000000000000000n;
|
|
37949
|
+
const effectiveNetAprRay = effectiveSupplyAprRay + spreadRay * apr.leverageMultiplierWad / WAD2;
|
|
37950
|
+
return {
|
|
37951
|
+
ok: true,
|
|
37952
|
+
value: { ...apr, lsdApr: lsd, effectiveSupplyAprRay, effectiveNetAprRay }
|
|
37953
|
+
};
|
|
37954
|
+
} catch (error) {
|
|
37955
|
+
if (isLeverageYieldLookupError(error)) return { ok: false, error };
|
|
37956
|
+
return { ok: false, error: lookupFailed("leverageYield", "getEffectiveApr", error) };
|
|
37957
|
+
}
|
|
37958
|
+
}
|
|
37959
|
+
/** Synchronously withdrawable assets for `owner` (clamped against leverage HF floor). */
|
|
37960
|
+
async getMaxWithdraw(vault, owner) {
|
|
37961
|
+
const inner = await Erc4626Service.getMaxWithdraw(vault, owner, this.hubProvider.publicClient);
|
|
37962
|
+
if (!inner.ok) return { ok: false, error: lookupFailed("leverageYield", "getMaxWithdraw", inner.error) };
|
|
37963
|
+
return { ok: true, value: inner.value };
|
|
37964
|
+
}
|
|
37965
|
+
/** Total underlying assets currently held by the vault (vault-asset units, 18 decimals) — TVL. */
|
|
37966
|
+
async getTotalAssets(vault) {
|
|
37967
|
+
const inner = await Erc4626Service.getTotalAssets(vault, this.hubProvider.publicClient);
|
|
37968
|
+
if (!inner.ok) return { ok: false, error: lookupFailed("leverageYield", "getTotalAssets", inner.error) };
|
|
37969
|
+
return { ok: true, value: inner.value };
|
|
37970
|
+
}
|
|
37971
|
+
/** Shares minted for a given asset deposit. */
|
|
37972
|
+
async previewDeposit(vault, assets) {
|
|
37973
|
+
const inner = await Erc4626Service.previewDeposit(vault, assets, this.hubProvider.publicClient);
|
|
37974
|
+
if (!inner.ok) return { ok: false, error: lookupFailed("leverageYield", "previewDeposit", inner.error) };
|
|
37975
|
+
return { ok: true, value: inner.value };
|
|
37976
|
+
}
|
|
37977
|
+
/** Shares burned for a given asset withdrawal. */
|
|
37978
|
+
async previewWithdraw(vault, assets) {
|
|
37979
|
+
const inner = await Erc4626Service.previewWithdraw(vault, assets, this.hubProvider.publicClient);
|
|
37980
|
+
if (!inner.ok) return { ok: false, error: lookupFailed("leverageYield", "previewWithdraw", inner.error) };
|
|
37981
|
+
return { ok: true, value: inner.value };
|
|
37982
|
+
}
|
|
37983
|
+
/** Assets received for a given share redemption. */
|
|
37984
|
+
async previewRedeem(vault, shares) {
|
|
37985
|
+
const inner = await Erc4626Service.previewRedeem(vault, shares, this.hubProvider.publicClient);
|
|
37986
|
+
if (!inner.ok) return { ok: false, error: lookupFailed("leverageYield", "previewRedeem", inner.error) };
|
|
37987
|
+
return { ok: true, value: inner.value };
|
|
37988
|
+
}
|
|
37989
|
+
/** Vault shares held by `owner`. */
|
|
37990
|
+
async getShareBalance(vault, owner) {
|
|
37991
|
+
try {
|
|
37992
|
+
const value = await this.hubProvider.publicClient.readContract({
|
|
37993
|
+
address: vault,
|
|
37994
|
+
abi: viem.erc20Abi,
|
|
37995
|
+
functionName: "balanceOf",
|
|
37996
|
+
args: [owner]
|
|
37997
|
+
});
|
|
37998
|
+
return { ok: true, value };
|
|
37999
|
+
} catch (error) {
|
|
38000
|
+
if (isLeverageYieldLookupError(error)) return { ok: false, error };
|
|
38001
|
+
return { ok: false, error: lookupFailed("leverageYield", "getShareBalance", error) };
|
|
38002
|
+
}
|
|
38003
|
+
}
|
|
38004
|
+
/**
|
|
38005
|
+
* Convenience: resolves the user's hub wallet from `(srcChainKey, srcAddress)` and
|
|
38006
|
+
* returns its on-chain `maxWithdraw`, less {@link MAX_WITHDRAW_DUST_BUFFER}. The trim
|
|
38007
|
+
* keeps the value safe for an asset-denominated ERC-4626 `withdraw` — the raw
|
|
38008
|
+
* `maxWithdraw` can trip the round-up that asks for one more share than the user holds.
|
|
38009
|
+
*/
|
|
38010
|
+
async getMaxWithdrawForUser(vault, srcChainKey, srcAddress) {
|
|
38011
|
+
try {
|
|
38012
|
+
const hubWallet = await this.hubProvider.getUserHubWalletAddress(srcAddress, srcChainKey);
|
|
38013
|
+
const maxWithdrawResult = await this.getMaxWithdraw(vault, hubWallet);
|
|
38014
|
+
if (!maxWithdrawResult.ok) return maxWithdrawResult;
|
|
38015
|
+
const buffered = maxWithdrawResult.value > MAX_WITHDRAW_DUST_BUFFER ? maxWithdrawResult.value - MAX_WITHDRAW_DUST_BUFFER : 0n;
|
|
38016
|
+
return { ok: true, value: buffered };
|
|
38017
|
+
} catch (error) {
|
|
38018
|
+
if (isLeverageYieldLookupError(error)) return { ok: false, error };
|
|
38019
|
+
return { ok: false, error: lookupFailed("leverageYield", "getMaxWithdrawForUser", error, { srcChainKey }) };
|
|
38020
|
+
}
|
|
38021
|
+
}
|
|
38022
|
+
/**
|
|
38023
|
+
* Convenience: resolves the user's hub wallet from `(srcChainKey, srcAddress)` and
|
|
38024
|
+
* returns its on-chain share balance.
|
|
38025
|
+
*/
|
|
38026
|
+
async getShareBalanceForUser(vault, srcChainKey, srcAddress) {
|
|
38027
|
+
try {
|
|
38028
|
+
const hubWallet = await this.hubProvider.getUserHubWalletAddress(srcAddress, srcChainKey);
|
|
38029
|
+
return await this.getShareBalance(vault, hubWallet);
|
|
38030
|
+
} catch (error) {
|
|
38031
|
+
if (isLeverageYieldLookupError(error)) return { ok: false, error };
|
|
38032
|
+
return { ok: false, error: lookupFailed("leverageYield", "getShareBalanceForUser", error, { srcChainKey }) };
|
|
38033
|
+
}
|
|
38034
|
+
}
|
|
38035
|
+
};
|
|
38036
|
+
|
|
36865
38037
|
// src/shared/entities/Sodax.ts
|
|
36866
38038
|
var Sodax = class {
|
|
36867
38039
|
instanceConfig;
|
|
@@ -36883,6 +38055,8 @@ var Sodax = class {
|
|
|
36883
38055
|
// Recovery service for withdrawing stuck hub-wallet assets back to a spoke chain
|
|
36884
38056
|
dex;
|
|
36885
38057
|
// Dex service enabling DEX operations
|
|
38058
|
+
leverageYield;
|
|
38059
|
+
// Leverage-yield service: cross-chain deposits / withdrawals into ERC-4626 leverage vaults on Sonic
|
|
36886
38060
|
config;
|
|
36887
38061
|
// Config service enabling configuration data fetching from the backend API or fallbacking to default values
|
|
36888
38062
|
hubProvider;
|
|
@@ -36890,9 +38064,10 @@ var Sodax = class {
|
|
|
36890
38064
|
spoke;
|
|
36891
38065
|
// spoke service enabling spoke chain operations
|
|
36892
38066
|
constructor(config) {
|
|
38067
|
+
const logger = resolveLogger(config?.logger);
|
|
36893
38068
|
this.instanceConfig = config ? mergeSodaxConfig(sodaxConfig, config) : sodaxConfig;
|
|
36894
|
-
this.backendApi = new BackendApiService(this.instanceConfig.api);
|
|
36895
|
-
this.config = new ConfigService({ api: this.backendApi, config: this.instanceConfig, userConfig: config });
|
|
38069
|
+
this.backendApi = new BackendApiService(this.instanceConfig.api, logger);
|
|
38070
|
+
this.config = new ConfigService({ api: this.backendApi, config: this.instanceConfig, userConfig: config, logger });
|
|
36896
38071
|
this.hubProvider = new EvmHubProvider({ config: this.config });
|
|
36897
38072
|
this.spoke = new SpokeService({ config: this.config, hubProvider: this.hubProvider });
|
|
36898
38073
|
this.swaps = new SwapService({
|
|
@@ -36927,6 +38102,11 @@ var Sodax = class {
|
|
|
36927
38102
|
config: this.config,
|
|
36928
38103
|
spoke: this.spoke
|
|
36929
38104
|
});
|
|
38105
|
+
this.leverageYield = new LeverageYieldService({
|
|
38106
|
+
hubProvider: this.hubProvider,
|
|
38107
|
+
config: this.config,
|
|
38108
|
+
spoke: this.spoke
|
|
38109
|
+
});
|
|
36930
38110
|
}
|
|
36931
38111
|
/**
|
|
36932
38112
|
* Initializes the Sodax instance with dynamic configuration.
|
|
@@ -37035,8 +38215,11 @@ exports.IntentsAbi = IntentsAbi;
|
|
|
37035
38215
|
exports.LOOKUP_CODES = LOOKUP_CODES;
|
|
37036
38216
|
exports.LTV_PRECISION = LTV_PRECISION;
|
|
37037
38217
|
exports.LendingPoolService = LendingPoolService;
|
|
38218
|
+
exports.LeverageYieldService = LeverageYieldService;
|
|
37038
38219
|
exports.LockupMultiplier = LockupMultiplier;
|
|
37039
38220
|
exports.LockupPeriod = LockupPeriod;
|
|
38221
|
+
exports.LsodaSymbols = LsodaSymbols;
|
|
38222
|
+
exports.LsodaTokens = LsodaTokens;
|
|
37040
38223
|
exports.MAX_UINT256 = MAX_UINT256;
|
|
37041
38224
|
exports.MigrationService = MigrationService;
|
|
37042
38225
|
exports.MintPositionEventAbi = MintPositionEventAbi;
|
|
@@ -37045,6 +38228,7 @@ exports.MoneyMarketService = MoneyMarketService;
|
|
|
37045
38228
|
exports.NEAR_CHAIN_KEYS = NEAR_CHAIN_KEYS;
|
|
37046
38229
|
exports.NEAR_CHAIN_KEYS_SET = NEAR_CHAIN_KEYS_SET;
|
|
37047
38230
|
exports.NEAR_DEFAULT_GAS = NEAR_DEFAULT_GAS;
|
|
38231
|
+
exports.NEAR_STORAGE_DEPOSIT = NEAR_STORAGE_DEPOSIT;
|
|
37048
38232
|
exports.NearSpokeService = NearSpokeService;
|
|
37049
38233
|
exports.PartnerFeeClaimService = PartnerFeeClaimService;
|
|
37050
38234
|
exports.PartnerService = PartnerService;
|
|
@@ -37108,6 +38292,7 @@ exports.bnUSDLegacyTokens = bnUSDLegacyTokens;
|
|
|
37108
38292
|
exports.bridgeConfig = bridgeConfig;
|
|
37109
38293
|
exports.bridgeInvariant = bridgeInvariant;
|
|
37110
38294
|
exports.bscSupportedTokens = bscSupportedTokens;
|
|
38295
|
+
exports.calcOpReturnOutputVbytes = calcOpReturnOutputVbytes;
|
|
37111
38296
|
exports.calculateAllReserveIncentives = calculateAllReserveIncentives;
|
|
37112
38297
|
exports.calculateAllUserIncentives = calculateAllUserIncentives;
|
|
37113
38298
|
exports.calculateAvailableBorrowsMarketReferenceCurrency = calculateAvailableBorrowsMarketReferenceCurrency;
|
|
@@ -37126,6 +38311,7 @@ exports.clRouterAbi = clRouterAbi;
|
|
|
37126
38311
|
exports.clTickLensAbi = clTickLensAbi;
|
|
37127
38312
|
exports.concentratedLiquidityConfig = concentratedLiquidityConfig;
|
|
37128
38313
|
exports.connectionAbi = connectionAbi;
|
|
38314
|
+
exports.consoleLogger = consoleLogger;
|
|
37129
38315
|
exports.convertTransactionInstructionToRaw = convertTransactionInstructionToRaw;
|
|
37130
38316
|
exports.createInvariant = createInvariant;
|
|
37131
38317
|
exports.deepMerge = deepMerge;
|
|
@@ -37218,6 +38404,13 @@ exports.isInjectiveChainKeyType = isInjectiveChainKeyType;
|
|
|
37218
38404
|
exports.isJsonRpcPayloadResponse = isJsonRpcPayloadResponse;
|
|
37219
38405
|
exports.isLegacybnUSDChainId = isLegacybnUSDChainId;
|
|
37220
38406
|
exports.isLegacybnUSDToken = isLegacybnUSDToken;
|
|
38407
|
+
exports.isLeverageYieldAllowanceCheckError = isLeverageYieldAllowanceCheckError;
|
|
38408
|
+
exports.isLeverageYieldApproveError = isLeverageYieldApproveError;
|
|
38409
|
+
exports.isLeverageYieldCreateIntentError = isLeverageYieldCreateIntentError;
|
|
38410
|
+
exports.isLeverageYieldError = isLeverageYieldError;
|
|
38411
|
+
exports.isLeverageYieldLookupError = isLeverageYieldLookupError;
|
|
38412
|
+
exports.isLeverageYieldPostExecutionError = isLeverageYieldPostExecutionError;
|
|
38413
|
+
exports.isLeverageYieldSwapError = isLeverageYieldSwapError;
|
|
37221
38414
|
exports.isMigrateOrchestrationError = isMigrateOrchestrationError;
|
|
37222
38415
|
exports.isMigrationAllowanceCheckError = isMigrationAllowanceCheckError;
|
|
37223
38416
|
exports.isMigrationApproveError = isMigrationApproveError;
|
|
@@ -37284,6 +38477,9 @@ exports.isUndefinedOrValidWalletProviderForChainKey = isUndefinedOrValidWalletPr
|
|
|
37284
38477
|
exports.isUnifiedBnUSDMigrateParams = isUnifiedBnUSDMigrateParams;
|
|
37285
38478
|
exports.isValidWalletProviderForChainKey = isValidWalletProviderForChainKey;
|
|
37286
38479
|
exports.kaiaSupportedTokens = kaiaSupportedTokens;
|
|
38480
|
+
exports.leverageYieldConfig = leverageYieldConfig;
|
|
38481
|
+
exports.leverageYieldInvariant = leverageYieldInvariant;
|
|
38482
|
+
exports.leverageYieldVaults = leverageYieldVaults;
|
|
37287
38483
|
exports.lightlinkSupportedTokens = lightlinkSupportedTokens;
|
|
37288
38484
|
exports.lookupFailed = lookupFailed;
|
|
37289
38485
|
exports.mapRelayFailure = mapRelayFailure;
|
|
@@ -37302,6 +38498,7 @@ exports.newbnUSDSpokeChainIds = newbnUSDSpokeChainIds;
|
|
|
37302
38498
|
exports.normalize = normalize;
|
|
37303
38499
|
exports.normalizeBN = normalizeBN;
|
|
37304
38500
|
exports.normalizePsbtToBase64 = normalizePsbtToBase64;
|
|
38501
|
+
exports.normalizeSignatureToBase64 = normalizeSignatureToBase64;
|
|
37305
38502
|
exports.normalizedToUsd = normalizedToUsd;
|
|
37306
38503
|
exports.optimismSupportedTokens = optimismSupportedTokens;
|
|
37307
38504
|
exports.pancakeSwapInfinityDefaultHookAbi = pancakeSwapInfinityDefaultHookAbi;
|
|
@@ -37326,9 +38523,11 @@ exports.relayTxAndWaitPacket = relayTxAndWaitPacket;
|
|
|
37326
38523
|
exports.requestAddress = requestAddress;
|
|
37327
38524
|
exports.requestJsonRpc = requestJsonRpc;
|
|
37328
38525
|
exports.requestSigning = requestSigning;
|
|
38526
|
+
exports.resolveLogger = resolveLogger;
|
|
37329
38527
|
exports.retry = retry;
|
|
37330
38528
|
exports.reverseEncodeAddress = reverseEncodeAddress;
|
|
37331
38529
|
exports.serializeAddressData = serializeAddressData;
|
|
38530
|
+
exports.silentLogger = silentLogger;
|
|
37332
38531
|
exports.sleep = sleep;
|
|
37333
38532
|
exports.sodaxConfig = sodaxConfig;
|
|
37334
38533
|
exports.sodaxInvariant = sodaxInvariant;
|
|
@@ -37356,6 +38555,7 @@ exports.swapsConfig = swapsConfig;
|
|
|
37356
38555
|
exports.uiPoolDataAbi = uiPoolDataAbi;
|
|
37357
38556
|
exports.universalRouterAbi = universalRouterAbi;
|
|
37358
38557
|
exports.unknownFailed = unknownFailed;
|
|
38558
|
+
exports.usesBip322MessageSigning = usesBip322MessageSigning;
|
|
37359
38559
|
exports.valueToBigNumber = valueToBigNumber;
|
|
37360
38560
|
exports.valueToZDBigNumber = valueToZDBigNumber;
|
|
37361
38561
|
exports.variableDebtTokenAbi = variableDebtTokenAbi;
|