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

chore(deployments): create txtx Runbook to upgrade L1/L2 deployments #441

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
74 changes: 74 additions & 0 deletions runbooks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Farcaster Protocol Runbooks

[![Txtx](https://img.shields.io/badge/Operated%20with-Txtx-gree?labelColor=gray)](https://txtx.sh)

## Available Runbooks

`txtx` Runbooks are the perfect companion to Foundry when creating Solidity smart contracts.
Foundry shines in making the development process a breeze; `txtx` makes deploying and operating on your contracts secure, simple, and reproducible.

The following Runbooks are available for this project.

### Deploy L1
Deploys all L1 contracts. To execute, run:
```console
txtx run deploy-l1 -u --env devnet
```

### Deploy L2
Deploys all L2 contracts. To execute, run:
```console
txtx run deploy-l2 -u --env devnet
```

### Grant Role
Calls the grantRole function of the StorageRegistry contract. To execute, run:
```console
txtx run grant-role --env devnet
```

## Getting Started

This repository is using [txtx](https://txtx.sh) for handling its on-chain operations.

`txtx` takes its inspiration from a battle tested devops best practice named `infrastructure as code`, that have transformed cloud architectures.


### Installation

#### macOS
```console
brew tap txtx/txtx
brew install txtx
```
#### Linux
```console
sudo snap install txtx
```

### List runbooks available in this repository
```console
$ txtx ls
ID Name Description
deploy-l1 Deploy L1 Deploys all L1 contracts
deploy-l2 Deploy L2 Deploys all L2 contracts
grant-role Grant Role Calls the grantRole function of the StorageRegistry contract
```

### Scaffold a new runbook

```console
$ txtx new
```

Access tutorials and documentation at [docs.txtx.sh](https://docs.txtx.sh) to understand the syntax and discover the powerful features of txtx.

Additionally, the [Visual Studio Code extension](https://marketplace.visualstudio.com/items?itemName=txtx.txtx) will make writing runbooks easier.



### Execute an existing runbook
```console
$ txtx run <runbook-id>
```

69 changes: 69 additions & 0 deletions runbooks/deployments/deploy-l1/main.tx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Txtx supports deployments to multiple blockchains, so here we load the
// "evm" addon, which allows our Runbook to use evm-specific actions.
addon "evm" {
// Variables defined in our `addon "evm"` block are use by default on all evm actions

// Pull the chain id from our txtx.yml manifest
chain_id = input.l1_chain_id
// Pull the rpc url from our txtx.yml manifest
rpc_api_url = input.l1_rpc_api_url
}

// Declare a signer named "deployer", which will be used to sign transactions.
// This signer is created using the secret key provided as an input to the runbook.
signer "deployer" "evm::secret_key" {
// Pull the secret key from our txtx.yml manifest
secret_key = input.deployer_secret_key
// Instead of a secret key, you can also use a mnemonic to create a signer
// mnemonic = inputs.deployer_mnemonic
}

// Instead of copy/pasting keys/menomnics, you can also use the supervisor UI to sign all
// transactions in a browser wallet. To do this, use the web_wallet instead of secret_key:
// signer "deployer" "evm::web_wallet" {
// expected_address = input.deployer_address
// }


// Declare an action named "deploy_fname_resolver" that will deploy the FNAME resolver contract
// using the Create2 opcode. By default, the foundry create2 factory will be used, but you can
// specify a different factory contract if needed. See the docs for more info:
// https://docs.txtx.sh/addons/evm/actions#deploy-contract-create2
action "deploy_fname_resolver" "evm::deploy_contract_create2" {
description = "Deploy the FNAME resolver contract using the Immutable Create2 Factory"
// Using this function, txtx can automatically pull all of the needed artifacts to deploy this contract
contract = evm::get_contract_from_foundry_project("FnameResolver")
// The constructor args for the deployment. For the FNAME resolver, we need
// the server URL, the signer address, and the owner address.
constructor_args = [
// The server URL, pulled from the `txtx.yml` manifest
input.fname_resolver_server_url,
// The signer address, pulled from the `txtx.yml` manifest.
// Wrap in the `evm::address` function to properly encode the string as an address.
evm::address(input.fname_resolver_signer_address),
// The owner address, pulled from the `txtx.yml` manifest
// Wrap in the `evm::address` function to properly encode the string as an address,
evm::address(input.fname_resolver_owner_address)
]
// Pull the salt from the `txtx.yml` manifest
salt = input.fname_resolver_create2_salt

// Specify the alternative create2 factory address to use the Immutable Create2 Factory
create2_factory_address = input.immutable_create2_factory_address
// Specify the function to call on the create2 factory contract
create2_factory_function_name = "safeCreate2"
// Specify the ABI of the create2 factory contract
create2_factory_abi = variable.immutable_factory_abi

// The signer to use for this action
signer = signer.deployer
// Uncomment this to include an assertion that the deployed address matches the expected address.
// If there isn't a match, the deployment will not take place, and Runbook execution will not progress,
// preventing subsequent actions from running.
// expected_contract_address = inputs.FNAME_RESOLVER_ADDRESS
}

output "fname_resolver_address" {
description = "FNAME Resolver contract address"
value = action.deploy_fname_resolver.contract_address
}
2 changes: 2 additions & 0 deletions runbooks/deployments/deploy-l1/network.devnet.tx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// By naming a file `<filename>.devnet.tx`, this file will only be included in the runbook when the environment is set to `devnet`:
// `txtx run deploy-l1 --env devnet`
2 changes: 2 additions & 0 deletions runbooks/deployments/deploy-l1/network.mainnet.tx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// By naming a file `<filename>.mainnet.tx`, this file will only be included in the runbook when the environment is set to `mainnet`:
// `txtx run deploy-l1 --env mainnet`
6 changes: 6 additions & 0 deletions runbooks/deployments/deploy-l1/variables.tx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// The variable keyword is used to define a variable that can be used in other parts of the runbook.
// In this case, the variable immutable_factory_abi is defined with the value of the ABI of the Immutable Create2 Factory contract.
variable "immutable_factory_abi" {
description = "ABI of the Immutable Create2 Factory"
value = "[{\"constant\":true,\"inputs\":[{\"name\":\"deploymentAddress\",\"type\":\"address\"}],\"name\":\"hasBeenDeployed\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"salt\",\"type\":\"bytes32\"},{\"name\":\"initializationCode\",\"type\":\"bytes\"}],\"name\":\"safeCreate2\",\"outputs\":[{\"name\":\"deploymentAddress\",\"type\":\"address\"}],\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"salt\",\"type\":\"bytes32\"},{\"name\":\"initCode\",\"type\":\"bytes\"}],\"name\":\"findCreate2Address\",\"outputs\":[{\"name\":\"deploymentAddress\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"salt\",\"type\":\"bytes32\"},{\"name\":\"initCodeHash\",\"type\":\"bytes32\"}],\"name\":\"findCreate2AddressViaHash\",\"outputs\":[{\"name\":\"deploymentAddress\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]"
}
217 changes: 217 additions & 0 deletions runbooks/deployments/deploy-l2/main.tx
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
addon "evm" {
chain_id = input.l2_chain_id
rpc_api_url = input.l2_rpc_api_url
create2_factory_address = input.immutable_create2_factory_address
create2_factory_function_name = "safeCreate2"
create2_factory_abi = "[{\"constant\":true,\"inputs\":[{\"name\":\"deploymentAddress\",\"type\":\"address\"}],\"name\":\"hasBeenDeployed\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"salt\",\"type\":\"bytes32\"},{\"name\":\"initializationCode\",\"type\":\"bytes\"}],\"name\":\"safeCreate2\",\"outputs\":[{\"name\":\"deploymentAddress\",\"type\":\"address\"}],\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"salt\",\"type\":\"bytes32\"},{\"name\":\"initCode\",\"type\":\"bytes\"}],\"name\":\"findCreate2Address\",\"outputs\":[{\"name\":\"deploymentAddress\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"salt\",\"type\":\"bytes32\"},{\"name\":\"initCodeHash\",\"type\":\"bytes32\"}],\"name\":\"findCreate2AddressViaHash\",\"outputs\":[{\"name\":\"deploymentAddress\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]"
}

signer "deployer" "evm::secret_key" {
secret_key = input.deployer_secret_key
}

action "deploy_storage_registry" "evm::deploy_contract_create2" {
description = "Deploy the Storage Registry contract using the Immutable Create2 Factory"
contract = evm::get_contract_from_foundry_project("StorageRegistry")
constructor_args = [
evm::address(input.storage_rent_price_feed_address),
evm::address(input.storage_rent_uptime_feed_address),
variable.initial_usd_unit_price,
variable.initial_max_units,
evm::address(input.storage_rent_vault_address),
evm::address(input.deployer),
evm::address(input.storage_rent_admin_address),
evm::address(input.storage_rent_operator_address),
evm::address(input.storage_rent_treasurer_address)
]
salt = input.storage_rent_create2_salt
signer = signer.deployer
}

action "deploy_id_registry" "evm::deploy_contract_create2" {
description = "Deploy the IdRegistry contract using the Immutable Create2 Factory"
contract = evm::get_contract_from_foundry_project("IdRegistry")
constructor_args = [
evm::address(input.migrator_address),
evm::address(input.deployer)
]
salt = input.id_registry_create2_salt
signer = signer.deployer
}

action "deploy_id_gateway" "evm::deploy_contract_create2" {
description = "Deploy the IdGateway contract using the Immutable Create2 Factory"
contract = evm::get_contract_from_foundry_project("IdGateway")
constructor_args = [
action.deploy_id_registry.contract_address,
action.deploy_storage_registry.contract_address,
evm::address(input.deployer)
]
salt = input.id_gateway_create2_salt
signer = signer.deployer
}

action "deploy_key_registry" "evm::deploy_contract_create2" {
description = "Deploy the KeyRegistry contract using the Immutable Create2 Factory"
contract = evm::get_contract_from_foundry_project("KeyRegistry")
constructor_args = [
action.deploy_id_registry.contract_address,
evm::address(input.migrator_address),
evm::address(input.deployer),
variable.key_registry_max_keys_per_fid
]
salt = input.key_registry_create2_salt
signer = signer.deployer
}

action "deploy_key_gateway" "evm::deploy_contract_create2" {
description = "Deploy the KeyGateway contract using the Immutable Create2 Factory"
contract = evm::get_contract_from_foundry_project("KeyGateway")
constructor_args = [
action.deploy_key_registry.contract_address,
// this value was in the deployment script, but is not in the constructor
// action.deploy_storage_registry.contract_address,
evm::address(input.key_registry_owner_address)
]
salt = input.key_gateway_create2_salt
signer = signer.deployer
}

action "deploy_signed_key_request_validator" "evm::deploy_contract_create2" {
description = "Deploy the SignedKeyRequestValidator contract using the Immutable Create2 Factory"
contract = evm::get_contract_from_foundry_project("SignedKeyRequestValidator")
constructor_args = [
action.deploy_id_registry.contract_address,
evm::address(input.metadata_validator_owner_address)
]
salt = input.signed_key_request_validator_create2_salt
signer = signer.deployer
}

action "deploy_bundler" "evm::deploy_contract_create2" {
description = "Deploy the Bundler contract using the Immutable Create2 Factory"
contract = evm::get_contract_from_foundry_project("Bundler")
constructor_args = [
action.deploy_id_gateway.contract_address,
action.deploy_key_gateway.contract_address,
]
salt = input.bundler_create2_salt
signer = signer.deployer
}

action "deploy_recovery_proxy" "evm::deploy_contract_create2" {
description = "Deploy the RecoveryProxy contract using the Immutable Create2 Factory"
contract = evm::get_contract_from_foundry_project("RecoveryProxy")
constructor_args = [
action.deploy_id_registry.contract_address,
evm::address(input.recovery_proxy_owner_address)
]
salt = input.recovery_proxy_create2_salt
signer = signer.deployer
}

// Post-deployment setup
action "set_id_gateway" "evm::call_contract" {
description = "Set the IdGateway contract address in the IdRegistry contract"
contract_address = action.deploy_id_registry.contract_address
contract_abi = action.deploy_id_registry.abi
function_name = "setIdGateway"
function_args = [
action.deploy_id_gateway.contract_address
]
signer = signer.deployer
}

action "transfer_id_registry_ownership" "evm::call_contract" {
description = "Transfer ownership of the IdRegistry contract to configured initial Id Registry Owner"
contract_address = action.deploy_id_registry.contract_address
contract_abi = action.deploy_id_registry.abi
function_name = "transferOwnership"
function_args = [
evm::address(input.id_registry_owner_address)
]
signer = signer.deployer
}

action "transfer_id_gateway_ownership" "evm::call_contract" {
description = "Transfer ownership of the IdGateway contract to configured initial Id Registry Owner"
contract_address = action.deploy_id_gateway.contract_address
contract_abi = action.deploy_id_gateway.abi
function_name = "transferOwnership"
function_args = [
evm::address(input.id_registry_owner_address)
]
signer = signer.deployer
}

action "key_registry_set_validator" "evm::call_contract" {
description = "Set the Validator for the KeyRegistry contract"
contract_address = action.deploy_key_registry.contract_address
contract_abi = action.deploy_key_registry.abi
function_name = "setValidator"
function_args = [
evm::uint32(1),
evm::uint8(1),
action.deploy_signed_key_request_validator.contract_address
]
signer = signer.deployer
}

action "set_key_registry_key_gateway" "evm::call_contract" {
description = "Set the KeyGateway contract address in the KeyRegistry contract"
contract_address = action.deploy_key_registry.contract_address
contract_abi = action.deploy_key_registry.abi
function_name = "setKeyGateway"
function_args = [
action.deploy_key_gateway.contract_address
]
signer = signer.deployer
}

action "transfer_key_registry_ownership" "evm::call_contract" {
description = "Transfer ownership of the KeyRegistry contract to configured initial Key Registry Owner"
contract_address = action.deploy_key_registry.contract_address
contract_abi = action.deploy_key_registry.abi
function_name = "transferOwnership"
function_args = [
evm::address(input.key_registry_owner_address)
]
signer = signer.deployer
}

action "grant_operator_role" "evm::call_contract" {
description = "Grant the OPERATOR_ROLE to the Bundler contract"
contract_address = action.deploy_storage_registry.contract_address
contract_abi = action.deploy_storage_registry.abi
function_name = "grantRole"
function_args = [
evm::bytes32(std::keccak256("OPERATOR_ROLE")),
action.deploy_bundler.contract_address
]
signer = signer.deployer
}

action "grant_admin_role" "evm::call_contract" {
description = "Grant the ADMIN_ROLE to the Bundler contract"
contract_address = action.deploy_storage_registry.contract_address
contract_abi = action.deploy_storage_registry.abi
function_name = "grantRole"
function_args = [
evm::bytes32("0x0000000000000000000000000000000000000000000000000000000000000000"),
evm::address(input.storage_rent_role_admin_address)
]
signer = signer.deployer
}

// I couldn't get this one transaction to succeed, it keeps reverting. Will need to investigate further.
// action "renounce_role" "evm::call_contract" {
// description = "Renounce the OPERATOR_ROLE from the deployer"
// contract_address = action.deploy_storage_registry.contract_address
// contract_abi = action.deploy_storage_registry.abi
// function_name = "renounceRole"
// function_args = [
// evm::bytes32("0x0000000000000000000000000000000000000000000000000000000000000000"),
// evm::address(input.deployer)
// ]
// signer = signer.deployer
// }
Loading