Skip to content

Commit

Permalink
feat(core): testnet faucet page with recaptcha (#344)
Browse files Browse the repository at this point in the history
* feat(ui-ux): testnet faucet page

* feat(ui-ux): add recaptcha v2

* fix(ui-ux): hide /faucet navigation and page if not in Testnet env

* feat(server): nestjs setup

* remove nvmrc

* fix(ui-ux): evmAddress input and captcha validation

* code refactor

* code cleanup

* feature(api): added api for faucet to allocate fund to user (#346)

* feature(api): added api for faucet to allocate fund to user

* added e2e test

* fixed pr comments

* added e2e for ratelimiting

* added invalid address test case

* fix lint

* fix typo

* fix lint

* fix ci

* fix ci

* fix ci

* fix ci

* fix ci

* fix ci

* fix ci

* fix ci

* moved faucet dir to apps/web

* changed to getRpcUrl instead of getBaseUrl

* api testing

* fixed the cors issue

* removed comment

* print out transaction hash or error

* ran prettier

* removed unused code

* fixed lint issues

* add import { NestFactory } from '@nestjs/core';

* fix import problems

* feat: use user input wallet address

* fix: use current connection

* fix: cors

* fix: use MetascanServerApp

* fix: update allowedHeaders MetascanServerApp

* feat(server): recaptcha guard on faucet route

* feat(ui-ux): handle recaptcha validation when sending funds

* add recaptcha public site key in /web/.env file

* fix format

* fix(server): private validateRecaptcha guard method

* update comments

* feat(ui-ux): added ux for faucet (#357)

* added loader and link to metascan

* fixing issue that txnHash does not exist on metascan yet

* reverted linking to metascan

* revert unnecessayr change for sectionDesc

* added more text

* Update apps/web/src/pages/faucet/index.tsx

Co-authored-by: Harsh R <53080940+fullstackninja864@users.noreply.github.com>

* did UI comments & ran prettier

* changed to using react-icon

* removed react-spinners

* set isLoading back to false

* ui comments

* Update apps/web/src/pages/faucet/index.tsx

Co-authored-by: Harsh R <53080940+fullstackninja864@users.noreply.github.com>

* Update apps/web/src/pages/faucet/index.tsx

Co-authored-by: Harsh R <53080940+fullstackninja864@users.noreply.github.com>

* used animate-spin

* used tailwind color

* revert isLoading value to false

* changed to divs

* will do button variants in a diff PR

* added invalid address error text

* enable button even after errors

* print default error msg

* made recaptcha dark

* minor fixes

---------

Co-authored-by: Harsh R <53080940+fullstackninja864@users.noreply.github.com>
Co-authored-by: Harsh <harshrathi.dev@gmail.com>

* fixed package.json

* updated ethers version

* UI fixes

* fix lint

* hide faucet page

* remove log

---------

Co-authored-by: Lyka Labrada <lykalabrada@gmail.com>
Co-authored-by: Harsh R <53080940+fullstackninja864@users.noreply.github.com>
Co-authored-by: pierregee <pierre@cakedefi.com>
Co-authored-by: nattadex <elocinnat99@gmail.com>
Co-authored-by: Harsh <harshrathi.dev@gmail.com>
  • Loading branch information
6 people authored Nov 24, 2023
1 parent e0f5721 commit a193692
Show file tree
Hide file tree
Showing 37 changed files with 13,281 additions and 6,831 deletions.
2 changes: 1 addition & 1 deletion apps/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
"@nestjs/throttler": "^5.0.1",
"@waveshq/standard-defichain-jellyfishsdk": "^2.6.1",
"@waveshq/walletkit-core": "^1.3.4",
"axios": "^1.5.0",
"axios": "^1.6.0",
"bignumber.js": "^9.1.2",
"cache-manager": "^5.2.4",
"class-validator": "^0.14.0",
Expand Down
4,887 changes: 3,040 additions & 1,847 deletions apps/server/pnpm-lock.yaml

Large diffs are not rendered by default.

65 changes: 65 additions & 0 deletions apps/server/src/MetascanServerApp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { NestFactory } from '@nestjs/core';
import { NestFastifyApplication } from '@nestjs/platform-fastify';

import { AppModule } from './app.module';

/**
* App which starts the default Metascan Server Application
*/
export class MetascanServerApp<App extends NestFastifyApplication = NestFastifyApplication> {
protected app?: App;

constructor(protected readonly module: any) {}

async createNestApp(): Promise<App> {
const app = await NestFactory.create(AppModule);
await this.configureApp(app);
// @ts-ignore
return app;
}

async configureApp(app): Promise<void> {
app.enableCors({
allowedHeaders: '*',
methods: ['GET', 'PUT', 'POST', 'DELETE'],
maxAge: 60 * 24 * 7,
origin:
process.env.NODE_ENV === 'production'
? [
'https://meta.defiscan.live/',
/https:\/\/([^.]*.\.)*defimetascan\.app/, // allow all subdomains of defimetascan
/https:\/\/([^.]*.)--defimetascan\.netlify\.app/, // allow all netlify preview deployments
/https?:\/\/localhost(:\d+)?/, // allow localhost connection
]
: '*',
});
}

/**
* Run any additional initialisation steps before starting the server.
* If there are additional steps, can be overriden by any extending classes
*/
async init() {
this.app = await this.createNestApp();
return this.app.init();
}

async start(): Promise<App> {
const app = await this.init();

const PORT = process.env.PORT || 3001;
await app.listen(PORT).then(() => {
// eslint-disable-next-line no-console
console.log(`Started server on port ${PORT}`);
});
return app;
}

/**
* Stop NestJs and un-assign this.app
*/
async stop(): Promise<void> {
await this.app?.close();
this.app = undefined;
}
}
6 changes: 4 additions & 2 deletions apps/server/src/faucet/FaucetController.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Controller, Get, HttpException, Inject, Param, Query, UseInterceptors } from '@nestjs/common';
import { Controller, HttpException, Inject, Param, Post, Query, UseGuards, UseInterceptors } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { EnvironmentNetwork } from '@waveshq/walletkit-core';
import { TransactionResponse } from 'ethers';

import { RecaptchaGuard } from '../recaptcha/RecaptchaGuard';
import { AddressValidationInterceptor } from './AddressValidationInterceptor';
import { DefaultNetworkInterceptor } from './DefaultNetworkInterceptor';
import { FaucetService } from './FaucetService';
Expand All @@ -16,7 +17,8 @@ export class FaucetController {
private configService: ConfigService,
) {}

@Get(':address')
@Post(':address')
@UseGuards(RecaptchaGuard)
@UseInterceptors(AddressValidationInterceptor, DefaultNetworkInterceptor)
async sendFunds(
@Param('address') address: string,
Expand Down
6 changes: 4 additions & 2 deletions apps/server/src/faucet/FaucetModule.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { HttpModule } from '@nestjs/axios';
import { CacheModule } from '@nestjs/cache-manager';
import { Module } from '@nestjs/common';

import { RecaptchaGuard } from '../recaptcha/RecaptchaGuard';
import { FaucetController } from './FaucetController';
import { FaucetService } from './FaucetService';

@Module({
imports: [CacheModule.register()],
imports: [CacheModule.register(), HttpModule],
controllers: [FaucetController],
providers: [FaucetService],
providers: [FaucetService, RecaptchaGuard],
})
export class FaucetModule {}
1 change: 1 addition & 0 deletions apps/server/src/faucet/FaucetService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export class FaucetService {
}

async sendFundsToUser(address: string, amount: string, network: EnvironmentNetwork): Promise<TransactionResponse> {
// Send funds to user if recaptcha validation is successful
const evmProviderService = new EVMProviderService(network);
const wallet = new ethers.Wallet(this.privateKey, evmProviderService.provider);
const nonce = await evmProviderService.provider.getTransactionCount(wallet.address);
Expand Down
13 changes: 4 additions & 9 deletions apps/server/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import { NestFactory } from '@nestjs/core';

import { AppModule } from './app.module';
import { MetascanServerApp } from './MetascanServerApp';

async function bootstrap() {
const app = await NestFactory.create(AppModule);
const PORT = process.env.PORT || 5741;
// eslint-disable-next-line @typescript-eslint/no-floating-promises
app.listen(PORT).then(() => {
// eslint-disable-next-line no-console
console.log(`Started server on port ${PORT}`);
});
const app = new MetascanServerApp(AppModule);
await app.start();
}

void bootstrap();
40 changes: 40 additions & 0 deletions apps/server/src/recaptcha/RecaptchaGuard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { HttpService } from '@nestjs/axios';
import { ExecutionContext, Injectable, Logger } from '@nestjs/common';
import { Request } from 'express';

@Injectable()
export class RecaptchaGuard {
private readonly logger: Logger;

constructor(private readonly httpService: HttpService) {
this.logger = new Logger(RecaptchaGuard.name);
}

async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest<Request>();
return this.validateRecaptcha(request);
}

private async validateRecaptcha(request: Request): Promise<boolean> {
const response = request.body.recaptchaValue;

if (!response) {
this.logger.log('Invalid body in recaptcha request');
return false;
}

const { data } = await this.httpService
.post(
`https://www.google.com/recaptcha/api/siteverify`,
null, // Since we're sending data in the body, set it to null
{
params: {
secret: process.env.SECRET_KEY,
response,
},
},
)
.toPromise();
return data.success;
}
}
5 changes: 4 additions & 1 deletion apps/web/.env
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
NEXT_PUBLIC_RPC_URL_MAINNET="https://blockscout.mainnet.ocean.jellyfishsdk.com"
NEXT_PUBLIC_RPC_URL_TESTNET="https://blockscout.testnet.ocean.jellyfishsdk.com"
NEXT_PUBLIC_RPC_URL_CHANGI="https://blockscout.changi.ocean.jellyfishsdk.com"
NEXT_PUBLIC_RPC_URL_CHANGI="https://blockscout.changi.ocean.jellyfishsdk.com"
NEXT_PUBLIC_SERVER_URL="http://localhost:3001/"

NEXT_PUBLIC_SITE_KEY="6LeeoO8oAAAAALPSYZr1_Itr9bBzzQBVDjgjMT0-"
6 changes: 3 additions & 3 deletions apps/web/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ const securityHeaders = [
value:
`default-src 'none';` +
`base-uri 'self';` +
`child-src 'self' app.netlify.com;` +
`child-src 'self' app.netlify.com https://www.google.com;` +
`form-action 'none';` +
`frame-ancestors 'none';` +
`img-src 'self' images.prismic.io data:;` +
`media-src 'self';` +
`object-src 'none';` +
`script-src 'self' app.netlify.com netlify-cdp-loader.netlify.app ${
`script-src 'self' app.netlify.com netlify-cdp-loader.netlify.app https://www.google.com/recaptcha/ https://www.gstatic.com ${
process.env.NODE_ENV === "development" ? `'unsafe-eval'` : ""
};` +
`style-src 'self' fonts.googleapis.com 'unsafe-inline';` +
`font-src fonts.gstatic.com;` +
`connect-src 'self' *.ocean.jellyfishsdk.com changi.dfi.team ${
process.env.NODE_ENV === "development"
? `ws://localhost:3000/_next/webpack-hmr base-goerli.blockscout.com eth-goerli.blockscout.com`
? `localhost:* 127.0.0.1:* ws://localhost:3000/_next/webpack-hmr base-goerli.blockscout.com eth-goerli.blockscout.com`
: ""
};`,
},
Expand Down
7 changes: 6 additions & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
"dependencies": {
"@floating-ui/react-dom": "^1.3.0",
"@headlessui/react": "^1.7.15",
"@nestjs/axios": "^3.0.1",
"@nestjs/common": "^10.2.5",
"@nestlab/google-recaptcha": "^3.6.0",
"@reduxjs/toolkit": "^1.9.5",
"@waveshq/standard-defichain-jellyfishsdk": "^2.4.1",
"@waveshq/standard-web": "^1.0.4",
Expand All @@ -25,11 +28,12 @@
"connectkit": "^1.4.0",
"dayjs": "^1.11.8",
"dayjs-plugin-utc": "^0.1.2",
"ethers": "^5.7.2",
"ethers": "^6.7.1",
"lodash": "^4.17.21",
"next": "13.4.12",
"react-code-blocks": "0.0.9-0",
"react-dropzone": "^14.2.3",
"react-google-recaptcha": "^3.1.0",
"react-qr-code": "^2.0.11",
"recharts": "^2.7.1",
"viem": "^1.1.0",
Expand All @@ -42,6 +46,7 @@
"@tailwindcss/line-clamp": "^0.4.4",
"@testing-library/cypress": "^9.0.0",
"@types/node": "18.16.3",
"@types/react-google-recaptcha": "^2.1.8",
"@types/recharts": "^1.8.24",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.6.0",
Expand Down
Loading

0 comments on commit a193692

Please sign in to comment.