diff --git a/BACKLOG.md b/BACKLOG.md index 89821a3..f7897b3 100644 --- a/BACKLOG.md +++ b/BACKLOG.md @@ -9,16 +9,15 @@ Legend: `[ ]` open, `[~]` in flight, `[x]` done. ## P0 - [ ] Third-party security audit (events + profile). Cost ~$30-80k for a Soroban-experienced firm. Scope it to the 2.0.0 / 1.2.1 tree. -- [ ] **Upgrade mainnet to events 1.8.0 + profile 1.2.1** (`docs/upgrade-runbook.md`, Section 8). Events 1.8.0 is the 2.0.0 code under a 1.x label (tag `events-v1.8.0`), so the backend keeps grants and held-refund claims off on mainnet until a 2.x upgrade. Carries every threat-model fix (`docs/threat-model.md` v1.1, Section 4.1.2), restores the 17,280-ledger upgrade timelock (EoP.10: the deployed 1.7.0 / 1.2.0 builds have none), and finishes the 1.7.0 migration that never ran: events still reports a 1.6.0 marker, and events 1 and 2 (`…609`, `…610`, both Completed with zero escrow) are still in the old layout and fail to decode. Rehearsed on testnet 2026-10-02 (events `CBEODVJG…` at 2.0.0, 185 events through `migrate_events`, `migrate` stamped). Backend first: read pages of 90, refund cranks of 15, hackathon selections split at 20 per call, `forfeit_milestone` on milestone rejection, `claim_refund` for held refunds, and no calls to the removed participation entry points. Then profile propose / apply / migrate, and events pause / propose / apply / one `migrate_events` page / `migrate` / unpause, all under the deployed zero timelock. `deploy_mainnet.sh` now refuses to unpause events until `migrate` has stamped, the gate the August upgrade lacked. ## P1 -- [ ] **Events 2.1.0 (grant milestone splits) to mainnet**, as its own upgrade after 1.8.0, waiting the 17,280-ledger timelock. Before it goes: rerun Scout on the 2.1.0 code and add it to `docs/threat-model.md` Section 6 (the scan covers 2.0.0; the testnet grant run already passed on 2.1.0, `docs/audit/testnet-grant-run-2.1.0.md`). Refresh the storage replay from 1.8.0 once mainnet runs it (`docs/storage-compatibility.md`). +- [ ] **Events 2.1.0 (grant milestone splits) to mainnet**, as its own upgrade after 1.8.0, waiting the 17,280-ledger timelock. Before it goes: rerun Scout on the 2.1.0 code and add it to `docs/threat-model.md` Section 6 (the scan covers 2.0.0; the testnet grant run already passed on 2.1.0, `docs/audit/testnet-grant-run-2.1.0.md`). Refresh the storage replay from 1.8.0 first, which mainnet runs since 17 October 2026 (`docs/storage-compatibility.md`). - [ ] Per-event prize claim window: `PRIZE_CLAIM_WINDOW_SECS` (90 days) is a module constant because adding a field to `EventRecord` requires a storage migration. Move it onto `EventRecord` at the next real migration window, per the per-event-config rule in `CLAUDE.md`. - [ ] `Error` enum headroom: events uses 42 of the 50-case `contracterror` cap after 2.0.0, and retired codes are never reused. Plan a consolidation before the cap, not when the next variant fails to compile. - [ ] Grant committee multi-sig primitive (dedicated signer set + quorum at the contract level vs the current address-level multi-sig). - [ ] Multi-token support audit: verify the whitelist mechanism handles tokens with non-Stellar 7-decimal scales (currently assumed uniform). -- [ ] Decide whether to appoint a crowdfunding release validator on mainnet once 1.8.0 is live, and who holds the key (`docs/admin-custody-policy.md` §4.1). Until one accepts, the admin multisig co-signs every crowdfunding release. +- [ ] Decide whether to appoint a crowdfunding release validator on mainnet now that 1.8.0 is live, and who holds the key (`docs/admin-custody-policy.md` §4.1). Until one accepts, the admin multisig co-signs every crowdfunding release. ## P2 (roadmap) @@ -30,6 +29,8 @@ Legend: `[ ]` open, `[~]` in flight, `[x]` done. ## Done +- [x] 2026-10-17: mainnet upgraded to events 1.8.0 (the 2.0.0 code, tag `events-v1.8.0`) and profile 1.2.1 (`docs/upgrade-runbook.md`, Section 8, with every transaction hash). Both migrated and unpaused, the 17,280-ledger timelock is live, and events 1 and 2 were converted and read again. Grants and held-refund claims stay off on mainnet until a 2.x upgrade. + - [x] 2026-10-02: measured batch headroom on the test host, which enforces mainnet limits (100 footprint entries, 50 writes): single-release selection 19 + 4 per award, grant selection 15 + 2 per award, refund crank 11 + 5 per refund, read page 3 + 1 per row. Caps are now 20 single-release awards per call, 40 per grant, 15 refunds per crank and 90-row pages, each tested at its maximum (`grant_scenarios::limits`). - [x] 2026-10-02: upgrade timelock restored in source (events 2.0.0, profile 1.2.1): 17,280 ledgers on mainnet builds, 0 with `--features testnet`; tests assert `UpgradeTimelockNotElapsed`. - [x] 2026-09-28: on-chain participation removed in events 2.0.0 (`apply`, `apply_to_bounty`, `remove_applicant`, `submit` and their registries); entries live in boundless-nestjs. diff --git a/README.md b/README.md index c391a96..00e4476 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,8 @@ earnings. | Network | Contract | Address | Version | |---------|----------|---------|---------| -| Mainnet | `boundless-events` | `CCFVEGOQJEM47LRAJU2LHEK4KTL5VYN7AOGZ2HH2GNHAMXTILNMMJGQZ` | `1.7.0` | -| Mainnet | `boundless-profile` | `CD3KH4OE7HDHHHUYFX3U4L7NLIILMXAY6HM5FEH2UH6UBOKX4HDNE3PC` | `1.2.0` | +| Mainnet | `boundless-events` | `CCFVEGOQJEM47LRAJU2LHEK4KTL5VYN7AOGZ2HH2GNHAMXTILNMMJGQZ` | `1.8.0` | +| Mainnet | `boundless-profile` | `CD3KH4OE7HDHHHUYFX3U4L7NLIILMXAY6HM5FEH2UH6UBOKX4HDNE3PC` | `1.2.1` | | Testnet | `boundless-events` | `CBEODVJGUYCIYTVXD7KI5UG3BJ2UE4T7AGI2TGY3T4Q5GQRFGTRYVTZP` | `2.1.0` | | Testnet | `boundless-profile` | `CCA3OAIBOZBUPHPRI5GI6N5PDTE7RTNLKAEID4JTC2YZIHIZNDX5Q6T3` | `1.2.1` | diff --git a/docs/multisig-guide.md b/docs/multisig-guide.md index 7776413..78dee93 100644 --- a/docs/multisig-guide.md +++ b/docs/multisig-guide.md @@ -577,7 +577,7 @@ On the events contract the admin **can:** - Change the default fee rate (`set_fee_bps`). - Change the fee account address (`set_fee_account`). This one needs all three signatures. - Pause the contract in an emergency (`pause`) and resume after a fix (`unpause`). -- Propose a contract upgrade (`propose_upgrade`). On current mainnet builds the upgrade cannot apply until about a day (17,280 ledgers) after the proposal, so users can see it coming. The deployed events 1.7.0 and profile 1.2.0 builds have no such wait; the 1.8.0 upgrade restores it. +- Propose a contract upgrade (`propose_upgrade`). On current mainnet builds the upgrade cannot apply until about a day (17,280 ledgers) after the proposal, so users can see it coming. Every mainnet build since the events 1.8.0 and profile 1.2.1 upgrade on 17 October 2026 has this wait; the 1.7.0 and 1.2.0 builds before it did not. - Apply a proposed upgrade after that wait (`apply_upgrade`), or cancel it (`cancel_pending_upgrade`). - Run the post-upgrade migration (`migrate_events`, then `migrate`). - Add or remove tokens from the whitelist (`register_supported_token`, `deregister_supported_token`). diff --git a/docs/storage-compatibility.md b/docs/storage-compatibility.md index ee64505..e144e47 100644 --- a/docs/storage-compatibility.md +++ b/docs/storage-compatibility.md @@ -2,7 +2,7 @@ `contracts/compatibility` replays the next mainnet upgrade against real mainnet storage. It loads contract data captured from a mainnet ledger, runs the builds -deployed on mainnet today, then proposes, applies and migrates the release build +deployed on mainnet at that ledger, then proposes, applies and migrates the release build of this tree the way `docs/upgrade-runbook.md` does it on chain: 1. Read every event, every winner record and the reputation of every winner @@ -59,7 +59,7 @@ To check that the fixture still reproduces from the history archive (Stellar CLI ## Refreshing for the next upgrade -Once mainnet runs 1.8.0 (tag `events-v1.8.0`), the replay should start from 1.8.0 instead: +The fixtures still hold events 1.7.0 and profile 1.2.0. Mainnet has run events 1.8.0 (tag `events-v1.8.0`) and profile 1.2.1 since 17 October 2026, so before the next mainnet upgrade the replay should start from those builds: 1. Fetch the deployed wasm for both contracts by hash into the fixtures directory and replace the two `mainnet-*.wasm` files. diff --git a/docs/threat-model.md b/docs/threat-model.md index 9796081..889a335 100644 --- a/docs/threat-model.md +++ b/docs/threat-model.md @@ -4,7 +4,7 @@ **Date:** 8 October 2026 **Prepared for:** Stellar Development Foundation, Soroban Security Audit Bank **Prepared by:** Boundless Engineering -**Contracts under review:** `boundless-events` 2.0.0 and `boundless-profile` 1.2.1, plus the grant milestone split that `boundless-events` 2.1.0 adds (Tamp.20), which reaches mainnet in a later upgrade. Mainnet runs 1.7.0 and 1.2.0 until the upgrade in Section 4.4, which installs the 2.0.0 code as events 1.8.0. +**Contracts under review:** `boundless-events` 2.0.0 and `boundless-profile` 1.2.1, plus the grant milestone split that `boundless-events` 2.1.0 adds (Tamp.20), which reaches mainnet in a later upgrade. Mainnet has run this code as events 1.8.0, with profile 1.2.1, since 17 October 2026 (Section 4.4). **Repository:** github.com/boundlessfi/boundless-contract **Changes in 1.3:** events 2.1.0 lets a grant pay its milestones fixed, unequal shares of each award (`ReleaseKind::Split`), adding Tamp.20. The testnet grant run was repeated on 2.1.0 with a split grant; the static analysis covers 2.0.0 and is due again for 2.1.0. **Changes in 1.2:** reworded for clarity, retired threats moved to their own table, flow steps renumbered after the participation removal, statuses limited to four defined values, and Section 4 reorganized. @@ -26,7 +26,7 @@ The model lists 77 threats across the six STRIDE categories, each tied to a step All ten Priority 1 threats, whose failure would be catastrophic, are mitigated and tested. -**Pending deployment** describes code, not threats: every fix since 1.7.0 is in the code under review but not yet on mainnet. Section 4.4 lists what the upgrade involves and what the backend must ship first. +**Pending deployment** describes code, not threats. Every fix since 1.7.0 reached mainnet on 17 October 2026 as events 1.8.0; Section 4.4 records that upgrade and what is still to come. ## 1. What are we working on? @@ -129,7 +129,7 @@ The threat tables in Section 2 refer to these steps by number. | Owner or manager | Can create, fund, select and cancel for their own event; may try to reclaim or redirect a pool. | Financial gain at builders' expense. | | Compromised backend | Can submit any transaction, corrupt off-chain data and leak secrets. With the master key it can sign as any managed wallet, never as the admin. | Theft from events run by managed wallets; limited by TB5. | | Compromised single admin signer | One of three signers; cannot meet the threshold alone. | A stepping stone to the quorum. | -| Compromised or coerced admin quorum | Full administrative control, including upgrades (after a 17,280-ledger timelock from 2.0.0; immediately on the 1.7.0 build still deployed). | Theft by replacing contract logic. | +| Compromised or coerced admin quorum | Full administrative control, including upgrades (after a 17,280-ledger timelock on every mainnet build since the 1.8.0 upgrade). | Theft by replacing contract logic. | | Token issuer | Can freeze or claw back balances under the asset's policy. | Compliance, not malice; treated as an availability event. | | Infrastructure providers (RPC, Didit) | Outages, stale or wrong data, forged messages. | Failure or compromise of a third party. | @@ -163,7 +163,7 @@ The model relies on these assumptions. A threat that needs one of them to fail i | Parameter | Value | |---|---| | Maximum fee | 1,000 bps (10%) | -| Upgrade timelock | 17,280 ledgers (about one day) on mainnet builds from 2.0.0, including its 1.8.0 mainnet label; 0 on testnet builds. The deployed 1.7.0 build has 0 (Section 4.4). | +| Upgrade timelock | 17,280 ledgers (about one day) on mainnet builds from 2.0.0, including its 1.8.0 mainnet label; 0 on testnet builds. The 1.7.0 build mainnet ran until 17 October 2026 had 0 (Section 4.4). | | Upgrade proposal expiry | 518,400 ledgers (about 30 days) | | Admin rotation and release validator acceptance windows | 120,960 ledgers (about 7 days) each | | Manager proposal expiry | 17,280 ledgers (about one day) | @@ -318,7 +318,7 @@ Of the 73 active threats, 10 are P1, 23 are P2, 23 are P3 and 17 are P4. ## 3. What are we going to do about it? -**Mitigated** means a control in the contracts or in operating procedure addresses the threat, and every contract-level control has a test. **Accepted** means the remaining risk is carried on purpose, for the reason given. Every control here is in the code under review; the fixes made in 2.0.0 reach mainnet only with the upgrade (Section 4.4). +**Mitigated** means a control in the contracts or in operating procedure addresses the threat, and every contract-level control has a test. **Accepted** means the remaining risk is carried on purpose, for the reason given. Every control here is in the code under review; the fixes made in 2.0.0 reached mainnet with the 1.8.0 upgrade (Section 4.4). | Threat | Treatment | Status | |---|---|---| @@ -340,7 +340,7 @@ Of the 73 active threats, 10 are P1, 23 are P2, 23 are P3 and 17 are P4. | **Tamp.7** | Where an overflow would matter (the event id counter, the contributor index, the winner index in `select_winners`, award and reservation totals), arithmetic is overflow-checked with a typed error. Counts that only go down (unclaimed prizes, the token list) stop at zero. The release build sets `overflow-checks = true`, so any remaining plain arithmetic stops the transaction instead of wrapping. | Mitigated | | **Tamp.8** | Each event keeps a running total of what it owes (`EventOwedTotal`). `select_winners` reserves each batch on top of it and refuses if the total would exceed `remaining_escrow`. Each payout lowers both together and fails safely if they disagree. `start_cancel` releases the reservation only when no prize is in its window and no grant award is owed (EoP.14). | Mitigated | | **Tamp.9** | `create_event` requires positive floors totalling at most the budget, and `select_winners` refuses any amount below its floor with `InvalidDistribution`. Positions without a floor take any positive amount, by design. | Mitigated | -| **Tamp.10** | `migrate_events` (admin only) converts records page by page from a stored cursor, up to 8 per call, and `migrate` refuses with `MigrationIncomplete` until all are converted. Old records are read with an exact copy of their original structure, and each floor is `total_budget × percent / 100`, exactly what that position would have been paid. Only the record is rewritten. The two records still in the old layout are completed mainnet bounties with every award paid, so nothing needs reserving again (Tamp.19). A test replays the full mainnet upgrade against captured mainnet storage, and testnet was migrated across 185 events in 24 calls. | Mitigated | +| **Tamp.10** | `migrate_events` (admin only) converts records page by page from a stored cursor, up to 8 per call, and `migrate` refuses with `MigrationIncomplete` until all are converted. Old records are read with an exact copy of their original structure, and each floor is `total_budget × percent / 100`, exactly what that position would have been paid. Only the record is rewritten. The two records that were still in the old layout, completed mainnet bounties with every award paid, were converted in the 17 October 2026 upgrade with nothing reserved again (Tamp.19). A test replays the full mainnet upgrade against captured mainnet storage, and testnet was migrated across 185 events in 24 calls. | Mitigated | | **Tamp.11** | The Soroban host does not allow re-entry during a call, and supported tokens are admin-approved. Both payout paths and `claim_refund` also record the payment, including the operation marker, before transferring, so a call-back would find it already recorded. | Mitigated | | **Tamp.12** | A selection listing a position twice is refused. Across selections, the stored award for each position stops it being awarded again. A grant selects once, with one award per recipient (Tamp.16). `claim_prize` refuses an award already paid with `PrizeAlreadyClaimed`. | Mitigated | | **Tamp.13** | Every call that moves money or changes a record carries a 32-byte operation id; a marker under the signing address and that id rejects repeats with `OpAlreadySeen`. Manager rotation and admin functions rely on Soroban's authorization nonces. A marker lasts at least 17,280 ledgers (about one day), longer than backend retries, after which the original signature has expired and a replay needs a new one. A refund batch size of zero is refused with `InvalidBatchSize`, so a stranger cannot burn the backend's next operation id. Test: `an_empty_refund_batch_cannot_burn_the_crank_op_id`. | Mitigated | @@ -388,7 +388,7 @@ Of the 73 active threats, 10 are P1, 23 are P2, 23 are P3 and 17 are P4. | **EoP.7** | The owner must sign the same transaction as the co-signer, the money goes only to the owner, and the amount is calculated from the pool. The backend prepares the transaction only for the signed-in owner. | Mitigated | | **EoP.8** | The manager can select, cancel and rotate. It cannot change the owner (no function exists), receive refunds, pay milestones, or change floors. It can award any address, including its own: judging is off chain by design, and the owner chooses whom to delegate to. `ManagerChanged` is on chain before any selection. | Accepted | | **EoP.9** | The recipient must hold an award in the grant roster, and the amount comes from that award (grants) or from `remaining_escrow / milestones_left` (crowdfunding), never from the caller. Each milestone is settled once and must be in range. | Mitigated | -| **EoP.10** | Mainnet builds have a 17,280-ledger upgrade timelock from events 2.0.0 (1.8.0 on mainnet) and profile 1.2.1; testnet builds keep zero, and `deploy_mainnet.sh` always builds without the testnet setting. The deployed 1.7.0 and 1.2.0 builds have no timelock until the upgrade in Section 4.4, which itself runs without one. Tests: `apply_upgrade_waits_out_the_timelock`, `apply_upgrade_waits_out_the_timelock_profile`. | Mitigated | +| **EoP.10** | Mainnet builds have a 17,280-ledger upgrade timelock from events 2.0.0 (1.8.0 on mainnet) and profile 1.2.1; testnet builds keep zero, and `deploy_mainnet.sh` always builds without the testnet setting. The 1.7.0 and 1.2.0 builds mainnet ran until 17 October 2026 had none, so the upgrade in Section 4.4 ran without one. Tests: `apply_upgrade_waits_out_the_timelock`, `apply_upgrade_waits_out_the_timelock_profile`. | Mitigated | | **EoP.11** | `admin_slash_reputation` requires the admin and a non-empty reason, emits `AdminReputationSlashed`, and is the only way to lower a score. | Mitigated | | **EoP.12** | Both migration functions require the admin, the cursor only moves forward, and `migrate` runs once per version. | Mitigated | | **EoP.13** | After the first binding, a change is `propose_events_contract`, a 17,280-ledger wait, then `accept_events_contract` within 7 days, all admin-only and all emitting events. The wait only means something once the profile's upgrade timelock is deployed (EoP.10). | Mitigated | @@ -421,7 +421,7 @@ None. Every threat is mitigated, accepted with a reason, or retired. ### 4.2 Closed in 2.0.0 -These fixes are in the code under review. None of them protects mainnet until the upgrade in Section 4.4. Tests for each are named in Section 3. +These fixes are in the code under review and have protected mainnet since the 1.8.0 upgrade on 17 October 2026 (Section 4.4). Tests for each are named in Section 3. | ID | Issue | Fix | Error or event | |---|---|---|---| @@ -461,9 +461,9 @@ These threats concerned on-chain applications and submissions, which 2.0.0 remov | Item | What is needed | |---|---| -| Mainnet upgrade | Mainnet runs events 1.7.0 and profile 1.2.0. Every fix in Section 4.2 takes effect only after the admin multisig upgrades both, to events 1.8.0 (the 2.0.0 code, tag `events-v1.8.0`) and profile 1.2.1, following `docs/upgrade-runbook.md` Section 8. The deployed builds have no timelock, so this upgrade applies immediately; every later one waits 17,280 ledgers (EoP.10). | -| Old-layout records (Tamp.10) | Two mainnet event records cannot be read until converted. Both are completed events with no escrow, so no money is affected. Mainnet holds eight events, so one `migrate_events` call during the upgrade converts the two and leaves the others unchanged, and `migrate` then stamps the version. | -| Backend changes that must ship first | Page winner and contributor reads at 90 or fewer and run refunds in batches of 15 or fewer (DoS.17); pages of 100 and batches of 25 already fail on the deployed build for large events. Split hackathon selections above 20 winners (DoS.17). Call `forfeit_milestone` when a grant milestone is rejected, or the grant can never be cancelled (EoP.14). Support `claim_refund` for refunds set aside (DoS.14). Stop calling the participation functions that 2.0.0 removed. | +| Mainnet upgrade | Done on 17 October 2026: the admin multisig upgraded mainnet to events 1.8.0 (the 2.0.0 code, tag `events-v1.8.0`) and profile 1.2.1, following `docs/upgrade-runbook.md` Section 8, which lists every transaction. Every fix in Section 4.2 is live there, and every later upgrade waits 17,280 ledgers (EoP.10). | +| Old-layout records (Tamp.10) | Done: one `migrate_events` call during the upgrade converted the two completed, zero-escrow records, the other six were left unchanged, and `migrate` stamped 1.8.0. All eight events read. | +| Backend changes that had to ship first | Shipped before the upgrade (boundless-nestjs #867 and #874). Page winner and contributor reads at 90 or fewer and run refunds in batches of 15 or fewer (DoS.17); pages of 100 and batches of 25 fail on large events. Split hackathon selections above 20 winners (DoS.17). Call `forfeit_milestone` when a grant milestone is rejected, or the grant can never be cancelled (EoP.14). Support `claim_refund` for refunds set aside (DoS.14). Stop calling the participation functions that 2.0.0 removed. | | Events 2.1.0 | Grant milestone splits (Tamp.20) run on testnet from 8 October 2026 and reach mainnet in a separate upgrade after 1.8.0, which waits the 17,280-ledger timelock (`docs/upgrade-runbook.md` Section 9). | ### 4.5 Accepted risks @@ -524,7 +524,7 @@ DoS.9 is mitigated, but one case remains: if a manager's key is lost after the f | `set_fee_bps`, `set_fee_account`, `set_profile_contract` | events | admin | Fee at most 10%; single step. | | `pause`, `unpause` | events, profile | admin | Admin functions keep working while paused. | | `register_supported_token`, `deregister_supported_token` | events | admin | | -| `propose_upgrade`, `apply_upgrade`, `cancel_pending_upgrade` | events, profile | admin | 17,280-ledger timelock on mainnet builds (none on the deployed 1.7.0 build); 30-day expiry. | +| `propose_upgrade`, `apply_upgrade`, `cancel_pending_upgrade` | events, profile | admin | 17,280-ledger timelock on mainnet builds; 30-day expiry. | | `migrate_events` | events | admin | At most 8 events per call. | | `migrate` | events, profile | admin | Once per version; events refuses with `MigrationIncomplete` until every record is converted. | | `set_events_contract` (first binding) | profile | admin | Only when no binding exists. | diff --git a/docs/upgrade-runbook.md b/docs/upgrade-runbook.md index c2e7915..ca1262b 100644 --- a/docs/upgrade-runbook.md +++ b/docs/upgrade-runbook.md @@ -3,9 +3,9 @@ How to upgrade a live `boundless-events` or `boundless-profile` contract. On mainnet every admin step is a 2-of-3 multisig transaction that `./deploy_mainnet.sh` prepares and checks but never submits; on testnet -`./deploy_and_upgrade.sh` signs with the local admin identity. Section 8 is -the specific procedure for taking mainnet from events 1.7.0 and profile 1.2.0 -to events 1.8.0 and profile 1.2.1. +`./deploy_and_upgrade.sh` signs with the local admin identity. Section 8 +records how mainnet went from events 1.7.0 and profile 1.2.0 to events 1.8.0 +and profile 1.2.1 on 17 October 2026. Related: `docs/contract-ops-runbook.md` (signing and submitting a multisig transaction), `docs/admin-custody-policy.md` (who signs what), @@ -32,9 +32,10 @@ There is no `upgrade()` entry point. Both contracts expose `version()`, | `PENDING_UPGRADE_TTL_LEDGERS` | 518,400 | about 30 days | same | The timelock that applies is the one compiled into the wasm that is live when -you propose, not the one you are proposing. The deployed mainnet builds, -events 1.7.0 and profile 1.2.0, were compiled with 0 for the August rollout; -events 1.8.0 and profile 1.2.1 restore 17,280, as does every later build. +you propose, not the one you are proposing. Mainnet has run events 1.8.0 and +profile 1.2.1 since 17 October 2026, both compiled with 17,280, so every +mainnet upgrade now waits about a day. The builds before them, events 1.7.0 +and profile 1.2.0, were compiled with 0 for the August rollout. Pausing events blocks every event-lifecycle call: `create_event`, `add_funds`, `select_winners`, `claim_prize`, `claim_milestone`, `forfeit_milestone`, @@ -226,7 +227,7 @@ contractmeta version differs from the one you pass, uploads, and proposes. Abandoning an upgrade before it is applied: cancel, then unpause. If the live contract's migration marker already trailed its version before you started, -as it does on mainnet events today, `prepare-unpause-*` refuses; set +`prepare-unpause-*` refuses; set `UNPAUSE_UNMIGRATED=1` for that one command, and only for an upgrade that was never applied. @@ -273,7 +274,30 @@ grants and held-refund claims for events 2.0.0 or later, so both stay off on mainnet until a 2.x upgrade. A refund 1.8.0 holds back during a cancellation stays claimable on chain and can be paid once that upgrade lands. -### 8.1 Where mainnet stands +Done on 17 October 2026 between 23:09 and 23:43 UTC. Both contracts report +the new version and wasm, are migrated to it, have nothing queued and are +unpaused, and all eight events read, including the two converted records. +Events was paused for about 19 minutes. Each transaction carries two +signatures: + +| Step | Transaction | +|---|---| +| Upload profile 1.2.1 wasm `8a8a1208…` | `dab6a185a849b681303db604adeb1135277e30c155d26d9c11cdf5a943dfd4ec` | +| Upload events 1.8.0 wasm `acf70ca0…` | `d334f95fc496f7de74e25755936dca44bc80e2fb671c67b76234949dd0451563` | +| Profile propose | `be1863a74230011fd8da6fc6ca2dfc15315255cba380a4b850bb0c65aae8aa95` | +| Profile apply | `54a3fb7a21a06d8558feac03b9e96d07eee45a9e57eea2c06f0e05ffe9f1d510` | +| Profile migrate | `f3f797f3ac886cbe76d384b21fe746f5839a4bfa8cdab7709482fc20560eb560` | +| Events pause | `f61294ae79c91347f51a7bb37179bd9901e7a0e27c9677b9ca4fb507bb2c06cb` | +| Events propose | `0403fd75579d6f8f630f51beaea7dd50a9a01d0348b4a0a41a95d34845f93883` | +| Events apply | `bd098ce7977ac8c9a5a4b0c1974e3437a5841f9966d25c6842d8ddee1b6bd4a0` | +| Events `migrate_events(8)`, 0 left | `adfee91d5970442c1ac4e5fd778047482f6e9c15a5a3550eafc6a9220e58210d` | +| Events migrate | `6387190529b5fdfdfb5d53ec53f67ce01cd0b55ee1d7d9d874d52c9f70850397` | +| Events unpause | `aaa3b32afdfdf5544bc9f575f042ac620580d32f4d89a14154175a87e1057a18` | + +The two uploads were signed by a funded CLI identity; every other step by the +admin multisig. The rest of this section is the procedure as it was run. + +### 8.1 Where mainnet stood before the upgrade | | events | profile | |---|---|---| @@ -406,8 +430,8 @@ wasm `c742cb96…` uploaded and proposed (`fc713ba2…`), applied (`d3f1e8c0…` 10 `migrate_events` pages, then `migrate` stamped 2.1.0 (`d91f1ddc…`). Grants created on 2.0.0 read and pay as before. -Mainnet takes 2.1.0 only after Section 8 has put it on 1.8.0, as a separate -upgrade that waits the 17,280-ledger timelock. That upgrade also turns on +Mainnet, on 1.8.0 since Section 8, takes 2.1.0 as a separate upgrade that +waits the 17,280-ledger timelock. That upgrade also turns on grants and held-refund claims, which the backend allows from 2.0.0. The backend sends a split only to a contract that reports 2.1.0 or later, so nothing has to ship first.