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): redesign Bridge fee & gasLimit and remove 2-step processing #16739

Merged
merged 25 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
462 changes: 166 additions & 296 deletions packages/protocol/contracts/bridge/Bridge.sol

Large diffs are not rendered by default.

71 changes: 13 additions & 58 deletions packages/protocol/contracts/bridge/IBridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,46 +16,34 @@ interface IBridge {

struct Message {
// Message ID whose value is automatically assigned.
uint128 id;
uint64 id;
// The max processing fee for the relayer. This fee has 3 parts:
// - the fee for message calldata.
// - the minimal fee reserve for general processing, excluding function call.
// - the invocation fee for the function call.
// Any unpaid fee will be refunded to the destOwner on the destination chain.
// Note that fee must be 0 if gasLimit is 0, or large enough to make the invocation fee
// non-zero.
uint64 fee;
// gasLimit that the processMessage call must have.
uint32 gasLimit;
// The address, EOA or contract, that interacts with this bridge.
// The value is automatically assigned.
address from;
// Source chain ID whose value is automatically assigned.
uint64 srcChainId;
// Destination chain ID where the `to` address lives.
uint64 destChainId;
// The owner of the message on the source chain.
address srcOwner;
// Destination chain ID where the `to` address lives.
uint64 destChainId;
// The owner of the message on the destination chain.
address destOwner;
// The destination address on the destination chain.
address to;
// Alternate address to send any refund on the destination chain.
// If blank, defaults to destOwner.
address refundTo;
// value to invoke on the destination chain.
uint256 value;
// Processing fee for the relayer.
uint256 fee;
// gasLimit to invoke on the destination chain. If this value is zero, only destOwner can
// process the message. This value will mostly be respected when retrying
// (gas > gas_limit) but not when processed by the owner.
uint256 gasLimit;
// callData to invoke on the destination chain.
bytes data;
// Optional memo.
string memo;
}

// Note that this struct shall take only 1 slot to minimize gas cost
struct ProofReceipt {
// The time a message is marked as received on the destination chain
uint64 receivedAt;
// The address that can execute the message after the invocation delay without an extra
// delay.
// For a failed message, preferredExecutor's value doesn't matter as only the owner can
// invoke the message.
address preferredExecutor;
}

// Struct representing the context of a bridge operation.
Expand All @@ -71,44 +59,11 @@ interface IBridge {
/// @param message The message.
event MessageSent(bytes32 indexed msgHash, Message message);

/// @notice Emitted when a message is received.
/// @param msgHash The hash of the message.
/// @param message The message.
/// @param isRecall True if the message is a recall.
event MessageReceived(bytes32 indexed msgHash, Message message, bool isRecall);

/// @notice Emitted when a message is recalled.
/// @param msgHash The hash of the message.
event MessageRecalled(bytes32 indexed msgHash);

/// @notice Emitted when a message is executed.
/// @param msgHash The hash of the message.
event MessageExecuted(bytes32 indexed msgHash);

/// @notice Emitted when a message is retried.
/// @param msgHash The hash of the message.
event MessageRetried(bytes32 indexed msgHash);

/// @notice Emitted when a message is marked failed.
/// @param msgHash The hash of the message.
event MessageFailed(bytes32 indexed msgHash);

/// @notice Emitted when the status of a message changes.
/// @param msgHash The hash of the message.
/// @param status The new status of the message.
event MessageStatusChanged(bytes32 indexed msgHash, Status status);

/// @notice Emitted when a message is suspended or unsuspended.
/// @param msgHash The hash of the message.
/// @param suspended True if the message is suspended.
/// @param receivedAt The received-at timestamp, 0 if suspended is true.
event MessageSuspended(bytes32 msgHash, bool suspended, uint64 receivedAt);

/// @notice Emitted when an address is banned or unbanned.
/// @param addr The address to ban or unban.
/// @param banned True if the address is banned.
event AddressBanned(address indexed addr, bool banned);

/// @notice Sends a message to the destination chain and takes custody
/// of Ether required in this contract.
/// @param _message The message to be sent.
Expand Down
47 changes: 23 additions & 24 deletions packages/protocol/contracts/bridge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,33 +24,34 @@ If user wants to bridge ether, he/she will initiate a bridge transaction with `s
```
struct Message {
// Message ID whose value is automatically assigned.
uint128 id;
uint64 id;
// The max processing fee for the relayer. This fee has 3 parts:
// - the fee for message calldata.
// - the minimal fee reserve for general processing, excluding function call.
// - the invocation fee for the function call.
// Any unpaid fee will be refunded to the destOwner on the destination chain.
// Note that fee must be 0 if gasLimit is 0, or large enough to make the invocation fee
// non-zero.
uint64 fee;
// gasLimit that the processMessage call must have.
uint32 gasLimit;
// The address, EOA or contract, that interacts with this bridge.
// The value is automatically assigned.
address from;
// Source chain ID whose value is automatically assigned.
uint64 srcChainId;
// Destination chain ID where the `to` address lives.
uint64 destChainId;
// The owner of the message on the source chain.
address srcOwner;
// Destination chain ID where the `to` address lives.
uint64 destChainId;
// The owner of the message on the destination chain.
address destOwner;
// The destination address on the destination chain.
address to;
// Alternate address to send any refund on the destination chain.
// If blank, defaults to destOwner.
address refundTo;
// value to invoke on the destination chain.
uint256 value;
// Processing fee for the relayer.
uint256 fee;
// gasLimit to invoke on the destination chain. If this value is zero, only destOwner can process the message.
uint256 gasLimit;
// callData to invoke on the destination chain.
bytes data;
// Optional memo.
string memo;
}
```

Expand All @@ -66,31 +67,29 @@ If user wants to bridge other tokens (`ERC20`, `ERC1155` or `ERC721`.) he/she wi
In case of ERC20 the transaction can be initiated by initializing a struct (below) and calling `sendToken`:

```
struct BridgeTransferOp {
uint256 destChainId;
struct BridgeTransferOp {
uint64 destChainId;
address destOwner;
address to;
uint64 fee;
address token;
uint32 gasLimit;
uint256 amount;
uint256 gasLimit;
uint256 fee;
address refundTo;
string memo;
}
```

In case of `ERC1155` or `ERC721`, the mechanism is the same but struct looks like this:

```
struct BridgeTransferOp {
uint256 destChainId;
struct BridgeTransferOp {
uint64 destChainId;
address destOwner;
address to;
uint64 fee;
address token;
uint32 gasLimit;
uint256[] tokenIds;
uint256[] amounts;
uint256 gasLimit;
uint256 fee;
address refundTo;
string memo;
}
```

Expand Down
4 changes: 3 additions & 1 deletion packages/protocol/contracts/signal/ISignalService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,15 @@ interface ISignalService {
/// @param _signal The signal (message) to send.
/// @param _proof Merkle proof that the signal was persisted on the
/// source chain.
/// @return numCacheOps_ The number of newly cached items.
function proveSignalReceived(
uint64 _chainId,
address _app,
bytes32 _signal,
bytes calldata _proof
)
external;
external
returns (uint256 numCacheOps_);

/// @notice Verifies if a signal has been received on the target chain.
/// This is the "readonly" version of proveSignalReceived.
Expand Down
7 changes: 5 additions & 2 deletions packages/protocol/contracts/signal/SignalService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,13 @@ contract SignalService is EssentialContract, ISignalService {
virtual
whenNotPaused
nonReentrant
returns (uint256 numCacheOps_)
{
CacheAction[] memory actions = // actions for caching
_verifySignalReceived(_chainId, _app, _signal, _proof, true);

for (uint256 i; i < actions.length; ++i) {
_cache(actions[i]);
numCacheOps_ += _cache(actions[i]);
}
}

Expand Down Expand Up @@ -259,12 +260,13 @@ contract SignalService is EssentialContract, ISignalService {
emit SignalSent(_app, _signal, slot_, _value);
}

function _cache(CacheAction memory _action) private {
function _cache(CacheAction memory _action) private returns (uint256 numCacheOps_) {
// cache state root
bool cacheStateRoot = _action.option == CacheOption.CACHE_BOTH
|| _action.option == CacheOption.CACHE_STATE_ROOT;

if (cacheStateRoot && _action.isFullProof && !_action.isLastHop) {
numCacheOps_ = 1;
_syncChainData(
_action.chainId, LibStrings.H_STATE_ROOT, _action.blockId, _action.rootHash
);
Expand All @@ -275,6 +277,7 @@ contract SignalService is EssentialContract, ISignalService {
|| _action.option == CacheOption.CACHE_SIGNAL_ROOT;

if (cacheSignalRoot && (_action.isFullProof || !_action.isLastHop)) {
numCacheOps_ += 1;
_syncChainData(
_action.chainId, LibStrings.H_SIGNAL_ROOT, _action.blockId, _action.signalRoot
);
Expand Down
19 changes: 4 additions & 15 deletions packages/protocol/contracts/tokenvault/BaseNFTVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,17 @@ abstract contract BaseNFTVault is BaseVault {
string name;
}

// Struct representing the details of a bridged token transfer operation.
/// @devStruct representing the details of a bridged token transfer operation.
/// 5 slots
struct BridgeTransferOp {
// Destination chain ID.
uint64 destChainId;
// The owner of the bridge message on the destination chain.
address destOwner;
// Recipient address.
address to;
// Address of the token.
uint64 fee;
address token;
// IDs of the tokens to transfer.
uint32 gasLimit;
uint256[] tokenIds;
// Amounts of tokens to transfer.
uint256[] amounts;
// Gas limit for the operation.
uint256 gasLimit;
// Processing fee for the relayer.
uint256 fee;
// Address for refund, if needed.
address refundTo;
// Optional memo.
string memo;
}

/// @notice ERC1155 interface ID.
Expand Down
6 changes: 2 additions & 4 deletions packages/protocol/contracts/tokenvault/ERC1155Vault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ contract ERC1155Vault is BaseNFTVault, ERC1155ReceiverUpgradeable {
/// @param _op Option for sending the ERC1155 token.
/// @return message_ The constructed message.

function sendToken(BridgeTransferOp memory _op)
function sendToken(BridgeTransferOp calldata _op)
external
payable
whenNotPaused
Expand All @@ -70,12 +70,10 @@ contract ERC1155Vault is BaseNFTVault, ERC1155ReceiverUpgradeable {
srcOwner: msg.sender,
destOwner: _op.destOwner != address(0) ? _op.destOwner : msg.sender,
to: resolve(_op.destChainId, name(), false),
refundTo: _op.refundTo,
value: msg.value - _op.fee,
fee: _op.fee,
gasLimit: _op.gasLimit,
data: data,
memo: _op.memo
data: data
});

// Send the message and obtain the message hash
Expand Down
11 changes: 4 additions & 7 deletions packages/protocol/contracts/tokenvault/ERC20Vault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,15 @@ contract ERC20Vault is BaseVault {
}

/// @dev Represents an operation to send tokens to another chain.
/// 4 slots
struct BridgeTransferOp {
uint64 destChainId;
address destOwner;
address to;
uint64 fee;
address token;
uint32 gasLimit;
uint256 amount;
uint256 gasLimit;
uint256 fee;
address refundTo;
string memo;
}

/// @notice Mappings from bridged tokens to their canonical tokens.
Expand Down Expand Up @@ -232,12 +231,10 @@ contract ERC20Vault is BaseVault {
srcOwner: msg.sender,
destOwner: _op.destOwner != address(0) ? _op.destOwner : msg.sender,
to: resolve(_op.destChainId, name(), false),
refundTo: _op.refundTo,
value: msg.value - _op.fee,
fee: _op.fee,
gasLimit: _op.gasLimit,
data: data,
memo: _op.memo
data: data
});

bytes32 msgHash;
Expand Down
6 changes: 2 additions & 4 deletions packages/protocol/contracts/tokenvault/ERC721Vault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ contract ERC721Vault is BaseNFTVault, IERC721Receiver {
/// by invoking the message call.
/// @param _op Option for sending the ERC721 token.
/// @return message_ The constructed message.
function sendToken(BridgeTransferOp memory _op)
function sendToken(BridgeTransferOp calldata _op)
external
payable
whenNotPaused
Expand All @@ -55,12 +55,10 @@ contract ERC721Vault is BaseNFTVault, IERC721Receiver {
srcOwner: msg.sender,
destOwner: _op.destOwner != address(0) ? _op.destOwner : msg.sender,
to: resolve(_op.destChainId, name(), false),
refundTo: _op.refundTo,
value: msg.value - _op.fee,
fee: _op.fee,
gasLimit: _op.gasLimit,
data: data,
memo: _op.memo
data: data
});

bytes32 msgHash;
Expand Down
10 changes: 3 additions & 7 deletions packages/protocol/genesis/GenerateGenesis.g.sol
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ contract TestGenerateGenesis is Test, AddressResolver {

assertEq(ownerSecurityCouncil, bridgeProxy.owner());

vm.expectRevert(Bridge.B_NOT_RECEIVED.selector);
vm.expectRevert(Bridge.B_PERMISSION_DENIED.selector);
bridgeProxy.processMessage(
IBridge.Message({
id: 0,
Expand All @@ -164,12 +164,10 @@ contract TestGenerateGenesis is Test, AddressResolver {
srcOwner: address(0),
destOwner: address(0),
to: address(0),
refundTo: address(0),
value: 0,
fee: 0,
gasLimit: 0,
data: "",
memo: ""
data: ""
}),
""
);
Expand All @@ -190,12 +188,10 @@ contract TestGenerateGenesis is Test, AddressResolver {
srcOwner: address(0),
destOwner: address(0),
to: address(0),
refundTo: address(0),
value: 0,
fee: 0,
gasLimit: 0,
data: "",
memo: ""
data: ""
}),
""
);
Expand Down