Skip to content

Commit a193692

Browse files
chloezxyylykalabradafullstackninja864pierregeenattadex
authored
feat(core): testnet faucet page with recaptcha (#344)
* 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>
1 parent e0f5721 commit a193692

37 files changed

Lines changed: 13281 additions & 6831 deletions

apps/server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
"@nestjs/throttler": "^5.0.1",
6666
"@waveshq/standard-defichain-jellyfishsdk": "^2.6.1",
6767
"@waveshq/walletkit-core": "^1.3.4",
68-
"axios": "^1.5.0",
68+
"axios": "^1.6.0",
6969
"bignumber.js": "^9.1.2",
7070
"cache-manager": "^5.2.4",
7171
"class-validator": "^0.14.0",

apps/server/pnpm-lock.yaml

Lines changed: 3040 additions & 1847 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { NestFactory } from '@nestjs/core';
2+
import { NestFastifyApplication } from '@nestjs/platform-fastify';
3+
4+
import { AppModule } from './app.module';
5+
6+
/**
7+
* App which starts the default Metascan Server Application
8+
*/
9+
export class MetascanServerApp<App extends NestFastifyApplication = NestFastifyApplication> {
10+
protected app?: App;
11+
12+
constructor(protected readonly module: any) {}
13+
14+
async createNestApp(): Promise<App> {
15+
const app = await NestFactory.create(AppModule);
16+
await this.configureApp(app);
17+
// @ts-ignore
18+
return app;
19+
}
20+
21+
async configureApp(app): Promise<void> {
22+
app.enableCors({
23+
allowedHeaders: '*',
24+
methods: ['GET', 'PUT', 'POST', 'DELETE'],
25+
maxAge: 60 * 24 * 7,
26+
origin:
27+
process.env.NODE_ENV === 'production'
28+
? [
29+
'https://meta.defiscan.live/',
30+
/https:\/\/([^.]*.\.)*defimetascan\.app/, // allow all subdomains of defimetascan
31+
/https:\/\/([^.]*.)--defimetascan\.netlify\.app/, // allow all netlify preview deployments
32+
/https?:\/\/localhost(:\d+)?/, // allow localhost connection
33+
]
34+
: '*',
35+
});
36+
}
37+
38+
/**
39+
* Run any additional initialisation steps before starting the server.
40+
* If there are additional steps, can be overriden by any extending classes
41+
*/
42+
async init() {
43+
this.app = await this.createNestApp();
44+
return this.app.init();
45+
}
46+
47+
async start(): Promise<App> {
48+
const app = await this.init();
49+
50+
const PORT = process.env.PORT || 3001;
51+
await app.listen(PORT).then(() => {
52+
// eslint-disable-next-line no-console
53+
console.log(`Started server on port ${PORT}`);
54+
});
55+
return app;
56+
}
57+
58+
/**
59+
* Stop NestJs and un-assign this.app
60+
*/
61+
async stop(): Promise<void> {
62+
await this.app?.close();
63+
this.app = undefined;
64+
}
65+
}

apps/server/src/faucet/FaucetController.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { CACHE_MANAGER } from '@nestjs/cache-manager';
2-
import { Controller, Get, HttpException, Inject, Param, Query, UseInterceptors } from '@nestjs/common';
2+
import { Controller, HttpException, Inject, Param, Post, Query, UseGuards, UseInterceptors } from '@nestjs/common';
33
import { ConfigService } from '@nestjs/config';
44
import { EnvironmentNetwork } from '@waveshq/walletkit-core';
55
import { TransactionResponse } from 'ethers';
66

7+
import { RecaptchaGuard } from '../recaptcha/RecaptchaGuard';
78
import { AddressValidationInterceptor } from './AddressValidationInterceptor';
89
import { DefaultNetworkInterceptor } from './DefaultNetworkInterceptor';
910
import { FaucetService } from './FaucetService';
@@ -16,7 +17,8 @@ export class FaucetController {
1617
private configService: ConfigService,
1718
) {}
1819

19-
@Get(':address')
20+
@Post(':address')
21+
@UseGuards(RecaptchaGuard)
2022
@UseInterceptors(AddressValidationInterceptor, DefaultNetworkInterceptor)
2123
async sendFunds(
2224
@Param('address') address: string,
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1+
import { HttpModule } from '@nestjs/axios';
12
import { CacheModule } from '@nestjs/cache-manager';
23
import { Module } from '@nestjs/common';
34

5+
import { RecaptchaGuard } from '../recaptcha/RecaptchaGuard';
46
import { FaucetController } from './FaucetController';
57
import { FaucetService } from './FaucetService';
68

79
@Module({
8-
imports: [CacheModule.register()],
10+
imports: [CacheModule.register(), HttpModule],
911
controllers: [FaucetController],
10-
providers: [FaucetService],
12+
providers: [FaucetService, RecaptchaGuard],
1113
})
1214
export class FaucetModule {}

apps/server/src/faucet/FaucetService.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export class FaucetService {
1818
}
1919

2020
async sendFundsToUser(address: string, amount: string, network: EnvironmentNetwork): Promise<TransactionResponse> {
21+
// Send funds to user if recaptcha validation is successful
2122
const evmProviderService = new EVMProviderService(network);
2223
const wallet = new ethers.Wallet(this.privateKey, evmProviderService.provider);
2324
const nonce = await evmProviderService.provider.getTransactionCount(wallet.address);

apps/server/src/main.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
1-
import { NestFactory } from '@nestjs/core';
2-
31
import { AppModule } from './app.module';
2+
import { MetascanServerApp } from './MetascanServerApp';
43

54
async function bootstrap() {
6-
const app = await NestFactory.create(AppModule);
7-
const PORT = process.env.PORT || 5741;
8-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
9-
app.listen(PORT).then(() => {
10-
// eslint-disable-next-line no-console
11-
console.log(`Started server on port ${PORT}`);
12-
});
5+
const app = new MetascanServerApp(AppModule);
6+
await app.start();
137
}
8+
149
void bootstrap();
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { HttpService } from '@nestjs/axios';
2+
import { ExecutionContext, Injectable, Logger } from '@nestjs/common';
3+
import { Request } from 'express';
4+
5+
@Injectable()
6+
export class RecaptchaGuard {
7+
private readonly logger: Logger;
8+
9+
constructor(private readonly httpService: HttpService) {
10+
this.logger = new Logger(RecaptchaGuard.name);
11+
}
12+
13+
async canActivate(context: ExecutionContext): Promise<boolean> {
14+
const request = context.switchToHttp().getRequest<Request>();
15+
return this.validateRecaptcha(request);
16+
}
17+
18+
private async validateRecaptcha(request: Request): Promise<boolean> {
19+
const response = request.body.recaptchaValue;
20+
21+
if (!response) {
22+
this.logger.log('Invalid body in recaptcha request');
23+
return false;
24+
}
25+
26+
const { data } = await this.httpService
27+
.post(
28+
`https://www.google.com/recaptcha/api/siteverify`,
29+
null, // Since we're sending data in the body, set it to null
30+
{
31+
params: {
32+
secret: process.env.SECRET_KEY,
33+
response,
34+
},
35+
},
36+
)
37+
.toPromise();
38+
return data.success;
39+
}
40+
}

apps/web/.env

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
NEXT_PUBLIC_RPC_URL_MAINNET="https://blockscout.mainnet.ocean.jellyfishsdk.com"
22
NEXT_PUBLIC_RPC_URL_TESTNET="https://blockscout.testnet.ocean.jellyfishsdk.com"
3-
NEXT_PUBLIC_RPC_URL_CHANGI="https://blockscout.changi.ocean.jellyfishsdk.com"
3+
NEXT_PUBLIC_RPC_URL_CHANGI="https://blockscout.changi.ocean.jellyfishsdk.com"
4+
NEXT_PUBLIC_SERVER_URL="http://localhost:3001/"
5+
6+
NEXT_PUBLIC_SITE_KEY="6LeeoO8oAAAAALPSYZr1_Itr9bBzzQBVDjgjMT0-"

apps/web/next.config.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,20 @@ const securityHeaders = [
44
value:
55
`default-src 'none';` +
66
`base-uri 'self';` +
7-
`child-src 'self' app.netlify.com;` +
7+
`child-src 'self' app.netlify.com https://www.google.com;` +
88
`form-action 'none';` +
99
`frame-ancestors 'none';` +
1010
`img-src 'self' images.prismic.io data:;` +
1111
`media-src 'self';` +
1212
`object-src 'none';` +
13-
`script-src 'self' app.netlify.com netlify-cdp-loader.netlify.app ${
13+
`script-src 'self' app.netlify.com netlify-cdp-loader.netlify.app https://www.google.com/recaptcha/ https://www.gstatic.com ${
1414
process.env.NODE_ENV === "development" ? `'unsafe-eval'` : ""
1515
};` +
1616
`style-src 'self' fonts.googleapis.com 'unsafe-inline';` +
1717
`font-src fonts.gstatic.com;` +
1818
`connect-src 'self' *.ocean.jellyfishsdk.com changi.dfi.team ${
1919
process.env.NODE_ENV === "development"
20-
? `ws://localhost:3000/_next/webpack-hmr base-goerli.blockscout.com eth-goerli.blockscout.com`
20+
? `localhost:* 127.0.0.1:* ws://localhost:3000/_next/webpack-hmr base-goerli.blockscout.com eth-goerli.blockscout.com`
2121
: ""
2222
};`,
2323
},

0 commit comments

Comments
 (0)