Skip to content

Commit d58c424

Browse files
authored
Merge branch 'main' into fix/TMCU-538_update-error-state-icons
2 parents e9abd90 + a0da5f1 commit d58c424

9 files changed

Lines changed: 365 additions & 320 deletions

File tree

app/components/Views/Homepage/Homepage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ const Homepage = forwardRef<SectionRefreshHandle>((_, ref) => {
7272
useImperativeHandle(ref, () => ({ refresh }), [refresh]);
7373

7474
return (
75-
<Box gap={6} marginBottom={8} testID="homepage-container">
75+
<Box gap={12} marginBottom={8} testID="homepage-container">
7676
<TokensSection
7777
ref={tokensSectionRef}
7878
sectionIndex={getSectionIndex(HomeSectionNames.TOKENS)}

app/core/NavigationService/NavigationService.ts

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,18 @@ class NavigationService {
9595
this.#assertNavigationRefType(navRef);
9696
this.#navigation = this.#createReactAwareNavigation(navRef);
9797

98-
// Agentic bridge — exposes navigation primitives on globalThis so that
99-
// AI coding agents (Claude Code, Cursor, etc.) can inspect and drive the
100-
// app remotely via Metro's Hermes CDP WebSocket. The bridge is consumed
101-
// by the scripts in `scripts/perps/agentic/` (cdp-bridge.js).
98+
// Agentic bridge — exposes navigation primitives and account helpers on
99+
// globalThis so that AI coding agents (Claude Code, Cursor, etc.) can
100+
// inspect and drive the app remotely via Metro's Hermes CDP WebSocket.
101+
// The bridge is consumed by the scripts in `scripts/perps/agentic/`.
102102
//
103-
// __DEV__ only — completely stripped from production builds.
103+
// Why NavigationService? It is the single guaranteed init point that runs
104+
// once the navigation container is ready, making it the natural place to
105+
// install any __DEV__ globals that need a live app reference (nav + Engine).
106+
// Account helpers (listAccounts, switchAccount) live here rather than in a
107+
// separate file to avoid an extra module boundary in __DEV__-only code.
108+
//
109+
// __DEV__ only — completely stripped from production builds by Metro/Babel.
104110
// See docs/perps/perps-agentic-feedback-loop.md for the full workflow.
105111
if (__DEV__) {
106112
Logger.log('[NavigationService] __AGENTIC__ bridge installed');
@@ -115,6 +121,47 @@ class NavigationService {
115121
getState: () => navRef.dangerouslyGetState(),
116122
canGoBack: () => navRef.canGoBack(),
117123
goBack: () => deferredNav.goBack(),
124+
listAccounts: () => {
125+
const ctrl = Engine.context.AccountsController;
126+
return ctrl
127+
.listAccounts()
128+
.map(
129+
(a: {
130+
id: string;
131+
address: string;
132+
metadata: { name: string };
133+
}) => ({
134+
id: a.id,
135+
address: a.address,
136+
name: a.metadata.name,
137+
}),
138+
);
139+
},
140+
getSelectedAccount: () => {
141+
const ctrl = Engine.context.AccountsController;
142+
const a = ctrl.getSelectedAccount();
143+
return { id: a.id, address: a.address, name: a.metadata.name };
144+
},
145+
switchAccount: (address: string) => {
146+
const ctrl = Engine.context.AccountsController;
147+
const accounts = ctrl.listAccounts();
148+
const target = accounts.find(
149+
(a: { address: string }) =>
150+
a.address.toLowerCase() === address.toLowerCase(),
151+
);
152+
if (!target) {
153+
throw new Error(`No account found for address ${address}`);
154+
}
155+
// Use Engine.setSelectedAddress to sync both AccountsController
156+
// and PreferencesController (not AccountsController.setSelectedAccount directly).
157+
Engine.setSelectedAddress(target.address);
158+
return {
159+
switched: true,
160+
id: target.id,
161+
address: target.address,
162+
name: target.metadata.name,
163+
};
164+
},
118165
};
119166
try {
120167
(globalThis as Record<string, unknown>).store = ReduxService.store;

docs/perps/perps-agentic-feedback-loop.md

Lines changed: 86 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,25 @@ scripts/perps/agentic/app-state.sh eval-async "<js-expression>" # run JS returni
110110
scripts/perps/agentic/app-state.sh nav # full navigation tree
111111
scripts/perps/agentic/app-state.sh can-go-back # check if can go back
112112
scripts/perps/agentic/app-state.sh go-back # navigate back
113+
scripts/perps/agentic/app-state.sh accounts # list all accounts
114+
scripts/perps/agentic/app-state.sh account # get selected account
115+
scripts/perps/agentic/app-state.sh switch-account <addr> # switch to account by address
116+
scripts/perps/agentic/app-state.sh recipe <team/name> # run a recipe (e.g. perps/positions)
117+
scripts/perps/agentic/app-state.sh recipe --list # list all available recipes
113118
scripts/perps/agentic/screenshot.sh [label] # take screenshot, returns path
114119
scripts/perps/agentic/start-metro.sh # ensure Metro is running
115120
scripts/perps/agentic/stop-metro.sh # stop Metro background process
116-
scripts/perps/agentic/reload-metro.sh # trigger hot-reload on all connected apps
117-
scripts/perps/agentic/test-trade-flow.sh # end-to-end trade validation harness
121+
scripts/perps/agentic/reload-metro.sh # trigger hot-reload on all connected apps
122+
```
123+
124+
**yarn shortcuts** (human-friendly aliases):
125+
126+
```bash
127+
yarn a:start # start/attach Metro
128+
yarn a:stop # stop Metro
129+
yarn a:status # current route
130+
yarn a:reload # hot-reload all connected apps
131+
yarn a:navigate # navigate to a screen (pass route + optional params)
118132
```
119133

120134
**Metro log**: `.agent/metro.log` — grep for errors after changes.
@@ -123,17 +137,19 @@ scripts/perps/agentic/test-trade-flow.sh # end-to-end tr
123137

124138
```
125139
scripts/perps/agentic/
126-
├── cdp-bridge.js # CDP engine: WebSocket client, target discovery, eval, navigate
127-
├── app-navigate.sh # Navigate wrapper (calls cdp-bridge + auto-screenshot)
128-
├── app-state.sh # State/route/eval wrapper (calls cdp-bridge)
129-
├── screenshot.sh # Cross-platform screenshot (iOS simctl / Android adb)
130-
├── start-metro.sh # Start Metro (or attach to existing)
131-
├── stop-metro.sh # Stop Metro background process
132-
├── reload-metro.sh # Trigger hot-reload on all connected apps
133-
└── test-trade-flow.sh # End-to-end trade validation (place → verify → close)
140+
├── cdp-bridge.js # CDP engine: WebSocket client, target discovery, eval, navigate
141+
├── app-navigate.sh # Navigate wrapper (calls cdp-bridge + auto-screenshot)
142+
├── app-state.sh # State/route/eval/accounts/recipe wrapper (calls cdp-bridge)
143+
├── screenshot.sh # Cross-platform screenshot (iOS simctl / Android adb)
144+
├── start-metro.sh # Start Metro (or attach to existing)
145+
├── stop-metro.sh # Stop Metro background process
146+
├── reload-metro.sh # Trigger hot-reload on all connected apps
147+
└── recipes/ # Per-team recipe files (see recipes/README.md)
148+
├── perps.json # Perps team recipes (positions, auth, balances, markets, trade-flow, etc.)
149+
└── README.md # How to add recipes for your team
134150
```
135151

136-
The `__AGENTIC__` bridge on `globalThis` exposes: `navigate()`, `getRoute()`, `getState()`, `canGoBack()`, `goBack()`. These work identically on both platforms via Metro's Hermes CDP.
152+
The `__AGENTIC__` bridge on `globalThis` exposes: `navigate()`, `getRoute()`, `getState()`, `canGoBack()`, `goBack()`, `listAccounts()`, `getSelectedAccount()`, `switchAccount()`. These work identically on both platforms via Metro's Hermes CDP.
137153

138154
> **Platform targeting**: CDP-based commands (navigate, state, eval, go-back) are platform-agnostic — they go through Metro's WebSocket and reach whichever app is connected. Screenshots require direct device access (`xcrun simctl` or `adb`), so `screenshot.sh` auto-detects the platform. When both iOS and Android devices are connected, set `PLATFORM=android` or `PLATFORM=ios` to disambiguate. Since `app-navigate.sh` takes a verification screenshot, pass `PLATFORM` when needed:
139155
>
@@ -239,53 +255,40 @@ scripts/perps/agentic/app-state.sh eval-async \
239255

240256
### Trade Flow Validation
241257

242-
`test-trade-flow.sh` is an end-to-end trade validation harness that places a real order, monitors WebSocket data flow, and verifies position state changes. It was created to validate fixes for TAT-2597 (position not appearing after trade) and TAT-2598 (missing prices / can't trade).
258+
The trade flow validation pattern uses three recipes in `recipes/perps.json` to capture pre/post state around an order. This replaces a shell script with composable `eval-async` calls that an agent can orchestrate directly.
243259

244-
**Usage:**
260+
**Recipes:**
261+
262+
| Recipe | Description |
263+
| ------------------- | -------------------------------------------------- |
264+
| `perps/pre-trade` | Position count + balance snapshot before the trade |
265+
| `perps/place-order` | **TEMPLATE** — market buy BTC $10 at 2x leverage |
266+
| `perps/post-trade` | Same snapshot shape as pre-trade for comparison |
267+
268+
**Orchestration pattern:**
245269

246270
```bash
247-
# Default: BTC long, $10, 2x leverage
248-
scripts/perps/agentic/test-trade-flow.sh
271+
# 1. Capture baseline
272+
scripts/perps/agentic/app-state.sh recipe perps/pre-trade
249273

250-
# Custom symbol, side, size
251-
SYMBOL=ETH SIDE=sell SIZE=0.01 USD_AMOUNT=20 \
252-
scripts/perps/agentic/test-trade-flow.sh
274+
# 2. Place order (template — edit the expression or use eval-async for custom params)
275+
scripts/perps/agentic/app-state.sh recipe perps/place-order
253276

254-
# Skip navigation (already on Perps screen)
255-
SKIP_NAV=1 scripts/perps/agentic/test-trade-flow.sh
277+
# 3. Wait for WebSocket updates
278+
sleep 5
256279

257-
# Keep position open after test
258-
SKIP_CLOSE=1 scripts/perps/agentic/test-trade-flow.sh
280+
# 4. Capture post-trade state
281+
scripts/perps/agentic/app-state.sh recipe perps/post-trade
259282
```
260283

261-
**Environment variables:**
262-
263-
| Variable | Default | Description |
264-
| ------------- | --------- | ------------------------------------------ |
265-
| `SYMBOL` | `BTC` | Market symbol |
266-
| `SIDE` | `buy` | `buy` (long) or `sell` (short) |
267-
| `SIZE` | `0.0001` | Position size in base asset |
268-
| `USD_AMOUNT` | `10` | Notional USD value |
269-
| `LEVERAGE` | `2` | Leverage multiplier |
270-
| `ORDER_TYPE` | `market` | `market` or `limit` |
271-
| `LIMIT_PRICE` | _(empty)_ | Price for limit orders |
272-
| `SLIPPAGE` | `500` | Max slippage in basis points |
273-
| `SKIP_NAV` | _(empty)_ | Set to `1` to skip navigation to Perps |
274-
| `SKIP_CLOSE` | _(empty)_ | Set to `1` to keep position open |
275-
| `PLATFORM` | `android` | Platform for screenshots |
276-
| `WAIT_SECS` | `5` | Seconds to wait for WS updates after order |
277-
278-
**What it validates:**
279-
280-
1. Engine accessibility via CDP
281-
2. Pre-trade position count capture
282-
3. Live price fetch from `getMarketDataWithPrices()`
283-
4. Order validation via `validateOrder()`
284-
5. Order placement via `placeOrder()` + latency measurement
285-
6. Post-trade position count increase (TAT-2597)
286-
7. WebSocket position callbacks (`PositionStreamChannel`) (TAT-2597)
287-
8. Price stream first-data receipt (TAT-2598)
288-
9. Position cleanup via `closePosition()` (unless `SKIP_CLOSE`)
284+
**Custom order via `eval-async`** (substitute your own params):
285+
286+
```bash
287+
scripts/perps/agentic/app-state.sh eval-async \
288+
"Engine.context.PerpsController.placeOrder({symbol:'ETH',isBuy:false,orderType:'market',size:'0.01',leverage:3,usdAmount:'20',maxSlippageBps:500}).then(function(r){return JSON.stringify(r)})"
289+
```
290+
291+
> **Important:** `perps/place-order` places a real order. It is labeled as a template/example. Always verify auth state (`perps/auth`) and balances (`perps/balances`) before running.
289292
290293
### Metro Log Debugging
291294

@@ -315,6 +318,39 @@ grep 'PERPS_DEBUG.*cache' .agent/metro.log | tail -20
315318

316319
> When debugging WS issues, the key signal is whether `PositionStreamChannel: WS callback` appears after placing an order. If it doesn't, the WebSocket subscription may be stale — check `ensureReady` logs for connection state.
317320
321+
### Account Management
322+
323+
The `__AGENTIC__` bridge exposes account methods at the root level. These are available via `cdp-bridge.js` commands or `app-state.sh` wrappers.
324+
325+
```bash
326+
# List all accounts (id, address, name)
327+
scripts/perps/agentic/app-state.sh accounts
328+
329+
# Get the currently selected account
330+
scripts/perps/agentic/app-state.sh account
331+
332+
# Switch to a different account by address
333+
scripts/perps/agentic/app-state.sh switch-account 0x1234...abcd
334+
```
335+
336+
Useful for auth scoping validation — switch accounts and verify that controller state (e.g. perps auth) updates correctly. Combine with `recipe perps/auth` to check auth state after switching.
337+
338+
### Recipes
339+
340+
Recipes are per-team JSON files in `scripts/perps/agentic/recipes/` that define reusable CDP expressions. This keeps domain-specific helpers in the scripts layer rather than the app source — any controller method accessible via `Engine.context` can be a recipe.
341+
342+
```bash
343+
# Run a recipe
344+
scripts/perps/agentic/app-state.sh recipe perps/positions
345+
scripts/perps/agentic/app-state.sh recipe perps/auth
346+
scripts/perps/agentic/app-state.sh recipe perps/markets
347+
348+
# List all available recipes
349+
scripts/perps/agentic/app-state.sh recipe --list
350+
```
351+
352+
**Adding recipes for your team:** Create `recipes/<team>.json` — see `recipes/README.md` for the format. Each recipe has a description, a JS expression, and an `async` flag.
353+
318354
---
319355

320356
## 5. State Paths & Routes

package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,12 @@
130130
"gen-bundle:ios": "NODE_OPTIONS='--max-old-space-size=8192' yarn react-native bundle --platform ios --dev false --entry-file index.js --bundle-output ios/main.jsbundle",
131131
"gen-bundle:android": "yarn react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/main.jsbundle",
132132
"circular:deps": "dpdm ./app/* --circular --exit-code circular:1 --warning=false",
133-
"generate-icons": "yarn ts-node app/component-library/components/Icons/Icon/scripts/generate-assets.ts"
133+
"generate-icons": "yarn ts-node app/component-library/components/Icons/Icon/scripts/generate-assets.ts",
134+
"a:start": "scripts/perps/agentic/start-metro.sh",
135+
"a:stop": "scripts/perps/agentic/stop-metro.sh",
136+
"a:status": "scripts/perps/agentic/app-state.sh status",
137+
"a:reload": "scripts/perps/agentic/reload-metro.sh",
138+
"a:navigate": "scripts/perps/agentic/app-navigate.sh"
134139
},
135140
"lint-staged": {
136141
"*.{js,jsx,ts,tsx}": [

scripts/perps/agentic/app-state.sh

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Query the running MetaMask app state via CDP bridge.
33
#
44
# Usage:
5+
# scripts/perps/agentic/app-state.sh status # Route + selected account snapshot
56
# scripts/perps/agentic/app-state.sh route # Current route name
67
# scripts/perps/agentic/app-state.sh state engine # Redux state at path
78
# scripts/perps/agentic/app-state.sh state engine.backgroundState.NetworkController
@@ -10,6 +11,11 @@
1011
# scripts/perps/agentic/app-state.sh nav # Full navigation state
1112
# scripts/perps/agentic/app-state.sh can-go-back # Can navigate back?
1213
# scripts/perps/agentic/app-state.sh go-back # Navigate back
14+
# scripts/perps/agentic/app-state.sh accounts # List all accounts
15+
# scripts/perps/agentic/app-state.sh account # Get selected account
16+
# scripts/perps/agentic/app-state.sh switch-account <addr> # Switch account
17+
# scripts/perps/agentic/app-state.sh recipe perps/positions # Run a recipe
18+
# scripts/perps/agentic/app-state.sh recipe --list # List recipes
1319

1420
set -euo pipefail
1521

@@ -19,6 +25,9 @@ COMMAND="${1:-route}"
1925
shift || true
2026

2127
case "$COMMAND" in
28+
status)
29+
node scripts/perps/agentic/cdp-bridge.js status
30+
;;
2231
route)
2332
node scripts/perps/agentic/cdp-bridge.js get-route
2433
;;
@@ -40,17 +49,35 @@ case "$COMMAND" in
4049
go-back)
4150
node scripts/perps/agentic/cdp-bridge.js go-back
4251
;;
52+
accounts)
53+
node scripts/perps/agentic/cdp-bridge.js list-accounts
54+
;;
55+
account)
56+
node scripts/perps/agentic/cdp-bridge.js get-selected-account
57+
;;
58+
switch-account)
59+
node scripts/perps/agentic/cdp-bridge.js switch-account "$@"
60+
;;
61+
recipe)
62+
node scripts/perps/agentic/cdp-bridge.js recipe "$@"
63+
;;
4364
*)
4465
echo "Usage: app-state.sh <command> [args...]"
4566
echo ""
4667
echo "Commands:"
68+
echo " status Route + selected account snapshot"
4769
echo " route Current route name and params"
4870
echo " state <dot.path> Redux state at the given path"
4971
echo " nav Full navigation state tree"
5072
echo " eval <expression> Evaluate arbitrary JS in app context (sync)"
5173
echo " eval-async <expr> Evaluate async/Promise expression"
5274
echo " can-go-back Check if navigation can go back"
5375
echo " go-back Navigate back"
76+
echo " accounts List all accounts"
77+
echo " account Get the currently selected account"
78+
echo " switch-account <addr> Switch to account by address"
79+
echo " recipe <team/name> Run a recipe (e.g. perps/positions)"
80+
echo " recipe --list List all available recipes"
5481
exit 1
5582
;;
5683
esac

0 commit comments

Comments
 (0)