Last active
March 20, 2021 23:43
-
-
Save nelsonhp/8c7175c90b44b9abc603ae8fcfc42e32 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Copyright 2021 Nelson Hunter Prendergast | |
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated | |
documentation files (the "Software"), to deal in the Software without restriction, including without limitation | |
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | |
and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in all copies or substantial portions | |
of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED | |
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
DEALINGS IN THE SOFTWARE. | |
*/ | |
object "AtomicProxy" { | |
/* | |
You are hereby warned: | |
- Using this contract is DANGEROUS. | |
- This code has not been reviewed. | |
- The entire contract is written in assembly. | |
- This code was written for fun, not production. | |
- Almost all safety has been thrown out the window to minimize gas. | |
What: | |
This contract allows a user to batch send transactions | |
with near optimal overhead. This contract never stores to | |
or loads from storage in any way ( unless done in a delegatecall ) | |
This contract allows you to perform: | |
- Multiple calls with no value | |
- A Single call with all passed value | |
- Multiple delegate calls | |
Why: | |
Gas is expensive and atomic transactions are useful. | |
How: | |
Black magic. | |
Usage: | |
CallData: | |
Contains a list of Tx Spec objects. Each Tx Spec | |
describes a transaction in a length prefix encoding. | |
These transactions will be called in order by | |
iteratively parsing the next object and performing | |
the encoded call. | |
Tx Spec: | |
Describes a transaction to send. | |
CallDataSize: | |
Number of bytes in calldata for this tx | |
< uint16 > | |
CallTypeFlags: | |
Determines if this tx is a call vs delegatecall and if value should be sent with call. | |
See switch case for details. | |
< uint8 > | |
Target: | |
Where call should be sent to. | |
< 20 bytes > | |
CallData: | |
Actual CallData to be sent encoded as abi.encode would marshall CallData | |
if you where encoding it using actual types and the function signature. | |
< Dynamic number of bytes > | |
Optional Value: | |
If the value of CallTypeFlags is set to 3, this value will be parsed | |
and used to populate the value argument to a call transaction. | |
[< uint256 >] | |
Operational Details: | |
A failure in any transaction will revert all transactions. | |
Only the last transaction in list will return data to caller. | |
The contract is owner protected, but the owner is set in the | |
runtime code. As such, the owner can not be changed, but gas | |
is saved by not loading from memory. | |
*/ | |
code { | |
// deploy the contract | |
datacopy(0, dataoffset("runtime"), datasize("runtime")) | |
// store caller as owner for access control checks | |
setimmutable("OWNER", caller()) | |
return(0, datasize("runtime")) | |
} | |
object "runtime" { | |
code { | |
// make sure caller is owner | |
if iszero(eq(loadimmutable("OWNER"), caller())) { | |
revert(0,0) | |
} | |
// calldata read offset | |
let offset := 0 | |
// size of calldata | |
let callDataSize := calldatasize() | |
// where to store result | |
let result := 0 | |
// iterate loop until not enough remaining data | |
for {} gt(sub(callDataSize, offset), 0x17) {} { | |
// read data at offset | |
let offsetData := calldataload(offset) | |
// isolate target address but leave HOB - EVM will zero for us | |
let target := shr(72, offsetData) | |
// get size of call data | |
let callSize := shr(240, offsetData) | |
// get pointer to head of this tx's calldata | |
let dataPtr := add(0x17, offset) | |
// copy calldata to memory | |
calldatacopy(0, dataPtr, callSize) | |
// update offset for next iteration | |
offset := add(dataPtr, callSize) | |
// determine case and perform call | |
switch and(offsetData, 0x0000ff00000000000000000000000000000000000000000000000000000000000000) | |
case 0x00000000000000000000000000000000000000000000000000000000000000000000 { // delegatecall = false value = false | |
result := call(gas(), target, 0, 0, callSize, 0, 0) | |
} | |
case 0x00000100000000000000000000000000000000000000000000000000000000000000 { // delegatecall = false value = true | |
result := call(gas(), target, callvalue(), 0, callSize, 0, 0) | |
} | |
case 0x00000200000000000000000000000000000000000000000000000000000000000000 { // delegatecall = true value = false | |
result := delegatecall(gas(), target, 0, callSize, 0, 0) | |
} | |
case 0x00000300000000000000000000000000000000000000000000000000000000000000 { // dynamicvalue = true | |
let value := calldataload(offset) | |
offset := add(0x20, offset) | |
result := call(gas(), target, value, 0, callSize, 0, 0) | |
} | |
default{ // something is broken | |
revert(0,0) | |
} | |
if eq(result, 0) { // stop on error | |
break | |
} | |
} | |
// return or revert based on value of result | |
let size := returndatasize() | |
returndatacopy(0, 0, size) | |
if eq(result, 0) { | |
revert(0, size) | |
} | |
return(0, size) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment