This repository provides tooling for deploying a Gasolina-based DVN on Cloudflare using Cloudflare Containers.
- Deploys the Gasolina Docker image as a Cloudflare Container
- Routes public traffic via a Cloudflare Worker
- Stores provider configuration in an R2 bucket using the S3-compatible API
- Manages secrets via
wrangler secret put
Sign up at cloudflare.com and request access to Cloudflare Containers. Your account ID is visible in the Cloudflare dashboard URL or via:
wrangler whoamiIn this directory, run:
pnpm ipnpm add -g wrangler
wrangler loginGo to Cloudflare Dashboard → R2 → Manage R2 API Tokens and create a token with Object Read & Write permissions. Note the Access Key ID and Secret Access Key — you will use them when uploading provider configs and as container secrets.
You need to decide how the DVN will sign payloads. There are two options:
| Strategy | Description | When to use |
|---|---|---|
| Mnemonic | You supply your own mnemonic(s), stored as Wrangler secrets. | You already manage your own keys. |
| KMS | AWS KMS provides HSM-backed asymmetric keys accessed via AWS credentials. | You want hardware-backed key security. |
Mnemonic setup: Prepare the JSON-stringified wallet definitions you will later set as the LAYERZERO_WALLETS secret. See Secrets below for the expected format.
KMS setup: Create and note the KMS key IDs in AWS. Grant an IAM user kms:GetPublicKey, kms:Sign, and kms:Verify permissions on those keys. You will pass the IAM credentials as Wrangler secrets.
config/index.ts — edit the CONFIG object:
accountId— your Cloudflare account IDenvironment—mainnetortestnetavailableChainNames— comma-separated chain names, e.g.ethereum,bsc,avalanchesignerType—MNEMONICorKMSr2BucketName— the R2 bucket you will create in the next stepappVersion— the Gasolina image tag to deploy
wrangler.toml — review the [env.mainnet] / [env.testnet] sections:
LAYERZERO_AVAILABLE_CHAIN_NAMES— must match the keys inproviders.jsonSIGNER_TYPE—MNEMONICorKMSCONFIG_BUCKET_NAME— must match your R2 bucket nameimagein[[containers]]— pin to a specific Gasolina version, e.g.gasolina:1.2.3
config/providers/<environment>/providers.json — add RPC provider entries for every chain listed in availableChainNames:
{
"ethereum": {
"uris": ["https://eth.llamarpc.com"],
"quorum": 1
}
}config/walletConfig/mainnet.json (mnemonic only) — define one entry per signer:
address— the signer wallet addresssecretName— an arbitrary label used to identify this signer
pnpm r2:create:mainnet
# or for testnet
pnpm r2:create:testnetexport R2_ACCOUNT_ID=<your-cloudflare-account-id>
export R2_ACCESS_KEY_ID=<r2-api-token-access-key-id>
export R2_SECRET_ACCESS_KEY=<r2-api-token-secret>
pnpm upload-providers:mainnet
# or for testnet
pnpm upload-providers:testnetSecrets are injected as environment variables into the container at runtime. None of them appear in wrangler.toml.
Required for all signer types — R2 credentials so the Gasolina container can read providers.json:
wrangler secret put AWS_ACCESS_KEY_ID --env mainnet # R2 API token access key ID
wrangler secret put AWS_SECRET_ACCESS_KEY --env mainnet # R2 API token secret
wrangler secret put AWS_S3_ENDPOINT --env mainnet # https://<account-id>.r2.cloudflarestorage.com
wrangler secret put CONFIG_BUCKET_NAME --env mainnet # R2 bucket nameMnemonic signer (SIGNER_TYPE=MNEMONIC):
wrangler secret put LAYERZERO_WALLETS --env mainnetThe expected value is a JSON-stringified array of wallet definitions:
[
{
"name": "Oracle1WalletName",
"byChainType": {
"EVM": {
"address": "0x...",
"mnemonic": "word1 word2 word3 ..."
}
}
}
]KMS signer (SIGNER_TYPE=KMS):
wrangler secret put KMS_CLOUD_TYPE --env mainnet # e.g. "AWS"
wrangler secret put LAYERZERO_KMS_IDS --env mainnet # comma-separated KMS key IDs
wrangler secret put KMS_AWS_ACCESS_KEY_ID --env mainnet
wrangler secret put KMS_AWS_SECRET_ACCESS_KEY --env mainnet
wrangler secret put KMS_AWS_REGION --env mainnetpnpm deploy:mainnet
# or for testnet
pnpm deploy:testnetAfter deployment completes, Wrangler prints the Worker URL. Send this URL to LayerZero Labs.
See Testing below.
curl https://<worker-url>/
# Expected: HEALTHYcurl https://<worker-url>/available-chainscurl https://<worker-url>/provider-healthIf any provider returns false, check the Worker logs (pnpm tail:mainnet) for details. Common causes: misconfigured URLs in providers.json, or the container lacking egress permissions. It is important to note that if using multiple providers and any of them fail, the result will be false.
curl https://<worker-url>/signer-info?chainName=ethereumThis should return address information for your signers. If it fails, verify your signer-related secrets and that the signer type in wrangler.toml matches the secrets you set.
From the project root directory:
ts-node scripts/testDeployment -u <worker-url> -e <environment>A successful response:
--- [200] Successful request ---
Response: {
signatures: [
{
signature: '<signature>',
address: '<address>'
}
]
}
You can enhance message verification by adding custom rules. Set up an API that Gasolina will call whenever a message is received. The API receives the full message context (including on-chain data) and returns a boolean indicating whether the message should be signed.
Setup: In wrangler.toml, set EXTRA_CONTEXT_REQUEST_URL in the relevant [env.<environment>.vars] section. Alternatively, set it as a secret via:
wrangler secret put EXTRA_CONTEXT_REQUEST_URL --env mainnetAPI input schema
{
sentEvent: { // PacketSent event, emitted from the Endpoint contract
lzMessageId: {
pathwayId: {
srcEid: number // Source chain eid
dstEid: number
sender: string // Sender oApp address on source chain
receiver: string // Receiver oApp address on destination chain
srcChainName: string
dstChainName: string
}
nonce: number
ulnSendVersion: UlnVersion
}
guid: string // onchain guid
message: string
options: {
lzReceive?: { gas: string; value: string }
nativeDrop?: { amount: string; receiver: string }[]
compose?: { index: number; gas: string; value: string }[]
ordered?: boolean
}
payload?: string
sendLibrary?: string
onChainEvent: {
chainName: string
txHash: string
blockHash: string
blockNumber: number
}
}
from: string // Address that initiated the transaction
}API output: Return a boolean — true to allow signing, false to reject.
Edit config/providers/<environment>/providers.json and re-run the upload script:
pnpm upload-providers:mainnetThe container picks up the new configuration on its next restart.
The following variables are set in wrangler.toml or via wrangler secret put. All are forwarded to the Gasolina container at runtime.
| Variable | Source | Required | Description |
|---|---|---|---|
LAYERZERO_ENVIRONMENT |
vars |
Yes | mainnet or testnet |
LAYERZERO_AVAILABLE_CHAIN_NAMES |
vars |
Yes | Comma-separated chain names, e.g. ethereum,arbitrum |
SERVER_PORT |
vars |
Yes | Port exposed by the container (8081) |
SIGNER_TYPE |
vars |
Yes | KMS or MNEMONIC |
LAYERZERO_SUPPORTED_ULN_VERSIONS |
vars |
Yes | ["V2"] |
PROVIDER_CONFIG_TYPE |
vars |
Yes | S3 (R2 uses the S3-compatible API) |
CONFIG_BUCKET_NAME |
secret |
Yes | R2 bucket name containing providers.json |
AWS_ACCESS_KEY_ID |
secret |
Yes | R2 API token access key ID |
AWS_SECRET_ACCESS_KEY |
secret |
Yes | R2 API token secret |
AWS_S3_ENDPOINT |
secret |
Yes | https://<account-id>.r2.cloudflarestorage.com |
LAYERZERO_WALLETS |
secret |
If mnemonic | JSON-stringified wallet definitions with mnemonics |
KMS_CLOUD_TYPE |
secret |
If KMS | AWS or GCP |
LAYERZERO_KMS_IDS |
secret |
If KMS | Comma-separated KMS key IDs |
KMS_AWS_ACCESS_KEY_ID |
secret |
If KMS | AWS IAM access key with KMS permissions |
KMS_AWS_SECRET_ACCESS_KEY |
secret |
If KMS | AWS IAM secret key |
KMS_AWS_REGION |
secret |
If KMS | AWS region of the KMS keys, e.g. us-east-1 |
EXTRA_CONTEXT_REQUEST_URL |
secret |
Optional | URL of the custom verification API |
LAYERZERO_METRIC_NAMESPACE |
vars |
Optional | Metric namespace, e.g. layerzero-gasolina |
Cloudflare Containers is in beta. Verify your account has been granted access and that wrangler is up to date (pnpm add -g wrangler@latest).
Check the Worker logs:
pnpm tail:mainnetCommon causes:
- Misconfigured RPC URLs in
providers.json - The container cannot reach external provider URLs (egress blocked)
providers.jsonwas not uploaded to the R2 bucket, or was uploaded to the wrong bucket
Verify that:
SIGNER_TYPEinwrangler.tomlmatches the secrets you configured- All required secrets for the chosen signer type have been set via
wrangler secret put - For KMS, the IAM credentials have
kms:GetPublicKey,kms:Sign, andkms:Verifypermissions
The container may be failing its health check on GET /. Check that all required environment variables and secrets are set:
wrangler secret list --env mainnetEnsure CONFIG_BUCKET_NAME, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_S3_ENDPOINT are all present.