Context
Propdates on Gnars are EAS attestations on Base (schema 0x8bd0d42901ce3cd9898dbea6ae2fbf1e796ef0923e7cbb0a1cecac2e42d47cb3, recipient = DAO token). The schema is append-only — there is no native update path. Attestations are created with revocable: true (src/services/propdates.ts:272), so the intended "edit" flow is revoke the old attestation, then create a new one.
Problem: the site currently renders revoked attestations as if they were live.
Root cause
-
The Builder SDK query propdates does not filter by revoked:
query propdates($schemaId: String!, $recipient: String!) {
attestations(where: { schemaId: { equals: $schemaId }, recipient: { equals: $recipient } }) {
...Attestation
}
}
(from @buildeross/sdk — node_modules/@buildeross/sdk/dist/chunk-*.js)
-
Our service layer does not drop revoked entries either:
fetchDaoPropdatesFromEas maps every attestation returned by the SDK.
listPropdates returns getPropDates(...) directly with no filter.
- There are zero references to
revoked anywhere under src/.
Result: even after a user revokes their attestation onchain, it keeps showing up in the proposal detail view.
Proposed fix
Filter revoked === true (or equivalently revocationTime > 0) in the propdates service before returning to consumers.
fetchDaoPropdatesFromEas → drop revoked attestations from attestations before the map in propdates.ts:69.
listPropdates → filter the array returned by getPropDates before returning.
- The
Propdate type (re-exported PropDate from @buildeross/sdk/eas) already exposes revoked / revocationTime fields.
- No schema or UI changes required. Thread/reply logic in Propdates.tsx keeps working because children of a revoked root would simply become orphaned — acceptable for v1, or we can drop replies whose parent is revoked as a follow-up.
Edit flow (to document alongside the fix)
Once the filter is in place, the canonical "edit a propdate" flow is:
- Only the original attester can revoke — EAS enforces
msg.sender == attestation.attester. A different wallet cannot edit someone else's propdate.
- Attester calls
revoke() on the Base EAS contract 0x4200000000000000000000000000000000000021 with:
{ schema: <PROPDATE_SCHEMA_UID>, data: { uid: <attestationId>, value: 0 } }
- Attester (or anyone) submits a new propdate via the existing UI (
PropdateForm) with the corrected content.
Gas cost on Base is negligible (cents).
Optional UX follow-ups (out of scope for this issue, but worth tracking):
- Expose a "Delete" / "Edit" button on the attester's own propdates that chains
revoke + re-submit in one flow.
- Show a subtle "edited" indicator when a new propdate's
originalMessageId matches a revoked one (requires a convention — current schema has no edit pointer; originalMessageId currently means "reply parent").
Why this matters
Without the filter, the documented revoke path is a no-op from the user's perspective: they pay gas to revoke and the bad content is still there. This blocks any real edit/cleanup workflow for authors who post a propdate they want to take back.
Acceptance criteria
Context
Propdates on Gnars are EAS attestations on Base (schema
0x8bd0d42901ce3cd9898dbea6ae2fbf1e796ef0923e7cbb0a1cecac2e42d47cb3, recipient = DAO token). The schema is append-only — there is no nativeupdatepath. Attestations are created withrevocable: true(src/services/propdates.ts:272), so the intended "edit" flow is revoke the old attestation, then create a new one.Problem: the site currently renders revoked attestations as if they were live.
Root cause
The Builder SDK query
propdatesdoes not filter byrevoked:(from
@buildeross/sdk—node_modules/@buildeross/sdk/dist/chunk-*.js)Our service layer does not drop revoked entries either:
fetchDaoPropdatesFromEasmaps every attestation returned by the SDK.listPropdatesreturnsgetPropDates(...)directly with no filter.revokedanywhere undersrc/.Result: even after a user revokes their attestation onchain, it keeps showing up in the proposal detail view.
Proposed fix
Filter
revoked === true(or equivalentlyrevocationTime > 0) in the propdates service before returning to consumers.fetchDaoPropdatesFromEas→ drop revoked attestations fromattestationsbefore themapin propdates.ts:69.listPropdates→ filter the array returned bygetPropDatesbefore returning.Propdatetype (re-exportedPropDatefrom@buildeross/sdk/eas) already exposesrevoked/revocationTimefields.Edit flow (to document alongside the fix)
Once the filter is in place, the canonical "edit a propdate" flow is:
msg.sender == attestation.attester. A different wallet cannot edit someone else's propdate.revoke()on the Base EAS contract0x4200000000000000000000000000000000000021with:PropdateForm) with the corrected content.Gas cost on Base is negligible (cents).
Optional UX follow-ups (out of scope for this issue, but worth tracking):
revoke+ re-submit in one flow.originalMessageIdmatches a revoked one (requires a convention — current schema has no edit pointer;originalMessageIdcurrently means "reply parent").Why this matters
Without the filter, the documented revoke path is a no-op from the user's perspective: they pay gas to revoke and the bad content is still there. This blocks any real edit/cleanup workflow for authors who post a propdate they want to take back.
Acceptance criteria
listDaoPropdates,listPropdates, andgetPropdateByTxid.docs/entry (or inline comment) describing the edit-via-revoke flow so future contributors don't re-ask this question.