-
Notifications
You must be signed in to change notification settings - Fork 2
trails validator patch #86
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…r ERC20, ERC721, and ERC1155 tokens - Added TrailsValidator contract with functions to validate token balances, allowances, and ownership. - Implemented deployment script for TrailsValidator using SingletonDeployer. - Generated run-latest.json for deployment transaction details.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR introduces a TrailsValidator utility contract ported from Sequence's RequireUtils, adapted to use msg.sender for validation checks. The contract provides validation functions for token balances, allowances, ownership, and expiration checks that can be used in Trails intent transactions for counterfactual address derivation.
Key Changes
- Added TrailsValidator contract with 8 validation functions for ERC20, ERC721, ERC1155 tokens and expiration checks
- Implemented CREATE2 deployment script using SingletonDeployer for deterministic cross-chain addresses
- Generated deployment artifacts for multiple chains (Base, Arbitrum, Polygon, Optimism)
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/TrailsValidator.sol | New validator contract with view functions for checking token balances, allowances, ownership, and expirations using msg.sender |
| script/TrailsValidator.s.sol | Deployment script using SingletonDeployer with zero salt for CREATE2 deployment |
| broadcast/TrailsValidator.s.sol//run-.json | Deployment artifacts showing successful contract deployments across multiple chains with consistent addresses (0x1882898c585ad2577944373dff44ebc234b35eaa) |
| broadcast/TrailsIntentEntrypoint.s.sol/137/run-*.json | Updated deployment artifacts for TrailsIntentEntrypoint contract on Polygon with new deployment address |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| contract TrailsValidator { | ||
| /** | ||
| * @notice Validates that a given expiration hasn't expired | ||
| * @dev Used as an optional transaction on a Sequence batch, to create expirable transactions. | ||
| * @param _expiration Expiration timestamp to check | ||
| */ | ||
| function requireNonExpired(uint256 _expiration) external view { | ||
| require(block.timestamp < _expiration, "TrailsValidator#requireNonExpired: EXPIRED"); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Validates that msg.sender has a minimum ERC20 token balance | ||
| * @param _token ERC20 token address | ||
| * @param _minBalance Minimum required balance | ||
| */ | ||
| function requireMinERC20Balance(address _token, uint256 _minBalance) external view { | ||
| uint256 balance = IERC20(_token).balanceOf(msg.sender); | ||
| require(balance >= _minBalance, "TrailsValidator#requireMinERC20Balance: BALANCE_TOO_LOW"); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Validates that msg.sender has a minimum native token balance | ||
| * @param _minBalance Minimum required balance | ||
| */ | ||
| function requireMinNativeBalance(uint256 _minBalance) external view { | ||
| require(msg.sender.balance >= _minBalance, "TrailsValidator#requireMinNativeBalance: BALANCE_TOO_LOW"); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Validates that msg.sender has a minimum ERC20 allowance for a spender | ||
| * @param _token ERC20 token address | ||
| * @param _spender Address allowed to spend the tokens | ||
| * @param _minAllowance Minimum required allowance | ||
| */ | ||
| function requireMinERC20Allowance(address _token, address _spender, uint256 _minAllowance) external view { | ||
| uint256 allowance = IERC20(_token).allowance(msg.sender, _spender); | ||
| require(allowance >= _minAllowance, "TrailsValidator#requireMinERC20Allowance: ALLOWANCE_TOO_LOW"); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Validates that msg.sender owns a specific ERC721 token | ||
| * @param _token ERC721 token address | ||
| * @param _tokenId Token ID to check for ownership | ||
| */ | ||
| function requireERC721Ownership(address _token, uint256 _tokenId) external view { | ||
| address owner = IERC721(_token).ownerOf(_tokenId); | ||
| require(owner == msg.sender, "TrailsValidator#requireERC721Ownership: NOT_OWNER"); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Validates that an ERC721 token owned by msg.sender is approved for a specific spender | ||
| * @param _token ERC721 token address | ||
| * @param _spender Address that should have approval | ||
| * @param _tokenId Token ID to check for approval | ||
| */ | ||
| function requireERC721Approval(address _token, address _spender, uint256 _tokenId) external view { | ||
| address approved = IERC721(_token).getApproved(_tokenId); | ||
| require( | ||
| approved == _spender || IERC721(_token).isApprovedForAll(msg.sender, _spender), | ||
| "TrailsValidator#requireERC721Approval: NOT_APPROVED" | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Validates that msg.sender has a minimum balance of an ERC1155 token | ||
| * @param _token ERC1155 token address | ||
| * @param _tokenId Token ID to check | ||
| * @param _minBalance Minimum required balance | ||
| */ | ||
| function requireMinERC1155Balance(address _token, uint256 _tokenId, uint256 _minBalance) external view { | ||
| uint256 balance = IERC1155(_token).balanceOf(msg.sender, _tokenId); | ||
| require(balance >= _minBalance, "TrailsValidator#requireMinERC1155Balance: BALANCE_TOO_LOW"); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Validates that an ERC1155 token is approved for a specific operator by msg.sender | ||
| * @param _token ERC1155 token address | ||
| * @param _operator Address that should have operator approval | ||
| */ | ||
| function requireERC1155Approval(address _token, address _operator) external view { | ||
| bool isApproved = IERC1155(_token).isApprovedForAll(msg.sender, _operator); | ||
| require(isApproved, "TrailsValidator#requireERC1155Approval: NOT_APPROVED"); | ||
| } | ||
| } |
Copilot
AI
Dec 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The TrailsValidator contract lacks test coverage. The repository uses comprehensive automated testing (as seen in TrailsIntentEntrypoint.t.sol), but no tests exist for the TrailsValidator contract. Consider adding tests to verify all validation functions work correctly with various scenarios including edge cases, reverts with correct error messages, and interactions with different token standards.
| function requireERC721Approval(address _token, address _spender, uint256 _tokenId) external view { | ||
| address approved = IERC721(_token).getApproved(_tokenId); | ||
| require( | ||
| approved == _spender || IERC721(_token).isApprovedForAll(msg.sender, _spender), | ||
| "TrailsValidator#requireERC721Approval: NOT_APPROVED" | ||
| ); | ||
| } |
Copilot
AI
Dec 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The requireERC721Approval function does not verify that msg.sender actually owns the token before checking approval. This could allow checking approval status for tokens not owned by the caller. Consider adding an ownership check similar to requireERC721Ownership to ensure msg.sender is the actual owner of the token before validating approval.
Uh oh!
There was an error while loading. Please reload this page.