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

Testnet faucet improvements #110

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions testnet-faucet/.env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
NEXT_PUBLIC_EXPLORER_URL=
NEXT_PUBLIC_USAGE_LIMIT_IN_HOURS=
NEXT_PUBLIC_CLOUDFLARE_TURNSTILE_SITE_KEY=
CLOUDFLARE_TURNSTILE_SECRET=
EXPLORER_API_URL=
SENDER_ADDRESS=
NODE_URL=
Expand Down
20 changes: 18 additions & 2 deletions testnet-faucet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,28 @@ Example enviroment variable values
NEXT_PUBLIC_EXPLORER_URL=https://ccdexplorer.io/
NEXT_PUBLIC_USAGE_LIMIT_IN_HOURS=1
NEXT_PUBLIC_CLOUDFLARE_TURNSTILE_SITE_KEY=3x00000000000000000000FF
CLOUDFLARE_TURNSTILE_SECRET=1x0000000000000000000000000000000AA
EXPLORER_API_URL=https://wallet-proxy.testnet.concordium.com/v1
SENDER_ADDRESS=4eDtVqZrkmcNEFziEMSs8S2anvkH5KnsYK4MhwedwGWK1pmjZe
NODE_URL=node.testnet.concordium.com
NODE_URL=grpc.testnet.concordium.com
NODE_PORT=20000
CCD_DEFAULT_AMOUNT=1
SENDER_PRIVATE_KEY=12...34
```

Note: The `3x00000000000000000000FF` value in the `NEXT_PUBLIC_CLOUDFLARE_TURNSTILE_SITE_KEY` is a site key provided by Cloudflare for testing purposes; it works fine locally. For setting up the Cloudflare Turnstile service in production, please refer to this [guide](docs/turnstile/SETUP.md).
Note: The values `3x00000000000000000000FF` for `NEXT_PUBLIC_CLOUDFLARE_TURNSTILE_SITE_KEY` and `1x0000000000000000000000000000000AA` for `CLOUDFLARE_TURNSTILE_SECRET` are provided by Cloudflare for testing purposes. They work correctly in a local environment. For setting up the Cloudflare Turnstile service in production, please refer to this [guide](docs/turnstile/SETUP.md).

## Available Scripts

This project includes several Yarn scripts to streamline development and production tasks. Below is a description of each script:

**`yarn dev`**: Runs the development server using Next.js. Use this script during development to see your changes live.
The development server will be available at `http://localhost:3000` by default.

**`yarn build`**: Compiles and optimizes your application for production. Run this script before deploying your project.

**`yarn start`**: Starts the optimized application in production mode. This requires running `yarn build` beforehand.

**`yarn lint`**: Analyzes your codebase for potential errors and enforces coding standards using ESLint.

Feel free to use these scripts as part of your development workflow!
5 changes: 3 additions & 2 deletions testnet-faucet/docs/turnstile/SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ The purpose of this guide is to show how to configure the production domain in V
6. In your Cloudflare dashboard. Go to Turnstile and fill it in as follows.
![step6](images/step6.png)

7. Click on create and copy the sitekey
7. Click on create and copy the sitekey and the secret

![step7](images/step7.png)

8. Go to the project on Vercel, then Settings / Environment variables and update the variable NEXT_PUBLIC_CLOUDFLARE_TURNSTILE_SITE_KEY
8. Go to the project on Vercel, then navigate to Settings > Environment Variables and update the variables NEXT_PUBLIC_CLOUDFLARE_TURNSTILE_SITE_KEY and CLOUDFLARE_TURNSTILE_SECRET.
![step8](images/step8.png)

9. Go to Deployments and click on the three points of the last deployment and then click on redeploy.
Expand Down
Binary file modified testnet-faucet/docs/turnstile/images/step7.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 12 additions & 12 deletions testnet-faucet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,27 @@
"lint": "next lint"
},
"dependencies": {
"@concordium/web-sdk": "^7.5.0",
"@headlessui/react": "^2.1.2",
"@concordium/web-sdk": "^7.5.1",
"@headlessui/react": "^2.2.0",
"@marsidev/react-turnstile": "^0.7.2",
"date-fns": "^3.6.0",
"next": "14.2.5",
"react": "^18",
"react-dom": "^18",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"rettiwt-api": "^3.1.1",
"usehooks-ts": "^3.1.0"
},
"devDependencies": {
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"eslint": "^8",
"@types/node": "^20.17.1",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"eslint": "^8.57.1",
"eslint-config-next": "14.2.5",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.1",
"postcss": "^8",
"eslint-plugin-import": "^2.31.0",
"postcss": "^8.4.47",
"prettier": "^3.3.3",
"tailwindcss": "^3.4.1",
"typescript": "^5"
"tailwindcss": "^3.4.14",
"typescript": "^5.6.3"
}
}
2 changes: 1 addition & 1 deletion testnet-faucet/src/lib/createGPRCClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ export default function createGRPCNodeClient(): ConcordiumGRPCNodeClient {
if (!NODE_PORT || !NODE_URL) {
benya7 marked this conversation as resolved.
Show resolved Hide resolved
throw new Error('NDDE_PORT or NODE_URL env vars not defined.');
}
return new ConcordiumGRPCNodeClient(NODE_URL as string, Number(NODE_PORT), credentials.createInsecure());
return new ConcordiumGRPCNodeClient(NODE_URL as string, Number(NODE_PORT), credentials.createSsl());
}
21 changes: 21 additions & 0 deletions testnet-faucet/src/lib/validateTurnsiteToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { TurnstileServerValidationResponse } from '@marsidev/react-turnstile';

const verifyEndpoint = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';

export default async function validateTurnsiteToken(token: string) {
const { CLOUDFLARE_TURNSTILE_SECRET } = process.env;
if (!CLOUDFLARE_TURNSTILE_SECRET) {
throw new Error('CLOUDFLARE_TURNSTILE_SECRET env vars not defined.');
}
const res = await fetch(verifyEndpoint, {
method: 'POST',
body: `secret=${encodeURIComponent(CLOUDFLARE_TURNSTILE_SECRET)}&response=${encodeURIComponent(token)}`,
headers: {
'content-type': 'application/x-www-form-urlencoded',
},
});

const data = (await res.json()) as TurnstileServerValidationResponse;

return data.success;
}
16 changes: 13 additions & 3 deletions testnet-faucet/src/pages/api/validateAndClaim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import checkUsageLimit from '@/lib/checkUsageLimit';
import createAccountTransaction from '@/lib/createAccountTrasantion';
import createGRPCNodeClient from '@/lib/createGPRCClient';
import getSenderAccountSigner from '@/lib/getSenderAccountSigner';
import validateTurnsiteToken from '@/lib/validateTurnsiteToken';

interface IBody {
humanToken: string;
receiver: string;
XPostId: string;
}
Expand All @@ -28,14 +30,22 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
throw new Error('SENDER_ADDRESS env vars undefined.');
}

const { XPostId, receiver } = req.body as IBody;
const { humanToken, receiver, XPostId } = req.body as IBody;

if (!XPostId || !receiver) {
if (!humanToken || !XPostId || !receiver) {
return res.status(400).json({
error: 'Missing parameters. Please provide XPostId, and receiver params.',
error: 'Missing parameters. Please provide humanToken, receiver and XPostId params.',
});
}
try {
const isHumanTokenValid = await validateTurnsiteToken(humanToken);

if (!isHumanTokenValid) {
return res.status(403).json({
error: `The provided token in the human verification is not valid.`,
});
}

const isAllowed = await checkUsageLimit(receiver);
if (!isAllowed) {
return res.status(401).json({
Expand Down
11 changes: 6 additions & 5 deletions testnet-faucet/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,17 @@ const getLatestTransactions = async () => {
}
};

const validateAndClaim = async (XPostId: string | undefined, receiver: string) => {
const validateAndClaim = async (humanToken: string, receiver: string, XPostId: string | undefined) => {
try {
const response = await fetch('/api/validateAndClaim', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
XPostId,
humanToken,
receiver,
XPostId,
}),
});

Expand Down Expand Up @@ -128,11 +129,11 @@ export default function Home() {
'width=500,height=500',
);

const handleVerifyTweetAndSendTokens = async () => {
const handleVerifyTweetAndSendTokens = async (token: string) => {
setTurnstileOpen(false);
setIsVerifyLoading(true);
try {
const response = await validateAndClaim(XPostId, address);
const response = await validateAndClaim(token, address, XPostId);

if (response.ok && !response.data.error) {
setIsValidVerification(true);
Expand Down Expand Up @@ -366,7 +367,7 @@ export default function Home() {
ref={turnstileRef}
siteKey={process.env.NEXT_PUBLIC_CLOUDFLARE_TURNSTILE_SITE_KEY as string}
options={{ theme: 'light', size: 'normal' }}
onSuccess={() => setTimeout(handleVerifyTweetAndSendTokens, 1500)}
onSuccess={(token) => setTimeout(() => handleVerifyTweetAndSendTokens(token), 1000)}
/>
</DialogPanel>
</div>
Expand Down
Loading