Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 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
14e29b6
Start draft PR
SoYan500 Jun 7, 2025
f92d76b
Merged branch pr-1-Bhing26-Koii-Task-Funder-Express for PR https://gi…
SoYan500 Jun 7, 2025
42e8938
Merged branch pr-2-Bhing26-Koii-Task-Funder-Express for PR https://gi…
SoYan500 Jun 7, 2025
995f674
Merged branch pr-3-Bhing26-Koii-Task-Funder-Express for PR https://gi…
SoYan500 Jun 7, 2025
1865771
Merged branch pr-4-Bhing26-Koii-Task-Funder-Express for PR https://gi…
SoYan500 Jun 7, 2025
98a2f34
Resolve package.json merge conflict and add all necessary dependencies
SoYan500 Jun 7, 2025
9b0dffe
Add mock crypto prices JSON for testing
SoYan500 Jun 7, 2025
1d76dcc
Update input validation middleware to support array of validators
SoYan500 Jun 7, 2025
441014c
Update mock crypto prices with full test-compliant structure
SoYan500 Jun 7, 2025
cd97ee1
Update SpiderMan route to handle case variations and invalid routes
SoYan500 Jun 7, 2025
a0ba6a5
Update input validation middleware with improved validation and next(…
SoYan500 Jun 7, 2025
620c06b
Update mock crypto prices with full test-required structure
SoYan500 Jun 7, 2025
f15aa4a
Update SpiderMan route to handle precise route variations and 404
SoYan500 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