Skip to content

Commit 657c9eb

Browse files
committed
Show all Steward vote outcomes
1 parent 5948280 commit 657c9eb

4 files changed

Lines changed: 257 additions & 77 deletions

File tree

README.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ npm run build --prefix web
3434

3535
## Frontend
3636

37-
The web app is a single proof page in `web/`. It reads the live `Steward.voteRequests(1698384)` state and `MiniGovernor.votes(1, Steward)` directly from Somnia Testnet.
37+
The web app is a single proof page in `web/`. It reads live `Steward.voteRequests(...)` and `MiniGovernor.votes(...)` state for the YES, NO, and ABSTAIN examples directly from Somnia Testnet.
3838

3939
Live frontend: `https://steward-ashy.vercel.app`
4040

@@ -56,14 +56,17 @@ npm run build --prefix web
5656
| Hello request id | `1697263` |
5757
| MiniGovernor | `0xa3773Ff7B2008bAb2E553E13e1E0ADE08a15f389` |
5858
| MiniGovernor deploy tx | `0x83b77eaad7965c9adf410052c11f0638c488a7a6d7a6b9a8ce5f90169040d62e` |
59-
| Proposal creation tx | `0xb31236f41cab27998bbf5593a1fbd8eda3f330eaf1c4b6b34523e5161d30852b` |
6059
| Steward | `0x6932C7827E7BFd9f0015Ed93fA120379E0d20541` |
6160
| Steward deploy tx | `0x723f8717b19a48b524858c5f1a1416be2016a2d393427a788e9e7b80af506147` |
6261
| Delegation tx | `0xdf49c68c630deef7a319b8d0f6daaefa392be433343784295c8833f9460fd45b` |
63-
| Steward vote request tx | `0x63c34767e59cc6988fd2ab5ecef9d1089e9f4445e1b1e18a9b490b0d0efc77ef` |
64-
| Steward callback + vote tx | `0xb74e25845472a2f591aa91eefe84e5e2828b41ac11acc78b41ceb1015500c52b` |
65-
| Steward request id | `1698384` |
66-
| Final result | `voteRequests(1698384)` is `Cast`, support `1`, reason `YES`; `MiniGovernor.votes(1, Steward)` is `1` |
62+
63+
### Steward vote proofs
64+
65+
| Outcome | Proposal | Request id | Proposal tx | Request tx | Callback + vote tx | Final state |
66+
| --- | --- | --- | --- | --- | --- | --- |
67+
| `YES` | `1` | `1698384` | `0xb31236f41cab27998bbf5593a1fbd8eda3f330eaf1c4b6b34523e5161d30852b` | `0x63c34767e59cc6988fd2ab5ecef9d1089e9f4445e1b1e18a9b490b0d0efc77ef` | `0xb74e25845472a2f591aa91eefe84e5e2828b41ac11acc78b41ceb1015500c52b` | `voteRequests(1698384)` is `Cast`, support `1`; `MiniGovernor.votes(1, Steward)` is `1` |
68+
| `NO` | `2` | `1738101` | `0xebc1961f3aa23078bb1d54e99d61fc4e8647caae1bae5e4e9f4ec48f2df53b3d` | `0x6d32b090d9ebacc6dd1dd46c01e0036bff3e684df4a28d3817823cd3747959fc` | `0xe14303e64f6a5db3d74919c94f42d3c14df3183e225f9996cc29cba86cc66dc3` | `voteRequests(1738101)` is `Cast`, support `2`; `MiniGovernor.votes(2, Steward)` is `2` |
69+
| `ABSTAIN` | `3` | `1738108` | `0x758f8dbc8cadf4887b301e33ab55c068ad983a4d507bd6cb9c5caa48b7060e53` | `0xa01f30ee06dbfa66b4a60414469d4f0e6406440f11e625a1197de88d797e851d` | `0xa157564585f473503627c801d6fb5992900dab3d5efcb31d4f15383c16487603` | `voteRequests(1738108)` is `Cast`, support `3`; `MiniGovernor.votes(3, Steward)` is `3` |
6770

6871
Explorer base: `https://shannon-explorer.somnia.network`
6972

scripts/verify-live.sh

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,40 @@ MINI_GOVERNOR="${MINI_GOVERNOR:-0xa3773Ff7B2008bAb2E553E13e1E0ADE08a15f389}"
77
REQUEST_ID="${REQUEST_ID:-1698384}"
88
PROPOSAL_ID="${PROPOSAL_ID:-1}"
99

10-
vote_request=$(cast call "$STEWARD" \
11-
"voteRequests(uint256)(uint256,uint256,uint8,uint8,uint8,string,uint256)" \
12-
"$REQUEST_ID" \
13-
--rpc-url "$RPC_URL")
14-
15-
governor_vote=$(cast call "$MINI_GOVERNOR" \
16-
"votes(uint256,address)(uint8)" \
17-
"$PROPOSAL_ID" \
18-
"$STEWARD" \
19-
--rpc-url "$RPC_URL")
20-
2110
echo "Steward live proof"
2211
echo "RPC: $RPC_URL"
2312
echo "Steward: $STEWARD"
2413
echo "MiniGovernor: $MINI_GOVERNOR"
25-
echo "Request: $REQUEST_ID"
2614
echo
27-
echo "$vote_request"
28-
echo
29-
echo "Governor vote for proposal $PROPOSAL_ID by Steward: $governor_vote"
15+
16+
verify_one() {
17+
local label="$1"
18+
local request_id="$2"
19+
local proposal_id="$3"
20+
local vote_request
21+
local governor_vote
22+
23+
vote_request=$(cast call "$STEWARD" \
24+
"voteRequests(uint256)(uint256,uint256,uint8,uint8,uint8,string,uint256)" \
25+
"$request_id" \
26+
--rpc-url "$RPC_URL")
27+
28+
governor_vote=$(cast call "$MINI_GOVERNOR" \
29+
"votes(uint256,address)(uint8)" \
30+
"$proposal_id" \
31+
"$STEWARD" \
32+
--rpc-url "$RPC_URL")
33+
34+
echo "$label request $request_id / proposal $proposal_id"
35+
echo "$vote_request"
36+
echo "Governor vote: $governor_vote"
37+
echo
38+
}
39+
40+
if [[ -n "${REQUEST_ID:-}" && -n "${PROPOSAL_ID:-}" && "${REQUEST_ID}" != "1698384" ]]; then
41+
verify_one "Custom" "$REQUEST_ID" "$PROPOSAL_ID"
42+
else
43+
verify_one "YES" 1698384 1
44+
verify_one "NO" 1738101 2
45+
verify_one "ABSTAIN" 1738108 3
46+
fi

web/src/main.tsx

Lines changed: 142 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -20,24 +20,71 @@ const client = createPublicClient({
2020
transport: http(),
2121
});
2222

23-
const proof: {
23+
type ProofCase = {
24+
label: string;
2425
steward: Address;
2526
governor: Address;
2627
requestId: bigint;
2728
proposalId: bigint;
29+
expectedSupport: number;
30+
expectedReason: string;
2831
proposal: string;
2932
criteria: string;
33+
proposalTx: `0x${string}`;
3034
requestTx: `0x${string}`;
3135
callbackTx: `0x${string}`;
32-
} = {
36+
};
37+
38+
const proofCases: ProofCase[] = [
39+
{
40+
label: "YES proof",
41+
steward: "0x6932C7827E7BFd9f0015Ed93fA120379E0d20541",
42+
governor: "0xa3773Ff7B2008bAb2E553E13e1E0ADE08a15f389",
43+
requestId: 1698384n,
44+
proposalId: 1n,
45+
expectedSupport: 1,
46+
expectedReason: "YES",
47+
proposal: "Allocate 500K USDC to a Q3 community grants program.",
48+
criteria: "Vote YES for community grants under 1M, NO for team token unlocks, ABSTAIN if unclear.",
49+
proposalTx: "0xb31236f41cab27998bbf5593a1fbd8eda3f330eaf1c4b6b34523e5161d30852b",
50+
requestTx: "0x63c34767e59cc6988fd2ab5ecef9d1089e9f4445e1b1e18a9b490b0d0efc77ef",
51+
callbackTx: "0xb74e25845472a2f591aa91eefe84e5e2828b41ac11acc78b41ceb1015500c52b",
52+
},
53+
{
54+
label: "NO proof",
55+
steward: "0x6932C7827E7BFd9f0015Ed93fA120379E0d20541",
56+
governor: "0xa3773Ff7B2008bAb2E553E13e1E0ADE08a15f389",
57+
requestId: 1738101n,
58+
proposalId: 2n,
59+
expectedSupport: 2,
60+
expectedReason: "NO",
61+
proposal: "Unlock 10% of foundation team tokens early.",
62+
criteria: "Vote YES for community grants under 1M, NO for team token unlocks, ABSTAIN if unclear.",
63+
proposalTx: "0xebc1961f3aa23078bb1d54e99d61fc4e8647caae1bae5e4e9f4ec48f2df53b3d",
64+
requestTx: "0x6d32b090d9ebacc6dd1dd46c01e0036bff3e684df4a28d3817823cd3747959fc",
65+
callbackTx: "0xe14303e64f6a5db3d74919c94f42d3c14df3183e225f9996cc29cba86cc66dc3",
66+
},
67+
{
68+
label: "ABSTAIN proof",
69+
steward: "0x6932C7827E7BFd9f0015Ed93fA120379E0d20541",
70+
governor: "0xa3773Ff7B2008bAb2E553E13e1E0ADE08a15f389",
71+
requestId: 1738108n,
72+
proposalId: 3n,
73+
expectedSupport: 3,
74+
expectedReason: "ABSTAIN",
75+
proposal: "Form a working group to explore future ecosystem partnerships without committing funds.",
76+
criteria: "Vote YES for community grants under 1M, NO for team token unlocks, ABSTAIN if unclear.",
77+
proposalTx: "0x758f8dbc8cadf4887b301e33ab55c068ad983a4d507bd6cb9c5caa48b7060e53",
78+
requestTx: "0xa01f30ee06dbfa66b4a60414469d4f0e6406440f11e625a1197de88d797e851d",
79+
callbackTx: "0xa157564585f473503627c801d6fb5992900dab3d5efcb31d4f15383c16487603",
80+
},
81+
];
82+
83+
const primaryProof = proofCases[0];
84+
85+
const proofAddresses = {
3386
steward: "0x6932C7827E7BFd9f0015Ed93fA120379E0d20541",
3487
governor: "0xa3773Ff7B2008bAb2E553E13e1E0ADE08a15f389",
35-
requestId: 1698384n,
36-
proposalId: 1n,
37-
proposal: "Allocate 500K USDC to a Q3 community grants program.",
38-
criteria: "Vote YES for community grants under 1M, NO for team token unlocks, ABSTAIN if unclear.",
39-
requestTx: "0x63c34767e59cc6988fd2ab5ecef9d1089e9f4445e1b1e18a9b490b0d0efc77ef",
40-
callbackTx: "0xb74e25845472a2f591aa91eefe84e5e2828b41ac11acc78b41ceb1015500c52b",
4188
};
4289

4390
const stewardAbi = [
@@ -73,12 +120,14 @@ const governorAbi = [
73120

74121
type ProofState = {
75122
loading: boolean;
76-
state?: number;
77-
platformStatus?: number;
78-
support?: number;
79-
reason?: string;
80-
receipt?: bigint;
81-
governorVote?: number;
123+
proofs?: Record<string, {
124+
state: number;
125+
platformStatus: number;
126+
support: number;
127+
reason: string;
128+
receipt: bigint;
129+
governorVote: number;
130+
}>;
82131
error?: string;
83132
};
84133

@@ -90,6 +139,13 @@ function shortAddress(value: string) {
90139
return `${value.slice(0, 6)}...${value.slice(-4)}`;
91140
}
92141

142+
function supportLabel(support?: number) {
143+
if (support === 1) return "YES";
144+
if (support === 2) return "NO";
145+
if (support === 3) return "ABSTAIN";
146+
return "PENDING";
147+
}
148+
93149
function App() {
94150
const [live, setLive] = useState<ProofState>({ loading: true });
95151

@@ -98,29 +154,39 @@ function App() {
98154

99155
async function load() {
100156
try {
101-
const voteRequest = await client.readContract({
102-
address: proof.steward,
103-
abi: stewardAbi,
104-
functionName: "voteRequests",
105-
args: [proof.requestId],
106-
});
107-
const governorVote = await client.readContract({
108-
address: proof.governor,
109-
abi: governorAbi,
110-
functionName: "votes",
111-
args: [proof.proposalId, proof.steward],
112-
});
157+
const entries = await Promise.all(
158+
proofCases.map(async (proof) => {
159+
const voteRequest = await client.readContract({
160+
address: proof.steward,
161+
abi: stewardAbi,
162+
functionName: "voteRequests",
163+
args: [proof.requestId],
164+
});
165+
const governorVote = await client.readContract({
166+
address: proof.governor,
167+
abi: governorAbi,
168+
functionName: "votes",
169+
args: [proof.proposalId, proof.steward],
170+
});
171+
return [
172+
proof.label,
173+
{
174+
state: voteRequest[2],
175+
platformStatus: voteRequest[3],
176+
support: voteRequest[4],
177+
reason: voteRequest[5],
178+
receipt: voteRequest[6],
179+
governorVote,
180+
},
181+
] as const;
182+
}),
183+
);
113184

114185
if (!active) return;
115186

116187
setLive({
117188
loading: false,
118-
state: voteRequest[2],
119-
platformStatus: voteRequest[3],
120-
support: voteRequest[4],
121-
reason: voteRequest[5],
122-
receipt: voteRequest[6],
123-
governorVote,
189+
proofs: Object.fromEntries(entries),
124190
});
125191
} catch (error) {
126192
if (!active) return;
@@ -139,7 +205,10 @@ function App() {
139205
};
140206
}, []);
141207

142-
const cast = live.state === 2 && live.support === 1 && live.governorVote === 1;
208+
const allCast = proofCases.every((proof) => {
209+
const state = live.proofs?.[proof.label];
210+
return state?.state === 2 && state.support === proof.expectedSupport && state.governorVote === proof.expectedSupport;
211+
});
143212

144213
return (
145214
<main>
@@ -161,31 +230,51 @@ function App() {
161230
reasons against the mandate, and casts a DAO vote onchain through an async callback.
162231
</p>
163232
<div className="actions">
164-
<a href={explorerTx(proof.callbackTx)} target="_blank" rel="noreferrer">
165-
Open vote tx
233+
<a href={explorerTx(primaryProof.callbackTx)} target="_blank" rel="noreferrer">
234+
Open YES vote tx
166235
</a>
167-
<a className="secondary" href={explorerTx(proof.requestTx)} target="_blank" rel="noreferrer">
168-
Open request tx
236+
<a className="secondary" href="#proof">
237+
View all outcomes
169238
</a>
170239
</div>
171240
</div>
172241

173242
<div className="receipt" id="proof">
174243
<div className="receiptTop">
175244
<span>Onchain vote proof</span>
176-
<strong>{live.loading ? "reading..." : cast ? "VOTED YES" : "CHECK STATE"}</strong>
245+
<strong>{live.loading ? "reading..." : allCast ? "YES · NO · ABSTAIN" : "CHECK STATE"}</strong>
177246
</div>
178-
<div className="proposal">
179-
<span>Proposal</span>
180-
<p>{proof.proposal}</p>
247+
<div className="decisionDeck">
248+
{proofCases.map((proof) => {
249+
const state = live.proofs?.[proof.label];
250+
const label = supportLabel(state?.support);
251+
const verified = state?.state === 2 && state.support === proof.expectedSupport;
252+
return (
253+
<article className={`voteCard ${label.toLowerCase()}`} key={proof.label}>
254+
<div>
255+
<span>{proof.label}</span>
256+
<strong>{live.loading ? "..." : label}</strong>
257+
</div>
258+
<p>{proof.proposal}</p>
259+
<small>{verified ? "Cast by Steward" : "Waiting for matching proof"}</small>
260+
<div className="txLinks">
261+
<a href={explorerTx(proof.proposalTx)} target="_blank" rel="noreferrer">
262+
Proposal
263+
</a>
264+
<a href={explorerTx(proof.requestTx)} target="_blank" rel="noreferrer">
265+
Request
266+
</a>
267+
<a href={explorerTx(proof.callbackTx)} target="_blank" rel="noreferrer">
268+
Vote
269+
</a>
270+
</div>
271+
</article>
272+
);
273+
})}
181274
</div>
182275
<div className="criteria">
183276
<span>Delegated criteria</span>
184-
<p>{proof.criteria}</p>
185-
</div>
186-
<div className="decision">
187-
<span>Somnia Agent output</span>
188-
<strong>{live.loading ? "..." : live.reason ?? "unavailable"}</strong>
277+
<p>{primaryProof.criteria}</p>
189278
</div>
190279
</div>
191280
</section>
@@ -216,19 +305,19 @@ function App() {
216305
<section className="live">
217306
<div>
218307
<span>Steward</span>
219-
<strong>{shortAddress(proof.steward)}</strong>
308+
<strong>{shortAddress(proofAddresses.steward)}</strong>
220309
</div>
221310
<div>
222-
<span>Request ID</span>
223-
<strong>{proof.requestId.toString()}</strong>
311+
<span>Request IDs</span>
312+
<strong>1698384 / 1738101 / 1738108</strong>
224313
</div>
225314
<div>
226-
<span>Request state</span>
227-
<strong>{live.loading ? "..." : live.state}</strong>
315+
<span>Request states</span>
316+
<strong>{live.loading ? "..." : allCast ? "Cast x3" : "Check"}</strong>
228317
</div>
229318
<div>
230-
<span>Governor vote</span>
231-
<strong>{live.loading ? "..." : live.governorVote}</strong>
319+
<span>Governor votes</span>
320+
<strong>{live.loading ? "..." : allCast ? "1 / 2 / 3" : "Check"}</strong>
232321
</div>
233322
</section>
234323

0 commit comments

Comments
 (0)