A gas-optimized smart contract implementation for payment systems that grant access to content or services, such as paywalls or premium subscriptions. The system supports multiple ERC20 tokens and different subscription durations.
- Support for multiple ERC20 tokens
- Monthly and yearly subscription tiers
- Gas-optimized implementation
- Owner-controlled token and price management
- Secure withdrawal system
- Notice: This contract does not implement auto-renewable purchase logic.
├── src/
│ └── BasicPaywallWithERC20.sol # Main contract
├── script/
│ ├── mocks
│ └── ERC20DecimalsMock.sol # ERC20 mock contract with configurable decimals
│ ├── Deploy.s.sol # Deployment script
│ └── HelperConfig.s.sol # Network configurations
├── test/
│ └── integration/
│ └── DeployTest.t.sol # Integration test files
│ └── unit/
│ └── BasicPaywallTest.t.sol # Unit test files
├── docs/
│ └── BasicPaywallWithERC20.md # Smart Contract documentation
└── Makefile # Build and deployment commands
└── foundry.toml # Foundry config
- Git
- You'll know you did it right if you can run
git --versionand see a response likegit version x.x.x
- You'll know you did it right if you can run
- Foundry
- You'll know you did it right if you can run
forge --versionand see a response likeforge 0.2.0
- You'll know you did it right if you can run
- Make
- You'll know you did it right if you can run
make --versionand see a response likeGNU Make 3.81
- You'll know you did it right if you can run
curl -L https://foundry.paradigm.xyz | bash
foundryup- Clone the repository:
git clone https://github.com/FcoGomez92/basic-paywall-with-erc20.git
cd basic-paywall-with-erc20- Install dependencies:
make allforge buildforge testforge fmtforge snapshotmake anvil- Import your deployer wallet:
cast wallet import deployer --interactive
# Enter private key when prompted
# Enter password when prompted- Create a
.envfile:
DEPLOYER_ADDRESS=<your-deployer-address>
SEPOLIA_RPC_URL=<your-sepolia-rpc-url>
ARBITRUM_RPC_URL=<your-arbitrum-rpc-url>
ETHERSCAN_API_KEY=<your-etherscan-api-key>
ARBISCAN_API_KEY=<your-arbiscan-api-key>- Configure the owner address in
script/HelperConfig.s.sol:
address constant OWNER_ACCOUNT = <your-owner-address>; // Replace with actual owner addressLocal deployment:
make deploySepolia testnet deployment:
make deploySepoliaArbitrum mainnet deployment:
make deployArbitrumThe main contract implements a subscription-based paywall system with the following key features:
purchase(address _token, PurchaseDuration duration): Purchase a subscriptionwithdrawToken(address _token, uint256 _amount): Withdraw specific amount of tokenswithdrawAll(): Withdraw all supported tokensupdatePrices(address token, uint256 _monthPrice, uint256 _yearPrice): Update subscription pricesaddSupportedToken(address token, uint256 monthPrice, uint256 yearPrice): Add new supported tokenremoveSupportedToken(address token): Remove supported token
For detailed documentation of all functions, events, errors and more, see the Contract Documentation.
The HelperConfig.s.sol contract provides configurations for:
- Local development (Anvil)
- Ethereum Sepolia Testnet
- Arbitrum Mainnet
Each network configuration includes:
- USDC and USDT addresses
- Monthly and yearly prices for each token
- Owner address
To add support for a new EVM-compatible chain, follow these steps:
- Add the RPC URL to your
.envfile:
NEW_CHAIN_RPC_URL=<your-new-chain-rpc-url>- Add the chain configuration to
foundry.toml:
[rpc_endpoints]
new_chain = "${NEW_CHAIN_RPC_URL}"
[etherscan]
new_chain = { key = "${NEW_CHAIN_EXPLORER_API_KEY}" }- Create a new configuration function in
script/HelperConfig.s.sol:
function getNewChainConfig() public pure returns (NetworkConfig memory) {
return NetworkConfig({
usdcAddress: <usdc-address-on-new-chain>,
usdtAddress: <usdt-address-on-new-chain>,
usdcMonthPrice: USDC_MONTH_PRICE,
usdtMonthPrice: USDT_MONTH_PRICE,
usdcYearPrice: USDC_YEAR_PRICE,
usdtYearPrice: USDT_YEAR_PRICE,
owner: OWNER_ACCOUNT
});
}- Add the chain ID and configuration to the constructor in
script/HelperConfig.s.sol:
uint256 constant NEW_CHAIN_ID = <your-chain-id>;
constructor() {
// ... existing configurations ...
networkConfigs[NEW_CHAIN_ID] = getNewChainConfig();
}- Add a new deployment command to the
Makefile:
deployNewChain :
@forge script script/Deploy.s.sol:Deploy --rpc-url new_chain --sender ${DEPLOYER_ADDRESS} --account deployer --broadcast --verify -vvvvNow you can deploy to the new chain using:
make deployNewChainThis project is licensed under the MIT License.
Francisco Gómez
- X: @FcoGomez92_
- GitHub: FcoGomez92