-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(contracts/core): start solve inbox
Start solve inbox prototype. Introduce single Inbox.request() method.
- Loading branch information
1 parent
8d6a6ee
commit 986412c
Showing
5 changed files
with
437 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
Oops, something went wrong.