forked from cerc-io/laconic-faucet
Compare commits
2 Commits
main
...
pm-update-
Author | SHA1 | Date | |
---|---|---|---|
e5a032cfa8 | |||
|
c5bf9793f7 |
@ -1,4 +1,4 @@
|
|||||||
[upstream]
|
[registry]
|
||||||
rpcEndpoint = "http://localhost:26657"
|
rpcEndpoint = "http://localhost:26657"
|
||||||
chainId = "laconic_9000-1"
|
chainId = "laconic_9000-1"
|
||||||
denom = "photon"
|
denom = "photon"
|
||||||
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
[server]
|
[server]
|
||||||
port = 3000
|
port = 3000
|
||||||
|
periodInSecs = 60
|
||||||
transferAmount = 1000000
|
transferAmount = 1000000
|
||||||
periodTransferLimit = 3000000
|
periodTransferLimit = 3000000
|
||||||
dbDir = "db"
|
dbDir = "db"
|
||||||
|
56
src/index.ts
56
src/index.ts
@ -12,10 +12,10 @@ import KeyvSqlite from '@keyv/sqlite';
|
|||||||
|
|
||||||
const CONFIG_PATH = 'environments/local.toml';
|
const CONFIG_PATH = 'environments/local.toml';
|
||||||
const FAUCET_DATA_FILE = 'faucet_data.sqlite';
|
const FAUCET_DATA_FILE = 'faucet_data.sqlite';
|
||||||
const FAUCET_DATA_TTL = 86400000; // 24 hrs
|
const TTL_MULTIPLIER = 1.5; // 1.5 times the configured period
|
||||||
|
|
||||||
interface Config {
|
interface Config {
|
||||||
upstream: {
|
registry: {
|
||||||
rpcEndpoint: string
|
rpcEndpoint: string
|
||||||
chainId: string
|
chainId: string
|
||||||
denom: string
|
denom: string
|
||||||
@ -25,12 +25,18 @@ interface Config {
|
|||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
port: number
|
port: number
|
||||||
|
periodInSecs: number
|
||||||
transferAmount: string
|
transferAmount: string
|
||||||
periodTransferLimit: string
|
periodTransferLimit: string
|
||||||
dbDir: string
|
dbDir: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface FaucetStoreValue {
|
||||||
|
periodStart: string
|
||||||
|
amountSent: string
|
||||||
|
}
|
||||||
|
|
||||||
async function main (): Promise<void> {
|
async function main (): Promise<void> {
|
||||||
// Read and parse the configuration
|
// Read and parse the configuration
|
||||||
const configFile = fs.readFileSync(CONFIG_PATH, 'utf-8');
|
const configFile = fs.readFileSync(CONFIG_PATH, 'utf-8');
|
||||||
@ -50,18 +56,32 @@ async function main (): Promise<void> {
|
|||||||
return res.status(400).json({ error: 'address is required' });
|
return res.status(400).json({ error: 'address is required' });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isValidAddress(accountAddress, config.upstream.prefix)) {
|
if (!isValidAddress(accountAddress, config.registry.prefix)) {
|
||||||
return res.status(400).json({ error: 'invalid address' });
|
return res.status(400).json({ error: 'invalid address' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check rate limit
|
const currentTimeInSeconds = Math.floor(Date.now() / 1000);
|
||||||
const now = Date.now();
|
let amountSentToAccount = BigInt(0);
|
||||||
const today = new Date(now).toISOString().split('T')[0];
|
let accountPeriodStart = currentTimeInSeconds;
|
||||||
const faucetStoreKey = `${accountAddress}:${today}`;
|
|
||||||
const amountSentToAddress = await faucetDataStore.get(faucetStoreKey) || '0';
|
|
||||||
|
|
||||||
if (BigInt(amountSentToAddress) + BigInt(config.server.transferAmount) > BigInt(config.server.periodTransferLimit)) {
|
// Get accounts data from faucet data store
|
||||||
return res.status(429).json({ error: 'Limit exceeded' });
|
const accountFaucetData: FaucetStoreValue = await faucetDataStore.get(accountAddress);
|
||||||
|
|
||||||
|
// Check rate limit
|
||||||
|
if (accountFaucetData !== undefined) {
|
||||||
|
const elapsedTime = currentTimeInSeconds - Number(accountFaucetData.periodStart);
|
||||||
|
|
||||||
|
// Check if saved entry is still within configured period
|
||||||
|
// Else, create a fresh entry
|
||||||
|
if (elapsedTime < config.server.periodInSecs) {
|
||||||
|
amountSentToAccount = BigInt(accountFaucetData.amountSent);
|
||||||
|
accountPeriodStart = Number(accountFaucetData.periodStart);
|
||||||
|
|
||||||
|
// Check if limit has been reached
|
||||||
|
if (amountSentToAccount + BigInt(config.server.transferAmount) > BigInt(config.server.periodTransferLimit)) {
|
||||||
|
return res.status(429).json({ error: 'Limit exceeded' });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -69,7 +89,11 @@ async function main (): Promise<void> {
|
|||||||
console.log(`Sent tokens to address: ${accountAddress}, txHash: ${txHash}`);
|
console.log(`Sent tokens to address: ${accountAddress}, txHash: ${txHash}`);
|
||||||
|
|
||||||
// Update rate limit
|
// Update rate limit
|
||||||
await faucetDataStore.set(faucetStoreKey, (BigInt(amountSentToAddress) + BigInt(config.server.transferAmount)).toString(), FAUCET_DATA_TTL);
|
const updatedAccountFaucetData: FaucetStoreValue = {
|
||||||
|
periodStart: accountPeriodStart.toString(),
|
||||||
|
amountSent: amountSentToAccount.toString()
|
||||||
|
};
|
||||||
|
await faucetDataStore.set(accountAddress, updatedAccountFaucetData, config.server.periodInSecs * TTL_MULTIPLIER * 1000);
|
||||||
|
|
||||||
res.json({ success: true, txHash });
|
res.json({ success: true, txHash });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -108,27 +132,27 @@ async function initKVStore (dbDir: string): Promise<Keyv> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function sendTokens (config: Config, recipientAddress: string, amount: string): Promise<string> {
|
async function sendTokens (config: Config, recipientAddress: string, amount: string): Promise<string> {
|
||||||
let faucetKey = config.upstream.faucetKey;
|
let faucetKey = config.registry.faucetKey;
|
||||||
if (faucetKey.startsWith('0x')) {
|
if (faucetKey.startsWith('0x')) {
|
||||||
faucetKey = faucetKey.slice(2);
|
faucetKey = faucetKey.slice(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
const wallet = await DirectSecp256k1Wallet.fromKey(
|
const wallet = await DirectSecp256k1Wallet.fromKey(
|
||||||
Buffer.from(faucetKey, 'hex'),
|
Buffer.from(faucetKey, 'hex'),
|
||||||
config.upstream.prefix
|
config.registry.prefix
|
||||||
);
|
);
|
||||||
const [faucetAccount] = await wallet.getAccounts();
|
const [faucetAccount] = await wallet.getAccounts();
|
||||||
|
|
||||||
const client = await SigningStargateClient.connectWithSigner(
|
const client = await SigningStargateClient.connectWithSigner(
|
||||||
config.upstream.rpcEndpoint,
|
config.registry.rpcEndpoint,
|
||||||
wallet,
|
wallet,
|
||||||
{ gasPrice: GasPrice.fromString(`${config.upstream.gasPrice}${config.upstream.denom}`) }
|
{ gasPrice: GasPrice.fromString(`${config.registry.gasPrice}${config.registry.denom}`) }
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await client.sendTokens(
|
const result = await client.sendTokens(
|
||||||
faucetAccount.address,
|
faucetAccount.address,
|
||||||
recipientAddress,
|
recipientAddress,
|
||||||
[{ denom: config.upstream.denom, amount: amount }],
|
[{ denom: config.registry.denom, amount: amount }],
|
||||||
'auto',
|
'auto',
|
||||||
'Faucet transfer'
|
'Faucet transfer'
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user