From 01f332c5d459111439de19a07c818ddd62399558 Mon Sep 17 00:00:00 2001 From: fernandomg Date: Wed, 8 Nov 2017 17:21:27 -0300 Subject: [PATCH 1/3] Separate Contracts in files and zip --- package.json | 1 + src/components/stepFour/index.js | 64 +++++++--- src/components/stepFour/utils.js | 129 +++++++++---------- src/utils/constants.js | 205 +++++++++++++++++++++++++------ 4 files changed, 271 insertions(+), 128 deletions(-) diff --git a/package.json b/package.json index 36405b0d4..e78bb46ce 100755 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "clipboard": "^1.7.1", "install": "^0.10.1", "jquery": "^3.2.1", + "jszip": "^3.1.4", "mobx": "^3.3.0", "mobx-react": "^4.3.3", "npm": "^5.3.0", diff --git a/src/components/stepFour/index.js b/src/components/stepFour/index.js index daee95eab..2bc84937e 100644 --- a/src/components/stepFour/index.js +++ b/src/components/stepFour/index.js @@ -2,7 +2,7 @@ import React from 'react' import '../../assets/stylesheets/application.css'; import { deployContract, getWeb3, checkWeb3, getNetworkVersion } from '../../utils/blockchainHelpers' import { setLastCrowdsaleRecursive, addWhiteListRecursive, setFinalizeAgentRecursive, setMintAgentRecursive, setReleaseAgentRecursive, updateJoinedCrowdsalesRecursive, transferOwnership, setReservedTokensListMultiple, setLastCrowdsale } from './utils' -import {download, handleContractsForFile, handleTokenForFile, handleCrowdsaleForFile, handlePricingStrategyForFile, handleFinalizeAgentForFile, handleConstantForFile, scrollToBottom } from './utils' +import { download, handleContractsForFile, handleConstantForFile, handlerForFile, scrollToBottom } from './utils' import { noMetaMaskAlert, noContractDataAlert } from '../../utils/alerts' import { defaultState, FILE_CONTENTS, DOWNLOAD_NAME, DOWNLOAD_TYPE, TOAST } from '../../utils/constants' import { getOldState, toFixed, floorToDecimals, toast } from '../../utils/utils' @@ -14,6 +14,7 @@ import { DisplayTextArea } from '../Common/DisplayTextArea' import { Loader } from '../Common/Loader' import { NAVIGATION_STEPS, TRUNC_TO_DECIMALS } from '../../utils/constants' import { copy } from '../../utils/copy'; +import JSZip from 'jszip' const { PUBLISH } = NAVIGATION_STEPS export class stepFour extends stepTwo { @@ -131,30 +132,59 @@ export class stepFour extends stepTwo { }); }*/ - handleContentByParent(content, docData) { - switch(content.parent) { - case 'token': - return handleTokenForFile(content, docData, this.state) + handleContentByParent(content, index = 0) { + const { parent } = content + + switch (parent) { case 'crowdsale': - return handleCrowdsaleForFile(content, docData, this.state) - case 'contracts': - return handleContractsForFile(content, docData, this.state) case 'pricingStrategy': - return handlePricingStrategyForFile(content, docData, this.state) case 'finalizeAgent': - return handleFinalizeAgentForFile(content, docData, this.state) + return handlerForFile(content, this.state[parent][0]) + case 'token': + return handlerForFile(content, this.state[parent]) + case 'contracts': + return handleContractsForFile(content, this.state, index) case 'none': - return handleConstantForFile(content, docData) + return handleConstantForFile(content) } } - downloadCrowdsaleInfo() { - var docData = { data: '' } - FILE_CONTENTS.forEach(content => { - this.handleContentByParent(content, docData) + downloadCrowdsaleInfo = () => { + const zip = new JSZip() + const commonHeader = FILE_CONTENTS.common.map(content => this.handleContentByParent(content)) + const contractsKeys = FILE_CONTENTS.files.order + const NEW_LINE = '\n\n' + const orderNumber = order => order.toString().padStart(3, '0'); + let prefix = 1 + + contractsKeys.forEach(key => { + if (this.state.contracts.hasOwnProperty(key)) { + const { txt, sol, name } = FILE_CONTENTS.files[key] + const { abiConstructor } = this.state.contracts[key] + + const tiersCount = Array.isArray(abiConstructor) ? abiConstructor.length : 1 + + for (let tier = 0; tier < tiersCount; tier++) { + const suffix = tiersCount > 1 ? `_${tier + 1}` : '' + const solFilename = `${orderNumber(prefix++)}_${name}${suffix}` + const txtFilename = `${orderNumber(prefix++)}_${name}${suffix}` + + zip.file( + `${solFilename}.sol`, + this.handleContentByParent(sol) + ) + zip.file( + `${txtFilename}.txt`, + commonHeader.concat(txt.map(content => this.handleContentByParent(content, tier))).join(NEW_LINE) + ) + } + } }) - console.log('docDAta', docData.data) - download(docData.data, DOWNLOAD_NAME, DOWNLOAD_TYPE) + + zip.generateAsync({ type: DOWNLOAD_TYPE.blob }) + .then(content => { + download({ zip: content, filename: DOWNLOAD_NAME }) + }) } deploySafeMathLibrary = () => { diff --git a/src/components/stepFour/utils.js b/src/components/stepFour/utils.js index f5f695411..2c3d531f8 100644 --- a/src/components/stepFour/utils.js +++ b/src/components/stepFour/utils.js @@ -301,91 +301,78 @@ export function setReleaseAgentRecursive (i, web3, abi, addr, finalizeAgentAddrs }) } -export const handleTokenForFile = (content, docData, state) => { - const title = content.value - const fileContent = title + state.token[content.field] - docData.data += fileContent + '\n\n' +export const handlerForFile = (content, type) => { + return `${content.value}${type[content.field]}` } -export const handleCrowdsaleForFile = (content, docData, state) => { - const title = content.value - const fileContent = title + state.crowdsale[0][content.field] - docData.data += fileContent + '\n\n' +export const handleConstantForFile = content => { + return `${content.value}${content.fileValue}` } -export const handlePricingStrategyForFile = (content, docData, state) => { - const title = content.value - const fileContent = title + state.pricingStrategy[0][content.field] - docData.data += fileContent + '\n\n' -} +export const handleContractsForFile = (content, state, index) => { + const title = content.value + const { field } = content + let fileContent = '' -export const handleFinalizeAgentForFile = (content, docData, state) => { - const title = content.value - const fileContent = title + state.finalizeAgent[0][content.field] - docData.data += fileContent + '\n\n' -} + if (field !== 'src' && field !== 'abi' && field !== 'addr') { + const contractField = state.contracts[content.child][field] + let fileBody -export const handleContractsForFile = (content, docData, state) => { - const title = content.value - if(content.field !== 'src' && content.field !== 'abi' && content.field !== 'addr') { - let fileBody - if ( Object.prototype.toString.call( state.contracts[content.child][content.field] ) === '[object Array]' ) { - for (let i = 0; i < state.contracts[content.child][content.field].length; i++) { - fileBody = state.contracts[content.child][content.field][i] - - if (!fileBody) return - let fileContent = title + " for " + state.crowdsale[i].tier + ":**** \n \n" + fileBody - docData.data += fileContent + '\n\n' - } - } else { - fileBody = state.contracts[content.child][content.field] - if (!fileBody) return - let fileContent = title + ":**** \n \n" + fileBody - docData.data += fileContent + '\n\n' - } - } else { - addSrcToFile(content, docData, state) + if (Array.isArray(contractField)) { + fileBody = contractField[index] + + if (!!fileBody) { + fileContent = title + ' for ' + state.crowdsale[index].tier + ':**** \n\n' + fileBody + } + } else if (!!contractField) { + fileContent = title + ':**** \n\n' + contractField } -} + } else { + fileContent = addSrcToFile(content, state, index) + } -export const handleConstantForFile = (content, docData) => { - const title = content.value - const fileContent = title + content.fileValue - docData.data += fileContent + '\n\n' + return fileContent } -const addSrcToFile = (content, docData, state) => { - const title = content.value - - if ( Object.prototype.toString.call( state.contracts[content.child][content.field] ) === '[object Array]' && content.field !== 'abi') { - for (let i = 0; i < state.contracts[content.child][content.field].length; i++) { - const body = state.contracts[content.child][content.field][i] - const text = title + " for " + state.crowdsale[i].tier + ": " + body - docData.data += text + '\n\n' - } +const addSrcToFile = (content, state, index) => { + const title = content.value + const { field } = content + const contractField = state.contracts[content.child][field] + let fileContent = '' + + if (Array.isArray(contractField) && field !== 'abi') { + fileContent = title + ' for ' + state.crowdsale[index].tier + ': ' + contractField[index] + } else { + if (field !== 'src') { + const body = field === 'abi' ? JSON.stringify(contractField) : contractField + fileContent = title + body } else { - const body = content.field === 'abi' ? JSON.stringify(state.contracts[content.child][content.field]) : state.contracts[content.child][content.field] - const text = title + body - docData.data += text + '\n\n' + fileContent = contractField } + } + + return fileContent } -export const download = (data, filename, type) => { - var file = new Blob([data], {type: type}); - if (window.navigator.msSaveOrOpenBlob) // IE10+ - window.navigator.msSaveOrOpenBlob(file, filename); - else { // Others - var a = document.createElement("a"), - url = URL.createObjectURL(file); - a.href = url; - a.download = filename; - document.body.appendChild(a); - a.click(); - setTimeout(function() { - document.body.removeChild(a); - window.URL.revokeObjectURL(url); - }, 0); - } +export const download = ({ data = {}, filename = '', type = '', zip = '' }) => { + let file = !zip ? new Blob([data], { type: type }) : zip + + if (window.navigator.msSaveOrOpenBlob) { // IE10+ + window.navigator.msSaveOrOpenBlob(file, filename) + } else { // Others + let a = document.createElement('a') + let url = URL.createObjectURL(file) + + a.href = url + a.download = filename + document.body.appendChild(a) + a.click() + + setTimeout(function () { + document.body.removeChild(a) + window.URL.revokeObjectURL(url) + }, 0) + } } export function scrollToBottom() { diff --git a/src/utils/constants.js b/src/utils/constants.js index 4a3ff2e98..7c4287037 100644 --- a/src/utils/constants.js +++ b/src/utils/constants.js @@ -135,48 +135,173 @@ export const initialStepThreeValues = { }] } -export const FILE_CONTENTS = [ - { field: 'name', value: 'Token name: ', parent: 'token', x: 10, y: 10 }, - { field: 'ticker', value: 'Token ticker: ', parent: 'token', x: 10, y: 10 }, - { field: 'decimals', value: 'Token decimals: ', parent: 'token', x: 10, y: 10 }, - { field: 'walletAddress', value: 'Multisig wallet address: ', parent: 'crowdsale', x: 10, y: 10 }, - { value: '*****************************', parent: 'none', fileValue: '', x: 10, y: 10 }, - { field: 'rate', value: 'Crowdsale rate: ', parent: 'pricingStrategy', x: 10, y: 10 }, - { field: 'startTime', value: 'Crowdsale start time: ', parent: 'crowdsale', x: 10, y: 10 }, - { field: 'endTime', value: 'Crowdsale end time: ', parent: 'crowdsale', x: 10, y: 10 }, - { value: 'Compiler Version: ', parent: 'none', fileValue: '0.4.11', x: 10, y: 10 }, - { value: 'Is optimization enabled?: ', parent: 'none', fileValue: 'true', x: 10, y: 10 }, - { value: '*****************************', parent: 'none', fileValue: '', x: 10, y: 10 }, - { value: 'SafeMathLib library name: ', parent: 'none', fileValue: 'SafeMathLibExt', x: 10, y: 10 }, - { field: 'addr', value: 'SafeMathLib library address: ', parent: 'contracts', child: 'safeMathLib', x: 10, y: 10 }, - { value: 'Crowdsale contract name: ', parent: 'none', fileValue: 'MintedTokenCappedCrowdsaleExt', x: 10, y: 10 }, - { field: 'addr', value: 'Crowdsale contract address', parent: 'contracts', child: 'crowdsale', x: 10, y: 10 }, - { value: 'Token contract name: ', parent: 'none', fileValue: 'CrowdsaleTokenExt', x: 10, y: 10 }, - { field: 'addr', value: 'Token contract address: ', parent: 'contracts', child: 'token', x: 10, y: 10 }, - { value: 'Pricing strategy contract name: ', parent: 'none', fileValue: 'FlatPricingExt', x: 10, y: 10 }, - { field: 'addr', value: 'Pricing strategy contract address', parent: 'contracts', child: 'pricingStrategy', x: 10, y: 10 }, - { value: 'Null finalize agent contract name: ', parent: 'none', fileValue: 'NullFinalizeAgentExt', x: 10, y: 10 }, - { value: 'Finalize agent contract name: ', parent: 'none', fileValue: 'ReservedTokensFinalizeAgent', x: 10, y: 10 }, - { field: 'addr', value: 'Finalize agent contract address', parent: 'contracts', child: 'finalizeAgent', x: 10, y: 10 }, - { value: '*****************************', parent: 'none', fileValue: '', x: 10, y: 10 }, - { field: 'src', value: '****SafeMathLib contract source:**** \n \n', parent: 'contracts', child: 'safeMathLib', x: 10, y: 10 }, - { field: 'abi', value: '****SafeMathLib contract ABI:**** \n \n', parent: 'contracts', child: 'safeMathLib', x: 10, y: 10 }, - { field: 'src', value: '****Crowdsale contract source:**** \n \n', parent: 'contracts', child: 'crowdsale', x: 10, y: 10 }, - { field: 'abi', value: '****Crowdsale contract ABI:**** \n \n', parent: 'contracts', child: 'crowdsale', x: 10, y: 10 }, - { field: 'abiConstructor', value: '****Crowdsale contract ABI encoded constructor arguments', parent: 'contracts', child: 'crowdsale', x: 10, y: 10 }, - { field: 'src', value: '****Token contract source:**** \n \n', parent: 'contracts', child: 'token', x: 10, y: 10 }, - { field: 'abi', value: '****Token contract ABI:**** \n \n', parent: 'contracts', child: 'token', x: 10, y: 10 }, - { field: 'abiConstructor', value: '****Token contract ABI encoded constructor arguments', parent: 'contracts', child: 'token', x: 10, y: 10 }, - { field: 'src', value: '****Pricing strategy contract source:**** \n \n', parent: 'contracts', child: 'pricingStrategy', x: 10, y: 10 }, - { field: 'abi', value: '****Pricing strategy contract ABI:**** \n \n', parent: 'contracts', child: 'pricingStrategy', x: 10, y: 10 }, - { field: 'abiConstructor', value: '****Pricing strategy contract ABI encoded constructor arguments', parent: 'contracts', child: 'pricingStrategy', x: 10, y: 10 }, - { field: 'src', value: '****Finalize agent contract source:**** \n \n', parent: 'contracts', child: 'finalizeAgent', x: 10, y: 10 }, - { field: 'abi', value: '****Finalize agent contract ABI:**** \n \n', parent: 'contracts', child: 'finalizeAgent', x: 10, y: 10 }, - { field: 'abiConstructor', value: '****Finalize agent contract ABI encoded constructor arguments', parent: 'contracts', child: 'finalizeAgent', x: 10, y: 10 } -] +export const FILE_CONTENTS = { + common: [ + { field: 'name', value: 'Token name: ', parent: 'token' }, + { field: 'ticker', value: 'Token ticker: ', parent: 'token' }, + { field: 'decimals', value: 'Token decimals: ', parent: 'token' }, + { field: 'walletAddress', value: 'Multisig wallet address: ', parent: 'crowdsale' }, + { value: '*****************************', parent: 'none', fileValue: '' }, + { field: 'rate', value: 'Crowdsale rate: ', parent: 'pricingStrategy' }, + { field: 'startTime', value: 'Crowdsale start time: ', parent: 'crowdsale' }, + { field: 'endTime', value: 'Crowdsale end time: ', parent: 'crowdsale' }, + { value: 'Compiler Version: ', parent: 'none', fileValue: '0.4.11' }, + { value: 'Is optimization enabled?: ', parent: 'none', fileValue: 'true' }, + { value: '*****************************', parent: 'none', fileValue: '' } + ], + files: { + order: [ + 'safeMathLib', + 'token', + 'pricingStrategy', + 'nullFinalizeAgent', + 'finalizeAgent', + 'crowdsale' + ], + safeMathLib: { + name: 'SafeMathLibExt', + txt: [ + { value: 'SafeMathLib library name: ', parent: 'none', fileValue: 'SafeMathLibExt' }, + { field: 'addr', value: 'SafeMathLib library address: ', parent: 'contracts', child: 'safeMathLib' }, + { value: '*****************************', parent: 'none', fileValue: '' }, + { field: 'abi', value: '****SafeMathLib contract ABI:**** \n\n', parent: 'contracts', child: 'safeMathLib' } + ], + sol: { + field: 'src', + value: '****SafeMathLib contract source:**** \n\n', + parent: 'contracts', + child: 'safeMathLib' + } + + }, + token: { + name: 'MintedTokenCappedCrowdsaleExt', + txt: [ + { value: 'Crowdsale contract name: ', parent: 'none', fileValue: 'MintedTokenCappedCrowdsaleExt' }, + { field: 'addr', value: 'Crowdsale contract address', parent: 'contracts', child: 'crowdsale' }, + { value: '*****************************', parent: 'none', fileValue: '' }, + { field: 'abi', value: '****Crowdsale contract ABI:**** \n\n', parent: 'contracts', child: 'crowdsale' }, + { + field: 'abiConstructor', + value: '****Crowdsale contract ABI encoded constructor arguments', + parent: 'contracts', + child: 'crowdsale' + } + ], + sol: { field: 'src', value: '****Crowdsale contract source:**** \n\n', parent: 'contracts', child: 'crowdsale' } + + }, + crowdsale: { + name: 'CrowdsaleTokenExt', + txt: [ + { value: 'Token contract name: ', parent: 'none', fileValue: 'CrowdsaleTokenExt' }, + { field: 'addr', value: 'Token contract address: ', parent: 'contracts', child: 'token' }, + { value: '*****************************', parent: 'none', fileValue: '' }, + { field: 'abi', value: '****Token contract ABI:**** \n\n', parent: 'contracts', child: 'token' }, + { + field: 'abiConstructor', + value: '****Token contract ABI encoded constructor arguments', + parent: 'contracts', + child: 'token' + } + ], + sol: { field: 'src', value: '****Token contract source:**** \n\n', parent: 'contracts', child: 'token' } + + }, + pricingStrategy: { + name: 'FlatPricingExt', + txt: [ + { value: 'Pricing strategy contract name: ', parent: 'none', fileValue: 'FlatPricingExt' }, + { field: 'addr', value: 'Pricing strategy contract address', parent: 'contracts', child: 'pricingStrategy' }, + { value: '*****************************', parent: 'none', fileValue: '' }, + { + field: 'abi', + value: '****Pricing strategy contract ABI:**** \n\n', + parent: 'contracts', + child: 'pricingStrategy' + }, + { + field: 'abiConstructor', + value: '****Pricing strategy contract ABI encoded constructor arguments', + parent: 'contracts', + child: 'pricingStrategy' + } + ], + sol: { + field: 'src', + value: '****Pricing strategy contract source:**** \n\n', + parent: 'contracts', + child: 'pricingStrategy' + } + + }, + nullFinalizeAgent: { + name: 'NullFinalizeAgentExt', + txt: [ + { value: 'Null finalize agent contract name: ', parent: 'none', fileValue: 'NullFinalizeAgentExt' }, + { + field: 'addr', + value: 'Null finalize agent contract address', + parent: 'contracts', + child: 'nullFinalizeAgent' + }, + { value: '*****************************', parent: 'none', fileValue: '' }, + { + field: 'abi', + value: '****Null Finalize agent contract ABI:**** \n\n', + parent: 'contracts', + child: 'nullFinalizeAgent' + }, + { + field: 'abiConstructor', + value: '****Null Finalize agent contract ABI encoded constructor arguments', + parent: 'contracts', + child: 'nullFinalizeAgent' + } + ], + sol: { + field: 'src', + value: '****Null Finalize agent contract source:**** \n\n', + parent: 'contracts', + child: 'nullFinalizeAgent' + } + + }, + finalizeAgent: { + name: 'ReservedTokensFinalizeAgent', + txt: [ + { value: 'Finalize agent contract name: ', parent: 'none', fileValue: 'ReservedTokensFinalizeAgent' }, + { field: 'addr', value: 'Finalize agent contract address', parent: 'contracts', child: 'finalizeAgent' }, + { value: '*****************************', parent: 'none', fileValue: '' }, + { + field: 'abi', + value: '****Finalize agent contract ABI:**** \n\n', + parent: 'contracts', + child: 'finalizeAgent' + }, + { + field: 'abiConstructor', + value: '****Finalize agent contract ABI encoded constructor arguments', + parent: 'contracts', + child: 'finalizeAgent' + } + ], + sol: { + field: 'src', + value: '****Finalize agent contract source:**** \n\n', + parent: 'contracts', + child: 'finalizeAgent' + } + + } + } +} export const DOWNLOAD_NAME = 'contractInfo' -export const DOWNLOAD_TYPE = 'text/plain' +export const DOWNLOAD_TYPE = { + text: 'text/plain', + blob: 'blob' +} export const TOAST = { TYPE: { From 3dfb8b8d0ed28033714c821dc3f8761a2c246c77 Mon Sep 17 00:00:00 2001 From: fernandomg Date: Wed, 8 Nov 2017 17:42:39 -0300 Subject: [PATCH 2/3] Merge branch 'master' into 'separate-files-and-zip' and solved conflicts --- src/components/stepFour/index.js | 5 ++--- src/components/stepFour/utils.js | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/stepFour/index.js b/src/components/stepFour/index.js index da3f3c87e..ba5c1bade 100644 --- a/src/components/stepFour/index.js +++ b/src/components/stepFour/index.js @@ -10,12 +10,11 @@ import { setReleaseAgentRecursive, updateJoinedCrowdsalesRecursive, transferOwnership, - setReservedTokensListMultiple, - setLastCrowdsale + setReservedTokensListMultiple } from './utils' import { download, handleContractsForFile, handleConstantForFile, handlerForFile, scrollToBottom } from './utils' import { noMetaMaskAlert, noContractDataAlert } from '../../utils/alerts' -import { defaultState, FILE_CONTENTS, DOWNLOAD_NAME, DOWNLOAD_TYPE, TOAST } from '../../utils/constants' +import { defaultState, FILE_CONTENTS, DOWNLOAD_TYPE, TOAST } from '../../utils/constants' import { getOldState, toFixed, floorToDecimals, toast } from '../../utils/utils' import { getEncodedABIClientSide } from '../../utils/microservices' import { stepTwo } from '../stepTwo' diff --git a/src/components/stepFour/utils.js b/src/components/stepFour/utils.js index b2c0d0c17..5b8582652 100644 --- a/src/components/stepFour/utils.js +++ b/src/components/stepFour/utils.js @@ -399,7 +399,7 @@ export function getDownloadName (tokenAddress) { return networkName; }) - .then((networkName) => `${DOWNLOAD_NAME}_${networkName}_${tokenAddress}.txt`); + .then((networkName) => `${DOWNLOAD_NAME}_${networkName}_${tokenAddress}`); resolve(whenNetworkName); }); From 35b5dc8a8da4041aa8b916c3d1630bac8a09c201 Mon Sep 17 00:00:00 2001 From: viktor Date: Thu, 9 Nov 2017 13:23:11 +0300 Subject: [PATCH 3/3] Datetime offset for downloaded files --- src/components/stepFour/utils.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/components/stepFour/utils.js b/src/components/stepFour/utils.js index 5b8582652..49fd93267 100644 --- a/src/components/stepFour/utils.js +++ b/src/components/stepFour/utils.js @@ -309,7 +309,14 @@ export function setReleaseAgentRecursive (i, web3, abi, addr, finalizeAgentAddrs } export const handlerForFile = (content, type) => { - return `${content.value}${type[content.field]}` + let suffix = '' + let checkIfTime = content.field == "startTime" || content.field == "endTime" + if (checkIfTime) { + let timezoneOffset = (new Date()).getTimezoneOffset()/60 + let operator = timezoneOffset > 0 ? "-" : "+" + suffix = " (GMT " + operator + " " + Math.abs(timezoneOffset) + ")" + } + return `${content.value}${type[content.field]}` + suffix } export const handleConstantForFile = content => { @@ -326,7 +333,7 @@ export const handleContractsForFile = (content, state, index) => { let fileBody if (Array.isArray(contractField)) { - fileBody = contractField[index] + fileBody = contractField[index] if (!!fileBody) { fileContent = title + ' for ' + state.crowdsale[index].tier + ':**** \n\n' + fileBody