Skip to main content

VEP-33: Allowance Token Interface

VEP: 33
author: EverDues <contact@everdues.com>
status: Review
type: Contract
created: 16.05.2023
requires: TIP-3, TIP-3.1, TIP-3.2, TIP-6

Abstract

VEP-33 describes an Allowance Token Interface by which token owners can allow a spender contract to spend a certain amount of tokens on their behalf to add support of pull payments (e.g. subscriptions, regular payouts, instalments, costs averaging etc).

Motivation

The present cryptocurrency payment infrastructure primarily depends on push transactions. In such a system, the payer initiates the payment and sends ('pushes') the funds to the payee's wallet address. While this system has proven effective for cryptocurrencies up to now, it also poses several challenges that impede mainstream adoption and restrict the versatility of cryptocurrencies across various commercial contexts. This proposal aspires to introduce the Allowance Token Interface as a solution to these challenges.

Subscription-based business models have witnessed exponential growth across numerous industries, such as media streaming, Software as a Service (SaaS), gaming, and more. Users are usually charged on a regular basis - monthly, quarterly, or annually. The existing push payment systems in the crypto landscape make recurring payments a cumbersome and complex process. This is because a new transaction needs to be manually initiated every time a payment is due, significantly reducing the feasibility of subscription-based models.

Consider the experience of a user named Alice, who maintains several subscriptions for streaming services, software, and online magazines, all payable in cryptocurrency. With the current push system, Alice would need to remember each due date and manually process each payment. The inconvenience and potential for missed payments could deter Alice and others like her from using cryptocurrencies for these transactions.

By incorporating this interface into the token, TIP3 token issuers can significantly boost user adoption and expand the practical applications for tokens in real-world scenarios, such as:

  • Subscription Services
  • Investments and Savings
  • Rent
  • Membership Fees
  • Donations
  • Online Learning and E-Courses

Specification

The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

Token root contract

No changes REQUIRED in existing standard TIP-3.2.

Token wallet contract

increaseAllowance

Allows the spender contract to withdraw from your wallet multiple times, up to the amount. If this function is called again for the same spender it increases the current allowance with the amount.

/// @notice This function increasing spender allowance
/// @param spender spender address
/// @param amount allowance amount
/// @param remaningGasTo remaining gas address
/// @param notify notify waller owner
/// @param payload transaction payload

function increaseAllowance(
address spender,
uint128 amount,
address remaningGasTo,
bool notify,
TvmCell payload
) external;

Note: Multi-allowance MUST be possible by executing increaseAllowance for each spender address.

decreaseAllowance

Decreases the current allowance with the amount. If the current spender allowance does not exist, do nothing.

/// @notice This function is decreasing spender allowance
/// @param spender spender address
/// @param amount allowance amount
/// @param remaningGasTo remaining gas address
/// @param notify notify wallet owner
/// @param payload transaction payload

function decreaseAllowance(
address spender,
uint128 amount,
address remaningGasTo,
bool notify,
TvmCell payload
) external;

Note:

  1. If the amount is >= that existing allowance then deletes the current allowance.

allowance

Returns the amount which spender is still allowed to withdraw from the wallet.

/// @notice This function return current spender allowance
/// @param spender spender address
/// @return amount initial and maximum allowance granted to spender

function allowance(address spender) external view returns (uint128 amount);

transferFrom

The transferFrom method is used for a withdrawal workflow, allowing spender contracts to transfer tokens on your behalf to a token wallet, owned by the spender. Token wallet address is derived automatically.

The function MUST throw an error unless the message sender has permission to withdraw such an amount of tokens granted via the approve function. The function MUST decrease the allowed number of tokens by the amount and call the acceptTransfer function of the recipientWallet with the amount as an argument and decrease the token wallet balance by amount. The function MUST throw an error, if the current balance of tokens is less than the amount.

If deployWalletValue is greater than 0, token wallet MUST deploy token wallet for recipient.


/// @notice This function is transfered tokens to a spender wallet
/// @param amount tokens amount
/// @param deployWalletValue value to deploy the recipient's wallet if needed
/// @param notify notify wallet owner
/// @param payload transaction payload

function transferFrom(
uint128 amount,
uint128 deployWalletValue,
address remainingGasTo,
bool notify,
TvmCell payload
) external;

Callbacks

Incoming approval callback

Notifies spender contract that an allowance was changed.

/// @notice This function notify spender contract about allowance changes
/// @param tokenRoot token root address
/// @param recoveryRate recovery rate
/// @param sender sender address
/// @param remaningGasTo remaining gas address
/// @param payload transaction payload

function onAcceptTokensAllowance(
address tokenRoot,
uint128 amount,
uint128 recoveryRate,
address sender,
address remainingGasTo,
TvmCell payload
) external;

Extended interface

disapprove

Cancel current spender allowance. Can be used as an alternative to decreaseAllowance to cancel allowance without need to know the amount of the current allowance.

/// @notice This function cancel spender allowance
/// @param spender spender address

function disapprove(address spender) external;

increaseAllowanceRenewable

An auto-renewable allowance enables many traditional financial concepts like credit and debit limits. A wallet owner can specify a spending limit, and limit the amount that can be charged from the wallet based on an allowance that recovers over time at a rate of recoveryRate up to a limit of amount. recoveryRate defines the amount of tokens per second that the allowance regains towards the initial maximum approval amount. If this function is called again for the same spender it increases the current allowance with the amount or current recovery rate according to the new recoveryRate.


/// @notice This function increasing spender allowance
/// @param spender spender address
/// @param amount tokens amount
/// @param recoveryRate recovery rate
/// @param remaningGasTo remaining gas address
/// @param notify notify waller owner
/// @param payload transaction payload

function increaseAllowanceRenewable(
address spender,
uint128 amount,
uint128 recoveryRate,
address remaningGasTo,
bool notify,
TvmCell payload
) external;

Note:

  1. Renewable allowances can be implemented with discrete resets per time cycle. However, a continuous recoveryRate allows for more flexible use cases not bound by reset cycles and can be implemented with simpler logic.
  2. Current recovered allowance (from the last execution of tranferFrom function) MUST be recalculated according new amount and/or recoveryRate.

decreaseAllowanceRenewable

Decreases the current allowance with the amount and/or current recovery rate with recoveryRate. If the current spender allowance does not exist, do nothing.

/// @notice This function is decreasing spender allowance
/// @param spender spender address
/// @param amount allowance amount
/// @param recoveryRate recovery rate
/// @param remaningGasTo remaining gas address
/// @param notify notify wallet owner
/// @param payload transaction payload

function decreaseAllowanceRenewable(
address spender,
uint128 amount,
uint128 recoveryRate,
address remaningGasTo,
bool notify,
TvmCell payload
) external;

Note:

  1. If the amount is >=that existing allowance then deletes the current allowance.
  2. Current recovered allowance (from the last execution of tranferFrom function) MUST be recalculated according new amount and/or recoveryRate.

allowanceRenewable

Returns approved max amount and recovery rate of allowance granted to spender. The amount returned by allowance method MUST be as of block.timestamp, if a renewable allowance for spender is present.

/// @notice  Returns approved max amount and recovery rate of allowance granted to `spender` 
/// @param spender allowed spender of token
/// @return amount initial and maximum allowance granted to spender
/// @return recoveryRate recovery amount per second

function allowanceRenewable() external view returns (uint128 amount, uint128 recoveryRate);

increaseAllowance, decreaseAllowance

Base methods increaseAllowance and decreaseAllowance MUST set recoveryRate to 0.

Interface detection

Allowance

interface IAllowanceTokenWallet {

/// @notice This function increasing `spender` allowance
/// @param spender spender address
/// @param amount tokens amount
/// @param remaningGasTo remaining gas address
/// @param notify notify waller owner
/// @param payload transaction payload
function increaseAllowance(
address spender,
uint128 amount,
address remaningGasTo,
bool notify,
TvmCell payload
) external;

/// @notice This function is decreasing `spender` allowance
/// @param spender spender address
/// @param amount allowance amount
/// @param remaningGasTo remaining gas address
/// @param notify notify wallet owner
/// @param payload transaction payload
function decreaseAllowance(
address spender,
uint128 amount,
address remaningGasTo,
bool notify,
TvmCell payload
) external;

/// @notice Returns amounts spendable by `spender`.
/// @param spender spender of token
/// @return remaining allowance at the current point in time
function allowance(address sender) external returns (uint128 amount);

/// @notice This function is transfered tokens to a spender wallet
/// @param amount tokens amount
/// @param deployWalletValue value to deploy the spender's wallet if needed
/// @param notify notify wallet owner
/// @param payload transaction payload
function transferFrom(
uint128 amount,
uint128 deployWalletValue,
address remainingGasTo,
bool notify,
TvmCell payload
) external;
}

The token wallet interface ID is 0x2FC61B07

Renewable Allowance

interface IAllowanceRenewableTokenWallet {

/// @notice This function increasing spender allowance
/// @param spender spender address
/// @param amount tokens amount
/// @param remaningGasTo remaining gas address
/// @param notify notify waller owner
/// @param payload transaction payload
function increaseAllowanceRenewable(
address spender,
uint128 amount,
uint128 recoveryRate,
address remaningGasTo,
bool notify,
TvmCell payload
) external;

/// @notice This function is decreasing spender allowance
/// @param spender spender address
/// @param amount allowance amount
/// @param recoveryRate recovery rate
/// @param remaningGasTo remaining gas address
/// @param notify notify wallet owner
/// @param payload transaction payload
function decreaseAllowanceRenewable(
address spender,
uint128 amount,
uint128 recoveryRate,
address remaningGasTo,
bool notify,
TvmCell payload
) external;

/// @notice Returns approved max amount and recovery rate of allowance granted to `spender`
/// @param spender allowed spender of token
/// @return amount initial and maximum allowance granted to spender
/// @return recoveryRate recovery amount per second
function allowanceRenewable(address sender) external returns (uint128 amount, uint128 recoveryRate);

/// @notice This function is transfered tokens to a spender wallet
/// @param amount tokens amount
/// @param deployWalletValue value to deploy the recipient's wallet if needed
/// @param notify notify wallet owner
/// @param payload transaction payload
function transferFrom(
uint128 amount,
uint128 deployWalletValue,
address remainingGasTo,
bool notify,
TvmCell payload
) external;
}

The token wallet interface ID is 0x7EB81C0D

Rationale

Auto-renewable allowance

In the most EVM networks ERC-20 tokens support allowances, with which token owners can allow a spender to spend a certain amount of tokens on their behalf. However, this is not ideal in circumstances involving recurring payments (e.g. subscriptions, regular payouts, instalments, costs averaging etc). Many existing DApps in EVM networks circumvent this limitation by requesting that users grant a large or unlimited allowance. This presents a security risk as malicious DApps can drain users’ accounts up to the allowance granted, and users may not be aware of the implications of granting allowances. An auto-renewable allowance is used to mitigate such risks by providing possibility to limit the amount charged from the token wallet based on an allowance that recovers over time.

increaseAllowance/decreaseAllowance

In ERC-20 token standard allowance is implemented with help of approve method but changing an allowance with this method brings the risk that someone may use both the old and the new allowance by unfortunate transaction ordering. To mitigate such risks we excluded this function from standard and proposed to use increaseAllowance and decreaseAllowance instead.

Backwards compatibility

This standard is compatible with [TIP-3.1], [TIP-3.2] standards.

Security considerations

There are no security considerations related directly to the implementation of this standard.

Copyright and related rights waived via CC0.

References