Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.peaq.xyz/llms.txt

Use this file to discover all available pages before exploring further.

Gas Station is a 2FA-gated faucet that funds fresh machine wallets on peaq chain so they can bond and register.

Why Gas Station exists

A newly generated machine keypair has zero balance. Before it can register an identity (which requires bonding 1 PEAQ), it needs native tokens for gas. Gas Station solves this bootstrap problem: the owner enrolls in 2FA once, then funds each machine wallet with a single SDK call.

2FA setup flow

1

Initiate 2FA enrollment

Call setupFaucet2FA with the owner’s address. The faucet returns an otpauthUri for manual entry and a qrImageUrl for scanning.
const setup = await client.setupFaucet2FA(
  ownerAddress,
  "https://depinstation.peaq.network"
);
console.log(setup);
// setup.otpauthUri  : paste into authenticator app
// setup.qrImageUrl  : render immediately, expires in ~2 minutes
2

Scan QR code with authenticator app

Open any TOTP-compatible authenticator (Google Authenticator, Authy, 1Password). Scan the QR code or manually enter the otpauthUri. The QR image expires after approximately 2 minutes.
3

Confirm 2FA activation

Submit the current TOTP code from the authenticator app to activate 2FA for this owner address.
await client.confirmFaucet2FA(
  ownerAddress,
  "https://depinstation.peaq.network",
  "123456" // TOTP code from authenticator
);

Funding a machine wallet

After 2FA is active, fund any machine wallet with fundFromGasStation. The response is a discriminated union: either success (transfer landed) or skipped (wallet already funded).
const result = await client.fundFromGasStation(
  {
    ownerAddress: "0xOwner...",
    targetWalletAddress: machineAddress,
    chainId: "peaq",
    twoFactorCode: "654321",
  },
  "https://depinstation.peaq.network"
);

if (result.status === "success") {
  console.log("Funded:", result.txHash, result.fundedAmount);
} else {
  // status === "skipped"
  console.log("Already funded:", result.currentBalance);
}

Success response

JS (camelCase). The SDK retains the idempotency key on the response so callers can persist it for retry recovery:
FieldTypeDescription
status"success"Transfer completed
requestIdstringIdempotency key (echoed from request, or SDK-generated UUID)
txHash0x-prefixed hexTransaction hash of the funding transfer
fundedAmountstring (decimal wei)Amount transferred
Python (snake_case). The SDK does not echo request_id on the response. If you need it for retry recovery, pass it in explicitly and hold the value yourself:
FieldTypeDescription
status"success"Transfer completed
tx_hash0x-prefixed hexTransaction hash of the funding transfer
funded_amountstring (decimal wei)Amount transferred

Skipped response

JS (camelCase):
FieldTypeDescription
status"skipped"Wallet already has sufficient balance
requestIdstringIdempotency key
currentBalancestring (decimal wei)Target wallet’s current balance
minGasBalancestring (decimal wei)Faucet-configured minimum threshold
Python (snake_case):
FieldTypeDescription
status"skipped"Wallet already has sufficient balance
current_balancestring (decimal wei)Target wallet’s current balance
min_gas_balancestring (decimal wei)Faucet-configured minimum threshold

Rate limits and caps

Gas Station enforces three independent throttles to prevent abuse, plus a per-chain minimum balance threshold that decides whether a funding request transfers anything at all:
LimitScopeSurfaces asDescription
Rate limitPer ownerRATE_LIMITEDToo many funding requests in too short a window. Back off and retry.
Daily funding capPer ownerCAP_EXCEEDED_OWNERTotal funded amount per owner per day.
Daily funding capPer walletCAP_EXCEEDED_WALLETTotal funded amount per target wallet per day.
Minimum gas balancePer chainskipped responseWhen the target wallet already holds at least this much, the faucet returns skipped with currentBalance/current_balance and minGasBalance/min_gas_balance instead of transferring.
Exact cap values, rate-limit windows, and the minimum gas balance are configured server-side and are subject to change. Branch on the error codes (or the skipped status) rather than hard-coding numbers.

Error codes

Gas Station defines 20 error codes. Not every endpoint can raise every code; the subset depends on what the endpoint actually does.

POST /2fa/setup: setupFaucet2FA / setup_faucet_2fa

CodeMeaning
INVALID_OWNER_ADDRESSOwner address failed server-side format validation (setup-only).
INVALID_PAYLOADRequest body did not match the spec (e.g. unknown format).
QR_GENERATION_FAILEDThe faucet failed to render the QR image (setup-only).
INTERNAL_ERRORUnhandled server-side failure.

POST /2fa/confirm: confirmFaucet2FA / confirm_faucet_2fa

CodeMeaning
INVALID_2FATOTP code did not match the secret.
2FA_NOT_CONFIGUREDOwner never completed setup. Call setupFaucet2FA first.
2FA_LOCKEDOwner locked after too many invalid attempts.
INTERNAL_ERRORUnhandled server-side failure.

POST /faucet/fund: fundFromGasStation / fund_from_gas_station

CodeMeaning
INVALID_2FATOTP code did not match.
2FA_NOT_CONFIGUREDOwner never completed setup.
2FA_NOT_ACTIVESetup done but never confirmed. Call confirmFaucet2FA first.
2FA_LOCKEDOwner locked after too many invalid attempts.
DUPLICATE_REQUESTA request with this requestId is still in flight.
REQUEST_ALREADY_PROCESSEDThis requestId has already completed.
RATE_LIMITEDFaucet rate limit exceeded.
CAP_EXCEEDED_OWNEROwner daily funding cap exceeded.
CAP_EXCEEDED_WALLETWallet daily funding cap exceeded.
INVALID_PAYLOADRequest body validation failed server-side.
INVALID_OWNER_ADDRESSMalformed ownerAddress.
INVALID_TARGET_ADDRESSMalformed targetWalletAddress.
INVALID_CHAIN_IDUnsupported chainId.
INVALID_REQUEST_IDThe supplied requestId was not a valid UUID.
TRANSFER_FAILEDOn-chain transfer failed.
CHAIN_RPC_ERRORFaucet’s upstream RPC provider failed.
QR_NOT_FOUNDQR token does not exist (rare; surfaces when a stale QR is referenced).
QR_EXPIREDQR token has expired.
INTERNAL_ERRORUnhandled server-side failure. The SDK also raises this code locally for non-JSON bodies and unexpected envelope shapes.
For SDK-owned message strings and HTTP status codes per code, see errors.