Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve deploy function on deploy-contract.ts #231

Merged
merged 6 commits into from
Aug 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 109 additions & 62 deletions packages/snfoundry/scripts-ts/deploy-contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -49,46 +50,72 @@ 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(
fs
.readFileSync(
path.resolve(
__dirname,
`../contracts/target/dev/contracts_${contractName}.compiled_contract_class.json`
`../contracts/target/dev/contracts_${contract}.compiled_contract_class.json`
)
)
.toString("ascii")
Expand All @@ -102,40 +129,53 @@ 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: "",
address: "",
};
}

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();

Expand All @@ -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 {
Expand All @@ -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);
}
}
};
Expand Down
56 changes: 46 additions & 10 deletions packages/snfoundry/scripts-ts/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> => {
* await deployContract(
* {
* contract: "YourContract",
* contractName: "YourContractExportName",
* constructorArgs: {
* owner: deployer.address,
* },
* options: {
* maxFee: BigInt(1000000000000)
* }
* }
* );
* };
*
* @example (deploy contract without contructorArgs)
* const deployScript = async (): Promise<void> => {
* await deployContract(
* {
* contract: "YourContract",
* contractName: "YourContractExportName",
* options: {
* maxFee: BigInt(1000000000000)
* }
* }
* );
* };
*
*
* @returns {Promise<void>}
*/
const deployScript = async (): Promise<void> => {
await deployContract(
{
owner: deployer.address, // the deployer address is the owner of the contract
await deployContract({
ikemHood marked this conversation as resolved.
Show resolved Hide resolved
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);
16 changes: 16 additions & 0 deletions packages/snfoundry/scripts-ts/helpers/colorize-log.ts
Original file line number Diff line number Diff line change
@@ -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);
9 changes: 8 additions & 1 deletion packages/snfoundry/scripts-ts/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import { Account, RpcProvider } from "starknet";
import { Account, RawArgs, RpcProvider, UniversalDetails } from "starknet";

export type Networks = Record<"devnet" | "sepolia" | "mainnet", Network>;

export type Network = {
provider: RpcProvider;
deployer: Account;
};

export type DeployContractParams = {
contract: string;
contractName?: string;
constructorArgs?: RawArgs;
options?: UniversalDetails;
};
Loading