Skip to content

Commit 6b838c5

Browse files
authored
feat: gas price cache (#422)
1 parent 72865db commit 6b838c5

File tree

35 files changed

+1724
-1548
lines changed

35 files changed

+1724
-1548
lines changed

CHANGELOG.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* add base models ([#5](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/5)) ([55db42b](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/55db42b16d88e95ca8f6927e3b4d07c939e677c8))
1010
* Add CLA assistant bot ([#130](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/130)) ([4ad5733](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/4ad5733daadefe5e52bd617eaa47039677443745))
1111
* add directory structure and example ([d946c10](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/d946c10fd96ee2d1ce2e373ba4ccfced31f985f9))
12-
* add evm intristic gas_limit validation ([dd1b2d6](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/dd1b2d6768d09f051791d0db68c912a38d273715))
12+
* add evm intrinsic gas_limit validation ([dd1b2d6](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/dd1b2d6768d09f051791d0db68c912a38d273715))
1313
* Add get_status method for EVM and Stellar ([#229](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/229)) ([e84217e](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/e84217e0fa941fcd580ad6b84ab6bfac939dd5f4))
1414
* Add Launctube plugin example ([#414](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/414)) ([5bda763](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/5bda7635f304923fcd4031f855009228eeefee4b))
1515
* Add logging improvements ([#28](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/28)) ([bb6751a](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/bb6751a4f868eb82787e7763a7995d3974ecfd49))
@@ -52,7 +52,7 @@
5252
* initial repo setup ([d8815b6](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/d8815b6752931003536aa427370ca8fb1c57231c))
5353
* Integrate Netlify with antora ([#74](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/74)) ([09e3d48](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/09e3d4894b54c58754b373da239e9d564df69aa9))
5454
* Local signing for stellar ([#178](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/178)) ([f69270a](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/f69270ade4c9a9239bba874ac74858c8e7375298))
55-
* Pass arbitrary payloads to script exectution ([#312](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/312)) ([adecaf5](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/adecaf5d73c3df9083c6a3fcf62ed669bc90b25c))
55+
* Pass arbitrary payloads to script execution ([#312](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/312)) ([adecaf5](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/adecaf5d73c3df9083c6a3fcf62ed669bc90b25c))
5656
* Plat 5744 implement an api key authentication mechanism ([#11](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/11)) ([8891887](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/88918872d51ab10632ec6d590689d52e59dfd640))
5757
* Plat 5768 setup metrics endpoint ([#50](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/50)) ([7c292a5](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/7c292a572a7aef8213969fc72cadca74f9016fe8))
5858
* Plat 6434 improve authorization header validation ([#122](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/122)) ([eed7c31](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/eed7c31e938c7b6ecaa82774ca5d3a508bb89281))
@@ -162,7 +162,7 @@
162162
* Plat 6286 write tests for metrics and middleware functions ([#70](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/70)) ([18124fb](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/18124fbbfbc26f300648a7a4050ebf9be72465ac))
163163
* PLAT-6426 Increase test coverage ([#118](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/118)) ([1fa41f0](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/1fa41f0f225c9d515690738e960073396dce66ce))
164164
* PLAT-6478 create unit test for use of on relayers dotenv ([#139](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/139)) ([509e166](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/509e1664518823ef3844e52e818707f3371ddbff))
165-
* plat-6480 allow transfering wrapped sol tokens ([#132](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/132)) ([f04e66a](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/f04e66a568c877c2a4c5c5378fb6017c2e41d2c6))
165+
* plat-6480 allow transferring wrapped sol tokens ([#132](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/132)) ([f04e66a](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/f04e66a568c877c2a4c5c5378fb6017c2e41d2c6))
166166
* Plat-6815 resubmission bug ([#353](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/353)) ([72ac174](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/72ac17471e3a0a6ac35e9a9bb9ff8fe5e8b94bf2))
167167
* plat-6888 aws kms signer issue ([#411](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/411)) ([3c12c88](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/3c12c88703c92526fe975eabba6ba0ffa9ca9c79))
168168
* Plugin result + adds tests for plugin ts lib ([#336](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/336)) ([b30246e](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/b30246e8922d3cb5bd3c5b92a7678f7591db5b97))
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
# OpenZeppelin Relayer - Gas Price Caching Example
2+
3+
This example demonstrates how to configure **gas price caching** for EVM networks using OpenZeppelin Relayer. Gas price caching improves performance by reducing RPC calls and implementing a **stale-while-revalidate (SWR)** strategy that serves cached data immediately while refreshing in the background.
4+
5+
This simple example uses **Sepolia testnet** to showcase gas price caching functionality.
6+
7+
## Key Features Demonstrated
8+
9+
- **Gas Price Caching Configuration**: Configure caching behavior with custom timings
10+
- **Performance Optimization**: Reduce RPC calls and improve response times
11+
- **Direct Network Configuration**: Network defined directly in `config.json` for simplicity
12+
13+
## How Gas Price Caching Works
14+
15+
The gas price cache uses a **stale-while-revalidate** strategy:
16+
17+
1. **Fresh Data**: When data is fresh (within `stale_after_ms`), it's served directly from cache
18+
2. **Stale Data**: When data is stale but not expired, it's served immediately while a background refresh is triggered
19+
3. **Expired Data**: When data is expired (after `expire_after_ms`), the cache returns no data and the service makes fresh RPC calls
20+
21+
### Cache Configuration Parameters
22+
23+
```json
24+
{
25+
"gas_price_cache": {
26+
"enabled": true,
27+
"stale_after_ms": 20000, // 20 seconds - when to trigger background refresh
28+
"expire_after_ms": 45000 // 45 seconds - when to force synchronous refresh
29+
}
30+
}
31+
```
32+
33+
- **`enabled`**: Enable/disable caching for the network
34+
- **`stale_after_ms`**: Milliseconds after which data is considered stale (triggers background refresh)
35+
- **`expire_after_ms`**: Milliseconds after which data expires (cache returns no data, forces direct RPC calls)
36+
37+
## Configuration Structure
38+
39+
In this example, networks are defined directly in the main `config.json` file with different cache configurations:
40+
41+
### Network Configuration with Gas Price Caching
42+
43+
```json
44+
{
45+
"relayers": [
46+
{
47+
"id": "sepolia-example",
48+
"name": "Sepolia Gas Cache Example",
49+
"network": "sepolia",
50+
"network_type": "evm"
51+
}
52+
],
53+
"signers": [...],
54+
"notifications": [...],
55+
"networks": [
56+
{
57+
"network": "sepolia",
58+
"chain_id": 11155111,
59+
"type": "evm",
60+
"is_testnet": true,
61+
"rpc_urls": [
62+
"https://sepolia.drpc.org",
63+
"https://1rpc.io/sepolia"
64+
],
65+
"gas_price_cache": {
66+
"enabled": true,
67+
"stale_after_ms": 20000,
68+
"expire_after_ms": 45000
69+
}
70+
}
71+
]
72+
}
73+
```
74+
75+
### Cache Configuration for Sepolia
76+
77+
The example uses balanced cache timings suitable for testnet development:
78+
79+
```json
80+
{
81+
"gas_price_cache": {
82+
"enabled": true,
83+
"stale_after_ms": 20000, // 20 seconds - fresh enough for testing
84+
"expire_after_ms": 45000 // 45 seconds - reasonable expiry
85+
}
86+
}
87+
```
88+
89+
## Getting Started
90+
91+
### Prerequisites
92+
93+
- [Docker](https://docs.docker.com/get-docker/)
94+
- [Docker Compose](https://docs.docker.com/compose/install/)
95+
- Rust (for key generation tools)
96+
97+
### Step 1: Clone the Repository
98+
99+
```bash
100+
git clone https://github.com/OpenZeppelin/openzeppelin-relayer
101+
cd openzeppelin-relayer
102+
```
103+
104+
### Step 2: Create a Signer
105+
106+
Create a new signer keystore using the provided key generation tool:
107+
108+
```bash
109+
cargo run --example create_key -- \
110+
--password <DEFINE_YOUR_PASSWORD> \
111+
--output-dir examples/gas-price-caching/config/keys \
112+
--filename local-signer.json
113+
```
114+
115+
**Note**: Replace `<DEFINE_YOUR_PASSWORD>` with a strong password for the keystore.
116+
117+
### Step 3: Environment Configuration
118+
119+
Create the environment file:
120+
121+
```bash
122+
cp examples/gas-price-caching/.env.example examples/gas-price-caching/.env
123+
```
124+
125+
Update the `.env` file with your configuration:
126+
127+
- `REDIS_URL`: Redis server URL
128+
- `KEYSTORE_PASSPHRASE`: The password you used for the keystore
129+
- `WEBHOOK_SIGNING_KEY`: Generate using `cargo run --example generate_uuid`
130+
- `API_KEY`: Generate using `cargo run --example generate_uuid`
131+
132+
### Step 4: Configure Webhook URL
133+
134+
Update the `url` field in the notifications section of `config/config.json`. For testing, you can use [Webhook.site](https://webhook.site) to get a test URL.
135+
136+
### Step 5: Set Environment Variables
137+
138+
Before running any commands to interact with the API, export your API key as an environment variable:
139+
140+
```bash
141+
export API_KEY="your-api-key-here"
142+
```
143+
144+
### Step 6: Run the Service
145+
146+
Start the service with Docker Compose:
147+
148+
```bash
149+
docker compose -f examples/gas-price-caching/docker-compose.yaml up
150+
```
151+
152+
The service will be available at `http://localhost:8080/api/v1`
153+
154+
## Testing Gas Price Caching
155+
156+
### Check Available Relayers
157+
158+
```bash
159+
curl -X GET http://localhost:8080/api/v1/relayers \
160+
-H "Content-Type: application/json" \
161+
-H "Authorization: Bearer $API_KEY"
162+
```
163+
164+
### Test Transaction with Cached Gas Prices
165+
166+
```bash
167+
curl -X POST http://localhost:8080/api/v1/relayers/sepolia-example/transactions \
168+
-H "Content-Type: application/json" \
169+
-H "Authorization: Bearer $API_KEY" \
170+
-d '{
171+
"value": 1000000000000000,
172+
"data": "0x",
173+
"to": "0x742d35Cc6640C21a1c7656d2c9C8F6bF5e7c3F8A",
174+
"gas_limit": 21000,
175+
"speed": "average"
176+
}'
177+
```
178+
179+
### Monitor Cache Performance
180+
181+
Check the logs to see cache behavior:
182+
183+
```bash
184+
docker compose -f examples/gas-price-caching/docker-compose.yaml logs -f relayer
185+
```
186+
187+
Look for log messages like:
188+
- `"Updated gas price snapshot for chain_id 11155111 in background"` - Background refresh
189+
190+
## Cache Configuration Best Practices
191+
192+
### RPC Provider Limits
193+
194+
- **Rate-Limited Providers**: Longer cache times to reduce calls
195+
- **Premium Providers**: Shorter cache times for fresher data
196+
- **Public RPCs**: Balanced approach to avoid hitting limits
197+
198+
199+
### Network Congestion Patterns
200+
201+
- **High Congestion Networks**: Shorter cache for rapid price changes
202+
- **Stable Networks**: Longer cache for consistent pricing
203+
- **Predictable Patterns**: Adjust cache based on known traffic patterns
204+
205+
## Performance Benefits
206+
207+
### Reduced RPC Calls
208+
209+
- **Before**: Every gas price request hits RPC
210+
- **After**: Most requests served from cache
211+
212+
### Better User Experience
213+
214+
- **Reduced Failures**: Less dependency on RPC availability
215+
- **Smoother Operations**: No delays during high traffic
216+
217+
## Troubleshooting
218+
219+
### Cache Not Working
220+
221+
1. Check that `gas_price_cache.enabled` is `true` in network config
222+
2. Verify network configuration is loaded correctly
223+
3. Check logs for cache-related errors
224+
225+
### High RPC Usage Despite Caching
226+
227+
1. Verify cache timings are appropriate for your use case
228+
2. Check if cache timings are too short (causing frequent refreshes)
229+
3. Monitor cache hit rates in logs
230+
231+
### Stale Gas Prices
232+
233+
1. Reduce `stale_after_ms` for more frequent background refreshes
234+
2. Check if RPC provider is returning outdated data
235+
3. Verify background refresh is working (check logs)
236+
237+
## When to Use Gas Price Caching
238+
239+
### **Ideal Use Cases**
240+
241+
- **High-Volume Applications**: Many transactions per minute
242+
- **User-Facing Applications**: Need fast response times
243+
- **Rate-Limited RPCs**: Need to reduce API calls
244+
245+
### ⚠️ **Use with Caution**
246+
247+
- **MEV Applications**: May need very fresh gas prices
248+
- **Arbitrage Trading**: Timing-critical applications
249+
- **Emergency Transactions**: When gas price accuracy is critical
250+
251+
### **Not Recommended**
252+
253+
- **Single-Use Scripts**: Overhead not worth it
254+
- **Very Low Transactions**: Cache rarely used
255+
- **Real-Time Trading**: Need absolute latest prices
256+
257+
## See Also
258+
259+
- [Network Configuration JSON File Example](../network-configuration-json-file/README.md) - Shows how to use separate JSON files with inheritance
260+
- [Network Configuration Config File Example](../network-configuration-config-file/README.md) - Direct config file approach (similar to this example)
261+
- [Basic Example](../basic-example/README.md) - Simple relayer setup without caching
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
{
2+
"relayers": [
3+
{
4+
"id": "sepolia-example",
5+
"name": "Sepolia Gas Cache Example",
6+
"network": "sepolia",
7+
"paused": false,
8+
"notification_id": "notification-example",
9+
"signer_id": "local-signer",
10+
"network_type": "evm",
11+
"policies": {
12+
"min_balance": 0
13+
}
14+
}
15+
],
16+
"notifications": [
17+
{
18+
"id": "notification-example",
19+
"type": "webhook",
20+
"url": "",
21+
"signing_key": {
22+
"type": "env",
23+
"value": "WEBHOOK_SIGNING_KEY"
24+
}
25+
}
26+
],
27+
"signers": [
28+
{
29+
"id": "local-signer",
30+
"type": "local",
31+
"config": {
32+
"path": "config/keys/local-signer.json",
33+
"passphrase": {
34+
"type": "env",
35+
"value": "KEYSTORE_PASSPHRASE"
36+
}
37+
}
38+
}
39+
],
40+
"networks": [
41+
{
42+
"average_blocktime_ms": 12000,
43+
"chain_id": 11155111,
44+
"explorer_urls": [
45+
"https://api-sepolia.etherscan.io/api",
46+
"https://sepolia.etherscan.io"
47+
],
48+
"features": [
49+
"eip1559"
50+
],
51+
"is_testnet": true,
52+
"network": "sepolia",
53+
"required_confirmations": 6,
54+
"rpc_urls": [
55+
"https://sepolia.drpc.org",
56+
"https://1rpc.io/sepolia",
57+
"https://ethereum-sepolia-rpc.publicnode.com",
58+
"https://ethereum-sepolia-public.nodies.app"
59+
],
60+
"symbol": "ETH",
61+
"type": "evm",
62+
"gas_price_cache": {
63+
"enabled": true,
64+
"stale_after_ms": 20000,
65+
"expire_after_ms": 45000
66+
}
67+
}
68+
],
69+
"plugins": []
70+
}

0 commit comments

Comments
 (0)