There is a looming risk in DeFi concerning real-world assets (RWAs) or security tokens in general and pools holding them (think Uniswap, AAVE, …) regarding Anti Money Laundering (AML) regulation.
Should a court order the token issuer (i.e. Circle) to freeze the holdings of an address that has deposited the funds into a pool (i.e. UniV3-ETH/USDC), that might risk the token issuer having to freeze the whole pool's holdings.
This risk is even amplified in the new Uniswap v4 architecture as all pools' funds will be held by a single contract, rather than each pool holding its own funds separately.
In order to mitigate that risk, pools could implement a protocol that allows the regulated token issuers to delegate their AML actions to these pools, so they can be enforced on a per-liquidity-provider basis.
So in case of our hypothetical court order, the pool would freeze the sanctioned address' LP token and potentially even transfer their share of the pooled principle back to the token issuer.
While that approach ideologically goes somewhat against the original intention behind crypto currencies, it just makes sense to manage risks and comply with legislation in a densly regulated space like RWAs.
Each regulated token needs to provide a set of functions for delegating their AML actions. It could be implemented by a contract that's separate from the token contract, but it would need to be discoverable via the token contract's address (details tbd).
interface UpstreamAML {
function isFrozen(address holder) external view returns (bool);
}
The pool on the other hand needs to check on every LP token interaction (deposit, withdrawal, transfer) if the holder is sanctioned by any of the principle tokens.
interface UpstreamAML {
struct SeizureWarrant {
address holder;
uint256 amount;
bool seizeAll;
}
event Seizure(
uint256 indexed warrantID,
address indexed holder,
uint256 amount
);
function getSeizureWarrant(uint256 id) external view returns (SeizureWarrant);
function hasBeenSeized(uint256 id, address fromPool) external view returns (bool);
/** Seizes funds from the caller. */
function seize(uint256 warrantID, uint256 amount) external;
}
The pool would need to implement an additional interface.
interface DownstreamAML {
interface Seizure {
address holder;
address token;
uint256 warrantID;
uint256 amount;
}
/** Permissionless. Anyone can call the function, but will only do anything if there is an upstream warrant. */
function seizeHoldings(address holder) external returns (Seizure[] calldata);
}
- Clarify exact requirements
- Are seizures necessary or is it sufficient to freeze the LP tokens?
- How to discover upstream AML contract (if not part of token contract)?