UNPKG

tokyo-solidity-template

Version:
401 lines (323 loc) 18.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _range = require("lodash/range"); var _range2 = _interopRequireDefault(_range); var _templateHelper = require("./templateHelper"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } // common constructor parameters for MintableBaseCrowdsale & MiniMeBaseCrowdsale // [ [solidity data type], [path for lodash.get] ] const makeArrayPath = (len, prefix, postfix = "") => (0, _range2.default)(len).map(i => `get(data, "${prefix}.${i}${postfix ? `.${postfix}` : ""}")`); /** * @title Parser * @notice Parses user's input to generate inheritance tree for token and crowdsale. * The result will be used to generate contract & migration file. */ class Parser { constructor(input) { this.input = input; } /* eslint-disable complexity */ parse() { const { input } = this; const f = () => ({ parentsList: [], // super contract name importStatements: [] // path to import suprt contract }); const meta = {}; meta.use_custom_token = input.token.use_custom_token; const token = f(); // for token contract const postToken = f(); // appended to toekn const crowdsale = f(); // for crowdsale contract const postCrowdsale = f(); // appended to crowdsale const codes = { // solidity or JS source code migration: "", crowdsale: { init: "" } }; const constructors = {}; // for constructors for Crowdsale, Locker let crowdsaleConstructorArgumentLength = 0; const tab2 = 2; const tab3 = 3; meta.projectName = input.project_name.replace(/\W/g, ""); codes.migration += ` ${(0, _templateHelper.writeTabs)(tab2)}const tokenDistributions = get(data, "input.sale.distribution.token"); ${(0, _templateHelper.writeTabs)(tab2)}const lockerRatios = tokenDistributions ${(0, _templateHelper.writeTabs)(tab3)}.filter(t => t.token_holder === "locker")[0].token_ratio; ${(0, _templateHelper.writeTabs)(tab2)}const crowdsaleRatio = tokenDistributions ${(0, _templateHelper.writeTabs)(tab3)}.filter(t => t.token_holder === "crowdsale")[0].token_ratio; `; // BaseCrowdsale.init() const initMigVars = ["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.max_cap\"))", "new BigNumber(get(data, \"input.sale.min_cap\"))", "new BigNumber(crowdsaleRatio)", "get(data, \"address.vault\")", "get(data, \"address.locker\")", "get(data, \"input.sale.new_token_owner\")"]; codes.migration += ` ${(0, _templateHelper.writeTabs)(tab2)}const initArgs = [${initMigVars.map(expr => `\n${(0, _templateHelper.writeTabs)(tab3)}${expr}`).join()} ${(0, _templateHelper.writeTabs)(tab2)}]; ${(0, _templateHelper.writeTabs)(tab2)} ${(0, _templateHelper.writeTabs)(tab2)}await crowdsale.init(initArgs.map(toLeftPaddedBuffer)); `; // BaseCrowdsale crowdsale.parentsList.push("BaseCrowdsale"); crowdsale.importStatements.push("import \"./base/crowdsale/BaseCrowdsale.sol\";"); constructors.BaseCrowdsale = [["uint", "input.sale.coeff"]]; // input.sale.distribution.ether // vault.initHolders codes.migration += ` ${(0, _templateHelper.writeTabs)(tab2)}const etherHolderAddresses = get(data, "input.sale.distribution.ether").map(({ether_holder}) => { ${(0, _templateHelper.writeTabs)(tab2)} if (isValidAddress(ether_holder)) return ether_holder; ${(0, _templateHelper.writeTabs)(tab2)} if (ether_holder.includes("multisig")) { ${(0, _templateHelper.writeTabs)(tab2)} const idx = Number(ether_holder.split("multisig")[1]); ${(0, _templateHelper.writeTabs)(tab2)} if (!isValidAddress(address.multisigs[idx])) throw new Error("Invalid multisig address", address.multisigs[idx]); ${(0, _templateHelper.writeTabs)(tab2)} ${(0, _templateHelper.writeTabs)(tab2)} return address.multisigs[idx]; ${(0, _templateHelper.writeTabs)(tab2)} } ${(0, _templateHelper.writeTabs)(tab2)}}); ${(0, _templateHelper.writeTabs)(tab2)}const etherHolderRatios = get(data, "input.sale.distribution.ether").map(e => e.ether_ratio); `; codes.migration += ` ${(0, _templateHelper.writeTabs)(tab2)}await vault.initHolders( ${(0, _templateHelper.writeTabs)(tab3)}etherHolderAddresses, ${(0, _templateHelper.writeTabs)(tab3)}etherHolderRatios, ${(0, _templateHelper.writeTabs)(tab2)}); `; // input.sale.distribution.token // crowdsale.initHolders codes.migration += ` ${(0, _templateHelper.writeTabs)(tab2)}const tokenHolderAddresses = get(data, "input.sale.distribution.token").map(({token_holder}) => { ${(0, _templateHelper.writeTabs)(tab2)} if (isValidAddress(token_holder)) return token_holder; ${(0, _templateHelper.writeTabs)(tab2)} if (token_holder === "crowdsale") return "0x00"; ${(0, _templateHelper.writeTabs)(tab2)} if (token_holder === "locker") return address.locker; ${(0, _templateHelper.writeTabs)(tab2)} if (token_holder.includes("multisig")) { ${(0, _templateHelper.writeTabs)(tab2)} const idx = Number(token_holder.split("multisig")[1]); ${(0, _templateHelper.writeTabs)(tab2)} if (!isValidAddress(address.multisigs[idx])) throw new Error("Invalid multisig address", address.multisigs[idx]); ${(0, _templateHelper.writeTabs)(tab2)} ${(0, _templateHelper.writeTabs)(tab2)} return address.multisigs[idx]; ${(0, _templateHelper.writeTabs)(tab2)} } ${(0, _templateHelper.writeTabs)(tab2)}}); ${(0, _templateHelper.writeTabs)(tab2)}const tokenHolderRatios = get(data, "input.sale.distribution.token").map(e => e.token_ratio); `; codes.migration += ` ${(0, _templateHelper.writeTabs)(tab2)}await crowdsale.initHolders( ${(0, _templateHelper.writeTabs)(tab3)}tokenHolderAddresses, ${(0, _templateHelper.writeTabs)(tab3)}tokenHolderRatios, ${(0, _templateHelper.writeTabs)(tab2)}); `; // parse input.token if (input.token.token_type.is_minime) { token.parentsList.push("MiniMeToken"); token.importStatements.push("import \"minimetoken/contracts/MiniMeToken.sol\";"); if (input.token.token_option.burnable) { token.parentsList.push("BurnableMiniMeToken"); token.importStatements.push("import \"./base/token/BurnableMiniMeToken.sol\";"); } if (input.token.token_option.pausable) { // do nothing. MiniMe is pausable as default } if (input.token.token_option.no_mint_after_sale) { token.parentsList.push("NoMintMiniMeToken"); token.importStatements.push("import \"./base/token/NoMintMiniMeToken.sol\";"); postCrowdsale.parentsList.push("FinishMintingCrowdsale"); postCrowdsale.importStatements.push("import \"./base/crowdsale/FinishMintingCrowdsale.sol\";"); constructors.FinishMintingCrowdsale = []; } crowdsale.parentsList.push("MiniMeBaseCrowdsale"); crowdsale.importStatements.push("import \"./base/crowdsale/MiniMeBaseCrowdsale.sol\";"); constructors.MiniMeBaseCrowdsale = [["address", "address.token"]]; } else { token.parentsList.push("CanReclaimToken"); token.importStatements.push("import \"openzeppelin-solidity/contracts/ownership/CanReclaimToken.sol\";"); token.parentsList.push("MintableToken"); token.importStatements.push("import \"openzeppelin-solidity/contracts/token/ERC20/MintableToken.sol\";"); crowdsale.parentsList.push("MintableBaseCrowdsale"); crowdsale.importStatements.push("import \"./base/crowdsale/MintableBaseCrowdsale.sol\";"); constructors.MintableBaseCrowdsale = [["address", "address.token"]]; if (input.token.token_option.burnable) { token.parentsList.push("BurnableToken"); token.importStatements.push("import \"openzeppelin-solidity/contracts/token/ERC20/BurnableToken.sol\";"); } if (input.token.token_option.pausable) { token.parentsList.push("PausableToken"); token.importStatements.push("import \"openzeppelin-solidity/contracts/token/ERC20/PausableToken.sol\";"); } if (input.token.token_option.no_mint_after_sale) { postCrowdsale.parentsList.push("FinishMintingCrowdsale"); postCrowdsale.importStatements.push("import \"./base/crowdsale/FinishMintingCrowdsale.sol\";"); constructors.FinishMintingCrowdsale = []; } } // parse input.sale // 1. BonusCrowdsale if (!input.sale.rate.is_static) { crowdsale.parentsList.push("BonusCrowdsale"); crowdsale.importStatements.push("import \"./base/crowdsale/BonusCrowdsale.sol\";"); constructors.BonusCrowdsale = []; const numTimeBonuses = input.sale.rate.bonus.use_time_bonus ? input.sale.rate.bonus.time_bonuses.length : 0; const bonusTimeStages = makeArrayPath(numTimeBonuses, "input.sale.rate.bonus.time_bonuses", "bonus_time_stage"); const bonusTimeRatios = makeArrayPath(numTimeBonuses, "input.sale.rate.bonus.time_bonuses", "bonus_time_ratio"); const numAmountBonuses = input.sale.rate.bonus.use_amount_bonus ? input.sale.rate.bonus.amount_bonuses.length : 0; const bonusAmountStages = makeArrayPath(numAmountBonuses, "input.sale.rate.bonus.amount_bonuses", "bonus_amount_stage"); const bonusAmountRatios = makeArrayPath(numAmountBonuses, "input.sale.rate.bonus.amount_bonuses", "bonus_amount_ratio"); codes.migration += ` ${(0, _templateHelper.writeTabs)(tab2)}const bonusTimeStages = [ ${(0, _templateHelper.writeTabs)(tab3)}${bonusTimeStages.join(`,\n${(0, _templateHelper.writeTabs)(tab3)}`)} ]; ${(0, _templateHelper.writeTabs)(tab2)}const bonusTimeRatios = [ ${(0, _templateHelper.writeTabs)(tab3)}${bonusTimeRatios.join(`,\n${(0, _templateHelper.writeTabs)(tab3)}`)} ]; `; codes.migration += ` ${(0, _templateHelper.writeTabs)(tab2)}const bonusAmountStages = [ ${(0, _templateHelper.writeTabs)(tab3)}${bonusAmountStages.join(`,\n${(0, _templateHelper.writeTabs)(tab3)}`)} ]; ${(0, _templateHelper.writeTabs)(tab2)}const bonusAmountRatios = [ ${(0, _templateHelper.writeTabs)(tab3)}${bonusAmountRatios.join(`,\n${(0, _templateHelper.writeTabs)(tab3)}`)} ]; `; codes.migration += ` ${(0, _templateHelper.writeTabs)(tab2)}await crowdsale.setBonusesForTimes( ${(0, _templateHelper.writeTabs)(tab3)}bonusTimeStages, ${(0, _templateHelper.writeTabs)(tab3)}bonusTimeRatios, ${(0, _templateHelper.writeTabs)(tab2)}); `; codes.migration += ` ${(0, _templateHelper.writeTabs)(tab2)}await crowdsale.setBonusesForAmounts( ${(0, _templateHelper.writeTabs)(tab3)}bonusAmountStages, ${(0, _templateHelper.writeTabs)(tab3)}bonusAmountRatios, ${(0, _templateHelper.writeTabs)(tab2)}); `; } // 2. PurchaseLimitedCrowdsale if (input.sale.valid_purchase.max_purchase_limit.gt(0)) { crowdsale.parentsList.push("PurchaseLimitedCrowdsale"); crowdsale.importStatements.push("import \"./base/crowdsale/PurchaseLimitedCrowdsale.sol\";"); constructors.PurchaseLimitedCrowdsale = [["uint", "input.sale.valid_purchase.max_purchase_limit", input.sale.valid_purchase.max_purchase_limit]]; } // 3. MinimumPaymentCrowdsale if (input.sale.valid_purchase.min_purchase_limit.gt(0)) { crowdsale.parentsList.push("MinimumPaymentCrowdsale"); crowdsale.importStatements.push("import \"./base/crowdsale/MinimumPaymentCrowdsale.sol\";"); constructors.MinimumPaymentCrowdsale = [["uint", "input.sale.valid_purchase.min_purchase_limit", input.sale.valid_purchase.min_purchase_limit]]; } // 4. BlockIntervalCrowdsale if (input.sale.valid_purchase.block_interval > 0) { crowdsale.parentsList.push("BlockIntervalCrowdsale"); crowdsale.importStatements.push("import \"./base/crowdsale/BlockIntervalCrowdsale.sol\";"); constructors.BlockIntervalCrowdsale = [["uint", "input.sale.valid_purchase.block_interval", input.sale.valid_purchase.block_interval]]; } // 5. KYCCrowdsale & StagedCrowdsale if (input.sale.stages.length > 0) { if (input.sale.stages.findIndex(s => s.kyc === true) >= 0) { crowdsale.parentsList.push("KYCCrowdsale"); crowdsale.importStatements.push("import \"./base/crowdsale/KYCCrowdsale.sol\";"); constructors.KYCCrowdsale = [["address", "address.kyc"]]; } crowdsale.parentsList.push("StagedCrowdsale"); crowdsale.importStatements.push("import \"./base/crowdsale/StagedCrowdsale.sol\";"); constructors.StagedCrowdsale = [["uint", "input.sale.stages.length", input.sale.stages.length]]; // *_length => *.length // StagedCrowdsale.initStages const numStages = input.sale.stages.length; const periodStartTimes = makeArrayPath(numStages, "input.sale.stages", "start_time"); const periodEndTimes = makeArrayPath(numStages, "input.sale.stages", "end_time"); const periodCapRatios = makeArrayPath(numStages, "input.sale.stages", "cap_ratio"); const periodMaxPurchaseLimits = makeArrayPath(numStages, "input.sale.stages", "max_purchase_limit"); const periodMinPurchaseLimits = makeArrayPath(numStages, "input.sale.stages", "min_purchase_limit"); const periodKycs = makeArrayPath(numStages, "input.sale.stages", "kyc"); codes.migration += ` ${(0, _templateHelper.writeTabs)(tab2)}const periodStartTimes = [ ${(0, _templateHelper.writeTabs)(tab3)}${periodStartTimes.join(`,\n${(0, _templateHelper.writeTabs)(tab3)}`)} ]; ${(0, _templateHelper.writeTabs)(tab2)}const periodEndTimes = [ ${(0, _templateHelper.writeTabs)(tab3)}${periodEndTimes.join(`,\n${(0, _templateHelper.writeTabs)(tab3)}`)} ]; ${(0, _templateHelper.writeTabs)(tab2)}const periodCapRatios = [ ${(0, _templateHelper.writeTabs)(tab3)}${periodCapRatios.join(`,\n${(0, _templateHelper.writeTabs)(tab3)}`)} ]; ${(0, _templateHelper.writeTabs)(tab2)}const periodMaxPurchaseLimits = [ ${(0, _templateHelper.writeTabs)(tab3)}${periodMaxPurchaseLimits.join(`,\n${(0, _templateHelper.writeTabs)(tab3)}`)} ]; ${(0, _templateHelper.writeTabs)(tab2)}const periodMinPurchaseLimits = [ ${(0, _templateHelper.writeTabs)(tab3)}${periodMinPurchaseLimits.join(`,\n${(0, _templateHelper.writeTabs)(tab3)}`)} ]; ${(0, _templateHelper.writeTabs)(tab2)}const periodKycs = [ ${(0, _templateHelper.writeTabs)(tab3)}${periodKycs.join(`,\n${(0, _templateHelper.writeTabs)(tab3)}`)} ]; `; codes.migration += ` ${(0, _templateHelper.writeTabs)(tab2)}await crowdsale.initStages( ${(0, _templateHelper.writeTabs)(tab3)}periodStartTimes, ${(0, _templateHelper.writeTabs)(tab3)}periodEndTimes, ${(0, _templateHelper.writeTabs)(tab3)}periodCapRatios, ${(0, _templateHelper.writeTabs)(tab3)}periodMaxPurchaseLimits, ${(0, _templateHelper.writeTabs)(tab3)}periodMinPurchaseLimits, ${(0, _templateHelper.writeTabs)(tab3)}periodKycs, ${(0, _templateHelper.writeTabs)(tab2)}); `; } // Locker.lock() let i = 0; // beneficiary index for (const beneficiary of input.locker.beneficiaries) { const numReleases = beneficiary.release.length; const releaseTimes = makeArrayPath(numReleases, `input.locker.beneficiaries.${i}.release`, "release_time"); const releaseRatios = makeArrayPath(numReleases, `input.locker.beneficiaries.${i}.release`, "release_ratio"); codes.migration += ` ${(0, _templateHelper.writeTabs)(tab2)}const release${i}Times = [ ${(0, _templateHelper.writeTabs)(tab3)}${releaseTimes.join(`,\n${(0, _templateHelper.writeTabs)(tab3)}`)} ]; ${(0, _templateHelper.writeTabs)(tab2)}const release${i}Ratios = [ ${(0, _templateHelper.writeTabs)(tab3)}${releaseRatios.join(`,\n${(0, _templateHelper.writeTabs)(tab3)}`)} ]; `; codes.migration += ` ${(0, _templateHelper.writeTabs)(tab2)}await locker.lock( ${(0, _templateHelper.writeTabs)(tab3)}get(data, "input.locker.beneficiaries.${i}.address"), ${(0, _templateHelper.writeTabs)(tab3)}get(data, "input.locker.beneficiaries.${i}.is_straight"), ${(0, _templateHelper.writeTabs)(tab3)}release${i}Times, ${(0, _templateHelper.writeTabs)(tab3)}release${i}Ratios, ${(0, _templateHelper.writeTabs)(tab2)}); `; i += 1; } // BaseCrowdsale.init() const intVars = ["_startTime", "_endTime", "_rate", "_cap", "_goal", "_crowdsaleRatio"]; const addrVars = ["_vault", "_locker", "_nextTokenOwner"]; i = 0; for (const varName of intVars) { codes.crowdsale.init += `${(0, _templateHelper.writeTabs)(tab2)}uint ${varName} = uint(args[${i++}]);\n`; } for (const varName of addrVars) { codes.crowdsale.init += `${(0, _templateHelper.writeTabs)(tab2)}address ${varName} = address(args[${i++}]);\n`; } codes.crowdsale.init += ` ${(0, _templateHelper.writeTabs)(tab2)}require(_endTime > _startTime); ${(0, _templateHelper.writeTabs)(tab2)}require(_rate > 0); ${(0, _templateHelper.writeTabs)(tab2)}require(_cap > 0); ${(0, _templateHelper.writeTabs)(tab2)}require(_goal > 0); ${(0, _templateHelper.writeTabs)(tab2)}require(_cap > _goal); ${(0, _templateHelper.writeTabs)(tab2)}require(_crowdsaleRatio > 0); ${(0, _templateHelper.writeTabs)(tab2)}require(_vault != address(0)); ${(0, _templateHelper.writeTabs)(tab2)}require(_locker != address(0)); ${(0, _templateHelper.writeTabs)(tab2)}require(_nextTokenOwner != address(0)); ${(0, _templateHelper.writeTabs)(tab2)} ${(0, _templateHelper.writeTabs)(tab2)}startTime = _startTime; ${(0, _templateHelper.writeTabs)(tab2)}endTime = _endTime; ${(0, _templateHelper.writeTabs)(tab2)}rate = _rate; ${(0, _templateHelper.writeTabs)(tab2)}cap = _cap; ${(0, _templateHelper.writeTabs)(tab2)}goal = _goal; ${(0, _templateHelper.writeTabs)(tab2)}crowdsaleRatio = _crowdsaleRatio; ${(0, _templateHelper.writeTabs)(tab2)}vault = MultiHolderVault(_vault); ${(0, _templateHelper.writeTabs)(tab2)}locker = Locker(_locker); ${(0, _templateHelper.writeTabs)(tab2)}nextTokenOwner = _nextTokenOwner; `; // append post contract declaration token.parentsList = [...token.parentsList, ...postToken.parentsList]; token.importStatements = [...token.importStatements, ...postToken.importStatements]; crowdsale.parentsList = [...crowdsale.parentsList, ...postCrowdsale.parentsList]; crowdsale.importStatements = [...crowdsale.importStatements, ...postCrowdsale.importStatements]; // constructor for The Crowdsale constructors.Crowdsale = []; crowdsale.parentsList.forEach(parent => { crowdsaleConstructorArgumentLength += constructors[parent].length; constructors.Crowdsale = [...constructors.Crowdsale, ...constructors[parent]]; }); return { meta, token, crowdsale, codes, constructors, initMigVars, crowdsaleConstructorArgumentLength }; } /* eslint-enable complexity */ } exports.default = Parser;