EIP2021 - Payoutable Token
# Simple Summary
An extension to the ERC-20 standard token that allows Token wallet owners to request payout from their wallet, by calling the smart contract and attaching a payout instruction string.
# Actors
# Token Wallet Owners
The person or company who owns the wallet, and will order payout.
# Token contract owner / agent
The entity, company responsible/owner of the token contract, and token issuing/minting. This actor is in charge of trying to fulfill all payout request(s), reading the payout instruction(s), and correlate the payout details.
# Orderer
An actor who is enabled to initiate payout orders on behalf of a token wallet owner.
# Abstract
Token wallet owners (or approved addresses) can order payout requests through blockchain. This is done by calling the orderPayoutFrom
or orderPayoutFrom
methods, which initiate the workflow for the token contract operator to either honor or reject the payout request. In this case, payout instructions are provided when submitting the request, which are used by the operator to determine the destination of the funds.
In general, it is not advisable to place explicit routing instructions for the payouts on a verbatim basis on the blockchain, and it is advised to use a private communication alternatives, such as private channels, encrypted storage or similar, to do so (external to the blockchain ledger). Another (less desirable) possibility is to place these instructions on the instructions field in encrypted form.
# Motivation
Nowadays most of the token payout requests, need a previous centralized transaction, to be able to define the payout destination to be able to execute the payout (burn transaction). In the aim of trying to bring all the needed steps into decentralization, exposing all the needed steps of token lifecycle and payment transactions, a payout request can allow wallet owner to initiate the payout order via blockchain. Key benefits:
- Payout, burning traceability is enhanced bringing the initiation into the ledger. All payment, payout statuses can be stored on chain.
- Almost all money/token lifecycle is covered via a decentralized approach, complemented with private communications which is common use in the ecosystem.
In this case, the following movement of tokens are done as the process progresses:
- Upon launch of the payout request, the appropriate amount of funds are placed on a hold with a predefined notary defined by the platform, and the payout is placed into a
Ordered
state - The operator then can put the payout request
InProcess
, which prevents the orderer of the payout from being able to cancel the payout request - After checking the payout is actually possible the operator then executes the hold, which moves the funds to a suspense wallet and places the payout into the
FundsInSuspense
state - The operator then moves the funds offchain (usually from the omnibus account) to the appropriate destination account, then burning the tokens from the suspense wallet and rendering the payout into the
Executed
state - Either before or after placing the request
InProcess
, the operator can also reject the payout, which returns the funds to the payer and eliminates the hold. The resulting end state of the payout isRejected
- When the payout is
Ordered
and before the operator places it into theInProcess
state, the orderer of the payout can also cancel it, which frees up the hold and puts the payout into the finalCancelled
state
# Specification
interface IPayoutable /* is ERC-20 */ {
enum PayoutStatusCode {
Nonexistent,
Ordered,
InProcess,
FundsInSuspense,
Executed,
Rejected,
Cancelled
}
function authorizePayoutOperator(address orderer) external returns (bool);
function revokePayoutOperator(address orderer) external returns (bool);
function orderPayout(string calldata operationId, uint256 value, string calldata instructions) external returns (bool);
function orderPayoutFrom(string calldata operationId, address walletToBePaidOut, uint256 value, string calldata instructions) external returns (bool);
function cancelPayout(string calldata operationId) external returns (bool);
function processPayout(string calldata operationId) external returns (bool);
function putFundsInSuspenseInPayout(string calldata operationId) external returns (bool);
function executePayout(string calldata operationId) external returns (bool);
function rejectPayout(string calldata operationId, string calldata reason) external returns (bool);
function isPayoutOperatorFor(address walletToDebit, address orderer) external view returns (bool);
function retrievePayoutData(string calldata operationId) external view returns (address walletToDebit, uint256 value, string memory instructions, PayoutStatusCode status);
event PayoutOrdered(address indexed orderer, string indexed operationId, address indexed walletToDebit, uint256 value, string instructions);
event PayoutInProcess(address indexed orderer, string indexed operationId);
event PayoutFundsInSuspense(address indexed orderer, string indexed operationId);
event PayoutExecuted(address indexed orderer, string indexed operationId);
event PayoutRejected(address indexed orderer, string indexed operationId, string reason);
event PayoutCancelled(address indexed orderer, string indexed operationId);
event PayoutOperatorAuthorized(address indexed walletToBePaidOut, address indexed orderer);
event PayoutOperatorRevoked(address indexed walletToBePaidOut, address indexed orderer);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# Functions
# authorizePayoutOperator
Wallet owner, allows a given address to be payout orderer.
Parameter | Description |
---|---|
orderer | The address of the orderer. |
# revokePayoutOperator
Wallet owner, Revokes a given address to be payout orderer.
Parameter | Description |
---|---|
orderer | The address of the orderer. |
# orderPayout
Creates a payout request, that will be processed by the token operator. The function must revert if the operation ID has been used before.
Parameter | Description |
---|---|
operationId | The unique ID to identify the request |
value | The amount to be paid out. |
instruction | A string including the payment instruction. |
# orderPayoutFrom
Creates a payout request, on behalf of a wallet owner, that will be processed by the token operator. The function must revert if the operation ID has been used before.
Parameter | Description |
---|---|
operationId | The unique ID to identify the request |
walletToBePaidOut | The wallet to be paid out on behalf. |
value | The amount to be paid out. |
instruction | A string including the payment instruction. |
# cancelPayout
Cancels a payout request.
Parameter | Description |
---|---|
operationId | The unique ID to identify the request that is going to be cancelled. This can only be done by token holder, or the payout initiator/orderer. |
reason | The specific reason that explains why the payout request was rejected. EIP-1066 codes can be used. |
# processPayout
Marks a payout request as on process. After the status is on process, order cannot be cancelled.
Parameter | Description |
---|---|
operationId | The unique ID to identify that the request is in process. |
# putFundsInSuspenseInPayout
Put a given payout in suspense. Can only be done if it is in process.
Parameter | Description |
---|---|
operationId | The unique ID to identify that the request is in process. |
# executePayout
Burn the amount of tokens and marks a payout request as executed.
Parameter | Description |
---|---|
operationId | The unique ID to identify the request that has been executed. |
# rejectPayout
Rejects a given operation with a reason.
Parameter | Description |
---|---|
operationId | The unique ID to identify the request that has been executed. |
reason | The specific reason that explains why the payout request was rejected. EIP-1066 codes can be used |
# isApprovedToOrderPayout
Checks that given player is allowed to order payout requests, for a given wallet.
Parameter | Description |
---|---|
walletToBePaidOut | The wallet to be paid out, and checked for approval permission. |
orderer | The address of the orderer, to be checked for approval permission. |
# retrievePayoutData
Retrieves all the payout request data. Only operator, tokenHolder, and orderer can get the given operation data.
Parameter | Description |
---|---|
orderer | The address of the orderer, to correlate the right data. |
operationId | The unique ID to identify the payout order. |
# Events
# Payout Ordered
Emitted when an token wallet owner orders a payout request.
Parameter | Description |
---|---|
operationId | The unique ID to identify the request |
walletToBePaidOut | The wallet that is requested to be paid out |
value | The amount to be funded. |
instruction | A string including the payment instruction. |
# PayoutFundsInSuspense
Emitted when an operator puts fund in suspense.
Parameter | Description |
---|---|
orderer | The address of the payout request orderer. |
operationId | The unique ID to identify the payout. |
# PayoutInProcess
Emitted when an operator accepts a payout request, and the operation is in process.
Parameter | Description |
---|---|
orderer | The address of the payout request orderer. |
operationId | The unique ID to identify the payout. |
# PayoutExecuted
Emitted when an operator has executed a payout request.
Parameter | Description |
---|---|
orderer | The address of the payout request orderer. |
operationId | The unique ID to identify the payout. |
# PayoutRejected
Emitted when an operator has rejected a payout request.
Parameter | Description |
---|---|
orderer | The address of the payout request orderer. |
operationId | The unique ID to identify the payout. |
reason | The specific reason that explains why the payout request was rejected. EIP-1066 codes can be used |
# PayoutCancelled
Emitted when a token holder, orderer, has cancelled a payout request. This can only be done if the operator hasn't put the payout order in process.
Parameter | Description |
---|---|
orderer | The address of the payout request orderer. |
operationId | The unique ID per payout issuer to identify the payout. |
# PayoutOperatorAuthorized
Emitted when a given player, operator, company or a given persona, has been approved to start payout request for a given token holder.
Parameter | Description |
---|---|
walletToBePaidOut | The wallet that the player is allowed to start payout requests |
orderer | The address that allows the the player to start requests. |
# PayoutOperatorRevoked
Emitted when a given player has been revoked initiate payout requests.
Parameter | Description |
---|---|
walletToBePaidOut | The wallet that the player is allowed to start payout requests |
orderer | The address that allows the the player to start requests. |
# Rationale
This standards provides a functionality to allow token holders to start payout requests in a decentralized way.
It's important to highlight that the token operator, need to process all payout request, updating the payout status based on the linked payment that will be done.
Payout instruction format is open. ISO payment standard like is a good start point.
This EIP uses EIP-1996 to hold the money after a payout is ordered. The token contract owner or agent, whose implementation is not part of this proposal, acts as a predefined notary to decide if the payout is executed or not.
The operationId
is a string and not something more gas efficient to allow easy traceability of the hold and allow human readable ids. It is up to the implementer if the string should be stored on-chain or only its hash, as it is enough to identify a hold.
The operationId
is a competitive resource. It is recommended, but not required, that the hold issuers used a unique prefix to avoid collisions.
# Backwards Compatibility
This EIP is fully backwards compatible as its implementation extends the functionality of ERC-20 and [ERC-1996].
# Implementation
The GitHub repository IoBuilders/payoutable-token (opens new window) contains the reference implementation.
# Contributors
This proposal has been collaboratively implemented by adhara.io (opens new window) and io.builders (opens new window).
# Copyright
Copyright and related rights waived via CC0 (opens new window).