@buildship/hardhat-ipfs-upload
Version:
Hardhat IPFS uploader via Buildship
167 lines • 7.64 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const child_process_1 = require("child_process");
const config_1 = require("hardhat/config");
const minimist_1 = __importDefault(require("minimist"));
const nft_storage_1 = require("nft.storage");
const NFT_STORAGE_API_KEY = process.env.NFT_STORAGE_API_KEY;
const argv = minimist_1.default(process.argv.slice(2));
// Uploads a compiled contract to IPFS and returns its hash
config_1.task("upload", "Uploads a compiled contract to IPFS and returns deploy link")
.addPositionalParam("contract", "Contract to deploy")
.addOptionalParam("args", "Deploy arguments")
.addOptionalParam("ascii", "ASCII art file path (.txt)")
.setAction(async (taskArgs, hre) => {
try {
if (!NFT_STORAGE_API_KEY) {
console.error("Please put NFT_STORAGE_API_KEY in .env");
process.exit(-1);
}
const client = new nft_storage_1.NFTStorage({ token: NFT_STORAGE_API_KEY });
await hre.run("compile");
// console.log('process.argv', process.argv)
const { contract, args, ascii } = taskArgs;
console.log("Using contract", contract);
if (!contract) {
console.log(`Usage: npx hardhat upload [contract name] --args '"arg1","arg2"'`);
return;
}
if (!fs_1.default.existsSync(contract)) {
console.error(`Contract ${contract} not found`);
return;
}
// const factory = await hre.ethers.getContractFactory(contractName);
// extract filename from contract
const filename = contract.split("/").pop().replace(".sol", "");
if (!filename) {
console.log(`File has no name`);
return;
}
// read dir ./artifacts/${contract} and open the {filename}.json file
const contractArtifact = JSON.parse(fs_1.default
.readFileSync(`./artifacts/${contract}/${filename}.json`)
.toString());
let solcInput, solcLongVersion;
try {
const artifactDbg = `./artifacts/${contract}/${filename}.dbg.json`;
const { buildInfo } = JSON.parse(fs_1.default.readFileSync(artifactDbg).toString());
const buildInfoPath = path_1.default.join(`./artifacts/${contract}`, buildInfo);
const info = fs_1.default.readFileSync(buildInfoPath).toString();
const { solcLongVersion: version, input } = JSON.parse(info);
solcInput = input;
solcLongVersion = version;
console.log(`Build info: ${buildInfoPath}`);
}
catch (err) {
console.log(`Build info not found:\n`, err.message);
return;
}
const { abi, bytecode, ...artifact } = contractArtifact;
// if process.argv elements contain "help"
if (argv._.find((elem) => elem.includes("help"))) {
console.log(`Usage: npx hardhat upload [contract name] --args '"arg1","arg2"'`);
return;
}
let flattened;
try {
// try flattening contract
const sourcePath = contract;
// create dir ./tmp
if (!fs_1.default.existsSync("./tmp")) {
fs_1.default.mkdirSync("./tmp");
}
// await run("flatten", [ sourcePath, "./tmp/Flattened.sol" ]);
// const sh = `npx truffle-flattener ${sourcePath} | awk '/SPDX-License-Identifier/&&c++>0 {next} 1' | awk '/pragma experimental ABIEncoderV2;/&&c++>0 {next} 1' > ./tmp/Flattened.sol`;
const sh = `npx hardhat flatten "${sourcePath}" > ./tmp/Flattened.sol`;
// run the flattener
console.log("\nRunning command:", sh);
await new Promise((resolve, reject) => {
child_process_1.exec(sh, (err, stdout, stderr) => {
if (err || stderr) {
console.log("Error flattening contract:", err);
return reject(err);
}
// pipe stdout and stderr to console
// tslint:disable-next-line
stdout &&
console.log(`\nOutput: ${stdout.split("\n").join("\n\t")}`);
// tslint:disable-next-line
stderr &&
console.log(`\nErrors: ${stderr.split("\n").join("\n\t")}`);
resolve(true);
});
});
flattened = fs_1.default.readFileSync("./tmp/Flattened.sol", "utf8");
if (!flattened) {
throw new Error("No flattened contract");
}
// from https://github.com/boringcrypto/dictator-dao/blob/a3de9f606d05852eb5cfa811a3f38870ab22800a/hardhat.config.js#L60
// Remove every line started with "// SPDX-License-Identifier:"
flattened = flattened.replace(/SPDX-License-Identifier:/gm, "License-Identifier:");
flattened = `// SPDX-License-Identifier: MIXED\n\n${flattened}`;
// Remove every line started with "pragma experimental ABIEncoderV2;" except the first one
flattened = flattened.replace(/pragma experimental ABIEncoderV2;\n/gm, ((i) => (m) => (!i++ ? m : ""))(0));
flattened = flattened.trim();
if (ascii) {
// read file from ascii and paste in front of flattened
const art = fs_1.default.readFileSync(ascii, "utf8");
flattened = `${art}\n\n${flattened}`;
}
// write it back
fs_1.default.writeFileSync("./tmp/Flattened.sol", flattened);
}
catch (err) {
// process exit with error message
console.error(`\nError:`, err);
return;
}
finally {
// rm flattened file
// tslint:disable-next-line
!process.env.KEEP && fs_1.default.unlinkSync("./tmp/Flattened.sol");
// fs.rmdirSync("./tmp");
}
console.log(`\nDeploying ${contract}`);
// const longVersion = await getLongVersion(
// hre.config.solidity.compilers[0].version
// );
// const longVersionString = longVersion.replace(/^v/, "");
const contractInfo = {
name: contractArtifact.contractName,
filename,
abi,
bytecode,
artifact,
extra: {
metadata: JSON.stringify({
...hre.config.solidity.compilers[0],
compiler: {
...hre.config.solidity.compilers[0],
version: solcLongVersion,
},
}),
},
input: solcInput,
flattened,
};
const blob = new nft_storage_1.Blob([JSON.stringify(contractInfo)], {
type: "application/json",
});
const cid = await client.storeBlob(blob);
console.log(`Metadata uploaded to https://cloudflare-ipfs.com/ipfs/${cid}\n`);
console.log(`Deploy here:`);
const argsString = args
? `?args=%5B${encodeURIComponent(args)}%5D`
: "?args=%5B%5D";
console.log(`https://gate-goerli.buildship.xyz/deploy/${cid}${argsString}`);
}
catch (err) {
console.error(err);
}
});
//# sourceMappingURL=upload.js.map