diff --git a/packages/explorer-backend/scripts/refresh_pending.js b/packages/explorer-backend/scripts/refresh_pending.js
index 4996a1e04..2f39d6e1d 100644
--- a/packages/explorer-backend/scripts/refresh_pending.js
+++ b/packages/explorer-backend/scripts/refresh_pending.js
@@ -2,7 +2,9 @@ require('dotenv').config()
const wait = require('wait')
const rateLimitToken = process.env.RATE_LIMIT_TOKEN
+const token = process.argv[2] ?? ''
console.log('rateLimitToken set', !!rateLimitToken)
+console.log('token', token)
async function main () {
let page = 0
@@ -11,7 +13,7 @@ async function main () {
if (page === 2) {
break
}
- const url0 = `https://explorer-api.hop.exchange/v1/transfers?page=${page}&bonded=pending&rate_limit_token=${rateLimitToken}`
+ const url0 = `https://explorer-api.hop.exchange/v1/transfers?page=${page}&bonded=pending&rate_limit_token=${rateLimitToken}&token=${token}`
const response0 = await fetch(url0)
const json0 = await response0.json()
const transferIds = json0.data.map(transfer => transfer.transferId)
diff --git a/packages/frontend/public/commit-transfers/index.html b/packages/frontend/public/commit-transfers/index.html
new file mode 100644
index 000000000..7aa8f7713
--- /dev/null
+++ b/packages/frontend/public/commit-transfers/index.html
@@ -0,0 +1 @@
+
diff --git a/packages/frontend/src/AppRoutes.tsx b/packages/frontend/src/AppRoutes.tsx
index ef702e96e..e2e49f682 100644
--- a/packages/frontend/src/AppRoutes.tsx
+++ b/packages/frontend/src/AppRoutes.tsx
@@ -12,6 +12,7 @@ const Convert = lazy(() => import(/* webpackChunkName: "Convert" */ '#pages/Conv
const Stats = lazy(() => import(/* webpackChunkName: "Stats" */ '#pages/Stats/index.js'))
const Withdraw = lazy(() => import(/* webpackChunkName: "Withdraw" */ '#pages/Withdraw/index.js'))
const Relay = lazy(() => import(/* webpackChunkName: "Relay" */ '#pages/Relay/index.js'))
+const CommitTransfers = lazy(() => import(/* webpackChunkName: "CommitTransfers" */ '#pages/CommitTransfers/index.js'))
const Faucet = lazy(() => import(/* webpackChunkName: "Faucet" */ '#pages/Faucet/index.js'))
const Health = lazy(() => import(/* webpackChunkName: "Health" */ '#pages/Health/index.js'))
const Rewards = lazy(() => import(/* webpackChunkName: "Rewards" */ '#pages/Rewards/index.js'))
@@ -61,6 +62,7 @@ const AppRoutes: FC = () => {
} />
} />
} />
+ } />
} />
} />
} />
diff --git a/packages/frontend/src/pages/CommitTransfers/CommitTransfers.tsx b/packages/frontend/src/pages/CommitTransfers/CommitTransfers.tsx
new file mode 100644
index 000000000..905f76d87
--- /dev/null
+++ b/packages/frontend/src/pages/CommitTransfers/CommitTransfers.tsx
@@ -0,0 +1,227 @@
+import Box from '@mui/material/Box'
+import React, { ChangeEvent, FC, useEffect, useState } from 'react'
+import Typography from '@mui/material/Typography'
+import useQueryParams from '#hooks/useQueryParams.js'
+import { Alert } from '#components/Alert/index.js'
+import { Button } from '#components/Button/Button.js'
+import { formatError } from '#utils/format.js'
+import { makeStyles } from '@mui/styles'
+import { reactAppNetwork } from '#config/index.js'
+import { useApp } from '#contexts/AppContext/index.js'
+import { useWeb3Context } from '#contexts/Web3Context.js'
+import RaisedSelect from '#components/selects/RaisedSelect.js'
+import MenuItem from '@mui/material/MenuItem'
+import Link from '@mui/material/Link'
+import SelectOption from '#components/selects/SelectOption.js'
+import { l2Networks, allNetworks } from '#config/networks.js'
+import Network from '#models/Network.js'
+import { findNetworkBySlug, findMatchingBridge } from '#utils/index.js'
+
+const useStyles = makeStyles((theme: any) => ({
+ root: {
+ maxWidth: '680px',
+ margin: '0 auto',
+ },
+ header: {
+ marginBottom: '4rem',
+ textAlign: 'center',
+ },
+ form: {
+ display: 'block',
+ marginBottom: '4rem',
+ },
+ card: {
+ marginBottom: '4rem',
+ },
+ loader: {
+ marginTop: '2rem',
+ textAlign: 'center',
+ },
+ notice: {
+ display: 'flex',
+ alignItems: 'center',
+ flexDirection: 'column',
+ },
+}))
+
+export const CommitTransfers: FC = () => {
+ const styles = useStyles()
+ const { sdk, networks, txConfirm, bridges, selectedBridge, setSelectedBridge } = useApp()
+ const { checkConnectedNetworkId } = useWeb3Context()
+ const { queryParams } = useQueryParams()
+ const [loading, setLoading] = useState(false)
+ const [error, setError] = useState('')
+ const [success, setSuccess] = useState('')
+ const [commitInfoMsg, setCommitInfoMsg] = useState('')
+ const [selectedFromNetwork, setSelectedFromNetwork] = useState(() => {
+ try {
+ const selectedFromNetworkSlug = localStorage.getItem('commitTransfers:selectedFromNetwork')
+ if (selectedFromNetworkSlug) {
+ const network = findNetworkBySlug(selectedFromNetworkSlug, l2Networks)
+ if (network) {
+ return network
+ }
+ }
+ } catch (err: any) {
+ }
+ return l2Networks[0]
+ })
+
+ const [selectedToNetwork, setSelectedToNetwork] = useState(() => {
+ try {
+ const selectedToNetworkSlug = localStorage.getItem('commitTransfers:selectedToNetwork')
+ if (selectedToNetworkSlug) {
+ const network = findNetworkBySlug(selectedToNetworkSlug, allNetworks)
+ if (network) {
+ return network
+ }
+ }
+ } catch (err: any) {
+ }
+ return allNetworks[0]
+ })
+
+ useEffect(() => {
+ try {
+ localStorage.setItem('commitTransfers:selectedFromNetwork', selectedFromNetwork?.slug ?? '')
+ } catch (err: any) {
+ console.error(err)
+ }
+ }, [selectedFromNetwork])
+
+ useEffect(() => {
+ try {
+ localStorage.setItem('commitTransfers:selectedToNetwork', selectedToNetwork?.slug ?? '')
+ } catch (err: any) {
+ console.error(err)
+ }
+ }, [selectedToNetwork])
+
+ const handleBridgeChange = (event: any) => {
+ const tokenSymbol = event.target.value as string
+
+ const bridge = findMatchingBridge(bridges, tokenSymbol)
+ if (bridge) {
+ setSelectedBridge(bridge)
+ }
+ }
+
+ async function handleSubmit(event: ChangeEvent) {
+ event.preventDefault()
+ try {
+ setLoading(true)
+ setError('')
+ setSuccess('')
+ setCommitInfoMsg('')
+ await new Promise((resolve, reject) => {
+ const run = async () => {
+ console.log('selectedFromNetwork', selectedFromNetwork)
+ const l2Wallet = await sdk.getSignerOrProvider(selectedFromNetwork.slug)
+ const token = selectedBridge.getTokenSymbol()
+ console.log('reactAppNetwork', reactAppNetwork)
+ console.log('l2Wallet', l2Wallet)
+ console.log('token', token)
+ const commitTxHash = '' // for debugging
+ if (!commitTxHash) {
+ const bridge = sdk.bridge(token)
+ const isNetworkConnected = await checkConnectedNetworkId(selectedFromNetwork.networkId)
+ if (!isNetworkConnected) {
+ throw new Error('wrong network connected')
+ }
+ const tx = await bridge.commitTransfers(selectedFromNetwork.slug, selectedToNetwork.slug)
+ console.log('commitTxHash', tx.hash)
+ setCommitInfoMsg(`Commit transfers transaction hash: ${tx.hash}`)
+ }
+ setLoading(false)
+ resolve(null)
+ }
+ run().catch(reject)
+ })
+ } catch (err: any) {
+ console.error(err)
+ setCommitInfoMsg('')
+ setError(formatError(err))
+ }
+ setLoading(false)
+ }
+
+ return (
+
+
+ Commit Transfers
+
+
+ This is to commit the transfer root hash which is necessary for manually withdrawals.
+
+
+
+ {error}
+
+
+ {success}
+
+ {commitInfoMsg && (
+
+ {commitInfoMsg}
+
+ )}
+
+ After the transfer root has been committed, the transfer can be relayed using the Relay page.
+
+
+ )
+}
diff --git a/packages/frontend/src/pages/CommitTransfers/index.tsx b/packages/frontend/src/pages/CommitTransfers/index.tsx
new file mode 100644
index 000000000..021f1211c
--- /dev/null
+++ b/packages/frontend/src/pages/CommitTransfers/index.tsx
@@ -0,0 +1,3 @@
+import { CommitTransfers } from './CommitTransfers.js'
+
+export default CommitTransfers
diff --git a/packages/frontend/src/pages/Convert/ConvertContext.tsx b/packages/frontend/src/pages/Convert/ConvertContext.tsx
index 86d0824e1..9d1b1fa08 100644
--- a/packages/frontend/src/pages/Convert/ConvertContext.tsx
+++ b/packages/frontend/src/pages/Convert/ConvertContext.tsx
@@ -512,7 +512,7 @@ const ConvertProvider: FC<{ children: ReactNode }> = ({ children }) => {
useEffect(() => {
const isUSDCe = sourceToken?.symbol === 'hUSDC.e' || destToken?.symbol === 'USDC.e'
if (isUSDCe && destNetwork?.isL1) {
- setInfo(<>Notice: The USDC.e bonder was deprecated on March 20th, 2024. To transfer hUSDC.e to L1, you will need to wait for the full exit process (7+ days after root commit) before withdrawing. Please reach out on Discord if you have any questions.>)
+ setInfo(<>Notice: The USDC.e bonder was deprecated on March 20th, 2024. To transfer hUSDC.e to L1, you will need to wait for the full exit process (7+ days after root commit) before withdrawing. After you have intiated the withdrawal on this page, you must commit the transfer root hash on the Commit Transfers Page here. Please reach out on Discord if you have any questions.>)
} else {
setInfo(null)
}
diff --git a/packages/frontend/src/pages/Relay/Relay.tsx b/packages/frontend/src/pages/Relay/Relay.tsx
index ba80c4fef..97c87def3 100644
--- a/packages/frontend/src/pages/Relay/Relay.tsx
+++ b/packages/frontend/src/pages/Relay/Relay.tsx
@@ -122,7 +122,7 @@ export const Relay: FC = () => {
}
const l1Wallet = await sdk.getSignerOrProvider(l1Network.slug)
const l2Wallet = await sdk.getSignerOrProvider(selectedNetwork.slug)
- let token = selectedBridge.getTokenSymbol()
+ const token = selectedBridge.getTokenSymbol()
let subgraphToken = token
if (token === 'USDC.e') {
subgraphToken = 'USDC' // it needs to be USDC for graph lookups for USDC.e
diff --git a/packages/frontend/src/pages/Withdraw/Withdraw.tsx b/packages/frontend/src/pages/Withdraw/Withdraw.tsx
index 30cdc5e59..0117aa692 100644
--- a/packages/frontend/src/pages/Withdraw/Withdraw.tsx
+++ b/packages/frontend/src/pages/Withdraw/Withdraw.tsx
@@ -154,7 +154,7 @@ export const Withdraw: FC = () => {
if (data) {
const { transactionHash, fromChain, toChain, toChainId, nonceUsed } = data
if (nonceUsed) {
- reject(new Error('The withdrawal for this transfer has already been processed'))
+ reject(new Error('The withdrawal for this transfer has already been processed. The funds should have been received at the destination. No further action is required.'))
return
}
await txConfirm?.show({
diff --git a/packages/sdk/src/HopBridge.ts b/packages/sdk/src/HopBridge.ts
index 1064d1b68..e84e2cf2e 100644
--- a/packages/sdk/src/HopBridge.ts
+++ b/packages/sdk/src/HopBridge.ts
@@ -801,7 +801,14 @@ export class HopBridge extends Base {
public async commitTransfers (sourceChain: TChain, destinationChain: TChain) {
sourceChain = this.toChainModel(sourceChain)
destinationChain = this.toChainModel(destinationChain)
- const l2Bridge = await this.getL2Bridge(sourceChain)
+ let l2Bridge = await this.getL2Bridge(sourceChain)
+ if (sourceChain.slug === ChainSlug.Polygon) {
+ const address = this.getConfigAddresses(this.tokenSymbol, sourceChain)?.l2MessengerProxy
+ if (address) {
+ const provider = await this.getSignerOrProvider(sourceChain)
+ l2Bridge = L2_Bridge__factory.connect(address, provider)
+ }
+ }
return l2Bridge.commitTransfers(destinationChain.chainId)
}
diff --git a/packages/stats-worker/package.json b/packages/stats-worker/package.json
index a87a4f541..d507259f3 100644
--- a/packages/stats-worker/package.json
+++ b/packages/stats-worker/package.json
@@ -1,6 +1,6 @@
{
"name": "@hop-protocol/stats-worker",
- "version": "0.0.19",
+ "version": "0.0.20",
"description": "Hop Protocol Stats Worker",
"author": "Authereum Labs, Inc.",
"license": "MIT",
diff --git a/packages/stats-worker/src/YieldStats.ts b/packages/stats-worker/src/YieldStats.ts
index 450d2fb8d..179a2241a 100644
--- a/packages/stats-worker/src/YieldStats.ts
+++ b/packages/stats-worker/src/YieldStats.ts
@@ -328,7 +328,12 @@ class YieldStats {
break
}
}
- if (areAllChainsMissing) {
+ // Temp fix to handle times when transfers don't occur for more than 24h
+ const lowTransferCountToken = 'rETH'
+ if (
+ areAllChainsMissing &&
+ token !== lowTransferCountToken
+ ) {
delete yieldData.pools[token]
delete yieldData.optimalYield[token]
if (yieldData.stakingRewards?.[token]) {