mirror of
https://github.com/serai-dex/serai.git
synced 2025-02-04 12:16:37 +00:00
Add IRouter
This commit is contained in:
parent
cf4123b0f8
commit
8de42cc2d4
4 changed files with 191 additions and 104 deletions
|
@ -27,7 +27,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
build_solidity_contracts::build(
|
build_solidity_contracts::build(
|
||||||
&["../../../networks/ethereum/schnorr/contracts", "../erc20/contracts"],
|
&["../../../networks/ethereum/schnorr/contracts", "../erc20/contracts", "contracts"],
|
||||||
"contracts",
|
"contracts",
|
||||||
&artifacts_path,
|
&artifacts_path,
|
||||||
)
|
)
|
||||||
|
@ -36,7 +36,11 @@ fn main() {
|
||||||
// This cannot be handled with the sol! macro. The Solidity requires an import
|
// This cannot be handled with the sol! macro. The Solidity requires an import
|
||||||
// https://github.com/alloy-rs/core/issues/602
|
// https://github.com/alloy-rs/core/issues/602
|
||||||
sol(
|
sol(
|
||||||
&["../../../networks/ethereum/schnorr/contracts/Schnorr.sol", "contracts/Router.sol"],
|
&[
|
||||||
|
"../../../networks/ethereum/schnorr/contracts/Schnorr.sol",
|
||||||
|
"contracts/IRouter.sol",
|
||||||
|
"contracts/Router.sol",
|
||||||
|
],
|
||||||
&(artifacts_path + "/router.rs"),
|
&(artifacts_path + "/router.rs"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
147
processor/ethereum/router/contracts/IRouter.sol
Normal file
147
processor/ethereum/router/contracts/IRouter.sol
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.8.26;
|
||||||
|
|
||||||
|
/// @title Serai Router
|
||||||
|
/// @author Luke Parker <lukeparker@serai.exchange>
|
||||||
|
/// @notice Intakes coins for the Serai network and handles relaying batches of transfers out
|
||||||
|
interface IRouter {
|
||||||
|
/// @title A signature
|
||||||
|
/// @dev Thin wrapper around `c, s` to simplify the API
|
||||||
|
struct Signature {
|
||||||
|
bytes32 c;
|
||||||
|
bytes32 s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @title The type of destination
|
||||||
|
/// @dev A destination is either an address or a blob of code to deploy and call
|
||||||
|
enum DestinationType {
|
||||||
|
Address,
|
||||||
|
Code
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @title A code destination
|
||||||
|
/**
|
||||||
|
* @dev If transferring an ERC20 to this destination, it will be transferred to the address the
|
||||||
|
* code will be deployed to. If transferring ETH, it will be transferred with the deployment of
|
||||||
|
* the code. `code` is deployed with CREATE (calling its constructor). The entire deployment
|
||||||
|
* (and associated sandboxing) must consume less than `gasLimit` units of gas or it will revert.
|
||||||
|
*/
|
||||||
|
struct CodeDestination {
|
||||||
|
uint32 gasLimit;
|
||||||
|
bytes code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @title An instruction to transfer coins out
|
||||||
|
/// @dev Specifies a destination and amount but not the coin as that's assumed to be contextual
|
||||||
|
struct OutInstruction {
|
||||||
|
DestinationType destinationType;
|
||||||
|
bytes destination;
|
||||||
|
uint256 amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice Emitted when the key for Serai's Ethereum validators is updated
|
||||||
|
/// @param nonce The nonce consumed to update this key
|
||||||
|
/// @param key The key updated to
|
||||||
|
event SeraiKeyUpdated(uint256 indexed nonce, bytes32 indexed key);
|
||||||
|
|
||||||
|
/// @notice Emitted when an InInstruction occurs
|
||||||
|
/// @param from The address which called `inInstruction` and caused this event to be emitted
|
||||||
|
/// @param coin The coin transferred in
|
||||||
|
/// @param amount The amount of the coin transferred in
|
||||||
|
/// @param instruction The Shorthand-encoded InInstruction for Serai to decode and handle
|
||||||
|
event InInstruction(
|
||||||
|
address indexed from, address indexed coin, uint256 amount, bytes instruction
|
||||||
|
);
|
||||||
|
|
||||||
|
/// @notice Emitted when a batch of `OutInstruction`s occurs
|
||||||
|
/// @param nonce The nonce consumed to execute this batch of transactions
|
||||||
|
/// @param messageHash The hash of the message signed for the executed batch
|
||||||
|
event Executed(uint256 indexed nonce, bytes32 indexed messageHash);
|
||||||
|
|
||||||
|
/// @notice Emitted when `escapeHatch` is invoked
|
||||||
|
/// @param escapeTo The address to escape to
|
||||||
|
event EscapeHatch(address indexed escapeTo);
|
||||||
|
|
||||||
|
/// @notice Emitted when coins escape through the escape hatch
|
||||||
|
/// @param coin The coin which escaped
|
||||||
|
event Escaped(address indexed coin);
|
||||||
|
|
||||||
|
/// @notice The contract has had its escape hatch invoked and won't accept further actions
|
||||||
|
error EscapeHatchInvoked();
|
||||||
|
/// @notice The signature was invalid
|
||||||
|
error InvalidSignature();
|
||||||
|
/// @notice The amount specified didn't match `msg.value`
|
||||||
|
error AmountMismatchesMsgValue();
|
||||||
|
/// @notice The call to an ERC20's `transferFrom` failed
|
||||||
|
error TransferFromFailed();
|
||||||
|
|
||||||
|
/// @notice An invalid address to escape to was specified.
|
||||||
|
error InvalidEscapeAddress();
|
||||||
|
/// @notice Escaping when escape hatch wasn't invoked.
|
||||||
|
error EscapeHatchNotInvoked();
|
||||||
|
|
||||||
|
/// @notice Update the key representing Serai's Ethereum validators
|
||||||
|
/// @dev This assumes the key is correct. No checks on it are performed
|
||||||
|
/// @param signature The signature by the current key authorizing this update
|
||||||
|
/// @param newSeraiKey The key to update to
|
||||||
|
function updateSeraiKey(Signature calldata signature, bytes32 newSeraiKey) external;
|
||||||
|
|
||||||
|
/// @notice Transfer coins into Serai with an instruction
|
||||||
|
/// @param coin The coin to transfer in (address(0) if Ether)
|
||||||
|
/// @param amount The amount to transfer in (msg.value if Ether)
|
||||||
|
/**
|
||||||
|
* @param instruction The Shorthand-encoded InInstruction for Serai to associate with this
|
||||||
|
* transfer in
|
||||||
|
*/
|
||||||
|
// Re-entrancy doesn't bork this function
|
||||||
|
// slither-disable-next-line reentrancy-events
|
||||||
|
function inInstruction(address coin, uint256 amount, bytes memory instruction) external payable;
|
||||||
|
|
||||||
|
/// @notice Execute some arbitrary code within a secure sandbox
|
||||||
|
/**
|
||||||
|
* @dev This performs sandboxing by deploying this code with `CREATE`. This is an external
|
||||||
|
* function as we can't meter `CREATE`/internal functions. We work around this by calling this
|
||||||
|
* function with `CALL` (which we can meter). This does forward `msg.value` to the newly
|
||||||
|
* deployed contract.
|
||||||
|
*/
|
||||||
|
/// @param code The code to execute
|
||||||
|
function executeArbitraryCode(bytes memory code) external payable;
|
||||||
|
|
||||||
|
/// @notice Execute a batch of `OutInstruction`s
|
||||||
|
/**
|
||||||
|
* @dev All `OutInstruction`s in a batch are only for a single coin to simplify handling of the
|
||||||
|
* fee
|
||||||
|
*/
|
||||||
|
/// @param signature The signature by the current key for Serai's Ethereum validators
|
||||||
|
/// @param coin The coin all of these `OutInstruction`s are for
|
||||||
|
/// @param fee The fee to pay (in coin) to the caller for their relaying of this batch
|
||||||
|
/// @param outs The `OutInstruction`s to act on
|
||||||
|
function execute(
|
||||||
|
Signature calldata signature,
|
||||||
|
address coin,
|
||||||
|
uint256 fee,
|
||||||
|
OutInstruction[] calldata outs
|
||||||
|
) external;
|
||||||
|
|
||||||
|
/// @notice Escapes to a new smart contract
|
||||||
|
/// @dev This should be used upon an invariant being reached or new functionality being needed
|
||||||
|
/// @param signature The signature by the current key for Serai's Ethereum validators
|
||||||
|
/// @param escapeTo The address to escape to
|
||||||
|
function escapeHatch(Signature calldata signature, address escapeTo) external;
|
||||||
|
|
||||||
|
/// @notice Escape coins after the escape hatch has been invoked
|
||||||
|
/// @param coin The coin to escape
|
||||||
|
function escape(address coin) external;
|
||||||
|
|
||||||
|
/// @notice Fetch the next nonce to use by an action published to this contract
|
||||||
|
/// return The next nonce to use by an action published to this contract
|
||||||
|
function nextNonce() external view returns (uint256);
|
||||||
|
|
||||||
|
/// @notice Fetch the current key for Serai's Ethereum validator set
|
||||||
|
/// @return The current key for Serai's Ethereum validator set
|
||||||
|
function seraiKey() external view returns (bytes32);
|
||||||
|
|
||||||
|
/// @notice Fetch the address escaped to
|
||||||
|
/// @return The address which was escaped to (address(0) if the escape hatch hasn't been invoked)
|
||||||
|
function escapedTo() external view returns (address);
|
||||||
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
pragma solidity ^0.8.26;
|
pragma solidity ^0.8.26;
|
||||||
|
|
||||||
// TODO: MIT licensed interface
|
|
||||||
|
|
||||||
import "IERC20.sol";
|
import "IERC20.sol";
|
||||||
|
|
||||||
import "Schnorr.sol";
|
import "Schnorr.sol";
|
||||||
|
|
||||||
|
import "IRouter.sol";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The Router directly performs low-level calls in order to fine-tune the gas settings. Since this
|
The Router directly performs low-level calls in order to fine-tune the gas settings. Since this
|
||||||
contract is meant to relay an entire batch of transactions, the ability to exactly meter
|
contract is meant to relay an entire batch of transactions, the ability to exactly meter
|
||||||
|
@ -32,7 +32,7 @@ contract Router {
|
||||||
/*
|
/*
|
||||||
We don't expose a getter for this as it shouldn't be expected to have any specific value at a
|
We don't expose a getter for this as it shouldn't be expected to have any specific value at a
|
||||||
given moment in time. If someone wants to know the address of their deployed contract, they can
|
given moment in time. If someone wants to know the address of their deployed contract, they can
|
||||||
have it emit an event and verify the emitting contract is the expected one.
|
have it emit IRouter.an event and verify the emitting contract is the expected one.
|
||||||
*/
|
*/
|
||||||
uint256 private _smartContractNonce;
|
uint256 private _smartContractNonce;
|
||||||
|
|
||||||
|
@ -51,87 +51,12 @@ contract Router {
|
||||||
/// @dev The address escaped to
|
/// @dev The address escaped to
|
||||||
address private _escapedTo;
|
address private _escapedTo;
|
||||||
|
|
||||||
/// @title The type of destination
|
|
||||||
/// @dev A destination is either an address or a blob of code to deploy and call
|
|
||||||
enum DestinationType {
|
|
||||||
Address,
|
|
||||||
Code
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @title A code destination
|
|
||||||
/**
|
|
||||||
* @dev If transferring an ERC20 to this destination, it will be transferred to the address the
|
|
||||||
* code will be deployed to. If transferring ETH, it will be transferred with the deployment of
|
|
||||||
* the code. `code` is deployed with CREATE (calling its constructor). The entire deployment
|
|
||||||
* (and associated sandboxing) must consume less than `gasLimit` units of gas or it will revert.
|
|
||||||
*/
|
|
||||||
struct CodeDestination {
|
|
||||||
uint32 gasLimit;
|
|
||||||
bytes code;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @title An instruction to transfer coins out
|
|
||||||
/// @dev Specifies a destination and amount but not the coin as that's assumed to be contextual
|
|
||||||
struct OutInstruction {
|
|
||||||
DestinationType destinationType;
|
|
||||||
bytes destination;
|
|
||||||
uint256 amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @title A signature
|
|
||||||
/// @dev Thin wrapper around `c, s` to simplify the API
|
|
||||||
struct Signature {
|
|
||||||
bytes32 c;
|
|
||||||
bytes32 s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @notice Emitted when the key for Serai's Ethereum validators is updated
|
|
||||||
/// @param nonce The nonce consumed to update this key
|
|
||||||
/// @param key The key updated to
|
|
||||||
event SeraiKeyUpdated(uint256 indexed nonce, bytes32 indexed key);
|
|
||||||
|
|
||||||
/// @notice Emitted when an InInstruction occurs
|
|
||||||
/// @param from The address which called `inInstruction` and caused this event to be emitted
|
|
||||||
/// @param coin The coin transferred in
|
|
||||||
/// @param amount The amount of the coin transferred in
|
|
||||||
/// @param instruction The Shorthand-encoded InInstruction for Serai to decode and handle
|
|
||||||
event InInstruction(
|
|
||||||
address indexed from, address indexed coin, uint256 amount, bytes instruction
|
|
||||||
);
|
|
||||||
|
|
||||||
/// @notice Emitted when a batch of `OutInstruction`s occurs
|
|
||||||
/// @param nonce The nonce consumed to execute this batch of transactions
|
|
||||||
/// @param messageHash The hash of the message signed for the executed batch
|
|
||||||
event Executed(uint256 indexed nonce, bytes32 indexed messageHash);
|
|
||||||
|
|
||||||
/// @notice Emitted when `escapeHatch` is invoked
|
|
||||||
/// @param escapeTo The address to escape to
|
|
||||||
event EscapeHatch(address indexed escapeTo);
|
|
||||||
|
|
||||||
/// @notice Emitted when coins escape through the escape hatch
|
|
||||||
/// @param coin The coin which escaped
|
|
||||||
event Escaped(address indexed coin);
|
|
||||||
|
|
||||||
/// @notice The contract has had its escape hatch invoked and won't accept further actions
|
|
||||||
error EscapeHatchInvoked();
|
|
||||||
/// @notice The signature was invalid
|
|
||||||
error InvalidSignature();
|
|
||||||
/// @notice The amount specified didn't match `msg.value`
|
|
||||||
error AmountMismatchesMsgValue();
|
|
||||||
/// @notice The call to an ERC20's `transferFrom` failed
|
|
||||||
error TransferFromFailed();
|
|
||||||
|
|
||||||
/// @notice An invalid address to escape to was specified.
|
|
||||||
error InvalidEscapeAddress();
|
|
||||||
/// @notice Escaping when escape hatch wasn't invoked.
|
|
||||||
error EscapeHatchNotInvoked();
|
|
||||||
|
|
||||||
/// @dev Updates the Serai key. This does not update `_nextNonce`
|
/// @dev Updates the Serai key. This does not update `_nextNonce`
|
||||||
/// @param nonceUpdatedWith The nonce used to update the key
|
/// @param nonceUpdatedWith The nonce used to update the key
|
||||||
/// @param newSeraiKey The key updated to
|
/// @param newSeraiKey The key updated to
|
||||||
function _updateSeraiKey(uint256 nonceUpdatedWith, bytes32 newSeraiKey) private {
|
function _updateSeraiKey(uint256 nonceUpdatedWith, bytes32 newSeraiKey) private {
|
||||||
_seraiKey = newSeraiKey;
|
_seraiKey = newSeraiKey;
|
||||||
emit SeraiKeyUpdated(nonceUpdatedWith, newSeraiKey);
|
emit IRouter.SeraiKeyUpdated(nonceUpdatedWith, newSeraiKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @notice The constructor for the relayer
|
/// @notice The constructor for the relayer
|
||||||
|
@ -153,9 +78,9 @@ contract Router {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev
|
* @dev
|
||||||
* Verify a signature of the calldata, placed immediately after the function selector. The calldata
|
* Verify a signature of the calldata, placed immediately after the function selector. The
|
||||||
* should be signed with the nonce taking the place of the signature's commitment to its nonce, and
|
* calldata should be signed with the nonce taking the place of the signature's commitment to
|
||||||
* the signature solution zeroed.
|
* its nonce, and the signature solution zeroed.
|
||||||
*/
|
*/
|
||||||
function verifySignature()
|
function verifySignature()
|
||||||
private
|
private
|
||||||
|
@ -163,7 +88,7 @@ contract Router {
|
||||||
{
|
{
|
||||||
// If the escape hatch was triggered, reject further signatures
|
// If the escape hatch was triggered, reject further signatures
|
||||||
if (_escapedTo != address(0)) {
|
if (_escapedTo != address(0)) {
|
||||||
revert EscapeHatchInvoked();
|
revert IRouter.EscapeHatchInvoked();
|
||||||
}
|
}
|
||||||
|
|
||||||
message = msg.data;
|
message = msg.data;
|
||||||
|
@ -175,7 +100,7 @@ contract Router {
|
||||||
(triggering undefined behavior).
|
(triggering undefined behavior).
|
||||||
*/
|
*/
|
||||||
if (messageLen < 68) {
|
if (messageLen < 68) {
|
||||||
revert InvalidSignature();
|
revert IRouter.InvalidSignature();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read _nextNonce into memory as the nonce we'll use
|
// Read _nextNonce into memory as the nonce we'll use
|
||||||
|
@ -202,7 +127,7 @@ contract Router {
|
||||||
|
|
||||||
// Verify the signature
|
// Verify the signature
|
||||||
if (!Schnorr.verify(_seraiKey, messageHash, signatureC, signatureS)) {
|
if (!Schnorr.verify(_seraiKey, messageHash, signatureC, signatureS)) {
|
||||||
revert InvalidSignature();
|
revert IRouter.InvalidSignature();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the next nonce
|
// Set the next nonce
|
||||||
|
@ -251,6 +176,10 @@ contract Router {
|
||||||
// @param newSeraiKey The key to update to
|
// @param newSeraiKey The key to update to
|
||||||
function updateSeraiKey() external {
|
function updateSeraiKey() external {
|
||||||
(uint256 nonceUsed, bytes memory args,) = verifySignature();
|
(uint256 nonceUsed, bytes memory args,) = verifySignature();
|
||||||
|
/*
|
||||||
|
We could replace this with a length check (if we don't simply assume the calldata is valid as
|
||||||
|
it was properly signed) + mload to save 24 gas but it's not worth the complexity.
|
||||||
|
*/
|
||||||
(,, bytes32 newSeraiKey) = abi.decode(args, (bytes32, bytes32, bytes32));
|
(,, bytes32 newSeraiKey) = abi.decode(args, (bytes32, bytes32, bytes32));
|
||||||
_updateSeraiKey(nonceUsed, newSeraiKey);
|
_updateSeraiKey(nonceUsed, newSeraiKey);
|
||||||
}
|
}
|
||||||
|
@ -267,7 +196,7 @@ contract Router {
|
||||||
function inInstruction(address coin, uint256 amount, bytes memory instruction) external payable {
|
function inInstruction(address coin, uint256 amount, bytes memory instruction) external payable {
|
||||||
// Check the transfer
|
// Check the transfer
|
||||||
if (coin == address(0)) {
|
if (coin == address(0)) {
|
||||||
if (amount != msg.value) revert AmountMismatchesMsgValue();
|
if (amount != msg.value) revert IRouter.AmountMismatchesMsgValue();
|
||||||
} else {
|
} else {
|
||||||
(bool success, bytes memory res) = address(coin).call(
|
(bool success, bytes memory res) = address(coin).call(
|
||||||
abi.encodeWithSelector(IERC20.transferFrom.selector, msg.sender, address(this), amount)
|
abi.encodeWithSelector(IERC20.transferFrom.selector, msg.sender, address(this), amount)
|
||||||
|
@ -278,7 +207,7 @@ contract Router {
|
||||||
ERC20 contract did in fact return true
|
ERC20 contract did in fact return true
|
||||||
*/
|
*/
|
||||||
bool nonStandardResOrTrue = (res.length == 0) || abi.decode(res, (bool));
|
bool nonStandardResOrTrue = (res.length == 0) || abi.decode(res, (bool));
|
||||||
if (!(success && nonStandardResOrTrue)) revert TransferFromFailed();
|
if (!(success && nonStandardResOrTrue)) revert IRouter.TransferFromFailed();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -303,7 +232,7 @@ contract Router {
|
||||||
|
|
||||||
It is the Serai network's role not to add support for any non-standard implementations.
|
It is the Serai network's role not to add support for any non-standard implementations.
|
||||||
*/
|
*/
|
||||||
emit InInstruction(msg.sender, coin, amount, instruction);
|
emit IRouter.InInstruction(msg.sender, coin, amount, instruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Perform an ERC20 transfer out
|
/// @dev Perform an ERC20 transfer out
|
||||||
|
@ -422,11 +351,11 @@ contract Router {
|
||||||
// slither-disable-next-line calls-loop
|
// slither-disable-next-line calls-loop
|
||||||
function execute() external {
|
function execute() external {
|
||||||
(uint256 nonceUsed, bytes memory args, bytes32 message) = verifySignature();
|
(uint256 nonceUsed, bytes memory args, bytes32 message) = verifySignature();
|
||||||
(,, address coin, uint256 fee, OutInstruction[] memory outs) =
|
(,, address coin, uint256 fee, IRouter.OutInstruction[] memory outs) =
|
||||||
abi.decode(args, (bytes32, bytes32, address, uint256, OutInstruction[]));
|
abi.decode(args, (bytes32, bytes32, address, uint256, IRouter.OutInstruction[]));
|
||||||
|
|
||||||
// TODO: Also include a bit mask here
|
// TODO: Also include a bit mask here
|
||||||
emit Executed(nonceUsed, message);
|
emit IRouter.Executed(nonceUsed, message);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Since we don't have a re-entrancy guard, it is possible for instructions from later batches to
|
Since we don't have a re-entrancy guard, it is possible for instructions from later batches to
|
||||||
|
@ -439,7 +368,7 @@ contract Router {
|
||||||
// slither-disable-next-line reentrancy-events
|
// slither-disable-next-line reentrancy-events
|
||||||
for (uint256 i = 0; i < outs.length; i++) {
|
for (uint256 i = 0; i < outs.length; i++) {
|
||||||
// If the destination is an address, we perform a direct transfer
|
// If the destination is an address, we perform a direct transfer
|
||||||
if (outs[i].destinationType == DestinationType.Address) {
|
if (outs[i].destinationType == IRouter.DestinationType.Address) {
|
||||||
/*
|
/*
|
||||||
This may cause a revert if the destination isn't actually a valid address. Serai is
|
This may cause a revert if the destination isn't actually a valid address. Serai is
|
||||||
trusted to not pass a malformed destination, yet if it ever did, it could simply re-sign a
|
trusted to not pass a malformed destination, yet if it ever did, it could simply re-sign a
|
||||||
|
@ -465,7 +394,8 @@ contract Router {
|
||||||
erc20TransferOut(nextAddress, coin, outs[i].amount);
|
erc20TransferOut(nextAddress, coin, outs[i].amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
(CodeDestination memory destination) = abi.decode(outs[i].destination, (CodeDestination));
|
(IRouter.CodeDestination memory destination) =
|
||||||
|
abi.decode(outs[i].destination, (IRouter.CodeDestination));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Perform the deployment with the defined gas budget.
|
Perform the deployment with the defined gas budget.
|
||||||
|
@ -498,7 +428,7 @@ contract Router {
|
||||||
(,, address escapeTo) = abi.decode(args, (bytes32, bytes32, address));
|
(,, address escapeTo) = abi.decode(args, (bytes32, bytes32, address));
|
||||||
|
|
||||||
if (escapeTo == address(0)) {
|
if (escapeTo == address(0)) {
|
||||||
revert InvalidEscapeAddress();
|
revert IRouter.InvalidEscapeAddress();
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
We want to define the escape hatch so coins here now, and latently received, can be forwarded.
|
We want to define the escape hatch so coins here now, and latently received, can be forwarded.
|
||||||
|
@ -506,21 +436,21 @@ contract Router {
|
||||||
received coins without penalty (if they update the escape hatch after unstaking).
|
received coins without penalty (if they update the escape hatch after unstaking).
|
||||||
*/
|
*/
|
||||||
if (_escapedTo != address(0)) {
|
if (_escapedTo != address(0)) {
|
||||||
revert EscapeHatchInvoked();
|
revert IRouter.EscapeHatchInvoked();
|
||||||
}
|
}
|
||||||
|
|
||||||
_escapedTo = escapeTo;
|
_escapedTo = escapeTo;
|
||||||
emit EscapeHatch(escapeTo);
|
emit IRouter.EscapeHatch(escapeTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @notice Escape coins after the escape hatch has been invoked
|
/// @notice Escape coins after the escape hatch has been invoked
|
||||||
/// @param coin The coin to escape
|
/// @param coin The coin to escape
|
||||||
function escape(address coin) external {
|
function escape(address coin) external {
|
||||||
if (_escapedTo == address(0)) {
|
if (_escapedTo == address(0)) {
|
||||||
revert EscapeHatchNotInvoked();
|
revert IRouter.EscapeHatchNotInvoked();
|
||||||
}
|
}
|
||||||
|
|
||||||
emit Escaped(coin);
|
emit IRouter.Escaped(coin);
|
||||||
|
|
||||||
// Fetch the amount to escape
|
// Fetch the amount to escape
|
||||||
uint256 amount = address(this).balance;
|
uint256 amount = address(this).balance;
|
||||||
|
|
|
@ -31,7 +31,13 @@ use serai_client::networks::ethereum::Address as SeraiAddress;
|
||||||
mod _abi {
|
mod _abi {
|
||||||
include!(concat!(env!("OUT_DIR"), "/serai-processor-ethereum-router/router.rs"));
|
include!(concat!(env!("OUT_DIR"), "/serai-processor-ethereum-router/router.rs"));
|
||||||
}
|
}
|
||||||
use _abi::Router as abi;
|
mod abi {
|
||||||
|
pub use super::_abi::IRouter::{
|
||||||
|
Signature, DestinationType, CodeDestination, OutInstruction, SeraiKeyUpdated, InInstruction,
|
||||||
|
Executed, EscapeHatch, Escaped,
|
||||||
|
};
|
||||||
|
pub use super::_abi::Router::*;
|
||||||
|
}
|
||||||
use abi::{
|
use abi::{
|
||||||
SeraiKeyUpdated as SeraiKeyUpdatedEvent, InInstruction as InInstructionEvent,
|
SeraiKeyUpdated as SeraiKeyUpdatedEvent, InInstruction as InInstructionEvent,
|
||||||
Executed as ExecutedEvent,
|
Executed as ExecutedEvent,
|
||||||
|
@ -326,7 +332,7 @@ impl Router {
|
||||||
]
|
]
|
||||||
.concat()
|
.concat()
|
||||||
.into(),
|
.into(),
|
||||||
gas_limit: 40927 * 120 / 100,
|
gas_limit: 40_889 * 120 / 100,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -353,7 +359,7 @@ impl Router {
|
||||||
.concat()
|
.concat()
|
||||||
.into(),
|
.into(),
|
||||||
// TODO
|
// TODO
|
||||||
gas_limit: 100_000 + ((200_000 + 10_000) * u128::try_from(outs_len).unwrap()),
|
gas_limit: (45_501 + ((200_000 + 10_000) * u128::try_from(outs_len).unwrap())) * 120 / 100,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue