# 3. Permission smart contracts

{% hint style="info" %}
This step is optional. Client can use Keyring's Pro and Connect onboardings without the on-chain credentials component.&#x20;
{% endhint %}

## How to permission contracts?&#x20;

Any smart contract function can be permissioned with a modifier, allowing only whitelisted wallets to perform the underlying action. Examples of functions you can gate:

* [x] **Asset:** Apply the guard at the asset level, so it travels across venues and hands.&#x20;
  * ERC20 Token: `transfer`, `transferFrom` ,  `mint` and `burn`. Embed modifier in the token code or use an ERC20 wrapper on top of an existing asset, with permissioned or permissionless `wrap`/`unwrap` - leaving the underlying asset's code untouched.
  * ERC4626 Vault: `mint`, `redeem`, `deposit`, `withdraw`. You can also permission only transfer out functions (`redeem,` `withdraw`) for permissionless entry and permissioned exit.
  * ERC721 / ERC1155: Permission NFTs by adding controls to `transfer` or `mint` functions.&#x20;
* [x] **Pool**: `swap`, `addLiquidity`, `removeLiquidity`.
* [x] **Subnet**: `block_publication`.

{% hint style="success" %}
Whitelists are fully composable. For instance the modifier could allow action only to wallets that verify {`whitelist A` OR `whitelist B` AND NOT `blacklist C`}.
{% endhint %}

## How to implement Keyring Permissioning?&#x20;

You can create your own hooks or modifiers to include a guard in your contracts. You simply read from  Keyring the status of an address with respect to a policy. This is a flexible and easy way to customise the integration to your needs.&#x20;

**Here is an example implementation:**&#x20;

{% hint style="info" %}
[You may click here to view the complete example in Remix IDE.](https://tinyurl.com/KeyringERC20)
{% endhint %}

Let's assume we want to guard an ERC20 transfer function.&#x20;

Firstly, define the Keyring interface for checking credentials.

```solidity
/**
 * @title IKeyring
 * @dev Interface for the Keyring contract to check credentials.
 */
interface IKeyring {
    /**
     * @notice Checks the credential of an entity against a specific policy.
     * @param policyId The ID of the policy to check against.
     * @param entity The address of the entity to check.
     * @return A boolean value indicating whether the entity's credentials pass the policy check.
     */
    function checkCredential(uint256 policyId, address entity) external view returns (bool);
}
```

Secondly, define the Policy ID and the Keyring address in the contract constructor. You may optionally define a `Whitelist` mapping with associated values.&#x20;

{% hint style="warning" %}
Keyring does not natively allow whitelisting via the Keyring infrastructure. What this means is that you should create a whitelist for those addresses that are not able to have credentials created for them, such as AMM pools or other smart contracts that need to allow transfers in/out of your asset. This does not mean whitelisting cannot be performed by Keyring on behalf of an integrating partner, but it will not be handled by default. Integrating partners should define their whitelist locally on smart contracts or seek assistance from Keyring to create a whitelist-capable integration.
{% endhint %}

```solidity

/// @notice Address of the Keyring contract.
address public KeyringAddress;
/// @notice ID of the Keyring policy.
uint256 public KeyringPolicyId;
/// @notice Mapping to track whitelist status of addresses.
mapping(address => bool) public Whitelist;

/**
* @notice Constructor to initialize the KeyringToken contract.
* @param initialOwner The address of the initial owner.
* @param keyring The address of the Keyring contract.
* @param policyId The ID of the Keyring policy.
*/
constructor(address initialOwner, address keyring, uint256 policyId)
    ERC20("KeyringToken", "KTK")
    Ownable(initialOwner) {
        KeyringAddress = keyring;
        KeyringPolicyId = policyId;
}
```

Then, override the functions that need permissioning. In this example OpenZepplin 5.0.2 is used for the base ERC20 implementation and so we override the `_update` function in order to perform a Keyring check.

```solidity
/**
* @dev Internal function to update the token transfer state.
* @param from The address sending the tokens.
* @param to The address receiving the tokens.
* @param amount The amount of tokens being transferred.
*/
function _update(
   address from,
   address to,
   uint256 amount
   ) internal virtual override {
        super._update(from, to, amount);
        if (to == address(0) || from == address(0) || Whitelist[to]) {
            // Do not check keyring for minting, burning, or whitelisted contracts
            return;
        }
        // Check the status of `to`
        address keyringAddress = KeyringAddress;
        if (keyringAddress != address(0)) {
            IKeyring k = IKeyring(keyringAddress);
            uint256 keyringPolicyId = KeyringPolicyId;
            bool ok = k.checkCredential(keyringPolicyId, to);
            if (!ok) {
                // Revert if credential check fails
                revert KeyringCredentialInvalid(to);
            }
        }
    }
```

{% hint style="danger" %}
When dealing with token transfers, make sure to properly address the `mint` function (where `from` is the null address 0x0000...) and the `burn` function (where the `to` is the zero address), as the `address(0)`is always regarded as unauthorised.
{% endhint %}
