Skip to main content
Regulatory compliance often requires preventing sanctioned addresses from interacting with your protocol. This policy ensures that wallets on the OFAC sanctions list cannot receive tokens, helping you maintain compliance with U.S. financial regulations.

How does it work?

By hooking the transfer and transferFrom functions of the ERC-20 contract into the Rules Engine with a policy that checks if the receiving wallet is on the OFAC sanctions list before allowing the transfer to complete.

Let’s break that down further.

The Office of Foreign Assets Control (OFAC) maintains a list of Specially Designated Nationals (SDN) and blocked addresses that U.S. persons are prohibited from transacting with. When a token transfer is initiated, the Rules Engine queries an onchain sanctions screening contract (such as Chainalysis’s Oracle) to check if the recipient address is sanctioned. If the recipient is on the sanctions list, the transaction reverts immediately, preventing any transfer of value to that address. This happens before any tokens move, ensuring full compliance. This is essential for:
  • Regulatory compliance: Meet U.S. sanctions requirements for digital assets
  • Risk management: Protect your protocol from association with sanctioned entities
  • Institutional adoption: Demonstrate robust compliance controls to institutional users

Implementation

The most common approach is to check against an OFAC sanctions list maintained by Chainalysis, which automatically updates the list for many popular EVM networks. If you’re project is on a different network or you’re still in testnet you’ll need to deploy a mock version of the contract to test against. The policy uses Foreign Calls to query the sanctions screening contract. You’ll need to:
  1. Deploy or integrate with a sanctions screening oracle (e.g., Chainalysis)
  2. Update the Address field in the Foreign Calls to point to your oracle adapter contract
  3. Ensure the oracle contract implements the isSanctioned(addr) function

Policy JSON

{
  "Policy": "OFAC Sanctions List",
  "Description": "Prevent access to any wallet on the OFAC sanctions list",
  "PolicyType": "open",
  "CallingFunctions": [
    {
      "Name": "transfer(address to, uint256 value)",
      "FunctionSignature": "transfer(address to, uint256 value)",
      "EncodedValues": "address to, uint256 value"
    },
    {
      "Name": "transferFrom(address from, address to, uint256 value)",
      "FunctionSignature": "transferFrom(address from, address to, uint256 value)",
      "EncodedValues": "address from, address to, uint256 value"
    }
  ],
  "ForeignCalls": [
    {
      "Name": "OFACDeniedForTransfer",
      "Address": "0x40C57923924B5c5c5455c48D93317139ADDaC8fb",
      "Function": "isSanctioned(addr)",
      "ReturnType": "bool",
      "ValuesToPass": "to",
      "MappedTrackerKeyValues": "",
      "CallingFunction": "transfer(address to, uint256 value)"
    },
    {
      "Name": "OFACDeniedForTransferFrom",
      "Address": "0x40C57923924B5c5c5455c48D93317139ADDaC8fb",
      "Function": "isSanctioned(addr)",
      "ReturnType": "bool",
      "ValuesToPass": "to",
      "MappedTrackerKeyValues": "",
      "CallingFunction": "transferFrom(address from, address to, uint256 value)"
    }
  ],
  "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(address to, uint256 value)"
    },
    {
      "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(address from, address to, uint256 value)"
    }
  ]
}
The policy example above is using the Chainalysis maintained OFAC list on Ethereum (0x40C57923924B5c5c5455c48D93317139ADDaC8fb). You can find the oracle address for other networks here: Chainalysis maintained OFAC oracles.
It’s critical that you add the rule to both the transfer and transferFrom functions to ensure sanctioned addresses cannot receive tokens through any transfer method.
I