Implement a cosmos-sdk chain faucet API #1
2
.gitignore
vendored
2
.gitignore
vendored
@ -128,3 +128,5 @@ dist
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
db
|
||||
|
10
package.json
10
package.json
@ -8,7 +8,8 @@
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.21",
|
||||
"typescript": "^5.5.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.1",
|
||||
"@typescript-eslint/parser": "^5.47.1",
|
||||
"eslint": "^8.35.0",
|
||||
"eslint-config-semistandard": "^15.0.1",
|
||||
"eslint-config-standard": "^16.0.3",
|
||||
@ -16,10 +17,13 @@
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"eslint-plugin-standard": "^5.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.1",
|
||||
"@typescript-eslint/parser": "^5.47.1"
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.5.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cosmjs/proto-signing": "^0.32.4",
|
||||
"@cosmjs/stargate": "^0.32.4",
|
||||
"@keyv/sqlite": "^3.6.7",
|
||||
"express": "^4.19.2"
|
||||
},
|
||||
"scripts": {
|
||||
|
84
src/index.ts
84
src/index.ts
@ -1,11 +1,85 @@
|
||||
import express from 'express';
|
||||
import Keyv from 'keyv';
|
||||
|
||||
import { DirectSecp256k1Wallet } from '@cosmjs/proto-signing';
|
||||
import { GasPrice, SigningStargateClient } from '@cosmjs/stargate';
|
||||
import KeyvSqlite from '@keyv/sqlite';
|
||||
|
||||
// TODO: Take from a config file
|
||||
const config = {
|
||||
rpcEndpoint: 'https://rpc.example.com',
|
||||
faucetKey: 'your faucet key here',
|
||||
chainId: 'laconic_9000-1',
|
||||
denom: 'photon',
|
||||
amount: '1000000',
|
||||
prefix: 'laconic',
|
||||
dailyLimit: '3000000',
|
||||
dbPath: './db/faucet_data.sqlite'
|
||||
};
|
||||
|
||||
// Initialize KV store with SQLite backend
|
||||
const keyv = new Keyv({
|
||||
store: new KeyvSqlite({
|
||||
uri: `sqlite://${config.dbPath}`,
|
||||
table: 'keyv'
|
||||
})
|
||||
});
|
||||
|
||||
const app = express();
|
||||
const port = 3000;
|
||||
app.use(express.json());
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
res.send('Hello World!');
|
||||
async function sendTokens (recipientAddress: string, amount: string): Promise<string> {
|
||||
const wallet = await DirectSecp256k1Wallet.fromKey(
|
||||
Buffer.from(config.faucetKey, 'hex'),
|
||||
config.prefix
|
||||
);
|
||||
const [faucetAccount] = await wallet.getAccounts();
|
||||
const client = await SigningStargateClient.connectWithSigner(config.rpcEndpoint, wallet, { gasPrice: GasPrice.fromString(`0.01${config.denom}`) });
|
||||
|
||||
const result = await client.sendTokens(
|
||||
faucetAccount.address,
|
||||
recipientAddress,
|
||||
[{ denom: config.denom, amount: amount }],
|
||||
'auto',
|
||||
'Faucet transfer'
|
||||
);
|
||||
|
||||
return result.transactionHash;
|
||||
}
|
||||
|
||||
app.post('/faucet', async (req, res) => {
|
||||
const { address } = req.body;
|
||||
|
||||
if (!address) {
|
||||
return res.status(400).json({ error: 'Address is required' });
|
||||
}
|
||||
|
||||
// Check rate limit
|
||||
const now = Date.now();
|
||||
const today = new Date(now).toISOString().split('T')[0];
|
||||
const key = `${address}:${today}`;
|
||||
const currentAmount = await keyv.get(key) || '0';
|
||||
|
||||
if (BigInt(currentAmount) + BigInt(config.amount) > BigInt(config.dailyLimit)) {
|
||||
return res.status(429).json({ error: 'Limit exceeded' });
|
||||
}
|
||||
|
||||
try {
|
||||
const txHash = await sendTokens(address, config.amount);
|
||||
|
||||
console.log(`Sent tokens to address ${address}`);
|
||||
|
||||
// Update rate limit
|
||||
await keyv.set(key, (BigInt(currentAmount) + BigInt(config.amount)).toString(), 86400000); // 24 hours TTL
|
||||
|
||||
res.json({ success: true, txHash });
|
||||
} catch (error) {
|
||||
console.error('Error sending tokens:', error);
|
||||
res.status(500).json({ error: 'Failed to send tokens' });
|
||||
}
|
||||
});
|
||||
|
||||
app.listen(port, () => {
|
||||
return console.log(`Express is listening at http://localhost:${port}`);
|
||||
const PORT = process.env.PORT || 3000;
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Faucet server running on port ${PORT}`);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user