Developer Hub
🔮 For applications
Guides & Tutorials
How to get Azuro Waves leaderboard

Get Azuro Waves Leaderboard

If you're not familiar with the basics of Azuro Waves, read about it here.

Off-waves periods

💡

There may be periods between waves when one Wave is over and the new one hasn't started yet, so there is no active wave at the moment. These gaps can last for hours, couple of days, or even a week.

In this case, the API will return:

  • wave: null (/waves/active)
  • 404 status for sub-paths (e.g. /waves/active/leaderboard).

Account for this in your UI. The Guidelines PDF includes a recommendation for the User Widget UI in this case.

UI Guidelines

Please follow the Azuro Waves Guidelines in your UI.

Azuro Waves Status Icon

Here's an example of an SVG icon with Waves Level gradient fills from the Guidelines.

<svg class="royal" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28">
  <style>
    .grey { fill: #C4CFE4; }
    .mist { fill: #A5D0E6; }
    .sky { fill: url(#sky); }
    .blue { fill: url(#blue); }
    .ultramarine { fill: url(#ultramarine); }
    .bright { fill: url(#bright); }
    .brilliant { fill: url(#brilliant); }
    .royal { fill: url(#royal); }
  </style>
  <circle cx="14" cy="14" r="14" fill="#fff"/>
  <path fill="inherit" fill-rule="evenodd" d="M24.3244 20.596C22.1475 23.9962 18.337 26.25 14 26.25 7.2345 26.25 1.75 20.7655 1.75 14S7.2345 1.75 14 1.75 26.25 7.2345 26.25 14c0 .7921-.0752 1.5667-.2188 2.317-1.4192-.2859-2.7104-1.1436-3.5393-2.3288-.351-.5021-.679-1.0222-1.0072-1.5427-.5745-.911-1.1496-1.8231-1.8501-2.6414-.9351-1.0927-2.1246-1.948-3.5315-2.302-.8169-.2055-1.6484-.2803-2.4889-.2811-2.7992-.0453-4.5898 1.5722-4.9935 1.9567a9.1596 9.1596 0 0 0-.0177.0166c-1.3776 1.2895-2.0763 2.8945-2.0763 4.7707 0 1.8762.6975 3.4857 2.073 4.7897l.0074.007c1.3941 1.2879 3.1177 1.9411 5.1221 1.9411 2.0043 0 3.7275-.6528 5.122-1.9411l.0037-.0037c.3228-.3018.6081-.6206.8568-.9549 1.0504 1.2352 2.3337 2.2916 3.9295 2.6637.227.0529.4548.0955.6832.1292Zm-10.6675-8.9778c.4022.0069.7416.0623 1.0425.1624.2906.1086.5515.2719.786.4914.4713.4411.7008.9951.7008 1.693 0 .6979-.229 1.2519-.7008 1.693h.0004c-.4704.4403-1.0451.6545-1.7566.6545-.7116 0-1.2829-.2129-1.753-.6512-.4577-.4415-.6805-.9964-.6805-1.6963 0-.7.2224-1.2552.6801-1.6964.4541-.4233 1.0046-.6364 1.6811-.6504Z" clip-rule="evenodd"/>
  <defs>
    <linearGradient id="sky" gradientUnits="userSpaceOnUse">
      <stop stop-color="#94D3F3" />
      <stop stop-color="#83D5FF" offset="100%" />
    </linearGradient>
    <linearGradient id="blue" gradientUnits="userSpaceOnUse">
      <stop stop-color="#83D5FF" />
      <stop stop-color="#64CAFF" offset="100%" />
    </linearGradient>
    <linearGradient id="ultramarine" gradientUnits="userSpaceOnUse">
      <stop stop-color="#64CAFF" />
      <stop stop-color="#05AAFF" offset="100%" />
    </linearGradient>
    <linearGradient id="bright" gradientUnits="userSpaceOnUse">
      <stop stop-color="#05AAFF" />
      <stop stop-color="#3499FE" offset="100%" />
    </linearGradient>
    <linearGradient id="brilliant" gradientUnits="userSpaceOnUse">
      <stop stop-color="#3499FE" />
      <stop stop-color="#3D67FF" offset="100%" />
    </linearGradient>
    <linearGradient id="royal" gradientUnits="userSpaceOnUse">
      <stop stop-color="#3D67FF" />
      <stop stop-color="#022E87" offset="100%" />
    </linearGradient>
  </defs>
</svg>

Common types

There are common types that are reused in the code examples below.

export enum WavesLevelName {
  Grey = 'Grey',
  Mist = 'Mist',
  Sky = 'Sky',
  Blue = 'Blue',
  Ultramarine = 'Ultramarine',
  Bright = 'Bright',
  Brilliant = 'Brilliant',
  Royal = 'Royal',
}
 
export type WavesLevelDescription = {
  level: number
  name: WavesLevelName
  /**  e.g. "1.4" */
  boost: `${number}`
  /**  e.g. "50000" */
  pointsNeeded: `${number}`
  comment: string
}
 
export type WavesParticipant = {
  address: `0x${string}`
  waveId: number
  levelActivated: boolean
  initialLevel: number
  level: number
  levelDescription: WavesLevelDescription
 
  /** "2.100000", total user points without level multiplier */
  points: `${number}`
  /** "2.100000", final points with level multiplier ('boost') */
  multipliedPoints: `${number}`
}

Base API URL

To work with Azuro Waves data, we'll use the public API. The base URL is:

const waveId: number | 'active' = 'active'
const baseUrl = `https://api.azuro.org/api/v1/public/waves/${waveId}`
 
// waveId
// e.g.
// https://api.azuro.org/api/v1/public/waves/1
// https://api.azuro.org/api/v1/public/waves/active

We recommend using the active key for the waveId parameter to retrieve current wave data and keep it up-to-date always.

Get User Stats

Please follow the UI Guidelines to display a widget with user Waves stats in your UI.

const fetchAzuroWavesUserStats = async (userAddress: `0x${string}`) => {
  const url = `https://api.azuro.org/api/v1/public/waves/active/participants/${userAddress.toLowerCase()}/stats`
  const response = await fetch(url)
 
  // 404 means user not found or there is no active wave at this moment
  if (response.status === 404) {
    return null
  }
 
  if (!response.ok) {
    throw new Error(`Status ${response.status}: ${response.statusText}`)
  }
 
  const result: StatsResponse = await response.json()
 
  return result
}
 
type StatsResponse = WavesParticipant

In WavesParticipant, you will see two points fields:

  • points: Base ("clear") points used to reach the next level (WavesLevelDescription['pointsNeeded']).
  • multipliedPoints: Total points shown in the Total Leaderboard.

Activate User Levels

All users start at the Grey level, even if they have enough points for Royal. To activate levels, users must perform an action once per wave (in Azuro UI, this is a click on a button titled "Enhance").

This action calls an API endpoint:

const activateAzuroWavesUser = async (userAddress: `0x${string}`) => {
  const url = `https://api.azuro.org/api/v1/public/waves/active/participants/${userAddress.toLowerCase()}/activate`
  // GET method, not POST
  const response = await fetch(url)
  const result: ActivateResponse = await response.json()
 
  return result
}
 
enum ParticipantActivationResult {
  Activated = 'Activated',
  AlreadyActivated = 'AlreadyActivated',
  InvalidParticipantAddress = 'InvalidParticipantAddress',
  ConcurrencyLockFailed = 'ConcurrencyLockFailed',
  UnknownError = 'UnknownError',
}
 
type ActivateResponse = {
  participant: WavesParticipant
  result: ParticipantActivationResult
}

The WavesParticipant field levelActivated indicates whether the user has activated their levels.

Fetch Total Leaderboard

Please use the color scheme for levels outlined in the UI Guidelines in your UI.

/**
 * Returns the top 10 Participants, sorted by their position.
 *
 * If `userAddress` is provided and the user exists in the leaderboard (has any score)
 * but their position isn't in the top 10,
 * they will be added to the end of the list with their actual position.
 */
const fetchAzuroWavesTotalLeaderboard = async (userAddress?: `0x${string}`) => {
  const url = userAddress
    ? `https://api.azuro.org/api/v1/public/waves/active/leaderboard?address=${userAddress.toLowerCase()}`
    : 'https://api.azuro.org/api/v1/public/waves/active/leaderboard'
 
  const response = await fetch(url)
 
  // 404 means there is no active wave at this moment
  if (response.status === 404) {
    return null
  }
 
  if (!response.ok) {
    throw new Error(`Status ${response.status}: ${response.statusText}`)
  }
 
  const result: WavesTotalResponse = await response.json()
 
  return result
}
 
type WavesTotalItem = WavesParticipant & {
  position: number
}
 
type WavesTotalResponse = WavesTotalItem[]

Fetch Leaderboard for Specific Period

Fetch existing periods of a wave

/**
  * Returns all periods of the Wave, DESC sorted.
  */
const fetchAzuroWavesPeriods = async () => {
  const response = await fetch('https://api.azuro.org/api/v1/public/waves/active/periods')
 
  // 404 means there is no active wave at this moment
  if (response.status === 404) {
    return null
  }
 
  if (!response.ok) {
    throw new Error(`Status ${response.status}: ${response.statusText}`)
  }
 
  const result: WavesPeriodsResponse = await response.json()
 
  return result
}
 
type WavesPeriod = {
  id: number
  /** ISO String "2024-05-13T00:00:00.000Z" */
  startsAt: string
  /** ISO String "2024-05-20T00:00:00.000Z", actually, it's a startsAt of next period */
  endsAt: string
  /** "123456.56789" */
  totalPoints: `${number}`
  waveId: number
}
 
type WavesPeriodsResponse = WavesPeriod[]

Fetch Specific Peroid Leaderboard

To fetch the leaderboard for a period, you must specify the periodStartsAt parameter as a Unix timestamp (in seconds).

At the end of a period, users receive bonus points (bonus = points * (positionMultiplier - 1)), regardless of their level. In the API, the position multiplier is represented as expectedPositionMultiplier. The prefix "expected" is used to emphasize that this value is not final for an active period.

/**
 * Returns the top 10 Participants, sorted by their position.
 *
 * If `userAddress` is provided and the user exists in the leaderboard (has any score)
 * but their position isn't in the top 10,
 * they will be added to the end of the list with their actual position.
 */
const fetchAzuroWavesPeriodLeaderboard = async (periodStartsAt: number, userAddress?: `0x${string}`) => {
  const url = userAddress
    ? `https://api.azuro.org/api/v1/public/waves/active/periods/${periodStartsAt}/leaderboard?address=${userAddress.toLowerCase()}`
    : `https://api.azuro.org/api/v1/public/waves/active/periods/${periodStartsAt}/leaderboard`
 
  const response = await fetch(url)
 
  // 404 means there is no active wave at this moment
  if (response.status === 404) {
    return null
  }
 
  if (!response.ok) {
    throw new Error(`Status ${response.status}: ${response.statusText}`)
  }
 
  const result: WavesPeriodLeaderboardResponse = await response.json()
 
  return result
}
 
type WavesPeriodLeaderboardItem = {
  position: number
  address: `0x${string}`
  /** e.g. "123456.567892" */
  points: `${number}`
  wavePeriodId: number
  /** e.g. "94.224632" */
  sharePercent: `${number}`
  /** e.g. "1.4" */
  expectedPositionMultiplier: `${number}`
}
 
type WavesPeriodLeaderboardResponse = WavesPeriodLeaderboardItem[]

Additional

Get Azuro Wave Levels Data

If you intend to display a table listing all existing levels, use this endpoint:

/**
  * Returns all Levels sorted from Grey to Royal.
  */
const fetchAzuroWavesLevels = async () => {
  const url = 'https://api.azuro.org/api/v1/public/waves/active/levels'
  const response = await fetch(url)
 
  // 404 means there is no active wave at this moment
  if (response.status === 404) {
    return null
  }
 
  if (!response.ok) {
    throw new Error(`Status ${response.status}: ${response.statusText}`)
  }
 
  const result: WavesLevelsResponse = await response.json()
 
  return result
}
 
type WavesLevelsResponse = WavesLevelDescription[]