From 440514bca55844dc5791c8ef3436ee2d4d187993 Mon Sep 17 00:00:00 2001 From: Haardik H Date: Wed, 2 Sep 2026 10:32:26 -0400 Subject: [PATCH 01/12] chore(vibenet): keep validity demos unlisted Co-authored-by: OpenCode --- app/vibenet/demos/catalogue.test.ts | 5 +++-- app/vibenet/demos/catalogue.ts | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/vibenet/demos/catalogue.test.ts b/app/vibenet/demos/catalogue.test.ts index 12f029d9..a0ffd520 100644 --- a/app/vibenet/demos/catalogue.test.ts +++ b/app/vibenet/demos/catalogue.test.ts @@ -44,10 +44,11 @@ describe('DEMOS', () => { expect(new Set(hrefs).size).toBe(hrefs.length); }); - it('lists Validity Transactions as a top-level group', () => { - const validity = listedDemos().find((demo) => demo.href === '/vibenet/demos/validity'); + it('keeps Validity Transactions reachable but hidden from the top-level grid', () => { + const validity = DEMOS.find((demo) => demo.href === '/vibenet/demos/validity'); expect(validity?.title).toBe('Validity Transactions'); expect(validity?.children?.map((demo) => demo.title)).toEqual(['Conditional Swaps', 'Race the Agent']); + expect(listedDemos()).not.toContain(validity); }); }); diff --git a/app/vibenet/demos/catalogue.ts b/app/vibenet/demos/catalogue.ts index 6aa8b24e..83faa9db 100644 --- a/app/vibenet/demos/catalogue.ts +++ b/app/vibenet/demos/catalogue.ts @@ -67,6 +67,7 @@ export const DEMOS: DemoEntry[] = [ 'Build intent-like flows from ordinary account transactions', ], available: true, + listed: false, children: [ { href: '/vibenet/demos/validity/conditional-swaps', From b79a2417a5ad7576270848631701e1ecc80626f8 Mon Sep 17 00:00:00 2001 From: Haardik H Date: Wed, 2 Sep 2026 14:27:07 -0400 Subject: [PATCH 02/12] refactor(vibenet): simplify conditional withdrawal contract Co-authored-by: OpenCode --- .../lib/artifacts/ConditionalWithdrawal.json | 6 +-- .../lib/conditionalWithdrawal.test.ts | 29 +++++------ .../validity/lib/conditionalWithdrawal.ts | 17 ++----- .../lib/contracts/ConditionalWithdrawal.sol | 28 ++--------- .../race-the-agent/RaceTheAgentDemo.tsx | 49 +++++++++++++++++++ 5 files changed, 75 insertions(+), 54 deletions(-) diff --git a/app/vibenet/demos/validity/lib/artifacts/ConditionalWithdrawal.json b/app/vibenet/demos/validity/lib/artifacts/ConditionalWithdrawal.json index 9953d934..fbc08f9d 100644 --- a/app/vibenet/demos/validity/lib/artifacts/ConditionalWithdrawal.json +++ b/app/vibenet/demos/validity/lib/artifacts/ConditionalWithdrawal.json @@ -12,13 +12,11 @@ "source": "../contracts/ConditionalWithdrawal.sol", "abi": [ {"inputs":[{"internalType":"contract IERC20","name":"vibe","type":"address"}],"stateMutability":"nonpayable","type":"constructor"}, - {"inputs":[],"name":"ENABLED_SLOT","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"}, {"inputs":[],"name":"VIBE","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"}, {"inputs":[],"name":"WITHDRAWAL_AMOUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}, - {"inputs":[],"name":"enabled","outputs":[{"internalType":"bool","name":"value","type":"bool"}],"stateMutability":"view","type":"function"}, - {"inputs":[],"name":"flip","outputs":[{"internalType":"bool","name":"value","type":"bool"}],"stateMutability":"nonpayable","type":"function"}, + {"inputs":[],"name":"enabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}, {"inputs":[{"internalType":"bool","name":"value","type":"bool"}],"name":"setEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"}, {"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"} ], - "bytecode": "0x60a060405234801561000f575f80fd5b506040516103b03803806103b083398101604081905261002e9161003f565b6001600160a01b031660805261006c565b5f6020828403121561004f575f80fd5b81516001600160a01b0381168114610065575f80fd5b9392505050565b60805161032661008a5f395f8181608301526101d101526103265ff3fe608060405234801561000f575f80fd5b506004361061007a575f3560e01c8063328d8f7211610058578063328d8f72146101065780633ccfd60b14610127578063848606331461012f578063cde4efa91461013e575f80fd5b806304a3b7cd1461007e57806304c879d6146100c2578063238dafe0146100e4575b5f80fd5b6100a57f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b6100d65f805160206102d183398151915281565b6040519081526020016100b9565b5f805160206102d18339815191525415155b60405190151581526020016100b9565b610125610114366004610293565b5f805160206102d183398151915255565b005b610125610158565b6100d6670de0b6b3a764000081565b5f805160206102d1833981519152805415908190556100f6565b5f805160206102d1833981519152546101ae5760405162461bcd60e51b81526020600482015260136024820152721dda5d1a191c985dd85b08191a5cd8589b1959606a1b60448201526064015b60405180910390fd5b60405163a9059cbb60e01b8152336004820152670de0b6b3a764000060248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a9059cbb906044016020604051808303815f875af115801561021f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061024391906102b5565b6102815760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b60448201526064016101a5565b565b8015158114610290575f80fd5b50565b5f602082840312156102a3575f80fd5b81356102ae81610283565b9392505050565b5f602082840312156102c5575f80fd5b81516102ae8161028356fea91a9aee734204743335c443df931dcb220441d8aa6c1355dc61503a4bec3129a264697066735822122086d03dd0ac93876dcc7d8450420e92071a584f9b6452b41d07282203bd45a11f64736f6c63430008180033" + "bytecode": "0x60a060405234801561000f575f80fd5b5060405161032e38038061032e83398101604081905261002e9161003f565b6001600160a01b031660805261006c565b5f6020828403121561004f575f80fd5b81516001600160a01b0381168114610065575f80fd5b9392505050565b6080516102a461008a5f395f8181605e015261016f01526102a45ff3fe608060405234801561000f575f80fd5b5060043610610055575f3560e01c806304a3b7cd14610059578063238dafe01461009d578063328d8f72146100b95780633ccfd60b146100db57806384860633146100e3575b5f80fd5b6100807f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b5f546100a99060ff1681565b6040519015158152602001610094565b6100d96100c7366004610231565b5f805460ff1916911515919091179055565b005b6100d9610100565b6100f2670de0b6b3a764000081565b604051908152602001610094565b5f5460ff1661014c5760405162461bcd60e51b81526020600482015260136024820152721dda5d1a191c985dd85b08191a5cd8589b1959606a1b60448201526064015b60405180910390fd5b60405163a9059cbb60e01b8152336004820152670de0b6b3a764000060248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a9059cbb906044016020604051808303815f875af11580156101bd573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906101e19190610253565b61021f5760405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b6044820152606401610143565b565b801515811461022e575f80fd5b50565b5f60208284031215610241575f80fd5b813561024c81610221565b9392505050565b5f60208284031215610263575f80fd5b815161024c8161022156fea26469706673582212204d29e01f9634f2f7d9fe8f91c0bdd1dafbb7f3368af71bef06b019d1634aacf764736f6c63430008180033" } diff --git a/app/vibenet/demos/validity/lib/conditionalWithdrawal.test.ts b/app/vibenet/demos/validity/lib/conditionalWithdrawal.test.ts index ab7e4597..01077df4 100644 --- a/app/vibenet/demos/validity/lib/conditionalWithdrawal.test.ts +++ b/app/vibenet/demos/validity/lib/conditionalWithdrawal.test.ts @@ -1,4 +1,4 @@ -import { decodeFunctionData, keccak256, toBytes } from 'viem'; +import { decodeFunctionData } from 'viem'; import { describe, expect, it } from 'vitest'; import artifact from './artifacts/ConditionalWithdrawal.json'; @@ -14,7 +14,6 @@ import { conditionalWithdrawalFundingAmount, encodeConditionalWithdrawalFunding, encodeConditionalWithdraw, - encodeFlipConditionalWithdrawal, encodeSetConditionalWithdrawalEnabled, predictConditionalWithdrawal, } from './conditionalWithdrawal'; @@ -39,21 +38,19 @@ describe('conditional withdrawal contract', () => { expect(CONDITIONAL_WITHDRAWAL_SALT).toBe( '0x75dea569b8cc7d9ea45d7d95a5d6bed33e1e378a31715724342462f8226adc8b', ); - expect(predictConditionalWithdrawal(VIBE)).toBe('0x7AE1BFB6116D154a0a27961a5d19C544D02015a9'); + expect(predictConditionalWithdrawal(VIBE)).toBe('0xC655E339224d57B087A02Df7827A4068ae69aba1'); expect(predictConditionalWithdrawal(OTHER_VIBE)).not.toBe(predictConditionalWithdrawal(VIBE)); }); - it('pins the stable storage word and exact enabled=true EIP-8130 predicate', () => { - expect(CONDITIONAL_WITHDRAWAL_ENABLED_SLOT).toBe( - keccak256(toBytes('vibenet.validity.conditional-withdrawal.enabled.v1')), - ); - expect(CONDITIONAL_WITHDRAWAL_ENABLED_MASK).toBe((1n << 256n) - 1n); + it('reads bool public enabled from slot 0 in the EIP-8130 predicate', () => { + expect(CONDITIONAL_WITHDRAWAL_ENABLED_SLOT).toBe(0n); + expect(CONDITIONAL_WITHDRAWAL_ENABLED_MASK).toBe(0xffn); expect(conditionalWithdrawalEnabledPredicate(WITHDRAWAL)).toEqual({ type: 'storage', params: { address: WITHDRAWAL, - slot: CONDITIONAL_WITHDRAWAL_ENABLED_SLOT, - mask: toWord((1n << 256n) - 1n), + slot: toWord(0n), + mask: toWord(0xffn), op: '=', value: toWord(1n), }, @@ -61,6 +58,14 @@ describe('conditional withdrawal contract', () => { }); it('encodes condition and fixed-withdrawal calls exactly', () => { + const functionNames = artifact.abi + .filter((item) => item.type === 'function') + .map((item) => item.name); + expect(functionNames).not.toContain('ENABLED_SLOT'); + expect(functionNames).not.toContain('flip'); + expect(functionNames).toContain('enabled'); + expect(functionNames).toContain('setEnabled'); + expect(functionNames).toContain('withdraw'); expect(encodeSetConditionalWithdrawalEnabled(WITHDRAWAL, true)).toEqual({ to: WITHDRAWAL, data: `0x328d8f72${'0'.repeat(63)}1`, @@ -68,10 +73,6 @@ describe('conditional withdrawal contract', () => { expect(encodeSetConditionalWithdrawalEnabled(WITHDRAWAL, false).data).toBe( `0x328d8f72${'0'.repeat(64)}`, ); - expect(encodeFlipConditionalWithdrawal(WITHDRAWAL)).toEqual({ - to: WITHDRAWAL, - data: '0xcde4efa9', - }); expect(encodeConditionalWithdraw(WITHDRAWAL)).toEqual({ to: WITHDRAWAL, data: '0x3ccfd60b', diff --git a/app/vibenet/demos/validity/lib/conditionalWithdrawal.ts b/app/vibenet/demos/validity/lib/conditionalWithdrawal.ts index 0d26477e..7959aeb5 100644 --- a/app/vibenet/demos/validity/lib/conditionalWithdrawal.ts +++ b/app/vibenet/demos/validity/lib/conditionalWithdrawal.ts @@ -24,9 +24,9 @@ import type { StoragePredicate } from './types'; export const conditionalWithdrawalAbi = artifact.abi as Abi; export const conditionalWithdrawalBytecode = artifact.bytecode as Hex; -export const CONDITIONAL_WITHDRAWAL_ENABLED_SLOT = - '0xa91a9aee734204743335c443df931dcb220441d8aa6c1355dc61503a4bec3129' as Hex; -export const CONDITIONAL_WITHDRAWAL_ENABLED_MASK = (1n << 256n) - 1n; +/** `bool public enabled` is the contract's first storage variable. */ +export const CONDITIONAL_WITHDRAWAL_ENABLED_SLOT = 0n; +export const CONDITIONAL_WITHDRAWAL_ENABLED_MASK = 0xffn; export const CONDITIONAL_WITHDRAWAL_AMOUNT = WAD; export const CONDITIONAL_WITHDRAWAL_REFILL_THRESHOLD = 1_000_000n * WAD; export const CONDITIONAL_WITHDRAWAL_FUNDING_TARGET = 2_000_000n * WAD; @@ -171,13 +171,6 @@ export function encodeSetConditionalWithdrawalEnabled( }; } -export function encodeFlipConditionalWithdrawal(withdrawal: Address): { to: Address; data: Hex } { - return { - to: withdrawal, - data: encodeFunctionData({ abi: conditionalWithdrawalAbi, functionName: 'flip' }), - }; -} - export function encodeConditionalWithdraw(withdrawal: Address): { to: Address; data: Hex } { return { to: withdrawal, @@ -185,11 +178,11 @@ export function encodeConditionalWithdraw(withdrawal: Address): { to: Address; d }; } -/** EIP-8130 condition requiring the stable enabled bit to equal true. */ +/** EIP-8130 condition requiring `bool public enabled` in storage slot 0 to be true. */ export function conditionalWithdrawalEnabledPredicate(withdrawal: Address): StoragePredicate { return storagePredicate( withdrawal, - BigInt(CONDITIONAL_WITHDRAWAL_ENABLED_SLOT), + CONDITIONAL_WITHDRAWAL_ENABLED_SLOT, CONDITIONAL_WITHDRAWAL_ENABLED_MASK, '=', 1n, diff --git a/app/vibenet/demos/validity/lib/contracts/ConditionalWithdrawal.sol b/app/vibenet/demos/validity/lib/contracts/ConditionalWithdrawal.sol index 253fc640..518ff663 100644 --- a/app/vibenet/demos/validity/lib/contracts/ConditionalWithdrawal.sol +++ b/app/vibenet/demos/validity/lib/contracts/ConditionalWithdrawal.sol @@ -5,43 +5,23 @@ interface IERC20 { function transfer(address to, uint256 amount) external returns (bool); } -/// @notice Permissionless condition switch backed by one stable storage word. +/// @notice Permissionless VIBE withdrawal controlled by a boolean condition. contract ConditionalWithdrawal { - /// @dev Stable forever: keccak256("vibenet.validity.conditional-withdrawal.enabled.v1"). - bytes32 public constant ENABLED_SLOT = - 0xa91a9aee734204743335c443df931dcb220441d8aa6c1355dc61503a4bec3129; uint256 public constant WITHDRAWAL_AMOUNT = 1 ether; IERC20 public immutable VIBE; + bool public enabled; constructor(IERC20 vibe) { VIBE = vibe; } - function enabled() public view returns (bool value) { - bytes32 slot = ENABLED_SLOT; - assembly { - value := iszero(iszero(sload(slot))) - } - } - function setEnabled(bool value) external { - bytes32 slot = ENABLED_SLOT; - assembly { - sstore(slot, value) - } - } - - function flip() external returns (bool value) { - bytes32 slot = ENABLED_SLOT; - assembly { - value := iszero(sload(slot)) - sstore(slot, value) - } + enabled = value; } function withdraw() external { - require(enabled(), "withdrawal disabled"); + require(enabled, "withdrawal disabled"); require(VIBE.transfer(msg.sender, WITHDRAWAL_AMOUNT), "transfer failed"); } } diff --git a/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx b/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx index c29c5288..6681a81b 100644 --- a/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx +++ b/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx @@ -76,6 +76,28 @@ type Observation = { enabled: boolean; block: bigint; at: number }; type AgentPhase = 'Waiting' | 'Scheduling' | 'Opening' | 'Closing' | 'Retrying'; const EMPTY_ATTEMPT: Attempt = { status: 'idle' }; +const CONTRACT_SNIPPET = `interface IERC20 { + function transfer(address to, uint256 amount) external returns (bool); +} + +contract ConditionalWithdrawal { + uint256 public constant WITHDRAWAL_AMOUNT = 1 ether; + IERC20 public immutable VIBE; + bool public enabled; + + constructor(IERC20 vibe) { + VIBE = vibe; + } + + function setEnabled(bool value) external { + enabled = value; + } + + function withdraw() external { + require(enabled, "withdrawal disabled"); + require(VIBE.transfer(msg.sender, WITHDRAWAL_AMOUNT), "transfer failed"); + } +}`; function rootAccount(account: StoredAccount, accounts: StoredAccount[]): StoredAccount { let current = account; @@ -778,6 +800,9 @@ function RaceTheAgentDemoInner() { }); const validityAttempts = attemptHistoryRows(validity, validityHistory); const manualAttempts = attemptHistoryRows(manual, manualHistory); + const predicateSnippet = withdrawal + ? JSON.stringify(conditionalWithdrawalEnabledPredicate(withdrawal), null, 2) + : 'Resolving the conditional withdrawal address…'; return ( +
+ Implementation + One boolean, one predicate. + + Solidity stores the first state variable, enabled, + in slot 0. The validity transaction reads that slot directly and becomes eligible only when the boolean is true. + +
+ + +
+
+
Observed chain state @@ -1102,6 +1140,17 @@ function AttemptHistoryCard({ title, attempts }: { title: string; attempts: Atte ); } +function CodeSnippet({ label, code }: { label: string; code: string }) { + return ( + + {label} +
+        {code}
+      
+
+ ); +} + function Metric({ label, value }: { label: string; value: string }) { return (
From a1dcef56bd15f6dcc1ec8f633d3da6754d5b1f25 Mon Sep 17 00:00:00 2001 From: Haardik H Date: Wed, 2 Sep 2026 14:31:13 -0400 Subject: [PATCH 03/12] feat(vibenet): tuck implementation into advanced details Co-authored-by: OpenCode --- .../race-the-agent/RaceTheAgentDemo.tsx | 81 +++++++++++++++---- 1 file changed, 67 insertions(+), 14 deletions(-) diff --git a/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx b/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx index 6681a81b..e9907cc1 100644 --- a/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx +++ b/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx @@ -20,6 +20,7 @@ import { VIBENET_EXPLORER_PATH, VIBENET_WS_URL } from '../../../library/config'; import { CopyableValue } from '../../../components/CopyableValue'; import { AccountDemoShell } from '../../_components/AccountDemoShell'; import { DemoHeader } from '../../_components/DemoHeader'; +import { ChevronIcon } from '../../_shared/dropdown'; import { newCallRow } from '../../account/library/calls'; import type { StoredAccount } from '../../account/library/model'; import { aaReceiptSucceeded, type AaReceiptLike } from '../../account/library/receipt'; @@ -965,18 +966,25 @@ function RaceTheAgentDemoInner() {
-
- Implementation - One boolean, one predicate. - - Solidity stores the first state variable, enabled, - in slot 0. The validity transaction reads that slot directly and becomes eligible only when the boolean is true. - -
- - +
+ +
+ Advanced details + Contract and validity predicate +
+ +
+
+ + Solidity stores the first state variable, enabled, + in slot 0. The validity transaction reads that slot directly and becomes eligible only when the boolean is true. + +
+ + +
-
+
@@ -1140,12 +1148,57 @@ function AttemptHistoryCard({ title, attempts }: { title: string; attempts: Atte ); } -function CodeSnippet({ label, code }: { label: string; code: string }) { +type CodeLanguage = 'json' | 'solidity'; +type CodeToken = { text: string; kind: 'comment' | 'key' | 'keyword' | 'literal' | 'number' | 'plain' | 'string' | 'type' }; + +const CODE_TOKEN_CLASS: Record = { + comment: 'text-bds-gray-50 dark:text-[#7f8c98]', + key: 'text-base-blue dark:text-[#7eb8ff]', + keyword: 'text-purple-700 dark:text-[#c792ea]', + literal: 'text-bds-orange-70 dark:text-[#ff9d76]', + number: 'text-bds-orange-70 dark:text-[#f5c542]', + plain: 'text-bds-gray-80 dark:text-[#d6deeb]', + string: 'text-bds-green-70 dark:text-[#7ee0a8]', + type: 'text-base-blue dark:text-[#82aaff]', +}; + +function tokenizeCode(source: string, language: CodeLanguage): CodeToken[] { + const pattern = language === 'json' + ? /("(?:\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(?:\s*:)?|\b(?:true|false|null)\b|-?\d+(?:\.\d*)?|[{}[\]:,])/g + : /(\/\/[^\n]*|"(?:\\.|[^"\\])*"|\b(?:contract|interface|function|external|public|immutable|constant|returns|return|require|bool|uint256|address)\b|\b(?:IERC20|ConditionalWithdrawal)\b|\b\d+(?:\s+ether)?\b)/g; + const tokens: CodeToken[] = []; + let last = 0; + for (const hit of source.matchAll(pattern)) { + const text = hit[0]; + const index = hit.index ?? 0; + if (index > last) tokens.push({ text: source.slice(last, index), kind: 'plain' }); + let kind: CodeToken['kind'] = 'plain'; + if (language === 'json') { + if (text.startsWith('"')) kind = text.endsWith(':') ? 'key' : 'string'; + else if (text === 'true' || text === 'false' || text === 'null') kind = 'literal'; + else if (/^-?\d/.test(text)) kind = 'number'; + } else if (text.startsWith('//')) kind = 'comment'; + else if (text.startsWith('"')) kind = 'string'; + else if (/^\d/.test(text)) kind = 'number'; + else if (text === 'IERC20' || text === 'ConditionalWithdrawal') kind = 'type'; + else kind = 'keyword'; + tokens.push({ text, kind }); + last = index + text.length; + } + if (last < source.length) tokens.push({ text: source.slice(last), kind: 'plain' }); + return tokens; +} + +function CodeSnippet({ label, code, language }: { label: string; code: string; language: CodeLanguage }) { return ( {label} -
-        {code}
+      
+        
+          {tokenizeCode(code, language).map((token, index) => (
+            {token.text}
+          ))}
+        
       
); From 93e06c78d7e1f459b1d6fb8cf6e3c5dc5da7301b Mon Sep 17 00:00:00 2001 From: Haardik H Date: Wed, 2 Sep 2026 14:47:39 -0400 Subject: [PATCH 04/12] refactor(vibenet): compact validity race layout Co-authored-by: OpenCode --- .../race-the-agent/RaceTheAgentDemo.tsx | 182 ++++++------------ 1 file changed, 57 insertions(+), 125 deletions(-) diff --git a/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx b/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx index e9907cc1..5f52c2d7 100644 --- a/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx +++ b/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx @@ -809,7 +809,7 @@ function RaceTheAgentDemoInner() { - -
- -
- - {prepared ? 'Race setup ready' : setupError ? 'Automatic setup needs attention' : setupRunning ? 'Preparing race automatically' : 'Waiting to start setup'} - - - {setupError ?? progress ?? (prepared - ? agentRunning ? `Condition agent: ${agentPhase}` : 'Singleton funded; restarting condition agent.' - : 'Waiting for account and chain state.')} - -
-
- {setupError ? : null} -
- -
+
@@ -890,7 +870,7 @@ function RaceTheAgentDemoInner() {
Guided race - Submit first. React second. + Race first. Then submit ahead.
- - {agentRunning ? agentPhase : setupRunning ? 'Starting after setup' : 'Waiting for setup'} - + {setupError ? ( +
+ {setupError} + +
+ ) : ( + + {progress ?? (agentRunning ? agentPhase : setupRunning ? 'Starting after setup' : 'Waiting for setup')} + + )}
0} - > - - - + 0} + > + +
{(error || agentError) ? (
@@ -934,36 +921,23 @@ function RaceTheAgentDemoInner() {
) : null}
-
-
-
-
+ +
+
Comparison - Same call, different timing model + Same call, different timing. +
+
- -
-
- - -
-
- - -
+ + Manual attempts begin on your click. Validity attempts can already be waiting when withdrawals open. + +
+ + +
+
@@ -975,10 +949,22 @@ function RaceTheAgentDemoInner() {
- - Solidity stores the first state variable, enabled, - in slot 0. The validity transaction reads that slot directly and becomes eligible only when the boolean is true. - +
+ + Solidity stores the first state variable, enabled, + in slot 0. The validity transaction reads that slot directly and becomes eligible only when the boolean is true. + + {withdrawal ? ( +
+ Singleton contract + +
+ ) : null} +
@@ -986,7 +972,7 @@ function RaceTheAgentDemoInner() {
-
+
Observed chain state
@@ -1013,19 +999,6 @@ function RaceTheAgentDemoInner() {
- - What the result means - Blocks beat stopwatches. - - The timestamps show when this browser sampled state or received a receipt. They are useful context, not authoritative sequencing. The lower included block landed first; the same block is a tie at this resolution. - - {withdrawal ? ( -
- Global singleton - -
- ) : null} -
); @@ -1117,12 +1090,12 @@ function ConditionPill({ enabled }: { enabled: boolean | null }) { function AttemptHistoryCard({ title, attempts }: { title: string; attempts: Attempt[] }) { return ( - +
{title} {attempts.length} total
-
+
{attempts.length === 0 ? ( No previous attempts yet. ) : attempts.map((attempt, index) => ( @@ -1246,47 +1219,6 @@ function RaceStep({ ); } -function AttemptCard({ - label, - headline, - description, - attempt, - accent, -}: { - label: string; - headline: string; - description: string; - attempt: Attempt; - accent: 'blue' | 'green'; -}) { - const explorer = attempt.hash ? `${VIBENET_EXPLORER_PATH}/tx/${attempt.hash}` : null; - return ( - - -
-
- {label} - {headline} -
- -
- {description} -
- - - - -
- {attempt.error ? {attempt.error} : null} - {explorer ? ( - - View transaction in explorer - - ) : null} -
- ); -} - function StatusPill({ status }: { status: Attempt['status'] }) { const positive = status === 'success'; const negative = status === 'reverted' || status === 'expired' || status === 'error'; From cbb7fd8333798e933256d8272848ecc3df33e904 Mon Sep 17 00:00:00 2001 From: Haardik H Date: Wed, 2 Sep 2026 14:56:27 -0400 Subject: [PATCH 05/12] refactor(vibenet): loosen validity race layout Co-authored-by: OpenCode --- .../race-the-agent/RaceTheAgentDemo.tsx | 70 +++++-------------- .../race-the-agent/comparison.test.ts | 8 --- .../validity/race-the-agent/comparison.ts | 5 -- 3 files changed, 17 insertions(+), 66 deletions(-) diff --git a/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx b/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx index 5f52c2d7..344684e7 100644 --- a/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx +++ b/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx @@ -47,7 +47,6 @@ import { probeSingleton } from '../lib/singleton'; import { connectJsonRpcStream, headNumber, type StreamHead } from '../lib/stream'; import { attemptHistoryRows, - canResetRace, canSubmitManual, canSubmitValidity, comparisonResult, @@ -139,7 +138,6 @@ function RaceTheAgentDemoInner() { const [agentPhase, setAgentPhase] = useState('Waiting'); const [agentRestartToken, setAgentRestartToken] = useState(0); const [agentError, setAgentError] = useState(null); - const [streamLive, setStreamLive] = useState(false); const [prepared, setPrepared] = useState(false); const [setupRunning, setSetupRunning] = useState(false); const [setupError, setSetupError] = useState(null); @@ -157,7 +155,6 @@ function RaceTheAgentDemoInner() { const generationRef = useRef(0); const observedRef = useRef(null); - const validityRef = useRef(validity); const accountKeyRef = useRef(null); const setupInFlightKeyRef = useRef(null); const setupReadyKeyRef = useRef(null); @@ -167,7 +164,6 @@ function RaceTheAgentDemoInner() { const rpcSendRef = useRef(null); const engineRef = useRef(engine); engineRef.current = engine; - validityRef.current = validity; observedRef.current = observed; useEffect(() => { @@ -279,7 +275,6 @@ function RaceTheAgentDemoInner() { const startPoll = () => { if (pollId !== undefined) return; rpcSendRef.current = null; - setStreamLive(false); void syncState(); pollId = window.setInterval(() => void syncState(), STATE_FALLBACK_POLL_MS); }; @@ -300,7 +295,6 @@ function RaceTheAgentDemoInner() { stream.close(); return; } - setStreamLive(true); void syncState(); }; @@ -319,7 +313,6 @@ function RaceTheAgentDemoInner() { rpcSendRef.current = null; if (pollId !== undefined) window.clearInterval(pollId); stream?.close(); - setStreamLive(false); }; }, [applyObservation, client, vibe, withdrawal]); @@ -771,25 +764,7 @@ function RaceTheAgentDemoInner() { } }; - const reset = () => { - if (!canResetRace(validityRef.current, validBefore)) { - setError(`This validity transaction is still pending and can land until its ${RACE_VALIDITY_SECONDS}-second expiry. Keep watching the receipt and chain state.`); - return; - } - setValidity(EMPTY_ATTEMPT); - setValidityHistory([]); - setValidityAttemptCount(0); - setManual(EMPTY_ATTEMPT); - setManualHistory([]); - setManualAttemptCount(0); - setValidBefore(null); - setError(null); - setAgentError(null); - setObservations(observedRef.current ? [observedRef.current] : []); - }; - const result = comparisonResult(validity, manual); - const resetAllowed = canResetRace(validity, validBefore); const readyToSubmit = prepared && observed?.enabled === false && canSubmitValidity(validity.status); const readyToWithdraw = canSubmitManual({ status: manual.status, @@ -815,17 +790,6 @@ function RaceTheAgentDemoInner() { eyebrow="Validity Transactions · live comparison" title="Race the Agent" description="The same permissionless withdrawal pays exactly 1 VIBE. One transaction waits in advance for storage to equal 1; the other can be fired at any time and succeeds or reverts against the state it reaches onchain." - actions={validity.status !== 'idle' || manual.status !== 'idle' ? ( - - ) : undefined} />
@@ -856,15 +820,7 @@ function RaceTheAgentDemoInner() {
- -
- - {streamLive - ? 'WebSocket observations follow each Vibenet head at roughly 200ms cadence.' - : 'WebSocket unavailable; state observations are polling every second.'}{' '} - Inclusion blocks below remain the primary ordering evidence. - @@ -872,7 +828,7 @@ function RaceTheAgentDemoInner() { Guided race Race first. Then submit ahead.
-
+
{withdrawal ? ( -
- Singleton contract - +
+
+ Singleton contract + +
+
+ Contract balance + + {contractBalance === null ? '—' : `${formatCompactVibe(contractBalance)} VIBE`} + +
) : null}
@@ -1204,7 +1168,7 @@ function RaceStep({ return (
diff --git a/app/vibenet/demos/validity/race-the-agent/comparison.test.ts b/app/vibenet/demos/validity/race-the-agent/comparison.test.ts index 821037f6..918ce84c 100644 --- a/app/vibenet/demos/validity/race-the-agent/comparison.test.ts +++ b/app/vibenet/demos/validity/race-the-agent/comparison.test.ts @@ -4,7 +4,6 @@ import { AGENT_DISABLED_DWELL_MAX_MS, AGENT_DISABLED_DWELL_MIN_MS, attemptHistoryRows, - canResetRace, canSubmitManual, canSubmitValidity, comparisonResult, @@ -63,13 +62,6 @@ describe('race lifecycle predicates', () => { expect(fields.validBefore).toBe(BigInt(now + 15_000)); }); - it('blocks reset only while a submitted validity transaction can still land', () => { - expect(canResetRace({ status: 'pending' }, 20_000, 10_000)).toBe(false); - expect(canResetRace({ status: 'pending' }, null, 20_001)).toBe(false); - expect(canResetRace({ status: 'pending' }, 20_000, 20_001)).toBe(true); - expect(canResetRace({ status: 'expired' }, 20_000, 10_000)).toBe(true); - }); - it('runs the condition agent only when automatic setup resources are ready', () => { expect(shouldRunConditionAgent({ prepared: true, hasAgent: true, hasClient: true, hasContract: true })).toBe(true); expect(shouldRunConditionAgent({ prepared: false, hasAgent: true, hasClient: true, hasContract: true })).toBe(false); diff --git a/app/vibenet/demos/validity/race-the-agent/comparison.ts b/app/vibenet/demos/validity/race-the-agent/comparison.ts index 2371db9a..5cc38497 100644 --- a/app/vibenet/demos/validity/race-the-agent/comparison.ts +++ b/app/vibenet/demos/validity/race-the-agent/comparison.ts @@ -54,11 +54,6 @@ export function isAttemptTerminal(status: AttemptStatus): boolean { return status === 'success' || status === 'reverted' || status === 'expired' || status === 'error'; } -export function canResetRace(validity: Attempt, validBefore: number | null, now = Date.now()): boolean { - if (validity.status !== 'pending') return true; - return validBefore !== null && now > validBefore; -} - export function canSubmitAttempt(status: AttemptStatus): boolean { return status !== 'pending' && status !== 'submitting'; } From d9fcd300ebf4c7c8db2a029f269421674b66ba3a Mon Sep 17 00:00:00 2001 From: Haardik H Date: Wed, 2 Sep 2026 14:59:09 -0400 Subject: [PATCH 06/12] refactor(vibenet): merge race status and comparison Co-authored-by: OpenCode --- .../race-the-agent/RaceTheAgentDemo.tsx | 50 ++++++++++--------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx b/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx index 344684e7..af6c4b57 100644 --- a/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx +++ b/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx @@ -792,7 +792,7 @@ function RaceTheAgentDemoInner() { description="The same permissionless withdrawal pays exactly 1 VIBE. One transaction waits in advance for storage to equal 1; the other can be fired at any time and succeeds or reverts against the state it reaches onchain." /> -
+
@@ -802,9 +802,9 @@ function RaceTheAgentDemoInner() {
-
+
{observed ? (observed.enabled ? '1' : '0') : '—'}
+
+ + +
-
- - +
+
+
+ Comparison + Same call, different timing. +
+ +
+ + Manual attempts begin on your click. Validity attempts can already be waiting when withdrawals open. + +
+ + +
@@ -878,25 +894,10 @@ function RaceTheAgentDemoInner() { ) : null} - -
-
- Comparison - Same call, different timing. -
- -
- - Manual attempts begin on your click. Validity attempts can already be waiting when withdrawals open. - -
- - -
-
-
+
+
Advanced details @@ -936,7 +937,7 @@ function RaceTheAgentDemoInner() {
-
+
Observed chain state
@@ -964,6 +965,7 @@ function RaceTheAgentDemoInner() {
+
); } From 1aa319c14089de93203494c9400435e8f215c622 Mon Sep 17 00:00:00 2001 From: Haardik H Date: Wed, 2 Sep 2026 15:00:58 -0400 Subject: [PATCH 07/12] refactor(vibenet): simplify race status display Co-authored-by: OpenCode --- .../race-the-agent/RaceTheAgentDemo.tsx | 55 ++++--------------- .../race-the-agent/comparison.test.ts | 22 -------- .../validity/race-the-agent/comparison.ts | 28 ---------- 3 files changed, 10 insertions(+), 95 deletions(-) diff --git a/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx b/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx index af6c4b57..17786700 100644 --- a/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx +++ b/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx @@ -49,7 +49,6 @@ import { attemptHistoryRows, canSubmitManual, canSubmitValidity, - comparisonResult, isAttemptTerminal, preserveCompletedAttempt, randomAgentDwellMs, @@ -764,7 +763,6 @@ function RaceTheAgentDemoInner() { } }; - const result = comparisonResult(validity, manual); const readyToSubmit = prepared && observed?.enabled === false && canSubmitValidity(validity.status); const readyToWithdraw = canSubmitManual({ status: manual.status, @@ -794,12 +792,9 @@ function RaceTheAgentDemoInner() {
-
-
- Shared onchain switch - Withdrawal condition -
- +
+ Shared onchain switch + Withdrawal condition
@@ -821,18 +816,15 @@ function RaceTheAgentDemoInner() {
-
-
-
- Comparison - Same call, different timing. -
- +
+
+ Comparison + Same call, different timing.
Manual attempts begin on your click. Validity attempts can already be waiting when withdrawals open. -
+
@@ -1039,29 +1031,14 @@ function formatCompactVibe(value: bigint): string { return Number(whole).toLocaleString(undefined, { notation: 'compact', maximumFractionDigits: 1 }); } -function ConditionPill({ enabled }: { enabled: boolean | null }) { - return ( - - {enabled === null ? 'unobserved' : enabled ? 'enabled · 1' : 'disabled · 0'} - - ); -} - function AttemptHistoryCard({ title, attempts }: { title: string; attempts: Attempt[] }) { return ( - +
{title} {attempts.length} total
-
+
{attempts.length === 0 ? ( No previous attempts yet. ) : attempts.map((attempt, index) => ( @@ -1201,15 +1178,3 @@ function StatusPill({ status }: { status: Attempt['status'] }) { ); } - -function ResultPill({ result, validity, manual }: { result: ReturnType; validity: Attempt; manual: Attempt }) { - let label = 'Race in progress'; - if (result === 'validity-first') label = 'Validity landed first'; - if (result === 'manual-first') label = 'Manual landed first'; - if (result === 'same-block') label = 'Same inclusion block'; - if (result === 'validity-only') label = 'Only validity succeeded'; - if (result === 'manual-only') label = 'Only manual succeeded'; - if (result === 'neither-succeeded') label = 'Neither transaction succeeded'; - if (result === 'none' && validity.status === 'idle' && manual.status === 'idle') label = 'Not started'; - return {label}; -} diff --git a/app/vibenet/demos/validity/race-the-agent/comparison.test.ts b/app/vibenet/demos/validity/race-the-agent/comparison.test.ts index 918ce84c..e007c698 100644 --- a/app/vibenet/demos/validity/race-the-agent/comparison.test.ts +++ b/app/vibenet/demos/validity/race-the-agent/comparison.test.ts @@ -6,7 +6,6 @@ import { attemptHistoryRows, canSubmitManual, canSubmitValidity, - comparisonResult, isAttemptTerminal, preserveCompletedAttempt, randomAgentDwellMs, @@ -23,27 +22,6 @@ import { blockNumberPredicate } from '../lib/predicates'; const WITHDRAWAL = '0x1111111111111111111111111111111111111111'; -function success(block: bigint): Attempt { - return { status: 'success', includedBlock: block }; -} - -describe('comparisonResult', () => { - it('uses inclusion block ordering instead of browser timestamps', () => { - expect(comparisonResult(success(10n), { ...success(11n), includedAt: 1 })).toBe('validity-first'); - expect(comparisonResult({ ...success(12n), includedAt: 1 }, success(11n))).toBe('manual-first'); - expect(comparisonResult(success(12n), success(12n))).toBe('same-block'); - }); - - it('describes one-sided and unfinished outcomes', () => { - expect(comparisonResult(success(10n), { status: 'reverted' })).toBe('validity-only'); - expect(comparisonResult({ status: 'expired' }, success(10n))).toBe('manual-only'); - expect(comparisonResult(success(10n), { status: 'idle' })).toBe('none'); - expect(comparisonResult({ status: 'pending' }, { status: 'idle' })).toBe('none'); - expect(comparisonResult({ status: 'expired' }, { status: 'idle' })).toBe('neither-succeeded'); - expect(comparisonResult({ status: 'reverted' }, { status: 'error' })).toBe('neither-succeeded'); - }); -}); - describe('isAttemptTerminal', () => { it('only stops on final receipt or expiry states', () => { expect(isAttemptTerminal('success')).toBe(true); diff --git a/app/vibenet/demos/validity/race-the-agent/comparison.ts b/app/vibenet/demos/validity/race-the-agent/comparison.ts index 5cc38497..d2cd2b46 100644 --- a/app/vibenet/demos/validity/race-the-agent/comparison.ts +++ b/app/vibenet/demos/validity/race-the-agent/comparison.ts @@ -22,34 +22,6 @@ export type Attempt = { error?: string; }; -export type ComparisonResult = - | 'validity-first' - | 'manual-first' - | 'same-block' - | 'validity-only' - | 'manual-only' - | 'neither-succeeded' - | 'none'; - -export function comparisonResult(validity: Attempt, manual: Attempt): ComparisonResult { - const validityLanded = validity.status === 'success' && validity.includedBlock !== undefined; - const manualLanded = manual.status === 'success' && manual.includedBlock !== undefined; - if (validityLanded && manualLanded) { - if (validity.includedBlock! < manual.includedBlock!) return 'validity-first'; - if (manual.includedBlock! < validity.includedBlock!) return 'manual-first'; - return 'same-block'; - } - if (validityLanded && isAttemptTerminal(manual.status)) return 'validity-only'; - if (manualLanded && isAttemptTerminal(validity.status)) return 'manual-only'; - if ( - (!validityLanded && isAttemptTerminal(validity.status) && manual.status === 'idle') || - (!validityLanded && !manualLanded && isAttemptTerminal(validity.status) && isAttemptTerminal(manual.status)) - ) { - return 'neither-succeeded'; - } - return 'none'; -} - export function isAttemptTerminal(status: AttemptStatus): boolean { return status === 'success' || status === 'reverted' || status === 'expired' || status === 'error'; } From dc0ddeacf2f97eafe505f18c28bf6f0383b98bbe Mon Sep 17 00:00:00 2001 From: Haardik H Date: Wed, 2 Sep 2026 15:03:39 -0400 Subject: [PATCH 08/12] fix(vibenet): fix race attempt panel heights Co-authored-by: OpenCode --- app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx b/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx index 17786700..d585b5d8 100644 --- a/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx +++ b/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx @@ -824,7 +824,7 @@ function RaceTheAgentDemoInner() { Manual attempts begin on your click. Validity attempts can already be waiting when withdrawals open. -
+
From 952ae73b0b54adfa3f88025a519e98c1259f7c69 Mon Sep 17 00:00:00 2001 From: Haardik H Date: Wed, 2 Sep 2026 15:05:51 -0400 Subject: [PATCH 09/12] fix(vibenet): fill race attempt panel height Co-authored-by: OpenCode --- app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx b/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx index d585b5d8..ce51c597 100644 --- a/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx +++ b/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx @@ -824,7 +824,7 @@ function RaceTheAgentDemoInner() { Manual attempts begin on your click. Validity attempts can already be waiting when withdrawals open. -
+
From 7a1c9440b85ec14e12ed8639fc15178fcba68c8e Mon Sep 17 00:00:00 2001 From: Haardik H Date: Wed, 2 Sep 2026 15:07:11 -0400 Subject: [PATCH 10/12] fix(vibenet): bound race comparison height Co-authored-by: OpenCode --- .../demos/validity/race-the-agent/RaceTheAgentDemo.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx b/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx index ce51c597..b6ec8830 100644 --- a/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx +++ b/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx @@ -790,8 +790,8 @@ function RaceTheAgentDemoInner() { description="The same permissionless withdrawal pays exactly 1 VIBE. One transaction waits in advance for storage to equal 1; the other can be fired at any time and succeeds or reverts against the state it reaches onchain." /> -
- +
+
Shared onchain switch Withdrawal condition @@ -824,14 +824,14 @@ function RaceTheAgentDemoInner() { Manual attempts begin on your click. Validity attempts can already be waiting when withdrawals open. -
+
- +
Guided race Race first. Then submit ahead. From 34857e23aed35db67849e85c167c13845f42f6d7 Mon Sep 17 00:00:00 2001 From: Haardik H Date: Wed, 2 Sep 2026 15:14:01 -0400 Subject: [PATCH 11/12] fix(vibenet): preserve guided race actions Co-authored-by: OpenCode --- .../validity/race-the-agent/RaceTheAgentDemo.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx b/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx index b6ec8830..0dd1e94e 100644 --- a/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx +++ b/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx @@ -836,7 +836,7 @@ function RaceTheAgentDemoInner() { Guided race Race first. Then submit ahead.
-
+
- Solidity stores the first state variable, enabled, + The contract stores the first state variable, enabled, in slot 0. The validity transaction reads that slot directly and becomes eligible only when the boolean is true. {withdrawal ? ( @@ -1147,7 +1147,7 @@ function RaceStep({ return (
@@ -1155,9 +1155,9 @@ function RaceStep({ {number}
- {title} - {detail} -
{children}
+ {title} + {detail} +
{children}
); } @@ -1169,7 +1169,7 @@ function StatusPill({ status }: { status: Attempt['status'] }) { Date: Wed, 2 Sep 2026 15:21:19 -0400 Subject: [PATCH 12/12] refactor(vibenet): feature Race the Agent first Co-authored-by: OpenCode --- app/vibenet/demos/catalogue.test.ts | 2 +- app/vibenet/demos/catalogue.ts | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/vibenet/demos/catalogue.test.ts b/app/vibenet/demos/catalogue.test.ts index a0ffd520..d646ad09 100644 --- a/app/vibenet/demos/catalogue.test.ts +++ b/app/vibenet/demos/catalogue.test.ts @@ -47,7 +47,7 @@ describe('DEMOS', () => { it('keeps Validity Transactions reachable but hidden from the top-level grid', () => { const validity = DEMOS.find((demo) => demo.href === '/vibenet/demos/validity'); expect(validity?.title).toBe('Validity Transactions'); - expect(validity?.children?.map((demo) => demo.title)).toEqual(['Conditional Swaps', 'Race the Agent']); + expect(validity?.children?.map((demo) => demo.title)).toEqual(['Race the Agent', 'Conditional Swaps']); expect(listedDemos()).not.toContain(validity); }); }); diff --git a/app/vibenet/demos/catalogue.ts b/app/vibenet/demos/catalogue.ts index 83faa9db..16fa3703 100644 --- a/app/vibenet/demos/catalogue.ts +++ b/app/vibenet/demos/catalogue.ts @@ -69,18 +69,6 @@ export const DEMOS: DemoEntry[] = [ available: true, listed: false, children: [ - { - href: '/vibenet/demos/validity/conditional-swaps', - title: 'Conditional Swaps', - summary: - 'Place a swap that waits for a target price, then lands or expires as a shared simulated market moves through its validity window.', - points: [ - 'Set a buy or sell price against a live VIBE/USDV pool', - 'Inspect the EIP-8130 predicates attached to the swap', - 'Watch pending orders fill, expire, or get replaced', - ], - available: true, - }, { href: '/vibenet/demos/validity/race-the-agent', title: 'Race the Agent', @@ -93,6 +81,18 @@ export const DEMOS: DemoEntry[] = [ ], available: true, }, + { + href: '/vibenet/demos/validity/conditional-swaps', + title: 'Conditional Swaps', + summary: + 'Place a swap that waits for a target price, then lands or expires as a shared simulated market moves through its validity window.', + points: [ + 'Set a buy or sell price against a live VIBE/USDV pool', + 'Inspect the EIP-8130 predicates attached to the swap', + 'Watch pending orders fill, expire, or get replaced', + ], + available: true, + }, ], }, ];