Staking APIs
Authentication

Authentication

  • YGG Staking APIs provide read-only access to staking data via HTTP RESTful APIs.
  • Partners must use HMAC signature verification to ensure data integrity and authenticity.
  • Because it's read-only, so request signature is 1-way only:
    • Request signature is not required
    • Response signature is included in the response headers
    • Response signature is needed to verify data integrity and authenticity

HMAC Signature Verification

🔐

All API responses from YGG include an HMAC signature. Partners must verify this signature to ensure data hasn't been tampered with.

Request Headers (sent by Partner):

HeaderDescriptionExample Value
X-API-KEYPartner API keyyour_api_key
Content-TypeRequest content typeapplication/json

Response Headers (received from YGG):

HeaderDescriptionExample Value
X-API-SIGNATUREHMAC Signature (YGG generated)92aa703cc483ea2cc90488c05e699179...

Important Security Notes

🔑

Keep these secure:

  • SHARED_SECRET_KEY: Provided by YGG, used for HMAC signature generation & verification
  • X-API-KEY: Provided by YGG, shared securely during integration

Key Components

  • X-API-SIGNATURE: HMAC signature generated by YGG, covering method, path, and response body. Any modification to the response invalidates the signature.

Read-Only API with Response Signature

📖

Important: For Staking APIs, YGG generates the signature in the response, and Partners verify it to ensure data integrity.

The Staking API authentication flow:

  1. Partner → YGG: Partner makes request with X-API-KEY
  2. YGG → Partner: YGG returns data with X-API-SIGNATURE in response headers
  3. Partner verifies: Partner verifies the signature to ensure data hasn't been tampered with

This approach ensures:

  • Data integrity: Partner can verify the response came from YGG
  • Tamper detection: Any modification to the response invalidates the signature
  • Read-only security: Appropriate for query operations

HMAC Signature Verification (Partner Side)

When YGG responds to your API request, it includes an X-API-SIGNATURE header. You must verify this signature to ensure the response is authentic and hasn't been tampered with.

Step 1: Extract Response Components

From the YGG API response, extract:

const method = 'GET'  // The HTTP method you used
const path = '/api/staking/snapshots'  // The request path
const responseBody = response.data  // The response JSON
const receivedSignature = response.headers['x-api-signature']  // YGG's signature

Step 2: Create Canonical Payload

Build the same payload that YGG used to generate the signature:

payload = method + path + JSON.stringify(responseBody)

Payload components:

  • HTTP Method: The method you used in the request (GET)
  • Request Path: /api/staking/snapshots
  • Response Body: JSON stringified using standard JSON.stringify()
📝

The response body must be stringified using the standard JSON.stringify() method. No need to sort keys.

Step 3: Verify HMAC Signature

// Generate expected signature using shared secret
const expectedSignature = hmac_sha256(SHARED_SECRET_KEY, payload)
 
// Compare with received signature (use constant-time comparison)
const isValid = constantTimeCompare(expectedSignature, receivedSignature)

Complete Example

Here's a full implementation example showing how to verify YGG's response signature:

const crypto = require('crypto')
 
// Configuration
const API_KEY = 'your_api_key'
const SHARED_SECRET_KEY = 'shared_secret_key_from_ygg'
const API_BASE_URL = 'YGG_API_BASE_URL'
 
function verifyResponseSignature(method, path, responseBody, receivedSignature) {
  // Step 1: Create canonical payload (same as YGG used)
  const payload = method + path + JSON.stringify(responseBody)
  
  // Step 2: Generate expected signature
  const expectedSignature = crypto.createHmac('sha256', SHARED_SECRET_KEY)
                                  .update(payload)
                                  .digest('hex')
  
  // Step 3: Compare signatures (constant-time comparison)
  return crypto.timingSafeEqual(
    Buffer.from(expectedSignature, 'hex'),
    Buffer.from(receivedSignature, 'hex')
  )
}
 
async function getStakeSnapshotsWithVerification(walletAddress, lastDays = 7) {
  const method = 'GET'
  const path = '/api/staking/snapshots'
  const queryParams = `?walletAddress=${walletAddress}&lastDays=${lastDays}`
  
  const response = await axios.get(`${YGG_API_BASE_URL}${path}${queryParams}`, {
    method: method,
    headers: {
      'X-API-KEY': API_KEY,
      'Content-Type': 'application/json'
    }
  })
  
  if (!response.ok) {
    throw new Error(`API request failed: ${response.status}`)
  }
  
  // Get response data and signature
  const responseBody = await response.json()
  const receivedSignature = response.headers.get('x-api-signature')
  
  // ✅ CRITICAL: Verify YGG's signature before using the data
  if (!receivedSignature) {
    throw new Error('Response missing X-API-SIGNATURE header')
  }
  
  const isValid = verifyResponseSignature(
    method,
    path,
    responseBody,
    receivedSignature
  )
  
  if (!isValid) {
    throw new Error('Invalid response signature - data may have been tampered with!')
  }
  
  console.log('✅ Response signature verified successfully')
  return responseBody
}
 
// Usage
const stakingData = await getStakeSnapshotsWithVerification('0x123456...', 7)
console.log(`Average staked: ${stakingData.averageStakedAmount}`)

How It Works

  1. Partner makes request with X-API-KEY header
  2. YGG processes request and generates HMAC signature for the response
  3. YGG returns response with data and X-API-SIGNATURE header
  4. Partner verifies signature using shared secret key to ensure data integrity

Getting Your API Key

⚠️

API keys are provided by YGG during the integration process. Contact the YGG team to receive your production API key.

Development vs Production

You will receive separate API keys for different environments:

  • Development/Staging: For testing and integration
  • Production: For live game environment

Security Best Practices

Protect Your API Key

🔐

Keep your API key secure:

  • Never commit API keys to version control
  • Store keys in environment variables
  • Use secret management services (AWS Secrets Manager, HashiCorp Vault, etc.)
  • Rotate keys periodically

Server-Side Only

⚠️

Never expose API keys in client-side code. Always make API calls from your game server.

Implementation Examples

const crypto = require('crypto')
const axios = require('axios')
 
// Store credentials in environment variables
const YGG_API_KEY = process.env.YGG_API_KEY
const YGG_SHARED_SECRET = process.env.YGG_SHARED_SECRET
const YGG_API_BASE_URL = 'YGG_API_BASE_URL'
 
function verifySignature(method, path, responseBody, receivedSignature) {
  const payload = method + path + JSON.stringify(responseBody)
  const expectedSignature = crypto.createHmac('sha256', YGG_SHARED_SECRET)
                                  .update(payload)
                                  .digest('hex')
  
  // Constant-time comparison to prevent timing attacks
  return crypto.timingSafeEqual(
    Buffer.from(expectedSignature, 'hex'),
    Buffer.from(receivedSignature, 'hex')
  )
}
 
async function getStakeSnapshots(walletAddress, lastDays = 7) {
  try {
    const method = 'GET'
    const path = '/api/staking/snapshots'
    const queryParams = `?walletAddress=${walletAddress}&lastDays=${lastDays}`
    
    const response = await axios.get(`${YGG_API_BASE_URL}${path}${queryParams}`, {
      headers: {
        'X-API-KEY': YGG_API_KEY,
        'Content-Type': 'application/json'
      },
      data: body
    })
    
    // Verify YGG's response signature
    const receivedSignature = response.headers['x-api-signature']
    
    if (!receivedSignature) {
      throw new Error('Response missing signature')
    }
    
    const isValid = verifySignature(method, path, response.data, receivedSignature)
    
    if (!isValid) {
      throw new Error('Invalid response signature!')
    }
    
    console.log('✅ Signature verified')
    return response.data
  } catch (error) {
    console.error('Failed to fetch staking data:', error)
    throw error
  }
}

Error Responses

When authentication fails, you'll receive an error response:

401 Unauthorized - Missing or Invalid API Key

{
  "success": false,
  "code": "UNAUTHORIZED",
  "message": "Invalid or missing API key"
}

Common causes:

  • Incorrect shared secret key
  • Payload components in wrong order
  • Wrong HTTP method used in signature
  • Incorrect path in signature

403 Forbidden

Valid API key but insufficient permissions:

{
  "success": false,
  "code": "FORBIDDEN",
  "message": "Access denied"
}

Debugging HMAC Signature Verification

🔍

If signature verification is failing, follow these steps to debug:

1. Verify Response Components

Print each component extracted from YGG's response:

console.log('Method:', method)                          // Should be: GET
console.log('Path:', path)                              // Should be: /api/staking/snapshots
console.log('Response Body:', responseBody)             // Full JSON response
console.log('Received Signature:', receivedSignature)   // From X-API-SIGNATURE header
console.log('Body (stringified):', JSON.stringify(responseBody))

2. Check JSON Stringification

Ensure the response body is stringified correctly:

// ✅ Correct - use standard JSON.stringify
JSON.stringify(responseBody)
// Output: {"walletAddress":"0x123","averageStakedAmount":285.71,...}

3. Verify Shared Secret

Make sure you're using the correct shared secret provided by YGG:

// The shared secret should match exactly what YGG provided
console.log('Using shared secret:', SHARED_SECRET_KEY)

4. Test Signature Verification

Create a test case with known values:

const method = 'GET'
const path = '/api/staking/snapshots'
const responseBody = {
  "averageStakedAmount": 285.71,
  "requestedAt": "2025-11-08T08:05:00Z",
  // ... rest of response
}
const bodyStr = JSON.stringify(responseBody)
 
const payload = method + path + bodyStr
console.log('Payload for verification:', payload)
 
const expectedSignature = crypto.createHmac('sha256', SHARED_SECRET_KEY)
                                .update(payload)
                                .digest('hex')
console.log('Expected Signature:', expectedSignature)
console.log('Received Signature:', receivedSignature)
console.log('Match:', expectedSignature === receivedSignature)

5. Common Mistakes

IssueProblemSolution
Wrong methodUsing 'POST' but should be 'GET'Use the same method as your request
Wrong pathPath includes query params or hostUse path only: /api/staking/snapshots
Wrong encodingUsing base64 instead of hexUse .digest('hex') not .digest('base64')
Missing headersResponse headers not capturedEnsure you capture response headers

Implementation Checklist

When implementing HMAC signature verification, ensure you:

Security & Credentials

  • Store API keys in environment variables or secret management service
  • Store shared secret key securely (never commit to version control)
  • Never expose API keys or secrets in client-side code
  • Make all API calls from your server
  • Use separate keys for development and production

Request Headers

  • Include X-API-KEY header in all requests
  • Include Content-Type: application/json header

Response Verification

  • Extract X-API-SIGNATURE from YGG response headers
  • Create canonical payload: method + path + JSON.stringify(responseBody)
  • Use standard JSON.stringify() for body serialization
  • Generate expected HMAC SHA256 signature using shared secret
  • Compare expected signature with received signature
  • Reject response if signature verification fails

Error Handling

  • Handle authentication errors gracefully
  • Log signature verification failures for debugging
📖

Important: For Staking APIs, YGG generates the signature in the response, and you verify it to ensure data integrity and authenticity.