-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMockAToken.sol
More file actions
66 lines (59 loc) · 3.18 KB
/
Copy pathMockAToken.sol
File metadata and controls
66 lines (59 loc) · 3.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IIdentityRegistry} from "./interfaces/IIdentityRegistry.sol";
import {ICompliancePolicy} from "./interfaces/ICompliancePolicy.sol";
/// @title MockAToken
/// @notice Devnet stand-in for a Cleanverse A-Token, for chains where CVA is not
/// deployed. On Monad it is not in the money path at all — the settlement asset
/// there is Cleanverse's own `cvaUSD`, supplied to the deploy script.
///
/// Unlike a bare mock, it gates every transfer through an external
/// `ICompliancePolicy` with the same `canTransfer(token, from, to, amount)`
/// shape as Cleanverse CCP's real `IATokenPolicy`, enforced in the token itself
/// via `_update` so no transfer path can bypass it.
///
/// It used to also carry an EIP-3009 `transferWithAuthorization` implementation
/// for gasless relaying. Nothing ever called it — not a test, not a script, not
/// the desk — and the real settlement path is a plain `approve`/`transferFrom`
/// through `FactoringEscrow`, so it went with the rest of the dead code.
///
/// Public `mint` makes it a self-serve devnet faucet. Do not use in prod.
contract MockAToken is ERC20 {
uint8 private immutable _decimals;
IIdentityRegistry public immutable identityRegistry;
ICompliancePolicy public immutable policy;
error NotCompliantHolder(address party);
constructor(string memory name_, string memory symbol_, uint8 dec_, address identityRegistryAddr, address policyAddr)
ERC20(name_, symbol_)
{
_decimals = dec_;
identityRegistry = IIdentityRegistry(identityRegistryAddr);
policy = ICompliancePolicy(policyAddr);
}
function decimals() public view override returns (uint8) {
return _decimals;
}
/// @notice Open devnet faucet mint. Minting itself is not gated (funding a wallet
/// isn't a transfer between two parties), but the minted balance can only
/// ever move onward to a verified wallet, exactly like a real A-Token.
function mint(address to, uint256 amount) external {
_mint(to, amount);
}
/// @dev The airtight CVA gate: overriding `_update` (OZ v5's single mint/burn/
/// transfer hook) means every path — plain transfer and transferFrom alike —
/// is gated identically. Mint (from == 0) and burn (to == 0) are left open;
/// only wallet-to-wallet moves are gated, mirroring a real A-Token's "only
/// moves between A-Pass wallets" behaviour. The gate itself is delegated to
/// `policy.canTransfer`, matching Cleanverse CCP's real
/// `IATokenPolicy.canTransfer(token, from, to, amount)` shape.
function _update(address from, address to, uint256 value) internal override {
if (from != address(0) && to != address(0)) {
if (!policy.canTransfer(address(this), from, to, value)) {
if (!identityRegistry.isVerified(from)) revert NotCompliantHolder(from);
revert NotCompliantHolder(to);
}
}
super._update(from, to, value);
}
}