Contracts explained

GothamCash is powered by a minimal and transparent smart contract, divided into several sub-contracts called pools. Each pool corresponds to a specific denomination. Below is a breakdown of how the GothamMixer contract works and what each part is responsible for.

The 0.1 BNB pool smart contract is open-source, and available there.


📌 Constants and Globals

uint256 public constant DEPOSIT_AMOUNT = 0.1 ether;
uint256 public constant FEE_PERCENT = 1;
uint256 public constant EXPIRY_TIME = 365 days;
address public owner;
  • DEPOSIT_AMOUNT: The fixed amount users can deposit (0.1 BNB / 1 BNB / 10 BNB).

  • FEE_PERCENT: Optional 1% fee for deposits/withdrawals (activatable).

  • EXPIRY_TIME: After 365 days, unclaimed deposits can be reclaimed by the owner.

  • owner: Address with admin rights (can reclaim expired funds, enable fees, etc.).


🧾 State Variables

mapping(bytes32 => bool) public commitments;
mapping(bytes32 => bool) public nullifiers;
mapping(bytes32 => uint256) public commitmentTimestamps;
mapping(bytes32 => address) public withdrawalAuthorizations;
bytes32[] public commitmentList;
bool public feesEnabled;
  • commitments: Tracks whether a deposit has been made for a given commitment hash.

  • nullifiers: Prevents double withdrawals (each withdrawal must be unique).

  • commitmentTimestamps: Stores the deposit time for expiration tracking.

  • withdrawalAuthorizations: Maps a commitment to the address allowed to withdraw it.

  • commitmentList: Keeps an indexable list of all deposits (used for reclaim).

  • feesEnabled: Indicates whether the 1% fee is active or not.


📥 Deposit Function

function deposit(bytes32 commitment) external payable
  • Requires the exact deposit amount (adjusted if fees are enabled).

  • Verifies the commitment has not been used before.

  • Records the deposit and timestamp.

  • Emits a Deposited event.

💡 The commitment is a hash of a secret (nullifier + secret), generated off-chain.


✍️ authorizeWithdrawal()

function authorizeWithdrawal(
  bytes32 nullifier,
  bytes32 secret,
  uint256 deadline,
  bytes calldata signature
)
  • Users sign a message containing:

    keccak256(nullifier, secret, msg.sender, deadline)
  • This prevents front-running and confirms intent.

  • The contract verifies the ECDSA signature using a helper library.

  • If valid, the sender is authorized to withdraw the corresponding commitment.

⚠️ This step is required to bind a withdrawal to a specific address.


💸 withdraw()

function withdraw(bytes32 nullifier, bytes32 secret)
  • Computes commitment = keccak256(nullifier, secret) and nullifierHash.

  • Validates:

    • The commitment exists.

    • The nullifier hasn't been used.

    • An address was authorized to withdraw it.

    • The caller is that authorized address.

  • Prevents reuse by marking:

    • The commitment as spent.

    • The nullifier as used.

    • The authorization as cleared.

  • Sends BNB to the authorized recipient.

🛡️ Withdrawals require both the secrets and a prior authorization to enhance security.


⏳ reclaimExpired() & reclaimExpiredBatch()

function reclaimExpired()
function reclaimExpiredBatch(uint256 start, uint256 end)
  • Allows the owner to recover funds from deposits older than 365 days.

  • Works by scanning the commitment list and checking timestamps.

  • Emits an ExpiredReclaimed event for transparency.

  • Those functions only affect the deposits made 365 days ago from now, thanks to EXPIRY_TIME.

  • They avoid abandoned funds and reinject those into the project funds.

🔁 The batch variant is more gas-efficient for large pools.


🧠 isExpired()

function isExpired(bytes32 commitment) external view returns (bool)
  • Helper view function to check if a commitment has expired and can be reclaimed.


👑 Admin Functions

function changeOwner(address newOwner)
function setFeesEnabled(bool enabled)
  • changeOwner: Lets the current owner transfer control.

  • setFeesEnabled: Activates or deactivates the 1% deposit/withdrawal fee.


🧮 View Helpers

solidityCopierModifierfunction getCommitmentCount() external view returns (uint256)
  • Returns the total number of unique commitments ever submitted.


🛑 Safety Mechanisms

receive() external payable { revert(); }
fallback() external { revert(); }
  • Rejects direct BNB transfers and undefined function calls to prevent misuse.


🔐 ECDSA Library

This contract includes a minimal internal version of the ECDSA signature library. It provides:

  • toEthSignedMessageHash: Adds the standard Ethereum prefix to prevent replay attacks.

  • recover: Extracts the signer’s address from a signed message.


Summary of Workflow

Step
Description

1️⃣ Deposit

User deposits exactly 0.1 BNB with a commitment hash.

2️⃣ Note storage

Off-chain, user stores the note (nullifier + secret).

3️⃣ Authorize

User signs the note with a deadline and calls authorizeWithdrawal().

4️⃣ Withdraw

User (or authorized relayer) calls withdraw() to redeem funds.

5️⃣ Optional

If funds aren't claimed after 365 days, owner can reclaim them.


This smart contract prioritizes simplicity, auditability, and privacy-by-design. All note logic (nullifier and secret generation) happens client-side, meaning the smart contract itself never holds user metadata.

Last updated