-
Notifications
You must be signed in to change notification settings - Fork 104
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
SRC-21: add out-of-band fee-on-transfer token standard #103
Open
moodysalem
wants to merge
7
commits into
starknet-io:main
Choose a base branch
from
moodysalem:out-of-band-fee-on-transfer
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 6 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
0f3eb33
add fee on transfer interface
moodysalem a4e5391
add an overload for paying fees that does not require knowing the cal…
moodysalem 1ee7477
clean up
moodysalem 02ac2bf
fix tadev comments
moodysalem 5e9ff1b
Merge branch 'main' into out-of-band-fee-on-transfer
moodysalem 42c94dd
assign SNIP number to 21
Eikix 57a42a6
Merge branch 'main' into out-of-band-fee-on-transfer
moodysalem File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
--- | ||
snip: 21 | ||
title: Out-of-band fee-on-transfer tokens | ||
author: Moody Salem <[email protected]> | ||
status: Living | ||
type: SRC | ||
created: 2024-07-29 | ||
--- | ||
|
||
## Simple Summary | ||
|
||
Defines a standard for fee-on-transfer tokens that maintains the semantics of fungible token transfers and has no | ||
changes to the existing tokens interface. | ||
|
||
## Abstract | ||
|
||
Fee-on-transfer is the idea of charging a fee whenever fungible tokens are transferred from one address to another. Such | ||
tokens are very popular on other networks. These tokens have inconsistent implementations and do not work well with many | ||
DeFi protocols, but are still popular for the deflationary aspect. | ||
|
||
In particular, some DeFi protocols such as Ekubo Protocol do not require tokens to be transferred to swap or add | ||
liquidity. This allows liquidity providers and swappers to entirely circumvent any possible implementation of a fee | ||
taken on transfer--the token contract does not need to be called at all in order to interact with Ekubo pools. | ||
|
||
## Motivation | ||
|
||
Due to the demand for this functionality, it's important to define a mechanism that makes the best use of Starknet's | ||
capabilities. This SRC defines an implementation of fee-on-transfer tokens that maintains the semantics of fungible | ||
token transfers, allowing it to be broadly compatible with Starknet DeFi, and also requires no changes to the token | ||
interface. This is possible because of Starknet's native account abstraction and multicall. | ||
|
||
## Specification | ||
|
||
```cairo | ||
#[starknet::interface] | ||
pub trait IFeeOnTransferToken<TContractState> { | ||
// Gets the amount of fees already paid for the given sender | ||
fn get_fees_paid(self: @TContractState, sender: ContractAddress) -> u128; | ||
|
||
// Returns the amount of fees required to transfer the specified amount of tokens from the given sender to the receiver. | ||
fn compute_fees_required( | ||
self: @TContractState, sender: ContractAddress, receiver: ContractAddress, amount: u128 | ||
) -> u128; | ||
|
||
// Pays fees from the given address for the specified sender. | ||
// If `from` is the caller, then it pays from the caller's balance. | ||
// Otherwise, it pays from the allowance of the `from` address to the caller as the spender. | ||
// Returns the total amount of fees paid for the sender | ||
fn pay_fees_verbose( | ||
ref self: TContractState, from: ContractAddress, sender: ContractAddress, amount: u128 | ||
) -> u128; | ||
|
||
// Same as pay_fees_verbose but always pays `from` the caller address | ||
fn pay_fees_for_sender(ref self: TContractState, sender: ContractAddress, amount: u128) -> u128; | ||
|
||
// Same as pay_fees_for_sender, but the sender is always the caller address | ||
fn pay_fees(ref self: TContractState, amount: u128) -> u128; | ||
|
||
// Withdraws any fees paid for the given sender to the specified recipient address. | ||
// This can be called by anyone for any address--fees are a transient payment location to enable transfers for a given address. Leftover fees should always be withdrawn in the same transaction. | ||
// Returns the amount of fees that were withdrawn. | ||
fn withdraw_fees_paid_verbose( | ||
ref self: TContractState, sender: ContractAddress, recipient: ContractAddress | ||
) -> u128; | ||
|
||
// Same as `withdraw_fees_paid_verbose` but recipient is always the caller | ||
fn withdraw_fees_paid_for_sender(ref self: TContractState, sender: ContractAddress) -> u128; | ||
|
||
// Same as `withdraw_fees_paid_for_sender` but the sender and recipient are both set to the caller | ||
fn withdraw_fees_paid(ref self: TContractState) -> u128; | ||
} | ||
``` | ||
|
||
The expected usage flow is as follows: | ||
|
||
- DApp stores off-chain metadata about whether a token is fee-on-transfer, and implements the following logic if it is | ||
- When depositing this token into a dapp, the `recipient` is the Dapp contract: | ||
- Dapp calls `compute_fees_required(user, dapp_contract, amount)` off-chain | ||
- Dapp adds `pay_fees(computed_fees)` call to the list of calls before all dapp calls | ||
- Dapp adds rest of calls as normal | ||
- Dapp adds `withdraw_fees_paid()` call to return any overpaid transfer fees | ||
- When withdrawing this token from a dapp, the `receiver` is the caller and the `sender` is the dapp | ||
- Dapp calls `pay_fees_for_sender(dapp_contract, amount)` | ||
- Dapp adds rest of calls as normal | ||
- Dapp adds `withdraw_fees_paid_for_sender(dapp_contract)` | ||
|
||
There are other ways this standard may be used, such as handling fees off-chain in a peripheral contract. This design | ||
expects there are no intermediate transfers of the token between the dapp contract and the user, i.e. it relies on | ||
`approve` and `transferFrom` in cases where the called contract is not where the tokens are custodied. | ||
|
||
## Implementation | ||
|
||
TBD | ||
|
||
## History | ||
|
||
- Created 2024-07-29 | ||
|
||
## Copyright | ||
|
||
Copyright and related rights waived via [MIT](../LICENSE). |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
syntax: below you write "DApp", "Dapp", "dapp". Better sticking to one choice.