Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various updates and fixes #21

Merged
merged 8 commits into from
Oct 6, 2023
Merged
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
10 changes: 9 additions & 1 deletion getTez/getTez.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,10 @@ const verifySolution = async ({
}

/* Entrypoint */
const formatAmount = (amount: number) =>
amount.toLocaleString(undefined, {
maximumFractionDigits: 7,
})

const getTez = async (args: GetTezArgs) => {
const validatedArgs = await validateArgs(args)
Expand All @@ -317,7 +321,11 @@ const getTez = async (args: GetTezArgs) => {
)

if (!(args.amount >= minTez && args.amount <= maxTez)) {
handleError(`Amount must be between ${minTez} and ${maxTez} tez.`)
handleError(
`Amount must be between ${formatAmount(minTez)} and ${formatAmount(
maxTez
)} tez.`
)
}

if (!challengesEnabled) {
Expand Down
120 changes: 59 additions & 61 deletions src/components/Faucet/FaucetRequestButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import ReCAPTCHA from "react-google-recaptcha"

import PowWorker from "../../powWorker?worker&inline"
import Config from "../../Config"
import { autoSelectInputText } from "../../lib/Utils"
import {
Challenge,
ChallengeResponse,
Expand All @@ -15,6 +16,24 @@ import {
} from "../../lib/Types"

const { minTez, maxTez } = Config.application
// Compute the step for the Tezos amount range slider.
const tezRangeStep = (() => {
const magnitude = Math.floor(Math.log10(maxTez))

// When maxTez is greater than 1
if (maxTez > 1) {
return Math.max(0.5, Math.pow(10, magnitude - 2))
}

// When maxTez is less than or equal to 1 and minTez is fractional
const minMagnitude = Math.abs(Math.floor(Math.log10(minTez)))
return Math.max(0.001, 1 / Math.pow(10, minMagnitude))
})()

const formatAmount = (amount: number) =>
amount.toLocaleString(undefined, {
maximumFractionDigits: 5,
})

export default function FaucetRequestButton({
address,
Expand All @@ -28,10 +47,17 @@ export default function FaucetRequestButton({
status: StatusContext
}) {
const [amount, setAmount] = useState<number>(minTez)
const formattedAmount = amount.toLocaleString()
const formattedAmount = formatAmount(amount)

const [isLocalLoading, setLocalLoading] = useState<boolean>(false)
const recaptchaRef: RefObject<ReCAPTCHA> = useRef(null)

// Ensure that `isLocalLoading` is false if user canceled pow worker.
// `status.isLoading` will be false.
useEffect(() => {
!status.isLoading && setLocalLoading(false)
}, [status.isLoading])

const startLoading = () => {
status.setLoading(true)
setLocalLoading(true)
Expand All @@ -53,24 +79,29 @@ export default function FaucetRequestButton({
setLocalLoading(false)
}

const validateAmount = (amount: number) =>
amount >= minTez && amount <= maxTez

const updateAmount = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = Number(e.target.value)
if (value >= minTez && value <= maxTez) {
const value = Number(e.target.value.slice(0, 16))
if (value === 0 || validateAmount(value)) {
setAmount(value)
}
}

const validateChallenge = (data: Partial<Challenge>): data is Challenge =>
!!(
data.challenge &&
data.difficulty &&
data.challengeCounter &&
data.challengesNeeded
)

const getProgress = (challengeCounter: number, challengesNeeded: number) =>
String(
Math.min(99, Math.floor((challengeCounter / challengesNeeded) * 100))
)

// Ensure that `isLocalLoading` is false if user canceled pow worker.
// `status.isLoading` will be false.
useEffect(() => {
!status.isLoading && setLocalLoading(false)
}, [status.isLoading])

const execCaptcha = async () => {
const captchaToken: any = await recaptchaRef.current?.executeAsync()
recaptchaRef.current?.reset()
Expand Down Expand Up @@ -111,29 +142,17 @@ export default function FaucetRequestButton({
return verifySolution({ solution: "", nonce: 0 })
}

let { challenge, difficulty, challengeCounter, challengesNeeded } =
await getChallenge()
let challengeRes = await getChallenge()

const powWorker = new PowWorker()
status.setPowWorker(powWorker)

try {
while (
challenge &&
difficulty &&
challengeCounter &&
challengesNeeded
) {
const powSolution = await solvePow(
{ challenge, difficulty, challengeCounter, challengesNeeded },
powWorker
)

const response = await verifySolution(powSolution)
challenge = response.challenge
difficulty = response.difficulty
challengeCounter = response.challengeCounter
challengesNeeded = response.challengesNeeded
while (validateChallenge(challengeRes)) {
const powSolution = await solvePow(challengeRes, powWorker)

const newChallengeRes = await verifySolution(powSolution)
challengeRes = newChallengeRes
}
} finally {
powWorker.terminate()
Expand Down Expand Up @@ -162,7 +181,7 @@ export default function FaucetRequestButton({
{ timeout: 5000 }
)

if (data.challenge && data.difficulty && data.challengeCounter) {
if (validateChallenge(data)) {
return data
} else {
stopLoadingError(data?.message || "Error getting challenge")
Expand Down Expand Up @@ -195,7 +214,7 @@ export default function FaucetRequestButton({
)

// If there is another challenge
if (data.challenge && data.difficulty && data.challengeCounter) {
if (validateChallenge(data)) {
return data
} else if (data.txHash) {
// All challenges were solved
Expand All @@ -217,28 +236,7 @@ export default function FaucetRequestButton({
return {}
}

const computeStep = () => {
const magnitude = Math.floor(Math.log10(maxTez))

switch (magnitude) {
case 1:
case 2:
return 1
case 3:
return 10
case 4:
return 100
case 5:
return 1_000
case 6:
return 10_000
default:
return 100_000
}
}

const currentStep = computeStep()
const adjustedMin = Math.ceil(minTez / currentStep) * currentStep
const step = amount === tezRangeStep ? minTez : tezRangeStep

return (
<>
Expand All @@ -253,26 +251,22 @@ export default function FaucetRequestButton({
<Form.Label>Select Tez Amount</Form.Label>
<Row className="mb-2">
<Col xs="auto" className="pe-0">
<Form.Label className="fw-bold">
{minTez.toLocaleString()}
</Form.Label>
<Form.Label className="fw-bold">{formatAmount(minTez)}</Form.Label>
</Col>

<Col>
<Form.Range
min={adjustedMin}
min={step}
max={maxTez}
step={currentStep}
step={step}
value={amount}
disabled={disabled}
onChange={updateAmount}
/>
</Col>

<Col xs="auto" className="ps-0">
<Form.Label className="fw-bold">
{maxTez.toLocaleString()}
</Form.Label>
<Form.Label className="fw-bold">{formatAmount(maxTez)}</Form.Label>
</Col>
</Row>

Expand All @@ -285,12 +279,16 @@ export default function FaucetRequestButton({
value={amount}
disabled={disabled}
onChange={updateAmount}
onFocus={(e) => e.target.select()}
onClick={autoSelectInputText}
/>
</Col>

<Col xs={12} sm={6} className="d-flex justify-content-sm-end">
<Button variant="primary" disabled={disabled} onClick={getTez}>
<Button
variant="primary"
disabled={disabled || !validateAmount(amount)}
onClick={getTez}
>
<DropletFill />
&nbsp;
{isLocalLoading
Expand Down
6 changes: 4 additions & 2 deletions src/components/Faucet/FaucetToInputRequest.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { validateKeyHash } from "@taquito/utils"
import { ChangeEvent, useState } from "react"
import { Form } from "react-bootstrap"
import { Network, StatusContext } from "../../lib/Types"
import { autoSelectInputText } from "../../lib/Utils"
import FaucetRequestButton from "./FaucetRequestButton"

import { Network, StatusContext } from "../../lib/Types"

export default function FaucetToInputRequest({
network,
status,
Expand Down Expand Up @@ -40,7 +42,7 @@ export default function FaucetToInputRequest({
className={inputClass}
disabled={status.isLoading}
onChange={handleInput}
onFocus={(e) => e.target.select()}
onClick={autoSelectInputText}
/>
<Form.Control.Feedback type="invalid" className="position-absolute">
Invalid address
Expand Down
2 changes: 1 addition & 1 deletion src/components/Faucet/UserInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ function UserInfo({
displayBalance: boolean
}) {
return (
<Card bg="light" text="dark" className="d-inline-block mw-100">
<Card bg="light" text="dark" className="d-inline-block mw-100 mb-1">
<Card.Body className="d-flex align-items-center p-1">
<Wallet2 size={20} />
<span className="text-truncate fw-bold ms-2 me-2">
Expand Down
34 changes: 14 additions & 20 deletions src/lib/Utils.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function minifyTezosAddress(address: string): string {
export const minifyTezosAddress = (address: string): string => {
if (address)
return `${address.substring(0, 4)}...${address.substring(
address.length - 4,
Expand All @@ -7,31 +7,25 @@ function minifyTezosAddress(address: string): string {
return address
}

function splitNumber(x: number) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
}
const splitNumber = (x: number) =>
x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")

function roundBalance(balance: number): number {
return Math.trunc(balance / 10000) / 100
}
export const roundBalance = (balance: number): number =>
Math.trunc(balance / 10000) / 100

function toBalance(balance: number): string {
export const toBalance = (balance: number): string => {
let roundedBalance = roundBalance(balance)
return splitNumber(roundedBalance)
}

function getMainData(data: string): string {
return data.split("").reverse().join("")
}
export const getMainData = (data: string): string =>
data.split("").reverse().join("")

function getPlainData(data: string): string {
return Buffer.from(data, "base64").toString("utf8")
}
export const getPlainData = (data: string): string =>
Buffer.from(data, "base64").toString("utf8")

export {
minifyTezosAddress,
roundBalance,
getMainData,
getPlainData,
toBalance,
export const autoSelectInputText = (e: React.SyntheticEvent) => {
const el = e.target as HTMLInputElement
el.focus()
el.select()
}
Loading