Skip to content

Commit

Permalink
Add SwapAndBridge feature (#64)
Browse files Browse the repository at this point in the history
* Add SwapAndBridge contract and Lido deployment and tests

* SwapAndBridge: Remove duplicate variables, add check against 0 address in constructor, and add slippage check

* SwapAndBridge: Remove duplicate variables, add check against 0 address in constructor, and add slippage check

* Added e2e tests.

* clean up deploy.sh

* SwapAndBridge.sol: add docs to constructor

* Clean up test

* update .env.example

* Clean up deployment script

* Clean up env file

* Add more balance for tests

* Add unit tests

* forge install: solady

v0.0.180

* remove unused contracts, update tests

* automate deployment

* fix env.example

* cleanup SwapAndBridge tests

* address review

* update contract license

* address review

* Fix E2E tests for Diva

* Minor fixes to Utils.sol

* Update Utils.sol with SwapAndBridge functionalities

* Fix import in Utils.sol

* Fix import in Utils.sol

* Fix variable usage

* Move env variables to contract constants to fix CI

* Fix formatting

* Remove use of env variables

* remove unused TOKEN_HOLDER_PRIV_KEY variable

* Save temporary data files to local storage

* fix fmt

* Move e2e tests to script

* Move integration tests to script; add unit tests

* remove old test folder

* Fix integration tests

* remove unnecessary function

* Add new unit tests

* Add mock contracts

* add tests for L2WdivETH

* Add AccessControl to VestingWallet

* Add NatSpec comments

* Update contract owner

* Added NatSpec and beneficiary check

* Remove redundant mappings

* Improve contract interface

* Increase test coverage

* Update data for Testnet migration (#135)

Merkle root was added and unneeded account removed from accounts_2.json

* Add address checking to contract

* Update error message

* Add virtual to functions

* Vesting Wallet on Ethereum L1 Mainnet (#123)

* Added L1VestingWallet, added testnet Vesting Plans, Added L1 Vesting deployment script

* Updated vesting plans script

* Update vesting data

* Add explanation to L1VestingWallet

* Update format

* Update READMEs

* Update vesting amount

* Update vesting amounts

* Update devnet data

* Update according to suggestion

* Update L1 contract code and deployment scripts

* Update README

* Update PR review

* Use OptimismMintableERC20Factory for Diva ERC20

* remove optimism library

* remove unnecessary contracts

* add deployment scripts

* fixing e2e

* fix e2e tests

* remove optimism lib

* fix e2e tests

* Add Airdrop contract Testnet deployment data (#159)

* Set 6_deployUSDTContract.sh script file as executable

* Set 5_deployAirdropContract.sh script file as executable

* Add Airdrop contract deployment data

* Fix GitHub CI regarding Forge version

* Added L2VotingPowerPaused and L2GovernorPaused contract with test cases (#165)

* Added L2VotingPowerPaused and L2GovernorPaused contract with test cases

* Add deployment script

* Update git submodules

* Add foundry.toml

* Change version function to be independent

* Update comments

* Update gitsubmodule, added Custom Errors

* Rearrange functions according to requirements

* pump forge-std to 1.8.2

* Add onReceive ERC721 and ERC1155 check

* Test adjustVotingPower is unpaused

* Add deployPausedDAO.sh

* Add forge clean to deploy script

* Clean before every forge script runs

* Used `../../` to navigate paused script

* rename assertInitParamsEq

* Merkle root for Airdrop contract is set inside deployment script (#161)

Set Merkle root on Airdrop contract deployment

* Add automated toolings for testing contracts (#151)

* Add Slither support in GH actions

* Add slither-mutate support for local execution

* Add readme for test directory

* Fix rebasing with development

* Add Echidna support for local execution

* Fix spelling typo

* fix env files

* forge install: openzeppelin-contracts

v5.0.2

* forge install: openzeppelin-contracts-upgradeable

v5.0.2

* forge install: openzeppelin-contracts

01ef448981be9d20ca85f2faf6ebdf591ce409f3

* forge install: openzeppelin-contracts-upgradeable

fbdb824a735891908d5588b28e0da5852d7ed7ba

* forge install: forge-std

v1.8.2

* cleanup

* cleanup

* address Slither and Matjaz comments

* add extra deployment checks

* Update script/swap_and_bridge/example/README.md

Co-authored-by: Matjaz Verbole <verbole@gmail.com>

* Update pr.yaml

* improving deployment script

* improving testing scripts

* improve test scripts and rename folder

* Address review comments

* Fix contracts folder path inside deployment directory

* Fix typo in e2e test and move contracts to test folder

* remove unnecessary interaction with deployment folder

* Fix contracts folder path inside deployment directory

* add testing contracts

* merge from development

* fix sed rule

* remove store of L@_DIVA_TOKEN

* Improve README file

* add readme

* fix typo

* add readme

* change folder structure

* Update script/swap_and_bridge/README.md

Co-authored-by: Matjaz Verbole <verbole@gmail.com>

---------

Co-authored-by: Franco NG <franco.ng@lightcurve.io>
Co-authored-by: Matjaz Verbole <verbole@gmail.com>
  • Loading branch information
3 people authored Jul 11, 2024
1 parent 830a7fa commit 29838dc
Show file tree
Hide file tree
Showing 20 changed files with 1,192 additions and 2 deletions.
26 changes: 26 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ DETERMINISTIC_ADDRESS_SALT="test_contract_address_salt"
# L1 standard bridge address (Mainnet, Goerli, Sepolia or custom)
L1_STANDARD_BRIDGE_ADDR=0xFBb0621E0B23b5478B630BD55a5f21f67730B0F1

# L2 standard bridge address
L2_STANDARD_BRIDGE_ADDR=0x4200000000000000000000000000000000000010

# L1 USDT token contract address to be bridged to L2
REMOTE_TOKEN_ADDR_USDT=0x0

# L1 RPC URL, e.g. Infura, Alchemy, or your own node
L1_RPC_URL=https://sepolia.infura.io/v3/YOUR_INFURA_KEY

Expand Down Expand Up @@ -76,3 +82,23 @@ L2_FORK_RPC_URL=https://optimism-sepolia.infura.io/v3/YOUR_INFURA_KEY

# Test network default mnemonic
TEST_NETWORK_MNEMONIC="test test test test test test test test test test test junk"

# *************** SWAP AND BRIDGE ***************

# L1 wdivETH token contract address to be bridged to L2
L1_TOKEN_ADDR_DIVA=

# L1 wstETH token contract address to be bridged to L2
L1_TOKEN_ADDR_LIDO=

# L1 custom bridge for wstETH
L1_LIDO_BRIDGE_ADDR=

# L2 wdivETH token contract
L2_TOKEN_ADDR_DIVA=

# L2 wstETH token contract
L2_TOKEN_ADDR_LIDO=

# L2 custom bridge for wstETH
L2_LIDO_BRIDGE_ADDR=
23 changes: 23 additions & 0 deletions .env.mainnet
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ DETERMINISTIC_ADDRESS_SALT="lisk_l2_token_deterministic_salt"
# L1 standard bridge address (Mainnet, Goerli, Sepolia or custom)
L1_STANDARD_BRIDGE_ADDR=0x2658723Bf70c7667De6B25F99fcce13A16D25d08

# L2 standard bridge address
L2_STANDARD_BRIDGE_ADDR=0x4200000000000000000000000000000000000010

# L1 USDT token contract address to be bridged to L2
REMOTE_TOKEN_ADDR_USDT=0xdAC17F958D2ee523a2206206994597C13D831ec7

Expand Down Expand Up @@ -80,3 +83,23 @@ L2_FORK_RPC_URL=https://optimism-sepolia.infura.io/v3/YOUR_INFURA_KEY

# Test network default mnemonic
TEST_NETWORK_MNEMONIC="test test test test test test test test test test test junk"

# *************** SWAP AND BRIDGE ***************

# L1 wdivETH token contract address to be bridged to L2
L1_TOKEN_ADDR_DIVA=

# L1 wstETH token contract address to be bridged to L2
L1_TOKEN_ADDR_LIDO=

# L1 custom bridge for wstETH
L1_LIDO_BRIDGE_ADDR=

# L2 wdivETH token contract
L2_TOKEN_ADDR_DIVA=

# L2 wstETH token contract
L2_TOKEN_ADDR_LIDO=

# L2 custom bridge for wstETH
L2_LIDO_BRIDGE_ADDR=
24 changes: 24 additions & 0 deletions .env.testnet
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ DETERMINISTIC_ADDRESS_SALT="lisk_l2_token_salt_test_network"
# L1 standard bridge address (Mainnet, Goerli, Sepolia or custom)
L1_STANDARD_BRIDGE_ADDR=0x1Fb30e446eA791cd1f011675E5F3f5311b70faF5

# L2 standard bridge address
L2_STANDARD_BRIDGE_ADDR=0x4200000000000000000000000000000000000010

# L1 USDT token contract address to be bridged to L2
REMOTE_TOKEN_ADDR_USDT=0x7169d38820dfd117c3fa1f22a697dba58d90ba06

Expand Down Expand Up @@ -79,3 +82,24 @@ L2_FORK_RPC_URL=https://optimism-goerli.infura.io/v3/YOUR_INFURA_KEY

# Test network default mnemonic
TEST_NETWORK_MNEMONIC="test test test test test test test test test test test junk"


# *************** SWAP AND BRIDGE ***************

# L1 wdivETH token contract address to be bridged to L2
L1_TOKEN_ADDR_DIVA=0x91701E62B2DA59224e92C42a970d7901d02C2F24

# L1 wstETH token contract address to be bridged to L2
L1_TOKEN_ADDR_LIDO=0xB82381A3fBD3FaFA77B3a7bE693342618240067b

# L1 custom bridge for wstETH
L1_LIDO_BRIDGE_ADDR=0xdDDbC273a81e6BC49c269Af55d007c08c005ea56

# L2 wdivETH token contract
L2_TOKEN_ADDR_DIVA=

# L2 wstETH token contract
L2_TOKEN_ADDR_LIDO=0xA363167588e8b3060fFFf69519bC440D1D8e4945

# L2 custom bridge for wstETH
L2_LIDO_BRIDGE_ADDR=0x7A7265ae66b094b60D9196fD5d677c11a6A03438
4 changes: 3 additions & 1 deletion documentation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ This folder contains the following documents essential for understanding the dep

5. [Migration Airdrop Documentation](./airdrop.md): This page explains the technical details concerning design and lifecycle of the migration airdrop smart contract that distributes LSK tokens as a reward to the users that migrated from the Lisk L1 network.

6. [Staking and Lisk DAO Documentation](./staking-governance.md): This page explains the technical details concerning Staking and Lisk DAO. The two topics are combined in one document as they are strongly linked to each other.
6. [Staking and Lisk DAO Documentation](./staking-governance.md): This page explains the technical details concerning Staking and Lisk DAO. The two topics are combined in one document as they are strongly linked to each other.

7. [Overview of the swap-and-bridge feature](./swap-and-bridge.md): This page explains the motivation, technical details, and integration deployments concerning the swap-and-bridge feature.
54 changes: 54 additions & 0 deletions documentation/swap-and-bridge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Overview of the swap-and-bridge feature

The swap-and-bridge feature allows users to swap ETH for an ERC20 Liquid Staking Token (LST) and to bridge it directly to an L2 with a single user interaction. This is achieved by implementing the `SwapAndBridge` contract, handling both conversion and bridging. For better integrating the LST with user interfaces, the contract swaps ETH for the wrapped version of it, that can always be converted back to the underlying LST.

## Motivation

Liquid staking pools allow users to participate in ETH staking without managing their own validator node or owning the full 32 ETH deposit amount. ETH from several users is pooled together and used to register a validator which then shares the rewards with the users. A Liquid Staking Token is an ERC20 token representing one of these staking positions. As such, it can be converted back to ETH, while at the same time being freely tradable as an independent token. Furthermore, this token provides the extra yield from the validators reward either by rebasing, i.e. changing the user balance to account for the extra tokens acquired, or, in their wrapped version, by changing the underlying conversion rate to ETH.

The swap-and-bridge feature prefers to interact with the wrapped version of a LST, as this tends to be better supported on the UX of several DeFi protocols (see the discussion for Lido's [wstETH](https://docs.lido.fi/contracts/wsteth/#why-use-wsteth)) and since the Optimism standard bridge [does not support rebasing tokens correctly](https://docs.optimism.io/builders/app-developers/bridging/standard-bridge).

A similar result for the end-user (swapping L1 ETH for L2 wrapped LST with a single interaction) can be achieved by using one of the several bridges available. However, this flow likely offers better security guarantees by relying on the L1<-->L2 bridging protocol and also stakes the ETH in the underlying staking pool protocol. Because of this, it should be considered an alternative option to stimulate participation in ETH staking.

## Rationale

Each `SwapAndBridge` contract supports a single wrapped LST, hence for each wrapped LST one wishes to support, a different `SwapAndBridge` contract needs to be deployed. We specified the contract to be as compatible as possible with a wide range of protocols. The minimum requirements are that the LST contract exposes the `receive` fallback to perform the staking and conversion of ETH to LST.

The `SwapAndBridge` contract itself uses the `receive` fallback as a shortcut to the the swap-and-bridge flow, so that users can send ETH to the contract and receive L2 LST directly.

The `SwapAndBridge` contract does not include any special role such as contract 'owner' and should not hold any ETH balance, with the notable exception of being the [target of a self-destructing contract](https://solidity-by-example.org/hacks/self-destruct/). Even in this case though, no one is able to extract the ETH balance of the contract. This means that, while no privileged operation can be executed on the contract in case of an emergency (like pausing it), no funds are at risk.

This repository does not include any UI-related code.

## Incident response plan

Because no special role is assigned and since we do not deploy any user-facing interface, there are no concrete steps that we can take in case of an emergency. As explained above though, the range of problems that can occur is quite limited:

- If a bug is present in the contract, users can lose at most the amount of ETH sent to perform the swap-and-bridge operation;
- The contract does not hold any mutable property, so the range of possible interactions that could result in a malicious state is limited.

## Specifications

### Constructor

The `SwapAndBridge` contract accepts the following parameters in its contructor:

- `address _l1Bridge`: The address of the L1 bridge contract used to transfer the tokens from the L1 to the L2. This bridge must implement the `depositERC20To` function. See, for instance, the [Optimism `L1StandardBridge` contract](https://github.com/ethereum-optimism/optimism/blob/op-contracts/v1.5.0/packages/contracts-bedrock/src/L1/L1StandardBridge.sol) or the [Lido `IL1ERC20Bridge` interface](https://github.com/lidofinance/lido-l2/blob/main/contracts/optimism/interfaces/IL1ERC20Bridge.sol);
- `address _l1Token`: The address of the L1 LST contract. This contract must implement the `receive` fallback to exchange the ETH sent with the transaction for the underlying LST. See, for instance, the [Lido `WstETH` contract](https://github.com/lidofinance/lido-dao/blob/master/contracts/0.6.12/WstETH.sol#L80) and [relative documentation](https://docs.lido.fi/contracts/wsteth/#staking-shortcut);
- `address _l2Token`: The address of the L2 LST contract. There are no particular restrictions on this contract apart from the usual bridging rules that depend on the bridge used. For instance, the standard bridge and the [`OptimismMintableERC20` standard](https://github.com/ethereum-optimism/optimism/blob/op-contracts/v1.5.0/packages/contracts-bedrock/src/universal/OptimismMintableERC20.sol) requires that this contract specifies `_l1Token` as the relative `REMOTE_TOKEN`.

### Public functions

The `SwapAndBridge` contract exposes the following public/external functions:

- `receive() external payable`: Convenience function allowing users to interact with the contract just by sending ETH. It redirects to `swapAndBridgeTo(msg.sender)`;
- `swapAndBridgeTo(address recipient) public payable`: Convenience function to swap ETH for LST without specifying the minimum amount of LST to be received. It redirects to `swapAndBridgeToWithMinimumAmount(recipient, 0)`;
- `swapAndBridgeToWithMinimumAmount(address recipient, uint256 minL1Tokens) public payable`: The core function performing boht ETH-->LST conversion (by sending ETH to the LST contract) and the L1 LST-->L2 LST bridging (by calling the bridge contract `depositERC20To` function). Several checks are included to ensure that the conversion is succesfull. It is possible to specify a `uint256 minL1Tokens` value larger than 0, in which case the contract will also check that swap resulted in an amount of LST larger than the specified minimum. A value of 0 passed to this function skips this check, allowing for any (non-zero) value to be accepted.

## Integrations

In this repository, we add integration tests and deployment scripts for two concrete liquid staking protocols: [Lido](https://lido.fi/) and [Diva](https://divastaking.com/).

The Diva deployment uses the Op-stack standard bridge as any other ERC20 token. The L2 LST contract can be deployed by running the `7_deployWdivETHContract.sh` script located in the parent folder.

The Lido deployment uses a custom bridge developed to deploy the Lido LST on L2s, as explained and specified in the [lido-l2 repository](https://github.com/lidofinance/lido-l2/tree/main). Because of this, the deployment of the Lido custom bridge and Lido L2 LST token are not part of this repository.
3 changes: 2 additions & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[profile.default]
evm_version = "cancun"
src = "src"
out = "out"
libs = ["lib"]
Expand Down Expand Up @@ -37,4 +38,4 @@ wrap_comments = true
[invariant]
runs = 50
depth = 50
fail_on_revert = false
fail_on_revert = false
17 changes: 17 additions & 0 deletions script/7_deployWdivETHContract.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env bash

echo "Instructing the shell to exit immediately if any command returns a non-zero exit status..."
set -e
echo "Done."

echo "Navigating to the root directory of the project..."
cd ../
echo "Done."

echo "Setting environment variables..."
source .env
echo "Done."

echo "Deploying OptimismMintableERC20 wdivETH token smart contract..."
cast send --rpc-url $L2_RPC_URL "0x4200000000000000000000000000000000000012" "createOptimismMintableERC20WithDecimals(address,string,string,uint8)" $L1_TOKEN_ADDR_DIVA "Wrapped Diva Ether Token" wdivETH 18 --private-key $PRIVATE_KEY
echo "Done."
45 changes: 45 additions & 0 deletions script/8_deploySwapAndBridge.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env bash

echo "Instructing the shell to exit immediately if any command returns a non-zero exit status..."
set -e
echo "Done."

echo "Navigating to the root directory of the project..."
cd ../
echo "Done."

echo "Setting environment variables..."
source .env
echo "Done."

echo "Deploying and if enabled verifying SwapAndBridge smart contract for DIVA protocol..."
if [ -z "$CONTRACT_VERIFIER" ]
then
forge script --rpc-url="$L1_RPC_URL" --broadcast -vvvv script/contracts/L1/SwapAndBridge.s.sol:SwapAndBridgeScript $L1_STANDARD_BRIDGE_ADDR $L1_TOKEN_ADDR_DIVA $L2_TOKEN_ADDR_DIVA --sig 'run(address,address,address)'
else
if [ $CONTRACT_VERIFIER = "blockscout" ]
then
forge script --rpc-url="$L1_RPC_URL" --broadcast --verify --verifier blockscout --verifier-url $L1_VERIFIER_URL -vvvv script/contracts/L1/SwapAndBridge.s.sol:SwapAndBridgeScript $L1_STANDARD_BRIDGE_ADDR $L1_TOKEN_ADDR_DIVA $L2_TOKEN_ADDR_DIVA --sig 'run(address,address,address)'
fi
if [ $CONTRACT_VERIFIER = "etherscan" ]
then
forge script --rpc-url="$L1_RPC_URL" --broadcast --verify --verifier etherscan --etherscan-api-key="$L1_ETHERSCAN_API_KEY" --verifier-url $L1_VERIFIER_URL -vvvv script/contracts/L1/SwapAndBridge.s.sol:SwapAndBridgeScript $L1_STANDARD_BRIDGE_ADDR $L1_TOKEN_ADDR_DIVA $L2_TOKEN_ADDR_DIVA --sig 'run(address,address,address)'
fi
fi
echo "Done."

echo "Deploying and if enabled verifying SwapAndBridge smart contract for LIDO protocol..."
if [ -z "$CONTRACT_VERIFIER" ]
then
forge script --rpc-url="$L1_RPC_URL" --broadcast -vvvv script/contracts/L1/SwapAndBridge.s.sol:SwapAndBridgeScript $L1_LIDO_BRIDGE_ADDR $L1_TOKEN_ADDR_LIDO $L2_TOKEN_ADDR_LIDO --sig 'run(address,address,address)'
else
if [ $CONTRACT_VERIFIER = "blockscout" ]
then
forge script --rpc-url="$L1_RPC_URL" --broadcast --verify --verifier blockscout --verifier-url $L1_VERIFIER_URL -vvvv script/contracts/L1/SwapAndBridge.s.sol:SwapAndBridgeScript $L1_LIDO_BRIDGE_ADDR $L1_TOKEN_ADDR_LIDO $L2_TOKEN_ADDR_LIDO --sig 'run(address,address,address)'
fi
if [ $CONTRACT_VERIFIER = "etherscan" ]
then
forge script --rpc-url="$L1_RPC_URL" --broadcast --verify --verifier etherscan --etherscan-api-key="$L1_ETHERSCAN_API_KEY" --verifier-url $L1_VERIFIER_URL -vvvv script/contracts/L1/SwapAndBridge.s.sol:SwapAndBridgeScript $L1_LIDO_BRIDGE_ADDR $L1_TOKEN_ADDR_LIDO $L2_TOKEN_ADDR_LIDO --sig 'run(address,address,address)'
fi
fi
echo "Done."
25 changes: 25 additions & 0 deletions script/contracts/L1/SwapAndBridge.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.23;

import { Script, console2 } from "forge-std/Script.sol";
import { SwapAndBridge } from "src/L1/SwapAndBridge.sol";

/// @title SwapAndBridgeScript - SwapAndBridge deployment script
/// @notice This contract is used to deploy SwapAndBridge contract.
contract SwapAndBridgeScript is Script {
/// @notice This function deploys the SwapAndBridge contract.
function run(address _l1Bridge, address _l1Token, address _l2Token) public {
assert(_l1Bridge != address(0));
assert(_l1Token != address(0));
assert(_l2Token != address(0));
// Deployer's private key. PRIVATE_KEY is set in .env file.
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");

console2.log("Deploying SwapAndBridge contract...");
vm.startBroadcast(deployerPrivateKey);
SwapAndBridge swapAndBridge = new SwapAndBridge(_l1Bridge, _l1Token, _l2Token);
vm.stopBroadcast();
assert(address(swapAndBridge) != address(0));
console2.log("SwapAndBridge successfully deployed at address: %s", address(swapAndBridge));
}
}
15 changes: 15 additions & 0 deletions script/swap_and_bridge/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Test Scripts for SwapAndBridge

Tests in this folder are run as script as they require to be run in a Sepolia forked environment, since they depend on the contracts owned and deployed by Lido and Diva teams.

Before running them, start an L1 and L2 fork node using the shell scripts `runL1TestNetwork.sh` and `runL2TestNetwork.sh` inside [script](..) folder respectively and make sure to run the tests from this folder.

Notice that these tests may fail fo reasons unrelated to the implementation of this feature, but rather because of problems with the target integration (for instance if the target LST contract does not mint new LSTs). If the `L2_TOKEN_ADDR_DIVA` value is not set in the `.env` file, the script will automatically deploy a test contract. Notice however that this deployment may fail if the very same test contract has already been deployed in your forked environment (which could be the case, for instance, if you run the scripts multiple times). In this case, just restart the fork nodes or modify the test contract name.

## E2E Tests

These tests simulate the user flow for transferring ETH on the L1 to either Lido or Diva LST on the Lisk L2 in a single transaction. Run them with `./e2e_test.sh`.

## Integration Tests

These tests run some checks on the interaction between the `SwapAndBridge` contract and the Lido and Diva LSTs. Run them with `./integration_test.sh`.
Loading

0 comments on commit 29838dc

Please sign in to comment.