Architecture & Implementation Roadmap for Passet Hub
Prepared by Manus AI | November 2025
Goal: Enable Web2-like user experiences on Passet Hub where users can interact with blockchain applications without holding PAS tokens for gas fees.
Gasless transactions represent a fundamental shift in blockchain user experience by abstracting away the complexity of gas fees and transaction management. Through the implementation of the ERC-2771 meta-transaction standard, users can sign transactions off-chain while a relayer service submits them on-chain and covers the gas costs. This approach eliminates one of the primary barriers to mainstream blockchain adoption: the requirement for users to understand and manage native tokens for transaction fees.
This report presents a comprehensive architecture and implementation roadmap for deploying gasless transactions on Passet Hub, Polkadot's EVM-compatible smart contract platform. The implementation leverages proven patterns from Ethereum while adapting to PolkaVM's unique constraints, including constructor storage limitations and multi-dimensional resource metering. The proposed system includes three core components: a simplified forwarder contract optimized for PolkaVM, a relay service for transaction submission and gas sponsorship, and recipient contracts that extract the real sender from forwarded transactions.
Previous testing on Passet Hub has validated that simplified forwarder contracts can successfully deploy and execute gasless transactions. The key to success is avoiding storage operations in constructors and using inline logic with immutable variables.
| Component | Status | Timeline |
|---|---|---|
| Forwarder Contract | Validated | Weeks 3-4 |
| Relay Service | Design Ready | Weeks 5-6 |
| Integration & Testing | Planned | Weeks 7-8 |
Traditional blockchain interactions require users to hold native tokens (like PAS on Passet Hub or ETH on Ethereum) to pay for transaction gas fees. This creates significant friction for new users who must first acquire tokens through exchanges, understand wallet management, and maintain sufficient balances for transactions. For many use cases, this complexity is a dealbreaker that prevents mainstream adoption.
Meta-transactions solve this problem by introducing a trusted intermediary called a relayer. Instead of submitting transactions directly to the blockchain, users sign transaction requests off-chain using standard cryptographic signatures. The relayer then submits these signed requests to a forwarder contract, paying the gas fees on behalf of the user. The forwarder verifies the signature and executes the requested transaction, with the target contract able to extract the real sender from the forwarded call.
| Benefit | Description | Impact |
|---|---|---|
| Frictionless Onboarding | Users can interact immediately without acquiring PAS | Dramatically reduced barrier to entry |
| Web2-Like UX | No gas fee management or wallet funding required | Familiar experience for mainstream users |
| Flexible Sponsorship | Projects can sponsor specific actions or user segments | Enables freemium models and user acquisition |
| Batch Operations | Relayers can batch multiple user requests | Improved efficiency and reduced costs |
| Security | Cryptographic signatures prevent unauthorized actions | Same security as direct transactions |
Allow users to mint NFTs without holding PAS tokens. The project sponsors the minting gas fees as a user acquisition cost, enabling seamless onboarding for collectors.
Enable in-game actions without requiring players to understand blockchain or manage gas. The game studio sponsors all transaction fees, providing a traditional gaming experience.
Allow users to post content, like, comment, and interact without gas fees. The platform sponsors transactions for active users, similar to Web2 social networks.
Enable first-time users to try DeFi protocols without initial token purchases. Projects sponsor initial transactions to reduce onboarding friction.
ERC-2771 is an Ethereum standard that defines a secure protocol for native meta-transactions. It specifies the interface between forwarder contracts and recipient contracts, ensuring interoperability across different implementations. The standard has been widely adopted in the Ethereum ecosystem and is supported by major infrastructure providers including OpenZeppelin, Biconomy, and Gelato.
| Component | Role | Responsibilities |
|---|---|---|
| Forwarder | Trusted intermediary | Verify signatures, execute calls, manage nonces |
| Recipient | Target contract | Extract real sender, trust forwarder, execute logic |
| Relayer | Off-chain service | Submit transactions, pay gas, batch requests |
| Signer | End user | Sign requests off-chain, specify actions |
ERC-2771 forward requests include the following fields, ensuring complete specification of the desired transaction while preventing replay attacks and unauthorized execution:
struct ForwardRequest {
address from; // Signer address (must match signature)
address to; // Target contract address
uint256 value; // Native token amount to forward
uint256 gas; // Gas limit for the call
uint256 nonce; // Unique identifier to prevent replay
uint256 deadline; // Expiration timestamp
bytes data; // Encoded function call data
}
Signatures use EIP-712 typed structured data, providing a secure and user-friendly signing experience. The signature covers all request fields along with a domain separator that includes the forwarder address and chain ID, preventing cross-contract and cross-chain replay attacks.
// EIP-712 Domain
struct EIP712Domain {
string name; // "SimpleForwarder"
string version; // "1"
uint256 chainId; // 420420422 (Passet Hub)
address verifyingContract; // Forwarder address
}
// Typed data hash
hash = keccak256(abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
structHash
))
The ERC-2771 standard specifies that the forwarder appends the real sender address to the calldata when forwarding requests. Recipient contracts inherit from ERC2771Context, which overrides msg.sender to extract this appended address. This approach maintains backward compatibility while enabling gasless functionality.
function _msgSender() internal view returns (address) {
if (msg.sender == trustedForwarder && msg.data.length >= 20) {
// Extract sender from last 20 bytes of calldata
return address(bytes20(msg.data[msg.data.length - 20:]));
}
return msg.sender; // Fallback to direct calls
}
OpenZeppelin provides a production-ready ERC2771Forwarder implementation that has been extensively audited and battle-tested across multiple networks. The implementation includes advanced features such as batch execution with refunding, comprehensive security checks, and gas-efficient nonce management. However, this implementation uses storage operations in the constructor and complex inheritance patterns that are incompatible with PolkaVM's current constraints.
| Feature | Purpose | Implementation |
|---|---|---|
| Nonce Management | Prevent replay attacks | Per-user sequential nonces |
| Deadline Validation | Prevent stale requests | Timestamp-based expiration |
| Signature Verification | Ensure authenticity | EIP-712 typed data signatures |
| Target Trust Check | Prevent unauthorized calls | isTrustedForwarder() validation |
| Value Validation | Prevent value mismatch | Strict msg.value checking |
Biconomy provides a widely-used forwarder implementation that powers gasless transactions for numerous production applications. The contract includes additional features such as domain separator caching and optimized gas usage. However, like OpenZeppelin's implementation, it relies on constructor storage operations and is not compatible with PolkaVM without significant modifications.
Gelato offers a comprehensive relay infrastructure including forwarder contracts and a managed relay service. Their implementation focuses on high-throughput scenarios and includes advanced features like conditional execution and fee payment in ERC-20 tokens. The forwarder contract follows similar patterns to OpenZeppelin but with additional complexity that makes PolkaVM adaptation challenging.
All standard Ethereum forwarder implementations fail to deploy on Passet Hub due to constructor storage operations. Previous testing confirmed that even production-grade, audited contracts from OpenZeppelin and Biconomy cannot be deployed without significant refactoring.
A simplified forwarder contract using only immutable variables and inline logic successfully deployed and operated on Passet Hub. This pattern maintains all core security features while working within PolkaVM constraints.
The most significant challenge for deploying forwarder contracts on Passet Hub is the prohibition of storage operations (sstore and sload) in constructors. This limitation affects standard patterns used throughout the Ethereum ecosystem, including OpenZeppelin's base contracts. The root cause appears to be fundamental to PolkaVM's current implementation and affects all tested contract patterns.
contract StandardForwarder {
string private _name; // Storage variable
string private _version; // Storage variable
constructor(string memory name, string memory version) {
_name = name; // sstore - FAILS
_version = version; // sstore - FAILS
}
}
contract SimpleForwarder {
// Use constants instead of storage
string private constant NAME = "SimpleForwarder";
string private constant VERSION = "1";
// No constructor needed, or empty constructor
constructor() {}
// Compute values on-demand
function DOMAIN_SEPARATOR() public view returns (bytes32) {
return keccak256(abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(NAME)),
keccak256(bytes(VERSION)),
block.chainid,
address(this)
));
}
}
PolkaVM enforces stricter limits than standard EVM, with a maximum contract code size of approximately 100KB and a call stack depth limit of 5. These constraints require careful contract design and optimization, particularly when considering the inclusion of libraries and inherited contracts.
Unlike Ethereum's single gas metric, PolkaVM uses three separate resource dimensions: ref_time (computation), proof_size (state proof size), and storage_deposit (state bloat management). The Ethereum RPC proxy automatically maps these to a single gas value for compatibility, but relay services should be aware of this multi-dimensional model for accurate cost estimation.
Passet Hub requires accounts to maintain a minimum balance (existential deposit) to remain active. This affects gasless transaction systems in two ways: new user accounts must receive sufficient initial balance, and forwarded transactions must account for this minimum when handling value transfers. The relay service should handle existential deposit requirements transparently.
The following implementation has been validated on Passet Hub and successfully handles gasless transactions while working within all PolkaVM constraints:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract SimpleForwarder {
// Constants (no storage in constructor)
string private constant NAME = "SimpleForwarder";
string private constant VERSION = "1";
// Nonce tracking (only storage that's allowed)
mapping(address => uint256) private _nonces;
// Request structure
struct ForwardRequest {
address from;
address to;
uint256 value;
uint256 gas;
uint256 nonce;
uint256 deadline;
bytes data;
}
// Events
event Executed(address indexed from, address indexed to, bool success);
// Domain separator (computed on-demand)
function DOMAIN_SEPARATOR() public view returns (bytes32) {
return keccak256(abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(NAME)),
keccak256(bytes(VERSION)),
block.chainid,
address(this)
));
}
// Get nonce for address
function getNonce(address from) public view returns (uint256) {
return _nonces[from];
}
// Verify request
function verify(
ForwardRequest calldata req,
bytes calldata signature
) public view returns (bool) {
if (block.timestamp > req.deadline) return false;
if (req.nonce != _nonces[req.from]) return false;
bytes32 digest = _getDigest(req);
address signer = _recoverSigner(digest, signature);
return signer == req.from && signer != address(0);
}
// Execute request
function execute(
ForwardRequest calldata req,
bytes calldata signature
) public payable returns (bool success) {
require(verify(req, signature), "Invalid request");
require(msg.value == req.value, "Value mismatch");
_nonces[req.from]++;
(success,) = req.to.call{gas: req.gas, value: req.value}(
abi.encodePacked(req.data, req.from)
);
emit Executed(req.from, req.to, success);
}
// Internal: Get typed data hash
function _getDigest(ForwardRequest calldata req) internal view returns (bytes32) {
bytes32 structHash = keccak256(abi.encode(
keccak256("ForwardRequest(address from,address to,uint256 value,uint256 gas,uint256 nonce,uint256 deadline,bytes data)"),
req.from,
req.to,
req.value,
req.gas,
req.nonce,
req.deadline,
keccak256(req.data)
));
return keccak256(abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
structHash
));
}
// Internal: Recover signer
function _recoverSigner(
bytes32 digest,
bytes calldata signature
) internal pure returns (address) {
require(signature.length == 65, "Invalid signature length");
bytes32 r;
bytes32 s;
uint8 v;
assembly {
let ptr := signature.offset
r := calldataload(ptr)
s := calldataload(add(ptr, 32))
v := byte(0, calldataload(add(ptr, 64)))
}
return ecrecover(digest, v, r, s);
}
}
The gasless transaction system consists of three primary components that work together to enable seamless user experiences. Each component has distinct responsibilities and interfaces, allowing for modular development and deployment.
| Component | Technology | Responsibilities |
|---|---|---|
| Forwarder Contract | Solidity 0.8.28 | Signature verification, nonce management, call forwarding |
| Relay Service | Node.js / Python | Request validation, gas estimation, transaction submission |
| Client SDK | TypeScript | Request creation, EIP-712 signing, relay communication |
The relay service is a critical off-chain component that bridges users and the blockchain. It must be highly available, secure, and efficient to provide a good user experience while managing gas costs effectively.
| Module | Function | Key Features |
|---|---|---|
| API Server | HTTP endpoints for request submission | Rate limiting, authentication, request validation |
| Validator | Off-chain request verification | Signature check, nonce validation, deadline verification |
| Gas Estimator | Predict transaction costs | PolkaVM-specific estimation, safety margins |
| Whitelist Manager | Access control | User/contract authorization, rate limits per user |
| Transaction Submitter | On-chain transaction management | Nonce management, retry logic, confirmation tracking |
| Monitoring | System health and metrics | Gas usage tracking, error rates, latency monitoring |
Authorize specific user addresses to use gasless transactions. Useful for beta programs, premium users, or verified accounts. Can include per-user rate limits and daily gas budgets.
Authorize specific target contracts for gasless calls. Useful for sponsoring specific actions (e.g., NFT minting) while preventing abuse through unauthorized contract calls.
Authorize specific function selectors on whitelisted contracts. Provides fine-grained control over which actions are sponsored (e.g., allow mint() but not transfer()).
Combine multiple whitelist strategies for maximum flexibility. For example, allow all users to call specific functions on specific contracts, with higher limits for verified users.
The client SDK abstracts the complexity of creating and signing forward requests, providing a simple API for dApp developers. It handles EIP-712 signature generation, nonce management, and communication with the relay service.
// Example SDK usage
import { GaslessProvider } from '@kitdot/gasless';
const provider = new GaslessProvider({
relayUrl: 'https://relay.example.com',
forwarderAddress: '0x...',
signer: ethers.signer
});
// Execute gasless transaction
const tx = await provider.sendTransaction({
to: nftContract.address,
data: nftContract.interface.encodeFunctionData('mint')
});
await tx.wait();
The following 8-week roadmap provides a structured approach to implementing gasless transactions on Passet Hub. Each phase builds upon the previous one, with clear milestones and deliverables.
| Task | Duration | Deliverable |
|---|---|---|
| Environment Setup | 2 days | Configured Hardhat project with Passet Hub network |
| Basic Contract Testing | 2 days | Deployed test contracts, documented constraints |
| Simplified Forwarder Prototype | 3 days | Working prototype with signature verification |
| Meta-Transaction Flow Test | 2 days | End-to-end test of sponsored transaction |
| Documentation | 1 day | Research findings and PolkaVM behavior notes |
| Task | Duration | Deliverable |
|---|---|---|
| Forwarder Contract Development | 3 days | Complete SimpleForwarder.sol with all features |
| Nonce Management | 2 days | Robust nonce system preventing replay attacks |
| ERC2771Context Implementation | 2 days | Simplified context contract for recipients |
| Sample Contracts | 2 days | NFT and token contracts using ERC2771Context |
| Unit Tests | 3 days | Comprehensive test suite with >90% coverage |
| Task | Duration | Deliverable |
|---|---|---|
| API Server Setup | 2 days | Express/Fastify server with REST endpoints |
| Request Validation | 2 days | Off-chain signature and nonce verification |
| Gas Estimation Module | 3 days | PolkaVM-aware gas estimation with safety margins |
| Whitelist System | 2 days | Database-backed whitelist with rate limiting |
| Transaction Submission | 2 days | Reliable submission with retry logic |
| Monitoring | 1 day | Metrics dashboard and alerting |
| Task | Duration | Deliverable |
|---|---|---|
| Client SDK Development | 3 days | TypeScript SDK with EIP-712 signing |
| Frontend Demo | 3 days | React app demonstrating gasless NFT minting |
| End-to-End Testing | 3 days | Complete user flow testing on Passet Hub |
| Performance Optimization | 2 days | Optimized gas costs and response times |
| Documentation | 3 days | Complete developer docs and tutorials |
| Phase | Duration | Key Deliverables |
|---|---|---|
| Phase 1: Research | Weeks 1-2 | Validated prototype, PolkaVM documentation |
| Phase 2: Contracts | Weeks 3-4 | Production forwarder, sample contracts, tests |
| Phase 3: Relay Service | Weeks 5-6 | Backend service, gas estimation, whitelist |
| Phase 4: Integration | Weeks 7-8 | Client SDK, demo app, documentation |
| Total Duration | 8 Weeks | Complete gasless transaction system |
Comprehensive unit tests ensure each component functions correctly in isolation. The test suite should achieve >90% code coverage and validate all edge cases and failure modes.
| Test Category | Test Cases | Coverage |
|---|---|---|
| Signature Verification | Valid signature accepted, invalid rejected, wrong signer rejected | Critical |
| Nonce Management | Sequential nonces enforced, replay prevented, per-user isolation | Critical |
| Deadline Validation | Expired requests rejected, valid deadlines accepted | High |
| Value Handling | Value forwarded correctly, mismatch rejected | High |
| Gas Limits | Gas limit respected, out-of-gas handled | Medium |
| Domain Separator | Correct chain ID, correct contract address | Medium |
| Test Category | Test Cases | Coverage |
|---|---|---|
| Request Validation | Valid requests accepted, invalid rejected, malformed rejected | Critical |
| Whitelist Enforcement | Authorized users accepted, unauthorized rejected, rate limits enforced | Critical |
| Gas Estimation | Accurate estimates, safety margins applied, edge cases handled | High |
| Transaction Submission | Successful submission, retry on failure, nonce management | High |
| Error Handling | Network errors handled, contract errors reported, timeouts handled | High |
Integration tests validate that all components work together correctly, testing the complete flow from user signature to on-chain execution.
Security tests attempt to exploit potential vulnerabilities and ensure the system is resilient against attacks.
| Attack Vector | Test | Expected Result |
|---|---|---|
| Replay Attack | Submit same signed request twice | Second submission rejected (nonce consumed) |
| Signature Manipulation | Modify request after signing | Signature verification fails |
| Cross-Chain Replay | Use signature from different chain | Domain separator mismatch, rejected |
| Gas Griefing | Request excessive gas limit | Relay caps gas or rejects request |
| Value Mismatch | Send different msg.value than requested | Forwarder rejects with value mismatch error |
| Unauthorized Target | Call non-whitelisted contract | Relay rejects based on whitelist |
Performance tests ensure the system can handle production load and identify optimization opportunities.
| Requirement | Details |
|---|---|
| Development Tools | Node.js 18+, Hardhat, TypeScript |
| Passet Hub Access | RPC endpoint, funded wallet with PAS tokens |
| Infrastructure | Server for relay service, database for whitelist |
| Monitoring | Logging system, metrics dashboard |
npx hardhat compile
npx hardhat ignition deploy ./ignition/modules/SimpleForwarder.js --network passetHub
Save the deployed forwarder address for relay service configuration.
npx hardhat ignition deploy ./ignition/modules/GaslessNFT.js --network passetHub
Pass the forwarder address as a constructor parameter.
// .env file
PASSET_HUB_RPC=https://testnet-passet-hub-eth-rpc.polkadot.io
FORWARDER_ADDRESS=0x...
RELAY_PRIVATE_KEY=0x...
WHITELIST_ENABLED=true
DATABASE_URL=postgresql://...
npm install
npm run build
npm start
Verify the service is running and accepting requests.
Ongoing maintenance ensures the system remains reliable and cost-effective. Key maintenance tasks include:
| Task | Frequency | Purpose |
|---|---|---|
| Monitor Gas Costs | Daily | Track spending, optimize if needed |
| Review Whitelist | Weekly | Add/remove users, adjust limits |
| Check Error Rates | Daily | Identify and fix issues |
| Update Dependencies | Monthly | Security patches, new features |
| Backup Database | Daily | Prevent data loss |
| Performance Review | Monthly | Optimize bottlenecks |