> For the complete documentation index, see [llms.txt](https://docs.plug.sucks/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.plug.sucks/distributions.md).

# distributions

### Process

Beraplug uses a [public Dune dashboard](https://dune.com/queries/3447423) to determine which wallets are eligible and how many points they have earned for a given round. A round is defined by a round ID number, a description of the round's prize, a start time and an end time.&#x20;

An offchain script is used to hash the round's metadata and eligible wallets, use that hash as the seed for a random number generator, and output the winning address associated with the chosen number.

### Independent verification

Because the process is deterministic based on the inputs, anyone can verify that the winner is accurate. Use the script below locally or on a hosted node.js environment like [Replit](https://replit.com/). Replace `roundId`, `roundStart`, `roundEnd`, and `prizeDescription` with the exact values from any entry from the game log. Check that the winner you compute matches the one on the log, and check the transaction links to verify the prizes have been transferred.

Please note that SQL, which Dune is built on, is not 100% deterministic due to how it handles floating point arithmetic and parallelization. In our testing, we see about a 0.07% chance of getting a different result. To account for this unlikely event, we execute the Dune query for each round at least twice. If the same winner is selected both times, we go with that. If the winners are different, we run the round a third time and use the majority winner. This increases round certainty to 99.9951%. If you perform independent verification and get a different result, please do not draw any conclusions until you see that result across multiple Dune executions.

{% code lineNumbers="true" %}

```javascript
require('dotenv').config();
const { parse } = require('csv-parse/sync');
const crypto = require('crypto');
const seedrandom = require('seedrandom');

// Replace this with your own Dune API key
const meta = {
  'x-dune-api-key': {{apiKey}}
}
const header = new Headers(meta);

// Replace these with the metadata for the Brown Hole round you want to validate
const roundId = {{roundId}};
const roundStart = '{{roundStart}}';
const roundEnd = '{{roundEnd}}';
const prizeDescription = '{{prizeDescription}}';

const params = {
  'query_parameters': {
    'starttime': roundStart,
    'endtime': roundEnd
  }
};
const body = JSON.stringify(params);

async function executeDuneQuery() {
  const executeUrl = 'https://api.dune.com/api/v1/query/3447423/execute';

  try {
    const execute = await fetch(executeUrl, {
      method: 'POST',
      headers: header,
      body: body
    });
    const executeResponse = await execute.json();
    console.log('Dune query submitted:', executeResponse.execution_id);

    await pollForCompletion(executeResponse.execution_id);
  } catch(error) {
    console.log('Error: ', error);
  }
}

async function pollForCompletion(executionId) {
  const statusUrl = `https://api.dune.com/api/v1/execution/${executionId}/status`;

  try {
    let completed = false;
    while (!completed) {
      const response = await fetch(statusUrl, { 
        method: 'GET',
        headers: header 
      });
      const statusResponse = await response.json();

      if (statusResponse.state === 'QUERY_STATE_COMPLETED') {
        completed = true;
        console.log('Dune query completed.');
        await getDuneResults(executionId);
      } else {
        console.log('Dune query still executing, waiting...');
        await new Promise(resolve => setTimeout(resolve, 5000)); // Wait for 5 seconds before polling again
      }
    }
  } catch (error) {
    console.error('Error polling for completion:', error);
  }
}

async function getDuneResults(executionId) {
  const resultsUrl = `https://api.dune.com/api/v1/execution/${executionId}/results/csv`;

  try {
    const dataResponse = await fetch(resultsUrl, { 
      method: 'GET',
      headers: header 
    });
    const data = await dataResponse.text();
    await flattenPoints(data);
  } catch (error) {
    console.log('Error getting results:', error);
  }
}

async function flattenPoints(data) {
  const records = parse(data, {
    columns: true,
    skip_empty_lines: true
  });

  let flatList = [];
  let eligibleWallets = [];
  records.forEach(record => {
    eligibleWallets.push(record.wallet);
    for (let i = 0; i < parseInt(record.points, 10); i++) {
      flatList.push(record.wallet);
    }
  });

  const flatListString = flatList.join('\n');
  const concatenatedString = `${roundId}\n${prizeDescription}\n${roundStart}\n${roundEnd}\n${eligibleWallets}`;

  const hash = crypto.createHash('sha256').update(concatenatedString).digest('hex');
  console.log('Hashed round data:', hash);
  const rng = seedrandom(hash);
  const randomValue = rng();
  console.log('Random number generated using hash as seed:', randomValue);

  const randomIndex = Math.floor(randomValue * flatList.length);
  console.log('Index corresponding to random number:', randomIndex);

  const winningAddress = flatList[randomIndex];
  console.log(`Winning address at chosen index: ${winningAddress}`);
  console.log('--------------------');
  console.log(`${winningAddress} wins ${prizeDescription} for Brown Hole round ${roundId} based on points tallied between ${roundStart} and ${roundEnd}`);
}

executeDuneQuery();
```

{% endcode %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.plug.sucks/distributions.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
