Cycle

The Scriptorium

Smart Assembly code templates and tools for on-chain development in Eve Frontier.

Building Your First Gate
IntermediateChapter 3 of 415 min read

Toll Collection and Fees

A Smart Gate can charge a toll for passage. In this chapter, we'll implement toll collection with revenue tracking for the gate owner.

Revenue Table

Add a table to track collected fees:

typescript
// Add to mud.config.ts
GateRevenue: {
  keySchema: { gateId: "uint256" },
  valueSchema: {
    totalCollected: "uint256",
    totalUses: "uint32",
    lastCollectionTime: "uint256",
  },
},

Updated Gate System

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

import { System } from "@latticexyz/world/src/System.sol";
import { GateConfig } from "../codegen/tables/GateConfig.sol";
import { GateRevenue } from "../codegen/tables/GateRevenue.sol";

contract GateTollSystem is System {
  error InsufficientToll(uint256 required, uint256 provided);
  error NoRevenueToWithdraw(uint256 gateId);

  function setToll(uint256 gateId, uint256 amount) public {
    address owner = GateConfig.getOwner(gateId);
    require(_msgSender() == owner, "Not gate owner");
    GateConfig.setToll(gateId, amount);
  }

  function collectToll(uint256 gateId) public payable {
    uint256 toll = GateConfig.getToll(gateId);

    if (toll > 0 && msg.value < toll) {
      revert InsufficientToll(toll, msg.value);
    }

    // Update revenue tracking
    uint256 totalCollected = GateRevenue.getTotalCollected(gateId);
    uint32 totalUses = GateRevenue.getTotalUses(gateId);

    GateRevenue.set(
      gateId,
      totalCollected + msg.value,
      totalUses + 1,
      block.timestamp
    );
  }

  function withdrawRevenue(uint256 gateId) public {
    address owner = GateConfig.getOwner(gateId);
    require(_msgSender() == owner, "Not gate owner");

    uint256 balance = GateRevenue.getTotalCollected(gateId);
    if (balance == 0) {
      revert NoRevenueToWithdraw(gateId);
    }

    // Reset collected amount
    GateRevenue.setTotalCollected(gateId, 0);

    // Transfer to owner
    payable(owner).transfer(balance);
  }
}

Revenue Dashboard Data

Gate owners can read their revenue from the frontend:

typescript
function useGateRevenue(gateId: string) {
  const [revenue, setRevenue] = useState({ total: 0n, uses: 0 });

  useEffect(() => {
    const data = client.tables.GateRevenue.get({
      gateId: BigInt(gateId),
    });

    if (data) {
      setRevenue({
        total: data.totalCollected,
        uses: data.totalUses,
      });
    }
  }, [gateId]);

  return revenue;
}

Pricing Strategy Tips

  • Free gates build goodwill and traffic
  • Low tolls (0.001 ETH) cover gas costs and deter spam
  • Premium tolls work for strategic routes with no alternatives
  • Dynamic pricing based on demand is possible but adds complexity

In the next chapter, we'll put it all together and deploy a working gate.

Sign in to track your progress.