diff --git a/examples/web-client/README.md b/examples/web-client/README.md index feccf7b..110e68f 100644 --- a/examples/web-client/README.md +++ b/examples/web-client/README.md @@ -18,3 +18,9 @@ npm run build ``` This app currently uses an in-browser mock transport so flows can be exercised without a wallet during development. + +## Included Flows + +- Create guild with SDK `createGuild` and display resolved guild/token/governor addresses. +- Governance proposal todo list using `governanceAction` and per-item vote actions via `vote`. +- Treasury send money and get approval actions via `treasuryAction` (`transfer` and `approve` modes). diff --git a/examples/web-client/src/App.tsx b/examples/web-client/src/App.tsx index b773aad..9be52ba 100644 --- a/examples/web-client/src/App.tsx +++ b/examples/web-client/src/App.tsx @@ -15,6 +15,15 @@ interface ProposalTodo { lastTxHash: string; } +interface TreasuryActivity { + id: number; + mode: 'transfer' | 'approve'; + target: string; + token: string; + amount: string; + txHash: string; +} + function isStarknetAddress(value: string): value is `0x${string}` { return /^0x[0-9a-fA-F]+$/.test(value); } @@ -43,6 +52,15 @@ export function App() { const [proposalForm, setProposalForm] = useState({ title: 'Fund toolchain grant', description: 'Approve monthly ops budget.' }); const [proposalTodos, setProposalTodos] = useState([]); const [governanceError, setGovernanceError] = useState(null); + const [treasuryForm, setTreasuryForm] = useState({ + guild: '0x111', + target: '0x777', + token: '0x222', + amount: '50', + mode: 'transfer' as 'transfer' | 'approve', + }); + const [treasuryActivities, setTreasuryActivities] = useState([]); + const [treasuryError, setTreasuryError] = useState(null); const bundle = useMemo(() => createBrowserClientBundle(network, factory), [network, factory]); const invokes = bundle.transport.getState().invokes; @@ -75,6 +93,7 @@ export function App() { governor: addresses.governor, }); setGovernorAddress(addresses.governor); + setTreasuryForm((prev) => ({ ...prev, guild: addresses.guild, token: addresses.token })); } catch (error) { setCreateError(error instanceof Error ? error.message : 'Create guild failed'); } @@ -149,6 +168,37 @@ export function App() { } } + async function onTreasurySubmit(event: FormEvent) { + event.preventDefault(); + setTreasuryError(null); + + try { + const actionType = treasuryForm.mode === 'transfer' ? 1 : 2; + const tx = await bundle.client.treasuryAction({ + guild: treasuryForm.guild as `0x${string}`, + actionType, + target: treasuryForm.target as `0x${string}`, + token: treasuryForm.token as `0x${string}`, + amount: BigInt(treasuryForm.amount), + calldata: [], + }); + + setTreasuryActivities((prev) => [ + { + id: prev.length + 1, + mode: treasuryForm.mode, + target: treasuryForm.target, + token: treasuryForm.token, + amount: treasuryForm.amount, + txHash: tx.transactionHash, + }, + ...prev, + ]); + } catch (error) { + setTreasuryError(error instanceof Error ? error.message : 'Treasury action failed'); + } + } + return (
@@ -207,7 +257,7 @@ export function App() {

Treasury Actions

-

Send money and approval actions land in slice 4.

+

Send money and approve spender actions now available below.

@@ -270,6 +320,78 @@ export function App() { {governanceError ?

{governanceError}

: null} +
+

Treasury: Send Money / Approve

+
+ + + + + +
+ +
+
+ + {treasuryActivities.length === 0 ? ( +

No treasury actions submitted yet.

+ ) : ( +
    + {treasuryActivities.map((item) => ( +
  • +
    +

    {item.mode === 'transfer' ? 'Transfer' : 'Approve'}

    +

    + Target: {item.target} +

    +

    + Token: {item.token} +

    +

    Amount: {item.amount}

    +
    + {item.txHash} +
  • + ))} +
+ )} + {treasuryError ?

{treasuryError}

: null} +
+

Create Guild

diff --git a/examples/web-client/src/styles.css b/examples/web-client/src/styles.css index f9335c1..08a75ab 100644 --- a/examples/web-client/src/styles.css +++ b/examples/web-client/src/styles.css @@ -159,6 +159,24 @@ button:hover { gap: 0.65rem; } +.treasury-list { + list-style: none; + margin: 0; + padding: 0; + display: grid; + gap: 0.55rem; +} + +.treasury-list li { + border: 1px solid #dbece1; + border-radius: 0.75rem; + padding: 0.7rem; + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 0.75rem; +} + .todo-list li { border: 1px solid #dbece1; border-radius: 0.75rem;