gnomic_airdrop source realm
Claim-based airdrop realm with Merkle proofs.
Claim-based airdrop realm with Merkle proofs.
Why Merkle rather than an on-chain list:
- the beneficiary list can run to tens of thousands of rows; writing it on-chain costs gas and storage deposit in proportion;
- here only 32 bytes go on-chain (the root), plus one record per ACTUAL claim. Whoever never claims costs nothing.
Operating flow:
- deploy this realm;
- fund it by transferring GRC20 tokens to it (its address is returned by RealmAddress());
- the admin publishes root, leaf count and deadline block with SetCampaign;
- each beneficiary calls Claim with their own proof;
- after the deadline the admin recovers the residue with Sweep.
The Merkle leaf is the string "<address>|<amount in base units>", UTF-8 encoded. The tree is Tendermint's "simple tree" (gno's crypto/merkle): leaf = SHA256(0x00||data), node = SHA256(0x01||left||right), split at the largest power of two below n. The generator in tools/merkle produces compatible proofs.
1
1
44
func Balance
ActionBalance is the token balance the realm currently holds.
func BurnedOf
ActionBurnedOf returns how much of addr's grant was destroyed.
func Claim
crossing ActionClaim pays amount tokens to the caller, if the Merkle proof (index, auntsHex) shows that "<caller>|<amount>" is a leaf of the published tree.
auntsHex is the hex concatenation of the sibling hashes, 32 bytes each, just as produced by tools/merkle.
func ClaimCount
ActionClaimCount is the number of successful claims.
func ClaimInstant
crossing ActionClaimInstant is the first of the two alternatives to Claim: the instant share (instantBps, 30%) is paid at once and the rest (70%) is given up the same way a forfeit is — half burned, half to the ecosystem fund — whenever it is chosen, even after the whole grant would have vested. The share is larger than Claim's immediate 10% on purpose: it makes the shortcut a real choice rather than a punishment. It cannot be undone and suits someone who prefers a certain fraction today to the whole a year from now. The residue is destroyed, to every holder's benefit.
Only the person concerned can choose it: there is no "on behalf of" form.
func ClaimWindowEnd
ActionClaimWindowEnd is the moment (unix) claims stop being accepted for the current campaign; SettleUnclaimed becomes callable from then on.
func ClaimedAmount
ActionClaimedAmount returns addr's total grant, vested or not.
func CloseCampaign
crossing ActionCloseCampaign stops claims immediately.
func EndHeight
ActionEndHeight is the deadline block (0 = no deadline).
func Epoch
ActionEpoch is the number of the current campaign.
func Forfeit
crossing ActionForfeit is the second alternative to Claim: the beneficiary gives the grant up without taking anything. Half is burned, half goes to the ecosystem fund — the same split SettleUnclaimed applies to whoever never acted at all.
The proof is still required and the grant is still recorded: without that, the same address could forfeit and then claim. The record makes it final. Only the person concerned can choose it: there is no "on behalf of" form.
func FundOf
ActionFundOf returns how much of addr's grant went to the ecosystem fund.
func GrantEpoch
ActionGrantEpoch returns the campaign in which addr obtained its grant.
func HasClaimed
ActionHasClaimed reports whether addr has already claimed in the current campaign.
func HasGrant
ActionHasGrant reports whether addr holds a grant, from this campaign or an earlier one.
func ImmediateBps
ActionImmediateBps is the share unlocked at once, in hundredths of a percent.
func InstantBps
ActionInstantBps is the share ClaimInstant pays at once, in hundredths of a percent.
func IsOpen
ActionIsOpen reports whether claims are currently accepted.
func IsSealed
ActionIsSealed reports whether the campaign has been sealed: if true, no new campaign can ever be opened.
func Leaf
ActionLeaf returns the canonical Merkle-leaf encoding for the (address, amount) pair. It must match byte for byte the one used by the off-chain generator.
func LeafCount
ActionLeafCount is the number of leaves in the published tree.
func NextWithdrawAt
ActionNextWithdrawAt returns the unix time from which addr may withdraw again (0 when there is no grant, the grant is closed, or no cooldown is running).
func Outstanding
ActionOutstanding is the total granted and not yet withdrawn: the part of the realm's balance that Sweep cannot touch.
func Render
func Root
ActionRoot returns the current Merkle root in hex.
func SealCampaign
crossing ActionSealCampaign closes off the possibility of opening new campaigns forever. Irreversible: no code path sets sealed back to false. Call it once the published root is the final one.
Until then the owner can fix a mistake by republishing the root; afterwards the airdrop is provably one-off and anyone can check with IsSealed().
func SetCampaign
crossing ActionSetCampaign publishes a new campaign: rootHex is the Merkle root in hex (32 bytes), total the number of leaves in the tree, end the deadline block (0 = no deadline).
Publishing a new campaign resets the claim history: whoever appears on both lists can claim again. That is deliberate — it allows recurring airdrops on the same realm without a redeploy.
func SettleUnclaimed
crossing ActionSettleUnclaimed disposes of whatever was never claimed, once the claim window has elapsed: half is burned, the other half goes to the team fund.
The rule is enforced here rather than promised: there is no Sweep and no BurnRemaining, so the owner cannot pocket the residue nor destroy all of it. Settlement waits for the window even if CloseCampaign stopped claims earlier, and grants already made stay untouchable — only the free part (balance minus outstanding) is settled. Anyone may call it: it is a public service, not a privilege.
func SettledBurned
ActionSettledBurned and SettledToFund report what SettleUnclaimed did.
func SettledToFund
Actionfunc TotalBurned
ActionTotalBurned is the total destroyed: instant exits, renunciations and the settlement of the unclaimed residue.
func TotalClaimed
ActionTotalClaimed is the total actually transferred to beneficiaries.
func TotalGranted
ActionTotalGranted is the total of all grants made, vested or not.
func TotalToFund
ActionTotalToFund is what voluntary forfeits sent to the ecosystem fund.
func TransferOwnership
crossing ActionTransferOwnership hands administration of the campaign over.
func Vested
ActionVested returns how much of addr's grant has vested so far. A closed grant (instant exit or forfeit) will never vest anything more: it reports what was paid, not where the curve would be.
func VestingEnd
ActionVestingEnd is the moment (unix) at which everything granted in the current campaign counts as fully vested.
func VestingStart
ActionVestingStart is the moment (unix) the CURRENT campaign's linear release runs from. Grants from earlier campaigns keep their own, readable with VestingStartOf.
func VestingStartOf
ActionVestingStartOf returns when vesting starts for addr's grant.
func Withdraw
crossing ActionWithdraw transfers to the caller the vested, not-yet-withdrawn part.
func WithdrawCooldown
ActionWithdrawCooldown is the minimum interval between two payouts, in seconds.
func WithdrawFor
crossing ActionWithdrawFor is like Withdraw but on another beneficiary's behalf: the tokens still go to beneficiary, whoever sends the transaction only pays the gas.
func Withdrawable
ActionWithdrawable returns how much addr can withdraw right now.
func Withdrawn
ActionWithdrawn returns how much addr has already withdrawn.
14
- chain stdlib
- chain/runtime stdlib
- chain/runtime/unsafe stdlib
- crypto/merkle stdlib
- encoding/hex stdlib
- gno.land/p/nt/avl/v0 package
- gno.land/p/nt/ownable/v0 package
- gno.land/p/nt/ufmt/v0 package
- gno.land/r/nt/grc20reg/v0 realm
- gno.land/r/nym-thegnomic001/gnomic realm
- math stdlib
- strconv stdlib
- strings stdlib
- time stdlib