From f0b13bc456620004a1787f62e87f404d95272356 Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Thu, 26 Sep 2024 10:37:34 -0700 Subject: [PATCH] feat(synapse-interface): refund RFQ transaction [SLT-272] (#3197) * Txn transaction refund tracking * Update store to support tracking * Query FastBridge contract for `bridgeStatuses` to find refund status * Track bridge transaction `bridgeQuote.routerAddress` in store * Fetch FastBridge contract address when only provided router address * add translations --- .../components/_Transaction/_Transaction.tsx | 21 +- .../components/_Transaction/_Transactions.tsx | 1 + .../components/AnimatedProgressBar.tsx | 6 +- .../_Transaction/components/TimeRemaining.tsx | 10 +- .../components/TransactionSupport.tsx | 17 +- .../helpers/calculateEstimatedTimeStatus.ts | 3 + .../helpers/useBridgeTxUpdater.ts | 11 +- .../_Transaction/helpers/useTxRefundStatus.ts | 103 +++ .../constants/abis/fastBridge.json | 851 ++++++++++++++++++ .../constants/abis/fastBridgeRouter.json | 387 ++++++++ packages/synapse-interface/messages/ar.json | 2 + .../synapse-interface/messages/en-US.json | 2 + packages/synapse-interface/messages/es.json | 2 + packages/synapse-interface/messages/fr.json | 2 + packages/synapse-interface/messages/jp.json | 2 + packages/synapse-interface/messages/tr.json | 2 + .../synapse-interface/messages/zh-CN.json | 2 + .../pages/state-managed-bridge/index.tsx | 1 + .../slices/_transactions/reducer.ts | 17 +- .../slices/transactions/actions.ts | 1 + .../utils/hooks/use_TransactionsListener.ts | 1 + 21 files changed, 1430 insertions(+), 14 deletions(-) create mode 100644 packages/synapse-interface/components/_Transaction/helpers/useTxRefundStatus.ts create mode 100644 packages/synapse-interface/constants/abis/fastBridge.json create mode 100644 packages/synapse-interface/constants/abis/fastBridgeRouter.json diff --git a/packages/synapse-interface/components/_Transaction/_Transaction.tsx b/packages/synapse-interface/components/_Transaction/_Transaction.tsx index 1a1158825d..1aea90352b 100644 --- a/packages/synapse-interface/components/_Transaction/_Transaction.tsx +++ b/packages/synapse-interface/components/_Transaction/_Transaction.tsx @@ -19,6 +19,7 @@ import { TransactionSupport } from './components/TransactionSupport' import { RightArrow } from '@/components/icons/RightArrow' import { Address } from 'viem' import { useIsTxReverted } from './helpers/useIsTxReverted' +import { useTxRefundStatus } from './helpers/useTxRefundStatus' interface _TransactionProps { connectedAddress: string @@ -30,11 +31,12 @@ interface _TransactionProps { destinationToken: Token originTxHash: string bridgeModuleName: string + routerAddress: string estimatedTime: number // in seconds timestamp: number currentTime: number kappa?: string - status: 'pending' | 'completed' | 'reverted' + status: 'pending' | 'completed' | 'reverted' | 'refunded' disabled: boolean } @@ -49,6 +51,7 @@ export const _Transaction = ({ destinationToken, originTxHash, bridgeModuleName, + routerAddress, estimatedTime, timestamp, currentTime, @@ -80,6 +83,7 @@ export const _Transaction = ({ isEstimatedTimeReached, isCheckTxStatus, isCheckTxForRevert, + isCheckTxForRefund, } = calculateEstimatedTimeStatus(currentTime, timestamp, estimatedTime) const [isTxCompleted, _kappa] = useBridgeTxStatus({ @@ -98,18 +102,29 @@ export const _Transaction = ({ isCheckTxForRevert && status === 'pending' ) + const isTxRefunded = useTxRefundStatus( + kappa, + routerAddress as Address, + originChain, + isCheckTxForRefund && + status === 'pending' && + bridgeModuleName === 'SynapseRFQ' + ) + useBridgeTxUpdater( connectedAddress, destinationChain, _kappa, originTxHash, isTxCompleted, - isTxReverted + isTxReverted, + isTxRefunded ) // Show transaction support if the transaction is delayed by more than 5 minutes and not finalized or reverted const showTransactionSupport = status === 'reverted' || + status === 'refunded' || (status === 'pending' && delayedTimeInMin && delayedTimeInMin <= -5) return ( @@ -184,7 +199,7 @@ export const _Transaction = ({ {status !== 'pending' && ( { const currentTime = getUnixTimeMinutesBeforeNow(0) const elapsedTime = currentTime - startTime @@ -25,7 +25,7 @@ export const AnimatedProgressBar = memo( const percentElapsed = (elapsedTime / estDuration) * 100 const isComplete = status === 'completed' - const isError = status === 'reverted' + const isError = status === 'reverted' || status === 'refunded' let duration = isComplete ? 0.5 : remainingTime diff --git a/packages/synapse-interface/components/_Transaction/components/TimeRemaining.tsx b/packages/synapse-interface/components/_Transaction/components/TimeRemaining.tsx index fda528f15c..f7920c9473 100644 --- a/packages/synapse-interface/components/_Transaction/components/TimeRemaining.tsx +++ b/packages/synapse-interface/components/_Transaction/components/TimeRemaining.tsx @@ -12,7 +12,7 @@ export const TimeRemaining = ({ isDelayed: boolean remainingTime: number delayedTime: number | null - status: 'pending' | 'completed' | 'reverted' + status: 'pending' | 'completed' | 'reverted' | 'refunded' }) => { const t = useTranslations('Time') @@ -36,6 +36,14 @@ export const TimeRemaining = ({ ) } + if (status === 'refunded') { + return ( + + {t('Refunded')} + + ) + } + if (isDelayed) { const delayedTimeInMin = Math.floor(delayedTime / 60) const absoluteDelayedTime = Math.abs(delayedTimeInMin) diff --git a/packages/synapse-interface/components/_Transaction/components/TransactionSupport.tsx b/packages/synapse-interface/components/_Transaction/components/TransactionSupport.tsx index 2803d73911..02b09ab5fa 100644 --- a/packages/synapse-interface/components/_Transaction/components/TransactionSupport.tsx +++ b/packages/synapse-interface/components/_Transaction/components/TransactionSupport.tsx @@ -1,8 +1,11 @@ import { useTranslations } from 'next-intl' import { TRANSACTION_SUPPORT_URL, DISCORD_URL } from '@/constants/urls' -export const TransactionSupport = ({ status }: { status: string }) => { - const isReverted = status === 'reverted' +export const TransactionSupport = ({ + status, +}: { + status: 'pending' | 'completed' | 'reverted' | 'refunded' +}) => { const t = useTranslations('Time') return ( @@ -10,12 +13,16 @@ export const TransactionSupport = ({ status }: { status: string }) => { id="transaction-support" className="flex items-center justify-between w-full py-1 pl-3 pr-1 text-sm" > - {isReverted ? ( + {status === 'reverted' && (
{t('Transaction reverted, funds returned')}
- ) : ( -
{t("What's taking so long?")}
)} + {status === 'refunded' && ( +
{t('Transaction refunded, funds returned')}
+ )} + + {status === 'pending' &&
{t("What's taking so long?")}
} +
30 + const isCheckTxForRefund = elapsedTime > fourHoursInSeconds const delayedTime = isEstimatedTimeReached ? remainingTime : null const delayedTimeInMin = remainingTime ? Math.floor(remainingTime / 60) : null @@ -33,5 +35,6 @@ export const calculateEstimatedTimeStatus = ( isEstimatedTimeReached, isCheckTxStatus, isCheckTxForRevert, + isCheckTxForRefund, } } diff --git a/packages/synapse-interface/components/_Transaction/helpers/useBridgeTxUpdater.ts b/packages/synapse-interface/components/_Transaction/helpers/useBridgeTxUpdater.ts index f493893111..9466b9f670 100644 --- a/packages/synapse-interface/components/_Transaction/helpers/useBridgeTxUpdater.ts +++ b/packages/synapse-interface/components/_Transaction/helpers/useBridgeTxUpdater.ts @@ -5,6 +5,7 @@ import { updateTransactionKappa, completeTransaction, revertTransaction, + refundTransaction, _TransactionDetails, } from '@/slices/_transactions/reducer' import { fetchAndStoreSingleNetworkPortfolioBalances } from '@/slices/portfolio/hooks' @@ -27,7 +28,8 @@ export const useBridgeTxUpdater = ( kappa: string, originTxHash: string, isTxComplete: boolean, - isTxReverted: boolean + isTxReverted: boolean, + isTxRefunded: boolean ) => { const dispatch = useAppDispatch() const { transactions } = use_TransactionsState() @@ -49,6 +51,13 @@ export const useBridgeTxUpdater = ( } }, [isTxReverted]) + /** Update tx for refunds in store */ + useEffect(() => { + if (isTxRefunded && storedTx.status !== 'refunded') { + dispatch(refundTransaction({ originTxHash })) + } + }, [isTxRefunded]) + /** Update tx for completion in store */ useEffect(() => { if (isTxComplete && originTxHash && kappa) { diff --git a/packages/synapse-interface/components/_Transaction/helpers/useTxRefundStatus.ts b/packages/synapse-interface/components/_Transaction/helpers/useTxRefundStatus.ts new file mode 100644 index 0000000000..351f97f04e --- /dev/null +++ b/packages/synapse-interface/components/_Transaction/helpers/useTxRefundStatus.ts @@ -0,0 +1,103 @@ +import { type Address } from 'viem' +import { isNumber, isString } from 'lodash' +import { useEffect, useState } from 'react' +import { readContract } from '@wagmi/core' + +import { type Chain } from '@/utils/types' +import { useIntervalTimer } from '@/utils/hooks/useIntervalTimer' +import { wagmiConfig } from '@/wagmiConfig' +import fastBridgeAbi from '@/constants/abis/fastBridge.json' +import fastBridgeRouterAbi from '@/constants/abis/fastBridgeRouter.json' + +enum BridgeStatus { + NULL, + REQUESTED, + RELAYER_PROVED, + RELAYER_CLAIMED, + REFUNDED, +} + +export const useTxRefundStatus = ( + txId: string | undefined, + routerAddress: Address, + chain: Chain, + checkForRefund: boolean +) => { + const [isRefunded, setIsRefunded] = useState(false) + const currentTime = useIntervalTimer(600000) + + const getTxRefundStatus = async () => { + try { + const bridgeContract = await getRFQBridgeContract( + routerAddress, + chain?.id + ) + + const status = await checkRFQTxBridgeStatus( + txId, + bridgeContract as Address, + chain?.id + ) + + if (status === BridgeStatus.REFUNDED) { + setIsRefunded(true) + } + } catch (error) { + console.error('Failed to get transaction refund status:', error) + } + } + + useEffect(() => { + if (checkForRefund) { + getTxRefundStatus() + } + }, [checkForRefund, txId, chain, currentTime]) + + return isRefunded +} + +const getRFQBridgeContract = async ( + routerAddress: Address, + chainId: number +): Promise => { + try { + const fastBridgeAddress = await readContract(wagmiConfig, { + abi: fastBridgeRouterAbi, + address: routerAddress, + functionName: 'fastBridge', + chainId, + }) + + if (!isString(fastBridgeAddress)) { + throw new Error('Invalid address') + } + + return fastBridgeAddress + } catch (error) { + throw new Error(error) + } +} + +const checkRFQTxBridgeStatus = async ( + txId: string, + bridgeContract: Address, + chainId: number +): Promise => { + try { + const status = await readContract(wagmiConfig, { + abi: fastBridgeAbi, + address: bridgeContract, + functionName: 'bridgeStatuses', + args: [txId], + chainId, + }) + + if (!isNumber(status)) { + throw new Error('Invalid status code') + } + + return status + } catch (error) { + throw new Error(error) + } +} diff --git a/packages/synapse-interface/constants/abis/fastBridge.json b/packages/synapse-interface/constants/abis/fastBridge.json new file mode 100644 index 0000000000..2efeb97a4b --- /dev/null +++ b/packages/synapse-interface/constants/abis/fastBridge.json @@ -0,0 +1,851 @@ +[ + { + "type": "constructor", + "inputs": [ + { "name": "_owner", "type": "address", "internalType": "address" } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "DEFAULT_ADMIN_ROLE", + "inputs": [], + "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "DISPUTE_PERIOD", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "FEE_BPS", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "FEE_RATE_MAX", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "GOVERNOR_ROLE", + "inputs": [], + "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "GUARD_ROLE", + "inputs": [], + "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "MIN_DEADLINE_PERIOD", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "REFUNDER_ROLE", + "inputs": [], + "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "REFUND_DELAY", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "RELAYER_ROLE", + "inputs": [], + "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "bridge", + "inputs": [ + { + "name": "params", + "type": "tuple", + "internalType": "struct IFastBridge.BridgeParams", + "components": [ + { + "name": "dstChainId", + "type": "uint32", + "internalType": "uint32" + }, + { "name": "sender", "type": "address", "internalType": "address" }, + { "name": "to", "type": "address", "internalType": "address" }, + { + "name": "originToken", + "type": "address", + "internalType": "address" + }, + { + "name": "destToken", + "type": "address", + "internalType": "address" + }, + { + "name": "originAmount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "destAmount", + "type": "uint256", + "internalType": "uint256" + }, + { "name": "sendChainGas", "type": "bool", "internalType": "bool" }, + { "name": "deadline", "type": "uint256", "internalType": "uint256" } + ] + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "bridgeProofs", + "inputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "outputs": [ + { "name": "timestamp", "type": "uint96", "internalType": "uint96" }, + { "name": "relayer", "type": "address", "internalType": "address" } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "bridgeRelays", + "inputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "bridgeStatuses", + "inputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "outputs": [ + { + "name": "", + "type": "uint8", + "internalType": "enum FastBridge.BridgeStatus" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "canClaim", + "inputs": [ + { + "name": "transactionId", + "type": "bytes32", + "internalType": "bytes32" + }, + { "name": "relayer", "type": "address", "internalType": "address" } + ], + "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "chainGasAmount", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "claim", + "inputs": [ + { "name": "request", "type": "bytes", "internalType": "bytes" }, + { "name": "to", "type": "address", "internalType": "address" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "deployBlock", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "dispute", + "inputs": [ + { + "name": "transactionId", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "getBridgeTransaction", + "inputs": [ + { "name": "request", "type": "bytes", "internalType": "bytes" } + ], + "outputs": [ + { + "name": "", + "type": "tuple", + "internalType": "struct IFastBridge.BridgeTransaction", + "components": [ + { + "name": "originChainId", + "type": "uint32", + "internalType": "uint32" + }, + { + "name": "destChainId", + "type": "uint32", + "internalType": "uint32" + }, + { + "name": "originSender", + "type": "address", + "internalType": "address" + }, + { + "name": "destRecipient", + "type": "address", + "internalType": "address" + }, + { + "name": "originToken", + "type": "address", + "internalType": "address" + }, + { + "name": "destToken", + "type": "address", + "internalType": "address" + }, + { + "name": "originAmount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "destAmount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "originFeeAmount", + "type": "uint256", + "internalType": "uint256" + }, + { "name": "sendChainGas", "type": "bool", "internalType": "bool" }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { "name": "nonce", "type": "uint256", "internalType": "uint256" } + ] + } + ], + "stateMutability": "pure" + }, + { + "type": "function", + "name": "getRoleAdmin", + "inputs": [ + { "name": "role", "type": "bytes32", "internalType": "bytes32" } + ], + "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getRoleMember", + "inputs": [ + { "name": "role", "type": "bytes32", "internalType": "bytes32" }, + { "name": "index", "type": "uint256", "internalType": "uint256" } + ], + "outputs": [{ "name": "", "type": "address", "internalType": "address" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getRoleMemberCount", + "inputs": [ + { "name": "role", "type": "bytes32", "internalType": "bytes32" } + ], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "grantRole", + "inputs": [ + { "name": "role", "type": "bytes32", "internalType": "bytes32" }, + { "name": "account", "type": "address", "internalType": "address" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "hasRole", + "inputs": [ + { "name": "role", "type": "bytes32", "internalType": "bytes32" }, + { "name": "account", "type": "address", "internalType": "address" } + ], + "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "nonce", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "protocolFeeRate", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "protocolFees", + "inputs": [{ "name": "", "type": "address", "internalType": "address" }], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "prove", + "inputs": [ + { "name": "request", "type": "bytes", "internalType": "bytes" }, + { "name": "destTxHash", "type": "bytes32", "internalType": "bytes32" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "refund", + "inputs": [ + { "name": "request", "type": "bytes", "internalType": "bytes" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "relay", + "inputs": [ + { "name": "request", "type": "bytes", "internalType": "bytes" } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "renounceRole", + "inputs": [ + { "name": "role", "type": "bytes32", "internalType": "bytes32" }, + { + "name": "callerConfirmation", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "revokeRole", + "inputs": [ + { "name": "role", "type": "bytes32", "internalType": "bytes32" }, + { "name": "account", "type": "address", "internalType": "address" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setChainGasAmount", + "inputs": [ + { + "name": "newChainGasAmount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setProtocolFeeRate", + "inputs": [ + { "name": "newFeeRate", "type": "uint256", "internalType": "uint256" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "supportsInterface", + "inputs": [ + { "name": "interfaceId", "type": "bytes4", "internalType": "bytes4" } + ], + "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "sweepProtocolFees", + "inputs": [ + { "name": "token", "type": "address", "internalType": "address" }, + { "name": "recipient", "type": "address", "internalType": "address" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "BridgeDepositClaimed", + "inputs": [ + { + "name": "transactionId", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "relayer", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "token", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "BridgeDepositRefunded", + "inputs": [ + { + "name": "transactionId", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "to", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "token", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "BridgeProofDisputed", + "inputs": [ + { + "name": "transactionId", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "relayer", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "BridgeProofProvided", + "inputs": [ + { + "name": "transactionId", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "relayer", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "transactionHash", + "type": "bytes32", + "indexed": false, + "internalType": "bytes32" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "BridgeRelayed", + "inputs": [ + { + "name": "transactionId", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "relayer", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "originChainId", + "type": "uint32", + "indexed": false, + "internalType": "uint32" + }, + { + "name": "originToken", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "destToken", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "originAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "destAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "chainGasAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "BridgeRequested", + "inputs": [ + { + "name": "transactionId", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "request", + "type": "bytes", + "indexed": false, + "internalType": "bytes" + }, + { + "name": "destChainId", + "type": "uint32", + "indexed": false, + "internalType": "uint32" + }, + { + "name": "originToken", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "destToken", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "originAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "destAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "sendChainGas", + "type": "bool", + "indexed": false, + "internalType": "bool" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ChainGasAmountUpdated", + "inputs": [ + { + "name": "oldChainGasAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "newChainGasAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "FeeRateUpdated", + "inputs": [ + { + "name": "oldFeeRate", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "newFeeRate", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "FeesSwept", + "inputs": [ + { + "name": "token", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "recipient", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RoleAdminChanged", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "previousAdminRole", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "newAdminRole", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RoleGranted", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "account", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RoleRevoked", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "account", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { "type": "error", "name": "AccessControlBadConfirmation", "inputs": [] }, + { + "type": "error", + "name": "AccessControlUnauthorizedAccount", + "inputs": [ + { "name": "account", "type": "address", "internalType": "address" }, + { "name": "neededRole", "type": "bytes32", "internalType": "bytes32" } + ] + }, + { + "type": "error", + "name": "AddressEmptyCode", + "inputs": [ + { "name": "target", "type": "address", "internalType": "address" } + ] + }, + { + "type": "error", + "name": "AddressInsufficientBalance", + "inputs": [ + { "name": "account", "type": "address", "internalType": "address" } + ] + }, + { "type": "error", "name": "AmountIncorrect", "inputs": [] }, + { "type": "error", "name": "ChainIncorrect", "inputs": [] }, + { "type": "error", "name": "DeadlineExceeded", "inputs": [] }, + { "type": "error", "name": "DeadlineNotExceeded", "inputs": [] }, + { "type": "error", "name": "DeadlineTooShort", "inputs": [] }, + { "type": "error", "name": "DisputePeriodNotPassed", "inputs": [] }, + { "type": "error", "name": "DisputePeriodPassed", "inputs": [] }, + { "type": "error", "name": "FailedInnerCall", "inputs": [] }, + { "type": "error", "name": "MsgValueIncorrect", "inputs": [] }, + { + "type": "error", + "name": "SafeERC20FailedOperation", + "inputs": [ + { "name": "token", "type": "address", "internalType": "address" } + ] + }, + { "type": "error", "name": "SenderIncorrect", "inputs": [] }, + { "type": "error", "name": "StatusIncorrect", "inputs": [] }, + { "type": "error", "name": "TokenNotContract", "inputs": [] }, + { "type": "error", "name": "TransactionRelayed", "inputs": [] }, + { "type": "error", "name": "ZeroAddress", "inputs": [] } +] diff --git a/packages/synapse-interface/constants/abis/fastBridgeRouter.json b/packages/synapse-interface/constants/abis/fastBridgeRouter.json new file mode 100644 index 0000000000..c5617fe1cf --- /dev/null +++ b/packages/synapse-interface/constants/abis/fastBridgeRouter.json @@ -0,0 +1,387 @@ +[ + { + "type": "constructor", + "inputs": [ + { + "name": "owner_", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "receive", + "stateMutability": "payable" + }, + { + "type": "function", + "name": "GAS_REBATE_FLAG", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes1", + "internalType": "bytes1" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "adapterSwap", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenIn", + "type": "address", + "internalType": "address" + }, + { + "name": "amountIn", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "amountOut", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "bridge", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "chainId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "token", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "originQuery", + "type": "tuple", + "internalType": "struct SwapQuery", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + }, + { + "name": "destQuery", + "type": "tuple", + "internalType": "struct SwapQuery", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "fastBridge", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getOriginAmountOut", + "inputs": [ + { + "name": "tokenIn", + "type": "address", + "internalType": "address" + }, + { + "name": "rfqTokens", + "type": "address[]", + "internalType": "address[]" + }, + { + "name": "amountIn", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "originQueries", + "type": "tuple[]", + "internalType": "struct SwapQuery[]", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "renounceOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setFastBridge", + "inputs": [ + { + "name": "fastBridge_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setSwapQuoter", + "inputs": [ + { + "name": "swapQuoter_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "swapQuoter", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "transferOwnership", + "inputs": [ + { + "name": "newOwner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "FastBridgeSet", + "inputs": [ + { + "name": "newFastBridge", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnershipTransferred", + "inputs": [ + { + "name": "previousOwner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newOwner", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SwapQuoterSet", + "inputs": [ + { + "name": "newSwapQuoter", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "DeadlineExceeded", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientOutputAmount", + "inputs": [] + }, + { + "type": "error", + "name": "MsgValueIncorrect", + "inputs": [] + }, + { + "type": "error", + "name": "PoolNotFound", + "inputs": [] + }, + { + "type": "error", + "name": "TokenAddressMismatch", + "inputs": [] + }, + { + "type": "error", + "name": "TokenNotContract", + "inputs": [] + }, + { + "type": "error", + "name": "TokenNotETH", + "inputs": [] + }, + { + "type": "error", + "name": "TokensIdentical", + "inputs": [] + } +] diff --git a/packages/synapse-interface/messages/ar.json b/packages/synapse-interface/messages/ar.json index fc6571c49e..a98de789cf 100644 --- a/packages/synapse-interface/messages/ar.json +++ b/packages/synapse-interface/messages/ar.json @@ -306,7 +306,9 @@ "Began": "بدأ", "Complete": "مكتمل", "Reverted": "تم الرجوع", + "Refunded": "تم استرداده", "Transaction reverted, funds returned": "تم الرجوع عن المعاملة، تم إرجاع الأموال", + "Transaction refunded, funds returned": "تم استرداد المعاملة، وتمت إعادة الأموال", "What's taking so long?": "لماذا يستغرق الأمر وقتًا طويلًا؟", "FAQ": "الأسئلة الشائعة", "Support": "الدعم", diff --git a/packages/synapse-interface/messages/en-US.json b/packages/synapse-interface/messages/en-US.json index 7c5a99c5d0..8ffc76e4a1 100644 --- a/packages/synapse-interface/messages/en-US.json +++ b/packages/synapse-interface/messages/en-US.json @@ -306,7 +306,9 @@ "Began": "Began", "Complete": "Complete", "Reverted": "Reverted", + "Refunded": "Refunded", "Transaction reverted, funds returned": "Transaction reverted, funds returned", + "Transaction refunded, funds returned": "Transaction refunded, funds returned", "What's taking so long?": "What's taking so long?", "FAQ": "F.A.Q", "Support": "Support", diff --git a/packages/synapse-interface/messages/es.json b/packages/synapse-interface/messages/es.json index c6ac15e7d8..d19e0158c7 100644 --- a/packages/synapse-interface/messages/es.json +++ b/packages/synapse-interface/messages/es.json @@ -306,7 +306,9 @@ "Began": "Comenzó", "Complete": "Completo", "Reverted": "Revertido", + "Refunded": "Reembolsado", "Transaction reverted, funds returned": "Transacción revertida, fondos devueltos", + "Transaction refunded, funds returned": "Transacción reembolsada, fondos devueltos", "What's taking so long?": "¿Por qué está tardando tanto?", "FAQ": "Preguntas frecuentes", "Support": "Soporte", diff --git a/packages/synapse-interface/messages/fr.json b/packages/synapse-interface/messages/fr.json index b5f249aaf1..8164e65434 100644 --- a/packages/synapse-interface/messages/fr.json +++ b/packages/synapse-interface/messages/fr.json @@ -306,7 +306,9 @@ "Began": "Commencé", "Complete": "Terminé", "Reverted": "Annulé", + "Refunded": "Remboursé", "Transaction reverted, funds returned": "Transaction annulée, fonds retournés", + "Transaction refunded, funds returned": "Transaction remboursée, fonds retournés", "What's taking so long?": "Qu'est-ce qui prend tant de temps ?", "FAQ": "FAQ", "Support": "Support", diff --git a/packages/synapse-interface/messages/jp.json b/packages/synapse-interface/messages/jp.json index c187de083b..b5b97ef750 100644 --- a/packages/synapse-interface/messages/jp.json +++ b/packages/synapse-interface/messages/jp.json @@ -306,7 +306,9 @@ "Began": "開始", "Complete": "完了", "Reverted": "取り消し", + "Refunded": "返金済み", "Transaction reverted, funds returned": "取引が取り消されました。資金が返還されました", + "Transaction refunded, funds returned": "取引が返金され、資金が戻されました", "What's taking so long?": "なぜ時間がかかっているのですか?", "FAQ": "よくある質問", "Support": "サポート", diff --git a/packages/synapse-interface/messages/tr.json b/packages/synapse-interface/messages/tr.json index dbe44d71b2..2afcf1ec6b 100644 --- a/packages/synapse-interface/messages/tr.json +++ b/packages/synapse-interface/messages/tr.json @@ -306,7 +306,9 @@ "Began": "Başladı", "Complete": "Tamamlandı", "Reverted": "Geri Alındı", + "Refunded": "İade edildi", "Transaction reverted, funds returned": "İşlem geri alındı, fonlar iade edildi", + "Transaction refunded, funds returned": "İşlem iade edildi, fonlar geri gönderildi", "What's taking so long?": "Neden bu kadar uzun sürüyor?", "FAQ": "SSS", "Support": "Destek", diff --git a/packages/synapse-interface/messages/zh-CN.json b/packages/synapse-interface/messages/zh-CN.json index 0d5cb9e02d..639c863131 100644 --- a/packages/synapse-interface/messages/zh-CN.json +++ b/packages/synapse-interface/messages/zh-CN.json @@ -306,7 +306,9 @@ "Began": "已开始", "Complete": "已完成", "Reverted": "已回退", + "Refunded": "已退款", "Transaction reverted, funds returned": "交易已回退,资金已退还", + "Transaction refunded, funds returned": "交易已退款,资金已退还", "What's taking so long?": "为什么这么久?", "FAQ": "常见问题", "Support": "支持", diff --git a/packages/synapse-interface/pages/state-managed-bridge/index.tsx b/packages/synapse-interface/pages/state-managed-bridge/index.tsx index e3128e0d6d..2a63c4ba56 100644 --- a/packages/synapse-interface/pages/state-managed-bridge/index.tsx +++ b/packages/synapse-interface/pages/state-managed-bridge/index.tsx @@ -302,6 +302,7 @@ const StateManagedBridge = () => { estimatedTime: bridgeQuote.estimatedTime, bridgeModuleName: bridgeQuote.bridgeModuleName, destinationAddress: destinationAddress, + routerAddress: bridgeQuote.routerAddress, }) ) try { diff --git a/packages/synapse-interface/slices/_transactions/reducer.ts b/packages/synapse-interface/slices/_transactions/reducer.ts index 2225c8ead0..cac413c616 100644 --- a/packages/synapse-interface/slices/_transactions/reducer.ts +++ b/packages/synapse-interface/slices/_transactions/reducer.ts @@ -14,10 +14,11 @@ export interface _TransactionDetails { originValue: string originTxHash: string bridgeModuleName: string + routerAddress: string estimatedTime: number timestamp: number kappa?: string - status: 'pending' | 'completed' | 'reverted' + status: 'pending' | 'completed' | 'reverted' | 'refunded' } export interface _TransactionsState { @@ -87,6 +88,19 @@ export const transactionsSlice = createSlice({ state.transactions[txIndex].status = 'reverted' } }, + refundTransaction: ( + state, + action: PayloadAction<{ originTxHash: string }> + ) => { + const { originTxHash } = action.payload + + const txIndex = state.transactions.findIndex( + (tx) => tx.originTxHash === originTxHash + ) + if (txIndex !== -1) { + state.transactions[txIndex].status = 'refunded' + } + }, clearTransactions: (state) => { state.transactions = [] }, @@ -99,6 +113,7 @@ export const { updateTransactionKappa, completeTransaction, revertTransaction, + refundTransaction, clearTransactions, } = transactionsSlice.actions diff --git a/packages/synapse-interface/slices/transactions/actions.ts b/packages/synapse-interface/slices/transactions/actions.ts index cb4ef22cc7..68400faaa9 100644 --- a/packages/synapse-interface/slices/transactions/actions.ts +++ b/packages/synapse-interface/slices/transactions/actions.ts @@ -16,6 +16,7 @@ export interface PendingBridgeTransaction { isSubmitted: boolean estimatedTime: number bridgeModuleName: string + routerAddress: string destinationAddress: Address | null } diff --git a/packages/synapse-interface/utils/hooks/use_TransactionsListener.ts b/packages/synapse-interface/utils/hooks/use_TransactionsListener.ts index b82d6ca8fe..e2f7b85697 100644 --- a/packages/synapse-interface/utils/hooks/use_TransactionsListener.ts +++ b/packages/synapse-interface/utils/hooks/use_TransactionsListener.ts @@ -50,6 +50,7 @@ export const use_TransactionsListener = () => { destinationChain: tx.destinationChain, destinationToken: tx.destinationToken, bridgeModuleName: tx.bridgeModuleName, + routerAddress: tx.routerAddress, estimatedTime: tx.estimatedTime, timestamp: tx.id, status: 'pending',