Docs
Dashboard

Fund a Gully Chain address

A Gully-operated onramp: a customer pays USD through the hosted checkout and Gully Pay sends the equivalent ETH or USDC.e on Gully Chain Testnet (chainId 485590) to the address they chose. There is no merchant; the payment lands on the Gully house store and the payout comes from a Gully hot wallet.

Feature flag

Behind FUND_ENABLED (default off) and GULLY_CHAIN_ENABLED. While off, GET /fund/config returns enabled: false and POST /fund/requests answers 503. Card payment is offered only when Stripe is live (USE_STRIPE_MOCK=false); otherwise the checkout is crypto-only.

Apps (Gully Street) link to the hosted page:

URL
https://gullypay.xyz/fund?network=gully-chain-testnet&to=0xYourAddress&amount=10&asset=ETH
QueryRequiredDescription
networkYesgully-chain-testnet
toYesEVM address that receives the funds
amountYesUSD the customer pays (2 decimals)
assetNoETH (default) or USDC.e

The page shows the limits and an indicative rate, creates the request, and sends the customer to the hosted checkout. The checkout returns to /fund/<requestId>, which polls the status.

Endpoints

GET/fund/configFlag, assets, limits, payment methods, indicative ETH/USD
POST/fund/requestsCreate a request and its checkout session
GET/fund/requests/:idStatus of a request (public, unguessable id)
POST /fund/requests
{ "network": "gully-chain-testnet", "to": "0x…", "asset": "ETH", "amountUsd": 10 }
Response
{
"id": "6f1c…",
"status": "created",
"network": "gully-chain-testnet",
"to": "0x…",
"asset": "ETH",
"usd": 10,
"payoutAmount": null,
"payoutTxHash": null,
"checkoutUrl": "https://gullypay.xyz/checkout/<sessionId>",
"sessionExpiresAt": "2026-10-25T19:30:00.000Z"
}

States

StatusMeaning
createdWaiting for the checkout payment
expiredNever paid; nothing to do
paidPayment confirmed; payout queued
payout_pendingA worker holds the request and is quoting/signing
payout_sentSigned and broadcast; payoutTxHash set, waiting for a receipt
confirmedPayout mined successfully
failedPayout reverted or was lost (transient, becomes refund_needed)
refund_neededStaff must refund the customer (see the admin panel)
refundedStaff recorded the refund

Every paid request ends in confirmed or refund_needed: a stale price, an under-funded hot wallet or an RPC error is retried with backoff (up to FUND_MAX_PAYOUT_ATTEMPTS), then surfaced to staff. The payout hash is written before the transaction is broadcast, so a crash can never produce a second payout; an unmined payout is re-broadcast and, after FUND_PAYOUT_TIMEOUT_MIN, flagged for review while the chain is still watched for it.

Pricing

FUND_PRICE_SOURCE selects the ETH/USD source: redstone (RedStone public API, default), onchain (the RedStone adapter deployed on Gully Chain, FUND_ETH_USD_AGGREGATOR) or fixed (FUND_FIXED_ETH_USD, accepted on testnets only). A quote older than FUND_PRICE_MAX_AGE_SEC is rejected and the payout waits. USDC.e is always 1 USD. Amounts are computed in integer math and stored as base-unit strings.

Limits and the hot wallet

FUND_MIN_USD, FUND_MAX_USD_PER_REQUEST, FUND_MAX_USD_PER_ADDRESS_PER_DAY and FUND_MAX_USD_PER_DAY are checked when the request is created and again before the payout. The hot wallet key is a file secret (FUND_HOT_WALLET_KEY_FILE) that must derive FUND_HOT_WALLET_ADDRESS; a payout never takes the native balance below FUND_HOT_WALLET_MIN_RESERVE_ETH, and the admin panel warns above FUND_HOT_WALLET_MAX_BALANCE_ETH.

Admin

The admin panel's Fund requests section lists every request with its state, attempts and last error, the hot wallet balance, and two actions on refund_needed rows: Retry (only when no payout transaction was signed) and Mark refunded (records the refund transaction).

GET/admin/fund-requests/summaryCounts, flag state, hot wallet balance
GET/admin/fund-requests?status=List requests
POST/admin/fund-requests/:id/retryQueue another payout attempt
POST/admin/fund-requests/:id/mark-refundedRecord a manual refund