Skip to content
This repository has been archived by the owner on Dec 3, 2024. It is now read-only.

Commit

Permalink
Merge branch 'main' into gstuart/native-token-destination
Browse files Browse the repository at this point in the history
  • Loading branch information
geoff-vball committed Mar 28, 2024
2 parents b380830 + 487c79f commit ca7fcc6
Show file tree
Hide file tree
Showing 22 changed files with 628 additions and 525 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,21 +54,21 @@ To run the E2E tests locally, you'll need to install Gingko following the instru
Then run the following command from the root of the repository:

```bash
./scripts/local/e2e_test.sh
./scripts/e2e_test.sh
```

### Run specific E2E tests

To run a specific E2E test, specify the environment variable `GINKGO_FOCUS`, which will then look for test descriptions that match the provided input. For example, to run the `Bridge an ERC20 token between two Subnets` test:

```bash
GINKGO_FOCUS="Bridge an ERC20 token between two Subnets" ./scripts/local/e2e_test.sh
GINKGO_FOCUS="Bridge an ERC20 token between two Subnets" ./scripts/e2e_test.sh
```

A substring of the full test description can be used as well:

```bash
GINKGO_FOCUS="Bridge an ERC20 token" ./scripts/local/e2e_test.sh
GINKGO_FOCUS="Bridge an ERC20 token" ./scripts/e2e_test.sh
```

The E2E tests also supports `GINKGO_LABEL_FILTER`, making it easy to group test cases and run them together. For example, to run all `ERC20Source` E2E tests:
Expand All @@ -82,5 +82,5 @@ The E2E tests also supports `GINKGO_LABEL_FILTER`, making it easy to group test
```
```bash
GINKGO_LABEL_FILTER="ERC20Source" ./scripts/local/e2e_test.sh
GINKGO_LABEL_FILTER="ERC20Source" ./scripts/e2e_test.sh
```
2 changes: 1 addition & 1 deletion abi-bindings/go/ERC20Destination/ERC20Destination.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion abi-bindings/go/ERC20Source/ERC20Source.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion abi-bindings/go/NativeTokenSource/NativeTokenSource.go

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion contracts/src/.solhint.json → contracts/.solhint.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
],
"func-named-parameters": ["error", 5],
"one-contract-per-file": "off",
"no-empty-blocks": "off"
"no-empty-blocks": "off",
"check-send-result": "off"
}
}
1 change: 1 addition & 0 deletions contracts/.solhintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lib/
Empty file removed contracts/src/.solhintignore
Empty file.
4 changes: 0 additions & 4 deletions contracts/src/NativeTokenSource.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ pragma solidity 0.8.18;

import {TeleporterTokenSource} from "./TeleporterTokenSource.sol";
import {INativeTokenBridge} from "./interfaces/INativeTokenBridge.sol";
import {IERC20} from "@openzeppelin/[email protected]/token/ERC20/ERC20.sol";
import {SafeERC20} from "@openzeppelin/[email protected]/token/ERC20/utils/SafeERC20.sol";
import {SendTokensInput} from "./interfaces/ITeleporterTokenBridge.sol";
import {IWrappedNativeToken} from "./interfaces/IWrappedNativeToken.sol";

Expand All @@ -24,8 +22,6 @@ import {IWrappedNativeToken} from "./interfaces/IWrappedNativeToken.sol";
* token bridge instance.
*/
contract NativeTokenSource is INativeTokenBridge, TeleporterTokenSource {
using SafeERC20 for IERC20;

/**
* @notice The wrapped native token contract that represents the native tokens on this chain.
*/
Expand Down
1 change: 0 additions & 1 deletion contracts/src/TeleporterTokenDestination.sol
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ abstract contract TeleporterTokenDestination is
// Deposit the funds sent from the user to the bridge,
// and set to adjusted amount after deposit
amount = _deposit(amount);
require(amount > 0, "TeleporterTokenDestination: zero send amount");
require(
amount > input.primaryFee + input.secondaryFee,
"TeleporterTokenDestination: insufficient amount to cover fees"
Expand Down
1 change: 0 additions & 1 deletion contracts/src/TeleporterTokenSource.sol
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ abstract contract TeleporterTokenSource is ITeleporterTokenBridge, TeleporterOwn
if (!isMultihop) {
amount = _deposit(amount);
}
require(amount > 0, "TeleporterTokenSource: zero send amount");
require(
amount > input.primaryFee, "TeleporterTokenSource: insufficient amount to cover fees"
);
Expand Down
39 changes: 39 additions & 0 deletions contracts/test/ERC20BridgeTests.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// (c) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

// SPDX-License-Identifier: Ecosystem

pragma solidity 0.8.18;

import {TeleporterTokenBridgeTest} from "./TeleporterTokenBridgeTests.t.sol";
import {IERC20Bridge} from "../src/interfaces/IERC20Bridge.sol";
import {SendTokensInput} from "../src/interfaces/ITeleporterTokenBridge.sol";
import {IERC20} from "@openzeppelin/[email protected]/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/[email protected]/token/ERC20/utils/SafeERC20.sol";

abstract contract ERC20BridgeTest is TeleporterTokenBridgeTest {
using SafeERC20 for IERC20;

IERC20Bridge public erc20Bridge;

function testZeroSendAmount() public {
vm.expectRevert("SafeERC20TransferFrom: balance not increased");
_send(_createDefaultSendTokensInput(), 0);
}

function _send(SendTokensInput memory input, uint256 amount) internal virtual override {
erc20Bridge.send(input, amount);
}

function _setUpExpectedDeposit(uint256 amount) internal virtual override {
// Increase the allowance of the bridge to transfer the funds from the user
feeToken.safeIncreaseAllowance(address(tokenBridge), amount);
// Check that transferFrom is called to deposit the funds sent from the user to the bridge
vm.expectCall(
address(feeToken),
abi.encodeCall(IERC20.transferFrom, (address(this), address(tokenBridge), amount))
);
vm.expectEmit(true, true, true, true, address(feeToken));
emit Transfer(address(this), address(tokenBridge), amount);
}
}
122 changes: 122 additions & 0 deletions contracts/test/ERC20DestinationTests.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// (c) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

// SPDX-License-Identifier: Ecosystem

pragma solidity 0.8.18;

import {ERC20BridgeTest} from "./ERC20BridgeTests.t.sol";
import {TeleporterTokenDestinationTest} from "./TeleporterTokenDestinationTests.t.sol";
import {ERC20Destination} from "../src/ERC20Destination.sol";
import {IERC20} from "@openzeppelin/[email protected]/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/[email protected]/token/ERC20/utils/SafeERC20.sol";

contract ERC20DestinationTest is ERC20BridgeTest, TeleporterTokenDestinationTest {
using SafeERC20 for IERC20;

ERC20Destination public app;

string public constant MOCK_TOKEN_NAME = "Test Token";
string public constant MOCK_TOKEN_SYMBOL = "TST";
uint8 public constant MOCK_TOKEN_DECIMALS = 18;

function setUp() public virtual override {
TeleporterTokenDestinationTest.setUp();

app = new ERC20Destination({
teleporterRegistryAddress: MOCK_TELEPORTER_REGISTRY_ADDRESS,
teleporterManager: address(this),
sourceBlockchainID: DEFAULT_SOURCE_BLOCKCHAIN_ID,
tokenSourceAddress: TOKEN_SOURCE_ADDRESS,
tokenName: MOCK_TOKEN_NAME,
tokenSymbol: MOCK_TOKEN_SYMBOL,
tokenDecimals: MOCK_TOKEN_DECIMALS
});

erc20Bridge = app;
tokenDestination = app;
tokenBridge = app;
feeToken = IERC20(app);

vm.expectEmit(true, true, true, true, address(app));
emit Transfer(address(0), address(this), 10e18);

vm.prank(MOCK_TELEPORTER_MESSENGER_ADDRESS);
app.receiveTeleporterMessage(
DEFAULT_SOURCE_BLOCKCHAIN_ID, TOKEN_SOURCE_ADDRESS, abi.encode(address(this), 10e18)
);
}

/**
* Initialization unit tests
*/
function testZeroTeleporterRegistryAddress() public {
vm.expectRevert("TeleporterUpgradeable: zero teleporter registry address");
new ERC20Destination({
teleporterRegistryAddress: address(0),
teleporterManager: address(this),
sourceBlockchainID: DEFAULT_SOURCE_BLOCKCHAIN_ID,
tokenSourceAddress: TOKEN_SOURCE_ADDRESS,
tokenName: MOCK_TOKEN_NAME,
tokenSymbol: MOCK_TOKEN_SYMBOL,
tokenDecimals: MOCK_TOKEN_DECIMALS
});
}

function testZeroTeleporterManagerAddress() public {
vm.expectRevert("Ownable: new owner is the zero address");
new ERC20Destination({
teleporterRegistryAddress: MOCK_TELEPORTER_REGISTRY_ADDRESS,
teleporterManager: address(0),
sourceBlockchainID: DEFAULT_SOURCE_BLOCKCHAIN_ID,
tokenSourceAddress: TOKEN_SOURCE_ADDRESS,
tokenName: MOCK_TOKEN_NAME,
tokenSymbol: MOCK_TOKEN_SYMBOL,
tokenDecimals: MOCK_TOKEN_DECIMALS
});
}

function testZeroBlockchainID() public {
vm.expectRevert(_formatErrorMessage("zero source blockchain ID"));
new ERC20Destination({
teleporterRegistryAddress: MOCK_TELEPORTER_REGISTRY_ADDRESS,
teleporterManager: address(this),
sourceBlockchainID: bytes32(0),
tokenSourceAddress: TOKEN_SOURCE_ADDRESS,
tokenName: MOCK_TOKEN_NAME,
tokenSymbol: MOCK_TOKEN_SYMBOL,
tokenDecimals: MOCK_TOKEN_DECIMALS
});
}

function testDeployToSameBlockchain() public {
vm.expectRevert(_formatErrorMessage("cannot deploy to same blockchain as source"));
new ERC20Destination({
teleporterRegistryAddress: MOCK_TELEPORTER_REGISTRY_ADDRESS,
teleporterManager: address(this),
sourceBlockchainID: DEFAULT_DESTINATION_BLOCKCHAIN_ID,
tokenSourceAddress: TOKEN_SOURCE_ADDRESS,
tokenName: MOCK_TOKEN_NAME,
tokenSymbol: MOCK_TOKEN_SYMBOL,
tokenDecimals: MOCK_TOKEN_DECIMALS
});
}

function testZeroTokenSourceAddress() public {
vm.expectRevert(_formatErrorMessage("zero token source address"));
new ERC20Destination({
teleporterRegistryAddress: MOCK_TELEPORTER_REGISTRY_ADDRESS,
teleporterManager: address(this),
sourceBlockchainID: DEFAULT_SOURCE_BLOCKCHAIN_ID,
tokenSourceAddress: address(0),
tokenName: MOCK_TOKEN_NAME,
tokenSymbol: MOCK_TOKEN_SYMBOL,
tokenDecimals: MOCK_TOKEN_DECIMALS
});
}

function _checkExpectedWithdrawal(address recipient, uint256 amount) internal override {
vm.expectEmit(true, true, true, true, address(app));
emit Transfer(address(0), recipient, amount);
}
}
70 changes: 70 additions & 0 deletions contracts/test/ERC20SourceTests.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// (c) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

// SPDX-License-Identifier: Ecosystem

pragma solidity 0.8.18;

import {ERC20BridgeTest} from "./ERC20BridgeTests.t.sol";
import {TeleporterTokenSourceTest} from "./TeleporterTokenSourceTests.t.sol";
import {ERC20Source} from "../src/ERC20Source.sol";
import {IERC20} from "@openzeppelin/[email protected]/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/[email protected]/token/ERC20/utils/SafeERC20.sol";
import {ExampleERC20} from "../lib/teleporter/contracts/src/Mocks/ExampleERC20.sol";

contract ERC20SourceTest is ERC20BridgeTest, TeleporterTokenSourceTest {
using SafeERC20 for IERC20;

ERC20Source public app;
IERC20 public mockERC20;

function setUp() public override {
TeleporterTokenSourceTest.setUp();

mockERC20 = new ExampleERC20();
app = new ERC20Source(
MOCK_TELEPORTER_REGISTRY_ADDRESS,
MOCK_TELEPORTER_MESSENGER_ADDRESS,
address(mockERC20)
);
erc20Bridge = app;
tokenSource = app;
tokenBridge = app;

feeToken = mockERC20;
}

/**
* Initialization unit tests
*/
function testZeroTeleporterRegistryAddress() public {
vm.expectRevert("TeleporterUpgradeable: zero teleporter registry address");
new ERC20Source(address(0), address(this), address(mockERC20));
}

function testZeroTeleporterManagerAddress() public {
vm.expectRevert("Ownable: new owner is the zero address");
new ERC20Source(
MOCK_TELEPORTER_REGISTRY_ADDRESS,
address(0),
address(mockERC20)
);
}

function testZeroFeeTokenAddress() public {
vm.expectRevert(_formatErrorMessage("zero fee token address"));
new ERC20Source(
MOCK_TELEPORTER_REGISTRY_ADDRESS,
address(this),
address(0)
);
}

function _checkExpectedWithdrawal(address recipient, uint256 amount) internal override {
vm.expectCall(
address(mockERC20), abi.encodeCall(IERC20.transfer, (address(recipient), amount))
);
vm.expectEmit(true, true, true, true, address(mockERC20));
emit Transfer(address(app), recipient, amount);
}
}
33 changes: 33 additions & 0 deletions contracts/test/NativeTokenBridgeTests.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// (c) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

// SPDX-License-Identifier: Ecosystem

pragma solidity 0.8.18;

import {TeleporterTokenBridgeTest} from "./TeleporterTokenBridgeTests.t.sol";
import {INativeTokenBridge} from "../src/interfaces/INativeTokenBridge.sol";
import {SendTokensInput} from "../src/interfaces/ITeleporterTokenBridge.sol";
import {IWrappedNativeToken} from "../src/interfaces/IWrappedNativeToken.sol";

abstract contract NativeTokenBridgeTest is TeleporterTokenBridgeTest {
INativeTokenBridge public nativeTokenBridge;

event Deposit(address indexed sender, uint256 amount);
event Withdrawal(address indexed sender, uint256 amount);

function testZeroSendAmount() public {
vm.expectRevert(_formatErrorMessage("insufficient amount to cover fees"));
_send(_createDefaultSendTokensInput(), 0);
}

function _send(SendTokensInput memory input, uint256 amount) internal virtual override {
nativeTokenBridge.send{value: amount}(input);
}

function _setUpExpectedDeposit(uint256 amount) internal virtual override {
vm.expectCall(address(feeToken), abi.encodeCall(IWrappedNativeToken.deposit, ()));
vm.expectEmit(true, true, true, true, address(feeToken));
emit Deposit(address(nativeTokenBridge), amount);
}
}
Loading

0 comments on commit ca7fcc6

Please sign in to comment.