Full lesson
vijay
A trustless freelancing platform on Solana
What it does
From the project README:
Vijay is a decentralized freelancing platform built on the Solana blockchain. It introduces a new trustless protocol where clients and freelancers can collaborate on projects securely, without intermediaries. The platform leverages smart contracts to manage registration, project assignment, escrowed payments, and task validation. It enables Clients and Freelancers to collaborate trustlessly using a milestone-based escrow system. Funds are securely held in program-controlled accounts and released on task approval, ensuring a transparent and secure freelancing experience.
- Original
- Harry-027/vijay
- SuperStack fork
- Blockchains/vijay @
bf21c0c - Stars
- 0
- Last push (original)
- 2025-05-14
- Languages
- TypeScript, Rust, CSS, JavaScript
- Detected stack
- AnchorNext.jsweb3.js
- Categories
- Infrastructure & Tooling
- Licence
- MIT
- Homepage
- https://vijay-woad.vercel.app
Architecture
57 source files. By extension: .tsx ×36, .rs ×12, .ts ×9.
| Top-level directory | Source files |
|---|---|
src | 39 |
anchor | 17 |
(root) | 1 |
On-chain programs: anchor/programs/vijay
Dapp: deploying
This fork is queued for its own live dapp on Sol.College, with a Devnet/Mainnet switch. It appears here, at /dapps/vijay/, once it is deployed and checked on devnet. Until then, run it locally with the commands below.
Key code, explained
Excerpts chosen automatically where the code uses the most Solana concepts, pinned to the fork's commit.
anchor/programs/vijay/src/instructions/project_init.rs L49–72 @ bf21c0c · MIT · originalpub struct ProjectInfo<'info> { #[account(mut)] pub signer: Signer<'info>, #[account( mut, seeds = [b"client", signer.key().as_ref()], bump, )] pub client: Account<'info, Client>, #[account( mut, seeds = [b"client_report", signer.key().as_ref()], bump, )] pub client_report_card: Account<'info, ClientReportCard>, #[account( init, space = 8 + Project::INIT_SPACE, payer = signer, seeds = [b"client_project", client.project_counter.checked_add(1).unwrap().to_le_bytes().as_ref(), signer.key().as_ref()], bump- Line 51
pub signer: Signer<'info>,: Requires this account to have signed the transaction. - Line 55
seeds = [b"client", signer.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 56
bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve. - Line 68
init,: Creates and pays for a new account. Space and payer must be specified. - Line 71
seeds = [b"client_project", client.project_counter.checked_add(1).unwrap().to_le_bytes().a: Uses checked arithmetic so overflow fails instead of wrapping.
anchor/programs/vijay/src/instructions/project_transfer.rs L73–96 @ bf21c0c · MIT · original let mut actual_total_projects = client_report.total_projects.checked_sub(client_report.projects_in_progress).ok_or(ErrorCode::NumericalOverflow)?; if actual_total_projects == 0 { actual_total_projects = 1; } client_report.success_rate = ((client_report.completed * 10000) / actual_total_projects) as u16; let total_risk_points = client_report.transferred + client_report.withdrawn; let total_finished_projects = total_risk_points + client_report.completed; client_report.risk_score = ((total_risk_points * 10000) / total_finished_projects) as u16; Ok(())} #[derive(Accounts)]#[instruction(project_id: u64, new_freelancer_key: Pubkey)]pub struct TransferInfo<'info> { #[account(mut)] pub signer: Signer<'info>, #[account( mut, seeds = [b"client_project", project_id.to_le_bytes().as_ref(), signer.key().as_ref()], bump- Line 74
let mut actual_total_projects = client_report.total_projects.checked_sub(client_report.pro: Uses checked arithmetic so overflow fails instead of wrapping. - Line 91
pub signer: Signer<'info>,: Requires this account to have signed the transaction. - Line 95
seeds = [b"client_project", project_id.to_le_bytes().as_ref(), signer.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 96
bump: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
anchor/programs/vijay/src/instructions/project_withdraw.rs L49–72 @ bf21c0c · MIT · original let mut actual_total_projects = client_report_card.total_projects.checked_sub(client_report_card.projects_in_progress).ok_or(ErrorCode::NumericalOverflow)?; if actual_total_projects == 0 { actual_total_projects = 1; } client_report_card.success_rate = ((client_report_card.completed * 10000) / actual_total_projects) as u16; let total_risk_points = client_report_card.withdrawn + client_report_card.transferred; let total_finished_projects = total_risk_points + client_report_card.completed; client_report_card.risk_score = ((total_risk_points * 10000) / total_finished_projects) as u16; Ok(())} #[derive(Accounts)]#[instruction(project_id: u64)]pub struct WithdrawInfo<'info> { #[account(mut)] pub signer: Signer<'info>, #[account( mut, seeds = [b"client_project", project_id.to_le_bytes().as_ref(), signer.key().as_ref()], bump- Line 49
let mut actual_total_projects = client_report_card.total_projects.checked_sub(client_repor: Uses checked arithmetic so overflow fails instead of wrapping. - Line 67
pub signer: Signer<'info>,: Requires this account to have signed the transaction. - Line 71
seeds = [b"client_project", project_id.to_le_bytes().as_ref(), signer.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 72
bump: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
anchor/programs/vijay/src/instructions/freelancer_init.rs L25–48 @ bf21c0c · MIT · original freelancer_report.risk_score = 0; freelancer_report.success_rate = 0; Ok(())} #[derive(Accounts)]pub struct FreelancerInfo<'info> { #[account(mut)] pub signer: Signer<'info>, #[account( init, space = 8 + Freelancer::INIT_SPACE, payer = signer, seeds = [b"freelancer", signer.key().as_ref()], bump )] pub freelancer: Account<'info, Freelancer>, #[account( init, space = 8 + FreelancerReportCard::INIT_SPACE, seeds = [b"freelancer_report", signer.key().as_ref()], bump,- Line 33
pub signer: Signer<'info>,: Requires this account to have signed the transaction. - Line 36
init,: Creates and pays for a new account. Space and payer must be specified. - Line 39
seeds = [b"freelancer", signer.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 40
bump: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
How to run, build and test
These commands come from the repo's own manifests (package.json scripts, Anchor.toml, Cargo, go.mod, Makefile). Check the README for prerequisites and environment variables.
| Directory | Command | Runs |
|---|---|---|
. | npm run dev | next dev |
. | npm run start | next start |
. | npm run build | next build |
. | npm run lint | next lint |
anchor | anchor build | compile programs and generate IDLs |
anchor | anchor test | local validator + test suite |
anchor | anchor run test | ../node_modules/.bin/jest --preset ts-jest |
What you could build from it
- Fork the SuperStack copy and extend it within the Infrastructure & Tooling gaps listed on the idea page.
- Learn the pattern first: Building and sending transactions from a client
- Learn the pattern first: Anchor account constraints