Skip to content

Commit 147d567

Browse files
common token disbursal rate limit (#186)
* common token disbursal rate limit * logging
1 parent 353f7a0 commit 147d567

5 files changed

Lines changed: 61 additions & 6 deletions

File tree

CouponService/couponService.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export type Coupon = {
1313
reset: boolean,
1414
skipIpRateLimit?: boolean,
1515
skipWalletRateLimit?: boolean,
16+
skipCommonTokenDisbursalRateLimit?: boolean,
1617
}
1718

1819
type CouponConfig = {

config.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@
3636
"RATELIMIT": {
3737
"MAX_LIMIT": 1,
3838
"WINDOW_SIZE": 1440
39+
},
40+
"COMMON_TOKEN_DISBURSAL_RL": {
41+
"MAX_LIMIT": 20,
42+
"WINDOW_SIZE": 60
3943
}
4044
},
4145
{

middlewares/rateLimiter.ts

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,21 @@ import { CouponService } from '../CouponService/couponService'
55

66
export class RateLimiter {
77
PATH: string
8-
type: 'ip' | 'wallet' | 'global'
8+
type: 'ip' | 'wallet' | 'global' | 'common_token_disbursal'
99

1010
constructor(
1111
app: any,
1212
configs: RateLimiterConfig[],
13-
type: 'ip' | 'wallet' | 'global',
13+
type: 'ip' | 'wallet' | 'global' | 'common_token_disbursal',
1414
couponService: CouponService,
1515
keyGenerator?: any,
1616
) {
17+
if (configs.length === 0) {
18+
this.PATH = '/api/sendToken'
19+
this.type = 'common_token_disbursal'
20+
return
21+
}
22+
1723
this.PATH = configs[0].RATELIMIT.PATH || '/api/sendToken'
1824
this.type = type
1925

@@ -27,7 +33,7 @@ export class RateLimiter {
2733
SKIP_FAILED_REQUESTS: RATELIMIT.SKIP_FAILED_REQUESTS || true,
2834
}
2935

30-
rateLimiters.set(config.ID, this.getLimiter(RL_CONFIG, keyGenerator))
36+
rateLimiters.set(config.ID, this.getLimiter(RL_CONFIG, config.ID, type, keyGenerator))
3137
})
3238

3339
if(configs[0]?.RATELIMIT?.REVERSE_PROXIES) {
@@ -43,6 +49,8 @@ export class RateLimiter {
4349
return next()
4450
} else if (this.type === 'wallet' && coupon && coupon.skipWalletRateLimit) {
4551
return next()
52+
} else if (this.type === 'common_token_disbursal' && coupon && coupon.skipCommonTokenDisbursalRateLimit) {
53+
return next()
4654
}
4755
}
4856

@@ -54,15 +62,30 @@ export class RateLimiter {
5462
})
5563
}
5664

57-
getLimiter(config: any, keyGenerator?: any): RateLimitRequestHandler {
65+
getLimiter(
66+
config: any,
67+
faucetConfigId: string,
68+
type: 'ip' | 'wallet' | 'global' | 'common_token_disbursal',
69+
keyGenerator?: any,
70+
): RateLimitRequestHandler {
5871
const limiter = rateLimit({
5972
windowMs: config.WINDOW_SIZE * 60 * 1000,
6073
max: config.MAX_LIMIT,
6174
standardHeaders: true,
6275
legacyHeaders: false,
6376
skipFailedRequests: config.SKIP_FAILED_REQUESTS,
64-
message: {
65-
message: `Too many requests. Please try again after ${config.WINDOW_SIZE} minutes`
77+
handler: (req, res, next, options) => {
78+
if(type === 'common_token_disbursal') {
79+
console.log(JSON.stringify({
80+
date: new Date(),
81+
type: "CommonTokenDisbursalRateLimit",
82+
faucetConfigId,
83+
ip: req.headers["cf-connecting-ip"] || req.ip
84+
}))
85+
}
86+
res.status(options.statusCode).send({
87+
message: `Too many requests. Please try again after ${config.WINDOW_SIZE} minutes`
88+
})
6689
},
6790
keyGenerator: keyGenerator ? keyGenerator : (req, res) => {
6891
const ip = this.getIP(req)

server.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
SendTokenResponse,
1111
ChainType,
1212
EVMInstanceAndConfig,
13+
ERC20Type,
14+
RateLimiterConfig,
1315
} from './types'
1416

1517
import {
@@ -50,6 +52,23 @@ const mainnetCheckService = new MainnetCheckService(MAINNET_BALANCE_CHECK_RPC)
5052

5153
new RateLimiter(app, [GLOBAL_RL], 'global', couponService)
5254

55+
// create array of common token disbursal rate limiter config from evmchains.COMMON_TOKEN_DISBURSAL_RL
56+
const commonTokenDisbursalRl = [...evmchains, ...erc20tokens].map((chain: ChainType | ERC20Type) => {
57+
if (chain.COMMON_TOKEN_DISBURSAL_RL) {
58+
return {
59+
ID: chain.ID,
60+
RATELIMIT: {
61+
MAX_LIMIT: chain.COMMON_TOKEN_DISBURSAL_RL.MAX_LIMIT,
62+
WINDOW_SIZE: chain.COMMON_TOKEN_DISBURSAL_RL.WINDOW_SIZE,
63+
}
64+
}
65+
}
66+
}).filter(Boolean) as RateLimiterConfig[]
67+
new RateLimiter(app, commonTokenDisbursalRl, 'common_token_disbursal', couponService, (req: any, res: any) => {
68+
// this common key will be used for all user requests allowing a unified rate limit.
69+
return 'global'
70+
})
71+
5372
new RateLimiter(app, [
5473
...evmchains,
5574
...erc20tokens

types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ export type ChainType = {
4141
RATELIMIT: {
4242
WINDOW_SIZE: number,
4343
MAX_LIMIT: number
44+
},
45+
COMMON_TOKEN_DISBURSAL_RL?: {
46+
WINDOW_SIZE: number,
47+
MAX_LIMIT: number
4448
}
4549
}
4650

@@ -57,6 +61,10 @@ export type ERC20Type = {
5761
WINDOW_SIZE: number,
5862
MAX_LIMIT: number
5963
},
64+
COMMON_TOKEN_DISBURSAL_RL?: {
65+
WINDOW_SIZE: number,
66+
MAX_LIMIT: number
67+
},
6068
IMAGE?: string,
6169
RECALIBRATE?: number,
6270
RPC?: string,

0 commit comments

Comments
 (0)