A complete token ecosystem built with Foundry that allows you to:
- Deploy a custom ERC20 token
- Create a vault contract that can receive tokens from users
- Send tokens from the vault to any wallet you choose
- Manage multiple token transfers efficiently
- Standard ERC20 token with minting capabilities
- Owner can mint new tokens
- Users can burn their own tokens
- Initial supply of 1,000,000 tokens
- Receive tokens: Users can send tokens to the vault
- Send tokens: Owner can send tokens to any wallet
- Batch transfers: Send tokens to multiple recipients in one transaction
- Emergency withdraw: Owner can withdraw all tokens of a specific type
- ETH support: Can receive and withdraw ETH
- Security: Reentrancy protection and access control
token-ecosystem/
├── src/
│ ├── MyToken.sol # Custom ERC20 token
│ └── TokenVault.sol # Token vault contract
├── script/
│ ├── DeployTokenEcosystem.s.sol # Deployment script
│ └── InteractTokenEcosystem.s.sol # Interaction script
├── test/
│ └── TokenEcosystem.t.sol # Comprehensive tests
├── foundry.toml # Foundry configuration
└── README.md # This file
- Foundry installed
- A private key for deployment
- Access to an Ethereum network (local or testnet)
forge install
forge build
forge test
First, set your private key as an environment variable:
# Windows PowerShell
$env:PRIVATE_KEY="your_private_key_here"
# Or create a .env file
echo "PRIVATE_KEY=your_private_key_here" > .env
Deploy to a local network:
# Start local node (in another terminal)
anvil
# Deploy contracts
forge script script/DeployTokenEcosystem.s.sol --rpc-url http://localhost:8545 --broadcast
Deploy to a testnet:
forge script script/DeployTokenEcosystem.s.sol --rpc-url $RPC_URL --broadcast --verify
Set the deployed contract addresses:
$env:TOKEN_ADDRESS="deployed_token_address"
$env:VAULT_ADDRESS="deployed_vault_address"
$env:RECIPIENT_1="recipient_address_1"
$env:RECIPIENT_2="recipient_address_2"
Run the interaction script:
forge script script/InteractTokenEcosystem.s.sol --rpc-url http://localhost:8545 --broadcast
Users can send tokens to the vault using the receiveTokens
function:
// First approve the vault to spend your tokens
token.approve(vaultAddress, amount);
// Then send tokens to the vault
vault.receiveTokens(tokenAddress, amount);
As the vault owner, you can send tokens to any wallet:
// Send to a single recipient
vault.sendTokens(tokenAddress, recipientAddress, amount);
// Send to multiple recipients
address[] memory recipients = [address1, address2];
uint256[] memory amounts = [amount1, amount2];
vault.sendTokensToMultiple(tokenAddress, recipients, amounts);
If needed, you can withdraw all tokens of a specific type:
vault.emergencyWithdraw(tokenAddress, recipientAddress);
mint(address to, uint256 amount)
- Mint new tokens (owner only)burn(uint256 amount)
- Burn tokens (anyone can burn their own tokens)
receiveTokens(address token, uint256 amount)
- Receive tokens from userssendTokens(address token, address to, uint256 amount)
- Send tokens to a single recipient (owner only)sendTokensToMultiple(address token, address[] recipients, uint256[] amounts)
- Send tokens to multiple recipients (owner only)emergencyWithdraw(address token, address to)
- Emergency withdraw all tokens (owner only)getTokenBalance(address token)
- Get tracked token balancegetActualTokenBalance(address token)
- Get actual token balance from contractwithdrawETH(address to, uint256 amount)
- Withdraw ETH (owner only)
- Access Control: Only the vault owner can send tokens
- Reentrancy Protection: Prevents reentrancy attacks
- Input Validation: Validates addresses and amounts
- Balance Tracking: Tracks token balances to prevent over-spending
- Emergency Functions: Emergency withdraw capabilities
The project includes comprehensive tests covering:
- Token deployment and basic functionality
- Vault operations (receive, send, batch send)
- Access control (only owner can send tokens)
- Edge cases (zero amounts, invalid addresses)
- Emergency functions
- ETH handling
Run tests with:
forge test
Run tests with verbose output:
forge test -vv
The contracts are optimized for gas efficiency:
- Uses
calldata
for read-only arrays - Minimal storage operations
- Efficient loops and calculations
- Reentrancy guard for security without excessive gas costs
After deployment, save the contract addresses:
- Token Address: The deployed MyToken contract
- Vault Address: The deployed TokenVault contract
Create a .env
file with:
PRIVATE_KEY=your_private_key_here
TOKEN_ADDRESS=deployed_token_address
VAULT_ADDRESS=deployed_vault_address
RECIPIENT_1=recipient_address_1
RECIPIENT_2=recipient_address_2
RPC_URL=your_rpc_url_here
ETHERSCAN_API_KEY=your_etherscan_api_key_here
- Compilation Errors: Make sure all dependencies are installed
- Deployment Failures: Check your private key and network connection
- Insufficient Gas: Increase gas limit for complex transactions
- Permission Errors: Ensure you're the vault owner for sending tokens
- Check the test files for usage examples
- Review the contract comments for function descriptions
- Use
forge test -vv
for detailed test output
MIT License - see LICENSE file for details.