forked from InsurNiffy/niff-Stellar-shurance
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvote.ts
More file actions
105 lines (95 loc) · 3.18 KB
/
Copy pathvote.ts
File metadata and controls
105 lines (95 loc) · 3.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import {
Claim,
ClaimSchema,
Eligibility,
EligibilitySchema,
VoteOption,
VoteResponse,
VoteResponseSchema,
} from '@/lib/schemas/vote'
import { getConfig } from '@/config/env'
const { apiUrl: API_BASE, explorerBase: EXPLORER_BASE } = getConfig()
async function handleResponse<T>(res: Response): Promise<T> {
if (!res.ok) {
const err = await res.json().catch(() => ({ message: 'Request failed' }))
throw new VoteAPIError(err.code ?? 'REQUEST_FAILED', err.message ?? 'Request failed')
}
return res.json()
}
export class VoteAPIError extends Error {
constructor(
public code: string,
message: string,
) {
super(message)
this.name = 'VoteAPIError'
}
}
export async function fetchClaim(claimId: string): Promise<Claim> {
const res = await fetch(`${API_BASE}/api/claims/${claimId}`)
const data = await handleResponse<unknown>(res)
return ClaimSchema.parse(data)
}
export async function fetchEligibility(
claimId: string,
walletAddress: string,
): Promise<Eligibility> {
const res = await fetch(
`${API_BASE}/api/claims/${claimId}/eligibility?wallet=${encodeURIComponent(walletAddress)}`,
)
const data = await handleResponse<unknown>(res)
return EligibilitySchema.parse(data)
}
/**
* Simulate the vote transaction server-side before opening the wallet popup.
* Returns null if simulation passes, or an error message string if it fails.
*/
export async function simulateVote(
claimId: string,
walletAddress: string,
vote: VoteOption,
): Promise<string | null> {
try {
const res = await fetch(`${API_BASE}/api/claims/${claimId}/vote/simulate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ walletAddress, vote }),
})
if (!res.ok) {
const err = await res.json().catch(() => ({ message: 'Simulation failed' }))
return err.message ?? 'Transaction simulation failed'
}
return null
} catch {
return null // non-blocking: proceed to wallet if simulation endpoint unavailable
}
}
export async function submitVote(
claimId: string,
walletAddress: string,
vote: VoteOption,
signedXdr: string,
): Promise<VoteResponse> {
const res = await fetch(`${API_BASE}/api/claims/${claimId}/vote`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ walletAddress, vote, signedXdr }),
})
const data = await handleResponse<unknown>(res)
return VoteResponseSchema.parse(data)
}
export function explorerUrl(txHash: string): string {
return `${EXPLORER_BASE}/${txHash}`
}
export const VOTE_ERROR_MESSAGES: Record<string, string> = {
NOT_ELIGIBLE_VOTER: 'Your wallet is not in the eligible voter list for this claim.',
DUPLICATE_VOTE: 'You have already cast a vote on this claim.',
VOTING_WINDOW_CLOSED: 'The voting window for this claim has closed.',
CLAIM_ALREADY_TERMINAL: 'This claim has already been resolved.',
CLAIM_NOT_FOUND: 'Claim not found.',
CLAIMS_PAUSED: 'Claim operations are currently paused by the contract admin.',
REQUEST_FAILED: 'Request failed. Please try again.',
}
export function getVoteErrorMessage(error: VoteAPIError): string {
return VOTE_ERROR_MESSAGES[error.code] ?? error.message
}