YIP-2: Launch uXAU-USDt engine, uXAU-uUSD flat curve swap, reduce voting waiting period to 6hours, fix the intents for uXTZ engines

Author: Florin Avram, @florin (Forum), Markus Laeng, @m.laeng, youves contributors
First publication date: 2023/08/11

Summary

In 2021-12-14 the youves community decided on the legacy YIP-002 to introduce a new synthetic asset which shall track the value of gold and be called uXAU. Back at the time community just had launched uDEFI and realised that without the ability to concentrate the liquidity at a specific target price, the synthetic asset would track only with conversion events involved (i.e. conversion options or liquidations). As there was no obvious solution to this challenge the decision was taken to only launch uXAU once a solution would be found. Meanwhile the research team at ubinetic found a solution to solve the liquidity at target problem built on top of the flat curves already used on the platform.

This proposal aims at expanding the Youves protocol with a new synthetic asset uXAU which will track the value of gold. For the community this means that a new engine will be deployed (we start with uXAU engine with USDT collateral) that allows users minting of uXAU tokens. Furthermore a modified flat curve swap which concentrates the liquidity at target is also part of this proposal. The price of Gold is provided by the Acurast on-chain oracle and observes for now these sources:

A second change we would like to bring to the DAO is to reduce the waiting time between a proposal being submitted and allowing our users to vote. Currently the waiting period is 11520 blocks (roughly 2 days) and we would like to reduce this period to 12 hours or less (depending on the response we get from our community). This reduces the waiting period, but still allows users to verify the proposal and if they find something they disagree with can still change their vote as our DAO implementation allows it.

At last we would like to fix a minor issue introduced in the previous YIP, by correctly updating the options contract of the uXTZ engines.

Rationale

Gold is an asset that it is underlooked in the Tezos ecosystem. By creating a synthetic asset that tracks gold we allow our users more ways in which they can use the platform and moreover we can foresee that the token will be adopted by other platforms, further increasing activity in the Tezos ecosystem.

Motivation

As stated above, we would like to be early adopters of a gold tracking synthetic asset. Moreover our community has expressed their interest in such an asset.

Caveats/Limitations

The Gold market (XAU/USD) operates within the conditions of “traditional finance” and mirrors the behaviour of a foreign exchange market. As a result, obtaining price feeds for gold during weekends and public holidays becomes unattainable. This behaviour poses a challenge for certain actions involving uXAU that rely on oracle price feeds for execution, as these actions are rendered impossible during non-forex trading hours.
Due to this limitations certain operations will not be allowed in either the uXAU engine or the uXAU - uUSD swap. These operations are the following:

  • minting uXAU
  • swaping either uXAU to uUSD or uUSD to uXAU
  • step-ins/liquidations
  • options fulfillment

Methodology

In the following section we will present the steps needed to create the new synthetic asset, engine and swap step by step.

  • Step 1 - Deploy a new uXAU - uUSD yielding flat curve swap. The flat curve will assume that the uXAU asset will have token id 4 in our synthetic assets contract. Until the proposal is executed this flat curve swap will be unusable. With a 0.35% fee split among the liquidity providers and the unified staking pool.

    Added on 2023-08-12: This uXAU - uUSD flat curve pool has already been deployed and tested on ghostnet (KT1DRoTPmuC5dkNwi15bb3jCA3K1LPixgEdq) and has already been deployed on mainnet (KT1Ad5yJzoiRRdMJPvhJiPJ7Cq8WbJnCS7bg).

    The code for the uXAU - uUSD flat curve pool is published on the youves GitHub.

  • Step 2 - Deploy a new uXAU engine with USDT collateral. Same as with the flat curve, the engine assumes that uXAU asset will have token id 4 in our synthetic assets contract.

    Added on 2023-08-12: This new engine has already been deployed and tested on ghostnet (KT1SYK5UnacFrVmoAcWoat69HtjAnRwt9tyc) and has already been deployed on mainnet (KT1VhU47n633rqJeAZESfbejnxeJmpXVx3AA). The code used is the same as for all token collateralized v3 engines, which is published on the youves GitHub.

    Until the proposal is executed the engine is not wired in the Youves protocol, therefore it will be unusable.

    The newly created engine will have the following parameters:

    • collateral ratio: 110%
    • introducer ratio: 10%
    • liquidation payout ratio: 6.25% liquidation reward
    • minting fee: 0.3125%
    • settlement payout ratio: 6.25% settlement premium
    • settlement ratio: 115%
    • settlement reward fee ratio: 10%
    • spread rate: 1% yearly
    • reference interest rate: 5.564109 %
  • Step 3 - Our team will develop the lambda for the proposal. The lambda will:

    • create the uXAU asset with token id 4 in our synthetic assets contract
    • wire the new uXAU engine with USDT collateral in the Youves protocol
    • reduce the waiting period between proposal submission and start voting for future proposals to a value agreed by the community
    • address the options contract for the uXTZ engines.
  • Step 4 - The community can review the flat curve swap, engine and lambda of the proposal

  • Step 5 - The lambda will be submitted to the DAO.

Proposal

The following section explains the content of the lambda that will be executed if YIP-2 is accepted by YOU stakers:

Change 1 - Add uXAU as a new synthetic asset token with token id 4

The lambda will create the uXAU assets that will have token id 4 in our synthetic assets contract KT1XRPEPXbZK25r3Htzp2o1x7xdMMmfocKNW.

Change 2 - Wire a new uXAU engine with USDT collateral in the youves protocol

The lambda will wire in the uXAU engine with USDT collateral in the youves protocol by doing the following operations:

  • setting the new engine KT1VhU47n633rqJeAZESfbejnxeJmpXVx3AA as an admin for the uXAU synthetic asset
  • set the uXAU engine as an admin for the stake manager KT1Ge5usHwAAH12PDxwP8FFYMdbMbXSRXSz4
  • set the staking factor for the uXAU engine in the stake manager
  • set the uXAU - uUSD swap KT1Ad5yJzoiRRdMJPvhJiPJ7Cq8WbJnCS7bg as the interest payments receiver.

After these changes the engine will be wired in the Youves protocol and users will be able to mint new uXAU synthetic assets or use it in any way they see fit. The engine will function like any other Youves engine, but having the limitations presented above.

Change 3 - Reduce the waiting period between proposal submission and voting period

The lambda will reduce the waiting period from 2 days to an agreed period of time. After this change the users will be able to vote faster for future proposals.

Change 4 - Update the options contract for uXTZ engines

The lambda will do the following changes:

  • Set KT1GL6CBm93edDHogUVQzasUd6m7384eZk3J as the options contract for the uXTZ/XTZ engine KT1Mf9Nr1KyGC6gUz9pGQnngzWbbZ6thShvc
  • Set KT1CHL9XVrt3Avr1mHkCiZBANEeJzbUSGqGB as the options contract for the uXTZ/SIRS engine KT1ByNrcyDxYLmamuJbeFJukYkLJaZ1W86Yr

After these changes the options will be enabled in the uXTZ/XTZ and uXTZ/SIRS engines.

Code

Source code in SmartPy (or any other language used)

This is the SmartPy source code of the lambda which will be executed with this proposal, YIP-2:

Also published on Github here.

import smartpy as sp

def execute_set_token_metadata(token_address, token_id, token_info):
    token_contract = sp.contract(
        sp.TPair(sp.TNat, sp.TMap(sp.TString, sp.TBytes)),
        token_address,
        entry_point="set_token_metadata",
    ).open_some()
    payload = sp.pair(token_id, token_info)
    return sp.transfer_operation(payload, sp.mutez(0), token_contract)

def set_uxau_token_metadata(unit):
    sp.set_type(unit, sp.TUnit)
    sp.result(
        sp.list([
            execute_set_token_metadata(
                sp.address("KT1XRPEPXbZK25r3Htzp2o1x7xdMMmfocKNW"),
                sp.nat(4),
                sp.map(
                    l={ "" : sp.bytes("0x697066733a2f2f516d553972507434354b51466d35315a46467a65727a6231586645726f7a6d31734d4678443478705851394e526e")},
                    tkey=sp.TString,
                    tvalue = sp.TBytes
                )
            )
        ])
    )

def execute_execute(administrable_address, executable_lambda):
    administrable_contract = sp.contract(
        sp.TLambda(sp.TUnit, sp.TList(sp.TOperation)),
        administrable_address,
        entry_point="execute",
    ).open_some()
    return sp.transfer_operation(
        sp.build_lambda(executable_lambda), sp.mutez(0), administrable_contract
    )

def execute_add_administrator(administrable_address, new_admin, token_id):
    administrable_contract = sp.contract(
        sp.TPair(sp.TAddress, sp.TNat),
        administrable_address,
        entry_point="set_administrator",
    ).open_some()
    payload = sp.pair(new_admin, token_id)
    return sp.transfer_operation(payload, sp.mutez(0), administrable_contract)

def add_administrator_to_contract(unit, administrable_address, admin_to_add, token_id):
    sp.set_type(unit, sp.TUnit)
    sp.result(
        sp.list(
            [execute_add_administrator(administrable_address, admin_to_add, token_id)]
        )
    )

def execute_set_stake_factor(administrable_address, address, factor):
    administrable_contract = sp.contract(
        sp.TPair(sp.TAddress, sp.TNat),
        administrable_address,
        entry_point="set_stake_factor",
    ).open_some()
    payload = sp.pair(address, factor)
    return sp.transfer_operation(payload, sp.mutez(0), administrable_contract)

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)

def DAO_YIP_02(unit):
    sp.set_type(unit, sp.TUnit)

    #############################
    # DAO new governance params #
    #############################
    dao_entrypoint = sp.contract(
        GOVERNANCE_PARAMS_TYPE,
        sp.address('KT1C3T98TqCm38cHPauZ4SopkQ4torCsxgab'), # youves DAO 
        entry_point="set_governance_params"
    ).open_some(message='DAO: Invalid entry point set_governance_params')

    # new governance params for DAO
    new_governance_params = sp.record(
        escrow_amount = sp.nat(1_000_000_000_000_000), # 1000 YOUs
        vote_delay_blocks = sp.nat(1440), # 6 hours
        vote_length_blocks = sp.nat(28800), # 5 days (remains the same)
        min_yes_votes_percentage_for_escrow_return = sp.nat(30), # 30% (remains unchanged)
        timelock_execution_delay_blocks = sp.nat(11520), # 2 days (remains unchanged)
        timelock_cancelation_delay_blocks = sp.nat(23040), # 4 days (remains unchanged)
        super_majority_percentage = sp.nat(80), # 80% (remains unchanged)
        quorum_cap = sp.record(
            lower = sp.nat(800_000_000_000_000_000), # 0.8M (remains unchanged)
            upper = sp.nat(3_744_000_000_000_000_000) # 3.744M (remains unchanged)
        ),
    )
    sp.set_type_expr(new_governance_params, GOVERNANCE_PARAMS_TYPE)
    
    # DAO LAMBDA
    sp.result(sp.list([
        execute_execute(sp.address("KT1FFE2LC5JpVakVjHm5mM36QVp2p3ZzH4hH"), set_uxau_token_metadata), # add uXAU token to the synthetic contract
        execute_execute(
            sp.address("KT1FFE2LC5JpVakVjHm5mM36QVp2p3ZzH4hH"),
            lambda unit: add_administrator_to_contract(
                unit,
                sp.address("KT1XRPEPXbZK25r3Htzp2o1x7xdMMmfocKNW"),
                sp.address('KT1VhU47n633rqJeAZESfbejnxeJmpXVx3AA'),
                sp.nat(4)
            )
        ), # add the uXAU engine as admin to the uXAU token
        execute_add_administrator(sp.address("KT1Ge5usHwAAH12PDxwP8FFYMdbMbXSRXSz4"), sp.address('KT1VhU47n633rqJeAZESfbejnxeJmpXVx3AA'), 0), # add the uXAU engine as admin to the stake manager
        # for the price 1oz gold = 1931.16 usd and 1 tez = 0.791470 usd the stake factor is 2439.96613896 * 10^18.
        execute_set_stake_factor(sp.address("KT1Ge5usHwAAH12PDxwP8FFYMdbMbXSRXSz4"), sp.address('KT1VhU47n633rqJeAZESfbejnxeJmpXVx3AA'), 2_439_966_138_960_000_000_000), # set the stake factor of the uXAU token.
        
        sp.transfer_operation(new_governance_params, sp.mutez(0), dao_entrypoint),

        execute_set_contracts_engine_v3(
            sp.address('KT1Mf9Nr1KyGC6gUz9pGQnngzWbbZ6thShvc'), # uXTZ/XTZ collateral engine
            sp.address('KT1C3T98TqCm38cHPauZ4SopkQ4torCsxgab'), # youves DAO as interest rate setter (remains the same)
            sp.address('KT1GL6CBm93edDHogUVQzasUd6m7384eZk3J'), # options contract
            sp.address('KT1Ge5usHwAAH12PDxwP8FFYMdbMbXSRXSz4'), # stake manager (remains the same)
            sp.address('KT1BFXgczFte2zftCTg7tL6Qk2capsFg6UFS'), # savings_pool (remains the same)
            sp.address('KT1PuCU5UAoaX2Hjcns2SEmJWBC34tfLjzaS'), # target price oracle (remains the same)
            sp.address('KT1KXvsh7vnPUkBj1oG1E3LUoFnKHsf7Wixo'), # unified staking proxy as rewards receiver (remains the same)
        ),
        execute_set_contracts_engine_v3(
            sp.address('KT1ByNrcyDxYLmamuJbeFJukYkLJaZ1W86Yr'), # uXTZ/SIRS collateral engine
            sp.address('KT1C3T98TqCm38cHPauZ4SopkQ4torCsxgab'), # youves DAO as interest rate setter (remains the same)
            sp.address('KT1CHL9XVrt3Avr1mHkCiZBANEeJzbUSGqGB'), # options contract
            sp.address('KT1Ge5usHwAAH12PDxwP8FFYMdbMbXSRXSz4'), # stake manager (remains the same)
            sp.address('KT1BFXgczFte2zftCTg7tL6Qk2capsFg6UFS'), # savings_pool (remains the same)
            sp.address('KT1TSwSAU1qUyRFYBv6ix5YzqLBparxJ3FAk'), # target price oracle (remains the same)
            sp.address('KT1KXvsh7vnPUkBj1oG1E3LUoFnKHsf7Wixo'), # unified staking proxy as rewards receiver (remains the same)
        ),
    ]))

Compiled Michelson code

This is the compiled Michelson code of the above SmartPy code.

Also published on Github here.

{ NIL operation; PUSH address "KT1ByNrcyDxYLmamuJbeFJukYkLJaZ1W86Yr"; CONTRACT %set_contracts (pair (pair address (pair address address)) (pair address (pair address address))); IF_NONE { PUSH int 176; 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 "KT1BFXgczFte2zftCTg7tL6Qk2capsFg6UFS" "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 176; 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 "KT1BFXgczFte2zftCTg7tL6Qk2capsFg6UFS" "KT1PuCU5UAoaX2Hjcns2SEmJWBC34tfLjzaS"))); TRANSFER_TOKENS; CONS; PUSH address "KT1C3T98TqCm38cHPauZ4SopkQ4torCsxgab"; CONTRACT %set_governance_params (pair (nat %escrow_amount) (pair (nat %vote_delay_blocks) (pair (nat %vote_length_blocks) (pair (nat %min_yes_votes_percentage_for_escrow_return) (pair (nat %timelock_execution_delay_blocks) (pair (nat %timelock_cancelation_delay_blocks) (pair (nat %super_majority_percentage) (pair %quorum_cap (nat %lower) (nat %upper))))))))); IF_NONE { PUSH string "DAO: Invalid entry point set_governance_params"; FAILWITH } {}; PUSH mutez 0; PUSH (pair nat nat) (Pair 800000000000000000 3744000000000000000); PUSH nat 80; PUSH nat 23040; PUSH nat 11520; PUSH nat 30; PUSH nat 28800; PUSH nat 1440; PUSH nat 1000000000000000; PAIR 8; DIG 4; DROP; TRANSFER_TOKENS; CONS; PUSH address "KT1Ge5usHwAAH12PDxwP8FFYMdbMbXSRXSz4"; CONTRACT %set_stake_factor (pair address nat); IF_NONE { PUSH int 276; FAILWITH } {}; PUSH mutez 0; PUSH (pair address nat) (Pair "KT1VhU47n633rqJeAZESfbejnxeJmpXVx3AA" 2439966138960000000000); TRANSFER_TOKENS; CONS; PUSH address "KT1Ge5usHwAAH12PDxwP8FFYMdbMbXSRXSz4"; CONTRACT %set_administrator (pair address nat); IF_NONE { PUSH int 212; FAILWITH } {}; PUSH mutez 0; PUSH (pair address nat) (Pair "KT1VhU47n633rqJeAZESfbejnxeJmpXVx3AA" 0); TRANSFER_TOKENS; CONS; PUSH address "KT1FFE2LC5JpVakVjHm5mM36QVp2p3ZzH4hH"; CONTRACT %execute (lambda unit (list operation)); IF_NONE { PUSH int 266; FAILWITH } {}; PUSH mutez 0; LAMBDA unit (list operation) { DROP; NIL operation; PUSH address "KT1XRPEPXbZK25r3Htzp2o1x7xdMMmfocKNW"; CONTRACT %set_administrator (pair address nat); IF_NONE { PUSH int 212; FAILWITH } {}; PUSH mutez 0; PUSH (pair address nat) (Pair "KT1VhU47n633rqJeAZESfbejnxeJmpXVx3AA" 4); TRANSFER_TOKENS; CONS }; TRANSFER_TOKENS; CONS; PUSH address "KT1FFE2LC5JpVakVjHm5mM36QVp2p3ZzH4hH"; CONTRACT %execute (lambda unit (list operation)); IF_NONE { PUSH int 266; FAILWITH } {}; PUSH mutez 0; LAMBDA unit (list operation) { DROP; NIL operation; PUSH address "KT1XRPEPXbZK25r3Htzp2o1x7xdMMmfocKNW"; CONTRACT %set_token_metadata (pair nat (map string bytes)); IF_NONE { PUSH int 257; FAILWITH } {}; PUSH mutez 0; PUSH (pair nat (map string bytes)) (Pair 4 {Elt "" 0x697066733a2f2f516d553972507434354b51466d35315a46467a65727a6231586645726f7a6d31734d4678443478705851394e526e}); TRANSFER_TOKENS; CONS }; TRANSFER_TOKENS; CONS }

SHA256 Hash of the Michelson code

SHA256 Hash of the above Michelson code:
7d7222b6a7dcc17ef7e629f13665796e6cc790b2c98f2c8cd0df4a5c4d288bbd

Edits:

2023-08-14: Added Markus as autor, added contract addresses of the deployed uXAU ghostnet engine and ghostnet swap contract, added contract addresses of the deployed uXAU mainnet engine and mainnet swap.

Very happy to see this proposal.

Could you please clarify the considered timezones? How is “weekend” defined? Are these US “public holidays”?
Is this information coming from the three mentioned sources and transmitted to the contract via the oracle?

Is there a more precise description somewhere? If I understand correctly, the parameters of the curve will vary depending on the inputs from the oracle. Is that correct?

Note : the title says “6 hours”.
I don’t get the “or less”. Do you mean this is a vote for a fixed value, say 12 hours, and that future proposals will a change it if wished?
Anyway, it seems to me a good idea to reduce this period that is not really useful.

@Clement

The proposal would will the waiting time to 1440 blocks, which is around 6hrs at 4 blocks per minute.

Is there a more precise description somewhere? If I understand correctly, the parameters of the curve will vary depending on the inputs from the oracle. Is that correct?

The curve itself is the same as for the other flat curve contracts, but the centre of the curve is set to the target price.

Btw. The code for this flat curve is identical with the one that was deployed on the latest xtz/uxtz flat curve, with the exception that the oracle for the xtz/uxtz is set to 1:1 instead of a real oracle which is reporting market prices, like it is the case for uxau.

Here’s the code: https://github.com/youves-com/youves-smart-contract/blob/main/contracts/swap/tez_cash_flat_cfmm_with_rewards.mligo

1 Like

Hey @Clement I wanted to come back to your question about market times and about the question when exactly the uXAU engine will be paused for minting and liquidations and when the uXAU/uUSD swap will be paused.

I’ll copy the text I wrote on discord concerning this topic:

It’s a bit tricky, because the API’s we are using (see uXAU forum post) are not very transparent about which markets they are observing and when the XAU price is not updated due to markets being closed (weeekends and holidays). This means we currently cannot tell exactly when the gold price will be paused.

How it works is that when the oracle queries the APIs it usually gets a timestamp together with the XAU price data from each of the 3 APIs that are currently defined. If at least 2 APIs respond and if the timestamp on both responses is recent, the oracle will consider the price from those APIs being valid and will form the final oracle price that is then used by youves.

If the timestamp is too old or not delivered by at least 2 of 3 APIs our oracle will not report a price for that epoch (an epoch is 15 mins). Now if the oracle didn’t report a price for 4 consecutive epocs, the price will be regarded as stale and the uXAU engine will not allow minting and uXAU / uUSD swaps will not be allowed by our uXAU flat curve (transactions would just fail if you tried). (other dexes might still allow trades).

This means that if gold markets close and the APIs stop reporting a recent price, it will take 4 epochs until minting on the uXAU engine will fail due to stale prices. And once gold markets open again and APIs restart to update the price, it will take max 1 epoch to be up again. Burning uXAU in order to reciver collateral will not be paused, even when the oracle price is stale. But Minting, Liquidations and Swaps will pe paused.

I hope this helps to understand how it will work. I hope I got everything correct…

an epoch is 15 mins OR 15 seconds?

@bitxia an epoch is 15 minutes. Means the price is updated every 15 minutes.

Ok.
If the CFMM pool use this price, then traders can game the platform. I mean the GOLD price will change a lot within 15 minutes and trader can swap with this fixed price at any time?