Conventions
Coding standards and team agreements for the Twin platform.
Language
| Context | Language | Example |
|---|---|---|
| Backend error messages | English | throw Errors.badRequest('Token not found') |
| Frontend UI text | Spanish | "Token no encontrado" |
| Code (variables, comments) | English | const walletBalance = ... |
If the frontend needs to display a backend error, translate it on the frontend side (e.g., via a translation map). Never translate throw messages in backend code.
Amount Conversion
Never use parseFloat * Math.pow(10, decimals) for converting token amounts. This causes overflow and scientific notation for large numbers.
Use string-based conversion instead:
Code
Backend schemas should validate base-unit amounts with pattern: '^[0-9]+$' (digits only).
Route Registration Order
Fastify matches routes in registration order. Static routes must be registered before parameterized routes:
Code
CRUD vs Operations Paths
| Type | Sidebar Section | URL Pattern | Purpose |
|---|---|---|---|
| CRUD | Configuracion | /wallets/<entity> | Create, edit, delete wallets |
| Operations | Operaciones | /operations/<entity> | Execute actions, view data, transaction history |
Exception: OpsToken CRUD is under /operations/tokens (tokens are reference entities, not wallets).
Route Permissions
When adding a new endpoint to the dashboard API, register it in 2 places:
- Route file -- The Fastify handler in
packages/dashboard/src/routes/ - Permissions config --
packages/shared/src/config/routes-permissions.ts(HTTP method + allowed roles)
Without the permissions entry, the checkPermissions middleware returns 403 Forbidden.
Cron Job Registration
New cron jobs must be registered in 3 places:
apps/cron-service/src/jobs/cron-keys.ts-- Enum keyapps/cron-service/src/jobs/manager.ts-- Import +jobHandlersmap entrypackages/dashboard/src/cron-handlers.ts-- Handler for on-demand execution from dashboard UI
BitGo Coin Naming
Wallets operating with ERC20 tokens use the full coin identifier including the token suffix:
| Context | Coin Format | Example |
|---|---|---|
| Token transfer (sendMany) | basechain:token | baseeth:argt, polygon:argt |
| Contract call (sendContractCall) | Base chain only | baseeth, polygon |
| GET endpoints | Base chain only | baseeth, polygon |
Use getBaseCoin() in bitgo-express.client.ts to strip the token suffix for GET requests.
Objection.js Gotcha: undefined vs null
Objection.js ignores undefined values in patch() calls. This is by design -- undefined means "don't change this field."
To explicitly clear a field, use null:
Code
This caused the cron lastError persistence bug -- errors were never cleared because the success path patched with undefined.
Frontend Error Handling
Never use empty catch {} blocks -- they silently discard error messages from the backend.
Code
Git Workflow
- Feature branches off
main(backend, dashboard, platform) - Bridge contracts use
masterbranch - Pull requests require review before merge
- Commit messages in English, concise, imperative mood