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
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:
bytes32permissionNonce - 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:
bytes calldatapermissionSignature - EIP-712raw 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
Type
Name
Comment
assetCategory
address
assetAddress
Contract address of the asset that is permitted to transfer
uint256
assetId
ID of the asset that is permitted to transfer
uint256
assetAmount
Amount of the asset that is permitted to transfer
bool
ignoreAssetIdAndAmount
Flag stating if asset ID and amount are ignored when checking the permissioned asset
address
recipient
Address of the recipient and permission signer
address
agent
Optional address of a permitted agent that can process the permission. If this value is zero, any agent can process the permission
uint40
expiration
Optional permission expiration timestamp in seconds. If this value is zero, the permission doesn't have an expiration
bool
isPersistent
Flag stating if a permission is valid for more than one use
bytes32
nonce
Additional value to distinguish otherwise identical permissions
Events
The Recipient Permission Manager contract defines two events and no custom errors.