diff --git a/packages/snfoundry/scripts-ts/deploy-contract.ts b/packages/snfoundry/scripts-ts/deploy-contract.ts index a332ee8a..cbfbe58d 100644 --- a/packages/snfoundry/scripts-ts/deploy-contract.ts +++ b/packages/snfoundry/scripts-ts/deploy-contract.ts @@ -3,40 +3,41 @@ import path from "path"; import { networks } from "./helpers/networks"; import yargs from "yargs"; import { - BlockIdentifier, CallData, - hash, stark, RawArgs, - constants, - ec, - validateAndParseAddress, transaction, -} from "starknet"; -import { Network } from "./types"; -import { - LegacyContractClass, - CompiledSierra, extractContractHashes, + DeclareContractPayload, + UniversalDetails, } from "starknet"; +import { DeployContractParams, Network } from "./types"; +import { green, red, yellow } from "./helpers/colorize-log"; const argv = yargs(process.argv.slice(2)).argv; const networkName: string = argv["network"]; let deployments = {}; - let deployCalls = []; const { provider, deployer }: Network = networks[networkName]; -const declareIfNot_NotWait = async (payload: any) => { +const declareIfNot_NotWait = async ( + payload: DeclareContractPayload, + options?: UniversalDetails +) => { const declareContractPayload = extractContractHashes(payload); try { await provider.getClassByHash(declareContractPayload.classHash); } catch (error) { - let { transaction_hash } = await deployer.declare(payload); - if (networkName == "sepolia" || networkName == "mainnet") { - await provider.waitForTransaction(transaction_hash); + try { + const { transaction_hash } = await deployer.declare(payload, options); + if (networkName === "sepolia" || networkName === "mainnet") { + await provider.waitForTransaction(transaction_hash); + } + } catch (e) { + console.error(red("Error declaring contract:"), e); + throw e; } } return { @@ -49,38 +50,64 @@ const deployContract_NotWait = async (payload: { classHash: string; constructorCalldata: RawArgs; }) => { - let { calls, addresses } = transaction.buildUDCCall( - payload, - deployer.address - ); - deployCalls.push(...calls); - return { - contractAddress: addresses[0], - }; + try { + const { calls, addresses } = transaction.buildUDCCall( + payload, + deployer.address + ); + deployCalls.push(...calls); + return { + contractAddress: addresses[0], + }; + } catch (error) { + console.error(red("Error building UDC call:"), error); + throw error; + } }; +/** + * Deploy a contract using the specified parameters. + * + * @param {DeployContractParams} params - The parameters for deploying the contract. + * @param {string} params.contract - The name of the contract to deploy. + * @param {string} [params.contractName] - The name to export the contract as (optional). + * @param {RawArgs} [params.constructorArgs] - The constructor arguments for the contract (optional). + * @param {UniversalDetails} [params.options] - Additional deployment options (optional). + * + * @returns {Promise<{ classHash: string; address: string }>} The deployed contract's class hash and address. + * + * @example + * ///Example usage of deployContract function + * await deployContract({ + * contract: "YourContract", + * contractName: "YourContractExportName", + * constructorArgs: { owner: deployer.address }, + * options: { maxFee: BigInt(1000000000000) } + * }); + */ const deployContract = async ( - constructorArgs: RawArgs, - contractName: string, - exportContractName?: string, - options?: { - maxFee: bigint; - } + params: DeployContractParams ): Promise<{ classHash: string; address: string; }> => { + const { contract, constructorArgs, contractName, options } = params; + try { await deployer.getContractVersion(deployer.address); } catch (e) { if (e.toString().includes("Contract not found")) { - throw new Error( - `The wallet you're using to deploy the contract is not deployed in ${networkName} network` - ); + const errorMessage = `The wallet you're using to deploy the contract is not deployed in the ${networkName} network.`; + console.error(red(errorMessage)); + throw new Error(errorMessage); + } else { + console.error(red("Error getting contract version: "), e); + throw e; } } let compiledContractCasm; + let compiledContractSierra; try { compiledContractCasm = JSON.parse( @@ -88,7 +115,7 @@ const deployContract = async ( .readFileSync( path.resolve( __dirname, - `../contracts/target/dev/contracts_${contractName}.compiled_contract_class.json` + `../contracts/target/dev/contracts_${contract}.compiled_contract_class.json` ) ) .toString("ascii") @@ -102,12 +129,14 @@ const deployContract = async ( const match = error.message.match( /\/dev\/(.+?)\.compiled_contract_class/ ); - const contractName = match ? match[1].split("_").pop() : "Unknown"; + const missingContract = match ? match[1].split("_").pop() : "Unknown"; console.error( - `The contract "${contractName}" doesn't exist or is not compiled` + red( + `The contract "${missingContract}" doesn't exist or is not compiled` + ) ); } else { - console.error(error); + console.error(red("Error reading compiled contract class file: "), error); } return { classHash: "", @@ -115,27 +144,38 @@ const deployContract = async ( }; } - const compiledContractSierra = JSON.parse( - fs - .readFileSync( - path.resolve( - __dirname, - `../contracts/target/dev/contracts_${contractName}.contract_class.json` + try { + compiledContractSierra = JSON.parse( + fs + .readFileSync( + path.resolve( + __dirname, + `../contracts/target/dev/contracts_${contract}.contract_class.json` + ) ) - ) - .toString("ascii") - ); + .toString("ascii") + ); + } catch (error) { + console.error(red("Error reading contract class file: "), error); + return { + classHash: "", + address: "", + }; + } const contractCalldata = new CallData(compiledContractSierra.abi); const constructorCalldata = constructorArgs ? contractCalldata.compile("constructor", constructorArgs) : []; - console.log("Deploying Contract ", contractName); - - let { classHash } = await declareIfNot_NotWait({ - contract: compiledContractSierra, - casm: compiledContractCasm, - }); + console.log(yellow("Deploying Contract "), contract); + + let { classHash } = await declareIfNot_NotWait( + { + contract: compiledContractSierra, + casm: compiledContractCasm, + }, + options + ); let randomSalt = stark.randomAddress(); @@ -145,14 +185,14 @@ const deployContract = async ( constructorCalldata, }); - console.log("Contract Deployed at ", contractAddress); + console.log(green("Contract Deployed at "), contractAddress); - let finalContractName = exportContractName || contractName; + let finalContractName = contractName || contract; deployments[finalContractName] = { classHash: classHash, address: contractAddress, - contract: contractName, + contract: contract, }; return { @@ -161,23 +201,30 @@ const deployContract = async ( }; }; -const executeDeployCalls = async () => { +const executeDeployCalls = async (options?: UniversalDetails) => { + if (deployCalls.length < 1) { + throw new Error( + red("Aborted: No contract to deploy. Please prepare the contracts with `deployContract`") + ); + } + try { - let { transaction_hash } = await deployer.execute(deployCalls); - console.log("Deploy Calls Executed at ", transaction_hash); - if (networkName == "sepolia" || networkName == "mainnet") { + let { transaction_hash } = await deployer.execute(deployCalls, options); + console.log(green("Deploy Calls Executed at "), transaction_hash); + if (networkName === "sepolia" || networkName === "mainnet") { await provider.waitForTransaction(transaction_hash); } } catch (error) { + console.error(red("Error executing deploy calls: "), error); // split the calls in half and try again recursively if (deployCalls.length > 1) { - let half = deployCalls.length / 2; + let half = Math.ceil(deployCalls.length / 2); let firstHalf = deployCalls.slice(0, half); - let secondHalf = deployCalls.slice(half, deployCalls.length); + let secondHalf = deployCalls.slice(half); deployCalls = firstHalf; - await executeDeployCalls(); + await executeDeployCalls(options); deployCalls = secondHalf; - await executeDeployCalls(); + await executeDeployCalls(options); } } }; diff --git a/packages/snfoundry/scripts-ts/deploy.ts b/packages/snfoundry/scripts-ts/deploy.ts index 4489299b..101aa7cf 100644 --- a/packages/snfoundry/scripts-ts/deploy.ts +++ b/packages/snfoundry/scripts-ts/deploy.ts @@ -4,21 +4,57 @@ import { exportDeployments, deployer, } from "./deploy-contract"; +import { green } from "./helpers/colorize-log"; +/** + * Deploy a contract using the specified parameters. + * + * @example (deploy contract with contructorArgs) + * const deployScript = async (): Promise => { + * await deployContract( + * { + * contract: "YourContract", + * contractName: "YourContractExportName", + * constructorArgs: { + * owner: deployer.address, + * }, + * options: { + * maxFee: BigInt(1000000000000) + * } + * } + * ); + * }; + * + * @example (deploy contract without contructorArgs) + * const deployScript = async (): Promise => { + * await deployContract( + * { + * contract: "YourContract", + * contractName: "YourContractExportName", + * options: { + * maxFee: BigInt(1000000000000) + * } + * } + * ); + * }; + * + * + * @returns {Promise} + */ const deployScript = async (): Promise => { - await deployContract( - { - owner: deployer.address, // the deployer address is the owner of the contract + await deployContract({ + contract: "YourContract", + constructorArgs: { + owner: deployer.address, }, - "YourContract" - ); + }); }; deployScript() - .then(() => { - executeDeployCalls().then(() => { - exportDeployments(); - }); - console.log("All Setup Done"); + .then(async () => { + await executeDeployCalls(); + exportDeployments(); + + console.log(green("All Setup Done")); }) .catch(console.error); diff --git a/packages/snfoundry/scripts-ts/helpers/colorize-log.ts b/packages/snfoundry/scripts-ts/helpers/colorize-log.ts new file mode 100644 index 00000000..1e37792c --- /dev/null +++ b/packages/snfoundry/scripts-ts/helpers/colorize-log.ts @@ -0,0 +1,16 @@ +const colors = { + reset: "\x1b[0m", + red: "\x1b[31m", + green: "\x1b[32m", + yellow: "\x1b[33m", +}; + +const colorize = (color: string, message: string): string => { + return `${color}${message}${colors.reset}`; +}; + +export const red = (message: string): string => colorize(colors.red, message); +export const green = (message: string): string => + colorize(colors.green, message); +export const yellow = (message: string): string => + colorize(colors.yellow, message); diff --git a/packages/snfoundry/scripts-ts/types.ts b/packages/snfoundry/scripts-ts/types.ts index a5965a61..eabc5c9f 100644 --- a/packages/snfoundry/scripts-ts/types.ts +++ b/packages/snfoundry/scripts-ts/types.ts @@ -1,4 +1,4 @@ -import { Account, RpcProvider } from "starknet"; +import { Account, RawArgs, RpcProvider, UniversalDetails } from "starknet"; export type Networks = Record<"devnet" | "sepolia" | "mainnet", Network>; @@ -6,3 +6,10 @@ export type Network = { provider: RpcProvider; deployer: Account; }; + +export type DeployContractParams = { + contract: string; + contractName?: string; + constructorArgs?: RawArgs; + options?: UniversalDetails; +};