This commit is contained in:
Sewmina Dilshan 2025-04-02 07:32:11 +05:30
commit 0fb11d9009
13 changed files with 3481 additions and 0 deletions

41
.gitignore vendored Normal file
View File

@ -0,0 +1,41 @@
lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
*.swp
pids
logs
results
tmp
# Build
public/css/main.css
# Coverage reports
coverage
# API keys and secrets
.env
# Dependency directory
node_modules
bower_components
# Editors
.idea
*.iml
# OS metadata
.DS_Store
Thumbs.db
# Ignore built ts files
dist/**/*
# ignore yarn.lock
yarn.lock

11
eslint.config.mjs Normal file
View File

@ -0,0 +1,11 @@
import globals from "globals";
import pluginJs from "@eslint/js";
import tseslint from "typescript-eslint";
export default [
{files: ["**/*.{js,mjs,cjs,ts}"]},
{languageOptions: { globals: globals.browser }},
pluginJs.configs.recommended,
...tseslint.configs.recommended,
];

2608
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

30
package.json Normal file
View File

@ -0,0 +1,30 @@
{
"name": "bets_controller",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"init": "ts-node src/init.ts",
"create": "ts-node src/create.ts",
"join": "ts-node src/join.ts",
"get": "ts-node src/get.ts",
"close": "ts-node src/close.ts",
"build": "tsc"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"devDependencies": {
"@eslint/js": "^9.13.0",
"eslint": "^9.13.0",
"globals": "^15.11.0",
"typescript": "^5.6.3",
"typescript-eslint": "^8.11.0"
},
"dependencies": {
"@coral-xyz/anchor": "^0.30.1",
"@solana/spl-token": "^0.4.9",
"@solana/web3.js": "^1.98.0",
"bs58": "^6.0.0"
}
}

292
src/bets.json Normal file
View File

@ -0,0 +1,292 @@
{
"address": "HxsDuhD7wcPxcMsrYdteMYxkffuwff8HoxhZ7NuFtM37",
"metadata": {
"name": "bets",
"version": "0.1.0",
"spec": "0.1.0",
"description": "Created with Anchor"
},
"instructions": [
{
"name": "close_bet",
"discriminator": [
185,
206,
13,
184,
176,
108,
140,
107
],
"accounts": [
{
"name": "bets_list",
"writable": true
},
{
"name": "bet_vault",
"writable": true
},
{
"name": "winner",
"writable": true
},
{
"name": "payer",
"writable": true,
"signer": true
},
{
"name": "system_program",
"address": "11111111111111111111111111111111"
}
],
"args": [
{
"name": "winner",
"type": "pubkey"
}
]
},
{
"name": "create_bet",
"discriminator": [
197,
42,
153,
2,
59,
63,
143,
246
],
"accounts": [
{
"name": "payer",
"writable": true,
"signer": true
},
{
"name": "bets_list",
"writable": true
},
{
"name": "bet_vault",
"writable": true,
"pda": {
"seeds": [
{
"kind": "const",
"value": [
98,
101,
116,
95,
118,
97,
117,
108,
116
]
},
{
"kind": "account",
"path": "payer"
},
{
"kind": "arg",
"path": "game_id"
},
{
"kind": "arg",
"path": "_nonce"
}
]
}
},
{
"name": "system_program",
"address": "11111111111111111111111111111111"
}
],
"args": [
{
"name": "wager",
"type": "u64"
},
{
"name": "game_id",
"type": "string"
},
{
"name": "nonce",
"type": "u64"
}
]
},
{
"name": "initialize",
"discriminator": [
175,
175,
109,
31,
13,
152,
155,
237
],
"accounts": [
{
"name": "bets_list",
"writable": true,
"pda": {
"seeds": [
{
"kind": "const",
"value": [
98,
101,
116,
115,
95,
108,
105,
115,
116
]
}
]
}
},
{
"name": "payer",
"writable": true,
"signer": true
},
{
"name": "system_program",
"address": "11111111111111111111111111111111"
}
],
"args": []
},
{
"name": "join_bet",
"discriminator": [
69,
116,
82,
26,
144,
192,
58,
238
],
"accounts": [
{
"name": "bet_vault",
"writable": true
},
{
"name": "payer",
"writable": true,
"signer": true
},
{
"name": "system_program",
"address": "11111111111111111111111111111111"
}
],
"args": [
{
"name": "game_id",
"type": "string"
}
]
}
],
"accounts": [
{
"name": "BetVault",
"discriminator": [
103,
78,
21,
234,
18,
250,
230,
209
]
},
{
"name": "BetsList",
"discriminator": [
231,
234,
50,
58,
81,
179,
239,
117
]
}
],
"errors": [
{
"code": 6000,
"name": "CustomError",
"msg": "Custom error message"
}
],
"types": [
{
"name": "BetVault",
"type": {
"kind": "struct",
"fields": [
{
"name": "game_id",
"type": "string"
},
{
"name": "owner",
"type": "pubkey"
},
{
"name": "joiner",
"type": "pubkey"
},
{
"name": "wager",
"type": "u64"
}
]
}
},
{
"name": "BetsList",
"type": {
"kind": "struct",
"fields": [
{
"name": "bets",
"type": {
"vec": "pubkey"
}
}
]
}
}
],
"constants": [
{
"name": "SEED",
"type": "string",
"value": "\"anchor\""
}
]
}

298
src/bets.ts Normal file
View File

@ -0,0 +1,298 @@
/**
* Program IDL in camelCase format in order to be used in JS/TS.
*
* Note that this is only a type helper and is not the actual IDL. The original
* IDL can be found at `target/idl/bets.json`.
*/
export type Bets = {
"address": "HxsDuhD7wcPxcMsrYdteMYxkffuwff8HoxhZ7NuFtM37",
"metadata": {
"name": "bets",
"version": "0.1.0",
"spec": "0.1.0",
"description": "Created with Anchor"
},
"instructions": [
{
"name": "closeBet",
"discriminator": [
185,
206,
13,
184,
176,
108,
140,
107
],
"accounts": [
{
"name": "betsList",
"writable": true
},
{
"name": "betVault",
"writable": true
},
{
"name": "winner",
"writable": true
},
{
"name": "payer",
"writable": true,
"signer": true
},
{
"name": "systemProgram",
"address": "11111111111111111111111111111111"
}
],
"args": [
{
"name": "winner",
"type": "pubkey"
}
]
},
{
"name": "createBet",
"discriminator": [
197,
42,
153,
2,
59,
63,
143,
246
],
"accounts": [
{
"name": "payer",
"writable": true,
"signer": true
},
{
"name": "betsList",
"writable": true
},
{
"name": "betVault",
"writable": true,
"pda": {
"seeds": [
{
"kind": "const",
"value": [
98,
101,
116,
95,
118,
97,
117,
108,
116
]
},
{
"kind": "account",
"path": "payer"
},
{
"kind": "arg",
"path": "gameId"
},
{
"kind": "arg",
"path": "nonce"
}
]
}
},
{
"name": "systemProgram",
"address": "11111111111111111111111111111111"
}
],
"args": [
{
"name": "wager",
"type": "u64"
},
{
"name": "gameId",
"type": "string"
},
{
"name": "nonce",
"type": "u64"
}
]
},
{
"name": "initialize",
"discriminator": [
175,
175,
109,
31,
13,
152,
155,
237
],
"accounts": [
{
"name": "betsList",
"writable": true,
"pda": {
"seeds": [
{
"kind": "const",
"value": [
98,
101,
116,
115,
95,
108,
105,
115,
116
]
}
]
}
},
{
"name": "payer",
"writable": true,
"signer": true
},
{
"name": "systemProgram",
"address": "11111111111111111111111111111111"
}
],
"args": []
},
{
"name": "joinBet",
"discriminator": [
69,
116,
82,
26,
144,
192,
58,
238
],
"accounts": [
{
"name": "betVault",
"writable": true
},
{
"name": "payer",
"writable": true,
"signer": true
},
{
"name": "systemProgram",
"address": "11111111111111111111111111111111"
}
],
"args": [
{
"name": "gameId",
"type": "string"
}
]
}
],
"accounts": [
{
"name": "betVault",
"discriminator": [
103,
78,
21,
234,
18,
250,
230,
209
]
},
{
"name": "betsList",
"discriminator": [
231,
234,
50,
58,
81,
179,
239,
117
]
}
],
"errors": [
{
"code": 6000,
"name": "customError",
"msg": "Custom error message"
}
],
"types": [
{
"name": "betVault",
"type": {
"kind": "struct",
"fields": [
{
"name": "gameId",
"type": "string"
},
{
"name": "owner",
"type": "pubkey"
},
{
"name": "joiner",
"type": "pubkey"
},
{
"name": "wager",
"type": "u64"
}
]
}
},
{
"name": "betsList",
"type": {
"kind": "struct",
"fields": [
{
"name": "bets",
"type": {
"vec": "pubkey"
}
}
]
}
}
],
"constants": [
{
"name": "seed",
"type": "string",
"value": "\"anchor\""
}
]
};

42
src/close.ts Normal file
View File

@ -0,0 +1,42 @@
import { Connection, PublicKey, Keypair, clusterApiUrl, LAMPORTS_PER_SOL, SystemProgram } from "@solana/web3.js";
import { Bets } from "./bets";
import { AnchorProvider, BN, Program, Wallet } from "@coral-xyz/anchor";
import { clusterUrl, cocSk, getRandomInt, testerSk } from "./shared";
const IDL = require('./bets.json');
async function close(){
const keypair = Keypair.fromSecretKey(Uint8Array.from(testerSk));
const cocKeypair = Keypair.fromSecretKey(Uint8Array.from(cocSk));
const connection = new Connection(clusterUrl);
const provider = new AnchorProvider(connection, new Wallet(keypair));
const program:Program<Bets> = new Program<Bets>(IDL,provider);
let solBalance = (await connection.getBalance(keypair.publicKey))/ LAMPORTS_PER_SOL;
console.log(`Tester ${keypair.publicKey} has ${solBalance} SOL`);
const [bet_list_pda] = await PublicKey.findProgramAddress([Buffer.from("bets_list")], program.programId);
console.log(`Bets list PDA : ${bet_list_pda}`);
const bet_list = await program.account.betsList.fetch(bet_list_pda);
for (const bet of bet_list.bets) {
const betAcc = await program.account.betVault.fetch(bet);
const winner = betAcc.owner;
console.log(`Closing ${bet}`);
const tx = await program.methods.closeBet(winner).accounts({
betVault: bet,
betsList: bet_list_pda,
winner: winner
}).rpc();
console.log(`tx: ${tx}`);
const hash = await connection.confirmTransaction(tx);
console.log(`tx complete: ${hash}`);
solBalance = (await connection.getBalance(keypair.publicKey))/ LAMPORTS_PER_SOL;
console.log(`Tester ${keypair.publicKey} has ${solBalance} SOL`);
}
}
close();

38
src/create.ts Normal file
View File

@ -0,0 +1,38 @@
import { Connection, PublicKey, Keypair, clusterApiUrl, LAMPORTS_PER_SOL } from "@solana/web3.js";
import { Bets } from "./bets";
import { AnchorProvider, BN, Program, Wallet } from "@coral-xyz/anchor";
import { clusterUrl, cocSk, getRandomInt, testerSk } from "./shared";
const IDL = require('./bets.json');
async function create(){
const keypair = Keypair.fromSecretKey(Uint8Array.from(testerSk));
const connection = new Connection(clusterUrl);
const provider = new AnchorProvider(connection, new Wallet(keypair));
const program:Program<Bets> = new Program<Bets>(IDL,provider);
let solBalance = (await connection.getBalance(keypair.publicKey))/ LAMPORTS_PER_SOL;
console.log(`Tester ${keypair.publicKey} has ${solBalance} SOL`);
if(solBalance <= 1){
console.log("Requesting airdrop due to insufficient funds");
try{
await connection.requestAirdrop(keypair.publicKey, 5);
}catch{
console.log("Airdrop failed");
}
solBalance = await connection.getBalance(keypair.publicKey);
console.log(`Tester now has ${solBalance} SOL`);
}
const [bet_list_pda] = await PublicKey.findProgramAddress([Buffer.from("bets_list")], program.programId);
console.log(`Bets list PDA : ${bet_list_pda}`);
const nonce = getRandomInt(100000000000);
const tx = await program.methods.createBet(new BN(100000000), "tetris", new BN(nonce)).accounts({
betsList: bet_list_pda,
}).rpc();
console.log(`create tx : ${tx}`);
await connection.confirmTransaction(tx);
console.log(`tx complete`);
}
create();

33
src/get.ts Normal file
View File

@ -0,0 +1,33 @@
import { Connection, PublicKey, Keypair, clusterApiUrl, LAMPORTS_PER_SOL } from "@solana/web3.js";
import { Bets } from "./bets";
import { AnchorProvider, BN, Program, Wallet } from "@coral-xyz/anchor";
import { clusterUrl, cocSk } from "./shared";
const IDL = require('./bets.json');
async function get(){
const keypair = Keypair.fromSecretKey(Uint8Array.from(cocSk));
const connection = new Connection(clusterUrl);
const provider = new AnchorProvider(connection, new Wallet(keypair));
const program: Program<Bets> = new Program<Bets>(IDL, provider);
const [bet_list_pda] = await PublicKey.findProgramAddress([Buffer.from("bets_list")], program.programId);
console.log(`Bets list PDA : ${bet_list_pda}`);
const bet_list = await program.account.betsList.fetch(bet_list_pda);
console.log(bet_list);
bet_list.bets.forEach(async bet =>{
const betAcc = await program.account.betVault.fetch(bet);
let solBalance = (await connection.getBalance(bet))/ LAMPORTS_PER_SOL;
console.log({
game_id: betAcc.gameId,
owner: betAcc.owner,
joiner: betAcc.joiner,
wager: betAcc.wager.toNumber() + `: ${betAcc.wager.toNumber() / LAMPORTS_PER_SOL}SOL`,
balance: solBalance + " SOL"
});
})
}
get();

18
src/init.ts Normal file
View File

@ -0,0 +1,18 @@
import { Connection, PublicKey, Keypair, clusterApiUrl } from "@solana/web3.js";
import { Bets } from "./bets";
import { AnchorProvider, Program, Wallet } from "@coral-xyz/anchor";
import { clusterUrl, cocSk } from "./shared";
const IDL = require('./bets.json');
async function Init(){
const keypair = Keypair.fromSecretKey(Uint8Array.from(cocSk));
const connection = new Connection(clusterUrl);
const provider = new AnchorProvider(connection, new Wallet(keypair));
const program: Program<Bets> = new Program<Bets>(IDL, provider);
console.log(program.programId);
await program.methods.initialize().rpc();
}
Init();

49
src/join.ts Normal file
View File

@ -0,0 +1,49 @@
import { Connection, PublicKey, Keypair, clusterApiUrl, LAMPORTS_PER_SOL, SystemProgram } from "@solana/web3.js";
import { Bets } from "./bets";
import { AnchorProvider, BN, Program, Wallet } from "@coral-xyz/anchor";
import { clusterUrl, cocSk, getRandomInt, testerSk } from "./shared";
const IDL = require('./bets.json');
async function join(){
const keypair = Keypair.fromSecretKey(Uint8Array.from(testerSk));
const connection = new Connection(clusterUrl);
const provider = new AnchorProvider(connection, new Wallet(keypair));
const program:Program<Bets> = new Program<Bets>(IDL,provider);
let solBalance = (await connection.getBalance(keypair.publicKey))/ LAMPORTS_PER_SOL;
console.log(`Tester ${keypair.publicKey} has ${solBalance} SOL`);
if(solBalance <= 1){
console.log("Requesting airdrop due to insufficient funds");
try{
await connection.requestAirdrop(keypair.publicKey, 5);
}catch{
console.log("Airdrop failed");
}
solBalance = await connection.getBalance(keypair.publicKey);
console.log(`Tester now has ${solBalance} SOL`);
}
const [bet_list_pda] = await PublicKey.findProgramAddress([Buffer.from("bets_list")], program.programId);
console.log(`Bets list PDA : ${bet_list_pda}`);
const bet_list = await program.account.betsList.fetch(bet_list_pda);
for (const bet of bet_list.bets) {
const betAcc = await program.account.betVault.fetch(bet);
if (betAcc.joiner.toBase58() == SystemProgram.programId.toBase58()) {
console.log(`Joining ${bet}`);
const tx = await program.methods.joinBet("tetris").accounts({
betVault: bet,
}).rpc();
console.log(`tx: ${tx}`);
const hash = await connection.confirmTransaction(tx);
console.log(`tx complete: ${hash}`);
}else{
console.log(`${bet} is already filled`);
}
}
}
join();

9
src/shared.ts Normal file
View File

@ -0,0 +1,9 @@
import { clusterApiUrl, PublicKey } from "@solana/web3.js";
export const clusterUrl = clusterApiUrl("devnet");
export const testerSk = [0,86,239,216,67,18,45,223,17,96,119,58,187,90,175,61,72,117,44,13,224,255,64,74,222,14,50,134,240,250,14,212,13,59,115,13,19,107,33,227,1,184,184,96,20,214,181,23,53,244,82,197,36,189,83,82,134,211,83,200,67,14,143,90];
export const cocSk = [202,150,67,41,155,133,176,172,9,100,150,190,239,37,69,73,18,16,76,65,164,197,99,134,240,151,112,65,61,122,95,41,9,44,6,237,108,123,86,90,144,27,1,160,101,95,239,35,53,91,195,220,22,214,2,84,132,37,20,236,133,242,104,197];
export function getRandomInt(max) {
return Math.floor(Math.random() * max);
}

12
tsconfig.json Normal file
View File

@ -0,0 +1,12 @@
{
"compilerOptions": {
"module": "commonjs",
"esModuleInterop": true,
"rootDir": "./src",
"target": "es6",
"moduleResolution": "node",
"sourceMap": true,
"outDir": "dist"
},
"lib": ["es2015"]
}