// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
//
error MofaSignatureInvalid();
error InsufficientNativeAmount();
error InvalidMultipleNativeTokens();
error FulfilmentChainInvalid();
error RequestAlreadyFulfilled();
error RouterNotRegistered();
error TransferFailed();
error CallerNotBungeeGateway();
error NoExecutionCacheFound();
error ExecutionCacheFailed();
error SwapOutputInsufficient();
error UnsupportedDestinationChainId();
error MinOutputNotMet();
error OnlyOwner();
error OnlyNominee();
error InvalidRequest();
error FulfilmentDeadlineNotMet();
error CallerNotDelegate();
error BungeeSiblingDoesNotExist();
error InvalidMsg();
error NotDelegate();
error RequestProcessed();
error RequestNotProcessed();
error InvalidSwitchboard();
error PromisedAmountNotMet();
error MsgReceiveFailed();
error RouterAlreadyWhitelisted();
error InvalidStake();
error RouterAlreadyRegistered();
error InvalidFulfil();
error InsufficientCapacity();
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import {IEIP712} from "./IEIP712.sol";
/// @title AllowanceTransfer
/// @notice Handles ERC20 token permissions through signature based allowance setting and ERC20 token transfers by checking allowed amounts
/// @dev Requires user's token approval on the Permit2 contract
interface IAllowanceTransfer is IEIP712 {
/// @notice Thrown when an allowance on a token has expired.
/// @param deadline The timestamp at which the allowed amount is no longer valid
error AllowanceExpired(uint256 deadline);
/// @notice Thrown when an allowance on a token has been depleted.
/// @param amount The maximum amount allowed
error InsufficientAllowance(uint256 amount);
/// @notice Thrown when too many nonces are invalidated.
error ExcessiveInvalidation();
/// @notice Emits an event when the owner successfully invalidates an ordered nonce.
event NonceInvalidation(
address indexed owner, address indexed token, address indexed spender, uint48 newNonce, uint48 oldNonce
);
/// @notice Emits an event when the owner successfully sets permissions on a token for the spender.
event Approval(
address indexed owner, address indexed token, address indexed spender, uint160 amount, uint48 expiration
);
/// @notice Emits an event when the owner successfully sets permissions using a permit signature on a token for the spender.
event Permit(
address indexed owner,
address indexed token,
address indexed spender,
uint160 amount,
uint48 expiration,
uint48 nonce
);
/// @notice Emits an event when the owner sets the allowance back to 0 with the lockdown function.
event Lockdown(address indexed owner, address token, address spender);
/// @notice The permit data for a token
struct PermitDetails {
// ERC20 token address
address token;
// the maximum amount allowed to spend
uint160 amount;
// timestamp at which a spender's token allowances become invalid
uint48 expiration;
// an incrementing value indexed per owner,token,and spender for each signature
uint48 nonce;
}
/// @notice The permit message signed for a single token allownce
struct PermitSingle {
// the permit data for a single token alownce
PermitDetails details;
// address permissioned on the allowed tokens
address spender;
// deadline on the permit signature
uint256 sigDeadline;
}
/// @notice The permit message signed for multiple token allowances
struct PermitBatch {
// the permit data for multiple token allowances
PermitDetails[] details;
// address permissioned on the allowed tokens
address spender;
// deadline on the permit signature
uint256 sigDeadline;
}
/// @notice The saved permissions
/// @dev This info is saved per owner, per token, per spender and all signed over in the permit message
/// @dev Setting amount to type(uint160).max sets an unlimited approval
struct PackedAllowance {
// amount allowed
uint160 amount;
// permission expiry
uint48 expiration;
// an incrementing value indexed per owner,token,and spender for each signature
uint48 nonce;
}
/// @notice A token spender pair.
struct TokenSpenderPair {
// the token the spender is approved
address token;
// the spender address
address spender;
}
/// @notice Details for a token transfer.
struct AllowanceTransferDetails {
// the owner of the token
address from;
// the recipient of the token
address to;
// the amount of the token
uint160 amount;
// the token to be transferred
address token;
}
/// @notice A mapping from owner address to token address to spender address to PackedAllowance struct, which contains details and conditions of the approval.
/// @notice The mapping is indexed in the above order see: allowance[ownerAddress][tokenAddress][spenderAddress]
/// @dev The packed slot holds the allowed amount, expiration at which the allowed amount is no longer valid, and current nonce thats updated on any signature based approvals.
function allowance(address user, address token, address spender)
external
view
returns (uint160 amount, uint48 expiration, uint48 nonce);
/// @notice Approves the spender to use up to amount of the specified token up until the expiration
/// @param token The token to approve
/// @param spender The spender address to approve
/// @param amount The approved amount of the token
/// @param expiration The timestamp at which the approval is no longer valid
/// @dev The packed allowance also holds a nonce, which will stay unchanged in approve
/// @dev Setting amount to type(uint160).max sets an unlimited approval
function approve(address token, address spender, uint160 amount, uint48 expiration) external;
/// @notice Permit a spender to a given amount of the owners token via the owner's EIP-712 signature
/// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce
/// @param owner The owner of the tokens being approved
/// @param permitSingle Data signed over by the owner specifying the terms of approval
/// @param signature The owner's signature over the permit data
function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external;
/// @notice Permit a spender to the signed amounts of the owners tokens via the owner's EIP-712 signature
/// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce
/// @param owner The owner of the tokens being approved
/// @param permitBatch Data signed over by the owner specifying the terms of approval
/// @param signature The owner's signature over the permit data
function permit(address owner, PermitBatch memory permitBatch, bytes calldata signature) external;
/// @notice Transfer approved tokens from one address to another
/// @param from The address to transfer from
/// @param to The address of the recipient
/// @param amount The amount of the token to transfer
/// @param token The token address to transfer
/// @dev Requires the from address to have approved at least the desired amount
/// of tokens to msg.sender.
function transferFrom(address from, address to, uint160 amount, address token) external;
/// @notice Transfer approved tokens in a batch
/// @param transferDetails Array of owners, recipients, amounts, and tokens for the transfers
/// @dev Requires the from addresses to have approved at least the desired amount
/// of tokens to msg.sender.
function transferFrom(AllowanceTransferDetails[] calldata transferDetails) external;
/// @notice Enables performing a "lockdown" of the sender's Permit2 identity
/// by batch revoking approvals
/// @param approvals Array of approvals to revoke.
function lockdown(TokenSpenderPair[] calldata approvals) external;
/// @notice Invalidate nonces for a given (token, spender) pair
/// @param token The token to invalidate nonces for
/// @param spender The spender to invalidate nonces for
/// @param newNonce The new nonce to set. Invalidates all nonces less than it.
/// @dev Can't invalidate more than 2**16 nonces per transaction.
function invalidateNonces(address token, address spender, uint48 newNonce) external;
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;
interface IBungeeGateway {
function setWhitelistedReceiver(address receiver, uint256 destinationChainId, address router) external;
function getWhitelistedReceiver(address router, uint256 destinationChainId) external view returns (address);
function inboundMsgFromSwitchboard(uint8 msgId, uint32 switchboardId, bytes calldata payload) external;
function isBungeeRouter(address router) external view returns (bool);
function withdrawnRequests(bytes32 requestHash) external view returns (bool);
function withdrawRequestOnOrigin(bytes32 requestHash) external;
function executeSOR(bytes calldata data) external payable returns (bytes memory);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
interface IEIP712 {
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ISignatureTransfer} from "./ISignatureTransfer.sol";
import {IAllowanceTransfer} from "./IAllowanceTransfer.sol";
/// @notice Permit2 handles signature-based transfers in SignatureTransfer and allowance-based transfers in AllowanceTransfer.
/// @dev Users must approve Permit2 before calling any of the transfer functions.
interface IPermit2 is ISignatureTransfer, IAllowanceTransfer {
// IPermit2 unifies the two interfaces so users have maximal flexibility with their approval.
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import {IEIP712} from "./IEIP712.sol";
/// @title SignatureTransfer
/// @notice Handles ERC20 token transfers through signature based actions
/// @dev Requires user's token approval on the Permit2 contract
interface ISignatureTransfer is IEIP712 {
/// @notice Thrown when the requested amount for a transfer is larger than the permissioned amount
/// @param maxAmount The maximum amount a spender can request to transfer
error InvalidAmount(uint256 maxAmount);
/// @notice Thrown when the number of tokens permissioned to a spender does not match the number of tokens being transferred
/// @dev If the spender does not need to transfer the number of tokens permitted, the spender can request amount 0 to be transferred
error LengthMismatch();
/// @notice Emits an event when the owner successfully invalidates an unordered nonce.
event UnorderedNonceInvalidation(address indexed owner, uint256 word, uint256 mask);
/// @notice The token and amount details for a transfer signed in the permit transfer signature
struct TokenPermissions {
// ERC20 token address
address token;
// the maximum amount that can be spent
uint256 amount;
}
/// @notice The signed permit message for a single token transfer
struct PermitTransferFrom {
TokenPermissions permitted;
// a unique value for every token owner's signature to prevent signature replays
uint256 nonce;
// deadline on the permit signature
uint256 deadline;
}
/// @notice Specifies the recipient address and amount for batched transfers.
/// @dev Recipients and amounts correspond to the index of the signed token permissions array.
/// @dev Reverts if the requested amount is greater than the permitted signed amount.
struct SignatureTransferDetails {
// recipient address
address to;
// spender requested amount
uint256 requestedAmount;
}
/// @notice Used to reconstruct the signed permit message for multiple token transfers
/// @dev Do not need to pass in spender address as it is required that it is msg.sender
/// @dev Note that a user still signs over a spender address
struct PermitBatchTransferFrom {
// the tokens and corresponding amounts permitted for a transfer
TokenPermissions[] permitted;
// a unique value for every token owner's signature to prevent signature replays
uint256 nonce;
// deadline on the permit signature
uint256 deadline;
}
/// @notice A map from token owner address and a caller specified word index to a bitmap. Used to set bits in the bitmap to prevent against signature replay protection
/// @dev Uses unordered nonces so that permit messages do not need to be spent in a certain order
/// @dev The mapping is indexed first by the token owner, then by an index specified in the nonce
/// @dev It returns a uint256 bitmap
/// @dev The index, or wordPosition is capped at type(uint248).max
function nonceBitmap(address, uint256) external view returns (uint256);
/// @notice Transfers a token using a signed permit message
/// @dev Reverts if the requested amount is greater than the permitted signed amount
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails The spender's requested transfer details for the permitted token
/// @param signature The signature to verify
function permitTransferFrom(
PermitTransferFrom memory permit,
SignatureTransferDetails calldata transferDetails,
address owner,
bytes calldata signature
) external;
/// @notice Transfers a token using a signed permit message
/// @notice Includes extra data provided by the caller to verify signature over
/// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition
/// @dev Reverts if the requested amount is greater than the permitted signed amount
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails The spender's requested transfer details for the permitted token
/// @param witness Extra data to include when checking the user signature
/// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash
/// @param signature The signature to verify
function permitWitnessTransferFrom(
PermitTransferFrom memory permit,
SignatureTransferDetails calldata transferDetails,
address owner,
bytes32 witness,
string calldata witnessTypeString,
bytes calldata signature
) external;
/// @notice Transfers multiple tokens using a signed permit message
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails Specifies the recipient and requested amount for the token transfer
/// @param signature The signature to verify
function permitTransferFrom(
PermitBatchTransferFrom memory permit,
SignatureTransferDetails[] calldata transferDetails,
address owner,
bytes calldata signature
) external;
/// @notice Transfers multiple tokens using a signed permit message
/// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition
/// @notice Includes extra data provided by the caller to verify signature over
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails Specifies the recipient and requested amount for the token transfer
/// @param witness Extra data to include when checking the user signature
/// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash
/// @param signature The signature to verify
function permitWitnessTransferFrom(
PermitBatchTransferFrom memory permit,
SignatureTransferDetails[] calldata transferDetails,
address owner,
bytes32 witness,
string calldata witnessTypeString,
bytes calldata signature
) external;
/// @notice Invalidates the bits specified in mask for the bitmap at the word position
/// @dev The wordPos is maxed at type(uint248).max
/// @param wordPos A number to index the nonceBitmap at
/// @param mask A bitmap masked against msg.sender's current bitmap at the word position
function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) external;
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;
interface ISingleOutputRequestImpl {
function withdrawRequestOnOrigin(bytes32 requestHash) external;
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;
/// @dev reverts with error when chainId does not match block.chainid
error InvalidChainId();
/// @dev reverts with error if the msg.value and Request inputAmount do not match
error InvalidMsgValue();
/// @dev reverts with error if the nonce has already been used
error InvalidNonce();
///@dev reverts with error if native / ERC-20 transfer fails
error TransferFailed();
/// @dev reverts with error if Request does not exist on the Inbox contract
error RequestDoesNotExist();
/// @dev reverts with error if Request has already been withdrawn by the user post-extraction
error RequestAlreadyWithdraw();
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract InboxEvents {
/// @notice Emitted when the user creates a Request on the Refuel Inbox
/// @param requestHash Hash of the request
/// @param ogSender Address of the user who created the request
/// @param request Encoded Request details
event RefuelRequestCreated(bytes32 indexed requestHash, address ogSender, bytes request);
/// @notice Emitted when the user withdraws a Request pre-extraction
/// @param requestHash Hash of the request
event FundsUnlocked(bytes32 indexed requestHash);
/// @notice Emitted when the user withdraws a Request post-extraction
/// @param requestHash Hash of the request
event RefuelRequestWithdrawn(bytes32 indexed requestHash);
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.17;
import {OnlyOwner, OnlyNominee} from "../common/BungeeErrors.sol";
abstract contract Ownable {
address private _owner;
address private _nominee;
event OwnerNominated(address indexed nominee);
event OwnerClaimed(address indexed claimer);
constructor(address owner_) {
_claimOwner(owner_);
}
modifier onlyOwner() {
if (msg.sender != _owner) {
revert OnlyOwner();
}
_;
}
function owner() public view returns (address) {
return _owner;
}
function nominee() public view returns (address) {
return _nominee;
}
function nominateOwner(address nominee_) external {
if (msg.sender != _owner) {
revert OnlyOwner();
}
_nominee = nominee_;
emit OwnerNominated(_nominee);
}
function claimOwner() external {
if (msg.sender != _nominee) {
revert OnlyNominee();
}
_claimOwner(msg.sender);
}
function _claimOwner(address claimer_) internal {
_owner = claimer_;
_nominee = address(0);
emit OwnerClaimed(claimer_);
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;
import {IPermit2} from "permit2/src/interfaces/IPermit2.sol";
import {ISignatureTransfer} from "permit2/src/interfaces/ISignatureTransfer.sol";
/// @title Permit2HashHelper
/// @notice Helper library to generate the WitnessHash used by Permit2
/// @dev We receive hashTypedData from Permit2 in isValidSignature function, used to verify the Commande details.
/// @dev This lib helps generate the witness hash which is used to create the hashTypedData
/// @dev reference : https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol#L85
/// @dev reference : https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/EIP712.sol#L38
library Permit2HashHelper {
string public constant _PERMIT_TRANSFER_FROM_WITNESS_TYPEHASH_STUB =
"PermitWitnessTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline,";
bytes32 public constant _TOKEN_PERMISSIONS_TYPEHASH = keccak256("TokenPermissions(address token,uint256 amount)");
function returnHashWithWitness(
address token,
uint256 amount,
uint256 nonce,
uint256 deadline,
bytes32 commandHash,
string memory witnessTypeString,
address bungeeGateway
) internal pure returns (bytes32) {
ISignatureTransfer.TokenPermissions memory tokenPermissions = ISignatureTransfer.TokenPermissions(
token,
amount
);
ISignatureTransfer.PermitTransferFrom memory permit = ISignatureTransfer.PermitTransferFrom(
tokenPermissions,
nonce,
deadline
);
return hashWithWitness(permit, commandHash, witnessTypeString, bungeeGateway);
}
function hashWithWitness(
ISignatureTransfer.PermitTransferFrom memory permit,
bytes32 witness,
string memory witnessTypeString,
address bungeeGateway
) internal pure returns (bytes32) {
bytes32 typeHash = keccak256(abi.encodePacked(_PERMIT_TRANSFER_FROM_WITNESS_TYPEHASH_STUB, witnessTypeString));
bytes32 tokenPermissionsHash = _hashTokenPermissions(permit.permitted);
return
keccak256(
abi.encode(typeHash, tokenPermissionsHash, bungeeGateway, permit.nonce, permit.deadline, witness)
);
}
function _hashTokenPermissions(
ISignatureTransfer.TokenPermissions memory permitted
) private pure returns (bytes32) {
return keccak256(abi.encode(_TOKEN_PERMISSIONS_TYPEHASH, permitted));
}
/// @notice Creates an EIP-712 typed data hash
function _hashTypedData(address permit2, bytes32 dataHash) internal view returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", IPermit2(permit2).DOMAIN_SEPARATOR(), dataHash));
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;
import {ISignatureTransfer} from "permit2/src/interfaces/ISignatureTransfer.sol";
// Library to get Permit 2 related data.
library Permit2Lib {
string public constant TOKEN_PERMISSIONS_TYPE = "TokenPermissions(address token,uint256 amount)";
function toPermit(
address inputToken,
uint256 inputAmount,
uint256 nonce,
uint256 deadline
) internal pure returns (ISignatureTransfer.PermitTransferFrom memory) {
return
ISignatureTransfer.PermitTransferFrom({
permitted: ISignatureTransfer.TokenPermissions({token: inputToken, amount: inputAmount}),
nonce: nonce,
deadline: deadline
});
}
function transferDetails(
uint256 amount,
address spender
) internal pure returns (ISignatureTransfer.SignatureTransferDetails memory) {
return ISignatureTransfer.SignatureTransferDetails({to: spender, requestedAmount: amount});
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;
import {WETH} from "solmate/src/tokens/WETH.sol";
import {ERC20, SafeTransferLib} from "solmate/src/utils/SafeTransferLib.sol";
import {Permit2HashHelper} from "../lib/Permit2HashHelper.sol";
import {RequestLib as SingleOutputRequestLib} from "../lib/SingleOutputRequestLib.sol";
import {InboxEvents} from "../common/InboxEvents.sol";
import {BasicRequest, Request as SingleOutputRequest} from "../common/SingleOutputStructs.sol";
import {Ownable} from "../utils/Ownable.sol";
import {IBungeeGateway} from "../interfaces/IBungeeGateway.sol";
import {ISingleOutputRequestImpl} from "../interfaces/ISingleOutputRequestImpl.sol";
import {IPermit2} from "permit2/src/interfaces/IPermit2.sol";
import {
InvalidChainId,
InvalidMsgValue,
InvalidNonce,
TransferFailed,
RequestDoesNotExist,
RequestAlreadyWithdraw
} from "../common/InboxErrors.sol";
/// @title Refuel Inbox
/// @notice Enables users to send Refuel (native token) bridging requests to Bungee Protocol auction
/// @dev To send Requests to Bungee Protocol, users are required to sign on a Request (EIP-712)
/// @dev Then submit Request + signed message to Bungee Protocol's off-chain auction service
/// @dev To enable creating orders on-chain, users can use Inboxes where request details are stored
/// @dev Request creation event is emitted which is picked up by BP's auction
/// @dev Bungee Protocol uses PERMIT-2, which uses EIP-1271 to validate stored requests on the Inbox
/// @author tech@socket.tech
contract RefuelInbox is Ownable, InboxEvents {
/*//////////////////////////////////////////////////////////////////////////
LIBS
//////////////////////////////////////////////////////////////////////////*/
using SingleOutputRequestLib for SingleOutputRequest;
using SafeTransferLib for ERC20;
/*//////////////////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////////////////*/
/// @notice Permit2 contract address
IPermit2 public immutable PERMIT2;
/// @notice Wrapped native token address
WETH public immutable WRAPPED_NATIVE_TOKEN;
/// @notice bungeeGateway contract address
IBungeeGateway public immutable BUNGEE_GATEWAY;
/// @notice Address used to denote native tokens on Bungee Protocol
address public constant NATIVE_TOKEN_ADDRESS = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
/// @notice MAGIC_VALUE returned for valid signatures according to EIP-1271
/// @dev https://eips.ethereum.org/EIPS/eip-1271
bytes4 internal constant MAGIC_VALUE = 0x1626ba7e;
/// @notice MAGIC_VALUE returned for valid signatures according to EIP-1271
/// @dev https://eips.ethereum.org/EIPS/eip-1271
bytes4 internal constant NON_MAGIC_VALUE = 0xffffffff;
/*//////////////////////////////////////////////////////////////////////////
PUBLIC STORAGE
//////////////////////////////////////////////////////////////////////////*/
/// @notice stores requests created by users
mapping(uint256 nonce => ReceivedRequest receivedRequest) public requestInbox;
/// @notice stores requests withdrawn by users post-extraction
mapping(bytes32 typedDataHash => bool withdrawn) public withdrawnInbox;
/*//////////////////////////////////////////////////////////////////////////
STRUCTS
//////////////////////////////////////////////////////////////////////////*/
/// @notice Struct used to store received requests against request nonce
/// @param ogSender sender of the request
/// @param typedDataHash typedDataHash of the Request's hash
struct ReceivedRequest {
address ogSender;
bytes32 typedDataHash;
}
/*//////////////////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////////////////*/
/// @dev constructor sets immutable variables and gives infinite WETH/Wrapped Native Token approval to Permit2
/// @param _owner owner address who'll be able to call onlyOwner methods
/// @param _permit2 permit2 contract address
/// @param _bungeeGateway singleOutputRequest BungeeGateway
/// @param _wrappedNativeToken wrapped native token address
constructor(
address _owner,
address _permit2,
address _bungeeGateway,
address payable _wrappedNativeToken
) Ownable(_owner) {
PERMIT2 = IPermit2(_permit2);
BUNGEE_GATEWAY = IBungeeGateway(_bungeeGateway);
WRAPPED_NATIVE_TOKEN = WETH(_wrappedNativeToken);
// Gives max Wrapped-NativeToken approval to Permit2 contract
WRAPPED_NATIVE_TOKEN.approve(address(PERMIT2), type(uint256).max);
}
/*//////////////////////////////////////////////////////////////////////////
RECEIVE FUNCTION
//////////////////////////////////////////////////////////////////////////*/
/// @notice receive() function
receive() external payable {}
/*//////////////////////////////////////////////////////////////////////////
EXTERNAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @notice called by the user to create a Refuel deposit request
/// @dev Creates Refuel Request, Wraps native token, stores typedDataHash, emits Request creation event
/// @param singleOutputRequest singleOutputRequest struct
function createRefuelRequest(SingleOutputRequest calldata singleOutputRequest) external payable {
// Validates Request and reverts if there are invalid details in the Request
_checkRequestValidity(singleOutputRequest.basicReq);
// Wraps native asset to WETH
WRAPPED_NATIVE_TOKEN.deposit{value: msg.value}();
// Creates RequestHash and TypedDataHash for the Request
(bytes32 requestHash, bytes32 typedDataHash) = _createTypedDataHash(singleOutputRequest);
// Store the hash against the nonce
requestInbox[singleOutputRequest.basicReq.nonce] = ReceivedRequest({
ogSender: msg.sender,
typedDataHash: typedDataHash
});
// Emit event
emit RefuelRequestCreated(requestHash, msg.sender, abi.encode(singleOutputRequest));
}
/// @notice called by the user to unlock/withdraw funds if Request is not fulfilled
/// @param singleOutputRequest singleOutputRequest struct
function withdrawFunds(SingleOutputRequest calldata singleOutputRequest) external {
// creates requestHash and typedDataHash for the Request
(bytes32 requestHash, bytes32 typedDataHash) = _createTypedDataHash(singleOutputRequest);
// checks if the request was created on Inbox
if (requestInbox[singleOutputRequest.basicReq.nonce].typedDataHash != typedDataHash)
revert RequestDoesNotExist();
// if nonce is valid, request hasn't been extracted and funds are unlocked to the user from Inbox
// if invalid, request has been extracted and funds are withdrawn from BungeeGateway and sent to user
if (_isNonceValid(singleOutputRequest.basicReq.nonce)) {
// CASE - PRE EXTRACTION
_withdraw(singleOutputRequest.basicReq.nonce, singleOutputRequest.basicReq.inputAmount, requestHash);
// deletes request from storage
delete requestInbox[singleOutputRequest.basicReq.nonce];
} else {
// CASE - POST EXTRACTION
// checks if the request has already been withdrawn
if (withdrawnInbox[typedDataHash]) revert RequestAlreadyWithdraw();
// If Request has not been withdrawn, it withdraws it
if (!BUNGEE_GATEWAY.withdrawnRequests(requestHash)) {
bytes memory payload = abi.encodeWithSelector(
ISingleOutputRequestImpl.withdrawRequestOnOrigin.selector,
requestHash
);
BUNGEE_GATEWAY.executeSOR(payload);
}
_withdraw(singleOutputRequest.basicReq.nonce, singleOutputRequest.basicReq.inputAmount, requestHash);
// Stores Withdrawn Requests
withdrawnInbox[typedDataHash] = true;
}
}
/*//////////////////////////////////////////////////////////////////////////
Signature Verification
//////////////////////////////////////////////////////////////////////////*/
/// @notice Called by PERMIT2 to verify the validity of witness data signed against the nonce for transfer
/// @param _hash PERMIT2 typedData hash
/// @param _signature Encoded Request Details passed from PERMIT2
function isValidSignature(bytes32 _hash, bytes calldata _signature) external view returns (bytes4 magicValue) {
// Decodes nonce from signature
uint256 nonce = abi.decode(_signature, (uint256));
// Checks if hashTypedData (_hash) from PERMIT2 does not match the order stored against nonce
if (requestInbox[nonce].typedDataHash == _hash) {
return MAGIC_VALUE;
} else {
return NON_MAGIC_VALUE;
}
}
/*//////////////////////////////////////////////////////////////////////////
ADMIN FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @notice admin / onlyOwner function to rescue funds in case mistakenly sent to the contract
/// @param token address of token rescused
/// @param to address to send rescused funds to
/// @param amount amount to be rescued
function rescue(address token, address to, uint256 amount) external onlyOwner {
_sendFundsFromContract(token, to, amount);
}
/*//////////////////////////////////////////////////////////////////////////
INTERNAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @dev validates request details and reverts if any of the conditions are not met
/// @param basicRequest BasicRequest struct details
function _checkRequestValidity(BasicRequest calldata basicRequest) internal view {
// reverts if inputAmount does not match msg.value
if (msg.value != basicRequest.inputAmount) revert InvalidMsgValue();
// reverts if originChainId is not the current chainId
if (basicRequest.originChainId != block.chainid) revert InvalidChainId();
// reverts if the nonce has already been used
if (requestInbox[basicRequest.nonce].typedDataHash != bytes32(0)) revert InvalidNonce();
}
/// @dev creates typedDataHash which is the hash the user signs on for permit2
/// @param singleOutputRequest singleOutputRequest struct
function _createTypedDataHash(
SingleOutputRequest calldata singleOutputRequest
) internal view returns (bytes32 requestHash, bytes32 typedDataHash) {
// Creates Request Hash
requestHash = singleOutputRequest.hashOriginRequest();
// Hash with witness
bytes32 hashWithWitness = Permit2HashHelper.returnHashWithWitness(
singleOutputRequest.basicReq.inputToken,
singleOutputRequest.basicReq.inputAmount,
singleOutputRequest.basicReq.nonce,
singleOutputRequest.basicReq.deadline,
requestHash,
SingleOutputRequestLib.PERMIT2_ORDER_TYPE,
singleOutputRequest.basicReq.bungeeGateway
);
// Typed Data Hash
typedDataHash = Permit2HashHelper._hashTypedData(address(PERMIT2), hashWithWitness);
}
/// @dev transfers native token / ERC-20 token from the contract
/// @param token address of token being sent
/// @param to address the token is sent to
/// @param amount amount being sent
function _sendFundsFromContract(address token, address to, uint256 amount) internal {
/// native token case
if (token == NATIVE_TOKEN_ADDRESS) {
(bool success, ) = to.call{value: amount, gas: 5000}("");
if (!success) revert TransferFailed();
return;
}
/// ERC20 case
ERC20(token).safeTransfer(to, amount);
}
/// @dev withdraws / unlocks funds back to the user if a Request is not fulfilled
/// @dev this method is called in case the request is not yet picked up by Bungee Protocol
/// @param nonce nonce of the Request
/// @param amount user's inputAmount that was locked and to be withdrawn
/// @param requestHash hash of the Request
function _withdraw(uint256 nonce, uint256 amount, bytes32 requestHash) internal {
// unwrap native token
WRAPPED_NATIVE_TOKEN.withdraw(amount);
// transfers funds to user
_sendFundsFromContract(NATIVE_TOKEN_ADDRESS, requestInbox[nonce].ogSender, amount);
// emits event
emit RefuelRequestWithdrawn(requestHash);
}
/// @notice Checks if the unordered nonce has been used on Permit2
/// @param nonce nonce value on Permit2 whose validity is to be checked
function _isNonceValid(uint256 nonce) internal view returns (bool) {
(uint256 wordPos, uint256 bitPos) = _bitmapPositions(nonce);
// fetches the nonceBitmap value from PERMIT2
uint256 bitmap = PERMIT2.nonceBitmap(address(this), wordPos);
uint256 bit = 1 << bitPos;
uint256 flipped = bitmap ^ bit;
// checks if the nonce has been used
if (flipped & bit == 0) return false;
return true;
}
/// @notice Bit shifts nonce and returns wordPos and bitPos
/// @param nonce nonce value
function _bitmapPositions(uint256 nonce) private pure returns (uint256 wordPos, uint256 bitPos) {
wordPos = uint248(nonce >> 8);
bitPos = uint8(nonce);
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferETH(address to, uint256 amount) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and store if it succeeded or not.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.17;
import {BasicRequest, Request, ExtractExec} from "../common/SingleOutputStructs.sol";
import {Permit2Lib} from "./Permit2Lib.sol";
/// @notice helpers for handling BasicRequest
library BasicRequestLib {
bytes internal constant BASIC_REQUEST_TYPE =
abi.encodePacked(
"BasicRequest(",
"uint256 originChainId,",
"uint256 destinationChainId,",
"uint256 deadline,",
"uint256 nonce,",
"address sender,",
"address receiver,",
"address delegate,",
"address bungeeGateway,",
"uint32 switchboardId,",
"address inputToken,",
"uint256 inputAmount,",
"address outputToken,",
"uint256 minOutputAmount,"
"uint256 refuelAmount)"
);
bytes32 internal constant BASIC_REQUEST_TYPE_HASH = keccak256(BASIC_REQUEST_TYPE);
/// @notice Hash of BasicRequest struct on the origin chain
/// @dev enforces originChainId to be the current chainId. Resulting hash would be the same on all chains.
/// @dev helps avoid extra checking of chainId in the contract
/// @param basicReq BasicRequest object to be hashed
function originHash(BasicRequest memory basicReq) internal view returns (bytes32) {
return
keccak256(
abi.encodePacked(
BASIC_REQUEST_TYPE_HASH,
abi.encode(
block.chainid,
basicReq.destinationChainId,
basicReq.deadline,
basicReq.nonce,
basicReq.sender,
basicReq.receiver,
basicReq.delegate,
basicReq.bungeeGateway,
basicReq.switchboardId,
basicReq.inputToken,
basicReq.inputAmount,
basicReq.outputToken,
basicReq.minOutputAmount,
basicReq.refuelAmount
)
)
);
}
/// @notice Hash of BasicRequest struct on the destination chain
/// @dev enforces destinationChain to be the current chainId. Resulting hash would be the same on all chains.
/// @dev helps avoid extra checking of chainId in the contract
/// @param basicReq BasicRequest object to be hashed
function destinationHash(BasicRequest memory basicReq) internal view returns (bytes32) {
return
keccak256(
abi.encodePacked(
BASIC_REQUEST_TYPE_HASH,
abi.encode(
basicReq.originChainId,
block.chainid,
basicReq.deadline,
basicReq.nonce,
basicReq.sender,
basicReq.receiver,
basicReq.delegate,
basicReq.bungeeGateway,
basicReq.switchboardId,
basicReq.inputToken,
basicReq.inputAmount,
basicReq.outputToken,
basicReq.minOutputAmount,
basicReq.refuelAmount
)
)
);
}
}
/// @title Bungee Request Library.
/// @author bungee protocol
/// @notice This library is responsible for all the hashing related to Request object.
library RequestLib {
using BasicRequestLib for BasicRequest;
// Permit 2 Witness Order Type.
string internal constant PERMIT2_ORDER_TYPE =
string(
abi.encodePacked(
"Request witness)",
abi.encodePacked(BasicRequestLib.BASIC_REQUEST_TYPE, REQUEST_TYPE),
Permit2Lib.TOKEN_PERMISSIONS_TYPE
)
);
// REQUEST TYPE encode packed
bytes internal constant REQUEST_TYPE =
abi.encodePacked(
"Request(",
"BasicRequest basicReq,",
"address swapOutputToken,",
"uint256 minSwapOutput,",
"bytes32 metadata,",
"bytes affiliateFees)"
);
// EXTRACT EXEC TYPE.
bytes internal constant EXTRACT_EXEC_TYPE =
abi.encodePacked(
"ExtractExec(",
"Request request,",
"address router,",
"uint256 promisedAmount,",
"uint256 promisedRefuelAmount,",
"bytes routerData,",
"bytes swapPayload,",
"address swapRouter,",
"bytes userSignature,",
"address beneficiary)"
);
// BUNGEE_REQUEST_TYPE
bytes internal constant BUNGEE_REQUEST_TYPE = abi.encodePacked(REQUEST_TYPE, BasicRequestLib.BASIC_REQUEST_TYPE);
// Keccak Hash of BUNGEE_REQUEST_TYPE
bytes32 internal constant BUNGEE_REQUEST_TYPE_HASH = keccak256(BUNGEE_REQUEST_TYPE);
// Exec Type.
bytes internal constant EXEC_TYPE = abi.encodePacked(EXTRACT_EXEC_TYPE, REQUEST_TYPE);
// Keccak Hash of Exec Type.
bytes32 internal constant EXTRACT_EXEC_TYPE_HASH = keccak256(EXEC_TYPE);
/// @notice Hash of request on the origin chain
/// @param request request that is signe by the user
function hashOriginRequest(Request memory request) internal view returns (bytes32) {
return
keccak256(
abi.encode(
BUNGEE_REQUEST_TYPE_HASH,
request.basicReq.originHash(),
request.swapOutputToken,
request.minSwapOutput,
request.metadata,
keccak256(request.affiliateFees)
)
);
}
/// @notice Hash of request on the destination chain
/// @param request request signed by the user
function hashDestinationRequest(Request memory request) internal view returns (bytes32) {
return
keccak256(
abi.encode(
BUNGEE_REQUEST_TYPE_HASH,
request.basicReq.destinationHash(),
request.swapOutputToken,
request.minSwapOutput,
request.metadata,
keccak256(request.affiliateFees)
)
);
}
/// @notice Hash of Extract Exec on the origin chain
/// @param execution Transmitter submitted extract exec object
function hashOriginExtractExec(ExtractExec memory execution) internal view returns (bytes32) {
return
keccak256(
abi.encode(
EXTRACT_EXEC_TYPE_HASH,
hashOriginRequest(execution.request),
execution.router,
execution.promisedAmount,
execution.promisedRefuelAmount,
keccak256(execution.routerData),
keccak256(execution.swapPayload),
execution.swapRouter,
keccak256(execution.userSignature),
execution.beneficiary
)
);
}
/// @notice hash a batch of extract execs
/// @param extractExecs batch of extract execs to be hashed
function hashOriginBatch(ExtractExec[] memory extractExecs) internal view returns (bytes32) {
unchecked {
bytes32 outputHash = keccak256("BUNGEE_EXTRACT_EXEC");
// Hash all of the extract execs present in the batch.
for (uint256 i = 0; i < extractExecs.length; i++) {
outputHash = keccak256(abi.encode(outputHash, hashOriginExtractExec(extractExecs[i])));
}
return outputHash;
}
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;
// Basic details in the request
struct BasicRequest {
// src chain id
uint256 originChainId;
// dest chain id
uint256 destinationChainId;
// deadline of the request
uint256 deadline;
// nonce used for uniqueness in signature
uint256 nonce;
// address of the user placing the request.
address sender;
// address of the receiver on destination chain
address receiver;
// delegate address that has some rights over the request signed
address delegate;
// address of bungee gateway, this address will have access to pull funds from the sender.
address bungeeGateway;
// id of the switchboard
uint32 switchboardId;
// address of the input token
address inputToken;
// amount of the input tokens
uint256 inputAmount;
// output token to be received on the destination.
address outputToken;
// minimum amount to be receive on the destination for the output token.
uint256 minOutputAmount;
// native token refuel amount on the destination chain
uint256 refuelAmount;
}
// The Request which user signs
struct Request {
// basic details in the request.
BasicRequest basicReq;
// swap output token that the user is okay with swapping input token to.
address swapOutputToken;
// minimum swap output the user is okay with swapping the input token to.
// Transmitter can choose or not choose to swap tokens.
uint256 minSwapOutput;
// any sort of metadata to be passed with the request
bytes32 metadata;
// fees of the affiliate if any
bytes affiliateFees;
}
// Transmitter's origin chain execution details for a request with promisedAmounts.
struct ExtractExec {
// User signed Request
Request request;
// address of the router being used for the request.
address router;
// promised amount for output token on the destination
uint256 promisedAmount;
// promised amount for native token refuel on the destination
uint256 promisedRefuelAmount;
// RouterPayload (router specific data) + RouterValue (value required by the router) etc etc
bytes routerData;
// swapPayload 0x00 if no swap is involved.
bytes swapPayload;
// swapRouterAddress
address swapRouter;
// user signature against the request
bytes userSignature;
// address of the beneficiary submitted by the transmitter.
// the beneficiary will be the one receiving locked tokens when a request is settled.
address beneficiary;
}
// Transmitter's destination chain execution details with fulfil amounts.
struct FulfilExec {
// User Signed Request
Request request;
// address of the router
address fulfilRouter;
// amount to be sent to the receiver for output token.
uint256 fulfilAmount;
// amount to be sent to the receiver for native token refuel.
uint256 refuelFulfilAmount;
// extraPayload for router.
bytes routerData;
// total msg.value to be sent to fulfil native token output token
uint256 msgValue;
}
struct ExtractedRequest {
uint256 expiry;
address router;
address sender;
address delegate;
uint32 switchboardId;
address token;
address transmitter; // For stake capacity
address beneficiary; // For Transmitter
uint256 amount;
uint256 promisedAmount; // For Transmitter
uint256 promisedRefuelAmount;
bytes affiliateFees; // For integrator
}
struct FulfilledRequest {
uint256 fulfilledAmount;
uint256 fulfilledRefuelAmount;
bool processed;
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import {ERC20} from "./ERC20.sol";
import {SafeTransferLib} from "../utils/SafeTransferLib.sol";
/// @notice Minimalist and modern Wrapped Ether implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/WETH.sol)
/// @author Inspired by WETH9 (https://github.com/dapphub/ds-weth/blob/master/src/weth9.sol)
contract WETH is ERC20("Wrapped Ether", "WETH", 18) {
using SafeTransferLib for address;
event Deposit(address indexed from, uint256 amount);
event Withdrawal(address indexed to, uint256 amount);
function deposit() public payable virtual {
_mint(msg.sender, msg.value);
emit Deposit(msg.sender, msg.value);
}
function withdraw(uint256 amount) public virtual {
_burn(msg.sender, amount);
emit Withdrawal(msg.sender, amount);
msg.sender.safeTransferETH(amount);
}
receive() external payable virtual {
deposit();
}
}
{
"compilationTarget": {
"src/inboxes/RefuelInbox.sol": "RefuelInbox"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_permit2","type":"address"},{"internalType":"address","name":"_bungeeGateway","type":"address"},{"internalType":"address payable","name":"_wrappedNativeToken","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidChainId","type":"error"},{"inputs":[],"name":"InvalidMsgValue","type":"error"},{"inputs":[],"name":"InvalidNonce","type":"error"},{"inputs":[],"name":"OnlyNominee","type":"error"},{"inputs":[],"name":"OnlyOwner","type":"error"},{"inputs":[],"name":"RequestAlreadyWithdraw","type":"error"},{"inputs":[],"name":"RequestDoesNotExist","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"requestHash","type":"bytes32"}],"name":"FundsUnlocked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"claimer","type":"address"}],"name":"OwnerClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nominee","type":"address"}],"name":"OwnerNominated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"requestHash","type":"bytes32"},{"indexed":false,"internalType":"address","name":"ogSender","type":"address"},{"indexed":false,"internalType":"bytes","name":"request","type":"bytes"}],"name":"RefuelRequestCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"requestHash","type":"bytes32"}],"name":"RefuelRequestWithdrawn","type":"event"},{"inputs":[],"name":"BUNGEE_GATEWAY","outputs":[{"internalType":"contract IBungeeGateway","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NATIVE_TOKEN_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT2","outputs":[{"internalType":"contract IPermit2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WRAPPED_NATIVE_TOKEN","outputs":[{"internalType":"contract WETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"uint256","name":"originChainId","type":"uint256"},{"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"delegate","type":"address"},{"internalType":"address","name":"bungeeGateway","type":"address"},{"internalType":"uint32","name":"switchboardId","type":"uint32"},{"internalType":"address","name":"inputToken","type":"address"},{"internalType":"uint256","name":"inputAmount","type":"uint256"},{"internalType":"address","name":"outputToken","type":"address"},{"internalType":"uint256","name":"minOutputAmount","type":"uint256"},{"internalType":"uint256","name":"refuelAmount","type":"uint256"}],"internalType":"struct BasicRequest","name":"basicReq","type":"tuple"},{"internalType":"address","name":"swapOutputToken","type":"address"},{"internalType":"uint256","name":"minSwapOutput","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"bytes","name":"affiliateFees","type":"bytes"}],"internalType":"struct Request","name":"singleOutputRequest","type":"tuple"}],"name":"createRefuelRequest","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_hash","type":"bytes32"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"isValidSignature","outputs":[{"internalType":"bytes4","name":"magicValue","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nominee_","type":"address"}],"name":"nominateOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nominee","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"requestInbox","outputs":[{"internalType":"address","name":"ogSender","type":"address"},{"internalType":"bytes32","name":"typedDataHash","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"rescue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"uint256","name":"originChainId","type":"uint256"},{"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"delegate","type":"address"},{"internalType":"address","name":"bungeeGateway","type":"address"},{"internalType":"uint32","name":"switchboardId","type":"uint32"},{"internalType":"address","name":"inputToken","type":"address"},{"internalType":"uint256","name":"inputAmount","type":"uint256"},{"internalType":"address","name":"outputToken","type":"address"},{"internalType":"uint256","name":"minOutputAmount","type":"uint256"},{"internalType":"uint256","name":"refuelAmount","type":"uint256"}],"internalType":"struct BasicRequest","name":"basicReq","type":"tuple"},{"internalType":"address","name":"swapOutputToken","type":"address"},{"internalType":"uint256","name":"minSwapOutput","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"bytes","name":"affiliateFees","type":"bytes"}],"internalType":"struct Request","name":"singleOutputRequest","type":"tuple"}],"name":"withdrawFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"typedDataHash","type":"bytes32"}],"name":"withdrawnInbox","outputs":[{"internalType":"bool","name":"withdrawn","type":"bool"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]