This document defines semantic versioning (MAJOR.MINOR.PATCH) for all Grainlify contracts, tracks breaking changes, and documents migration and compatibility expectations across versions.
Contracts covered:
- grainlify-core
- program-escrow
- bounty-escrow (placeholder until stabilized)
- MAJOR: Incompatible API/storage changes and/or required migration
- MINOR: Backward-compatible features or optional fields
- PATCH: Backward-compatible bug fixes or docs/tests only
All contracts expose both a numeric version for on-chain checks and a semantic string for off-chain tooling. Numeric encoding policy: major10_000 + minor100 + patch. Example: 1.2.3 => 10203.
Current: 1.0.0 (numeric 10000) Planned next: 2.0.0 (numeric 20000)
| SemVer | Numeric | Date | Description | Breaking |
|---|---|---|---|---|
| 1.0.0 | 10000 | TBA | Initial release with admin + multisig upgrade hooks and version tracking | No |
| 1.1.0 | 10100 | TBA | Add migration state/events and PreviousVersion, no storage schema changes | No |
| 2.0.0 | 20000 | TBA | Introduce explicit migration API and compatibility checks, require migrate() call | Yes |
| From | To | Migration | Function | Notes |
|---|---|---|---|---|
| 1.0.x | 1.1.x | No | N/A | Fully compatible; features optional |
| 1.x | 2.0.0 | Yes | migrate_v1_to_v2 | State journal introduced; emits events |
| 2.0.x | 2.1.x | No | N/A | Backward compatible feature additions |
- 1.x -> 2.0.0
- Deploy new WASM, then call migrate(target=2, hash)
- Verify via get_migration_state(); ensure to_version == 2
- Update off-chain indexers to listen to (migration) events
- Note:
migrate()accepts raw sequential version targets (e.g.2), whilerequire_min_version()andget_version_numeric_encoded()use numeric-encoded values (e.g.20000).
Breaking changes: require explicit migrate() before using new features that rely on migrated state.
Current: 1.0.0 (numeric 10000)
| SemVer | Numeric | Date | Description | Breaking |
|---|---|---|---|---|
| 1.0.0 | 10000 | TBA | Initial public release of program escrow | No |
| 1.0.1 | 10001 | TBA | Documentation/events clarifications; no storage changes | No |
| From | To | Migration | Function | Notes |
|---|---|---|---|---|
| 1.0.x | 1.0.y | No | N/A | Patch only |
- 1.0.0 -> 1.0.1
- No on-chain migration required; upgrade WASM only
- Upload the new WASM and record
new_wasm_hash. - For grainlify-core single-admin upgrades, call
schedule_upgrade(new_wasm_hash)and wait until the returnedexecutable_attimestamp. The default delay is 86,400 seconds; admins may configure a delay no lower than 300 seconds withset_upgrade_delay(delay_seconds). - Execute
upgrade(new_wasm_hash)only after the timelock is ready. The scheduled hash must match the execution hash. - If migration is required, call migrate(target_sequential_version, migration_hash)
- Verify with get_version(), get_migration_state()
- Update clients to enforce minimal compatible version
// Schedule upgrade, execute after the timelock, then migrate
auth_admin.require_auth();
let scheduled = contract.schedule_upgrade(&env, &new_wasm_hash);
assert!(scheduled.executable_at >= scheduled.scheduled_at);
// Wait until ledger timestamp >= scheduled.executable_at.
contract.upgrade(&env, &new_wasm_hash);
let hash = BytesN::from_array(&env, &[0u8;32]);
contract.migrate(&env, &2, &hash);
assert_eq!(contract.get_version(&env), 2);- upgrade scheduled event: (wasm_hash, scheduled_at, executable_at, delay_seconds)
- upgrade executed event: (wasm_hash, executed_at, previous_version)
- migration event: (from_version, to_version, timestamp, migration_hash, success)
- monitoring metrics emitted for upgrade/migrate
- Off-chain SDKs should enforce minimal version using numeric encoding
- On-chain functions may guard behavior with require_min_version(min_semver_numeric)
- 2.0.0 (core): Require explicit migration; introduce MigrationState recording as hard requirement for post-2.x features.
- WASM hashes should be recorded post-deploy in this document under the appropriate version row when known.