Burnsome.cro

Developer documentation (integrations, levels, and Top 100 leaderboards)

Developer Documentation

Reference documentation for dApp developers integrating burn flows, EOA levels, and contract-path leaderboards. This documentation reflects the implementation in Burnsome.cro.sol (Solidity contract: BurnsomeCro).

1) Overview & Attribution Rules

Contract address: Burnsome.cro -> 0x153524AE331B34c2Eae738dcF408De5e97437822

What the contract does

  • Accepts CRO and forwards the burn portion to the canonical burn address (0x…dEaD).
  • Retains a small fee portion in the contract for operations/maintenance.
  • Tracks burn credit for addresses in:
    • Lifetime totals
    • Rolling 90-day totals using day buckets (UTC days)
    • USD equivalents using Band StdReference CRO/USD (stored as 1e18-scaled “USD WAD”)
  • Computes an EOA level 0–10 based on 90-day USD totals.
  • Maintains a Top 100 leaderboard for “contract-path” burners (ranked by 90-day USD).
  • Supports on-chain “known names” for contract addresses (admin-managed, max 32 bytes) to improve leaderboard/UI readability.
  • UI convention: For contract addresses, the dApp displays Contract name (known name or Unknown). For EOAs, the dApp displays Cronos ID only when resolvable; otherwise that field is hidden.

Credited vs burned vs fee

Credit basis: all stats and levels use the full msg.value (“credited”).

Burned and fee amounts are derived from credited value by BPS constants.

MetricDescriptionHow computed
Credited Full CRO amount sent to the contract in the burn transaction. creditedWei = msg.value
Fee CRO retained by the contract. feeWei = creditedWei * FEE_BPS / 10000
Burned CRO forwarded to burn address. burnWei = creditedWei - feeWei

EOA-path vs contract-path attribution

BurnsomeCro splits accounting into two tracks using the internal “EOA-path” heuristic: msg.sender == tx.origin.

  • EOA-path (msg.sender == tx.origin): credits the EOA stats for the beneficiary; drives EOA levels.
  • Contract-path (msg.sender != tx.origin): credits contract stats for the calling contract; used for the Top 100 leaderboard.

Smart wallets / AA note: wallets that execute through a contract may be classified as contract-path (because msg.sender != tx.origin). Integrators should be aware of this behavior when designing UX.

Badges (Level / Rank images)

For convenience, the contract exposes a badge URI for an address: an off-chain image that represents either the address’ EOA level (for EOAs) or its Top 100 rank (for contracts). See Badge API.

Rolling window definition (90-day day buckets)

  • Window length: WINDOW_DAYS = 90
  • Bucket key: day = uint32(block.timestamp / 1 days) (UTC day number)
  • Returned slot arrays cover exactly 90 UTC days from oldest to newest.

2) Units & Rounding

Wei vs CRO

  • Wei: 1 CRO = 1e18 wei
  • CRO return values in view functions are typically whole CRO, rounded half-up from wei.

Half-up rounding rule used by the contract

uint256 roundedCRO = (amtWei + (WEI_PER_CRO / 2)) / WEI_PER_CRO

Examples: 2.4 CRO → 2.9 → 2, 2.5 CRO → 3 → 3

USD WAD (1e18-scaled USD)

  • USD amounts are stored/returned as USD WAD = USD × 1e18.
  • CRO/USD rate from oracle is also treated as 1e18-scaled (rateWad).
  • Wei → USD WAD conversion uses: usdWad = creditedWei * rateWad / 1e18.

JavaScript conversion helpers (ethers v6)

// USD WAD (1e18) -> human string (example: 4 decimals)
function usdWadToString(usdWad, decimals = 4) {
  const s = usdWad.toString().padStart(19, "0"); // ensure at least 1 whole digit
  const whole = s.slice(0, -18);
  const frac  = s.slice(-18, -18 + decimals);
  return `${whole}.${frac}`;
}

// Wei -> CRO string with 18 decimals
import { formatEther } from "ethers";
const cro = formatEther(weiValue);

// Whole-CRO view values are already rounded integers

3) Write API (Burn Entry Points)

All burns emit events (see Events) and update rolling/lifetime stats.

3.0 previewBurnCRO(uint256 amountWei)

external view
function previewBurnCRO(uint256 amountWei)
  external view
  returns (
    uint256 burnWei,
    uint256 feeWei,
    uint256 creditedUsdWad,
    uint256 burnedUsdWad,
    uint256 feeUsdWad,
    uint256 croUsdRateWad,
    bool oracleUsedFallback,
    bool wouldUseEOAPath,
    uint8 eoaLevelAfter,
    bool contractInTop100After,
    uint8 contractRankAfter,
    uint8 indicatorAfter,
    string memory indicatorUriAfter
  );

Purpose: Quote what a burn would do without sending a transaction: fee split, USD equivalents, and which indicator (EOA level or contract rank) would apply.

  • wouldUseEOAPath indicates whether the contract would treat the caller as EOA-path (msg.sender == tx.origin).
  • indicatorAfter is the “display number” the dApp should show after the burn:
    • EOA-path: the EOA’s reported level (with the “never drop back to 0 after reaching level 1” rule applied).
    • Contract-path: the contract’s Top 100 rank (0 if not in Top 100).
  • indicatorUriAfter points to the off-chain image for that indicator number (see Badge API).

3.0.1 previewBurnFor(address beneficiary, uint256 amountWei)

external view
function previewBurnFor(address beneficiary, uint256 amountWei)
  external view
  returns (
    uint256 burnWei,
    uint256 feeWei,
    uint256 creditedUsdWad,
    uint256 burnedUsdWad,
    uint256 feeUsdWad,
    uint256 croUsdRateWad,
    bool oracleUsedFallback,
    uint8 eoaLevelAfter,
    uint8 indicatorAfter,
    string memory indicatorUriAfter
  );

Purpose: Quote what a burnFor(beneficiary) would do (EOA credit), including the beneficiary’s post-burn level/indicator and URI.

3.1 burnCRO()

external payable nonReentrant
function burnCRO() external payable;

Purpose: Burn CRO where the beneficiary is the caller (msg.sender).

Attribution: Uses EOA-path heuristic. If the caller is an EOA, this updates EOA stats; if the caller is a contract, this updates contract-path stats.

InputsDescription
msg.valueAmount of CRO (in wei) credited to stats; fee/burn split applied.

How to call (ethers.js)

await burnsome.connect(signer).burnCRO({
  value: ethers.parseEther("10.0")
});

Behavior

  • Reverts if msg.value == 0.
  • Computes fee and burn split using FEE_BPS and forwards burn to BURN_ADDRESS.
  • Updates rolling 90-day + lifetime totals in Wei, rounded CRO, and USD WAD.

3.2 burnFor(address beneficiary)

external payable nonReentrant EOA-only sender
function burnFor(address beneficiary) external payable;

Purpose: Burn CRO while crediting the burn to a beneficiary EOA address (sponsor/gift credit).

Requirements (reverts if violated)

  • Caller must be an EOA: msg.sender == tx.origin (“EOA only”).
  • beneficiary != address(0).
  • Beneficiary must be an EOA (no code): beneficiary.code.length == 0.

How to call (ethers.js)

await burnsome.connect(signer).burnFor(beneficiaryAddress, {
  value: ethers.parseEther("5.0")
});

Return value

No return value (state-changing transaction). Use events or read APIs to observe updated totals/levels.

3.3 Direct CRO transfer (receive)

If CRO is sent to the contract address with empty calldata, the contract’s receive() function executes and performs the same logic as burnCRO().

// ethers.js
await signer.sendTransaction({
  to: burnsomeAddress,
  value: ethers.parseEther("10.0")
});

Some wallets may require increasing gas settings when sending a direct transfer to a contract address.

3.4 Fallback behavior

If a transaction hits fallback() (non-empty calldata that does not match a valid selector), it reverts with "Unknown function".

3.5 Maintenance / Fees withdrawal (owner/authorized)

external nonReentrant onlyOwnerOrAuthorized
function withdrawMyFees(uint256 amountWei) external;

Purpose: Withdraw accumulated fee balance to the caller (only owner or authorized addresses).

  • If amountWei == 0, withdraws the full contract balance.
  • Reverts if balance is insufficient or amount is zero.

3.6 Burn contract's balance (owner/authorized)

external nonReentrant onlyOwnerOrAuthorized
function burnFromContractBalance(uint256 amountWei) external;

Purpose: Burn CRO that is already held by this contract (typically accumulated fees).

This burn is credited to address(this) on the contract-path stats, so the contract address can participate in Top 100.

  • If amountWei == 0, burns the full contract balance.
  • Reverts if balance is insufficient or amount is zero.

3.7 Set Known Name (owner/authorized)

external nonReentrant onlyOwnerOrAuthorized
function setKnownName(address account, string calldata name) external;

Purpose: Add/update a human-readable “known name” for a contract address for UI/leaderboard display.

  • Max length: bytes(name).length <= 32 (reverts if longer).
  • Clear: set name to an empty string ("") to remove the stored name.
  • Emits KnownNameUpdated(account, name).

How to call (ethers.js)

await burnsome.connect(adminSigner).setKnownName(
  "0xContractAddressHere",
  "MyContractName"
);

4) Read API (All View/Pure Functions)

All functions below are read-only (view or pure), meaning you can call them via eth_call without sending a transaction or paying gas (beyond node/provider requirements).

4.1 Oracle Read

getCROUSDOracleData()

function getCROUSDOracleData()
  external view
  returns (uint256 rateWad, uint256 lastUpdatedBase, uint256 lastUpdatedQuote);

Returns:

  • rateWad: CRO/USD rate, 1e18-scaled
  • lastUpdatedBase, lastUpdatedQuote: timestamps from oracle reference data
const [rateWad, lub, luq] = await burnsome.getCROUSDOracleData();

getCROUSDPriceWad()

function getCROUSDPriceWad()
  external view
  returns (uint256 rateWad);

Returns: rateWad (CRO/USD, 1e18-scaled).

4.2 EOA Totals (90-day and Lifetime)

getEOA90dWei(address account)

function getEOA90dWei(address account) external view returns (uint256);

Returns: rolling 90-day credited amount for account, in wei.

getEOA90dCRO(address account)

function getEOA90dCRO(address account) external view returns (uint256);

Returns: rolling 90-day credited amount in whole CRO (half-up rounded from wei).

getEOA90dUSD(address account)

function getEOA90dUSD(address account) external view returns (uint256);

Returns: rolling 90-day credited amount in USD WAD (USD × 1e18).

getEOA90dBoth(address account)

function getEOA90dBoth(address account)
  external view
  returns (uint256 amountWei, uint256 amountCRO, uint256 amountUsdWad);

Returns: 90-day totals in Wei, rounded CRO, and USD WAD.

getEOALifetimeWei(address account)

function getEOALifetimeWei(address account) external view returns (uint256);

Returns: lifetime credited amount in wei.

getEOALifetimeCRO(address account)

function getEOALifetimeCRO(address account) external view returns (uint256);

Returns: lifetime credited amount in whole CRO (rounded).

getEOALifetimeUSD(address account)

function getEOALifetimeUSD(address account) external view returns (uint256);

Returns: lifetime credited amount in USD WAD.

getEOALifetimeBoth(address account)

function getEOALifetimeBoth(address account)
  external view
  returns (uint256 amountWei, uint256 amountCRO, uint256 amountUsdWad);

4.3 EOA Level

getLevelOfEOA(address account)

function getLevelOfEOA(address account) external view returns (uint8);

Returns: EOA level 0..10, derived from the EOA’s rolling 90-day USD WAD total.

Level thresholds (90-day USD)

Thresholds start at $0.20 (USD WAD = EOA_LEVEL1_USD_WAD) and double each level up to level 10.

LevelRequired 90-day USDUSD WAD (USD × 1e18)
1$0.200.20e18
2$0.400.40e18
3$0.800.80e18
4$1.601.60e18
5$3.203.20e18
6$6.406.40e18
7$12.8012.80e18
8$25.6025.60e18
9$51.2051.20e18
10$102.40102.40e18

Never back to 0 rule: If an address has ever reached level 1, it will never return level 0 again. If the computed level is 0 later, the function returns 1.

4.4 EOA Status Bundles

getEOAStatusWei(address account)

function getEOAStatusWei(address account)
  external view
  returns (uint8 level, uint256 amount90dWei, uint256 lifetimeWei, bool everReachedLevel1);

getEOAStatusCRO(address account)

function getEOAStatusCRO(address account)
  external view
  returns (uint8 level, uint256 amount90dCRO, uint256 lifetimeCRO, bool everReachedLevel1);

getEOAStatusUSD(address account)

function getEOAStatusUSD(address account)
  external view
  returns (uint8 level, uint256 amount90dUsdWad, uint256 lifetimeUsdWad, bool everReachedLevel1);

getEOAStatusBoth(address account)

function getEOAStatusBoth(address account)
  external view
  returns (
    uint8 level,
    uint256 amount90dUsdWad,
    uint256 lifetimeUsdWad,
    uint256 amount90dCRO,
    uint256 lifetimeCRO,
    bool everReachedLevel1
  );

4.5 EOA 90-day Slots (day-by-day)

getEOA90dSlots(address account)

function getEOA90dSlots(address account)
  external view
  returns (
    uint32 todayDay,
    uint32[] memory days,
    uint256[] memory amountWei,
    uint256[] memory amountCRO,
    uint256[] memory amountUsdWad,
    uint256 totalWei,
    uint256 totalUsdWad
  );

Purpose: Returns the 90 rolling day buckets (oldest → newest) so UIs can predict future drops.

  • todayDay is uint32(block.timestamp / 1 days) (UTC day number).
  • Arrays have length WINDOW_DAYS (90): aligned indices represent the same day.
  • days[i] is the UTC day number; it “expires” once current day ≥ days[i] + 90.

Example: find the next day-bucket that will drop (client-side)

const [todayDay, days, weiArr, croArr, usdArr, totalWei, totalUsd] =
  await burnsome.getEOA90dSlots(user);

for (let i = 0; i < days.length; i++) {
  if (usdArr[i] > 0n) {
    const dropDay = days[i] + 90;          // UTC day number when that bucket stops counting
    const dropTs  = BigInt(dropDay) * 86400n; // approximate UTC timestamp (seconds)
    break;
  }
}

4.6 Contract-Path Status & Top 100

getContractStatus(address account)

function getContractStatus(address account)
  external view
  returns (
    uint256 amount90dUsdWad,
    uint256 lifetimeUsdWad,
    uint256 amount90dCRO,
    uint256 lifetimeCRO,
    bool inTop100,
    uint8 indexIfInTop100
  );

Purpose: Read contract-path stats and leaderboard presence for account.

  • Ranking basis is 90-day USD WAD, not CRO.
  • indexIfInTop100 is 1..100 when inTop100 == true, otherwise 0.

getKnownName(address account)

function getKnownName(address account)
  external view
  returns (string memory name);

Purpose: Read the admin-managed known name for an address. Returns an empty string if unset.

getTop100()

function getTop100()
  external view
  returns (
    address[] memory accounts,
    string[] memory knownNames,
    uint256[] memory amount90dUsdWad,
    uint256[] memory amount90dCRO,
    uint256[] memory lifetimeUsdWad,
    uint256[] memory lifetimeCRO
  );

Purpose: Return the current Top 100 contract-path accounts ranked by 90-day USD credited amount.

  • Arrays are aligned by index.
  • Returned length is the current tracked count (up to 100).

UI convention: The Top 100 page displays the known name when available; otherwise it displays Unknown. Copy actions always copy the underlying address.

getContract90dSlots(address account)

function getContract90dSlots(address account)
  external view
  returns (
    uint32 todayDay,
    uint32[] memory days,
    uint256[] memory amountWei,
    uint256[] memory amountCRO,
    uint256[] memory amountUsdWad,
    uint256 totalWei,
    uint256 totalUsdWad
  );

Purpose: Same slot structure as EOA slots, but for contract-path accounting.

4.7 Badge & Indicator URIs

The contract exposes a simple “badge” interface so third parties can query an address and get a single indicator number plus an off-chain image URI.

EOA: badge = EOA level (0–10, with “once level 1 then never drop to 0”).

Contract: badge = Top 100 rank (0 if not in Top 100).

Base URI

Badge images follow: https://burnsomecro.com/badge/eoa/<badgeNumber>.png or https://burnsomecro.com/badge/contracts/<badgeNumber>.png

getBadge(address account)

function getBadge(address account)
      external view
      returns (
        uint8 badgeNumber,
        string memory badgeUri,
        bool isContract,
        uint8 eoaLevel,
        bool inTop100,
        uint8 contractRank,
        bool everReachedLevel1
      );
  • badgeNumber: the single “display number” to use (EOA level or contract rank).
  • badgeUri: image URI for badgeNumber.
  • isContract: true if account.code.length != 0.
  • eoaLevel: EOA level (0–10, reported). For contracts, this is 0.
  • inTop100: contract is currently in the Top 100 set.
  • contractRank: Top 100 rank (0 if not in Top 100).
  • everReachedLevel1: EOA-only flag that prevents dropping back to 0.

UI convention used by the Burnsome dApp: If isContract == true, the “Check Badge” panel labels the identity field as Contract name and always shows it (value is the stored known name, or Unknown if unset). If isContract == false, the label is Cronos ID and the field is only shown when a Cronos ID is resolvable for that EOA.

getBadgeMetadata(address account)

function getBadgeMetadata(address account)
      external view
      returns (string memory);

Returns a lightweight JSON metadata blob (as a plain string) suitable for third-party integrations (e.g., badge/NFT-style renderers). The image field points to the off-chain badge PNG.

4.8 Pure overrides that intentionally revert

transferOwnership(address) (disabled)

// pure override; always reverts
        function transferOwnership(address newOwner) public pure override;

This is intentionally disabled to prevent bypassing the contract’s two-step ownership flow. It reverts with "Use proposeOwnershipTransfer".

renounceOwnership() (disabled)

// pure override; always reverts
        function renounceOwnership() public pure override;

This is intentionally disabled and reverts with "Renounce disabled".

5) Public Getters (Auto-generated View Functions)

Solidity generates view getters for all public state variables and constants. These are commonly used by UIs for configuration display and global totals.

5.1 Public constants

GetterTypeMeaning
OWNERSHIP_PROPOSAL_WINDOW()uint256Time allowed to confirm an ownership proposal (default: 1 day).
DEFAULT_BAND_STDREFERENCE()addressDefault Band StdReference address embedded in the contract.
BPS_DENOMINATOR()uint1610,000.
BURN_BPS()uint169,750 (97.50%).
FEE_BPS()uint16250 (2.50%).
WEI_PER_CRO()uint2561e18.
USD_WAD()uint2561e18 (USD WAD scaling).
EOA_LEVEL1_USD_WAD()uint256Level-1 threshold in USD WAD ($0.20 × 1e18).
WINDOW_DAYS()uint1690.
BADGE_BASE_URI()stringBase URI for badge images (default: https://burnsomecro.com/badge/).
BURN_ADDRESS()address payable0x000000000000000000000000000000000000dEaD.
BASE_URI()stringBase URL prefix for off-chain badge images (default: https://burnsomecro.com/badge/).

5.2 Global lifetime totals

GetterTypeDescription
totalCreditedLifetimeWei()uint256Total credited lifetime (wei).
totalCreditedLifetimeCRO()uint256Total credited lifetime (whole CRO; rounded).
totalCreditedLifetimeUsdWad()uint256Total credited lifetime (USD WAD).
totalBurnedLifetimeWei()uint256Total burned lifetime (wei) forwarded to burn address.
totalBurnedLifetimeCRO()uint256Total burned lifetime (whole CRO; rounded).
totalBurnedLifetimeUsdWad()uint256Total burned lifetime (USD WAD).
totalFeesLifetimeWei()uint256Total fees lifetime (wei) retained.
totalFeesLifetimeCRO()uint256Total fees lifetime (whole CRO; rounded).
totalFeesLifetimeUsdWad()uint256Total fees lifetime (USD WAD).

Rounding note: the *CRO total fields are stored as rounded whole-CRO values. They should be treated as human-readable aggregates, not exact wei-precision accounting.

5.3 Admin/config state getters

GetterTypeDescription
priceOracle()addressConfigured Band StdReference oracle contract address.
authorized(address)boolWhether an address is authorized for admin actions.
proposedNewOwner()addressPending owner in two-step transfer flow.
proposalProposer()addressWho proposed the owner transfer.
proposalTimestamp()uint256When the proposal was created.
owner()addressCurrent owner (from OpenZeppelin Ownable).

6) Events

Events are useful for indexers, analytics, activity feeds, and off-chain caching.

EventWhen emittedKey fields
ContractDeployed On deployment. newContract
Burned On every burn. sender, beneficiary, isEOAPath, creditedWei, burnedWei, feeWei
BurnedUSD On every burn (USD equivalents). sender, beneficiary, isEOAPath, creditedUsdWad, burnedUsdWad, feeUsdWad, croUsdRateWad
OracleCacheUpdated When the on-chain oracle cache is refreshed. rateWad, timestamp
BurnedV2 Indexer-friendly burn event (includes USD + oracle fallback + day bucket). sender, beneficiary, isEOAPath, creditedWei, burnedWei, feeWei, creditedUsdWad, burnedUsdWad, feeUsdWad, croUsdRateWad, oracleUsedFallback, day
LevelChanged When an EOA’s reported level changes after a burn. account, oldLevel, newLevel, usd90dWad, day
Top100Changed When a contract enters or leaves Top 100 due to a burn. account, inTop100, indexPlus1, score90dUsdWad, day
OracleUpdated When admin updates oracle address. oldOracle, newOracle
Withdrawn When fees are withdrawn. by, amountWei
AuthorizedAddressAdded Admin action. account
AuthorizedAddressRemoved Admin action. account
KnownNameUpdated When an owner/authorized address adds, updates, or clears a known name for an address. account, name
OwnershipTransferProposed Admin proposes ownership transfer. proposer, newOwner, timestamp
OwnershipTransferConfirmed Admin confirms ownership transfer. confirmer, newOwner, timestamp

7) Common Revert Messages

Burn flow

  • No CROmsg.value == 0
  • Oracle not set / oracle? — oracle address missing
  • Oracle rate=0 — oracle returned a zero rate
  • Burn transfer failed — forwarding burn amount to burn address failed
  • Unknown function — hit fallback() with non-empty calldata

burnFor

  • EOA only — sender must satisfy msg.sender == tx.origin
  • Beneficiary addr? — beneficiary is zero address
  • Beneficiary not EOA — beneficiary has code (is a contract)

Fees withdrawal

  • No fees — withdraw amount is zero
  • Insufficient fees — requested amount exceeds contract balance
  • Withdraw failed — sending withdrawal to caller failed

Admin / authorization

  • Not owner or authorized — admin function called by non-admin
  • Already authorized / !authorized — add/remove authorization constraints
  • addr? — provided address is the zero address (used by admin setters)
  • Name too long — known name exceeds 32 bytes
  • No proposal, Proposal expired, !Own proposal — two-step ownership flow
  • Use proposeOwnershipTransfer, Renounce disabled — OZ ownership functions are intentionally disabled