Skip to content

Commit 2608a40

Browse files
authored
trails validator patch (#86)
* feat: Implement TrailsValidator contract with validation utilities for 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. * chore: Update comment to clarify porting of RequireUtils to TrailsValidator
1 parent 5e45686 commit 2608a40

File tree

10 files changed

+616
-22
lines changed

10 files changed

+616
-22
lines changed

broadcast/TrailsIntentEntrypoint.s.sol/137/run-1764604931093.json

Lines changed: 75 additions & 0 deletions
Large diffs are not rendered by default.

broadcast/TrailsIntentEntrypoint.s.sol/137/run-latest.json

Lines changed: 23 additions & 22 deletions
Large diffs are not rendered by default.

broadcast/TrailsValidator.s.sol/10/run-1765878977792.json

Lines changed: 65 additions & 0 deletions
Large diffs are not rendered by default.

broadcast/TrailsValidator.s.sol/10/run-latest.json

Lines changed: 65 additions & 0 deletions
Large diffs are not rendered by default.

broadcast/TrailsValidator.s.sol/137/run-latest.json

Lines changed: 75 additions & 0 deletions
Large diffs are not rendered by default.

broadcast/TrailsValidator.s.sol/42161/run-latest.json

Lines changed: 60 additions & 0 deletions
Large diffs are not rendered by default.

broadcast/TrailsValidator.s.sol/8453/run-1765878942118.json

Lines changed: 65 additions & 0 deletions
Large diffs are not rendered by default.

broadcast/TrailsValidator.s.sol/8453/run-latest.json

Lines changed: 65 additions & 0 deletions
Large diffs are not rendered by default.

script/TrailsValidator.s.sol

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
pragma solidity ^0.8.30;
4+
5+
import {SingletonDeployer, console} from "erc2470-libs/script/SingletonDeployer.s.sol";
6+
import {TrailsValidator} from "../src/TrailsValidator.sol";
7+
8+
contract Deploy is SingletonDeployer {
9+
function run() external {
10+
uint256 privateKey = vm.envUint("PRIVATE_KEY");
11+
address deployerAddress = vm.addr(privateKey);
12+
console.log("Deployer Address:", deployerAddress);
13+
14+
bytes32 salt = bytes32(0);
15+
16+
bytes memory initCode = type(TrailsValidator).creationCode;
17+
address validator = _deployIfNotAlready("TrailsValidator", initCode, salt, privateKey);
18+
19+
console.log("TrailsValidator deployed at:", validator);
20+
}
21+
}

src/TrailsValidator.sol

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
pragma solidity ^0.8.30;
4+
5+
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
6+
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
7+
import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
8+
9+
// From: https://github.com/0xsequence/wallet-contracts/blob/db6789c3f8ad774dc55253f0599e0f2f0833f76a/contracts/modules/utils/RequireUtils.sol
10+
// Ported `RequireUtils` to `TrailsValidator` with modifications to use msg.sender
11+
12+
/**
13+
* @title TrailsValidator
14+
* @notice Validation utilities for Trails intent transactions using msg.sender
15+
* @dev All functions use msg.sender to check the caller's balances/allowances,
16+
* which allows these calls to be included in counterfactual address derivation
17+
* without creating circular dependencies.
18+
*/
19+
contract TrailsValidator {
20+
/**
21+
* @notice Validates that a given expiration hasn't expired
22+
* @dev Used as an optional transaction on a Sequence batch, to create expirable transactions.
23+
* @param _expiration Expiration timestamp to check
24+
*/
25+
function requireNonExpired(uint256 _expiration) external view {
26+
require(block.timestamp < _expiration, "TrailsValidator#requireNonExpired: EXPIRED");
27+
}
28+
29+
/**
30+
* @notice Validates that msg.sender has a minimum ERC20 token balance
31+
* @param _token ERC20 token address
32+
* @param _minBalance Minimum required balance
33+
*/
34+
function requireMinERC20Balance(address _token, uint256 _minBalance) external view {
35+
uint256 balance = IERC20(_token).balanceOf(msg.sender);
36+
require(balance >= _minBalance, "TrailsValidator#requireMinERC20Balance: BALANCE_TOO_LOW");
37+
}
38+
39+
/**
40+
* @notice Validates that msg.sender has a minimum native token balance
41+
* @param _minBalance Minimum required balance
42+
*/
43+
function requireMinNativeBalance(uint256 _minBalance) external view {
44+
require(msg.sender.balance >= _minBalance, "TrailsValidator#requireMinNativeBalance: BALANCE_TOO_LOW");
45+
}
46+
47+
/**
48+
* @notice Validates that msg.sender has a minimum ERC20 allowance for a spender
49+
* @param _token ERC20 token address
50+
* @param _spender Address allowed to spend the tokens
51+
* @param _minAllowance Minimum required allowance
52+
*/
53+
function requireMinERC20Allowance(address _token, address _spender, uint256 _minAllowance) external view {
54+
uint256 allowance = IERC20(_token).allowance(msg.sender, _spender);
55+
require(allowance >= _minAllowance, "TrailsValidator#requireMinERC20Allowance: ALLOWANCE_TOO_LOW");
56+
}
57+
58+
/**
59+
* @notice Validates that msg.sender owns a specific ERC721 token
60+
* @param _token ERC721 token address
61+
* @param _tokenId Token ID to check for ownership
62+
*/
63+
function requireERC721Ownership(address _token, uint256 _tokenId) external view {
64+
address owner = IERC721(_token).ownerOf(_tokenId);
65+
require(owner == msg.sender, "TrailsValidator#requireERC721Ownership: NOT_OWNER");
66+
}
67+
68+
/**
69+
* @notice Validates that an ERC721 token owned by msg.sender is approved for a specific spender
70+
* @param _token ERC721 token address
71+
* @param _spender Address that should have approval
72+
* @param _tokenId Token ID to check for approval
73+
*/
74+
function requireERC721Approval(address _token, address _spender, uint256 _tokenId) external view {
75+
address approved = IERC721(_token).getApproved(_tokenId);
76+
require(
77+
approved == _spender || IERC721(_token).isApprovedForAll(msg.sender, _spender),
78+
"TrailsValidator#requireERC721Approval: NOT_APPROVED"
79+
);
80+
}
81+
82+
/**
83+
* @notice Validates that msg.sender has a minimum balance of an ERC1155 token
84+
* @param _token ERC1155 token address
85+
* @param _tokenId Token ID to check
86+
* @param _minBalance Minimum required balance
87+
*/
88+
function requireMinERC1155Balance(address _token, uint256 _tokenId, uint256 _minBalance) external view {
89+
uint256 balance = IERC1155(_token).balanceOf(msg.sender, _tokenId);
90+
require(balance >= _minBalance, "TrailsValidator#requireMinERC1155Balance: BALANCE_TOO_LOW");
91+
}
92+
93+
/**
94+
* @notice Validates that an ERC1155 token is approved for a specific operator by msg.sender
95+
* @param _token ERC1155 token address
96+
* @param _operator Address that should have operator approval
97+
*/
98+
function requireERC1155Approval(address _token, address _operator) external view {
99+
bool isApproved = IERC1155(_token).isApprovedForAll(msg.sender, _operator);
100+
require(isApproved, "TrailsValidator#requireERC1155Approval: NOT_APPROVED");
101+
}
102+
}

0 commit comments

Comments
 (0)