Reference@t402/polkadot

@t402/polkadot

Polkadot Asset Hub implementation for T402 payments using system assets.

Installation

pnpm add @t402/polkadot

Overview

The @t402/polkadot package enables T402 payments on Polkadot Asset Hub (formerly Statemint) using native system assets. It uses the exact-direct scheme — the client submits an asset transfer extrinsic directly on-chain.

Key features:

  • System assets — USDT (Asset ID 1984) on Asset Hub
  • Parachain native — runs on Polkadot’s Asset Hub parachain
  • Subscan verification — transaction verification via Subscan indexer
  • Multi-network — supports Polkadot, Kusama, and Westend Asset Hubs

Network IDs

NetworkCAIP-2 IDSS58 PrefixRPC Endpoint
Polkadot Asset Hubpolkadot:68d56f15f85d3136970ec16946040bc10wss://polkadot-asset-hub-rpc.polkadot.io
Kusama Asset Hubpolkadot:48239ef607d7928874027a43a67689209727dfb3d3dc5e5b03a39bdc2eda771a2wss://kusama-asset-hub-rpc.polkadot.io
Westend Asset Hubpolkadot:e143f23803ac50e8f6f8e62695d1ce9e42wss://westend-asset-hub-rpc.polkadot.io

CAIP-2 identifiers for Polkadot networks use the first 32 characters of the genesis block hash. RPC uses WebSocket (wss://), not HTTP.

Client Usage

import { createExactDirectPolkadotClient } from '@t402/polkadot/exact-direct/client'
 
const client = createExactDirectPolkadotClient({
  address: '15oF4uVJwmo4TdGW7VfQxNLavjCXviqWrb9sGQgMT9kNNQjL',
  signAndSubmitExtrinsic: async (call, network) => {
    // Use your Polkadot wallet (Polkadot.js, Talisman, SubWallet)
    const result = await wallet.signAndSubmit(call)
    return {
      extrinsicHash: result.hash,
      extrinsicId: `${result.blockHash}-${result.index}`
    }
  }
})
 
const payload = await client.createPaymentPayload(2, requirements)

Server Usage

import { registerExactDirectPolkadotServer } from '@t402/polkadot/exact-direct/server'
 
const server = registerExactDirectPolkadotServer({
  rpcUrl: 'wss://polkadot-asset-hub-rpc.polkadot.io'
})

Facilitator Usage

import { createExactDirectPolkadotFacilitator } from '@t402/polkadot/exact-direct/facilitator'
 
const facilitator = createExactDirectPolkadotFacilitator({
  indexerUrl: 'https://assethub-polkadot.api.subscan.io'
})
 
const result = await facilitator.verify(payload, requirements)
const settled = await facilitator.settle(payload, requirements)

PaymentRequirements

const requirements = {
  scheme: 'exact-direct',
  network: 'polkadot:68d56f15f85d3136970ec16946040bc1',
  amount: '1000000', // 1 USDT (6 decimals)
  asset: '1984', // Asset ID for USDT on Asset Hub
  payTo: '15oF4uVJwmo4TdGW7VfQxNLavjCXviqWrb9sGQgMT9kNNQjL',
  maxTimeoutSeconds: 300
}

Payment Flow

  1. Client builds an assets.transfer extrinsic for the specified asset ID
  2. Client signs and submits to the Asset Hub parachain
  3. Extrinsic is included in a block
  4. Client returns the extrinsic hash and ID as proof
  5. Facilitator verifies via Subscan API

Token Addresses

TokenNetworkAsset IDDecimals
USDTPolkadot Asset Hub19846
USDTKusama Asset Hub19846
Test USDTWestend Asset Hub19846
⚠️

On Polkadot Asset Hub, tokens are identified by numeric Asset IDs, not contract addresses. USDT is Asset ID 1984 across all Asset Hub networks.

Payload Structure

interface ExactDirectPolkadotPayload {
  extrinsicHash: string;  // extrinsic hash
  extrinsicId: string;    // format: "{blockHash}-{extrinsicIndex}"
  from: string;           // sender SS58 address
  to: string;             // recipient SS58 address
  amount: string;         // amount in smallest units
  assetId: number;        // Asset ID (1984 for USDT)
}

Address Format

Polkadot uses SS58 encoding with network-specific prefixes:

NetworkSS58 PrefixExample
Polkadot015oF4uVJwmo4...
Kusama2HNZata7iMY...
Westend425GrwvaEF5...

The same public key produces different SS58 addresses on different networks.

Error Codes

CodeDescription
invalid_payload_structureMissing extrinsicHash or extrinsicId
network_mismatchExtrinsic network doesn’t match
amount_mismatchTransferred amount less than required
recipient_mismatchTransfer recipient doesn’t match payTo
asset_id_mismatchAsset ID doesn’t match requirements
extrinsic_not_foundExtrinsic not found on-chain
extrinsic_failedExtrinsic exists but failed

Example: Full Integration

import { t402Client } from '@t402/fetch'
import { registerExactDirectPolkadotClient } from '@t402/polkadot/exact-direct/client'
 
const client = new t402Client()
registerExactDirectPolkadotClient(client, {
  address: wallet.address,
  signAndSubmitExtrinsic: wallet.signAndSubmit
})
 
const response = await client.fetch('https://api.example.com/premium')

Go SDK

import "github.com/t402-io/t402/sdks/go/mechanisms/polkadot"
 
// Client
client := polkadot.NewExactDirectPolkadotScheme(signer, &polkadot.Config{})
payload, err := client.CreatePaymentPayload(ctx, requirements)
 
// Facilitator
facilitator := polkadot.NewExactDirectPolkadotFacilitator(indexerURL)
result, err := facilitator.Verify(ctx, payload, requirements)