Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
79772c6
feat: escrow contract
0xKitsune May 6, 2026
b90d340
feat: escrow precompile
0xKitsune May 6, 2026
a8aea32
feat: tip403 logic
0xKitsune May 6, 2026
e9cbb0c
wip: simplify storage variables
0xKitsune May 6, 2026
be701cd
docs: comment
0xKitsune May 6, 2026
ebd6013
chore: cleanup validate policy
0xKitsune May 6, 2026
eab4a98
feat: tip20 transfer
0xKitsune May 6, 2026
5057561
Merge branch 'main' into kit/tip-1028-impl
0xKitsune May 6, 2026
00f0063
chore: move to t6
0xKitsune May 6, 2026
4aa170b
feat: tip20 transfer logic
0xKitsune May 6, 2026
4b4787c
feat: update transfer or escrow
0xKitsune May 6, 2026
79cf39f
chore: simplify transfer_or_escrow
0xKitsune May 6, 2026
aa52cd0
feat: transfer_from
0xKitsune May 6, 2026
3576f75
feat: memo functions
0xKitsune May 6, 2026
f6d0cc6
feat: mint paths
0xKitsune May 6, 2026
794ac82
fix: transfer_from
0xKitsune May 6, 2026
b76e926
fix: update to call transfer_or_escrow
0xKitsune May 6, 2026
84ff524
chore: simplify escrow address check, add docs
0xKitsune May 6, 2026
dd84248
docs: update comments
0xKitsune May 6, 2026
5977a06
docs: update comments
0xKitsune May 6, 2026
c068a86
docs: simplify comments
0xKitsune May 6, 2026
76511af
chore: remove redundant check
0xKitsune May 6, 2026
8588ab4
chore: rename fn
0xKitsune May 6, 2026
faa88df
wip: simplify escrow logic
0xKitsune May 6, 2026
bf0ff93
wip: update mint checks
0xKitsune May 6, 2026
54782ec
docs: comments
0xKitsune May 6, 2026
ec79af2
chore: rename function
0xKitsune May 6, 2026
cff7814
test: tip1028 tests
0xKitsune May 7, 2026
eed39a5
test: tip20 tests
0xKitsune May 7, 2026
c3b5f75
test: update 403 and tip20 tests
0xKitsune May 7, 2026
cfe48f5
test: invariant tests
0xKitsune May 7, 2026
5914c67
fix: deploy escrow at t6 block, update genesis args, fix tests
0xKitsune May 7, 2026
c131127
fix: update TIP-1028 ABI and genesis fixture
0xKitsune May 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions crates/contracts/src/precompiles/escrow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
pub use ITIP1028Escrow::{
ITIP1028EscrowErrors as TIP1028EscrowError, ITIP1028EscrowEvents as TIP1028EscrowEvent,
};

crate::sol! {
#[derive(Debug, PartialEq, Eq)]
#[sol(abi)]
interface ITIP1028Escrow {
enum InboundKind {
TRANSFER,
MINT
}

struct ClaimReceiptV1 {
address originator;
address recipient;
uint64 blockedAt;
uint64 blockedNonce;
uint8 blockedReason;
InboundKind kind;
bytes32 memo;
}

function blockedReceiptBalance(address token, address recoveryContract, uint8 receiptVersion, bytes calldata receipt) external view returns (uint256 amount);
function claimBlocked(address token, address recoveryContract, uint8 receiptVersion, bytes calldata receipt, address to) external;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: should we rename to simply claim?


event TransferBlocked(address indexed token, address indexed from, address indexed receiver, uint8 receiptVersion, uint64 blockedNonce, uint64 blockedAt, address recipient, uint256 amount, uint8 blockedReason, address recoveryContract, bytes32 memo);
event BlockedReceiptClaimed(address indexed token, address indexed receiver, uint8 receiptVersion, uint64 indexed blockedNonce, uint64 blockedAt, address originator, address recipient, address recoveryContract, address caller, address to, uint256 amount);

error UnauthorizedClaimer();
error InvalidReceiptClaim();
error InsufficientEscrowBalance();
error EscrowAddressReserved();
error InvalidClaimAddress();
error InvalidToken();
}
}

impl TIP1028EscrowError {
pub const fn unauthorized_claimer() -> Self {
Self::UnauthorizedClaimer(ITIP1028Escrow::UnauthorizedClaimer {})
}

pub const fn invalid_receipt_claim() -> Self {
Self::InvalidReceiptClaim(ITIP1028Escrow::InvalidReceiptClaim {})
}

pub const fn insufficient_escrow_balance() -> Self {
Self::InsufficientEscrowBalance(ITIP1028Escrow::InsufficientEscrowBalance {})
}

pub const fn escrow_address_reserved() -> Self {
Self::EscrowAddressReserved(ITIP1028Escrow::EscrowAddressReserved {})
}

pub const fn invalid_claim_address() -> Self {
Self::InvalidClaimAddress(ITIP1028Escrow::InvalidClaimAddress {})
}

pub const fn invalid_token() -> Self {
Self::InvalidToken(ITIP1028Escrow::InvalidToken {})
}
}
3 changes: 3 additions & 0 deletions crates/contracts/src/precompiles/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod account_keychain;
pub mod address_registry;
pub mod common_errors;
pub mod escrow;
pub mod nonce;
pub mod signature_verifier;
pub mod stablecoin_dex;
Expand All @@ -15,6 +16,7 @@ pub use account_keychain::*;
pub use address_registry::*;
use alloy_primitives::{Address, address};
pub use common_errors::*;
pub use escrow::*;
pub use nonce::*;
pub use signature_verifier::*;
pub use stablecoin_dex::*;
Expand Down Expand Up @@ -43,3 +45,4 @@ pub const ADDRESS_REGISTRY_ADDRESS: Address =
address!("0xFDC0000000000000000000000000000000000000");
pub const SIGNATURE_VERIFIER_ADDRESS: Address =
address!("0x5165300000000000000000000000000000000000");
pub const ESCROW_ADDRESS: Address = address!("0xE5C0000000000000000000000000000000000000");
20 changes: 20 additions & 0 deletions crates/contracts/src/precompiles/tip403_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ crate::sol! {
COMPOUND
}

enum BlockedReason {
NONE,
TOKEN_FILTER,
RECEIVE_POLICY
}
Comment thread
0xrusowsky marked this conversation as resolved.

// View Functions
function policyIdCounter() external view returns (uint64);
function policyExists(uint64 policyId) external view returns (bool);
Expand All @@ -22,6 +28,8 @@ crate::sol! {
function isAuthorizedRecipient(uint64 policyId, address user) external view returns (bool);
function isAuthorizedMintRecipient(uint64 policyId, address user) external view returns (bool);
function compoundPolicyData(uint64 policyId) external view returns (uint64 senderPolicyId, uint64 recipientPolicyId, uint64 mintRecipientPolicyId);
function receivePolicy(address account) external view returns (bool hasReceivePolicy, uint64 senderPolicyId, PolicyType senderPolicyType, uint64 tokenFilterId, PolicyType tokenFilterType, address recoveryAddress);
function validateReceivePolicy(address token, address sender, address receiver) external view returns (bool authorized, BlockedReason blockedReason);

// State-Changing Functions
function createPolicy(address admin, PolicyType policyType) external returns (uint64);
Expand All @@ -30,13 +38,15 @@ crate::sol! {
function modifyPolicyWhitelist(uint64 policyId, address account, bool allowed) external;
function modifyPolicyBlacklist(uint64 policyId, address account, bool restricted) external;
function createCompoundPolicy(uint64 senderPolicyId, uint64 recipientPolicyId, uint64 mintRecipientPolicyId) external returns (uint64);
function setReceivePolicy(uint64 senderPolicyId, uint64 tokenFilterId, address recoveryAddress) external;

// Events
event PolicyAdminUpdated(uint64 indexed policyId, address indexed updater, address indexed admin);
event PolicyCreated(uint64 indexed policyId, address indexed updater, PolicyType policyType);
event WhitelistUpdated(uint64 indexed policyId, address indexed updater, address indexed account, bool allowed);
event BlacklistUpdated(uint64 indexed policyId, address indexed updater, address indexed account, bool restricted);
event CompoundPolicyCreated(uint64 indexed policyId, address indexed creator, uint64 senderPolicyId, uint64 recipientPolicyId, uint64 mintRecipientPolicyId);
event ReceivePolicyUpdated(address indexed account, uint64 senderPolicyId, uint64 tokenFilterId, address recoveryAddress);

// Errors
error Unauthorized();
Expand All @@ -45,6 +55,8 @@ crate::sol! {
error InvalidPolicyType();
error IncompatiblePolicyType();
error VirtualAddressNotAllowed();
error InvalidReceivePolicyType();
error InvalidReceivePolicyAddress();
}
}

Expand Down Expand Up @@ -94,4 +106,12 @@ impl TIP403RegistryError {
pub const fn virtual_address_not_allowed() -> Self {
Self::VirtualAddressNotAllowed(ITIP403Registry::VirtualAddressNotAllowed {})
}

pub const fn invalid_receive_policy_type() -> Self {
Self::InvalidReceivePolicyType(ITIP403Registry::InvalidReceivePolicyType {})
}

pub const fn invalid_receive_policy_address() -> Self {
Self::InvalidReceivePolicyAddress(ITIP403Registry::InvalidReceivePolicyAddress {})
}
}
1 change: 1 addition & 0 deletions crates/e2e/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mod snapshot;
// FIXME: subblocks are currently flaky.
// mod subblocks;
mod sync;
mod tip1028_escrow;
mod v4_at_genesis;

#[test_traced]
Expand Down
Loading
Loading