From 3b3950d8c9ce13f77d5c0cef08d37b58656a073c Mon Sep 17 00:00:00 2001 From: praveen-klaytn Date: Wed, 30 Oct 2024 09:56:11 +0530 Subject: [PATCH] Added faucet api --- .env.example | 18 ++++++--- app.js | 36 +++++++++++++++-- package.json | 4 +- services/faucetService.js | 84 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 132 insertions(+), 10 deletions(-) create mode 100644 services/faucetService.js diff --git a/.env.example b/.env.example index 5b934d9..0353c4c 100644 --- a/.env.example +++ b/.env.example @@ -5,11 +5,11 @@ COIN_GECKO_BASE_URL="https://api.coingecko.com/api/v3/coins/markets?x_cg_demo_ap COIN_GECKO_POSTFIX="&vs_currency=usd&ids=klay-token" # info form submission projects and partner -TOKEN_AUTH_WEBSTIE=a33077fa53b1abb014ccd189ae77f7744080865286ae6fd3f88d92eee25dad53 -COLLECTION_PARTNER_ID=66a8ba5239a3fbe8e678dae4 -FORM_ID_SUBMISSION_PROJECTS=66b0c1cb15d49137ecd3e59c -FORM_ID_SUBMISSION_IOK=66c9a377866d1ce393c8b686 -COLLECTION_CATEGORIES_PARTNERS_ID=66a8ba5239a3fbe8e678db00 +TOKEN_AUTH_WEBSTIE= +COLLECTION_PARTNER_ID= +FORM_ID_SUBMISSION_PROJECTS= +FORM_ID_SUBMISSION_IOK= +COLLECTION_CATEGORIES_PARTNERS_ID= # In milliseconds ex: 24*60*60*1000 => every 24 hours JOB_INTERVAL=86400000 @@ -17,4 +17,10 @@ CALL_INTERVAL_API=10*60*1000 # Kaia Releases KAIACHAIN_GITHUB_API="https://api.github.com/repos/kaiachain/kaia/releases?per_page=100" -KAIACHAIN_GITHUB_API_KEY= \ No newline at end of file +KAIACHAIN_GITHUB_API_KEY= + +# Faucet +KAIROS_RPC_URL=https://public-en-kairos.node.kaia.io +FAUCET_PRIVATE_KEY= +FAUCET_PUBLIC_KEY= +FAUCET_TRANSFER_VALUE=50 \ No newline at end of file diff --git a/app.js b/app.js index 26e0c4e..d352310 100644 --- a/app.js +++ b/app.js @@ -1,7 +1,6 @@ var express = require("express"); var app = express(); const fetch = require("node-fetch"); -// const cors = require("cors"); var StatsData = {}; const { Flipside } = require("@flipsidecrypto/sdk"); @@ -9,6 +8,7 @@ require("dotenv").config(); require("./services/partner.js"); const kaiachainService = require("./services/kaiachainService.js"); +const faucetService = require('./services/faucetService.js'); const flipside = new Flipside( process.env.FLIPSIDE_API_KEY, @@ -20,8 +20,6 @@ const COIN_GECKO_URL = process.env.COINGECKO_API_KEY + process.env.COIN_GECKO_POSTFIX; -// app.use(cors()); - app.get("/analytics", async function (req, res) { return res.status(200).json({ success: true, data: StatsData }); }); @@ -30,6 +28,38 @@ app.get("/health", async function (req, res) { return res.status(200).json({ success: true }); }); +app.get("/faucet/balance", async function (req, res) { + try { + let address = req.query.address || ""; + if(!address) { + throw new Error("Address is required") + } + let results = await faucetService.getBalance(address); + return res.status(200).json({ success: true, data: results}) + } catch(err) { + console.log(err.message); + return res.status(200).json({ success: false, message: err.message, data: {} }); + } +}); + +app.get("/faucet/run", async function (req, res) { + try { + let address = req.query.address || ""; + if(!address) { + throw new Error("Address is required") + } + let results = await faucetService.runFaucet(address); + return res.status(200).json({ success: true, data: results}) + } catch(err) { + console.log(err.message); + return res.status(200).json({ success: false, message: err.message }); + } +}); + +app.get("/health", async function (req, res) { + return res.status(200).json({ success: true }); +}); + app.get('/node/releases', async (req, res) => { try { let start = parseInt(req.query.start || "0"); diff --git a/package.json b/package.json index b1dd0a2..8c5228a 100644 --- a/package.json +++ b/package.json @@ -11,9 +11,11 @@ "license": "ISC", "dependencies": { "@flipsidecrypto/sdk": "^2.1.0", + "@kaiachain/web3js-ext": "^1.0.1", "cors": "^2.8.5", "dotenv": "^16.4.5", "express": "^4.19.2", - "node-fetch": "^2.7.0" + "node-fetch": "^2.7.0", + "web3": "^4.14.0" } } diff --git a/services/faucetService.js b/services/faucetService.js new file mode 100644 index 0000000..c546a24 --- /dev/null +++ b/services/faucetService.js @@ -0,0 +1,84 @@ +const { KlaytnWeb3, TxType, toPeb } = require("@kaiachain/web3js-ext"); +const { Web3 } = require("web3"); + +global.faucetCache = {}; +let service = {}; + +const provider = new Web3.providers.HttpProvider(process.env.KAIROS_RPC_URL); +const web3 = new KlaytnWeb3(provider); + +async function clearOldCache() { + let entries = Object.entries(global.faucetCache); + for (let i = 0; i < entries.length; i++) { + let item = entries[i]; + if (item[1]) + if (Date.now() - 86400000 > item[1]) { + delete global.faucetCache[item[0]]; + } + } +} + +function isValidClaim(_address) { + _address = _address.toLowerCase(); + let lastRegisteredTime = global.faucetCache[_address] || 0; + return Date.now() - 86400000 > lastRegisteredTime; +} + +service.getBalance = async (_address) => { + let isAddress = web3.utils.isAddress(_address); + clearOldCache(); + if (isAddress) { + let balance = await web3.eth.getBalance(_address); + console.log(balance); + balance = web3.utils.fromWei(balance, "ether"); + return { balance }; + } else { + throw new Error("Not valid Address"); + } +}; + +service.runFaucet = async (_address) => { + try { + let isAddress = web3.utils.isAddress(_address); + if (isAddress) { + _address = _address.toLowerCase(); + + if (isValidClaim(_address)) { + const senderAccount = web3.eth.accounts.privateKeyToAccount( + process.env.FAUCET_PRIVATE_KEY + ); + + const tx = { + type: TxType.ValueTransfer, + from: process.env.FAUCET_PUBLIC_KEY, + to: _address, + value: toPeb(process.env.FAUCET_TRANSFER_VALUE, "KLAY"), + }; + + const signResult = await senderAccount.signTransaction(tx); + console.log("rawTx", signResult.rawTransaction); + + const receipt = await web3.eth.sendSignedTransaction( + signResult.rawTransaction + ); + console.log(receipt); + + let balance = await web3.eth.getBalance(_address); + balance = web3.utils.fromWei(balance, "ether"); + + global.faucetCache[_address] = Date.now(); + + return { balance, txnHash: receipt.transactionHash }; + } else { + throw new Error("Already claimed"); + } + } else { + throw new Error("Not valid Address"); + } + } catch (err) { + console.log(err); + throw new Error("Problem while claiming"); + } +}; + +module.exports = service;