Merge pull request #13 from status-im/wallet-creation-script
Wallet creation script
This commit is contained in:
commit
31392d373f
|
@ -0,0 +1,2 @@
|
|||
node_modules
|
||||
package-lock.json
|
|
@ -0,0 +1,44 @@
|
|||
## Wallet Creation Script
|
||||
|
||||
This script is an easy way to create KeycardWallet instances for the payment network. Wallets can be created in batches.
|
||||
|
||||
## Installation
|
||||
|
||||
`npm install`
|
||||
|
||||
## Usage
|
||||
|
||||
You launch the script with either
|
||||
|
||||
`node -r esm index.js <options>`
|
||||
|
||||
or
|
||||
|
||||
`npm run wallet-creation -- <options>` (the -- is required so all other options are sent to the script and not to npm)
|
||||
|
||||
The only required options are
|
||||
|
||||
`--registry 0x...`: the address of the KeycardWalletFactory
|
||||
|
||||
and then either
|
||||
|
||||
`--keycard 0x...`: the address of the Keycard to associate to the wallet
|
||||
|
||||
or
|
||||
|
||||
`--file path`: the path to a file containing a list of Keycard addresses, one per line
|
||||
|
||||
Other options are
|
||||
`--endpoint`: the address of the RPC endpoint. The client as an empty origin so make sure to start the Ethereum node as needed. For ws, you can just start it with --wsorigins="*". The default value is ws://127.0.0.1:8546.
|
||||
|
||||
`--sender`: the address signing and sending the transactions. This account will pay for gas. **THIS ACCOUNT MUST BE ALREADY UNLOCKED**. If not specified, accounts[0] is used. Also in this case, the account must be unlocked.
|
||||
|
||||
`--maxTxValue`: the maxTxValue for payment transaction. This can be changed later but providing a meaningful value on creation can be convenient. Defaults to 100000000.
|
||||
|
||||
`--minBlockDistance`: how many blocks must elapse between two consecutive payments. This solves the possible attack of having the Keycard sign several transactions at once. The higher the value, the more time must pass between transactions. This can be changed later too. Defaults to 5.
|
||||
|
||||
`--out path`: the path to the output file to use. The output is a list containing the keycard addresses and their respective wallet address. The two addresses are separated by a comma and are stored one record per line. If there was an error during the creation of a wallet null will be printed as the wallet address. If not specified, the output will be printed on console.
|
||||
|
||||
## Notes
|
||||
|
||||
Creating the wallets is very fast, but printing the output files must wait for receipts. Depending on the network this might take a lot of time. You can kill the script after confirming that the tx are fine on Etherscan and you are not interested in the output.
|
|
@ -0,0 +1,70 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import Web3 from 'web3';
|
||||
import parseArgs from 'minimist';
|
||||
import fs from 'fs';
|
||||
|
||||
const factoryABI = [{"constant":false,"inputs":[{"name":"_wallet","type":"address"},{"name":"_keycard","type":"address"}],"name":"unregisterFromOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x25a6e9a1"},{"constant":false,"inputs":[{"name":"_oldOwner","type":"address"},{"name":"_newOwner","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x299a7bcc"},{"constant":false,"inputs":[{"name":"_oldKeycard","type":"address"},{"name":"_newKeycard","type":"address"}],"name":"setKeycard","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x4349c8bc"},{"constant":false,"inputs":[{"name":"_owner","type":"address"},{"name":"_keycard","type":"address"}],"name":"unregister","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x4a45b60b"},{"constant":false,"inputs":[{"name":"keycard","type":"address"},{"components":[{"name":"maxTxValue","type":"uint256"},{"name":"minBlockDistance","type":"uint256"}],"name":"settings","type":"tuple"},{"name":"keycardIsOwner","type":"bool"}],"name":"create","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x5213737f"},{"constant":true,"inputs":[{"name":"owner","type":"address"}],"name":"countWalletsForOwner","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x624de70e"},{"constant":false,"inputs":[{"name":"_owner","type":"address"},{"name":"_keycard","type":"address"}],"name":"register","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xaa677354"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"keycardsWallets","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xcf7661b9"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"uint256"}],"name":"ownersWallets","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xde59dda1"},{"anonymous":false,"inputs":[{"indexed":false,"name":"wallet","type":"address"}],"name":"NewWallet","type":"event","signature":"0xd627a1aeb13261b560c345aaf7d003d55a27193b9284c0b941f53cd62a045f16"}];
|
||||
const argv = parseArgs(process.argv.slice(2), {string: ["registry", "keycard"], default: {"endpoint": "ws://127.0.0.1:8546", "maxTxValue": 100000000, "minBlockDistance": 5}});
|
||||
|
||||
const web3 = new Web3(argv["endpoint"]);
|
||||
const KeycardWalletFactory = new web3.eth.Contract(factoryABI, argv["registry"]);
|
||||
const STDOUT = 1;
|
||||
|
||||
async function getDefaultSender() {
|
||||
let accounts = await web3.eth.getAccounts();
|
||||
return accounts[0];
|
||||
}
|
||||
|
||||
async function createWallet(sender, keycard, maxTxValue, minBlockDistance) {
|
||||
let methodCall = KeycardWalletFactory.methods.create(keycard.toLowerCase(), {maxTxValue: maxTxValue, minBlockDistance: minBlockDistance}, true);
|
||||
|
||||
try {
|
||||
let gasAmount = await methodCall.estimateGas({from: sender})
|
||||
let receipt = await methodCall.send({from: sender, gas: gasAmount});
|
||||
const event = receipt.events.NewWallet;
|
||||
return event.returnValues.wallet;
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function run() {
|
||||
KeycardWalletFactory.transactionConfirmationBlocks = 3;
|
||||
let sender = argv["sender"] || await getDefaultSender();
|
||||
|
||||
let keycards;
|
||||
|
||||
if (argv["file"]) {
|
||||
let file = fs.readFileSync(argv["file"], 'utf8');
|
||||
keycards = file.split("\n").map((addr) => addr.trim());
|
||||
} else if (argv["keycard"]) {
|
||||
keycards = [argv["keycard"]]
|
||||
} else {
|
||||
console.error("either the --file or the --keycard option must be specified");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!argv["registry"]) {
|
||||
console.error("the ---registry option must be specified");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let walletAddresses = await Promise.all(keycards.map((keycard) => createWallet(sender, keycard, argv["maxTxValue"], argv["minBlockDistance"])));
|
||||
let zippedAddresses = keycards.map((keycard, i) => [keycard, walletAddresses[i]]);
|
||||
|
||||
let fid;
|
||||
if (argv["out"]) {
|
||||
fid = fs.openSync(argv["out"], "w", o0644);
|
||||
} else {
|
||||
fid = STDOUT;
|
||||
}
|
||||
|
||||
zippedAddresses.forEach((tuple) => fs.writeSync(fid, `${tuple[0]},${tuple[1]}\n`));
|
||||
|
||||
fs.close(fid);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
run();
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "wallet-creation",
|
||||
"version": "0.0.1",
|
||||
"description": "Creates wallets in batch using the KeycardWalletFactory",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"wallet-creation": "node -r esm index.js"
|
||||
},
|
||||
"bin": {
|
||||
"wallet-creation": "./index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"esm": "^3.2.25",
|
||||
"minimist": "^1.2.0",
|
||||
"web3": "^1.2.6"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue