forked from ping-pub/faucet
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfaucet.js
157 lines (124 loc) · 5.6 KB
/
faucet.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import express from 'express';
import * as path from 'path'
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
import { StargateClient, SigningStargateClient, GasPrice } from "@cosmjs/stargate";
import { FrequencyChecker } from './checker.js';
import conf from './config.js'
const app = express()
const checker = new FrequencyChecker(conf)
app.get('/', (req, res) => {
res.sendFile(path.resolve('./index.html'));
})
app.get('/faucet/config', async (req, res) => {
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(conf.sender.mnemonic, conf.sender.option);
const [firstAccount] = await wallet.getAccounts();
const project = conf.project;
project.sample = firstAccount.address;
project.denom = conf.tx.amount.denom;
// Parse the denom and make amount readable if there's a prefix
if (project.denom.startsWith('u')) {
project.amount = conf.tx.amount.amount / Math.pow(10, 6);
project.denom = project.denom.substring(1).toUpperCase();
} else if (project.denom.startsWith('n')) {
project.amount = conf.tx.amount.amount / Math.pow(10, 9);
project.denom = project.denom.substring(1).toUpperCase();
} else if (project.denom.startsWith('a')) {
project.amount = conf.tx.amount.amount / Math.pow(10, 18);
project.denom = project.denom.substring(1).toUpperCase();
} else {
project.denom = conf.tx.amount.amount
}
res.send(project);
})
app.get('/faucet/health', async (req, res) => {
try {
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(conf.sender.mnemonic, conf.sender.option);
const [firstAccount] = await wallet.getAccounts();
const includeBalance = req.query.showbalance ? true : false;
const client = await StargateClient.connect(conf.blockchain.rpc_endpoint);
const balances = await client.getAllBalances(firstAccount.address);
const faucetDispenseCoin = conf.tx.amount;
// Find the balance for the required denomination
const balance = balances.find(b => b.denom === faucetDispenseCoin.denom);
const response = { status: 'insufficient_funds' };
// Check if the balance is sufficient and update the response
if (balance && (BigInt(balance.amount) >= BigInt(faucetDispenseCoin.amount))) {
response.status = 'ok';
if (includeBalance) {
response.balance = balance;
}
}
res.send(response);
} catch (error) {
console.error(error);
res.status(500).send({ status: 'error', message: error.message });
}
});
app.get('/send/:address', async (req, res) => {
const { address } = req.params;
if (address) {
try {
// if valid address
if (address.startsWith(conf.sender.option.prefix)) {
// if not rate-limited
if (await checker.checkAddress(address) && await checker.checkIp(req.ip)) {
// if the wallet does not have tokens already
if (await checkWalletHasExistingTokens(address)) {
console.log('FAILED - well funded: request tokens to ', address, req.ip);
res.status(429).send({ result: "Too many requests. Wallet is already funded" })
return;
}
const txhash = await sendTx(address);
if (txhash) {
// Update the rate-limit for the IP and address
checker.update(req.ip);
checker.update(address);
console.log('SUCCESS - tokens sent: request tokens to ', address, req.ip, txhash);
res.send({ result: txhash });
} else {
console.log('FAILED - tx failed: request tokens to ', address, req.ip);
res.status(500).send({ result: "Transaction hash not found" });
}
} else {
console.log('FAILED - rate limited: request tokens to ', address, req.ip);
res.status(429).send({ result: "Too many requests. Try again later" });
}
} else {
console.log('FAILED - invalid address: request tokens to ', address, req.ip);
res.send({ result: `Address ${address} is not supported` });
}
} catch (err) {
console.error(err);
res.status(500).send({ result: 'Fatal error. Contact team!' });
}
} else {
// send result
res.send({ result: 'Address is required' });
}
})
app.listen(conf.port, () => {
console.log(`Faucet app listening on port ${conf.port}`);
// To be set if reverse proxy enabled
app.set('trust proxy', true)
})
async function checkWalletHasExistingTokens(recipient) {
const client = await StargateClient.connect(conf.blockchain.rpc_endpoint);
const balances = await client.getAllBalances(recipient);
// Find the balance being dispensed in requester's bank
const walletBalance = balances.find(b => b.denom === conf.tx.amount.denom);
if (walletBalance && (BigInt(walletBalance.amount) >= BigInt(conf.tx.amount.amount))) {
return true
}
return false
}
async function sendTx(recipient) {
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(conf.sender.mnemonic, conf.sender.option);
const [firstAccount] = await wallet.getAccounts();
const rpcEndpoint = conf.blockchain.rpc_endpoint;
const gasPrice = GasPrice.fromString(conf.tx.gasPrices);
const amount = conf.tx.amount;
const client = await SigningStargateClient.connectWithSigner(rpcEndpoint, wallet, { gasPrice: gasPrice });
// 1.5 is the gas multiplier. gas is estimated automatically when this is set. ref : https://github.com/cosmos/cosmjs/blob/b4aa877843c1206104b0207f3053a7d59b2d734f/packages/cli/examples/simulate.ts
const result = await client.sendTokens(firstAccount.address, recipient, [amount], 1.5, "dispensing tokens from faucet");
return result && result.transactionHash ? result.transactionHash.toString() : null;
}