Summary
The marker module implements complex transfer mechanics for restricted coins through bank module integration using SendRestrictionFn, supporting transfer permissions, force transfers, required attributes, deposits, withdrawals, bypass accounts, and specialized quarantine interactions, with detailed validation flows for ensuring proper authorization and compliance across various transfer scenarios and account types.
There are some complex interactions involved with transfers of restricted coins.
General
Accounting of restricted coins is handled by the bank module. Restricted funds can be moved using the bank module's
MsgSend
or MsgMutliSend
. They can also be moved using the marker module's MsgTransferRequest
.During such transfers several things are checked using a
SendRestrictionFn
injected into the bank module. This restriction is applied in almost all instances when funds are being moved between accounts. The exceptions are delegations, undelegations, minting, burning, and marker withdrawals. A MsgTransferRequest
also bypasses the SendRestrictionFn
in order to include the admin
account in the logic.Definitions
Transfer Permission
One permission that can be granted to an address is
transfer
. The transfer
permission is granted to accounts that represent a "Transfer Agent" or "Transfer Authority" for restricted marker tokens. An address with transfer
permission can utilize MsgTransferRequest
to move restricted funds from one account to another. The source account must be the admin's own account, or else there must be a MarkerTransferAuthorization
grant (in the authz
module) from the source account to the admin.MsgSend
and MsgMultiSend
can also be used by an address with transfer
permission to move funds out of their own account.Force Transfer Permission
If a restricted marker allows forced transfers, the
force_transfer
permission grants an account the ability to use the Transfer
endpoint to move marker funds out of almost any account. An account with force_transfer
cannot use other means to move marker funds (e.g. MsgSend
) unless they also have transfer
access.Forced Transfers
A restricted coin marker can be configured to allow forced transfers. If allowed, an account with
force_transfer
permission can use a MsgTransferRequest
to transfer the restricted coins out of almost any account to another. Forced transfer cannot be used to move restricted coins out of module accounts or smart contract accounts, though. Forced transfers can only be made using a MsgTransferRequest
.Required Attributes
Required attributes allow a marker Transfer Authority to define a set of account attestations created with the name/attribute modules to certify an account as an approved holder of the token. Accounts that possess all of the required attributes are considered authorized by the Transfer Authority to receive the token from normal bank send operations without a specific Transfer Authority approval. Required attributes are only supported on restricted markers.
For example, say account A has some restricted coins of a marker that has required attributes. Also say account B has all of those required attributes, and account C does not. Account A could use a
MsgSend
to send those restricted coins to account B. However, account B could not send them to account C (unless B also has transfer
permission).If a restricted coin marker does not have any required attributes defined, the only way the funds can be moved is by someone with
transfer
permission.Individuality
If multiple restricted coin denoms are being moved at once, each denom is considered separately.
For example, if the sender has
transfer
permission on one of them, it does not also apply to the other(s).Deposits
A deposit is when any funds are being sent to a marker's account. The funds being sent do not have to be in the denom of the destination marker.
Whenever funds are being deposited into a marker, the sender (or transfer authority) must have
deposit
permission on the target marker. If the funds to deposit are restricted coins, the sender (or transfer authority) also needs transfer
permission on the funds being moved; required attributes are not taken into account.Withdraws
A withdrawal is when any funds are being sent out of a marker's account. The funds being sent do not have to be in the denom of the source marker.
Withdraws can be made using the
Withdraw
endpoint, or another endpoint that utilizes a transfer agent (e.g. the exchange module's MarketCommitmentSettle
).Whenever funds are being withdrawn, the transfer agent must have
withdraw
permission on the source marker. If the funds to withdraw are of the source marker's denom, the source marker must be active. The transfer agent must also have transfer
permission on any restricted coins being moved.Bypass Accounts
There are several hard-coded module account addresses that are given special consideration in the marker module's
SendRestrictionFn
:authtypes.FeeCollectorName
- Allows paying fees with restricted coins.
reward
- Allows reward programs to use restricted coins.
quarantine
- Allows quarantine and acceptance of quarantined coins.
gov
- Allows deposits to have quarantined coins.
distribution
- Allows collection of delegation rewards in restricted coins.
stakingtypes.BondedPoolName
- Allows delegation of restricted coins.
stakingtypes.NotBondedPoolName
- Allows delegation of restricted coins.
All of these are treated equally in the application of a marker's send restrictions.
For restricted markers without required attributes:
- If the
toAddr
is a bypass account, thefromAddr
must have transfer authority.
- If the
fromAddr
is a bypass account, it's assumed that the funds got where they currently are because someone with transfer authority got them there, so this transfer is allowed.
For restricted markers with required attributes:
- If the
toAddr
is a bypass account, the transfer is allowed regardless of whether thefromAddr
has transfer authority. It's assumed that the next destination's attributes will be properly checked before allowing the funds to leave the bypass account.
- If the
fromAddr
is a bypass account, thetoAddr
must have the required attributes.
Bypass accounts are not considered during a
MsgTransferRequest
.Send Restrictions
The marker module injects a
SendRestrictionFn
into the bank module. This function is responsible for deciding whether any given movement of funds (e.g. a MsgSend
) is allowed from the marker module's point of view. However, it is bypassed for movements initiated within the marker module (e.g. during a Transfer
).Flowcharts
The SendRestrictionFn
The
SendRestrictionFn
uses the following flow to decide whether a send is allowed. It utilizes the checkSenderMarker, checkReceiverMarker, and validateSendDenom flows.The process includes:
- Checking for bypass conditions or special sender accounts
- Checking if receiver is fee collector
- Checking for restricted coins in amount
- Getting transfer agent from context
- Validating sender marker permissions
- Validating receiver marker permissions
- Validating each denomination in the transfer amount
checkSenderMarker
This flow checks that, if this is a withdrawal, nothing (yet) prevents the send. It verifies:
- Whether sender is a marker
- Transfer agent availability and withdraw access
- Whether amount includes sender marker's denom
- Whether sender marker is active
checkReceiverMarker
This flow checks that, if this is a deposit, nothing (yet) prevents the send. It verifies:
- Whether receiver is a restricted marker
- Transfer agent availability
- Deposit access permissions for sender or transfer agent
validateSendDenom
Each Denom is checked using validateSendDenom, which validates:
- Marker existence and active status for the denom
- Restricted coin status and fee collector exceptions
- Transfer agent permissions
- Deny list restrictions
- Required attributes compliance
- Bypass account considerations
Note that
force_transfer
access is not considered at all in the SendRestrictionFn
. Only a MsgTransferRequest
can be used to force a transfer.MsgTransferRequest
A
MsgTransferRequest
bypasses the SendRestrictionFn
and applies its own logic. A MsgTransferRequest
only allows for a single coin amount, i.e. there's only one Denom to consider. It makes use of the checkReceiverMarker flow.The process includes:
- Checking if denom is a restricted coin
- Verifying admin has transfer or force-transfer permissions
- Running checkReceiverMarker validation
- Checking admin/sender relationship and authorization
- Validating forced transfer permissions and restrictions
- Checking for blocked addresses
Quarantine Complexities
There are some notable complexities involving restricted coins and quarantined accounts.
Sending Restricted Coins to a Quarantined Account
The marker module's
SendRestrictionFn
is applied before the quarantine module's. So, when funds are being sent to a quarantined account, the marker module runs its check using the original Sender
and Receiver
(i.e. the Receiver
is not QFH).If the
Receiver
is a quarantined account, the validateSendDenom flow checks:- Whether sender has transfer permission for the denom
- Whether denom has required attributes
- Whether receiver has those required attributes
If the Send is allowed, and the
Receiver
is a quarantined account, the quarantine module's SendRestrictionFn
will then change the Send's destination to QFH (the Quarantined-funds-holder account) and make a record of the transfer.Accepting Quarantined Restricted Coins
Once funds have been sent to QFH, the
Receiver
will probably want to accept them, and have them sent to their account. They issue an Accept to the quarantine module which utilizes the bank module's Send functionality to try to transfer funds from QFH to the Receiver
.QFH is a bypass account. Since
Receiver
is a quarantined account, the validateSendDenom flow checks:- Whether denom has required attributes
- Whether receiver has those required attributes
An important subtle part of this process is the rechecking of
Receiver
attributes. It's possible for the initial send to be okay (causing funds to be quarantined), then later, during this Accept, the send is not okay, and the quarantined funds are effectively locked with QFH until the Receiver
gets the required attributes.If the marker does not have required attributes though, it's assumed that they were originally sent by someone with transfer authority, so they are allowed to continue from here too.
Successful Quarantine and Accept Sequence
When restricted coin funds are sent to a quarantined account, the marker's
SendRestrictionFn
is called using the original Sender
and Receiver
. Then, the quarantine's SendRestrictionFn
is called which will return QFH for the new destination. Funds are then transferred from Sender
to QFH.When the
Receiver
attempts to Accept those quarantined funds, the marker's SendRestrictionFn
is called again, this time using QFH (as the sender) and Receiver
. The quarantine's SendRestrictionFn
is bypassed, so the destination is not changed. Funds are then transferred from QFH to Receiver
.