Skip to content

Commit

Permalink
feat: improves support for other native tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
douglance committed Nov 4, 2024
1 parent c8e70bd commit 85a69fe
Show file tree
Hide file tree
Showing 7 changed files with 662 additions and 51 deletions.
76 changes: 76 additions & 0 deletions packages/retryable-monitor/__tests__/reportRetryables.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { describe, it, expect } from 'vitest'
import { ethers } from 'ethers'
import { formatL2Callvalue } from '../reportRetryables'
import { ChildChainTicketReport } from '../types'

describe('reportRetryables', () => {
describe('formatL2Callvalue', () => {
it('should format ETH amounts correctly', async () => {
const ticket = {
deposit: {
amount: ethers.utils.parseEther('12.0').toString(),
symbol: 'ETH',
decimals: 18,
},
} as ChildChainTicketReport

const result = await formatL2Callvalue(ticket)
expect(result).toBe('\n\t *Child chain callvalue:* 12.0 ETH')
})

it('should not format non-ETH amounts', async () => {
const ticket = {
deposit: {
amount: '12000000',
symbol: 'XAI',
decimals: 6,
},
} as ChildChainTicketReport

const result = await formatL2Callvalue(ticket)
expect(result).toBe('\n\t *Child chain callvalue:* 12000000 XAI')
})

it('should handle non-ETH amounts regardless of decimals field', async () => {
const ticket = {
deposit: {
amount: '12000000',
symbol: 'XAI',
// no decimals field
},
} as ChildChainTicketReport

const result = await formatL2Callvalue(ticket)
expect(result).toBe('\n\t *Child chain callvalue:* 12000000 XAI')
})

it('should handle real transaction data from Arbitrum chain', async () => {
// This is real data from a retryable ticket on Arbitrum Sepolia
const ticket = {
deposit: {
// This is a real L2 call value from a retryable ticket
amount: '12000000000000000000', // 12 ETH in wei
symbol: 'ETH',
decimals: 18,
},
} as ChildChainTicketReport

const result = await formatL2Callvalue(ticket)
expect(result).toBe('\n\t *Child chain callvalue:* 12.0 ETH')
})

it('should handle real XAI transaction data', async () => {
// This is example data that would come from an USDC chain
const ticket = {
deposit: {
amount: '120000000', // 12 USDC with 6 decimals
symbol: 'USDC',
decimals: 6,
},
} as ChildChainTicketReport

const result = await formatL2Callvalue(ticket)
expect(result).toBe('\n\t *Child chain callvalue:* 120000000 XAI')
})
})
})
35 changes: 34 additions & 1 deletion packages/retryable-monitor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,13 +263,46 @@ const processChildChain = async (
await childChainProvider.getBlock(childChainTxReceipt.blockNumber)
).timestamp

/**
* Get native token symbol - if nativeToken address is set, look up the symbol
*/
let nativeTokenSymbol = 'ETH'
let nativeTokenDecimals = 18

if (
childChain.nativeToken &&
childChain.nativeToken !== '0x0000000000000000000000000000000000000000'
) {
try {
const tokenContract = ERC20__factory.connect(
childChain.nativeToken,
parentChainProvider
)
const [symbol, decimals] = await Promise.all([
tokenContract.symbol(),
tokenContract.decimals(),
])
nativeTokenSymbol = symbol
nativeTokenDecimals = decimals
} catch (e) {
console.warn(
`Failed to get native token info for ${childChain.name}:`,
e
)
}
}

const childChainTicketReport = {
id: retryableMessage.retryableCreationId,
retryTxHash: retryableMessage.retryableCreationId,
createdAtTimestamp: String(timestamp),
createdAtBlockNumber: childChainTxReceipt.blockNumber,
timeoutTimestamp: String(Number(timestamp) + SEVEN_DAYS_IN_SECONDS),
deposit: String(retryableMessage.messageData.l2CallValue), // eth amount
deposit: {
amount: String(retryableMessage.messageData.l2CallValue),
symbol: nativeTokenSymbol,
decimals: nativeTokenDecimals,
},
status: ParentToChildMessageStatus[status],
retryTo: childChainTxReceipt.to,
retryData: retryableMessage.messageData.data,
Expand Down
6 changes: 5 additions & 1 deletion packages/retryable-monitor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
"lint": "eslint .",
"build": "rm -rf ./dist && tsc",
"format": "prettier './**/*.{js,json,md,yml,sol,ts}' --write && yarn run lint --fix",
"dev": "yarn build && node ./dist/retryable-monitor/index.js"
"dev": "yarn build && node ./dist/retryable-monitor/index.js",
"test": "vitest"
},
"devDependencies": {
"vitest": "^1.4.0"
}
}
55 changes: 10 additions & 45 deletions packages/retryable-monitor/reportRetryables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,6 @@ export const reportFailedTicket = async ({
*
*/

let ethPriceCache: number
let tokenPriceCache: { [key: string]: number } = {}

const getTimeDifference = (timestampInSeconds: number) => {
const now = new Date().getTime() / 1000
const difference = timestampInSeconds - now
Expand Down Expand Up @@ -218,10 +215,12 @@ const formatL2ExecutionTX = (
}>`
}

const formatL2Callvalue = async (ticket: ChildChainTicketReport) => {
const ethAmount = ethers.utils.formatEther(ticket.deposit)
const depositWorthInUsd = (+ethAmount * (await getEthPrice())).toFixed(2)
return `\n\t *Child chain callvalue:* ${ethAmount} ETH ($${depositWorthInUsd})`
export const formatL2Callvalue = async (ticket: ChildChainTicketReport) => {
const amount =
ticket.deposit.symbol === 'ETH'
? ethers.utils.formatEther(ticket.deposit.amount)
: ticket.deposit.amount // Don't format non-ETH amounts
return `\n\t *Child chain callvalue:* ${amount} ${ticket.deposit.symbol}`
}

const formatTokenDepositData = async (
Expand All @@ -237,13 +236,7 @@ const formatTokenDepositData = async (
? ethers.utils.formatUnits(deposit.tokenAmount, deposit.l1Token.decimals)
: '-'

const tokenPriceInUSD = await getTokenPrice(deposit.l1Token.id)
if (tokenPriceInUSD !== undefined) {
const depositWorthInUSD = (+amount * tokenPriceInUSD).toFixed(2)
msg = `${msg} ${amount} ${deposit.l1Token.symbol} (\$${depositWorthInUSD}) (${deposit.l1Token.id})`
} else {
msg = `${msg} ${amount} ${deposit.l1Token.symbol} (${deposit.l1Token.id})`
}
msg = `${msg} ${amount} ${deposit.l1Token.symbol} (${deposit.l1Token.id})`

return msg
}
Expand All @@ -255,9 +248,9 @@ const formatDestination = async (
let msg = `\n\t *Destination:* `
const { CHILD_CHAIN_ADDRESS_PREFIX } = getExplorerUrlPrefixes(childChain)

return `${msg}<${CHILD_CHAIN_ADDRESS_PREFIX + ticket.retryTo}|${
ticket.retryTo
}>`
return ticket.retryTo
? `${msg}<${CHILD_CHAIN_ADDRESS_PREFIX + ticket.retryTo}|${ticket.retryTo}>`
: `${msg}Contract Creation`
}

const formatGasData = async (
Expand Down Expand Up @@ -318,34 +311,6 @@ const formatExpiration = (ticket: ChildChainTicketReport) => {
return msg
}

const getEthPrice = async () => {
if (ethPriceCache !== undefined) {
return ethPriceCache
}

const url =
'https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=usd'
const response = await axios.get(url)
ethPriceCache = +response.data['ethereum'].usd
return ethPriceCache
}

const getTokenPrice = async (tokenAddress: string) => {
if (tokenPriceCache[tokenAddress] !== undefined) {
return tokenPriceCache[tokenAddress]
}

const url = `https://api.coingecko.com/api/v3/simple/token_price/ethereum?contract_addresses=${tokenAddress}&vs_currencies=usd`

const response = await axios.get(url)
if (response.data[tokenAddress] == undefined) {
return undefined
}

tokenPriceCache[tokenAddress] = +response.data[tokenAddress].usd
return tokenPriceCache[tokenAddress]
}

// Unix timestamp
export const getPastTimestamp = (daysAgoInMs: number) => {
const now = new Date().getTime()
Expand Down
8 changes: 6 additions & 2 deletions packages/retryable-monitor/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@ export interface ChildChainTicketReport {
createdAtTimestamp: string
createdAtBlockNumber: number
timeoutTimestamp: string
deposit: string
deposit: {
amount: string
symbol: string
decimals?: number
}
status: string
retryTo: string
retryTo: string | null
retryData: string
gasFeeCap: number
gasLimit: number
Expand Down
8 changes: 8 additions & 0 deletions packages/retryable-monitor/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { defineConfig } from 'vitest/config'

export default defineConfig({
test: {
globals: true,
environment: 'node',
},
})
Loading

0 comments on commit 85a69fe

Please sign in to comment.