Skip to content

Commit

Permalink
Adding Fees.sol and wiring it up. (#2148)
Browse files Browse the repository at this point in the history
* Adding Fees.sol and wiring it up.

* Remove initial fee.

* fix tenscan.

* Some book keeping.

* Addressed PR comments.

* Generated ABI bindings.

---------

Co-authored-by: StefanIliev545 <[email protected]>
  • Loading branch information
StefanIliev545 and StefanIliev545 authored Dec 2, 2024
1 parent f3ebdce commit 183bba1
Show file tree
Hide file tree
Showing 20 changed files with 869 additions and 68 deletions.

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions contracts/generated/EthereumBridge/EthereumBridge.go

Large diffs are not rendered by default.

709 changes: 709 additions & 0 deletions contracts/generated/Fees/Fees.go

Large diffs are not rendered by default.

Large diffs are not rendered by default.

34 changes: 17 additions & 17 deletions contracts/generated/MerkleTreeMessageBus/MerkleTreeMessageBus.go

Large diffs are not rendered by default.

34 changes: 17 additions & 17 deletions contracts/generated/MessageBus/MessageBus.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion contracts/generated/ObsERC20/ObsERC20.go

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions contracts/generated/ObscuroBridge/ObscuroBridge.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion contracts/generated/SystemDeployer/SystemDeployer.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion contracts/generated/WrappedERC20/WrappedERC20.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion contracts/src/bridge/IBridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ interface IBridge {
address asset,
uint256 amount,
address receiver
) external;
) external payable;

// This function is called to retrieve assets that have been sent on the other layer.
// In the basic implementation it is only callable from the CrossChainMessenger when a message is
Expand Down
2 changes: 1 addition & 1 deletion contracts/src/bridge/L1/ObscuroBridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ contract ObscuroBridge is
address asset,
uint256 amount,
address receiver
) external override {
) external payable override {
require(amount > 0, "Attempting empty transfer.");
require(
hasRole(ERC20_TOKEN_ROLE, asset),
Expand Down
2 changes: 1 addition & 1 deletion contracts/src/bridge/L2/EthereumBridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ contract EthereumBridge is
address asset,
uint256 amount,
address receiver
) external {
) external payable {
require(hasTokenMapping(asset), "No mapping for token.");

WrappedERC20 token = wrappedTokens[asset];
Expand Down
2 changes: 1 addition & 1 deletion contracts/src/messaging/IMessageBus.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ interface IMessageBus {
uint32 topic,
bytes calldata payload,
uint8 consistencyLevel
) external returns (uint64 sequence);
) external payable returns (uint64 sequence);

function sendValueToL2(
address receiver,
Expand Down
45 changes: 36 additions & 9 deletions contracts/src/messaging/MessageBus.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "./Structs.sol";

import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "../system/Fees.sol";

contract MessageBus is IMessageBus, Initializable, OwnableUpgradeable {

Expand All @@ -14,8 +15,9 @@ contract MessageBus is IMessageBus, Initializable, OwnableUpgradeable {
_disableInitializers();
}

function initialize(address caller) public initializer {
function initialize(address caller, address feesAddress) public initializer {
__Ownable_init(caller);
fees = IFees(feesAddress);
}

// Since this contract exists on the L2, when messages are added from the L1, we can have the from address be the same as self.
Expand All @@ -26,10 +28,6 @@ contract MessageBus is IMessageBus, Initializable, OwnableUpgradeable {
_;
}

function messageFee() internal virtual returns (uint256) {
return 0;
}

// This mapping contains the block timestamps where messages become valid
// It is used in order to have challenge period.
mapping(bytes32 => uint256) messageFinalityTimestamps;
Expand All @@ -41,6 +39,7 @@ contract MessageBus is IMessageBus, Initializable, OwnableUpgradeable {
// Whenever a message is published, this sequence number increments.
// This gives ordering to messages, guaranteed by us.
mapping(address => uint64) addressSequences;
IFees fees;

function incrementSequence(
address sender
Expand All @@ -54,8 +53,18 @@ contract MessageBus is IMessageBus, Initializable, OwnableUpgradeable {
uint256 amount
) external payable {
require(msg.value > 0 && msg.value == amount, "Attempting to send value without providing Ether");

uint256 amountToTransfer = msg.value;
if (address(fees) != address(0)) {
uint256 feesToTransfer = getValueTransferFee();
require(msg.value >= feesToTransfer, "Insufficient funds to send value");
amountToTransfer = msg.value - feesToTransfer;
(bool ok, ) = address(fees).call{value: feesToTransfer}("");
require(ok, "Failed to send fees to fees contract");
}

uint64 sequence = incrementSequence(msg.sender);
emit ValueTransfer(msg.sender, receiver, msg.value, sequence);
emit ValueTransfer(msg.sender, receiver, amountToTransfer, sequence);
}

function receiveValueFromL2(
Expand All @@ -66,6 +75,21 @@ contract MessageBus is IMessageBus, Initializable, OwnableUpgradeable {
require(ok, "failed sending value");
}

function getFixedDataLength() internal pure returns (uint256) {
return 4 + // nonce (uint32)
4 + // topic (uint32)
1 + // consistencyLevel (uint8)
8; // sequence (uint64)
}

function getValueTransferFee() internal view returns (uint256) {
return fees.messageFee(32); //just a hash
}

function getMessageFee(uint256 payloadLength) internal view returns (uint256) {
return fees.messageFee(payloadLength + getFixedDataLength());
}

// This method is called from contracts to publish messages to the other linked message bus.
// nonce - This is provided and serves as deduplication nonce. It can also be used to group a batch of messages together.
// topic - This is the topic for which the payload is published.
Expand All @@ -79,9 +103,12 @@ contract MessageBus is IMessageBus, Initializable, OwnableUpgradeable {
uint32 topic,
bytes calldata payload,
uint8 consistencyLevel
) external override returns (uint64 sequence) {
//TODO: implement messageFee mechanism.
//require(msg.value >= messageFee());
) external payable override returns (uint64 sequence) {
if (address(fees) != address(0)) { // No fee required for L1 to L2 messages.
require(msg.value >= getMessageFee(payload.length), "Insufficient funds to publish message");
(bool ok, ) = address(fees).call{value: msg.value}("");
require(ok, "Failed to send fees to fees contract");
}

sequence = incrementSequence(msg.sender);
emit LogMessagePublished(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,6 @@ abstract contract CrossChainEnabledObscuro is Initializable {
bytes memory payload = abi.encode(
ICrossChainMessenger.CrossChainCall(target, message, gas)
);
messageBus.publishMessage(nonce++, topic, payload, consistencyLevel);
messageBus.publishMessage{value: msg.value}(nonce++, topic, payload, consistencyLevel);
}
}
54 changes: 54 additions & 0 deletions contracts/src/system/Fees.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

interface IFees {
function messageFee(uint256 messageSize) external view returns (uint256);
}

// Contract that will contain fees for contracts that need to apply them
contract Fees is Initializable, OwnableUpgradeable {

uint256 private _messageFeePerByte;

// Constructor disables initializer;
// Only owner functions will not be callable on implementation
constructor() {
_disableInitializers();
}

// initialization function to be used by the proxy.
function initialize(uint256 initialMessageFeePerByte, address eoaOwner) public initializer {
__Ownable_init(eoaOwner);
_messageFeePerByte = initialMessageFeePerByte;
}

// Helper function to calculate the fee for a message
function messageFee(uint256 messageSize) external view returns (uint256) {
return _messageFeePerByte * messageSize;
}

// The EOA owner can set the message fee to ensure sequencer is not publishing
// at a loss
function setMessageFee(uint256 newMessageFeePerByte) external onlyOwner{
_messageFeePerByte = newMessageFeePerByte;
}

// The EOA owner can collect the fees
function withdrawalCollectedFees() external onlyOwner {
payable(owner()).transfer(address(this).balance);
}

// For now just the whole balance as we only collect fees
// from the message bus
function collectedFees() external view returns (uint256) {
return address(this).balance;
}

// For now channel all here as we only collect fees
// from the message bus
receive() external payable {
}
}
17 changes: 14 additions & 3 deletions contracts/src/system/SystemDeployer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.so
import {MessageBus} from "../messaging/MessageBus.sol";
import "./TransactionPostProcessor.sol";
import {PublicCallbacks} from "./PublicCallbacks.sol";
import {Fees} from "./Fees.sol";

contract SystemDeployer {
event SystemContractDeployed(string name, address contractAddress);

constructor(address eoaAdmin) {
deployAnalyzer(eoaAdmin);
deployMessageBus(eoaAdmin);
address feesProxy = deployFees(eoaAdmin, 0);
deployMessageBus(eoaAdmin, feesProxy);
deployPublicCallbacks(eoaAdmin);
}

Expand All @@ -23,9 +25,9 @@ contract SystemDeployer {
emit SystemContractDeployed("TransactionsPostProcessor", transactionsPostProcessorProxy);
}

function deployMessageBus(address eoaAdmin) internal {
function deployMessageBus(address eoaAdmin, address feesAddress) internal {
MessageBus messageBus = new MessageBus();
bytes memory callData = abi.encodeWithSelector(messageBus.initialize.selector, eoaAdmin);
bytes memory callData = abi.encodeWithSelector(messageBus.initialize.selector, eoaAdmin, feesAddress);
address messageBusProxy = deployProxy(address(messageBus), eoaAdmin, callData);

emit SystemContractDeployed("MessageBus", messageBusProxy);
Expand All @@ -39,6 +41,15 @@ contract SystemDeployer {
emit SystemContractDeployed("PublicCallbacks", publicCallbacksProxy);
}

function deployFees(address eoaAdmin, uint256 initialMessageFeePerByte) internal returns (address) {
Fees fees = new Fees();
bytes memory callData = abi.encodeWithSelector(fees.initialize.selector, initialMessageFeePerByte, eoaAdmin);
address feesProxy = deployProxy(address(fees), eoaAdmin, callData);

emit SystemContractDeployed("Fees", feesProxy);
return feesProxy;
}

function deployProxy(address _logic, address _admin, bytes memory _data) internal returns (address proxyAddress) {
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
_logic, // Address of the logic contract
Expand Down
2 changes: 1 addition & 1 deletion contracts/test/bridge-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ describe("Bridge", function () {

const encodedCalldata = await messengerL2.encodeCall(await bridgeL2.getAddress(), encodedData);

const tx = busL1.publishMessage(0, 0, encodedCalldata, 0);
const tx = busL1.publishMessage(0, 0, encodedCalldata, 0, {value: 100});
expect(tx, "Anyone should be able to publish a message!");

messages = await submitMessagesFromTx(await tx);
Expand Down
2 changes: 1 addition & 1 deletion integration/tenscan/tenscan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func TestTenscan(t *testing.T) {
statusCode, body, err := fasthttp.Get(nil, fmt.Sprintf("%s/count/contracts/", serverAddress))
assert.NoError(t, err)
assert.Equal(t, 200, statusCode)
assert.Equal(t, "{\"count\":10}", string(body))
assert.Equal(t, "{\"count\":13}", string(body))

statusCode, body, err = fasthttp.Get(nil, fmt.Sprintf("%s/count/transactions/", serverAddress))
assert.NoError(t, err)
Expand Down

0 comments on commit 183bba1

Please sign in to comment.