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

SRC-21: add out-of-band fee-on-transfer token standard #103

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions SNIPS/snip-x.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
snip: $SNIP_ID
Eikix marked this conversation as resolved.
Show resolved Hide resolved
title: Out-of-band fee-on-transfer tokens
author: Moody Salem <moody@ekubo.org>
status: Living
type: SRC
created: 2024-07-29
---

## Simple Summary
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you add a flow diagram to explain how the normal scenario unfolds?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added some context as well as some methods to the interface to the specification

Could use some feedback from FOT developers on how it is proposed to work

Choose a reason for hiding this comment

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

@moodysalem, thanks for proposing this out-of-band FOT standard. As a FOT developer, I have some thoughts:

Implementation seems feasible. Curious about what specifically makes FOT tokens difficult in Ekubo's architecture?

I think pre-paying tax might add friction, but it's probably manageable. Your approach could prevent 'k' invariant issues in pools. It seems easier to implement since the transfer function doesn't alter token amounts in the pool.


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) -> ContractAddress;
moodysalem marked this conversation as resolved.
Show resolved Hide resolved

// Returns the amount of fees required to transfer the specified amount of tokens from the given sender to the given recipient.
fn compute_fees_required(
self: @TContractState, sender: ContractAddress, recipient: ContractAddress, amount: u128
) -> u128;

// Pay fees from the given address for the specified sender.
moodysalem marked this conversation as resolved.
Show resolved Hide resolved
// 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.
fn pay_fees_from(
ref self: TContractState, from: ContractAddress, sender: ContractAddress
) -> u128;

// Same as pay_fees_from but always pays from the caller address
fn pay_fees(ref self: TContractState, sender: ContractAddress) -> 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(
ref self: TContractState, sender: ContractAddress, recipient: ContractAddress
) -> u128;
}
```

## Implementation

TBD

## History

- Created 2024-07-29

## Copyright

Copyright and related rights waived via [MIT](../LICENSE).