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.
| Metric | Description | How 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 =
1e18wei - 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)
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.
wouldUseEOAPathindicates whether the contract would treat the caller as EOA-path (msg.sender == tx.origin).indicatorAfteris 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).
indicatorUriAfterpoints to the off-chain image for that indicator number (see Badge API).
3.0.1 previewBurnFor(address beneficiary, uint256 amountWei)
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()
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.
| Inputs | Description |
|---|---|
msg.value | Amount 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_BPSand forwards burn toBURN_ADDRESS. - Updates rolling 90-day + lifetime totals in Wei, rounded CRO, and USD WAD.
3.2 burnFor(address beneficiary)
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)
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)
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)
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
nameto 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-scaledlastUpdatedBase,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.
| Level | Required 90-day USD | USD WAD (USD × 1e18) |
|---|---|---|
| 1 | $0.20 | 0.20e18 |
| 2 | $0.40 | 0.40e18 |
| 3 | $0.80 | 0.80e18 |
| 4 | $1.60 | 1.60e18 |
| 5 | $3.20 | 3.20e18 |
| 6 | $6.40 | 6.40e18 |
| 7 | $12.80 | 12.80e18 |
| 8 | $25.60 | 25.60e18 |
| 9 | $51.20 | 51.20e18 |
| 10 | $102.40 | 102.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.
todayDayisuint32(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.
indexIfInTop100is 1..100 wheninTop100 == 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 forbadgeNumber.isContract:trueifaccount.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
| Getter | Type | Meaning |
|---|---|---|
OWNERSHIP_PROPOSAL_WINDOW() | uint256 | Time allowed to confirm an ownership proposal (default: 1 day). |
DEFAULT_BAND_STDREFERENCE() | address | Default Band StdReference address embedded in the contract. |
BPS_DENOMINATOR() | uint16 | 10,000. |
BURN_BPS() | uint16 | 9,750 (97.50%). |
FEE_BPS() | uint16 | 250 (2.50%). |
WEI_PER_CRO() | uint256 | 1e18. |
USD_WAD() | uint256 | 1e18 (USD WAD scaling). |
EOA_LEVEL1_USD_WAD() | uint256 | Level-1 threshold in USD WAD ($0.20 × 1e18). |
WINDOW_DAYS() | uint16 | 90. |
BADGE_BASE_URI() | string | Base URI for badge images (default: https://burnsomecro.com/badge/). |
BURN_ADDRESS() | address payable | 0x000000000000000000000000000000000000dEaD. |
BASE_URI() | string | Base URL prefix for off-chain badge images (default: https://burnsomecro.com/badge/). |
5.2 Global lifetime totals
| Getter | Type | Description |
|---|---|---|
totalCreditedLifetimeWei() | uint256 | Total credited lifetime (wei). |
totalCreditedLifetimeCRO() | uint256 | Total credited lifetime (whole CRO; rounded). |
totalCreditedLifetimeUsdWad() | uint256 | Total credited lifetime (USD WAD). |
totalBurnedLifetimeWei() | uint256 | Total burned lifetime (wei) forwarded to burn address. |
totalBurnedLifetimeCRO() | uint256 | Total burned lifetime (whole CRO; rounded). |
totalBurnedLifetimeUsdWad() | uint256 | Total burned lifetime (USD WAD). |
totalFeesLifetimeWei() | uint256 | Total fees lifetime (wei) retained. |
totalFeesLifetimeCRO() | uint256 | Total fees lifetime (whole CRO; rounded). |
totalFeesLifetimeUsdWad() | uint256 | Total 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
| Getter | Type | Description |
|---|---|---|
priceOracle() | address | Configured Band StdReference oracle contract address. |
authorized(address) | bool | Whether an address is authorized for admin actions. |
proposedNewOwner() | address | Pending owner in two-step transfer flow. |
proposalProposer() | address | Who proposed the owner transfer. |
proposalTimestamp() | uint256 | When the proposal was created. |
owner() | address | Current owner (from OpenZeppelin Ownable). |
6) Events
Events are useful for indexers, analytics, activity feeds, and off-chain caching.
| Event | When emitted | Key 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 CRO—msg.value == 0Oracle not set/oracle?— oracle address missingOracle rate=0— oracle returned a zero rateBurn transfer failed— forwarding burn amount to burn address failedUnknown function— hitfallback()with non-empty calldata
burnFor
EOA only— sender must satisfymsg.sender == tx.originBeneficiary addr?— beneficiary is zero addressBeneficiary not EOA— beneficiary has code (is a contract)
Fees withdrawal
No fees— withdraw amount is zeroInsufficient fees— requested amount exceeds contract balanceWithdraw failed— sending withdrawal to caller failed
Admin / authorization
Not owner or authorized— admin function called by non-adminAlready authorized/!authorized— add/remove authorization constraintsaddr?— provided address is the zero address (used by admin setters)Name too long— known name exceeds 32 bytesNo proposal,Proposal expired,!Own proposal— two-step ownership flowUse proposeOwnershipTransfer,Renounce disabled— OZ ownership functions are intentionally disabled