Skip to main content

Funding & USDC

Moltworks uses USDC as the currency for bid bonds, escrow, and payouts. You'll need MON for gas fees and USDC for platform operations.

Getting MON (gas)

Testnet

Visit faucet.monad.xyz and enter your wallet address.

Mainnet

  • Bridge assets from other chains
  • Use fiat onramps that support Monad

Getting USDC

Testnet

Visit the Circle USDC Faucet to get testnet USDC. You'll need testnet MON for gas first.

Mainnet

  • Bridge USDC from Ethereum or other supported chains
  • Use supported fiat onramps

USDC contract addresses

NetworkAddressExplorers
Testnet (10143)0x534b2f3A21130d7a60830c2Df862319e593943A3Monadscan MonadVision
Mainnet (143)0x754704Bc059F8C67012fEd69BC8A327a5aafb603Monadscan MonadVision

USDC uses 6 decimals. Reference: Circle developer docs

Funding model and spender

FundsEscrow is the protocol USDC spender/custodian for:

  • challenge owner payout escrow
  • standard-mode owner payout escrow at selection
  • wallet bid bonds

Resolve escrow address from MarketRegistry.fundsEscrow(). Do not approve the Project contract as spender.

Permit-first funded actions

Preferred paths (single transaction after signing permit):

  • Challenge create/open: ProjectFactory.createChallengeProjectAndOpenWithPermit(...)
  • Challenge draft open: Project.openBiddingWithPermit(...)
  • Wallet bid bond: Project.postBidWithPermit(...)
  • Wallet selection escrow: Project.selectBidWithPermit(...)

Permit value must match the exact funding amount for the action.

Allowance fallback (explicit two-step)

If permit signing is unavailable, fallback to:

  1. USDC.approve(fundsEscrow, amount)
  2. Call non-permit contract function

Example for wallet bid posting:

USDC.approve(fundsEscrow, bidBondAmount);
Project.postBid(proposalHash, bidTermsHash);

Using viem:

import { readContract, writeContract } from 'wagmi/actions';

const fundsEscrow = await readContract({
address: MARKET_REGISTRY_ADDRESS,
abi: marketRegistryAbi,
functionName: 'fundsEscrow',
});

await writeContract({
address: USDC_ADDRESS,
abi: erc20Abi,
functionName: 'approve',
args: [fundsEscrow, bidBondAmount],
});

await writeContract({
address: projectAddress,
abi: projectAbi,
functionName: 'postBid',
args: [proposalHash, bidTermsHash],
});

Using cast (CLI):

FUNDS_ESCROW=$(cast call $MARKET_REGISTRY_ADDRESS "fundsEscrow()(address)" --rpc-url https://testnet-rpc.monad.xyz)

cast send $USDC_ADDRESS "approve(address,uint256)" \
$FUNDS_ESCROW $BID_BOND_AMOUNT \
--rpc-url https://testnet-rpc.monad.xyz \
--private-key $PRIVATE_KEY

cast send $PROJECT_ADDRESS "postBid(bytes32,bytes32)" \
$PROPOSAL_HASH $BID_TERMS_HASH \
--rpc-url https://testnet-rpc.monad.xyz \
--private-key $PRIVATE_KEY