Skip to content

Commit

Permalink
build: add deposit helper contract (#3)
Browse files Browse the repository at this point in the history
* build: deposit helper contract

* chore: cleanup

* chore: add docs on all deposit helpers

* chore: fmt

* chore: fmt

* chore: add withdraw helper contract

* docs: format

* feat: deploy mainnet
  • Loading branch information
antoncoding authored Jan 29, 2024
1 parent 842b12d commit 94bd746
Show file tree
Hide file tree
Showing 10 changed files with 219 additions and 73 deletions.
7 changes: 5 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,8 @@ jobs:
with:
version: nightly

- name: Run tests
run: forge test -vvv --fork-url https://rpc.lyra.finance
- name: Run lyra fork tests
run: forge test --match-contract FORK_LYRA_ --fork-url https://rpc.lyra.finance

- name: Run Mainnet fork tests
run: forge test --match-contract FORK_MAINNET_ --fork-url https://mainnet.infura.io/v3/743507feddbd4a8088614092511076bc -vvv
76 changes: 74 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,83 @@ $ forge build

### Test

We have a few fork tests for deposit and withdraw helpers. You can run them on Mainnet / Lyra chain with commands below:

```shell
# tests on Lyra chain
$ forge test --fork-url <LyraMainnetRPC>
$ forge test --match-contract FORK_LYRA_ --fork-url https://rpc.lyra.finance

# tests on Mainnet
$ forge test --match-contract FORK_MAINNET_ --fork-url <mainnet rpc>
```

### Using the contracts

Go see [this repo](https://github.com/antoncoding/lyra-aa-example) for examples
Go see [this repo](https://github.com/antoncoding/lyra-aa-example) for examples


# Deployments

## Gasless Deposit Forwarders
Gasless forwarders are used to make sure users only with ERC20 can deposit to Lyra

### ETH
| Network | USDC Selfpaying Forwarder | USDC Sponsored Forwarder |
| -------- | -------- | --- |
| Ethereum Mainnet | [0x00efac83a3168568e258ab1ec85e85c10cbaf74e](https://etherscan.io/address/0x00efac83a3168568e258ab1ec85e85c10cbaf74e#code) | [0xf0372da389db728a3173a7b91c5cb4437a6319ea](https://etherscan.io/address/0xf0372da389db728a3173a7b91c5cb4437a6319ea)|

* On Ethereum Mainnet, only USDC supports gasless deposit.
* We use a differnet forwarder here that only work with USDC (`receiveWithAuth`) to minimize gas cost.

### Arbitrum

| Network | SelfPaying Permit Forwarder | Sponsored Permit Forwarder |
| -------- | -------- | --- |
| Arbitrum | [0x00eFAc83a3168568e258ab1Ec85E85C10cBAf74E](https://arbiscan.io/address/0x00eFAc83a3168568e258ab1Ec85E85C10cBAf74E) | [0xC3621651c550F3c1BC146ffAe0975a566423Da17](https://arbiscan.io/address/0xC3621651c550F3c1BC146ffAe0975a566423Da17) |
| Arbitrum Sepolia | - | [0xE3436F0F982fbbAf88f28DACE9b36a85c97aECdE](https://sepolia.arbiscan.io/address/0xE3436F0F982fbbAf88f28DACE9b36a85c97aECdE) |

* On Arbitrum, all ERC20s can be gasless (with `permit`)

### Optimism
| Network | SelfPaying Permit Forwarder | Sponsored Permit Forwarder |
| -------- | -------- | --- |
| Optimism | [0xAa7Dd6fa6B604b776BCE03Af6ED717c00E66538E](https://optimistic.etherscan.io/address/0xAa7Dd6fa6B604b776BCE03Af6ED717c00E66538E#code) | [0x062B67001A6dd9FC6Aa1CFB9c246AcfFC4BfAdC5](https://optimistic.etherscan.io/address/0x062B67001A6dd9FC6Aa1CFB9c246AcfFC4BfAdC5#code) |
| Optimism Sepolia | - | [0x1480Cfe30213b134f757757d328949AAe406eA33](https://sepolia-optimistic.etherscan.io/address/0x1480Cfe30213b134f757757d328949AAe406eA33#code) |


* On Optimism, only USDC has `permit` now, but other ERC20s can potentially use these contracts to achieve gasless deposit if they have permit.


## Deposit Helper

### `LyraDepositWrapper`

Help wrapping ETH and deposit with socket vault in one go. Can also be used with ERC20 deposits to calculate L2 address


### Mainnet
| Network | Address |
| -------- | -------- |
| Ethereum | [0x18a0f3F937DD0FA150d152375aE5A4E941d1527b](https://etherscan.io/address/0x18a0f3f937dd0fa150d152375ae5a4e941d1527b#code) |
| Optimism | [0xC65005131Cfdf06622b99E8E17f72Cf694b586cC](https://optimistic.etherscan.io/address/0xC65005131Cfdf06622b99E8E17f72Cf694b586cC#code) |
| Arbitrum | [0x076BB6117750e80AD570D98891B68da86C203A88](https://arbiscan.io/address/0x076BB6117750e80AD570D98891B68da86C203A88#readContract) |

### Testnet
| Network | Address |
| -------- | -------- |
| Sepolia | [0x46e75b6983126896227a5717f2484efb04a0c151](https://sepolia.etherscan.io/address/0x46e75b6983126896227a5717f2484efb04a0c151#readContract) |
| Op-Sepolia | [0x3E7DEc059a3692c184BF0D0AC3d9Af7570DF6A3c](https://sepolia-optimistic.etherscan.io/address/0x3E7DEc059a3692c184BF0D0AC3d9Af7570DF6A3c#code) |
| Arbi-Sepolia | [0x5708bDE1c5e49b62cfd46D07b5cd3c898930Ef23](https://sepolia.arbiscan.io/address/0x5708bDE1c5e49b62cfd46D07b5cd3c898930Ef23#readContract) |



## Withdraw Helper

### `WithdrawHelperV2`

withdraw ERC20s from Lyra Chain back to Mainnet / L2s, paid socket fee in token.

| Network | Address |
| -------- | -------- |
| Lyra | [0x0E4e5779F633F823d007f3C27fa6feFb22B45316](https://explorer.lyra.finance/address/0x0E4e5779F633F823d007f3C27fa6feFb22B45316) |
| Lyra Testnet | [0xD7080B2399B88c3520F8F793f4758D0C6ccDf48a](https://explorerl2new-prod-testnet-0eakp60405.t.conduit.xyz/address/0xD7080B2399B88c3520F8F793f4758D0C6ccDf48a) |
1 change: 0 additions & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,5 @@ optimizer_runs = 2000

[rpc_endpoints]
mainnet = "https://mainnet.infura.io/v3/${INFURA_PROJECT_ID}"
op_goerli = "https://optimism-goerli.infura.io/v3/ea21b2313cdf43c28f25b849d7a75274"
lyra = "https://rpc.lyra.finance"
lyra_testnet = "https://l2-prod-testnet-0eakp60405.t.conduit.xyz"
72 changes: 72 additions & 0 deletions src/helpers/LyraDepositWrapper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "../../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {IWETH} from "../interfaces/IWETH.sol";
import {ISocketVault} from "../interfaces/ISocketVault.sol";
import {ILightAccountFactory} from "../interfaces/ILightAccountFactory.sol";

/**
* @title LyraDepositWrapper
* @dev Helper contract to wrap ETH into L2 WETH, or deposit any token to L2 smart contract wallet address
*/
contract LyraDepositWrapper {
///@dev L2 USDC address.
address public immutable weth;

///@dev Light Account factory address.
address public constant lightAccountFactory = 0x000000893A26168158fbeaDD9335Be5bC96592E2;

constructor(address _weth) {
weth = _weth;
}

/**
* @notice Wrap ETH into WETH and deposit to Lyra Chain via socket vault
*/
function depositETHToLyra(address socketVault, bool isSCW, uint256 gasLimit, address connector) external payable {
uint256 socketFee = ISocketVault(socketVault).getMinFees(connector, gasLimit);

uint256 depositAmount = msg.value - socketFee;

IWETH(weth).deposit{value: depositAmount}();
IERC20(weth).approve(socketVault, type(uint256).max);

address recipient = _getL2Receiver(isSCW);

ISocketVault(socketVault).depositToAppChain{value: socketFee}(recipient, depositAmount, gasLimit, connector);
}

/**
* @notice Deposit any token to Lyra Chain via socket vault.
* @dev This function help calculate L2 smart wallet addresses for users
*/
function depositToLyra(
address token,
address socketVault,
bool isSCW,
uint256 amount,
uint256 gasLimit,
address connector
) external payable {
IERC20(token).transferFrom(msg.sender, address(this), amount);
IERC20(token).approve(socketVault, type(uint256).max);

address recipient = _getL2Receiver(isSCW);

ISocketVault(socketVault).depositToAppChain{value: msg.value}(recipient, amount, gasLimit, connector);
}

/**
* @notice Return the receiver address on L2
*/
function _getL2Receiver(bool isScwWallet) internal view returns (address) {
if (isScwWallet) {
return ILightAccountFactory(lightAccountFactory).getAddress(msg.sender, 0);
} else {
return msg.sender;
}
}

receive() external payable {}
}
7 changes: 7 additions & 0 deletions src/interfaces/IWETH.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pragma solidity >=0.5.0;

interface IWETH {
function deposit() external payable;
function transfer(address to, uint256 value) external returns (bool);
function withdraw(uint256) external;
}
56 changes: 56 additions & 0 deletions test/fork-tests/deposit/DepositHelper.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.18;

import "lib/forge-std/src/Test.sol";

import {LyraDepositWrapper} from "src/helpers/LyraDepositWrapper.sol";
import {IERC20} from "../../../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {IWETH} from "src/interfaces/IWETH.sol";

/**
* forge test --fork-url https://mainnet.infura.io/v3/${INFURA_PROJECT_ID} -vvv
*/
contract FORK_MAINNET_LyraDepositWrapper is Test {
address public weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);

address public wethVault = address(0xD4efe33C66B8CdE33B8896a2126E41e5dB571b7e);
address public wethConnector = address(0xCf814e58f1649F94d37E51f730D6bF72409fA09c);

LyraDepositWrapper public wrapper;

uint256 public alicePk = 0xbabebabe;
address public alice = vm.addr(alicePk);

/**
* Only run the test when running with --fork flag, and connected to Lyra mainnet
*/
modifier onlyMainnet() {
if (block.chainid != 1) return;
_;
}

function setUp() public onlyMainnet {
wrapper = new LyraDepositWrapper(weth);

vm.deal(alice, 100 ether);
}

function test_deposit_ETH() public onlyMainnet {
vm.prank(alice);

wrapper.depositETHToLyra{value: 20 ether}(wethVault, true, 200_000, wethConnector);
}

function test_deposit_WETH() public onlyMainnet {
vm.startPrank(alice);
IWETH(weth).deposit{value: 20 ether}();
IERC20(weth).approve(address(wrapper), type(uint256).max);

uint256 socketFee = 0.03 ether;

wrapper.depositToLyra{value: socketFee}(weth, wethVault, true, 20 ether, 200_000, wethConnector);
vm.stopPrank();
}

receive() external payable {}
}
Empty file removed test/fork-tests/gelato/.gitkeep
Empty file.
64 changes: 0 additions & 64 deletions test/fork-tests/gelato/SelfPayingForwarder.t.sol

This file was deleted.

2 changes: 1 addition & 1 deletion test/fork-tests/withdraw/LyraWithdrawWrapper.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {USDC} from "src/mocks/USDC.sol";
/**
* forge test --fork-url https://rpc.lyra.finance -vvv
*/
contract FORK_LyraWithdrawalTest is Test {
contract FORK_LYRA_LyraWithdrawalTest is Test {
address public immutable usdc = address(0x6879287835A86F50f784313dBEd5E5cCC5bb8481);

address public immutable controller = address(0x4C9faD010D8be90Aba505c85eacc483dFf9b8Fa9);
Expand Down
7 changes: 4 additions & 3 deletions test/fork-tests/withdraw/LyraWithdrawWrapperV2.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import {USDC} from "src/mocks/USDC.sol";
/**
* forge test --fork-url https://rpc.lyra.finance -vvv
*/
contract FORK_LyraWithdrawalV2Test is Test {
contract FORK_LYRA_LyraWithdrawalV2Test is Test {
address public usdc = address(0x6879287835A86F50f784313dBEd5E5cCC5bb8481);

// withdraw as official USDC
address public usdcController = address(0x4C9faD010D8be90Aba505c85eacc483dFf9b8Fa9);
address public usdc_Mainnet_Connector = address(0x1281C1464449DB73bdAa30928BCC63Dc25D8D187);
address public usdc_Arbi_Connector = address(0xBdE9e687F3A23Ebbc972c58D510dfc1f58Fb35EF); // as native USDC

// withdraw as USDC.e
address public usdc_Arbi_Connector = address(0xFc1e42b0C3Ff8d69FBe639a6a674fF5f0FcE778D);

// wbtc asset
address public wBTC = address(0x9b80ab732a6F1030326Af0014f106E12C4Db18EC);
Expand Down Expand Up @@ -74,7 +76,6 @@ contract FORK_LyraWithdrawalV2Test is Test {
}

function test_fork_RevertIf_tokenMismatch() public onlyLyra {
// _mintLyraUSDC(address(wrapper), 1000e6);
uint256 amount = 100e6;

vm.startPrank(alice);
Expand Down

0 comments on commit 94bd746

Please sign in to comment.