diff --git a/src/hooks/tbtc/useDepositTelemetry.ts b/src/hooks/tbtc/useDepositTelemetry.ts
index 292de809b..727e02451 100644
--- a/src/hooks/tbtc/useDepositTelemetry.ts
+++ b/src/hooks/tbtc/useDepositTelemetry.ts
@@ -5,6 +5,7 @@ import { BitcoinNetwork } from "../../threshold-ts/types"
import { verifyDepositAddress } from "../../utils/verifyDepositAddress"
import { useCaptureMessage } from "../sentry"
import { ApiUrl, endpointUrl } from "../../enums"
+import { isLocalhost } from "../../utils/isLocalhost"
export const useDepositTelemetry = (network: BitcoinNetwork) => {
const captureMessage = useCaptureMessage()
@@ -43,6 +44,14 @@ export const useDepositTelemetry = (network: BitcoinNetwork) => {
}
)
+ // Skip telemetry submission in localhost to avoid CORS issues
+ if (isLocalhost()) {
+ console.info(
+ "Skipping deposit telemetry submission in localhost environment"
+ )
+ return
+ }
+
const requestBody = {
depositAddress,
depositor: depositor.identifierHex,
@@ -61,8 +70,19 @@ export const useDepositTelemetry = (network: BitcoinNetwork) => {
requestBody,
{ timeout: 10000 }
)
- } catch (error) {
- throw new Error("Failed to submit deposit telemetry", { cause: error })
+ } catch (error: any) {
+ // Log the error but don't throw it to prevent blocking the deposit flow
+ console.warn("Failed to submit deposit telemetry:", error.message)
+
+ // In production, throw only for non-CORS errors
+ if (
+ !isLocalhost() &&
+ (error.response || error.code !== "ERR_NETWORK")
+ ) {
+ throw new Error("Failed to submit deposit telemetry", {
+ cause: error,
+ })
+ }
}
},
[verifyDepositAddress, network, captureMessage]
diff --git a/src/pages/tBTC/Deposit/Minting/InitiateMinting.tsx b/src/pages/tBTC/Deposit/Minting/InitiateMinting.tsx
index 19a745a04..0a7837655 100644
--- a/src/pages/tBTC/Deposit/Minting/InitiateMinting.tsx
+++ b/src/pages/tBTC/Deposit/Minting/InitiateMinting.tsx
@@ -87,7 +87,7 @@ const InitiateMintingComponent: FC<{
withSymbol
/>{" "}
and will receive{" "}
-
+ {tBTCMintAmount ? (
-
+ ) : (
+ --
+ )}
{chainInfo.mintingProcessDescription}
diff --git a/src/pages/tBTC/Deposit/Minting/MakeDeposit.tsx b/src/pages/tBTC/Deposit/Minting/MakeDeposit.tsx
index 683f0dc92..d63e050bf 100644
--- a/src/pages/tBTC/Deposit/Minting/MakeDeposit.tsx
+++ b/src/pages/tBTC/Deposit/Minting/MakeDeposit.tsx
@@ -108,19 +108,33 @@ const BTCAddressSection: FC<{
maxW="205px"
borderRadius="8px"
>
-
+ {btcDepositAddress ? (
+
+ ) : (
+
+ Loading address...
+
+ )}
-
+
- {btcDepositAddress}
+ {btcDepositAddress || "..."}
{
+ return isTestnet ? TBTC_API_ENDPOINTS.TESTNET : TBTC_API_ENDPOINTS.MAINNET
+}
diff --git a/src/threshold-ts/tbtc/index.ts b/src/threshold-ts/tbtc/index.ts
index 184475081..02b7532b4 100644
--- a/src/threshold-ts/tbtc/index.ts
+++ b/src/threshold-ts/tbtc/index.ts
@@ -1,5 +1,6 @@
import { BlockTag, TransactionReceipt } from "@ethersproject/abstract-provider"
import { Web3Provider, JsonRpcProvider } from "@ethersproject/providers"
+import axios from "axios"
import {
BitcoinClient,
BitcoinTx,
@@ -9,12 +10,16 @@ import {
CrossChainDepositor,
Deposit,
DepositRequest,
+ DepositReceipt,
+ BitcoinRawTxVectors,
ElectrumClient,
ethereumAddressFromSigner,
EthereumBridge,
chainIdFromSigner,
Hex,
loadEthereumCoreContracts,
+ packRevealDepositParameters,
+ extractBitcoinRawTxVectors,
TBTC as SDK,
Chains,
DestinationChainName,
@@ -66,6 +71,7 @@ import { SupportedChainIds } from "../../networks/enums/networks"
import { getThresholdLibProvider } from "../../utils/getThresholdLib"
import { getEthereumDefaultProviderChainId } from "../../utils/getEnvVariable"
import { getCrossChainRpcUrl } from "../../networks/utils/getCrossChainRpcUrl"
+import { getApiEndpoints } from "./constants"
export enum BridgeActivityStatus {
PENDING = "PENDING",
@@ -352,6 +358,22 @@ export interface ITBTC {
*/
revealDeposit(utxo: BitcoinUtxo): Promise
+ /**
+ * Reveals the given deposit using gasless reveal through a relayer service.
+ * This method calls an external API to trigger the deposit transaction via a relayer.
+ * @param depositTx Bitcoin raw transaction vectors
+ * @param depositOutputIndex Index of the deposit output
+ * @param deposit Deposit receipt containing the deposit parameters
+ * @param vault Optional vault address
+ * @returns Transaction receipt from the gasless reveal
+ */
+ gaslessRevealDeposit(
+ depositTx: BitcoinRawTxVectors,
+ depositOutputIndex: number,
+ deposit: DepositReceipt,
+ vault?: ChainIdentifier
+ ): Promise
+
/**
* Gets a revealed deposit from the bridge.
* @param utxo Deposit UTXO of the revealed deposit
@@ -1107,6 +1129,46 @@ export class TBTC implements ITBTC {
const { value, ...transactionOutpoint } = utxo
if (!this._deposit) throw new EmptyDepositObjectError()
+ // Check if we should use gasless reveal for L1 networks
+ const isL1 =
+ !this._crossChainConfig.isCrossChain && this._ethereumConfig.chainId
+ if (isL1) {
+ // Use gasless reveal for L1 networks
+ const depositReceipt = this._deposit.getReceipt()
+
+ // Get the raw bitcoin transaction
+ const rawTx = await this._bitcoinClient.getRawTransaction(
+ utxo.transactionHash
+ )
+
+ // Extract transaction vectors
+ const depositTx = extractBitcoinRawTxVectors(rawTx)
+
+ // Get vault if available
+ const vaultAddress = this._tbtcVaultContract?.address
+ let vault: ChainIdentifier | undefined
+ if (vaultAddress) {
+ // Create a proper ChainIdentifier object
+ vault = {
+ identifierHex: vaultAddress.slice(2).toLowerCase(),
+ equals: function (other: ChainIdentifier): boolean {
+ return this.identifierHex === other.identifierHex
+ },
+ }
+ }
+
+ const receipt = await this.gaslessRevealDeposit(
+ depositTx,
+ utxo.outputIndex,
+ depositReceipt,
+ vault
+ )
+
+ this.removeDepositData()
+ return receipt
+ }
+
+ // Use regular reveal for L2/cross-chain networks
const result = await this._deposit.initiateMinting(transactionOutpoint)
this.removeDepositData()
@@ -1125,6 +1187,88 @@ export class TBTC implements ITBTC {
throw new Error("Unexpected result type from initiateMinting")
}
+ /**
+ * Reveals the given deposit using gasless reveal through a relayer service.
+ * This method calls an external API to trigger the deposit transaction via a relayer.
+ * @param {BitcoinRawTxVectors} depositTx Bitcoin raw transaction vectors
+ * @param {number} depositOutputIndex Index of the deposit output
+ * @param {DepositReceipt} deposit Deposit receipt containing the deposit parameters
+ * @param {ChainIdentifier} vault Optional vault address
+ * @return {Promise} Transaction receipt from the gasless reveal
+ */
+ gaslessRevealDeposit = async (
+ depositTx: BitcoinRawTxVectors,
+ depositOutputIndex: number,
+ deposit: DepositReceipt,
+ vault?: ChainIdentifier
+ ): Promise => {
+ const { fundingTx, reveal } = packRevealDepositParameters(
+ depositTx,
+ depositOutputIndex,
+ deposit,
+ vault
+ )
+
+ // For gasless reveals, we need the deposit owner address
+ const depositOwner = deposit.depositor
+
+ // Determine the API endpoint based on network
+ const isTestnet = this.bitcoinNetwork === BitcoinNetwork.Testnet
+ const apiEndpoints = getApiEndpoints(isTestnet)
+ const apiUrl = apiEndpoints.GASLESS_REVEAL
+
+ try {
+ const response = await axios.post(apiUrl, {
+ fundingTx,
+ reveal,
+ destinationChainDepositOwner: `0x${depositOwner.identifierHex}`,
+ })
+
+ const { data } = response
+ if (!data || data.status !== "success") {
+ throw new Error(
+ `Unexpected response from /api/gasless-reveal: ${JSON.stringify(
+ data
+ )}`
+ )
+ }
+
+ // Convert the response to match TransactionReceipt format
+ const receipt: TransactionReceipt = {
+ to: data.data.contractAddress || "",
+ from: "",
+ contractAddress: data.data.contractAddress || "",
+ transactionIndex: 0,
+ root: undefined,
+ gasUsed: BigNumber.from(0),
+ logsBloom: "",
+ blockHash: "",
+ transactionHash: data.data.transactionHash,
+ logs: [],
+ blockNumber: data.data.blockNumber,
+ confirmations: 0,
+ cumulativeGasUsed: BigNumber.from(0),
+ effectiveGasPrice: BigNumber.from(0),
+ byzantium: true,
+ type: 0,
+ status: data.data.status,
+ }
+
+ return receipt
+ } catch (error: any) {
+ if (error.response) {
+ console.error("Gasless reveal API error response:", error.response.data)
+ throw new Error(
+ error.response.data?.message ||
+ error.response.data?.error ||
+ "Failed to perform gasless reveal"
+ )
+ }
+ console.error("Error calling /api/gasless-reveal endpoint:", error)
+ throw error
+ }
+ }
+
getRevealedDeposit = async (utxo: BitcoinUtxo): Promise => {
const sdk = await this._getSdk()
const deposit = await sdk.tbtcContracts.bridge.deposits(
diff --git a/src/utils/isLocalhost.ts b/src/utils/isLocalhost.ts
new file mode 100644
index 000000000..95ca06698
--- /dev/null
+++ b/src/utils/isLocalhost.ts
@@ -0,0 +1,17 @@
+/**
+ * Check if the current environment is localhost/development
+ * @return {boolean} True if running on localhost
+ */
+export const isLocalhost = (): boolean => {
+ if (typeof window === "undefined") return false
+
+ const hostname = window.location.hostname
+ return (
+ hostname === "localhost" ||
+ hostname === "127.0.0.1" ||
+ hostname === "0.0.0.0" ||
+ hostname.startsWith("192.168.") ||
+ hostname.startsWith("10.") ||
+ hostname.endsWith(".local")
+ )
+}