Skip to content

Commit

Permalink
Merge pull request #112 from liquity/custom-chains
Browse files Browse the repository at this point in the history
These changes allow arbitrary chains to be set, both in the env vars and/or in the config at runtime (no UI for this yet).

It also includes changes in the Liquity testnet deployment workflow to pass the chain related env vars to the app.
  • Loading branch information
bpierre committed Apr 12, 2024
2 parents e000686 + 5e94371 commit 1578f2d
Show file tree
Hide file tree
Showing 8 changed files with 341 additions and 71 deletions.
22 changes: 17 additions & 5 deletions .github/workflows/testnet-deployment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ jobs:

- name: Install dependencies
run: pnpm install
working-directory: contracts

- name: Install Vercel CLI
run: pnpm install --global vercel@canary
Expand All @@ -92,13 +91,26 @@ jobs:
uses: actions/download-artifact@v4
with:
name: deployment-context

- name: Pull Vercel Environment Information
run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
path: ./contracts

- name: Prepare Environment from Latest Deployment Context
run: pnpm tsx utils/deployment-artifacts-to-app-env.ts ../deployment-context-latest.json ../frontend/.env.local
working-directory: contracts
env:
APP_ENV_FILE: ../frontend/.env.local
DEPLOYMENT_CONTEXT_FILE: ./deployment-context-latest.json
run: |
echo 'NEXT_PUBLIC_CHAIN_ID=1337' >> $APP_ENV_FILE
echo 'NEXT_PUBLIC_CHAIN_NAME=Liquity Testnet' >> $APP_ENV_FILE
echo 'NEXT_PUBLIC_CHAIN_CURRENCY=Ether|ETH|18' >> $APP_ENV_FILE
echo 'NEXT_PUBLIC_CHAIN_RPC_URL=https://testnet.liquity.org/rpc' >> $APP_ENV_FILE
echo 'NEXT_PUBLIC_CHAIN_BLOCK_EXPLORER=https://testnet.liquity.org/' >> $APP_ENV_FILE
echo 'NEXT_PUBLIC_CHAIN_CONTRACT_MULTICALL=0xffB2BAa708261533b51D18394fd8AFd0E32aBC55|488' >> $APP_ENV_FILE
pnpm tsx utils/deployment-artifacts-to-app-env.ts $DEPLOYMENT_CONTEXT_FILE $APP_ENV_FILE --append
- name: Pull Vercel Environment Information
working-directory: frontend
run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}

- name: Build Project Artifacts
run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}
Expand Down
37 changes: 26 additions & 11 deletions contracts/utils/deployment-artifacts-to-app-env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,19 @@ converts the deployment artifacts created by ./deploy into environment
variables to be used by the Next.js app located in frontend/.
Usage:
./deployment-artifacts-to-app-env.ts <INPUT_JSON> <OUTPUT_ENV>
./deployment-artifacts-to-app-env.ts <INPUT_JSON> <OUTPUT_ENV> [OPTIONS]
Options:
--help, -h Show this help message.
--append Append to the output file instead of overwriting it.
`;

const ZAddress = z.string().regex(/^0x[0-9a-fA-F]{40}$/);

const ZDeploymentContext = z.object({
options: z.object({
chainId: z.number(),
// XXX hotfix: we were leaking Github secrets in "deployer"
// deployer: z.string(), // can be an address or a private key
help: z.boolean(),
openDemoTroves: z.boolean(),
rpcUrl: z.string(),
verify: z.boolean(),
verifier: z.string(),
}),
deployedContracts: z.record(ZAddress),
});
Expand All @@ -33,19 +28,39 @@ type DeploymentContext = z.infer<typeof ZDeploymentContext>;
const NULL_ADDRESS = `0x${"0".repeat(40)}`;

export async function main() {
if ("help" in argv || "h" in argv || argv._.length !== 2) {
const options = {
help: "help" in argv || "h" in argv,
append: "append" in argv,
inputJsonPath: argv._[0],
outputEnvPath: argv._[1],
};

if (options.help) {
echo`${HELP}`;
process.exit(0);
}

const deploymentContext = parseDeploymentContext(await fs.readFile(argv._[0], "utf-8"));
if (!options.inputJsonPath || !options.outputEnvPath) {
console.error("\nInvalid number of arguments provided (--help for instructions).\n");
process.exit(1);
}

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

const outputEnv = objectToEnvironmentVariables(
deploymentContextToAppEnvVariables(deploymentContext),
);

await fs.writeFile(argv._[1], outputEnv);
console.log(outputEnv);
await fs.ensureFile(options.outputEnvPath);
if (options.append) {
await fs.appendFile(options.outputEnvPath, `\n${outputEnv}\n`);
} else {
await fs.writeFile(options.outputEnvPath, `${outputEnv}\n`);
}

console.log(`\nEnvironment variables written to ${options.outputEnvPath}.\n`);
}

function objectToEnvironmentVariables(object: Record<string, unknown>) {
Expand Down
23 changes: 23 additions & 0 deletions frontend/.env
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=1

# Ethereum
NEXT_PUBLIC_CHAIN_ID=1
NEXT_PUBLIC_CHAIN_NAME=Ethereum
NEXT_PUBLIC_CHAIN_CURRENCY=Ether|ETH|18
NEXT_PUBLIC_CHAIN_RPC_URL=https://cloudflare-eth.com
NEXT_PUBLIC_CHAIN_BLOCK_EXPLORER=Etherscan|https://etherscan.io
NEXT_PUBLIC_CHAIN_CONTRACT_ENS_REGISTRY=0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e
NEXT_PUBLIC_CHAIN_CONTRACT_ENS_RESOLVER=0xce01f8eee7E479C928F8919abD53E553a36CeF67|19258213
NEXT_PUBLIC_CHAIN_CONTRACT_MULTICALL=0xca11bde05977b3631167028862be2a173976ca11|14353601

# Hardhat
# NEXT_PUBLIC_CHAIN_ID=31337
# NEXT_PUBLIC_CHAIN_NAME=Hardhat
# NEXT_PUBLIC_CHAIN_CURRENCY=Ether|ETH|18
# NEXT_PUBLIC_CHAIN_RPC_URL=http://127.0.0.1:8545

# Liquity Testnet
# NEXT_PUBLIC_CHAIN_ID=1337
# NEXT_PUBLIC_CHAIN_NAME=Liquity Testnet
# NEXT_PUBLIC_CHAIN_CURRENCY=Ether|ETH|18
# NEXT_PUBLIC_CHAIN_RPC_URL=https://testnet.liquity.org/rpc
# NEXT_PUBLIC_CHAIN_BLOCK_EXPLORER=https://testnet.liquity.org/
# NEXT_PUBLIC_CHAIN_CONTRACT_MULTICALL=0xffB2BAa708261533b51D18394fd8AFd0E32aBC55|488

NEXT_PUBLIC_CONTRACT_ACTIVE_POOL=0x0000000000000000000000000000000000000000
NEXT_PUBLIC_CONTRACT_BOLD_TOKEN=0x0000000000000000000000000000000000000000
Expand Down
90 changes: 85 additions & 5 deletions frontend/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# BOLD App
# Liquity v2 App

## Preview

Expand Down Expand Up @@ -67,16 +67,95 @@ cp .env .env.local

See [./src/env.ts](./src/env.ts) for details about how the environment variables are being imported by the app.

<details>
<summary>Supported Variables</summary>

### `NEXT_PUBLIC_CHAIN_ID`

The Ethereum network to connect to.

Supports the following IDs:
```dosini
# Example
NEXT_PUBLIC_CHAIN_ID=1
```

### `NEXT_PUBLIC_CHAIN_NAME`

The name of the Ethereum network.

```dosini
# Example
NEXT_PUBLIC_CHAIN_NAME=Ethereum
```

### `NEXT_PUBLIC_CHAIN_CURRENCY`

The currency of the Ethereum network.

```dosini
# Format
NEXT_PUBLIC_CHAIN_CURRENCY=name|symbol|decimals

# Example
NEXT_PUBLIC_CHAIN_CURRENCY=Ether|ETH|18
```

### `NEXT_PUBLIC_CHAIN_RPC_URL`

The RPC URL for the Ethereum network.

```dosini
# Example
NEXT_PUBLIC_CHAIN_RPC_URL=https://cloudflare-eth.com
```

### `NEXT_PUBLIC_CHAIN_BLOCK_EXPLORER`

The block explorer for the Ethereum network. Optional.

- `1` (mainnet)
- `31337` (hardhat)
```dosini
# Format
NEXT_PUBLIC_CHAIN_BLOCK_EXPLORER=name|url

Defaults to `1` (mainnet) if the chain ID is not supported.
# Example
NEXT_PUBLIC_CHAIN_BLOCK_EXPLORER=Etherscan|https://etherscan.io
```

### `NEXT_PUBLIC_CHAIN_CONTRACT_ENS_REGISTRY`

The address of the ENS registry contract. Optional.

```dosini
# Format
NEXT_PUBLIC_CHAIN_CONTRACT_ENS_REGISTRY=address

# Example
NEXT_PUBLIC_CHAIN_CONTRACT_ENS_REGISTRY=0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e
```

### `NEXT_PUBLIC_CHAIN_CONTRACT_ENS_RESOLVER`

The address of the ENS resolver contract. Optional.

```dosini
# Format
NEXT_PUBLIC_CHAIN_CONTRACT_ENS_RESOLVER=address|blockCreated

# Example
NEXT_PUBLIC_CHAIN_CONTRACT_ENS_RESOLVER=0xce01f8eee7E479C928F8919abD53E553a36CeF67|19258213
```

### `NEXT_PUBLIC_CHAIN_CONTRACT_MULTICALL`

The address of the Multicall contract. Optional.

```dosini
# Format
NEXT_PUBLIC_CHAIN_CONTRACT_MULTICALL=address|blockCreated

# Example
NEXT_PUBLIC_CHAIN_CONTRACT_MULTICALL=0xca11bde05977b3631167028862be2a173976ca11|14353601
```

### `NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID`

Expand All @@ -85,3 +164,4 @@ A WalletConnect project ID which can be obtained by [creating a WalletConnect pr
### `NEXT_PUBLIC_CONTRACT_…`

Addresses of the Liquity contracts.
</details>
37 changes: 37 additions & 0 deletions frontend/src/comps/Config/Config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@
import type { ReactNode } from "react";

import {
CHAIN_BLOCK_EXPLORER,
CHAIN_CONTRACT_ENS_REGISTRY,
CHAIN_CONTRACT_ENS_RESOLVER,
CHAIN_CONTRACT_MULTICALL,
CHAIN_CURRENCY,
CHAIN_ID,
CHAIN_NAME,
CHAIN_RPC_URL,
CONTRACT_ACTIVE_POOL,
CONTRACT_BOLD_TOKEN,
CONTRACT_BORROWER_OPERATIONS,
Expand All @@ -23,6 +30,29 @@ import { z } from "zod";

export const ConfigSchema = z.object({
chainId: z.number(),
chainName: z.string(),
chainCurrency: z.object({
name: z.string(),
symbol: z.string(),
decimals: z.number(),
}),
chainBlockExplorer: z.object({
name: z.string(),
url: z.string(),
}).optional(),
chainContractEnsRegistry: z.object({
address: zAddress(),
block: z.number().optional(),
}).optional(),
chainContractEnsResolver: z.object({
address: zAddress(),
block: z.number().optional(),
}).optional(),
chainContractMulticall: z.object({
address: zAddress(),
block: z.number().optional(),
}).optional(),
chainRpcUrl: z.string(),
contractActivePool: zAddress(),
contractBoldToken: zAddress(),
contractBorrowerOperations: zAddress(),
Expand All @@ -41,6 +71,13 @@ type Config = z.infer<typeof ConfigSchema>;

const defaultConfig: Config = {
chainId: CHAIN_ID,
chainName: CHAIN_NAME,
chainCurrency: CHAIN_CURRENCY,
chainBlockExplorer: CHAIN_BLOCK_EXPLORER,
chainContractEnsRegistry: CHAIN_CONTRACT_ENS_REGISTRY,
chainContractEnsResolver: CHAIN_CONTRACT_ENS_RESOLVER,
chainContractMulticall: CHAIN_CONTRACT_MULTICALL,
chainRpcUrl: CHAIN_RPC_URL,
contractActivePool: CONTRACT_ACTIVE_POOL,
contractBoldToken: CONTRACT_BOLD_TOKEN,
contractBorrowerOperations: CONTRACT_BORROWER_OPERATIONS,
Expand Down
Loading

0 comments on commit 1578f2d

Please sign in to comment.