Skip to content

Commit 01c7dda

Browse files
authored
Add Trading Limit tracking and grafana charts (#45)
* chore: remove alfajores * chore: change cron name from raw source to id to allow different metric configs per chain * feat: add EUROCEUR relayer signer monitoring for sepolia * fix: wrong celoscan sepolia url * feat: migrate aegis alerts into terraform * fix: correct CELOETH rate feed config * feat: added link to chainlink data feed for trading mode alerts * feat: add trading limits * chore: address ai review issues * docs: update cursor pr command * chore: small tweaks * feat: add trading limit alerts
1 parent b95ef2b commit 01c7dda

27 files changed

+1729
-206
lines changed

.cursor/commands/code-review.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Code Review Checklist
2+
3+
- You are an expert code reviewer. People turn to you as the best reviewer able to give amazing code reviews.
4+
- Be extremely critical and thorough: Check naming, spelling, formatting, line breaks, code style, conventions, structure, all rule-specific requirements, and all code comments for rule compliance.
5+
- Suggest specific improvements with code examples where applicable.
6+
7+
## Overview
8+
9+
Comprehensive checklist for conducting thorough code reviews to ensure quality, security, and maintainability.
10+
11+
## Review Categories
12+
13+
### Functionality
14+
15+
- [ ] Code does what it's supposed to do
16+
- [ ] Edge cases are handled
17+
- [ ] Error handling is appropriate
18+
- [ ] No obvious bugs or logic errors
19+
20+
### Code Quality
21+
22+
- [ ] Code is readable and well-structured
23+
- [ ] Functions are small and focused
24+
- [ ] Variable names are descriptive
25+
- [ ] No code duplication
26+
- [ ] Follows project conventions
27+
28+
### Security
29+
30+
- [ ] No obvious security vulnerabilities
31+
- [ ] Input validation is present
32+
- [ ] Sensitive data is handled properly
33+
- [ ] No hardcoded secrets
34+
35+
## Tactical Advice on how to get the code to review
36+
37+
1. Make sure you're in the root folder of the current cursor project
38+
1. If you're being asked to review an open PR use `gh pr list` to show open PRs, and if a PR number is provided, use `gh pr view <number>` to get PR details, then use `gh pr diff <number>` to get the diff
39+
1. If you're being asked to review the diff of the current branch against main, run `git --no-pager diff main` to get a full diff in the terminal
40+
1. Analyze the changes and provide a thorough code review that includes:
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Create PR Description
2+
3+
## Overview
4+
5+
Create a well-structured pull request description and instructions on how to review the PR.
6+
7+
## Steps
8+
9+
1. **Prepare branch**
10+
- Verify branch is up to date with main
11+
12+
2. **Write PR description**
13+
- Summarize changes clearly and concisely
14+
- Include context and motivation
15+
- List any breaking changes
16+
- Add screenshots if UI changes
17+
- Add a "How to review" section that details what reviewers should run or check to see the changes in this PR working in action
18+
19+
3. **Set up PR**
20+
- Print PR title and description in a code block as raw markdown for easy copy/paste into github
21+
22+
## PR Template
23+
24+
## Description
25+
26+
- [ ] Feature A
27+
- [ ] Bug fix B
28+
- [ ] Unit tests pass
29+
- [ ] Manual testing completed
30+
31+
### Optional subsection with further context / motivation where necessary
32+
33+
## How to test
34+
35+
1. Check out this branch locally
36+
2. Start a server via `<command>`
37+
3. Do XYZ to verify the feature works
38+
4. Review the code
39+
- Focus on sections ABC

README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,18 @@ We use Terraform to deploy Grafana Dashboards and Grafana Alerts. The end-to-end
304304
# Get this from the Discord channel integration settings of #🏦︱reserve-alerts
305305
discord_alerts_webhook_url_reserve =
306306
307+
# Get this from the Discord channel integration settings of #🚨︱trading-modes-sepolia
308+
discord_alerts_webhook_url_trading_modes_staging =
309+
310+
# Get this from the Discord channel integration settings of #🚨︱trading-modes-celo
311+
discord_alerts_webhook_url_trading_modes_prod =
312+
313+
# Get this from the Discord channel integration settings of #🚨︱trading-limits-celo
314+
discord_alerts_webhook_url_trading_limits =
315+
316+
# Get this from the Discord channel integration settings of #🚨︱aegis
317+
discord_alerts_webhook_url_aegis =
318+
307319
# Get this from the Discord channel integration settings of #alerts-catch-all
308320
discord_alerts_webhook_url_catch_all =
309321
@@ -341,6 +353,37 @@ Grafana uses the following concepts for managing alerts:
341353
- [**Contact Points**](https://grafana.com/docs/grafana/latest/alerting/fundamentals/notifications/contact-points/): Alert channels like Discord, Splunk/VictorOps, Email etc.
342354
- [**Notification Policies**](https://grafana.com/docs/grafana/latest/alerting/fundamentals/notifications/notification-policies/): Routing rules to determine which alerts get routed to what contact points.
343355

356+
#### Current Alerts
357+
358+
**Oracle Relayer Alerts** (`service=oracle-relayers`):
359+
360+
- Stale price feeds (oldest report expired)
361+
- Low CELO balance for relayer wallets
362+
- Routed to: Discord channels (`#🚨︱stg-oracle-relayers`, `#🚨︱prod-oracle-relayers`) + VictorOps/Splunk (mainnet only)
363+
364+
**Reserve Balance Alerts** (`service=reserve`):
365+
366+
- Low reserve balances for CELO, USDC, USDT, EUROC
367+
- Routed to: Discord channel (`#🚨︱reserve`)
368+
369+
**Trading Mode Alerts** (`service=exchanges`):
370+
371+
- Trading halted (circuit breakers tripped)
372+
- Routed to: Discord channels (`#🚨︱stg-trading-modes`, `#🚨︱prod-trading-modes`)
373+
374+
**Trading Limits Alerts** (`service=trading-limits`):
375+
376+
- **L0 Short-term Limits** (5-minute window): Alerts at 90% utilization → Discord only
377+
- **L1 Medium-term Limits** (daily window): Alerts at 90% utilization → Discord + VictorOps/Splunk
378+
- **LG Global Lifetime Limits**: Alerts at 90% utilization → Discord + VictorOps/Splunk
379+
- Routed to: Discord channel (`#🚨︱trading-limits-celo`) + VictorOps/Splunk (L1 & LG only)
380+
381+
**Aegis Service Alerts** (`service=aegis`):
382+
383+
- Failed RPC calls
384+
- Service not reporting new data
385+
- Routed to: Discord channel (`#🚨︱aegis`) + VictorOps/Splunk
386+
344387
### Terraform Troubleshooting
345388

346389
#### Terraform fails to delete Grafana Resources

config.yaml

Lines changed: 135 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ chains:
4040
contracts:
4141
SortedOracles: '0xefb84935239dacdecf7c5ba76d8de40b077b7b33'
4242
BreakerBox: '0x303ED1df62Fa067659B586EbEe8De0EcE824Ab39'
43+
Broker: '0x777A8255cA72412f0d706dc03C9D1987306B4CaD'
4344
# trunk-ignore(gitleaks/generic-api-key,checkov/CKV_SECRET_6)
4445
CELOToken: '0x471ece3750da237f93b8e339c536989b8978a438'
4546
USDT: '0x48065fbbe25f71c9282ddf5e1cd6d6a887483d5e' # https://tether.to/en/supported-protocols
@@ -86,12 +87,50 @@ chains:
8687
CELOXOF: '0x73F93dcc49cB8A239e2032663e9475dd5ef29A08' # eXOF address
8788
CELOZAR: '0xD064b6CcFF2AE8968bA6725e9A92f3F0431bf5D0' # keccak(relayed:CELOZAR)
8889

90+
# Trading Limit IDs (for Broker.tradingLimitsState)
91+
# Format: [POOL]_POOL_[TOKEN]_LIMIT
92+
CUSD_CGHS_POOL_CUSD_LIMIT: '0x3562f9d29eba092b857480a85d5edf4ebd9f33d331bb423287c1d0bca883a9ad'
93+
CUSD_CGHS_POOL_CGHS_LIMIT: '0x3562f9d29eba092b857480a8d1e9686c3d7cf039575c465715d230d14a0ef294'
94+
CUSD_CGBP_POOL_CUSD_LIMIT: '0x6c369bfb1598b2f7718671226d98ccdecc2cccf98d9ffe07339a8f38df93cae4'
95+
CUSD_CGBP_POOL_CGBP_LIMIT: '0x6c369bfb1598b2f771867122d7334779b765af91dca3072b1e5a80e06386e588'
96+
CUSD_CZAR_POOL_CUSD_LIMIT: '0x4206e101b13bf29e40b2bfed3aac8f31981906907ad712d9487ad7c6427029e1'
97+
CUSD_CZAR_POOL_CZAR_LIMIT: '0x4206e101b13bf29e40b2bfed00c4e21d270f0308f2941aa60d4c73d116800a3d'
98+
CUSD_CAUD_POOL_CUSD_LIMIT: '0xd580d237231109e6a96d67d8520d890ae552e1bd7c43f0310aa0b49468fbbded'
99+
CUSD_CAUD_POOL_CAUD_LIMIT: '0xd580d237231109e6a96d67d855253150245af6ab7a62ae692295e92e51be073e'
100+
CUSD_CELO_POOL_CUSD_LIMIT: '0x3135b662c38265d06551770969468c6dcbb730f659f590a04c469913fe222506'
101+
AXLEUROC_CEUR_POOL_CEUR_LIMIT: '0xfca6d94b46122eb9a4b86cf90b97d4ecd9c29f1e3622a371094e239d2d79c53b'
102+
AXLEUROC_CEUR_POOL_AXLEUROC_LIMIT: '0xfca6d94b46122eb9a4b86cf9d5fd2df436cb48e71bbc66c36fa8a75b58e37965'
103+
CUSD_CCHF_POOL_CUSD_LIMIT: '0x3ddbc61433314a4b7d3cbb56d65c145a44a9b4ca3e167f97f74960a2e0bdccd6'
104+
CUSD_CCHF_POOL_CCHF_LIMIT: '0x3ddbc61433314a4b7d3cbb56155b85bf58168cc958a63a4fc3cd38d6dfe2f7e1'
105+
CUSD_CNGN_POOL_CUSD_LIMIT: '0x67a5122dab72931be57196e0dde7697f803992c0e6450739e8c587927d382ee6'
106+
CUSD_CNGN_POOL_CNGN_LIMIT: '0x67a5122dab72931be57196e049caaab07a82cfaf749602b569c9dc6a4f2e69bd'
107+
CUSD_CKES_POOL_CUSD_LIMIT: '0x89de88b8eb790de26f4649f5359680855d6e5420728979de2b9d80da43bc01a1'
108+
CUSD_CKES_POOL_CKES_LIMIT: '0x89de88b8eb790de26f4649f506a15597f53b88fa9d91d1f6e0ab13417e5e995b'
109+
CUSD_USDC_POOL_CUSD_LIMIT: '0xacc988382b66ee5456086643aaa0724a2065b91372d313837286e318ab0dd7fd'
110+
CUSD_AXLUSDC_POOL_CUSD_LIMIT: '0x0d739efbfc30f303e8d1976c5766a85601557b135532b3e04b032e6e458c070a'
111+
CUSD_USDT_POOL_CUSD_LIMIT: '0x773bcec109cee923b5e04706721231c0214a7afd3069f93d1446d6c33cec66b1'
112+
CUSD_CCAD_POOL_CUSD_LIMIT: '0x517ccc3bcab9f35e2e24143a7a45e1100aa405782e2dba6e941aade88f84e9c4'
113+
CUSD_CCAD_POOL_CCAD_LIMIT: '0x517ccc3bcab9f35e2e24143af352b8951f534093451412efbce8e743321ea2cb'
114+
CUSD_CPHP_POOL_CUSD_LIMIT: '0x7952984d7278ca3417febf52f701da0f00f450a094f73c8a941b43937201ac39'
115+
CUSD_CPHP_POOL_CPHP_LIMIT: '0x7952984d7278ca3417febf529101788a827ed41dbf002bbe0eb578a5d7728968'
116+
CUSD_CJPY_POOL_CUSD_LIMIT: '0x7c3b41fbd140c6fb54ff9f8f0d26e483c25e09ca1ec8a2f792b367d8421e4d06'
117+
CUSD_CJPY_POOL_CJPY_LIMIT: '0x7c3b41fbd140c6fb54ff9f8fbf25c3b5b5cbee667634271bdf7e8e0a12264b0c'
118+
CUSD_CCOP_POOL_CUSD_LIMIT: '0x1c9378bd0973ff313a599d3e899bbcacf1c7ed2bfc7057cc4e5ed5a11aa3bc11'
119+
CUSD_CCOP_POOL_CCOP_LIMIT: '0x1c9378bd0973ff313a599d3e75902a9092032a5e1b2121d55eeda17c7c52d5d1'
120+
CUSD_CEUR_POOL_CUSD_LIMIT: '0x746455363e8f55d04e0a2cc0368c5b5e229850546c9f96578c2a39c4a1f801e2'
121+
CUSD_CEUR_POOL_CEUR_LIMIT: '0x746455363e8f55d04e0a2cc098a78ff281aa068bd064ef421d2e0e82cf9fe3bb'
122+
CUSD_CBRL_POOL_CUSD_LIMIT: '0xd11d52b973ddbb983cc2087add971587db9b75eba3bc960de4a6d1e9b5aaf62f'
123+
CUSD_CBRL_POOL_CBRL_LIMIT: '0xd11d52b973ddbb983cc2087a439987ac5aaeb04a9ee2f431aa400e75e7b09982'
124+
CUSD_EXOF_POOL_CUSD_LIMIT: '0xc9664df358594c5eaf2f410ac52c0ac83ced532d7c33d18ae1d6f12069c57e51'
125+
CUSD_EXOF_POOL_EXOF_LIMIT: '0xc9664df358594c5eaf2f410ac088df12f17eb8e9b8361f4dfdf9ec658f86cc73'
126+
89127
- id: celoSepolia
90128
label: celo-sepolia
91129
httpRpcUrl: https://forno.celo-sepolia.celo-testnet.org
92130
contracts:
93131
SortedOracles: '0xfaa7Ca2B056E60F6733aE75AA0709140a6eAfD20'
94132
BreakerBox: '0x578bD46003B9D3fd4c3C3f47c98B329562a6a1dE'
133+
Broker: '0xB9Ae2065142EB79b6c5EB1E8778F883fad6B07Ba'
95134
# trunk-ignore(gitleaks/generic-api-key,checkov/CKV_SECRET_6)
96135
CELOToken: '0x019E1E529200fEf594A93A261141dDDeef92F855'
97136
vars:
@@ -165,8 +204,101 @@ metrics:
165204
- [XOFUSD]
166205
- [ZARUSD]
167206

207+
# Trading limits state - tracks all three netflow limits
208+
# Netflow represents the net amount traded in/out, used to prevent price manipulation
209+
# netflow0: short-term L0 limit, netflow1: medium-term L1 limit, netflowGlobal: lifetime limit
210+
- source: Broker.tradingLimitsState(bytes32 limitId)(uint32 lastUpdated0,uint32 lastUpdated1,int48 netflow0,int48 netflow1,int48 netflowGlobal)
211+
schedule: 0/10 * * * * *
212+
type: gauge
213+
chains: [celo]
214+
variants:
215+
# cUSD limits
216+
- [CUSD_CAUD_POOL_CUSD_LIMIT]
217+
- [CUSD_CBRL_POOL_CUSD_LIMIT]
218+
- [CUSD_CCAD_POOL_CUSD_LIMIT]
219+
- [CUSD_CCHF_POOL_CUSD_LIMIT]
220+
- [CUSD_CCOP_POOL_CUSD_LIMIT]
221+
- [CUSD_CEUR_POOL_CUSD_LIMIT]
222+
- [CUSD_CGBP_POOL_CUSD_LIMIT]
223+
- [CUSD_CGHS_POOL_CUSD_LIMIT]
224+
- [CUSD_CJPY_POOL_CUSD_LIMIT]
225+
- [CUSD_CKES_POOL_CUSD_LIMIT]
226+
- [CUSD_CNGN_POOL_CUSD_LIMIT]
227+
- [CUSD_CPHP_POOL_CUSD_LIMIT]
228+
- [CUSD_EXOF_POOL_CUSD_LIMIT]
229+
- [CUSD_CZAR_POOL_CUSD_LIMIT]
230+
- [CUSD_CELO_POOL_CUSD_LIMIT]
231+
- [CUSD_USDC_POOL_CUSD_LIMIT]
232+
- [CUSD_AXLUSDC_POOL_CUSD_LIMIT]
233+
- [CUSD_USDT_POOL_CUSD_LIMIT]
234+
# Non-cUSD limits
235+
- [CUSD_CAUD_POOL_CAUD_LIMIT]
236+
- [CUSD_CBRL_POOL_CBRL_LIMIT]
237+
- [CUSD_CCAD_POOL_CCAD_LIMIT]
238+
- [CUSD_CCHF_POOL_CCHF_LIMIT]
239+
- [CUSD_CCOP_POOL_CCOP_LIMIT]
240+
- [CUSD_CEUR_POOL_CEUR_LIMIT]
241+
- [CUSD_CGBP_POOL_CGBP_LIMIT]
242+
- [CUSD_CGHS_POOL_CGHS_LIMIT]
243+
- [CUSD_CJPY_POOL_CJPY_LIMIT]
244+
- [CUSD_CKES_POOL_CKES_LIMIT]
245+
- [CUSD_CNGN_POOL_CNGN_LIMIT]
246+
- [CUSD_CPHP_POOL_CPHP_LIMIT]
247+
- [CUSD_EXOF_POOL_EXOF_LIMIT]
248+
- [CUSD_CZAR_POOL_CZAR_LIMIT]
249+
# axlEUROC/cEUR pool
250+
- [AXLEUROC_CEUR_POOL_CEUR_LIMIT]
251+
- [AXLEUROC_CEUR_POOL_AXLEUROC_LIMIT]
252+
253+
# Trading limits configuration - defines the trading limit thresholds
254+
# The config changes rarely, so we poll hourly
255+
# Returns: timestep0, timestep1 (time windows), limit0, limit1, limitGlobal (thresholds), flags (enabled status)
256+
- source: Broker.tradingLimitsConfig(bytes32 limitId)(uint32 timestep0,uint32 timestep1,int48 limit0,int48 limit1,int48 limitGlobal,uint8 flags)
257+
schedule: 0 0 * * * *
258+
type: gauge
259+
chains: [celo]
260+
variants:
261+
# cUSD limits
262+
- [CUSD_CAUD_POOL_CUSD_LIMIT]
263+
- [CUSD_CBRL_POOL_CUSD_LIMIT]
264+
- [CUSD_CCAD_POOL_CUSD_LIMIT]
265+
- [CUSD_CCHF_POOL_CUSD_LIMIT]
266+
- [CUSD_CCOP_POOL_CUSD_LIMIT]
267+
- [CUSD_CEUR_POOL_CUSD_LIMIT]
268+
- [CUSD_CGBP_POOL_CUSD_LIMIT]
269+
- [CUSD_CGHS_POOL_CUSD_LIMIT]
270+
- [CUSD_CJPY_POOL_CUSD_LIMIT]
271+
- [CUSD_CKES_POOL_CUSD_LIMIT]
272+
- [CUSD_CNGN_POOL_CUSD_LIMIT]
273+
- [CUSD_CPHP_POOL_CUSD_LIMIT]
274+
- [CUSD_EXOF_POOL_CUSD_LIMIT]
275+
- [CUSD_CZAR_POOL_CUSD_LIMIT]
276+
- [CUSD_CELO_POOL_CUSD_LIMIT]
277+
- [CUSD_USDC_POOL_CUSD_LIMIT]
278+
- [CUSD_AXLUSDC_POOL_CUSD_LIMIT]
279+
- [CUSD_USDT_POOL_CUSD_LIMIT]
280+
# Non-cUSD limits
281+
- [CUSD_CAUD_POOL_CAUD_LIMIT]
282+
- [CUSD_CBRL_POOL_CBRL_LIMIT]
283+
- [CUSD_CCAD_POOL_CCAD_LIMIT]
284+
- [CUSD_CCHF_POOL_CCHF_LIMIT]
285+
- [CUSD_CCOP_POOL_CCOP_LIMIT]
286+
- [CUSD_CEUR_POOL_CEUR_LIMIT]
287+
- [CUSD_CGBP_POOL_CGBP_LIMIT]
288+
- [CUSD_CGHS_POOL_CGHS_LIMIT]
289+
- [CUSD_CJPY_POOL_CJPY_LIMIT]
290+
- [CUSD_CKES_POOL_CKES_LIMIT]
291+
- [CUSD_CNGN_POOL_CNGN_LIMIT]
292+
- [CUSD_CPHP_POOL_CPHP_LIMIT]
293+
- [CUSD_EXOF_POOL_EXOF_LIMIT]
294+
- [CUSD_CZAR_POOL_CZAR_LIMIT]
295+
# axlEUROC/cEUR pool
296+
- [AXLEUROC_CEUR_POOL_CEUR_LIMIT]
297+
- [AXLEUROC_CEUR_POOL_AXLEUROC_LIMIT]
298+
168299
# Checks for rate feed freshness
169-
- source: SortedOracles.isOldestReportExpired(address rateFeed)(bool,address)
300+
# Note: We only care about isExpired in Grafana, but must declare both for proper ABI decoding
301+
- source: SortedOracles.isOldestReportExpired(address rateFeed)(bool isExpired,address oracle)
170302
schedule: 0/10 * * * * *
171303
type: gauge
172304
chains: all
@@ -213,7 +345,7 @@ metrics:
213345

214346
# Checks if wallets or contracts of interest have enough CELO to pay for transactions
215347
- source: CELOToken.balanceOf(address owner)(uint256)
216-
schedule: 0/10 * * * * *
348+
schedule: 0 * * * * *
217349
type: gauge
218350
chains: [celo]
219351
variants:
@@ -250,9 +382,8 @@ metrics:
250382
- [RelayerSignerZARUSD]
251383
- [Reserve]
252384

253-
# We disabled the Mainnet EUROCEUR feed, hence sepolia only
254385
- source: CELOToken.balanceOf(address owner)(uint256)
255-
schedule: 0/10 * * * * *
386+
schedule: 0 * * * * *
256387
type: gauge
257388
chains: [celoSepolia]
258389
variants:

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"build": "nest build",
1010
"deploy": "./bin/deploy.sh",
1111
"dev": "npm run start:dev",
12-
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
12+
"format": "prettier --write \"src/**/*\"",
1313
"grafana": "open $(terraform -chdir=terraform state show module.grafana_dashboard.grafana_dashboard.aegis | grep 'url' | awk -F '\"' '{print $2}')",
1414
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
1515
"logs": "./bin/logs.sh",

0 commit comments

Comments
 (0)