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

Required gas, events, reentrancy, go linter #66

Merged
merged 14 commits into from
Apr 5, 2024
31 changes: 31 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# This file configures github.com/golangci/golangci-lint.

run:
timeout: 3m
tests: true
# skip auto-generated files.
skip-dirs:
- "abi-bindings/go"
skip-files:
- ".*mock.*"

issues:
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
max-same-issues: 0

linters:
disable-all: true
enable:
- goimports
- gosimple
- govet
- ineffassign
- misspell
- unconvert
- unused
- whitespace
- lll

linters-settings:
gofmt:
simplify: true
162 changes: 154 additions & 8 deletions abi-bindings/go/ERC20Destination/ERC20Destination.go

Large diffs are not rendered by default.

162 changes: 154 additions & 8 deletions abi-bindings/go/ERC20Source/ERC20Source.go

Large diffs are not rendered by default.

162 changes: 154 additions & 8 deletions abi-bindings/go/NativeTokenSource/NativeTokenSource.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion contracts/src/ERC20Destination.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ contract ERC20Destination is IERC20Bridge, TeleporterTokenDestination, ERC20 {
*
* @dev See {IERC20Bridge-send}
*/
function send(SendTokensInput calldata input, uint256 amount) external {
function send(SendTokensInput calldata input, uint256 amount) external nonReentrant {
_send(input, amount);
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/src/ERC20Source.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ contract ERC20Source is IERC20Bridge, TeleporterTokenSource {
/**
* @dev See {IERC20Bridge-send}
*/
function send(SendTokensInput calldata input, uint256 amount) external {
function send(SendTokensInput calldata input, uint256 amount) external nonReentrant {
_send(input, amount, false);
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/src/NativeTokenSource.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ contract NativeTokenSource is INativeTokenBridge, TeleporterTokenSource {
/**
* @dev See {INativeTokenBridge-send}
*/
function send(SendTokensInput calldata input) external payable {
function send(SendTokensInput calldata input) external payable nonReentrant {
_send(input, msg.value, false);
}

Expand Down
13 changes: 8 additions & 5 deletions contracts/src/TeleporterTokenDestination.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@ abstract contract TeleporterTokenDestination is
/// @notice The ERC20 token this contract uses to pay for Teleporter fees.
address public immutable feeTokenAddress;

// TODO: these are values brought from the example ERC20Bridge contract.
// Need to figure out appropriate values.
uint256 public constant SEND_TOKENS_REQUIRED_GAS = 300_000;
/// @notice Required gas limit for sending tokens to another chain.
uint256 public constant SEND_TOKENS_REQUIRED_GAS = 220_000;

/**
* @notice Initializes this destination token bridge instance to receive
Expand Down Expand Up @@ -84,6 +83,10 @@ abstract contract TeleporterTokenDestination is
* - `amount` must be greater than `input.primaryFee`
*/
function _send(SendTokensInput calldata input, uint256 amount) internal virtual {
require(
input.destinationBlockchainID != bytes32(0),
"TeleporterTokenDestination: zero destination blockchain ID"
);
require(
input.destinationBridgeAddress != address(0),
"TeleporterTokenDestination: zero destination bridge address"
Expand Down Expand Up @@ -120,7 +123,6 @@ abstract contract TeleporterTokenDestination is
destinationBlockchainID: sourceBlockchainID,
destinationAddress: tokenSourceAddress,
feeInfo: TeleporterFeeInfo({feeTokenAddress: feeTokenAddress, amount: input.primaryFee}),
// TODO: placeholder value
requiredGasLimit: SEND_TOKENS_REQUIRED_GAS,
allowedRelayerAddresses: input.allowedRelayerAddresses,
message: abi.encode(
Expand All @@ -138,7 +140,7 @@ abstract contract TeleporterTokenDestination is
})
);

emit SendTokens(messageID, msg.sender, amount);
emit SendTokens(messageID, msg.sender, input, amount);
}

/**
Expand All @@ -161,6 +163,7 @@ abstract contract TeleporterTokenDestination is
);
(address recipient, uint256 amount) = abi.decode(message, (address, uint256));

emit WithdrawTokens(recipient, amount);
_withdraw(recipient, amount);
}

Expand Down
13 changes: 8 additions & 5 deletions contracts/src/TeleporterTokenSource.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,8 @@ abstract contract TeleporterTokenSource is ITeleporterTokenBridge, TeleporterOwn
=> mapping(address destinationBridgeAddress => uint256 balance)
) public bridgedBalances;

// TODO: these are values brought from the example ERC20Bridge contract.
// Need to figure out appropriate values.
uint256 public constant SEND_TOKENS_REQUIRED_GAS = 300_000;
/// @notice Required gas limit for sending tokens to another chain.
uint256 public constant SEND_TOKENS_REQUIRED_GAS = 80_000;
Copy link
Collaborator

@michaelkaplan13 michaelkaplan13 Apr 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that token source contracts are intended to be compatible with any destination contracts that implement the required interface, I think we should have the gas limit for messages sent from source -> destination be provided as a parameter for each call to send. A destination contracts can have arbitrarily complex logic, so we shouldn't try to fit one gas limit to all of them.

It could make sense to have some minimum or default that we don't allow the passed value to be less than, but I think we'll need to allow for callers to specify higher values if needed.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added requiredGasLimit to SendTokensInput since destination token bridges also need to pass in case for multihop.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The required gas limit for message from destination token bridge -> source is still a constant in TeleporterTokenDestination though

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the same logic applies to messages to the source, since the source can be composed with an arbitrary ERC20, which can also execute arbitrary logic.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, maybe we should have the value set in the constructor of TeleporterTokenDestination? Its value should be more or less constant for a given source contract, but it could change from one source to another as Cam called out.


/**
* @notice Initializes this source token bridge instance to send
Expand Down Expand Up @@ -76,6 +75,10 @@ abstract contract TeleporterTokenSource is ITeleporterTokenBridge, TeleporterOwn
uint256 amount,
bool isMultihop
) internal virtual {
require(
input.destinationBlockchainID != bytes32(0),
"TeleporterTokenSource: zero destination blockchain ID"
);
require(
input.destinationBlockchainID != blockchainID,
"TeleporterTokenSource: cannot bridge to same chain"
Expand Down Expand Up @@ -106,14 +109,13 @@ abstract contract TeleporterTokenSource is ITeleporterTokenBridge, TeleporterOwn
destinationBlockchainID: input.destinationBlockchainID,
destinationAddress: input.destinationBridgeAddress,
feeInfo: TeleporterFeeInfo({feeTokenAddress: feeTokenAddress, amount: input.primaryFee}),
// TODO: Set requiredGasLimit
requiredGasLimit: SEND_TOKENS_REQUIRED_GAS,
allowedRelayerAddresses: input.allowedRelayerAddresses,
message: abi.encode(input.recipient, amount)
})
);

emit SendTokens(messageID, msg.sender, amount);
emit SendTokens(messageID, msg.sender, input, amount);
}

/**
Expand Down Expand Up @@ -150,6 +152,7 @@ abstract contract TeleporterTokenSource is ITeleporterTokenBridge, TeleporterOwn
input.destinationBridgeAddress == address(this),
"TeleporterTokenSource: invalid bridge address"
);
emit WithdrawTokens(input.recipient, amount);
_withdraw(input.recipient, amount);
return;
}
Expand Down
13 changes: 11 additions & 2 deletions contracts/src/interfaces/ITeleporterTokenBridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,16 @@ struct SendTokensInput {
interface ITeleporterTokenBridge is ITeleporterReceiver {
/**
* @notice Emitted when tokens are sent to another chain.
* TODO: might want to add SendTokensInput as a parameter
*/
event SendTokens(bytes32 indexed teleporterMessageID, address indexed sender, uint256 amount);
event SendTokens(
bytes32 indexed teleporterMessageID,
address indexed sender,
SendTokensInput input,
uint256 amount
);

/**
* @notice Emitted when tokens are withdrawn from the token bridge contract.
*/
event WithdrawTokens(address indexed recipient, uint256 amount);
}
18 changes: 16 additions & 2 deletions contracts/test/TeleporterTokenBridgeTests.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,24 @@ abstract contract TeleporterTokenBridgeTest is Test {
ITeleporterTokenBridge public tokenBridge;
IERC20 public feeToken;

event SendTokens(bytes32 indexed teleporterMessageID, address indexed sender, uint256 amount);
event SendTokens(
bytes32 indexed teleporterMessageID,
address indexed sender,
SendTokensInput input,
uint256 amount
);

event WithdrawTokens(address indexed recipient, uint256 amount);

event Transfer(address indexed from, address indexed to, uint256 value);

function testZeroDestinationBlockchainID() public {
SendTokensInput memory input = _createDefaultSendTokensInput();
input.destinationBlockchainID = bytes32(0);
vm.expectRevert(_formatErrorMessage("zero destination blockchain ID"));
_send(input, 0);
}

function testZeroDestinationBridge() public {
SendTokensInput memory input = _createDefaultSendTokensInput();
input.destinationBridgeAddress = address(0);
Expand Down Expand Up @@ -120,7 +134,7 @@ abstract contract TeleporterTokenBridgeTest is Test {

_checkExpectedTeleporterCalls(input, bridgedAmount);
vm.expectEmit(true, true, true, true, address(tokenBridge));
emit SendTokens(_MOCK_MESSAGE_ID, address(this), bridgedAmount);
emit SendTokens(_MOCK_MESSAGE_ID, address(this), input, bridgedAmount);
_send(input, amount);
}

Expand Down
2 changes: 2 additions & 0 deletions contracts/test/TeleporterTokenDestinationTests.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ abstract contract TeleporterTokenDestinationTest is TeleporterTokenBridgeTest {
function testReceiveWithdrawSuccess() public {
uint256 amount = 2;
vm.prank(MOCK_TELEPORTER_MESSENGER_ADDRESS);
vm.expectEmit(true, true, true, true, address(tokenDestination));
emit WithdrawTokens(DEFAULT_RECIPIENT_ADDRESS, amount);
_checkExpectedWithdrawal(DEFAULT_RECIPIENT_ADDRESS, amount);
tokenDestination.receiveTeleporterMessage(
DEFAULT_SOURCE_BLOCKCHAIN_ID,
Expand Down
6 changes: 5 additions & 1 deletion contracts/test/TeleporterTokenSourceTests.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ abstract contract TeleporterTokenSourceTest is TeleporterTokenBridgeTest {
});

vm.prank(MOCK_TELEPORTER_MESSENGER_ADDRESS);
vm.expectEmit(true, true, true, true, address(tokenSource));
emit WithdrawTokens(DEFAULT_RECIPIENT_ADDRESS, bridgedAmount);
_checkExpectedWithdrawal(DEFAULT_RECIPIENT_ADDRESS, bridgedAmount);
tokenSource.receiveTeleporterMessage(
DEFAULT_DESTINATION_BLOCKCHAIN_ID,
Expand Down Expand Up @@ -141,7 +143,9 @@ abstract contract TeleporterTokenSourceTest is TeleporterTokenBridgeTest {
_checkExpectedTeleporterCalls(input, bridgedAmount);

vm.expectEmit(true, true, true, true, address(tokenSource));
emit SendTokens(_MOCK_MESSAGE_ID, address(MOCK_TELEPORTER_MESSENGER_ADDRESS), bridgedAmount);
emit SendTokens(
_MOCK_MESSAGE_ID, address(MOCK_TELEPORTER_MESSENGER_ADDRESS), input, bridgedAmount
);

vm.prank(MOCK_TELEPORTER_MESSENGER_ADDRESS);
tokenSource.receiveTeleporterMessage(
Expand Down
3 changes: 1 addition & 2 deletions scripts/lint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ function golangLinter() {
function runAll() {
solFormat
solLinter
# TODO: add back in when we have golang tests
# golangLinter
golangLinter
}

function printHelp() {
Expand Down
Loading