Skip to content

Commit

Permalink
Merge pull request #152 from EjembiEmmanuel/main
Browse files Browse the repository at this point in the history
feat: add Kakarot support
  • Loading branch information
Darlington02 authored Oct 14, 2024
2 parents 8ee4d82 + 4f1c6fa commit b30b0ea
Show file tree
Hide file tree
Showing 4 changed files with 324 additions and 82 deletions.
194 changes: 150 additions & 44 deletions bin/cli.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import fs from "fs";
import path from "path";
import ora from "ora";

const git_repo = "https://github.com/horuslabsio/Starknet-Scaffold";
const git_repo = "https://github.com/horuslabsio/Starknet-Scaffold.git";
const dojo_starter = "https://github.com/dojoengine/dojo-starter.git";

// convert libs to promises
const exec = promisify(cp.exec);
Expand All @@ -15,7 +16,7 @@ const rm = promisify(fs.rm);
// Initialize readline interface
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
output: process.stdout,
});

// Function to ask questions in the terminal
Expand All @@ -28,19 +29,29 @@ let projectPath;
const installPackage = async () => {
try {
// Ask for package name
const packageName = await askQuestion('Enter your package name: ');
const packageName = await askQuestion("Enter your package name: ");

// Ask for package type
const packageTypeChoices = ['contract_only', 'fullstack', 'dojo', 'debugger'];
console.log('Available package types:');
packageTypeChoices.forEach((type, index) => console.log(`${index + 1}. ${type}`));
const packageTypeChoices = [
"contract_only",
"fullstack",
"debugger",
"dojo",
"kakarot",
];
console.log("Available package types:");
packageTypeChoices.forEach((type, index) =>
console.log(`${index + 1}. ${type}`)
);

let packageType;
while (!packageType) {
const packageTypeChoice = await askQuestion('Select the package type (1-3): ');
const packageTypeChoice = await askQuestion(
"Select the package type (1-5): "
);
packageType = packageTypeChoices[parseInt(packageTypeChoice) - 1];
if (!packageType) {
console.log('Invalid choice. Please select a valid package type.');
console.log("Invalid choice. Please select a valid package type.");
}
}

Expand All @@ -49,11 +60,12 @@ const installPackage = async () => {
projectPath = path.join(currentPath, packageName);

if (fs.existsSync(projectPath)) {
console.log(`The file ${projectName} already exist in the current directory, please give it another name.`);
process.exit(1);
}
else {
fs.mkdirSync(projectPath);
console.log(
`The file ${projectName} already exist in the current directory, please give it another name.`
);
process.exit(1);
} else {
fs.mkdirSync(projectPath);
}

// Clone the repository
Expand All @@ -63,11 +75,24 @@ const installPackage = async () => {
gitSpinner.succeed();

let cleanupTasks = [];
let excluded_files = [".git", ".github", "CONTRIBUTING.md", "bin", "burner", "website", "docs", "CNAME"];
let excluded_files = [
".git",
".github",
"CONTRIBUTING.md",
"bin",
"burner",
"website",
"docs",
"CNAME",
];

if (packageType === "fullstack" || packageType === "dojo") {
const FRONTEND_BASE_PATH = "frontend/src/app";
const componentsToRemove = [
if (
packageType === "fullstack" ||
packageType === "dojo" ||
packageType === "kakarot"
) {
const FRONTEND_BASE_PATH = "frontend/src/app";
const componentsToRemove = [
`${FRONTEND_BASE_PATH}/burner`,
`${FRONTEND_BASE_PATH}/wikipedia`,
`${FRONTEND_BASE_PATH}/scaffold-deployer`,
Expand All @@ -78,49 +103,45 @@ const installPackage = async () => {
`${FRONTEND_BASE_PATH}/components/AssetTransferModal.tsx`,
`${FRONTEND_BASE_PATH}/components/ConnectionModal.tsx`,
`${FRONTEND_BASE_PATH}/components/ContractExecutionModal.tsx`,
];
cleanupTasks.push(
];
cleanupTasks.push(
...componentsToRemove.map((comp) =>
rm(path.join(projectPath, comp), {
rm(path.join(projectPath, comp), {
recursive: true,
force: true,
}),
})
),
...excluded_files.map((comp) =>
rm(path.join(projectPath, comp), {
rm(path.join(projectPath, comp), {
recursive: true,
force: true,
}),
),
})
)
);
}
else if (packageType == "contract_only") {
} else if (packageType == "contract_only") {
let componentsToRemove = [...excluded_files, "frontend", ".editorconfig"];
cleanupTasks.push(
...componentsToRemove.map((comp) =>
rm(path.join(projectPath, comp), {
recursive: true,
force: true,
}),
),
)
}
else {
recursive: true,
force: true,
})
)
);
} else {
cleanupTasks.push(
...excluded_files.map((comp) =>
rm(path.join(projectPath, comp), {
rm(path.join(projectPath, comp), {
recursive: true,
force: true,
}),
),
})
)
);
}

// remove useless files
const cleanSpinner = ora("Removing useless files").start();
await Promise.all([
...cleanupTasks,
]);
await Promise.all([...cleanupTasks]);

process.chdir(projectPath);
// remove the packages needed for cli
Expand All @@ -129,19 +150,53 @@ const installPackage = async () => {

// install dependencies
const npmSpinner = ora("Installing dependencies...").start();
if(packageType == "dojo") {
await exec("npm run install --legacy-peer-deps && npm run initialize-dojo");
}
else if(packageType !== "contract_only") {
if (packageType == "dojo") {
await exec(
`git clone --depth 1 ${dojo_starter} ${projectPath}/dojo-starter --quiet`
);

const dojo_version = getDojoVersion(
path.join(projectPath, "/dojo-starter/Scarb.toml")
);

await exec(
`npm run install --dojo-version=${dojo_version} --legacy-peer-deps`
);

await exec("npm run initialize-dojo");

fs.rmSync(path.join(projectPath, "/dojo-starter"), {
recursive: true,
force: true,
});
} else if (packageType == "kakarot") {
await exec("npm run initialize-kakarot");

await exec("npm run setup-kakarot");

const tool_versions = await getVersionsFromToolFile(
path.join(projectPath, "/contracts/.tool-versions")
);

await exec(
`npm run install --scarb-version=${tool_versions.scarb} --legacy-peer-deps`
);

// await exec("npm run install-tools");
} else if (packageType !== "contract_only") {
await exec("npm run install --legacy-peer-deps");
}
npmSpinner.succeed();

console.log("The installation is done!");
console.log("You can now run the scaffold with:");
console.log(` cd ${packageName}`);
console.log(` npm run start`);

if (packageType == "kakarot") {
console.log(` npm run start-kakarot`);
} else {
console.log(` npm run start`);
}
} catch (err) {
// clean up in case of error, so the user does not have to do it manually
fs.rmSync(projectPath, { recursive: true, force: true });
Expand All @@ -151,4 +206,55 @@ const installPackage = async () => {
}
};

/**
* Reads the .tool-versions file and returns the versions of packages.
* @param {string} filePath - The path to the .tool-versions file.
* @returns {Promise<Object>} - A promise that resolves to an object containing package names and their versions.
*/
function getVersionsFromToolFile(filePath) {
return new Promise((resolve, reject) => {
fs.readFile(filePath, "utf8", (err, data) => {
if (err) {
return reject(err);
}

const versions = {};
const lines = data.trim().split("\n");

for (const line of lines) {
const [packageName, version] = line
.split(" ")
.map((item) => item.trim());
if (packageName && version) {
versions[packageName] = version;
}
}

resolve(versions);
});
});
}

/**
* Reads the Scarb.toml file and returns the versions of dojo
* @param {string} filePath - The path to the Scarb.toml file.
* @returns {string} - A string corresponding to the dojo version.
*/
function getDojoVersion(filePath) {
const tomlContent = fs.readFileSync(filePath, "utf-8");

// Use a regular expression to match the Dojo version tag
const dojoVersionMatch = tomlContent.match(
/dojo\s*=\s*{[^}]*tag\s*=\s*"v([\d\w\.\-]+)"/
);

// Check if the match was found and return the version with 'v' prefix
if (dojoVersionMatch && dojoVersionMatch[1]) {
return dojoVersionMatch[1];
}

// Return null if no version found
return null;
}

installPackage();
57 changes: 52 additions & 5 deletions docs/src/chapter_5.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,26 +53,26 @@ generates a profile which is added to scarb.toml and can be passed to other comm
### Deploy Account
To deploy an account:
```
npm run deploy-account --profile=<MY_PROFILE> --name=<ACCOUNT_NAME> --feetoken=<FEE_TOKEN> --maxfee=<MAX_FEE>
npm run deploy-account --profile=<MY_PROFILE> --name=<ACCOUNT_NAME> --fee-token=<FEE_TOKEN> --max-fee=<MAX_FEE>
```
where the profile is gotten from `snfoundry.toml`, name is the prepared account and maxfee is the specified max fee.

### Delete Account
To delete an account:
```
npm run delete-account --profile=<MY_PROFILE> --accountfile=<PATH_TO_ACCOUNT_FILE> --name=<ACCOUNT_NAME> --network=<alpha-mainnet | alpha-goerli>
npm run delete-account --profile=<MY_PROFILE> --account-file=<PATH_TO_ACCOUNT_FILE> --name=<ACCOUNT_NAME> --network=<alpha-mainnet | alpha-goerli>
```

### Declare Contract
To declare a Starknet contract:
```
npm run declare-contract --profile=<MY_PROFILE> --contract=<CONTRACT_NAME> --feetoken=<FEE_TOKEN>
npm run declare-contract --profile=<MY_PROFILE> --contract-name=<CONTRACT_NAME> --url=<RPC_URL> --fee-token=<FEE_TOKEN>
```

### Deploy Contract
To deploy a contract:
```
npm run deploy-contract --profile=<MY_PROFILE> --feetoken=<FEE_TOKEN> --class=<CONTRACT_CLASSHASH>
npm run deploy-contract --profile=<MY_PROFILE> --feetoken=<FEE_TOKEN> --class=<CONTRACT_CLASSHASH> --url=<RPC_URL>
```

### Starknet-Devnet
Expand All @@ -81,7 +81,6 @@ Confirm that Docker is installed and running to use starknet-devnet. To run devn
npm run devnet
```


## Dojo Scripts
The `contracts` folder contains all the tools needed to write, build, test and deploy dojo projects. It is built with sozo and katana. Here are common operations you can perform on your dojo contracts.

Expand Down Expand Up @@ -109,6 +108,54 @@ To migrate your dojo project, from the base repository run:
npm run migrate-dojo --name=<PROJECT_NAME>
```

## Kakarot Scripts
Below are npm scripts provided by `Starknet-Scaffold` for your Kakarot development.

### Setup Kakarot
To setup Kakarot, from the base repository run:
```
npm run setup-kakarot
```

### Start Kakarot
To start Kakarot, from the base repository run:
```
npm run start-kakarot
```

### Deploy Kakarot L1 Messaging Contracts to Local RPC
To deploy Kakarot l1 messaging contracts locally, from the base repository run:
```
npm run deploy-kakarot-l1-messaging-contracts-local
```

### Deploy Kakarot EVM Contract
To deploy a Kakarot EVM contract, from the base repository run:
```
npm run deploy-kakarot-evm-contract --contract-path=<PATH_TO_CONTRACT> --rpc-url=<RPC_URL> --private-key=<PRIVATE_KEY>
```
If you need to specify constructor args, run:
```
npm run deploy-kakarot-evm-contract --contract-path=<PATH_TO_CONTRACT> --constructor-args=<CONSTRUCTOR_ARGS> --rpc-url=<RPC_URL> --private-key=<PRIVATE_KEY>
```

### Declare Cairo Contract Using Keystore
To declare a Cairo contract using keystore, from the base repository run:
```
npm run keystore-declare-contract --keystore=<PATH_TO_KEYSTORE_FILE> --account=<PATH_TO_ACCOUNTS_FILE> --contract-name=<CONTRACT_NAME> --url=<RPC_URL> --fee-token=<FEE_TOKEN>
```

### Deploy Cairo Contract Using Keystore
To deploy a Cairo contract using keystore, from the base repository run:
```
npm run keystore-deploy-contract --keystore=<PATH_TO_KEYSTORE_FILE> --account=<PATH_TO_ACCOUNTS_FILE> --url=<RPC_URL> --fee-token=<FEE_TOKEN> --class-hash=<CLASS_HASH>
```

### Whitelist Contract
To whitelist a contract, from the base repository run:
```
npm run whitelist-contract --contract-address=<CONTRACT_ADDRESS>
```

## User Interface Scripts

Expand Down
Loading

0 comments on commit b30b0ea

Please sign in to comment.