This project uses Soldeer for dependency management.
-
Clone the repository
git clone https://github.com/spaceandtimelabs/sxt-token.git cd sxt-token -
Install dependencies using Soldeer
forge soldeer install
This can be deployed by running the following
- Set environment variables, preferrably using an env file
.envfile# .env file # This variable must be the url of the RPC node ETH_RPC_URL= # Uncomment the following line when using a keystore account #ETH_KEYSTORE_ACCOUNT= # Uncomment the following line when using a private key #PRIVATE_KEY=
source .env - Dry run the transaction using any of the following (or variations). NOTE: this will ensure that the sum of the recipient amounts is equal to the expected total supply value.
- Use a Ledger hardware wallet
forge script script/SpaceAndTime.s.sol --rpc-url=$ETH_RPC_URL --ledger - Use a Trezor hardware wallet
forge script script/SpaceAndTime.s.sol --rpc-url=$ETH_RPC_URL --trezor - Use the foundry keystore, which can be set up using
cast wallet. Be sure to set theETH_KEYSTORE_ACCOUNTenv variable.forge script script/SpaceAndTime.s.sol --rpc-url=$ETH_RPC_URL - Use a private key
forge script script/SpaceAndTime.s.sol --rpc-url=$ETH_RPC_URL --private-key=$PRIVATE_KEY
- Use a Ledger hardware wallet
- Add
--broadcastto actually run the deployment.
SpaceAndTime.sol is the ERC20 contract for the SXT token, generated using the OpenZeppelin Wizard. To regenerate the contract, follow these steps:
- visit the OpenZeppelin Wizard
- select
ERC20 - set name to
Space and Time - set symbol to
SXT - set premint to
5000000000(5 billion) - check
Pausable - check
Permit - select
Voteswith block number so Uses voting durations expressed as block numbers. - select
RolesunderAccess Control
SXTDeployer.sol is a helper contract for deploying the SXT token. It is used to deploy the token and mint the initial supply to the deployer.
The project includes comprehensive testing for the Merkle tree implementation used in the SXTTokenDistributor contract. The tests are designed to verify the functionality of the Merkle tree with various dataset sizes, from small trees (10 accounts) to extremely large trees (up to 100,000 accounts).
By default, the tests run with 10 accounts for quick testing and CI:
forge test --match-test "MerkleTreeTest"To run tests with a specific number of accounts, use the MERKLE_TEST_ACCOUNTS environment variable:
# Test with 100 accounts
MERKLE_TEST_ACCOUNTS=100 forge test --match-test "MerkleTreeTest"
# Test with 1,000 accounts
MERKLE_TEST_ACCOUNTS=1000 forge test --match-test "MerkleTreeTest"
# Test with 10,000 accounts
MERKLE_TEST_ACCOUNTS=10000 forge test --match-test "MerkleTreeTest"
# Test with 100,000 accounts
MERKLE_TEST_ACCOUNTS=100000 forge test --match-test "MerkleTreeTest"For extremely large trees (50,000+ accounts), the test uses a sample-based approach to generate the Merkle root and verify claims for a subset of accounts. This approach allows testing the core functionality without running into memory or gas limitations.
The Merkle tree tests implement several optimization strategies to handle large datasets:
-
Configurable Account Numbers: The number of test accounts can be configured via the
MERKLE_TEST_ACCOUNTSenvironment variable. -
Adaptive Testing Approach:
- For small trees (≤ 10,000 accounts): Standard testing with full Merkle tree generation
- For large trees (10,000-50,000 accounts): Optimized batch processing
- For extremely large trees (> 50,000 accounts): Sample-based Merkle tree approach
-
Gas Optimizations:
vm.txGasPrice(0)to bypass gas restrictions for large batch processing- Batch processing with configurable batch sizes
- Fixed token amounts to reduce gas usage
-
Memory Optimizations:
- Chunk-based processing to avoid stack too deep errors
- Sample-based approach for extremely large datasets
The tests cover various scenarios including:
- Basic claim functionality
- Multiple claims
- Duplicate claim prevention
- Invalid proof handling
- Random sampling for large trees
- Claiming all tokens in a tree
These tests ensure that the SXTTokenDistributor contract correctly implements the Merkle tree verification logic and can handle distributions to a large number of recipients.
The project uses several tools to ensure code quality:
The .solhint.json file contains configuration for the Solidity linter. Notable decisions include:
-
not-rely-on-time: This rule is disabled because the
SXTDistributorWithDeadlinecontract intentionally usesblock.timestampfor its core functionality (deadline checks). The security implications are documented in the contract, and the usage is considered safe for this use case since:- The time window for claims is expected to be long (days/weeks)
- A few seconds/minutes of manipulation would not significantly impact the contract's functionality
- There is no financial incentive for miners to manipulate the timestamp for this contract
-
imports-order: This rule is disabled to allow for more flexible organization of imports.
Slither security analysis is used to identify potential vulnerabilities. The slither-disable-next-line timestamp comments are used in the SXTDistributorWithDeadline contract to acknowledge the intentional use of block.timestamp for time-based logic.
The project maintains 100% test coverage for all contracts, which can be verified by running:
bash jobs/check_coverage.shThis ensures that all code paths are tested and functioning as expected.