Recipient Permission Manager
1. Summary
This contract is responsible for checking valid recipient permissions and tracking granted and revoked permissions. It is necessary to prevent the stalking attack.
2. Contract details
RecipientPermissionManager.sol is written in Solidity version 0.8.15
Features
Checks for valid recipient permissions
Tracks granted and revoked permissions
Provides a function to hash the
RecipientPermission
struct
Functions
grantRecipientPermission
Overview
A function to grant an on-chain recipient permission.
This function takes one argument supplied by the caller:
RecipientPermission calldata
permission
- The recipient permission struct
Implementation
function grantRecipientPermission(RecipientPermission calldata permission) external {
// Check that caller is permission signer
require(msg.sender == permission.recipient, "Sender is not permission recipient");
bytes32 permissionHash = recipientPermissionHash(permission);
// Check that permission is not have been granted
require(grantedPermissions[permissionHash] == false, "Recipient permission is granted");
// Check that permission is not have been revoked
require(revokedPermissionNonces[msg.sender][permission.nonce] == false, "Recipient permission nonce is revoked");
// Grant permission
grantedPermissions[permissionHash] = true;
// Emit event
emit RecipientPermissionGranted(permissionHash);
}
revokeRecipientPermission
Overview
A function to revoke permissions.
This function takes one argument supplied by the caller:
bytes32
permissionNonce
- A nonce of the permission being revoked for the caller
Implementation
function revokeRecipientPermission(bytes32 permissionNonce) external {
// Check that permission is has not been revoked
require(
revokedPermissionNonces[msg.sender][permissionNonce] == false,
"Recipient permission nonce is revoked"
);
// Revoke permission
revokedPermissionNonces[msg.sender][permissionNonce] = true;
// Emit event
emit RecipientPermissionNonceRevoked(msg.sender, permissionNonce);
}
recipientPermissionHash
Overview
A function to hash a permission struct using keccak256
.
This function takes one argument supplied by the caller:
RecipientPermission memory
permission
- The recipient permission struct
Implementation
function recipientPermissionHash(RecipientPermission memory permission) public view returns (bytes32) {
return keccak256(abi.encodePacked(
"\x19\x01",
// Domain separator is composing to prevent replay attack in case of an Ethereum fork
keccak256(abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes("AssetTransferRights")),
keccak256(bytes("0.1")),
block.chainid,
address(this)
)),
keccak256(abi.encode(
RECIPIENT_PERMISSION_TYPEHASH,
permission.assetCategory,
permission.assetAddress,
permission.assetId,
permission.assetAmount,
permission.ignoreAssetIdAndAmount,
permission.recipient,
permission.expiration,
permission.isPersistent,
permission.nonce
))
));
}
_checkValidPermission
Overview
An internal function that verifies the validity of supplied permission and marks it as used.
This function takes four arguments supplied by the ATR Module:
address
sender
- Address of the account that is sending the assetMultiToken.Asset memory
asset
- An asset struct (see MultiToken)RecipientPermission memory
permission
- The recipient permission structbytes calldata
permissionSignature
- EIP-712 raw signature of the recipient permission struct. This signature is not required if the permission was granted on-chain
Implementation
function _useValidPermission(
address sender,
MultiToken.Asset memory asset,
RecipientPermission memory permission,
bytes calldata permissionSignature
) internal {
// Check that permission is not expired
uint40 expiration = permission.expiration;
require(expiration == 0 || block.timestamp < expiration, "Recipient permission is expired");
// Check permitted agent
address agent = permission.agent;
require(agent == address(0) || sender == agent, "Caller is not permitted agent");
// Check correct asset
require(permission.assetCategory == asset.category, "Invalid permitted asset");
require(permission.assetAddress == asset.assetAddress, "Invalid permitted asset");
// Check id and amount if ignore flag is false
if (permission.ignoreAssetIdAndAmount == false) {
require(permission.assetId == asset.id, "Invalid permitted asset");
require(permission.assetAmount == asset.amount, "Invalid permitted asset");
} // Skip id and amount check if ignore flag is true
// Check that permission nonce is not revoked
address recipient = permission.recipient;
bytes32 nonce = permission.nonce;
require(revokedPermissionNonces[recipient][nonce] == false, "Recipient permission nonce is revoked");
// Compute EIP-712 structured data hash
bytes32 permissionHash = recipientPermissionHash(permission);
// Check that permission is granted
// Via on-chain tx, EIP-1271 or off-chain signature
if (grantedPermissions[permissionHash] == true) {
// Permission is granted on-chain, no need to check signature
} else if (recipient.code.length > 0) {
// Check that permission is valid
require(IERC1271(recipient).isValidSignature(permissionHash, permissionSignature) == EIP1271_VALID_SIGNATURE, "Signature on behalf of contract is invalid");
} else {
// Check that permission signature is valid
require(ECDSA.recover(permissionHash, permissionSignature) == recipient, "Permission signer is not stated as recipient");
}
// Mark used permission nonce as revoked if not persistent
if (permission.isPersistent == false) {
revokedPermissionNonces[recipient][nonce] = true;
emit RecipientPermissionNonceRevoked(recipient, nonce);
}
}
Recipient Permission Struct
Events
The Recipient Permission Manager contract defines two events and no custom errors.
event RecipientPermissionGranted(bytes32 indexed permissionHash);
event RecipientPermissionNonceRevoked(address indexed recipient, bytes32 indexed permissionNonce);
RecipientPermissionGranted
RecipientPermissionGranted event is emitted when on-chain recipient permission is granted.
This event has one parameter:
bytes32 indexed
permissionHash
- Hash of the recipient permission struct returned by the recipientPermissionHash function
RecipientPermissionNonceRevoked
RecipientPermissionNonceRevoked event is emitted when a recipient revokes previously granted permission.
This event has two parameters:
address indexed
recipient
- Address of the recipient who revoked the permissionbytes32 indexed
permissionNonce
- Nonce of the revoked permission (see RecipientPermission struct for more information)
Last updated