From 9613c4543b23c8ee13fa5b09b7832a1b420a2f99 Mon Sep 17 00:00:00 2001 From: FabijanC Date: Tue, 14 Dec 2021 12:41:07 +0100 Subject: [PATCH] Refactor running Starknet commands to fix devnet interaction on macOS+venv (#29) * Fix path documentation for starknet-verify * Support different mainnet namings * Remove unused methods * Refactor runCommand --- README.md | 8 +- src/constants.ts | 3 + src/index.ts | 140 +++++++++------------- src/starknet-wrappers.ts | 244 ++++++++++++++++++++++++++++++++++----- src/types.ts | 90 +++++++-------- 5 files changed, 316 insertions(+), 169 deletions(-) diff --git a/README.md b/README.md index f866f8a7..d54fe565 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,9 @@ module.exports = { ``` you can use it by calling `npx hardhat starknet-deploy --starknet-network myNetwork`. -The Alpha testnet and mainnet are available by default, you don't need to define them in the config file; just pass them with `--starknet-network alpha` or `--starknet-network alphaMainnet`. +The Alpha networks are available by default, you don't need to define them in the config file; just pass: +- `--starknet-network alpha` or `--starknet-network goerli-alpha` for Alpha Testnet (on Goerli) +- `--starknet-network alpha-mainnet` for Alpha Mainnet If you're passing constructor arguments, pass them space separated, but as a single string (due to limitations of the plugin system). ``` @@ -57,10 +59,10 @@ You would typically use the input feature when deploying a single contract requi ### `starknet-verify` ``` -npx hardhat starknet-verify [--starknet-network ] [--path PATH/TO/CONTRACT] [--address CONTRACT_ADDRESS] +npx hardhat starknet-verify [--starknet-network ] [--path ] [--address ] ``` -Queries Voyager to verify `PATH/TO/CONTRACT.cairo` deployed at `CONTRACT_ADDRESS` +Queries [Voyager](https://voyager.online/) to [verify the contract](https://voyager.online/verifyContract) deployed at `` using the source file at ``. Like in the previous command, this plugin relies on `--starknet-network`, but will default to 'alpha' network in case this parameter is not passed. diff --git a/src/constants.ts b/src/constants.ts index 8c618b6f..a758eff6 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -5,12 +5,15 @@ export const DEFAULT_STARKNET_SOURCES_PATH = "contracts"; export const DEFAULT_STARKNET_ARTIFACTS_PATH = "starknet-artifacts"; export const DOCKER_REPOSITORY = "shardlabs/cairo-cli"; export const DEFAULT_DOCKER_IMAGE_TAG = "0.6.2"; + export const ALPHA_TESTNET = "goerli-alpha"; export const ALPHA_TESTNET_INTERNALLY = "alpha"; export const ALPHA_MAINNET = "alpha-mainnet"; +export const ALPHA_MAINNET_INTERNALLY = "alphaMainnet"; export const DEFAULT_STARKNET_NETWORK = ALPHA_TESTNET_INTERNALLY; export const ALPHA_URL = "https://alpha4.starknet.io"; export const ALPHA_MAINNET_URL = "https://alpha-mainnet.starknet.io"; + export const CHECK_STATUS_TIMEOUT = 1000; // ms export const LEN_SUFFIX = "_len"; export const VOYAGER_GOERLI_CONTRACT_API_URL = "https://goerli.voyager.online/api/contract/"; diff --git a/src/index.ts b/src/index.ts index 971357d9..5607691c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,9 +6,9 @@ import { HardhatPluginError } from "hardhat/plugins"; import { ProcessResult } from "@nomiclabs/hardhat-docker"; import "./type-extensions"; import { StarknetContractFactory, iterativelyCheckStatus, extractTxHash } from "./types"; -import { PLUGIN_NAME, ABI_SUFFIX, DEFAULT_STARKNET_SOURCES_PATH, DEFAULT_STARKNET_ARTIFACTS_PATH, DEFAULT_DOCKER_IMAGE_TAG, DOCKER_REPOSITORY, DEFAULT_STARKNET_NETWORK, ALPHA_URL, ALPHA_MAINNET_URL, VOYAGER_GOERLI_CONTRACT_API_URL, VOYAGER_MAINNET_CONTRACT_API_URL, ALPHA_MAINNET, ALPHA_TESTNET, ALPHA_TESTNET_INTERNALLY} from "./constants"; +import { PLUGIN_NAME, ABI_SUFFIX, DEFAULT_STARKNET_SOURCES_PATH, DEFAULT_STARKNET_ARTIFACTS_PATH, DEFAULT_DOCKER_IMAGE_TAG, DOCKER_REPOSITORY, DEFAULT_STARKNET_NETWORK, ALPHA_URL, ALPHA_MAINNET_URL, VOYAGER_GOERLI_CONTRACT_API_URL, VOYAGER_MAINNET_CONTRACT_API_URL, ALPHA_MAINNET, ALPHA_TESTNET, ALPHA_TESTNET_INTERNALLY, ALPHA_MAINNET_INTERNALLY} from "./constants"; import { HardhatConfig, HardhatRuntimeEnvironment, HardhatUserConfig, HttpNetworkConfig } from "hardhat/types"; -import { adaptLog, adaptUrl, getDefaultHttpNetworkConfig } from "./utils"; +import { adaptLog, getDefaultHttpNetworkConfig } from "./utils"; import { DockerWrapper, VenvWrapper } from "./starknet-wrappers"; import { glob } from "glob"; import { promisify } from "util"; @@ -65,14 +65,6 @@ function processExecuted(executed: ProcessResult): number { return executed.statusCode ? 1 : 0; } -function hasCairoExtension(filePath: string) { - return path.extname(filePath) === ".cairo"; -} - -function isStarknetContract(filePath: string) { - return hasCairoExtension(filePath); -} - function isStarknetCompilationArtifact(filePath: string) { const content = fs.readFileSync(filePath).toString(); let parsed = null; @@ -90,26 +82,15 @@ function getFileName(filePath: string) { } /** - * Populate `pathsObj` with paths from `colonSeparatedStr`. - * `pathsObj` maps a path to itself. - * @param pathsObj - * @param colonSeparatedStr + * First deletes the file if it already exists. Then creates an empty file at the provided path. + * Unlinking/deleting is necessary if user switched from docker to venv. + * @param filePath the file to be recreated */ -function addPaths(pathsObj: any, colonSeparatedStr: string): void { - for (let p of colonSeparatedStr.split(":")) { - if (!path.isAbsolute(p)) { - throw new HardhatPluginError(PLUGIN_NAME, `Path is not absolute: ${p}`); - } - - // strip trailing slash(es) - p = p.replace(/\/*$/, ""); - - // duplicate paths will cause errors - if (`${p}/` in pathsObj) { - continue; - } - pathsObj[p] = p; +function initializeFile(filePath: string) { + if (fs.existsSync(filePath)) { + fs.unlinkSync(filePath); } + fs.closeSync(fs.openSync(filePath, "w")); } // add sources path @@ -221,32 +202,19 @@ task("starknet-compile", "Compiles Starknet contracts") const outputPath = path.join(dirPath, `${fileName}.json`); const abiPath = path.join(dirPath, `${fileName}${ABI_SUFFIX}`); const cairoPath = (defaultSourcesPath + ":" + root) + (args.cairoPath ? ":" + args.cairoPath : ""); - const compileArgs = [ - file, - "--output", outputPath, - "--abi", abiPath, - "--cairo_path", cairoPath, - ]; - const binds = { - [sourcesPath]: sourcesPath, - [artifactsPath]: artifactsPath, - }; - addPaths(binds, cairoPath); - // unlinking/deleting is necessary if user switched from docker to venv - if (fs.existsSync(outputPath)) { - fs.unlinkSync(outputPath); - } - if (fs.existsSync(abiPath)) { - fs.unlinkSync(abiPath); - } + fs.mkdirSync(dirPath, { recursive: true }); - const executed = await hre.starknetWrapper.runCommand( - "starknet-compile", - compileArgs, - Object.keys(binds) - ); + initializeFile(outputPath); + initializeFile(abiPath); - statusCode +=processExecuted(executed); + const executed = await hre.starknetWrapper.compile({ + file, + output: outputPath, + abi: abiPath, + cairoPath, + }); + + statusCode += processExecuted(executed); } } @@ -261,6 +229,11 @@ function isTestnet(networkName: string): boolean { || networkName === ALPHA_TESTNET_INTERNALLY; } +function isMainnet(networkName: string): boolean { + return networkName === ALPHA_MAINNET + || networkName === ALPHA_MAINNET_INTERNALLY; +} + /** * Extracts gatewayUrl from args or process.env.STARKNET_NETWORK. Sets hre.starknet.network if provided. * @@ -270,8 +243,12 @@ function isTestnet(networkName: string): boolean { */ function getGatewayUrl(args: any, hre: HardhatRuntimeEnvironment): string { let gatewayUrl: string = args.gatewayUrl; - const networkName: string = args.starknetNetwork || process.env.STARKNET_NETWORK; - + let networkName: string = args.starknetNetwork || process.env.STARKNET_NETWORK; + if (isMainnet(networkName)) { + networkName = ALPHA_MAINNET_INTERNALLY; + } else if (isTestnet(networkName)) { + networkName = ALPHA_TESTNET_INTERNALLY; + } if (gatewayUrl && !networkName) { return gatewayUrl; } @@ -314,11 +291,6 @@ task("starknet-deploy", "Deploys Starknet contracts which have been compiled.") const defaultArtifactsPath = hre.config.paths.starknetArtifacts; const artifactsPaths: string[] = args.paths || [defaultArtifactsPath]; - const inputs: string[] = []; - if (args.inputs) { - inputs.push("--inputs", ...args.inputs.split(/\s+/)); - } - let statusCode = 0; const txHashes: string[] = []; for (let artifactsPath of artifactsPaths) { @@ -331,16 +303,11 @@ task("starknet-deploy", "Deploys Starknet contracts which have been compiled.") const files = paths.filter(isStarknetCompilationArtifact); for(const file of files){ console.log("Deploying", file); - const executed = await hre.starknetWrapper.runCommand( - "starknet", - [ - "deploy", - "--contract", file, - "--gateway_url", adaptUrl(gatewayUrl), - ...inputs - ], - [artifactsPath] - ); + const executed = await hre.starknetWrapper.deploy({ + contract: file, + gatewayUrl, + inputs: args.inputs ? args.inputs.split(/\s+/) : undefined, + }); if(args.wait){ const execResult = processExecuted(executed); if(execResult == 0){ @@ -348,28 +315,28 @@ task("starknet-deploy", "Deploys Starknet contracts which have been compiled.") } statusCode += execResult; } - else + else { statusCode += processExecuted(executed); + } } } - if(args.wait){ // If the "wait" flag was passed as an argument, check the previously stored transaction hashes for their statuses + if (args.wait){ // If the "wait" flag was passed as an argument, check the previously stored transaction hashes for their statuses console.log("Checking deployment transactions..."); - const promises = txHashes.map( hash => new Promise((resolve, reject) => {iterativelyCheckStatus( + const promises = txHashes.map( hash => new Promise((resolve, reject) => iterativelyCheckStatus( hash, hre.starknetWrapper, gatewayUrl, gatewayUrl, - () => { - console.log("Deployment transaction " + hash + " status is now PENDING"); + status => { + console.log(`Deployment transaction ${hash} is now ${status}`); resolve(); }, - (error) => { - console.log("Deployment transaction " + hash + " status is REJECTED"); + error => { + console.log(`Deployment transaction ${hash} is REJECTED`); reject(error); } - ) - })); + ))); await Promise.allSettled(promises); } @@ -380,13 +347,11 @@ task("starknet-deploy", "Deploys Starknet contracts which have been compiled.") async function findPath(traversable: string, name: string) { let files = await traverseFiles(traversable); - files = files.filter(file => { - return file.endsWith(name); - }); - if(files.length == 0){ + files = files.filter(f => f.endsWith(name)); + if (files.length == 0){ return null; } - else if(files.length == 1){ + else if (files.length == 1){ return files[0]; } else { @@ -444,15 +409,16 @@ task("starknet-verify", "Verifies the contract in the Starknet network.") .setAction(async (args, hre) => { let voyagerUrl = VOYAGER_GOERLI_CONTRACT_API_URL; - if(!isTestnet(args.starknetNetwork)){ - if(args.starknetNetwork === ALPHA_MAINNET) + if (!isTestnet(args.starknetNetwork)) { + if (isMainnet(args.starknetNetwork)) { voyagerUrl = VOYAGER_MAINNET_CONTRACT_API_URL; - else{ + } else { const msg = `Unknown starknet-network provided: ${args.starknetNetwork}`; throw new HardhatPluginError(PLUGIN_NAME, msg); } } - voyagerUrl+=args.address + "/code"; + + voyagerUrl += args.address + "/code"; let isVerified = false; try{ const resp = await axios.get(voyagerUrl,{ diff --git a/src/starknet-wrappers.ts b/src/starknet-wrappers.ts index 85c4d155..595832b8 100644 --- a/src/starknet-wrappers.ts +++ b/src/starknet-wrappers.ts @@ -4,22 +4,142 @@ import * as fs from "fs"; import { HardhatPluginError } from "hardhat/plugins"; import * as path from "path"; import { PLUGIN_NAME } from "./constants"; +import { Choice } from "./types"; +import { adaptUrl } from "./utils"; -export type StarknetCommand = "starknet" | "starknet-compile"; +export interface CompileOptions { + file: string, + output: string, + abi: string, + cairoPath: string, +} + +export interface DeployOptions { + contract: string, + gatewayUrl: string, + inputs?: string[], + signature?: string[], +} + +export interface InvokeOrCallOptions { + choice: Choice, + address: string, + abi: string, + functionName: string, + inputs?: string[], + signature?: string[], + gatewayUrl: string, + feederGatewayUrl: string, +} -export interface StarknetWrapper { - runCommand(command: StarknetCommand, args: string[], paths?: string[]): Promise; +export interface GetTxStatusOptions { + hash: string, + gatewayUrl: string, + feederGatewayUrl: string, +} + +export abstract class StarknetWrapper { + protected prepareCompileOptions(options: CompileOptions): string[] { + return [ + options.file, + "--abi", options.abi, + "--output", options.output, + "--cairo_path", options.cairoPath + ]; + } + + public abstract compile(options: CompileOptions): Promise; + + protected prepareDeployOptions(options: DeployOptions): string[] { + const prepared = [ + "deploy", + "--contract", options.contract, + "--gateway_url", options.gatewayUrl, + ]; + + if (options.inputs && options.inputs.length) { + prepared.push("--inputs", ...options.inputs); + } + + if (options.signature && options.signature.length) { + prepared.push("--signature", ...options.signature); + } + + return prepared; + } + + public abstract deploy(options: DeployOptions): Promise; + + protected prepareInvokeOrCalOptions(options: InvokeOrCallOptions): string[] { + const prepared = [ + options.choice, + "--abi", options.abi, + "--feeder_gateway_url", options.feederGatewayUrl, + "--gateway_url", options.gatewayUrl, + "--function", options.functionName, + "--address", options.address, + ]; + + if (options.inputs && options.inputs.length) { + prepared.push("--inputs", ...options.inputs); + } + + if (options.signature && options.signature.length) { + prepared.push("--signature", ...options.signature); + } + + return prepared; + } + + public abstract invokeOrCall(options: InvokeOrCallOptions): Promise; + + protected prepareGetTxStatusOptions(options: GetTxStatusOptions): string[] { + return [ + "tx_status", + "--hash", options.hash, + "--gateway_url", options.gatewayUrl, + "--feeder_gateway_url", options.feederGatewayUrl, + ]; + } + + public abstract getTxStatus(options: GetTxStatusOptions): Promise; } function getFullImageName(image: Image): string { return `${image.repository}:${image.tag}`; } -export class DockerWrapper implements StarknetWrapper { +type String2String = { [path: string]: string }; + +/** + * Populate `paths` with paths from `colonSeparatedStr`. + * `paths` maps a path to itself. + * @param paths + * @param colonSeparatedStr + */ + function addPaths(paths: String2String, colonSeparatedStr: string): void { + for (let p of colonSeparatedStr.split(":")) { + if (!path.isAbsolute(p)) { + throw new HardhatPluginError(PLUGIN_NAME, `Path is not absolute: ${p}`); + } + + // strip trailing slash(es) + p = p.replace(/\/*$/, ""); + + // duplicate paths will cause errors + if (`${p}/` in paths) { + continue; + } + paths[p] = p; + } +} + +export class DockerWrapper extends StarknetWrapper { private docker: HardhatDocker; private image: Image; constructor(image: Image) { + super(); this.image = image; console.log(`${PLUGIN_NAME} plugin using dockerized environment (${getFullImageName(image)})`); } @@ -35,22 +155,73 @@ export class DockerWrapper implements StarknetWrapper { return this.docker; } - public async runCommand(command: StarknetCommand, args: string[], paths?: string[]) { + public async compile(options: CompileOptions): Promise { + const binds: String2String = { + [options.file]: options.file, + [options.abi]: options.abi, + [options.output]: options.output, + }; + + addPaths(binds, options.cairoPath); + + const dockerOptions = { + binds, + networkMode: "host" + }; + + const preparedOptions = this.prepareCompileOptions(options); + + const docker = await this.getDocker(); + return docker.runContainer(this.image, ["starknet-compile", ...preparedOptions], dockerOptions); + } + + public async deploy(options: DeployOptions): Promise { + const binds: String2String = { + [options.contract]: options.contract, + }; + + const dockerOptions = { + binds, + networksMode: "host" + }; + + options.gatewayUrl = adaptUrl(options.gatewayUrl); + const preparedOptions = this.prepareDeployOptions(options); + const docker = await this.getDocker(); - const binds: { [path: string]: string } = {}; + return docker.runContainer(this.image, ["starknet", ...preparedOptions], dockerOptions); + } - if (paths) { - for (const path of paths) { - binds[path] = path; - } - } + public async invokeOrCall(options: InvokeOrCallOptions): Promise { + const binds: String2String = { + [options.abi]: options.abi, + }; - const options = { + const dockerOptions = { binds, - networkMode: "host" - } + networksMode: "host" + }; + + options.gatewayUrl = adaptUrl(options.gatewayUrl); + options.feederGatewayUrl = adaptUrl(options.feederGatewayUrl); + const preparedOptions = this.prepareInvokeOrCalOptions(options); - return docker.runContainer(this.image, [command, ...args], options); + const docker = await this.getDocker(); + return docker.runContainer(this.image, ["starknet", ...preparedOptions], dockerOptions); + } + + public async getTxStatus(options: GetTxStatusOptions): Promise { + const binds: String2String = {}; + + const dockerOptions = { + binds, + networksMode: "host" + }; + + const preparedOptions = this.prepareGetTxStatusOptions(options); + + const docker = await this.getDocker(); + return docker.runContainer(this.image, ["starknet", ...preparedOptions], dockerOptions); } } @@ -60,13 +231,12 @@ function checkCommandPath(commandPath: string): void { } } -export class VenvWrapper implements StarknetWrapper { +export class VenvWrapper extends StarknetWrapper { private starknetCompilePath: string; private starknetPath: string; - private command2path: Map; - constructor(venvPath: string) { + super(); let venvPrefix = ""; if (venvPath === "active") { console.log(`${PLUGIN_NAME} plugin using the active environment.`); @@ -83,18 +253,12 @@ export class VenvWrapper implements StarknetWrapper { this.starknetPath = path.join(venvPrefix, "starknet"); checkCommandPath(this.starknetPath); } - - this.command2path = new Map([ - ["starknet", this.starknetPath], - ["starknet-compile", this.starknetCompilePath] - ]); } - public async runCommand(command: StarknetCommand, args: string[], _paths?: string[]): Promise { - const commandPath = this.command2path.get(command); - const process = spawnSync(commandPath, args); - if(!process.stdout){ - const msg = "Command not found. If you're using a Python virtual environment, check that it has 'cairo-lang' installed."; + private async execute(commandPath: string, preparedOptions: string[]): Promise { + const process = spawnSync(commandPath, preparedOptions); + if (!process.stdout){ + const msg = `${commandPath} not found. Check that your Python virtual environment has 'cairo-lang' installed.`; throw new HardhatPluginError(PLUGIN_NAME, msg); } return { @@ -103,4 +267,28 @@ export class VenvWrapper implements StarknetWrapper { stderr: process.stderr }; } + + public async compile(options: CompileOptions): Promise { + const preparedOptions = this.prepareCompileOptions(options); + const executed = await this.execute(this.starknetCompilePath, preparedOptions); + return executed; + } + + public async deploy(options: DeployOptions): Promise { + const preparedOptions = this.prepareDeployOptions(options); + const executed = await this.execute(this.starknetPath, preparedOptions); + return executed; + } + + public async invokeOrCall(options: InvokeOrCallOptions): Promise { + const preparedOptions = this.prepareInvokeOrCalOptions(options); + const executed = await this.execute(this.starknetPath, preparedOptions); + return executed; + } + + public async getTxStatus(options: GetTxStatusOptions): Promise { + const preparedOptions = this.prepareGetTxStatusOptions(options); + const executed = await this.execute(this.starknetPath, preparedOptions); + return executed; + } } diff --git a/src/types.ts b/src/types.ts index ebecdf3f..aea84549 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2,7 +2,7 @@ import * as fs from "fs"; import * as starknet from "./starknet-types"; import { HardhatPluginError } from "hardhat/plugins"; import { PLUGIN_NAME, CHECK_STATUS_TIMEOUT } from "./constants"; -import { adaptLog, adaptUrl } from "./utils"; +import { adaptLog } from "./utils"; import { adaptInput, adaptOutput } from "./adapt"; import { StarknetWrapper } from "./starknet-wrappers"; @@ -50,6 +50,7 @@ export interface StringMap { [key: string]: any; } +export type Choice = "call" | "invoke"; function extractFromResponse(response: string, regex: RegExp) { const matched = response.match(regex); @@ -75,16 +76,16 @@ function extractAddress(response: string) { tx_status: TxStatus } -async function checkStatus(txHash: string, starknetWrapper: StarknetWrapper, gatewayUrl: string, feederGatewayUrl: string): Promise { - const executed = await starknetWrapper.runCommand("starknet", [ - "tx_status", - "--hash", txHash, - "--gateway_url", adaptUrl(gatewayUrl), - "--feeder_gateway_url", adaptUrl(feederGatewayUrl) - ]); +async function checkStatus(hash: string, starknetWrapper: StarknetWrapper, gatewayUrl: string, feederGatewayUrl: string): Promise { + const executed = await starknetWrapper.getTxStatus({ + hash, + gatewayUrl, + feederGatewayUrl + }); if (executed.statusCode) { throw new HardhatPluginError(PLUGIN_NAME, executed.stderr.toString()); } + const response = executed.stdout.toString(); try { const responseParsed = JSON.parse(response); @@ -109,12 +110,12 @@ export async function iterativelyCheckStatus( starknetWrapper: StarknetWrapper, gatewayUrl: string, feederGatewayUrl: string, - resolve: () => void, + resolve: (status: string) => void, reject: (reason?: any) => void ) { const statusObject = await checkStatus(txHash, starknetWrapper, gatewayUrl, feederGatewayUrl); if (isTxAccepted(statusObject)) { - resolve(); + resolve(statusObject.tx_status); } else if (isTxRejected(statusObject)) { reject(new Error("Transaction rejected.")); } else { @@ -151,11 +152,11 @@ function readAbi(abiPath: string): starknet.Abi { * @param signature array of transaction signature elements * @param starknetArgs destination array */ -function handleSignature(signature: Array, starknetArgs: string[]) { +function handleSignature(signature: Array): string[] { if (signature) { - starknetArgs.push("--signature"); - signature.forEach(part => starknetArgs.push(part.toString())); + return signature.map(s => s.toString()); } + return []; } export class StarknetContractFactory { @@ -209,16 +210,12 @@ export class StarknetContractFactory { * @returns the newly created instance */ async deploy(constructorArguments?: StringMap, signature?: Array): Promise { - const starknetArgs = [ - "deploy", - "--contract", this.metadataPath, - "--gateway_url", adaptUrl(this.gatewayUrl) - ]; - - this.handleConstructorArguments(constructorArguments, starknetArgs); - handleSignature(signature, starknetArgs); - - const executed = await this.starknetWrapper.runCommand("starknet", starknetArgs, [this.metadataPath]); + const executed = await this.starknetWrapper.deploy({ + contract: this.metadataPath, + inputs: this.handleConstructorArguments(constructorArguments), + signature: handleSignature(signature), + gatewayUrl: this.gatewayUrl, + }); if (executed.statusCode) { const msg = "Could not deploy contract. Check the network url in config. Is it responsive?"; throw new HardhatPluginError(PLUGIN_NAME, msg); @@ -248,16 +245,16 @@ export class StarknetContractFactory { }); } - private handleConstructorArguments(constructorArguments: StringMap, starknetArgs: string[]): void { + private handleConstructorArguments(constructorArguments: StringMap): string[] { if (this.constructorAbi) { if (!constructorArguments || Object.keys(constructorArguments).length === 0) { throw new HardhatPluginError(PLUGIN_NAME, "Constructor arguments required but not provided."); } - const construtorArguments = adaptInput( + const argumentArray = adaptInput( this.constructorAbi.name, constructorArguments, this.constructorAbi.inputs, this.abi ); - starknetArgs.push("--inputs"); - construtorArguments.forEach(arg => starknetArgs.push(arg)); + + return argumentArray; } if (constructorArguments && Object.keys(constructorArguments).length) { @@ -266,6 +263,8 @@ export class StarknetContractFactory { } // other case already handled } + + return []; } /** @@ -305,7 +304,7 @@ export class StarknetContract { this.feederGatewayUrl = config.feederGatewayUrl; } - private async invokeOrCall(kind: "invoke" | "call", functionName: string, args?: StringMap, signature?: Array) { + private async invokeOrCall(choice: Choice, functionName: string, args?: StringMap, signature?: Array) { if (!this.address) { throw new HardhatPluginError(PLUGIN_NAME, "Contract not deployed"); } @@ -316,33 +315,22 @@ export class StarknetContract { throw new HardhatPluginError(PLUGIN_NAME, msg); } - const starknetArgs = [ - kind, - "--address", this.address, - "--abi", this.abiPath, - "--function", functionName, - "--gateway_url", adaptUrl(this.gatewayUrl), - "--feeder_gateway_url", adaptUrl(this.feederGatewayUrl) - ]; - if (Array.isArray(args)) { throw new HardhatPluginError(PLUGIN_NAME, "Arguments should be passed in the form of an object."); } - if (args && Object.keys(args).length) { - starknetArgs.push("--inputs"); - const adapted = adaptInput(functionName, args, func.inputs, this.abi); - starknetArgs.push(...adapted); - } - - if (signature) { - starknetArgs.push("--signature"); - signature.forEach(part => starknetArgs.push(part.toString())); - } - - const executed = await this.starknetWrapper.runCommand("starknet", starknetArgs, [this.abiPath]); + const executed = await this.starknetWrapper.invokeOrCall({ + choice, + address: this.address, + abi: this.abiPath, + functionName: functionName, + inputs: adaptInput(functionName, args, func.inputs, this.abi), + signature: handleSignature(signature), + gatewayUrl: this.gatewayUrl, + feederGatewayUrl: this.feederGatewayUrl + }); if (executed.statusCode) { - const msg = `Could not ${kind} ${functionName}:\n` + executed.stderr.toString(); + const msg = `Could not ${choice} ${functionName}:\n` + executed.stderr.toString(); const replacedMsg = adaptLog(msg); throw new HardhatPluginError(PLUGIN_NAME, replacedMsg); } @@ -368,7 +356,7 @@ export class StarknetContract { this.starknetWrapper, this.gatewayUrl, this.feederGatewayUrl, - resolve, + status => resolve(), reject ); });