tokyo-solidity-template
Version:
Make solidity contract based on user input
217 lines (181 loc) • 9.3 kB
JavaScript
const moment = require("moment");
const get = require("lodash/get");
const ethUtils = require("ethereumjs-util");
const schema = require("tokyo-schema").default;
const BigNumber = web3.BigNumber;
const { toBuffer, setLengthLeft, addHexPrefix, isValidAddress } = ethUtils;
/**
* Contract Artifacts
*/
const KYC = artifacts.require("./KYC.sol");
const Vault = artifacts.require("./MultiHolderVault.sol");
const Locker = artifacts.require("./Locker.sol");
const Multisig = artifacts.require("./MultiSigWallet.sol");
const Token = artifacts.require("./SampleProjectToken.sol");
const Crowdsale = artifacts.require("./SampleProjectCrowdsale.sol");
module.exports = async function (deployer, network, accounts) {
console.log(accounts)
const address = {};
const { value, error } = schema.validate(getInput());
const input = value;
const data = { address, input };
// contract instance
let kyc, vault, locker, multisigs, token, crowdsale;
/**
* Deploy contracts sequentually
* 1. KYC / Vault / Locker / Multisigs(optional)
* 2. Token
* 3. Crowdsale
* 4. Initialize contracts
* - transfer ownerships of vault, token, lcoker to crowdsale
* - Crowdsale.init()
*/
deployer.deploy([
KYC,
[
Vault,
"0x557678cf28594495ef4b08a6447726f931f8d787",
1000
],
[
Token,
// token arguments...
"0x00"
]
]).then(async () => {
kyc = await KYC.deployed();
vault = await Vault.deployed();
token = await Token.deployed();
address.kyc = kyc.address;
address.vault = vault.address;
address.token = token.address;
}).then(async () => {
multisigs = await Promise.all([
Multisig.new(["0x557678cf28594495ef4b08a6447726f931f8d787", "0x557678cf28594495ef4b08a6447726f931f8d788"], 1),
Multisig.new(["0x557678cf28594495ef4b08a6447726f931f8d789", "0x557678cf28594495ef4b08a6447726f931f8d78a"], 1)
]);
address.multisigs = multisigs.map(m => m.address);
console.log("Multisigs :", address.multisigs.join(", "));
}).then(async () => deployer.deploy([
[
Locker,
get(data, "address.token"),
new BigNumber("1000"),
["0x557678cf28594495ef4b08a6447726f931f8d787", "0x557678cf28594495ef4b08a6447726f931f8d788"],
[new BigNumber("200"), new BigNumber("800")]
],
])).then(async () => {
locker = await Locker.deployed();
address.locker = locker.address;
}).then(() => deployer.deploy([
[
Crowdsale,
[
get(data, "address.token"), // address.token
new BigNumber("500000000000000000000"), // input.sale.valid_purchase.max_purchase_limit
new BigNumber("10000000000000000"), // input.sale.valid_purchase.min_purchase_limit
new BigNumber("20"), // input.sale.valid_purchase.block_interval
get(data, "address.kyc"), // address.kyc
new BigNumber("2"), // input.sale.stages_length
].map(toLeftPaddedBuffer)
]
])).then(async () => {
crowdsale = await Crowdsale.deployed();
address.crowdsale = crowdsale.address;
}).then(async () => {
const tokenDistributions = get(data, "input.sale.distribution.token");
const lockerRatios = tokenDistributions
.filter(t => t.token_holder === "locker")[0].token_ratio;
const crowdsaleRatio = tokenDistributions
.filter(t => t.token_holder === "crowdsale")[0].token_ratio;
const initArgs = [
new BigNumber(get(data, "input.sale.start_time")),
new BigNumber(get(data, "input.sale.end_time")),
new BigNumber(get(data, "input.sale.rate.base_rate")),
new BigNumber(get(data, "input.sale.coeff")),
new BigNumber(get(data, "input.sale.max_cap")),
new BigNumber(get(data, "input.sale.min_cap")),
new BigNumber(lockerRatios),
new BigNumber(crowdsaleRatio),
get(data, "address.vault"),
get(data, "address.locker"),
get(data, "input.sale.new_token_owner")
];
await crowdsale.init(initArgs.map(toLeftPaddedBuffer));
const holderAddresses = get(data, "input.sale.distribution.ether").map(({ether_holder}) => {
if (isValidAddress(ether_holder)) return ether_holder;
if (ether_holder.includes("multisig")) {
const idx = Number(ether_holder.split("multisig")[1]);
if (!isValidAddress(address.multisigs[idx])) throw new Error("Invalid multisig address", address.multisigs[idx]);
return address.multisigs[idx];
}
});
const holderRatios = get(data, "input.sale.distribution.ether").map(e => e.ether_ratio);
await vault.initHolders(
holderAddresses,
holderRatios,
);
const bonusTimes = [ 1553385600, 1553558400 ];
const bonusTimeValues = [ 100, 50 ];
const bonusAmounts = [ 100000000000000000000, 10000000000000000000, 1000000000000000000 ];
const bonusAmountValues = [ 200, 100, 50 ];
await crowdsale.setBonusesForTimes(
bonusTimes,
bonusTimeValues,
);
await crowdsale.setBonusesForAmounts(
bonusAmounts,
bonusAmountValues,
);
const periodStartTimes = [ 1553299200, 1553472000 ];
const periodEndTimes = [ 1553385600, 1553644800 ];
const periodCapRatios = [ 100, 0 ];
const periodMaxPurchaseLimits = [ 50000000000000000, 50000000000000000 ];
const periodMinPurchaseLimits = [ 100000000000000, 100000000000000 ];
const periodKycs = [ true, true ];
await crowdsale.initPeriods(
periodStartTimes,
periodEndTimes,
periodCapRatios,
periodMaxPurchaseLimits,
periodMinPurchaseLimits,
periodKycs,
);
const release1Times = [ 1553731200, 1553904000 ];
const release1Ratios = [ 300, 1000 ];
await locker.lock(
"0x557678cf28594495ef4b08a6447726f931f8d787",
true,
release1Times,
release1Ratios,
);
const release2Times = [ 1553644800, 1553731200, 1553904000 ];
const release2Ratios = [ 200, 500, 1000 ];
await locker.lock(
"0x557678cf28594495ef4b08a6447726f931f8d788",
false,
release2Times,
release2Ratios,
);
}).then(async () => {
// transfer ownerships to crowdsale
await Promise.all([
vault.transferOwnership(crowdsale.address),
locker.transferOwnership(crowdsale.address),
token.changeController(crowdsale.address),
]);
});
};
function getInput() {
return JSON.parse('{"project_name":"Sample Project","token":{"token_type":{"is_minime":true},"token_option":{"burnable":true,"pausable":true},"token_name":"Sample String","token_symbol":"SS","decimals":18},"sale":{"max_cap":"4000000000000000000000","min_cap":"1000000000000000000000","start_time":"2019/03/23 00:00:00","end_time":"2019/03/27 00:00:00","coeff":"1000","rate":{"is_static":false,"base_rate":"200","bonus":{"use_time_bonus":true,"use_amount_bonus":true,"time_bonuses":[{"bonus_time_stage":"2019/03/24 00:00:00","bonus_time_ratio":"100"},{"bonus_time_stage":"2019/03/26 00:00:00","bonus_time_ratio":"50"}],"amount_bonuses":[{"bonus_amount_stage":"100000000000000000000","bonus_amount_ratio":"200"},{"bonus_amount_stage":"10000000000000000000","bonus_amount_ratio":"100"},{"bonus_amount_stage":"1000000000000000000","bonus_amount_ratio":"50"}]}},"distribution":{"token":[{"token_holder":"crowdsale","token_ratio":"800"},{"token_holder":"locker","token_ratio":"100"},{"token_holder":"0x557678cf28594495ef4b08a6447726f931f8d787","token_ratio":"100"}],"ether":[{"ether_holder":"0x557678cf28594495ef4b08a6447726f931f8d787","ether_ratio":"800"},{"ether_holder":"0x557678cf28594495ef4b08a6447726f931f8d788","ether_ratio":"200"}]},"stages":[{"start_time":"2019/03/23 00:00:00","end_time":"2019/03/24 00:00:00","cap_ratio":"100","max_purchase_limit":"50000000000000000","min_purchase_limit":"100000000000000","kyc":true},{"start_time":"2019/03/25 00:00:00","end_time":"2019/03/27 00:00:00","cap_ratio":"0","max_purchase_limit":"50000000000000000","min_purchase_limit":"100000000000000","kyc":true}],"valid_purchase":{"max_purchase_limit":"500000000000000000000","min_purchase_limit":"10000000000000000","block_interval":20},"new_token_owner":"0x557678cf28594495ef4b08a6447726f931f8d787"},"multisig":{"use_multisig":true,"infos":[{"num_required":1,"owners":["0x557678cf28594495ef4b08a6447726f931f8d787","0x557678cf28594495ef4b08a6447726f931f8d788"]},{"num_required":1,"owners":["0x557678cf28594495ef4b08a6447726f931f8d789","0x557678cf28594495ef4b08a6447726f931f8d78a"]}]},"locker":{"use_locker":true,"beneficiaries":[{"address":"0x557678cf28594495ef4b08a6447726f931f8d787","ratio":"200","is_straight":true,"release":[{"release_time":"2019/03/28 00:00:00","release_ratio":"300"},{"release_time":"2019/03/30 00:00:00","release_ratio":"1000"}]},{"address":"0x557678cf28594495ef4b08a6447726f931f8d788","ratio":"800","is_straight":false,"release":[{"release_time":"2019/03/27 00:00:00","release_ratio":"200"},{"release_time":"2019/03/28 00:00:00","release_ratio":"500"},{"release_time":"2019/03/30 00:00:00","release_ratio":"1000"}]}]}}');
}
function toLeftPaddedBuffer(v) {
if (typeof v === "boolean") {
v = Number(v);
} else if (v instanceof BigNumber) {
v = addHexPrefix(v.toString(16));
}
const buf = toBuffer(v);
const hex = setLengthLeft(buf, 32).toString("hex");
return addHexPrefix(hex);
}