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:
- 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:
- 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. - Current recovered allowance (from the last execution of
tranferFrom
function) MUST be recalculated according newamount
and/orrecoveryRate
.
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:
- If the
amount
is>=
that existing allowance then deletes the current allowance. - Current recovered allowance (from the last execution of
tranferFrom
function) MUST be recalculated according newamount
and/orrecoveryRate
.
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
Copyright and related rights waived via CC0.