I call it the Ethereum Gatling Gun In this article I outline a method for sending arbitrary amounts of arbitrary transactions in bulk within a single transaction on the Ethereum chain. I also demonstrate the flexibility of the method in practice by performing the following four transactions within one: 1. Send 1 Ether to some other wallet 2. Send 1 ERC223 token to some other wallet 3. Call a contract with no function arguments 4. Call a contract with some function arguments as well as an Ether value For a TLDR version, you may skip directly to the section. For an in-depth version you may continue reading here and I shall guide you down the rabbit hole. Usage Demonstration Introduction One of the main issues blockchains are facing today is their lack of speed. Transactions on the Ethereum Mainnet can take minutes to mine, which is unacceptable for any application aspiring to be a true on-chain DApp (Decentralized Application). In an ideal world, we would have a blockchain that can mine any amount of transactions in sub-second times, would never get congested by traffic, and would scale well enough to cover all demand for space. In such an ideal world, any and every interaction with the DApp User Interface could be a transaction, much like today it may be an API call. Unfortunately, we do not live in an ideal world. So how close can we get to this ideal scenario with the current state of the Ethereum Mainnet? I posed this question to myself a while ago and decided to investigate. Turns out, the answer is: Closer than you may think. While we are not able to address the space issue, we are able to address the speed issue to some extent. This article is the story of writing the fastest Ethereum wallet, one which brings the transaction-per-mouse-click idea closer to the realm of possibility. My goal was to write a smart contract, which would have a special function with a single purpose: To receive multiple transactions encoded into its payload and (You may sense now where the name “Ethereum Gatling Gun” came from..) Such a contract would allow composing a user’s actions in the DApp UI into a sequence of on-chain operations, which could all be fired together once the user is done making changes. fire them all in rapid succession within a single transaction. This contract would also be fully extensible to operate all on-chain counterparts of any additional UI functionality that may be added in the future of a DApp’s life, since it acts as a wallet itself. Before we continue, I must warn readers that the work and code presented in this article is experimental and potentially ! Any contract capable of firing hundreds of transactions in rapid succession must be operated with extreme caution, for obvious reasons. Also, this article is half article, half technical rant. very dangerous if used incorrectly Simple Proxy Contract We can start off simple and write a smart contract that can forward a transaction onto the network in its own name. The type in Solidity has a function, which can be used to call one contract from another contract. address call Forwarding transactions using the function is exactly how Multi-Signature Wallets (MultiSigs) achieve their purpose. Instead of forwarding incoming transactions instantly, they save the transaction into storage and only forward it once all the MultiSig owners approve it. call We can use the function to create a very simple Proxy contract, which forwards all transactions instantly: call pragma solidity ^0.4.25; contract { function execute( target, uint256 weiValue, bytes payload ) public { target.call.value(weiValue)(payload); } } Proxy address I have deployed this contract (along with some fallback functions, because why not) for anybody to play with. It has no concept of ownership, so it will forward transactions from literally anybody. here To demonstrate the contract in action, I transferred a single Bether ERC223 token to the contract in this . I then called the Proxy in , instructing the Proxy to send the token back, which it did. transaction another transaction Our next step is to extend this contract to fire multiple transactions, not just one. Limitations of Solidity and Experimental Encoders The obvious way to extend the Proxy contract to forward many transactions together would be to turn the arguments of the function into arrays and simply loop over them in the function like this: execute pragma solidity ^ ; contract ArrayProxy { (uint256 = ; < targets. ; ++){ targets[ ].call.value(weiValues[ ])(payloads[ ]); } } } 0.4 .25 { function execute ( address[] targets, uint256[] weiValues, bytes[] payloads ) public for i 0 i length i i i i There is, however, an issue. The Solidity 0.4.25 compiler does not support function arguments that are arrays of variable-length types. Out argument is exactly such: is a variable-length type and we want an array of them. The Solidity compiler will complain about this with the following message: bytes[] bytes Error: This type is only supported in the new experimental ABI encoder. Use "pragma experimental ABIEncoderV2;" to enable the feature. address[] targets, uint256[] weiValues, bytes[] payloads ^--------------^ The experimental ABI encoder mentioned in the above error message is being slowly extended in the newest releases of the Solidity Compiler 0.5. There are several issues with this encoder. First of all, it is experimental, and activating anything labeled as “experimental” in the context of blockchains is a red flag in and of itself. The delightfully ominous warning message you get when you try to turn on the experimental encoder, “ ”, does not help. do Do not use experimental features on live deployments Furthermore, the encoder is a work in progress, meaning features are being added to it all the time, and I was not going to wait for and trailblaze experimental features with live mainnet rapid-fire wallets… So how can we build a rapid-fire wallet in Solidity 0.4.25 without any experimental features? We must go beyond Solidity. We must venture into the dreaded EVM bytecode itself. Solidity != EVM, Leveraging Inline Assembly While this may be a subjective feeling, it seems to me that the line between Solidity and the EVM (Ethereum Virtual Machine) is extremely blurred in the Blockchain community, to the point where “smart contracts” are almost synonymous with “Solidity”. Lets clearly disambiguate them. All smart contracts on the Ethereum network are written in bytecode (the hexadecimal string you see on Etherscan when you open up the code of a contract). The EVM is responsible for executing bytecode. Solidity is a programming language that compiles human-readable code into bytecode. The reason we use Solidity is that writing entire contracts in bytecode by hand is extremely inconvenient, and Solidity is the most popular compiler that generates EVM bytecode. But, fundamentally, without the help of Solidity. there is nothing preventing you from writing a smart contract in hexadecimal by hand This distinction is crucial, because smart contracts written in Solidity can only leverage as much of the EVM functionality as the current Solidity compiler supports. If you want your smart contract to do something Solidity doesn’t support, you can still implement it by writing the bytecode yourself. Conveniently, Solidity gives programmers the option to specify bytecode in the blocks in Solidity code, in case we ever needed to go beyond what Solidity can do. This is equivalent to telling Solidity: “You know what, don’t even bother trying to generate bytecode for this particular bit, just use this bytecode I wrote myself”. assembly As it turns out, while the type is not supported by solidity, we could emulate it (in some alternate form) ourselves using bytecode. So let us do just that. bytes[] Dissecting the CALL opcode The first thing we need to realize is that we don’t necessarily need the explicit type at all. What we actually need is to call another contract with some data, and the type is just the way Solidity would have done it (if it could). But Solidity != EVM, so let’s see how the EVM performs internal transactions and what form it needs the payloads in. bytes[] bytes[] The EVM has a opcode, which triggers internal transactions. This is how we manually invoke it from Solidity: CALL assembly{ ( , , , , , , ) } call <gas_limit> <target> <wei_value> <data_location> <data_length> <output_location> <output_length> If we want to use the operation, we must provide/compute the seven arguments it consumes. The two arguments that concern us are the and , since they are the ones that define the payload we send. The rest are trivial. CALL <data_location> <data_length> Basically, when we invoke the operation, it will call the target contract with a payload comprised of the first bytes located at memory location . This means that if we can somehow get all the payloads of all the transactions into one contiguous area in memory, we can loop over limit/offset pairs and invoke directly on the computed memory locations, triggering all the transactions. CALL <data_length> <data_location> CALL Luckily, there is a very convenient data type in Solidity for storing contiguous bytes. It is, somewhat unsurprisingly, . We can replace our desire for a type with a simple argument, and we can add a new argument of payload lengths for navigating the contiguous payloads in the single argument. bytes bytes[] bytes uint256[] bytes Writing the Gatling Gun wallet So let’s stitch it all together. We shall create a fire function, which will have the following signature: function fire( bytes, address targets, uint256 lengths, uint256 values ) [] [] [] The first argument consists of all the payloads of all the transactions simply appended to each other. The argument is an array of addresses of the contracts you wish to call. The array is an array of lengths of the individual payloads in the argument, and finally, the argument are the values in wei that are to be attached to each transaction. bytes targets lengths bytes values The first thing our function must do is ensure that all the arrays are the same length: require(targets. == lengths. ); require(targets. == . ); length length length values length Next, we move the argument into memory. This is a bit more involved, since we need to find out where in the calldata the bytes array is located, how long it is, move it to memory and shift the free memory pointer (0x40) to the end of the data. We do all this in assembly: bytes ; { := (0x40) let payloadLengthLocation := (4, calldataload(4)) let payloadLength := (payloadLengthLocation) let payloadLocation := (32, payloadLengthLocation) (payloadMemoryLocation, payloadLocation, payloadLength) (0x40, add(payloadMemoryLocation, payloadLength)) } uint256 payloadMemoryLocation assembly payloadMemoryLocation mload add calldataload add calldatacopy mstore Once the payloads are in memory, we can set up the loop over all transactions, which computes the limits and offsets. We also require that all the internal transactions succeeded, to ensure either all transactions complete or none do. This is all done in Solidity: uint256 offset = success for(uint256 i = target = targets[i] uint256 limit = lengths[i] uint256 value = values[i] require(success) offset += limit } 0 ; bool ; 0 ; i < targets.length; i++){ address ; ; ; # ASSEMBLY MAGIC HERE ; ; Now all we need to do is replace the “ ” part with actual assembly, that invokes the operation on the correct payload: ASSEMBLY MAGIC CALL { := ( gas, target, value, add(payloadMemoryLocation, offset), limit, , ) } assembly success call 0 0 That’s it! Combined, this code allows you to execute hundreds of arbitrary transactions within a single transaction. But enough chit-chat, let’s demonstrate it in practice. Usage Demonstration First thing we must do is get the Gatling Gun wallet onto the blockchain. To make this process easier, I wrote and deployed a Gatling Gun Deployment contract (equivalent contract is also if you want to play with it using testnet Ether) This contract allows anybody to get themselves their own Gatling Gun wallet by simply calling the function of this contract, which creates a new one and sets the owner address to the one provided. here here on Ropsten deployGatlingGun(address owner) Actually operating the Gatling Gun wallet is still fairly complicated, so I put together a very simple minimalist UI, which allows deploying and operating these wallets with relative ease via Metamask. The UI is at , along with a demo video of how it can be used. This UI is only meant as a testing ground for operating the wallet. If you decide to use the Gatling Gun wallets in any project/DApp, I would recommend interfacing with it directly from your own client code via Metamask. eth-gatling-gun.com If you head over to the tab, you will be presented with this screen: Operation Supposing you don’t have one yet, you may click the “Deploy New Gatling Gun” button, which will take you automatically to the following screen after the transaction is mined: The address at the top will be the address of your new Gatling Gun wallet. The text field with the “Add Transaction(s)” button is where we put all the transactions we want to send. Remember, each transaction has a target address, an amount of Ether, and a Payload. We need to specify these three values (separated by a minus sign) for each transaction. We can specify more of these triplets (multiple transactions) by separating them with the semicolon. Now we are ready for executing the transactions mentioned in the article’s preface. For this demo I decided the following operations all fired at once should sufficiently showcase the wallet’s flexibility: 1. Send 1 Ether to some other wallet 2. Send 1 ERC223 token to some other wallet 3. Call a contract with no function arguments 4. Call a contract with some function arguments as well as an Ether value Though, of course, you can fire a lot more than four if you want. In order to send Ether from the Gatling Gun wallet, we must first send some Ether to it. Remember, the Gatling Gun has it’s own Ether, which it uses when you tell it to send Ether to somewhere else. So I charged the wallet with 2 Ether in . I also sent one ERC223 token to it in . this transaction this transaction For the third transaction, we need some contract to call. I created a very simple contract that increments a single value each time it is called and emits an event. The contract with verified source code is . here For the fourth transaction, we need a contract that can receive Ether payments in one of its functions. For that I created a simple contract that emits events every time it receives some Ether, along with the value sent to it in the payload. The contract is . uint256 here So let’s create the target-value-payload triplets for each of these transactions. For , we will be sending the 1 Ether to one of my testing wallets: . This will be the target. The value will be 1 Ether (1000000000000000000 Wei) and the payload will be empty ( ). Together this adds up to: transaction 1 0x785b8612b225b06764499f61e098725864ecd26b 0x 0x785b8612b225b06764499f61e098725864ecd26b - 1000000000000000000 - 0x For we are actually targeting the token contract itself, which is . The value in Wei will be zero, since token transfers are free. The payload is more complicated. The transfer function’s signature is , which will be the beginning of our payload. This is followed by a 256 bit representation of the recipient address, which we will set to my address: transaction 2 0x14c926f2290044b647e1bf2072e67b495eff1905 0xa9059cbb 000000000000000000000000abcd412dd0e1b3a3bf1131e927450f71f2e9085a This is then followed by the amount we wish to transfer, which is in our case 1 token (1000000000000000000 in wei): 0000000000000000000000000000000000000000000000000de0b6b3a7640000 Together, this transaction adds up to: 0x14c926f2290044b647e1bf2072e67b495eff1905 - 0 - 0xa9059cbb000000000000000000000000abcd412dd0e1b3a3bf1131e927450f71f2e9085a0000000000000000000000000000000000000000000000000de0b6b3a7640000 is very simple. We call the contract at address , value will be zero and the payload will be the signature of the function, which is . Together that is: Transaction 3 0xeeb66b5624ddfa13bee72d9e9dc418a34a74b5c5 increment() 0xd09de08a 0xeeb66b5624ddfa13bee72d9e9dc418a34a74b5c5 - 0 - 0xd09de08a makes use of all the possible inputs.We call the contract at address with a value of 1 Ether and call the function. Its signature is and the payload we send to it can be any number, let’s say 123. Encoded into hex and put together the transaction looks like this: Transaction 4 0x06741096ef84fd751b0805a96583123b5cb11540 payment(uint256 number) 0x8b3c99e3 0x06741096ef84fd751b0805a96583123b5cb11540 - 1000000000000000000 - 0x8b3c99e3000000000000000000000000000000000000000000000000000000000000007b We can append all these transactions to each other with a semicolon separator to end up with the following: 0x785b8612b225b06764499f61e098725864ecd26b - 1000000000000000000 - 0x ; 0x14c926f2290044b647e1bf2072e67b495eff1905 - 0 - 0xa9059cbb000000000000000000000000abcd412dd0e1b3a3bf1131e927450f71f2e9085a0000000000000000000000000000000000000000000000000de0b6b3a7640000 ; 0xeeb66b5624ddfa13bee72d9e9dc418a34a74b5c5 - 0 - 0xd09de08a ; 0x06741096ef84fd751b0805a96583123b5cb11540 - 1000000000000000000 - 0x8b3c99e3000000000000000000000000000000000000000000000000000000000000007b There is nothing else we need to do with this text, we can simply copy and paste it into the transactions text field on the Operation page like this and hit “Add Transaction(s)”: Once you add the transactions, you will see them all in the transactions list, which you can edit using the [remove] buttons. All that’s left to do is hit Fire! I fired this exact sequence of transactions in on the Mainnet. this transaction As you can see in the transaction overview below, both Ether transfers as well as the token transfer were registered. The is the best place for investigating what happened during the transaction’s execution, as all our testing contracts emit events. The first two are the standard ERC223 token events. The last two are the events from our two test contracts: Event Log tab Evaluation At this point you may naturally want to ask “What makes this better than sending the transactions out one by one?”. There are three main reasons. First of all, . I have simulated each of our four transactions individually to demonstrate the difference in amount of Gas used. sending out transactions in bulk is cheaper If you look at the individual versions of , , and , you will notice that their combined amount of gas used is 127 603. Transaction 1 Transaction 2 Transaction 3 Transaction 4 Comparing this to the cost of the which cost 78 378 we can see that the bulk transaction was 38% cheaper. bulk transaction This is because each Ethereum transaction has a base cost of 21 000 gas. This base cost is only applied to the bulk transaction once, no matter how many transactions it fires internally. In fact, the more transactions you fire at once, the more gas you save per transaction. Second of all, . Suppose you sent the four transactions out as individual transactions. You must count on the fact that some of them may fail while the rest succeeds. sending out transactions in bulk gives us transactional safety This is chaotic. The bulk transaction enforces an all-or-nothing policy, meaning you can ensure that either all of your changes are applied or none of them are (and you can even modify any of them before a second try if you wish). Finally, . When designing a blockchain DApp one inevitably reaches the issue of having to bother the user with Metamask popups or any other means they use for blockchain communication. sending out transactions in bulk is more user-friendly This is inevitable for a true DApp that wishes to give the user full control. Bulk transactions allow the frontend of a DApp to “compose” and change the bulk transaction dynamically as the user interacts with the DApp and only ask the user to confirm their changes at once after the user is done making them. all Also before I wrap up, some of you may be wondering whether the Gatling Gun wallet can be used to deploy contracts. After all, regular wallets can so why not the Gatling Gun wallet? Well, you’re in luck, because it can! There is a function in each Gatling Gun wallet, which does exactly what you’d expect, but I really didn’t want to get into that in this article. deploy(bytes initCode, uint256 value) Conclusion This project and article are the result of my curiosity and desire to share interesting things with like-minded people out there. I hope you enjoyed the read. If this is something your project could make use of and you want to know more, check out the demonstration video on the . ETH Gatling Gun homepage If you would like to contribute to this project in any way, I have placed the contract as well as the website code here on . The website code is very minimalist and self-contained (does not import any external scripts or CSS). This means anyone can run it locally simply by cloning the repository. Bitbucket