Skip to content

Commit

Permalink
feat: declare contract script
Browse files Browse the repository at this point in the history
  • Loading branch information
EjembiEmmanuel committed Dec 18, 2024
1 parent 4ea6841 commit 8a0372a
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 3 deletions.
12 changes: 12 additions & 0 deletions configs/deployments.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"devnet": {},
"sepolia": {
"HelloStarknet": {
"contract": "HelloStarknet",
"classHash": "0x659beca87b6eb6621573f9155f15f970caf513d419ba46b545b29439e0045c6",
"constructorArgs": [],
"address": ""
}
},
"mainnet": {}
}
3 changes: 2 additions & 1 deletion configs/scaffold.config.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
{
"network": "sepolia",
"url": "https://free-rpc.nethermind.io/sepolia-juno/",
"feeToken": "eth",
"maxFee": 894843045483,
"account": {
"name": "scaffold",
"profile": "scaffold"
},
"contract-names": [
"contracts": [
"HelloStarknet"
]
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
"scripts": {
"prepare-account": "node scaffold_scripts/prepare-account.js",
"deploy-account": "node scaffold_scripts/deploy-account.js",
"get-contract-names": "node scaffold_scripts/get-contract-names.js",
"generate-contract-names": "node scaffold_scripts/generateContractNames.mjs",
"build-contracts": "cd contracts && scarb build",
"test-contracts": "cd contracts && snforge test",
"format-contracts": "cd contracts && scarb fmt",
"declare-contract": "node scaffold_scripts/declareContract.mjs",
"verify-contracts": "cd contracts && sncast verify --contract-address ${npm_config_contract_address} --contract-name ${npm_config_contract_name} --verifier walnut --network ${npm_config_network}",
"contract-scripts": "cd contracts/scripts && sncast script run ${npm_config_script} --url ${npm_config_url}",
"generate-interface": "cd contracts && src5_rs parse",
"delete-account": "cd contracts && sncast --profile ${npm_config_profile} --accounts-file ${npm_config_accounts_file} account delete --name ${npm_config_name} --network ${npm_config_network}",
"declare-contract": "cd contracts && sncast --profile ${npm_config_profile} declare --contract-name ${npm_config_contract_name} --fee-token ${npm_config_fee_token}",
"deploy-contract": "cd contracts && sncast --profile ${npm_config_profile} deploy --fee-token ${npm_config_fee_token} --class-hash ${npm_config_class_hash}",
"initialize-dojo": "rm -rf contracts && mkdir contracts && cd contracts && sozo init ${npm_config_name}",
"build-dojo": "cd contracts/${npm_config_name} && sozo build",
Expand Down
168 changes: 168 additions & 0 deletions scaffold_scripts/declareContract.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import cp, { execSync } from "child_process";
import { readFileSync, existsSync, writeFileSync } from "fs";
import { resolve } from "path";
import { createInterface } from "readline";
import { promisify } from "util";
import ora from "ora";

// Resolve config path
const configPath = resolve(process.cwd(), "configs/scaffold.config.json");
const deploymentsPath = resolve(
process.cwd(),
"configs/deployments.config.json"
);

// convert libs to promises
const exec = promisify(cp.exec);

// Check if the config file exists
if (!existsSync(configPath) || !existsSync(deploymentsPath)) {
console.error("Error: Config file not found. Ensure the correct setup.");
process.exit(1);
}

// Load and parse the configuration file
let config;
try {
config = JSON.parse(readFileSync(configPath, "utf-8"));
} catch (error) {
console.error("Error reading or parsing the config file:", error.message);
process.exit(1);
}

// Initialize readline interface
const rl = createInterface({
input: process.stdin,
output: process.stdout,
});

/**
* Prompt the user with a question and return their response.
* @param {string} question - The question to ask.
* @returns {Promise<string>} - User's input.
*/
const askQuestion = (question) =>
new Promise((resolve) => rl.question(question, resolve));

/**
* Display contract names and prompt the user to select one.
* @returns {Promise<string>} - The selected contract name.
*/
async function getContract() {
const contracts = config["contracts"] || [];

if (!contracts.length) {
console.error(
"No contracts found. Please run 'npm run generate-contract-names' and try again."
);
process.exit(1);
}

console.log("Available Contracts:");
contracts.forEach((contract, index) =>
console.log(`${index + 1}. ${contract}`)
);

while (true) {
const choice = await askQuestion(
`Select the contract to declare (1-${contracts.length}): `
);
const selectedIndex = parseInt(choice, 10) - 1;

if (selectedIndex >= 0 && selectedIndex < contracts.length) {
return contracts[selectedIndex];
}

console.log("Invalid choice. Please try again.");
}
}

/**
* Validate if `sncast` is available in the environment.
*/
async function validateSncast() {
try {
await exec("sncast --version");
} catch {
console.error("Error: `sncast` is not installed or not in PATH.");
process.exit(1);
}
}

/**
* Reads a JSON configuration file and updates the deployment details.
* @param {string} network - The network of deployment.
* @param {string} contract - The name of the declared contract.
* @param {string} classHash - The declared class hash.
*/
function updateDeploymentConfig(network, contract, classHash) {
try {
const deployments = JSON.parse(readFileSync(deploymentsPath, "utf-8"));

if (!deployments[network]) {
deployments[network] = {};
}

// Update and write the configuration file
deployments[network][contract] = {
contract,
classHash,
constructorArgs: [],
address: "",
};

// Save updated deployments
writeFileSync(deploymentsPath, JSON.stringify(deployments, null, 2));
} catch (err) {
throw new Error(`Error updating deployments config file: ${err.message}`);
}
}

(async () => {
const spinner = ora();

try {
// Validate `sncast` availability
validateSncast();

// Destructure config variables
const {
network,
feeToken,
account: { profile },
} = config;

// Get the selected contract name
const contract = await getContract();

// Build the command
const command = `cd contracts && sncast --profile ${profile} declare --contract-name ${contract} --fee-token ${feeToken}`;

spinner.start("Declaring contract...");
const output = await exec(command);

if (output.stderr) {
throw new Error(output.stderr);
}

const classHashMatch = output.stdout.match(
/class_hash:\s*(0x[0-9a-fA-F]+)/
);
const classHash = classHashMatch ? classHashMatch[1] : null;

if (!classHash) {
throw new Error("class_hash not found in command output.");
}

updateDeploymentConfig(network, contract, classHash);

spinner.succeed("Contract declared successfully.");
console.log("Run 'npm deploy-contract' to deploy a contract.");
} catch (error) {
spinner.fail("Error during contract declaration.");
console.error("Error:", error.message);
process.exit(1);
} finally {
rl.close();
}
})();

0 comments on commit 8a0372a

Please sign in to comment.