Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(protocol): implement releaseEther & releaseERC20 #13008

Merged
merged 35 commits into from
Jan 25, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
ee57239
feat(protocol): implement releaseEther in Bridge
dantaik Jan 22, 2023
0fe9e22
add releaseERC20 in TokenVault
dantaik Jan 22, 2023
d3b910e
Update TokenVault.sol
dantaik Jan 22, 2023
4d5afa1
Update TokenVault.sol
dantaik Jan 22, 2023
00e469b
Update TokenVault.test.ts
dantaik Jan 22, 2023
5fec36c
Update TokenVault.sol
dantaik Jan 22, 2023
c35e1c6
more
dantaik Jan 22, 2023
a3aa609
Update TokenVault.sol
dantaik Jan 22, 2023
3648501
rename
dantaik Jan 22, 2023
7808ba7
Update TokenVault.sol
dantaik Jan 22, 2023
84778d8
Update TokenVault.test.ts
dantaik Jan 22, 2023
9850e4c
Update packages/protocol/contracts/bridge/TokenVault.sol
dantaik Jan 23, 2023
00d2369
Update packages/protocol/contracts/bridge/libs/LibBridgeRelease.sol
dantaik Jan 23, 2023
e13f5f2
fix
dantaik Jan 23, 2023
6354e31
fix
dantaik Jan 23, 2023
39bfc15
fix
dantaik Jan 23, 2023
c59b41e
Update CHANGELOG.md
dantaik Jan 23, 2023
94ef627
Revert "Update CHANGELOG.md"
dantaik Jan 23, 2023
77c23de
Revert "fix"
dantaik Jan 23, 2023
f3ccf5f
more
dantaik Jan 23, 2023
9556e8b
Update EtherVault.test.ts
dantaik Jan 23, 2023
7c44229
Update TokenVault.test.ts
dantaik Jan 23, 2023
7133ee4
fix
dantaik Jan 23, 2023
2c7ae10
fix and rename
dantaik Jan 23, 2023
123cf16
more
dantaik Jan 23, 2023
b3f0fcf
rename
dantaik Jan 23, 2023
7c68df4
rename
dantaik Jan 23, 2023
ffbc436
Update EtherVault.sol
dantaik Jan 23, 2023
fafe786
Update Tokenomics.test.ts
dantaik Jan 23, 2023
e2f2738
Merge branch 'main' into protocol_release_tokens_step2
dantaik Jan 23, 2023
6a650b1
Merge branch 'main' into protocol_release_tokens_step2
cyberhorsey Jan 23, 2023
93aeaec
returnXXX to releaseXXX
dantaik Jan 24, 2023
56a65d9
fix
dantaik Jan 24, 2023
1710088
Merge branch 'main' into protocol_release_tokens_step2
dantaik Jan 24, 2023
f225042
Merge branch 'main' into protocol_release_tokens_step2
dantaik Jan 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 17 additions & 1 deletion packages/protocol/contracts/bridge/Bridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import "../common/EssentialContract.sol";
import "./IBridge.sol";
import "./libs/LibBridgeData.sol";
import "./libs/LibBridgeProcess.sol";
import "./libs/LibBridgeRelease.sol";
import "./libs/LibBridgeRetry.sol";
import "./libs/LibBridgeSend.sol";
import "./libs/LibBridgeStatus.sol";
Expand Down Expand Up @@ -65,6 +66,19 @@ contract Bridge is EssentialContract, IBridge {
});
}

function releaseEther(
IBridge.Message calldata message,
bytes calldata proof
) external nonReentrant {
return
LibBridgeRelease.releaseEther({
state: state,
resolver: AddressResolver(this),
message: message,
proof: proof
});
}

function processMessage(
Message calldata message,
bytes calldata proof
Expand Down Expand Up @@ -142,7 +156,9 @@ contract Bridge is EssentialContract, IBridge {
LibBridgeSend.isDestChainEnabled(AddressResolver(this), _chainId);
}

function hashMessage(Message memory message) public pure returns (bytes32) {
function hashMessage(
Message calldata message
cyberhorsey marked this conversation as resolved.
Show resolved Hide resolved
) public pure override returns (bytes32) {
return LibBridgeData.hashMessage(message);
}

Expand Down
13 changes: 12 additions & 1 deletion packages/protocol/contracts/bridge/IBridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,22 @@ interface IBridge {
}

event SignalSent(address sender, bytes32 msgHash);

event MessageSent(bytes32 indexed msgHash, Message message);
event EtherReleased(bytes32 indexed msgHash, address to, uint256 amount);

/// Sends a message to the destination chain and takes custody
/// of Ether required in this contract. All extra Ether will be refunded.
function sendMessage(
Message memory message
) external payable returns (bytes32 msgHash);

// Release Ether with a proof that the message processing on the destination
// chain has been failed.
function releaseEther(
IBridge.Message calldata message,
bytes calldata proof
) external;

/// Checks if a msgHash has been stored on the bridge contract by the
/// current address.
function isMessageSent(bytes32 msgHash) external view returns (bool);
Expand All @@ -65,4 +72,8 @@ interface IBridge {

/// Returns the bridge state context.
function context() external view returns (Context memory context);

function hashMessage(
IBridge.Message calldata message
) external pure returns (bytes32);
}
105 changes: 91 additions & 14 deletions packages/protocol/contracts/bridge/TokenVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ contract TokenVault is EssentialContract {
string name;
}

struct MessageDeposit {
address token;
uint256 amount;
}
/*********************
* State Variables *
*********************/
Expand All @@ -51,6 +55,8 @@ contract TokenVault is EssentialContract {
// chainId => canonical address => bridged address
mapping(uint256 => mapping(address => address)) public canonicalToBridged;

mapping(bytes32 => MessageDeposit) public messageDeposits;

uint256[47] private __gap;

/*********************
Expand All @@ -67,25 +73,32 @@ contract TokenVault is EssentialContract {
);

event EtherSent(
bytes32 indexed msgHash,
address indexed from,
address indexed to,
uint256 destChainId,
uint256 amount,
bytes32 signal
uint256 amount
);

event EtherReceived(address from, uint256 amount);

event ERC20Sent(
bytes32 indexed msgHash,
address indexed from,
address indexed to,
uint256 destChainId,
address token,
uint256 amount,
bytes32 signal
uint256 amount
);

event ERC20Released(
bytes32 indexed msgHash,
address indexed from,
address token,
uint256 amount
);
event ERC20Received(
bytes32 indexed msgHash,
address indexed from,
address indexed to,
address from,
uint256 srcChainId,
address token,
uint256 amount
Expand Down Expand Up @@ -135,17 +148,20 @@ contract TokenVault is EssentialContract {
message.refundAddress = refundAddress;
message.memo = memo;

require(message.callValue == 0, "V:callValue");
Copy link
Contributor

Choose a reason for hiding this comment

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

@dantaik can you explain why this check is needed? from my understanding, the memory is initialized, and the uint256 fields callValue is initialized as 0. And between the initialization and this require statement, there is no modification to callValue.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's true, I added this check to prevent future PRs to change the value of callValue without realizing it must be 0.

Copy link
Contributor

Choose a reason for hiding this comment

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

ok ill add a comment for that.


// Ether are held by the Bridge on L1 and by the EtherVault on L2, not
// the TokenVault
bytes32 signal = IBridge(resolve("bridge", false)).sendMessage{
bytes32 msgHash = IBridge(resolve("bridge", false)).sendMessage{
value: msg.value
}(message);

emit EtherSent({
to: to,
msgHash: msgHash,
from: message.owner,
to: message.to,
destChainId: destChainId,
amount: message.depositValue,
signal: signal
amount: message.depositValue
});
}

Expand Down Expand Up @@ -224,11 +240,65 @@ contract TokenVault is EssentialContract {
message.refundAddress = refundAddress;
message.memo = memo;

bytes32 signal = IBridge(resolve("bridge", false)).sendMessage{
bytes32 msgHash = IBridge(resolve("bridge", false)).sendMessage{
value: msg.value
}(message);

emit ERC20Sent(to, destChainId, token, _amount, signal);
messageDeposits[msgHash] = MessageDeposit(token, _amount);

emit ERC20Sent({
msgHash: msgHash,
from: message.owner,
to: to,
destChainId: destChainId,
token: token,
amount: _amount
});
}

/**
* Release deposited ERC20 back to the owner on the source TokenVault with
* a proof that the message processing on the destination Bridge has failed.
*
* @param message The message that corresponds the ERC20 deposit on the
* source chain.
* @param proof The proof from the destination chain to show the message
* has failed.
*/
function releaseERC20(
IBridge.Message calldata message,
bytes calldata proof
) external nonReentrant {
require(message.owner != address(0), "B:owner");
require(message.srcChainId == block.chainid, "B:srcChainId");

IBridge bridge = IBridge(resolve("bridge", false));
bytes32 msgHash = bridge.hashMessage(message);

address token = messageDeposits[msgHash].token;
uint256 amount = messageDeposits[msgHash].amount;
require(token != address(0), "B:etherReleased");
dantaik marked this conversation as resolved.
Show resolved Hide resolved
require(
bridge.isMessageFailed(msgHash, message.destChainId, proof),
"V:notFailed"
);

messageDeposits[msgHash] = MessageDeposit(address(0), 0);

if (amount > 0) {
if (isBridgedToken[token]) {
BridgedERC20(token).bridgeMintTo(message.owner, amount);
} else {
ERC20Upgradeable(token).safeTransfer(message.owner, amount);
}
}

emit ERC20Released({
msgHash: msgHash,
from: message.owner,
token: token,
amount: amount
});
}

/**
Expand Down Expand Up @@ -262,7 +332,14 @@ contract TokenVault is EssentialContract {
BridgedERC20(token).bridgeMintTo(to, amount);
}

emit ERC20Received(to, from, ctx.srcChainId, token, amount);
emit ERC20Received({
msgHash: ctx.msgHash,
from: from,
to: to,
srcChainId: ctx.srcChainId,
token: token,
amount: amount
});
}

/*********************
Expand Down
4 changes: 2 additions & 2 deletions packages/protocol/contracts/bridge/libs/LibBridgeData.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ library LibBridgeData {
struct State {
uint256 nextMessageId;
IBridge.Context ctx; // 3 slots
uint256[46] __gap;
mapping(bytes32 => bool) etherReleased;
uint256[45] __gap;
}

struct StatusProof {
Expand All @@ -36,7 +37,6 @@ library LibBridgeData {

// Note: These events must match the ones defined in Bridge.sol.
event MessageSent(bytes32 indexed msgHash, IBridge.Message message);

event DestChainEnabled(uint256 indexed chainId, bool enabled);

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ library LibBridgeInvoke {

function invokeMessageCall(
LibBridgeData.State storage state,
IBridge.Message memory message,
IBridge.Message calldata message,
bytes32 msgHash,
uint256 gasLimit
) internal returns (bool success) {
Expand Down
52 changes: 52 additions & 0 deletions packages/protocol/contracts/bridge/libs/LibBridgeRelease.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: MIT
// _____ _ _ _ _
// |_ _|_ _(_) |_____ | | __ _| |__ ___
// | |/ _` | | / / _ \ | |__/ _` | '_ (_-<
// |_|\__,_|_|_\_\___/ |____\__,_|_.__/__/

pragma solidity ^0.8.9;

import "./LibBridgeData.sol";
import "./LibBridgeStatus.sol";

/**
* @author dantaik <dan@taiko.xyz>
*/
library LibBridgeRelease {
// using LibAddress for address;
dantaik marked this conversation as resolved.
Show resolved Hide resolved
using LibBridgeData for IBridge.Message;

event EtherReleased(bytes32 indexed msgHash, address to, uint256 amount);

function releaseEther(
LibBridgeData.State storage state,
AddressResolver resolver,
IBridge.Message calldata message,
bytes calldata proof
) internal {
require(message.owner != address(0), "B:owner");
require(message.srcChainId == block.chainid, "B:srcChainId");

bytes32 msgHash = message.hashMessage();
require(state.etherReleased[msgHash] == false, "B:etherReleased");
require(
LibBridgeStatus.isMessageFailed(
resolver,
msgHash,
message.destChainId,
proof
),
"B:notFailed"
);

state.etherReleased[msgHash] = true;
dantaik marked this conversation as resolved.
Show resolved Hide resolved

uint256 releaseAmount = message.depositValue + message.callValue;

if (releaseAmount > 0) {
(bool success, ) = message.owner.call{value: releaseAmount}("");
davidtaikocha marked this conversation as resolved.
Show resolved Hide resolved
require(success, "B:transfer");
}
emit EtherReleased(msgHash, message.owner, releaseAmount);
}
}
12 changes: 6 additions & 6 deletions packages/protocol/contracts/bridge/libs/LibBridgeStatus.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,6 @@ library LibBridgeStatus {
return MessageStatus(value);
}

function getMessageStatusSlot(
bytes32 msgHash
) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("MESSAGE_STATUS", msgHash));
}

function isMessageFailed(
AddressResolver resolver,
bytes32 msgHash,
Expand Down Expand Up @@ -92,6 +86,12 @@ library LibBridgeStatus {
});
}

function getMessageStatusSlot(
bytes32 msgHash
) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("MESSAGE_STATUS", msgHash));
}

function _setMessageStatus(bytes32 msgHash, MessageStatus status) private {
bytes32 slot = getMessageStatusSlot(msgHash);
uint256 value = uint256(status);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ contract TestLibBridgeInvoke {
event MessageInvoked(bytes32 signal, bool success);

function invokeMessageCall(
IBridge.Message memory message,
IBridge.Message calldata message,
bytes32 signal,
uint256 gasLimit
) public payable {
Expand Down