|
| 1 | +#!/usr/bin/env node |
| 2 | + |
| 3 | +const Web3 = require("web3"); |
| 4 | +const fs = require("fs"); |
| 5 | +const path = require('path'); |
| 6 | +const RLP = require('rlp'); |
| 7 | +const BigNumber = require('bignumber.js') |
| 8 | + |
| 9 | +process.on('unhandledRejection', console.error.bind(console)) |
| 10 | + |
| 11 | +const { configPath, gasPriceGwei, printPrivateKey, rpcUrl, signedTxOutput, dontSendTx, networkAddress, chainId: chainIdInput } = require('yargs') |
| 12 | + .usage('Usage: $0 --config-path [path] --gas-price-gwei [gwei] --print-private-key [bool] --rpc-url [url] --signed-tx-output [path] --dont-send-tx [bool] --network-address [address] --chain-id') |
| 13 | + .demandOption(['gasPriceGwei', 'rpcUrl']) |
| 14 | + .boolean('printPrivateKey') |
| 15 | + .boolean('dontSendTx') |
| 16 | + .argv; |
| 17 | +const web3 = new Web3(new Web3.providers.HttpProvider(rpcUrl)); |
| 18 | +const solc = require('solc') |
| 19 | + |
| 20 | +const rand = web3.utils.randomHex(7); |
| 21 | +const privateKey = web3.utils.sha3("js sucks" + rand); |
| 22 | +//console.log("privateKey", privateKey); |
| 23 | + |
| 24 | +if (printPrivateKey) { |
| 25 | + console.log("privateKey", privateKey); |
| 26 | + let path = "privatekey_" + web3.utils.randomHex(7) + ".txt"; |
| 27 | + fs.writeFileSync(path, privateKey, function(err) { |
| 28 | + if(err) { |
| 29 | + return console.log(err); |
| 30 | + } |
| 31 | + }); |
| 32 | +} |
| 33 | +const account = web3.eth.accounts.privateKeyToAccount(privateKey); |
| 34 | +const sender = account.address; |
| 35 | +const gasPrice = BigNumber(gasPriceGwei).mul(10 ** 9); |
| 36 | +const signedTxs = []; |
| 37 | +let nonce; |
| 38 | +let chainId = chainIdInput; |
| 39 | + |
| 40 | +console.log("from",sender); |
| 41 | + |
| 42 | +async function sendTx(txObject) { |
| 43 | + const txTo = txObject._parent.options.address; |
| 44 | + |
| 45 | + let gasLimit; |
| 46 | + try { |
| 47 | + gasLimit = await txObject.estimateGas(); |
| 48 | + } |
| 49 | + catch (e) { |
| 50 | + gasLimit = 500 * 1000; |
| 51 | + } |
| 52 | + |
| 53 | + if(txTo !== null) { |
| 54 | + gasLimit = 500 * 1000; |
| 55 | + } |
| 56 | + |
| 57 | + gasLimit *= 1.2; |
| 58 | + gasLimit -= gasLimit % 1; |
| 59 | + //console.log(gasLimit); |
| 60 | + const txData = txObject.encodeABI(); |
| 61 | + const txFrom = account.address; |
| 62 | + const txKey = account.privateKey; |
| 63 | + |
| 64 | + const tx = { |
| 65 | + from : txFrom, |
| 66 | + to : txTo, |
| 67 | + nonce : nonce, |
| 68 | + data : txData, |
| 69 | + gas : gasLimit, |
| 70 | + chainId, |
| 71 | + gasPrice |
| 72 | + }; |
| 73 | + |
| 74 | + const signedTx = await web3.eth.accounts.signTransaction(tx, txKey); |
| 75 | + nonce++; |
| 76 | + // don't wait for confirmation |
| 77 | + signedTxs.push(signedTx.rawTransaction) |
| 78 | + if (!dontSendTx) { |
| 79 | + web3.eth.sendSignedTransaction(signedTx.rawTransaction, {from:sender}); |
| 80 | + } |
| 81 | +} |
| 82 | + |
| 83 | +async function deployContract(solcOutput, contractName, ctorArgs) { |
| 84 | + |
| 85 | + const actualName = contractName; |
| 86 | + const bytecode = solcOutput.contracts[actualName].bytecode; |
| 87 | + |
| 88 | + const abi = solcOutput.contracts[actualName].interface; |
| 89 | + const myContract = new web3.eth.Contract(JSON.parse(abi)); |
| 90 | + const deploy = myContract.deploy({data:"0x" + bytecode, arguments: ctorArgs}); |
| 91 | + let address = "0x" + web3.utils.sha3(RLP.encode([sender,nonce])).slice(12).substring(14); |
| 92 | + address = web3.utils.toChecksumAddress(address); |
| 93 | + |
| 94 | + await sendTx(deploy); |
| 95 | + |
| 96 | + myContract.options.address = address; |
| 97 | + |
| 98 | + |
| 99 | + return [address,myContract]; |
| 100 | +} |
| 101 | + |
| 102 | +const contractPath = path.join(__dirname, "../contracts/"); |
| 103 | + |
| 104 | +let reserveAddress; |
| 105 | +let conversionRatesAddress; |
| 106 | + |
| 107 | +let reserveContract; |
| 108 | +let conversionRatesContract; |
| 109 | + |
| 110 | +let reservePermissions; |
| 111 | +let conversionRatesPermissions; |
| 112 | + |
| 113 | +const depositAddresses = []; |
| 114 | +let validDurationBlock = 24; |
| 115 | +let taxWalletAddress = 0x0; |
| 116 | +let taxFeesBps = 1000; |
| 117 | + |
| 118 | +const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; |
| 119 | + |
| 120 | +const tokens = []; |
| 121 | +const tokenControlInfo = {}; |
| 122 | +const tokenNameToAddress = { "ETH" : ethAddress }; |
| 123 | + |
| 124 | + |
| 125 | +function parseInput( jsonInput ) { |
| 126 | + // tokens |
| 127 | + const tokenInfo = jsonInput["tokens"]; |
| 128 | + Object.keys(tokenInfo).forEach(function(key) { |
| 129 | + const val = tokenInfo[key]; |
| 130 | + const symbol = key; |
| 131 | + const name = val["name"]; |
| 132 | + const address = val["address"]; |
| 133 | + |
| 134 | + tokenNameToAddress[symbol] = address; |
| 135 | + |
| 136 | + tokens.push(address); |
| 137 | + const dict = { |
| 138 | + minimalRecordResolution : val["minimalRecordResolution"], |
| 139 | + maxPerBlockImbalance : val["maxPerBlockImbalance"], |
| 140 | + maxTotalImbalance : val["maxTotalImbalance"] |
| 141 | + }; |
| 142 | + tokenControlInfo[address] = dict; |
| 143 | + }); |
| 144 | + |
| 145 | + // exchanges |
| 146 | + const exchangeInfo = jsonInput["exchanges"]; |
| 147 | + Object.keys(exchangeInfo).forEach(function(exchange) { |
| 148 | + Object.keys(exchangeInfo[exchange]).forEach(function(token){ |
| 149 | + const depositAddress = exchangeInfo[exchange][token]; |
| 150 | + const dict = {}; |
| 151 | + dict[token] = depositAddress; |
| 152 | + depositAddresses.push(dict); |
| 153 | + }); |
| 154 | + }); |
| 155 | + |
| 156 | + reservePermissions = jsonInput.permission["KyberReserve"]; |
| 157 | + conversionRatesPermissions = jsonInput.permission["ConversionRates"]; |
| 158 | + validDurationBlock = jsonInput["valid duration block"]; |
| 159 | + |
| 160 | + // output file name |
| 161 | + outputFileName = jsonInput["output filename"]; |
| 162 | +}; |
| 163 | + |
| 164 | +async function setPermissions(contract, alerters, operators, admin) { |
| 165 | + console.log("set operator(s) " + contract.address); |
| 166 | + for(let i = 0 ; i < operators.length ; i++ ) { |
| 167 | + const operator = operators[i]; |
| 168 | + console.log(operator); |
| 169 | + await sendTx(contract.methods.addOperator(operator)); |
| 170 | + } |
| 171 | + console.log("set alerter(s)"); |
| 172 | + for(let i = 0 ; i < alerters.length ; i++ ) { |
| 173 | + const alerter = alerters[i]; |
| 174 | + console.log(alerter); |
| 175 | + await sendTx(contract.methods.addAlerter(alerter)); |
| 176 | + } |
| 177 | + |
| 178 | + console.log("transferAdminQuickly"); |
| 179 | + console.log(admin); |
| 180 | + await sendTx(contract.methods.transferAdminQuickly(admin)); |
| 181 | +} |
| 182 | + |
| 183 | + |
| 184 | +async function main() { |
| 185 | + nonce = await web3.eth.getTransactionCount(sender); |
| 186 | + console.log("nonce",nonce); |
| 187 | + |
| 188 | + chainId = chainId || await web3.eth.net.getId() |
| 189 | + console.log('chainId', chainId); |
| 190 | + console.log('starting compilation'); |
| 191 | + output = await require("./compileContracts.js").compileContracts(); |
| 192 | + console.log("finished compilation"); |
| 193 | + |
| 194 | + if (!dontSendTx) { |
| 195 | + await waitForEth(); |
| 196 | + } |
| 197 | + |
| 198 | + let networkAddress = '0x65bF64Ff5f51272f729BDcD7AcFB00677ced86Cd'; |
| 199 | + console.log('network address: ', networkAddress); |
| 200 | + |
| 201 | + console.log("deploying EnhancedStepFunctions"); |
| 202 | + [conversionRatesAddress,conversionRatesContract] = await deployContract(output, "EnhancedStepFunctions.sol:EnhancedStepFunctions", [sender]); |
| 203 | + console.log("enhanced steps fpr pricing contract", conversionRatesAddress); |
| 204 | + |
| 205 | + console.log("deploying kyber reserve"); |
| 206 | + [reserveAddress,reserveContract] = await deployContract(output, "KyberReserve.sol:KyberReserve", [networkAddress,conversionRatesAddress,sender]); |
| 207 | + |
| 208 | + console.log("reserve", reserveAddress); |
| 209 | + |
| 210 | + console.log("deploying enhanced step functions wrapper"); |
| 211 | + [wrapperAddress, wrapperContract] = await deployContract(output, "WrapConversionRate.sol:WrapConversionRate", [conversionRatesAddress]); |
| 212 | + |
| 213 | + console.log("wrapperAddress", wrapperAddress); |
| 214 | + |
| 215 | + operators = ['0xF76d38Da26c0c0a4ce8344370D7Ae4c34B031dea','0xf3d872b9e8d314820dc8e99dafbe1a3feedc27d5'] |
| 216 | + admin = '0xf3d872b9e8d314820dc8e99dafbe1a3feedc27d5'; |
| 217 | + |
| 218 | + await setPermissions(reserveContract, operators, operators, admin); |
| 219 | + await setPermissions(conversionRatesContract, operators, operators, admin); |
| 220 | + await setPermissions(wrapperContract, operators, operators, admin); |
| 221 | + |
| 222 | + console.log("done for now...") |
| 223 | +return; |
| 224 | + |
| 225 | + // conversion rates |
| 226 | + console.log("conversion rate - add token"); |
| 227 | + for( let i = 0 ; i < tokens.length ; i++ ) { |
| 228 | + console.log(tokens[i]); |
| 229 | + await sendTx(conversionRatesContract.methods.addToken(tokens[i])); |
| 230 | + } |
| 231 | + |
| 232 | + console.log("conversion rate - set valid duration block"); |
| 233 | + await sendTx(conversionRatesContract.methods.setValidRateDurationInBlocks(validDurationBlock)); |
| 234 | + console.log("conversion rate - setReserveAddress"); |
| 235 | + await sendTx(conversionRatesContract.methods.setReserveAddress(reserveAddress)); |
| 236 | + |
| 237 | + console.log("conversion rate - set control info"); |
| 238 | + for( let i = 0 ; i < tokens.length ; i++ ) { |
| 239 | + console.log(tokens[i]); |
| 240 | + const dict = tokenControlInfo[tokens[i]]; |
| 241 | + await sendTx(conversionRatesContract.methods.setTokenControlInfo(tokens[i], |
| 242 | + dict.minimalRecordResolution, |
| 243 | + dict.maxPerBlockImbalance, |
| 244 | + dict.maxTotalImbalance)); |
| 245 | + } |
| 246 | + |
| 247 | + console.log("conversion rate - enable token trade"); |
| 248 | + for( let i = 0 ; i < tokens.length ; i++ ) { |
| 249 | + console.log(tokens[i]); |
| 250 | + const dict = tokenControlInfo[tokens[i]]; |
| 251 | + await sendTx(conversionRatesContract.methods.enableTokenTrade(tokens[i])); |
| 252 | + } |
| 253 | + |
| 254 | + console.log("conversion rate - add temp operator"); |
| 255 | + await sendTx(conversionRatesContract.methods.addOperator(sender)); |
| 256 | + console.log("conversion rate - set qty step function to 0"); |
| 257 | + for( let i = 0 ; i < tokens.length ; i++ ) { |
| 258 | + console.log(tokens[i]); |
| 259 | + await sendTx(conversionRatesContract.methods.setQtyStepFunction(tokens[i], |
| 260 | + [0], |
| 261 | + [0], |
| 262 | + [0], |
| 263 | + [0])); |
| 264 | + } |
| 265 | + |
| 266 | + console.log("conversion rate - set imbalance step function to 0"); |
| 267 | + for( let i = 0 ; i < tokens.length ; i++ ) { |
| 268 | + console.log(tokens[i]); |
| 269 | + await sendTx(conversionRatesContract.methods.setImbalanceStepFunction(tokens[i], |
| 270 | + [0], |
| 271 | + [0], |
| 272 | + [0], |
| 273 | + [0])); |
| 274 | + } |
| 275 | + |
| 276 | + console.log("conversion rate - remove temp operator"); |
| 277 | + await sendTx(conversionRatesContract.methods.removeOperator(sender)); |
| 278 | + |
| 279 | + await setPermissions(conversionRatesContract, conversionRatesPermissions); |
| 280 | + |
| 281 | + console.log("last nonce is", nonce); |
| 282 | + |
| 283 | + if (signedTxOutput) { |
| 284 | + fs.writeFileSync(signedTxOutput, signedTxsJson); |
| 285 | + } |
| 286 | +} |
| 287 | + |
| 288 | +function printParams(jsonInput) { |
| 289 | + dictOutput = {}; |
| 290 | + dictOutput["tokens"] = jsonInput.tokens; |
| 291 | + dictOutput["tokens"]["ETH"] = {"name" : "Ethereum", "decimals" : 18, "address" : ethAddress }; |
| 292 | + dictOutput["exchanges"] = jsonInput.exchanges; |
| 293 | + dictOutput["permission"] = jsonInput.permission; |
| 294 | + dictOutput["valid duration block"] = jsonInput["valid duration block"]; |
| 295 | + dictOutput["reserve"] = reserveAddress; |
| 296 | + dictOutput["pricing"] = conversionRatesAddress; |
| 297 | + dictOutput["network"] = networkAddress; |
| 298 | + const json = JSON.stringify(dictOutput, null, 2); |
| 299 | + console.log(json); |
| 300 | + const outputFileName = jsonInput["output filename"]; |
| 301 | + console.log(outputFileName, 'write'); |
| 302 | + fs.writeFileSync(outputFileName, json); |
| 303 | +} |
| 304 | + |
| 305 | + |
| 306 | +function sleep(ms){ |
| 307 | + return new Promise(resolve=>{ |
| 308 | + setTimeout(resolve,ms) |
| 309 | + }) |
| 310 | +} |
| 311 | + |
| 312 | +async function waitForEth() { |
| 313 | + while(true) { |
| 314 | + const balance = await web3.eth.getBalance(sender); |
| 315 | + console.log("waiting for balance to account " + sender); |
| 316 | + if(balance.toString() !== "0") { |
| 317 | + console.log("received " + balance.toString() + " wei"); |
| 318 | + return; |
| 319 | + } |
| 320 | + else await sleep(10000) |
| 321 | + } |
| 322 | +} |
| 323 | + |
| 324 | + |
| 325 | +let filename; |
| 326 | +let content; |
| 327 | + |
| 328 | +main(); |
0 commit comments