#!/usr/bin/env -S deno run -A import { ProgramDoesNotExistError, ProgramExitedError, ProgramTerminatedError, SubmitProgramError, } from "https://gear-js.deno.dev/api/src/errors/index.ts"; import { ProgramMap } from "https://gear-js.deno.dev/api/src/types/interfaces/index.ts"; import { Option } from "https://deno.land/x/polkadot@0.2.45/types/index.ts"; import { HexString } from "https://deno.land/x/polkadot@0.2.45/util/types.ts"; import { parse } from "https://deno.land/std/toml/mod.ts"; import { decodeAddress, GearApi, GearKeyring, } from "https://gear-js.deno.dev/api/index.ts"; // import { waitForInit } from "./waitForInit.ts"; import { postMetadata } from "./postMetadata.ts"; import { setCodeName, setProgramName } from "./setName.ts"; import { meta, metaHex } from "./meta.ts"; import { config } from "https://deno.land/x/dotenv/mod.ts"; import { metaVerify } from "./verify.ts"; import { code } from "./code.ts"; async function cargoBuild() { let cmd = ["cargo", "build"]; let p = Deno.run({ cmd }); let { code } = await p.status(); if (code !== 0) { throw Error(`Command '${cmd.join(" ")}' exited with code ${code}`); } } function packageName(): string { let cargoToml = Deno.readTextFileSync("Cargo.toml"); const parsedToml = parse(cargoToml) as { [key: string]: any }; const packageName = parsedToml["package"]["name"]; return packageName; } async function initGearApi(RPC_NODE: string) { return await GearApi.create({ providerAddress: RPC_NODE, }); } async function uploadProgram(): string { let program = { code: await code(), gasLimit: 1000000000, value: 0, // initPayload: "0x00", }; let { codeId } = await api.program.upload( program, // meta, ); if (!await api.code.exists(codeId)) { // console.log("CodeID not found, uploading..."); await new Promise((resolve, reject) => { api.program.signAndSend(alice, ({ events, status }) => { // console.log(`STATUS: ${status.toString()}`); if (status.isFinalized) { resolve(status.asFinalized); } events.forEach(({ event }) => { if (event.method === "ExtrinsicFailed") { reject(api.getExtrinsicFailedError(event).docs.join("\n")); } }); }); }); } else { // console.log("CodeID already exists, skipping upload..."); } return codeId; } async function deployProgram(codeId: string) { let aliceHex = decodeAddress(alice.address); // console.log("decodedAddress:", aliceHex); let gas = await api.program.calculateGas.initCreate( aliceHex, codeId, INIT_PAYLOAD, 0, true, meta(), ); // console.log(`GasLimit: ${gas}\n`); let { programId, extrinsic } = api.program.create({ codeId, initPayload: INIT_PAYLOAD, gasLimit: gas.min_limit, }, meta()); // console.log({ codeId, programId }); let out = await new Promise((resolve, reject) => { api.program.signAndSend(alice, ({ events, status }) => { // console.log(`STATUS: ${status.toString()}`); if (status.isFinalized) { resolve(status.asFinalized); } events.forEach(({ event }) => { if (event.method === "ExtrinsicFailed") { reject(api.getExtrinsicFailedError(event).docs.join("\n")); } }); }); }); return programId; } async function makeSetCodeNamePayload(codeId: string) { for (let i = 0; i < 10; i++) { await new Promise((resolve) => setTimeout(resolve, 3000)); // assert program exists if (await api.code.exists(codeId)) { break; } } let genesis = api.genesisHash.toHex(); let params = { genesis, codeId, name: PROGRAM_NAME, }; return params; } async function makeSetProgramNamePayload(programId: string) { for (let i = 0; i < 10; i++) { await new Promise((resolve) => setTimeout(resolve, 3000)); // assert program exists if (await api.program.exists(programId)) { break; } } let genesis = api.genesisHash.toHex(); let params = { genesis, programId, name: PROGRAM_NAME, }; return params; } async function makePostMetadataPayload(codeId: string) { for (let i = 0; i < 10; i++) { await new Promise((resolve) => setTimeout(resolve, 3000)); // assert program exists if (await api.code.exists(codeId)) { break; } } let genesis = api.genesisHash.toHex(); let params = { genesis, metaHex: metaHex(), codeHash: codeId, }; return params; } // `wss://rpc-node.gear-tech.io`; const DEFAULT_RPC_NODE = `wss://testnet.vara.rs`; // const DEFAULT_RPC_NODE = `wss://node-workshop.gear.rs`; async function init() { let dotenv = config(); let PROGRAM_NAME = Deno.env.get("PROGRAM_NAME") || dotenv.PROGRAM_NAME || packageName() || "unknown"; let RPC_NODE = Deno.env.get("RPC_NODE") || dotenv.RPC_NODE || DEFAULT_RPC_NODE; let DEV_KEY = Deno.env.get("DEV_KEY") || dotenv.DEV_KEY || "//Alice"; let INIT_PAYLOAD = parseJSON( Deno.env.get("INIT_PAYLOAD") || dotenv.INIT_PAYLOAD || "null", ) || "0x00"; console.log("Package Name:", PROGRAM_NAME); console.log(`api (${RPC_NODE}) is initializing. Please hold on...`); let api = await initGearApi(RPC_NODE); let alice = await GearKeyring.fromSuri(DEV_KEY); let { data: { free } } = await api.query.system.account(alice.address); console.log(`Dev key: ${alice.address}; free balance:`, free.toHuman()); return { PROGRAM_NAME, RPC_NODE, INIT_PAYLOAD, api, alice, }; } let { PROGRAM_NAME, RPC_NODE, INIT_PAYLOAD, alice, api } = await init(); async function checkInit() { let metadata = meta(); if (INIT_PAYLOAD === null && metadata.types.init.input !== null) { throw Error( "Your contract requires an init input, but INIT_PAYLOAD is null. Deployment might fail!", ); } } async function main() { await cargoBuild(); await checkInit(); console.info("Skip verifying metadata..."); // metaVerify(); console.info("Uploading program..."); let codeId = await uploadProgram(); console.info({ codeId }); console.info("Deploying program..."); let programId = await deployProgram(codeId); console.info({ programId }); console.info("Making post metadata payload..."); let metaParams = await makePostMetadataPayload(codeId); console.info(metaParams); console.info("Posting metadata..."); let metaResp = await postMetadata(metaParams); console.info(metaResp); console.info("Making setCodeName payload..."); let codeNameParams = await makeSetCodeNamePayload(codeId); console.info(codeNameParams); console.info("Setting code name..."); let codeNameResp = await setCodeName(codeNameParams); console.info(codeNameResp); console.info("Making setProgramName payload..."); let programNameParams = await makeSetProgramNamePayload(programId); console.info(programNameParams); console.info("Setting program name..."); let programNameResp = await setProgramName(programNameParams); console.info(programNameResp); console.info( "Program deloyed:", `https://idea.gear-tech.io/programs/${programId}?node=${RPC_NODE}`, ); // Deno.writeTextFileSync( "./dist/deploy.json", JSON.stringify( { codeId, programId, RPC_NODE, }, null, " ",),); } main().then(() => Deno.exit(0)); function parseJSON(str: string): any | null { if (str.length === 0) { return null; } try { return JSON.parse(str); } catch (error) { console.error("Failed to parse JSON:", error); return null; } }