Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/thin-seahorses-fry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@eth-optimism/sdk': patch
---

Add approval functions to the SDK
23 changes: 21 additions & 2 deletions packages/sdk/src/adapters/eth-bridge.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { ethers, Overrides } from 'ethers'
import { ethers, Contract, Overrides, BigNumber } from 'ethers'
import { TransactionRequest, BlockTag } from '@ethersproject/abstract-provider'
import { predeploys } from '@eth-optimism/contracts'
import { predeploys, getContractInterface } from '@eth-optimism/contracts'
import { hexStringEquals } from '@eth-optimism/core-utils'

import {
Expand All @@ -17,6 +17,14 @@ 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?: {
Expand Down Expand Up @@ -104,6 +112,17 @@ export class ETHBridgeAdapter extends StandardBridgeAdapter {
}

populateTransaction = {
approve: async (
l1Token: AddressLike,
l2Token: AddressLike,
amount: NumberLike,
opts?: {
overrides?: Overrides
}
): Promise<TransactionRequest> => {
throw new Error(`approvals not necessary for ETH bridge`)
},

deposit: async (
l1Token: AddressLike,
l2Token: AddressLike,
Expand Down
70 changes: 70 additions & 0 deletions packages/sdk/src/adapters/standard-bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,38 @@ export class StandardBridgeAdapter implements IBridgeAdapter {
}
}

public async approval(
l1Token: AddressLike,
l2Token: AddressLike,
signer: ethers.Signer
): Promise<BigNumber> {
if (!(await this.supportsTokenPair(l1Token, l2Token))) {
throw new Error(`token pair not supported by bridge`)
}

const token = new Contract(
toAddress(l1Token),
getContractInterface('L2StandardERC20'), // Any ERC20 will do
this.messenger.l1Provider
)

return token.allowance(await signer.getAddress(), this.l1Bridge.address)
}

public async approve(
l1Token: AddressLike,
l2Token: AddressLike,
amount: NumberLike,
signer: Signer,
opts?: {
overrides?: Overrides
}
): Promise<TransactionResponse> {
return signer.sendTransaction(
await this.populateTransaction.approve(l1Token, l2Token, amount, opts)
)
}

public async deposit(
l1Token: AddressLike,
l2Token: AddressLike,
Expand Down Expand Up @@ -217,6 +249,31 @@ export class StandardBridgeAdapter implements IBridgeAdapter {
}

populateTransaction = {
approve: async (
l1Token: AddressLike,
l2Token: AddressLike,
amount: NumberLike,
opts?: {
overrides?: Overrides
}
): Promise<TransactionRequest> => {
if (!(await this.supportsTokenPair(l1Token, l2Token))) {
throw new Error(`token pair not supported by bridge`)
}

const token = new Contract(
toAddress(l1Token),
getContractInterface('L2StandardERC20'), // Any ERC20 will do
this.messenger.l1Provider
)

return token.populateTransaction.approve(
this.l1Bridge.address,
amount,
opts?.overrides || {}
)
},

deposit: async (
l1Token: AddressLike,
l2Token: AddressLike,
Expand Down Expand Up @@ -288,6 +345,19 @@ export class StandardBridgeAdapter implements IBridgeAdapter {
}

estimateGas = {
approve: async (
l1Token: AddressLike,
l2Token: AddressLike,
amount: NumberLike,
opts?: {
overrides?: Overrides
}
): Promise<BigNumber> => {
return this.messenger.l1Provider.estimateGas(
await this.populateTransaction.approve(l1Token, l2Token, amount, opts)
)
},

deposit: async (
l1Token: AddressLike,
l2Token: AddressLike,
Expand Down
64 changes: 62 additions & 2 deletions packages/sdk/src/cross-chain-messenger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,15 @@ export class CrossChainMessenger implements ICrossChainMessenger {
if (Provider.isProvider(this.l1SignerOrProvider)) {
return this.l1SignerOrProvider
} else {
return this.l1SignerOrProvider.provider
return this.l1SignerOrProvider.provider as any
}
}

get l2Provider(): Provider {
if (Provider.isProvider(this.l2SignerOrProvider)) {
return this.l2SignerOrProvider
} else {
return this.l2SignerOrProvider.provider
return this.l2SignerOrProvider.provider as any
}
}

Expand Down Expand Up @@ -844,6 +844,36 @@ export class CrossChainMessenger implements ICrossChainMessenger {
)
}

public async approval(
l1Token: AddressLike,
l2Token: AddressLike,
opts?: {
signer?: Signer
}
): Promise<BigNumber> {
const bridge = await this.getBridgeForTokenPair(l1Token, l2Token)
return bridge.approval(l1Token, l2Token, opts?.signer || this.l1Signer)
}

public async approveERC20(
l1Token: AddressLike,
l2Token: AddressLike,
amount: NumberLike,
opts?: {
signer?: Signer
overrides?: Overrides
}
): Promise<TransactionResponse> {
return (opts?.signer || this.l1Signer).sendTransaction(
await this.populateTransaction.approveERC20(
l1Token,
l2Token,
amount,
opts
)
)
}

public async depositERC20(
l1Token: AddressLike,
l2Token: AddressLike,
Expand Down Expand Up @@ -986,6 +1016,18 @@ export class CrossChainMessenger implements ICrossChainMessenger {
)
},

approveERC20: async (
l1Token: AddressLike,
l2Token: AddressLike,
amount: NumberLike,
opts?: {
overrides?: Overrides
}
): Promise<TransactionRequest> => {
const bridge = await this.getBridgeForTokenPair(l1Token, l2Token)
return bridge.populateTransaction.approve(l1Token, l2Token, amount, opts)
},

depositERC20: async (
l1Token: AddressLike,
l2Token: AddressLike,
Expand Down Expand Up @@ -1082,6 +1124,24 @@ export class CrossChainMessenger implements ICrossChainMessenger {
)
},

approveERC20: async (
l1Token: AddressLike,
l2Token: AddressLike,
amount: NumberLike,
opts?: {
overrides?: Overrides
}
): Promise<BigNumber> => {
return this.l1Provider.estimateGas(
await this.populateTransaction.approveERC20(
l1Token,
l2Token,
amount,
opts
)
)
},

depositERC20: async (
l1Token: AddressLike,
l2Token: AddressLike,
Expand Down
73 changes: 73 additions & 0 deletions packages/sdk/src/interfaces/bridge-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,41 @@ export interface IBridgeAdapter {
l2Token: AddressLike
): Promise<boolean>

/**
* Queries the account's approval amount for a given L1 token.
*
* @param l1Token The L1 token address.
* @param l2Token The L2 token address.
* @param signer Signer to query the approval for.
* @returns Amount of tokens approved for deposits from the account.
*/
approval(
l1Token: AddressLike,
l2Token: AddressLike,
signer: Signer
): Promise<BigNumber>

/**
* Approves a deposit into the L2 chain.
*
* @param l1Token The L1 token address.
* @param l2Token The L2 token address.
* @param amount Amount of the token to approve.
* @param signer Signer used to sign and send the transaction.
* @param opts Additional options.
* @param opts.overrides Optional transaction overrides.
* @returns Transaction response for the approval transaction.
*/
approve(
l1Token: AddressLike,
l2Token: AddressLike,
amount: NumberLike,
signer: Signer,
opts?: {
overrides?: Overrides
}
): Promise<TransactionResponse>

/**
* Deposits some tokens into the L2 chain.
*
Expand Down Expand Up @@ -131,6 +166,25 @@ export interface IBridgeAdapter {
* Follows the pattern used by ethers.js.
*/
populateTransaction: {
/**
* Generates a transaction for approving some tokens to deposit into the L2 chain.
*
* @param l1Token The L1 token address.
* @param l2Token The L2 token address.
* @param amount Amount of the token to approve.
* @param opts Additional options.
* @param opts.overrides Optional transaction overrides.
* @returns Transaction that can be signed and executed to deposit the tokens.
*/
approve(
l1Token: AddressLike,
l2Token: AddressLike,
amount: NumberLike,
opts?: {
overrides?: Overrides
}
): Promise<TransactionRequest>

/**
* Generates a transaction for depositing some tokens into the L2 chain.
*
Expand Down Expand Up @@ -181,6 +235,25 @@ export interface IBridgeAdapter {
* Follows the pattern used by ethers.js.
*/
estimateGas: {
/**
* Estimates gas required to approve some tokens to deposit into the L2 chain.
*
* @param l1Token The L1 token address.
* @param l2Token The L2 token address.
* @param amount Amount of the token to approve.
* @param opts Additional options.
* @param opts.overrides Optional transaction overrides.
* @returns Gas estimate for the transaction.
*/
approve(
l1Token: AddressLike,
l2Token: AddressLike,
amount: NumberLike,
opts?: {
overrides?: Overrides
}
): Promise<BigNumber>

/**
* Estimates gas required to deposit some tokens into the L2 chain.
*
Expand Down
Loading