Перейти к содержанию

Faucet Endpoints (Development)

Аудитория: разработчики, QA инженеры, интеграционные тестировщики Последнее обновление: 2025-11-17 Краткое содержание: Детальная документация Faucet endpoints для development окружения — request test USDC, check faucet status, monitor balance. Полное руководство по интеграции для тестирования и разработки.

⚠️ ВАЖНО: Faucet endpoints доступны ТОЛЬКО в development окружении. В production они отключены.


Обзор Endpoints

Метод Endpoint Описание Auth Required Rate Limit
POST /api/faucet/request Запрос test USDC ❌ Нет 1 req/24ч на адрес
GET /api/faucet/status Конфигурация faucet ❌ Нет 100 req/час
GET /api/faucet/balance Баланс faucet контракта ❌ Нет 100 req/час
GET /api/faucet/history История распределения ❌ Нет 100 req/час

💧 POST /api/faucet/request

Запросить test USDC токены для development и тестирования.

Запрос

POST /api/faucet/request
Content-Type: application/json

{
  "walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
  "amount": "1000.00",
  "reason": "Testing investment flow"
}

Схема тела запроса:

Поле Тип Обязательно Описание
walletAddress string ✅ Да Адрес кошелька получателя (0x..., 42 chars)
amount string ❌ Нет Запрашиваемая сумма (по умолчанию: 1000 USDC, макс: 10000)
reason string ❌ Нет Причина запроса (для логирования)

Правила валидации:

  • Адрес кошелька должен быть валидным Ethereum адресом (checksummed)
  • Сумма должна быть ≥ 100 USDC и ≤ 10000 USDC
  • Один запрос на кошелёк за 24 часа
  • Faucet должен иметь достаточный баланс

Ответ

Успех (200 OK):

{
  "success": true,
  "data": {
    "transactionHash": "0xabcd1234...",
    "walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
    "amount": "1000.00",
    "currency": "USDC",
    "status": "completed",
    "blockNumber": 12345678,
    "confirmations": 3,
    "gasUsed": "50000",
    "gasCost": "0.00125 ETH",
    "timestamp": "2025-10-03T12:34:56Z",
    "nextAllowedRequest": "2025-10-04T12:34:56Z",
    "explorerUrl": "https://etherscan.io/tx/0xabcd1234..."
  },
  "timestamp": "2025-10-03T12:34:56Z"
}

Поля ответа:

Поле Тип Описание
data.transactionHash string On-chain transaction hash
data.amount string Фактически выданная сумма (SafeDecimal)
data.status string Статус: pending, completed, failed
data.blockNumber number Блок где транзакция была майнена
data.confirmations number Количество подтверждений
data.nextAllowedRequest string Timestamp когда разрешен следующий запрос
data.explorerUrl string URL blockchain explorer

Превышен лимит (429 Too Many Requests):

{
  "success": false,
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Faucet can only be used once per 24 hours per address",
    "details": {
      "lastRequest": "2025-10-03T10:00:00Z",
      "nextAllowedRequest": "2025-10-04T10:00:00Z",
      "remainingTime": "22 hours 34 minutes"
    }
  },
  "timestamp": "2025-10-03T12:34:56Z"
}

Недостаточный баланс (422 Unprocessable Entity):

{
  "success": false,
  "error": {
    "code": "INSUFFICIENT_FAUCET_BALANCE",
    "message": "Faucet has insufficient balance for this request",
    "details": {
      "requested": "1000.00",
      "available": "500.00",
      "suggestion": "Request a smaller amount or contact support to refill faucet"
    }
  }
}

Ошибки:

Код статуса Код ошибки Описание
400 INVALID_WALLET_ADDRESS Невалидный формат адреса кошелька
400 INVALID_AMOUNT Сумма вне разрешенного диапазона (100-10000)
422 INSUFFICIENT_FAUCET_BALANCE Слишком низкий баланс faucet
429 RATE_LIMIT_EXCEEDED Слишком много запросов (1 за 24ч)
503 BLOCKCHAIN_ERROR Blockchain транзакция провалилась
503 FAUCET_DISABLED Faucet отключен (production окружение)

Пример cURL

# Запрос test USDC
curl -X POST https://app.saga.surf/api/faucet/request \
  -H "Content-Type: application/json" \
  -d '{
    "walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
    "amount": "1000.00",
    "reason": "Testing investment flow"
  }'

# Запрос суммы по умолчанию (1000 USDC)
curl -X POST https://app.saga.surf/api/faucet/request \
  -H "Content-Type: application/json" \
  -d '{
    "walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0"
  }'

Пример TypeScript

const requestTestUSDC = async (
  walletAddress: string,
  amount: string = '1000.00',
  reason?: string
) => {
  try {
    const response = await fetch('/api/faucet/request', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        walletAddress,
        amount,
        reason
      })
    });

    if (!response.ok) {
      const error = await response.json();

      if (error.error.code === 'RATE_LIMIT_EXCEEDED') {
        console.log('⏱️ Rate limit exceeded');
        console.log(`  Next request: ${error.error.details.nextAllowedRequest}`);
        console.log(`  Wait: ${error.error.details.remainingTime}`);
        return null;
      }

      throw new Error(error.error.message);
    }

    const { data } = await response.json();

    console.log('💧 Test USDC Received:');
    console.log(`  Amount: $${data.amount}`);
    console.log(`  TX Hash: ${data.transactionHash}`);
    console.log(`  Status: ${data.status}`);
    console.log(`  Confirmations: ${data.confirmations}`);
    console.log(`  Gas Used: ${data.gasUsed} (${data.gasCost})`);
    console.log(`\n🔗 Explorer: ${data.explorerUrl}`);
    console.log(`⏰ Next Request: ${data.nextAllowedRequest}`);

    return data;
  } catch (error) {
    console.error('❌ Faucet request failed:', error.message);
    throw error;
  }
};

// Usage
const wallet = await ethereum.request({ method: 'eth_requestAccounts' });
await requestTestUSDC(wallet[0], '1000.00', 'Testing deposits');

Пример React Hook

import { useState } from 'react';
import { useAccount } from 'wagmi';

interface FaucetResponse {
  transactionHash: string;
  amount: string;
  nextAllowedRequest: string;
  explorerUrl: string;
}

export const useFaucet = () => {
  const { address } = useAccount();
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [lastRequest, setLastRequest] = useState<FaucetResponse | null>(null);

  const requestUSDC = async (amount: string = '1000.00') => {
    if (!address) {
      setError('Wallet not connected');
      return null;
    }

    setLoading(true);
    setError(null);

    try {
      const response = await fetch('/api/faucet/request', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          walletAddress: address,
          amount,
          reason: 'Testing from dApp'
        })
      });

      if (!response.ok) {
        const errorData = await response.json();
        throw new Error(errorData.error.message);
      }

      const { data } = await response.json();
      setLastRequest(data);
      return data;
    } catch (err) {
      setError(err.message);
      return null;
    } finally {
      setLoading(false);
    }
  };

  return { requestUSDC, loading, error, lastRequest };
};

// Component usage
const FaucetButton = () => {
  const { requestUSDC, loading, error, lastRequest } = useFaucet();

  return (
    <div>
      <button onClick={() => requestUSDC('1000.00')} disabled={loading}>
        {loading ? 'Requesting...' : 'Get Test USDC'}
      </button>
      {error && <p className="error">{error}</p>}
      {lastRequest && (
        <div>
          <p> Received ${lastRequest.amount}</p>
          <a href={lastRequest.explorerUrl} target="_blank">View TX</a>
        </div>
      )}
    </div>
  );
};

⚙️ GET /api/faucet/status

Получить конфигурацию faucet и текущий статус.

Запрос

GET /api/faucet/status

Аутентификация не требуется.

Ответ

Успех (200 OK):

{
  "success": true,
  "data": {
    "enabled": true,
    "environment": "development",
    "configuration": {
      "usdcContractAddress": "0x5FbDB2315678afecb367f032d93F642f64180aa3",
      "defaultAmount": "1000.00",
      "minAmount": "100.00",
      "maxAmount": "10000.00",
      "rateLimitPeriod": "24h",
      "maxRequestsPerAddress": 1
    },
    "balance": {
      "usdc": "500000.00",
      "eth": "10.5",
      "lowBalanceThreshold": "10000.00",
      "needsRefill": false
    },
    "statistics": {
      "totalDistributed": "125000.00",
      "totalRequests": 125,
      "uniqueAddresses": 87,
      "lastDistribution": "2025-10-03T12:30:00Z",
      "averageRequestAmount": "1000.00"
    },
    "limits": {
      "dailyLimit": "50000.00",
      "distributedToday": "12000.00",
      "remainingToday": "38000.00"
    },
    "network": {
      "chainId": 1337,
      "blockNumber": 12345678,
      "gasPrice": "25 gwei"
    }
  },
  "timestamp": "2025-10-03T12:34:56Z"
}

Поля ответа:

Поле Тип Описание
data.enabled boolean Статус включенности faucet
data.configuration object Настройки конфигурации faucet
data.balance object Текущие балансы faucet
data.balance.needsRefill boolean Флаг ниже порога
data.statistics object Статистика использования
data.limits object Дневные лимиты распределения
data.network object Информация о blockchain сети

Отключен (503 Service Unavailable):

{
  "success": false,
  "error": {
    "code": "FAUCET_DISABLED",
    "message": "Faucet is disabled in production environment",
    "details": {
      "environment": "production",
      "reason": "Faucet only available in development"
    }
  }
}

Пример cURL

# Получить статус faucet
curl -X GET https://app.saga.surf/api/faucet/status

# Проверить нужно ли пополнение
curl -s https://app.saga.surf/api/faucet/status | jq '.data.balance.needsRefill'

Пример TypeScript

const checkFaucetStatus = async () => {
  const response = await fetch('/api/faucet/status');

  if (!response.ok) {
    const error = await response.json();
    console.log('❌ Faucet unavailable:', error.error.message);
    return null;
  }

  const { data } = await response.json();

  console.log('💧 Faucet Status:');
  console.log(`  Enabled: ${data.enabled ? '✅' : '❌'}`);
  console.log(`  Environment: ${data.environment}`);

  console.log('\n💰 Balance:');
  console.log(`  USDC: $${data.balance.usdc}`);
  console.log(`  ETH: ${data.balance.eth} ETH`);
  console.log(`  Needs Refill: ${data.balance.needsRefill ? '⚠️ YES' : '✅ NO'}`);

  console.log('\n📊 Configuration:');
  console.log(`  Default Amount: $${data.configuration.defaultAmount}`);
  console.log(`  Range: $${data.configuration.minAmount} - $${data.configuration.maxAmount}`);
  console.log(`  Rate Limit: ${data.configuration.maxRequestsPerAddress} per ${data.configuration.rateLimitPeriod}`);

  console.log('\n📈 Statistics:');
  console.log(`  Total Distributed: $${data.statistics.totalDistributed}`);
  console.log(`  Total Requests: ${data.statistics.totalRequests}`);
  console.log(`  Unique Addresses: ${data.statistics.uniqueAddresses}`);

  console.log('\n📊 Daily Limits:');
  console.log(`  Limit: $${data.limits.dailyLimit}`);
  console.log(`  Distributed: $${data.limits.distributedToday}`);
  console.log(`  Remaining: $${data.limits.remainingToday}`);

  return data;
};

💵 GET /api/faucet/balance

Получить текущий баланс faucet контракта.

Запрос

GET /api/faucet/balance

Аутентификация не требуется.

Ответ

Успех (200 OK):

{
  "success": true,
  "data": {
    "contractAddress": "0x5FbDB2315678afecb367f032d93F642f64180aa3",
    "balances": {
      "usdc": {
        "balance": "500000.00",
        "decimals": 6,
        "symbol": "USDC"
      },
      "eth": {
        "balance": "10.5",
        "decimals": 18,
        "symbol": "ETH"
      }
    },
    "thresholds": {
      "usdc": {
        "low": "10000.00",
        "critical": "1000.00",
        "status": "healthy"
      },
      "eth": {
        "low": "1.0",
        "critical": "0.1",
        "status": "healthy"
      }
    },
    "lastRefill": {
      "usdc": {
        "amount": "100000.00",
        "timestamp": "2025-10-01T10:00:00Z",
        "txHash": "0xrefill1234..."
      }
    },
    "estimatedRemaining": {
      "requests": 500,
      "days": 20
    }
  },
  "timestamp": "2025-10-03T12:34:56Z"
}

Поля ответа:

Поле Тип Описание
data.balances object Breakdown балансов токенов
data.thresholds object Пороги балансов и статус
data.thresholds.*.status string Статус: healthy, low, critical
data.lastRefill object Информация о последнем пополнении
data.estimatedRemaining object Оценка оставшейся ёмкости

Пример cURL

# Получить баланс faucet
curl -X GET https://app.saga.surf/api/faucet/balance

# Мониторить баланс USDC
watch -n 10 'curl -s https://app.saga.surf/api/faucet/balance | jq .data.balances.usdc.balance'

Пример TypeScript

const checkFaucetBalance = async () => {
  const response = await fetch('/api/faucet/balance');
  const { data } = await response.json();

  console.log('💵 Faucet Balance:');
  console.log(`  Contract: ${data.contractAddress}`);

  console.log('\n💰 Balances:');
  Object.entries(data.balances).forEach(([token, info]: [string, any]) => {
    const status = data.thresholds[token].status;
    const icon = status === 'healthy' ? '✅' : status === 'low' ? '⚠️' : '🚨';
    console.log(`  ${icon} ${info.symbol}: ${info.balance} (${status})`);
  });

  console.log('\n📊 Thresholds:');
  Object.entries(data.thresholds).forEach(([token, threshold]: [string, any]) => {
    console.log(`  ${token.toUpperCase()}:`);
    console.log(`    Low: ${threshold.low}`);
    console.log(`    Critical: ${threshold.critical}`);
  });

  if (data.lastRefill.usdc) {
    console.log('\n🔄 Last Refill:');
    console.log(`  Amount: $${data.lastRefill.usdc.amount}`);
    console.log(`  Time: ${data.lastRefill.usdc.timestamp}`);
    console.log(`  TX: ${data.lastRefill.usdc.txHash}`);
  }

  console.log('\n⏱️ Estimated Remaining:');
  console.log(`  Requests: ~${data.estimatedRemaining.requests}`);
  console.log(`  Days: ~${data.estimatedRemaining.days}`);

  return data;
};

📜 GET /api/faucet/history

Получить историю распределения faucet.

Запрос

GET /api/faucet/history?limit=50&walletAddress=0x742d35...

Query параметры:

Параметр Тип Обязательно Описание По умолчанию
limit number ❌ Нет Количество записей (макс: 100) 50
walletAddress string ❌ Нет Фильтр по адресу кошелька Все
startDate string ❌ Нет Фильтр от даты (ISO 8601) -
endDate string ❌ Нет Фильтр до даты (ISO 8601) -

Ответ

Успех (200 OK):

{
  "success": true,
  "data": [
    {
      "id": "faucet_req_001",
      "walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
      "amount": "1000.00",
      "transactionHash": "0xabcd1234...",
      "status": "completed",
      "requestedAt": "2025-10-03T12:00:00Z",
      "completedAt": "2025-10-03T12:00:15Z",
      "blockNumber": 12345678,
      "gasUsed": "50000",
      "reason": "Testing investment flow"
    },
    {
      "id": "faucet_req_002",
      "walletAddress": "0x8ba1f109551bD432803012645Ac136ddd64DBA72",
      "amount": "500.00",
      "transactionHash": "0xef567890...",
      "status": "completed",
      "requestedAt": "2025-10-03T11:30:00Z",
      "completedAt": "2025-10-03T11:30:20Z",
      "blockNumber": 12345600,
      "gasUsed": "50000"
    }
  ],
  "pagination": {
    "limit": 50,
    "total": 125,
    "hasMore": true
  },
  "timestamp": "2025-10-03T12:34:56Z"
}

Пример cURL

# Получить недавнюю историю
curl -X GET https://app.saga.surf/api/faucet/history?limit=10

# Фильтр по кошельку
curl -X GET "https://app.saga.surf/api/faucet/history?walletAddress=0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0"

Связанная документация

Публичные Endpoints:

Разработка:




📋 Метаданные

Версия: 2.6.268

Обновлено: 2025-10-21

Статус: Published