Skip to content

Commit 6e4b9ef

Browse files
decofeCentaur AI0xrusowsky
authored
feat: add ITIP1028Escrow interface and TIP-1028 receive policy support (#108)
* feat: add ITIP1028Escrow interface and TIP-1028 receive policy support Adds the TIP-1028 escrow interface (ITIP1028Escrow) with: - InboundKind enum, ClaimReceiptV1 struct - blockedReceiptBalance and claim functions - TransferBlocked and BlockedReceiptClaimed events - Error types: UnauthorizedClaimer, InvalidReceiptClaim, InsufficientEscrowBalance, EscrowAddressReserved, InvalidClaimAddress, InvalidToken Updates ITIP403Registry with TIP-1028 receive policy members: - BlockedReason enum (NONE, TOKEN_FILTER, RECEIVE_POLICY) - receivePolicy view, validateReceivePolicy view, setReceivePolicy mutator - ReceivePolicyUpdated event - InvalidReceivePolicyType and InvalidRecoveryAuthority errors Wires escrow into StdPrecompiles (0xE5c0...0000) and Tempo base contract. --------- Co-authored-by: Centaur AI <ai@centaur.local> Co-authored-by: 0xrusowsky <0xrusowsky@proton.me>
1 parent 9f2ccba commit 6e4b9ef

4 files changed

Lines changed: 200 additions & 0 deletions

File tree

src/StdPrecompiles.sol

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {IAddressRegistry} from "./interfaces/IAddressRegistry.sol";
66
import {IFeeManager} from "./interfaces/IFeeManager.sol";
77
import {ISignatureVerifier} from "./interfaces/ISignatureVerifier.sol";
88
import {ITIP403Registry} from "./interfaces/ITIP403Registry.sol";
9+
import {ITIP1028Escrow} from "./interfaces/ITIP1028Escrow.sol";
910
import {ITIP20Factory} from "./interfaces/ITIP20Factory.sol";
1011
import {IStablecoinDEX} from "./interfaces/IStablecoinDEX.sol";
1112
import {IValidatorConfig} from "./interfaces/IValidatorConfig.sol";
@@ -26,6 +27,7 @@ library StdPrecompiles {
2627
address internal constant VALIDATOR_CONFIG_V2_ADDRESS = 0xCcCCCCcC00000000000000000000000000000001;
2728
address internal constant ADDRESS_REGISTRY_ADDRESS = 0xfDC0000000000000000000000000000000000000;
2829
address internal constant SIGNATURE_VERIFIER_ADDRESS = 0x5165300000000000000000000000000000000000;
30+
address internal constant ESCROW_ADDRESS = 0xE5c0000000000000000000000000000000000000;
2931

3032
IFeeManager internal constant TIP_FEE_MANAGER = IFeeManager(TIP_FEE_MANAGER_ADDRESS);
3133
ITIP403Registry internal constant TIP403_REGISTRY = ITIP403Registry(TIP403_REGISTRY_ADDRESS);
@@ -37,4 +39,5 @@ library StdPrecompiles {
3739
IValidatorConfigV2 internal constant VALIDATOR_CONFIG_V2 = IValidatorConfigV2(VALIDATOR_CONFIG_V2_ADDRESS);
3840
IAddressRegistry internal constant ADDRESS_REGISTRY = IAddressRegistry(ADDRESS_REGISTRY_ADDRESS);
3941
ISignatureVerifier internal constant SIGNATURE_VERIFIER = ISignatureVerifier(SIGNATURE_VERIFIER_ADDRESS);
42+
ITIP1028Escrow internal constant TIP1028_ESCROW = ITIP1028Escrow(ESCROW_ADDRESS);
4043
}

src/Tempo.sol

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {IStablecoinDEX} from "./interfaces/IStablecoinDEX.sol";
1212
import {ITIP20} from "./interfaces/ITIP20.sol";
1313
import {ITIP20Factory} from "./interfaces/ITIP20Factory.sol";
1414
import {ITIP403Registry} from "./interfaces/ITIP403Registry.sol";
15+
import {ITIP1028Escrow} from "./interfaces/ITIP1028Escrow.sol";
1516
import {IValidatorConfig} from "./interfaces/IValidatorConfig.sol";
1617
import {IValidatorConfigV2} from "./interfaces/IValidatorConfigV2.sol";
1718

@@ -48,6 +49,10 @@ abstract contract Tempo {
4849
ITIP403Registry public constant tip403Registry = StdPrecompiles.TIP403_REGISTRY;
4950
address public constant TIP403_REGISTRY = StdPrecompiles.TIP403_REGISTRY_ADDRESS;
5051

52+
// TIP-1028 escrow precompile
53+
ITIP1028Escrow public constant escrow = StdPrecompiles.TIP1028_ESCROW;
54+
address public constant ESCROW = StdPrecompiles.ESCROW_ADDRESS;
55+
5156
// Address registry precompile
5257
IAddressRegistry public constant addrRegistry = StdPrecompiles.ADDRESS_REGISTRY;
5358
address public constant ADDRESS_REGISTRY = StdPrecompiles.ADDRESS_REGISTRY_ADDRESS;

src/interfaces/ITIP1028Escrow.sol

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
pragma solidity >=0.8.13 <0.9.0;
3+
4+
import {ITIP403Registry} from "./ITIP403Registry.sol";
5+
6+
/// @title The interface for TIP-1028 escrow
7+
/// @notice Escrow holding blocked inbound TIP-20 transfers and mints until claimed
8+
interface ITIP1028Escrow {
9+
/// @notice The kind of inbound operation that was blocked
10+
/// @param TRANSFER A regular TIP-20 transfer
11+
/// @param MINT A TIP-20 mint
12+
enum InboundKind {
13+
TRANSFER,
14+
MINT
15+
}
16+
17+
/// @notice V1 claim receipt encoding for a blocked inbound
18+
/// @param originator The original sender (transfer) or issuer (mint)
19+
/// @param recipient The intended recipient of the blocked inbound
20+
/// @param blockedAt The block number at which the inbound was blocked
21+
/// @param blockedNonce The escrow-scoped nonce assigned to this blocked receipt
22+
/// @param blockedReason The reason the inbound was blocked
23+
/// @param kind Whether the blocked inbound was a transfer or mint
24+
/// @param memo Application-specific memo attached to the inbound
25+
struct ClaimReceiptV1 {
26+
address originator;
27+
address recipient;
28+
uint64 blockedAt;
29+
uint64 blockedNonce;
30+
ITIP403Registry.BlockedReason blockedReason;
31+
InboundKind kind;
32+
bytes32 memo;
33+
}
34+
35+
/// @notice Returns the escrowed balance for a specific blocked receipt
36+
/// @param token The TIP-20 token address
37+
/// @param recoveryAuthority The recovery authority that was assigned to the blocked receipt
38+
/// @param receiptVersion The version of the claim receipt encoding
39+
/// @param receipt The ABI-encoded claim receipt
40+
/// @return amount The escrowed balance for the receipt
41+
function blockedReceiptBalance(
42+
address token,
43+
address recoveryAuthority,
44+
uint8 receiptVersion,
45+
bytes calldata receipt
46+
) external view returns (uint256 amount);
47+
48+
/// @notice Claims a blocked receipt, releasing escrowed funds to the specified address
49+
/// @param token The TIP-20 token address
50+
/// @param recoveryAuthority The recovery authority that was assigned to the blocked receipt
51+
/// @param receiptVersion The version of the claim receipt encoding
52+
/// @param receipt The ABI-encoded claim receipt
53+
/// @param to The address to release the escrowed funds to
54+
function claim(address token, address recoveryAuthority, uint8 receiptVersion, bytes calldata receipt, address to)
55+
external;
56+
57+
/// @notice Emitted when an inbound transfer or mint is blocked by a receive policy
58+
/// @param token The TIP-20 token address
59+
/// @param from The sender of the blocked inbound
60+
/// @param receiver The receiver whose receive policy blocked the inbound
61+
/// @param receiptVersion The version of the claim receipt encoding
62+
/// @param blockedNonce The escrow-scoped nonce assigned to this blocked receipt
63+
/// @param blockedAt The block number at which the inbound was blocked
64+
/// @param recipient The intended recipient of the blocked inbound
65+
/// @param amount The amount of tokens blocked
66+
/// @param blockedReason The reason the inbound was blocked
67+
/// @param recoveryAuthority The recovery authority assigned to the blocked receipt
68+
/// @param memo Application-specific memo attached to the inbound
69+
event TransferBlocked(
70+
address indexed token,
71+
address indexed from,
72+
address indexed receiver,
73+
uint8 receiptVersion,
74+
uint64 blockedNonce,
75+
uint64 blockedAt,
76+
address recipient,
77+
uint256 amount,
78+
ITIP403Registry.BlockedReason blockedReason,
79+
address recoveryAuthority,
80+
bytes32 memo
81+
);
82+
83+
/// @notice Emitted when a blocked receipt is claimed
84+
/// @param token The TIP-20 token address
85+
/// @param receiver The receiver whose escrow held the blocked funds
86+
/// @param receiptVersion The version of the claim receipt encoding
87+
/// @param blockedNonce The escrow-scoped nonce of the claimed receipt
88+
/// @param blockedAt The block number at which the inbound was originally blocked
89+
/// @param originator The original sender of the blocked inbound
90+
/// @param recipient The intended recipient of the blocked inbound
91+
/// @param recoveryAuthority The recovery authority that authorized the claim
92+
/// @param caller The address that called claim
93+
/// @param to The address that received the released funds
94+
/// @param amount The amount of tokens released
95+
event BlockedReceiptClaimed(
96+
address indexed token,
97+
address indexed receiver,
98+
uint8 receiptVersion,
99+
uint64 indexed blockedNonce,
100+
uint64 blockedAt,
101+
address originator,
102+
address recipient,
103+
address recoveryAuthority,
104+
address caller,
105+
address to,
106+
uint256 amount
107+
);
108+
109+
/// @notice Error when the caller is not authorized to claim the blocked receipt
110+
error UnauthorizedClaimer();
111+
112+
/// @notice Error when the claim receipt is invalid or does not match any blocked receipt
113+
error InvalidReceiptClaim();
114+
115+
/// @notice Error when the escrow balance is insufficient (already claimed or zero)
116+
error InsufficientEscrowBalance();
117+
118+
/// @notice Error when the escrow address itself is used where it should not be
119+
error EscrowAddressReserved();
120+
121+
/// @notice Error when the claim destination address is invalid
122+
error InvalidClaimAddress();
123+
124+
/// @notice Error when the token address is invalid
125+
error InvalidToken();
126+
}

src/interfaces/ITIP403Registry.sol

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ interface ITIP403Registry {
1414
COMPOUND
1515
}
1616

17+
/// @notice Reasons an inbound transfer can be blocked by a receive policy
18+
/// @param NONE The transfer is not blocked
19+
/// @param TOKEN_FILTER The token is not allowed by the token filter policy
20+
/// @param RECEIVE_POLICY The sender is not allowed by the receive policy
21+
enum BlockedReason {
22+
NONE,
23+
TOKEN_FILTER,
24+
RECEIVE_POLICY
25+
}
26+
1727
/// @notice Data structure containing policy configuration
1828
/// @param policyType The type of policy (whitelist or blacklist)
1929
/// @param admin The address authorized to modify this policy
@@ -40,6 +50,12 @@ interface ITIP403Registry {
4050
/// @notice TIP-1022 virtual addresses cannot be used as literal policy members
4151
error VirtualAddressNotAllowed();
4252

53+
/// @notice Error when a receive policy references an incompatible policy type
54+
error InvalidReceivePolicyType();
55+
56+
/// @notice Error when the recovery authority address is invalid
57+
error InvalidRecoveryAuthority();
58+
4359
/// @notice Emitted when a policy's admin is updated
4460
/// @param policyId The ID of the policy that was updated
4561
/// @param updater The address that performed the update
@@ -132,6 +148,15 @@ interface ITIP403Registry {
132148
uint64 mintRecipientPolicyId
133149
);
134150

151+
/// @notice Emitted when an account's receive policy is updated
152+
/// @param account The account whose receive policy was updated
153+
/// @param senderPolicyId The sender policy ID for the receive policy
154+
/// @param tokenFilterId The token filter policy ID for the receive policy
155+
/// @param recoveryAuthority The recovery authority address for blocked receipts
156+
event ReceivePolicyUpdated(
157+
address indexed account, uint64 senderPolicyId, uint64 tokenFilterId, address recoveryAuthority
158+
);
159+
135160
/// @notice TIP-1015: Creates a new immutable compound policy
136161
/// @param senderPolicyId Policy ID to check for transfer senders
137162
/// @param recipientPolicyId Policy ID to check for transfer recipients
@@ -168,4 +193,45 @@ interface ITIP403Registry {
168193
external
169194
view
170195
returns (uint64 senderPolicyId, uint64 recipientPolicyId, uint64 mintRecipientPolicyId);
196+
197+
// =========================================================================
198+
// TIP-1028: Receive Policies
199+
// =========================================================================
200+
201+
/// @notice Returns the receive policy configuration for an account
202+
/// @param account The account to query
203+
/// @return hasReceivePolicy Whether the account has a receive policy set
204+
/// @return senderPolicyId The sender policy ID for inbound transfer authorization
205+
/// @return senderPolicyType The type of the sender policy
206+
/// @return tokenFilterId The token filter policy ID
207+
/// @return tokenFilterType The type of the token filter policy
208+
/// @return recoveryAuthority The recovery authority address for blocked receipts
209+
function receivePolicy(address account)
210+
external
211+
view
212+
returns (
213+
bool hasReceivePolicy,
214+
uint64 senderPolicyId,
215+
PolicyType senderPolicyType,
216+
uint64 tokenFilterId,
217+
PolicyType tokenFilterType,
218+
address recoveryAuthority
219+
);
220+
221+
/// @notice Validates an inbound transfer against the receiver's receive policy
222+
/// @param token The TIP-20 token being transferred
223+
/// @param sender The sender of the transfer
224+
/// @param receiver The receiver of the transfer
225+
/// @return authorized Whether the transfer is authorized
226+
/// @return blockedReason The reason the transfer was blocked (NONE if authorized)
227+
function validateReceivePolicy(address token, address sender, address receiver)
228+
external
229+
view
230+
returns (bool authorized, BlockedReason blockedReason);
231+
232+
/// @notice Sets the caller's receive policy for inbound transfers
233+
/// @param senderPolicyId The sender policy ID to use for inbound transfer authorization
234+
/// @param tokenFilterId The token filter policy ID
235+
/// @param recoveryAuthority The recovery authority address for blocked receipts
236+
function setReceivePolicy(uint64 senderPolicyId, uint64 tokenFilterId, address recoveryAuthority) external;
171237
}

0 commit comments

Comments
 (0)