Skip to content

API Endpoint: Get Smart Contract rewards #109

@sallymoc

Description

@sallymoc

What? Why? Who?

Implement an API endpoint that returns the history of reward distributions for a specific smart contract address, paginated.

The main consumer right now is the Qubic Explorer.
This is needed for the Smart Contract page (address page), where users can see all the reward (dividend) distributions that a given contract has made over time, across all epochs.

Acceptance Criteria

  • The endpoint returns paginated reward distributions for the given contract address
  • Results are ordered by most recent first
  • Response includes pagination metadata
  • Response includes totalAllTimeDistributed — the total amount distributed by this contract across all available epochs (not just the current page) and as we do not have all epochs data, we should receive also which is the start epoch where data is available.
  • Response includes total number of distributions
  • Each distribution item contains: epoch, contractAddress, tickNumber, totalAmount, amountPerShare, transferCount, timestamp
  • Returns an error if the given address is not a valid contract address.

Out of Scope

  • Epoch-level rewards endpoint (separate issue)
  • Individual transfer-level detail (we only return aggregated summaries per distribution)

Technical Sketch (How?)

How to identify reward distributions in Bob log events

Bob emits log events per tick. Each event has these relevant fields:

Field Description
tick Tick number where the event occurred
epoch Epoch number
logId Sequential ID within the tick (determines ordering)
type Log type: 0 = QU_TRANSFER, 255 = CUSTOM_MESSAGE
body.from Source address (on QU_TRANSFER events)
body.to Destination address (on QU_TRANSFER events)
body.amount Amount transferred (on QU_TRANSFER events)
body.customMessage Operation code (on CUSTOM_MESSAGE events only)

A reward distribution appears as a group of consecutive events within a single tick, wrapped by two markers:

logId 100: type=255, body.customMessage=6217575821008262227       ← START marker
logId 101: type=0, body.from=CONTRACT_X, body.to=ADDR_1, body.amount=1000   ← payment
logId 102: type=0, body.from=CONTRACT_X, body.to=ADDR_2, body.amount=1000   ← payment
...
logId 777: type=255, body.customMessage=6217575821008457285       ← END marker

All QU_TRANSFER events (type=0) with a logId between the START and END markers belong to that distribution. They all share the same body.from (the contract address).

Step-by-step logic

  1. Find all START markers: events where type = 255 AND body.customMessage = 6217575821008262227
  2. Find all END markers: events where type = 255 AND body.customMessage = 6217575821008457285
  3. Pair each START with its END: for each START, find the END in the same tick with the smallest logId greater than the START's logId
  4. Collect reward transfers: for each START/END pair, get all events where type = 0 AND same tick AND logId is between START and END — then filter only those where body.from = {input address}
  5. Aggregate per distribution:
    • totalAmount = sum of all body.amount
    • transferCount = count of transfers
    • amountPerShare = totalAmount / 676 (676 = fixed number of computors)
    • contractAddress = body.from from any transfer
    • tickNumber = the tick of the START marker
    • timestamp = timestamp of the tick
    • epoch = epoch of the tick
  6. Order by tickNumber descending (most recent first)
  7. Paginate: apply offset and size to the ordered results
  8. Calculate totals across all pages: totalAllTimeDistributed = sum of totalAmount from ALL distributions (not just the current page), totalCount = total number of distributions

Expected input

  • Smart contract address
  • Pagination parameters

Expected output

{
  "contractAddress": "BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
  "distributions": [
    {
      "epoch": 152,
      "contractAddress": "BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
      "tickNumber": 42187300,
      "totalAmount": 676000,
      "amountPerShare": 1000.0,
      "transferCount": 451,
      "timestamp": "2024-12-17T10:00:00Z"
    },
    {
      "epoch": 151,
      "contractAddress": "BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
      "tickNumber": 42100500,
      "totalAmount": 338000,
      "amountPerShare": 500.0,
      "transferCount": 423,
      "timestamp": "2024-12-12T08:30:00Z"
    } 

...

  ],
  "totalAllTimeDistributed": 1014000,
   "hits": {
        "total": 10000,
        "from": 0,
        "size": 50
    },
}

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    Status

    🔖 Ready

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions