Assessment reports>Chirp Network>Discussion>Design requirements evaluation

Design requirements evaluation

In addition to checking for possible security issues, we evaluated several functional requirements derived from a design document shared by Chirp Wireless.

Administrative functionality

The design document shared by Chirp Wireless explicitly required the following administrative features.

Configurable minting schedule

The contracts should implement a configurable token emission and distribution schedule. The configuration can be set before the contracts are deployed and also changed at a later date. Therefore, our evaluation is limited to the functional correctness of the implementation of the minting schedule and not to the schedule configuration (including time durations and asset amounts).

The contracts allow to make changes to the schedule via the set_entry, insert_entry, and remove_entry functions. These functions require to provide a ScheduleAdminCap to authorize the action.

The minting schedule is defined as a series of ScheduleEntry objects:

public struct ScheduleEntry<phantom T> has store, drop {
    /// Start time for this entry in milliseconds, indicating when the
    /// entry was activated. This field can be null if the entry has not
    /// been activated yet.
    start_time_ms: std::option::Option<u64>,
    /// Current count of completed minting epochs within this entry. Each epoch
    /// represents a single minting operation under the defined parameters.
    current_epoch: u64,
    /// Defines the operational parameters for minting including the period,
    /// distribution pools, and the amounts to be minted for each pool over
    /// the specified number of epochs.
    stage: Stage<T>,
}

Each ScheduleEntry describes one stage in the schedule and consists of a number of epochs. Every epoch in a given stage has the same duration and distributes a configurable amount of assets to one or more pools. The epoch configuration is described in the Stage structure:

public struct Stage<phantom T> has store, copy, drop {
    /// Time shift in milliseconds from the end of the previous stage,
    /// setting the delay before this stage activates.
    timeshift_ms: u64,
    /// Number of epochs this stage will persist, each epoch represents a
    /// discrete minting event.
    number_of_epochs: u64,
    /// Duration of each epoch within this stage in milliseconds.
    epoch_duration_ms: u64,
    /// Addresses of the pools where the minted tokens are distributed.
    pools: vector<String>,
    /// Specific amounts of tokens to mint per pool per epoch.
    amounts: vector<u64>,
}

The timeshift_ms field can be set to force a delay between the end of the last epoch of the previous stage and the start of the first epoch of the current stage.

Administrative roles' ownership flexibility and security

Administrative roles of the contracts must be transferable.

The ScheduleAdminCap capability and the package-upgrade capabilities obtained by the deployer are freely transferable.

Configurable liquidity pool addresses

The contracts must support changing the addresses of the liquidity pools.

This is implemented by the set_address_pool function, which can be used to update the address of any liquidity pool by referring to it via its name. The action is authorized by providing a ScheduleAdminCap capability.

Multi-sig compatibility

Administrative capabilities and liquidity pools must be compatible with multi-sig addresses.

Sui supports k-of-n signatures natively, and it is possible to transfer capabilities to these multi-sig addresses; on-chain modules are oblivious to this and do not distinguish between regular signers and multi-sig signers. For more information, refer to Sui documentation pages on multi-sig:

  • https://docs.sui.io/guides/developer/cryptography/multisig

  • https://docs.sui.io/concepts/cryptography/transaction-auth/multisig

Pools should be able to be governed by a contract

In the version of the code under review, pools are configured as fixed regular addresses. It is an explicit requirement that pools should be able to be governed by a smart contract.

This is possible via the Sui transfer-to-object feature. Since pools take ownership of Coin<T> objects shared using transfer::public_transfer, the address of a pool can be replaced with the address of any object. The module defining the pool object can call receive to obtain the transferred Coin<T> and implement any access-control logic and Coin redistribution policy as desired.

Upgradeability

The contracts should be future proof and support being upgraded.

Sui contracts are immutable in nature; once published, a given version of the code of a module is permanently stored and always available for any user. However, newer versions of a package can be published and used to access the previous state of the program. Chirp correctly implements the most common and recommended Sui upgradeability pattern, versioned shared objects. In this pattern, objects storing the shared state of the contract are augmented with a version field, which is checked by the contract code to ensure it matches the expected version. New contract upgrades change the version field, effectively disabling older versions of the code and forcing the adoption of the newest published version.

For more information about upgradeability, refer to https://docs.sui.io/concepts/sui-move-concepts/packages/upgrade#versioned-shared-objects.

Keeper rewards workflow

The design requirements specify that keeper rewards are to be minted according to a configurable schedule that regulates the rate of emission and the distribution of the minted $CHIRP tokens to the various pools.

Minting the tokens allocated to any given epoch is an unpermissioned action that can be initiated by anyone by calling the mint function. The amount of tokens allocated as keeper rewards by the schedule are to be sent to a pool address, which is controlled by Chirp Network. Chirp will distribute rewards to individual keepers according to off-chain calculations.

This is achieved via the deposit function, which stores a given amount of coins associated to the address of the respective recipient. Coins held by the depository can be claimed fully or in part, exclusively by their recipient, by invoking claim. Multiple deposits can occur before a recipient claims their assets.

The contracts correctly implement the flow described by the requirements.

Zellic © 2025Back to top ↑