Skip to content

Commit

Permalink
feat(contracts/core): start solve inbox
Browse files Browse the repository at this point in the history
Start solve inbox prototype.

Introduce single Inbox.request() method.
  • Loading branch information
kevinhalliday committed Nov 1, 2024
1 parent 8d6a6ee commit 986412c
Show file tree
Hide file tree
Showing 5 changed files with 437 additions and 8 deletions.
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: 547385)
Inbox_request_Test:test_request_nativeMultiToken() (gas: 605559)
Inbox_request_Test:test_request_reverts() (gas: 890565)
Inbox_request_Test:test_request_singleNative() (gas: 363817)
Inbox_request_Test:test_request_singleToken() (gas: 426493)
Inbox_request_Test:test_request_two() (gas: 671069)
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 = uint96(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);
}
}
77 changes: 77 additions & 0 deletions contracts/core/src/solve/Solve.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity =0.8.24;

library Solve {
enum Status {
/// @dev Request opened by a user
Open,
/// @dev Request accepted by a solver
Accepted,
/// @dev Request cancelled by the creator
Cancelled,
/// @dev Request rejected by solvers (any allowed solvers can reject in v1)
/// This is optional. Solver can also just not fulfill the request.
Rejected,
/// @dev Request fulfilled by a solver
Fulfilled,
/// @dev Request paid out to the solver
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 status Request status (open, accepted, cancelled, rejected, fulfilled, paid).
* @param fulfilledBy Address of the entity that fulfilled the request.
* @param call Details of the call to be executed on another chain.
* @param deposits Array of deposits backing the request.
*/
struct Request {
bytes32 id;
uint96 updatedAt;
address from;
Status status;
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

0 comments on commit 986412c

Please sign in to comment.