You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A delegate is an address which has been authorized to send the tokens of a principal address. When calling send on the token contract from the delegate, the tokens will be sent from the principal account not the delegate. In addition, each send involving a delegate will be explicitly authorized by an authorizer contract.
Delegate/Authorizer vs Operator
There is a key difference between an operator and a delegate.
To send tokens with an operator, the operator must call operatorSend on the token contract. This should either be done automatically as a result of some action or by calling a function directly on the operator.
To send tokens with a delegate, the delegate only needs to call send on the token contract. The token contract will figure out on his own that send was called from a delegate and first call the authorizer contract for authorization, then send from the principal address being represented by the delegate. This allows for checks to be used with wallets which just implement send.
Delegate/Authorizer vs tokensToSend
tokensToSend is registered via EIP-820 together with tokensReceived. This means that the same function is called for all ERC-777 tokens. This is fine for tokensReceived to notify the recipient and prevent locking because the logic is the same for all tokens.
This is not true for tokensToSend which is aimed at checking whether a send is authorized or not. In this case the logic may not be the same for all tokens. Maybe a specific tokens should be rate-limited, an other one should be limited to be sent to a particular address and so on. Because there is only a single tokensToSend function for all tokens, this function must be modified and the contract redeployed for every new tokens which must be handled.
Authorizers on the other hand are registered directly on the token contract and are only applied for the specific token. This keeps the logic simple and prevent constant modification and redeployment of the contract.
Another advantage of delegates is that they use a separate account than the principal account. In the case of tokensToSend, since it is deployed for the account via EIP-820, the same key used to send tokens can also unregister the tokensToSend, essentially making the check a self-imposed voluntary check which can be bypassed at any time.
Delegate is a separate address, tied to an authorizer; but only the principal account key has the power to change the authorizer. This means that the delegate can never bypass the checks.
Use Cases
Debit card
Consider a principal account holding a large amount of tokens. The owner of the account must carry the private key with him at all time to use his tokens which may be dangerous.
A delegate with a daily quota could be authorized to send some of the tokens. The owner could carry this delegate on his phone and use his phone for limited transactions on a daily basis, the same way a debit card works. And when the owner needs to do an occasional large transaction, he can use the principal private key which is stored securely somewhere less practical to access (for example on a hardware wallet in a safe.).
Cold to hot wallet transfer
Consider a cold wallet holding a large amount of tokens and a second hot wallet used for daily transactions.
The cold wallet can have a delegate which is only authorized to send to the hot wallet. The key of the cold wallet is stored securely and almost never used. The delegate is used to replenish the hot wallet from the cold wallet.
In addition backups of the delegate key can be given to trusted entities for safe keeping. This way even if the cold wallet key is lost, the delegate key can be used to recover the funds.
And if the delegate key is compromised the only thing an attacker can do is send the tokens from the cold to the hot wallet. This is a bit safer than entrusting the key of the cold wallet directly.
Specifications
IAuthorizer
The authorizer must be a contract implementing the following interface:
interface IAuthorizer {
function authorizeSend(address to, uint256 value, bytes userData) public;
}
Authorize a third party delegate to send msg.sender's tokens upon authorization of authorizer.
The function MUST throw if:
delegate is msg.sender
delegate is already a delegate for another principal
delegate holds tokens (non-zero balance)
authorizer is an operator
authorizer is already an authorizer for some delegate
NOTE: While authorizer MUST be a contract, there is no requirement to check it when authorizing the delegate and authorizer. When sending tokens however the authorizeSend function of the authorizer MUST be called, which will result in a throw if authorizer is not a contract or does not implement Authorizer.
parameters
delegate: Address that will to be authorized to send msg.sender's tokens.
authorizer: Contract which will authorize _delegate to send tokens.
revokeDelegate
functionrevokeDelegate(addressdelegate)public
Revoke a third party _delegate's rights to send msg.sender's tokens.
The function MUST throw if:
delegate is not the delegate of msg.sender.
NOTE: While the authorizer does not need to be specified when revoking, internally the authorizer MUST also be revoked (i.e. not marked as the authorizer for delegate ).
parameters
delegate: Address of the delegate for msg.sender.
TODO Improvement: Since, there can be only one delegate per account, a two-way mapping of the delegate could be kept such that msg.sender does not need to specify the delegate when revoking
Indicate that the delegate address is not representing tokenHolder anymore and cannot send its tokens (i.e. is not delegate for tokenHolder).
This event MUST be fired on a successful call to revokeDelegate.
parameters
delegate: Address which is not representing tokenHolder anymore.
authorizer: Contract which will authorize _delegate to send tokens.
tokenHolder: address not being represented by delegate anymore.
Furthermore the send', 'operatorSend', 'transfer' and 'transferFrom method specification as well as the Burned event specification must be extended with the following constraint:
If from is a delegate, then from.authorize MUST be called and pass the correct values for to, value and userData.
NOTE: While intuitively the authorize call happens before sending the tokens, in the implementation, this call SHOULD be after the update of balances and before the call to tokensReceived to respect the Checks-Effects-Interactions (CEI) pattern and prevent potential reentry attacks.
When from is a delegate, the tokensReceived, Sent and Burnt must be called with the true from (i.e. the token holder being represented by the delegate) not the delegate itself. Delegates are transparent from the point of view of the recipient and MUST NOT be communicated.
The text was updated successfully, but these errors were encountered:
0xjac
changed the title
Add delegates
Add delegate & authorizer feature
Feb 13, 2018
Delegate & Authorizer
A delegate is an address which has been authorized to send the tokens of a principal address. When calling
send
on the token contract from the delegate, the tokens will be sent from the principal account not the delegate. In addition, each send involving a delegate will be explicitly authorized by an authorizer contract.Delegate/Authorizer vs Operator
There is a key difference between an operator and a delegate.
To send tokens with an operator, the operator must call
operatorSend
on the token contract. This should either be done automatically as a result of some action or by calling a function directly on the operator.To send tokens with a delegate, the delegate only needs to call
send
on the token contract. The token contract will figure out on his own that send was called from a delegate and first call the authorizer contract for authorization, then send from the principal address being represented by the delegate. This allows for checks to be used with wallets which just implementsend
.Delegate/Authorizer vs
tokensToSend
tokensToSend
is registered via EIP-820 together withtokensReceived
. This means that the same function is called for all ERC-777 tokens. This is fine fortokensReceived
to notify the recipient and prevent locking because the logic is the same for all tokens.This is not true for
tokensToSend
which is aimed at checking whether a send is authorized or not. In this case the logic may not be the same for all tokens. Maybe a specific tokens should be rate-limited, an other one should be limited to be sent to a particular address and so on. Because there is only a singletokensToSend
function for all tokens, this function must be modified and the contract redeployed for every new tokens which must be handled.Authorizers on the other hand are registered directly on the token contract and are only applied for the specific token. This keeps the logic simple and prevent constant modification and redeployment of the contract.
Another advantage of delegates is that they use a separate account than the principal account. In the case of
tokensToSend
, since it is deployed for the account via EIP-820, the same key used to send tokens can also unregister thetokensToSend
, essentially making the check a self-imposed voluntary check which can be bypassed at any time.Delegate is a separate address, tied to an authorizer; but only the principal account key has the power to change the authorizer. This means that the delegate can never bypass the checks.
Use Cases
Debit card
Consider a principal account holding a large amount of tokens. The owner of the account must carry the private key with him at all time to use his tokens which may be dangerous.
A delegate with a daily quota could be authorized to send some of the tokens. The owner could carry this delegate on his phone and use his phone for limited transactions on a daily basis, the same way a debit card works. And when the owner needs to do an occasional large transaction, he can use the principal private key which is stored securely somewhere less practical to access (for example on a hardware wallet in a safe.).
Cold to hot wallet transfer
Consider a cold wallet holding a large amount of tokens and a second hot wallet used for daily transactions.
The cold wallet can have a delegate which is only authorized to send to the hot wallet. The key of the cold wallet is stored securely and almost never used. The delegate is used to replenish the hot wallet from the cold wallet.
In addition backups of the delegate key can be given to trusted entities for safe keeping. This way even if the cold wallet key is lost, the delegate key can be used to recover the funds.
And if the delegate key is compromised the only thing an attacker can do is send the tokens from the cold to the hot wallet. This is a bit safer than entrusting the key of the cold wallet directly.
Specifications
IAuthorizer
The authorizer must be a contract implementing the following interface:
authorize
Authorizes a send for the from address being represented by the delegate.
To prevent the send, the function MUST throw.
Ierc777 (Token Contract)
In addition, the
Ierc777
interface must be extended to add the following methods and events:Methods
authorizeDelegate
Authorize a third party
delegate
to sendmsg.sender
's tokens upon authorization ofauthorizer
.The function MUST throw if:
delegate
ismsg.sender
delegate
is already a delegate for another principaldelegate
holds tokens (non-zero balance)authorizer
is an operatorauthorizer
is already an authorizer for some delegateNOTE: While
authorizer
MUST be a contract, there is no requirement to check it when authorizing the delegate and authorizer. When sending tokens however theauthorizeSend
function of the authorizer MUST be called, which will result in a throw ifauthorizer
is not a contract or does not implementAuthorizer
.revokeDelegate
Revoke a third party
_delegate
's rights to sendmsg.sender
's tokens.The function MUST throw if:
delegate
is not the delegate ofmsg.sender
.NOTE: While the authorizer does not need to be specified when revoking, internally the authorizer MUST also be revoked (i.e. not marked as the authorizer for
delegate
).TODO Improvement: Since, there can be only one delegate per account, a two-way mapping of the delegate could be kept such that
msg.sender
does not need to specify the delegate when revokingdelegateFor
Get the address for which
delegate
is authorized to send tokens or0x0
ifdelegate
is not a delegate.authorizerOf
Get the address for which
delegate
is authorized to send tokens or0x0
ifdelegate
is not a delegate.Events
AuthorizedDelegate
Indicate that the
delegate
address is representingtokenHolder
and send its token (i.e. is a delegate fortokenHolder
).This event MUST be fired on a successful call to
authorizeDelegate
.RevokedDelegate
Indicate that the
delegate
address is not representingtokenHolder
anymore and cannot send its tokens (i.e. is not delegate fortokenHolder
).This event MUST be fired on a successful call to
revokeDelegate
.Furthermore the
send', 'operatorSend', 'transfer' and 'transferFrom
method specification as well as theBurned
event specification must be extended with the following constraint:The text was updated successfully, but these errors were encountered: