Skip to content

Commit 2a86721

Browse files
authored
chore(app): wip stargaze faucet added (#3956)
2 parents 63065bf + bb62c39 commit 2a86721

File tree

3 files changed

+439
-63
lines changed

3 files changed

+439
-63
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,347 @@
1+
<script lang="ts">
2+
import { URLS } from "$lib/constants"
3+
import request from "graphql-request"
4+
import { cn } from "$lib/utilities/shadcn.ts"
5+
import { Label } from "$lib/components/ui/label"
6+
import { createQuery } from "@tanstack/svelte-query"
7+
import Truncate from "$lib/components/truncate.svelte"
8+
import * as Card from "$lib/components/ui/card/index.ts"
9+
import { Input } from "$lib/components/ui/input/index.ts"
10+
import { Button } from "$lib/components/ui/button/index.ts"
11+
import SpinnerSVG from "$lib/components/spinner-svg.svelte"
12+
import { cosmosStore } from "$/lib/wallet/cosmos/config.ts"
13+
import { derived, writable, type Writable } from "svelte/store"
14+
import { getCosmosChainBalances } from "$lib/queries/balance/cosmos"
15+
import { createCosmosSdkAddressRegex } from "$lib/utilities/address.ts"
16+
import { bech32ToBech32Address, isValidBech32Address } from "@unionlabs/client"
17+
import type { AwaitedReturnType, DiscriminatedUnion } from "$lib/utilities/types.ts"
18+
import { faucetUnoMutation2 } from "$lib/graphql/queries/faucet.ts"
19+
import { Turnstile } from "svelte-turnstile"
20+
21+
type DydxFaucetState = DiscriminatedUnion<
22+
"kind",
23+
{
24+
IDLE: {}
25+
VERIFYING: {}
26+
VERIFIED: {}
27+
SUBMITTING: { captchaToken: string }
28+
RESULT_OK: { message: string }
29+
RESULT_ERR: { error: string }
30+
VERIFICATION_FAILED: { error: string }
31+
}
32+
>
33+
34+
let stargazeAddress = derived(cosmosStore, $cosmosStore =>
35+
$cosmosStore.address
36+
? bech32ToBech32Address({
37+
address: $cosmosStore.address,
38+
toPrefix: "stars"
39+
})
40+
: ""
41+
)
42+
43+
let stargazeFaucetState: Writable<DydxFaucetState> = writable({
44+
kind: "IDLE"
45+
})
46+
let turnstileToken = ""
47+
let resetTurnstile: () => void
48+
let showTurnstile = false
49+
50+
const verifyWithTurnstile = () => {
51+
if ($stargazeFaucetState.kind === "IDLE") {
52+
showTurnstile = true
53+
stargazeFaucetState.set({ kind: "VERIFYING" })
54+
resetTurnstile?.()
55+
}
56+
}
57+
58+
const requestStarsFromFaucet = async () => {
59+
console.info("stargazeAddress: ", $stargazeAddress)
60+
61+
if ($stargazeFaucetState.kind === "VERIFIED") {
62+
stargazeFaucetState.set({
63+
kind: "SUBMITTING",
64+
captchaToken: turnstileToken
65+
})
66+
}
67+
68+
if ($stargazeFaucetState.kind === "SUBMITTING") {
69+
try {
70+
const result = await request(URLS().GRAPHQL, faucetUnoMutation2, {
71+
chainId: "elgafar-1",
72+
denom: "ustars",
73+
address: $stargazeAddress,
74+
captchaToken: $stargazeFaucetState.captchaToken
75+
})
76+
77+
if (!result.send) {
78+
stargazeFaucetState.set({
79+
kind: "RESULT_ERR",
80+
error: "Empty faucet response"
81+
})
82+
turnstileToken = ""
83+
showTurnstile = false
84+
return
85+
}
86+
87+
if (result.send.startsWith("ERROR")) {
88+
console.error(result.send)
89+
stargazeFaucetState.set({
90+
kind: "RESULT_ERR",
91+
error: result.send.endsWith("ratelimited")
92+
? "You already got USTARS from the faucet today. Try again in 24 hours."
93+
: "Error from faucet"
94+
})
95+
turnstileToken = ""
96+
showTurnstile = false
97+
return
98+
}
99+
100+
stargazeFaucetState.set({
101+
kind: "RESULT_OK",
102+
message: result.send
103+
})
104+
turnstileToken = ""
105+
showTurnstile = false
106+
} catch (error) {
107+
console.error(error)
108+
stargazeFaucetState.set({
109+
kind: "RESULT_ERR",
110+
error: `Faucet error: ${error}`
111+
})
112+
turnstileToken = ""
113+
showTurnstile = false
114+
}
115+
}
116+
}
117+
118+
const resetVerification = () => {
119+
if ($stargazeFaucetState.kind === "VERIFICATION_FAILED") {
120+
turnstileToken = ""
121+
showTurnstile = false
122+
stargazeFaucetState.set({ kind: "IDLE" })
123+
}
124+
}
125+
126+
const handleTurnstileCallback = (
127+
e: CustomEvent<{ token: string; preClearanceObtained: boolean }>
128+
) => {
129+
turnstileToken = e.detail.token
130+
if ($stargazeFaucetState.kind === "VERIFYING") {
131+
stargazeFaucetState.set({ kind: "VERIFIED" })
132+
}
133+
}
134+
135+
const handleTurnstileError = (e: CustomEvent<{ code: string }>) => {
136+
if ($stargazeFaucetState.kind === "VERIFYING") {
137+
stargazeFaucetState.set({
138+
kind: "VERIFICATION_FAILED",
139+
error: `Verification error: ${e.detail.code}`
140+
})
141+
}
142+
}
143+
144+
let stargazeBalance = createQuery(
145+
derived(stargazeAddress, $stargazeAddress => ({
146+
queryKey: ["stargaze-balance", $stargazeAddress],
147+
enabled: $stargazeAddress?.indexOf("stars") === 0,
148+
refetchInterval: () => ($stargazeAddress?.indexOf("stars") === 0 ? 5_000 : false),
149+
queryFn: async () =>
150+
await getCosmosChainBalances({
151+
walletAddress: `${$stargazeAddress}`,
152+
url: "https://stargaze-testnet-api.polkachu.com"
153+
}),
154+
select: (data: AwaitedReturnType<typeof getCosmosChainBalances>) =>
155+
data?.find(balance => balance?.symbol === "ustars")
156+
}))
157+
)
158+
</script>
159+
160+
<!-- stargaze faucet -->
161+
<Card.Root
162+
class={cn(
163+
"w-full max-w-lg rounded-lg font-sans",
164+
"bg-[#181825] text-[#99f0cf] dark:bg-[#2D2D44]/50 dark:text-[#99f0cf]",
165+
)}
166+
>
167+
<Card.Header>
168+
<Card.Title class="flex justify-between select-none">
169+
<p class="flex gap-x-3">
170+
<a
171+
target="_blank"
172+
rel="noopener noreferrer"
173+
href="https://www.stargaze.zone/"
174+
>
175+
<img src="/images/logo/stargaze-logo.svg" alt="" class="w-10" />
176+
</a>
177+
Stargaze Faucet
178+
</p>
179+
</Card.Title>
180+
</Card.Header>
181+
<Card.Content>
182+
{#if $stargazeFaucetState.kind === "RESULT_OK"}
183+
<p>
184+
Tokens sent: <a
185+
target="_blank"
186+
rel="noopener noreferrer"
187+
href={`https://testnet.ping.pub/stargaze/tx/${$stargazeFaucetState.message}`}
188+
>
189+
<Truncate
190+
class="underline"
191+
value={$stargazeFaucetState.message}
192+
type="hash"
193+
/>
194+
</a>
195+
</p>
196+
{:else if $stargazeFaucetState.kind === "RESULT_ERR"}
197+
<p class="mb-4">
198+
{$stargazeFaucetState.error}
199+
</p>
200+
<Button
201+
class={cn(
202+
"bg-[#99f0cf] text-[#010001] dark:bg-[#99f0cf] dark:text-[#010001]",
203+
"disabled:opacity-100 disabled:bg-black/20 rounded-md focus:ring-0 focus-visible:ring-0",
204+
)}
205+
on:click={() => stargazeFaucetState.set({ kind: "IDLE" })}
206+
>
207+
Retry
208+
</Button>
209+
{:else}
210+
<form
211+
action="?"
212+
method="POST"
213+
name="faucet-form"
214+
class="flex flex-col w-full gap-4"
215+
on:submit|preventDefault
216+
>
217+
<div>
218+
<Label for="address">Address</Label>
219+
<div class="flex items-start gap-2">
220+
<div class="w-full">
221+
<div class="relative w-full mb-2">
222+
<Input
223+
type="text"
224+
minlength={44}
225+
maxlength={44}
226+
readonly={true}
227+
required={true}
228+
autocorrect="off"
229+
id="stargaze-address"
230+
autocomplete="off"
231+
spellcheck="false"
232+
autocapitalize="none"
233+
value={$stargazeAddress}
234+
data-lpignore={true}
235+
data-1p-ignore={true}
236+
placeholder="stars14ea6…"
237+
name="stargaze-wallet-address"
238+
class={cn(
239+
"bg-[#2D2D44] text-[#ffffff] dark:bg-[#181825] dark:text-[#ffffff]",
240+
"disabled:opacity-100 disabled:bg-black/20 rounded-md focus:ring-0 focus-visible:ring-0",
241+
)}
242+
pattern={createCosmosSdkAddressRegex({ prefix: "stargaze" })
243+
.source}
244+
/>
245+
</div>
246+
<div class="flex justify-between px-1">
247+
<div class="text-xs">
248+
<p>
249+
{#if $stargazeAddress?.indexOf("stars") === 0 && $stargazeBalance.status === "success"}
250+
<!--
251+
<span>Balance: </span>
252+
{$stargazeBalance?.data?.balance ?? 0}
253+
ustars
254+
!-->
255+
{:else}
256+
Connect cosmos wallet
257+
{/if}
258+
</p>
259+
</div>
260+
</div>
261+
</div>
262+
</div>
263+
</div>
264+
{#if showTurnstile}
265+
<Turnstile
266+
siteKey="0x4AAAAAAA-eVs5k0b8Q1dl5"
267+
on:callback={handleTurnstileCallback}
268+
on:error={handleTurnstileError}
269+
theme="auto"
270+
size="normal"
271+
bind:reset={resetTurnstile}
272+
/>
273+
{/if}
274+
<div class="flex flex-row items-center gap-4">
275+
{#if $stargazeFaucetState.kind === "IDLE" || $stargazeFaucetState.kind === "VERIFYING"}
276+
<Button
277+
type="button"
278+
on:click={(event) => {
279+
event.preventDefault();
280+
verifyWithTurnstile();
281+
}}
282+
disabled={!isValidBech32Address($stargazeAddress) ||
283+
$stargazeFaucetState.kind === "VERIFYING"}
284+
class={cn(
285+
"min-w-[110px] disabled:cursor-not-allowed disabled:opacity-50 rounded-md",
286+
"bg-[#99f0cf] text-[#010001] dark:bg-[#99f0cf] dark:text-[#010001]",
287+
)}
288+
>
289+
Verify
290+
{#if $stargazeFaucetState.kind === "VERIFYING"}
291+
<span class="ml-2">
292+
<SpinnerSVG className="w-4 h-4" />
293+
</span>
294+
{/if}
295+
</Button>
296+
{:else if $stargazeFaucetState.kind === "VERIFIED" || $stargazeFaucetState.kind === "SUBMITTING" || true}
297+
<Button
298+
type="button"
299+
on:click={(event) => {
300+
event.preventDefault();
301+
requestStarsFromFaucet();
302+
}}
303+
disabled={$stargazeFaucetState.kind === "SUBMITTING"}
304+
class={cn(
305+
"min-w-[110px] disabled:cursor-not-allowed disabled:opacity-50 rounded-md",
306+
"bg-[#99f0cf] text-[#010001] dark:bg-[#99f0cf] dark:text-[#010001]",
307+
)}
308+
>
309+
Submit
310+
{#if $stargazeFaucetState.kind === "SUBMITTING"}
311+
<span class="ml-2">
312+
<SpinnerSVG className="w-4 h-4" />
313+
</span>
314+
{/if}
315+
</Button>
316+
{:else if $stargazeFaucetState.kind === "VERIFICATION_FAILED"}
317+
<Button
318+
type="button"
319+
on:click={(event) => {
320+
event.preventDefault();
321+
resetVerification();
322+
}}
323+
class={cn(
324+
"min-w-[110px] disabled:cursor-not-allowed disabled:opacity-50 rounded-md",
325+
"bg-[#99f0cf] text-[#ffffff] dark:bg-[#99f0cf] dark:text-[#ffffff]",
326+
)}
327+
>
328+
Reset
329+
</Button>
330+
<p class="text-xs text-red-500">{$stargazeFaucetState.error}</p>
331+
{/if}
332+
<p class="text-[10px]">
333+
This faucet is protected by Cloudflare Turnstile. Funds provided by
334+
<a
335+
class="text-[#13ffa4]"
336+
target="_blank"
337+
rel="noopener noreferrer"
338+
href="https://www.stargaze.zone/"
339+
>
340+
stargaze.zone.
341+
</a>
342+
</p>
343+
</div>
344+
</form>
345+
{/if}
346+
</Card.Content>
347+
</Card.Root>

0 commit comments

Comments
 (0)