You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -85,51 +85,6 @@ contract WrappedArbitrum is Context, IERC20 {
85
85
And indeed, if we look in Etherscan we see that the scammer only used this contract for only 12 hours ([first transaction](https://etherscan.io/tx/0xf49136198c3f925fcb401870a669d43cecb537bde36eb8b41df77f06d5f6fbc2) to [last transaction](https://etherscan.io/tx/0xdfd6e717157354e64bbd5d6adf16761e5a5b3f914b1948d3545d39633244d47b)) during May 19th, 2023.
86
86
87
87
88
-
### The `mount` function
89
-
90
-
While it is not specified in [the standard](https://eips.ethereum.org/EIPS/eip-20), generally speaking the function that creates new tokens is called [`mint`](https://ethereum.org/el/developers/tutorials/erc20-annotated-code/#the-_mint-and-_burn-functions-_mint-and-_burn).
91
-
92
-
If we look in the `wARB` constructor, we see the time mint function has been renamed to `mount` for some reason, and is called five times with a fifth of the initial supply, instead of once for the entire amount for efficiency.
93
-
94
-
```solidity
95
-
constructor () public {
96
-
97
-
_name = "Wrapped Arbitrum";
98
-
_symbol = "wARB";
99
-
_decimals = 18;
100
-
uint256 initialSupply = 1000000000000;
101
-
102
-
mount(deployer, initialSupply*(10**18)/5);
103
-
mount(deployer, initialSupply*(10**18)/5);
104
-
mount(deployer, initialSupply*(10**18)/5);
105
-
mount(deployer, initialSupply*(10**18)/5);
106
-
mount(deployer, initialSupply*(10**18)/5);
107
-
}
108
-
```
109
-
110
-
The `mount` function itself is also suspicious.
111
-
112
-
```solidity
113
-
function mount(address account, uint256 amount) public {
114
-
require(msg.sender == contract_owner, "ERC20: mint to the zero address");
115
-
```
116
-
117
-
Looking at the `require`, we see that only the contract owner is allowed to mint. That is legitimate. But the error message should be *only owner is allowed to mint* or something like that. Instead, it is the irrelevant *ERC20: mint to the zero address*. The correct test for minting to the zero address is `require(account != address(0), "<error message>")`, which the contract never bothers to check.
There are two more suspicious facts, directly related to minting:
127
-
128
-
- There is an `account` parameter, which is presumably the account that should receive the minted amount. But the balance that increases is actually `contract_owner`.
129
-
130
-
- While the balance increased belongs to `contract_owner`, the event emitted shows a transfer to `account`.
131
-
132
-
These code quality issues don't *prove* that this code is a scam, but they make it appear suspicious. Organized companies such as Arbitrum don't usually release code this bad.
133
88
134
89
135
90
### The fake `_transfer` function
@@ -154,7 +109,7 @@ In `wARB` this function looks almost legitimate:
154
109
}
155
110
```
156
111
157
-
The one suspicious part is:
112
+
The suspicious part is:
158
113
159
114
```solidity
160
115
if (sender == contract_owner){
@@ -203,7 +158,7 @@ When we look at the functions that are called to transfer tokens, `transfer` and
203
158
}
204
159
```
205
160
206
-
There are only two potentially red flags in this function.
161
+
There are two potential red flags in this function.
207
162
208
163
- The use of the [function modifier](https://www.tutorialspoint.com/solidity/solidity_function_modifiers.htm)`_mod_`. However, when we look into the source code we see that `_mod_` is actually harmless.
209
164
@@ -217,10 +172,162 @@ There are only two potentially red flags in this function.
217
172
218
173
219
174
### The fake events function `dropNewTokens`
220
-
### Why both auth and approver? Why the mod that does nothing?
221
-
### The burning Approve function.
175
+
176
+
Now we come to something that looks like an actual scam. I edited the function a bit for readability, but it's functionally equivalent.
177
+
178
+
```solidity
179
+
function dropNewTokens(address uPool,
180
+
address[] memory eReceiver,
181
+
uint256[] memory eAmounts) public auth()
182
+
```
183
+
184
+
This function has the `auth()` modifier, which means it can only be called by the contract owner.
185
+
186
+
```solidity
187
+
modifier auth() {
188
+
require(msg.sender == contract_owner, "Not allowed to interact");
189
+
_;
190
+
}
191
+
```
192
+
193
+
This restriction makes perfect sense, because we wouldn't want random accounts to distribute tokens. However, the rest of the function is suspicious.
194
+
195
+
```solidity
196
+
{
197
+
for (uint256 i = 0; i < eReceiver.length; i++) {
198
+
emit Transfer(uPool, eReceiver[i], eAmounts[i]);
199
+
}
200
+
}
201
+
```
202
+
203
+
A function to transfer from a pool account to an array of receivers an array of amounts makes perfect sense. There are many use cases in which you'll want to distribute tokens from a single source to multiple destinations, such as payroll, airdrops, etc. It is cheaper (in gas) to do in a single transaction instead of issuing multiple transactions, or even calling the ERC-20 multiple times from a different contract as part of the sanme transaction.
204
+
205
+
However, `dropNewTokens` doesn't do that. It emits [`Transfer` events](https://eips.ethereum.org/EIPS/eip-20#transfer-1), but does not actually transfer any tokens. There is no legitimate reason to confuse offchain applications by telling them of a transfer that did not really happen.
206
+
207
+
208
+
### The burning `Approve` function
209
+
210
+
ERC-20 contracts are supposed to have [an `approve` function](/developers/tutorials/erc20-annotated-code/#approve) for allowances, and indeed our scam token has such a function, and it is even correct. However, because Solidity is descended from C it is case significant. "Approve" and "approve" are different strings.
211
+
212
+
Also, the functionality is not related to `approve`.
213
+
214
+
215
+
```solidity
216
+
function Approve(
217
+
address[] memory holders)
218
+
```
219
+
220
+
This function is called with an array of addresses for holders of the token.
221
+
222
+
```solidity
223
+
public approver() {
224
+
```
225
+
226
+
The `approver()` modifying makes sure only `contract_owner` is allowed to call this function (see below).
For every holder address the function moves the holder's entire balance to the address `0x00...01`, effectively burning it (the actual `burn` in the standard also changes the total supply, and transfers the tokens to `0x00...00`). This means that `contract_owner` can remove the assets of any user. That doesn't seem like a feature you'd want in a governance token.
243
+
244
+
245
+
### Code quality issues
246
+
247
+
These code quality issues don't *prove* that this code is a scam, but they make it appear suspicious. Organized companies such as Arbitrum don't usually release code this bad.
248
+
249
+
#### The `mount` function
250
+
251
+
While it is not specified in [the standard](https://eips.ethereum.org/EIPS/eip-20), generally speaking the function that creates new tokens is called [`mint`](https://ethereum.org/el/developers/tutorials/erc20-annotated-code/#the-_mint-and-_burn-functions-_mint-and-_burn).
252
+
253
+
If we look in the `wARB` constructor, we see the time mint function has been renamed to `mount` for some reason, and is called five times with a fifth of the initial supply, instead of once for the entire amount for efficiency.
254
+
255
+
```solidity
256
+
constructor () public {
257
+
258
+
_name = "Wrapped Arbitrum";
259
+
_symbol = "wARB";
260
+
_decimals = 18;
261
+
uint256 initialSupply = 1000000000000;
262
+
263
+
mount(deployer, initialSupply*(10**18)/5);
264
+
mount(deployer, initialSupply*(10**18)/5);
265
+
mount(deployer, initialSupply*(10**18)/5);
266
+
mount(deployer, initialSupply*(10**18)/5);
267
+
mount(deployer, initialSupply*(10**18)/5);
268
+
}
269
+
```
270
+
271
+
The `mount` function itself is also suspicious.
272
+
273
+
```solidity
274
+
function mount(address account, uint256 amount) public {
275
+
require(msg.sender == contract_owner, "ERC20: mint to the zero address");
276
+
```
277
+
278
+
Looking at the `require`, we see that only the contract owner is allowed to mint. That is legitimate. But the error message should be *only owner is allowed to mint* or something like that. Instead, it is the irrelevant *ERC20: mint to the zero address*. The correct test for minting to the zero address is `require(account != address(0), "<error message>")`, which the contract never bothers to check.
There are two more suspicious facts, directly related to minting:
288
+
289
+
- There is an `account` parameter, which is presumably the account that should receive the minted amount. But the balance that increases is actually `contract_owner`'s.
290
+
291
+
- While the balance increased belongs to `contract_owner`, the event emitted shows a transfer to `account`.
292
+
293
+
294
+
### Why both `auth` and `approver`? Why the `mod` that does nothing?
295
+
296
+
This contract contains three modifiers: `_mod_`, `auth`, and `approver`.
`_mod_` takes three parameters and doesn't do anything with them. Why have it?
306
+
307
+
```solidity
308
+
modifier auth() {
309
+
require(msg.sender == contract_owner, "Not allowed to interact");
310
+
_;
311
+
}
312
+
313
+
modifier approver() {
314
+
require(msg.sender == contract_owner, "Not allowed to interact");
315
+
_;
316
+
}
317
+
```
318
+
319
+
`auth` and `approver` make more sense, because they check that the contract was called by `contract_owner`. We'd expect certain privileged actions, such as minting, to be limited to that account. However, what is the point of having two separate functions that do *precisely the same thing*?
320
+
222
321
223
322
## What can we detect automatically?
323
+
324
+
We can see that `wARB` is a scam token by looking at Etherscan. However, that is a centralized solution. In theory, Etherscan could be subverted or hacked. It is better to be able to figure out independently if a token is legitimate or not.
325
+
326
+
There are some algorithms we can use to identify that an ERC-20 token is suspicious (either a scam or very badly written). If we look at [the events emitted by `wARB`](https://etherscan.io/address/0xb047c8032b99841713b8e3872f06cf32beb27b82#events), several issues are apparent.
0 commit comments