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

fix: unbox command. #2337

Merged
merged 2 commits into from
Sep 15, 2023
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
2 changes: 2 additions & 0 deletions yarn-project/boxes/private-token/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,12 @@
"postcss-loader": "^7.3.3",
"prettier": "^3.0.3",
"resolve-typescript-plugin": "^2.0.1",
"stream-browserify": "^3.0.0",
"style-loader": "^3.3.3",
"ts-jest": "^29.1.0",
"ts-loader": "^9.4.4",
"ts-node": "^10.9.1",
"tty-browserify": "^0.0.1",
"typescript": "^5.0.4",
"util": "^0.12.5",
"webpack": "^5.88.2",
Expand Down
17 changes: 13 additions & 4 deletions yarn-project/boxes/private-token/src/app/home.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { CompleteAddress } from '@aztec/aztec.js';
import { useState } from 'react';
import { SANDBOX_URL } from '../config.js';
import { Banner, Spinner } from './components/index.js';
import { WalletDropdown } from './components/wallet_dropdown.js';
import { Contract } from './contract.js';
Expand Down Expand Up @@ -48,13 +49,21 @@ export function Home() {
<Spinner />
</div>
)}
{!isLoadingWallet && !!selectedWallet && (
{!isLoadingWallet && (
<div className="py-8">
{!!selectWalletError && `Failed to load accounts: ${selectWalletError}`}
{!selectWalletError && <Contract wallet={selectedWallet} onDeploy={() => setIsContractDeployed(true)}/>}
{!!selectWalletError && (
<>
{`Failed to load accounts. Error: ${selectWalletError}`}
<br />
{`Make sure the Aztec Sandbox is running at: ${SANDBOX_URL}`}
</>
)}
{!selectWalletError && !selectedWallet && `No accounts.`}
{!selectWalletError && !!selectedWallet && (
<Contract wallet={selectedWallet} onDeploy={() => setIsContractDeployed(true)} />
)}
</div>
)}
{!isLoadingWallet && !selectedWallet && `${selectWalletError} ${selectedWallet}`}
</div>
</div>

Expand Down
2 changes: 1 addition & 1 deletion yarn-project/boxes/private-token/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { PrivateTokenContractAbi } from './artifacts/PrivateToken.js'; // update

export const contractAbi: ContractAbi = PrivateTokenContractAbi;

const SANDBOX_URL: string = process.env.SANDBOX_URL || 'http://localhost:8080';
export const SANDBOX_URL: string = process.env.SANDBOX_URL || 'http://localhost:8080';
export const rpcClient: AztecRPC = createAztecRpcClient(SANDBOX_URL);

export const CONTRACT_ADDRESS_PARAM_NAMES = ['owner', 'contract_address', 'recipient'];
Expand Down
61 changes: 34 additions & 27 deletions yarn-project/cli/src/unbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,26 @@ const NOIR_CONTRACTS_PATH = 'yarn-project/noir-contracts/src/contracts';
const BOXES_PATH = 'yarn-project/boxes';

/**
* Converts a contract name in "upper camel case" to a folder name in snake case.
* Converts a contract name in "upper camel case" to a folder name in snake case or kebab case.
* @param contractName - The contract name.
* @returns The folder name.
* */
function contractNameToFolder(contractName: string): string {
return contractName.replace(/[\w]([A-Z])/g, m => m[0] + '_' + m[1]).toLowerCase();
function contractNameToFolder(contractName: string, separator = '-'): string {
return contractName.replace(/[\w]([A-Z])/g, m => `${m[0]}${separator}${m[1]}`).toLowerCase();
}

/**
* If the box contains the noir contract source code, we don't need to download it from github.
* Otherwise, we download the contract source code from the `noir-contracts` and `noir-libs` subpackages.
*/
async function isDirectoryNonEmpty(directoryPath: string): Promise<boolean> {
const files = await fs.readdir(directoryPath);
return files.length > 0;
try {
const files = await fs.readdir(directoryPath);
return files.length > 0;
} catch (e) {
// Directory does not exist.
return false;
}
}

/**
Expand All @@ -44,7 +49,7 @@ async function isDirectoryNonEmpty(directoryPath: string): Promise<boolean> {
* @param localOutputPath - local path to copy to
*/
async function copyFolderFromGithub(data: JSZip, repositoryFolderPath: string, localOutputPath: string, log: LogFn) {
log('downloading from github:', repositoryFolderPath);
log(`Downloading from github: ${repositoryFolderPath}`);
const repositoryDirectories = Object.values(data.files).filter(file => {
return file.dir && file.name.startsWith(repositoryFolderPath);
});
Expand Down Expand Up @@ -83,13 +88,14 @@ async function downloadContractAndBoxFromGithub(
outputPath: string,
log: LogFn,
): Promise<void> {
const tagName = `aztec-packages-v${tagVersion}`;
// small string conversion, in the ABI the contract name looks like PrivateToken
// but in the repostory it looks like private_token
const snakeCaseContractName = contractNameToFolder(contractName);

log(`Downloaded '@aztex/boxes/${snakeCaseContractName}' to ${outputPath}`);
const kebabCaseContractName = contractNameToFolder(contractName, '-');
log(`Downloading @aztex/boxes/${kebabCaseContractName} to ${outputPath}...`);
// Step 1: Fetch the monorepo ZIP from GitHub, matching the CLI version
const url = `https://github.com/${GITHUB_OWNER}/${GITHUB_REPO}/archive/refs/tags/aztec-packages-v${tagVersion}.zip`;
const url = `https://github.com/${GITHUB_OWNER}/${GITHUB_REPO}/archive/refs/tags/${tagName}.zip`;
const response = await fetch(url);
const buffer = await response.arrayBuffer();

Expand All @@ -98,39 +104,39 @@ async function downloadContractAndBoxFromGithub(

// Step 2: copy the '@aztec/boxes/{contract-name}' subpackage to the output directory
// this is currently only implemented for PrivateToken under 'boxes/private-token/'
const repoDirectoryPrefix = `${GITHUB_REPO}-v${tagVersion}/`;
const repoDirectoryPrefix = `${GITHUB_REPO}-${tagName}`;

const boxPath = `${repoDirectoryPrefix}${BOXES_PATH}/${snakeCaseContractName}`;
const boxPath = `${repoDirectoryPrefix}/${BOXES_PATH}/${kebabCaseContractName}`;
await copyFolderFromGithub(data, boxPath, outputPath, log);

const boxContainsNoirSource = await isDirectoryNonEmpty(`${outputPath}/src/contracts`);
const contractTargetDirectory = path.join(outputPath, 'src', 'contracts');
const boxContainsNoirSource = await isDirectoryNonEmpty(contractTargetDirectory);
if (boxContainsNoirSource) {
return;
}

// this remaining logic only kicks in if the box doesn't already have a src/contracts folder
// in which case we optimistically grab the noir source files from the
// noir-contracts and noir-libs subpackages and pray that the versions are compatible
log('Copying noir contracts...');

// source noir files for the contract are in this folder
const contractFolder = `${NOIR_CONTRACTS_PATH}/${snakeCaseContractName}_contract`;
const snakeCaseContractName = contractNameToFolder(contractName, '_');
const contractDirectoryPath = `${repoDirectoryPrefix}/${NOIR_CONTRACTS_PATH}/${snakeCaseContractName}_contract`;
// copy the noir contracts to the output directory under subdir /src/contracts/
const contractDirectoryPath = `${repoDirectoryPrefix}${contractFolder}/`;

const contractFiles = Object.values(data.files).filter(file => {
return !file.dir && file.name.startsWith(contractDirectoryPath);
});

const contractTargetDirectory = path.join(outputPath, 'src', 'contracts');
await fs.mkdir(contractTargetDirectory, { recursive: true });
// Nargo.toml file needs to be in the root of the contracts directory,
// and noir files in the src/ subdirectory
await fs.mkdir(path.join(contractTargetDirectory, 'src'), { recursive: true });
for (const file of contractFiles) {
const targetPath = path.join(contractTargetDirectory, file.name.replace(contractDirectoryPath, ''));
log(`Copying ${file.name} to ${targetPath}`);
const filename = file.name.replace(`${contractDirectoryPath}/`, '');
const targetPath = path.join(contractTargetDirectory, filename);
const content = await file.async('nodebuffer');
await fs.writeFile(targetPath, content);
log(`Copied ${file.name} to ${targetPath}`);
log(` ✓ ${filename}`);
}
}
/**
Expand All @@ -143,7 +149,8 @@ async function downloadContractAndBoxFromGithub(
*/
async function updatePackagingConfigurations(packageVersion: string, outputPath: string, log: LogFn): Promise<void> {
await updatePackageJsonVersions(packageVersion, outputPath, log);
await updateTsConfig(outputPath, log);
await updateTsConfig('tsconfig.json', outputPath, log);
await updateTsConfig('tsconfig.dest.json', outputPath, log);
await updateNargoToml(packageVersion, outputPath, log);
}

Expand All @@ -160,7 +167,7 @@ async function updateNargoToml(packageVersion: string, outputPath: string, log:
const updatedLines = lines.map(line => line.replace(/tag="master"/g, `tag="v${packageVersion}"`));
const updatedContent = updatedLines.join('\n');
await fs.writeFile(nargoTomlPath, updatedContent);
log(`Updated Nargo.toml to point to local copy of noir-libs`);
log(`Updated Nargo.toml to point to local copy of noir-libs.`);
}

/**
Expand All @@ -169,9 +176,9 @@ async function updateNargoToml(packageVersion: string, outputPath: string, log:
* so we remove the entries to install the the workspace packages from npm.
* @param outputPath - directory we are unboxing to
*/
async function updateTsConfig(outputPath: string, log: LogFn) {
async function updateTsConfig(filename: string, outputPath: string, log: LogFn) {
try {
const tsconfigJsonPath = path.join(outputPath, 'tsconfig.json');
const tsconfigJsonPath = path.join(outputPath, filename);
const data = await fs.readFile(tsconfigJsonPath, 'utf8');
const config = JSON.parse(data);

Expand All @@ -180,9 +187,9 @@ async function updateTsConfig(outputPath: string, log: LogFn) {
const updatedData = JSON.stringify(config, null, 2);
await fs.writeFile(tsconfigJsonPath, updatedData, 'utf8');

log('tsconfig.json has been updated');
log(`Updated ${filename}.`);
} catch (error) {
log('Error updating tsconfig.json:', error);
log(`Error updating ${filename}.`);
throw error;
}
}
Expand Down Expand Up @@ -234,7 +241,7 @@ async function updatePackageJsonVersions(packageVersion: string, outputPath: str
const updatedContent = JSON.stringify(packageData, null, 2);
await fs.writeFile(packageJsonPath, updatedContent);

log(`Updated package.json versions to ${packageVersion}`);
log(`Updated package.json versions to: ${packageVersion}`);
}

/**
Expand Down
2 changes: 2 additions & 0 deletions yarn-project/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -12516,11 +12516,13 @@ __metadata:
react-dom: ^18.2.0
resolve-typescript-plugin: ^2.0.1
serve: ^14.2.1
stream-browserify: ^3.0.0
style-loader: ^3.3.3
tailwindcss: ^3.3.3
ts-jest: ^29.1.0
ts-loader: ^9.4.4
ts-node: ^10.9.1
tty-browserify: ^0.0.1
typescript: ^5.0.4
util: ^0.12.5
webpack: ^5.88.2
Expand Down