Skip to main content
This guide demonstrates how the Forte Rules Engine was integrated into the Martian Mining demo application to enforce compliance rules on the $KRYPT ERC-20 token. You can see this implementation in action at martianmining.io. The $KRYPT token uses two critical compliance rules:
  • OFAC Sanctions Screening: Prevents transfers to sanctioned addresses
  • KYC Access Level Requirement: Ensures recipients have completed appropriate KYC verification
mmc-main-page

Martian Mining Demo

See the $KRYPT token and Rules Engine in action on the live demo site

Policy Overview

The $KRYPT policy enforces compliance checks on every transfer operation. When a user attempts to transfer tokens, the Rules Engine:
  1. Checks if the recipient is on the OFAC sanctions list
  2. Verifies the recipient has completed sufficient KYC verification
  3. Only allows the transfer if both checks pass

Policy Definition

Here’s the complete policy configuration for the $KRYPT token:
{
  "Policy": " $KRYPT Policy",
  "Description": "This policy controls the KRYPT token in Martian Mining Demo App",
  "PolicyType": "open",
  "CallingFunctions": [
    {
      "Name": "Transfer",
      "FunctionSignature": "transfer(address to, uint256 value)",
      "EncodedValues": "address to, uint256 value"
    },
    {
      "Name": "TransferFrom",
      "FunctionSignature": "transferFrom(address from, address to, uint256 value)",
      "EncodedValues": "address from, address to, uint256 value"
    }
  ],
  "ForeignCalls": [
    {
      "Name": "OFACDeniedForTransfer",
      "Address": "0x333172b31BA33EFC4874315D1894c2016d325D3f",
      "Function": "isDenied(address)",
      "ReturnType": "bool",
      "ValuesToPass": "to",
      "MappedTrackerKeyValues": "",
      "CallingFunction": "Transfer"
    },
    {
      "Name": "OFACDeniedForTransferFrom",
      "Address": "0x333172b31BA33EFC4874315D1894c2016d325D3f",
      "Function": "isDenied(address)",
      "ReturnType": "bool",
      "ValuesToPass": "to",
      "MappedTrackerKeyValues": "",
      "CallingFunction": "TransferFrom"
    },
    {
      "Name": "KYCDeniedForTransfer",
      "Address": "0x85092B1D66B05c8898aDA2b1864EA01F48C600BF",
      "Function": "getAccessLevel(address)",
      "ReturnType": "uint256",
      "ValuesToPass": "to",
      "MappedTrackerKeyValues": "",
      "CallingFunction": "Transfer"
    },
    {
      "Name": "KYCDeniedForTransferFrom",
      "Address": "0x85092B1D66B05c8898aDA2b1864EA01F48C600BF",
      "Function": "getAccessLevel(address)",
      "ReturnType": "uint256",
      "ValuesToPass": "to",
      "MappedTrackerKeyValues": "",
      "CallingFunction": "TransferFrom"
    }
  ],
  "MappedTrackers": [],
  "Trackers": [],
  "Rules": [
    {
      "Name": "OFAC Deny List for Transfer",
      "Description": "This rule checks if the receiver is on the OFAC Deny List for the transfer function",
      "Condition": "FC:OFACDeniedForTransfer == false",
      "PositiveEffects": [],
      "NegativeEffects": ["revert(\"Receiver is on OFAC Deny List\")"],
      "CallingFunction": "Transfer"
    },
    {
      "Name": "OFAC Deny List for TransferFrom",
      "Description": "This rule checks if the receiver is on the OFAC Deny List for the transferFrom function",
      "Condition": "FC:OFACDeniedForTransferFrom == false",
      "PositiveEffects": [],
      "NegativeEffects": ["revert(\"Receiver is on OFAC Deny List\")"],
      "CallingFunction": "TransferFrom"
    },
    {
      "Name": "KYC Enforcement for Transfer",
      "Description": "This rule ensures wallet has completed KYC for the transfer function",
      "Condition": "FC:KYCDeniedForTransfer > 0",
      "PositiveEffects": [],
      "NegativeEffects": ["revert(\"Insufficient KYC Access Level\")"],
      "CallingFunction": "Transfer"
    },
    {
      "Name": "KYC Enforcement for TransferFrom",
      "Description": "This rule ensures wallet has completed KYC for the transferFrom function",
      "Condition": "FC:KYCDeniedForTransferFrom > 0",
      "PositiveEffects": [],
      "NegativeEffects": ["revert(\"Insufficient KYC Access Level\")"],
      "CallingFunction": "TransferFrom"
    }
  ]
}

How the Rules Work

OFAC Sanctions Screening

The OFAC rules query an onchain oracle contract to check if the recipient address is on the sanctions list:
  • Foreign Call: isDenied(address) returns true if the address is sanctioned
  • Rule Logic: If FC:OFACDeniedForTransfer == false, the transfer is blocked
  • Result: Sanctioned addresses cannot receive $KRYPT tokens

KYC Access Level Requirement

The KYC rules verify that the recipient has completed appropriate identity verification:
  • Foreign Call: getAccessLevel(address) returns a uint256 representing KYC level (0 = no KYC, >0 = KYC completed)
  • Rule Logic: If FC:KYCDeniedForTransfer > 0, the wallet has completed KYC
  • Result: Only KYC-verified wallets can receive $KRYPT tokens
Both rules check the recipient (to) address, not the sender. This ensures compliance is maintained for all token holdings, preventing tokens from entering non-compliant wallets regardless of who initiates the transfer.

Contract Integration

The KryptToken contract integrates with the Rules Engine by inheriting from RulesEngineClientCustom and applying modifiers to the transfer functions:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "src/RulesEngineIntegration.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";

contract KryptToken is RulesEngineClientCustom, ERC20, AccessControl {
    bytes32 constant TOKEN_ADMIN_ROLE = keccak256("TOKEN_ADMIN_ROLE");

    constructor(address _tokenAdmin) ERC20("Krypt Token", "$KRYPT") {
        _grantRole(TOKEN_ADMIN_ROLE, _tokenAdmin);
        _setRoleAdmin(TOKEN_ADMIN_ROLE, TOKEN_ADMIN_ROLE);
    }

    function setCallingContractAdmin(
        address callingContractAdmin
    ) public override onlyRole(TOKEN_ADMIN_ROLE) {
        super.setCallingContractAdmin(callingContractAdmin);
    }

    function mint(
        address to,
        uint256 amount
    ) public onlyRole(TOKEN_ADMIN_ROLE) {
        _mint(to, amount);
    }

    function transfer(
        address to,
        uint256 value
    ) public override checkRulesBeforeTransfer(to, value) returns (bool) {
        return super.transfer(to, value);
    }

    function transferFrom(
        address from,
        address to,
        uint256 value
    )
        public
        override
        checkRulesBeforeTransferFrom(from, to, value)
        returns (bool)
    {
        return super.transferFrom(from, to, value);
    }
}

Rule Execution Flow

When a user attempts to transfer $KRYPT tokens:
  1. User calls transfer() or transferFrom(): The transaction is initiated
  2. Rules Engine intercepts: The modifier checkRulesBeforeTransfer or checkRulesBeforeTransferFrom executes first
  3. Foreign Calls execute:
    • OFACDeniedForTransfer checks the recipient against the sanctions list
    • KYCDeniedForTransfer checks the recipient’s KYC status
  4. Rules evaluate:
    • If recipient is sanctioned → transaction reverts
    • If recipient lacks KYC → transaction reverts
    • If both checks pass → transfer proceeds
  5. Transfer executes: If all rules pass, the standard ERC-20 transfer completes

Why Both Functions Are Protected

The policy includes rules for both transfer and transferFrom to ensure comprehensive coverage:
  • transfer(): Direct transfers where the sender is the token owner
  • transferFrom(): Approval-based transfers using allowances
It’s critical to protect both functions to prevent bypassing compliance checks through allowance-based transfers.

Learn More