@stableflow/bridges 3.0.3 → 3.1.0-rc-d2d5e08ec7-20260706

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.js CHANGED
@@ -189,6 +189,7 @@ var OneClickService = class {
189
189
  appFees = [],
190
190
  swapType = "EXACT_INPUT",
191
191
  isProxy = true,
192
+ slippageTolerance,
192
193
  ...restParams
193
194
  } = params;
194
195
  const _quoteType = `OneClickService ${fromToken?.chainName}->${toToken?.chainName}`;
@@ -206,7 +207,8 @@ var OneClickService = class {
206
207
  ...appFees
207
208
  ],
208
209
  referral: "stableflow",
209
- amount: amountWei
210
+ amount: amountWei,
211
+ slippageTolerance: slippageTolerance * 100
210
212
  };
211
213
  if (swapType === "EXACT_OUTPUT") {
212
214
  quoteParams.amount = Big__default.default(amountWei || 0).div(10 ** fromToken.decimals).times(10 ** toToken.decimals).toFixed(0);
@@ -4846,6 +4848,7 @@ var FraxZero2OneClickService = class extends FraxZeroService {
4846
4848
  refundTo: middleChainRecipientAddress,
4847
4849
  wallet: middleChainWallet,
4848
4850
  appFees: [
4851
+ ...params.appFees || [],
4849
4852
  {
4850
4853
  recipient: "reffer.near",
4851
4854
  // No bridge fee will be charged temporarily
@@ -5065,6 +5068,7 @@ var OneClick2FraxZeroService = class extends FraxZeroService {
5065
5068
  isProxy: true,
5066
5069
  recipient: middleChainRecipientAddress,
5067
5070
  appFees: [
5071
+ ...params.appFees || [],
5068
5072
  {
5069
5073
  recipient: "reffer.near",
5070
5074
  // No bridge fee will be charged temporarily
@@ -5420,6 +5424,7 @@ var OneClickUsdt0Service = class {
5420
5424
  isProxy: true,
5421
5425
  recipient: middleChainRecipientAddress,
5422
5426
  appFees: [
5427
+ ...params.appFees || [],
5423
5428
  {
5424
5429
  recipient: "reffer.near",
5425
5430
  // No bridge fee will be charged temporarily
@@ -5667,11 +5672,228 @@ var ServiceMap = {
5667
5672
  [core.Service.OneClickUsdt0]: from_oneclick_default2,
5668
5673
  [core.Service.Native]: native_default
5669
5674
  };
5675
+ var OVERRIDE_KEYS = /* @__PURE__ */ new Set([
5676
+ "value",
5677
+ "gasLimit",
5678
+ "gasPrice",
5679
+ "maxFeePerGas",
5680
+ "maxPriorityFeePerGas",
5681
+ "nonce",
5682
+ "from",
5683
+ "type",
5684
+ "accessList",
5685
+ "chainId",
5686
+ "customData",
5687
+ "blockTag"
5688
+ ]);
5689
+ var toDecimalString = (v) => {
5690
+ if (v === void 0 || v === null) {
5691
+ return "0";
5692
+ }
5693
+ if (typeof v === "bigint") {
5694
+ return v.toString();
5695
+ }
5696
+ if (typeof v === "number") {
5697
+ return Math.trunc(v).toString();
5698
+ }
5699
+ return String(v);
5700
+ };
5701
+ var assertEvm = (token) => {
5702
+ if (!token || token.chainType !== "evm") {
5703
+ throw new Error("API-driven custody currently supports EVM chains only");
5704
+ }
5705
+ };
5706
+ var getSendAmountWei = (serviceType, quote) => {
5707
+ const { isExactOutput } = getQuoteModes({
5708
+ quoteData: quote,
5709
+ bridgeStore: { quoteDataService: serviceType }
5710
+ });
5711
+ if (isExactOutput) {
5712
+ return quote?.quote?.minAmountIn;
5713
+ }
5714
+ return quote?.quoteParam?.amountWei;
5715
+ };
5716
+ var encodeContractCall = (sendParam) => {
5717
+ const { contract, method, param } = sendParam;
5718
+ if (!contract?.interface || contract?.target === void 0) {
5719
+ throw new Error("Invalid sendParam: missing contract interface/target");
5720
+ }
5721
+ const to = typeof contract.target === "string" ? contract.target : String(contract.target);
5722
+ let args = Array.isArray(param) ? param : [];
5723
+ let value;
5724
+ let gasLimit;
5725
+ if (args.length > 0) {
5726
+ const last = args[args.length - 1];
5727
+ const isOverride = last !== null && typeof last === "object" && !Array.isArray(last) && Object.keys(last).length > 0 && Object.keys(last).every((k) => OVERRIDE_KEYS.has(k));
5728
+ if (isOverride) {
5729
+ args = args.slice(0, -1);
5730
+ value = last.value;
5731
+ gasLimit = last.gasLimit;
5732
+ }
5733
+ }
5734
+ const data = contract.interface.encodeFunctionData(method, args);
5735
+ return {
5736
+ to,
5737
+ data,
5738
+ value: toDecimalString(value),
5739
+ gasLimit: gasLimit === void 0 || gasLimit === null ? void 0 : toDecimalString(gasLimit)
5740
+ };
5741
+ };
5742
+ var buildTransferTx = (params) => {
5743
+ const { chainId, from, fromToken, depositAddress, amountWei } = params;
5744
+ if (!depositAddress) {
5745
+ throw new Error("Quote has no depositAddress to build transfer transaction");
5746
+ }
5747
+ if (fromToken.contractAddress === "eth") {
5748
+ return {
5749
+ chainId,
5750
+ from,
5751
+ to: depositAddress,
5752
+ data: "0x",
5753
+ value: toDecimalString(amountWei)
5754
+ };
5755
+ }
5756
+ const iface = new ethers.ethers.Interface(core.erc20Abi);
5757
+ const data = iface.encodeFunctionData("transfer", [depositAddress, amountWei]);
5758
+ return {
5759
+ chainId,
5760
+ from,
5761
+ to: fromToken.contractAddress,
5762
+ data,
5763
+ value: "0"
5764
+ };
5765
+ };
5766
+ var buildSendTx = (serviceType, quote) => {
5767
+ const fromToken = quote?.quoteParam?.fromToken;
5768
+ assertEvm(fromToken);
5769
+ const chainId = fromToken.chainId;
5770
+ const from = quote?.quoteParam?.refundTo;
5771
+ const sendParam = quote?.sendParam;
5772
+ if (sendParam?.txRequest) {
5773
+ const txRequest = sendParam.txRequest;
5774
+ return {
5775
+ chainId,
5776
+ from,
5777
+ to: txRequest.target,
5778
+ data: txRequest.calldata,
5779
+ value: toDecimalString(txRequest.value),
5780
+ gasLimit: txRequest.gasLimit === void 0 || txRequest.gasLimit === null ? void 0 : toDecimalString(txRequest.gasLimit)
5781
+ };
5782
+ }
5783
+ const { isOneClickService } = getQuoteModes({
5784
+ quoteData: quote,
5785
+ bridgeStore: { quoteDataService: serviceType }
5786
+ });
5787
+ if (isOneClickService) {
5788
+ if (sendParam) {
5789
+ return { chainId, from, ...encodeContractCall(sendParam) };
5790
+ }
5791
+ return buildTransferTx({
5792
+ chainId,
5793
+ from,
5794
+ fromToken,
5795
+ depositAddress: quote?.quote?.depositAddress,
5796
+ amountWei: getSendAmountWei(serviceType, quote)
5797
+ });
5798
+ }
5799
+ if (!sendParam) {
5800
+ throw new Error("Quote has no sendParam to build transaction");
5801
+ }
5802
+ return { chainId, from, ...encodeContractCall(sendParam) };
5803
+ };
5804
+ var buildApproveTx = (serviceType, quote) => {
5805
+ if (!quote?.needApprove) {
5806
+ return {};
5807
+ }
5808
+ const fromToken = quote?.quoteParam?.fromToken;
5809
+ assertEvm(fromToken);
5810
+ if (fromToken.contractAddress === "eth") {
5811
+ return {};
5812
+ }
5813
+ const chainId = fromToken.chainId;
5814
+ const from = quote?.quoteParam?.refundTo;
5815
+ const spender = quote?.approveSpender;
5816
+ if (!spender) {
5817
+ throw new Error("Quote requires approval but has no approveSpender");
5818
+ }
5819
+ const amountWei = getSendAmountWei(serviceType, quote);
5820
+ const iface = new ethers.ethers.Interface(core.erc20Abi);
5821
+ const approveTx = {
5822
+ chainId,
5823
+ from,
5824
+ to: fromToken.contractAddress,
5825
+ data: iface.encodeFunctionData("approve", [spender, amountWei]),
5826
+ value: "0"
5827
+ };
5828
+ const result = { approveTx };
5829
+ if (fromToken.chainName === "Ethereum") {
5830
+ result.approveResetTx = {
5831
+ chainId,
5832
+ from,
5833
+ to: fromToken.contractAddress,
5834
+ data: iface.encodeFunctionData("approve", [spender, "0"]),
5835
+ value: "0"
5836
+ };
5837
+ }
5838
+ return result;
5839
+ };
5840
+ var buildPermitTypedData = async (params) => {
5841
+ const { permitToken, permitAmountWei, permitSpender, owner } = params;
5842
+ assertEvm(permitToken);
5843
+ if (!owner) {
5844
+ throw new Error("buildPermitTypedData requires the permit owner address");
5845
+ }
5846
+ const provider = utilsEvm.evmRpcFallbackProvider(permitToken);
5847
+ const erc20 = new ethers.ethers.Contract(permitToken.contractAddress, core.erc20Abi, provider);
5848
+ const [nonce, name] = await Promise.all([
5849
+ erc20.nonces(owner),
5850
+ erc20.name()
5851
+ ]);
5852
+ const version = permitToken.symbol === "USDC" ? "2" : "1";
5853
+ const deadline = Math.floor(Date.now() / 1e3) + 60 * 60 * 24 * 3;
5854
+ const domain = {
5855
+ name,
5856
+ version,
5857
+ chainId: Number(permitToken.chainId),
5858
+ verifyingContract: permitToken.contractAddress
5859
+ };
5860
+ const types = {
5861
+ Permit: [
5862
+ { name: "owner", type: "address" },
5863
+ { name: "spender", type: "address" },
5864
+ { name: "value", type: "uint256" },
5865
+ { name: "nonce", type: "uint256" },
5866
+ { name: "deadline", type: "uint256" }
5867
+ ]
5868
+ };
5869
+ const values = {
5870
+ owner,
5871
+ spender: permitSpender,
5872
+ value: toDecimalString(permitAmountWei),
5873
+ nonce: nonce.toString(),
5874
+ deadline
5875
+ };
5876
+ return {
5877
+ domain,
5878
+ types,
5879
+ values,
5880
+ deadline,
5881
+ nonce: Number(nonce),
5882
+ token: permitToken.contractAddress,
5883
+ spender: permitSpender,
5884
+ owner
5885
+ };
5886
+ };
5887
+
5888
+ // src/sfa.ts
5670
5889
  var submitOthersTx = (requestBody) => {
5671
5890
  return core.postRequest("/v0/trade/add", requestBody);
5672
5891
  };
5673
- var BridgeSFA = class {
5892
+ var BridgeSFA = class _BridgeSFA {
5674
5893
  static async getAllQuote(params) {
5894
+ const cs = new core.Csl(core.OpenAPI.DEBUG);
5895
+ const csl = cs.log;
5896
+ csl("BridgeSFA.getAllQuote", "blue-400", "params: %o", params);
5675
5897
  const results = [];
5676
5898
  let { minInputAmount = "1", disabledServices } = params;
5677
5899
  if (Big__default.default(minInputAmount).lte(0)) {
@@ -5701,7 +5923,6 @@ var BridgeSFA = class {
5701
5923
  core.Service.FraxZeroOneClick,
5702
5924
  core.Service.OneClickFraxZero
5703
5925
  ].includes(service)) {
5704
- _params.slippageTolerance = params.slippageTolerance * 100;
5705
5926
  _params.originAsset = params.fromToken.assetId;
5706
5927
  _params.destinationAsset = params.toToken.assetId;
5707
5928
  _params.refundType = "ORIGIN_CHAIN";
@@ -5734,6 +5955,7 @@ var BridgeSFA = class {
5734
5955
  return;
5735
5956
  }
5736
5957
  const quoteParams = formatQuoteParams(_service);
5958
+ csl("BridgeSFA.getAllQuote", "blue-400", "%s quoteParams: %o", _service, quoteParams);
5737
5959
  quoteServices.push({
5738
5960
  service: _service,
5739
5961
  quote: () => {
@@ -5786,6 +6008,7 @@ var BridgeSFA = class {
5786
6008
  }
5787
6009
  return results;
5788
6010
  }
6011
+ csl("BridgeSFA.getAllQuote", "blue-400", "quoteServices: %o", quoteServices);
5789
6012
  const promises = [];
5790
6013
  for (const quoteService of quoteServices) {
5791
6014
  const promise = (async () => {
@@ -5827,12 +6050,11 @@ var BridgeSFA = class {
5827
6050
  const sendParams = {
5828
6051
  wallet
5829
6052
  };
5830
- const { isExactOutput, isOneClickService, isQuoteParamDepositAddress } = getQuoteModes({
6053
+ const { isExactOutput, isOneClickService } = getQuoteModes({
5831
6054
  quoteData: quote,
5832
6055
  bridgeStore: { quoteDataService: serviceType }
5833
6056
  });
5834
6057
  const fromToken = quote.quoteParam.fromToken;
5835
- const toToken = quote.quoteParam.toToken;
5836
6058
  const depositAddress = quote.quote?.depositAddress;
5837
6059
  const sendParam = quote.sendParam;
5838
6060
  let _amountWei = quote.quoteParam?.amountWei;
@@ -5852,45 +6074,11 @@ var BridgeSFA = class {
5852
6074
  csl("BridgeSFA.send", "blue-400", "sendParams: %o", sendParams);
5853
6075
  const txhash = await service.send(sendParams);
5854
6076
  try {
5855
- const reportData = {
5856
- project: core.ServiceBackend[serviceType],
5857
- address: quote.quoteParam?.refundTo,
5858
- amount: isExactOutput ? quote.quote.amountInFormatted : Big__default.default(_amountWei || 0).div(10 ** (fromToken?.decimals || 6)).toFixed(fromToken?.decimals || 6, 0),
5859
- out_amount: quote.outputAmount,
5860
- deposit_address: isOneClickService ? quote.quote?.depositAddress : "",
5861
- receive_address: quote.quoteParam?.recipient,
5862
- from_chain: fromToken.blockchain,
5863
- symbol: /^USD₮0$/i.test(fromToken.symbol) ? "USDT" : fromToken.symbol,
5864
- to_chain: toToken.blockchain,
5865
- to_symbol: /^USD₮0$/i.test(toToken.symbol) ? "USDT" : toToken.symbol,
5866
- tx_hash: ""
5867
- };
5868
- if (permitSignature) {
5869
- if ([core.Service.OneClickUsdt0].includes(serviceType)) {
5870
- reportData.layer_zero_permit = {
5871
- ...permitSignature,
5872
- ...quote.permitAdditionalData
5873
- };
5874
- }
5875
- if ([core.Service.OneClickFraxZero, core.Service.FraxZeroOneClick].includes(serviceType)) {
5876
- reportData.frax_zero_permit = {
5877
- ...permitSignature,
5878
- ...quote.permitAdditionalData
5879
- };
5880
- }
5881
- }
5882
- reportData.tx_hash = txhash;
5883
- let _depositAddress = txhash;
5884
- if (isQuoteParamDepositAddress) {
5885
- _depositAddress = quote.quoteParam?.depositAddress || txhash;
5886
- }
5887
- if (!reportData.deposit_address) {
5888
- reportData.deposit_address = _depositAddress;
5889
- }
5890
- if (serviceType === core.Service.Native) {
5891
- const quoteIds = quote?.orders?.map?.((order) => order.quoteId) || [];
5892
- reportData.quoteIds = quoteIds;
5893
- }
6077
+ const reportData = _BridgeSFA.buildReportData(serviceType, {
6078
+ quote,
6079
+ txhash,
6080
+ permitSignature
6081
+ });
5894
6082
  csl("BridgeSFA.send", "blue-400", "reportData: %o", reportData);
5895
6083
  submitOthersTx(reportData);
5896
6084
  } catch (error) {
@@ -5898,6 +6086,137 @@ var BridgeSFA = class {
5898
6086
  }
5899
6087
  return txhash;
5900
6088
  }
6089
+ /**
6090
+ * Build the backend report payload for a submitted source-chain transaction.
6091
+ * Shared by `send` (fire-and-forget) and `report` (awaited).
6092
+ */
6093
+ static buildReportData(serviceType, params) {
6094
+ const { quote, txhash, permitSignature } = params;
6095
+ const { isExactOutput, isOneClickService, isQuoteParamDepositAddress } = getQuoteModes({
6096
+ quoteData: quote,
6097
+ bridgeStore: { quoteDataService: serviceType }
6098
+ });
6099
+ const fromToken = quote.quoteParam.fromToken;
6100
+ const toToken = quote.quoteParam.toToken;
6101
+ let _amountWei = quote.quoteParam?.amountWei;
6102
+ if (isExactOutput) {
6103
+ _amountWei = quote.quote?.minAmountIn;
6104
+ }
6105
+ const reportData = {
6106
+ project: core.ServiceBackend[serviceType],
6107
+ address: quote.quoteParam?.refundTo,
6108
+ amount: isExactOutput ? quote.quote.amountInFormatted : Big__default.default(_amountWei || 0).div(10 ** (fromToken?.decimals || 6)).toFixed(fromToken?.decimals || 6, 0),
6109
+ out_amount: quote.outputAmount,
6110
+ deposit_address: isOneClickService ? quote.quote?.depositAddress : "",
6111
+ receive_address: quote.quoteParam?.recipient,
6112
+ from_chain: fromToken.blockchain,
6113
+ symbol: /^USD₮0$/i.test(fromToken.symbol) ? "USDT" : fromToken.symbol,
6114
+ to_chain: toToken.blockchain,
6115
+ to_symbol: /^USD₮0$/i.test(toToken.symbol) ? "USDT" : toToken.symbol,
6116
+ tx_hash: ""
6117
+ };
6118
+ if (permitSignature) {
6119
+ if ([core.Service.OneClickUsdt0].includes(serviceType)) {
6120
+ reportData.layer_zero_permit = {
6121
+ ...permitSignature,
6122
+ ...quote.permitAdditionalData
6123
+ };
6124
+ }
6125
+ if ([core.Service.OneClickFraxZero, core.Service.FraxZeroOneClick].includes(serviceType)) {
6126
+ reportData.frax_zero_permit = {
6127
+ ...permitSignature,
6128
+ ...quote.permitAdditionalData
6129
+ };
6130
+ }
6131
+ }
6132
+ reportData.tx_hash = txhash;
6133
+ let _depositAddress = txhash;
6134
+ if (isQuoteParamDepositAddress) {
6135
+ _depositAddress = quote.quoteParam?.depositAddress || txhash;
6136
+ }
6137
+ if (!reportData.deposit_address) {
6138
+ reportData.deposit_address = _depositAddress;
6139
+ }
6140
+ if (serviceType === core.Service.Native) {
6141
+ const quoteIds = quote?.orders?.map?.((order) => order.quoteId) || [];
6142
+ reportData.quoteIds = quoteIds;
6143
+ }
6144
+ return reportData;
6145
+ }
6146
+ /**
6147
+ * API-driven custody: build the serializable, unsigned transaction data for a
6148
+ * route WITHOUT a signer. Intended for MPC wallets (e.g. Fireblocks) that sign
6149
+ * and broadcast through their own infrastructure.
6150
+ *
6151
+ * Returns any required `approveTx` (and `approveResetTx` on Ethereum), the
6152
+ * `permitTypedData` to sign off-chain when `quote.needPermit`, and the main
6153
+ * `tx`. After the integrator broadcasts `tx`, call `BridgeSFA.report` with the
6154
+ * resulting hash (and the permit signature, if any) so StableFlow can execute
6155
+ * the destination leg.
6156
+ *
6157
+ * EVM only for now; non-EVM source chains throw.
6158
+ */
6159
+ static async buildTransaction(serviceType, params) {
6160
+ const service = ServiceMap[serviceType];
6161
+ if (!service) {
6162
+ throw new Error(`Invalid service type: ${serviceType}`);
6163
+ }
6164
+ const { quote } = params;
6165
+ if (!quote) {
6166
+ throw new Error("Quote data is required");
6167
+ }
6168
+ const tx = buildSendTx(serviceType, quote);
6169
+ const { approveTx, approveResetTx } = buildApproveTx(serviceType, quote);
6170
+ const result = {
6171
+ chainId: tx.chainId,
6172
+ from: tx.from,
6173
+ tx
6174
+ };
6175
+ if (approveTx) {
6176
+ result.approveTx = approveTx;
6177
+ }
6178
+ if (approveResetTx) {
6179
+ result.approveResetTx = approveResetTx;
6180
+ }
6181
+ if (quote.needPermit) {
6182
+ result.permitTypedData = await buildPermitTypedData({
6183
+ permitToken: quote.permitToken,
6184
+ permitAmountWei: quote.permitAmountWei,
6185
+ permitSpender: quote.permitSpender,
6186
+ owner: quote.quoteParam?.evmAddress || quote.quoteParam?.refundTo
6187
+ });
6188
+ }
6189
+ return result;
6190
+ }
6191
+ /**
6192
+ * API-driven custody: report an externally signed & broadcast source-chain
6193
+ * transaction back to StableFlow so it can track and execute the destination
6194
+ * leg. Pair with `BridgeSFA.buildTransaction`.
6195
+ *
6196
+ * When `quote.needPermit === true`, `permitSignature` is required.
6197
+ */
6198
+ static report(serviceType, params) {
6199
+ const service = ServiceMap[serviceType];
6200
+ if (!service) {
6201
+ throw new Error(`Invalid service type: ${serviceType}`);
6202
+ }
6203
+ const { quote, hash, permitSignature } = params;
6204
+ if (!quote) {
6205
+ throw new Error("Quote data is required");
6206
+ }
6207
+ if (!hash) {
6208
+ throw new Error("Transaction hash is required");
6209
+ }
6210
+ if (quote.needPermit && !permitSignature) {
6211
+ throw new Error("Permit signature is required");
6212
+ }
6213
+ const reportData = _BridgeSFA.buildReportData(serviceType, {
6214
+ quote,
6215
+ txhash: hash,
6216
+ permitSignature
6217
+ });
6218
+ return submitOthersTx(reportData);
6219
+ }
5901
6220
  static async getStatus(serviceType, params) {
5902
6221
  const cs = new core.Csl(core.OpenAPI.DEBUG);
5903
6222
  const csl = cs.log;
@@ -6016,6 +6335,9 @@ exports.ServiceMap = ServiceMap;
6016
6335
  exports.USDT0_CONFIG = USDT0_CONFIG;
6017
6336
  exports.USDT0_LEGACY_MESH_TRANSFTER_FEE = USDT0_LEGACY_MESH_TRANSFTER_FEE;
6018
6337
  exports.addressToBytes32 = addressToBytes32;
6338
+ exports.buildApproveTx = buildApproveTx;
6339
+ exports.buildPermitTypedData = buildPermitTypedData;
6340
+ exports.buildSendTx = buildSendTx;
6019
6341
  exports.formatNumber = formatNumber;
6020
6342
  exports.formatQuoteError = formatQuoteError;
6021
6343
  exports.getHopMsgFee = getHopMsgFee;