YIP-3: Redirecting uXTZ interest rate payments to a new uXTZ/XTZ flat curve, setting trading fees on the old uXTZ/XTZ flat curve to 0%

YIP-3 - Redirecting uXTZ interest rate payments to a new uXTZ/XTZ flat curve, setting trading fees on the old uXTZ/XTZ flat curve to 0%

Author:
Markus, @m.laeng (Forum), @djangobits (Twitter)

Related posts:
yyXTZ post mortem: Postmortem Incident 8 | youves documentation

Related forum post:

First publication date:
2023/09/29

Summary

The LP token contract for yyXTZ, which represents the liquidity token in the current uXTZ/XTZ flat curve pool, contains a bug that can result in yyXTZ tokens becoming locked when they are sent to a smart contract unable to invoke the allow entrypoint of the yyXTZ token contract. This issue is detailed in post mortem 8, Postmortem Incident 8 | youves documentation.

As there is no way to replace only the LP token contract, it is necessary to replace the entire uXTZ/XTZ flat curve contract and deploy a corrected LP token contract alongside it.

Furthermore, in order to recover the funds locked during the incident outlined in post mortem 8, it is essential to reduce the trading fees of the old uXTZ/XTZ pool to 0%.

This YIP proposes to redirect the interest rate payments to a newly deployed uXTZ/XTZ flat curve pool and to set the trading fees on the old uXTZ/XTZ pool to 0%.

Rationale

Replacement of the uXTZ/XTZ flat curve pool

The existing uXTZ/XTZ pool, with the address KT1BFXgczFte2zftCTg7tL6Qk2capsFg6UFS, was originally deployed with a flawed LP token contract, identified as KT1NtfNBPAo8UrcMexMyrKR5WCHb3VRiocvx (the yyXTZ token contract). As elaborated in post mortem 8 (Postmortem Incident 8 | youves documentation), the bug within the current yyXTZ token contract results in the following consequences:

For all calls to the transfer entrypoint, the calling address must first request an initial approval from the yyXTZ contract by invoking its approval entrypoint. While this step is automatically performed by the most common Tezos wallets, Tezos smart contracts cannot trigger this entrypoint unless they were specifically designed to do so. Consequently, yyXTZ tokens that are sent to a contract, such as Quipuswap’s KT1VVLLnLLZEwewB6g8fj7BMDsnsaU72Vatx, become immovable since this contract lacks the capability to invoke the approval entrypoint.

Moreover, it’s not possible to replace the faulty LP token contract while retaining the existing flat curve contract. Consequently, the entire uXTZ/XTZ pool must be redeployed with an improved LP token contract. Liquidity providers will be required to withdraw their liquidity from the current pool and provide it to the new one.

This proposal intends to designate a newly deployed uXTZ/XTZ pool as the recipient of uXTZ interest rate payments. This new pool will feature an updated LP token contract, now referred to as yTEZ (rebranded from the faulty yyXTZ token).

The new uXTZ/XTZ token contract with it’s fixed LP token contract has been deployed and was thoroughly tested on ghostnet:

Ghostnet deployment of the revised uXTZ/XTZ pool and it’s LP token contract

Flat curve contract on mainnet: KT1BCpUHbBCGzdLphaWrcNdh8zDXn6VgFurE
It’s LP token contract on ghostnet: KT1A4RLDk9m5THiRDKWk6J9DFAWdfhUaFh8M

Mainnet deployment of the revised uXTZ/XTZ pool and it’s LP token contract

The new uXTZ/XTZ contract has also been deployed to mainnet and has also been fully tested (including sending yTEZ tokens from smart contracts):

Flat curve contract on mainnet: KT1SPUvH5khHtirTEVeECiKrnh4FFXxWZ6ui
It’s LP token contract on ghostnet: KT1NodvAh8uTny1uU35rLAErzkTG66uxKNiM

This is the new icon for yTEZ, after rebranding:

yTEZ

The latest code for this flat curve and its lp token contract is published here:
https://github.com/youves-com/youves-smart-contract/blob/main/contracts/swap/

Set the trading fees on the faulty uXTZ/XTZ

Furthermore, this proposal requests a reduction of the trading fees on the existing flat curve pool at KT1BFXgczFte2zftCTg7tL6Qk2capsFg6UFS from the current rate of 0.35% to 0%. This adjustment is essential for an upcoming recovery operation, which involves a flash mint operation to drain the XTZ locked in the old pool due to the liquidity tokens being unrecoverably stuck in the Quipuswap pool.

Additional details about this recovery operation will be shared soon, but in short it will work like this:

  1. A new smart contract, specifically designed to execute this recovery operation, will be designated as an additional admin for the uXTZ engine.
  2. The smart contract will carry out a single operation group, triggered by the youves keyholders, consisting of the following steps:
  3. Retrieve the current balance of all remaining uXTZ within the pool.
  4. Mint an exact equivalent of this balance into the Youves Operations Multisig wallet, which is controlled by three Youves contributors (Ale, Florin, Markus). This balance will be later distributed to the remaining liquidity providers.
  5. Mint a huge amount of uXTZ
  6. Perform a swap operation within the uXTZ/XTZ pool, draining the maximum amount of XTZ from the pool and transferring it to the Youves Operations Multisig Wallet.
  7. Burn both the substantial amount created in step 5 and the balance determined in step 3 from the pool, which comprises all the remaining uXTZ within the pool.

It is crucial to note that this operation group will be executed atomically, meaning that it will either succeed entirely or fail completely, without the possibility of interference or leaving it in an incomplete state. At the conclusion of this operation, the total circulating uXTZ amount will remain the same as before.

Following the successful execution of the recovery operation, pro rata shares for all remaining liquidity providers, including those who locked their yyXTZ in Quipuswap, will be computed, and the corresponding amounts will be distributed from the Youves Operations Multisig Wallet.

The Youves Operations Multisig wallet is a smart contract requiring a 2/3 agreement from three Youves contributors to authorize transactions. Its address is KT1KAr9hnFEEFXmJ1EUS4ZYMy9G8eK7bHbQM, and it is currently utilized for disbursing funds to oracle processors and was also employed in the recovery of uXAU tokens during the incident described in post mortem 7.

Because this draining swap operation will involve a lot of temporarily existing uXTZ, we need to ensure that none of this flash-minted uXTZ (which will only exist during this operation), can be split off as trading fees. Any uXTZ leaking from this operation would not be covered by collateral in uXTZ vaults.

Motivation

The current uXTZ/XTZ pool is no longer functional, as its liquidity tokens (yyXTZ) should not be entrusted to any other contract. To align with our strategic vision, as outlined in 🚀 Shoot for the Stars With youves — Our Strategic Vision ✌️ | by youves | Medium, it is imperative to replace both the pool and its associated LP token.

Furthermore, it is essential to address the needs of liquidity providers who acted in good faith and in the best interests of the platform and all YOU token holders by depositing yyXTZ into a Quipuswap pool. As the youves engines provide the means for such a recovery operation, it is our responsibility to make these providers whole again.

Analysis

Currently, we have identified 38 liquidity providers holding yyXTZ tokens, and among them, there are 3 liquidity providers who have transferred a portion of their yyXTZ to the Quipuswap contract, where it is currently locked. We anticipate that the majority of these providers will relocate their liquidity. For those who choose not to do so, we will ensure their reimbursement after the recovery operation, with XTZ and uXTZ transfers directly into their wallets.

We do acknowledge that there may be a slight amount of XTZ that cannot be recovered from the old pool, primarily due to the characteristics of the flat curve. However, this loss will be negligible when compared to the loss incurred from the locked yyXTZ tokens.

Proposal

This proposal covers two steps, these are the specific changes this proposal will execute:

1. Set the new pool as receiver for the interest rate payments

In all three existing uXTZ engines, the variable savings_pool_contract will be set to the new uXTZ/XTZ contract, which is KT1SPUvH5khHtirTEVeECiKrnh4FFXxWZ6ui. Formerly, it was the old uXTZ/XTZ flat curve contract with rewards KT1BFXgczFte2zftCTg7tL6Qk2capsFg6UFS.

This will be done by calling set_contracts on all 3 uXTZ engines and passing in the same addresses as before, except for the savings_pool_contract which will be set to the new pool KT1SPUvH5khHtirTEVeECiKrnh4FFXxWZ6ui.

Affected uXTZ engines are:

KT1AnDFRcdB652Jy5JFtmu7SampSPAzDkK7g, uXTZ/USDt engine
KT1Mf9Nr1KyGC6gUz9pGQnngzWbbZ6thShvc, uXTZ/XTZ engine
KT1ByNrcyDxYLmamuJbeFJukYkLJaZ1W86Yr, uXTZ/SIRS engine

2. Set the trading fees on the old uXTZ/XTZ pool to 0%

In the old uXTZ/XTZ flat curve pool with rewards KT1BFXgczFte2zftCTg7tL6Qk2capsFg6UFS, the trading fee will be set to 0% by calling changeFee and setting the denominator to 100 and the numerator to 100 as well, which will result in all tokens be swapped on a swap call, without any fees being split off. This is necessary to allow for the recovery operation as explained above.

Code

Source code in SmartPy

The source code of the lambda to be executed is the following:

Link to source code: https://github.com/youves-com/youves-proposals/blob/main/yip-03/yip-03-smartpy.py

def execute_set_contracts_engine_v3(
    engine_address,
    interest_rate_setter_contract,
    options_contract,
    governance_token_contract,
    savings_pool_contract,
    target_price_oracle,
    reward_pool_contract,
):

    engine_contract = sp.contract(
        sp.TPair(
            sp.TPair(sp.TAddress, sp.TPair(sp.TAddress, sp.TAddress)),
            sp.TPair(sp.TAddress, sp.TPair(sp.TAddress, sp.TAddress)),
        ),
        engine_address,
        entry_point="set_contracts",
    ).open_some()

    payload = sp.pair(
        sp.pair(
            governance_token_contract,
            sp.pair(interest_rate_setter_contract, options_contract),
        ),
        sp.pair(
            reward_pool_contract, sp.pair(savings_pool_contract, target_price_oracle)
        ),
    )

    return sp.transfer_operation(payload, sp.mutez(0), engine_contract)

Compiled Michelson code

The compiled Michelson code of the above SmartPy code is the following:

Link to compiled code: https://github.com/youves-com/youves-proposals/blob/main/yip-03/yip-03-michelson.tz

{ DROP; NIL operation; PUSH address "KT1ByNrcyDxYLmamuJbeFJukYkLJaZ1W86Yr"; CONTRACT %set_contracts (pair (pair address (pair address address)) (pair address (pair address address))); IF_NONE { PUSH int 181; FAILWITH } {}; PUSH mutez 0; PUSH (pair (pair address (pair address address)) (pair address (pair address address))) (Pair (Pair "KT1Ge5usHwAAH12PDxwP8FFYMdbMbXSRXSz4" (Pair "KT1C3T98TqCm38cHPauZ4SopkQ4torCsxgab" "KT1CHL9XVrt3Avr1mHkCiZBANEeJzbUSGqGB")) (Pair "KT1KXvsh7vnPUkBj1oG1E3LUoFnKHsf7Wixo" (Pair "KT1SPUvH5khHtirTEVeECiKrnh4FFXxWZ6ui" "KT1TSwSAU1qUyRFYBv6ix5YzqLBparxJ3FAk"))); TRANSFER_TOKENS; CONS; PUSH address "KT1Mf9Nr1KyGC6gUz9pGQnngzWbbZ6thShvc"; CONTRACT %set_contracts (pair (pair address (pair address address)) (pair address (pair address address))); IF_NONE { PUSH int 181; FAILWITH } {}; PUSH mutez 0; PUSH (pair (pair address (pair address address)) (pair address (pair address address))) (Pair (Pair "KT1Ge5usHwAAH12PDxwP8FFYMdbMbXSRXSz4" (Pair "KT1C3T98TqCm38cHPauZ4SopkQ4torCsxgab" "KT1GL6CBm93edDHogUVQzasUd6m7384eZk3J")) (Pair "KT1KXvsh7vnPUkBj1oG1E3LUoFnKHsf7Wixo" (Pair "KT1SPUvH5khHtirTEVeECiKrnh4FFXxWZ6ui" "KT1PuCU5UAoaX2Hjcns2SEmJWBC34tfLjzaS"))); TRANSFER_TOKENS; CONS; PUSH address "KT1AnDFRcdB652Jy5JFtmu7SampSPAzDkK7g"; CONTRACT %set_contracts (pair (pair address (pair address address)) (pair address (pair address address))); IF_NONE { PUSH int 181; FAILWITH } {}; PUSH mutez 0; PUSH (pair (pair address (pair address address)) (pair address (pair address address))) (Pair (Pair "KT1Ge5usHwAAH12PDxwP8FFYMdbMbXSRXSz4" (Pair "KT1C3T98TqCm38cHPauZ4SopkQ4torCsxgab" "KT1BUR5mjwBWzojKRqWrng8ASBh3N3LLV7NM")) (Pair "KT1KXvsh7vnPUkBj1oG1E3LUoFnKHsf7Wixo" (Pair "KT1SPUvH5khHtirTEVeECiKrnh4FFXxWZ6ui" "KT1PvKziQx7pJhfr3FdvkhMPwCwLxjd32HkZ"))); TRANSFER_TOKENS; CONS; PUSH address "KT1BFXgczFte2zftCTg7tL6Qk2capsFg6UFS"; CONTRACT %changeFee (pair nat nat); IF_NONE { PUSH int 167; FAILWITH } {}; PUSH mutez 0; PUSH (pair nat nat) (Pair 100 100); TRANSFER_TOKENS; CONS }

SHA256 Hash of the Michelson code

SHA256 Hash of the above Michelson code above:

826e3b75836ac8c77e9606d6ab259bb454b2363b6d759248d2a8c57fd958285b

1 Like