mirror of
https://github.com/serai-dex/serai.git
synced 2025-01-12 05:44:53 +00:00
Transfer ETH with CREATE, not prior to CREATE
Saves a few thousand gas.
This commit is contained in:
parent
1e1b821d34
commit
ae76749513
1 changed files with 32 additions and 20 deletions
|
@ -126,23 +126,28 @@ contract Router {
|
||||||
emit InInstruction(msg.sender, coin, amount, instruction);
|
emit InInstruction(msg.sender, coin, amount, instruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform a transfer out
|
/*
|
||||||
function _transferOut(address to, address coin, uint256 value) private {
|
|
||||||
/*
|
|
||||||
We on purposely do not check if these calls succeed. A call either succeeded, and there's no
|
We on purposely do not check if these calls succeed. A call either succeeded, and there's no
|
||||||
problem, or the call failed due to:
|
problem, or the call failed due to:
|
||||||
A) An insolvency
|
A) An insolvency
|
||||||
B) A malicious receiver
|
B) A malicious receiver
|
||||||
C) A non-standard token
|
C) A non-standard token
|
||||||
A is an invariant, B should be dropped, C is something out of the control of this contract.
|
A is an invariant, B should be dropped, C is something out of the control of this contract.
|
||||||
It is again the Serai's network role to not add support for any non-standard tokens,
|
It is again the Serai's network role to not add support for any non-standard tokens,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Perform an ERC20 transfer out
|
||||||
|
function _erc20TransferOut(address to, address coin, uint256 value) private {
|
||||||
|
coin.call{ gas: 100_000 }(abi.encodeWithSelector(IERC20.transfer.selector, msg.sender, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform an ETH/ERC20 transfer out
|
||||||
|
function _transferOut(address to, address coin, uint256 value) private {
|
||||||
if (coin == address(0)) {
|
if (coin == address(0)) {
|
||||||
// Enough gas to service the transfer and a minimal amount of logic
|
// Enough gas to service the transfer and a minimal amount of logic
|
||||||
// TODO: If we're constructing a contract, we can do this at the same time as construction
|
|
||||||
to.call{ value: value, gas: 5_000 }("");
|
to.call{ value: value, gas: 5_000 }("");
|
||||||
} else {
|
} else {
|
||||||
coin.call{ gas: 100_000 }(abi.encodeWithSelector(IERC20.transfer.selector, msg.sender, value));
|
_erc20TransferOut(to, coin, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,13 +156,14 @@ contract Router {
|
||||||
letting them execute whatever calls they're coded for. Since we can't meter CREATE, we call
|
letting them execute whatever calls they're coded for. Since we can't meter CREATE, we call
|
||||||
CREATE from this function which we call not internally, but with CALL (which we can meter).
|
CREATE from this function which we call not internally, but with CALL (which we can meter).
|
||||||
*/
|
*/
|
||||||
function arbitaryCallOut(bytes memory code) external {
|
function arbitaryCallOut(bytes memory code) external payable {
|
||||||
// Because we're creating a contract, increment our nonce
|
// Because we're creating a contract, increment our nonce
|
||||||
_smartContractNonce += 1;
|
_smartContractNonce += 1;
|
||||||
|
|
||||||
|
uint256 msg_value = msg.value;
|
||||||
address contractAddress;
|
address contractAddress;
|
||||||
assembly {
|
assembly {
|
||||||
contractAddress := create(0, add(code, 0x20), mload(code))
|
contractAddress := create(msg_value, add(code, 0x20), mload(code))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,18 +199,24 @@ contract Router {
|
||||||
abi.decode(transactions[i].destination, (AddressDestination));
|
abi.decode(transactions[i].destination, (AddressDestination));
|
||||||
_transferOut(destination.destination, coin, transactions[i].value);
|
_transferOut(destination.destination, coin, transactions[i].value);
|
||||||
} else {
|
} else {
|
||||||
// The destination is a piece of initcode. We calculate the hash of the will-be contract,
|
// Prepare for the transfer
|
||||||
// transfer to it, and then run the initcode
|
uint256 eth_value = 0;
|
||||||
address nextAddress =
|
if (coin == address(0)) {
|
||||||
address(uint160(uint256(keccak256(abi.encode(address(this), _smartContractNonce)))));
|
// If it's ETH, we transfer the value with the call
|
||||||
|
eth_value = transactions[i].value;
|
||||||
|
} else {
|
||||||
|
// If it's an ERC20, we calculate the hash of the will-be contract and transfer to it
|
||||||
|
// before deployment. This avoids needing to deploy, then call again, offering a few
|
||||||
|
// optimizations
|
||||||
|
address nextAddress =
|
||||||
|
address(uint160(uint256(keccak256(abi.encode(address(this), _smartContractNonce)))));
|
||||||
|
_erc20TransferOut(nextAddress, coin, transactions[i].value);
|
||||||
|
}
|
||||||
|
|
||||||
// Perform the transfer
|
// Perform the deployment with the defined gas budget
|
||||||
_transferOut(nextAddress, coin, transactions[i].value);
|
|
||||||
|
|
||||||
// Perform the calls with a set gas budget
|
|
||||||
(CodeDestination memory destination) =
|
(CodeDestination memory destination) =
|
||||||
abi.decode(transactions[i].destination, (CodeDestination));
|
abi.decode(transactions[i].destination, (CodeDestination));
|
||||||
address(this).call{ gas: destination.gas_limit }(
|
address(this).call{ gas: destination.gas_limit, value: eth_value }(
|
||||||
abi.encodeWithSelector(Router.arbitaryCallOut.selector, destination.code)
|
abi.encodeWithSelector(Router.arbitaryCallOut.selector, destination.code)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue