@t402/stacks
Stacks (Bitcoin L2) implementation for T402 payments using SIP-010 fungible tokens.
Installation
pnpm add @t402/stacksOverview
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 addresses —
SP(mainnet) orST(testnet) prefixed
Network IDs
| Network | CAIP-2 ID | Chain ID | API Endpoint |
|---|---|---|---|
| Mainnet | stacks:1 | 1 | https://api.mainnet.hiro.so |
| Testnet | stacks:2147483648 | 2147483648 | https://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
- Client builds a SIP-010
transfercontract call - Client signs and broadcasts the Stacks transaction
- Transaction is included in a Stacks block (anchored to Bitcoin)
- Client returns the
txIdas proof - 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)
| Token | Network | Contract Address | Decimals |
|---|---|---|---|
| sUSDC | Mainnet | SP3Y2ZSH8P7D50B0VBTSX11S7XSG24M1VB9YFQA4K.token-susdc | 6 |
| sUSDC | Testnet | ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-susdc | 6 |
⚠️
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
| Prefix | Network | Example |
|---|---|---|
SP | Mainnet | SP3Y2ZSH8P7D50B0VBTSX11S7XSG24M1VB9YFQA4K |
ST | Testnet | ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM |
Principals can be:
- Standard —
SP.../ST...(user wallets) - Contract —
SP....<contract-name>(deployed contracts)
Error Codes
| Code | Description |
|---|---|
invalid_payload_structure | Missing txId, from, to, or amount |
network_mismatch | Transaction network doesn’t match |
amount_mismatch | Transferred amount less than required |
recipient_mismatch | Transfer recipient doesn’t match payTo |
contract_mismatch | SIP-010 contract doesn’t match asset |
transaction_not_found | Transaction ID not found |
transaction_failed | Transaction 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)