Contract Security Model
What the Superhero contracts defend against by design: the guarantees they inherit from the language, the signature schemes they verify, the economic invariants of the bonding curve, and the gates around governance. The contracts have not undergone a third-party security audit.
Language-level guarantees
Sophia is a strongly typed functional language with no arbitrary mutable global state.
Each contract has exactly one state value, replaced wholesale rather than mutated in place.
Every FATE instruction is typed, and a type mismatch aborts the call and reverts every change.
Together these remove the class of failure that dominates EVM incident reports: partially
updated state surviving a failed call, and reentrancy against a half-written storage slot.
Cryptographic integrity
Delegated posting
post_without_tip_sig verifies an Ed25519 signature over a Blake2b digest with
Crypto.verify_sig. The message is framed with the æternity signed-message prefix, the
28-byte constant #1a61657465726e697479205369676e6564204d6573736167653a0a20, which decodes to
a length byte, aeternity Signed Message: with a newline, and a second length byte. That
framing prevents a signature produced in another æternity protocol context from being replayed
as a post. The post signature carries no nonce; relayers track which signed posts they have
already submitted. See Tipping v3.
Identity linking
AddressLink takes an explicit nonce argument on every link and unlink, backed by a
nonce_seq : map(address, int) in state and readable through get_nonce. A reused nonce
aborts with INVALID_NONCE, so replay protection is on chain.
Writes are additionally gated on the provider owner (NOT_PROVIDER_OWNER). The user's
signature proves consent to the link; the provider owner's key submits it. Superhero owns the
providers it operates and submits every link on the user's behalf. AENS names, by contrast,
are verifiable directly against the naming system. See AddressLink.
Economic security
- Bonding curve solvency. The sell curve sits strictly below the buy curve at every supply
level, so the AE reserve held in the sale contract always covers buying back the entire
circulating supply. There is no bank-run state. The gap is the 0.5% sell factor
(
sell_return_percentage = 9950) plus the 0.5% affiliation fee added at the sale layer. - Fixed spread allocation. The 1% spread splits 0.5% to the community DAO and 0.5% to the affiliation system, with the split fixed in the contracts. On a sale, the contract retains exactly what it would owe if the same count were sold straight back, and only the remainder reaches the DAO.
- Collection creation is burned, not collected.
create_collectionrequires exactly 555 AE and spends it toak_11111111111111111111111111111111273Yts, the burn address. Nobody profits from namespace creation. - Vote creation costs 555 AE.
DAO.add_voterequires exactly555 * 10^18aettos, paid to the DAO treasury. Proposal spam has a real price, and the price accrues to the community. - Slippage is caller-controlled.
sell(count, minimal_return_required)reverts withMINIMAL_RETURN_NOT_MEETbelow your floor, andbuyreverts withAE_AMOUNT_NOT_SUFFICIENTif the price moved past what you attached. Set a floor on every live sell. - Overpayment is returned in full.
buycomputesCall.value - total_priceand spends the whole difference back, with no dust floor and no retention, so a slippage buffer costs nothing when the price does not move. - Reward withdrawal requires real trading. An inviter can withdraw affiliation rewards
only after four invitees (
unique_invitee_count_threshold = 4) have each bought at least 10 AE worth (unique_invitee_buy_amount_threshold = 10 * 10 ^ 18).
Governance security
-
Quorum and supermajority. Both live in
DAO.apply_vote_subject, and these are the exact comparisons:let in_favor_percentage = (vote_state[true] * 100) / (vote_state[true] + vote_state[false]) require(in_favor_percentage >= 55, "GREATER_55_PERCENT_AGREEMENT_REQUIRED") let stake_percentage = (total_vote_stake * 100) / get_token().total_supply() require(stake_percentage >= 10, "GREATER_10_PERCENT_STAKE_REQUIRED")The supermajority denominator is votes cast, so abstaining does not oppose. The quorum denominator is
total_supply()read at execution time. Both are truncating integer divisions compared with>=. Two furtherrequires guard the divisions:NO_VOTE_STAKEandNO_TOKEN_SUPPLY. -
Token locking. Tokens voted with are locked in the vote contract through the AEX-9 allowance mechanism, so the same tokens cannot be transferred, sold back to the curve, or voted in a concurrent proposal.
revoke_vote(open votes only) andwithdraw(closed votes only) are exact complements throughis_closed(), so exactly one is callable at any height and locked tokens are never stuck. -
Results are frozen before they are actionable.
final_vote_state()returnsoption(vote_state)and yieldsNonewhile a vote is open (a height gate,Chain.block_height >= close_height).DAO.apply_vote_subjectaborts withVOTE_NOT_YET_FINALonNone, so a proposal cannot be executed on a favourable mid-vote tally. -
Community DAO votes are immutable once closed.
DAOVote.revoke_voterequires!is_closed(), so a closed tally cannot be altered afterwards. -
Time-bounded execution. A passed vote must be applied within
vote_timeoutkey blocks of its close height (960 blocks, roughly two days), orapply_vote_subjectaborts withVOTE_RESULT_TIMEOUTED. Stale proposals cannot be resurrected when circumstances change, and the short window limits how fartotal_supply()can move between close and execution. -
Single execution. The
votesmap stores an applied flag; a second call aborts withVOTE_ALREADY_APPLIED. -
DAO migration safeguards. A
ChangeDAOvote transfers ownership, beneficiary rights and the treasury balance. The target must implementis_dao() = true, which prevents a treasury being voted into a plain account or an unrelated contract, and it must reference the same community management contract (NEW_DAO_HAS_TO_REFERENCE_SAME_COMMUNITY_MANAGEMENT). -
Minting is gated to known sales.
mint_protocol_dao_tokenrequiresSet.member(Call.caller, state.known_token_sales)and aborts withNOT_KNOWN_TOKEN_SALE. Only sale contracts the factory itself deployed can mint protocol tokens. Community token supply is likewise owner-minted: the owner is the sale contract, so supply tracks the curve.
Related
- Deployed Addresses: what is deployed, per network
- DAO: the vote execution gates described above
- BondingCurveExponential: the solvency property in detail
- Token risks: the same picture for someone deciding how much to put in
- Wallet security: key handling, the risk most users actually face
- Registry and Poll: the æternity governance contracts and the off-chain tally
- AffiliationTreasury: the reward threshold and the payout window