Reference@t402/stacks

@t402/stacks

Stacks (Bitcoin L2) implementation for T402 payments using SIP-010 fungible tokens.

Installation

pnpm add @t402/stacks

Overview

The @t402/stacks package enables T402 payments on Stacks, a Bitcoin Layer 2 with Proof-of-Transfer consensus. It uses the exact-direct scheme with SIP-010 fungible token contracts written in Clarity.

Key features:

  • Bitcoin settlement — Stacks transactions are anchored to Bitcoin
  • SIP-010 standard — Clarity-based fungible token interface
  • Hiro API — transaction verification via Hiro’s Stacks API
  • Principal addressesSP (mainnet) or ST (testnet) prefixed

Network IDs

NetworkCAIP-2 IDChain IDAPI Endpoint
Mainnetstacks:11https://api.mainnet.hiro.so
Testnetstacks:21474836482147483648https://api.testnet.hiro.so

Client Usage

import { createExactDirectStacksClient } from '@t402/stacks/exact-direct/client'
 
const client = createExactDirectStacksClient({
  address: 'SP3Y2ZSH8P7D50B0VBTSX11S7XSG24M1VB9YFQA4K',
  signAndBroadcast: async (txOptions) => {
    // Use Hiro Wallet or Leather
    const result = await wallet.signAndBroadcast(txOptions)
    return result.txId
  }
})
 
const payload = await client.createPaymentPayload(2, requirements)

Server Usage

import { registerExactDirectStacksServer } from '@t402/stacks/exact-direct/server'
 
const server = registerExactDirectStacksServer({
  apiUrl: 'https://api.mainnet.hiro.so'
})

Facilitator Usage

import { createExactDirectStacksFacilitator } from '@t402/stacks/exact-direct/facilitator'
 
const facilitator = createExactDirectStacksFacilitator({
  apiUrl: 'https://api.mainnet.hiro.so'
})
 
const result = await facilitator.verify(payload, requirements)
const settled = await facilitator.settle(payload, requirements)

PaymentRequirements

const requirements = {
  scheme: 'exact-direct',
  network: 'stacks:1',
  amount: '1000000', // 1 sUSDC (6 decimals)
  asset: 'SP3Y2ZSH8P7D50B0VBTSX11S7XSG24M1VB9YFQA4K.token-susdc', // SIP-010 contract
  payTo: 'SP1MERCHANT...',
  maxTimeoutSeconds: 600
}

Payment Flow

  1. Client builds a SIP-010 transfer contract call
  2. Client signs and broadcasts the Stacks transaction
  3. Transaction is included in a Stacks block (anchored to Bitcoin)
  4. Client returns the txId as proof
  5. Facilitator verifies via Hiro API

Stacks transactions take ~10 minutes for Bitcoin anchoring, but the facilitator can verify mempool transactions for faster confirmation.

Token Addresses (SIP-010 Contracts)

TokenNetworkContract AddressDecimals
sUSDCMainnetSP3Y2ZSH8P7D50B0VBTSX11S7XSG24M1VB9YFQA4K.token-susdc6
sUSDCTestnetST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-susdc6
⚠️

SIP-010 contract addresses use the format {principal}.{contract-name}. The full contract address must be specified in the asset field.

Payload Structure

interface ExactDirectStacksPayload {
  txId: string;              // Stacks transaction ID
  from: string;              // sender principal
  to: string;                // recipient principal
  amount: string;            // amount in smallest units
  contractAddress: string;   // full SIP-010 contract address
}

SIP-010 Interface

The SIP-010 transfer function in Clarity:

(define-public (transfer
  (amount uint)
  (sender principal)
  (recipient principal)
  (memo (optional (buff 34)))
)

Address Format

PrefixNetworkExample
SPMainnetSP3Y2ZSH8P7D50B0VBTSX11S7XSG24M1VB9YFQA4K
STTestnetST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM

Principals can be:

  • StandardSP... / ST... (user wallets)
  • ContractSP....<contract-name> (deployed contracts)

Error Codes

CodeDescription
invalid_payload_structureMissing txId, from, to, or amount
network_mismatchTransaction network doesn’t match
amount_mismatchTransferred amount less than required
recipient_mismatchTransfer recipient doesn’t match payTo
contract_mismatchSIP-010 contract doesn’t match asset
transaction_not_foundTransaction ID not found
transaction_failedTransaction exists but aborted

Example: Full Integration

import { t402Client } from '@t402/fetch'
import { registerExactDirectStacksClient } from '@t402/stacks/exact-direct/client'
 
const client = new t402Client()
registerExactDirectStacksClient(client, {
  address: wallet.stxAddress,
  signAndBroadcast: wallet.signAndBroadcast
})
 
const response = await client.fetch('https://api.example.com/premium')

Go SDK

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