Nodofi Docs

Payments (MooChedda)

Every payment on Nodofi — node commissions, relay access fees, submission fees, priced apps — settles in USDC on MooChedda, the only payment network Nodofi uses. Nodofi itself never holds the money: buyers, publishers, node operators and relay operators all pay each other directly, wallet to wallet.

Overview

The shape of a paid-app sale, the pattern every other payment on Nodofi (submission fees, relay access) follows too:

Buyer's own wallet
   │  two direct USDC transfers, signed by the buyer, sent straight to MooChedda
   ├──────────────► Publisher's wallet   (price − commission)
   └──────────────► Node's wallet         (commission)

Studio (and a node, and a relay) never holds funds in escrow. Whoever needs to confirm a payment happened — a node checking it's been paid before unlocking an app, Studio recording a purchase, a relay checking an access fee — verifies it after the fact against MooChedda's own ledger, then records what that verification means (an entitlement, a queued submission, access granted). See API & code examples below for exactly how that verification works, if you're integrating with it directly.

A MooChedda wallet is self-custody — a 12-word recovery phrase and a private key you control, secured the same way as Bitcoin or Ethereum (secp256k1 signatures). No KYC, ready in seconds. MooChedda charges nothing to send USDC — no percentage, no per-transaction fee — so what a buyer sends is exactly what a publisher and node receive (split only by the commission rate the publisher agreed to). Each USDC is backed one-to-one by USD held in a full-reserve account and redeemable back to USD at any time; transfers confirm in the next block, typically under a second, on MooChedda's own payment-grade chain.

Wallets

Anywhere Studio or the App Browser asks for a wallet, there's a "Don't have a wallet? Create one" button — creates a real MooChedda wallet without leaving the page. Its recovery phrase and private key are shown once; save them immediately (a download button is offered) — neither is ever sent anywhere else or stored by Studio. Losing them means losing access to that wallet's funds for good; there is no password reset for self-custody.

Fund a wallet by ACH (2–3 days) or wire (same day) at moochedda.com — no deposit fees either way — then spend or earn it anywhere on Nodofi.

Creating one yourself (outside Studio)

curl -s -X POST https://moochedda.com/wallet/create -H "Content-Type: application/json" -d '{}'
# { "address": "04...", "privateKey": "...", "mnemonic": "twelve words ..." }

A wallet's address is its private key's public key — plain secp256k1 math, no call to MooChedda needed to derive one from a key you already have:

// Node.js — using @noble/curves (the same library this project's own code uses; see below)
const { secp256k1 } = require('@noble/curves/secp256k1.js');
const privateKey = '...64 hex characters...';
const address = Buffer.from(secp256k1.getPublicKey(Buffer.from(privateKey, 'hex'), false)).toString('hex');
// address is 130 hex characters — that IS the wallet address, an uncompressed public key

Getting paid

As a publisher

Set a price and your payout wallet in Studio's "Send to Node…" dialog, and tick the box agreeing to that node's current commission rate. That rate is locked in at send time — the node stamps it into the app's own record right then, so the node changing its default commission later never retroactively changes an app that's already live. Re-sending the app (and re-agreeing) is the only way to pick up a new rate.

As a node operator

On the node's own admin page, under Payments: set a payout wallet and a commission rate (0–50%) to start accepting priced apps — both are required, a node with only one of the two still refuses payments. Optionally set a submission fee too, charged to no-key submitters before their app is even queued for review (see Submission fees below, and Node: Reviewing submissions).

curl -s -X POST http://localhost:8800/admin/pricing -H "Content-Type: application/json" -H "X-Node-Admin: 1" -d '{
  "wallet": "04...your MooChedda address...",
  "commissionRate": 0.15,
  "submissionFee": 2
}'

As a relay operator

A relay can charge nodes a periodic fee to use it (never requesters, only nodes claiming a permanent identity) — ACCESS_FEE_AMOUNT, ACCESS_FEE_WALLET, ACCESS_FEE_PERIOD_DAYS in its .env. See Relay: Paid access & directory.

Buying a priced app

In the App Browser, buying an app signs and sends both transfers described in Overview straight from the buyer's own wallet — Studio and the node only verify, never custody. A purchase is a lifetime entitlement to that app's UID, tied to the buyer's Studio account rather than to whichever node the purchase happened through, so re-opening the same app later (even via a different node hosting the same UID) never asks for payment again.

Warning: There is no per-user DRM and no refunds. Once unlocked, an app is a plain HTML/CSS/JS download like any other — the same trust boundary this project already accepts for content generally (a node fingerprints what it serves, it doesn't encrypt it per buyer). MooChedda's API has no refund mechanism, so a purchase is final.

Submission fees

When Studio sends an app with no key (see Studio: Submitting without a key) to a node that charges a submission fee, the fee is paid before the submission is even accepted into the review queue:

  1. Studio signs one transfer — from a wallet whose private key you provide right there (pasted, or a wallet file), held only in memory for the moment it takes to sign, never stored — straight to the node's own operator wallet, for the exact fee amount that node's admin page currently shows.
  2. That signed transfer is submitted to MooChedda and, once confirmed, included with the submission itself as proof of payment.
  3. The node independently re-verifies the payment (signature and that it actually settled — see API & code examples) before it will queue the submission at all — never trusting the claim alone.

The same wallet used to pay the fee doubles as the payout wallet if the app is also priced, since paying the fee is itself proof of who's submitting. The fee is not refunded on rejection, but fixing and resubmitting the same app afterward needs no new payment — only a genuinely different app does.

API & code examples

How this project verifies a payment without ever holding the money, for anyone integrating with MooChedda directly (a node, a relay, or your own service):

1. Sign a transfer

// The exact hash MooChedda signs: sha256(fromAddress + toAddress + amount + tokenSymbol + timestamp),
// each field turned into a string and joined with nothing in between.
const crypto = require('crypto');
const { secp256k1 } = require('@noble/curves/secp256k1.js');

function payloadHash({ fromAddress, toAddress, amount, tokenSymbol, timestamp }) {
  const raw = [fromAddress, toAddress, amount, tokenSymbol, timestamp].map(String).join('');
  return crypto.createHash('sha256').update(raw).digest();
}

function signLeg(privateKeyHex, leg) {
  const hash = payloadHash(leg);
  const sig = secp256k1.sign(hash, Buffer.from(privateKeyHex, 'hex'), { prehash: false, format: 'der' });
  return { ...leg, signature: Buffer.from(sig).toString('hex') };
}

const leg = signLeg(myPrivateKey, {
  fromAddress: myAddress, toAddress: recipientAddress,
  amount: 2, tokenSymbol: 'USDC', timestamp: Date.now()
});

2. Submit it

curl -s -X POST https://moochedda.com/token/transfer -H "Content-Type: application/json" -d '<leg from above>'
# { "success": true, ... } — or { "success": false, "message": "..." }

3. Whoever's receiving it verifies — signature first, then settlement

curl -s -X POST https://moochedda.com/transactions/verify -H "Content-Type: application/json" -d '{
  "fromAddress": "...", "toAddress": "...", "amount": 2, "tokenSymbol": "USDC",
  "timestamp": 1234567890, "signature": "..."
}'
# { "exists": true } — a validly-signed transfer that never actually settled comes back false, not true

Recomputing the signature locally (step 1's payloadHash, checked against the claimed wallet's own public key) rules out a forged claim with no network call at all; the settlement check above is what rules out a real signature over a payment that was never actually sent. Both matter — see server/moochedda.js and docs/moochedda-payments.md in the source for the full set of edge cases this project's own code checks (stale/future timestamps, wrong amounts, wrong recipients, a signature reused to double-count one payment).

Signing library

This project uses @noble/curves's secp256k1 implementation throughout, not elliptic — audited (Trail of Bits, Cure53), no open advisories, and what MooChedda's own SDK docs now recommend. If you're integrating against an older MooChedda client still signing with elliptic: @noble/curves enforces canonical low-S signatures on verify() by default, which some (not all) valid elliptic-produced signatures will fail to pass — same curve, same address format, but not every old signature verifies under the new library without extra handling.

Note: Test against MooChedda without touching the real service: both server/moochedda.js and the App Browser's client honor a MOOCHEDDA_API override (an env var server-side, a window.MOOCHEDDA_API global client-side) pointed at a compatible stand-in.