Documentation Index
Fetch the complete documentation index at: https://docs.layerzero.network/llms.txt
Use this file to discover all available pages before exploring further.
This guide provides step-by-step instructions for deploying your own Gasolina instance on AWS or Google Cloud Platform. Gasolina is a REST API service that verifies LayerZero messages and produces signatures for DVN contracts.
Prerequisites
Before deploying Gasolina, ensure you have:
| Requirement | Details |
|---|
| Cloud Provider Account | AWS account with CDK permissions, or GCP account with billing enabled |
| Node.js | Version 18 or higher |
| Package Manager | npm, yarn, or pnpm |
| Cloud CLI | AWS CLI + CDK CLI, or gcloud CLI + Terraform |
| RPC Providers | Reliable endpoints for each supported chain (2+ per chain recommended) |
AWS Implementation
Step 1: Clone and Setup
# Clone the repository
git clone https://github.com/LayerZero-Labs/gasolina-aws.git
cd gasolina-aws
# Install dependencies
pnpm install
# or: yarn install
# Configure AWS CLI with your credentials
aws configure
# Verify authentication
aws sts get-caller-identity
Step 3: Choose Signer Type
You have two options for managing signing keys:
AWS KMS (Recommended)
Mnemonic-based
AWS KMS provides HSM-backed signing keys that are automatically created and managed:
- Set
signerType: 'KMS' in your configuration
- Specify
kmsNumOfSigners for the number of keys to create
- Keys are automatically created during CDK deployment
Benefits:
- Hardware security module protection
- Automatic key rotation support
- No manual secret management
For mnemonic-based signers, create secrets in AWS Secrets Manager:# Create a secret for each signer
aws secretsmanager create-secret \
--name "gasolina-signer-1" \
--secret-string '{
"LAYERZERO_WALLET_MNEMONIC": "your twelve word mnemonic phrase here",
"LAYERZERO_WALLET_PATH": "m/44'"'"'/60'"'"'/0'"'"'/0/0"
}'
Store mnemonics securely and never commit them to version control. Use AWS Secrets Manager or similar secure storage.
Edit cdk/gasolina/config/index.ts:
export const CONFIG = {
'123456789012': {
// Your AWS account number
projectName: 'my-gasolina-mainnet', // Must be globally unique
environment: 'mainnet', // or "testnet"
availableChainNames: 'ethereum,bsc,avalanche,polygon,arbitrum,optimism',
signerType: 'KMS', // or "MNEMONIC"
kmsNumOfSigners: 3, // Only if using KMS
// Optional: Extra context verification
extraContextGasolinaUrl: 'https://your-verification-api.com/verify',
},
};
| Config Option | Description |
|---|
projectName | Unique project identifier (used for S3 bucket naming) |
environment | mainnet or testnet |
availableChainNames | Comma-separated list of supported chains |
signerType | KMS for HSM-backed or MNEMONIC for secret-based |
kmsNumOfSigners | Number of KMS signing keys to create |
extraContextGasolinaUrl | Optional custom verification endpoint |
Edit cdk/gasolina/config/providers/mainnet/providers.json:
{
"1": {
"chainName": "ethereum",
"uris": [
"https://eth-mainnet.g.alchemy.com/v2/YOUR-API-KEY",
"https://mainnet.infura.io/v3/YOUR-PROJECT-ID"
]
},
"56": {
"chainName": "bsc",
"uris": ["https://bsc-dataseed1.binance.org", "https://bsc-dataseed2.ninicoin.io"]
}
}
- Use at least 2 RPC providers per chain for redundancy
- Prioritize reliable providers (Alchemy, Infura, QuickNode)
- Order URIs by preference (first URI is primary)
If using mnemonics, edit cdk/gasolina/config/walletConfig/mainnet.json:
{
"definitions": [
{
"address": "0x1234567890123456789012345678901234567890",
"secretName": "gasolina-signer-1"
},
{
"address": "0x0987654321098765432109876543210987654321",
"secretName": "gasolina-signer-2"
}
]
}
Step 7: Bootstrap CDK (First Time Only)
cd cdk/gasolina
cdk bootstrap
Step 8: Deploy Infrastructure
# Review the deployment plan
cdk diff
# Deploy the infrastructure
cdk deploy
After successful deployment, you’ll see:
Outputs:
Oracle.ApiGatewayUrl = https://xxxxxxxxxx.execute-api.region.amazonaws.com
Step 9: Test Deployment
# Test the health endpoint
curl https://xxxxxxxxxx.execute-api.region.amazonaws.com
# Test signer info
curl "https://xxxxxxxxxx.execute-api.region.amazonaws.com/signer-info?chainName=ethereum"
# Run comprehensive test
cd ../../ # Back to repository root
ts-node scripts/testDeployment.ts -u https://xxxxxxxxxx.execute-api.region.amazonaws.com -e mainnet
A successful response looks like:
--- [200] Successful request ---
Response: {
signatures: [
{ signature: '<signature>', address: '<address>' },
{ signature: '<signature>', address: '<address>' }
]
}
Step 1: Clone and Setup
# Clone the repository
git clone https://github.com/LayerZero-Labs/gasolina-gcp.git
cd gasolina-gcp
# Install dependencies
yarn install
# Set your project
gcloud config set project YOUR-PROJECT-ID
# Authenticate
gcloud auth application-default login
# Enable required APIs
gcloud services enable cloudkms.googleapis.com
gcloud services enable run.googleapis.com
gcloud services enable secretmanager.googleapis.com
# Create a GCS bucket for Terraform state
gsutil mb -p YOUR-PROJECT-ID -l US-EAST1 gs://your-project-gasolina-tfstate
Edit terraform/lz-mainnet-verifier.backend.conf:
bucket = "your-project-gasolina-tfstate"
prefix = "mainnet"
Edit terraform/lz-mainnet-verifier.tfvars:
/* Project variables */
project = "your-gcp-project"
project_id = "123456789012"
region = "us-east1"
zone = "us-east1-c"
/* General variables */
env = "mainnet"
/* KMS-HSM variables */
num_signers = 3
/* App variables */
app_name = "gasolina-api"
available_chain_names = "ethereum,bsc,avalanche,polygon,arbitrum,optimism"
Edit terraform/providers-mainnet.json:
{
"1": {
"chainName": "ethereum",
"uris": [
"https://eth-mainnet.g.alchemy.com/v2/YOUR-API-KEY",
"https://mainnet.infura.io/v3/YOUR-PROJECT-ID"
]
},
"56": {
"chainName": "bsc",
"uris": ["https://bsc-dataseed1.binance.org", "https://bsc-dataseed2.ninicoin.io"]
}
}
cd terraform
# Initialize Terraform
terraform init -backend-config=lz-mainnet-verifier.backend.conf -reconfigure
# Review the deployment plan
terraform plan --var-file=lz-mainnet-verifier.tfvars
# Apply the deployment
terraform apply --var-file=lz-mainnet-verifier.tfvars
Step 8: Test Deployment
# Get the Cloud Run URL
GASOLINA_URL=$(gcloud run services describe gasolina-api \
--region=us-east1 \
--format='value(status.url)')
# Test health endpoint
curl $GASOLINA_URL
# Test signer info
curl "$GASOLINA_URL/signer-info?chainName=ethereum"
# Run comprehensive test
cd .. # Back to repository root
ts-node scripts/testDeployment.ts -u $GASOLINA_URL -e mainnet
Integration with LayerZero
Once your Gasolina instance is deployed and tested:
Step 1: Share Gasolina URL
Provide your Gasolina API URL to LayerZero Labs:
https://your-gasolina-instance.com
Step 2: DVN Contract Deployment
LayerZero will:
- Query your
/signer-info endpoint to retrieve signer addresses
- Deploy DVN contracts on all supported chains with:
- Your signer addresses registered
- Agreed-upon quorum threshold
- Essence wallet as initial
ADMIN_ROLE holder
- Provide you with the DVN contract addresses for each chain
Step 3: OApp Configuration
OApps configure your DVN using the contract addresses:
// Example UlnConfig for OApp
UlnConfig({
confirmations: 15,
requiredDVNCount: 2,
optionalDVNCount: 0,
optionalDVNThreshold: 0,
requiredDVNs: [
0xYourDVNContractAddress, // Your Gasolina DVN
0xOtherDVNAddress // Another DVN
],
optionalDVNs: []
})
Advanced Configuration
Add custom verification logic by implementing an API endpoint:
// Your custom verification API
app.post('/verify', async (req, res) => {
const {sentEvent, from} = req.body;
// Implement your verification rules
const isValid = await verifyCustomRules(sentEvent, from);
res.json({valid: isValid});
});
API Input Schema:
{
sentEvent: {
lzMessageId: {
pathwayId: {
srcEid: number, // Source endpoint ID
dstEid: number, // Destination endpoint ID
sender: string, // Sender OApp address
receiver: string, // Receiver OApp address
},
nonce: number,
},
guid: string,
message: string,
options: { /* ... */ },
onChainEvent: {
chainName: string,
txHash: string,
blockNumber: number,
}
},
from: string // Transaction initiator
}
Configure the URL in your infrastructure config:
extraContextGasolinaUrl: 'https://your-api.com/verify';
Multi-Signer Setup
For enhanced security and availability, deploy multiple Gasolina instances with different signers:
| Instance | Endpoint | Signers |
|---|
| Gasolina 1 | https://gasolina1.example.com | Signers A, B |
| Gasolina 2 | https://gasolina2.example.com | Signers C, D |
| Gasolina 3 | https://gasolina3.example.com | Signers E, F |
Essence requests signatures from all instances in parallel, then combines them for submission.
Configure quorum to require signatures from multiple instances (e.g., 4 of 6 signers across 3 instances).
Managing Configuration Changes
As a Gasolina operator, you maintain full control over the DVN through the signer quorum.
Change Quorum Threshold
ts-node scripts/configChangePayloads/createSetQuorumSignatures.ts \
-e mainnet \
-c ethereum,bsc,avalanche \
--oldQuorum 2 \
--newQuorum 3
Add a Signer
ts-node scripts/configChangePayloads/createAddOrRemoveSignerSignatures.ts \
-e mainnet \
-c ethereum,bsc,avalanche \
-q 2 \
--signerAddress 0xNewSignerAddress \
--shouldRevoke 0
Remove a Signer
ts-node scripts/configChangePayloads/createAddOrRemoveSignerSignatures.ts \
-e mainnet \
-c ethereum,bsc,avalanche \
-q 2 \
--signerAddress 0xOldSignerAddress \
--shouldRevoke 1
Emergency Admin Takeover
The DVN contract includes a quorumChangeAdmin function that allows the signer quorum to reassign the admin role without requiring the current admin’s permission. This ensures signers maintain ultimate control over the DVN even if the admin role has been delegated to a service like Essence.
ExecuteParam Structure
The function accepts a single ExecuteParam struct:
struct ExecuteParam {
uint32 vid; // DVN instance identifier (endpoint v1 eid or v2 eid % 30000)
address target; // DVN contract address (must equal address(this))
bytes callData; // abi.encode(newAdminAddress)
uint256 expiration; // Unix timestamp (must be in the future)
bytes signatures; // Concatenated quorum signatures, sorted by signer address
}
Step-by-Step Implementation
Step 1: Prepare the Parameters
const ethers = require('ethers');
// Configuration
const dvnAddress = '0xYourDVNContractAddress';
const newAdminAddress = '0xYourControlledAddress';
const vid = 101; // Your DVN's vid (check contract or use endpoint v1 eid)
const expiration = Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60; // 1 week from now
// Encode the new admin address as callData
const callData = ethers.utils.defaultAbiCoder.encode(['address'], [newAdminAddress]);
Step 2: Generate the Hash for Signing
// Hash follows the DVN contract's hashCallData format
const hash = ethers.utils.keccak256(
ethers.utils.solidityPack(
['uint32', 'address', 'uint256', 'bytes'],
[vid, dvnAddress, expiration, callData],
),
);
Step 3: Collect Quorum Signatures
Each signer must sign the hash. Signatures must be sorted by signer address (ascending) before concatenation:
// Each signer signs the hash
const signers = [signer1, signer2, signer3]; // Your Wallet instances
const signatures = [];
for (const signer of signers) {
const sig = await signer.signMessage(ethers.utils.arrayify(hash));
signatures.push({
address: await signer.getAddress(),
signature: sig,
});
}
// Sort by address (required by contract)
signatures.sort((a, b) => a.address.toLowerCase().localeCompare(b.address.toLowerCase()));
// Concatenate signatures
const concatenatedSignatures = ethers.utils.solidityPack(
signatures.map(() => 'bytes'),
signatures.map((s) => s.signature),
);
Step 4: Submit the Transaction
const dvnAbi = [
'function quorumChangeAdmin((uint32 vid, address target, bytes callData, uint256 expiration, bytes signatures) _param) external',
];
const dvnContract = new ethers.Contract(dvnAddress, dvnAbi, provider);
// Anyone can submit this transaction - no special permissions required
const tx = await dvnContract.quorumChangeAdmin({
vid: vid,
target: dvnAddress,
callData: callData,
expiration: expiration,
signatures: concatenatedSignatures,
});
await tx.wait();
console.log('Admin role transferred to:', newAdminAddress);
Validation Requirements
The contract validates:
| Check | Requirement |
|---|
| Expiration | expiration > block.timestamp |
| Target | target == address(this) (the DVN contract) |
| VID | vid == contract.vid |
| Signatures | Must meet quorum, sorted by signer address |
| Replay | Hash must not have been used before |
Before Taking Over Admin:
- Gas infrastructure: Ensure you have gas management ready on all chains where you’ll submit transactions
- Quorum coordination: Collect signatures from enough signers to meet the quorum threshold
- Transaction systems: Have reliable transaction submission infrastructure prepared
- Test first: Test the process on testnet before executing on mainnet
The quorumChangeAdmin function is defined in DVN.sol lines 137-160.
Operational Management
Monitoring
- CloudWatch Logs: Automatic log collection
- CloudWatch Alarms: Set up error rate monitoring
- SNS Notifications: Configure alerts
# Tail logs
aws logs tail /ecs/gasolina-api --follow
- Cloud Logging: Automatic log collection
- Alerting Policies: Configure error notifications
- Notification Channels: Set up email/Slack alerts
# View recent logs
gcloud logging read "resource.type=cloud_run_revision" --limit 50
Scaling
| Platform | Scaling Method |
|---|
| AWS | ECS auto-scales based on CPU/memory thresholds |
| GCP | Cloud Run auto-scales based on request volume |
Key Rotation
KMS Keys (Recommended)
- AWS KMS supports automatic key rotation
- Update DVN contract if key ID changes
Mnemonic Keys
- Generate new mnemonic
- Update Secrets Manager
- Update wallet configuration
- Redeploy application
- Update DVN contract with new signer addresses
Troubleshooting
Common Issues
| Issue | Solution |
|---|
| ”API has not been used in project” (GCP) | Wait a few minutes for API enablement to propagate |
| ”Resource already exists” (AWS) | Delete GasolinaMetricLogGroup and S3 bucket, retry |
| RPC connection failures | Verify endpoints, check API keys, add backup providers |
| Signature verification failures | Ensure signer addresses match DVN contract config |
Debug Commands
# AWS: Check ECS logs
aws logs tail /ecs/gasolina-api --follow
# GCP: Check Cloud Run logs
gcloud logging read "resource.type=cloud_run_revision" --limit 50
# Test signing manually
curl -X POST https://your-instance.com \
-H "Content-Type: application/json" \
-d @test-payload.json
Security Best Practices
-
Key Management
- Use HSM-backed keys (KMS) for production
- Implement key rotation policies
- Monitor key usage patterns
-
Network Security
- Restrict API Gateway access if needed
- Implement IP allowlisting where appropriate
- Use VPC endpoints for internal communication
-
Monitoring
- Set up alerts for unusual signature request patterns
- Monitor RPC provider response times
- Track error rates and latency
-
Backup and Recovery
- Backup configuration regularly
- Document recovery procedures
- Test disaster recovery plans periodically
Repository Links
| Resource | Link |
|---|
| AWS Infrastructure | gasolina-aws |
| GCP Infrastructure | gasolina-gcp |
| DVN Contract | DVN.sol |
Next Steps
For implementation support, reach out through LayerZero Discord or open an issue in the respective GitHub repository.