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