In the world of Software as a Service (SaaS) and digital licensing, automation is key. Integrating cryptocurrency payments—especially ERC-20 tokens on Layer 2 networks like Base—presents an opportunity to streamline license issuance. However, this requires a robust and reliable system to verify that a payment has been successfully completed before granting access or a license. This article will guide you through the technical steps required to validate ERC-20 payments on the Base network and automate software license issuance.
The ERC-20 Payment Verification Flow
Verifying an ERC-20 payment on a blockchain involves examining the details of a specific transaction to confirm that the correct token, the proper amount, and the intended recipient were transferred. This process is fundamental to ensuring that only valid payments trigger license issuance, preventing fraud or errors.
Detailed Steps for Payment Validation
1. Obtaining and Verifying the Transaction Receipt
The first step is to obtain the transaction receipt of the transaction claimed to contain the payment. This is typically done via an RPC call to a Base node. The receipt contains vital information about the outcome of the transaction.
Once obtained, you must check the status field. A status of 1 (or 0x1 in hexadecimal) indicates that the transaction executed successfully. A status of 0 (or 0x0) means the transaction failed or reverted, and in that case, it must not be considered a valid payment.
Additionally, the receipt will contain the original blockHash, blockNumber, and transactionHash, which are useful for future reference and to ensure you are examining the correct receipt.
2. Decoding ERC-20 Transfer Logs
ERC-20 transactions involving token transfers emit a Transfer event. This event is recorded in the logs of the transaction receipt. To validate the payment, we need to find and decode this log.
The Transfer event has a specific signature, identified by its topic hash: 0xddf252ad1be2c89b69c2b068fc378fa9b47e5684e2469eb0534e8cd886dca178. This hash corresponds to Transfer(address,address,uint256).
Inside the Transfer event log, you will find:
-
address: The contract address of the ERC-20 token that emitted the event. -
topics: An array of up to four indexed values. ForTransfer,topics[0]is the event signature hash,topics[1]is the sender's address (from), andtopics[2]is the recipient's address (to). -
data: Contains the transferred token value (value), encoded as auint256.
from web3 import Web3
# Assume 'receipt' is the result of eth_getTransactionReceipt
# and 'w3' is a Web3 instance connected to Base RPC.
TRANSFER_EVENT_TOPIC = Web3.keccak(text="Transfer(address,address,uint256)").hex()
for log in receipt.logs:
# The first topic is always the event hash
if log.topics and log.topics[0].hex() == TRANSFER_EVENT_TOPIC:
# The token contract address is 'log.address'
token_address = log.address
# Decode 'from' and 'to' from the indexed topics
sender_address = w3.to_checksum_address(log.topics[1].hex())
recipient_address = w3.to_checksum_address(log.topics[2].hex())
# Decode the 'data' value
# The value is a uint256, which is the only non-indexed parameter
amount_wei = int(log.data.hex(), 16) # Convert from hex to int
# Here you would have token_address, sender_address, recipient_address, amount_wei
# to perform subsequent verifications.
break # Assume a single relevant Transfer event for simplicity
This is an illustrative outline and is not copied directly from any repository.
3. Verifying Key Transfer Parameters
With the decoded data, you can now perform critical validations:
- Token Address (Contract Address): Compare
token_address(the address of the ERC-20 contract that emitted the event) with the expected token address (e.g., USDC, DAI). If it does not match, the payment is invalid. - Final Recipient (Recipient Address): Compare
recipient_address(the address where tokens were transferred) with your service payment address (REMI_PAYMENT_ADDRESS). It is crucial that tokens were sent to the correct account. - Minimum Amount (Minimum Amount): Compare
amount_wei(the transferred value in the smallest token unit, like wei for ETH or gwei for other 18-decimal tokens) with the minimum amount required for the license. Make sure to account for token decimals when performing this check.
If all these verifications succeed, you can consider the ERC-20 payment valid.
Automatic License Issuance and Audit Log
Once the payment has been successfully validated, the system can proceed with automatic software license issuance. This generally involves:
- License Generation: Creating a license record containing details such as the buyer's email address, issue date, expiration date, and optionally, a unique license key.
- Storage: Saving this license in a persistent database.
- Notification: Sending the license to the user via email or another method.
It is equally important to maintain a detailed audit log. Every step of the process, from receiving the validation request to issuing the license, must be recorded. This includes the transactionHash, sender address, token, amount, recipient address, and the outcome of the license issuance. An audit log is invaluable for debugging, dispute resolution, and traceability.
REMI Enterprise Suite: A Concrete Implementation
REMI Enterprise Suite is a modular multi-agent AI framework designed for secure, traceable enterprise environments. Within its architecture, REMI offers a concrete implementation of this payment validation and license issuance flow.
The remi_tx_validator.py component handles low-level logic. It connects to the Base network via an RPC URL (BASE_RPC_URL), retrieves the transaction receipt, decodes the Transfer logs, and verifies the token address, minimum amount, and final recipient (REMI_PAYMENT_ADDRESS).
The license_service.py, a FastAPI backend, interacts with remi_tx_validator.py to process license issuance requests. Once remi_tx_validator.py confirms payment validity, license_service.py proceeds to issue and validate licenses, tracking their expiration and allowing email searches.
Licenses and audit logs are stored in MongoDB via the db.py module, with the database name configurable via REMI_DB_NAME. The app.py interface, built with Streamlit, serves as a command center for operations and license management.
REMI also supports automated license issuance via Stripe webhooks for fiat payments, and uses GitHub automation to create issues using a bot token. LLM access is configurable via LLM_HOST (e.g., a local Ollama endpoint with Llama3). The suite is designed for deployment with Dockerfile, docker-compose.yml, and render.yaml, requiring Python 3.10+ and environment variables such as MONGO_URI, REMI_PAYMENT_ADDRESS, BASE_RPC_URL, REMI_API_KEY, and LLM_HOST.
Important Considerations and Limitations
Reorganization Depth (Reorg Depth)
Blockchains can experience "reorgs" where a previously accepted block chain is replaced by an alternative. This means a transaction that appeared finalized in a block could be reversed if that block is reorganized out of the main chain.
To mitigate this risk, it is best practice to wait a certain number of confirmation blocks (known as reorg depth) before considering a payment final and issuing the license. On networks like Base, waiting between 6 and 10 additional blocks after the block containing the transaction significantly reduces the probability of a reorg affecting your payment. This introduces a slight delay in license issuance but increases security.
RPC Provider Trust (RPC Trust)
The validity of your verification depends entirely on the information provided by the RPC node you connect to. If the RPC node is malicious or compromised, it could supply incorrect or manipulated data, leading to erroneous validation.
You have several options:
- Third-party RPC nodes: Convenient and scalable, but require trust in the provider. Research the reputation and reliability of the service.
- Self-hosted nodes: Offer maximum control and trust since you operate them. However, they carry maintenance, synchronization, and resource overhead.
The choice depends on your security requirements, budget, and operational expertise.
Idempotency
It is crucial that your system is idempotent—meaning processing the same transactionHash multiple times always produces the same result without issuing duplicate licenses. Once a transactionHash has been validated and a license issued, any subsequent attempt to validate that same transactionHash should be detected and rejected to prevent double issuance. Storing the transactionHash in the audit log along with the license status helps implement this.
Conclusion
Automating software license issuance by validating ERC-20 payments on networks like Base offers significant efficiency. By following a rigorous verification process—from transaction receipt confirmation and Transfer log decoding to key payment parameter validation—you can build a robust system. Recognizing and mitigating inherent blockchain limitations, such as reorg depth and RPC trust, is essential for the reliability of your solution. Implementations like REMI Enterprise Suite demonstrate how these principles can be applied to create secure, traceable enterprise systems.
Try It Out
- Live Demo: https://remi-enterprise-suite.onrender.com (free tier, may take about a minute to wake up)
- Source Code (MIT): https://github.com/Jramone3/REMI_Enterprise_Suite
- Need commercial terms for your enterprise? Check the commercial license or request it through the repository.
Top comments (1)
tr.ee/dev-to