stellar / stellar-protocol

Developer discussion about possible changes to the protocol.
523 stars 303 forks source link

Add asset transaction fee support #101

Open dsri opened 6 years ago

dsri commented 6 years ago

Hi - We are interested in support for an adjustable internal transaction fee for assets that would look something like this:

  1. For every transaction, a percentage of the sent amount is redirected to a specified fee pool account separate from the issuing account. This can be in addition to the XLM transaction fee.
  2. The fee pool is distributed later, and transactions involving the issuing account and fee pool account are exempt from the fee.
  3. Working accounts can also be made exempt through an account whitelist.
  4. An initial fee rate is specified when the asset is issued, but can be adjusted by the issuing account. The rate can be set at least as precisely as '0.01%'.

Appreciate any thoughts or feedback about this. Thanks!

dgobaud commented 6 years ago

I am interested in this too!

dgobaud commented 6 years ago

Would be good to be able to say the fee is not modifiable so people don't worry about you setting the fee to 100% etc later

johansten commented 6 years ago

I'm generally opposed to this kind of rent-seeking, since there's literally no cost at all for the issuer to change an owner.

Assuming it was possible though, where would we add it?

Since assets don't exist outside of the trustlines table, there isn't any asset entity to store this kind of information inside. Ripple does it by having their asset code defined as a (asset-code, transfer-rate)-tuple, but that means that all your users needs to be aware, and that you can set trust lines for arbitrary transfer rates.

dgobaud commented 6 years ago

There actually might be some compliance costs for some security tokens when they move between different owners - a transfer agent might charge based on number of exchanges etc

jedmccaleb commented 6 years ago

Several people have asked for something like this. I'm open to adding a fee on sends but not on trades.

The issue is it is unclear how to specify the fees and people want different things of arbitrary complexity. Is it % of the amount sent Is it a fixed amount is it a % + a fixed amount like a credit card is it a % that caps out at a maximum etc

The other option to provide a business model for anchors would be to add some sort of negative interest rate but its not clear to me how you would do this efficiently and also I suspect that that is psychologically worse for consumers.

for johansten: you wouldn't be forced to use assets that have these fees of course.

dgobaud commented 6 years ago

Supporting all 4 of those options would be great and letting the issuer configure it

ScottHogge commented 6 years ago

Allowing asset issuers to attach trading fees to their assets is a necessity for many use cases, and is going to happen one way or another. Not providing a native way of doing it will only lead to sub-optimal implementations that are no good for anyone.

Why trading fees are necessary (a few of many reasons):

For many asset issuers, there may be no financial incentive to tokenize real world assets if they cannot collect trading fees.

Issuers may have expenses related to trading. For example, issuers may wish to incentivize market makers to add liquidity to the market by providing a rebate (negative fee) for trades that add liquidity. This is how almost all traditional markets attract liquidity, e.g., Nasdaq pays participants who add liquidity anywhere from $0.0015-$0.003/share (and charges those who remove $0.003/share).

To the best of my knowledge, there are currently 3 methods of implementing trading fees:

Method 1) Using the co-signer method (approach 2 in issue #135 / issue #146 ), an issuer can require signing of all transactions on their customers accounts to ensure that for every ManageOffer operation there is a corresponding Payment operation representing the fee. The tricky part becomes what happens when a customer wishes to cancel or modify an offer, because this method attaches fees to placing offers, not to the matching of offers (i.e. trades).

To cancel an offer and return the fees, you cannot simply ensure that the inverse of the Payment is part of the transaction, because you need to know the actual quantity that was canceled when the transaction makes it to the ledger, not your view of the offer at time of signing. There exists a race condition between the signing of the transaction to return fees, executions the network may make before processing the fee-return transaction, and any other ManageOffer operations that are pending for that offer. What we are left with here is requiring an outside service to monitor the ledger and return fees for canceled offers after the fact. Same thing, but more complicated, for updating offers.

Method 2) Monitoring the ledger to calculate fees for trades as they happen, periodically invoicing the customer, and using the co-signer method to prevent withdrawals from customers who have not authorized payment on their outstanding invoices.

Method 3) Keeping customer account private keys and handling fees yourself.

Recommendation

Would be good to be able to say the fee is not modifiable so people don't worry about you setting the fee to 100% etc later

Rather than implementing fee structures as part of trustlines or something like that, I suggest simply adding them as fields (preferably an array of fees) to the ManageOffer operation which then can be co-signed as described in issues #135 and #146. This would also allow for user-specific fees.

fees: [ { add_liquidity_fee: .1% (possibly negative) remove_liquidity_fee: .1% (possibly negative) fee_based_on_selling_asset: true (since all offers are represented as sells, you may wish to collect fees as a percent of MARBLE for both MARBLE-ROCK and ROCK-MARBLE trades fee_recipiant: GASJFSDJF2DS3…… }, {...another fee, possibly burning some utility token or something like that... } ]

TL;DR; Trading fees are going to happen. All we can do is attempt to guide how they are implemented.

johansten commented 6 years ago

@ScottHogge

I think it's a bit dishonest to compare how a centralized exchange with large infrastructure costs compare to a decentralized exchange where none of the nodes are getting paid.

If anyone should be paid liquidity fees it's the nodes that make up the network, not issuers.

EDIT:

Maybe a better way of saying this would be, you can't look at issuers in isolation. Incentivizing networks participants is a network-wide problem.

We might need to fix these things first/too?

ScottHogge commented 6 years ago

@johansten

I don't see the utility in conflating these issues, nor any reason why they cannot be discussed in parallel.

Neither nodes nor issuers provide liquidity, i.e., resting orders on the book, providing a free option to trade the advertised size/price, which active traders may choose to take. Liquidity is generally provided by market makers and, to a lesser extent, passive traders. In traditional markets, market makers are incentivized to provide liquidity with rebates (negative fees). This is one of the primary means by which exchanges compete with each other for liquidity. If, for example, issuers or specialized exchanges built on top of the SDEX wish to incentivize market makers to provide liquidity for their tokens/platforms on Stellar rather than a centralized exchange like Binance, they need a mechanism for doing so.

Moreover, you don't really address the main point of my post: There are already ways of implementing trading fees; they are simply cumbersome and more prone to error. When a business model requires charging fees, people are going to charge fees. All I'm saying is that for the benefit of the platform, we should provide a native way to do it safely and transparently.

johansten commented 6 years ago

@ScottHogge

I hear what you're saying, and I know there's an opportunity cost to providing liquidity. Infrastructure costs are real though.

Coming from the retail side, but with more than a decade of trading experience, what I see are trading fees based on volume traded, with (potential) rebates for orders that add liquidity, depending on the platform.

One problem with adding fees by adding an explicit payment is that you have no idea, at the time you create your order, if it will add liquidity or not, since the order book might change before your order is placed. Your order might both remove, and add liquidity, and you can't know in advance. And how would you specify who to pay the fee/rebate to?

So, it has to be integral to the system.

Now, who specifies the rebate?

The network, and/or individual issuers?

ScottHogge commented 6 years ago

@johansten

I hear what you're saying, and I know there's an opportunity cost to providing liquidity. Infrastructure costs are real though.

It's not just opportunity cost, there is serious adverse selection risk that market makers need to be compensated for.

One problem with adding fees by adding an explicit payment is that you have no idea, at the time you create your order, if it will add liquidity or not, since the order book might change before your order is placed. Your order might both remove, and add liquidity, and you can't know in advance. And how would you specify who to pay the fee/rebate to?

That's exactly why I recommended the above structure for adding fee descriptions to ManageOffer:

fees: [ { add_liquidity_fee: .1% (possibly negative) remove_liquidity_fee: .1% (possibly negative) fee_based_on_selling_asset: true (since all offers are represented as sells, you may wish to collect fees as a percent of MARBLE for both MARBLE-ROCK and ROCK-MARBLE trades fee_recipiant: GASJFSDJF2DS3…… }, {...another fee, possibly burning some utility token or something like that... } ]

As it is right now, using the currently available methods I described, you have to charge everyone a fee, but can then monitor the ledger to return fees + a rebate to accounts whose offers added liquidity. Very possible, just cumbersome and not transparent.

Now, who specifies the rebate?

I'm advocating for a solution that can be used in conjunction with #135 / #146

theaeolianmachine commented 5 years ago

@ScottHogge or @dsri, would you be up for putting this in a formal draft?

Also having not read the entire thread, would CAP-0018 suffice to solve these problems? Mostly wanted to bring it to your attention; my intiution says no, but we're mostly doing cleanup here.