Skip to content
Open
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
20 changes: 17 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
MNEMONIC="test test test test test test test test test test test junk"
PRIVATE_KEY=""
INFURA_API_KEY=""
ETHERSCAN_API_KEY=""
PRIVATE_KEY=
LEDGER_ACCOUNT=

INFURA_API_KEY=
ETHEREUM_MAINNET_RPC_URL=
ETHEREUM_SEPOLIA_RPC_URL=

ETHERSCAN_API_KEY=

INITIAL_OWNER=
TIMELOCK_ADMIN_ADDRESS=
TIMELOCK_MIN_DELAY=
TIMELOCK_ADDRESS=
TOKEN_PROXY_ADDRESS=
TOKEN_NEW_IMPLEMENTATION_ADDRESS=

SAFE_API_KEY=
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ coverage
artifacts/
.vscode
typechain-types
safe-config*.json
166 changes: 164 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ Update `scripts/input.json` with your multisig address:
### 2. Generate Deterministic Deployment Transactions

```bash
npm run generate
npm run generate:deployment:sepolia
npm run generate:deployment:mainnet
```

This creates `scripts/output.json` with deployment transactions that use CREATE2 for deterministic addresses:
This creates `scripts/deployment/output-deployment.json` with deployment transactions that use CREATE2 for deterministic addresses:

- TimelockController: starts with `0x00ad`
- Implementation: starts with `0x001b`
Expand Down Expand Up @@ -64,6 +65,86 @@ This verifies all contracts on Etherscan and validates deployment parameters.
3. **TransparentUpgradeableProxy** - Proxy pointing to implementation
4. **ProxyAdmin** - Owned by TimelockController (deployed by proxy constructor)

## Deploy with Safe Multisig

1. Create `scripts/safe/safe-config-<network>.json` for the configuration of the Safe account filling the owners, threshold, private key for the sender of the tx, etc. for your new Safe Multisig.

Example for Ethereum Sepolia:
```
{
"chain": {
"id": 11155111,
"rpcUrls": {
"default": {
"http": ["https://eth-sepolia.g.alchemy.com/v2/<your_apiKey>"]
}
}
},
"owners": ["ethAddress1", "ethAddress2"],
"privateKeySender": "privateKeySender",
"threshold": 2,
"apiKey": "your_safe_api_key",
"safeAddress": "",
"contractNetworks": {
"11155111": {
"safeProxyFactoryAddress": "0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2",
"multiSendAddress": "0xA238CBeb142c10Ef7Ad8442C6D1f9E89e07e7761",
"multiSendCallOnlyAddress": "0x40A2aCCbd92BCA938b02010E17A5b8929b49130D",
"fallbackHandlerAddress": "0xf48f2B2d2a534e402487b3ee7C18c33Aec0Fe5e4",
"safeSingletonAddress": "0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552"
}
}
}
```

To get Safe API Key you can get information [here](https://docs.safe.global/core-api/how-to-use-api-keys)

2. Deploy new Safe. Signer will be first owner by default (safeConfig.privateKeys[0] in `scripts/safe/deploySafe.ts`)
```bash
npm run safe:deploy:sepolia
```
3. Copy the safe address deployed to the `safeAddress` parameter of your `safe-config-<network>.json`
4. Configure `input.json` with `MULTISIG` value properly. Be sure the address is a checksum address.
5. Generate the transactions for the deployment that will be needed to be executed by each owner of the Safe Multisig.
```bash
npm run generate:deployment:sepolia
```
This will generate `output-deployment.json` with the transactions to be proposed, confirmed and executed later.
6. Propose first transaction for the owner configured in your `hardhat.config.ts` (change `PRIVATE_KEY` or `LEDGER_ACCOUNT` from your `.env` for each owner) with `scripts/safe/deployment/proposeTransaction.ts`. The transaction will be confirmed also for this owner.
```
const transactionIndex = 0; // Index of the transaction to propose
```

Then execute:

```bash
npm run safe:propose:deployment:transaction:sepolia
```
7. Confirm the first transaction with the other owners needed for the threshold (change `PRIVATE_KEY` or `LEDGER_ACCOUNT` from your `.env` for each owner) checking `transactionIndex` in `scripts/safe/deployment/confirmTransaction.ts`.
```
const transactionIndex = 0; // Index of the transaction to confirm
```

Then execute:

```bash
npm run safe:confirm:deployment:transaction:sepolia
```
8. Once you have confirmed the first transaction with the different owners you can execute the transaction. Check `transactionIndex` in `scripts/safe/deployment/executeTransaction.ts`.
```
const transactionIndex = 0; // Index of the transaction to execute

```

```bash
npm run safe:execute:deployment:transaction:sepolia
```
9. Once done the process for the first transaction proceed in the same way from the step `6.` but for the second (transactionIndex = 1) and third transaction (transactionIndex = 2) for the deployment in order to complete the deployment with the Safe Multisig.
10. Verify the upgrade
```bash
npm run verify:deployment:sepolia
```

## Upgrade Process

All upgrades go through the Timelock (2-day minimum delay):
Expand All @@ -73,6 +154,79 @@ All upgrades go through the Timelock (2-day minimum delay):
3. Wait 2 days (minimum delay)
4. Execute upgrade transaction

## Upgrade with Safe Multisig

1. Having `scripts/safe/safe-config-<network>.json` and `input.json` from deployment, fill `TIMELOCK_PROXY` and `BILLIONS_TOKEN_PROXY` with checksum address in `input.json` and follow next steps.
2. Generate the transactions for the upgrade that will be needed to be executed by each owner of the Safe Multisig.
```bash
npm run generate:upgrade:sepolia
```
This will generate `output-schedule-upgrade.json` and `output-execute-upgrade.json` with the transactions to be proposed, confirmed and executed later.
3. Propose first transaction for the owner configured in your `hardhat.config.ts` (change `PRIVATE_KEY` or `LEDGER_ACCOUNT` from your `.env` for each owner) with `scripts/safe/schedule-upgrade/proposeTransaction.ts`. The transaction will be confirmed also for this owner.
```
const transactionIndex = 0; // Index of the transaction to propose
```

Then execute:

```bash
npm run safe:propose:schedule:upgrade:transaction:sepolia
```
4. Confirm the first transaction with the other owners needed for the threshold (change `PRIVATE_KEY` or `LEDGER_ACCOUNT` from your `.env` for each owner) checking `transactionIndex` in `scripts/safe/schedule-upgrade/confirmTransaction.ts`.
```
const transactionIndex = 0; // Index of the transaction to confirm
```

Then execute:

```bash
npm run safe:confirm:schedule:upgrade:transaction:sepolia
```
5. Once you have confirmed the first transaction with the different owners you can execute the transaction. Check `transactionIndex` in `scripts/safe/schedule-upgrade/executeTransaction.ts`.
```
const transactionIndex = 0; // Index of the transaction to execute

```

```bash
npm run safe:execute:schedule:upgrade:transaction:sepolia
```
6. Once done the process for the first transaction proceed in the same way from the step `3.` but for the second (transactionIndex = 1) for the schedule-upgrade in order to complete the schedule-upgrade with the Safe Multisig.
7. Wait 2 days (minimum delay) for the execution of the upgrade.
8. Propose first transaction for the owner configured in your `hardhat.config.ts` (change `PRIVATE_KEY` or `LEDGER_ACCOUNT` from your `.env` for each owner) with `scripts/safe/execute-upgrade/proposeTransaction.ts`. The transaction will be confirmed also for this owner.
```
const transactionIndex = 0; // Index of the transaction to propose
```

Then execute:

```bash
npm run safe:propose:execute:upgrade:transaction:sepolia
```
9. Confirm the first transaction with the other owners needed for the threshold (change `PRIVATE_KEY` or `LEDGER_ACCOUNT` from your `.env` for each owner) checking `transactionIndex` in `scripts/safe/execute-upgrade/confirmTransaction.ts`.
```
const transactionIndex = 0; // Index of the transaction to confirm
```

Then execute:

```bash
npm run safe:confirm:execute:upgrade:transaction:sepolia
```
10. Once you have confirmed the first transaction with the different owners you can execute the transaction. Check `transactionIndex` in `scripts/safe/execute-upgrade/executeTransaction.ts`.
```
const transactionIndex = 0; // Index of the transaction to execute

```

```bash
npm run safe:execute:execute:upgradetransaction:sepolia
```
11. Verify the upgrade
```bash
npm run verify:upgrade:sepolia
```

## Architecture

```
Expand All @@ -97,3 +251,11 @@ TimelockController (2-day delay)
- ProxyAdmin controlled by Timelock
- No emergency pause or admin functions on token
- All token supply minted at initialization

## Ethereum Mainnet Deployment
The deployment in Ethereum Mainnet was done with the Safe Multisig `0xa31c18d0e9EBab1cB4d4B96193DCb44058F4bb75`.
| Smart contract | Address |
|:-----------------------:|:------------------------------------------:|
| **TimelockController** | [0x00ad9eb03caf7c5e616ed843d46294e1932cc8ca](https://etherscan.io/address/0x00ad9eb03caf7c5e616ed843d46294e1932cc8ca) |
| **BillionsNetworkToken (proxy)** | [0xb1110919016846972056ab995054d65560d5f05e](https://etherscan.io/address/0xb1110919016846972056ab995054d65560d5f05e) |

72 changes: 67 additions & 5 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import type { HardhatUserConfig } from 'hardhat/config';
import '@openzeppelin/hardhat-upgrades';
import '@nomicfoundation/hardhat-toolbox';
import '@nomicfoundation/hardhat-ledger';
import 'dotenv/config';
import { HttpNetworkUserConfig } from 'hardhat/types';

const PRIVATE_KEY = process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : undefined;
const MNEMONIC = process.env.MNEMONIC ? { mnemonic: process.env.MNEMONIC } : undefined;
const LEDGER_ACCOUNT = process.env.LEDGER_ACCOUNT ? [process.env.LEDGER_ACCOUNT] : undefined;

const sharedNetworkConfig: HttpNetworkUserConfig = {};
if (LEDGER_ACCOUNT) {
sharedNetworkConfig.ledgerAccounts = LEDGER_ACCOUNT;
} else {
sharedNetworkConfig.accounts = PRIVATE_KEY || MNEMONIC;
}

const config: HardhatUserConfig = {
solidity: {
Expand All @@ -18,21 +28,73 @@ const config: HardhatUserConfig = {
},
networks: {
sepolia: {
url: process.env.SEPOLIA_RPC_URL || `https://sepolia.infura.io/v3/${process.env.INFURA_API_KEY}`,
accounts: PRIVATE_KEY || MNEMONIC,
...sharedNetworkConfig,
url: process.env.ETHEREUM_SEPOLIA_RPC_URL || `https://sepolia.infura.io/v3/${process.env.INFURA_API_KEY}`,
chainId: 11155111,
},
mainnet: {
url: process.env.MAINNET_RPC_URL || `https://mainnet.infura.io/v3/${process.env.INFURA_API_KEY}`,
accounts: PRIVATE_KEY || MNEMONIC,
...sharedNetworkConfig,
url: process.env.ETHEREUM_MAINNET_RPC_URL || `https://mainnet.infura.io/v3/${process.env.INFURA_API_KEY}`,
chainId: 1,
},
'billions-testnet': {
...sharedNetworkConfig,
url: process.env.BILLIONS_TESTNET_RPC_URL,
chainId: 6913,
},
'billions-mainnet': {
...sharedNetworkConfig,
url: process.env.BILLIONS_MAINNET_RPC_URL,
chainId: 6913,
},
// hardhat: {
// chainId: 11155111,
// forking: {
// url: `${process.env.ETHEREUM_SEPOLIA_RPC_URL}`,
// },
// chains: {
// 11155111: {
// hardforkHistory: {
// london: 100000,
// },
// },
// },
// accounts: [
// {
// privateKey: process.env.PRIVATE_KEY as string,
// balance: "1000000000000000000000000",
// },
// ],
// },
localhost: {
url: 'http://127.0.0.1:8545',
},
},
etherscan: {
apiKey: process.env.ETHERSCAN_API_KEY,
apiKey: {
sepolia: `${process.env.ETHERSCAN_API_KEY}`,
mainnet: `${process.env.ETHERSCAN_API_KEY}`,
'billions-testnet': 'abc',
'billions-mainnet': 'abc',
},
customChains: [
{
network: 'billions-testnet',
chainId: 6913,
urls: {
apiURL: 'https://billions-testnet-blockscout.eu-north-2.gateway.fm/api/',
browserURL: 'https://billions-testnet-blockscout.eu-north-2.gateway.fm',
},
},
{
network: 'billions-mainnet',
chainId: 45056,
urls: {
apiURL: 'https://billions-blockscout.eu-north-2.gateway.fm/api/',
browserURL: 'https://billions-blockscout.eu-north-2.gateway.fm',
},
},
],
},
paths: {
sources: './contracts',
Expand Down
Loading