Full lesson
w3-solana
What it does
From the project README:
w3-solana provides a simple and easy way for developers and users to access resources on the chain, we'll call it w3solana for the time being.
- Original
- solanafuns/w3-solana
- SuperStack fork
- Blockchains/w3-solana @
4516015 - Stars
- 0
- Last push (original)
- 2024-04-20
- Languages
- Rust, Go, TypeScript, CSS, Dockerfile, JavaScript
- Detected stack
- Native Rustsolana-go
- Categories
- Other
- Licence
- MIT
Architecture
27 source files. By extension: .rs ×15, .go ×8, .ts ×2, .tsx ×2.
| Top-level directory | Source files |
|---|---|
gateway | 8 |
w3 | 7 |
w3-uploader | 6 |
vite-project | 4 |
src | 2 |
❌ Does not build as-is, checked 2026-11-02 Tested on 2026-11-02: full log results.json
Build it yourself: step-by-step guide
Every command below was run on 2026-11-02 by tests/run_fork.py against the pinned commit, and the results come from that run. The full output is in the committed log.
1. Prerequisites (versions we tested with)
- Rust:
rustc 1.99.1 (58cf8f9c3 2026-10-13) - Solana CLI (Agave):
solana-cli 1.18.17 (src:c027cfc3; feat:4215500110, client:Agave) - Node.js:
v24.22.1 - npm:
11.21.1 - pnpm:
10.34.6
2. Clone at the tested commit
git clone https://github.com/Blockchains/w3-solana.git && cd w3-solana
git checkout 4516015499860a2326345f4351b36f6853ad983f3. Configure
No environment file is needed.
7. Deploy to a local validator
8. Run and exercise it
| Directory | Command | Result | Time |
|---|---|---|---|
. | bash /workspace/sol-college/repo/tests/flows/w3-solana.sh | ❌ exit 1 | 2s |
Output tail: bash /workspace/sol-college/repo/tests/flows/w3-solana.sh
lock file version 4 requires `-Znext-lockfile-bump`
Error: Unable to open program file: No such file or directory (os error 2)How it works
Every instruction handler in the program, with the accounts struct it validates. Each link points to the line at the pinned commit.
| Instruction | Accounts | Where |
|---|---|---|
PutContent | native instruction variant | w3-uploader/src/sdk.rs:78 |
PutContent | native instruction variant | w3/src/instruction.rs:11 |
Extend it
- Add an integration test that runs the main flow end to end and asserts every account state change.
- Add a negative test: call each instruction with the wrong signer or account and check that it fails.
- Add an event (
emit!) to each state-changing instruction and subscribe to it from the client. - Replace any unchecked
AccountInfowith typed accounts plushas_one/seedsconstraints. - Make the frontend read the cluster from an env var, deploy it to GitHub Pages and point it at your devnet program.
- Wire the build into CI by copying this site's verify-forks workflow.
Dapp: deploying
This fork is queued for its own live dapp on Sol.College, with a Devnet/Mainnet switch. It appears here, at /dapps/w3-solana/, once it is deployed and checked on devnet. Until then, run it locally with the commands below.
Key code, explained
Excerpts chosen automatically where the code uses the most Solana concepts, pinned to the fork's commit.
gateway/pkg/solana/client.go L85–108 @ 4516015 · MIT · original } k, bump_seed, err := common.FindProgramAddress(seeds, s.Program) logrus.WithFields(logrus.Fields{ "seeds": seeds, "program": s.Program.ToBase58(), "bump": bump_seed, "account": k.ToBase58(), }).Info("find url account !!!") return k, err} // let base_seed = ".w3-solana-name";// let (config_pda, bump_seed) = PdaHelper::new(program_id.clone())// .find_program_address(&[base_seed.as_bytes(), name.as_bytes()]); func (s *W3Site) GetSiteConfig(name string) (*NameConfig, error) { baseSeed := ".w3-solana-name" seeds := [][]byte{} seeds = append(seeds, []byte(baseSeed)) seeds = append(seeds, []byte(name)) k, _, err := common.FindProgramAddress(seeds, s.NameResolver) if err != nil { return nil, err- Line 91
"bump": bump_seed,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve. - Line 99
// .find_program_address(&[base_seed.as_bytes(), name.as_bytes()]);: Derives a PDA from seeds and the program ID and returns the address with its canonical bump. - Line 104
seeds = append(seeds, []byte(baseSeed)): Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
w3/src/process/trunk.rs L25–48 @ 4516015 · MIT · original let trunk_account: &AccountInfo<'_> = next_account_info(account_info_iter)?; let system_program_account: &AccountInfo<'_> = next_account_info(account_info_iter)?; let pda_helper = PdaHelper::new(program_id.clone()); { msg!("do trunk meta_account update !!!"); let (meta_pda, bump_seed) = pda_helper.find_program_address_by_text(&path); msg!("meta_pda: {:?}", meta_pda); msg!("meta_account.key: {:?}", meta_account.key); assert!(meta_account.key == &meta_pda); let meta_page = &PageData::TrunkPage { trunks: trunk_no }; let meta_data = meta_page.try_to_vec()?; if meta_account.data_is_empty() { let rent: Rent = Rent::get()?; let data_size = meta_data.len(); let rent_lamports = rent.minimum_balance(data_size); invoke_signed( &system_instruction::create_account( payer.key, &meta_pda, rent_lamports,- Line 31
msg!("do trunk meta_account update !!!");: Logs to the program log, which is visible in explorers and the test output. - Line 32
let (meta_pda, bump_seed) = pda_helper.find_program_address_by_text(&path);: Derives a PDA from seeds and the program ID and returns the address with its canonical bump. - Line 44
invoke_signed(: Makes a CPI signed by a PDA. The signer seeds prove the program controls that address.
gateway/pkg/solana/client_test.go L13–36 @ 4516015 · MIT · original bytes := program.Bytes() t.Logf("bytes is -> %v ", bytes) name := "w3sol" baseSeed := ".w3-solana-name" seeds := [][]byte{} seeds = append(seeds, []byte(baseSeed)) seeds = append(seeds, []byte(name)) t.Logf("seeds is -> %v", seeds) k, bump, err := common.FindProgramAddress(seeds, program) t.Error(err) t.Logf("key is %s", k.ToBase58()) t.Logf("bump is %d", bump) } func TestLoadPageAccount(t *testing.T) { viper.SetDefault("network", "localnet") program := common.PublicKeyFromString("3P8bzeDY4i2QFU7AYtKH9jqnZqa7gShqB56tJPQDvmsS") path := "/index.html"- Line 20
seeds = append(seeds, []byte(baseSeed)): Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 25
k, bump, err := common.FindProgramAddress(seeds, program): Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
w3-uploader/src/client.rs L97–120 @ 4516015 · MIT · original let web_path = format!("/{}", relative_path.replace("\\", "/")); // 确保路径使用 web 标准的斜杠 info!("Web path: {}", web_path); self.upload_file(&web_path, full_path); } } } } } Ok(()) } pub fn config_name(&self, name: &str) { info!("Configuring with name: {}", name); let config_seeds = vec![".w3-solana-name".as_bytes(), name.as_bytes()]; info!("config_seeds {:?}", config_seeds); let (config_account, bump_seed) = self.helper.find_program_address(&config_seeds); info!("Account: {}", config_account); info!("Bump seed: {}", bump_seed); let instruction_enum = InstructionData::NameMapping { name: name.to_string(), program: self.program, default_page: "/index.html".to_string(), };- Line 111
let config_seeds = vec![".w3-solana-name".as_bytes(), name.as_bytes()];: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 113
let (config_account, bump_seed) = self.helper.find_program_address(&config_seeds);: Derives a PDA from seeds and the program ID and returns the address with its canonical bump.
How to run, build and test
These commands come from the repo's own manifests (package.json scripts, Anchor.toml, Cargo, go.mod, Makefile). Check the README for prerequisites and environment variables.
| Directory | Command | Runs |
|---|---|---|
vite-project | npm run dev | vite |
vite-project | npm run build | tsc && vite build |
vite-project | npm run lint | eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0 |
. | cargo build | Rust workspace build |
. | cargo test | Rust tests |
What you could build from it
- Fork the SuperStack copy and extend it within the Other gaps listed on the idea page.
- Learn the pattern first: Program Derived Addresses (PDAs)
- Learn the pattern first: Cross-Program Invocations (CPIs)