Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow FA1.2/FA2 collaterals for minting #213

Closed
utdemir opened this issue Jul 27, 2021 · 9 comments
Closed

Allow FA1.2/FA2 collaterals for minting #213

utdemir opened this issue Jul 27, 2021 · 9 comments
Labels
design Discussion about the design of a feature enhancement New feature or request

Comments

@utdemir
Copy link
Contributor

utdemir commented Jul 27, 2021

From the original issue #171:

allow FA1.2 / FA2 token as collateral for burrows instead of tez only. A single form of collateral is still accepted for a given checker instance, but different instances can have different collateral

Currently, Checker only allows tez as a collateral for minting kit. These tez end up in "burrow" contracts that are still controlled by checker, but delegation rights can be set individually.

This issue is to allow different tokens as collaterals instead of tez. Likely, burrow's won't be necessary for that case.

@utdemir
Copy link
Contributor Author

utdemir commented Jul 27, 2021

One way to implement this would be via conditional compilation. We can abstract away collateral handling to a separate module, and conditionally compile different versions of it based on whether the collateral is tez or a token.

Another way I can think of is to have a wrapper FA2 contract for tez to avoid conditional compilation. We can modify checker to only allow a FA2 (or FA1.2) token for collateral, removing the code regarding burrows. Then, we can write a FA2 wrapper for tez, and move the burrow logic inside that contract to allow delegation (or alternatively, use ctez). Then, if I am not missing anything, that wrapper used with checker-with-fa2-collateral contract will have feature parity with the current checker.

The advantage would mainly be avoiding conditional compilation, and shrinking the main contract complexity a bit (since those two components can be tested separately), the cost is the cost increase for using tez compared to conditional compilation.

@utdemir
Copy link
Contributor Author

utdemir commented Aug 2, 2021

Using a FA2 token as a collateral would also require us to obtain the "collateral token / tez" price, so we can use the existing CFMM (which is ctez-kit) and the oracle feed. As far as I know, there is no standard way obtain this information.

Unless we use the collateral token instead of tez everywhere, including the CFMM and the oracle feed.

@murbard, what do you think? Is there a convention for getting a FA2 tokens price in tez, or should we also make the CFMM work with the collateral token?

@gkaracha
Copy link
Contributor

gkaracha commented Aug 6, 2021

Another thing I am not entirely clear about: What about the creation deposit? Do we want it to have the same type as the collateral or is it always going to be tez?

@gkaracha

This comment has been minimized.

@gkaracha
Copy link
Contributor

Just hid my earlier comment above since that design had a few flaws; we now have a more accurate design I'll add below.

@gkaracha
Copy link
Contributor

gkaracha commented Sep 15, 2021

So, to realize the design @utdemir described above we need two sets of changes:

  1. Checker must be changed to always deal with FA2 internally, avoiding thus conditional compilation, and

  2. A Tez/FA2 wrapper contract must be shipped, to be used when collateral=tez. This wrapper contract allows users to deposit tez in exchange for "tez tokens" which are exchangeable using the contract's FA2 interface. The value of each tez token is always considered exactly 1:1 with tez. Checker can then interact with these tokens using the same core logic it uses for other FA2 tokens.

    This contract functions in a similar way to how the current Checker implementation handles burrows. When a user initially deposits tez into the contract, a "vault" contract is originated which holds their tez. This allows users to continue to delegate their tez like they can do in the current Checker implementation. Tez tokens can be exchanged between users via the contract's FA2 interface. In addition to updating the FA2 ledger, transfers emit internal operations which move tez from the sender's vault contract to the recipient's vault. This ensures that a user's token balance always matches 1:1 with the amount of actual tez in their vault.

    The overall architecture of the wrapper looks somewhat like this:

Overview

So, overall, the system after the changes we describe below would look somewhat like this:

overall-design-better-quality
The FA2 Contract in the image above is the FA2 contract for collateral type, or in the case of tez is the tez/fa2 wrapper contract referred to earlier.

Detailed Description of tez/FA2 Wrapper

Main Contract

State / Storage

  • Tez token ledger (bigmap)
    • Also needs to store the address of each user's vault contract
  • Other usual FA2 metadata (operators, token info, etc.)

transfer (from: address, to: address, amount: nat)

  • Fail if Tezos.amount <> 0
  • Fail if either to or from are vault contracts
  • Fail if either to or from are Tezos.self_address
  • Validate and update token balances in FA2 ledger with usual FA2 logic
  • Construct operations which originate vault contracts for any of to or from which do not already have one and ensure that their addresses are stored in the contract storage.
  • Get the address of the vaults corresponding to from and to: from_vault_addr and to_vault_addr
  • Emit the following operations:
    • Any vault origination operations generated in the previous step
    • A %send_tez call to the from_vault_addr address' vault contract with (to_vault_addr, amount)

set_delegate (delegate: key_hash option)

  • Fail if Tezos.amount <> 0
  • Emit the following operations:
    • a %set_delegate call to the vault corresponding to Tezos.sender

deposit (_: unit)

  • Increase Tezos.sender's token balance by Tezos.amount
  • Emit the following operations:
    • If a vault contract corresponding to Tezos.sender does not already exist:
      • An operation originating the vault
        • The new address will need to be saved in the main contract's storage
    • A %receive_tez call transferring Tezos.amount to Tezos.sender's vault contract

withdraw (amount: nat)

  • Fail if Tezos.amount <> 0
  • Fail if amount is greater than the Tezos.sender's available FA2 token balance
  • Reduce Tezos.sender's token balance by amount
  • Emit the following operations:
    • A %send_tez operation to the vault corresponding to Tezos.sender with (Tezos.sender, amount)

Vault Contracts

State / Storage

  • Main contract address

set_delegate (delegate: key_hash option)

  • Fails if Tezos.sender is not the main contract
  • assert (Tezos.amount = 0)
  • Emits an operation setting the delegate

receive_tez (_: unit)

  • Just needed for receiving tez, does nothing.

send_tez (to: address, amount: tez)

  • Fails if Tezos.sender is not the main contract
  • Emits the following operations:
    • If to address has a %receive_tez entrypoint:
      • A %receive_tez call to to with amount tez
    • Else:
      • A plain transaction to to with amount tez
      • This case is used for withdrawals

Changes for Checker

Storage and Operational Changes

  • Remove field burrow.delegate.
  • Extend external_contracts with collateral_fa2 : address.
  • Extend external_contracts with some form of oracle for tez_in_tok.

Build System Changes

  • If we are using FA2 as collateral we need a mock FA2 contract as part of our deployment process.
  • If we are using TEZ as collateral we need to deploy the FA2/tez wrapper as part of our deployment process.
  • %sealContract needs to take three addresses as input (actually 4; we need the address for the tez_in_tok oracle too).

Changes per Entrypoint

touch

Internal Logic Changes

  • Question: how to access the collateral/tez price when an FA2 token is used as collateral?
  • touch_oldest will also be affected by the changes in touch_liquidation_slice.

create_burrow (burrow_no, tok : nat * nat)

Prerequisites

  • Tezos.sender must own at least tok tokens in the corresponding FA2 contract.
  • Checker must be configured as an operator for the Tezos.sender's FA2 contract.

Internal Logic Changes

  • assert (Tezos.amount = 0)
  • Emit an operation originating a new burrow contract.
  • Emit an FA2 %transfer operation from Tezos.sender to the newly-originated burrow contract.

deposit_collateral (burrow_no, tok : nat * nat)

Prerequisites

  • Tezos.sender must own at least tok tokens in the corresponding FA2 contract.
  • Checker must be configured as an operator for the Tezos.sender's FA2 contract.

Internal Logic Changes

  • assert (Tezos.amount = 0)
  • Emit an FA2 %transfer operation from Tezos.sender to the burrow contract for burrow_no.

withdraw_collateral (burrow_no, tok : nat * nat)

Internal Logic Changes

  • assert (Tezos.amount = 0)
  • Emit a call to burrowContract%transfer, which internally will:
    • Emit an FA2 %transfer operation from the burrow contract for burrow_no to Tezos.sender.

mint_kit

No changes (no collateral is moved).

burn_kit

No changes (no collateral is moved).

activate_burrow (burrow_no, tok : nat * nat)

Prerequisites

  • Tezos.sender must own at least tok tokens in the corresponding FA2 contract.
  • Checker must be configured as an operator for the Tezos.sender's FA2 contract.

Internal Logic Changes

  • assert (Tezos.amount = 0)
  • Emit an FA2 %transfer operation from Tezos.sender to the burrow contract for burrow_no.

deactivate_burrow (burrow_no, recipient : nat * address)

Internal Logic Changes

  • assert (Tezos.amount = 0)
  • Emit a call to burrowContract%transfer, which internally will:
    • Emit an FA2 %transfer operation from the burrow contract for burrow_no to Tezos.sender.

mark_for_liquidation (burrow_no : nat)

Internal Logic Changes

  • assert (Tezos.amount = 0)
  • Emit a call to burrowContract%transfer, which internally will:
    • Emit an FA2 %transfer operation from the burrow contract for burrow_no to Tezos.sender.

touch_liquidation_slices (slices: leaf_ptr list)

Internal Logic Changes

  • For each slice, emit a call to burrowContract%transfer, which internally will:
    • Emit an FA2 %transfer operation from the burrow contract to Checker.

cancel_liquidation_slice

No changes (no collateral is moved).

touch_burrow

No changes (no collateral is moved).

set_burrow_delegate

Internal Logic Changes

  • Check if collateral FA2 has %setDelegate entrypoint:
    • If so (i.e., it is the tez/fa2 wrapper), emit an operation calling the burrow contract for burrow_no's %set_delegate entrypoint.
      • Internally, this will emit an operation calling the FA2's %setDelegate entrypoint.
    • else (i.e., it is a usual FA2 contract), fail with a specific error code (e.g., error_SetDelegateNotSupported).

buy_kit

No changes (no collateral is moved).

sell_kit

No changes (no collateral is moved).

add_liquidity

No changes (no collateral is moved).

remove_liquidity

No changes (no collateral is moved).

liquidation_auction_place_bid

No changes (no collateral is moved).

liquidation_auction_claim_win (auction_id: liquidation_auction_id)

Internal Logic Changes

  • Emit an FA2 %transfer operation from Checker to the Tezos.sender.

receive_slice_from_burrow

No longer necessary. To be removed.

receive_price

No changes (no collateral is moved).

transfer

No changes (no collateral is moved).

balance_of

No changes (no collateral is moved).

update_operators

No changes (no collateral is moved).

NOTE: As in the current implementation of Checker, questions remain around whether we should provide a method for receiving staking rewards (see #246). With the move to using FA2 in Checker, this question now applies to this tez wrapper contract instead.

QUESTION: how to access the collateral/tez price when an FA2 token is used as collateral? See @utdemir's comment above: #213 (comment)

@gkaracha
Copy link
Contributor

Pasting here the cases we discussed in different channels, so that we have all the info on the issue itself:

Concerning prices (assuming an index-based checker), we have the following two scenaria:

  1. COLLATERAL = TEZ
    • cfmm contains ctez
    • index : TEZ/CHF
    • KIT/CHF = TEZ/CHF (from index) * CTEZ/TEZ (from ctez) * KIT/CTEZ (from cfmm)
  2. COLLATERAL = FA2
    • cfmm contains FA2
    • index : FA2/CHF
    • KIT/CHF = FA2/CHF (from index) * KIT/FA2 (from cfmm)

It's currently unclear whether we can achieve this deviation without conditional compilation (we could use the storage for dynamic checks but that doesn't sound great either). Perhaps when we generalize in the 3rd direction specified in #171 we'll be able to come up with a better design. I expect the build system to be even more complicated at that point and more code to be generated at deployment time anyway.

gkaracha added a commit that referenced this issue Oct 5, 2021
This is only an early untested draft. Hopefully minimal changes
will be needed, but both changes and relevant tests will be added
when the contract becomes integrated into checker (i.e., when the
rest of the changes described in #213 are implemented).
gkaracha added a commit that referenced this issue Oct 15, 2021
First part of #213: integration of the tez wrapper into checker's
workflow. In essence this PR makes checker deal with FA2
exclusively. To keep CI green, usage of the tez wrapper is
hardwired; adding the switch to be able to change the collateral
type should be part of a separate PR. This PR is already pretty
big.

Other changes:
* Removed the restrictions on `from_` and `to_` in
  `TezWrapper.transfer` to save on gas costs (they were optional
  anyway).
* Fixed a bug in `TezWrapper.transfer` (transfer of zero tez
  should not fail due to non-originated vaults).
* Made `tezWrapper.ml` more verbose to significantly reduce its
  gas costs (see bot message below).
@gkaracha
Copy link
Contributor

I think we can finally close this. The existence of #303 is proof enough that now we can build, deploy, and interact with Checker for both types of collateral (TEZ or FA2). Further improvements regarding testing and documentation are kept track of elsewhere (in #284 and #118, respectively).

@dorranh
Copy link
Contributor

dorranh commented Nov 26, 2021

🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
design Discussion about the design of a feature enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants