A Solana memo program written in pure sBPF assembly with type safety and test coverage.
This project demonstrates how to build Solana programs directly in assembly language. The memo program accepts text messages and logs them to the blockchain, implemented in sBPF assembly.
- Pure sBPF Assembly - Written directly in assembly
- No Signer Requirement - Accepts 0+ accounts (matches official Solana Memo Program)
- Empty Memo Protection - Rejects transactions with no memo text
- Type-Safe TypeScript - type definitions with runtime validation
- Comprehensive Testing - 7 test cases covering all scenarios
- Error Code Validation - Typed error codes with human-readable messages
- Code Quality - Proper error handling and edge case coverage
-
Clone the repository
git clone https://github.com/Code-Parth/memo-program-sbpf.git
cd memo-program-sbpf -
Install dependencies
yarn install
-
Start local Solana validator (in a separate terminal)
solana-test-validator
-
Generate program keypair
solana-keygen new --outfile deploy/memo-program-sbpf-keypair.json --force --no-passphrase
memo-program-ts/
├── src/
│ └── memo-program-ts/
│ └── memo-program-ts.s # sBPF assembly source code
├── tests/
│ ├── types.ts # TypeScript type definitions
│ └── memo-program-sbpf.test.ts # Comprehensive test suite
├── deploy/
│ └── memo-program-sbpf-keypair.json # Program keypair (generated)
├── package.json # Node.js dependencies
├── tsconfig.json # TypeScript configuration
└── README.md # This file
# Compile the assembly program
sbpf build
# Deploy to local validator
sbpf deploy
# Run tests
sbpf test
# or
yarn test
# Build, deploy, and test in one command
sbpf e2e# 1. Start validator (separate terminal)
solana-test-validator
# 2. Build, deploy, and test
sbpf e2eThe memo program (src/memo-program-sbpf/memo-program-sbpf.s) follows this flow:
- Read Account Count - Load number of accounts from input buffer
- Fast Path Check - If zero accounts, jump directly to instruction data
- Skip Accounts - Efficiently iterate through accounts (real and duplicate) to reach instruction data
- Read Memo Data - Extract instruction data length and pointer
- Validate Length - Ensure memo is not empty (error 0x1 if empty)
- Log Memo - Call
sol_log_syscall to log the memo text - Return Success - Exit with code 0
The program parses Solana's aligned BPF input buffer:
Offset 0x00: num_accounts (8 bytes)
For each account:
+0x00: marker (1 byte) - 0xFF for real, index for duplicate
+0x01: is_signer (1 byte)
+0x02: is_writable (1 byte)
+0x03: is_executable (1 byte)
+0x04: padding (4 bytes)
+0x08: pubkey (32 bytes)
+0x28: owner (32 bytes)
+0x48: lamports (8 bytes)
+0x50: data_len (8 bytes)
+0x58: data (data_len bytes)
+0x58+data_len: reserved (10,240 bytes) - MAX_PERMITTED_DATA_INCREASE
+0x58+data_len+10240: rent_epoch (8 bytes)
Total account size = 10,336 + data_len
After all accounts:
instruction_data_len (8 bytes)
instruction_data (variable bytes) - the memo text
The critical fix was calculating the correct account size:
.equ MAX_PERMITTED_DATA_INCREASE, 10240
.equ ACCOUNT_FIXED_SIZE, 10336 # 88 + 10240 + 8
add64 r7, ACCOUNT_FIXED_SIZE # Add 10336
add64 r7, r3 # Add data_lenThe project includes 7 comprehensive tests:
| # | Test Name | Description | Expected Result |
|---|---|---|---|
| 1 | Valid memo with signer | Standard memo operation | ✅ Success |
| 2 | Memo without signer | No signer required (optimized) | ✅ Success |
| 3 | Empty memo | Zero-length instruction data | ❌ Error 0x1 |
| 4 | Long memo (256+ chars) | Large instruction data | ✅ Success |
| 5 | Multiple memos | 3 memos in one transaction | ✅ All logged |
| 6 | Multiple signers | 3 signers on one memo | ✅ Success |
| 7 | Type validation | TypeScript type safety checks | ✅ All types valid |
# Run all tests
yarn test
# Run tests with sbpf (includes build & deploy)
sbpf e2e| Code | Hex | Name | Description |
|---|---|---|---|
| 0 | 0x0 | Success | Transaction completed successfully |
| 1 | 0x1 | EmptyMemo | Instruction data is empty (no memo text) |
- Edit
src/memo-program-ts/memo-program-ts.s - Rebuild:
sbpf build - Deploy:
sbpf deploy - Test:
yarn test
- Open
tests/memo-program-sbpf.test.ts - Add new test case:
it("should handle your test case", async function () { // Your test logic here });
- Run:
yarn test
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This project is licensed under the MIT License.
- Built with sBPF by Blueshift
- Inspired by the Blueshift Assembly Course
- Solana BPF loader serialisation format