Deployed Frontend URL: Solana Notes
Solana Program ID: FchaqsnrXy4NyEZ1ddpkz1MNVnMo1pStEro3Jak9zvLk
A decentralized notes application built on Solana where users can create, edit, and delete personal notes stored entirely on-chain. Each note is stored in its own Program Derived Address (PDA) account, allowing efficient management of multiple notes per user. The application demonstrates core Solana concepts including PDA derivation, account management, rent reclamation, and proper authorization checks.
- Wallet-Based Authentication: Users connect their Solana wallet (Phantom, Solflare) to interact with the dApp
- Create Notes: Store text notes (up to 1000 characters) on-chain with automatic timestamp recording
- Edit Notes: Update note content while preserving creation timestamp and updating modification time
- Delete Notes: Remove notes and reclaim rent (approximately 0.002 SOL per note)
- Automatic Note Fetching: All user notes are automatically loaded when wallet connects
- Individual PDAs: Each note uses a separate PDA account for gas-efficient operations
- Authorization: Only note owners can edit or delete their notes
- Timestamps: Creation and last-update timestamps stored on-chain
-
Connect Wallet
- Visit the deployed frontend URL
- Ensure your wallet is set to Devnet
- Click "Select Wallet" button
- Choose your wallet
- Approve the connection request
-
Get Devnet SOL (if needed)
- Visit https://faucet.solana.com
- Enter your wallet address
- Request devnet SOL (needed for transaction fees)
-
Create a Note
- Type your note content in the text area (max 1000 characters)
- Click "Create Note"
- Approve the transaction in your wallet
- Note appears in the list below
-
Edit a Note
- Click "Edit" button on any existing note
- Modify the content in the text area
- Click "Save"
- Approve the transaction
- Updated note shows with new "Updated" timestamp
-
Delete a Note
- Click "Delete" button on any note
- Confirm the deletion in the popup
- Approve the transaction in your wallet
- Note is removed and rent is returned to your wallet
The Solana program is structured using modular Anchor framework patterns with clear separation of concerns:
programs/notes_app/src/
├── lib.rs # Program entry point and instruction routing
├── state.rs # Note account structure definition
├── errors.rs # Custom error types
└── instructions/
├── mod.rs # Instruction module exports
├── create_note.rs # Note creation logic
├── update_note.rs # Note update logic
└── delete_note.rs # Note deletion logic
The program uses a single PDA pattern for note accounts, ensuring each note is uniquely identified and owned:
PDAs Used:
- Note Account PDA: Stores individual note data
- Seeds:
["note", user_pubkey, note_id] - Purpose: Deterministically derive unique addresses for each user's notes
- Why these seeds:
"note"- Namespace identifier to avoid collisionsuser_pubkey- Ensures notes are user-specificnote_id(u64) - Allows multiple notes per user with sequential IDs
- Benefits:
- No need to store note addresses off-chain
- Users can have unlimited notes
- Each note can be independently created/deleted
- Rent is reclaimed individually per note
- Seeds:
Instructions Implemented:
-
create_note- Purpose: Initialize a new note account and store content
- Parameters:
content: String,note_id: u64 - Validations:
- Content length ≤ 1000 characters
- Content is not empty
- Actions:
- Derives PDA from seeds
- Initializes account with space allocation
- Stores content, authority, timestamps
- Charges user for rent
- Rent: ~0.002 SOL (reclaimable on delete)
-
update_note- Purpose: Modify existing note content
- Parameters:
content: String - Validations:
- Note exists
- Caller is the note authority (owner)
- Content length ≤ 1000 characters
- Content is not empty
- Actions:
- Updates content field
- Updates
updated_attimestamp - Preserves
created_attimestamp
- Cost: ~0.00001 SOL (transaction fee only)
-
delete_note- Purpose: Close note account and reclaim rent
- Parameters: None
- Validations:
- Note exists
- Caller is the note authority
- Actions:
- Closes the account
- Returns rent (~0.002 SOL) to user
- Cost: ~0.00001 SOL (transaction fee, but rent is returned)
#[account]
#[derive(InitSpace)]
pub struct Note {
pub authority: Pubkey, // 32 bytes - Owner's wallet address
#[max_len(1000)]
pub content: String, // 4 + 1000 bytes - Note text with length prefix
pub note_id: u64, // 8 bytes - Unique identifier within user's notes
pub created_at: i64, // 8 bytes - Unix timestamp of creation
pub updated_at: i64, // 8 bytes - Unix timestamp of last modification
}
// Total account size: 8 (discriminator) + 1060 bytes = 1068 bytesThe project includes 15 comprehensive tests covering all instructions with both success and failure scenarios.
Happy Path Tests:
- Create single note: Verifies note creation with valid content and ID
- Create multiple notes: Tests creating 3+ notes with different IDs sequentially
- Update note content: Confirms content changes and timestamp updates
- Update note multiple times: Ensures repeated updates work correctly
- Delete note: Verifies account closure and rent reclamation
- Delete multiple notes: Tests batch deletion operations
- PDA derivation validation: Confirms different users/IDs produce unique PDAs
Unhappy Path Tests:
- Empty content on create: Expects
ContentEmptyerror when content is "" - Content too long on create: Expects error when content > 1000 characters
- Duplicate note ID: Expects failure when trying to reuse same note_id
- Empty content on update: Validates update with empty string fails
- Content too long on update: Validates update with >1000 chars fails
- Update non-existent note: Expects
AccountNotInitializederror - Delete non-existent note: Expects
AccountNotInitializederror - Delete already deleted note: Confirms double-delete fails
# Navigate to anchor project
cd anchor_project
# Run all tests (starts local validator, deploys, tests, and cleans up)
anchor test
# Expected output: 15 passing testsTest Statistics:
- Total Tests: 15
- Happy Paths: 7
- Unhappy Paths: 8
- Test Execution Time: ~40 seconds
- Coverage: All instructions, all validation logic, PDA derivation
Design Decisions:
-
Why separate PDAs per note instead of a single account with Vec?
- Gas efficiency: Only pay for storage you use
- Scalability: No account size limits (can have unlimited notes)
- Rent reclamation: Delete individual notes and get rent back
- Parallel operations: Multiple notes can be modified simultaneously
-
Why user provides note_id instead of auto-increment?
- Simplicity: No need for a counter PDA
- Cost: Saves rent for additional counter account
- Flexibility: Frontend can manage IDs (timestamp-based, sequential, etc.)
- Current implementation: Frontend queries all notes and uses
max_id + 1
-
Security considerations:
has_one = authorityconstraint ensures only owners can modify/delete- Input validation on all instructions (length, empty checks)
- Rent is paid by user and returned on delete (no griefing attacks)
- Program is immutable once deployed (for this educational version)
Known Limitations:
- Devnet only: Not audited or recommended for mainnet
- No pagination: Frontend fetches all notes at once (fine for <100 notes)
- No note recovery: Deleted notes are permanently removed
- Sequential ID management: Frontend must track highest ID (could use timestamps instead)
Testing Notes:
- Tests use
Date.now()offset for unique note IDs per test run - Some tests skip on airdrop rate limits (devnet limitation, not code issue)
- All tests verify both success criteria AND error messages
Frontend Implementation:
- Built with React 18 + TypeScript + Vite
- Uses
@solana/wallet-adapterfor wallet connections - Buffer polyfill added for browser compatibility
- Responsive design with inline styles (could be improved with Tailwind)
- Real-time updates after each transaction
Deployment:
- Program deployed on Solana Devnet
- IDL uploaded on-chain for public accessibility
- Frontend hosted on Netlify with automatic deployments
- All source code available in repository
Thank you for reviewing this project! Feel free to test the dApp or run tests locally. Feedback is appreciated!
