fallen-icarus / cardano-swaps

A distributed order-book DEX using composable atomic swaps with full delegation control and endogenous liquidity.
Apache License 2.0
69 stars 13 forks source link

Feature request: allow closing of completed swap without user approval #16

Closed SynthLuvr closed 6 months ago

SynthLuvr commented 7 months ago

This applies specifically to one-way swaps.

When a user requests to swap one token for another, they lock their tokens in the swap script. A trader can then fulfill that swap request by depositing the requested tokens into the swap script.

The requested tokens are now available to the user attached to their staking key. However, for the tokens to become spendable, the user has to "unlock" the tokens by signing with their stake key. This presents a point of friction because now the user has to visit the dapp again to use their tokens. Whereas a user may expect to submit a swap request, then come back later and either the requested tokens are in their wallet or they're not. If they're not in the wallet, it's reasonable for a user to need to take action to close the swap request (if desired). If the swap is completed, a user shouldn't need to go back to the dapp to make the tokens spendable.

The ideal case is that after a one-swap is fully completed, the tokens are deposited into the user's wallet and become spendable like any other Cardano native token.

To implement this, perhaps a new entry can be inserted into the SwapDatum indicating the final destination after the swap is completed. A simple check can be made to verify this value matches the wallet initiating the swap request. (Or alternatively, signing the transaction is authorization enough, thereby allowing the user to create a composable swap that sends the tokens to another destination after the swap is completed.)

For unlocking the tokens after a swap is completed, I see two ways this behavior could be implemented:

  1. Allow destination to be changed to the authorized address within the datum during the execution of the Swap redeemer for the swap script if: a) the token balance reaches zero, and b) all beacon tokens are burned (i.e. the swap is completed).
  2. Create a new redeemer for the swap script that allows spending if: a) the token balance is zero and b) all beacon tokens are burned (i.e. the swap is completed), and c) the destination matches the authorized destination in the datum.

The second approach seems the most straightforward to implement. This would give a dapp permission to unlock tokens on a user's behalf. The disadvantages are that it still requires an additional transaction to unlock the tokens, the dapp owner would need to cover the cost of this transaction if they're to offer this user convenience, and there's no guarantee that the dapp owner will execute these requests on behalf of the user.

The first approach is ideal but will require more modifications and thought. Some assurances could also be provided to the user that the if the swap is fully executed, then they'll receive the tokens at their authorized destination. This will cut down on the number of transactions required to finish a swap.

fallen-icarus commented 7 months ago

Thanks for the request! I think you misunderstand how cardano-swaps works.

The second approach seems the most straightforward to implement. This would give a dapp permission to unlock tokens on a user's behalf. The disadvantages are that it still requires an additional transaction to unlock the tokens, the dapp owner would need to cover the cost of this transaction if they're to offer this user convenience, and there's no guarantee that the dapp owner will execute these requests on behalf of the user.

Cardano-swaps is fully p2p. There is no "dapp owner". Users directly interact with their own DApp UTxOs which means users can craft and sign their own transactions that spend them. You seem to be thinking that cardano-swaps operates similarly to contemporary DApps where users cannot directly interact with their own DApp UTxOs. Contemporary DApps require users to go through middlemen in order to move their funds around. This is why contemporary DApps require dedicated transaction just to withdraw funds. This is not how cardano-swaps works. Cardano-swaps is radically different than contemporary DApps. Please try to keep this in mind while you read the rest of my response.

The ideal case is that after a one-swap is fully completed, the tokens are deposited into the user's wallet and become spendable like any other Cardano native token.

I'm going to re-word your concern to make sure I understand it correctly. If Alice creates a swap for ADA -> DJED (she has ADA and wants DJED), and then Bob fully satisfies the swap so that now Alice has DJED in her swap, your concern is that now Alice must close her swap in a dedicated transaction in order to use her new DJED.

Assuming I am understanding you correctly, this is not true. You do not need a dedicated transaction to close swaps. Since Alice can directly create and sign her own transaction that uses her swap UTxO, she is free to use her swap UTxO however she likes; she just needs to burn the beacons when she decides to use it, and sign with her staking key to prove the swap UTxO really belongs to her.

To give a concrete example, imagine if Alice wants to buy an NFT using her new DJED that is currently sitting in her finished swap UTxO. She can close the swap and buy the NFT with the DJED in one transaction. Alice personally creates and signs this transaction. This transaction just requires Alice to sign with her staking key, and since she is the one who created this transaction, this signing requirement is not an issue. The UX is exactly the same as creating and signing a transaction that spends one of Alice's personal UTxO. She does not need to first withdraw the DJED in one transaction, and then buy the NFT in another transaction.

Your ideal case is already possible thanks to cardano-swaps allowing users to directly interact with their own DApp UTxOs. The limitation you are concerned about only applies to DApps that do not allow users to directly interact with their funds while on the DApp.

Let me know if i misunderstood something, or if you disagree with something I said.

SynthLuvr commented 7 months ago

Thanks for the response. I understand cardano-swaps quite well. I think there's a misunderstanding though in the expression of the request, so let's try and get on the same understanding. Firstly there's a misunderstanding of the term "dapp." Perhaps a better description is "frontend."

The case that you describe is indeed possible, assuming it is the user who is initiating the transactions. However, this isn't a good assumption. In practice, because cardano-swaps is a p2p orderbook DEX, it is other users who execute these transactions -- they are naturally incentivized to do so. It typically wouldn't be the user executing against one-way swap requests, because the liquidity probably wouldn't be there. Where there's a lack incentive is the closing of a completed swap. That is, after a swap has been completed by another user (not the original user), the original user then has to go and submit another transaction to make the tokens spendable. This is inheritably less efficient than it needs to be, because it requires three transactions instead of two.

To illustrate this further, let's say Alice wants to swap ADA for DJED. Alice isn't happy with any of the current prices on offer, so she creates a one-way swap request at the price she desires. After some time, Bob thinks that Alice's price looks attractive, so agrees to Alice's swap request. The ADA in Alice's swap request is now removed (excluding min UTXO) and DJED is deposited instead. From Alice's perspective, her swap request has been completed.

Unfortunately for Alice, she cannot spend her DJED yet. She has to go and submit another transaction to "unlock" her DJED so that she can spend it. This is the pain-point Alice wants solved. When Bob completed Alice's swap request, he could've helped Alice out a little more so she could avoid submitting another transaction.

Granted, this is a bit complicated to implement, so I've proposed an alternative solution by adding another redeemer. I believe both options are useful, so we are currently exploring how to implement them.

SynthLuvr commented 7 months ago

I understand your point of combining two actions into one, but I don't believe this is practical. You are assuming that Alice is a technical user. She is not. We can help Alice further by embedding this unlock feature directly into cardano-swaps either during the swap process or allowing a third-party to execute it on her behalf via a new redeemer. This will make the DEX more composable.

SynthLuvr commented 7 months ago

Maybe I can explain this in another way for clarity.

Alice has ADA. Alice wants to send DJED to Tom. Alice only wants to sell her ADA at a specific price. Alice wants to do this in only one transaction.

Alice submits her transaction and within the datum specifies the destination endpoint for the DJED.

Bob is searching for arbitrage opportunities, and notices that Alice's request is profitable. Bob executes Alice's request, and during this process, DJED is transferred to Tom.

Alice has now swapped ADA for DJED and sent DJED to Tom, thanks to the help of Bob. This was completed in only 2 transactions.

fallen-icarus commented 7 months ago

Thanks for the clarifying example. Sorry for the misunderstanding :sweat_smile:. I was originally going to write a long reply discussing the pros and cons of this feature request, but I want to make sure there are no other misunderstandings first.

In your first comment, you said this:

... a user shouldn't need to go back to the dapp to make the tokens spendable.

and then in a later comment, you said this:

... there's a misunderstanding of the term "dapp." Perhaps a better description is "frontend."

Are you imagining that a user's personal wallet and cardano-swaps are two separate frontends? These p2p protocols are meant to be like the wifi protocol; you don't need a dedicated "wifi app" to use it. All devices just natively have wifi features. Part of the grant I received from catalyst is to create a PoC frontend that has user-friendly ways of doing these compositions. I am trying to design the UX such that users don't "visit the DApp"; instead, it is cardano wallet (like eternl or yoroi) that just natively has DEX support just like how phones just natively have wifi support. You don't need a dedicated application to interact with cardano-swaps. This is not the same as a DApp connector. It is literally a wallet that can create/swap/close limit orders directly, in addition to normal wallet actions. Imagine if Daedalus just had an option for you to create limit orders, check open orders, and close limit orders directly. Daedalus would be able to completely hide from the user that the swaps are technically at a separate address. In this scenario, a wallet is not just a collection of pubkey addresses, it is a collection of pubkey addresses and swap addresses that use those pubkeys as the staking credential. It is one wallet software that natively has a DEX built-in. That is why I try to call these "protocols" instead of "DApps"; they can be directly integrated into any wallet software. You don't need a dedicated frontend to interact with cardano-swaps. I deliberately designed it with the intention that it would be directly integrated into wallets like daedalus, just like how wifi is directly integrated into devices.

I already have a very basic version of the wallet working. The workflow I am trying to create is that of a "transaction builder". Users just specify actions they want to happen (eg, create a limit order with this price, pay this person, and create this loan offer), possibly specific additional UTxOs to spend, and the builder just does the rest. The user doesn't need to care which address the UTxOs are technically located in - the builder takes care of that. The wallet manages all of the user's addresses (pubkey, swap, loans, etc) to the point where the user doesn't need to know which one is which. The user doesn't even need to know the extra addresses exist; the wallet can always re-derive all of them from just their pubkeys.

I understand your point of combining two actions into one, but I don't believe this is practical. You are assuming that Alice is a technical user. She is not.

With my wallet, this is actually really easy. Alice just needs to tell the transaction builder: "I want to close this limit order and pay Tom 100 DJED". The builder does everything else. Alice does not need to know any command line stuff, she doesn't even need to know how to actually close the swap. The way the wallet works, she doesn't even need to know about the beacons. The wallet will automate all of that. It is one wallet software that natively has all DeFi features built-in. Does this change your view on how hard you think this composition will be?

SynthLuvr commented 7 months ago

Does this change your view on how hard you think this composition will be?

No, it doesn't.

I appreciate your grand vision and view, and I can understand if our use cases for cardano-swaps differs. I think it's a fantastic grand vision, but a lot of this depends on future functionality. Whereas we are trying to make cardano-swaps work today.

Are you imagining that a user's personal wallet and cardano-swaps are two separate frontends?

I view cardano-swaps as a protocol that can have many frontends. Some of these frontends will have differing visions. Furthermore, I see cardano-swaps as a reusable collection of open source validators, which naturally leads to their functionality needing to be extended (safely).

These p2p protocols are meant to be like the wifi protocol; you don't need a dedicated "wifi app" to use it.

That's a nice vision for the future, but today these smart contracts need a mechanism to use them.

going to write a long reply discussing the pros and cons of this feature request

I think this will be useful so it can be decided on whether these changes should be merged upstream.

fallen-icarus commented 7 months ago

I'm going to put things into my own words.

Motivation

The motivation seems (to me) to be that you think creating composed transactions, like closing and sending, will be difficult for most users to do because most frontends do not, and likely will not, support a UI for creating these composed transactions. Even if my wallet will, other frontends will not. Therefore, cardano-swaps should be expanded to directly support features to fill this void.

The example used is, if Alice is converting her ADA to DJED just to pay Tom, just encode the swap so that the new DJED goes directly to Tom when Bob satisfies Alice's swap; Alice's swap would automatically be closed (and the beacons burned) when Bob fully satisfies her swap. This would save Alice the trouble of having to close her swap in one transaction and send the DJED to Tom in another transaction, since (according to the argument) most frontends will not enable her to easily do both in one transaction. The claim is that only tech savvy people will be able to do both in one transaction.

Considerations for Any Solution

Any approach to implement this feature must address these concerns (in order of importance to me):

  1. Cardano-swaps is not just a DEX; it is meant to be the liquidity backbone for all DeFi DApps - If the execution footprint (ie, the fee or the execution budget) for a swap increases, it is less able to play this role due to a loss in overall composability. Each swap must have as small of an execution footprint as possible. It would be bad if there was a very expensive DApp that couldn't compose properly with cardano-swaps due to the swaps being a bit too large; this would effectively mean that DApp is cut off from all DeFi liquidity. Any noticeable increase in the execution footprint must seriously be worth it.
  2. Partially swapping and fully swapping must have comparable fees and use up comparable amounts of the transaction's script execution budget - If they are not comparable, there is an incentive to avoid whichever one is more expensive.
  3. Double satisfaction of direct payments to Tom - Currently, using the datum of the output is the cheapest method. But this means Tom's address must be able to accept this datum. In the future, there may be other possibilities, but for now, Tom's address cannot be any address. The output could be locked forever if it is locked at a plutus address that requires a different datum.
  4. What should the datum be? - Even if you address double satisfaction without using the datum, what if Alice needs to attach a specific datum to Tom's output? Enforcing that the proper datum is used increases the execution footprint which goes back to the first concern.

This is inheritably less efficient than it needs to be, because it requires three transactions instead of two.

The goal here is not efficiency in the number of transactions required. The goal is economic efficiency; this includes the total economic efficiency for all of Cardano DeFi (this protocol is supposed to be the liquidity backbone). If eliminating that extra transaction makes it economically less efficient overall, that is :-1:.

Approaches

I believe your first suggestion mainly fails with the second concern since it likely makes full swaps more expensive than partial swaps: burning the beacons is extra logic and requires an extra script execution, plus any datum enforcement logic is purely extra. This extra cost could incentivize swappers to avoid fully satisfying swaps, which translates to avoiding smaller swaps, if possible.

If we instead require all payments (including partial payments) to go to Tom with the change being returned to Alice's swap address, then we could easily make partial swaps more expensive than full swaps. Checking the extra change output in addition to the output to Tom is going to be costly (the change output isn't necessary for the final swap). Similar approaches I had benchmarked showed a degradation of at least 20% in total throughput. Max throughput itself isn't important, but it is inversely proportional to execution footprint. The extra cost could incentivize swappers to avoid partial swaps, which translates to avoiding larger swaps, if possible.

The current design treats partial and full swaps exactly the same so these perverse incentives aren't a concern.

If we just made it optional to send to Tom's address during partial payments, it would likely never be used since it would be more expensive than just depositing it all into the swap UTxO. This scenario would effectively just turn into the scenario where only the final swap goes to Tom. The extra logic during partial swaps would likely go unused.

Adding an extra incentive (Alice allocates a cut for swappers who send an output to Tom) may incentivize swappers to accept the higher cost for sending the assets to Tom. This approach would have to be optional so as to not penalize users who don't care if it goes to another address. However, since these swaps will have extra logic to enforce, they will likely have a higher execution footprint than swaps that do not opt to send the results somewhere else. I would guess it still results in a 10-15% performance degradation for swaps that choose to send the assets elsewhere. This could create a bifurcation of liquidity because aggregators may try to avoid these swaps. This single swap could have the same execution footprint as 2-3 normal swaps. Therefore, aggregators need to weigh the profit from satisfying this one swap with an output to Tom, against satisfying 3 normal swaps. And you also have to consider how much Alice is willing to pay for this extra service. She has to cover the cost for the aggregator to execute the extra logic on her behalf and cover the opportunity cost from forgoing 2-3 other normal swaps. Covering the opportunity cost for 2-3 normal swaps could easily be prohibitively expensive - arbitrage opportunities can easily be very profitable. Would Alice be willing to pay enough to incentivize aggregators? This is a question that should be answered before pursuing this approach.

Your second suggestion could work, but as you said, it doesn't eliminate the 3rd transaction. And it has the same concern of whether Alice will be willing to pay enough to incentivize someone else to close it for her. Any swap closed takes the place of another normal swap, and therefore, a potential arbitrage opportunity.

Another option is to create an entirely new swap type (like one-way or two-way swap). This swap would be dedicated to this feature while leaving the current swaps alone. However, this has the same incentive problem as the previous approaches.

The last option I thought about is to just enable Tom to be able to close Alice's swap once it is satsified. With this approach, you wouldn't need to incentivize anything. Alice can specify his pubkey in the swap datum and the protocol could enforce that Tom can only close it if it is indeed fully satisfied. A beacon token unique to Tom could be used so that Tom can easily find Alice's swap UTxO. This action would get its own "close" redeemer. However, since the swap datum is now larger and the swap execution now needs to also guard this new beacon, there is still a performance penalty for all swaps. If we didn't use beacons, Alice would need to tell Tom where to find her swap UTxO off-chain which severely limits this feature's trustless applications. Also, even without the beacons, the datum would still be larger which has a slight penalty on any swaps that use this.

Closing Thoughts

The more I think about this feature the more I am against it. All of the approaches either have a performance penalty or questionable incentives. And this is all just to enable a feature that is already possible. You just need to use the protocol as intended - create composed transactions.

I completely disagree with the perspective that most frontends will not support creating composed transactions in the future. I think there is tremendous economic value in composed transactions. So much so, that I believe frontends that support creating composed transactions will out-compete those that don't. I think the only reasons this outcome hasn't occurred yet is 1) most people don't know what is possible (and therefore, aren't demanding them yet) and 2) DApp developers are not designing their DApps with composability in mind (some of them are hard-coded not to compose). Just like with the frontends, I think DApps that can compose will out-compete DApps that cannot compose. The economic benefits are too great, IMO. The demand for composable DApps will further drive demand for frontends that support creating composed transactions.

The wallet I am building is meant to showcase what is possible. AFAICT it would be the very first frontend to support composed transactions. I should be done with a PoC by end of year (I have to finish the other protocols first - as per the grant). In the end, I think it will be so easy to create these composed transactions that the motivation for this feature will largely go away. I would expect the composed transaction for Alice to only cost about 0.05-0.10 ADA for each swap closed (closing two swaps in a single transaction is only 0.2 ADA total). If this is indeed the cost, I don't see Alice being willing to pay enough to incentivize someone else to send the DJED to Tom on her behalf.

Whereas we are trying to make cardano-swaps work today.

I am not trying to make it work today. I am trying to make it work the way I envision it. If it was going to be years until my vision was actualized, I would be more open to this suggestion, but we are talking about months until the wallet is ready. As I said before, I already have a very basic wallet prototype working.

a lot of this depends on future functionality.

I agree. That is why I believe it is better to take a wait-and-see approach. If, after I release the wallet, people don't really seem interested, I would reconsider this feature request.

I am going to tag some other people who might be interested in this discussion. @rphair @TerminadaPool @zhekson1

SynthLuvr commented 7 months ago

Thank you for providing your thorough analysis.

I think the most viable step forward for us is to continue with building out a fork, and then we can come back and re-evaluate in a few months time.

Looking forward to seeing your wallet come to life!

zhekson1 commented 7 months ago

As far as the main branch goes, I largely agree with @fallen-icarus here. The point of this project is to pave the way for what a truly composable dApp ecosystem could look like, which means taking into account not only what exists today, but what is possible to build tomorrow. This is not a purely philosophical decision, it is a very practical (though long-term focused) decision for the reasons mentioned above, chiefly efficiency & incentive alignment.

The beauty of eUTxO is that most logic can (and likely should) be pushed off chain. The upside is lower on-chain overhead (and thus higher throughput + economic efficiency), and higher generic-ism in transaction intent. Practically, this requires a lot more off-chain infra than what is currently floating around - a bit of a chicken/egg as we are all used to in this industry.

And so, for the purposes of this project, I think modifying on-chain code to compensate for anything that could be done off-chain is almost never worth it (it'd require an extraordinary reason). This is especially true here, where the feature can (and will) be implemented on the wallet/frontend side, not on-chain. Of course it also means that other "dApps" would need to support processing such inputs, but we've got to start somewhere (also why the P2P-DeFi family is designed the way it is as a whole).

Of course, I am totally supportive (and elated) that you find this useful enough to fork. I hope it serves your project well, I am totally open to being proved wrong here, and I look forward to a warranted reconsideration.

Godspeed to you and all that you're working on! 🚀

TerminadaPool commented 7 months ago

Thanks for detailing your vision @fallen-icarus. I appreciate the explanation of your design concerns.

Design concern No. 2:

Partially swapping and fully swapping must have comparable fees and use up comparable amounts of the transaction's script execution budget - If they are not comparable, there is an incentive to avoid whichever one is more expensive.

This raises an issue I had in my mind. Imagine the case of stable coin swaps where the exchange rate is expected to stay very close to 1:1. Say you listed a swap 100,000 USDM -> 100,000 Djed. Then say someone completed 99,990 of this swap leaving only 10 USDM remaining. Effectively the exchange rate for this remaining 10 USDM -> 10 Djed has now increased somewhat when you take into account the transaction fee. This exchange rate increase could be significant enough in a stable coin to stable coin swap that the remaining 10 USDM might never get swapped??? Which could be a bit annoying for users seeking their entire swap to be executed since they won't like tidying up these uncompleted loose ends.

I used the example of a stable coin -> stable coin swap because this exchange rate is expected to remain tight, which would exacerbate the problem I am concerned about.

IE: Is there an annoying attack where some nasty person goes around completing 99.9% of swaps leaving 0.1% remaining thus rendering the remainder un-profitable to fully close out, which could cause these residual bits to be left on-chain??? And, why would someone do this? Well, maybe just to be nasty?

Is there some mechanism to ensure that lower exchange rate small amount swaps are fully completed before taking liquidity from higher exchange rate swaps? Would it be possible for the user to specify a minimum execution chunk size that can remain if the swap is not fully completed to avoid un-profitable residues?

Sorry to derail the original issue conversation but your explanation reminded me of this concern.

fallen-icarus commented 7 months ago

@TerminadaPool I think this is a place where the market can likely take care of itself.

This exchange rate increase could be significant enough in a stable coin to stable coin swap that the remaining 10 USDM might never get swapped???

The exchange rate is not the only thing that determines how "useful" a swap will be. Time value of money, arbitrage opportunities, and concurrency competition also play a role. To highlight this, consider the following three examples:

The concurrency risk in the last scenario also applies to the previous scenarios. If the arbitrage chain needs to pass through a popular (for arbitragers) trading pair, the arbitrager can deliberately choose an "out of the money" swap for that pair just to minimize the overall risk of collision. The point I am trying to highlight is that it is really hard to say what constitutes a dust swap. There is still economic utility for swaps that, individually, would be unprofitable. This is especially true due to how composable this protocol is with everything. What counts as too unprofitable depends on what opportunities are available. Due to the sheer number of arbitrage possibilities that would exist on a fully adopted cardano-swaps, arbitragers may even be willing to make a 25% loss on a single swap just to be able to claim an opportunity that yields 10% overall. Even a swap with only 1 USDM available could be worth it. The same may be true for users who want to compose the p2p protocols.

I'm sure there will be some "residual swaps" just because users literally forgot about them. But the moment the last offer asset is taken, the beacons will stop broadcasting those swaps (the only exception is ada which cannot be fully taken due to the minUTxOValue). Plus, I think there is tremendous potential for the off-chain infrastructure to help out - they can filter out swaps that have less than some total value available. Since this protocol is fully open, users can choose what level of filtering they want. At the end of the day, the beacons are just a trick to make off-chain filtering easier. The swaps don't need to be gone from the UTxO set; there just needs to be a simple heuristic that the off-chain infrastructure can use to filter them out. For the residual ada swaps, that could be "filter out all swaps offering ada that have less than 5 ada available." If I were to add expiring limit orders, the off-chain infrastructure can easily filter out swaps that have already expired.

Sorry to derail the original issue conversation but your explanation reminded me of this concern.

I actually think this concern is related because it ultimately comes down to the risk/reward of a swap. If you increase the cost of a single swap by adding a feature, you can dramatically alter the composition landscape. If you make it too expensive, it makes an attack like the one you describe more effective. This is just another reason to only increase the execution footprint of swaps if it is absolutely necessary. I think it also highlights how increasing the execution footprint of swaps can make DeFi less economically efficient overall.

SynthLuvr commented 7 months ago

Thanks for continuing the discussion. The concern seems to be mostly around execution footprint.

Instead of embedding the logic into cardano-swaps, perhaps this additional logic can be moved into a third-party staking validator. In the staking_credential_approves function, it's checking for either withdrawal from a staking validator or a staking pubkey. Most users will use pubkey. However, this might give us opportunity to add more capability to cardano-swaps via a parameterized staking validator.

Albeit, this comes at the disadvantage of sacrificing one of cardano-swaps' core principles: for users to always remain custody of their tokens. In this approach, staking rights would be transferred to another validator. In exchange for this forfeiture of staking rights, it does open up a lot of opportunity for composability.

SynthLuvr commented 7 months ago

Hmm, but then if the staking validator is parameterized, each variant would need to be registered. That's not very composable then...

Still, food for thought.

SynthLuvr commented 7 months ago

We've put together two proof of concepts: the first using a fork of cardano-swaps, and the second with a custom stake validator. Both variations seem to be working correctly. We will continue to evaluate available options.

fallen-icarus commented 7 months ago

@SynthLuvr A few comments:

Most users will use pubkey.

I do not think this is the right perspective. While technically true, if regulators are going to allow corporations to adopt DeFi, the corporations' UTxOs must, at the very least, use multisig. Some of them may prefer their own custom logic. Either way, it must be possible for corporations to use a staking script. I am against repurposing the address' staking credential for protocol logic. Doing so will likely cut off a huge amount of potential liquidity.

In exchange for this forfeiture of staking rights, ...

I cannot see any scenario in which I will be in favor of sacrificing staking rights. IMO there is zero point in building a DApp that directly undermines the security of the blockchain it is running on. You are just making it easier for rogue nation states to attack the DeFi ecosystem by centralizing (or side-lining) a huge amount of stake.

Instead of embedding the logic into cardano-swaps, perhaps this additional logic can be moved ...

It doesn't matter where the logic is located. What matters is the total amount of logic that runs in the same transaction where a swap occurs.


If you really want to try out this feature, I would suggest creating an entirely new kind of swap. Then you can see just how much demand there is for this feature by directly comparing it against the demand for the one-way swap. As long as the swap you create is composable, they should be relatively interchangeable. If you are correct about the utility of this feature, most users should gravitate towards your swap type instead of creating composed transactions.

SynthLuvr commented 7 months ago

Thanks for the feedback. I think there are some misunderstandings, differences in visions for the future, and differences in definition of composability.

We have our working prototypes implemented and will continue to flesh them out further. All three solutions we've ideated work and are viable. Creating an entirely new kind of swap is unnecessary.

SynthLuvr commented 7 months ago

However, since the swap datum is now larger and the swap execution now needs to also guard this new beacon, there is still a performance penalty for all swaps

The concern here seems to be around creating a datum larger than strictly necessary.

What if we store this extra datum outside of the swap datum? Then it won't affect the performance of swaps. We can introduce a new redeemer to close the swap and allow moving of funds if certain conditions are met. If we're able to attach more data to the datum, we could also attach a staking validator, allowing us to extend cardano-swaps without forking. Ideally we should avoid cardano-swaps forks, because then it creates segmented liquidity.

Unfortunately, the only viable implementation I can think of involves quite a bit of complexity: introduce a second multi-validator that has a spending policy and minting policy, and check for the token matching the policy ID in a new redeemer in the existing one-way validator.

This should, in theory, give us the desired functionality, while also meeting the desires to keep the additional datum data entirely opt-in without interfering with any existing swap logic.

In the new minting policy, it can allow minting if the token is sent to the user's swap address, signed by the user's stake key, and a valid datum is attached. In essence, this allows attaching a datum to an existing datum, without modification of the original datum. A user could initiate a one-way swap and create two UTXOs -- one for the swap, and one for the extra data. This could also create an incentive for the final execution instructions to be completed, because the extra datum requires min ADA, and that min ADA could be extracted by anyone executing the transaction.

In the datum, we can put the hash of the swap datum, a destination address, optionally another datum field in the case the destination address is a script, and optionally a stake key in the case the user wants to add additional logic (effectively allowing cardano-swaps to be extended without a fork).

The UTXO and token will be locked in a spending validator that can be unlocked if the token is burned. The token can be burned if the conditions of the token transfer are met:

  1. Output is being sent to destination
  2. A swap UTXO with datum (excluding prev_input) hash matches the swap hash
  3. Datum matches the extra datum in the datum, if present
  4. Staking validator is attached, if present

For cases where the user wants to cancel the swap, it could also be burned if signed by the original stake key.

The new redeemer in the swap validator should be straightforward, allowing spending if:

  1. The offer token balance is zero
  2. The beacon tokens are burned
  3. The new token is burned
fallen-icarus commented 7 months ago

I'd be open to adjusting swaps to allow arbitrary extensions as long as:

  1. The change is very simple.
  2. It does not open up security holes.
  3. It is proven to work for multiple different extensions.

A simple change could be adding a new extensions field to the datum that is a list of script credentials. These script credentials can approve spending using a dedicated redeemer. People can just leave the list empty to opt out of extra features.

But I have not seriously thought this through and am concerned it may open up security holes. TBH I think brainstorming designs has limited utility; a lot of nuances only appear when you actually sit down to build it end-to-end. I am concerned some of those nuances can dramatically increase the protocol's complexity.

Another concern I have is how front-ends will interact with this new list. There is currently very little a user can do to verify datums; even hardware wallets struggle with this. What if a malicious front-end puts a malicious script credential in this new list?

Even if my concerns are unfounded due to possible solutions to them, I really want to see that this approach works for more extensions than just the one in this issue. I want to avoid the situation where this solution works for your feature, but not for another feature. Unfortunately, I am personally struggling to come up with possible useful extensions.

SynthLuvr commented 7 months ago

Thanks for the discussion.

TBH I think brainstorming designs has limited utility; a lot of nuances only appear when you actually sit down to build it end-to-end. I am concerned some of those nuances can dramatically increase the protocol's complexity.

I think what's most important is getting cardano-swaps to work in the real world, not just working in theory. So I agree in part that some of these nuances don't pop up until it comes time to actually integrate, which is exactly why we're having this discussion.

Unfortunately, I am personally struggling to come up with possible useful extensions.

This to me is confusing. I'm unsure if I've done a poor job of explaining myself in written form, or if there's simply some fundamental misunderstanding somewhere. When I talk with others it has seemed surprising that cardano-swaps didn't already support this feature. I've spoken with DEX aggregator teams and I honestly believe it'll be hard for cardano-swaps to compete with the likes of other DEXs if it doesn't support this ability to make swaps composable. Then again, I think that's what we seem to have differences of opinion on -- composability in theory, versus composability in the real world. I think SundaeSwap's v3 has done a great job in thinking through different ways to make their swaps more extensible. I think cardano-swaps has a lot of potential too if we can overcome this hurdle.

As we get closer to deployment, I do really want to avoid having the problem of segmented liquidity. Fortunately, it's relatively straightforward to switch from one set of validators to another.

SynthLuvr commented 7 months ago

A simple change could be adding a new extensions field to the datum that is a list of script credentials. These script credentials can approve spending using a dedicated redeemer. People can just leave the list empty to opt out of extra features.

If you're open to this approach, that would really simplify things a lot for us. We did do some benchmarks and it does introduce a very tiny performance cost. It's negligible though.

If we're to do this approach, then we will require users to register the parameterized staking validator upon initialization of the one-way swap. We did create a small working prototype for this, and the approach does work. This also creates an incentive because ADA is locked up during the registration, and some of that ADA can be collected by whoever performs the execution.

zhekson1 commented 7 months ago

Something I still don't quite understand: what is the value of implementing this optional "extensions" feature upstream (for ALL instances of cardano-swaps) as opposed to specific instances where the extra logic is necessary?

Cardano-swaps is already fully composable with most DeFi, and it would be composable with "forks" that implement the extensions feature. As I mentioned previously, the "in-theory" vs "in-practice" dilemma is largely due to a lack of standards/infra in the space, and not due to limitations of on-chain code. This is especially true in the context of frontends/transaction-building.

I really would like to see concrete examples of the benefit of having the extensions feature present in ALL instances of cardano-swaps, versus just specific instances of swaps that need the feature for additional logic, especially since the two can still compose with one another. In other words, why implement this feature upstream when it can be implemented by a fork, and still compose? If I am understanding correctly - you mentioned this could segment liquidity, but I don't see how... could you elaborate?

Perhaps it may be useful to involve other entities you mentioned (aggregators, DEXes, e.t.c.) in this discussion to flesh out the details.

fallen-icarus commented 6 months ago

If you're open to this approach ...

I am not open to this approach, it was just a brainstorming thought. I already mentioned a few concerns I have with it. And actually, the more I think about it, the more I am against it because current off-chain infrastructure does not make it easy for users to verify datums. It seems trivial for a malicious front-end to covertly weaponize this new field.

SynthLuvr commented 6 months ago

I think it's no longer productive to continue this discussion. A fork is the appropriate path forward, and the market will decide what's best.

fallen-icarus commented 1 month ago

It is still a work in progress, but I have open-sourced what I have so far for the wallet I am building. From the screenshots, you can see it is easy to close a swap in the same transaction as a bunch of other stuff.