keycard-redeem/scripts/create-redeemable.js

238 lines
7.0 KiB
JavaScript
Raw Normal View History

2020-04-01 10:18:28 +00:00
#!/usr/bin/env node
2020-05-08 09:54:42 +00:00
const Web3 = require('web3');
const parseArgs = require('minimist');
const fs = require('fs');
const keccak256 = require('js-sha3').keccak256;
const BigNumber = require('bignumber.js');
2020-04-01 10:18:28 +00:00
2020-05-07 11:59:58 +00:00
const argv = parseArgs(process.argv.slice(2), {boolean: ["nft", "deploy-factory", "deploy-bucket"], string: ["sender", "factory", "bucket", "token"], default: {"endpoint": "ws://127.0.0.1:8546", "start-in-days": 0, "validity-days": 365, "max-tx-delay-blocks": 10, "amount-decimals": 18}});
2020-04-01 10:18:28 +00:00
const web3 = new Web3(argv["endpoint"]);
2020-04-28 09:36:34 +00:00
const classPrefix = argv["nft"] ? "NFT" : "ERC20";
2020-04-01 10:18:28 +00:00
2020-05-07 11:59:58 +00:00
const BucketConfig = loadJSON(`./dist/contracts/${classPrefix}Bucket.json`);
const BucketFactoryConfig = loadJSON(`./dist/contracts/${classPrefix}BucketFactory.json`);
const IERC721 = loadJSON(`./dist/contracts/IERC721.json`);
const IERC20Detailed = loadJSON(`./dist/contracts/IERC20Detailed.json`);
2020-04-13 09:08:37 +00:00
const BucketFactory = new web3.eth.Contract(BucketFactoryConfig["abiDefinition"]);
const Bucket = new web3.eth.Contract(BucketConfig["abiDefinition"]);
const ERC721 = new web3.eth.Contract(IERC721["abiDefinition"]);
2020-05-07 11:59:58 +00:00
const ERC20 = new web3.eth.Contract(IERC20Detailed["abiDefinition"]);
2020-04-01 10:18:28 +00:00
2020-05-07 11:59:58 +00:00
function loadJSON(path) {
let file = fs.readFileSync(path, "utf-8");
let loadedAsset = JSON.parse(file);
return loadedAsset;
2020-04-01 10:18:28 +00:00
}
async function getDefaultSender() {
2020-05-07 11:59:58 +00:00
let accounts = await web3.eth.getAccounts();
return accounts[0];
2020-04-01 10:18:28 +00:00
}
function loadAccount(account, passfile) {
2020-05-07 11:59:58 +00:00
let json = fs.readFileSync(account, "utf-8");
let pass = fs.readFileSync(passfile, "utf-8").split("\n")[0].replace("\r", "");
return web3.eth.accounts.decrypt(json, pass);
2020-04-01 10:18:28 +00:00
}
async function sendMethod(methodCall, sender, to) {
2020-05-07 11:59:58 +00:00
let receipt;
if (typeof(sender) == "string") {
let gasAmount = await methodCall.estimateGas({from: sender});
receipt = await methodCall.send({from: sender, gas: gasAmount});
} else {
let gasAmount = await methodCall.estimateGas({from: sender.address});
let data = methodCall.encodeABI();
let signedTx = await sender.signTransaction({to: to, data: data, gas: gasAmount});
receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction);
}
return receipt;
2020-04-01 10:18:28 +00:00
}
async function deployFactory(sender) {
2020-05-07 11:59:58 +00:00
let code = "0x" + BucketFactoryConfig["code"];
let methodCall = BucketFactory.deploy({data: code});
let receipt = await sendMethod(methodCall, sender, null);
return receipt.contractAddress;
2020-04-01 10:18:28 +00:00
}
async function deployBucket(sender, factory, token, startInDays, validityInDays, maxTxDelayBlocks) {
2020-05-07 11:59:58 +00:00
let now = Math.round(new Date().getTime() / 1000);
let startDate = now + (60 * 60 * 24 * startInDays);
let expirationDate = now + (60 * 60 * 24 * validityInDays);
BucketFactory.options.address = factory;
let methodCall = BucketFactory.methods.create(token.toLowerCase(), startDate, expirationDate, maxTxDelayBlocks);
try {
let receipt = await sendMethod(methodCall, sender, BucketFactory.options.address);
return receipt.events.BucketCreated.returnValues.bucket;
} catch(err) {
console.error(err);
return null;
}
2020-04-01 10:18:28 +00:00
}
2020-05-07 11:59:58 +00:00
async function createRedeemable(sender, keycard) {
let methodCall = Bucket.methods.createRedeemable(keycard.keycard, keycard.amount, keycard.code);
2020-04-13 09:08:37 +00:00
2020-05-07 11:59:58 +00:00
try {
let receipt = await sendMethod(methodCall, sender, Bucket.options.address);
return receipt;
} catch(err) {
console.error(err);
return null;
}
2020-04-13 09:08:37 +00:00
}
function createNFTData(keycard, code) {
2020-05-07 11:59:58 +00:00
return keycard.toLowerCase() + code.replace("0x", "");
2020-04-13 09:08:37 +00:00
}
function senderAddress(sender) {
2020-05-07 11:59:58 +00:00
if (typeof(sender) == "string") {
return sender;
} else {
return sender.address;
}
2020-04-13 09:08:37 +00:00
}
2020-05-07 11:59:58 +00:00
async function transferNFT(sender, keycard) {
let methodCall = ERC721.methods.safeTransferFrom(senderAddress(sender), Bucket.options.address, keycard.amount, createNFTData(keycard.keycard, keycard.code));
2020-04-13 09:08:37 +00:00
2020-05-07 11:59:58 +00:00
try {
let receipt = await sendMethod(methodCall, sender, ERC721.options.address);
return receipt;
} catch(err) {
console.error(err);
return null;
}
2020-04-01 10:18:28 +00:00
}
function processCode(code) {
2020-05-07 11:59:58 +00:00
if (!code.startsWith("0x")) {
code = "0x" + Buffer.from(code, 'utf8').toString('hex');
}
return "0x" + keccak256(code);
}
function processAmount(amount, decimals) {
if (amount.startsWith("0x")) {
return amount;
} else {
return new BigNumber(amount).multipliedBy(Math.pow(10, decimals)).toString();
}
}
function processLine(line, decimals) {
let c = line.split(",").map((e) => e.toLowerCase().trim());
return {keycard: c[0], amount: processAmount(c[1], decimals), code: processCode(c[2])};
}
2020-05-07 11:59:58 +00:00
async function getToken(token, readIfMissing) {
return (token || !readIfMissing) ? token : await Bucket.methods.tokenContract().call();
}
2020-05-07 11:59:58 +00:00
async function getDecimals(decimals, readIfMissing) {
if (decimals) {
return decimals
} else if (readIfMissing) {
return await ERC20.methods.decimals().call();
} else {
return 0;
}
2020-04-01 10:18:28 +00:00
}
async function run() {
2020-05-07 11:59:58 +00:00
BucketFactory.transactionConfirmationBlocks = 3;
Bucket.transactionConfirmationBlocks = 3;
ERC721.transactionConfirmationBlocks = 3;
let sender;
let hasDoneSomething = false;
if (argv["account"]) {
if (!argv["passfile"]) {
console.error("the ---passfile option must be specified when using the --account option");
process.exit(1);
}
if (argv["sender"]) {
console.warn("--account used, --sender will be ignored");
2020-04-01 10:18:28 +00:00
}
2020-05-07 11:59:58 +00:00
sender = loadAccount(argv["account"], argv["passfile"]);
} else {
sender = argv["sender"] || await getDefaultSender();
}
2020-04-23 12:51:05 +00:00
2020-05-07 11:59:58 +00:00
let factory;
if (argv["deploy-factory"]) {
factory = await deployFactory(sender);
hasDoneSomething = true;
console.log("Factory deployed at: " + factory);
} else {
factory = argv["factory"];
}
let bucket;
if (argv["deploy-bucket"]) {
if (!factory) {
console.error("the --factory or --deploy-factory option must be specified");
process.exit(1);
2020-04-01 10:18:28 +00:00
}
2020-05-07 11:59:58 +00:00
if (!argv["token"]) {
console.error("the --token option must be specified");
process.exit(1);
2020-04-01 10:18:28 +00:00
}
2020-04-23 12:51:05 +00:00
2020-05-07 11:59:58 +00:00
if (!argv["validity-days"]) {
console.error("the --validity-days option must be specified");
process.exit(1);
2020-04-01 10:18:28 +00:00
}
2020-05-07 11:59:58 +00:00
bucket = await deployBucket(sender, factory, argv["token"], argv["start-in-days"], argv["validity-days"], argv["max-tx-delay-blocks"]);
hasDoneSomething = true;
console.log("Bucket deployed at: " + bucket);
} else {
bucket = argv["bucket"];
}
Bucket.options.address = bucket;
let keycards;
if (argv["file"]) {
const token = await getToken(argv["token"], (argv["nft"] || !argv["amount-decimals"]));
ERC721.options.address = token;
ERC20.options.address = token;
const decimals = await getDecimals(argv["amount-decimals"], !argv["nft"]);
let file = fs.readFileSync(argv["file"], 'utf8');
keycards = file.split("\n").map((line) => processLine(line, decimals));
for (let keycard of keycards) {
await argv["nft"] ? transferNFT(sender, keycard) : createRedeemable(sender, keycard);
}
} else if (!hasDoneSomething) {
console.error("the --file option must be specified");
2020-04-01 10:18:28 +00:00
process.exit(0);
2020-05-07 11:59:58 +00:00
}
process.exit(0);
2020-04-01 10:18:28 +00:00
}
2020-04-29 22:34:57 +00:00
run();