@deeeed/metamask-harness 0.80.0 → 0.82.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/adapters/manifest.json +48 -64
  3. package/adapters/{terminal → web-dapp}/cleanup.mjs +9 -9
  4. package/adapters/{terminal → web-dapp}/inject.mjs +14 -14
  5. package/adapters/{terminal → web-dapp}/launch.mjs +27 -24
  6. package/adapters/web-dapp/lib/blocked-hosts.mjs +24 -0
  7. package/adapters/{terminal → web-dapp}/lib/cdp-client.mjs +1 -1
  8. package/adapters/{terminal → web-dapp}/lib/display.mjs +1 -1
  9. package/adapters/{terminal → web-dapp}/lib/hud.mjs +1 -1
  10. package/adapters/web-dapp/lib/policy.mjs +79 -0
  11. package/adapters/{terminal → web-dapp}/lib/processes.mjs +8 -8
  12. package/adapters/{terminal → web-dapp}/lib/readiness.mjs +32 -34
  13. package/adapters/{terminal → web-dapp}/stop.mjs +5 -4
  14. package/adapters/{terminal → web-dapp}/verify.mjs +9 -6
  15. package/adapters/{terminal → web-dapp}/wallet-host.mjs +44 -227
  16. package/dist/adapters/core/actions.js +4 -1
  17. package/dist/adapters/extension/actions.js +6 -2
  18. package/dist/adapters/extension/network-observer.js +13 -0
  19. package/dist/adapters/extension/run.js +115 -31
  20. package/dist/adapters/mobile/actions.js +4 -1
  21. package/dist/adapters/mobile/network-observer.js +68 -0
  22. package/dist/adapters/mobile/performance-observer.js +1 -3
  23. package/dist/adapters/mobile/run.js +32 -9
  24. package/dist/adapters/perps-order-inputs.js +36 -0
  25. package/dist/adapters/plugin-surface.js +98 -0
  26. package/dist/adapters/proof-failure.js +11 -0
  27. package/dist/adapters/surface.js +1 -1
  28. package/dist/adapters/{terminal → web-dapp}/actions.js +11 -8
  29. package/dist/adapters/web-dapp/console-capture.js +63 -0
  30. package/dist/adapters/web-dapp/doctor-checks.js +33 -0
  31. package/dist/adapters/{terminal → web-dapp}/launch.js +21 -19
  32. package/dist/adapters/web-dapp/overlay.js +21 -0
  33. package/dist/adapters/web-dapp/readiness.js +10 -0
  34. package/dist/adapters/{terminal → web-dapp}/run.js +22 -17
  35. package/dist/adapters/{terminal → web-dapp}/stop.js +5 -4
  36. package/dist/adapters/web-dapp/surface.js +149 -0
  37. package/dist/adapters.js +20 -4
  38. package/dist/cli.js +34 -6
  39. package/dist/command-contract.js +29 -7
  40. package/dist/commands/check.js +32 -8
  41. package/dist/commands/completion-candidates.js +5 -5
  42. package/dist/commands/doctor.js +19 -2
  43. package/dist/commands/farmslot-ready.js +1 -0
  44. package/dist/commands/help.js +1 -1
  45. package/dist/commands/launch/device.js +5 -5
  46. package/dist/commands/self-test.js +10 -5
  47. package/dist/engine.js +47 -0
  48. package/dist/funding-execution-context.js +81 -0
  49. package/dist/manifest.js +48 -24
  50. package/dist/mm-harness-cli.js +20 -3
  51. package/dist/performance-observation.js +7 -15
  52. package/dist/recipe-assistance-context.js +6 -7
  53. package/dist/runner.js +28 -8
  54. package/docs/CONTRIBUTING.md +4 -2
  55. package/docs/RECIPES.md +31 -3
  56. package/docs/cli-contract.md +5 -3
  57. package/library/actions/core/perps/_controller.mjs +24 -28
  58. package/library/actions/core/perps/_rewards.mjs +186 -0
  59. package/library/actions/core/perps/close_positions.mjs +1 -1
  60. package/library/actions/core/perps/place_order.mjs +167 -45
  61. package/library/actions/core/perps/quote_fees.mjs +7 -4
  62. package/library/actions/core/wallet/list_accounts.mjs +1 -1
  63. package/library/actions/extension/perps/close_visible_position.mjs +5 -0
  64. package/library/actions/extension/perps/edit_margin.mjs +4 -0
  65. package/library/actions/extension/perps/mutation-receipt.mjs +95 -3
  66. package/library/actions/extension/perps/perps.mjs +320 -3
  67. package/library/actions/extension/perps/place_order.mjs +3 -1
  68. package/library/actions/extension/perps/update_position_tpsl.mjs +4 -0
  69. package/library/actions/extension/platform/cdp.mjs +71 -3
  70. package/library/actions/mobile/perps/perps.mjs +216 -11
  71. package/library/actions/mobile/perps/place_order.mjs +3 -1
  72. package/library/actions/shared/console-collector.mjs +1 -1
  73. package/library/actions/shared/perps/order-policy.mjs +177 -0
  74. package/library/actions/{core/wallet/_fixture-accounts.mjs → shared/wallet/fixture-accounts.mjs} +18 -3
  75. package/library/actions/{terminal → web-dapp}/app/assert_no_console_errors.mjs +4 -4
  76. package/library/actions/{terminal → web-dapp}/app/launch.mjs +8 -7
  77. package/library/actions/{terminal → web-dapp}/platform/hud.mjs +7 -6
  78. package/library/actions/{terminal → web-dapp}/platform/page.mjs +13 -12
  79. package/library/actions/{terminal → web-dapp}/platform/runtime.mjs +16 -12
  80. package/library/actions/web-dapp/ui/navigate.mjs +24 -0
  81. package/library/actions/web-dapp/wallet/_signature-log.mjs +32 -0
  82. package/library/actions/{terminal → web-dapp}/wallet/assert_signatures.mjs +8 -14
  83. package/library/actions/{terminal → web-dapp}/wallet/list_accounts.mjs +2 -2
  84. package/library/actions/web-dapp/wallet/read_signatures.mjs +38 -0
  85. package/library/manifests/core.action-manifest.json +6 -2
  86. package/library/manifests/extension.action-manifest.json +4 -0
  87. package/library/manifests/mobile.action-manifest.json +12 -0
  88. package/library/manifests/{terminal.action-manifest.json → web-dapp.action-manifest.json} +18 -702
  89. package/package.json +6 -6
  90. package/site/assets/help-recipes.json +1 -0
  91. package/adapters/terminal/lib/mainnet-guard.mjs +0 -177
  92. package/adapters/terminal/lib/origin.mjs +0 -30
  93. package/adapters/terminal/lib/page-script.mjs +0 -246
  94. package/adapters/terminal/lib/strict-wallet.mjs +0 -138
  95. package/dist/adapters/terminal/console-capture.js +0 -59
  96. package/dist/adapters/terminal/doctor-checks.js +0 -22
  97. package/dist/adapters/terminal/overlay.js +0 -18
  98. package/dist/adapters/terminal/readiness.js +0 -11
  99. package/dist/adapters/terminal/surface.js +0 -133
  100. package/dist/commands/call.js +0 -671
  101. package/dist/commands/list-executables.js +0 -245
  102. package/dist/commands/manifest.js +0 -477
  103. package/dist/commands/run-engine.js +0 -1301
  104. package/dist/commands/run.js +0 -730
  105. package/dist/discovery.js +0 -15
  106. package/dist/metamask-action-validation.js +0 -68
  107. package/dist/network-observation.js +0 -290
  108. package/library/actions/terminal/perps/_venue.mjs +0 -242
  109. package/library/actions/terminal/perps/assert_orders.mjs +0 -54
  110. package/library/actions/terminal/perps/assert_positions.mjs +0 -40
  111. package/library/actions/terminal/perps/read_orders.mjs +0 -26
  112. package/library/actions/terminal/perps/read_positions.mjs +0 -25
  113. package/library/actions/terminal/perps/read_snapshot.mjs +0 -40
  114. package/library/actions/terminal/perps/revoke_agent.mjs +0 -58
  115. package/library/actions/terminal/perps/teardown_state.mjs +0 -104
  116. package/library/actions/terminal/ui/navigate.mjs +0 -31
  117. package/library/actions/terminal/wallet/_signature-log.mjs +0 -177
  118. package/library/actions/terminal/wallet/read_signatures.mjs +0 -37
@@ -1,14 +1,15 @@
1
- // Explicit lifecycle step: start (or reuse) the terminal slot browser with the
1
+ // Explicit lifecycle step: start (or reuse) the web-dapp slot browser with the
2
2
  // requested signer (MetaMask extension or injected strict wallet) and fixture
3
3
  // account, and the wallet host that records wallet requests. A fresh_profile
4
4
  // start forgets the app's stored agent key and the extension's dapp permission.
5
5
 
6
- import { appPort, cdpPort, isDirectRun, resolveSigner, runAdapter } from '../platform/runtime.mjs';
7
- import { launchTerminalBrowser } from '../../../../adapters/terminal/launch.mjs';
6
+ import { appPort, cdpPort, isDirectRun, resolveSigner, runAdapter, webDappPolicy } from '../platform/runtime.mjs';
7
+ import { launchWebDappBrowser } from '../../../../adapters/web-dapp/launch.mjs';
8
8
 
9
9
  export async function launch(input) {
10
10
  const node = input.node ?? {};
11
- const state = await launchTerminalBrowser({
11
+ const policy = webDappPolicy();
12
+ const state = await launchWebDappBrowser({
12
13
  target: input.context?.projectRoot,
13
14
  'cdp-port': cdpPort(input),
14
15
  'app-port': appPort(input),
@@ -22,8 +23,8 @@ export async function launch(input) {
22
23
  'slow-mo': node.slow_mo_ms,
23
24
  network: node.network,
24
25
  'mainnet-confirmation': node.mainnet_confirmation,
25
- 'start-chain': node.start_chain ?? 42161,
26
- });
26
+ 'start-chain': node.start_chain ?? policy.startChain,
27
+ }, process.env);
27
28
  return {
28
29
  action: input.action,
29
30
  status: 'ready',
@@ -38,7 +39,7 @@ export async function launch(input) {
38
39
  slowMoMs: state.slowMoMs ?? 0,
39
40
  extension: state.extension ? { id: state.extension.id, version: state.extension.version } : null,
40
41
  freshProfile: state.freshProfile,
41
- proofPath: 'terminal-browser-launch',
42
+ proofPath: `${policy.adapterId}-browser-launch`,
42
43
  };
43
44
  }
44
45
 
@@ -1,11 +1,12 @@
1
- // app.hud for the terminal: fold the runner's progress update into
1
+ // app.hud for web-dapp: fold the runner's progress update into
2
2
  // <runtime>/hud.json and draw it on the app tab now. The wallet host redraws
3
3
  // the same state after every navigation. The HUD is advisory: a missing
4
4
  // browser or a busy tab skips the update instead of failing the recipe.
5
5
 
6
- import { clearHudState, hudRenderExpression, nextHudState, readHudState, writeHudState } from '../../../../adapters/terminal/lib/hud.mjs';
7
- import { isAppUrl } from '../../../../adapters/terminal/lib/origin.mjs';
8
- import { appOrigin, cdpPort, readBrowserState, terminalRuntimeDir } from './runtime.mjs';
6
+ import { isAppUrl } from '@farmslot/adapter-web/origin';
7
+
8
+ import { clearHudState, hudRenderExpression, nextHudState, readHudState, writeHudState } from '../../../../adapters/web-dapp/lib/hud.mjs';
9
+ import { appOrigin, cdpPort, readBrowserState, webDappRuntimeDir } from './runtime.mjs';
9
10
  import { importRecipeHarnessRuntimeCdp } from '../../harness-exports.mjs';
10
11
 
11
12
  // --hud show draws in any mode; --hud auto (the default) only for a visible
@@ -46,14 +47,14 @@ async function drawOnAppTabs(input, expression, timeoutMs = 3000) {
46
47
  return results;
47
48
  }
48
49
 
49
- export async function showTerminalHud(input, { hudPolicy = 'auto' } = {}) {
50
+ export async function showAppHud(input, { hudPolicy = 'auto' } = {}) {
50
51
  const node = input.node ?? {};
51
52
  const projectRoot = input.context?.projectRoot;
52
53
  const nodeId = input.context?.nodeId ?? '';
53
54
  const browserState = projectRoot ? await readBrowserState(projectRoot) : null;
54
55
  if (!browserState) return { hud: false, skipped: true, reason: 'no slot browser yet' };
55
56
  if (!hudVisible(hudPolicy, browserState)) return { hud: false, skipped: true, reason: 'headless browser (--hud show draws anyway)' };
56
- const runtimeDir = terminalRuntimeDir(projectRoot);
57
+ const runtimeDir = webDappRuntimeDir(projectRoot);
57
58
  let state = null;
58
59
  if (node.clear === true) {
59
60
  clearHudState(runtimeDir);
@@ -1,4 +1,4 @@
1
- // Select the CDP page a terminal UI action drives: the app tab (default) or the
1
+ // Select the CDP page a web-dapp UI action drives: the app tab (default) or the
2
2
  // MetaMask wallet surface (side panel / notification / popup) that shows the
3
3
  // pending dapp request. Pages are never brought to front unless
4
4
  // FARMSLOT_FOCUS_BROWSER=1, as in the Extension adapter: Page.bringToFront
@@ -6,10 +6,11 @@
6
6
  // app tab is the only tab of its window (the wallet host closes the others),
7
7
  // so it stays the window's active tab and keeps painting without it.
8
8
 
9
- import { resolveSlowMo } from '../../../../adapters/terminal/lib/display.mjs';
10
- import { HUD_ELEMENT } from '../../../../adapters/terminal/lib/hud.mjs';
11
- import { isAppUrl } from '../../../../adapters/terminal/lib/origin.mjs';
12
- import { cdpListenerPids } from '../../../../adapters/terminal/lib/processes.mjs';
9
+ import { isAppUrl } from '@farmslot/adapter-web/origin';
10
+
11
+ import { resolveSlowMo } from '../../../../adapters/web-dapp/lib/display.mjs';
12
+ import { HUD_ELEMENT } from '../../../../adapters/web-dapp/lib/hud.mjs';
13
+ import { cdpListenerPids } from '../../../../adapters/web-dapp/lib/processes.mjs';
13
14
  import { appOrigin, cdpPort, readBrowserState } from './runtime.mjs';
14
15
  import { importRecipeHarnessRuntimeCdp } from '../../harness-exports.mjs';
15
16
 
@@ -40,7 +41,7 @@ export function pickAppTarget(targets, origin) {
40
41
  return pages.find((target) => isAppUrl(target.url, origin)) ?? pages.find((target) => target.url === 'about:blank') ?? null;
41
42
  }
42
43
 
43
- export async function selectTerminalTarget(input) {
44
+ export async function selectAppTarget(input) {
44
45
  const node = input.node ?? {};
45
46
  const port = cdpPort(input);
46
47
  const surface = resolveSurface(node);
@@ -74,7 +75,7 @@ export async function selectTerminalTarget(input) {
74
75
  // the DOM instead.
75
76
  class WalletSurfacePage extends CdpWebPage {
76
77
  evaluate(expression) {
77
- return this.evaluateInIsolatedWorld(expression, 'mm-terminal-wallet-surface');
78
+ return this.evaluateInIsolatedWorld(expression, 'web-dapp-wallet-surface');
78
79
  }
79
80
  }
80
81
 
@@ -88,7 +89,7 @@ export function hudVisibilityExpression(visible) {
88
89
  return `(() => { const host = document.documentElement.querySelector(':scope > ${HUD_ELEMENT}'); if (!host) return false; host.style.visibility = ${JSON.stringify(visible ? '' : 'hidden')}; return true; })()`;
89
90
  }
90
91
 
91
- class TerminalAppPage extends CdpWebPage {
92
+ class AppPage extends CdpWebPage {
92
93
  async screenshot(context, relPath, metadata = {}) {
93
94
  const hidden = await this.evaluate(hudVisibilityExpression(false)).catch(() => false);
94
95
  try {
@@ -143,7 +144,7 @@ class TerminalAppPage extends CdpWebPage {
143
144
  }
144
145
  const top = document.elementFromPoint(rect.left + rect.width / 2, rect.top + rect.height / 2);
145
146
  return { error: 'Target is fully covered by ' + (top ? top.tagName.toLowerCase() : 'nothing') + ': ' + ${JSON.stringify(selector)} };
146
- })()`, 'mm-terminal-click');
147
+ })()`, 'web-dapp-click');
147
148
  }
148
149
 
149
150
  async setInput(selector, value) {
@@ -216,14 +217,14 @@ export function assertAppListenersUnchanged(state, current) {
216
217
 
217
218
  const PAGE_DEPS = Object.freeze({
218
219
  listeners: (port) => cdpListenerPids(port),
219
- select: (input) => selectTerminalTarget(input),
220
+ select: (input) => selectAppTarget(input),
220
221
  connect: (url) => CdpSession.connect(url),
221
222
  browserState: (projectRoot) => readBrowserState(projectRoot),
222
223
  sleep: (ms) => sleep(ms),
223
224
  env: process.env,
224
225
  });
225
226
 
226
- export async function withTerminalPage(input, callback, deps = PAGE_DEPS) {
227
+ export async function withAppPage(input, callback, deps = PAGE_DEPS) {
227
228
  const state = input.context?.projectRoot ? await deps.browserState(input.context.projectRoot) : null;
228
229
  const delay = slowMoFor(state, deps.env);
229
230
  if (delay > 0) await deps.sleep(delay);
@@ -236,7 +237,7 @@ export async function withTerminalPage(input, callback, deps = PAGE_DEPS) {
236
237
  await session.call('Page.enable');
237
238
  await session.call('Runtime.enable');
238
239
  if (focusBrowserRequested(deps.env)) await session.call('Page.bringToFront');
239
- const page = surface === 'wallet' ? new WalletSurfacePage(session) : new TerminalAppPage(session);
240
+ const page = surface === 'wallet' ? new WalletSurfacePage(session) : new AppPage(session);
240
241
  const result = await callback(page);
241
242
  return result && typeof result === 'object' && !Array.isArray(result)
242
243
  ? { ...result, surface, cdpPort: port, targetUrl: target.url.replace(/0x[0-9a-fA-F]{40}/gu, '<address>') }
@@ -1,6 +1,6 @@
1
- // Resolve the Web Terminal slot runtime: ports, runtime paths, browser state and
2
- // wallet-fixture accounts. Key material is only ever returned as a viem account
3
- // object; callers must never log or persist it.
1
+ // Resolve the web-dapp slot runtime: the venue policy, ports, runtime paths,
2
+ // browser state and wallet-fixture accounts. Key material is only ever
3
+ // returned as a viem account object; callers must never log or persist it.
4
4
 
5
5
  import { realpathSync } from 'node:fs';
6
6
  import { readFile, writeFile } from 'node:fs/promises';
@@ -11,6 +11,9 @@ import { mnemonicToAccount, privateKeyToAccount } from 'viem/accounts';
11
11
 
12
12
  import { recipeRuntimeDir } from '@farmslot/recipe-cli/harness';
13
13
  import { walletFixturePath } from '../../harness-exports.mjs';
14
+ import { POLICY_ENV, webDappAdapterId, webDappPolicy } from '../../../../adapters/web-dapp/lib/policy.mjs';
15
+
16
+ export { POLICY_ENV, webDappAdapterId, webDappPolicy };
14
17
 
15
18
  export const SIGNER_MODES = Object.freeze(['extension', 'injected']);
16
19
  export const DEFAULT_ACCOUNT = 'dev1';
@@ -26,12 +29,13 @@ export function canonicalPath(target) {
26
29
  }
27
30
  }
28
31
 
29
- export function terminalRuntimeDir(projectRoot) {
30
- return path.join(canonicalPath(projectRoot), recipeRuntimeDir(), 'terminal');
32
+ // <checkout>/<runtime>/<adapter id>, e.g. temp/recipe/runtime/terminal.
33
+ export function webDappRuntimeDir(projectRoot) {
34
+ return path.join(canonicalPath(projectRoot), recipeRuntimeDir(), webDappPolicy().adapterId);
31
35
  }
32
36
 
33
- export function terminalRuntimePath(projectRoot, ...segments) {
34
- return path.join(terminalRuntimeDir(projectRoot), ...segments);
37
+ export function webDappRuntimePath(projectRoot, ...segments) {
38
+ return path.join(webDappRuntimeDir(projectRoot), ...segments);
35
39
  }
36
40
 
37
41
  function portFrom(raw, label) {
@@ -53,7 +57,7 @@ export function appPort(input = {}, env = process.env) {
53
57
  portFrom(env.WATCHER_PORT, 'WATCHER_PORT');
54
58
  if (!port) {
55
59
  throw new Error(
56
- 'terminal adapter requires the slot dev-server port.\n' +
60
+ `${webDappPolicy().adapterId} adapter requires the slot dev-server port.\n` +
57
61
  'Next: pass --watcher-port <port> (Farmslot: {{port}}) or set TERMINAL_APP_PORT.',
58
62
  );
59
63
  }
@@ -71,7 +75,7 @@ export function cdpPort(input = {}, env = process.env) {
71
75
  portFrom(env.CDP_PORT, 'CDP_PORT');
72
76
  if (!port) {
73
77
  throw new Error(
74
- 'terminal adapter requires a per-slot browser CDP port.\n' +
78
+ `${webDappPolicy().adapterId} adapter requires a per-slot browser CDP port.\n` +
75
79
  'Next: pass --cdp-port <port> (Farmslot: {{cdp_port}}).',
76
80
  );
77
81
  }
@@ -92,7 +96,7 @@ export async function writePrivateJson(file, value) {
92
96
  }
93
97
 
94
98
  export async function readBrowserState(projectRoot) {
95
- return readJsonFile(terminalRuntimePath(projectRoot, 'browser.json'));
99
+ return readJsonFile(webDappRuntimePath(projectRoot, 'browser.json'));
96
100
  }
97
101
 
98
102
  export function resolveSigner(raw) {
@@ -123,7 +127,7 @@ export async function loadWalletFixture(projectRoot, env = process.env) {
123
127
  if (!fixture) {
124
128
  throw new Error(
125
129
  `wallet fixture not found at ${file}.\n` +
126
- 'Next: mm-harness install --adapter terminal --target <checkout> with RECIPE_WALLET_FIXTURE set, or copy the fixture there (0600).',
130
+ `Next: mm-harness install --adapter ${webDappPolicy().adapterId} --target <checkout> with RECIPE_WALLET_FIXTURE set, or copy the fixture there (0600).`,
127
131
  );
128
132
  }
129
133
  if (!Array.isArray(fixture.accounts) || fixture.accounts.length === 0) {
@@ -195,7 +199,7 @@ export async function runAdapter(callback) {
195
199
  const output = await callback(input);
196
200
  await writeFile(input.outputPath, `${JSON.stringify(output, null, 2)}\n`);
197
201
  } catch (error) {
198
- process.stderr.write(`[terminal] ${input.action} failed: ${error?.stack ?? error}\n`);
202
+ process.stderr.write(`[${webDappAdapterId()}] ${input.action} failed: ${error?.stack ?? error}\n`);
199
203
  process.exitCode = 1;
200
204
  }
201
205
  }
@@ -0,0 +1,24 @@
1
+ // Navigate the app tab to an explicit app path, or a product page the venue
2
+ // policy names (the Terminal's order/<MARKET>, portfolio, home), and wait for
3
+ // the document to be ready.
4
+
5
+ import { appOrigin, isDirectRun, runAdapter, webDappPolicy } from '../platform/runtime.mjs';
6
+ import { withAppPage } from '../platform/page.mjs';
7
+
8
+ export function pathFor(node = {}, policy = webDappPolicy()) {
9
+ if (typeof node.path === 'string' && node.path.startsWith('/')) return node.path;
10
+ return policy.pagePath(node);
11
+ }
12
+
13
+ export async function navigate(input) {
14
+ const node = input.node ?? {};
15
+ const url = `${appOrigin(input)}${pathFor(node)}`;
16
+ return withAppPage({ ...input, node: { ...node, surface: 'app' } }, async (page) => {
17
+ await page.navigate(url, Number(node.timeout_ms ?? 120000));
18
+ await page.waitForDocumentReady({ expectedUrl: url, timeoutMs: Number(node.timeout_ms ?? 120000) });
19
+ if (node.settle_ms) await new Promise((resolve) => setTimeout(resolve, Number(node.settle_ms)));
20
+ return { action: input.action, url, page: node.page ?? 'order', market: node.market ?? null };
21
+ });
22
+ }
23
+
24
+ if (isDirectRun(import.meta.url)) runAdapter((input) => navigate(input));
@@ -0,0 +1,32 @@
1
+ // The wallet request log: where the wallet host writes it, and what the venue
2
+ // forbids in it. Reading, windowing and asserting the log are
3
+ // @farmslot/adapter-web/dapp's; the venue's rules come from its policy
4
+ // (`signatureLog`, the Terminal's L1 and mainnet rules).
5
+
6
+ import { webDappPolicy, webDappRuntimePath } from '../platform/runtime.mjs';
7
+
8
+ export function signatureLog(projectRoot) {
9
+ return {
10
+ logFile: webDappRuntimePath(projectRoot, 'wallet-requests.jsonl'),
11
+ cursorFile: webDappRuntimePath(projectRoot, 'wallet-requests.cursor.json'),
12
+ };
13
+ }
14
+
15
+ // What the venue forbids in the log (a SignaturePolicy), e.g. the Terminal's L1 and mainnet rules.
16
+ export function signaturePolicy() {
17
+ return webDappPolicy().signatureLog ?? {};
18
+ }
19
+
20
+ // The id the actions report their evidence under, e.g. terminal-wallet-request-log.
21
+ export function evidenceId(suffix) {
22
+ return `${webDappPolicy().adapterId}-${suffix}`;
23
+ }
24
+
25
+ // Evidence goes to <artifacts>/<adapter id>-wallet/<node>-signature-log.json.
26
+ export function logArtifactTarget(input) {
27
+ return {
28
+ artifactsDir: input.context?.artifactsDir,
29
+ nodeId: input.context?.nodeId ?? input.action,
30
+ folder: evidenceId('wallet'),
31
+ };
32
+ }
@@ -3,22 +3,16 @@
3
3
  // payloads, active-chain matching and, in extension mode, confirmations shown.
4
4
  // The full window is retained as evidence whether or not the assertion holds.
5
5
 
6
+ import { awaitSignatureLog, writeLogArtifact } from '@farmslot/adapter-web/dapp';
7
+
6
8
  import { isDirectRun, runAdapter } from '../platform/runtime.mjs';
7
- import { evaluateSignatureLog, windowSinceCursor, writeLogArtifact } from './_signature-log.mjs';
9
+ import { evidenceId, logArtifactTarget, signatureLog, signaturePolicy } from './_signature-log.mjs';
8
10
 
9
11
  export async function assertSignatures(input) {
10
12
  const node = input.node ?? {};
11
- const projectRoot = input.context?.projectRoot;
12
- const deadline = Date.now() + Math.max(0, Number(node.timeout_ms ?? 0));
13
- let window = await windowSinceCursor(projectRoot);
14
- let result = evaluateSignatureLog(window.entries, node);
15
- // Entries arrive asynchronously from the wallet host; allow a short settle.
16
- while (!result.ok && Date.now() < deadline) {
17
- await new Promise((resolve) => setTimeout(resolve, 500));
18
- window = await windowSinceCursor(projectRoot);
19
- result = evaluateSignatureLog(window.entries, node);
20
- }
21
- const artifacts = await writeLogArtifact(input, {
13
+ // Entries arrive asynchronously from the wallet host; timeout_ms allows a short settle.
14
+ const { window, result } = await awaitSignatureLog(signatureLog(input.context?.projectRoot), node, signaturePolicy());
15
+ const artifacts = await writeLogArtifact(logArtifactTarget(input), {
22
16
  cursor: window.cursor,
23
17
  total: window.total,
24
18
  expectations: node,
@@ -30,13 +24,13 @@ export async function assertSignatures(input) {
30
24
  }
31
25
  return {
32
26
  action: input.action,
33
- source: 'terminal-wallet-request-log',
27
+ source: evidenceId('wallet-request-log'),
34
28
  cursor: window.cursor,
35
29
  count: window.entries.length,
36
30
  summary: result.summary,
37
31
  entries: window.entries,
38
32
  artifacts,
39
- proofPath: 'terminal-wallet-request-log',
33
+ proofPath: evidenceId('wallet-request-log'),
40
34
  };
41
35
  }
42
36
 
@@ -1,7 +1,7 @@
1
1
  // List the wallet-fixture account names and addresses available to the slot
2
2
  // browser, marking the one the running browser uses. Addresses only.
3
3
 
4
- import { isDirectRun, loadWalletFixture, readBrowserState, runAdapter, viemAccountFromEntry } from '../platform/runtime.mjs';
4
+ import { isDirectRun, loadWalletFixture, readBrowserState, runAdapter, viemAccountFromEntry, webDappPolicy } from '../platform/runtime.mjs';
5
5
 
6
6
  export async function listAccounts(input) {
7
7
  const projectRoot = input.context?.projectRoot;
@@ -20,7 +20,7 @@ export async function listAccounts(input) {
20
20
  accounts,
21
21
  count: accounts.length,
22
22
  redacted: true,
23
- proofPath: 'terminal-wallet-accounts',
23
+ proofPath: `${webDappPolicy().adapterId}-wallet-accounts`,
24
24
  };
25
25
  }
26
26
 
@@ -0,0 +1,38 @@
1
+ // Return the wallet request log since the last reset and retain it as run
2
+ // evidence. reset=true moves the window start to the end of the log so the next
3
+ // read covers only what follows (an explicit setup step, recorded in the trace).
4
+
5
+ import { resetWindow, summarize, windowSinceCursor, writeLogArtifact } from '@farmslot/adapter-web/dapp';
6
+
7
+ import { isDirectRun, runAdapter } from '../platform/runtime.mjs';
8
+ import { evidenceId, logArtifactTarget, signatureLog, signaturePolicy } from './_signature-log.mjs';
9
+
10
+ export async function readSignatures(input) {
11
+ const log = signatureLog(input.context?.projectRoot);
12
+ if (input.node?.reset === true) {
13
+ const { cursor, previousEntries } = await resetWindow(log);
14
+ return {
15
+ action: input.action,
16
+ source: evidenceId('wallet-request-log'),
17
+ reset: true,
18
+ cursor,
19
+ previousEntries,
20
+ proofPath: evidenceId('wallet-request-log'),
21
+ };
22
+ }
23
+ const { cursor, entries, total } = await windowSinceCursor(log);
24
+ const summary = summarize(entries, signaturePolicy());
25
+ const artifacts = await writeLogArtifact(logArtifactTarget(input), { cursor, total, summary, entries });
26
+ return {
27
+ action: input.action,
28
+ source: evidenceId('wallet-request-log'),
29
+ cursor,
30
+ count: entries.length,
31
+ summary,
32
+ entries,
33
+ artifacts,
34
+ proofPath: evidenceId('wallet-request-log'),
35
+ };
36
+ }
37
+
38
+ if (isDirectRun(import.meta.url)) runAdapter((input) => readSignatures(input));
@@ -819,7 +819,7 @@
819
819
  "execution_capabilities": []
820
820
  },
821
821
  "metamask.perps.quote_fees": {
822
- "description": "core Quote an order's fees through the perps controller's calculateFees (HyperLiquid, testnet by default, read-only): protocol and MetaMask fee rates and amounts (plus the winning fee source on a core that reports it), with an optional rewards discount supplied as RewardsController:getPerpsDiscountForAccount.",
822
+ "description": "core Quote an order's fees through the perps controller's calculateFees (HyperLiquid, testnet by default, read-only): protocol and MetaMask fee rates and amounts (plus the winning fee source on a core that reports it), with the rewards discount the headless RewardsController answers: the rewards API's answer for the selected wallet fixture account, the calls Mobile makes (UAT by default, like Mobile dev builds; CORE_PERPS_REWARDS_API_URL selects another).",
823
823
  "schema": {
824
824
  "type": "object",
825
825
  "properties": {
@@ -852,7 +852,7 @@
852
852
  },
853
853
  "rewards_discount_bips": {
854
854
  "type": "integer",
855
- "description": "Rewards discount the headless RewardsController answers with, an integer in basis points from 0 through 10000 (6500 = 65%). Omitted: no rewards source, the controller's 'not known yet'."
855
+ "description": "Rewards discount the headless RewardsController answers with, an integer in basis points from 0 through 10000 (6500 = 65%). Replaces the rewards API's answer, to prove a discount the fixture accounts do not hold."
856
856
  },
857
857
  "account": {
858
858
  "type": "string",
@@ -1093,6 +1093,10 @@
1093
1093
  "type": "number",
1094
1094
  "default": 3
1095
1095
  },
1096
+ "close_immediately": {
1097
+ "type": "boolean",
1098
+ "description": "Close the opened position again inside this action and prove the market flat (market orders only; the market must start flat). Required on mainnet, with leverage 1 and notional at most $15 from a fixture account."
1099
+ },
1096
1100
  "account": {
1097
1101
  "type": "string",
1098
1102
  "description": "Wallet fixture account name (dev1, dev2, ...) or the 0x address of one; defaults to dev1."
@@ -4371,6 +4371,10 @@
4371
4371
  "type": "number",
4372
4372
  "default": 1
4373
4373
  },
4374
+ "close_immediately": {
4375
+ "type": "boolean",
4376
+ "description": "Close the opened position again inside this action and prove the market flat (market orders only; the market must start flat). Required on mainnet, with leverage 1 and notional at most $15 from a fixture account."
4377
+ },
4374
4378
  "provider": {
4375
4379
  "type": "string",
4376
4380
  "description": "Exact Perps provider required before background dispatch."
@@ -2775,6 +2775,18 @@
2775
2775
  "leverage": {
2776
2776
  "type": "number",
2777
2777
  "default": 3
2778
+ },
2779
+ "network": {
2780
+ "type": "string",
2781
+ "enum": [
2782
+ "mainnet",
2783
+ "testnet"
2784
+ ],
2785
+ "description": "Perps network the order must run on; checked against the live app. A mainnet order must declare mainnet."
2786
+ },
2787
+ "close_immediately": {
2788
+ "type": "boolean",
2789
+ "description": "Close the opened position again inside this action and prove the market flat (market orders only; the market must start flat). Required on mainnet, with leverage 1 and notional at most $15 from a fixture account."
2778
2790
  }
2779
2791
  },
2780
2792
  "additionalProperties": false