Skip to content

Sweat-Foundation/near-versionist

Repository files navigation

near-versionist

A secure upgrade framework for NEAR smart contracts with proposal workflows, custom authorization, and cooldown periods.

Overview

near-versionist enables controlled, secure upgrades for NEAR smart contracts through a proposal-based workflow. Instead of immediately deploying new code, changes go through a propose → review → accept cycle with configurable authorization and mandatory cooldown periods.

Features

  • Proposal Workflow - New code must be proposed before deployment, allowing review
  • Custom Authorization - Define who can propose and who can accept updates via the UpdatableAuth trait
  • Cooldown Periods - Mandatory waiting time before accepting proposals
  • Checksum Verification - SHA-256 verification prevents race conditions during acceptance
  • Optional Init Calls - Execute initialization functions after upgrade

Installation

Add to your Cargo.toml:

[dependencies]
near-versionist = "0.2"

Quick Start

use near_sdk::{near, AccountId};
use near_versionist::{updatable, UpdatableAuth};

#[updatable(cooldown = 604800000)]  // 7-day cooldown
#[near(contract_state)]
pub struct Contract {
    owner_id: AccountId,
    maintainer_id: AccountId,
}

impl UpdatableAuth for Contract {
    fn is_authorized_to_propose_update(&self, account_id: &AccountId) -> bool {
        &self.maintainer_id == account_id
    }

    fn is_authorized_to_accept_update(&self, account_id: &AccountId) -> bool {
        &self.owner_id == account_id
    }
}

Generated Methods

The #[updatable] macro generates these methods on your contract:

Method Description
get_update_proposal() View current proposal (checksum, timestamp, cooldown)
propose_update(code) Propose new contract code
propose_update_with_init_call(code, method, args, gas) Propose with post-upgrade initialization
accept_update(checksum) Accept and deploy (after cooldown)
reject_update() Reject current proposal

Demo

1. Propose an Update

# Build your new contract
cargo near build

# Encode the WASM as base64
CODE=$(base64 -i target/near/my_contract.wasm)

# Propose the update
near call my-contract.near propose_update \
  --args "{\"code\": \"$CODE\"}" \
  --accountId maintainer.near \
  --gas 300Tgas

2. Review the Proposal

near view my-contract.near get_update_proposal

Output:

{
  "checksum": "a1b2c3d4...",
  "init_call": null,
  "proposed_at": 1706000000000000000,
  "cooldown": 604800000
}

3. Accept After Cooldown

near call my-contract.near accept_update \
  --args '{"checksum": "a1b2c3d4..."}' \
  --accountId owner.near \
  --gas 300Tgas

Authorization

Implement the UpdatableAuth trait to control access:

impl UpdatableAuth for Contract {
    // Who can propose new code
    fn is_authorized_to_propose_update(&self, account_id: &AccountId) -> bool {
        self.maintainers.contains(account_id)
    }

    // Who can accept/reject proposals
    fn is_authorized_to_accept_update(&self, account_id: &AccountId) -> bool {
        &self.owner_id == account_id
    }
}

Default implementation allows all accounts - always override for production use.

Project Structure

near-versionist/
├── near-versionist/        # Runtime library with UpdatableAuth trait
├── near-versionist-macros/ # #[updatable] procedural macro
└── examples/contract/      # Example contract implementation

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages