Core Protocol Flow

1. Wallet Initialization

Users obtain a forwarding address through Grindery's account abstraction layer. This is a deterministic smart contract wallet address derived from:
  • Authentication method (email/OAuth/SMS via Web3Auth or similar)
  • Salt for uniqueness
  • Factory contract address
The forwarding address gets associated with a client wallet address through an on-chain transaction:
plain text
BillingLedger.associateWallet(forwardingAddress, clientAddress)
This creates a one-way binding - the forwarding address can only deposit to this specific client's ledger balance.

2. Funding Flow

When USDC (or other tokens) arrive at the forwarding address:
  1. Detection: Cache service monitors Transfer events via RPC subscription
  1. Auto-deposit: Cache service calls depositFromForwarding() on behalf of user
  1. Conversion: Non-USDC tokens get swapped to USDC at current rates via DEX aggregator
  1. Credit: User's balance in Billing Ledger increases by deposited amount
The Billing Ledger maintains internal accounting:
solidity
mapping(address => uint256) public balances; // client => available balance mapping(address => uint256) public locked; // client => locked for pending charges

3. Authorization Commitment

User creates authorization by signing an EIP-712 structured message:
solidity
struct Authorization { address provider; uint256 maxPerCharge; uint256 totalLimit; uint256 rateLimit; // max charges per hour uint256 expiry; }
This gets submitted on-chain:
solidity
BillingLedger.commitAuthorization( Authorization auth, bytes signature )
The contract stores this as:
solidity
mapping(bytes32 => AuthState) authorizations; // authId => state mapping(address => bytes32[]) userAuths; // user => authIds

4. Charge Request Structure

Providers create charges by packing data into bytes:
solidity
struct ChargeRequest { address user; uint256 amount; uint256 timestamp; bytes32 authId; uint256 nonce; bytes metadata; // optional attribution data }
Provider signs this with their private key, creating a charge tuple:
plain text
(chargeRequest, signature)

5. Off-Chain Authorization Check

Before providing service, provider queries the cache service:
plain text
GET /authorize?user=0x...&amount=100&taskId=...
Cache service validates against:
  • Cached authorization rules
  • Current balance minus pending charges
  • Rate limits (Redis sliding window)
  • Previous charges for idempotency
Response time: <10ms

6. Batch Settlement Process

Providers accumulate signed charges in local buffer. Settlement triggers on:
  • 30 second timer
  • 1000 charges accumulated
  • Manual trigger
Batch submission:
solidity
BillingLedger.settleBatch( ChargeRequest[] requests, bytes[] signatures )
Smart contract validates each charge:
solidity
for (uint i = 0; i < requests.length; i++) { // 1. Verify provider signature require(ecrecover(hash, sig) == auth.provider); // 2. Verify authorization exists and is valid require(auth.expiry > block.timestamp); require(auth.totalUsed + amount <= auth.totalLimit); // 3. Verify user has balance require(balances[user] >= amount); // 4. Verify timestamp freshness require(block.timestamp - timestamp < 900); // 15 min // 5. Verify provider nonce require(nonces[provider] == nonce); // 6. Update balances balances[user] -= amount; balances[provider] += amount; nonces[provider] = nonce // 7. Emit settlement event emit ChargeSettled(user, provider, amount, nonce); }

7. Dispute Mechanism

Within dispute window (2 hours), users can reverse charges:
solidity
BillingLedger.dispute( ChargeRequest request, bytes providerSignature )
Contract verifies:
  1. Charge was correctly signed by provider
  1. Still within dispute window
  1. Not previously disputed
  1. User initiated dispute
Then reverses:
solidity
balances[user] += request.amount; balances[provider] -= request.amount; disputes[chargeHash] = true; emit ChargeDisputed(user, provider, amount, nonce);

8. Multi-Hop Attribution

For agent-to-agent billing, metadata contains attribution chain:
json
{ "principal": "0xUserWallet", "chain": [ {"agent": "0xAssistant", "task": "orchestrate"}, {"agent": "0xSubAgent", "task": "execute"} ], "rootTaskId": "abc-123" }
Each agent in chain:
  1. Receives charge from upstream
  1. Issues charge to downstream
  1. Profit = upstream charge - downstream costs
Billing Ledger nets all balances in single settlement.

Cache Service Architecture

The cache service runs as a high-performance middleware:
Components:
  • Redis: Authorization cache, rate limits, pending charges
  • PostgreSQL: Transaction logs, analytics
  • WebSocket: Real-time balance updates
  • RPC Node: Blockchain monitoring
Key Operations:
  1. Auto-deposit: Monitors forwarding addresses, submits deposits
  1. Balance tracking: Maintains real-time balance with pending charges
  1. Charge aggregation: Collects charges from multiple providers
  1. Batch optimization: Groups charges by gas efficiency
  1. Dispute monitoring: Tracks dispute windows and outcomes

Security Considerations

Double-spend prevention: Nonce provides idempotency Signature validation: EIP-712 prevents replay attacks
Time bounds: 15-minute timestamp window prevents stale charges Rate limiting: Per-authorization rate limits prevent abuse Atomic settlement: All-or-nothing batch processing

Gas Optimization

  • Lazy wallet deployment: Forwarding addresses only deploy on first transaction
  • Batch settlement: 1000 charges = 1 transaction (~300k gas)
  • Packed encoding: Charge data packed into bytes32 where possible
  • Event logs: Detailed data in logs, minimal on-chain storage
This architecture achieves:
  • Latency: <10ms authorization
  • Cost: <$0.001 per charge at scale
  • Throughput: 10,000+ charges/second off-chain
  • Settlement: ~1000 charges per on-chain transaction