- Local Setup
- Switching between environments
- Debugging Local Problems
- Viewing Logs
- npm tasks and dev scripts
- Updating the Cloud Function
- Deploying a new Oracle Relayer
- Aegis Export for Monitoring Relayers
The oracle relayer infrastructure is organized into 2 GCP projects — one per environment:
| Environment | GCP Project | Cloud Functions | Pub/Sub Topics |
|---|---|---|---|
| testnet | oracle-relayer-testnet |
relay-celo-sepolia, relay-monad-testnet, relay-polygon-testnet |
relay-testnet-celo-sepolia, relay-testnet-monad-testnet, relay-testnet-polygon-testnet |
| mainnet | oracle-relayer-mainnet |
relay-celo, relay-monad |
relay-mainnet-celo, relay-mainnet-monad |
Each environment hosts multiple cloud functions (one per chain), sharing the same source code but differentiated by the CHAIN environment variable. Terraform workspaces map to environments (testnet / mainnet), and for_each iterates over chains within each workspace.
-
Install the
gcloudCLI# For macOS brew install google-cloud-sdk # For other systems, see https://cloud.google.com/sdk/docs/install
-
Install
trunk(one linter to rule them all)# For macOS brew install trunk-io # For other systems, see https://docs.trunk.io/check/usage
Optionally, you can also install the Trunk VS Code Extension
-
Install
jq(used in shell scripts)# For macOS brew install jq # For other systems, see https://jqlang.github.io/jq/
-
Install
terraform# For macOS brew tap hashicorp/tap brew install hashicorp/tap/terraform # For other systems, see https://developer.hashicorp.com/terraform/install
-
Run terraform setup script
# Checks required permissions, provisions terraform providers, modules, and workspaces ./bin/set-up-terraform.sh -
Set your local
gcloudproject:# Points your local gcloud config to the right project and caches values frequently used in shell scripts ./bin/get-project-vars.sh -
Create a
infra/terraform.tfvarsfile. This is like.envfor Terraform:touch infra/terraform.tfvars # This file is `.gitignore`d to avoid accidentally leaking sensitive data -
Add the following values to your
terraform.tfvars:# Get it via `gcloud organizations list` org_id = "<our-org-id>" # Get it via `gcloud billing accounts list` (pick the GmbH account) billing_account = "<our-billing-account-id>" # Get it via `gcloud secrets versions access latest --secret relayer-mnemonic` # Note that the mnemonic is shared across all environments. # To fetch secrets, you'll need the `Secret Manager Secret Accessor` IAM role assigned to your Google Cloud Account relayer_mnemonic = "<relayer-mnemonic>" # Discord webhook URL for testnet alerts discord_webhook_url_testnet = "<testnet-webhook-url>" # Discord webhook URL for mainnet alerts discord_webhook_url_mainnet = "<mainnet-webhook-url>" # Get it from our VictorOps by going to `Integrations` > `Stackdriver` and copying the URL. The routing key can be found under the settings tab victorops_webhook_url = "<victorops-webhook-url>/<victorops-routing-key>"
-
Verify that everything works
# Switch your local gcloud context to the testnet environment npm run testnet # See if you can fetch logs for a specific chain npm run logs:celo-sepolia npm run logs:monad-testnet # Switch your local gcloud context to the mainnet environment npm run mainnet # See if you can fetch mainnet logs npm run logs:celo npm run logs:monad # Try running the function locally npm install npm run dev # Fire a mock request against your local function npm test # Optionally accepts a rate feed and relayer contract arg npm test "GBP/USD" "0x215d3ba962597DeFb38Da439ED4dB8E8a63e409a" # See if you can manually trigger a relay on celo-sepolia for a specific rate feed npm run test:celo-sepolia "EUR/USD" # See if you can manually trigger a relay on monad-testnet npm run test:monad-testnet "AUSD/USD"
- There are 2 GCP projects: one for testnet chains and one for mainnet chains
- You can quickly switch between environments via
npm run testnetornpm run mainnet - Each environment hosts multiple chains (e.g., testnet has celo-sepolia and monad-testnet)
Most dev scripts under ./bin are using gcloud commands.
These gcloud commands per default always run against the currently active project.
Alternatively, you'd always need to explicitly pass a --project-id flag to every gcloud command which can get annoying quickly.
For most local terraform or gcloud problems, your first steps should always be to:
- Clear your local shell script cache via
npm run cache:clear - Re-run the Terraform setup script via
./bin/set-up-terraform.sh
The Oracle Relayer uses structured logging with Google Cloud Logging. Logs include severity levels, timestamps, rate feed labels, and trace IDs for correlating function invocations.
All log commands require a chain argument:
npm run logs:celo-sepolia # View recent celo-sepolia logs
npm run logs:monad-testnet # View recent monad-testnet logs
npm run logs:celo # View recent celo mainnet logs
npm run logs:monad # View recent monad mainnet logsView recent logs (last 50):
npm run logs:celo-sepolia # All logs for celo-sepolia
./bin/get-function-logs.sh celo CELO/USD # Filter by rate feedStream logs in real-time:
npm run logs:tail:celo-sepolia # All logs
./bin/tail-function-logs.sh celo-sepolia CELO/USD # Filter by rate feedPress Ctrl+C to stop tailing.
Generate Log Explorer URLs:
npm run logs:url:celo-sepolia # All logs
./bin/get-function-logs-url.sh celo-sepolia CELO/USD # Filter by rate feedThis generates URLs for:
- Logs Explorer (recommended): Full-featured viewer with filtering and grouping
- Cloud Run Logs: For debugging function startup issues (excludes function execution logs)
# View recent logs
gcloud logging read 'resource.labels.service_name="relay-celo-sepolia"' \
--project oracle-relayer-testnet-XXXX --limit 50
# Tail logs in real-time
gcloud beta logging tail 'resource.labels.service_name="relay-celo-sepolia"' \
--project oracle-relayer-testnet-XXXX
# Filter by rate feed
gcloud beta logging tail 'resource.labels.service_name="relay-celo-sepolia" AND labels.rateFeed="CELO/USD"' \
--project oracle-relayer-testnet-XXXX- Use the logger instance (not
console.log) for structured logging - Use appropriate severity:
logger.info()for normal operations,logger.warn()for non-blocking issues,logger.error()for failures - Filter by rate feed when debugging specific oracles to reduce noise
- Local Function Development
dev: Starts a local server for the cloud function code (with hot-reloading vianodemon)start: Starts a local server for the cloud function code (without hot-reloading)test: Triggers a local cloud function server with a mocked PubSub event
- Switching Between Environments
testnet: Switches the terraform workspace and your localgcloudproject to testnetmainnet: Switches the terraform workspace and your localgcloudproject to mainnet
- Deploying and Destroying
deploy:testnet: Deploys all testnet chains (viaterraform apply)deploy:mainnet: Deploys all mainnet chains (viaterraform apply)deploy:function:celo-sepolia: Deploys only the cloud function for celo-sepolia (viagcloud functions deploy)deploy:function:monad-testnet: Deploys only the cloud function for monad-testnet (viagcloud functions deploy)deploy:function:polygon-testnet: Deploys only the cloud function for polygon-testnet (viagcloud functions deploy)deploy:function:celo: Deploys only the cloud function for celo (viagcloud functions deploy)deploy:function:monad: Deploys only the cloud function for monad (viagcloud functions deploy)plan:testnet: Shorthand for runningterraform planfor the testnet environmentplan:mainnet: Shorthand for runningterraform planfor the mainnet environmentdestroy:testnet: Destroys entire testnet project (viaterraform destroy)destroy:mainnet: Destroys entire mainnet project (viaterraform destroy)
- View Logs (see Viewing Logs section)
logs:celo-sepolia: View recent celo-sepolia logs (last 50 entries)logs:monad-testnet: View recent monad-testnet logslogs:celo: View recent celo mainnet logslogs:monad: View recent monad mainnet logslogs:tail:<chain>: Stream logs in real-time for a specific chainlogs:url:<chain>: Generate log explorer URLs for a specific chain
- Manually Triggering a Relay
test:celo-sepolia: Manually trigger a relay on celo-sepolia, e.g.npm run test:celo-sepolia PHP/USDtest:monad-testnet: Manually trigger a relay on monad-testnet, e.g.npm run test:monad-testnet AUSD/USDtest:celo: Manually trigger a relay on celo, e.g.npm run test:celo CELO/ETHtest:monad: Manually trigger a relay on monad, e.g.npm run test:monad AUSD/USD
- General Helper & DX Scripts
cache:clear: Clears local shell script cache and refresh it with current valuesgenerate:env: Auto-generates/updates a local.envrequired by a locally running cloud function servertodo: Lists allTODOandFIXMEcommentsget:relayer:signer: Prints the signer address that calls the relay function on the given rate feed's relayer contract.refill:<chain>: Refills all relayer signer addresses with a low balance on the given network (e.g.,refill:celo,refill:celo-sepolia)
- Shell Scripts
set-up-terraform.sh: Checks required IAM permissions, provisions terraform providers, modules, and workspacescheck-gcloud-login.sh: Checks for Google Cloud login and application-default credentials.
The relayer signer addresses run out of native tokens from time to time and need to be refilled. This can be done by adding a REFILLER_PRIVATE_KEY to the .env file (e.g. the deployer private key) and running the appropriate refill script, which will transfer tokens to all signer addresses running low on balance.
npm run refill:celo
npm run refill:celo-sepolia
npm run refill:monad
npm run refill:monad-testnetYou have two options to deploy the Cloud Function code, terraform or gcloud cli. Both are perfectly fine to use.
- Via
terraformby runningnpm run deploy:[testnet|mainnet]- How? The npm task will:
- Call
terraform applywith the correct workspace which re-deploys all functions in the environment with the latest code from your local machine
- Call
- Pros
- Keeps the terraform state clean
- Same command for all changes, regardless of infra or cloud function code
- Deploys all chains in the environment at once
- Cons
- Less familiar way of deploying cloud functions (if you're used to
gcloud functions deploy) - Less log output
- Slightly slower because
terraform applywill always fetch the current state from the cloud storage bucket before deploying
- Less familiar way of deploying cloud functions (if you're used to
- How? The npm task will:
- Via
gcloudby runningnpm run deploy:function:[celo-sepolia|monad-testnet|polygon-testnet|celo|monad]- How? The npm task will:
- Look up the service account used by the cloud function
- Call
gcloud functions deploywith the correct parameters
- Pros
- Familiar way of deploying cloud functions
- More log output making deployment failures slightly faster to debug
- Slightly faster because we're skipping the terraform state lookup
- Deploys a single chain's function without touching others
- Cons
- Will lead to inconsistent terraform state (because terraform is tracking the function source code and its version)
- Different commands to remember when updating infra components vs cloud function source code
- Will only work for updating a pre-existing cloud function's code, will fail for a first-time deploy
- How? The npm task will:
- Deploy the new relayer contracts via the relayer factory. Exemplary deployment scripts can be found in the MU07 Deployment Scripts
- Ensure the new relayers have been whitelisted in SortedOracles on the relevant chain (otherwise relay() transactions will fail)
- Add the addresses of the deployed relayers to relayer_addresses.json under the appropriate chain key (e.g.,
celo-sepolia,monad-testnet,polygon-testnet,celo,monad) - Run
npm run deploy:testnetand/ornpm run deploy:mainnetto create GCP cloud scheduler jobs for the new relayers - Add the new relayers to aegis for monitoring
To add a new chain to an existing environment:
- Add the chain to
local.environment_chainsininfra/main.tf - Add its relayer addresses to
infra/relayer_addresses.jsonunder the chain name - Run
npm run deploy:[testnet|mainnet]— Terraform will create the new function, pub/sub topic, scheduler jobs, and monitoring
- Run
npm run aegis:exportto print out an aegis config template in your local CLI - Copy the relevant sections for the relayers you want to add to aegis
- Paste them into aegis' config.yaml
- Run aegis in dev mode via
npm run dev, check that there are no errors in the log outputs - Submit a PR with your changes
- After successful code review, deploy your changes via
npm run deployin aegis