@tokamak-network/thanos-sdk
Version:
Tools for working with Thanos
201 lines (188 loc) • 5.77 kB
text/typescript
/* eslint-disable @typescript-eslint/no-unused-vars */
import { ethers, Overrides, BigNumber } from 'ethers'
import { TransactionRequest, BlockTag } from '@ethersproject/abstract-provider'
import { predeploys } from '@tokamak-network/core-utils'
import {
NumberLike,
AddressLike,
TokenBridgeMessage,
MessageDirection,
} from '../interfaces'
import { toAddress, omit } from '../utils'
import { StandardBridgeAdapter } from './standard-bridge'
/**
* Bridge adapter for the ETH bridge.
*/
export class ETHBridgeAdapter extends StandardBridgeAdapter {
public async approval(
l1Token: AddressLike,
l2Token: AddressLike,
signer: ethers.Signer
): Promise<BigNumber> {
throw new Error(`approval not necessary for ETH bridge`)
}
public async getDepositsByAddress(
address: AddressLike,
opts?: {
fromBlock?: BlockTag
toBlock?: BlockTag
}
): Promise<TokenBridgeMessage[]> {
const events = await this.l1Bridge.queryFilter(
this.l1Bridge.filters.ETHDepositInitiated(address),
opts?.fromBlock,
opts?.toBlock
)
return events
.filter((event) => {
// Only find ETH withdrawals.
return this.supportsTokenPair(event.args.l1Token, event.args.l2Token)
})
.map((event) => {
return {
direction: MessageDirection.L1_TO_L2,
from: event.args.from,
to: event.args.to,
l1Token: ethers.constants.AddressZero,
l2Token: predeploys.ETH,
amount: event.args.amount,
data: event.args.extraData,
logIndex: event.logIndex,
blockNumber: event.blockNumber,
transactionHash: event.transactionHash,
}
})
.sort((a, b) => {
// Sort descending by block number
return b.blockNumber - a.blockNumber
})
}
public async getWithdrawalsByAddress(
address: AddressLike,
opts?: {
fromBlock?: BlockTag
toBlock?: BlockTag
}
): Promise<TokenBridgeMessage[]> {
const events = await this.l2Bridge.queryFilter(
this.l2Bridge.filters.WithdrawalInitiated(undefined, undefined, address),
opts?.fromBlock,
opts?.toBlock
)
return events
.filter((event) => {
// Only find ETH withdrawals.
return this.supportsTokenPair(event.args.l1Token, event.args.l2Token)
})
.map((event) => {
return {
direction: MessageDirection.L2_TO_L1,
from: event.args.from,
to: event.args.to,
l1Token: ethers.constants.AddressZero,
l2Token: predeploys.ETH,
amount: event.args.amount,
data: event.args.extraData,
logIndex: event.logIndex,
blockNumber: event.blockNumber,
transactionHash: event.transactionHash,
}
})
.sort((a, b) => {
// Sort descending by block number
return b.blockNumber - a.blockNumber
})
}
public async supportsTokenPair(
l1Token: AddressLike,
l2Token: AddressLike
): Promise<boolean> {
// Only support ETH deposits and withdrawals.
return this.filterEthDepositsAndWithdrawls(l1Token, l2Token)
}
populateTransaction = {
approve: async (
l1Token: AddressLike,
l2Token: AddressLike,
amount: NumberLike,
opts?: {
overrides?: Overrides
}
): Promise<never> => {
throw new Error(`approvals not necessary for ETH bridge`)
},
deposit: async (
l1Token: AddressLike,
l2Token: AddressLike,
amount: NumberLike,
opts?: {
recipient?: AddressLike
l2GasLimit?: NumberLike
overrides?: Overrides
}
): Promise<TransactionRequest> => {
if (!(await this.supportsTokenPair(l1Token, l2Token))) {
throw new Error(`token pair not supported by bridge`)
}
if (opts?.recipient === undefined) {
return this.l1Bridge.populateTransaction.bridgeETH(
amount,
opts?.l2GasLimit || 200_000, // Default to 200k gas limit.
'0x', // No data.
{
...omit(opts?.overrides || {}, 'value'),
value: amount,
}
)
} else {
return this.l1Bridge.populateTransaction.bridgeETHTo(
toAddress(opts.recipient),
amount,
opts?.l2GasLimit || 200_000, // Default to 200k gas limit.
'0x', // No data.
{
...omit(opts?.overrides || {}, 'value'),
value: amount,
}
)
}
},
withdraw: async (
l1Token: AddressLike,
l2Token: AddressLike,
amount: NumberLike,
opts?: {
recipient?: AddressLike
overrides?: Overrides
}
): Promise<TransactionRequest> => {
if (!(await this.supportsTokenPair(l1Token, l2Token))) {
throw new Error(`token pair not supported by bridge`)
}
if (opts?.recipient === undefined) {
return this.l2Bridge.populateTransaction.withdraw(
toAddress(l2Token),
amount,
0, // L1 gas not required.
'0x', // No data.
{
...omit(opts?.overrides || {}, 'value'),
value: 0, // we change the native token in L2 chain to TON, so pass zero as `value` in `_extraData`
}
)
} else {
return this.l2Bridge.populateTransaction.withdrawTo(
toAddress(l2Token),
toAddress(opts.recipient),
amount,
0, // L1 gas not required.
'0x', // No data.
{
...omit(opts?.overrides || {}, 'value'),
value: 0, // we change the native token in L2 chain to TON, so pass zero as `value` in `_extraData`
}
)
}
},
}
}