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(contracts/core): start solve inbox #2348

Merged
merged 1 commit into from
Nov 1, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 14 additions & 8 deletions contracts/core/.gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ Admin_Test:test_pause_unpause_bridge() (gas: 21501844)
Admin_Test:test_pause_unpause_xcall() (gas: 26426087)
Admin_Test:test_pause_unpause_xsubmit() (gas: 26425838)
Admin_Test:test_upgrade() (gas: 30507332)
AllocPredeploys_Test:test_num_allocs() (gas: 1181152549)
AllocPredeploys_Test:test_num_allocs() (gas: 1181198035)
AllocPredeploys_Test:test_predeploys() (gas: 1181134337)
AllocPredeploys_Test:test_preinstalls() (gas: 1181850775)
AllocPredeploys_Test:test_proxies() (gas: 1408777576)
FeeOracleV1_Test:test_bulkSetFeeParams() (gas: 172862)
FeeOracleV1_Test:test_feeFor() (gas: 122551)
FeeOracleV1_Test:test_setBaseGasLimit() (gas: 32208)
FeeOracleV1_Test:test_setGasPrice() (gas: 40996)
FeeOracleV1_Test:test_setManager() (gas: 45845)
FeeOracleV1_Test:test_setProtocolFee() (gas: 31442)
FeeOracleV1_Test:test_setToNativeRate() (gas: 41049)
FeeOracleV1_Test:test_bulkSetFeeParams() (gas: 173154)
FeeOracleV1_Test:test_feeFor() (gas: 122830)
FeeOracleV1_Test:test_setBaseGasLimit() (gas: 32375)
FeeOracleV1_Test:test_setGasPrice() (gas: 41034)
FeeOracleV1_Test:test_setManager() (gas: 45904)
FeeOracleV1_Test:test_setProtocolFee() (gas: 31610)
FeeOracleV1_Test:test_setToNativeRate() (gas: 41132)
FeeOracleV2_Test:test_bulkSetFeeParams() (gas: 119117)
FeeOracleV2_Test:test_feeFor() (gas: 103301)
FeeOracleV2_Test:test_setBaseGasLimit() (gas: 32009)
Expand All @@ -22,6 +22,12 @@ FeeOracleV2_Test:test_setExecGasPrice() (gas: 44247)
FeeOracleV2_Test:test_setManager() (gas: 45775)
FeeOracleV2_Test:test_setProtocolFee() (gas: 32226)
FeeOracleV2_Test:test_setToNativeRate() (gas: 43640)
Inbox_request_Test:test_request_multiToken() (gas: 547583)
Inbox_request_Test:test_request_nativeMultiToken() (gas: 605757)
Inbox_request_Test:test_request_reverts() (gas: 891165)
Inbox_request_Test:test_request_singleNative() (gas: 364015)
Inbox_request_Test:test_request_singleToken() (gas: 426691)
Inbox_request_Test:test_request_two() (gas: 671465)
InitializableHelper_Test:test_disableInitalizers() (gas: 181686)
InitializableHelper_Test:test_getInitialized() (gas: 178023)
OmniBridgeL1_Test:test_bridge() (gas: 233678)
Expand Down
114 changes: 114 additions & 0 deletions contracts/core/src/solve/Inbox.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity =0.8.24;

import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import { SafeERC20, IERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { Solve } from "./Solve.sol";

/**
* @title Inbox
* @notice Entrypoint and alt-mempoool for user solve requests.
*/
contract Inbox is ReentrancyGuardUpgradeable {
using SafeERC20 for IERC20;

error NoDeposits();
error InvalidCall();
error InvalidDeposit();
error ZeroDeposit();

/**
* @notice Emitted when a request is created.
* @param id ID of the request.
* @param from Address of the user who created the request.
* @param call Details of the call to be executed on another chain.
* @param deposits Array of deposits backing the request.
*/
event Requested(bytes32 indexed id, address indexed from, Solve.Call call, Solve.Deposit[] deposits);

/**
* @dev uint repr of last assigned request ID.
*/
uint256 internal _lastId;

/**
* @notice Map ID to request.
*/
mapping(bytes32 id => Solve.Request) internal _requests;

function initialize() public initializer {
__ReentrancyGuard_init();
}

/**
* @notice Returns the request with the given ID.
*/
function getRequest(bytes32 id) external view returns (Solve.Request memory) {
return _requests[id];
}

/**
* @notice Open a request to execute a call on another chain, backed by deposits.
* Token deposits are transferred from msg.sender to this inbox.
* @param call Details of the call to be executed on another chain.
* @param deposits Array of deposits backing the request.
*/
function request(Solve.Call calldata call, Solve.TokenDeposit[] calldata deposits)
external
payable
nonReentrant
returns (bytes32 id)
{
if (call.target == address(0)) revert InvalidCall();
if (call.destChainId == 0) revert InvalidCall();
if (call.data.length == 0) revert InvalidCall();
if (deposits.length == 0 && msg.value == 0) revert NoDeposits();

Solve.Request storage req = _openRequest(msg.sender, call, deposits);

emit Requested(req.id, req.from, req.call, req.deposits);

return req.id;
}

/**
* @dev Open a new request in storage at `id`.
* Transfer token deposits from msg.sender to this inbox.
* Duplicate token addresses are allowed.
*/
function _openRequest(address from, Solve.Call calldata call, Solve.TokenDeposit[] calldata deposits)
internal
returns (Solve.Request storage req)
{
bytes32 id = _nextId();

req = _requests[id];
req.id = id;
req.updatedAt = uint40(block.timestamp);
req.from = from;
req.call = call;

if (msg.value > 0) {
req.deposits.push(Solve.Deposit({ isNative: true, token: address(0), amount: msg.value }));
}

for (uint256 i = 0; i < deposits.length; i++) {
if (deposits[i].amount == 0) revert InvalidDeposit();
if (deposits[i].token == address(0)) revert InvalidDeposit();

req.deposits.push(Solve.Deposit({ isNative: false, token: deposits[i].token, amount: deposits[i].amount }));

// NOTE: all external methods must be nonReentrant
// This allows us to transfer while opening the request - saving some gas.
IERC20(deposits[i].token).safeTransferFrom(msg.sender, address(this), deposits[i].amount);
}
}

/**
* @dev Increment and return _lastId.
*/
function _nextId() internal returns (bytes32) {
_lastId++;
return bytes32(_lastId);
}
}
70 changes: 70 additions & 0 deletions contracts/core/src/solve/Solve.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity =0.8.24;

library Solve {
enum Status {
Open,
Accepted,
Cancelled,
Rejected,
Fulfilled,
Paid
}

/**
* @notice A request to execute a call on another chain, backed by a deposit.
* @param id ID for the request, globally unique per inbox.
* @param updatedAt Timestamp request status was last updated.
* @param from Address of the user who created the request.
* @param fulfilledBy Address of the solver that fulfilled the request.
* @param status Request status (open, accepted, cancelled, rejected, fulfilled, paid).
* @param call Details of the call to be executed on another chain.
* @param deposits Array of deposits backing the request.
*/
struct Request {
bytes32 id;
uint40 updatedAt;
Status status;
address from;
address fulfilledBy;
Call call;
Deposit[] deposits;
}

/**
* @notice Details of a call to be executed on another chain.
* @param destChainId ID of the destination chain.
* @param value Amount of native currency to send with the call.
* @param target Address of the target contract on the destination chain.
* @param data Encoded data to be sent with the call.
*/
struct Call {
uint64 destChainId;
address target;
uint256 value;
bytes data;
}

/**
* @notice Details of a deposit backing a request.
* @param isNative Whether the deposit is in native currency.
* @param token Address of the token, address(0) if native.
* @param amount Deposit amount.
*/
struct Deposit {
bool isNative;
address token;
uint256 amount;
}

/**
* @notice Details of a token deposit backing a request.
* @dev Not stored, only used in opening a request.
* @param token Address of the token.
* @param amount Deposit amount.
*/
struct TokenDeposit {
address token;
uint256 amount;
}
}
Loading
Loading