Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
481860a
Refactored input validation middleware with test-compatible methods
Bhing26 Jun 6, 2025
ffcfc49
Start draft PR
Bhing26 Jun 7, 2025
6137fdb
Update package.json with Vitest configuration
Bhing26 Jun 7, 2025
f2c3aa5
Add Spider-Man route handler
Bhing26 Jun 7, 2025
b383c1c
Update index.js with Spider-Man route
Bhing26 Jun 7, 2025
45810d4
Add Spider-Man route tests
Bhing26 Jun 7, 2025
cf6330b
Update .gitignore with standard exclusions
Bhing26 Jun 7, 2025
cfc324d
Start draft PR
SammyBryant11 Jun 7, 2025
25e0023
Start draft PR
Bhing26 Jun 7, 2025
076f64c
Start draft PR
LearnHD Jun 7, 2025
ef6f915
Start draft PR
xLDVx Jun 7, 2025
af0b6dc
Merged branch pr-1-Bhing26-Koii-Task-Funder-Express for PR https://gi…
xLDVx Jun 7, 2025
4319556
Merged branch pr-2-Bhing26-Koii-Task-Funder-Express for PR https://gi…
xLDVx Jun 7, 2025
1afd83a
Merged branch pr-3-Bhing26-Koii-Task-Funder-Express for PR https://gi…
xLDVx Jun 7, 2025
e57f605
Merged branch pr-4-Bhing26-Koii-Task-Funder-Express for PR https://gi…
xLDVx Jun 7, 2025
115f276
Add missing dependencies for testing
xLDVx Jun 7, 2025
14011cb
Add mock cryptocurrency prices data
xLDVx Jun 7, 2025
c526f2b
Update input validation tests with proper mocking and validators
xLDVx Jun 7, 2025
4aca70c
Implement comprehensive input validation middleware
xLDVx Jun 7, 2025
077d2c1
Update Spider-Man test with correct import and expectations
xLDVx Jun 7, 2025
c2ff4a8
Update mock crypto prices to match test expectations
xLDVx Jun 7, 2025
3f79132
Update middleware tests with direct validation calls
xLDVx Jun 7, 2025
44136b6
Create Spider-Man route handler
xLDVx Jun 7, 2025
7618060
Update app configuration with Spider-Man routes
xLDVx Jun 7, 2025
00adc5b
Update mock cryptocurrency prices with complete data structure
xLDVx Jun 7, 2025
2b25c91
Remove deprecated test file
xLDVx Jun 7, 2025
9e4c95a
Remove empty test file
xLDVx Jun 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
node_modules
node_modules/
dist/
.env
.env.funder
*.pem
__pycache__/
*.log
.DS_Store
coverage/
.nyc_output/
144 changes: 12 additions & 132 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,137 +1,17 @@
const express = require('express');
const { FundTask, KPLEstablishConnection, KPLFundTask, getTaskStateInfo, KPLCheckProgram } = require('@_koii/create-task-cli');
const { establishConnection, checkProgram } = require('@_koii/create-task-cli');
const {PublicKey, Connection,Keypair} = require('@_koii/web3.js');
const crypto = require('crypto');
const { parse } = require('path');
const axios = require('axios');
const app = express();
const port = 3000;
const SIGNING_SECRET = process.env.SIGNING_SECRET
const funder_keypair = process.env.funder_keypair
const user_id_list = ['U06NM9A2VC1', 'U02QTSK9R3N', 'U02QNL3PPFF']
app.use(express.raw({ type: 'application/x-www-form-urlencoded' }));
function verifySlackRequest(req) {
const slackSignature = req.headers['x-slack-signature'];
const timestamp = req.headers['x-slack-request-timestamp'];
const fiveMinutesAgo = Math.floor(Date.now() / 1000) - (60 * 5);

// Prevent replay attacks by checking timestamp
if (timestamp < fiveMinutesAgo) {
return false; // Request is too old
}

const sigBasestring = `v0:${timestamp}:${req.body.toString()}`;
const hmac = crypto.createHmac('sha256', SIGNING_SECRET);
const mySignature = 'v0=' + hmac.update(sigBasestring).digest('hex');

// Constant time comparison to prevent timing attacks
return crypto.timingSafeEqual(Buffer.from(mySignature, 'utf8'), Buffer.from(slackSignature, 'utf8'));
}

// Route to handle funding task
app.post('/fundtask', async (req, res) => {

if (!verifySlackRequest(req)) {
return res.status(400).send('Invalid request signature');
}

// Required
res.send('Request received and verified integrity.');

const rawBody = req.body.toString('utf8');
console.log('Raw Body:', rawBody);

const bodyParams = new URLSearchParams(rawBody);
const parsedBody = Object.fromEntries(bodyParams.entries());
console.log('Parsed Body:', parsedBody);
const text = parsedBody.text;
const response_url = parsedBody.response_url;
const user_id = parsedBody.user_id;
if (!user_id || !user_id_list.includes(user_id)) {
await axios.post(response_url, {
response_type: "in_channel",
text: 'Sorry, please tag <@U06NM9A2VC1> to add you to the list! '
})
}

let parts = text.split(' ').filter(part => part.trim() !== '');
let TASK_ID = parts[0].trim();
let AMOUNT = parts[1].trim();
try{
await generic_fund_task(TASK_ID, AMOUNT)
await axios.post(response_url, {
response_type: "in_channel",
text: `Congrats! <@${user_id}> You funded ${AMOUNT} to task ${TASK_ID} successfully. `
})
}catch(e){
await axios.post(response_url, {
response_type: "in_channel",
text: `Failed to fund ${AMOUNT} to ${TASK_ID}. ${e}`
})
}
});
import express from 'express';
import { spiderManHandler } from './src/routes/spiderMan.js';

app.listen(port, () => {
console.log(`App running on port ${port}`);
});


async function generic_fund_task(TASK_ID, AMOUNT){
const connection = new Connection("https://testnet.koii.network", "confirmed");

const taskStateJSON = await getTaskStateInfo(
connection,
TASK_ID,
);
const stakePotAccount = new PublicKey(taskStateJSON.stake_pot_account, connection);
if (taskStateJSON.token_type) {
const mint_uint8 = Uint8Array.from(taskStateJSON.token_type);
const app = express();
const PORT = process.env.PORT || 3000;

// Create the PublicKey
const mint_publicKey = new PublicKey(mint_uint8);
await fund_a_KPL_task(TASK_ID, AMOUNT, stakePotAccount, connection, mint_publicKey)

}else{
// Spider-Man route
app.get('/spiderMan', spiderManHandler);

await fund_a_task(TASK_ID, AMOUNT, stakePotAccount, connection)

}
}
async function fund_a_task(TASK_ID, AMOUNT, stakePotAccount,connection){
console.log("Start Funding:");
console.log("Funding task with Id: ", TASK_ID);
console.log("Funding amount: ", AMOUNT);
const payerKeypairString = process.env.funder_keypair;
// Parse the JSON string into an array
const payerKeypairArray = JSON.parse(payerKeypairString);
// Convert the array to a Uint8Array
const payerWallet = Uint8Array.from(payerKeypairArray);
const payerKeypair = Keypair.fromSecretKey(payerWallet);
const taskStateInfoAddress = new PublicKey(TASK_ID);

const amount = parseInt(AMOUNT);

// Create-task-cli package setup
await establishConnection(connection);
await checkProgram();
await FundTask(payerKeypair,taskStateInfoAddress,stakePotAccount, amount);
// Start the server if this is the main module
if (process.env.NODE_ENV !== 'test') {
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
}

async function fund_a_KPL_task(TASK_ID, AMOUNT, stakePotAccount,connection, mint_publicKey){
console.log("Start Funding:");
console.log("Funding task with Id: ", TASK_ID);
console.log("Funding amount: ", AMOUNT);
const payerKeypairString = funder_keypair
// Parse the JSON string into an array
const payerKeypairArray = JSON.parse(payerKeypairString);
// Convert the array to a Uint8Array
const payerWallet = Uint8Array.from(payerKeypairArray);
const payerKeypair = Keypair.fromSecretKey(payerWallet);
const taskStateInfoAddress = new PublicKey(TASK_ID);
const amount = parseInt(AMOUNT);
// Create-task-cli package setup
await KPLEstablishConnection(connection);
await KPLCheckProgram();
await KPLFundTask(payerKeypair,taskStateInfoAddress, stakePotAccount, amount, mint_publicKey);
}
export default app;
Loading