Skip to content

Commit 015659f

Browse files
Development (#131)
* mainnet balance checks * cfg * logs * typo * json * nits * Coupon and Mainnet balance fixes (#130) * Development (mainnet balance check) (#128) * mainnet balance checks * cfg * logs * typo * json * nits * update prod config (#129) * exclude config field * checks fix * nits * logging
1 parent 845e22f commit 015659f

4 files changed

Lines changed: 110 additions & 58 deletions

File tree

config.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"SKIP_FAILED_REQUESTS": false
1010
}
1111
},
12-
"NATIVE_CLIENT": false,
12+
"NATIVE_CLIENT": true,
1313
"DEBUG": true,
1414
"couponConfig": {
1515
"IS_ENABLED": true,
@@ -26,14 +26,14 @@
2626
"IMAGE": "https://glacier-api.avax.network/proxy/chain-assets/main/chains/43113/chain-logo.png",
2727
"MAX_PRIORITY_FEE": "10000000000",
2828
"MAX_FEE": "100000000000",
29-
"DRIP_AMOUNT": 2,
29+
"DRIP_AMOUNT": 0.01,
3030
"DECIMALS": 18,
3131
"RECALIBRATE": 30,
3232
"COUPON_REQUIRED": true,
3333
"MAINNET_BALANCE_CHECK_RPC": "https://api.avax.network/ext/C/rpc",
3434
"RATELIMIT": {
35-
"MAX_LIMIT": 1,
36-
"WINDOW_SIZE": 1440
35+
"MAX_LIMIT": 5,
36+
"WINDOW_SIZE": 60
3737
}
3838
},
3939
{

server.ts

Lines changed: 41 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
SendTokenResponse,
1111
ChainType,
1212
EVMInstanceAndConfig,
13-
CouponValidity
1413
} from './types'
1514

1615
import {
@@ -22,7 +21,13 @@ import {
2221
DEBUG,
2322
} from './config.json'
2423
import { CouponService } from './CouponService/couponService'
25-
import { checkMainnetBalance } from './utils/mainnetBalanceCheck'
24+
import {
25+
PIPELINE_CHECKS,
26+
PipelineCheckValidity,
27+
checkCouponPipeline,
28+
checkMainnetBalancePipeline,
29+
pipelineFailureMessage
30+
} from './utils/pipelineChecks'
2631

2732
dotenv.config()
2833

@@ -73,11 +78,13 @@ const getChainByID = (chains: ChainType[], id: string): ChainType | undefined =>
7378
return reply
7479
}
7580

81+
const separateConfigFields = ['COUPON_REQUIRED', 'MAINNET_BALANCE_CHECK_RPC']
82+
7683
// Populates the missing config keys of the child using the parent's config
7784
const populateConfig = (child: any, parent: any): any => {
7885
Object.keys(parent || {}).forEach((key) => {
79-
// Do not copy COUPON config (in ERC20 tokens) from host chain
80-
if(key !== 'COUPON_REQUIRED' && !child[key]) {
86+
// Do not copy configs of separateConfigFields (in ERC20 tokens) from host chain
87+
if(!separateConfigFields.includes(key) && !child[key]) {
8188
child[key] = parent[key]
8289
}
8390
})
@@ -130,60 +137,50 @@ router.post('/sendToken', captcha.middleware, async (req: any, res: any) => {
130137
const dripAmount = erc20Instance?.config.DRIP_AMOUNT ?? evm.config.DRIP_AMOUNT
131138

132139
/**
133-
* MAINNET BALANCE OR COUPON VALIDATION checks
134-
* 1. If mainnet balance check is enabled, users would be required to have mainnet balance
135-
* 2. If coupon validation is enabled, users would need a specific coupon id to get tokens
136-
* 3. If both are enabled, then any one would be sufficient
140+
* Pipeline Checks
141+
* 1. Pipelines are checks or rules that a request goes through before being processed
142+
* 2. The request should pass at least one pipeline check
143+
* 3. If no pipeline check is required for a token, then directly process the request
144+
* 4. Currently, we have 2 pipeline checks: Coupon Check & Mainnet Balance Check
137145
*/
146+
const mainnetCheckEnabledRPC = (erc20Instance ? erc20Instance.config.MAINNET_BALANCE_CHECK_RPC : evm.config.MAINNET_BALANCE_CHECK_RPC) ?? false
147+
const couponCheckEnabled = couponConfig.IS_ENABLED && ((erc20Instance ? erc20Instance.config.COUPON_REQUIRED : evm.config.COUPON_REQUIRED) ?? false)
138148

139-
// mainnet balance checks
140-
const mainnetCheckEnabledRPC = erc20Instance?.config.MAINNET_BALANCE_CHECK_RPC ?? evm.config.MAINNET_BALANCE_CHECK_RPC ?? false
141-
let mainnetCheckPassed = false
142-
if (mainnetCheckEnabledRPC && (await checkMainnetBalance(faucetConfigId, mainnetCheckEnabledRPC, address))) {
143-
mainnetCheckPassed = true
144-
}
149+
let pipelineValidity: PipelineCheckValidity = {isValid: false, dripAmount}
150+
!pipelineValidity.isValid && couponCheckEnabled && await checkCouponPipeline(couponService, pipelineValidity, faucetConfigId, coupon)
151+
152+
// don't check mainnet balance, if coupon is provided
153+
!pipelineValidity.isValid && !coupon && mainnetCheckEnabledRPC && await checkMainnetBalancePipeline(pipelineValidity, mainnetCheckEnabledRPC, address)
145154

146-
// validate coupon
147-
let couponValidity: CouponValidity = {isValid: false, amount: dripAmount}
148155
if (
149-
// check coupon validation only if mainnet check failed (either no-balance or check not enabled)
150-
!mainnetCheckPassed &&
151-
152-
// coupon checks
153-
couponConfig.IS_ENABLED &&
154-
// if request is for erc20 tokens
155-
((erc20Instance && erc20Instance.config.COUPON_REQUIRED) ||
156-
// if request is for evm native token
157-
(erc20Instance === undefined && evm.config.COUPON_REQUIRED))
156+
(mainnetCheckEnabledRPC || couponCheckEnabled) &&
157+
!pipelineValidity.isValid
158158
) {
159-
// if coupon is required but not passed in request
160-
if (coupon === undefined) {
161-
res.status(400).send({message: "Coupon is required for this chain or token!"})
162-
return
163-
}
164-
couponValidity = await couponService.consumeCouponAmount(coupon, faucetConfigId, dripAmount)
165-
if (!couponValidity.isValid) {
166-
res.status(400).send({message: "Invalid or expired coupon provided. Contact support team on Discord!"})
167-
return
168-
}
159+
// failed
160+
res.status(400).send({message: pipelineValidity.errorMessage + pipelineFailureMessage(mainnetCheckEnabledRPC, couponCheckEnabled)})
161+
return
169162
}
170163

171164
// logging requests (if enabled)
172-
DEBUG && console.log("New faucet request:", JSON.stringify({
173-
"type": "NewFaucetRequest",
174-
"address": address,
175-
"chain": chain,
176-
"erc20": erc20,
177-
"ip": req.headers["cf-connecting-ip"] || req.ip
165+
DEBUG && console.log(JSON.stringify({
166+
type: "NewFaucetRequest",
167+
faucetConfigId,
168+
address,
169+
chain,
170+
erc20,
171+
checkPassedType: pipelineValidity.checkPassedType,
172+
dripAmount: pipelineValidity.dripAmount,
173+
mainnetBalance: pipelineValidity.mainnetBalance,
174+
ip: req.headers["cf-connecting-ip"] || req.ip
178175
}))
179176

180177
// send request
181-
evm.instance.sendToken(address, erc20, couponValidity.amount, async (data: SendTokenResponse) => {
178+
evm.instance.sendToken(address, erc20, pipelineValidity.dripAmount, async (data: SendTokenResponse) => {
182179
const { status, message, txHash } = data
183180

184181
// reclaim coupon if transaction is failed
185-
if (coupon && couponValidity.isValid && txHash === undefined) {
186-
await couponService.reclaimCouponAmount(coupon, dripAmount)
182+
if (pipelineValidity.checkPassedType === PIPELINE_CHECKS.COUPON && coupon && txHash === undefined) {
183+
await couponService.reclaimCouponAmount(coupon, pipelineValidity.dripAmount)
187184
}
188185
res.status(status).send({message, txHash})
189186
})

utils/mainnetBalanceCheck.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import axios from "axios"
22

3-
export async function checkMainnetBalance(faucetConfigId: string, rpc: string, address: string, threshold = 0): Promise<boolean> {
3+
export async function checkMainnetBalance(rpc: string, address: string, threshold = 0): Promise<{isValid: boolean, balance: number}> {
4+
const response = {isValid: false, balance: 0}
45
try {
56
const response = await axios.post<any, any>(rpc, {
67
jsonrpc: "2.0",
@@ -10,17 +11,11 @@ export async function checkMainnetBalance(faucetConfigId: string, rpc: string, a
1011
})
1112
const balance = parseInt(response.data.result)
1213
if (balance > threshold) {
13-
console.log("Successful FaucetMainnetBalanceCheck:", JSON.stringify({
14-
type: "FaucetMainnetBalanceCheckSuccess",
15-
chain: faucetConfigId,
16-
address: address,
17-
balance: balance,
18-
}))
19-
return true
14+
return {isValid: true, balance}
2015
}
2116
} catch(err) {
2217
console.error('ERROR: checkMainnetBalance', err)
23-
return false
18+
return response
2419
}
25-
return false
20+
return response
2621
}

utils/pipelineChecks.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { CouponService } from '../CouponService/couponService'
2+
import { checkMainnetBalance } from './mainnetBalanceCheck'
3+
4+
export enum PIPELINE_CHECKS {
5+
MAINNET_BALANCE = 'mainnet_check',
6+
COUPON = 'coupon_check'
7+
}
8+
9+
export type PipelineCheckValidity = {
10+
isValid: boolean,
11+
checkPassedType?: PIPELINE_CHECKS,
12+
errorMessage?: string,
13+
dripAmount: number,
14+
mainnetBalance?: number,
15+
}
16+
17+
export function pipelineFailureMessage(mainnetBalanceCheckEnabled: boolean | string, couponCheckEnabled: boolean): string {
18+
if (mainnetBalanceCheckEnabled && couponCheckEnabled) {
19+
return "Either mainnet balance or a valid coupon is required!"
20+
} else if (mainnetBalanceCheckEnabled) {
21+
return "Mainnet balance is required!"
22+
} else if (couponCheckEnabled) {
23+
return "A valid coupon is required!"
24+
}
25+
26+
return ""
27+
}
28+
29+
export async function checkCouponPipeline(
30+
couponService: CouponService,
31+
pipelineCheckValidity: PipelineCheckValidity,
32+
faucetConfigId: string,
33+
coupon?: string,
34+
) {
35+
// if coupon is required but not passed in request
36+
if (coupon === undefined) {
37+
return
38+
}
39+
40+
const couponValidity = await couponService.consumeCouponAmount(coupon, faucetConfigId, pipelineCheckValidity.dripAmount)
41+
if (!couponValidity.isValid) {
42+
pipelineCheckValidity.errorMessage = "Invalid or expired coupon provided. Contact support team on Discord! "
43+
return
44+
} else {
45+
pipelineCheckValidity.isValid = true
46+
pipelineCheckValidity.dripAmount = couponValidity.amount
47+
pipelineCheckValidity.checkPassedType = PIPELINE_CHECKS.COUPON
48+
}
49+
}
50+
51+
export async function checkMainnetBalancePipeline(pipelineCheckValidity: PipelineCheckValidity, rpc: string, address: string) {
52+
const {isValid, balance} = await checkMainnetBalance(rpc, address)
53+
if (isValid) {
54+
pipelineCheckValidity.isValid = true
55+
pipelineCheckValidity.checkPassedType = PIPELINE_CHECKS.MAINNET_BALANCE
56+
pipelineCheckValidity.mainnetBalance = balance
57+
} else {
58+
pipelineCheckValidity.errorMessage = "Mainnet balance check failed! "
59+
}
60+
}

0 commit comments

Comments
 (0)