From fd6ea3ee9ef6f39d0d96281f4234e370fae6ffa0 Mon Sep 17 00:00:00 2001
From: Kelvin Fichter <kelvin@optimism.io>
Date: Mon, 7 Feb 2022 12:33:26 -0500
Subject: [PATCH] feat(sdk): allow withdrawals or deposits to target

This commit updates the SDK to add support for withdrawing or depositing
to a target address (as opposed to simply depositing or withdrawing to
yourself).
---
 .changeset/big-windows-shout.md               |  5 ++
 packages/sdk/src/adapters/eth-bridge.ts       | 55 ++++++++++++-----
 packages/sdk/src/adapters/standard-bridge.ts  | 59 ++++++++++++++-----
 packages/sdk/src/cross-chain-messenger.ts     | 12 ++++
 packages/sdk/src/interfaces/bridge-adapter.ts | 12 ++++
 .../src/interfaces/cross-chain-messenger.ts   | 24 ++++++++
 6 files changed, 137 insertions(+), 30 deletions(-)
 create mode 100644 .changeset/big-windows-shout.md

diff --git a/.changeset/big-windows-shout.md b/.changeset/big-windows-shout.md
new file mode 100644
index 000000000..d6b5b4250
--- /dev/null
+++ b/.changeset/big-windows-shout.md
@@ -0,0 +1,5 @@
+---
+'@eth-optimism/sdk': patch
+---
+
+Adds support for depositing or withdrawing to a target address
diff --git a/packages/sdk/src/adapters/eth-bridge.ts b/packages/sdk/src/adapters/eth-bridge.ts
index ad1360da0..429f85b38 100644
--- a/packages/sdk/src/adapters/eth-bridge.ts
+++ b/packages/sdk/src/adapters/eth-bridge.ts
@@ -100,6 +100,7 @@ export class ETHBridgeAdapter extends StandardBridgeAdapter {
       l2Token: AddressLike,
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         l2GasLimit?: NumberLike
         overrides?: Overrides
       }
@@ -108,14 +109,26 @@ export class ETHBridgeAdapter extends StandardBridgeAdapter {
         throw new Error(`token pair not supported by bridge`)
       }
 
-      return this.l1Bridge.populateTransaction.depositETH(
-        opts?.l2GasLimit || 200_000, // Default to 200k gas limit.
-        '0x', // No data.
-        {
-          ...omit(opts?.overrides || {}, 'value'),
-          value: amount,
-        }
-      )
+      if (opts?.recipient === undefined) {
+        return this.l1Bridge.populateTransaction.depositETH(
+          opts?.l2GasLimit || 200_000, // Default to 200k gas limit.
+          '0x', // No data.
+          {
+            ...omit(opts?.overrides || {}, 'value'),
+            value: amount,
+          }
+        )
+      } else {
+        return this.l1Bridge.populateTransaction.depositETHTo(
+          toAddress(opts.recipient),
+          opts?.l2GasLimit || 200_000, // Default to 200k gas limit.
+          '0x', // No data.
+          {
+            ...omit(opts?.overrides || {}, 'value'),
+            value: amount,
+          }
+        )
+      }
     },
 
     withdraw: async (
@@ -123,6 +136,7 @@ export class ETHBridgeAdapter extends StandardBridgeAdapter {
       l2Token: AddressLike,
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         overrides?: Overrides
       }
     ): Promise<TransactionRequest> => {
@@ -130,13 +144,24 @@ export class ETHBridgeAdapter extends StandardBridgeAdapter {
         throw new Error(`token pair not supported by bridge`)
       }
 
-      return this.l2Bridge.populateTransaction.withdraw(
-        toAddress(l2Token),
-        amount,
-        0, // L1 gas not required.
-        '0x', // No data.
-        opts?.overrides || {}
-      )
+      if (opts?.recipient === undefined) {
+        return this.l2Bridge.populateTransaction.withdraw(
+          toAddress(l2Token),
+          amount,
+          0, // L1 gas not required.
+          '0x', // No data.
+          opts?.overrides || {}
+        )
+      } else {
+        return this.l2Bridge.populateTransaction.withdrawTo(
+          toAddress(l2Token),
+          toAddress(opts.recipient),
+          amount,
+          0, // L1 gas not required.
+          '0x', // No data.
+          opts?.overrides || {}
+        )
+      }
     },
   }
 }
diff --git a/packages/sdk/src/adapters/standard-bridge.ts b/packages/sdk/src/adapters/standard-bridge.ts
index f2bcbf025..7acd2f847 100644
--- a/packages/sdk/src/adapters/standard-bridge.ts
+++ b/packages/sdk/src/adapters/standard-bridge.ts
@@ -208,6 +208,7 @@ export class StandardBridgeAdapter implements IBridgeAdapter {
     amount: NumberLike,
     signer: Signer,
     opts?: {
+      recipient?: AddressLike
       l2GasLimit?: NumberLike
       overrides?: Overrides
     }
@@ -223,6 +224,7 @@ export class StandardBridgeAdapter implements IBridgeAdapter {
     amount: NumberLike,
     signer: Signer,
     opts?: {
+      recipient?: AddressLike
       overrides?: Overrides
     }
   ): Promise<TransactionResponse> {
@@ -237,6 +239,7 @@ export class StandardBridgeAdapter implements IBridgeAdapter {
       l2Token: AddressLike,
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         l2GasLimit?: NumberLike
         overrides?: Overrides
       }
@@ -245,14 +248,26 @@ export class StandardBridgeAdapter implements IBridgeAdapter {
         throw new Error(`token pair not supported by bridge`)
       }
 
-      return this.l1Bridge.populateTransaction.depositERC20(
-        toAddress(l1Token),
-        toAddress(l2Token),
-        amount,
-        opts?.l2GasLimit || 200_000, // Default to 200k gas limit.
-        '0x', // No data.
-        opts?.overrides || {}
-      )
+      if (opts?.recipient === undefined) {
+        return this.l1Bridge.populateTransaction.depositERC20(
+          toAddress(l1Token),
+          toAddress(l2Token),
+          amount,
+          opts?.l2GasLimit || 200_000, // Default to 200k gas limit.
+          '0x', // No data.
+          opts?.overrides || {}
+        )
+      } else {
+        return this.l1Bridge.populateTransaction.depositERC20To(
+          toAddress(l1Token),
+          toAddress(l2Token),
+          toAddress(opts.recipient),
+          amount,
+          opts?.l2GasLimit || 200_000, // Default to 200k gas limit.
+          '0x', // No data.
+          opts?.overrides || {}
+        )
+      }
     },
 
     withdraw: async (
@@ -260,6 +275,7 @@ export class StandardBridgeAdapter implements IBridgeAdapter {
       l2Token: AddressLike,
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         overrides?: Overrides
       }
     ): Promise<TransactionRequest> => {
@@ -267,13 +283,24 @@ export class StandardBridgeAdapter implements IBridgeAdapter {
         throw new Error(`token pair not supported by bridge`)
       }
 
-      return this.l2Bridge.populateTransaction.withdraw(
-        toAddress(l2Token),
-        amount,
-        0, // L1 gas not required.
-        '0x', // No data.
-        opts?.overrides || {}
-      )
+      if (opts?.recipient === undefined) {
+        return this.l2Bridge.populateTransaction.withdraw(
+          toAddress(l2Token),
+          amount,
+          0, // L1 gas not required.
+          '0x', // No data.
+          opts?.overrides || {}
+        )
+      } else {
+        return this.l2Bridge.populateTransaction.withdrawTo(
+          toAddress(l2Token),
+          toAddress(opts.recipient),
+          amount,
+          0, // L1 gas not required.
+          '0x', // No data.
+          opts?.overrides || {}
+        )
+      }
     },
   }
 
@@ -283,6 +310,7 @@ export class StandardBridgeAdapter implements IBridgeAdapter {
       l2Token: AddressLike,
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         l2GasLimit?: NumberLike
         overrides?: Overrides
       }
@@ -297,6 +325,7 @@ export class StandardBridgeAdapter implements IBridgeAdapter {
       l2Token: AddressLike,
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         overrides?: Overrides
       }
     ): Promise<BigNumber> => {
diff --git a/packages/sdk/src/cross-chain-messenger.ts b/packages/sdk/src/cross-chain-messenger.ts
index e1d4b9103..6217dd199 100644
--- a/packages/sdk/src/cross-chain-messenger.ts
+++ b/packages/sdk/src/cross-chain-messenger.ts
@@ -815,6 +815,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
   public async depositETH(
     amount: NumberLike,
     opts?: {
+      recipient?: AddressLike
       signer?: Signer
       l2GasLimit?: NumberLike
       overrides?: Overrides
@@ -828,6 +829,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
   public async withdrawETH(
     amount: NumberLike,
     opts?: {
+      recipient?: AddressLike
       signer?: Signer
       overrides?: Overrides
     }
@@ -842,6 +844,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
     l2Token: AddressLike,
     amount: NumberLike,
     opts?: {
+      recipient?: AddressLike
       signer?: Signer
       l2GasLimit?: NumberLike
       overrides?: Overrides
@@ -862,6 +865,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
     l2Token: AddressLike,
     amount: NumberLike,
     opts?: {
+      recipient?: AddressLike
       signer?: Signer
       overrides?: Overrides
     }
@@ -949,6 +953,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
     depositETH: async (
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         l2GasLimit?: NumberLike
         overrides?: Overrides
       }
@@ -964,6 +969,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
     withdrawETH: async (
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         overrides?: Overrides
       }
     ): Promise<TransactionRequest> => {
@@ -980,6 +986,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
       l2Token: AddressLike,
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         l2GasLimit?: NumberLike
         overrides?: Overrides
       }
@@ -993,6 +1000,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
       l2Token: AddressLike,
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         overrides?: Overrides
       }
     ): Promise<TransactionRequest> => {
@@ -1047,6 +1055,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
     depositETH: async (
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         l2GasLimit?: NumberLike
         overrides?: Overrides
       }
@@ -1059,6 +1068,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
     withdrawETH: async (
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         overrides?: Overrides
       }
     ): Promise<BigNumber> => {
@@ -1072,6 +1082,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
       l2Token: AddressLike,
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         l2GasLimit?: NumberLike
         overrides?: Overrides
       }
@@ -1091,6 +1102,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
       l2Token: AddressLike,
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         overrides?: Overrides
       }
     ): Promise<BigNumber> => {
diff --git a/packages/sdk/src/interfaces/bridge-adapter.ts b/packages/sdk/src/interfaces/bridge-adapter.ts
index f027197dd..cb1c848fa 100644
--- a/packages/sdk/src/interfaces/bridge-adapter.ts
+++ b/packages/sdk/src/interfaces/bridge-adapter.ts
@@ -109,6 +109,7 @@ export interface IBridgeAdapter {
    * @param amount Amount of the token to deposit.
    * @param signer Signer used to sign and send the transaction.
    * @param opts Additional options.
+   * @param opts.recipient Optional address to receive the funds on L2. Defaults to sender.
    * @param opts.l2GasLimit Optional gas limit to use for the transaction on L2.
    * @param opts.overrides Optional transaction overrides.
    * @returns Transaction response for the deposit transaction.
@@ -119,6 +120,7 @@ export interface IBridgeAdapter {
     amount: NumberLike,
     signer: Signer,
     opts?: {
+      recipient?: AddressLike
       l2GasLimit?: NumberLike
       overrides?: Overrides
     }
@@ -132,6 +134,7 @@ export interface IBridgeAdapter {
    * @param amount Amount of the token to withdraw.
    * @param signer Signer used to sign and send the transaction.
    * @param opts Additional options.
+   * @param opts.recipient Optional address to receive the funds on L1. Defaults to sender.
    * @param opts.overrides Optional transaction overrides.
    * @returns Transaction response for the withdraw transaction.
    */
@@ -141,6 +144,7 @@ export interface IBridgeAdapter {
     amount: NumberLike,
     signer: Signer,
     opts?: {
+      recipient?: AddressLike
       overrides?: Overrides
     }
   ): Promise<TransactionResponse>
@@ -157,6 +161,7 @@ export interface IBridgeAdapter {
      * @param l2Token The L2 token address.
      * @param amount Amount of the token to deposit.
      * @param opts Additional options.
+     * @param opts.recipient Optional address to receive the funds on L2. Defaults to sender.
      * @param opts.l2GasLimit Optional gas limit to use for the transaction on L2.
      * @param opts.overrides Optional transaction overrides.
      * @returns Transaction that can be signed and executed to deposit the tokens.
@@ -166,6 +171,7 @@ export interface IBridgeAdapter {
       l2Token: AddressLike,
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         l2GasLimit?: NumberLike
         overrides?: Overrides
       }
@@ -178,6 +184,7 @@ export interface IBridgeAdapter {
      * @param l2Token The L2 token address.
      * @param amount Amount of the token to withdraw.
      * @param opts Additional options.
+     * @param opts.recipient Optional address to receive the funds on L1. Defaults to sender.
      * @param opts.overrides Optional transaction overrides.
      * @returns Transaction that can be signed and executed to withdraw the tokens.
      */
@@ -186,6 +193,7 @@ export interface IBridgeAdapter {
       l2Token: AddressLike,
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         overrides?: Overrides
       }
     ): Promise<TransactionRequest>
@@ -203,6 +211,7 @@ export interface IBridgeAdapter {
      * @param l2Token The L2 token address.
      * @param amount Amount of the token to deposit.
      * @param opts Additional options.
+     * @param opts.recipient Optional address to receive the funds on L2. Defaults to sender.
      * @param opts.l2GasLimit Optional gas limit to use for the transaction on L2.
      * @param opts.overrides Optional transaction overrides.
      * @returns Gas estimate for the transaction.
@@ -212,6 +221,7 @@ export interface IBridgeAdapter {
       l2Token: AddressLike,
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         l2GasLimit?: NumberLike
         overrides?: Overrides
       }
@@ -224,6 +234,7 @@ export interface IBridgeAdapter {
      * @param l2Token The L2 token address.
      * @param amount Amount of the token to withdraw.
      * @param opts Additional options.
+     * @param opts.recipient Optional address to receive the funds on L1. Defaults to sender.
      * @param opts.overrides Optional transaction overrides.
      * @returns Gas estimate for the transaction.
      */
@@ -232,6 +243,7 @@ export interface IBridgeAdapter {
       l2Token: AddressLike,
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         overrides?: Overrides
       }
     ): Promise<BigNumber>
diff --git a/packages/sdk/src/interfaces/cross-chain-messenger.ts b/packages/sdk/src/interfaces/cross-chain-messenger.ts
index af73739d6..ba91e3d6c 100644
--- a/packages/sdk/src/interfaces/cross-chain-messenger.ts
+++ b/packages/sdk/src/interfaces/cross-chain-messenger.ts
@@ -397,6 +397,7 @@ export interface ICrossChainMessenger {
    * @param amount Amount of ETH to deposit (in wei).
    * @param opts Additional options.
    * @param opts.signer Optional signer to use to send the transaction.
+   * @param opts.recipient Optional address to receive the funds on L2. Defaults to sender.
    * @param opts.l2GasLimit Optional gas limit to use for the transaction on L2.
    * @param opts.overrides Optional transaction overrides.
    * @returns Transaction response for the deposit transaction.
@@ -405,6 +406,7 @@ export interface ICrossChainMessenger {
     amount: NumberLike,
     opts?: {
       signer?: Signer
+      recipient?: AddressLike
       l2GasLimit?: NumberLike
       overrides?: Overrides
     }
@@ -416,6 +418,7 @@ export interface ICrossChainMessenger {
    * @param amount Amount of ETH to withdraw.
    * @param opts Additional options.
    * @param opts.signer Optional signer to use to send the transaction.
+   * @param opts.recipient Optional address to receive the funds on L1. Defaults to sender.
    * @param opts.overrides Optional transaction overrides.
    * @returns Transaction response for the withdraw transaction.
    */
@@ -423,6 +426,7 @@ export interface ICrossChainMessenger {
     amount: NumberLike,
     opts?: {
       signer?: Signer
+      recipient?: AddressLike
       overrides?: Overrides
     }
   ): Promise<TransactionResponse>
@@ -435,6 +439,7 @@ export interface ICrossChainMessenger {
    * @param amount Amount to deposit.
    * @param opts Additional options.
    * @param opts.signer Optional signer to use to send the transaction.
+   * @param opts.recipient Optional address to receive the funds on L2. Defaults to sender.
    * @param opts.l2GasLimit Optional gas limit to use for the transaction on L2.
    * @param opts.overrides Optional transaction overrides.
    * @returns Transaction response for the deposit transaction.
@@ -445,6 +450,7 @@ export interface ICrossChainMessenger {
     amount: NumberLike,
     opts?: {
       signer?: Signer
+      recipient?: AddressLike
       l2GasLimit?: NumberLike
       overrides?: Overrides
     }
@@ -458,6 +464,7 @@ export interface ICrossChainMessenger {
    * @param amount Amount to withdraw.
    * @param opts Additional options.
    * @param opts.signer Optional signer to use to send the transaction.
+   * @param opts.recipient Optional address to receive the funds on L1. Defaults to sender.
    * @param opts.overrides Optional transaction overrides.
    * @returns Transaction response for the withdraw transaction.
    */
@@ -467,6 +474,7 @@ export interface ICrossChainMessenger {
     amount: NumberLike,
     opts?: {
       signer?: Signer
+      recipient?: AddressLike
       overrides?: Overrides
     }
   ): Promise<TransactionResponse>
@@ -534,6 +542,7 @@ export interface ICrossChainMessenger {
      *
      * @param amount Amount of ETH to deposit.
      * @param opts Additional options.
+     * @param opts.recipient Optional address to receive the funds on L2. Defaults to sender.
      * @param opts.l2GasLimit Optional gas limit to use for the transaction on L2.
      * @param opts.overrides Optional transaction overrides.
      * @returns Transaction that can be signed and executed to deposit the ETH.
@@ -541,6 +550,7 @@ export interface ICrossChainMessenger {
     depositETH(
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         l2GasLimit?: NumberLike
         overrides?: Overrides
       }
@@ -551,12 +561,14 @@ export interface ICrossChainMessenger {
      *
      * @param amount Amount of ETH to withdraw.
      * @param opts Additional options.
+     * @param opts.recipient Optional address to receive the funds on L1. Defaults to sender.
      * @param opts.overrides Optional transaction overrides.
      * @returns Transaction that can be signed and executed to withdraw the ETH.
      */
     withdrawETH(
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         overrides?: Overrides
       }
     ): Promise<TransactionRequest>
@@ -568,6 +580,7 @@ export interface ICrossChainMessenger {
      * @param l2Token Address of the L2 token.
      * @param amount Amount to deposit.
      * @param opts Additional options.
+     * @param opts.recipient Optional address to receive the funds on L2. Defaults to sender.
      * @param opts.l2GasLimit Optional gas limit to use for the transaction on L2.
      * @param opts.overrides Optional transaction overrides.
      * @returns Transaction that can be signed and executed to deposit the tokens.
@@ -577,6 +590,7 @@ export interface ICrossChainMessenger {
       l2Token: AddressLike,
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         l2GasLimit?: NumberLike
         overrides?: Overrides
       }
@@ -589,6 +603,7 @@ export interface ICrossChainMessenger {
      * @param l2Token Address of the L2 token.
      * @param amount Amount to withdraw.
      * @param opts Additional options.
+     * @param opts.recipient Optional address to receive the funds on L1. Defaults to sender.
      * @param opts.overrides Optional transaction overrides.
      * @returns Transaction that can be signed and executed to withdraw the tokens.
      */
@@ -597,6 +612,7 @@ export interface ICrossChainMessenger {
       l2Token: AddressLike,
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         overrides?: Overrides
       }
     ): Promise<TransactionRequest>
@@ -661,6 +677,7 @@ export interface ICrossChainMessenger {
      *
      * @param amount Amount of ETH to deposit.
      * @param opts Additional options.
+     * @param opts.recipient Optional address to receive the funds on L2. Defaults to sender.
      * @param opts.l2GasLimit Optional gas limit to use for the transaction on L2.
      * @param opts.overrides Optional transaction overrides.
      * @returns Gas estimate for the transaction.
@@ -668,6 +685,7 @@ export interface ICrossChainMessenger {
     depositETH(
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         l2GasLimit?: NumberLike
         overrides?: Overrides
       }
@@ -678,12 +696,14 @@ export interface ICrossChainMessenger {
      *
      * @param amount Amount of ETH to withdraw.
      * @param opts Additional options.
+     * @param opts.recipient Optional address to receive the funds on L1. Defaults to sender.
      * @param opts.overrides Optional transaction overrides.
      * @returns Gas estimate for the transaction.
      */
     withdrawETH(
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         overrides?: Overrides
       }
     ): Promise<BigNumber>
@@ -695,6 +715,7 @@ export interface ICrossChainMessenger {
      * @param l2Token Address of the L2 token.
      * @param amount Amount to deposit.
      * @param opts Additional options.
+     * @param opts.recipient Optional address to receive the funds on L2. Defaults to sender.
      * @param opts.l2GasLimit Optional gas limit to use for the transaction on L2.
      * @param opts.overrides Optional transaction overrides.
      * @returns Gas estimate for the transaction.
@@ -704,6 +725,7 @@ export interface ICrossChainMessenger {
       l2Token: AddressLike,
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         l2GasLimit?: NumberLike
         overrides?: Overrides
       }
@@ -716,6 +738,7 @@ export interface ICrossChainMessenger {
      * @param l2Token Address of the L2 token.
      * @param amount Amount to withdraw.
      * @param opts Additional options.
+     * @param opts.recipient Optional address to receive the funds on L1. Defaults to sender.
      * @param opts.overrides Optional transaction overrides.
      * @returns Gas estimate for the transaction.
      */
@@ -724,6 +747,7 @@ export interface ICrossChainMessenger {
       l2Token: AddressLike,
       amount: NumberLike,
       opts?: {
+        recipient?: AddressLike
         overrides?: Overrides
       }
     ): Promise<BigNumber>
-- 
2.23.0