HyperPaxeer NetworkPaxeer Network

Computable Token Machine (CTM)

Revolutionary token standard where tokens are fully-fledged execution environments

Overview

The Computable Token Machine is a revolutionary token standard that combines ERC-20 functionality with the Diamond Standard (EIP-2535). Each CTM is both a standard token AND a modular execution environment capable of running its own applications, managing state, and evolving over time.

Beyond value, beyond utility—tokens that think.

Live Contract

Deployed CTM

What is CTM?

The Computable Token Machine is a revolutionary token standard that combines ERC-20 functionality with the Diamond Standard (EIP-2535). Each CTM is both a standard token AND a modular execution environment capable of running its own applications, managing state, and evolving over time.

Key Features

Core Concepts

Two Personalities

CTM acts as both a token and a machine:

On the outside, a CTM behaves like any standard ERC-20 token. It can be:

  • Held in wallets
  • Traded on exchanges
  • Used in DeFi protocols
  • Transferred between addresses

No special handling required - it's just a token!

Internally, the CTM acts as a proxy that routes function calls to various logic contracts called "Programs" (or Facets). These Programs can be:

  • Added without redeployment
  • Replaced to fix bugs or add features
  • Removed when no longer needed
  • Composed together for complex behavior

All while maintaining the same contract address!

Programs (Facets)

Programs are stateless Solidity contracts that contain the logic executed by the CTM. Each Program manages its own state within a unique storage slot to prevent collisions.

Example Programs:

  • Voting and Governance
  • Staking and Rewards
  • DEX Integration
  • NFT Minting
  • Custom Game Logic
  • Automated Trading
  • On-chain AI Agents

Diamond Standard (EIP-2535)

CTM is built on the Diamond Standard, which allows a single contract to use multiple logic contracts (facets/programs). This pattern enables:

  • ✅ Unlimited contract size
  • ✅ Upgradability
  • ✅ Modular functionality
  • ✅ Single address persistence

Quick Start

Clone Repository

git clone https://github.com/dev-paxeer/Computable-Token-Machine-v1.0.0
cd Computable-Token-Machine-v1.0.0
npm install

Configure Network

Add Paxeer Network details to hardhat.config.js:

networks: {
  paxeer: {
    url: "https://public-rpc.paxeer.app/rpc",
    chainId: 125,
    accounts: [process.env.PRIVATE_KEY]
  }
}

Deploy

npx hardhat run scripts/deploy.js --network paxeer

This deploys:

  • TokenVM.sol (the main proxy contract)
  • DiamondCutFacet (for adding/removing Programs)
  • DiamondLoupeFacet (for inspecting installed Programs)
  • OwnershipFacet (access control)
  • ERC20Facet (token functionality)

Creating Programs

Program Structure

Programs must be stateless and manage state within a unique storage slot to prevent collisions.

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

import {ERC20Facet} from "./ERC20Facet.sol";

contract VotingProgram {
    
    struct VotingStorage {
        mapping(uint256 => string) proposals;
        mapping(uint256 => mapping(address => uint256)) votes;
        uint256 proposalCount;
    }

    bytes32 constant VOTING_STORAGE_POSITION = 
        keccak256("ctm.program.storage.voting");

    function votingStorage() internal pure 
        returns (VotingStorage storage vs) 
    {
        bytes32 position = VOTING_STORAGE_POSITION;
        assembly {
            vs.slot := position
        }
    }

    function createProposal(string calldata _description) external {
        VotingStorage storage vs = votingStorage();
        vs.proposalCount++;
        vs.proposals[vs.proposalCount] = _description;
    }

    function vote(uint256 _proposalId) external {
        uint256 voterBalance = ERC20Facet(address(this))
            .balanceOf(msg.sender);
        require(voterBalance > 0, "Must hold tokens");

        VotingStorage storage vs = votingStorage();
        vs.votes[_proposalId][msg.sender] = voterBalance;
    }
    
    function getProposal(uint256 _proposalId) 
        external view returns (string memory) 
    {
        VotingStorage storage vs = votingStorage();
        return vs.proposals[_proposalId];
    }
    
    function getVotes(uint256 _proposalId, address _voter) 
        external view returns (uint256) 
    {
        VotingStorage storage vs = votingStorage();
        return vs.votes[_proposalId][_voter];
    }
}

Adding Programs to CTM

Use the diamondCut function to register new Programs:

const diamondCut = await ethers.getContractAt('IDiamondCut', ctmAddress);

await diamondCut.diamondCut(
  [{
    facetAddress: votingProgramAddress,
    action: FacetCutAction.Add, // 0 = Add, 1 = Replace, 2 = Remove
    functionSelectors: getSelectors(votingProgram)
  }],
  ethers.constants.AddressZero,
  '0x'
);

Helper Function for Selectors

function getSelectors(contract) {
  const signatures = Object.keys(contract.interface.functions);
  const selectors = signatures.reduce((acc, val) => {
    if (val !== 'init(bytes)') {
      acc.push(contract.interface.getSighash(val));
    }
    return acc;
  }, []);
  return selectors;
}

Program Best Practices

Security Considerations

Critical Security Points:

  1. Storage Layout: Never use standard global state variables. Always use the diamond storage pattern with unique keccak256 slots.

  2. Access Control: The diamondCut function is extremely powerful. Ensure it's protected by robust ownership or governance control.

  3. Stateless Logic: Programs are logic contracts and should not hold funds or have constructors that set state.

  4. Testing: Thoroughly test all Programs before deployment. Once added to a CTM, they have access to the token's storage and capabilities.

Auditing Checklist

Before deploying CTM or adding new Programs:

  • Storage slots use unique keccak256 hashes
  • No global state variables in Programs
  • Access control properly configured
  • Programs don't hold funds directly
  • All Programs thoroughly tested
  • diamondCut function is protected
  • Inter-program interactions tested
  • Gas optimization reviewed
  • Security audit completed (for production)

Advanced Examples

Staking Program

contract StakingProgram {
    struct StakingStorage {
        mapping(address => uint256) stakedAmount;
        mapping(address => uint256) stakingTimestamp;
        uint256 rewardRate; // rewards per second
    }

    bytes32 constant STAKING_STORAGE = keccak256("ctm.program.staking");

    function stakingStorage() internal pure 
        returns (StakingStorage storage ss) 
    {
        bytes32 position = STAKING_STORAGE;
        assembly { ss.slot := position }
    }

    function stake(uint256 amount) external {
        ERC20Facet token = ERC20Facet(address(this));
        require(token.transferFrom(msg.sender, address(this), amount), "Transfer failed");
        
        StakingStorage storage ss = stakingStorage();
        ss.stakedAmount[msg.sender] += amount;
        ss.stakingTimestamp[msg.sender] = block.timestamp;
    }

    function unstake(uint256 amount) external {
        StakingStorage storage ss = stakingStorage();
        require(ss.stakedAmount[msg.sender] >= amount, "Insufficient stake");
        
        uint256 rewards = calculateRewards(msg.sender);
        ss.stakedAmount[msg.sender] -= amount;
        
        ERC20Facet token = ERC20Facet(address(this));
        require(token.transfer(msg.sender, amount + rewards), "Transfer failed");
    }

    function calculateRewards(address user) public view returns (uint256) {
        StakingStorage storage ss = stakingStorage();
        uint256 timeStaked = block.timestamp - ss.stakingTimestamp[user];
        return ss.stakedAmount[user] * ss.rewardRate * timeStaked / 1e18;
    }
}

Governance Program

contract GovernanceProgram {
    struct GovernanceStorage {
        mapping(uint256 => Proposal) proposals;
        uint256 proposalCount;
        uint256 votingPeriod;
    }

    struct Proposal {
        string description;
        uint256 forVotes;
        uint256 againstVotes;
        uint256 endTime;
        bool executed;
    }

    bytes32 constant GOVERNANCE_STORAGE = keccak256("ctm.program.governance");

    function governanceStorage() internal pure 
        returns (GovernanceStorage storage gs) 
    {
        bytes32 position = GOVERNANCE_STORAGE;
        assembly { gs.slot := position }
    }

    function propose(string calldata description) external returns (uint256) {
        GovernanceStorage storage gs = governanceStorage();
        gs.proposalCount++;
        
        gs.proposals[gs.proposalCount] = Proposal({
            description: description,
            forVotes: 0,
            againstVotes: 0,
            endTime: block.timestamp + gs.votingPeriod,
            executed: false
        });
        
        return gs.proposalCount;
    }

    function vote(uint256 proposalId, bool support) external {
        GovernanceStorage storage gs = governanceStorage();
        Proposal storage proposal = gs.proposals[proposalId];
        
        require(block.timestamp < proposal.endTime, "Voting ended");
        
        uint256 weight = ERC20Facet(address(this)).balanceOf(msg.sender);
        
        if (support) {
            proposal.forVotes += weight;
        } else {
            proposal.againstVotes += weight;
        }
    }
}

Resources

Next Steps

How is this guide?

On this page