@massalabs/massa-sc-utils
Version:
Massa smart contract utils
187 lines (185 loc) • 9.88 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.deployWebsite = void 0;
const tslib_1 = require("tslib");
const massa_web3_1 = require("@massalabs/massa-web3");
const SmartContractDeployer_1 = require("./SmartContractDeployer");
const path = require("path");
const fs = require("fs");
const chalk = require("chalk");
const Handlebars = require("handlebars");
const INIT_SC = `import { generate_event, create_sc, Storage } from 'massa-sc-std';
function initWebsite(): string {
const bytes = '{{bytes}}';
const sc_address = create_sc(bytes);
return sc_address;
}
export function main(_args: string): i32 {
const sc_address = initWebsite();
const eventMsg = "Address:" + sc_address;
generate_event(eventMsg);
return 0;
}
`;
const CHUNK_SC = `import { generate_event, Storage } from 'massa-sc-std';
function appendBytesToWebsite(): void {
const bytes = '{{bytes}}';
Storage.append_data_for('{{scAddress}}', 'massa_web', bytes);
}
export function main(_args: string): i32 {
appendBytesToWebsite();
generate_event('Appended bytes chunk {{chunkIndex}} for contract@address: {{scAddress}}');
return 0;
}
`;
const DEFAULT_MAX_BLOCK_BYTES = 50000;
const initScTemplate = Handlebars.compile(INIT_SC);
const appendChunksToScTemplate = Handlebars.compile(CHUNK_SC);
const deployInitialScChunk = (websiteZipFile, chunkBase64Bytes, chunkIndex, tempASFiles, contractData, web3Client, deployerAccount, max_block_bytes) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
const batchBytes = chunkBase64Bytes.substring(0, max_block_bytes);
const scToCompile = initScTemplate({ bytes: batchBytes });
const tempFileName = `temp-${path.basename(websiteZipFile)}-${chunkIndex}.ts`;
const scDeployFileTemp = `${path.dirname(websiteZipFile)}/${tempFileName}`;
tempASFiles.push(scDeployFileTemp);
fs.writeFileSync(scDeployFileTemp, scToCompile, { flag: "w+" });
const opId = yield (0, SmartContractDeployer_1.deploySmartContract)(scDeployFileTemp, contractData, web3Client, true, deployerAccount);
return opId;
});
const deploySuccessiveScChunk = (websiteZipFile, chunkBase64Bytes, chunkIndex, tempASFiles, contractData, web3Client, deployerAccount, scAddress, max_block_bytes) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
const batchBytes = chunkBase64Bytes.substring(0, max_block_bytes);
const scToCompile = appendChunksToScTemplate({ bytes: batchBytes, chunkIndex: chunkIndex, scAddress: scAddress });
const tempFileName = `temp-${path.basename(websiteZipFile)}-${chunkIndex}.ts`;
const scDeployFileTemp = `${path.dirname(websiteZipFile)}/${tempFileName}`;
tempASFiles.push(scDeployFileTemp);
fs.writeFileSync(scDeployFileTemp, scToCompile, { flag: "w+" });
const opId = yield (0, SmartContractDeployer_1.deploySmartContract)(scDeployFileTemp, contractData, web3Client, true, deployerAccount);
return opId;
});
const deployWebsite = (websiteZipFile, contractData, web3Client, awaitFinalization, deployerAccount) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
const header = "=".repeat(process.stdout.columns - 1);
console.log(header);
console.log(`Deploying website...`);
console.log(header);
const tempASFiles = [];
const deploymentTxOpIds = [];
try {
// do checks
if (!deployerAccount.address) {
const msg = chalk.red(`Missing deployer account. Maybe you forgot to add one ?`);
console.error(msg);
throw new Error(msg);
}
// set default values in case of missing ones
contractData.maxGas = contractData.maxGas || 200000;
contractData.fee = contractData.fee || 0;
contractData.datastore = new Map();
// compute current max block size and take max 50% of its capacity
let max_block_bytes = DEFAULT_MAX_BLOCK_BYTES;
try {
const max_block_size_massa = (yield web3Client.publicApi().getNodeStatus()).config.max_block_size;
console.log(`Massa max. block size = ${chalk.yellow(max_block_size_massa)}`);
max_block_bytes = Math.floor(max_block_size_massa * 0.45);
console.log(`Using max. block size = ${chalk.yellow(max_block_bytes)}`);
}
catch (ex) {
const msg = chalk.red(`Could not determine the max block size. Using a default block size of ${DEFAULT_MAX_BLOCK_BYTES}`);
console.warn(msg);
}
// read wasm file contents in base 64
const contents = fs.readFileSync(websiteZipFile, "base64");
const bytes = Buffer.byteLength(contents, "utf-8");
// split data into smaller batches if necessary
const nBatches = Math.floor(bytes / max_block_bytes);
console.log(`Wasm File will be split into ${nBatches} files`);
const lastBatchPortion = bytes % max_block_bytes;
if (nBatches > 0) {
// deploy the very first chunk to get the new sc address
let deploymentOperationId = null;
try {
deploymentOperationId = yield deployInitialScChunk(websiteZipFile, contents.substring(0, max_block_bytes), 0, tempASFiles, contractData, web3Client, deployerAccount, max_block_bytes);
deploymentTxOpIds.push(deploymentOperationId);
}
catch (ex) {
const msg = chalk.red(`Error deploying first sc chunk`);
console.error(msg);
throw new Error(ex);
}
// await final state
console.log(`Awaiting ${chalk.green("FINAL")} transaction status....`);
let status;
try {
status = yield web3Client.smartContracts().awaitRequiredOperationStatus(deploymentOperationId, massa_web3_1.EOperationStatus.FINAL);
console.log(`Transaction with Operation ID ${chalk.yellow(deploymentOperationId)} has reached finality!`);
}
catch (ex) {
const msg = chalk.red(`Error getting finality of transaction ${chalk.yellow(deploymentOperationId)}`);
console.error(msg);
throw new Error(ex);
}
if (status !== massa_web3_1.EOperationStatus.FINAL) {
const msg = chalk.red(`Transaction ${chalk.yellow(deploymentOperationId)} did not reach finality after considerable amount of time. Try redeploying anew`);
console.error(msg);
throw new Error(msg);
}
// poll smart contract events for the opId and find an event that contains the emitted sc address
console.log(`Getting smart contract address...`);
let scAddress;
try {
const eventsFilter = {
start: null,
end: null,
original_caller_address: null,
original_operation_id: deploymentOperationId,
emitter_address: null,
};
const events = yield massa_web3_1.EventPoller.getEventsOnce(eventsFilter, web3Client);
const addressEvent = events.find(event => event.data.includes("Address:"));
if (!addressEvent) {
throw new Error("No events were emitted from contract containing a message `Address:...`. Please make sure to include such a message in order to fetch the sc address");
}
scAddress = addressEvent.data.split(":")[1];
console.log(`Smart Contract Address = ${chalk.yellow(scAddress)}`);
}
catch (ex) {
const msg = chalk.red(`Error getting smart contract address. Maybe you forgot to emit an event of the type "Address:xyz" ?`);
console.error(msg);
throw new Error(msg);
}
// loop over the remaining chunks and deploy them by appending
let startBatch = max_block_bytes;
let endBatch = 0;
for (let chunkIndex = 1; chunkIndex < nBatches - 1; chunkIndex++) {
endBatch = startBatch + max_block_bytes;
const batchBytes = contents.substring(startBatch, endBatch);
console.log(`Next Bytes Batch ${chunkIndex} - length ${batchBytes.length} bytes`);
deploymentTxOpIds.push(yield deploySuccessiveScChunk(websiteZipFile, batchBytes, chunkIndex, tempASFiles, contractData, web3Client, deployerAccount, scAddress, max_block_bytes));
startBatch = endBatch;
}
console.log(`Final Batch ${nBatches} - length ${contents.substring(endBatch).length} bytes`);
deploymentTxOpIds.push(yield deploySuccessiveScChunk(websiteZipFile, contents.substring(endBatch), nBatches, tempASFiles, contractData, web3Client, deployerAccount, scAddress, max_block_bytes));
}
else {
console.log(`Single Batch ${0} - length ${lastBatchPortion} bytes`);
deploymentTxOpIds.push(yield deployInitialScChunk(websiteZipFile, contents, 0, tempASFiles, contractData, web3Client, deployerAccount, max_block_bytes));
}
}
catch (ex) {
const msg = chalk.red(`Error deploying website zip file ${chalk.yellow(websiteZipFile)} to Massa Network`);
console.error(msg);
throw new Error(ex);
}
finally {
// delete temp .ts files
for (let tempFileIndex = 0; tempFileIndex < tempASFiles.length; tempFileIndex++) {
if (fs.existsSync(tempASFiles[tempFileIndex])) {
//fs.unlinkSync(tempASFiles[tempFileIndex]);
}
}
}
console.log(header);
console.log(`Website Deployment finished!`);
console.log(header);
return deploymentTxOpIds;
});
exports.deployWebsite = deployWebsite;
//# sourceMappingURL=WebsiteDeployer.js.map