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
functiongrantRecipientPermission(RecipientPermissioncalldata permission) external {// Check that caller is permission signerrequire(msg.sender == permission.recipient,"Sender is not permission recipient");bytes32 permissionHash =recipientPermissionHash(permission);// Check that permission is not have been grantedrequire(grantedPermissions[permissionHash] ==false,"Recipient permission is granted");// Check that permission is not have been revokedrequire(revokedPermissionNonces[msg.sender][permission.nonce] ==false,"Recipient permission nonce is revoked");// Grant permission grantedPermissions[permissionHash] =true;// Emit eventemitRecipientPermissionGranted(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
functionrevokeRecipientPermission(bytes32 permissionNonce) external {// Check that permission is has not been revokedrequire( revokedPermissionNonces[msg.sender][permissionNonce] ==false,"Recipient permission nonce is revoked" );// Revoke permission revokedPermissionNonces[msg.sender][permissionNonce] =true;// Emit eventemitRecipientPermissionNonceRevoked(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.Assetmemory asset,RecipientPermissionmemory permission,bytescalldata permissionSignature) internal {// Check that permission is not expireduint40 expiration = permission.expiration;require(expiration ==0|| block.timestamp < expiration,"Recipient permission is expired");// Check permitted agentaddress agent = permission.agent;require(agent ==address(0) || sender == agent,"Caller is not permitted agent");// Check correct assetrequire(permission.assetCategory == asset.category,"Invalid permitted asset");require(permission.assetAddress == asset.assetAddress,"Invalid permitted asset");// Check id and amount if ignore flag is falseif (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 revokedaddress recipient = permission.recipient;bytes32 nonce = permission.nonce;require(revokedPermissionNonces[recipient][nonce] ==false,"Recipient permission nonce is revoked");// Compute EIP-712 structured data hashbytes32 permissionHash =recipientPermissionHash(permission);// Check that permission is granted// Via on-chain tx, EIP-1271 or off-chain signatureif (grantedPermissions[permissionHash] ==true) {// Permission is granted on-chain, no need to check signature } elseif (recipient.code.length >0) {// Check that permission is validrequire(IERC1271(recipient).isValidSignature(permissionHash, permissionSignature) == EIP1271_VALID_SIGNATURE,"Signature on behalf of contract is invalid"); } else {// Check that permission signature is validrequire(ECDSA.recover(permissionHash, permissionSignature) == recipient,"Permission signer is not stated as recipient"); }// Mark used permission nonce as revoked if not persistentif (permission.isPersistent ==false) { revokedPermissionNonces[recipient][nonce] =true;emitRecipientPermissionNonceRevoked(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.