Skip to content
Open

Dev #2175

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/relayer-simulation-revert-409.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@anticapture/relayer": patch
"@anticapture/gateful": patch
"@anticapture/dashboard": patch
---

Answer a gasless vote or delegation that would revert on-chain (a vote already cast, a used nonce) with 409 SIMULATION_FAILED instead of 500 INTERNAL, documented in the relay routes Gateful exposes, and make a gasless vote reach the relayer once: the dashboard checks hasVoted before asking for a signature, confirms the relayed transaction on the DAO's chain instead of through the wallet, never reports a relayed vote as failed, and keeps the vote's submission state across modal closes and remounts so the ballot is not offered again
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { CheckCircle2 } from "lucide-react";

import { Button } from "@/shared/components";

interface VoteAlreadyCastContentProps {
onClose: () => void;
}

/**
* The governor already holds a vote from this address, so the ballot on
* screen was never cast. Ends the flow with a way out rather than the ballot,
* which could only send the same refusal again.
*/
export const VoteAlreadyCastContent = ({
onClose,
}: VoteAlreadyCastContentProps) => (
<div className="flex flex-col items-center gap-3 px-4 py-8">
<div className="bg-surface-opacity-success flex size-12 items-center justify-center rounded-full">
<CheckCircle2 className="text-success size-6" />
</div>

<h3 className="text-primary text-center text-lg font-medium">
You have already voted on this proposal
</h3>

<p className="text-secondary max-w-sm text-center text-sm leading-5">
Your earlier vote is the one that counts. It shows on this page once it is
indexed.
</p>

<Button variant="outline" onClick={onClose}>
Close
</Button>
</div>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { ExternalLink, Hourglass } from "lucide-react";
import type { Hash } from "viem";

import { InlineAlert } from "@/shared/components/design-system/alerts/inline-alert/InlineAlert";

interface VotePendingContentProps {
hash: Hash | null;
explorerBaseUrl?: string;
}

const shortenHash = (hash: string) => `${hash.slice(0, 10)}…${hash.slice(-8)}`;

/**
* A vote that may be on-chain but could not be confirmed here. It offers no
* way to vote again: the wallet would sign the same ballot, and the relayer
* would only be asked to replay it.
*/
export const VotePendingContent = ({
hash,
explorerBaseUrl,
}: VotePendingContentProps) => (
<div className="flex flex-col items-center gap-3 px-4 py-8">
<div className="bg-surface-contrast flex size-12 items-center justify-center rounded-full">
<Hourglass className="text-secondary size-6" />
</div>

<h3 className="text-primary text-center text-lg font-medium">
Your vote was sent
</h3>

{hash && explorerBaseUrl && (
<a
href={`${explorerBaseUrl}/tx/${hash}`}
target="_blank"
rel="noopener noreferrer"
className="text-link inline-flex items-center gap-1 text-sm leading-5 hover:underline"
>
{shortenHash(hash)}
<ExternalLink className="size-3.5 shrink-0" />
</a>
)}

<InlineAlert
variant="warning"
text={
hash
? "It is not confirmed yet. Follow it on the explorer. This page shows your vote on its own once it is indexed, so there is no need to vote again."
: "The relayer did not answer clearly, so the vote may already be on its way. This page shows your vote on its own once it is indexed, so there is no need to vote again."
}
/>
</div>
);
Loading
Loading