KIP 160: An Update of Treasury Fund Rebalancing Source

AuthorOllie, Yumiel, Ian, Aidan
StatusFinal
TypeStandards Track
CategoryCore
Created2024-04-22
Requires 103

Simple Summary

An update of treasury fund rebalancing.

Abstract

The treasury fund rebalancing is updated, resulting in updates to the treasury rebalance contract v2 and the related core logic.

Motivation

According to KIP-103, the treasury rebalancing refers to the act of burning existing fund balances and minting new funds. This event happens at a reserved time, such as a hard fork block number. Through the TreasuryRebalance contract, the disclosure of the treasury fund activities can be conducted transparently and verifiably. However, there are a few shortcomings of KIP-103; (1) it only permits the decrease in the total balance of the funds, (2) its rebalance blocknumber or memo is immutable. Once these values are set, they cannot be changed, even if they are incorrectly set.

To address those above, this proposal made some improvements. First of all, this proposal expands to the general cases so that we don’t have to consider whether the final result is burn or mint. The other improvement is about making rebalanceBlocknumber in the RebalanceContract editable. The rebalanceBlocknumber should be matched with the related hardfork block number.

Specification

Before proceeding, this proposal will first organize and clarify the terminology. Here, the previous/new fund addresses are referred to as Zeroed, Allocated unlike in KIP-103. Retired, Newbie were ambiguous and did not clearly indicate that the previous fund balance is zeroed and the new fund balance is allocated, which degraded the readability.

To consider both total burn/total mint cases

In KIP-103, rebalancing is limited to cases where the total balance decreases. This proposal aims to generalize the process by removing the requirement that the total balance of Zeroeds should exceed the total minting amount of Allocateds. Consequently, the checking code is eliminated from the contract and core code.

In the TreasuryRebalanceV2 contract, the finalizeApproval method has been revised as follows. Initially, this require statement, require(getTreasuryAmount() < sumOfZeroedBalance()), was present. However, it has been removed to accommodate all cases.

   /**
    * @dev finalizeApproval sets the status to Approved,
    *      After this stage, approvals will be restricted.
    */
   function finalizeApproval()
        public
        onlyOwner
        onlyAtStatus(Status.Registered)
    {
        checkZeroedsApproved();
        status = Status.Approved;
        emit StatusChanged(status);
    }

The next validation has also been removed from the core logic. It previously ensured that the total balance of Zeroeds was greater than the total balance of Allocateds. However, it has been removed to support all cases.

  • totalZeroedAmount >= totalAllocatedAmount

To enable editing of RebalanceBlocknumber defined in treasury rebalance contract

Within the treasury rebalance contract, there exists a storage value named RebalanceBlocknumber, which ideally should correspond to the associated hard fork block number. Despite being able to update the hard fork block number, the RebalanceBlocknumber field was immutable. To align the behavior of this field with that of the hard fork block number, this proposal advocates for making the RebalanceBlocknumber field editable.

The next method is added to the TreasuryRebalanceV2 contract.

    /**
     * @dev updates rebalance block number
     * @param _rebalanceBlockNumber is the updated target block number of the execution the rebalance in Core
     */
    function updateRebalanceBlocknumber(
        uint256 _rebalanceBlockNumber
    ) public onlyOwner {
        require(block.number < rebalanceBlockNumber, "current block shouldn't be past the currently set block number");
        require(block.number < _rebalanceBlockNumber, "rebalance blockNumber should be greater than current block");
        rebalanceBlockNumber = _rebalanceBlockNumber;
    }

To enable editing of Memo defined in treasury rebalance contract

The execution result of treasury fund rebalancing V2 is printed as an INFO-level log on each node. This log is added as a memo to the contract, making it permanently viewable. However, once finalized, any modifications to the memo value were restricted even if set incorrectly. This proposal aims to seperate the memo setting process from the contract finalization, allowing the memo value to be set repeatedly.

The setPendingMemo is added and finalizeContract of the TreasuryRebalanceV2 contract is revised as follows.

    string public pendingMemo; // temporary storage for memo
    string public memo; // result of the treasury fund rebalance

    /**
     * @dev sets the pendingMemo of the Contract. Once finalized, the memo cannot be modified.
     * @param _memo is the result of the rebalance after executing successfully in the core.
     */
    function setPendingMemo(string memory _memo) external onlyOwner onlyAtStatus(Status.Approved) {
        pendingMemo = _memo;
    }

    /**
     * @dev sets the status of the contract to Finalize. Once finalized the storage data
     * of the contract cannot be modified. Also it sets the memo with pendingMemo value.
     * It will be used for public disclosure.
     * Can only be called by the current owner at Approved state after the execution of rebalance in the core
     */
    function finalizeContract() external onlyOwner onlyAtStatus(Status.Approved) {
        require(block.number > rebalanceBlockNumber, "Contract can only finalize after executing rebalancing");
        require(bytes(pendingMemo).length > 0, "no pending memo, cannot finalize without memo");

        memo = pendingMemo;
        status = Status.Finalized;
        emit Finalized(memo, status);
    }

Result

In the Kip-103 memo format, the zeroed item showed the balance of the zereods before rebalancing, while the allocated item showed the balance of the allocateds after rebalancing. It was insufficient for interpreting the rebalancing results. Therefore, this proposal changes the format to display balances both before and after rebalancing. The newly proposed memo format is shown below with balance/amount in kei (peb of Klaytn).

Format

{
  "before": { 
    "zeroed": { "0xZeroedAddress1": [balance of zeroedAddress1 before rebalance],  "0xZeroedAddress2": [balance of zeroedAddress2 before rebalance], ...},
    "allocated": {  "0xAllocatedAddress1": [balance of AllocatedAddress1 before rebalance],  "0xAllocatedAddress2": [balance of AllocatedAddress1 before rebalance], ...}
  },
  "after": {  
    zeroed": {  "0xZeroedAddress1": [balance of zeroedAddress1 after rebalance], "0xZeroedAddress2": [balance of zeroedAddress2 after rebalance], ...},
    "allocated": { "0xAllocatedAddress1": [balance of AllocatedAddress1 after rebalance],  "0xAllocatedAddress2": [balance of AllocatedAddress1 after rebalance], ...}
  },
  "burnt": [burnt amount],
  "success": [true/false]
}

Rationale

While executing the core logic of the KIP-103 treasury rebalance, the unintended increase in the nonce of the ‘0x0’ address occured. In KIP-160, this issue can be resolved by replacing the usage of kip103ContractBackend with BlockchainContractBackend. For your reference, ContractBackend facilitates the interaction between the core and contracts.

The following pseudocode illustrates how to define a contract callers for each rebalancing.

RebalanceTreasury() {
	if current block number is equal to KIP-160 hard fork block number {
		// Define the caller for the NewTreasuryRebalanceV2 contract 
		caller, err = rebalance.NewTreasuryRebalanceV2Caller(chain.Config().Kip160ContractAddress, backends.NewBlockchainContractBackend(chain, nil, nil))
	}
	if current block number is equal to KIP-103 hard fork block number {
		// Define the caller for the NewTreasuryRebalance contract 
		caller, err = rebalance.NewTreasuryRebalanceCaller(chain.Config().Kip103ContractAddress, &Kip103ContractCaller{state: state, chain: chain, header: header},
	}
	if err != nil {
		stop
	}
}

Test cases

TBD

Reference

TBD