From 6769a5e32c19a4567ce10c99ca7eac9ddf608ce9 Mon Sep 17 00:00:00 2001 From: Haardik H Date: Wed, 2 Sep 2026 14:27:07 -0400 Subject: [PATCH 1/2] 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 9953d93..fbc08f9 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 ab7e459..01077df 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 d1b806e..6945044 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; @@ -169,13 +169,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, @@ -183,11 +176,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 253fc64..518ff66 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 fe0319d..7eb8f0f 100644 --- a/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx +++ b/app/vibenet/demos/validity/race-the-agent/RaceTheAgentDemo.tsx @@ -77,6 +77,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"); + } +}`; export function RaceTheAgentDemo() { return ( @@ -772,6 +794,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 @@ -1096,6 +1134,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 830830f66ef6f1ffe1cd2bc8e370408c28981d46 Mon Sep 17 00:00:00 2001 From: Haardik H Date: Wed, 2 Sep 2026 14:31:13 -0400 Subject: [PATCH 2/2] 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 7eb8f0f..61a9844 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'; @@ -959,18 +960,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. + +
+ + +
-
+
@@ -1134,12 +1142,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}
+          ))}
+        
       
);