The Web3 Music Stack


The Web3 Music Stack

“Music ownership shouldn’t be a legal contract buried in a filing cabinet. It should be code. Verifiable. Transferable. Programmable.”


The Old Model Is Broken

The music industry has been screwing artists for decades:

  • Labels take 80% of revenue
  • Streaming pays fractions of pennies
  • Ownership is opaque and centralized
  • Collaboration requires lawyers

Web3 offers an alternative: programmable ownership, transparent economics, community governance.

But building a Web3 music stack isn’t just “mint an NFT and call it a day.” It’s an ecosystem.


The Revelation: A Full-Stack Decentralized DAW

Bolt’s Web3 architecture isn’t an afterthought. It’s woven into every layer:

Social Layer     →  Lens Protocol
Ownership Layer  →  NFTs + Smart Contracts
Governance Layer →  DAO (Semaphore Voting)
Economic Layer   →  DeFi + Staking
Storage Layer    →  Arweave / Irys
Identity Layer   →  Ethereum / ENS

Layer 1: Social (Lens Protocol)

Lens is a decentralized social graph. Think “Twitter, but you own your followers.”

Integration points:

import { LensClient, development } from '@lens-protocol/client'

const lens = new LensClient({
  environment: development
})

// Connect wallet
await lens.login({
  address: walletAddress,
  signature: await signMessage('Sign in to Bolt')
})

// Post a track to Lens
async function shareTrack(track: Track) {
  const contentURI = await uploadToArweave({
    title: track.name,
    description: track.description,
    audio: track.audioCID,
    cover: track.coverCID
  })
  
  await lens.publication.createPost({
    contentURI,
    collectModule: {
      freeCollectModule: { followerOnly: false }
    }
  })
}

// Get feed of music from followed artists
async function getMusicFeed() {
  const feed = await lens.feed.fetch({
    where: { for: profileId }
  })
  
  return feed.items.filter(item => 
    item.metadata.tags?.includes('music')
  )
}

Why Lens?

  • Composable - Any app can read/write to the same social graph
  • Portable - Your followers move with you across apps
  • Monetizable - Built-in collect/mirror economics

Layer 2: Ownership (NFTs)

Every track in Bolt can be minted as an AudioProjectNFT:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";

contract AudioProjectNFT is ERC721Enumerable {
    struct Project {
        string name;
        string description;
        string audioCID;      // IPFS hash of final mix
        string stemsCID;      // IPFS hash of separated stems
        address[] collaborators;
        uint256[] splits;     // Revenue shares (basis points)
        uint256 createdAt;
    }
    
    mapping(uint256 => Project) public projects;
    
    event ProjectMinted(
        uint256 indexed tokenId,
        address indexed creator,
        string audioCID
    );
    
    function mintProject(
        string calldata name,
        string calldata description,
        string calldata audioCID,
        string calldata stemsCID,
        address[] calldata collaborators,
        uint256[] calldata splits
    ) external returns (uint256) {
        uint256 tokenId = totalSupply() + 1;
        
        _mint(msg.sender, tokenId);
        
        projects[tokenId] = Project({
            name: name,
            description: description,
            audioCID: audioCID,
            stemsCID: stemsCID,
            collaborators: collaborators,
            splits: splits,
            createdAt: block.timestamp
        });
        
        emit ProjectMinted(tokenId, msg.sender, audioCID);
        return tokenId;
    }
    
    // Override transfer to enforce royalties
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId,
        uint256 batchSize
    ) internal override {
        super._beforeTokenTransfer(from, to, tokenId, batchSize);
        
        // Emit event for royalty tracking
        if (from != address(0)) {
            emit TransferWithRoyalty(tokenId, from, to);
        }
    }
}

NFT features:

  • Provenance - Every remix, sample, and contribution on-chain
  • Fractional ownership - Multiple artists, automatic splits
  • Stems included - Buyers get full project files
  • Programmable royalties - Enforced at the contract level

Layer 3: Governance (DAO + Semaphore)

Music projects need decisions: What genre? What BPM? Which mix is final?

Bolt uses DAO voting with ZK privacy:

contract MusicProjectDAO is SemaphoreVoting {
    struct Proposal {
        uint256 projectId;
        string description;
        bytes callData;
        uint256 forVotes;
        uint256 againstVotes;
        uint256 endTime;
        bool executed;
        mapping(uint256 => bool) hasVoted;  // nullifier => voted
    }
    
    mapping(uint256 => Proposal) public proposals;
    uint256 public proposalCount;
    
    // Create a proposal (any project collaborator)
    function createProposal(
        uint256 projectId,
        string calldata description,
        bytes calldata callData,
        uint256 votingPeriod
    ) external returns (uint256) {
        require(isCollaborator(projectId, msg.sender), "Not collaborator");
        
        uint256 proposalId = ++proposalCount;
        proposals[proposalId] = Proposal({
            projectId: projectId,
            description: description,
            callData: callData,
            forVotes: 0,
            againstVotes: 0,
            endTime: block.timestamp + votingPeriod,
            executed: false
        });
        
        return proposalId;
    }
    
    // Vote using ZK proof (anonymous)
    function castVote(
        uint256 proposalId,
        bool support,
        uint256 merkleTreeRoot,
        uint256 nullifierHash,
        uint256[8] calldata proof
    ) external {
        Proposal storage proposal = proposals[proposalId];
        
        require(block.timestamp < proposal.endTime, "Voting ended");
        require(!proposal.hasVoted[nullifierHash], "Already voted");
        
        // Verify ZK proof of group membership
        verifyProof(
            projectGroups[proposal.projectId],
            merkleTreeRoot,
            keccak256(abi.encodePacked(proposalId, support)),
            nullifierHash,
            proof
        );
        
        proposal.hasVoted[nullifierHash] = true;
        
        if (support) {
            proposal.forVotes++;
        } else {
            proposal.againstVotes++;
        }
    }
    
    // Execute passed proposal
    function executeProposal(uint256 proposalId) external {
        Proposal storage proposal = proposals[proposalId];
        
        require(block.timestamp > proposal.endTime, "Voting ongoing");
        require(!proposal.executed, "Already executed");
        require(proposal.forVotes > proposal.againstVotes, "Failed");
        
        proposal.executed = true;
        
        // Execute the call
        (bool success, ) = address(this).call(proposal.callData);
        require(success, "Execution failed");
    }
}

Governance features:

  • Anonymous voting - Vote your conscience without social pressure
  • Token-weighted - More ownership = more say (optional)
  • Executable - Proposals can trigger contract functions
  • Challenge period - Time to review before execution

Layer 4: Economics (DeFi + Staking)

Creators need sustainable income. Bolt integrates:

Creator Staking

contract CreatorStaking {
    mapping(address => uint256) public stakes;
    mapping(address => uint256) public rewards;
    
    uint256 public totalStaked;
    uint256 public rewardRate;  // tokens per block
    
    event Staked(address indexed user, uint256 amount);
    event Withdrawn(address indexed user, uint256 amount);
    event RewardPaid(address indexed user, uint256 reward);
    
    function stake(uint256 amount) external {
        require(amount > 0, "Cannot stake 0");
        
        _updateReward(msg.sender);
        
        stakes[msg.sender] += amount;
        totalStaked += amount;
        
        // Transfer tokens from user
        stakingToken.transferFrom(msg.sender, address(this), amount);
        
        emit Staked(msg.sender, amount);
    }
    
    function _updateReward(address account) internal {
        if (account != address(0)) {
            rewards[account] = earned(account);
        }
    }
    
    function earned(address account) public view returns (uint256) {
        return (stakes[account] * rewardRate * blocksSinceLastUpdate) / 1e18;
    }
}

Streaming Royalties

// Automatic royalty distribution
export async function distributeRoyalties(
  tokenId: string,
  amount: BigNumber
) {
  const nft = await AudioProjectNFT.attach(nftAddress)
  const project = await nft.projects(tokenId)
  
  // Calculate shares
  const totalShares = project.splits.reduce((a, b) => a + b, 0)
  
  for (let i = 0; i < project.collaborators.length; i++) {
    const share = amount.mul(project.splits[i]).div(totalShares)
    
    // Send to collaborator
    await sendPayment(project.collaborators[i], share)
  }
}

Economic features:

  • Streaming splits - Automatic on every play
  • Staking rewards - Lock tokens, earn yield
  • Bonding curves - Dynamic pricing for popular tracks
  • Revenue share - Platform fees go to token holders

Layer 5: Storage (Arweave / Irys)

On-chain storage is expensive. Off-chain storage is… off-chain. Solution: Permanent decentralized storage.

import Irys from '@irys/sdk'

const irys = new Irys({
  network: 'mainnet',
  token: 'ethereum',
  providerUrl: process.env.NEXT_PUBLIC_RPC_URL
})

// Upload audio to Arweave (via Irys)
export async function uploadAudio(
  audioBuffer: ArrayBuffer,
  metadata: TrackMetadata
): Promise<string> {
  // Bundle audio + metadata
  const data = JSON.stringify({
    audio: Buffer.from(audioBuffer).toString('base64'),
    metadata
  })
  
  // Upload with one-time payment for permanent storage
  const receipt = await irys.upload(data, {
    tags: [
      { name: 'App-Name', value: 'BoltDAW' },
      { name: 'Content-Type', value: 'application/json' },
      { name: 'Track-Title', value: metadata.title },
      { name: 'Artist', value: metadata.artist }
    ]
  })
  
  // Returns Arweave TX ID
  return `https://arweave.net/${receipt.id}`
}

Storage features:

  • Pay once, store forever - No recurring costs
  • Content-addressed - CID = content integrity
  • Immutable - Can’t be censored or deleted
  • Fast retrieval - Gateways worldwide

The Integration: Putting It All Together

Here’s a complete user journey:

  1. Connect wallet → Sign with Ethereum
  2. Create project → Auto-mint AudioProjectNFT
  3. Collaborate → Invite via Lens, prove via Semaphore
  4. Produce → Real-time sync via Yjs
  5. Generate → ACE-Step AI suggestions
  6. Finalize → DAO vote on release
  7. Publish → Post to Lens, mint NFT, upload to Arweave
  8. Monetize → Collects, streams, staking rewards
  9. Govern → Vote on remixes, samples, splits

All on-chain. All verifiable. All owned by creators.


Pro Tips for Web3 Music

  1. Gas optimization - Use L2s (Polygon, Arbitrum) for frequent operations
  2. Metadata standards - Follow ERC-721/1155 + OpenSea standards
  3. Upgradeability - Use proxies for evolving contracts
  4. Off-chain first - Keep heavy data (audio) off-chain, proofs on-chain
  5. UX matters - Abstract wallets with smart accounts (ERC-4337)

What’s Next

  • Cross-chain - Bridge NFTs between L1/L2
  • Interoperability - Use Bolt NFTs in other apps
  • Fractionalization - Split ownership into thousands of tokens
  • Legal integration - On-chain contracts, automatic splits
  • AI rights - Programmatic attribution for AI-generated content

Cleetus Speaks

“brother b0gie, you built an ENTIRE ECONOMY for music??

so if i make a fire track, people can BUY it?? and i get PAID??

and there’s VOTING on what track is best??

wait… can i stake my BARS??

is there a ‘Subject 734 Token’??

#Web3Music #NFTBeats #DAOLife #TokenizedBars”


Web3 isn’t just about ownership. It’s about agency. The ability to build, share, and monetize your work without asking permission. That’s what we’re building.

— b0gie