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

Implement the tez wrapper contract #255

Merged
merged 42 commits into from
Oct 5, 2021
Merged

Implement the tez wrapper contract #255

merged 42 commits into from
Oct 5, 2021

Conversation

gkaracha
Copy link
Contributor

@gkaracha gkaracha commented Sep 22, 2021

As described in #213 (comment). Currently WIP.

In addition to adding a draft of the tez wrapper contract, this PR performs some general changes as well.

  • The contents of fa2Interface.ml are partitioned into three files:

    • fa2Interface.ml contains all user-facing FA2-conforming types (general purpose)
    • fa2Ledger.ml contains the definition of fa2_state and functions operating on it (general purpose)
    • fa2Implementation.ml contains the implementations of the three FA2 entrypoints (fa2_run_balance_of, fa2_run_update_operators, and fa2_run_transfer) (checker-specific).

    The reason for this partitioning is so that we can reuse the common parts (API in fa2Interface.ml and generic infrastructure in fa2Ledger.ml) for the tez wrapper contract, while allowing the tez wrapper contract to implement its own versions of the FA2 entrypoints (which are indeed different than those of checker).

    NOTE: All three modules are tested in testFa2.ml.

  • Inline pragmas are added to three functions (set_fa2_ledger_value, ledger_issue, and ledger_withdraw) to avoid gas explosion. Ideally we would not inline any of these functions, but the order of definitions in ligo programs can have a (surprisingly) serious impact on gas costs. The partitioning above required some reordering of definitions which led to failures, hence the inline pragmas. This makes some sense to me since I expect the stack to be different, but such an effect on gas costs feels like a LIGO bug to me.

@gkaracha
Copy link
Contributor Author

Now it should be complete, though it's pretty rough around the edges. There are a few things I need to fix before undrafting the PR. I am especially bothered by how specialized fa2Interface.ml is to checker; I might end up refactoring it into two modules: the fa2Interface that is indeed shared among all FA2-contracts (so it can be reused by both Checker and TezWrapper) and the checker-specific parts (e.g., valid tokens being kit and lqt). Other things that are certainly missing: tests, fa2 metadata.

@dorranh could you also have a look and let me know if you see any red flags? I know it's still a draft but I could use a second pair of eyes 😅

Copy link
Contributor

@dorranh dorranh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I had a look over the current draft. It looks good to me and is in line with the spec we had previously discussed. Just left a few general comments 🙂

(* 3. Create a vault, if it does not exist already, and update the map. *)
let op_opt, state, vault_address = find_vault_address state !Ligo.Tezos.sender in
(* 4. Instruct the vault to send the actual tez to the owner *)
let op = match (LigoOp.Tezos.get_entrypoint_opt "%vault_send_tez_to_contract" vault_address : (Ligo.tez * Ligo.address) Ligo.contract option) with
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if we want to send the tez to a contract or via a bare transaction here to enable withdrawals directly to user accounts? Not sure if there are any negative implications to doing this though

src/ligoOp.ml Show resolved Hide resolved
| None -> ([op], state)
| Some origination -> ([origination; op], state)

let[@inline] set_delegate (state: tez_wrapper_state) (kho: Ligo.key_hash option) : LigoOp.operation list * tez_wrapper_state =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if we need to create the vault if it doesn't exist when calling this entrypoint, but it does at least make the behavior consistent with the FA2 entrypoints

Copy link
Contributor Author

@gkaracha gkaracha Sep 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a fair point, we could also fail without any problems. I mostly tried to make vault origination implicit (and eager) everywhere, so that, indeed, similarly to FA2, operations work fine even if an account is not created yet or if a user has zero tez, etc. I guess this approach is rather eager when it comes to vault creation, but I think that's actually nice: we never have to fail due to "vault non-existent" errors. I can't think of a downside at the moment but perhaps we should revisit the approach.

| None -> ops (* vault is already originated *)
| Some origination -> (origination :: ops) in (* originate now *) (* NOTE: reversed! *)

(* TODO: Do we really need to restrict the to_ and from_ here? *)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I worry about with this is that we could end up causing vaults to be created for vaults if someone transfers tez-tokens to a vault address. I'm not sure if this causes vulnerabilities, but it feels a bit odd.

e.g.

I transfer to address of Vault A -> No record exists in ledger or Vault A -> Vault for Vault A is originated and transfer is executed

At that point the tokens are lost to the vault since we can't issue transfers from Vault A directly, though I guess the end result is just that the tokens are lost (not much different than sending tokens to an unknown address)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see what you mean. Yes, that could happen but indeed it's not much different than sending tokens to an unknown address. I'm sure that it cannot be used for an attack since we're using bigmaps (the performance of the tez wrapper will not degrade no matter how many vaults someone can create), but I guess it is a little weird. On the other hand, adding the check will raise gas costs, but people will still be able to create vaults for useless accounts if they want so the problem is not entirely gone. 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, looking more into this, I think that implementing a check about whether an address belongs to a vault is more complicated than it seems at first glance: the big map is indexed by user address, so figuring out whether an address is a vault address cannot be done unless we keep track of them in a separate map. I added a check for !Ligo.Tezos.self_address for now, but we might have to reconvene about the vault check.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah true, I hadn't thought about that. Given that we're trying to avoid gas costs I agree that it might make more sense to prioritize that over user-friendliness here since allowing users to create extra vault contracts shouldn't affect the functionality of the wrapper.

@gkaracha gkaracha force-pushed the GEN branch 5 times, most recently from f0e639b to 7e6aa28 Compare September 29, 2021 09:15
@gkaracha gkaracha marked this pull request as ready for review October 4, 2021 12:35
@gkaracha gkaracha requested a review from dorranh October 4, 2021 12:35
Copy link
Contributor

@dorranh dorranh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one open question here, but otherwise LGTM! We should add some extra tests for the wrapper contract, but I'm happy creating a separate issue for this if you prefer.

@gkaracha
Copy link
Contributor Author

gkaracha commented Oct 4, 2021

We should add some extra tests for the wrapper contract, but I'm happy creating a separate issue for this if you prefer.

Yeah, totally. I didn't add any since now it's not called at all, but after #213 is completed we should be able to add some meaningful tests.

@github-actions
Copy link

github-actions bot commented Oct 4, 2021

Gas costs fe25e1d 699f704 Diff
touch 30396 30471 75
remove_liquidity 12556 12584 28
add_liquidity 12639 12654 15
sell_kit 11724 11734 10
buy_kit 12492 12502 10
mint_kit 6923 6926 3
burn_kit 7151 7154 3
set_burrow_delegate 9833 9836 3
deposit_collateral 8898 8901 3
deactivate_burrow 11276 11279 3
update_operators 5407 5410 3
withdraw_collateral 11130 11133 3
activate_burrow 8918 8921 3
create_burrow 6912 6915 3
touch_burrow 5919 5921 2
transfer 6129 6131 2
Entrypoint sizes fe25e1d 699f704 Diff
add_liquidity 1967 1833 -134
remove_liquidity 2038 1953 -85
touch 56590 56661 71
sell_kit 1372 1346 -26
buy_kit 1376 1350 -26
liquidation_auction_place_bid 2229 2206 -23
touch_liquidation_slices 14482 14465 -17
Test coverage fe25e1d 699f704 Diff
fa2Interface.ml 100 0 -100
fa2Implementation.ml None 100 100
fa2Ledger.ml None 100 100
common.ml 100 97.87 -2.1299999999999955
checker.ml 90.18 90.1 -0.0800000000000125
TOTAL 93.93 93.86 -0.07000000000000739

@gkaracha
Copy link
Contributor Author

gkaracha commented Oct 4, 2021

Ah, the message bot is back to work, thanks @dorranh!

@dorranh
Copy link
Contributor

dorranh commented Oct 4, 2021

Ah, the message bot is back to work, thanks @dorranh!

It's alive! 🧑‍🔬

The problem is that we have checker-specific logic in fa2Interface.ml
so we cannot really reuse it for the tezWrapper (e.g., the token_ids
used are checker-specific, no vaults are involved, ..). Ideally, I'd
keep fa2Interface.ml clean (i.e., leave only the types and basic
infrastructure in it), and have a separate module (e.g., fa2State or
something) with checker-specific data. This way, tezWrapper can open
only fa2Interface and implement the specialized functions that it
needs.
@github-actions
Copy link

github-actions bot commented Oct 5, 2021

Gas costs acf48a5 4f1eec0 Diff
touch 30396 30471 75
remove_liquidity 12556 12584 28
add_liquidity 12639 12654 15
buy_kit 12492 12502 10
sell_kit 11724 11734 10
set_burrow_delegate 9833 9836 3
touch_burrow 5919 5922 3
mint_kit 6923 6926 3
create_burrow 6912 6915 3
deactivate_burrow 11276 11279 3
update_operators 5407 5410 3
deposit_collateral 8898 8901 3
burn_kit 7151 7154 3
activate_burrow 8918 8921 3
withdraw_collateral 11130 11133 3
transfer 6129 6131 2
Entrypoint sizes acf48a5 4f1eec0 Diff
add_liquidity 1967 1833 -134
remove_liquidity 2038 1953 -85
touch 56590 56661 71
buy_kit 1376 1350 -26
sell_kit 1372 1346 -26
liquidation_auction_place_bid 2229 2206 -23
touch_liquidation_slices 14482 14465 -17
Test coverage acf48a5 4f1eec0 Diff
fa2Ledger.ml None 100 100
fa2Interface.ml 100 0 -100
fa2Implementation.ml None 100 100
common.ml 100 97.87 -2.1299999999999955
checker.ml 90.18 90.1 -0.0800000000000125
TOTAL 93.93 93.86 -0.07000000000000739

@gkaracha gkaracha merged commit 511b66b into master Oct 5, 2021
@gkaracha gkaracha deleted the GEN branch October 5, 2021 08:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants