From 08aeb031456b4834aa0d3a272f60c0e1a4794487 Mon Sep 17 00:00:00 2001 From: Kavit-Patel Date: Wed, 18 Dec 2024 16:58:06 +0530 Subject: [PATCH 1/4] Token Vesting with Employee Token Claim feature completed --- project-8-token-vesting/anchor/.eslintrc.json | 30 - project-8-token-vesting/anchor/.gitignore | 12 + .../anchor/.prettierignore | 7 + project-8-token-vesting/anchor/.swcrc | 29 - project-8-token-vesting/anchor/Anchor.toml | 11 +- project-8-token-vesting/anchor/README.md | 11 - project-8-token-vesting/anchor/jest.config.ts | 30 - .../anchor/migrations/deploy.ts | 5 +- project-8-token-vesting/anchor/package.json | 15 - .../{vesting => dapptokenvesting}/Cargo.toml | 14 +- .../{vesting => dapptokenvesting}/Xargo.toml | 0 .../{vesting => dapptokenvesting}/src/lib.rs | 93 ++- project-8-token-vesting/anchor/project.json | 56 -- .../anchor/src/dapptokenvesting-exports.ts | 31 + project-8-token-vesting/anchor/src/index.ts | 2 +- .../anchor/src/vesting-exports.ts | 29 - .../anchor/tests/bankrun.spec.ts | 172 ----- .../anchor/tests/dapptokenvesting.spec.ts | 76 +++ .../anchor/tests/fixtures/vesting.so | Bin 312512 -> 0 bytes project-8-token-vesting/anchor/tsconfig.json | 44 +- .../anchor/tsconfig.lib.json | 12 - .../anchor/tsconfig.spec.json | 14 - .../dist/web/.nx-helpers/compiled.js | 6 + .../dist/web/.nx-helpers/compose-plugins.js | 22 + .../dist/web/.nx-helpers/with-nx.js | 303 +++++++++ .../dist/web/next.config.js | 30 + project-8-token-vesting/dist/web/package.json | 8 + .../dist/web/public/.gitkeep | 0 .../dist/web/public/logo.png | Bin 0 -> 4609 bytes project-8-token-vesting/package.json | 15 +- .../web/app/account/[address]/page.tsx | 4 +- .../web/app/account/page.tsx | 4 +- .../web/app/api/hello/route.ts | 2 +- .../web/app/clusters/page.tsx | 4 +- .../web/app/createMint/page.tsx | 5 + .../web/app/createVesting/page.tsx | 5 + .../web/app/dapptokenvesting/page.tsx | 5 + .../web/app/employeetoken/page.tsx | 6 + .../web/{public => app}/favicon.ico | Bin project-8-token-vesting/web/app/global.css | 19 - project-8-token-vesting/web/app/globals.css | 49 ++ project-8-token-vesting/web/app/layout.tsx | 39 +- project-8-token-vesting/web/app/mint/page.tsx | 5 + .../web/app/page.module.css | 2 - project-8-token-vesting/web/app/page.tsx | 4 +- .../web/app/react-query-provider.tsx | 12 +- .../web/app/vestedemployees/page.tsx | 5 + .../web/app/vesting/page.tsx | 5 - .../account/account-data-access.tsx | 48 +- .../account/account-detail-feature.tsx | 38 +- .../account/account-list-feature.tsx | 14 +- .../web/components/account/account-ui.tsx | 262 ++++---- .../cluster/cluster-data-access.tsx | 107 ++-- .../components/cluster/cluster-feature.tsx | 29 +- .../web/components/cluster/cluster-ui.tsx | 57 +- .../components/common/common-data-access.tsx | 37 ++ .../web/components/common/common-loader.tsx | 24 + .../web/components/common/common-types.ts | 5 + .../web/components/common/common-utils.tsx | 2 + .../dashboard/dashboard-feature.tsx | 20 +- .../employee/employee-data-access.tsx | 161 +++++ .../components/employee/employee-feature.tsx | 23 + .../web/components/employee/employee-types.ts | 36 ++ .../web/components/employee/employee-ui.tsx | 341 ++++++++++ .../components/mintMeme/meme-data-access.tsx | 121 ++++ .../web/components/mintMeme/meme-feature.tsx | 45 ++ .../web/components/mintMeme/meme-types.ts | 5 + .../web/components/mintMeme/meme-ui.tsx | 187 ++++++ .../web/components/solana/solana-provider.tsx | 26 +- .../web/components/ui/navbar-link-list.tsx | 119 ++++ .../web/components/ui/ui-layout.tsx | 92 ++- .../vesting/vesting-data-access.tsx | 96 --- .../components/vesting/vesting-feature.tsx | 39 -- .../web/components/vesting/vesting-types.ts | 39 ++ .../web/components/vesting/vesting-ui.tsx | 169 ----- .../vesting/vestingdapp-data-access.tsx | 228 +++++++ .../vesting/vestingdapp-feature.tsx | 47 ++ .../web/components/vesting/vestingdapp-ui.tsx | 586 ++++++++++++++++++ project-8-token-vesting/web/public/logo.png | Bin 12306 -> 4609 bytes 79 files changed, 3084 insertions(+), 1171 deletions(-) delete mode 100644 project-8-token-vesting/anchor/.eslintrc.json create mode 100644 project-8-token-vesting/anchor/.gitignore create mode 100644 project-8-token-vesting/anchor/.prettierignore delete mode 100644 project-8-token-vesting/anchor/.swcrc delete mode 100644 project-8-token-vesting/anchor/README.md delete mode 100644 project-8-token-vesting/anchor/jest.config.ts delete mode 100644 project-8-token-vesting/anchor/package.json rename project-8-token-vesting/anchor/programs/{vesting => dapptokenvesting}/Cargo.toml (55%) rename project-8-token-vesting/anchor/programs/{vesting => dapptokenvesting}/Xargo.toml (100%) rename project-8-token-vesting/anchor/programs/{vesting => dapptokenvesting}/src/lib.rs (73%) delete mode 100644 project-8-token-vesting/anchor/project.json create mode 100644 project-8-token-vesting/anchor/src/dapptokenvesting-exports.ts delete mode 100644 project-8-token-vesting/anchor/src/vesting-exports.ts delete mode 100644 project-8-token-vesting/anchor/tests/bankrun.spec.ts create mode 100644 project-8-token-vesting/anchor/tests/dapptokenvesting.spec.ts delete mode 100755 project-8-token-vesting/anchor/tests/fixtures/vesting.so delete mode 100644 project-8-token-vesting/anchor/tsconfig.lib.json delete mode 100644 project-8-token-vesting/anchor/tsconfig.spec.json create mode 100644 project-8-token-vesting/dist/web/.nx-helpers/compiled.js create mode 100644 project-8-token-vesting/dist/web/.nx-helpers/compose-plugins.js create mode 100644 project-8-token-vesting/dist/web/.nx-helpers/with-nx.js create mode 100644 project-8-token-vesting/dist/web/next.config.js create mode 100644 project-8-token-vesting/dist/web/package.json create mode 100644 project-8-token-vesting/dist/web/public/.gitkeep create mode 100644 project-8-token-vesting/dist/web/public/logo.png create mode 100644 project-8-token-vesting/web/app/createMint/page.tsx create mode 100644 project-8-token-vesting/web/app/createVesting/page.tsx create mode 100644 project-8-token-vesting/web/app/dapptokenvesting/page.tsx create mode 100644 project-8-token-vesting/web/app/employeetoken/page.tsx rename project-8-token-vesting/web/{public => app}/favicon.ico (100%) delete mode 100644 project-8-token-vesting/web/app/global.css create mode 100644 project-8-token-vesting/web/app/globals.css create mode 100644 project-8-token-vesting/web/app/mint/page.tsx delete mode 100644 project-8-token-vesting/web/app/page.module.css create mode 100644 project-8-token-vesting/web/app/vestedemployees/page.tsx delete mode 100644 project-8-token-vesting/web/app/vesting/page.tsx create mode 100644 project-8-token-vesting/web/components/common/common-data-access.tsx create mode 100644 project-8-token-vesting/web/components/common/common-loader.tsx create mode 100644 project-8-token-vesting/web/components/common/common-types.ts create mode 100644 project-8-token-vesting/web/components/common/common-utils.tsx create mode 100644 project-8-token-vesting/web/components/employee/employee-data-access.tsx create mode 100644 project-8-token-vesting/web/components/employee/employee-feature.tsx create mode 100644 project-8-token-vesting/web/components/employee/employee-types.ts create mode 100644 project-8-token-vesting/web/components/employee/employee-ui.tsx create mode 100644 project-8-token-vesting/web/components/mintMeme/meme-data-access.tsx create mode 100644 project-8-token-vesting/web/components/mintMeme/meme-feature.tsx create mode 100644 project-8-token-vesting/web/components/mintMeme/meme-types.ts create mode 100644 project-8-token-vesting/web/components/mintMeme/meme-ui.tsx create mode 100644 project-8-token-vesting/web/components/ui/navbar-link-list.tsx delete mode 100644 project-8-token-vesting/web/components/vesting/vesting-data-access.tsx delete mode 100644 project-8-token-vesting/web/components/vesting/vesting-feature.tsx create mode 100644 project-8-token-vesting/web/components/vesting/vesting-types.ts delete mode 100644 project-8-token-vesting/web/components/vesting/vesting-ui.tsx create mode 100644 project-8-token-vesting/web/components/vesting/vestingdapp-data-access.tsx create mode 100644 project-8-token-vesting/web/components/vesting/vestingdapp-feature.tsx create mode 100644 project-8-token-vesting/web/components/vesting/vestingdapp-ui.tsx diff --git a/project-8-token-vesting/anchor/.eslintrc.json b/project-8-token-vesting/anchor/.eslintrc.json deleted file mode 100644 index 0121389..0000000 --- a/project-8-token-vesting/anchor/.eslintrc.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "extends": ["../.eslintrc.json"], - "ignorePatterns": ["!**/*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], - "rules": {} - }, - { - "files": ["*.ts", "*.tsx"], - "rules": {} - }, - { - "files": ["*.js", "*.jsx"], - "rules": {} - }, - { - "files": ["*.json"], - "parser": "jsonc-eslint-parser", - "rules": { - "@nx/dependency-checks": [ - "error", - { - "ignoredFiles": ["{projectRoot}/rollup.config.{js,ts,mjs,mts}"] - } - ] - } - } - ] -} diff --git a/project-8-token-vesting/anchor/.gitignore b/project-8-token-vesting/anchor/.gitignore new file mode 100644 index 0000000..22e78bc --- /dev/null +++ b/project-8-token-vesting/anchor/.gitignore @@ -0,0 +1,12 @@ +.anchor +.DS_Store +target/debug +target/deploy +target/release +target/sbf-solana-solana +target/test-ledger +target/.rustc_info.json +**/*.rs.bk +node_modules +test-ledger +.yarn diff --git a/project-8-token-vesting/anchor/.prettierignore b/project-8-token-vesting/anchor/.prettierignore new file mode 100644 index 0000000..4142583 --- /dev/null +++ b/project-8-token-vesting/anchor/.prettierignore @@ -0,0 +1,7 @@ +.anchor +.DS_Store +target +node_modules +dist +build +test-ledger diff --git a/project-8-token-vesting/anchor/.swcrc b/project-8-token-vesting/anchor/.swcrc deleted file mode 100644 index 28e88ec..0000000 --- a/project-8-token-vesting/anchor/.swcrc +++ /dev/null @@ -1,29 +0,0 @@ -{ - "jsc": { - "target": "es2017", - "parser": { - "syntax": "typescript", - "decorators": true, - "dynamicImport": true - }, - "transform": { - "decoratorMetadata": true, - "legacyDecorator": true - }, - "keepClassNames": true, - "externalHelpers": true, - "loose": true - }, - "module": { - "type": "es6" - }, - "sourceMaps": true, - "exclude": [ - "jest.config.ts", - ".*\\.spec.tsx?$", - ".*\\.test.tsx?$", - "./src/jest-setup.ts$", - "./**/jest-setup.ts$", - ".*.js$" - ] -} diff --git a/project-8-token-vesting/anchor/Anchor.toml b/project-8-token-vesting/anchor/Anchor.toml index 0d6bfbd..4a8bcb4 100644 --- a/project-8-token-vesting/anchor/Anchor.toml +++ b/project-8-token-vesting/anchor/Anchor.toml @@ -1,21 +1,22 @@ [toolchain] +anchor_version = "0.30.1" [features] resolution = true skip-lint = false -[programs.localnet] -vesting = "GFdLg11UBR8ZeePW43ZyD1gY4z4UQ96LPa22YBgnn4z8" +[programs.devnet] +vesting = "BnRyuRkBiWQ6D3Tsu5n4AuzzHjSbFHBcXKs63e51p1ft" [registry] -url = "https://api.apr.dev" +url = "https://api.devnet.solana.com" [provider] -cluster = "Localnet" +cluster = "Devnet" wallet = "~/.config/solana/id.json" [scripts] -test = "../node_modules/.bin/nx run anchor:jest" +test = "../node_modules/.bin/jest --preset ts-jest" [test] startup_wait = 5000 diff --git a/project-8-token-vesting/anchor/README.md b/project-8-token-vesting/anchor/README.md deleted file mode 100644 index f845a14..0000000 --- a/project-8-token-vesting/anchor/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# anchor - -This library was generated with [Nx](https://nx.dev). - -## Building - -Run `nx build anchor` to build the library. - -## Running unit tests - -Run `nx test anchor` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/project-8-token-vesting/anchor/jest.config.ts b/project-8-token-vesting/anchor/jest.config.ts deleted file mode 100644 index fd64796..0000000 --- a/project-8-token-vesting/anchor/jest.config.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* eslint-disable */ -import { readFileSync } from 'fs'; - -// Reading the SWC compilation config and remove the "exclude" -// for the test files to be compiled by SWC -const { exclude: _, ...swcJestConfig } = JSON.parse( - readFileSync(`${__dirname}/.swcrc`, 'utf-8') -); - -// disable .swcrc look-up by SWC core because we're passing in swcJestConfig ourselves. -// If we do not disable this, SWC Core will read .swcrc and won't transform our test files due to "exclude" -if (swcJestConfig.swcrc === undefined) { - swcJestConfig.swcrc = false; -} - -// Uncomment if using global setup/teardown files being transformed via swc -// https://nx.dev/packages/jest/documents/overview#global-setup/teardown-with-nx-libraries -// jest needs EsModule Interop to find the default exported setup/teardown functions -// swcJestConfig.module.noInterop = false; - -export default { - displayName: 'anchor', - preset: '../jest.preset.js', - transform: { - '^.+\\.[tj]s$': ['@swc/jest', swcJestConfig], - }, - moduleFileExtensions: ['ts', 'js', 'html'], - testEnvironment: '', - coverageDirectory: '../coverage/anchor', -}; diff --git a/project-8-token-vesting/anchor/migrations/deploy.ts b/project-8-token-vesting/anchor/migrations/deploy.ts index 221cf4f..e914965 100644 --- a/project-8-token-vesting/anchor/migrations/deploy.ts +++ b/project-8-token-vesting/anchor/migrations/deploy.ts @@ -2,9 +2,10 @@ // single deploy script that's invoked from the CLI, injecting a provider // configured from the workspace's Anchor.toml. -import * as anchor from '@coral-xyz/anchor'; +const anchor = require("@coral-xyz/anchor"); +import { AnchorProvider } from '@coral-xyz/anchor' -module.exports = async function (provider) { +module.exports = async function (provider: AnchorProvider) { // Configure client to use the provider. anchor.setProvider(provider); diff --git a/project-8-token-vesting/anchor/package.json b/project-8-token-vesting/anchor/package.json deleted file mode 100644 index 622c9d6..0000000 --- a/project-8-token-vesting/anchor/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "@token-vesting/anchor", - "version": "0.0.1", - "dependencies": { - "@coral-xyz/anchor": "^0.30.1", - "@solana/spl-token": "^0.4.8", - "@solana/web3.js": "1.94.0", - "anchor-bankrun": "^0.4.0", - "solana-bankrun": "^0.2.0", - "spl-token-bankrun": "0.2.5" - }, - "main": "./index.cjs", - "module": "./index.js", - "private": true -} diff --git a/project-8-token-vesting/anchor/programs/vesting/Cargo.toml b/project-8-token-vesting/anchor/programs/dapptokenvesting/Cargo.toml similarity index 55% rename from project-8-token-vesting/anchor/programs/vesting/Cargo.toml rename to project-8-token-vesting/anchor/programs/dapptokenvesting/Cargo.toml index 23b069f..1789181 100644 --- a/project-8-token-vesting/anchor/programs/vesting/Cargo.toml +++ b/project-8-token-vesting/anchor/programs/dapptokenvesting/Cargo.toml @@ -1,22 +1,22 @@ [package] -name = "vesting" +name = "dapptokenvesting" version = "0.1.0" description = "Created with Anchor" edition = "2021" [lib] crate-type = ["cdylib", "lib"] -name = "vesting" +name = "dapptokenvesting" [features] +default = [] +cpi = ["no-entrypoint"] no-entrypoint = [] no-idl = [] no-log-ix-name = [] -cpi = ["no-entrypoint"] -default = [] -idl-build = ["anchor-lang/idl-build", "anchor-spl/idl-build"] +idl-build = ["anchor-lang/idl-build","anchor-spl/idl-build"] [dependencies] -anchor-lang = { version="0.30.1", features=["init-if-needed"] } +anchor-lang = { version = "0.30.1", features = ["init-if-needed"] } anchor-spl = "0.30.1" -solana-program = "1.18.17" \ No newline at end of file +solana-program = "1.18.17" diff --git a/project-8-token-vesting/anchor/programs/vesting/Xargo.toml b/project-8-token-vesting/anchor/programs/dapptokenvesting/Xargo.toml similarity index 100% rename from project-8-token-vesting/anchor/programs/vesting/Xargo.toml rename to project-8-token-vesting/anchor/programs/dapptokenvesting/Xargo.toml diff --git a/project-8-token-vesting/anchor/programs/vesting/src/lib.rs b/project-8-token-vesting/anchor/programs/dapptokenvesting/src/lib.rs similarity index 73% rename from project-8-token-vesting/anchor/programs/vesting/src/lib.rs rename to project-8-token-vesting/anchor/programs/dapptokenvesting/src/lib.rs index 1aee746..c93ef60 100644 --- a/project-8-token-vesting/anchor/programs/vesting/src/lib.rs +++ b/project-8-token-vesting/anchor/programs/dapptokenvesting/src/lib.rs @@ -1,15 +1,16 @@ use anchor_lang::prelude::*; use anchor_spl::associated_token::AssociatedToken; -use anchor_spl::token_interface::{ self, Mint, TokenAccount, TokenInterface, TransferChecked }; +use anchor_spl::token_interface::{self, Mint, TokenAccount, TokenInterface, TransferChecked}; -declare_id!("GFdLg11UBR8ZeePW43ZyD1gY4z4UQ96LPa22YBgnn4z8"); +declare_id!("BnRyuRkBiWQ6D3Tsu5n4AuzzHjSbFHBcXKs63e51p1ft"); #[program] pub mod vesting { use super::*; pub fn create_vesting_account( ctx: Context, - company_name: String + company_name: String, + initial_fund_amount: u64, ) -> Result<()> { *ctx.accounts.vesting_account = VestingAccount { owner: ctx.accounts.signer.key(), @@ -20,6 +21,24 @@ pub mod vesting { bump: ctx.bumps.vesting_account, }; + let cpi_accounts = TransferChecked { + from: ctx + .accounts + .signer_associated_token_account + .to_account_info(), + mint: ctx.accounts.mint.to_account_info(), + to: ctx.accounts.treasury_token_account.to_account_info(), + authority: ctx.accounts.signer.to_account_info(), + }; + + let cpi_program = ctx.accounts.token_program.to_account_info(); + let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); + + token_interface::transfer_checked( + cpi_ctx, + initial_fund_amount, + ctx.accounts.mint.decimals, + )?; Ok(()) } @@ -28,7 +47,7 @@ pub mod vesting { start_time: i64, end_time: i64, total_amount: i64, - cliff_time: i64 + cliff_time: i64, ) -> Result<()> { *ctx.accounts.employee_account = EmployeeAccount { beneficiary: ctx.accounts.beneficiary.key(), @@ -46,49 +65,61 @@ pub mod vesting { pub fn claim_tokens(ctx: Context, _company_name: String) -> Result<()> { let employee_account = &mut ctx.accounts.employee_account; + let now = Clock::get()?.unix_timestamp; - - // Check if the current time is before the cliff time + if now < employee_account.cliff_time { return Err(ErrorCode::ClaimNotAvailableYet.into()); } - // Calculate the vested amount + let time_since_start = now.saturating_sub(employee_account.start_time); - let total_vesting_time = employee_account.end_time.saturating_sub( - employee_account.start_time - ); + let total_vesting_time = employee_account + .end_time + .saturating_sub(employee_account.start_time); + let vested_amount = if now >= employee_account.end_time { employee_account.total_amount } else { (employee_account.total_amount * time_since_start) / total_vesting_time }; - - //Calculate the amount that can be withdrawn + let claimable_amount = vested_amount.saturating_sub(employee_account.total_withdrawn); - // Check if there is anything left to claim - if claimable_amount == 0 { + + if claimable_amount <= 0 { return Err(ErrorCode::NothingToClaim.into()); } + + let decimals = ctx.accounts.mint.decimals; + if claimable_amount < 0 { + return Err(ErrorCode::InvalidClaimAmount.into()); + } + + let claimable_amount_scaled = (claimable_amount as u64) + .checked_mul(10_u64.pow(decimals as u32)) + .ok_or(ErrorCode::InvalidClaimAmount)?; + let transfer_cpi_accounts = TransferChecked { from: ctx.accounts.treasury_token_account.to_account_info(), mint: ctx.accounts.mint.to_account_info(), to: ctx.accounts.employee_token_account.to_account_info(), authority: ctx.accounts.treasury_token_account.to_account_info(), }; + let cpi_program = ctx.accounts.token_program.to_account_info(); - let signer_seeds: &[&[&[u8]]] = &[ - &[ - b"vesting_treasury", - ctx.accounts.vesting_account.company_name.as_ref(), - &[ctx.accounts.vesting_account.treasury_bump], - ], - ]; - let cpi_context = CpiContext::new(cpi_program, transfer_cpi_accounts).with_signer( - signer_seeds - ); - let decimals = ctx.accounts.mint.decimals; - token_interface::transfer_checked(cpi_context, claimable_amount as u64, decimals)?; + + let signer_seeds: &[&[&[u8]]] = &[&[ + b"vesting_treasury", + ctx.accounts.vesting_account.company_name.as_ref(), + &[ctx.accounts.vesting_account.treasury_bump], + ]]; + + let cpi_context = + CpiContext::new(cpi_program, transfer_cpi_accounts).with_signer(signer_seeds); + + token_interface::transfer_checked(cpi_context, claimable_amount_scaled, decimals)?; + employee_account.total_withdrawn += claimable_amount; + Ok(()) } } @@ -98,6 +129,12 @@ pub mod vesting { pub struct CreateVestingAccount<'info> { #[account(mut)] pub signer: Signer<'info>, + #[account( + mut, + associated_token::mint = mint, + associated_token::authority = signer + )] + pub signer_associated_token_account: InterfaceAccount<'info, TokenAccount>, #[account( init, space = 8 + VestingAccount::INIT_SPACE, @@ -202,8 +239,10 @@ pub struct EmployeeAccount { #[error_code] pub enum ErrorCode { - #[msg("Claiming is not available yet.")] + #[msg("Claiming is not available yet. ")] ClaimNotAvailableYet, #[msg("There is nothing to claim.")] NothingToClaim, + #[msg("Claimed Amount is invalid. ")] + InvalidClaimAmount, } diff --git a/project-8-token-vesting/anchor/project.json b/project-8-token-vesting/anchor/project.json deleted file mode 100644 index edf5256..0000000 --- a/project-8-token-vesting/anchor/project.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "name": "anchor", - "$schema": "../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "anchor/src", - "projectType": "library", - "tags": [], - "targets": { - "build": { - "executor": "@nx/rollup:rollup", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/anchor", - "main": "anchor/src/index.ts", - "tsConfig": "anchor/tsconfig.lib.json", - "assets": [], - "project": "anchor/package.json", - "compiler": "swc", - "format": ["cjs", "esm"] - } - }, - "lint": { - "executor": "@nx/eslint:lint" - }, - "test": { - "executor": "nx:run-commands", - "options": { - "cwd": "anchor", - "commands": ["anchor test"], - "parallel": false - } - }, - "anchor": { - "executor": "nx:run-commands", - "options": { - "cwd": "anchor", - "commands": ["anchor"], - "parallel": false - } - }, - "localnet": { - "executor": "nx:run-commands", - "options": { - "cwd": "anchor", - "commands": ["anchor localnet"], - "parallel": false - } - }, - "jest": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "anchor/jest.config.ts" - } - } - } -} diff --git a/project-8-token-vesting/anchor/src/dapptokenvesting-exports.ts b/project-8-token-vesting/anchor/src/dapptokenvesting-exports.ts new file mode 100644 index 0000000..517a0d7 --- /dev/null +++ b/project-8-token-vesting/anchor/src/dapptokenvesting-exports.ts @@ -0,0 +1,31 @@ +// Here we export some useful types and functions for interacting with the Anchor program. +import { AnchorProvider, Program } from "@coral-xyz/anchor"; +import { Cluster, PublicKey } from "@solana/web3.js"; +import DapptokenvestingIDL from "../target/idl/vesting.json"; +import type { Vesting as Dapptokenvesting } from "../target/types/vesting"; + +// Re-export the generated IDL and type +export { Dapptokenvesting, DapptokenvestingIDL }; + +// The programId is imported from the program IDL. +export const DAPPTOKENVESTING_PROGRAM_ID = new PublicKey( + DapptokenvestingIDL.address +); + +// This is a helper function to get the Dapptokenvesting Anchor program. +export function getDapptokenvestingProgram(provider: AnchorProvider) { + return new Program(DapptokenvestingIDL as Dapptokenvesting, provider); +} + +// This is a helper function to get the program ID for the Dapptokenvesting program depending on the cluster. +export function getDapptokenvestingProgramId(cluster: Cluster) { + switch (cluster) { + case "devnet": + case "testnet": + // This is the program ID for the Dapptokenvesting program on devnet and testnet. + return new PublicKey("BnRyuRkBiWQ6D3Tsu5n4AuzzHjSbFHBcXKs63e51p1ft"); + case "mainnet-beta": + default: + return DAPPTOKENVESTING_PROGRAM_ID; + } +} diff --git a/project-8-token-vesting/anchor/src/index.ts b/project-8-token-vesting/anchor/src/index.ts index 44d1f46..1e38c91 100644 --- a/project-8-token-vesting/anchor/src/index.ts +++ b/project-8-token-vesting/anchor/src/index.ts @@ -1,3 +1,3 @@ // This file was generated by preset-anchor. Programs are exported from this file. -export * from './vesting-exports'; +export * from './dapptokenvesting-exports' diff --git a/project-8-token-vesting/anchor/src/vesting-exports.ts b/project-8-token-vesting/anchor/src/vesting-exports.ts deleted file mode 100644 index b33f423..0000000 --- a/project-8-token-vesting/anchor/src/vesting-exports.ts +++ /dev/null @@ -1,29 +0,0 @@ -// Here we export some useful types and functions for interacting with the Anchor program. -import { AnchorProvider, Program } from '@coral-xyz/anchor'; -import { Cluster, PublicKey } from '@solana/web3.js'; -import VestingIDL from '../target/idl/vesting.json'; -import type { Vesting } from '../target/types/vesting'; - -// Re-export the generated IDL and type -export { Vesting, VestingIDL }; - -// The programId is imported from the program IDL. -export const VESTING_PROGRAM_ID = new PublicKey(VestingIDL.address); - -// This is a helper function to get the Vesting Anchor program. -export function getVestingProgram(provider: AnchorProvider) { - return new Program(VestingIDL as Vesting, provider); -} - -// This is a helper function to get the program ID for the Vesting program depending on the cluster. -export function getVestingProgramId(cluster: Cluster) { - switch (cluster) { - case 'devnet': - case 'testnet': - // This is the program ID for the Vesting program on devnet and testnet. - return new PublicKey('2vKg76rA1Ho27YD4uuc2Z2FCwRTySxdyHup1JjsXS6dp'); - case 'mainnet-beta': - default: - return VESTING_PROGRAM_ID; - } -} diff --git a/project-8-token-vesting/anchor/tests/bankrun.spec.ts b/project-8-token-vesting/anchor/tests/bankrun.spec.ts deleted file mode 100644 index 82ee749..0000000 --- a/project-8-token-vesting/anchor/tests/bankrun.spec.ts +++ /dev/null @@ -1,172 +0,0 @@ -// No imports needed: web3, anchor, pg and more are globally available -import * as anchor from "@coral-xyz/anchor"; -import { BankrunProvider } from "anchor-bankrun"; -import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; -import { BN, Program } from "@coral-xyz/anchor"; - -import { - startAnchor, - Clock, - BanksClient, - ProgramTestContext, -} from "solana-bankrun"; - -import { createMint, mintTo } from "spl-token-bankrun"; -import { PublicKey, Keypair } from "@solana/web3.js"; -import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet"; - -import IDL from "../target/idl/vesting.json"; -import { Vesting } from "../target/types/vesting"; -import { SYSTEM_PROGRAM_ID } from "@coral-xyz/anchor/dist/cjs/native/system"; - -describe("Vesting Smart Contract Tests", () => { - const companyName = "Company"; - let beneficiary: Keypair; - let vestingAccountKey: PublicKey; - let treasuryTokenAccount: PublicKey; - let employeeAccount: PublicKey; - let provider: BankrunProvider; - let program: Program; - let banksClient: BanksClient; - let employer: Keypair; - let mint: PublicKey; - let beneficiaryProvider: BankrunProvider; - let program2: Program; - let context: ProgramTestContext; - - beforeAll(async () => { - beneficiary = new anchor.web3.Keypair(); - - // set up bankrun - context = await startAnchor( - "", - [{ name: "vesting", programId: new PublicKey(IDL.address) }], - [ - { - address: beneficiary.publicKey, - info: { - lamports: 1_000_000_000, - data: Buffer.alloc(0), - owner: SYSTEM_PROGRAM_ID, - executable: false, - }, - }, - ] - ); - provider = new BankrunProvider(context); - - anchor.setProvider(provider); - - program = new Program(IDL as Vesting, provider); - - banksClient = context.banksClient; - - employer = provider.wallet.payer; - - // Create a new mint - // @ts-ignore - mint = await createMint(banksClient, employer, employer.publicKey, null, 2); - - // Generate a new keypair for the beneficiary - beneficiaryProvider = new BankrunProvider(context); - beneficiaryProvider.wallet = new NodeWallet(beneficiary); - - program2 = new Program(IDL as Vesting, beneficiaryProvider); - - // Derive PDAs - [vestingAccountKey] = PublicKey.findProgramAddressSync( - [Buffer.from(companyName)], - program.programId - ); - - [treasuryTokenAccount] = PublicKey.findProgramAddressSync( - [Buffer.from("vesting_treasury"), Buffer.from(companyName)], - program.programId - ); - - [employeeAccount] = PublicKey.findProgramAddressSync( - [ - Buffer.from("employee_vesting"), - beneficiary.publicKey.toBuffer(), - vestingAccountKey.toBuffer(), - ], - program.programId - ); - }); - - it("should create a vesting account", async () => { - const tx = await program.methods - .createVestingAccount(companyName) - .accounts({ - signer: employer.publicKey, - mint, - tokenProgram: TOKEN_PROGRAM_ID, - }) - .rpc({ commitment: "confirmed" }); - - const vestingAccountData = await program.account.vestingAccount.fetch( - vestingAccountKey, - "confirmed" - ); - console.log( - "Vesting Account Data:", - JSON.stringify(vestingAccountData, null, 2) - ); - - console.log("Create Vesting Account Transaction Signature:", tx); - }); - - it("should fund the treasury token account", async () => { - const amount = 10_000 * 10 ** 9; - const mintTx = await mintTo( - // @ts-ignores - banksClient, - employer, - mint, - treasuryTokenAccount, - employer, - amount - ); - - console.log("Mint to Treasury Transaction Signature:", mintTx); - }); - - it("should create an employee vesting account", async () => { - const tx2 = await program.methods - .createEmployeeVesting(new BN(0), new BN(100), new BN(100), new BN(0)) - .accounts({ - beneficiary: beneficiary.publicKey, - vestingAccount: vestingAccountKey, - }) - .rpc({ commitment: "confirmed", skipPreflight: true }); - - console.log("Create Employee Account Transaction Signature:", tx2); - console.log("Employee account", employeeAccount.toBase58()); - }); - - it("should claim tokens", async () => { - await new Promise((resolve) => setTimeout(resolve, 1000)); - - const currentClock = await banksClient.getClock(); - context.setClock( - new Clock( - currentClock.slot, - currentClock.epochStartTimestamp, - currentClock.epoch, - currentClock.leaderScheduleEpoch, - 1000n - ) - ); - - console.log("Employee account", employeeAccount.toBase58()); - - const tx3 = await program2.methods - .claimTokens(companyName) - .accounts({ - tokenProgram: TOKEN_PROGRAM_ID, - }) - .rpc({ commitment: "confirmed" }); - - console.log("Claim Tokens transaction signature", tx3); - }); -}); diff --git a/project-8-token-vesting/anchor/tests/dapptokenvesting.spec.ts b/project-8-token-vesting/anchor/tests/dapptokenvesting.spec.ts new file mode 100644 index 0000000..c177b06 --- /dev/null +++ b/project-8-token-vesting/anchor/tests/dapptokenvesting.spec.ts @@ -0,0 +1,76 @@ +import * as anchor from '@coral-xyz/anchor' +import {Program} from '@coral-xyz/anchor' +import {Keypair} from '@solana/web3.js' +import {Dapptokenvesting} from '../target/types/dapptokenvesting' + +describe('dapptokenvesting', () => { + // Configure the client to use the local cluster. + const provider = anchor.AnchorProvider.env() + anchor.setProvider(provider) + const payer = provider.wallet as anchor.Wallet + + const program = anchor.workspace.Dapptokenvesting as Program + + const dapptokenvestingKeypair = Keypair.generate() + + it('Initialize Dapptokenvesting', async () => { + await program.methods + .initialize() + .accounts({ + dapptokenvesting: dapptokenvestingKeypair.publicKey, + payer: payer.publicKey, + }) + .signers([dapptokenvestingKeypair]) + .rpc() + + const currentCount = await program.account.dapptokenvesting.fetch(dapptokenvestingKeypair.publicKey) + + expect(currentCount.count).toEqual(0) + }) + + it('Increment Dapptokenvesting', async () => { + await program.methods.increment().accounts({ dapptokenvesting: dapptokenvestingKeypair.publicKey }).rpc() + + const currentCount = await program.account.dapptokenvesting.fetch(dapptokenvestingKeypair.publicKey) + + expect(currentCount.count).toEqual(1) + }) + + it('Increment Dapptokenvesting Again', async () => { + await program.methods.increment().accounts({ dapptokenvesting: dapptokenvestingKeypair.publicKey }).rpc() + + const currentCount = await program.account.dapptokenvesting.fetch(dapptokenvestingKeypair.publicKey) + + expect(currentCount.count).toEqual(2) + }) + + it('Decrement Dapptokenvesting', async () => { + await program.methods.decrement().accounts({ dapptokenvesting: dapptokenvestingKeypair.publicKey }).rpc() + + const currentCount = await program.account.dapptokenvesting.fetch(dapptokenvestingKeypair.publicKey) + + expect(currentCount.count).toEqual(1) + }) + + it('Set dapptokenvesting value', async () => { + await program.methods.set(42).accounts({ dapptokenvesting: dapptokenvestingKeypair.publicKey }).rpc() + + const currentCount = await program.account.dapptokenvesting.fetch(dapptokenvestingKeypair.publicKey) + + expect(currentCount.count).toEqual(42) + }) + + it('Set close the dapptokenvesting account', async () => { + await program.methods + .close() + .accounts({ + payer: payer.publicKey, + dapptokenvesting: dapptokenvestingKeypair.publicKey, + }) + .rpc() + + // The account should no longer exist, returning null. + const userAccount = await program.account.dapptokenvesting.fetchNullable(dapptokenvestingKeypair.publicKey) + expect(userAccount).toBeNull() + }) +}) diff --git a/project-8-token-vesting/anchor/tests/fixtures/vesting.so b/project-8-token-vesting/anchor/tests/fixtures/vesting.so deleted file mode 100755 index 80ac0244f21563ee8df0d6da2a53b6270054b5e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 312512 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 { + const { PHASE_PRODUCTION_SERVER, PHASE_DEVELOPMENT_SERVER } = await Promise.resolve().then(() => require('next/constants')); + // Two scenarios where we want to skip graph creation: + // 1. Running production server means the build is already done so we just need to start the Next.js server. + // 2. During graph creation (i.e. create nodes), we won't have a graph to read, and it is not needed anyway since it's a build-time concern. + // + // NOTE: Avoid any `require(...)` or `import(...)` statements here. Development dependencies are not available at production runtime. + if (PHASE_PRODUCTION_SERVER === phase || global.NX_GRAPH_CREATION) { + const { nx, ...validNextConfig } = _nextConfig; + return { + distDir: '.next', + ...validNextConfig, + }; + } + else { + const { createProjectGraphAsync, joinPathFragments, offsetFromRoot, workspaceRoot, } = require('@nx/devkit'); + let graph; + try { + graph = await createProjectGraphAsync(); + } + catch (e) { + throw new Error('Could not create project graph. Please ensure that your workspace is valid.', { cause: e }); + } + const originalTarget = { + project: process.env.NX_TASK_TARGET_PROJECT, + target: process.env.NX_TASK_TARGET_TARGET, + configuration: process.env.NX_TASK_TARGET_CONFIGURATION, + }; + const { node: projectNode, options, projectName: project, } = getNxContext(graph, originalTarget); + const projectDirectory = projectNode.data.root; + // Get next config + const nextConfig = getNextConfig(_nextConfig, context); + // For Next.js 13.1 and greater, make sure workspace libs are transpiled. + forNextVersion('>=13.1.0', () => { + if (!graph.dependencies[project]) + return; + const { readTsConfigPaths } = require('@nx/js'); + const { findAllProjectNodeDependencies, } = require('nx/src/utils/project-graph-utils'); + const paths = readTsConfigPaths(); + const deps = findAllProjectNodeDependencies(project); + nextConfig.transpilePackages ??= []; + for (const dep of deps) { + const alias = getAliasForProject(graph.nodes[dep], paths); + if (alias) { + nextConfig.transpilePackages.push(alias); + } + } + }); + // process.env.NX_NEXT_OUTPUT_PATH is set when running @nx/next:build + options.outputPath = + process.env.NX_NEXT_OUTPUT_PATH || options.outputPath; + // outputPath may be undefined if using run-commands or other executors other than @nx/next:build. + // In this case, the user should set distDir in their next.config.js. + if (options.outputPath && phase !== PHASE_DEVELOPMENT_SERVER) { + const outputDir = `${offsetFromRoot(projectDirectory)}${options.outputPath}`; + // If running dev-server, we should keep `.next` inside project directory since Turbopack expects this. + // See: https://github.com/nrwl/nx/issues/19365 + nextConfig.distDir = + nextConfig.distDir && nextConfig.distDir !== '.next' + ? joinPathFragments(outputDir, nextConfig.distDir) + : joinPathFragments(outputDir, '.next'); + } + // If we are running a static serve of the Next.js app, we need to change the output to 'export' and the distDir to 'out'. + if (process.env.NX_SERVE_STATIC_BUILD_RUNNING === 'true') { + nextConfig.output = 'export'; + nextConfig.distDir = 'out'; + } + const userWebpackConfig = nextConfig.webpack; + const { createWebpackConfig } = require('@nx/next/src/utils/config'); + nextConfig.webpack = (a, b) => createWebpackConfig(workspaceRoot, projectDirectory, options.fileReplacements, options.assets)(userWebpackConfig ? userWebpackConfig(a, b) : a, b); + return nextConfig; + } + }; +} +exports.withNx = withNx; +function getNextConfig(nextConfig = {}, context = getWithNxContext()) { + // If `next-compose-plugins` is used, the context argument is invalid. + if (!context.libsDir || !context.workspaceRoot) { + context = getWithNxContext(); + } + const userWebpack = nextConfig.webpack || ((x) => x); + const { nx, ...validNextConfig } = nextConfig; + return { + eslint: { + ignoreDuringBuilds: true, + ...(validNextConfig.eslint ?? {}), + }, + ...validNextConfig, + webpack: (config, options) => { + /* + * Update babel to support our monorepo setup. + * The 'upward' mode allows the root babel.config.json and per-project .babelrc files to be picked up. + */ + if (nx?.babelUpwardRootMode) { + options.defaultLoaders.babel.options.babelrc = true; + options.defaultLoaders.babel.options.rootMode = 'upward'; + } + /* + * Modify the Next.js webpack config to allow workspace libs to use css modules. + * Note: This would be easier if Next.js exposes css-loader and sass-loader on `defaultLoaders`. + */ + // Include workspace libs in css/sass loaders + const includes = [ + require('path').join(context.workspaceRoot, context.libsDir), + ]; + const nextCssLoaders = config.module.rules.find((rule) => typeof rule.oneOf === 'object'); + // webpack config is not as expected + if (!nextCssLoaders) + return config; + /* + * 1. Modify css loader to enable module support for workspace libs + */ + const nextCssLoader = nextCssLoaders.oneOf.find((rule) => rule.sideEffects === false && regexEqual(rule.test, /\.module\.css$/)); + // Might not be found if Next.js webpack config changes in the future + if (nextCssLoader && nextCssLoader.issuer) { + nextCssLoader.issuer.or = nextCssLoader.issuer.and + ? nextCssLoader.issuer.and.concat(includes) + : includes; + delete nextCssLoader.issuer.and; + } + /* + * 2. Modify sass loader to enable module support for workspace libs + */ + const nextSassLoader = nextCssLoaders.oneOf.find((rule) => rule.sideEffects === false && + regexEqual(rule.test, /\.module\.(scss|sass)$/)); + // Might not be found if Next.js webpack config changes in the future + if (nextSassLoader && nextSassLoader.issuer) { + nextSassLoader.issuer.or = nextSassLoader.issuer.and + ? nextSassLoader.issuer.and.concat(includes) + : includes; + delete nextSassLoader.issuer.and; + } + /* + * 3. Modify error loader to ignore css modules used by workspace libs + */ + const nextErrorCssModuleLoader = nextCssLoaders.oneOf.find((rule) => rule.use && + rule.use.loader === 'error-loader' && + rule.use.options && + (rule.use.options.reason === + 'CSS Modules \u001b[1mcannot\u001b[22m be imported from within \u001b[1mnode_modules\u001b[22m.\n' + + 'Read more: https://err.sh/next.js/css-modules-npm' || + rule.use.options.reason === + 'CSS Modules cannot be imported from within node_modules.\nRead more: https://err.sh/next.js/css-modules-npm')); + // Might not be found if Next.js webpack config changes in the future + if (nextErrorCssModuleLoader) { + nextErrorCssModuleLoader.exclude = includes; + } + /** + * 4. Modify css loader to allow global css from node_modules to be imported from workspace libs + */ + const nextGlobalCssLoader = nextCssLoaders.oneOf.find((rule) => rule.include?.and?.find((include) => regexEqual(include, /node_modules/))); + // Might not be found if Next.js webpack config changes in the future + if (nextGlobalCssLoader && nextGlobalCssLoader.issuer) { + nextGlobalCssLoader.issuer.or = nextGlobalCssLoader.issuer.and + ? nextGlobalCssLoader.issuer.and.concat(includes) + : includes; + delete nextGlobalCssLoader.issuer.and; + } + /** + * 5. Add SVGR support if option is on. + */ + // Default SVGR support to be on for projects. + if (nx?.svgr !== false) { + // TODO(v20): Remove file-loader and use `?react` querystring to differentiate between asset and SVGR. + // It should be: + // use: [{ + // test: /\.svg$/i, + // type: 'asset', + // resourceQuery: /react/, // *.svg?react + // }, + // { + // test: /\.svg$/i, + // issuer: /\.[jt]sx?$/, + // resourceQuery: { not: [/react/] }, // exclude react component if *.svg?react + // use: ['@svgr/webpack'], + // }], + // See: + // - SVGR: https://react-svgr.com/docs/webpack/#use-svgr-and-asset-svg-in-the-same-project + // - Vite: https://www.npmjs.com/package/vite-plugin-svgr + // - Rsbuild: https://github.com/web-infra-dev/rsbuild/pull/1783 + // Note: We also need a migration for any projects that are using SVGR to convert + // `import { ReactComponent as X } from './x.svg` to + // `import X from './x.svg?react'; + config.module.rules.push({ + test: /\.svg$/, + issuer: { not: /\.(css|scss|sass)$/ }, + resourceQuery: { + not: [ + /__next_metadata__/, + /__next_metadata_route__/, + /__next_metadata_image_meta__/, + ], + }, + use: [ + { + loader: require.resolve('@svgr/webpack'), + options: { + svgo: false, + titleProp: true, + ref: true, + }, + }, + { + loader: require.resolve('file-loader'), + options: { + // Next.js hard-codes assets to load from "static/media". + // See: https://github.com/vercel/next.js/blob/53d017d/packages/next/src/build/webpack-config.ts#L1993 + name: 'static/media/[name].[hash].[ext]', + }, + }, + ], + }); + } + return userWebpack(config, options); + }, + }; +} +exports.getNextConfig = getNextConfig; +function getAliasForProject(node, paths) { + // Match workspace libs to their alias in tsconfig paths. + for (const [alias, lookup] of Object.entries(paths ?? {})) { + const lookupContainsDepNode = lookup.some((lookupPath) => lookupPath.startsWith(node?.data?.root) || + lookupPath.startsWith('./' + node?.data?.root)); + if (lookupContainsDepNode) { + return alias; + } + } + return null; +} +exports.getAliasForProject = getAliasForProject; +// Runs a function if the Next.js version satisfies the range. +function forNextVersion(range, fn) { + const semver = require('semver'); + const nextJsVersion = require('next/package.json').version; + if (semver.satisfies(nextJsVersion, range)) { + fn(); + } +} +exports.forNextVersion = forNextVersion; +// Support for older generated code: `const withNx = require('@nx/next/plugins/with-nx');` +module.exports = withNx; +// Support for newer generated code: `const { withNx } = require(...);` +module.exports.withNx = withNx; +module.exports.getNextConfig = getNextConfig; +module.exports.getAliasForProject = getAliasForProject; diff --git a/project-8-token-vesting/dist/web/next.config.js b/project-8-token-vesting/dist/web/next.config.js new file mode 100644 index 0000000..419dfb5 --- /dev/null +++ b/project-8-token-vesting/dist/web/next.config.js @@ -0,0 +1,30 @@ +//@ts-check + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const { composePlugins, withNx } = require('./.nx-helpers/compiled.js'); + +/** + * @type {import('./.nx-helpers/compiled.js').WithNxOptions} + **/ +const nextConfig = { + webpack: (config) => { + config.externals = [ + ...(config.externals || []), + 'bigint', + 'node-gyp-build', + ]; + return config; + }, + nx: { + // Set this to true if you would like to use SVGR + // See: https://github.com/gregberge/svgr + svgr: false, + }, +}; + +const plugins = [ + // Add more Next.js plugins to this list if needed. + withNx, +]; + +module.exports = composePlugins(...plugins)(nextConfig); diff --git a/project-8-token-vesting/dist/web/package.json b/project-8-token-vesting/dist/web/package.json new file mode 100644 index 0000000..03c2c03 --- /dev/null +++ b/project-8-token-vesting/dist/web/package.json @@ -0,0 +1,8 @@ +{ + "name": "web", + "version": "0.0.1", + "scripts": { + "start": "next start" + }, + "dependencies": {} +} diff --git a/project-8-token-vesting/dist/web/public/.gitkeep b/project-8-token-vesting/dist/web/public/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/project-8-token-vesting/dist/web/public/logo.png b/project-8-token-vesting/dist/web/public/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..4676bea533187ea656110337da628ee0ce8d463f GIT binary patch literal 4609 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 literal 0 HcmV?d00001 diff --git a/project-8-token-vesting/package.json b/project-8-token-vesting/package.json index 674658a..2e6dd3e 100644 --- a/project-8-token-vesting/package.json +++ b/project-8-token-vesting/package.json @@ -9,6 +9,7 @@ "anchor-test": "nx run anchor:anchor test", "feature": "nx generate @solana-developers/preset-react:feature", "build": "nx build web", + "start": "nx serve web", "dev": "nx serve web" }, "private": true, @@ -19,7 +20,6 @@ "@solana/wallet-adapter-base": "^0.9.23", "@solana/wallet-adapter-react": "^0.15.35", "@solana/wallet-adapter-react-ui": "^0.9.35", - "@solana/web3.js": "1.94.0", "@tabler/icons-react": "3.5.0", "@tailwindcss/typography": "0.5.13", "@tanstack/react-query": "5.40.0", @@ -30,9 +30,12 @@ "encoding": "0.1.13", "jotai": "2.8.3", "next": "14.0.4", - "react": "18.3.1", - "react-dom": "18.3.1", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-flatpickr": "^3.10.13", "react-hot-toast": "2.4.1", + "react-icons": "^5.4.0", + "react-perfect-scrollbar": "^1.5.8", "tslib": "^2.3.0" }, "devDependencies": { @@ -48,10 +51,12 @@ "@swc/core": "~1.3.85", "@swc/helpers": "~0.5.2", "@swc/jest": "0.2.20", + "@types/bn.js": "^5.1.6", "@types/jest": "^29.4.0", "@types/node": "18.16.9", - "@types/react": "18.3.1", - "@types/react-dom": "18.3.0", + "@types/react": "^19.0.1", + "@types/react-dom": "^19.0.2", + "@types/react-flatpickr": "^3.8.11", "@typescript-eslint/eslint-plugin": "^7.3.0", "@typescript-eslint/parser": "^7.3.0", "autoprefixer": "10.4.13", diff --git a/project-8-token-vesting/web/app/account/[address]/page.tsx b/project-8-token-vesting/web/app/account/[address]/page.tsx index aea13b8..5bb6688 100644 --- a/project-8-token-vesting/web/app/account/[address]/page.tsx +++ b/project-8-token-vesting/web/app/account/[address]/page.tsx @@ -1,5 +1,5 @@ -import AccountDetailFeature from '@/components/account/account-detail-feature'; +import AccountDetailFeature from '@/components/account/account-detail-feature' export default function Page() { - return ; + return } diff --git a/project-8-token-vesting/web/app/account/page.tsx b/project-8-token-vesting/web/app/account/page.tsx index d937df6..1b0e276 100644 --- a/project-8-token-vesting/web/app/account/page.tsx +++ b/project-8-token-vesting/web/app/account/page.tsx @@ -1,5 +1,5 @@ -import AccountListFeature from '@/components/account/account-list-feature'; +import AccountListFeature from '@/components/account/account-list-feature' export default function Page() { - return ; + return } diff --git a/project-8-token-vesting/web/app/api/hello/route.ts b/project-8-token-vesting/web/app/api/hello/route.ts index de70bac..e74df4e 100644 --- a/project-8-token-vesting/web/app/api/hello/route.ts +++ b/project-8-token-vesting/web/app/api/hello/route.ts @@ -1,3 +1,3 @@ export async function GET(request: Request) { - return new Response('Hello, from API!'); + return new Response('Hello, from API!') } diff --git a/project-8-token-vesting/web/app/clusters/page.tsx b/project-8-token-vesting/web/app/clusters/page.tsx index 5ac2a9a..1f3ca0e 100644 --- a/project-8-token-vesting/web/app/clusters/page.tsx +++ b/project-8-token-vesting/web/app/clusters/page.tsx @@ -1,5 +1,5 @@ -import ClusterFeature from '@/components/cluster/cluster-feature'; +import ClusterFeature from '@/components/cluster/cluster-feature' export default function Page() { - return ; + return } diff --git a/project-8-token-vesting/web/app/createMint/page.tsx b/project-8-token-vesting/web/app/createMint/page.tsx new file mode 100644 index 0000000..2e75799 --- /dev/null +++ b/project-8-token-vesting/web/app/createMint/page.tsx @@ -0,0 +1,5 @@ +import TokenMemeFeature from "@/components/mintMeme/meme-feature"; + +export default function Page() { + return ; +} diff --git a/project-8-token-vesting/web/app/createVesting/page.tsx b/project-8-token-vesting/web/app/createVesting/page.tsx new file mode 100644 index 0000000..44a23be --- /dev/null +++ b/project-8-token-vesting/web/app/createVesting/page.tsx @@ -0,0 +1,5 @@ +import VestingdappFeature from "@/components/vesting/vestingdapp-feature"; + +export default function Page() { + return ; +} diff --git a/project-8-token-vesting/web/app/dapptokenvesting/page.tsx b/project-8-token-vesting/web/app/dapptokenvesting/page.tsx new file mode 100644 index 0000000..44a23be --- /dev/null +++ b/project-8-token-vesting/web/app/dapptokenvesting/page.tsx @@ -0,0 +1,5 @@ +import VestingdappFeature from "@/components/vesting/vestingdapp-feature"; + +export default function Page() { + return ; +} diff --git a/project-8-token-vesting/web/app/employeetoken/page.tsx b/project-8-token-vesting/web/app/employeetoken/page.tsx new file mode 100644 index 0000000..b7caca6 --- /dev/null +++ b/project-8-token-vesting/web/app/employeetoken/page.tsx @@ -0,0 +1,6 @@ +import EmployeeFeature from "@/components/employee/employee-feature"; +import TokenMemeFeature from "@/components/mintMeme/meme-feature"; + +export default function Page() { + return ; +} diff --git a/project-8-token-vesting/web/public/favicon.ico b/project-8-token-vesting/web/app/favicon.ico similarity index 100% rename from project-8-token-vesting/web/public/favicon.ico rename to project-8-token-vesting/web/app/favicon.ico diff --git a/project-8-token-vesting/web/app/global.css b/project-8-token-vesting/web/app/global.css deleted file mode 100644 index 390a546..0000000 --- a/project-8-token-vesting/web/app/global.css +++ /dev/null @@ -1,19 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -html, -body { - height: 100%; -} - -.wallet-adapter-button-trigger { - background: rgb(100, 26, 230) !important; - border-radius: 8px !important; - padding-left: 16px !important; - padding-right: 16px !important; -} -.wallet-adapter-dropdown-list, -.wallet-adapter-button { - font-family: inherit !important; -} diff --git a/project-8-token-vesting/web/app/globals.css b/project-8-token-vesting/web/app/globals.css new file mode 100644 index 0000000..e2610a5 --- /dev/null +++ b/project-8-token-vesting/web/app/globals.css @@ -0,0 +1,49 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +html, +body { + margin: 0; + padding: 0; + height: 100%; + width: 100%; + font-size: 16px; + overflow-x: hidden; + -webkit-text-size-adjust: 100%; +} + +.wallet-adapter-button-trigger { + background: rgb(100, 26, 230) !important; + border-radius: 8px !important; + padding-left: 16px !important; + padding-right: 16px !important; +} +.wallet-adapter-dropdown-list, +.wallet-adapter-button { + font-family: inherit !important; +} +.ps__rail-x, +.ps__rail-y { + background-color: transparent !important; + z-index: 1; +} + +.ps__thumb-x { + background-color: #888; + height: 10px !important; + border-radius: 5px; + min-width: 30px !important; +} + +.ps__thumb-y { + background-color: #888; + width: 10px !important; + border-radius: 5px; + min-height: 30px !important; +} + +.ps__thumb-x:hover, +.ps__thumb-y:hover { + background-color: #555; +} diff --git a/project-8-token-vesting/web/app/layout.tsx b/project-8-token-vesting/web/app/layout.tsx index 739922f..19dddba 100644 --- a/project-8-token-vesting/web/app/layout.tsx +++ b/project-8-token-vesting/web/app/layout.tsx @@ -1,18 +1,25 @@ -import './global.css'; -import { UiLayout } from '@/components/ui/ui-layout'; -import { ClusterProvider } from '@/components/cluster/cluster-data-access'; -import { SolanaProvider } from '@/components/solana/solana-provider'; -import { ReactQueryProvider } from './react-query-provider'; +import "./globals.css"; +import { ClusterProvider } from "@/components/cluster/cluster-data-access"; +import { SolanaProvider } from "@/components/solana/solana-provider"; +import { UiLayout } from "@/components/ui/ui-layout"; +import { ReactQueryProvider } from "./react-query-provider"; export const metadata = { - title: 'token-vesting', - description: 'Generated by create-solana-dapp', + title: "dapp-token-vesting", + description: "Generated by create-solana-dapp", }; const links: { label: string; path: string }[] = [ - { label: 'Account', path: '/account' }, - { label: 'Clusters', path: '/clusters' }, - { label: 'Vesting Program', path: '/vesting' }, + { label: "Account", path: "/account" }, + { label: "Clusters", path: "/clusters" }, +]; +const companyLinks: { label: string; path: string }[] = [ + { label: "Token Program", path: "/mint" }, + { label: "Vesting Program", path: "/dapptokenvesting" }, + { label: "Vested Employees", path: "/vestedemployees" }, +]; +const employeeLinks: { label: string; path: string }[] = [ + { label: "Claim Token", path: "/employeetoken" }, ]; export default function RootLayout({ @@ -22,11 +29,21 @@ export default function RootLayout({ }) { return ( + - {children} + + {children} + diff --git a/project-8-token-vesting/web/app/mint/page.tsx b/project-8-token-vesting/web/app/mint/page.tsx new file mode 100644 index 0000000..2e75799 --- /dev/null +++ b/project-8-token-vesting/web/app/mint/page.tsx @@ -0,0 +1,5 @@ +import TokenMemeFeature from "@/components/mintMeme/meme-feature"; + +export default function Page() { + return ; +} diff --git a/project-8-token-vesting/web/app/page.module.css b/project-8-token-vesting/web/app/page.module.css deleted file mode 100644 index 8a13e21..0000000 --- a/project-8-token-vesting/web/app/page.module.css +++ /dev/null @@ -1,2 +0,0 @@ -.page { -} diff --git a/project-8-token-vesting/web/app/page.tsx b/project-8-token-vesting/web/app/page.tsx index 3926721..cf7d07d 100644 --- a/project-8-token-vesting/web/app/page.tsx +++ b/project-8-token-vesting/web/app/page.tsx @@ -1,5 +1,5 @@ -import DashboardFeature from '@/components/dashboard/dashboard-feature'; +import AccountListFeature from "@/components/account/account-list-feature"; export default function Page() { - return ; + return ; } diff --git a/project-8-token-vesting/web/app/react-query-provider.tsx b/project-8-token-vesting/web/app/react-query-provider.tsx index 537b89c..e12cf8d 100644 --- a/project-8-token-vesting/web/app/react-query-provider.tsx +++ b/project-8-token-vesting/web/app/react-query-provider.tsx @@ -1,15 +1,15 @@ -'use client'; +'use client' -import React, { ReactNode, useState } from 'react'; -import { ReactQueryStreamedHydration } from '@tanstack/react-query-next-experimental'; -import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; +import React, { ReactNode, useState } from 'react' +import { ReactQueryStreamedHydration } from '@tanstack/react-query-next-experimental' +import { QueryClientProvider, QueryClient } from '@tanstack/react-query' export function ReactQueryProvider({ children }: { children: ReactNode }) { - const [client] = useState(new QueryClient()); + const [client] = useState(new QueryClient()) return ( {children} - ); + ) } diff --git a/project-8-token-vesting/web/app/vestedemployees/page.tsx b/project-8-token-vesting/web/app/vestedemployees/page.tsx new file mode 100644 index 0000000..44a23be --- /dev/null +++ b/project-8-token-vesting/web/app/vestedemployees/page.tsx @@ -0,0 +1,5 @@ +import VestingdappFeature from "@/components/vesting/vestingdapp-feature"; + +export default function Page() { + return ; +} diff --git a/project-8-token-vesting/web/app/vesting/page.tsx b/project-8-token-vesting/web/app/vesting/page.tsx deleted file mode 100644 index bc538b4..0000000 --- a/project-8-token-vesting/web/app/vesting/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import VestingFeature from '@/components/vesting/vesting-feature'; - -export default function Page() { - return ; -} diff --git a/project-8-token-vesting/web/components/account/account-data-access.tsx b/project-8-token-vesting/web/components/account/account-data-access.tsx index 53cdb8e..ff1c342 100644 --- a/project-8-token-vesting/web/components/account/account-data-access.tsx +++ b/project-8-token-vesting/web/components/account/account-data-access.tsx @@ -1,7 +1,7 @@ -'use client'; +"use client"; -import { useConnection, useWallet } from '@solana/wallet-adapter-react'; -import { TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from '@solana/spl-token'; +import { TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from "@solana/spl-token"; +import { useConnection, useWallet } from "@solana/wallet-adapter-react"; import { Connection, LAMPORTS_PER_SOL, @@ -10,16 +10,16 @@ import { TransactionMessage, TransactionSignature, VersionedTransaction, -} from '@solana/web3.js'; -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import toast from 'react-hot-toast'; -import { useTransactionToast } from '../ui/ui-layout'; +} from "@solana/web3.js"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import toast from "react-hot-toast"; +import { useTransactionToast } from "../ui/ui-layout"; export function useGetBalance({ address }: { address: PublicKey }) { const { connection } = useConnection(); return useQuery({ - queryKey: ['get-balance', { endpoint: connection.rpcEndpoint, address }], + queryKey: ["get-balance", { endpoint: connection.rpcEndpoint, address }], queryFn: () => connection.getBalance(address), }); } @@ -28,8 +28,8 @@ export function useGetSignatures({ address }: { address: PublicKey }) { const { connection } = useConnection(); return useQuery({ - queryKey: ['get-signatures', { endpoint: connection.rpcEndpoint, address }], - queryFn: () => connection.getConfirmedSignaturesForAddress2(address), + queryKey: ["get-signatures", { endpoint: connection.rpcEndpoint, address }], + queryFn: () => connection.getSignaturesForAddress(address), }); } @@ -38,7 +38,7 @@ export function useGetTokenAccounts({ address }: { address: PublicKey }) { return useQuery({ queryKey: [ - 'get-token-accounts', + "get-token-accounts", { endpoint: connection.rpcEndpoint, address }, ], queryFn: async () => { @@ -52,6 +52,7 @@ export function useGetTokenAccounts({ address }: { address: PublicKey }) { ]); return [...tokenAccounts.value, ...token2022Accounts.value]; }, + refetchOnWindowFocus: false, }); } @@ -63,11 +64,11 @@ export function useTransferSol({ address }: { address: PublicKey }) { return useMutation({ mutationKey: [ - 'transfer-sol', + "transfer-sol", { endpoint: connection.rpcEndpoint, address }, ], mutationFn: async (input: { destination: PublicKey; amount: number }) => { - let signature: TransactionSignature = ''; + let signature: TransactionSignature = ""; try { const { transaction, latestBlockhash } = await createTransaction({ publicKey: address, @@ -76,19 +77,16 @@ export function useTransferSol({ address }: { address: PublicKey }) { connection, }); - // Send transaction and await for signature signature = await wallet.sendTransaction(transaction, connection); - // Send transaction and await for signature await connection.confirmTransaction( { signature, ...latestBlockhash }, - 'confirmed' + "confirmed" ); - console.log(signature); return signature; } catch (error: unknown) { - console.log('error', `Transaction failed! ${error}`, signature); + console.log("error", `Transaction failed! ${error}`, signature); return; } @@ -100,19 +98,19 @@ export function useTransferSol({ address }: { address: PublicKey }) { return Promise.all([ client.invalidateQueries({ queryKey: [ - 'get-balance', + "get-balance", { endpoint: connection.rpcEndpoint, address }, ], }), client.invalidateQueries({ queryKey: [ - 'get-signatures', + "get-signatures", { endpoint: connection.rpcEndpoint, address }, ], }), ]); }, - onError: (error) => { + onError: (error: unknown) => { toast.error(`Transaction failed! ${error}`); }, }); @@ -124,7 +122,7 @@ export function useRequestAirdrop({ address }: { address: PublicKey }) { const client = useQueryClient(); return useMutation({ - mutationKey: ['airdrop', { endpoint: connection.rpcEndpoint, address }], + mutationKey: ["airdrop", { endpoint: connection.rpcEndpoint, address }], mutationFn: async (amount: number = 1) => { const [latestBlockhash, signature] = await Promise.all([ connection.getLatestBlockhash(), @@ -133,7 +131,7 @@ export function useRequestAirdrop({ address }: { address: PublicKey }) { await connection.confirmTransaction( { signature, ...latestBlockhash }, - 'confirmed' + "confirmed" ); return signature; }, @@ -142,13 +140,13 @@ export function useRequestAirdrop({ address }: { address: PublicKey }) { return Promise.all([ client.invalidateQueries({ queryKey: [ - 'get-balance', + "get-balance", { endpoint: connection.rpcEndpoint, address }, ], }), client.invalidateQueries({ queryKey: [ - 'get-signatures', + "get-signatures", { endpoint: connection.rpcEndpoint, address }, ], }), diff --git a/project-8-token-vesting/web/components/account/account-detail-feature.tsx b/project-8-token-vesting/web/components/account/account-detail-feature.tsx index cc7a2f7..113af33 100644 --- a/project-8-token-vesting/web/components/account/account-detail-feature.tsx +++ b/project-8-token-vesting/web/components/account/account-detail-feature.tsx @@ -1,33 +1,28 @@ -'use client'; +'use client' -import { PublicKey } from '@solana/web3.js'; -import { useMemo } from 'react'; +import { PublicKey } from '@solana/web3.js' +import { useMemo } from 'react' -import { useParams } from 'next/navigation'; +import { useParams } from 'next/navigation' -import { ExplorerLink } from '../cluster/cluster-ui'; -import { AppHero, ellipsify } from '../ui/ui-layout'; -import { - AccountBalance, - AccountButtons, - AccountTokens, - AccountTransactions, -} from './account-ui'; +import { ExplorerLink } from '../cluster/cluster-ui' +import { AppHero, ellipsify } from '../ui/ui-layout' +import { AccountBalance, AccountButtons, AccountTokens, AccountTransactions } from './account-ui' export default function AccountDetailFeature() { - const params = useParams(); + const params = useParams() const address = useMemo(() => { if (!params.address) { - return; + return } try { - return new PublicKey(params.address); + return new PublicKey(params.address) } catch (e) { - console.log(`Invalid public key`, e); + console.log(`Invalid public key`, e) } - }, [params]); + }, [params]) if (!address) { - return
Error loading account
; + return
Error loading account
} return ( @@ -36,10 +31,7 @@ export default function AccountDetailFeature() { title={} subtitle={
- +
} > @@ -52,5 +44,5 @@ export default function AccountDetailFeature() { - ); + ) } diff --git a/project-8-token-vesting/web/components/account/account-list-feature.tsx b/project-8-token-vesting/web/components/account/account-list-feature.tsx index fe4006e..3443c76 100644 --- a/project-8-token-vesting/web/components/account/account-list-feature.tsx +++ b/project-8-token-vesting/web/components/account/account-list-feature.tsx @@ -1,15 +1,15 @@ -'use client'; +'use client' -import { useWallet } from '@solana/wallet-adapter-react'; -import { WalletButton } from '../solana/solana-provider'; +import { useWallet } from '@solana/wallet-adapter-react' -import { redirect } from 'next/navigation'; +import { redirect } from 'next/navigation' +import { WalletButton } from '../solana/solana-provider' export default function AccountListFeature() { - const { publicKey } = useWallet(); + const { publicKey } = useWallet() if (publicKey) { - return redirect(`/account/${publicKey.toString()}`); + return redirect(`/account/${publicKey.toString()}`) } return ( @@ -18,5 +18,5 @@ export default function AccountListFeature() { - ); + ) } diff --git a/project-8-token-vesting/web/components/account/account-ui.tsx b/project-8-token-vesting/web/components/account/account-ui.tsx index e04bcc0..1ab14aa 100644 --- a/project-8-token-vesting/web/components/account/account-ui.tsx +++ b/project-8-token-vesting/web/components/account/account-ui.tsx @@ -1,20 +1,22 @@ -'use client'; +"use client"; -import { useWallet } from '@solana/wallet-adapter-react'; -import { LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js'; -import { IconRefresh } from '@tabler/icons-react'; -import { useQueryClient } from '@tanstack/react-query'; -import { useMemo, useState } from 'react'; -import { AppModal, ellipsify } from '../ui/ui-layout'; -import { useCluster } from '../cluster/cluster-data-access'; -import { ExplorerLink } from '../cluster/cluster-ui'; +import { useWallet } from "@solana/wallet-adapter-react"; +import { LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js"; +import { IconRefresh } from "@tabler/icons-react"; +import { useQueryClient } from "@tanstack/react-query"; +import { useMemo, useState } from "react"; +import { AppModal, ellipsify } from "../ui/ui-layout"; +import { useCluster } from "../cluster/cluster-data-access"; +import { ExplorerLink } from "../cluster/cluster-ui"; import { useGetBalance, useGetSignatures, useGetTokenAccounts, useRequestAirdrop, useTransferSol, -} from './account-data-access'; +} from "./account-data-access"; +import PerfectScrollbar from "react-perfect-scrollbar"; +import "react-perfect-scrollbar/dist/css/styles.css"; export function AccountBalance({ address }: { address: PublicKey }) { const query = useGetBalance({ address }); @@ -22,10 +24,10 @@ export function AccountBalance({ address }: { address: PublicKey }) { return (

query.refetch()} > - {query.data ? : '...'} SOL + {query.data ? : "..."} SOL

); @@ -53,9 +55,9 @@ export function AccountBalanceCheck({ address }: { address: PublicKey }) { is not found on this cluster. + + + )} + + + + )} )} @@ -255,61 +261,65 @@ export function AccountTransactions({ address }: { address: PublicKey }) { {query.data.length === 0 ? (
No transactions found.
) : ( - - - - - - - - - - - {items?.map((item) => ( - - - - - - - ))} - {(query.data?.length ?? 0) > 5 && ( - - - - )} - -
SignatureSlotBlock TimeStatus
- - - - - {new Date((item.blockTime ?? 0) * 1000).toISOString()} - - {item.err ? ( -
- Failed -
- ) : ( -
Success
- )} -
- -
+
+ + + + + + + + + + + + {items?.map((item) => ( + + + + + + + ))} + {(query.data?.length ?? 0) > 5 && ( + + + + )} + +
SignatureSlotBlock TimeStatus
+ + + + + {new Date((item.blockTime ?? 0) * 1000).toISOString()} + + {item.err ? ( +
+ Failed +
+ ) : ( +
Success
+ )} +
+ +
+
+
)} )} @@ -350,7 +360,7 @@ function ModalAirdrop({ address: PublicKey; }) { const mutation = useRequestAirdrop({ address }); - const [amount, setAmount] = useState('2'); + const [amount, setAmount] = useState("2"); return ( Wallet not connected; diff --git a/project-8-token-vesting/web/components/cluster/cluster-data-access.tsx b/project-8-token-vesting/web/components/cluster/cluster-data-access.tsx index 358b6c0..6d9aa87 100644 --- a/project-8-token-vesting/web/components/cluster/cluster-data-access.tsx +++ b/project-8-token-vesting/web/components/cluster/cluster-data-access.tsx @@ -1,16 +1,16 @@ -'use client'; +'use client' -import { clusterApiUrl, Connection } from '@solana/web3.js'; -import { atom, useAtomValue, useSetAtom } from 'jotai'; -import { atomWithStorage } from 'jotai/utils'; -import { createContext, ReactNode, useContext } from 'react'; -import toast from 'react-hot-toast'; +import { clusterApiUrl, Connection } from '@solana/web3.js' +import { atom, useAtomValue, useSetAtom } from 'jotai' +import { atomWithStorage } from 'jotai/utils' +import { createContext, ReactNode, useContext } from 'react' +import toast from 'react-hot-toast' export interface Cluster { - name: string; - endpoint: string; - network?: ClusterNetwork; - active?: boolean; + name: string + endpoint: string + network?: ClusterNetwork + active?: boolean } export enum ClusterNetwork { @@ -35,92 +35,83 @@ export const defaultClusters: Cluster[] = [ endpoint: clusterApiUrl('testnet'), network: ClusterNetwork.Testnet, }, -]; +] -const clusterAtom = atomWithStorage( - 'solana-cluster', - defaultClusters[0] -); -const clustersAtom = atomWithStorage( - 'solana-clusters', - defaultClusters -); +const clusterAtom = atomWithStorage('solana-cluster', defaultClusters[0]) +const clustersAtom = atomWithStorage('solana-clusters', defaultClusters) const activeClustersAtom = atom((get) => { - const clusters = get(clustersAtom); - const cluster = get(clusterAtom); + const clusters = get(clustersAtom) + const cluster = get(clusterAtom) return clusters.map((item) => ({ ...item, active: item.name === cluster.name, - })); -}); + })) +}) const activeClusterAtom = atom((get) => { - const clusters = get(activeClustersAtom); + const clusters = get(activeClustersAtom) - return clusters.find((item) => item.active) || clusters[0]; -}); + return clusters.find((item) => item.active) || clusters[0] +}) export interface ClusterProviderContext { - cluster: Cluster; - clusters: Cluster[]; - addCluster: (cluster: Cluster) => void; - deleteCluster: (cluster: Cluster) => void; - setCluster: (cluster: Cluster) => void; - getExplorerUrl(path: string): string; + cluster: Cluster + clusters: Cluster[] + addCluster: (cluster: Cluster) => void + deleteCluster: (cluster: Cluster) => void + setCluster: (cluster: Cluster) => void + getExplorerUrl(path: string): string } -const Context = createContext( - {} as ClusterProviderContext -); +const Context = createContext({} as ClusterProviderContext) export function ClusterProvider({ children }: { children: ReactNode }) { - const cluster = useAtomValue(activeClusterAtom); - const clusters = useAtomValue(activeClustersAtom); - const setCluster = useSetAtom(clusterAtom); - const setClusters = useSetAtom(clustersAtom); + const cluster = useAtomValue(activeClusterAtom) + const clusters = useAtomValue(activeClustersAtom) + const setCluster = useSetAtom(clusterAtom) + const setClusters = useSetAtom(clustersAtom) const value: ClusterProviderContext = { cluster, clusters: clusters.sort((a, b) => (a.name > b.name ? 1 : -1)), addCluster: (cluster: Cluster) => { try { - new Connection(cluster.endpoint); - setClusters([...clusters, cluster]); + new Connection(cluster.endpoint) + setClusters([...clusters, cluster]) } catch (err) { - toast.error(`${err}`); + toast.error(`${err}`) } }, deleteCluster: (cluster: Cluster) => { - setClusters(clusters.filter((item) => item.name !== cluster.name)); + setClusters(clusters.filter((item) => item.name !== cluster.name)) }, setCluster: (cluster: Cluster) => setCluster(cluster), - getExplorerUrl: (path: string) => - `https://explorer.solana.com/${path}${getClusterUrlParam(cluster)}`, - }; - return {children}; + getExplorerUrl: (path: string) => `https://explorer.solana.com/${path}${getClusterUrlParam(cluster)}`, + } + return {children} } export function useCluster() { - return useContext(Context); + return useContext(Context) } function getClusterUrlParam(cluster: Cluster): string { - let suffix = ''; + let suffix = '' switch (cluster.network) { case ClusterNetwork.Devnet: - suffix = 'devnet'; - break; + suffix = 'devnet' + break case ClusterNetwork.Mainnet: - suffix = ''; - break; + suffix = '' + break case ClusterNetwork.Testnet: - suffix = 'testnet'; - break; + suffix = 'testnet' + break default: - suffix = `custom&customUrl=${encodeURIComponent(cluster.endpoint)}`; - break; + suffix = `custom&customUrl=${encodeURIComponent(cluster.endpoint)}` + break } - return suffix.length ? `?cluster=${suffix}` : ''; + return suffix.length ? `?cluster=${suffix}` : '' } diff --git a/project-8-token-vesting/web/components/cluster/cluster-feature.tsx b/project-8-token-vesting/web/components/cluster/cluster-feature.tsx index abbc95c..3ccf3b8 100644 --- a/project-8-token-vesting/web/components/cluster/cluster-feature.tsx +++ b/project-8-token-vesting/web/components/cluster/cluster-feature.tsx @@ -1,31 +1,22 @@ -'use client'; +'use client' -import { useState } from 'react'; -import { AppHero } from '../ui/ui-layout'; -import { ClusterUiModal } from './cluster-ui'; -import { ClusterUiTable } from './cluster-ui'; +import { useState } from 'react' +import { AppHero } from '../ui/ui-layout' +import { ClusterUiModal } from './cluster-ui' +import { ClusterUiTable } from './cluster-ui' export default function ClusterFeature() { - const [showModal, setShowModal] = useState(false); + const [showModal, setShowModal] = useState(false) return (
- - setShowModal(false)} - /> -
- ); + ) } diff --git a/project-8-token-vesting/web/components/cluster/cluster-ui.tsx b/project-8-token-vesting/web/components/cluster/cluster-ui.tsx index 43b349f..a785ae3 100644 --- a/project-8-token-vesting/web/components/cluster/cluster-ui.tsx +++ b/project-8-token-vesting/web/components/cluster/cluster-ui.tsx @@ -1,12 +1,13 @@ -'use client'; +"use client"; -import { useConnection } from '@solana/wallet-adapter-react'; -import { IconTrash } from '@tabler/icons-react'; -import { useQuery } from '@tanstack/react-query'; -import { ReactNode, useState } from 'react'; -import { AppModal } from '../ui/ui-layout'; -import { ClusterNetwork, useCluster } from './cluster-data-access'; -import { Connection } from '@solana/web3.js'; +import { useConnection } from "@solana/wallet-adapter-react"; +import { IconTrash } from "@tabler/icons-react"; +import { useQuery } from "@tanstack/react-query"; +import React, { ReactNode, useState } from "react"; +import { AppModal } from "../ui/ui-layout"; +import { ClusterNetwork, useCluster } from "./cluster-data-access"; +import { Connection } from "@solana/web3.js"; +import { SetStateAction } from "jotai"; export function ExplorerLink({ path, @@ -35,7 +36,7 @@ export function ClusterChecker({ children }: { children: ReactNode }) { const { connection } = useConnection(); const query = useQuery({ - queryKey: ['version', { cluster, endpoint: connection.rpcEndpoint }], + queryKey: ["version", { cluster, endpoint: connection.rpcEndpoint }], queryFn: () => connection.getVersion(), retry: 1, }); @@ -48,10 +49,7 @@ export function ClusterChecker({ children }: { children: ReactNode }) { Error connecting to cluster {cluster.name} - @@ -60,7 +58,11 @@ export function ClusterChecker({ children }: { children: ReactNode }) { return children; } -export function ClusterUiSelect() { +export function ClusterUiSelect({ + setShowMenu, +}: { + setShowMenu: React.Dispatch>; +}) { const { clusters, setCluster, cluster } = useCluster(); return (
@@ -69,15 +71,18 @@ export function ClusterUiSelect() {
    {clusters.map((item) => (
  • @@ -96,13 +101,13 @@ export function ClusterUiModal({ show: boolean; }) { const { addCluster } = useCluster(); - const [name, setName] = useState(''); + const [name, setName] = useState(""); const [network, setNetwork] = useState(); - const [endpoint, setEndpoint] = useState(''); + const [endpoint, setEndpoint] = useState(""); return ( { @@ -112,10 +117,10 @@ export function ClusterUiModal({ addCluster({ name, network, endpoint }); hideModal(); } else { - console.log('Invalid cluster name'); + console.log("Invalid cluster name"); } } catch { - console.log('Invalid cluster endpoint'); + console.log("Invalid cluster endpoint"); } }} submitLabel="Save" @@ -161,7 +166,7 @@ export function ClusterUiTable() { {clusters.map((item) => ( - +
    @@ -179,7 +184,7 @@ export function ClusterUiTable() {
    - Network: {item.network ?? 'custom'} + Network: {item.network ?? "custom"}
    {item.endpoint} @@ -188,9 +193,9 @@ export function ClusterUiTable() {
- ); + ) } diff --git a/project-8-token-vesting/web/components/employee/employee-data-access.tsx b/project-8-token-vesting/web/components/employee/employee-data-access.tsx new file mode 100644 index 0000000..713f8d3 --- /dev/null +++ b/project-8-token-vesting/web/components/employee/employee-data-access.tsx @@ -0,0 +1,161 @@ +import { PublicKey, SystemProgram, Transaction } from "@solana/web3.js"; +import { useMutation, useQuery } from "@tanstack/react-query"; +import { useCommonProgram } from "../common/common-data-access"; +import { + ASSOCIATED_TOKEN_PROGRAM_ID, + getAssociatedTokenAddress, + TOKEN_PROGRAM_ID, +} from "@solana/spl-token"; +import { useConnection, useWallet } from "@solana/wallet-adapter-react"; +import toast from "react-hot-toast"; +import { useTransactionToast } from "../ui/ui-layout"; +import { useRouter } from "next/navigation"; + +export function useEmployee(walletKey: PublicKey) { + const { cluster, program } = useCommonProgram(); + const wallet = useWallet(); + const { connection } = useConnection(); + const router = useRouter(); + + const transactionToast = useTransactionToast(); + + const calculateClaimableTokens = ( + startTime: number, + endTime: number, + cliffTime: number, + totalAmount: number, + totalWithdrawn: number, + currentTime: number + ): number => { + if (currentTime < cliffTime) { + return 0; + } + + const elapsedTime = Math.min(currentTime, endTime) - startTime; + const vestingDuration = endTime - startTime; + + if (vestingDuration <= 0) { + throw new Error( + "Invalid vesting schedule: endTime must be greater than startTime." + ); + } + + const vestableAmount = (totalAmount * elapsedTime) / vestingDuration; + const claimableTokens = Math.max(0, vestableAmount - totalWithdrawn); + + return claimableTokens; + }; + + const employeeAccounts = useQuery({ + queryKey: ["employeeTokenVestingAccounts", "all", { cluster }], + queryFn: async () => { + const queryResponse = await program.account.employeeAccount.all([ + { + memcmp: { + offset: 8, + bytes: walletKey.toBase58(), + }, + }, + ]); + const vestingAccKeys = queryResponse.map( + (acc) => acc.account.vestingAccount + ); + const relatedVestingAccounts = await Promise.all( + vestingAccKeys.map((va) => program.account.vestingAccount.fetch(va)) + ); + + const data = queryResponse.map((empAcc, index) => { + const vestingDetail = relatedVestingAccounts[index]; + return { + pda: empAcc.publicKey.toString(), + beneficiary: empAcc.account.beneficiary.toString(), + startTime: empAcc.account.startTime.toNumber(), + endTime: empAcc.account.endTime.toNumber(), + totalAmount: empAcc.account.totalAmount.toNumber(), + totalWithdrawn: empAcc.account.totalWithdrawn.toNumber(), + cliffTime: empAcc.account.cliffTime.toNumber(), + vestingAccount: empAcc.account.vestingAccount.toString(), + companyName: vestingDetail.companyName, + treasuryTokenAccount: vestingDetail.treasuryTokenAccount.toString(), + token: vestingDetail.mint.toString(), + }; + }); + return data; + }, + }); + + const claimTokens = useMutation({ + mutationKey: ["claim-tokens"], + mutationFn: async ({ + companyName, + vestingAccountPubkey, + employeeAccountPubkey, + mintPubkey, + treasuryTokenAccountPubkey, + }: { + companyName: string; + vestingAccountPubkey: PublicKey; + employeeAccountPubkey: PublicKey; + mintPubkey: PublicKey; + treasuryTokenAccountPubkey: PublicKey; + }) => { + if (!walletKey) { + throw new Error("Wallet or program is not initialized."); + } + + const ata = await getAssociatedTokenAddress( + mintPubkey, + walletKey, + false, + TOKEN_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + + const { blockhash } = await connection.getLatestBlockhash(); + const transaction = new Transaction({ + recentBlockhash: blockhash, + feePayer: walletKey, + }); + + const instruction = await program.methods + .claimTokens(companyName) + .accounts({ + beneficiary: walletKey, + employeeAccount: employeeAccountPubkey, + vestingAccount: vestingAccountPubkey, + mint: mintPubkey, + treasuryTokenAccount: treasuryTokenAccountPubkey, + employeeTokenAccount: ata, + tokenProgram: TOKEN_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + systemProgram: SystemProgram.programId, + } as any) + .instruction(); + transaction.add(instruction); + + if (!wallet.signTransaction) { + toast.error( + "The wallet adapter does not support signing transactions." + ); + throw new Error( + "The wallet adapter does not support signing transactions." + ); + } + const signedTransaction = await wallet.signTransaction(transaction); + const signature = await connection.sendRawTransaction( + signedTransaction.serialize(), + { + skipPreflight: false, + } + ); + transactionToast(signature); + router.push("/employeetoken"); + return signature; + }, + onError: (error: Error) => { + toast.error(`Claim tokens failed: ${error.message}`); + }, + }); + + return { employeeAccounts, calculateClaimableTokens, claimTokens }; +} diff --git a/project-8-token-vesting/web/components/employee/employee-feature.tsx b/project-8-token-vesting/web/components/employee/employee-feature.tsx new file mode 100644 index 0000000..4ad7b1b --- /dev/null +++ b/project-8-token-vesting/web/components/employee/employee-feature.tsx @@ -0,0 +1,23 @@ +"use client"; + +import { useWallet } from "@solana/wallet-adapter-react"; +import { WalletButton } from "../solana/solana-provider"; +import { EmployeeUi } from "./employee-ui"; + +export default function EmployeeFeature() { + const { publicKey } = useWallet(); + + return publicKey ? ( +
+ +
+ ) : ( +
+
+
+ +
+
+
+ ); +} diff --git a/project-8-token-vesting/web/components/employee/employee-types.ts b/project-8-token-vesting/web/components/employee/employee-types.ts new file mode 100644 index 0000000..0010a02 --- /dev/null +++ b/project-8-token-vesting/web/components/employee/employee-types.ts @@ -0,0 +1,36 @@ +import { PublicKey } from "@solana/web3.js"; + +export interface ITokenAccount { + mintAddress: string; + tokenAccount: string; + tokenAmount: number; +} +export interface IClaimableTokens { + startTime: number; + endTime: number; + cliffTime: number; + totalAmount: number; + totalWithdrawn: number; + currentTime: number; +} +export interface IEmployeeVesting { + pda: string; + beneficiary: string; + token: string; + startTime: number; + endTime: number; + cliffTime: number; + totalAmount: number; + totalWithdrawn: number; + treasuryTokenAccount: string; + vestingAccount: string; + companyName: string; +} + +export interface IClaimTokens { + companyName: string; + vestingAccountPubkey: PublicKey; + employeeAccountPubkey: PublicKey; + mintPubkey: PublicKey; + treasuryTokenAccountPubkey: PublicKey; +} diff --git a/project-8-token-vesting/web/components/employee/employee-ui.tsx b/project-8-token-vesting/web/components/employee/employee-ui.tsx new file mode 100644 index 0000000..60a58ac --- /dev/null +++ b/project-8-token-vesting/web/components/employee/employee-ui.tsx @@ -0,0 +1,341 @@ +"use client"; + +import PerfectScrollbar from "react-perfect-scrollbar"; +import "react-perfect-scrollbar/dist/css/styles.css"; +import { PublicKey } from "@solana/web3.js"; +import { useEmployee } from "./employee-data-access"; +import { IoMdRefresh } from "react-icons/io"; +import { useEffect, useState } from "react"; +import { IClaimTokens, IEmployeeVesting } from "./employee-types"; +import Loader from "../common/common-loader"; +import { useConnection } from "@solana/wallet-adapter-react"; +import { QueryObserverResult, RefetchOptions } from "@tanstack/react-query"; + +export function EmployeeUi({ publicKey }: { publicKey: PublicKey }) { + const { + employeeAccounts: employeeAccountsQuery, + calculateClaimableTokens, + claimTokens, + } = useEmployee(publicKey); + + if (employeeAccountsQuery.isLoading) { + return ( +
+ +
+ ); + } + if ( + !employeeAccountsQuery.isLoading && + (!employeeAccountsQuery.data || employeeAccountsQuery.data.length === 0) + ) { + return ( +
+ + Your haven't added to any token vesting program. + +
+ ); + } + return ( +
+ {employeeAccountsQuery.isLoading ? ( +
+ +
+ ) : employeeAccountsQuery.error ? ( + + Error occured whicle fetching your token vesting accounts. + + ) : employeeAccountsQuery.data ? ( +
+ +
+ ) : ( +
+

No accounts

+ No accounts found. +
+ )} +
+ ); +} + +function EmployeeProgramCard({ + employeeAccounts, + refetch, + claimableTokens, + claimTokens, +}: { + employeeAccounts: IEmployeeVesting[]; + refetch: ( + options?: RefetchOptions | undefined + ) => Promise>; + claimableTokens: ( + startTime: number, + endTime: number, + cliffTime: number, + totalAmount: number, + totalWithdrawn: number, + currentTime: number + ) => number; + claimTokens: ({ + companyName, + vestingAccountPubkey, + employeeAccountPubkey, + mintPubkey, + treasuryTokenAccountPubkey, + }: IClaimTokens) => Promise; +}) { + const { connection } = useConnection(); + const [claimableTokenMap, setClaimableTokenMap] = useState<{ + [key: string]: string; + }>({}); + const [claimTokenLoading, setClaimTokenLoading] = useState<{ + [key: string]: boolean; + }>({}); + const [claimingState, setClaimingState] = useState(false); + useEffect(() => { + if (!claimingState) { + const initialClaimableTokens: { [key: string]: string } = {}; + const initialClaimLoading: { [key: string]: boolean } = {}; + employeeAccounts.forEach(async (employeeAccount) => { + setClaimTokenLoading({ [employeeAccount.pda]: false }); + const currentClaimableTokens = await claimableTokens( + employeeAccount.startTime, + employeeAccount.endTime, + employeeAccount.cliffTime, + employeeAccount.totalAmount, + employeeAccount.totalWithdrawn, + Math.floor(Date.now() / 1000) + ).toFixed(2); + initialClaimableTokens[employeeAccount.pda] = currentClaimableTokens; + initialClaimLoading[employeeAccount.pda] = false; + }); + setClaimableTokenMap(initialClaimableTokens); + setClaimTokenLoading(initialClaimLoading); + } + }, [employeeAccounts, claimableTokens, claimingState]); + + const refreshClaimableTokens = ( + pda: string, + startTime: number, + endTime: number, + cliffTime: number, + totalAmount: number, + totalWithdrawn: number, + currentTime: number + ) => { + const currentClaimableTokens = claimableTokens( + startTime, + endTime, + cliffTime, + totalAmount, + totalWithdrawn, + currentTime + ).toFixed(2); + setClaimableTokenMap((prev) => ({ + ...prev, + [pda]: currentClaimableTokens, + })); + }; + + return ( +
+
+
+

+ Employees Vesting Account List +

+
+ {employeeAccounts.length > 0 ? ( +
+ +
+ {employeeAccounts.map((employeeAccount) => ( +
+
+

+ Account +

+

+ {employeeAccount.pda} +

+
+
+

+ Wallet Address +

+

+ {employeeAccount.beneficiary} +

+
+
+

+ Token +

+

+ {employeeAccount.token} +

+
+
+
+

+ Start +

+

+ {new Date( + employeeAccount.startTime * 1000 + ).toLocaleString()} +

+
+
+

+ Cliff +

+

+ {new Date( + employeeAccount.cliffTime * 1000 + ).toLocaleString()} +

+
+
+
+
+

+ End +

+

+ {new Date( + employeeAccount.endTime * 1000 + ).toLocaleString()} +

+
+
+

+ Total +

+

+ {employeeAccount.totalAmount} +

+
+
+
+

+ Already Withdrawn : +

+

+ {employeeAccount.totalWithdrawn} +

+
+
+

+ Claimable Token : +

+

+ {claimableTokenMap[employeeAccount.pda]} + + refreshClaimableTokens( + employeeAccount.pda, + employeeAccount.startTime, + employeeAccount.endTime, + employeeAccount.cliffTime, + employeeAccount.totalAmount, + employeeAccount.totalWithdrawn, + Math.floor(Date.now() / 1000) + ) + } + className="text-md cursor-pointer md:scale-110 hover:scale-125 active:scale-95" + > + + +

+
+ +
+ ))} +
+
+
+ ) : ( +

+ No employee vesting accounts found. +

+ )} +
+
+ ); +} diff --git a/project-8-token-vesting/web/components/mintMeme/meme-data-access.tsx b/project-8-token-vesting/web/components/mintMeme/meme-data-access.tsx new file mode 100644 index 0000000..2575491 --- /dev/null +++ b/project-8-token-vesting/web/components/mintMeme/meme-data-access.tsx @@ -0,0 +1,121 @@ +"use client"; + +import { WalletContextState } from "@solana/wallet-adapter-react"; +import { + Connection, + Keypair, + PublicKey, + SystemProgram, + Transaction, +} from "@solana/web3.js"; +import toast from "react-hot-toast"; +import { + createAssociatedTokenAccountInstruction, + createInitializeMintInstruction, + createMintToInstruction, + getAssociatedTokenAddress, + MINT_SIZE, + TOKEN_PROGRAM_ID, +} from "@solana/spl-token"; +import { useMutation } from "@tanstack/react-query"; +import { useTransactionToast } from "../ui/ui-layout"; +import { useRouter } from "next/navigation"; + +export const useCreateMintAndTokenAccount = () => { + const transactionToast = useTransactionToast(); + const createMintAndTokenAccount = useMutation< + { signature: string; mint: PublicKey; associatedTokenAccount: PublicKey }, + Error, + { + connection: Connection; + walletAdapter: WalletContextState; + tokenAmount: number | bigint | string; + } + >({ + mutationKey: ["mintToken", "create"], + mutationFn: async ({ connection, walletAdapter, tokenAmount }) => { + if (!walletAdapter || !walletAdapter.connected) { + throw new Error("Wallet not connected. Please connect your wallet."); + } + + const walletPublicKey = walletAdapter.publicKey; + if (!walletPublicKey) { + throw new Error("Wallet public key not available."); + } + + const mint = Keypair.generate(); + const associatedTokenAccount = await getAssociatedTokenAddress( + mint.publicKey, + walletPublicKey + ); + + const { blockhash } = await connection.getLatestBlockhash(); + const transaction = new Transaction({ + recentBlockhash: blockhash, + feePayer: walletPublicKey, + }); + + const mintRent = await connection.getMinimumBalanceForRentExemption( + MINT_SIZE + ); + const decimals = 9; + const mintAmount = BigInt(tokenAmount) * BigInt(10 ** decimals); + + transaction.add( + SystemProgram.createAccount({ + fromPubkey: walletPublicKey, + newAccountPubkey: mint.publicKey, + lamports: mintRent, + space: MINT_SIZE, + programId: TOKEN_PROGRAM_ID, + }), + createInitializeMintInstruction( + mint.publicKey, + decimals, + walletPublicKey, + walletPublicKey + ), + createAssociatedTokenAccountInstruction( + walletPublicKey, + associatedTokenAccount, + walletPublicKey, + mint.publicKey + ), + createMintToInstruction( + mint.publicKey, + associatedTokenAccount, + walletPublicKey, + mintAmount + ) + ); + + transaction.partialSign(mint); + + if (!walletAdapter.signTransaction) { + toast.error( + "The wallet adapter does not support signing transactions." + ); + throw new Error( + "The wallet adapter does not support signing transactions." + ); + } + const signedTransaction = await walletAdapter.signTransaction( + transaction + ); + const signature = await connection.sendRawTransaction( + signedTransaction.serialize(), + { + skipPreflight: false, + } + ); + return { signature, mint: mint.publicKey, associatedTokenAccount }; + }, + onSuccess: async ({ signature }) => { + transactionToast(signature); + }, + onError: (error) => { + toast.error(`Failed to create mint token: ${error.message}`); + }, + }); + return { createMintAndTokenAccount }; +}; diff --git a/project-8-token-vesting/web/components/mintMeme/meme-feature.tsx b/project-8-token-vesting/web/components/mintMeme/meme-feature.tsx new file mode 100644 index 0000000..fd386f1 --- /dev/null +++ b/project-8-token-vesting/web/components/mintMeme/meme-feature.tsx @@ -0,0 +1,45 @@ +"use client"; + +import { useWallet } from "@solana/wallet-adapter-react"; +import { WalletButton } from "../solana/solana-provider"; +import { AppHero, ellipsify } from "../ui/ui-layout"; +import { ExplorerLink } from "../cluster/cluster-ui"; +import { MemeTokenCreate, MemeTokenList } from "./meme-ui"; +import { useCommonProgram } from "../common/common-data-access"; +import { usePathname } from "next/navigation"; + +export default function TokenMemeFeature() { + const { publicKey } = useWallet(); + const { programId } = useCommonProgram(); + const pathName = usePathname(); + + return publicKey ? ( +
+ {pathName === "/createMint" && ( + +

+ +

+ +
+ )} + {pathName === "/mint" && } +
+ ) : ( +
+
+
+ +
+
+
+ ); +} diff --git a/project-8-token-vesting/web/components/mintMeme/meme-types.ts b/project-8-token-vesting/web/components/mintMeme/meme-types.ts new file mode 100644 index 0000000..317d22e --- /dev/null +++ b/project-8-token-vesting/web/components/mintMeme/meme-types.ts @@ -0,0 +1,5 @@ +export interface ITokenAccount { + mintAddress: string; + tokenAccount: string; + tokenAmount: number; +} diff --git a/project-8-token-vesting/web/components/mintMeme/meme-ui.tsx b/project-8-token-vesting/web/components/mintMeme/meme-ui.tsx new file mode 100644 index 0000000..2540cba --- /dev/null +++ b/project-8-token-vesting/web/components/mintMeme/meme-ui.tsx @@ -0,0 +1,187 @@ +"use client"; + +import { useMemo, useState } from "react"; +import { useCreateMintAndTokenAccount } from "./meme-data-access"; +import { useConnection, useWallet } from "@solana/wallet-adapter-react"; +import Link from "next/link"; +import PerfectScrollbar from "react-perfect-scrollbar"; +import "react-perfect-scrollbar/dist/css/styles.css"; +import { useRouter } from "next/navigation"; +import Loader from "../common/common-loader"; +import { useGetTokenAccounts } from "../account/account-data-access"; +import { AccountInfo, ParsedAccountData, PublicKey } from "@solana/web3.js"; +import { ExplorerLink } from "../cluster/cluster-ui"; + +export function MemeTokenCreate({ publicKey }: { publicKey: PublicKey }) { + const wallet = useWallet(); + const { connection } = useConnection(); + const router = useRouter(); + const [tokenAmount, setTokenAmount] = useState(""); + const { createMintAndTokenAccount } = useCreateMintAndTokenAccount(); + const [isSignatureConfirming, setIsSignatureConfirming] = + useState(false); + + return ( +
+ setTokenAmount(e.target.value)} + className="w-full p-2 border border-gray-300 rounded-md focus:ring focus:ring-blue-300" + /> + +
+ ); +} + +export function MemeTokenList({ publicKey }: { publicKey: PublicKey }) { + const query = useGetTokenAccounts({ address: publicKey }); + const tokenAccounts = useMemo(() => { + return query.data; + }, [query.data]); + if (tokenAccounts?.length === 0) { + return ( +
+ + Create New Token Program + + Your connected wallet doesnot have any token accounts. +
+ ); + } + return ( +
+ {query.isPending ? ( +
+ +
+ ) : tokenAccounts && tokenAccounts?.length > 0 ? ( + + ) : ( +
+

No accounts

+ No accounts found. Create one above to get started. +
+ )} +
+ ); +} + +function MemeTokenCard({ + account: tokenAccounts, +}: { + account: { pubkey: PublicKey; account: AccountInfo }[]; +}) { + const wallet = useWallet(); + if (!wallet.publicKey) { + return; + } + return ( +
+ {tokenAccounts.length === 0 && ( + + Create New Token Program + + )} +
+
+

+ Your Token Accounts List +

+ + + Add New Token + +
+ {tokenAccounts.length > 0 ? ( +
+ + + + + + + + + + + + {tokenAccounts.map(({ account, pubkey }) => ( + + + + + + ))} + +
+ Token Account + + Mint Address + + Token Amount +
+ + + + + {account.data.parsed.info.tokenAmount.uiAmount} +
+
+
+ ) : ( +

No token accounts found.

+ )} +
+
+ ); +} diff --git a/project-8-token-vesting/web/components/solana/solana-provider.tsx b/project-8-token-vesting/web/components/solana/solana-provider.tsx index e756401..5c73985 100644 --- a/project-8-token-vesting/web/components/solana/solana-provider.tsx +++ b/project-8-token-vesting/web/components/solana/solana-provider.tsx @@ -1,25 +1,27 @@ -'use client'; +"use client"; -import dynamic from 'next/dynamic'; -import { AnchorProvider } from '@coral-xyz/anchor'; -import { WalletError } from '@solana/wallet-adapter-base'; +import dynamic from "next/dynamic"; +import { AnchorProvider } from "@coral-xyz/anchor"; +import { WalletError } from "@solana/wallet-adapter-base"; import { AnchorWallet, useConnection, useWallet, ConnectionProvider, WalletProvider, -} from '@solana/wallet-adapter-react'; -import { WalletModalProvider } from '@solana/wallet-adapter-react-ui'; -import { ReactNode, useCallback, useMemo } from 'react'; -import { useCluster } from '../cluster/cluster-data-access'; +} from "@solana/wallet-adapter-react"; +import { WalletModalProvider } from "@solana/wallet-adapter-react-ui"; +import { ReactNode, useCallback, useMemo } from "react"; +import { useCluster } from "../cluster/cluster-data-access"; -require('@solana/wallet-adapter-react-ui/styles.css'); +require("@solana/wallet-adapter-react-ui/styles.css"); export const WalletButton = dynamic( async () => - (await import('@solana/wallet-adapter-react-ui')).WalletMultiButton, - { ssr: false } + (await import("@solana/wallet-adapter-react-ui")).WalletMultiButton, + { + ssr: false, + } ); export function SolanaProvider({ children }: { children: ReactNode }) { @@ -43,6 +45,6 @@ export function useAnchorProvider() { const wallet = useWallet(); return new AnchorProvider(connection, wallet as AnchorWallet, { - commitment: 'confirmed', + commitment: "confirmed", }); } diff --git a/project-8-token-vesting/web/components/ui/navbar-link-list.tsx b/project-8-token-vesting/web/components/ui/navbar-link-list.tsx new file mode 100644 index 0000000..2aba306 --- /dev/null +++ b/project-8-token-vesting/web/components/ui/navbar-link-list.tsx @@ -0,0 +1,119 @@ +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import React, { useEffect } from "react"; +import { WalletButton } from "../solana/solana-provider"; +import { ClusterUiSelect } from "../cluster/cluster-ui"; +import { SetStateAction } from "jotai"; + +const NavbarLinkList = ({ + links, + companyLinks, + employeeLinks, + showMenu, + setShowMenu, +}: { + links: { label: string; path: string }[]; + companyLinks: { label: string; path: string }[]; + employeeLinks: { label: string; path: string }[]; + showMenu: boolean; + setShowMenu: React.Dispatch>; +}) => { + const pathname = usePathname(); + useEffect(() => { + const handleResize = () => { + if (window.innerWidth >= 724) { + setShowMenu(false); + } + }; + + window.addEventListener("resize", handleResize); + + return () => { + window.removeEventListener("resize", handleResize); + }; + }, []); + return ( +
    + {links.map(({ label, path }) => ( +
  • setShowMenu(false)} className=""> + + {label} + +
  • + ))} +
  • e.stopPropagation()}> +
    +
    Company
    +
      + {companyLinks.map(({ label, path }) => ( +
    • + setShowMenu(false)} + href={path} + className={`btn btn-sm ${ + pathname.startsWith(path) ? "btn-primary" : "btn-ghost" + }`} + > + {label} + +
    • + ))} +
    +
    +
  • +
  • e.stopPropagation()}> +
    +
    Employee
    +
      + {employeeLinks.map(({ label, path }) => ( +
    • + setShowMenu(false)} + href={path} + className={`btn btn-sm ${ + pathname.startsWith(path) ? "btn-primary" : "btn-ghost" + }`} + > + {label} + +
    • + ))} +
    +
    +
  • +
  • +
  • +
      +
    • e.stopPropagation()}> + +
    • +
    • { + e.stopPropagation(); + }} + > + +
    • +
    +
  • +
+ ); +}; + +export default NavbarLinkList; diff --git a/project-8-token-vesting/web/components/ui/ui-layout.tsx b/project-8-token-vesting/web/components/ui/ui-layout.tsx index e869268..6e84e6b 100644 --- a/project-8-token-vesting/web/components/ui/ui-layout.tsx +++ b/project-8-token-vesting/web/components/ui/ui-layout.tsx @@ -1,52 +1,48 @@ -'use client'; +"use client"; -import { WalletButton } from '../solana/solana-provider'; -import * as React from 'react'; -import { ReactNode, Suspense, useEffect, useRef } from 'react'; +import Link from "next/link"; +import * as React from "react"; +import { ReactNode, Suspense, useEffect, useRef } from "react"; +import toast, { Toaster } from "react-hot-toast"; -import Link from 'next/link'; -import { usePathname } from 'next/navigation'; - -import { AccountChecker } from '../account/account-ui'; -import { - ClusterChecker, - ClusterUiSelect, - ExplorerLink, -} from '../cluster/cluster-ui'; -import toast, { Toaster } from 'react-hot-toast'; +import { AccountChecker } from "../account/account-ui"; +import { ClusterChecker, ExplorerLink } from "../cluster/cluster-ui"; +import { TiThMenu } from "react-icons/ti"; +import NavbarLinkList from "./navbar-link-list"; export function UiLayout({ children, links, + companyLinks, + employeeLinks, }: { children: ReactNode; links: { label: string; path: string }[]; + companyLinks: { label: string; path: string }[]; + employeeLinks: { label: string; path: string }[]; }) { - const pathname = usePathname(); - + const [showMenu, setShowMenu] = React.useState(false); return ( -
+
-
+
- Logo + Logo -
    - {links.map(({ label, path }) => ( -
  • - - {label} - -
  • - ))} -
-
-
- - +
setShowMenu((prev) => !prev)} + className="md:hidden" + > + +
+ +
@@ -64,17 +60,17 @@ export function UiLayout({
-