Skip to content

Commit

Permalink
Deployment script: fix the addresses output
Browse files Browse the repository at this point in the history
The script now separates the protocol addresses from the each group of
collateral addresses.

This commit also updates the deployment-artifacts-to-app-env script to
generate env vars separated in a similar way.
  • Loading branch information
bpierre committed Sep 3, 2024
1 parent f196f6e commit 144063d
Show file tree
Hide file tree
Showing 2 changed files with 188 additions and 42 deletions.
116 changes: 110 additions & 6 deletions contracts/utils/deploy-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ e.g. --chain-id can be set via CHAIN_ID instead. Parameters take precedence over

const ANVIL_FIRST_ACCOUNT = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";

const PROTOCOL_CONTRACTS_VALID_NAMES = [
"WETHTester",
"BoldToken",
"CollateralRegistry",
"HintHelpers",
"MultiTroveGetter",
];

const argv = minimist(process.argv.slice(2), {
alias: {
h: "help",
Expand Down Expand Up @@ -185,27 +193,43 @@ Deploying Liquity contracts with the following settings:
`broadcast/DeployLiquity2.s.sol/${options.chainId}/run-latest.json`,
);

const collateralContracts = await getAllCollateralsContracts(deployedContracts, options);

// XXX hotfix: we were leaking Github secrets in "deployer"
// TODO: check if "deployer" is a private key, and calculate its address and use it instead?
const { deployer, ...safeOptions } = options;

const protocolContracts = Object.fromEntries(
filterProtocolContracts(deployedContracts),
);

// write env file
await fs.writeJson("deployment-context-latest.json", {
options: safeOptions,
deployedContracts: Object.fromEntries(deployedContracts),
deployedContracts,
collateralContracts,
protocolContracts,
});

// format deployed contracts
const longestContractName = Math.max(
...deployedContracts.map(([name]) => name.length),
);
const deployedContractsFormatted = deployedContracts
.map(([name, address]) => `${name.padEnd(longestContractName)} ${address}`)
.join("\n");

echo("Contract deployment complete.");
const formatContracts = (contracts: Array<string[]>) =>
contracts.map(([name, address]) => ` ${name.padEnd(longestContractName)} ${address}`).join("\n");

echo("Protocol contracts:");
echo("");
echo(formatContracts(filterProtocolContracts(deployedContracts)));
echo("");
echo(deployedContractsFormatted);
echo(
collateralContracts.map((collateral, index) => (
`Collateral ${index + 1} contracts:\n\n${formatContracts(Object.entries(collateral))}`
)).join("\n\n"),
);
echo("");
echo("Deployment complete.");
echo("");
}

Expand Down Expand Up @@ -255,6 +279,10 @@ async function getDeployedContracts(jsonPath: string) {
throw new Error("Invalid deployment log: " + JSON.stringify(latestRun));
}

function filterProtocolContracts(contracts: Awaited<ReturnType<typeof getDeployedContracts>>) {
return contracts.filter(([name]) => PROTOCOL_CONTRACTS_VALID_NAMES.includes(name));
}

function safeParseInt(value: string) {
const parsed = parseInt(value, 10);
return isNaN(parsed) ? undefined : parsed;
Expand Down Expand Up @@ -296,3 +324,79 @@ async function parseArgs() {

return { options, networkPreset };
}

async function castCall(
rpcUrl: string,
contract: string,
method: string,
...args: string[]
) {
try {
const result = await $`cast call ${contract} ${method} ${args.join(" ")} --rpc-url '${rpcUrl}'`;
return result.stdout.trim();
} catch (error) {
console.error(`Error calling ${contract} ${method} ${args.join(" ")}: ${error}`);
throw error;
}
}

async function getCollateralContracts(
collateralIndex: number,
collateralRegistry: string,
rpcUrl: string,
) {
const [token, troveManager] = await Promise.all([
castCall(rpcUrl, collateralRegistry, "getToken(uint256)(address)", String(collateralIndex)),
castCall(rpcUrl, collateralRegistry, "getTroveManager(uint256)(address)", String(collateralIndex)),
]);

const [
activePool,
borrowerOperations,
sortedTroves,
stabilityPool,
] = await Promise.all([
castCall(rpcUrl, troveManager, "activePool()(address)"),
castCall(rpcUrl, troveManager, "borrowerOperations()(address)"),
castCall(rpcUrl, troveManager, "sortedTroves()(address)"),
castCall(rpcUrl, troveManager, "stabilityPool()(address)"),
]);

return {
activePool,
borrowerOperations,
sortedTroves,
stabilityPool,
token,
troveManager,
};
}

async function getAllCollateralsContracts(
deployedContracts: Array<string[]>,
options: Awaited<ReturnType<typeof parseArgs>>["options"],
) {
const deployedContractsRecord = Object.fromEntries(deployedContracts);

const ccall = async (contract: string, method: string, ...args: string[]) => {
const result = await $`cast call ${contract} ${method} ${args.join(" ")} --rpc-url '${options.rpcUrl}'`;
return result.stdout.trim();
};

const totalCollaterals = Number(
await ccall(
deployedContractsRecord.CollateralRegistry,
"totalCollaterals()",
),
);

return Promise.all(
Array.from({ length: totalCollaterals }, (_, index) => (
getCollateralContracts(
index,
deployedContractsRecord.CollateralRegistry,
options.rpcUrl,
)
)),
);
}
114 changes: 78 additions & 36 deletions contracts/utils/deployment-artifacts-to-app-env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,31 @@ const argv = minimist(process.argv.slice(2), {

const ZAddress = z.string().regex(/^0x[0-9a-fA-F]{40}$/);
const ZDeploymentContext = z.object({
deployedContracts: z.record(ZAddress),
deployedContracts: z.array(z.tuple([z.string(), ZAddress])),
collateralContracts: z.array(
z.object({
activePool: ZAddress,
borrowerOperations: ZAddress,
sortedTroves: ZAddress,
stabilityPool: ZAddress,
token: ZAddress,
troveManager: ZAddress,
}),
),
protocolContracts: z.object({
BoldToken: ZAddress,
CollateralRegistry: ZAddress,
HintHelpers: ZAddress,
MultiTroveGetter: ZAddress,
WETHTester: ZAddress,
}),
});

type DeploymentContext = z.infer<typeof ZDeploymentContext>;

const NULL_ADDRESS = `0x${"0".repeat(40)}`;

export async function main() {
export function main() {
const options = {
help: argv["help"],
append: argv["append"],
Expand All @@ -58,24 +75,24 @@ export async function main() {
process.exit(1);
}

const { deployedContracts } = parseDeploymentContext(
await fs.readFile(options.inputJsonPath, "utf-8"),
const deploymentContext = parseDeploymentContext(
fs.readFileSync(options.inputJsonPath, "utf-8"),
);

const outputEnv = objectToEnvironmentVariables(
deployedContractsToAppEnvVariables(deployedContracts),
deployedContractsToAppEnvVariables(deploymentContext),
);

if (!options.outputEnvPath) {
console.log(outputEnv);
process.exit(0);
}

await fs.ensureFile(options.outputEnvPath);
fs.ensureFileSync(options.outputEnvPath);
if (options.append) {
await fs.appendFile(options.outputEnvPath, `\n${outputEnv}\n`);
fs.appendFileSync(options.outputEnvPath, `\n${outputEnv}\n`);
} else {
await fs.writeFile(options.outputEnvPath, `${outputEnv}\n`);
fs.writeFileSync(options.outputEnvPath, `${outputEnv}\n`);
}

console.log(`\nEnvironment variables written to ${options.outputEnvPath}.\n`);
Expand All @@ -85,14 +102,24 @@ function objectToEnvironmentVariables(object: Record<string, unknown>) {
return Object.entries(object)
.map(([key, value]) => `${key}=${value}`)
.sort()
.sort((a, b) => {
if (a.includes("_COLL_") && !b.includes("_COLL_")) {
return -1;
}
if (!a.includes("_COLL_") && b.includes("_COLL_")) {
return 1;
}
return 0;
})
.join("\n");
}

function deployedContractsToAppEnvVariables(deployedContracts: DeploymentContext["deployedContracts"]) {
function deployedContractsToAppEnvVariables(deployedContext: DeploymentContext) {
const appEnvVariables: Record<string, string> = {};

for (const [contractName, address] of Object.entries(deployedContracts)) {
const envVarName = contractNameToAppEnvVariable(contractName);
// protocol contracts
for (const [contractName, address] of Object.entries(deployedContext.protocolContracts)) {
const envVarName = contractNameToAppEnvVariable(contractName, "CONTRACT");
if (envVarName) {
appEnvVariables[envVarName] = address;
}
Expand All @@ -101,36 +128,48 @@ function deployedContractsToAppEnvVariables(deployedContracts: DeploymentContext
appEnvVariables.NEXT_PUBLIC_CONTRACT_FUNCTION_CALLER = NULL_ADDRESS;
appEnvVariables.NEXT_PUBLIC_CONTRACT_HINT_HELPERS = NULL_ADDRESS;

// collateral contracts
for (const [index, contract] of Object.entries(deployedContext.collateralContracts)) {
for (const [contractName, address] of Object.entries(contract)) {
const envVarName = contractNameToAppEnvVariable(contractName, `COLL_${index}_CONTRACT`);
if (envVarName) {
appEnvVariables[envVarName] = address;
}
}
}

return appEnvVariables;
}

function contractNameToAppEnvVariable(contractName: string) {
function contractNameToAppEnvVariable(contractName: string, prefix: string = "") {
prefix = `NEXT_PUBLIC_${prefix}`;
switch (contractName) {
case "ActivePool":
return "NEXT_PUBLIC_CONTRACT_ACTIVE_POOL";
// protocol contracts
case "BoldToken":
return "NEXT_PUBLIC_CONTRACT_BOLD_TOKEN";
case "BorrowerOperations":
return "NEXT_PUBLIC_CONTRACT_BORROWER_OPERATIONS";
case "CollSurplusPool":
return "NEXT_PUBLIC_CONTRACT_COLL_SURPLUS_POOL";
case "DefaultPool":
return "NEXT_PUBLIC_CONTRACT_DEFAULT_POOL";
case "ERC20Faucet":
return "NEXT_PUBLIC_CONTRACT_COLL_TOKEN";
case "GasPool":
return "NEXT_PUBLIC_CONTRACT_GAS_POOL";
case "MockInterestRouter":
return "NEXT_PUBLIC_CONTRACT_INTEREST_ROUTER";
case "PriceFeedTestnet":
return "NEXT_PUBLIC_CONTRACT_PRICE_FEED";
case "SortedTroves":
return "NEXT_PUBLIC_CONTRACT_SORTED_TROVES";
case "StabilityPool":
return "NEXT_PUBLIC_CONTRACT_STABILITY_POOL";
case "TroveManager":
case "TroveManagerTester":
return "NEXT_PUBLIC_CONTRACT_TROVE_MANAGER";
return `${prefix}_BOLD_TOKEN`;
case "CollateralRegistry":
return `${prefix}_COLLATERAL_REGISTRY`;
case "HintHelpers":
return `${prefix}_HINT_HELPERS`;
case "MultiTroveGetter":
return `${prefix}_MULTI_TROVE_GETTER`;
case "WETH":
case "WETHTester":
return `${prefix}_WETH`;

// collateral contracts
case "activePool":
return `${prefix}_ACTIVE_POOL`;
case "borrowerOperations":
return `${prefix}_BORROWER_OPERATIONS`;
case "sortedTroves":
return `${prefix}_SORTED_TROVES`;
case "stabilityPool":
return `${prefix}_STABILITY_POOL`;
case "token":
return `${prefix}_TOKEN`;
case "troveManager":
return `${prefix}_TROVE_MANAGER`;
}
return null;
}
Expand Down Expand Up @@ -158,6 +197,9 @@ function parseDeploymentContext(content: string) {
}).join("\n"),
);
console.error("");
console.error("Received:");
console.error(JSON.stringify(json, null, 2));

process.exit(1);
}

Expand Down

0 comments on commit 144063d

Please sign in to comment.