Abstract
An investor lost a significant amount of Ether during the failed attempt to purchase AirSwap tokens during its ICO, a decentralized cryptocurrency exchange that started their public crowdsale on Tuesday. The Investor was trying to buy $508,000 (1,700 Ether) worth of AirSwap tokens, but the trade failed and the investor lost $70,000 (236.9516 Ether) in form of Ethereum transaction fees.
Background
AirSwap is a decentralized token exchange based on the Swap protocol whitepaper. Swap provides a decentralized trading solution based on a peer-to-peer design. The design solves two problems encountered in a peer-to-peer trading environment: counter-party discovery and pricing suggestions.
The token sale happened between 10 October 2017 10:10:10 AM Eastern Time and 11 October 2017 at 9:10:10 AM Eastern Time. The token sale was whitelisted and shortlisted investors were entitled to allocation of the individual cap (3.3 ETH) during the entire 23 hours of the main sale. The whitelist registration process happened between October 4, 2017 at 10:00AM Eastern Time and October 6, 2017 at 4:00PM Eastern Time. During the whitelist registration there were more than 18,000 submissions of which 12,719 were successfully whitelisted for the sale.
AirSwap was running the sale through their own platform https://launch.airswap.io/ to avoid Ethereum network congestion, high gas fees, and to put their protocol and smart contracts to work. It is a web application that connects to the Ethereum network. MetaMask and Parity browser extensions were required for the AirSwap sale.
The Trade
The investor, only known at this point by their Ethereum blockchain address 0xf51ec864d5fb2f184198e369fe063fc77045a3ad, was trying to buy about $508,000 worth of AirSwap tokens or 1,700 ether according to the transaction history. As Etherscan explains, although the the trade failed/cancelled, the investor still lost $70,000 (236.9516 Ether) in form of Ethereum gas (transaction fees). The error during the transaction was, “Error encountered during contract execution [Bad instruction]”.
In a “Cancelled” transaction, the Ether value was not deducted from the sending address. However the Transactions fees (Gas Used x Gas Price) will still be deducted from the sending account (From Address).
[Bad instruction] warning indicates a logical error during contract execution, if interacting with a TokenSale/ICO Contract, its possible that the:
– ICO has yet to start
– ICO has ended
– ICO has reached its maximum contribution limit
The date of the transaction is “Oct-11-2017 02:24:23 PM +UTC” (Oct-11-2017 9:24:23 AM Eastern Time) but the ICO end time was approximately 11 October 2017 at 9:10:10 AM Eastern Time. There was also a L(AST) Chance Sale on Oct 11 2017 that started on 10:10:10 AM Eastern Time to 12:12:12 PM Eastern Time.
In an ugly twist of fate the user’s transaction seemed in between these sale periods which may have resulted in his transactions being cancelled. It appears that the unfortunate participant was targeting the L(AST) Chance Sale, which was an uncapped sale running on the AirSwap token launcher platform. The total amount for the sale was dependent on the number of tokens that weren’t sold in the main sale on October 10th or beta tester sale on October 9th. During the main sale 30M AST were sold to 9,400 individual buyers. The investor was likely targeting some of the remaining tokens but was early to the sale.
The transaction possibly reverted because the transaction wasn’t yet valid. So while the investor wanted tokens from the contract, the contract wasn’t yet ready to transfer them because there is a hard-lock making them non-transferable until the 17th. On top of that, it also looks like the user may have been trying to transfer too many tokens at once.
Technical Analysis
The core of this issue resides not in either of the contracts, but instead, in the Ethereum VM (EVM) itself. This bloomberg article alludes to the fact that investors “do forfeit the amount of ether necessary to make the deal possible”. This is technically correct, as this is more commonly known as the gas cost of the execution for the contract within the community. The end-user has two options in most cases when they’re working with a send to a contract, which is the maximum gas that they are willing to spend, as well as the value of each unit of gas, in ETH that is to be spent. It’s very common to raise the value of the gas you’re willing to spend in order to prioritize your transfers.
In this particular instance, the gas-usage was extremely high. This was because the user had set their gas price to approximately 4000 times the typical amount. Other payments in their block were approximately 0.0000001 ETH per gas, but the user had it set to .0004. There was no bug in the contracts, just the behavior doesn’t work /quite/ as expected due to the way that everything fits and slides together. If the REVERT opcode had acted properly, the contract would have returned back > 90% of the gas, making this still an expensive issue at $7,000 spent for a failed transaction, however it would have saved the user $63,000.
Importance of REVERT opcode
Now referring to this question on stackexchange in an Ethereum smart contract there are two functions of interest: assert() and require().
assert(false) compiles to 0xfe, which is an invalid opcode, using up all remaining gas, and reverting all changes.
require(false) compiles to (0xfd), which is currently invalid, but after Metropolis will become REVERT, meaning it will refund the remaining gas, and return a value (useful for debugging).
Prior to the Metropolis fork, the require() and assert() functions actually behave identically, but they already compile to different opcodes when the condition is false. This means that your contracts will behave very differently after Metropolis depending on which one you use.
If one reads their solidity contract
function availableBalance(address _owner) constant returns(uint256) {
if (balanceLocks[_owner].unlockDate < now) {
return balances[_owner];
} else {
assert(balances[_owner] >= balanceLocks[_owner].amount);
return balances[_owner] - balanceLocks[_owner].amount;
}
}
One can see that in function availableBalance there is the statement assert(balances[_owner] >= balanceLocks[_owner].amount) which is an invalid opcode and could use up all remaining gas and revert all changes. This is one of the two possible locations that used up all the gas in the user’s transaction.
As it stands, right now, the Ethereum VM doesn’t formally support the REVERT opcode, only as of 28 days ago did the actual REVERT opcode (0xfd) become placed inside of the EVM source code. This changes with the Metropolis (Byzantium hardfork) release, version 1.7.1, block number 4,370,000 (Nominally Oct. 17). At that time it will become active as part of the hard-fork. Right now, the REVERT opcode throws an exception, which is expected, because it’s not formally supported. Due to this, as the actual functional code that supports REVERT isn’t active yet, it means that the gas, was fully consumed.
The reason that the consumption was so high, was due to the fact that the gas was transferred via an OPCODE CALL, which is normal in these cases, forwarding along the bulk of the available gas to ensure the sub-function can execute. Internally, this other contract did also use a REVERT code, but as it is not live it simply threw an exception rather than returning the gas. Once the hard fork happens, the REVERT opcode will become live, and that won’t be an issue any longer. So this error goes away in ~1 week. Many contracts reviewed by Hosho and other auditing companies have moved to using the revert() call which does use the upcoming REVERT opcode, to prepare for this, allowing the unused gas to be returned back, and ensuring that these are less costly.
How to setup Gas during ICO
Ether (ETH) is the fuel for that Ethereum network. when you send tokens, interact with a contract, send ETH, or do anything else on the blockchain, you must pay for that computation. That payment is calculated in Gas and gas is paid in ETH. You are paying for the computation, regardless of whether your transaction succeeds or fails. Even if it fails, the miners must validate and execute your transaction (compute) and therefore you must pay for that computation just like you would pay for a successful transaction. You can see your TX fee (gas limit * gas price) in ETH & USD when you search for your transaction on etherscan.io. The gas limit is called the limit because it’s the maximum units of gas you are
willing to spend on a transaction. However, the units of gas necessary for a transaction are already defined by how much code is executed on the blockchain.
It is very important to understand how gas works in order to make judicious decision while submitting a transaction to a Smart contract. If you are out of gas during a transaction, the transaction will be unable to complete. If you have extra gas during a transaction all unused gas is refunded to you at the end. If you send 1 ETH and use a gas limit of 400,000 you will receive 400,000 – 21,000* back. However, if you were sending 1 ETH to a contract and your transaction to the contract fails (say, the Token Creation Period is already over), you will use the entire 400,000 and receive nothing back. You can use our tool to calculate GWEI <-> WEI <-> USD here, which can be helpful when you want to know your TX fee in ETH, rather than GWEI. You can do so by lowering the amount you pay per unit of gas. The price you pay for each unit increases or decreases how quickly your transaction will be mined.
During Token Creation Periods, these costs go crazy due to supply / demand. 50 GWEI is the max gas price most new Token Creation Period contracts will accept. Anything above that and your TX will fail. Check with the Token Creation Period if you wish to invest in, ideally 50 GWEI would be the amount you should send in that case.
Finally
While there was noting wrong with the smart contract developed by Airswap, the whole 2 Hour window created a favorable condition for token grab between investors, but did cost few investors dearly. Responsible companies should make strides to avoid such a situation occurring during their ICO. Burning $70,000 (236.9516 Ether) as gas is not a small thing and this is something not only the investors but also the ICO team should have taken into consideration. It is time that the whole Ethereum ecosystem as a whole address the issue of exorbitant Gas prices and help make Ethereum transactions more safe and rational.
Reference
- https://www.bloomberg.com/news/articles/2017-10-11/buyer-beware-as-70-000-goes-up-in-smoke-on-broken-ico-trade
- https://github.com/airswap/contracts/blob/master/contracts/AirSwapToken.sol
- https://myetherwallet.github.io/knowledge-base/gas/what-is-gas-ethereum.html
- https://etherscan.io/tx/0x167b6e3217536e66e69f906a457b2457c6cc4f95928a47b0443ad895b23c6e76
- https://ethereum.stackexchange.com/questions/15166/difference-between-require-and-assert-and-the-difference-between-revert-and-thro/24185#24185
- https://etherscanio.freshdesk.com/support/solutions/articles/16000062876-my-transaction-had-been-cancelled-error-is-out-of-gas-or-bad-instructions-when-will-the-funds-r)
- https://blog.airswap.io/airswap-token-buyers-guide-14916228d516