Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@x402/avm

Package Overview
Dependencies
Maintainers
2
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@x402/avm - npm Package Compare versions

Comparing version
2.10.0
to
2.11.0
+47
dist/esm/chunk-MWOLCZUZ.mjs
var __defProp = Object.defineProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// src/constants.ts
var ALGORAND_MAINNET_CAIP2 = "algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=";
var ALGORAND_TESTNET_CAIP2 = "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=";
var CAIP2_NETWORKS = [ALGORAND_MAINNET_CAIP2, ALGORAND_TESTNET_CAIP2];
var ALGORAND_MAINNET_GENESIS_HASH = "wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=";
var ALGORAND_TESTNET_GENESIS_HASH = "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=";
var USDC_MAINNET_ASA_ID = "31566704";
var USDC_TESTNET_ASA_ID = "10458941";
var USDC_DECIMALS = 6;
var USDC_CONFIG = {
[ALGORAND_MAINNET_CAIP2]: {
asaId: USDC_MAINNET_ASA_ID,
name: "USDC",
decimals: USDC_DECIMALS
},
[ALGORAND_TESTNET_CAIP2]: {
asaId: USDC_TESTNET_ASA_ID,
name: "USDC",
decimals: USDC_DECIMALS
}
};
var MAX_REASONABLE_FEE_PER_TXN = 5e3;
function maxReasonableGroupFee(groupSize) {
return MAX_REASONABLE_FEE_PER_TXN * groupSize;
}
export {
__export,
ALGORAND_MAINNET_CAIP2,
ALGORAND_TESTNET_CAIP2,
CAIP2_NETWORKS,
ALGORAND_MAINNET_GENESIS_HASH,
ALGORAND_TESTNET_GENESIS_HASH,
USDC_MAINNET_ASA_ID,
USDC_TESTNET_ASA_ID,
USDC_DECIMALS,
USDC_CONFIG,
MAX_REASONABLE_FEE_PER_TXN,
maxReasonableGroupFee
};
//# sourceMappingURL=chunk-MWOLCZUZ.mjs.map
{"version":3,"sources":["../../src/constants.ts"],"sourcesContent":["/**\n * Algorand Network Constants for x402 AVM Implementation\n *\n * CAIP-2 Network Identifiers use the format: algorand:<genesis-hash-base64>\n * Genesis hashes uniquely identify Algorand networks.\n */\n\n// ============================================================================\n// CAIP-2 Network Identifiers (V2)\n// ============================================================================\n\n/**\n * CAIP-2 network identifier for Algorand Mainnet\n * Format: algorand:<genesis-hash-base64>\n */\nexport const ALGORAND_MAINNET_CAIP2 = \"algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=\";\n\n/**\n * CAIP-2 network identifier for Algorand Testnet\n * Format: algorand:<genesis-hash-base64>\n */\nexport const ALGORAND_TESTNET_CAIP2 = \"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=\";\n\n/**\n * All supported CAIP-2 network identifiers\n */\nexport const CAIP2_NETWORKS = [ALGORAND_MAINNET_CAIP2, ALGORAND_TESTNET_CAIP2] as const;\n\n// ============================================================================\n// Genesis Hashes\n// ============================================================================\n\n/**\n * Algorand Mainnet genesis hash (base64 encoded)\n */\nexport const ALGORAND_MAINNET_GENESIS_HASH = \"wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=\";\n\n/**\n * Algorand Testnet genesis hash (base64 encoded)\n */\nexport const ALGORAND_TESTNET_GENESIS_HASH = \"SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=\";\n\n// ============================================================================\n// USDC ASA (Algorand Standard Asset) Configuration\n// ============================================================================\n\n/**\n * USDC ASA ID on Algorand Mainnet\n *\n * @see https://algoexplorer.io/asset/31566704\n */\nexport const USDC_MAINNET_ASA_ID = \"31566704\";\n\n/**\n * USDC ASA ID on Algorand Testnet\n *\n * @see https://testnet.algoexplorer.io/asset/10458941\n */\nexport const USDC_TESTNET_ASA_ID = \"10458941\";\n\n/**\n * USDC decimals (same across all networks)\n */\nexport const USDC_DECIMALS = 6;\n\n/**\n * USDC configuration per network\n */\nexport const USDC_CONFIG: Record<string, { asaId: string; name: string; decimals: number }> = {\n [ALGORAND_MAINNET_CAIP2]: {\n asaId: USDC_MAINNET_ASA_ID,\n name: \"USDC\",\n decimals: USDC_DECIMALS,\n },\n [ALGORAND_TESTNET_CAIP2]: {\n asaId: USDC_TESTNET_ASA_ID,\n name: \"USDC\",\n decimals: USDC_DECIMALS,\n },\n};\n\n// ============================================================================\n// Transaction Limits\n// ============================================================================\n\n/**\n * Maximum reasonable fee per transaction in microAlgos (5000 µAlgo).\n *\n * Algorand transaction fees are calculated as:\n * fee = max(current_fee_per_byte * transaction_size_in_bytes, min_fee)\n *\n * Under normal (non-congested) conditions, current_fee_per_byte is 0,\n * so fee = min_fee = 1000 µAlgo (0.001 ALGO).\n *\n * During network congestion, fees can rise. This constant is set to 5x\n * the minimum fee (5000 µAlgo) as a reasonable upper bound per transaction.\n *\n * For fee payer transactions that cover an entire group via fee pooling,\n * use `maxReasonableGroupFee(groupSize)` which multiplies this per-txn\n * cap by the number of transactions in the group.\n */\nexport const MAX_REASONABLE_FEE_PER_TXN = 5000;\n\n/**\n * Calculates the maximum reasonable fee for a fee payer transaction\n * that covers an entire atomic group via fee pooling.\n *\n * @param groupSize - Number of transactions in the atomic group\n * @returns Maximum acceptable fee in microAlgos\n */\nexport function maxReasonableGroupFee(groupSize: number): number {\n return MAX_REASONABLE_FEE_PER_TXN * groupSize;\n}\n\n// Address validation: use isValidAddress() from @algorandfoundation/algokit-utils/common\n// Address length: use ALGORAND_ADDRESS_LENGTH from @algorandfoundation/algokit-utils/common\n"],"mappings":";;;;;;;AAeO,IAAM,yBAAyB;AAM/B,IAAM,yBAAyB;AAK/B,IAAM,iBAAiB,CAAC,wBAAwB,sBAAsB;AAStE,IAAM,gCAAgC;AAKtC,IAAM,gCAAgC;AAWtC,IAAM,sBAAsB;AAO5B,IAAM,sBAAsB;AAK5B,IAAM,gBAAgB;AAKtB,IAAM,cAAiF;AAAA,EAC5F,CAAC,sBAAsB,GAAG;AAAA,IACxB,OAAO;AAAA,IACP,MAAM;AAAA,IACN,UAAU;AAAA,EACZ;AAAA,EACA,CAAC,sBAAsB,GAAG;AAAA,IACxB,OAAO;AAAA,IACP,MAAM;AAAA,IACN,UAAU;AAAA,EACZ;AACF;AAsBO,IAAM,6BAA6B;AASnC,SAAS,sBAAsB,WAA2B;AAC/D,SAAO,6BAA6B;AACtC;","names":[]}
import {
encodeTransaction,
isTestnetNetwork
} from "./chunk-QEY4CIDB.mjs";
import {
USDC_CONFIG
} from "./chunk-MWOLCZUZ.mjs";
// src/exact/client/scheme.ts
import { AlgorandClient } from "@algorandfoundation/algokit-utils/algorand-client";
import {
Transaction,
encodeTransactionRaw,
groupTransactions,
makeEmptyTransactionSigner
} from "@algorandfoundation/algokit-utils/transact";
import { microAlgo } from "@algorandfoundation/algokit-utils/amount";
var ExactAvmScheme = class {
/**
* Creates a new ExactAvmScheme instance.
*
* @param signer - The AVM signer for client operations
* @param config - Optional configuration for Algod client
*/
constructor(signer, config) {
this.signer = signer;
this.config = config;
this.scheme = "exact";
}
/**
* Creates a payment payload for the Exact scheme.
*
* Constructs an atomic transaction group with:
* - Optional fee payer transaction (if feePayer specified in requirements.extra)
* - ASA transfer transaction to payTo address
*
* Uses TransactionComposer for automatic suggested params, group ID assignment,
* and fee pooling. For sponsored (gasless) transactions, exact fees are calculated
* from actual encoded transaction sizes using the protocol fee formula:
* fee = max(fee_per_byte × txn_size_in_bytes, min_fee)
*
* @param x402Version - The x402 protocol version
* @param paymentRequirements - The payment requirements
* @returns Promise resolving to a payment payload result
*/
async createPaymentPayload(x402Version, paymentRequirements) {
const { amount, asset, payTo, network, extra } = paymentRequirements;
const algorandClient = this.getAlgorandClient(network);
const assetId = this.getAssetId(asset, network);
const feePayer = extra?.feePayer;
let paymentIndex = 0;
const emptySigner = makeEmptyTransactionSigner();
const composer = algorandClient.newGroup();
if (feePayer) {
composer.addPayment({
sender: feePayer,
receiver: feePayer,
amount: microAlgo(0),
note: `x402-fee-payer-${Date.now()}`,
signer: emptySigner
});
paymentIndex = 1;
}
composer.addAssetTransfer({
sender: this.signer.address,
receiver: payTo,
assetId: BigInt(assetId),
amount: BigInt(amount),
staticFee: feePayer ? microAlgo(0) : void 0,
// 0 fee when fee payer covers
note: `x402-payment-v${x402Version}-${Date.now()}`,
signer: emptySigner
});
const built = await composer.build();
let transactions = built.transactions.map((tws) => tws.txn);
if (feePayer) {
const sp = await algorandClient.getSuggestedParams();
const feePerByte = Number(sp.fee);
const minFee = Number(sp.minFee);
let totalGroupFee = BigInt(0);
for (const txn of transactions) {
const txnSize = encodeTransactionRaw(txn).length;
const txnFee = feePerByte > 0 ? Math.max(feePerByte * txnSize, minFee) : minFee;
totalGroupFee += BigInt(txnFee);
}
const ungrouped = transactions.map((txn, i) => {
const fee = i === 0 ? totalGroupFee : BigInt(0);
return new Transaction({ ...txn, fee, group: void 0 });
});
transactions = groupTransactions(ungrouped);
}
const encodedTxns = transactions.map((txn) => encodeTransactionRaw(txn));
const clientIndexes = transactions.map((txn, i) => txn.sender.toString() === this.signer.address ? i : -1).filter((i) => i !== -1);
const signedTxns = await this.signer.signTransactions(encodedTxns, clientIndexes);
const paymentGroup = encodedTxns.map((txnBytes, i) => {
const signedTxn = signedTxns[i];
if (signedTxn) {
return encodeTransaction(signedTxn);
}
return encodeTransaction(txnBytes);
});
const payload = {
paymentGroup,
paymentIndex
};
return {
x402Version,
payload
};
}
/**
* Creates or retrieves an AlgorandClient for the given network.
*
* @param network - Network identifier (CAIP-2 format)
* @returns AlgorandClient instance
*/
getAlgorandClient(network) {
if (this.config?.algorandClient) {
return this.config.algorandClient;
}
if (this.config?.algodUrl) {
return AlgorandClient.fromConfig({
algodConfig: {
server: this.config.algodUrl,
token: this.config.algodToken ?? ""
}
});
}
return isTestnetNetwork(network) ? AlgorandClient.testNet() : AlgorandClient.mainNet();
}
/**
* Gets the asset ID from the requirements or defaults to USDC
*
* @param asset - Asset identifier from requirements
* @param network - Network identifier
* @returns Asset ID as string
*/
getAssetId(asset, network) {
if (/^\d+$/.test(asset)) {
return asset;
}
const usdcConfig = USDC_CONFIG[network];
if (usdcConfig) {
return usdcConfig.asaId;
}
return asset;
}
};
export {
ExactAvmScheme
};
//# sourceMappingURL=chunk-OMKK3QVS.mjs.map
{"version":3,"sources":["../../src/exact/client/scheme.ts"],"sourcesContent":["/**\n * AVM Client Scheme for Exact Payment Protocol\n *\n * Creates atomic transaction groups for Algorand ASA transfers.\n * Uses AlgorandClient and TransactionComposer from algokit-utils v10\n * for transaction construction, fee pooling, and group management.\n */\n\nimport { AlgorandClient } from \"@algorandfoundation/algokit-utils/algorand-client\";\nimport {\n Transaction,\n encodeTransactionRaw,\n groupTransactions,\n makeEmptyTransactionSigner,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport { microAlgo } from \"@algorandfoundation/algokit-utils/amount\";\nimport type {\n PaymentRequirements,\n SchemeNetworkClient,\n PaymentPayloadResult,\n} from \"@x402/core/types\";\nimport type { ClientAvmSigner, ClientAvmConfig } from \"../../signer\";\nimport type { ExactAvmPayloadV2 } from \"../../types\";\nimport { encodeTransaction } from \"../../utils\";\nimport { USDC_CONFIG } from \"../../constants\";\nimport { isTestnetNetwork } from \"../../utils\";\n\n/**\n * AVM client implementation for the Exact payment scheme.\n *\n * Creates atomic transaction groups with ASA transfers for x402 payments.\n * Supports optional fee payer transactions for gasless payments.\n */\nexport class ExactAvmScheme implements SchemeNetworkClient {\n readonly scheme = \"exact\";\n\n /**\n * Creates a new ExactAvmScheme instance.\n *\n * @param signer - The AVM signer for client operations\n * @param config - Optional configuration for Algod client\n */\n constructor(\n private readonly signer: ClientAvmSigner,\n private readonly config?: ClientAvmConfig,\n ) {}\n\n /**\n * Creates a payment payload for the Exact scheme.\n *\n * Constructs an atomic transaction group with:\n * - Optional fee payer transaction (if feePayer specified in requirements.extra)\n * - ASA transfer transaction to payTo address\n *\n * Uses TransactionComposer for automatic suggested params, group ID assignment,\n * and fee pooling. For sponsored (gasless) transactions, exact fees are calculated\n * from actual encoded transaction sizes using the protocol fee formula:\n * fee = max(fee_per_byte × txn_size_in_bytes, min_fee)\n *\n * @param x402Version - The x402 protocol version\n * @param paymentRequirements - The payment requirements\n * @returns Promise resolving to a payment payload result\n */\n async createPaymentPayload(\n x402Version: number,\n paymentRequirements: PaymentRequirements,\n ): Promise<PaymentPayloadResult> {\n const { amount, asset, payTo, network, extra } = paymentRequirements;\n\n const algorandClient = this.getAlgorandClient(network);\n\n // Get asset ID (from requirements or default USDC)\n const assetId = this.getAssetId(asset, network);\n\n // Get fee payer address from extra if provided\n const feePayer = extra?.feePayer as string | undefined;\n let paymentIndex = 0;\n\n // Use an empty signer for building — we sign manually after\n // (fee payer txns stay unsigned for the facilitator to sign)\n const emptySigner = makeEmptyTransactionSigner();\n\n // Build the transaction group using TransactionComposer\n const composer = algorandClient.newGroup();\n\n if (feePayer) {\n // First pass: add fee payer with a placeholder fee.\n // The actual fee will be recalculated after build() using exact transaction sizes.\n composer.addPayment({\n sender: feePayer,\n receiver: feePayer,\n amount: microAlgo(0),\n note: `x402-fee-payer-${Date.now()}`,\n signer: emptySigner,\n });\n paymentIndex = 1;\n }\n\n composer.addAssetTransfer({\n sender: this.signer.address,\n receiver: payTo,\n assetId: BigInt(assetId),\n amount: BigInt(amount),\n staticFee: feePayer ? microAlgo(0) : undefined, // 0 fee when fee payer covers\n note: `x402-payment-v${x402Version}-${Date.now()}`,\n signer: emptySigner,\n });\n\n // Build transactions (assigns group ID, suggested params, fees)\n const built = await composer.build();\n let transactions = built.transactions.map(tws => tws.txn);\n\n // For sponsored transactions: recalculate the fee payer's fee using\n // exact encoded sizes of all transactions in the group.\n // Algorand fee formula: fee = max(fee_per_byte × txn_size, min_fee)\n //\n // After changing fees, the group ID must be recomputed because it is\n // derived from each transaction's encoded bytes (which include the fee).\n if (feePayer) {\n const sp = await algorandClient.getSuggestedParams();\n const feePerByte = Number(sp.fee);\n const minFee = Number(sp.minFee);\n\n // Calculate exact fee for each transaction based on its actual encoded size\n let totalGroupFee = BigInt(0);\n for (const txn of transactions) {\n const txnSize = encodeTransactionRaw(txn).length;\n const txnFee = feePerByte > 0 ? Math.max(feePerByte * txnSize, minFee) : minFee;\n totalGroupFee += BigInt(txnFee);\n }\n\n // Strip group ID, set correct fees, then re-group.\n // groupTransactions() requires group to be absent, computes the new group hash,\n // and returns new Transaction objects with the correct group ID.\n const ungrouped = transactions.map((txn, i) => {\n const fee = i === 0 ? totalGroupFee : BigInt(0);\n return new Transaction({ ...txn, fee, group: undefined });\n });\n transactions = groupTransactions(ungrouped);\n }\n\n // Encode all transactions to raw bytes\n const encodedTxns = transactions.map(txn => encodeTransactionRaw(txn));\n\n // Determine which transactions the client should sign\n const clientIndexes = transactions\n .map((txn, i) => (txn.sender.toString() === this.signer.address ? i : -1))\n .filter(i => i !== -1);\n\n // Sign client's transactions\n const signedTxns = await this.signer.signTransactions(encodedTxns, clientIndexes);\n\n // Build payment group with signed/unsigned transactions\n const paymentGroup: string[] = encodedTxns.map((txnBytes, i) => {\n const signedTxn = signedTxns[i];\n if (signedTxn) {\n return encodeTransaction(signedTxn);\n }\n // Return unsigned transaction for facilitator to sign\n return encodeTransaction(txnBytes);\n });\n\n const payload: ExactAvmPayloadV2 = {\n paymentGroup,\n paymentIndex,\n };\n\n return {\n x402Version,\n payload: payload as unknown as Record<string, unknown>,\n };\n }\n\n /**\n * Creates or retrieves an AlgorandClient for the given network.\n *\n * @param network - Network identifier (CAIP-2 format)\n * @returns AlgorandClient instance\n */\n private getAlgorandClient(network: string): AlgorandClient {\n if (this.config?.algorandClient) {\n return this.config.algorandClient;\n }\n if (this.config?.algodUrl) {\n return AlgorandClient.fromConfig({\n algodConfig: {\n server: this.config.algodUrl,\n token: this.config.algodToken ?? \"\",\n },\n });\n }\n // Auto-detect network\n return isTestnetNetwork(network) ? AlgorandClient.testNet() : AlgorandClient.mainNet();\n }\n\n /**\n * Gets the asset ID from the requirements or defaults to USDC\n *\n * @param asset - Asset identifier from requirements\n * @param network - Network identifier\n * @returns Asset ID as string\n */\n private getAssetId(asset: string, network: string): string {\n // If asset is already a numeric string, use it directly\n if (/^\\d+$/.test(asset)) {\n return asset;\n }\n\n // Try to get from USDC config\n const usdcConfig = USDC_CONFIG[network];\n if (usdcConfig) {\n return usdcConfig.asaId;\n }\n\n // Default to the asset as-is (might be an ASA ID)\n return asset;\n }\n}\n"],"mappings":"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","names":[]}
import {
ALGORAND_MAINNET_GENESIS_HASH,
ALGORAND_TESTNET_CAIP2,
ALGORAND_TESTNET_GENESIS_HASH
} from "./chunk-MWOLCZUZ.mjs";
// src/utils.ts
import {
decodeTransaction as decodeUnsignedTxn,
decodeSignedTransaction as decodeSignedTxn
} from "@algorandfoundation/algokit-utils/transact";
import { isValidAddress } from "@algorandfoundation/algokit-utils/common";
import { convertToTokenAmount, numberToDecimalString } from "@x402/core/utils";
import { Address, encodeAddress, decodeAddress } from "@algorandfoundation/algokit-utils/common";
function encodeTransaction(txn) {
return Buffer.from(txn).toString("base64");
}
function decodeTransaction(encoded) {
return new Uint8Array(Buffer.from(encoded, "base64"));
}
function decodeSignedTransaction(encoded) {
const bytes = decodeTransaction(encoded);
return decodeSignedTxn(bytes);
}
function decodeUnsignedTransaction(encoded) {
const bytes = decodeTransaction(encoded);
return decodeUnsignedTxn(bytes);
}
function isValidAlgorandAddress(address) {
return isValidAddress(address);
}
function getSenderFromTransaction(txnBytes, isSigned = true) {
if (isSigned) {
const signedTxn = decodeSignedTxn(txnBytes);
return signedTxn.txn.sender.toString();
}
const txn = decodeUnsignedTxn(txnBytes);
return txn.sender.toString();
}
function convertFromTokenAmount(atomicAmount, decimals) {
const amount = BigInt(atomicAmount);
const divisor = BigInt(10 ** decimals);
const intPart = amount / divisor;
const decPart = amount % divisor;
if (decPart === BigInt(0)) {
return intPart.toString();
}
const decStr = decPart.toString().padStart(decimals, "0");
const trimmedDec = decStr.replace(/0+$/, "");
return `${intPart}.${trimmedDec}`;
}
function getNetworkFromCaip2(caip2) {
if (!caip2.startsWith("algorand:")) {
return null;
}
const genesisHash = caip2.slice("algorand:".length);
if (genesisHash === ALGORAND_MAINNET_GENESIS_HASH) {
return "mainnet";
}
if (genesisHash === ALGORAND_TESTNET_GENESIS_HASH) {
return "testnet";
}
return null;
}
function isAlgorandNetwork(network) {
return network.startsWith("algorand:");
}
function isTestnetNetwork(network) {
return network === ALGORAND_TESTNET_CAIP2;
}
function getGenesisHashFromTransaction(txn) {
if (!txn.genesisHash) {
throw new Error("Transaction does not have a genesis hash");
}
return Buffer.from(txn.genesisHash).toString("base64");
}
function validateGroupId(txns) {
if (txns.length <= 1) {
return true;
}
let expectedGroupId = null;
for (const txnBytes of txns) {
const txn = decodeUnsignedTxn(txnBytes);
const groupId = txn.group ? Buffer.from(txn.group).toString("base64") : null;
if (expectedGroupId === null) {
expectedGroupId = groupId;
} else if (groupId !== expectedGroupId) {
return false;
}
}
return true;
}
function getTransactionId(signedTxnBytes) {
const signedTxn = decodeSignedTxn(signedTxnBytes);
return signedTxn.txn.txId();
}
function hasSignature(signedTxnBytes) {
const signedTxn = decodeSignedTxn(signedTxnBytes);
return signedTxn.sig !== void 0 || signedTxn.lsig !== void 0 || signedTxn.msig !== void 0;
}
export {
encodeTransaction,
decodeTransaction,
decodeSignedTransaction,
decodeUnsignedTransaction,
isValidAlgorandAddress,
getSenderFromTransaction,
convertFromTokenAmount,
getNetworkFromCaip2,
isAlgorandNetwork,
isTestnetNetwork,
getGenesisHashFromTransaction,
validateGroupId,
getTransactionId,
hasSignature,
convertToTokenAmount
};
//# sourceMappingURL=chunk-QEY4CIDB.mjs.map
{"version":3,"sources":["../../src/utils.ts"],"sourcesContent":["/**\n * AVM (Algorand) Utilities for x402 Payment Protocol\n *\n * Provides utility functions for Algod client creation, transaction encoding/decoding,\n * address validation, and network identification.\n */\n\nimport {\n decodeTransaction as decodeUnsignedTxn,\n decodeSignedTransaction as decodeSignedTxn,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport { isValidAddress } from \"@algorandfoundation/algokit-utils/common\";\nimport {\n ALGORAND_MAINNET_GENESIS_HASH,\n ALGORAND_TESTNET_GENESIS_HASH,\n ALGORAND_TESTNET_CAIP2,\n} from \"./constants\";\n\n/**\n * Encodes transaction bytes to base64 string\n *\n * @param txn - Transaction bytes (Uint8Array)\n * @returns Base64 encoded string\n */\nexport function encodeTransaction(txn: Uint8Array): string {\n return Buffer.from(txn).toString(\"base64\");\n}\n\n/**\n * Decodes a base64 encoded transaction to bytes\n *\n * @param encoded - Base64 encoded transaction string\n * @returns Transaction bytes (Uint8Array)\n */\nexport function decodeTransaction(encoded: string): Uint8Array {\n return new Uint8Array(Buffer.from(encoded, \"base64\"));\n}\n\n/**\n * Decodes a signed transaction from base64 msgpack\n *\n * @param encoded - Base64 encoded signed transaction\n * @returns Decoded signed transaction object\n */\nexport function decodeSignedTransaction(encoded: string) {\n const bytes = decodeTransaction(encoded);\n return decodeSignedTxn(bytes);\n}\n\n/**\n * Decodes an unsigned transaction from base64 msgpack\n *\n * @param encoded - Base64 encoded unsigned transaction\n * @returns Decoded transaction object\n */\nexport function decodeUnsignedTransaction(encoded: string) {\n const bytes = decodeTransaction(encoded);\n return decodeUnsignedTxn(bytes);\n}\n\n/**\n * Validates an Algorand address\n *\n * Uses isValidAddress from algokit-utils which performs full checksum validation.\n *\n * @param address - The address to validate\n * @returns True if the address is valid\n */\nexport function isValidAlgorandAddress(address: string): boolean {\n return isValidAddress(address);\n}\n\n/**\n * Gets the sender address from a transaction (signed or unsigned)\n *\n * @param txnBytes - Transaction bytes\n * @param isSigned - Whether the transaction is signed (default: true)\n * @returns Sender address string\n */\nexport function getSenderFromTransaction(txnBytes: Uint8Array, isSigned: boolean = true): string {\n if (isSigned) {\n const signedTxn = decodeSignedTxn(txnBytes);\n return signedTxn.txn.sender.toString();\n }\n const txn = decodeUnsignedTxn(txnBytes);\n return txn.sender.toString();\n}\n\n// Re-export from core for backward compatibility\nexport { convertToTokenAmount, numberToDecimalString } from \"@x402/core/utils\";\n\n/**\n * Converts atomic units to decimal amount\n *\n * @param atomicAmount - Amount in atomic units (string or bigint)\n * @param decimals - Number of decimal places\n * @returns Decimal amount as a string\n */\nexport function convertFromTokenAmount(atomicAmount: string | bigint, decimals: number): string {\n const amount = BigInt(atomicAmount);\n const divisor = BigInt(10 ** decimals);\n const intPart = amount / divisor;\n const decPart = amount % divisor;\n\n if (decPart === BigInt(0)) {\n return intPart.toString();\n }\n\n const decStr = decPart.toString().padStart(decimals, \"0\");\n // Remove trailing zeros\n const trimmedDec = decStr.replace(/0+$/, \"\");\n return `${intPart}.${trimmedDec}`;\n}\n\n/**\n * Gets the network type from a CAIP-2 identifier\n *\n * @param caip2 - CAIP-2 network identifier\n * @returns Network type (\"mainnet\" | \"testnet\") or null if unknown\n */\nexport function getNetworkFromCaip2(caip2: string): \"mainnet\" | \"testnet\" | null {\n if (!caip2.startsWith(\"algorand:\")) {\n return null;\n }\n\n const genesisHash = caip2.slice(\"algorand:\".length);\n\n if (genesisHash === ALGORAND_MAINNET_GENESIS_HASH) {\n return \"mainnet\";\n }\n if (genesisHash === ALGORAND_TESTNET_GENESIS_HASH) {\n return \"testnet\";\n }\n\n return null;\n}\n\n/**\n * Checks if a network identifier is an Algorand network\n *\n * @param network - Network identifier (CAIP-2 format)\n * @returns True if the network is an Algorand network\n */\nexport function isAlgorandNetwork(network: string): boolean {\n return network.startsWith(\"algorand:\");\n}\n\n/**\n * Checks if a network identifier is a testnet\n *\n * @param network - Network identifier (CAIP-2 format)\n * @returns True if the network is a testnet\n */\nexport function isTestnetNetwork(network: string): boolean {\n return network === ALGORAND_TESTNET_CAIP2;\n}\n\n/**\n * Gets the genesis hash from a transaction\n *\n * @param txn - The transaction object\n * @param txn.genesisHash - The genesis hash bytes\n * @returns Base64 encoded genesis hash\n */\nexport function getGenesisHashFromTransaction(txn: { genesisHash?: Uint8Array }): string {\n if (!txn.genesisHash) {\n throw new Error(\"Transaction does not have a genesis hash\");\n }\n return Buffer.from(txn.genesisHash).toString(\"base64\");\n}\n\n/**\n * Validates that all transactions in a group have the same group ID\n *\n * @param txns - Array of transaction bytes\n * @returns True if all transactions have matching group IDs\n */\nexport function validateGroupId(txns: Uint8Array[]): boolean {\n if (txns.length <= 1) {\n return true;\n }\n\n let expectedGroupId: string | null = null;\n\n for (const txnBytes of txns) {\n const txn = decodeUnsignedTxn(txnBytes);\n const groupId = txn.group ? Buffer.from(txn.group).toString(\"base64\") : null;\n\n if (expectedGroupId === null) {\n expectedGroupId = groupId;\n } else if (groupId !== expectedGroupId) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Extracts the transaction ID from signed transaction bytes\n *\n * @param signedTxnBytes - Signed transaction bytes\n * @returns Transaction ID string\n */\nexport function getTransactionId(signedTxnBytes: Uint8Array): string {\n const signedTxn = decodeSignedTxn(signedTxnBytes);\n return signedTxn.txn.txId();\n}\n\n/**\n * Checks if a signed transaction has a valid signature\n *\n * @param signedTxnBytes - Signed transaction bytes\n * @returns True if the transaction has a signature\n */\nexport function hasSignature(signedTxnBytes: Uint8Array): boolean {\n const signedTxn = decodeSignedTxn(signedTxnBytes);\n return (\n signedTxn.sig !== undefined || signedTxn.lsig !== undefined || signedTxn.msig !== undefined\n );\n}\n\n// Re-export algokit-utils types that consumers may need\nexport { Address, encodeAddress, decodeAddress } from \"@algorandfoundation/algokit-utils/common\";\n"],"mappings":";;;;;;;AAOA;AAAA,EACE,qBAAqB;AAAA,EACrB,2BAA2B;AAAA,OACtB;AACP,SAAS,sBAAsB;AA8E/B,SAAS,sBAAsB,6BAA6B;AAsI5D,SAAS,SAAS,eAAe,qBAAqB;AAvM/C,SAAS,kBAAkB,KAAyB;AACzD,SAAO,OAAO,KAAK,GAAG,EAAE,SAAS,QAAQ;AAC3C;AAQO,SAAS,kBAAkB,SAA6B;AAC7D,SAAO,IAAI,WAAW,OAAO,KAAK,SAAS,QAAQ,CAAC;AACtD;AAQO,SAAS,wBAAwB,SAAiB;AACvD,QAAM,QAAQ,kBAAkB,OAAO;AACvC,SAAO,gBAAgB,KAAK;AAC9B;AAQO,SAAS,0BAA0B,SAAiB;AACzD,QAAM,QAAQ,kBAAkB,OAAO;AACvC,SAAO,kBAAkB,KAAK;AAChC;AAUO,SAAS,uBAAuB,SAA0B;AAC/D,SAAO,eAAe,OAAO;AAC/B;AASO,SAAS,yBAAyB,UAAsB,WAAoB,MAAc;AAC/F,MAAI,UAAU;AACZ,UAAM,YAAY,gBAAgB,QAAQ;AAC1C,WAAO,UAAU,IAAI,OAAO,SAAS;AAAA,EACvC;AACA,QAAM,MAAM,kBAAkB,QAAQ;AACtC,SAAO,IAAI,OAAO,SAAS;AAC7B;AAYO,SAAS,uBAAuB,cAA+B,UAA0B;AAC9F,QAAM,SAAS,OAAO,YAAY;AAClC,QAAM,UAAU,OAAO,MAAM,QAAQ;AACrC,QAAM,UAAU,SAAS;AACzB,QAAM,UAAU,SAAS;AAEzB,MAAI,YAAY,OAAO,CAAC,GAAG;AACzB,WAAO,QAAQ,SAAS;AAAA,EAC1B;AAEA,QAAM,SAAS,QAAQ,SAAS,EAAE,SAAS,UAAU,GAAG;AAExD,QAAM,aAAa,OAAO,QAAQ,OAAO,EAAE;AAC3C,SAAO,GAAG,OAAO,IAAI,UAAU;AACjC;AAQO,SAAS,oBAAoB,OAA6C;AAC/E,MAAI,CAAC,MAAM,WAAW,WAAW,GAAG;AAClC,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,MAAM,MAAM,YAAY,MAAM;AAElD,MAAI,gBAAgB,+BAA+B;AACjD,WAAO;AAAA,EACT;AACA,MAAI,gBAAgB,+BAA+B;AACjD,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAQO,SAAS,kBAAkB,SAA0B;AAC1D,SAAO,QAAQ,WAAW,WAAW;AACvC;AAQO,SAAS,iBAAiB,SAA0B;AACzD,SAAO,YAAY;AACrB;AASO,SAAS,8BAA8B,KAA2C;AACvF,MAAI,CAAC,IAAI,aAAa;AACpB,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AACA,SAAO,OAAO,KAAK,IAAI,WAAW,EAAE,SAAS,QAAQ;AACvD;AAQO,SAAS,gBAAgB,MAA6B;AAC3D,MAAI,KAAK,UAAU,GAAG;AACpB,WAAO;AAAA,EACT;AAEA,MAAI,kBAAiC;AAErC,aAAW,YAAY,MAAM;AAC3B,UAAM,MAAM,kBAAkB,QAAQ;AACtC,UAAM,UAAU,IAAI,QAAQ,OAAO,KAAK,IAAI,KAAK,EAAE,SAAS,QAAQ,IAAI;AAExE,QAAI,oBAAoB,MAAM;AAC5B,wBAAkB;AAAA,IACpB,WAAW,YAAY,iBAAiB;AACtC,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAQO,SAAS,iBAAiB,gBAAoC;AACnE,QAAM,YAAY,gBAAgB,cAAc;AAChD,SAAO,UAAU,IAAI,KAAK;AAC5B;AAQO,SAAS,aAAa,gBAAqC;AAChE,QAAM,YAAY,gBAAgB,cAAc;AAChD,SACE,UAAU,QAAQ,UAAa,UAAU,SAAS,UAAa,UAAU,SAAS;AAEtF;","names":[]}
+1
-0

@@ -56,2 +56,3 @@ "use strict";

// src/utils.ts
var import_utils = require("@x402/core/utils");
var import_common2 = require("@algorandfoundation/algokit-utils/common");

@@ -58,0 +59,0 @@ function encodeTransaction(txn) {

+1
-1

@@ -1,1 +0,1 @@

{"version":3,"sources":["../../../../src/exact/client/index.ts","../../../../src/exact/client/scheme.ts","../../../../src/utils.ts","../../../../src/constants.ts"],"sourcesContent":["export { ExactAvmScheme } from \"./scheme\";\n","/**\n * AVM Client Scheme for Exact Payment Protocol\n *\n * Creates atomic transaction groups for Algorand ASA transfers.\n * Uses AlgorandClient and TransactionComposer from algokit-utils v10\n * for transaction construction, fee pooling, and group management.\n */\n\nimport { AlgorandClient } from \"@algorandfoundation/algokit-utils/algorand-client\";\nimport {\n Transaction,\n encodeTransactionRaw,\n groupTransactions,\n makeEmptyTransactionSigner,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport { microAlgo } from \"@algorandfoundation/algokit-utils/amount\";\nimport type {\n PaymentRequirements,\n SchemeNetworkClient,\n PaymentPayloadResult,\n} from \"@x402/core/types\";\nimport type { ClientAvmSigner, ClientAvmConfig } from \"../../signer\";\nimport type { ExactAvmPayloadV2 } from \"../../types\";\nimport { encodeTransaction } from \"../../utils\";\nimport { USDC_CONFIG } from \"../../constants\";\nimport { isTestnetNetwork } from \"../../utils\";\n\n/**\n * AVM client implementation for the Exact payment scheme.\n *\n * Creates atomic transaction groups with ASA transfers for x402 payments.\n * Supports optional fee payer transactions for gasless payments.\n */\nexport class ExactAvmScheme implements SchemeNetworkClient {\n readonly scheme = \"exact\";\n\n /**\n * Creates a new ExactAvmScheme instance.\n *\n * @param signer - The AVM signer for client operations\n * @param config - Optional configuration for Algod client\n */\n constructor(\n private readonly signer: ClientAvmSigner,\n private readonly config?: ClientAvmConfig,\n ) {}\n\n /**\n * Creates a payment payload for the Exact scheme.\n *\n * Constructs an atomic transaction group with:\n * - Optional fee payer transaction (if feePayer specified in requirements.extra)\n * - ASA transfer transaction to payTo address\n *\n * Uses TransactionComposer for automatic suggested params, group ID assignment,\n * and fee pooling. For sponsored (gasless) transactions, exact fees are calculated\n * from actual encoded transaction sizes using the protocol fee formula:\n * fee = max(fee_per_byte × txn_size_in_bytes, min_fee)\n *\n * @param x402Version - The x402 protocol version\n * @param paymentRequirements - The payment requirements\n * @returns Promise resolving to a payment payload result\n */\n async createPaymentPayload(\n x402Version: number,\n paymentRequirements: PaymentRequirements,\n ): Promise<PaymentPayloadResult> {\n const { amount, asset, payTo, network, extra } = paymentRequirements;\n\n const algorandClient = this.getAlgorandClient(network);\n\n // Get asset ID (from requirements or default USDC)\n const assetId = this.getAssetId(asset, network);\n\n // Get fee payer address from extra if provided\n const feePayer = extra?.feePayer as string | undefined;\n let paymentIndex = 0;\n\n // Use an empty signer for building — we sign manually after\n // (fee payer txns stay unsigned for the facilitator to sign)\n const emptySigner = makeEmptyTransactionSigner();\n\n // Build the transaction group using TransactionComposer\n const composer = algorandClient.newGroup();\n\n if (feePayer) {\n // First pass: add fee payer with a placeholder fee.\n // The actual fee will be recalculated after build() using exact transaction sizes.\n composer.addPayment({\n sender: feePayer,\n receiver: feePayer,\n amount: microAlgo(0),\n note: `x402-fee-payer-${Date.now()}`,\n signer: emptySigner,\n });\n paymentIndex = 1;\n }\n\n composer.addAssetTransfer({\n sender: this.signer.address,\n receiver: payTo,\n assetId: BigInt(assetId),\n amount: BigInt(amount),\n staticFee: feePayer ? microAlgo(0) : undefined, // 0 fee when fee payer covers\n note: `x402-payment-v${x402Version}-${Date.now()}`,\n signer: emptySigner,\n });\n\n // Build transactions (assigns group ID, suggested params, fees)\n const built = await composer.build();\n let transactions = built.transactions.map(tws => tws.txn);\n\n // For sponsored transactions: recalculate the fee payer's fee using\n // exact encoded sizes of all transactions in the group.\n // Algorand fee formula: fee = max(fee_per_byte × txn_size, min_fee)\n //\n // After changing fees, the group ID must be recomputed because it is\n // derived from each transaction's encoded bytes (which include the fee).\n if (feePayer) {\n const sp = await algorandClient.getSuggestedParams();\n const feePerByte = Number(sp.fee);\n const minFee = Number(sp.minFee);\n\n // Calculate exact fee for each transaction based on its actual encoded size\n let totalGroupFee = BigInt(0);\n for (const txn of transactions) {\n const txnSize = encodeTransactionRaw(txn).length;\n const txnFee = feePerByte > 0 ? Math.max(feePerByte * txnSize, minFee) : minFee;\n totalGroupFee += BigInt(txnFee);\n }\n\n // Strip group ID, set correct fees, then re-group.\n // groupTransactions() requires group to be absent, computes the new group hash,\n // and returns new Transaction objects with the correct group ID.\n const ungrouped = transactions.map((txn, i) => {\n const fee = i === 0 ? totalGroupFee : BigInt(0);\n return new Transaction({ ...txn, fee, group: undefined });\n });\n transactions = groupTransactions(ungrouped);\n }\n\n // Encode all transactions to raw bytes\n const encodedTxns = transactions.map(txn => encodeTransactionRaw(txn));\n\n // Determine which transactions the client should sign\n const clientIndexes = transactions\n .map((txn, i) => (txn.sender.toString() === this.signer.address ? i : -1))\n .filter(i => i !== -1);\n\n // Sign client's transactions\n const signedTxns = await this.signer.signTransactions(encodedTxns, clientIndexes);\n\n // Build payment group with signed/unsigned transactions\n const paymentGroup: string[] = encodedTxns.map((txnBytes, i) => {\n const signedTxn = signedTxns[i];\n if (signedTxn) {\n return encodeTransaction(signedTxn);\n }\n // Return unsigned transaction for facilitator to sign\n return encodeTransaction(txnBytes);\n });\n\n const payload: ExactAvmPayloadV2 = {\n paymentGroup,\n paymentIndex,\n };\n\n return {\n x402Version,\n payload: payload as unknown as Record<string, unknown>,\n };\n }\n\n /**\n * Creates or retrieves an AlgorandClient for the given network.\n *\n * @param network - Network identifier (CAIP-2 format)\n * @returns AlgorandClient instance\n */\n private getAlgorandClient(network: string): AlgorandClient {\n if (this.config?.algorandClient) {\n return this.config.algorandClient;\n }\n if (this.config?.algodUrl) {\n return AlgorandClient.fromConfig({\n algodConfig: {\n server: this.config.algodUrl,\n token: this.config.algodToken ?? \"\",\n },\n });\n }\n // Auto-detect network\n return isTestnetNetwork(network) ? AlgorandClient.testNet() : AlgorandClient.mainNet();\n }\n\n /**\n * Gets the asset ID from the requirements or defaults to USDC\n *\n * @param asset - Asset identifier from requirements\n * @param network - Network identifier\n * @returns Asset ID as string\n */\n private getAssetId(asset: string, network: string): string {\n // If asset is already a numeric string, use it directly\n if (/^\\d+$/.test(asset)) {\n return asset;\n }\n\n // Try to get from USDC config\n const usdcConfig = USDC_CONFIG[network];\n if (usdcConfig) {\n return usdcConfig.asaId;\n }\n\n // Default to the asset as-is (might be an ASA ID)\n return asset;\n }\n}\n","/**\n * AVM (Algorand) Utilities for x402 Payment Protocol\n *\n * Provides utility functions for Algod client creation, transaction encoding/decoding,\n * address validation, and network identification.\n */\n\nimport {\n decodeTransaction as decodeUnsignedTxn,\n decodeSignedTransaction as decodeSignedTxn,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport { isValidAddress } from \"@algorandfoundation/algokit-utils/common\";\nimport {\n ALGORAND_MAINNET_GENESIS_HASH,\n ALGORAND_TESTNET_GENESIS_HASH,\n ALGORAND_TESTNET_CAIP2,\n} from \"./constants\";\n\n/**\n * Encodes transaction bytes to base64 string\n *\n * @param txn - Transaction bytes (Uint8Array)\n * @returns Base64 encoded string\n */\nexport function encodeTransaction(txn: Uint8Array): string {\n return Buffer.from(txn).toString(\"base64\");\n}\n\n/**\n * Decodes a base64 encoded transaction to bytes\n *\n * @param encoded - Base64 encoded transaction string\n * @returns Transaction bytes (Uint8Array)\n */\nexport function decodeTransaction(encoded: string): Uint8Array {\n return new Uint8Array(Buffer.from(encoded, \"base64\"));\n}\n\n/**\n * Decodes a signed transaction from base64 msgpack\n *\n * @param encoded - Base64 encoded signed transaction\n * @returns Decoded signed transaction object\n */\nexport function decodeSignedTransaction(encoded: string) {\n const bytes = decodeTransaction(encoded);\n return decodeSignedTxn(bytes);\n}\n\n/**\n * Decodes an unsigned transaction from base64 msgpack\n *\n * @param encoded - Base64 encoded unsigned transaction\n * @returns Decoded transaction object\n */\nexport function decodeUnsignedTransaction(encoded: string) {\n const bytes = decodeTransaction(encoded);\n return decodeUnsignedTxn(bytes);\n}\n\n/**\n * Validates an Algorand address\n *\n * Uses isValidAddress from algokit-utils which performs full checksum validation.\n *\n * @param address - The address to validate\n * @returns True if the address is valid\n */\nexport function isValidAlgorandAddress(address: string): boolean {\n return isValidAddress(address);\n}\n\n/**\n * Gets the sender address from a transaction (signed or unsigned)\n *\n * @param txnBytes - Transaction bytes\n * @param isSigned - Whether the transaction is signed (default: true)\n * @returns Sender address string\n */\nexport function getSenderFromTransaction(txnBytes: Uint8Array, isSigned: boolean = true): string {\n if (isSigned) {\n const signedTxn = decodeSignedTxn(txnBytes);\n return signedTxn.txn.sender.toString();\n }\n const txn = decodeUnsignedTxn(txnBytes);\n return txn.sender.toString();\n}\n\n/**\n * Converts a decimal amount to atomic units (token's smallest unit)\n *\n * @param decimalAmount - The decimal amount as a string (e.g., \"1.50\")\n * @param decimals - Number of decimal places (e.g., 6 for USDC)\n * @returns Amount in atomic units as a string\n *\n * @example\n * ```typescript\n * convertToTokenAmount(\"1.50\", 6) // Returns \"1500000\"\n * convertToTokenAmount(\"0.10\", 6) // Returns \"100000\"\n * ```\n */\nexport function convertToTokenAmount(decimalAmount: string, decimals: number): string {\n const amount = parseFloat(decimalAmount);\n if (isNaN(amount)) {\n throw new Error(`Invalid amount: ${decimalAmount}`);\n }\n\n // Handle decimal conversion properly\n const [intPart, decPart = \"\"] = String(amount).split(\".\");\n const paddedDec = decPart.padEnd(decimals, \"0\").slice(0, decimals);\n const tokenAmount = (intPart + paddedDec).replace(/^0+/, \"\") || \"0\";\n\n return tokenAmount;\n}\n\n/**\n * Converts atomic units to decimal amount\n *\n * @param atomicAmount - Amount in atomic units (string or bigint)\n * @param decimals - Number of decimal places\n * @returns Decimal amount as a string\n */\nexport function convertFromTokenAmount(atomicAmount: string | bigint, decimals: number): string {\n const amount = BigInt(atomicAmount);\n const divisor = BigInt(10 ** decimals);\n const intPart = amount / divisor;\n const decPart = amount % divisor;\n\n if (decPart === BigInt(0)) {\n return intPart.toString();\n }\n\n const decStr = decPart.toString().padStart(decimals, \"0\");\n // Remove trailing zeros\n const trimmedDec = decStr.replace(/0+$/, \"\");\n return `${intPart}.${trimmedDec}`;\n}\n\n/**\n * Gets the network type from a CAIP-2 identifier\n *\n * @param caip2 - CAIP-2 network identifier\n * @returns Network type (\"mainnet\" | \"testnet\") or null if unknown\n */\nexport function getNetworkFromCaip2(caip2: string): \"mainnet\" | \"testnet\" | null {\n if (!caip2.startsWith(\"algorand:\")) {\n return null;\n }\n\n const genesisHash = caip2.slice(\"algorand:\".length);\n\n if (genesisHash === ALGORAND_MAINNET_GENESIS_HASH) {\n return \"mainnet\";\n }\n if (genesisHash === ALGORAND_TESTNET_GENESIS_HASH) {\n return \"testnet\";\n }\n\n return null;\n}\n\n/**\n * Checks if a network identifier is an Algorand network\n *\n * @param network - Network identifier (CAIP-2 format)\n * @returns True if the network is an Algorand network\n */\nexport function isAlgorandNetwork(network: string): boolean {\n return network.startsWith(\"algorand:\");\n}\n\n/**\n * Checks if a network identifier is a testnet\n *\n * @param network - Network identifier (CAIP-2 format)\n * @returns True if the network is a testnet\n */\nexport function isTestnetNetwork(network: string): boolean {\n return network === ALGORAND_TESTNET_CAIP2;\n}\n\n/**\n * Gets the genesis hash from a transaction\n *\n * @param txn - The transaction object\n * @param txn.genesisHash - The genesis hash bytes\n * @returns Base64 encoded genesis hash\n */\nexport function getGenesisHashFromTransaction(txn: { genesisHash?: Uint8Array }): string {\n if (!txn.genesisHash) {\n throw new Error(\"Transaction does not have a genesis hash\");\n }\n return Buffer.from(txn.genesisHash).toString(\"base64\");\n}\n\n/**\n * Validates that all transactions in a group have the same group ID\n *\n * @param txns - Array of transaction bytes\n * @returns True if all transactions have matching group IDs\n */\nexport function validateGroupId(txns: Uint8Array[]): boolean {\n if (txns.length <= 1) {\n return true;\n }\n\n let expectedGroupId: string | null = null;\n\n for (const txnBytes of txns) {\n const txn = decodeUnsignedTxn(txnBytes);\n const groupId = txn.group ? Buffer.from(txn.group).toString(\"base64\") : null;\n\n if (expectedGroupId === null) {\n expectedGroupId = groupId;\n } else if (groupId !== expectedGroupId) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Extracts the transaction ID from signed transaction bytes\n *\n * @param signedTxnBytes - Signed transaction bytes\n * @returns Transaction ID string\n */\nexport function getTransactionId(signedTxnBytes: Uint8Array): string {\n const signedTxn = decodeSignedTxn(signedTxnBytes);\n return signedTxn.txn.txId();\n}\n\n/**\n * Checks if a signed transaction has a valid signature\n *\n * @param signedTxnBytes - Signed transaction bytes\n * @returns True if the transaction has a signature\n */\nexport function hasSignature(signedTxnBytes: Uint8Array): boolean {\n const signedTxn = decodeSignedTxn(signedTxnBytes);\n return (\n signedTxn.sig !== undefined || signedTxn.lsig !== undefined || signedTxn.msig !== undefined\n );\n}\n\n// Re-export algokit-utils types that consumers may need\nexport { Address, encodeAddress, decodeAddress } from \"@algorandfoundation/algokit-utils/common\";\n","/**\n * Algorand Network Constants for x402 AVM Implementation\n *\n * CAIP-2 Network Identifiers use the format: algorand:<genesis-hash-base64>\n * Genesis hashes uniquely identify Algorand networks.\n */\n\n// ============================================================================\n// CAIP-2 Network Identifiers (V2)\n// ============================================================================\n\n/**\n * CAIP-2 network identifier for Algorand Mainnet\n * Format: algorand:<genesis-hash-base64>\n */\nexport const ALGORAND_MAINNET_CAIP2 = \"algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=\";\n\n/**\n * CAIP-2 network identifier for Algorand Testnet\n * Format: algorand:<genesis-hash-base64>\n */\nexport const ALGORAND_TESTNET_CAIP2 = \"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=\";\n\n/**\n * All supported CAIP-2 network identifiers\n */\nexport const CAIP2_NETWORKS = [ALGORAND_MAINNET_CAIP2, ALGORAND_TESTNET_CAIP2] as const;\n\n// ============================================================================\n// Genesis Hashes\n// ============================================================================\n\n/**\n * Algorand Mainnet genesis hash (base64 encoded)\n */\nexport const ALGORAND_MAINNET_GENESIS_HASH = \"wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=\";\n\n/**\n * Algorand Testnet genesis hash (base64 encoded)\n */\nexport const ALGORAND_TESTNET_GENESIS_HASH = \"SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=\";\n\n// ============================================================================\n// USDC ASA (Algorand Standard Asset) Configuration\n// ============================================================================\n\n/**\n * USDC ASA ID on Algorand Mainnet\n *\n * @see https://algoexplorer.io/asset/31566704\n */\nexport const USDC_MAINNET_ASA_ID = \"31566704\";\n\n/**\n * USDC ASA ID on Algorand Testnet\n *\n * @see https://testnet.algoexplorer.io/asset/10458941\n */\nexport const USDC_TESTNET_ASA_ID = \"10458941\";\n\n/**\n * USDC decimals (same across all networks)\n */\nexport const USDC_DECIMALS = 6;\n\n/**\n * USDC configuration per network\n */\nexport const USDC_CONFIG: Record<string, { asaId: string; name: string; decimals: number }> = {\n [ALGORAND_MAINNET_CAIP2]: {\n asaId: USDC_MAINNET_ASA_ID,\n name: \"USDC\",\n decimals: USDC_DECIMALS,\n },\n [ALGORAND_TESTNET_CAIP2]: {\n asaId: USDC_TESTNET_ASA_ID,\n name: \"USDC\",\n decimals: USDC_DECIMALS,\n },\n};\n\n// ============================================================================\n// Transaction Limits\n// ============================================================================\n\n/**\n * Maximum reasonable fee per transaction in microAlgos (5000 µAlgo).\n *\n * Algorand transaction fees are calculated as:\n * fee = max(current_fee_per_byte * transaction_size_in_bytes, min_fee)\n *\n * Under normal (non-congested) conditions, current_fee_per_byte is 0,\n * so fee = min_fee = 1000 µAlgo (0.001 ALGO).\n *\n * During network congestion, fees can rise. This constant is set to 5x\n * the minimum fee (5000 µAlgo) as a reasonable upper bound per transaction.\n *\n * For fee payer transactions that cover an entire group via fee pooling,\n * use `maxReasonableGroupFee(groupSize)` which multiplies this per-txn\n * cap by the number of transactions in the group.\n */\nexport const MAX_REASONABLE_FEE_PER_TXN = 5000;\n\n/**\n * Calculates the maximum reasonable fee for a fee payer transaction\n * that covers an entire atomic group via fee pooling.\n *\n * @param groupSize - Number of transactions in the atomic group\n * @returns Maximum acceptable fee in microAlgos\n */\nexport function maxReasonableGroupFee(groupSize: number): number {\n return MAX_REASONABLE_FEE_PER_TXN * groupSize;\n}\n\n// Address validation: use isValidAddress() from @algorandfoundation/algokit-utils/common\n// Address length: use ALGORAND_ADDRESS_LENGTH from @algorandfoundation/algokit-utils/common\n"],"mappings":"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","names":["import_transact","import_common"]}
{"version":3,"sources":["../../../../src/exact/client/index.ts","../../../../src/exact/client/scheme.ts","../../../../src/utils.ts","../../../../src/constants.ts"],"sourcesContent":["export { ExactAvmScheme } from \"./scheme\";\n","/**\n * AVM Client Scheme for Exact Payment Protocol\n *\n * Creates atomic transaction groups for Algorand ASA transfers.\n * Uses AlgorandClient and TransactionComposer from algokit-utils v10\n * for transaction construction, fee pooling, and group management.\n */\n\nimport { AlgorandClient } from \"@algorandfoundation/algokit-utils/algorand-client\";\nimport {\n Transaction,\n encodeTransactionRaw,\n groupTransactions,\n makeEmptyTransactionSigner,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport { microAlgo } from \"@algorandfoundation/algokit-utils/amount\";\nimport type {\n PaymentRequirements,\n SchemeNetworkClient,\n PaymentPayloadResult,\n} from \"@x402/core/types\";\nimport type { ClientAvmSigner, ClientAvmConfig } from \"../../signer\";\nimport type { ExactAvmPayloadV2 } from \"../../types\";\nimport { encodeTransaction } from \"../../utils\";\nimport { USDC_CONFIG } from \"../../constants\";\nimport { isTestnetNetwork } from \"../../utils\";\n\n/**\n * AVM client implementation for the Exact payment scheme.\n *\n * Creates atomic transaction groups with ASA transfers for x402 payments.\n * Supports optional fee payer transactions for gasless payments.\n */\nexport class ExactAvmScheme implements SchemeNetworkClient {\n readonly scheme = \"exact\";\n\n /**\n * Creates a new ExactAvmScheme instance.\n *\n * @param signer - The AVM signer for client operations\n * @param config - Optional configuration for Algod client\n */\n constructor(\n private readonly signer: ClientAvmSigner,\n private readonly config?: ClientAvmConfig,\n ) {}\n\n /**\n * Creates a payment payload for the Exact scheme.\n *\n * Constructs an atomic transaction group with:\n * - Optional fee payer transaction (if feePayer specified in requirements.extra)\n * - ASA transfer transaction to payTo address\n *\n * Uses TransactionComposer for automatic suggested params, group ID assignment,\n * and fee pooling. For sponsored (gasless) transactions, exact fees are calculated\n * from actual encoded transaction sizes using the protocol fee formula:\n * fee = max(fee_per_byte × txn_size_in_bytes, min_fee)\n *\n * @param x402Version - The x402 protocol version\n * @param paymentRequirements - The payment requirements\n * @returns Promise resolving to a payment payload result\n */\n async createPaymentPayload(\n x402Version: number,\n paymentRequirements: PaymentRequirements,\n ): Promise<PaymentPayloadResult> {\n const { amount, asset, payTo, network, extra } = paymentRequirements;\n\n const algorandClient = this.getAlgorandClient(network);\n\n // Get asset ID (from requirements or default USDC)\n const assetId = this.getAssetId(asset, network);\n\n // Get fee payer address from extra if provided\n const feePayer = extra?.feePayer as string | undefined;\n let paymentIndex = 0;\n\n // Use an empty signer for building — we sign manually after\n // (fee payer txns stay unsigned for the facilitator to sign)\n const emptySigner = makeEmptyTransactionSigner();\n\n // Build the transaction group using TransactionComposer\n const composer = algorandClient.newGroup();\n\n if (feePayer) {\n // First pass: add fee payer with a placeholder fee.\n // The actual fee will be recalculated after build() using exact transaction sizes.\n composer.addPayment({\n sender: feePayer,\n receiver: feePayer,\n amount: microAlgo(0),\n note: `x402-fee-payer-${Date.now()}`,\n signer: emptySigner,\n });\n paymentIndex = 1;\n }\n\n composer.addAssetTransfer({\n sender: this.signer.address,\n receiver: payTo,\n assetId: BigInt(assetId),\n amount: BigInt(amount),\n staticFee: feePayer ? microAlgo(0) : undefined, // 0 fee when fee payer covers\n note: `x402-payment-v${x402Version}-${Date.now()}`,\n signer: emptySigner,\n });\n\n // Build transactions (assigns group ID, suggested params, fees)\n const built = await composer.build();\n let transactions = built.transactions.map(tws => tws.txn);\n\n // For sponsored transactions: recalculate the fee payer's fee using\n // exact encoded sizes of all transactions in the group.\n // Algorand fee formula: fee = max(fee_per_byte × txn_size, min_fee)\n //\n // After changing fees, the group ID must be recomputed because it is\n // derived from each transaction's encoded bytes (which include the fee).\n if (feePayer) {\n const sp = await algorandClient.getSuggestedParams();\n const feePerByte = Number(sp.fee);\n const minFee = Number(sp.minFee);\n\n // Calculate exact fee for each transaction based on its actual encoded size\n let totalGroupFee = BigInt(0);\n for (const txn of transactions) {\n const txnSize = encodeTransactionRaw(txn).length;\n const txnFee = feePerByte > 0 ? Math.max(feePerByte * txnSize, minFee) : minFee;\n totalGroupFee += BigInt(txnFee);\n }\n\n // Strip group ID, set correct fees, then re-group.\n // groupTransactions() requires group to be absent, computes the new group hash,\n // and returns new Transaction objects with the correct group ID.\n const ungrouped = transactions.map((txn, i) => {\n const fee = i === 0 ? totalGroupFee : BigInt(0);\n return new Transaction({ ...txn, fee, group: undefined });\n });\n transactions = groupTransactions(ungrouped);\n }\n\n // Encode all transactions to raw bytes\n const encodedTxns = transactions.map(txn => encodeTransactionRaw(txn));\n\n // Determine which transactions the client should sign\n const clientIndexes = transactions\n .map((txn, i) => (txn.sender.toString() === this.signer.address ? i : -1))\n .filter(i => i !== -1);\n\n // Sign client's transactions\n const signedTxns = await this.signer.signTransactions(encodedTxns, clientIndexes);\n\n // Build payment group with signed/unsigned transactions\n const paymentGroup: string[] = encodedTxns.map((txnBytes, i) => {\n const signedTxn = signedTxns[i];\n if (signedTxn) {\n return encodeTransaction(signedTxn);\n }\n // Return unsigned transaction for facilitator to sign\n return encodeTransaction(txnBytes);\n });\n\n const payload: ExactAvmPayloadV2 = {\n paymentGroup,\n paymentIndex,\n };\n\n return {\n x402Version,\n payload: payload as unknown as Record<string, unknown>,\n };\n }\n\n /**\n * Creates or retrieves an AlgorandClient for the given network.\n *\n * @param network - Network identifier (CAIP-2 format)\n * @returns AlgorandClient instance\n */\n private getAlgorandClient(network: string): AlgorandClient {\n if (this.config?.algorandClient) {\n return this.config.algorandClient;\n }\n if (this.config?.algodUrl) {\n return AlgorandClient.fromConfig({\n algodConfig: {\n server: this.config.algodUrl,\n token: this.config.algodToken ?? \"\",\n },\n });\n }\n // Auto-detect network\n return isTestnetNetwork(network) ? AlgorandClient.testNet() : AlgorandClient.mainNet();\n }\n\n /**\n * Gets the asset ID from the requirements or defaults to USDC\n *\n * @param asset - Asset identifier from requirements\n * @param network - Network identifier\n * @returns Asset ID as string\n */\n private getAssetId(asset: string, network: string): string {\n // If asset is already a numeric string, use it directly\n if (/^\\d+$/.test(asset)) {\n return asset;\n }\n\n // Try to get from USDC config\n const usdcConfig = USDC_CONFIG[network];\n if (usdcConfig) {\n return usdcConfig.asaId;\n }\n\n // Default to the asset as-is (might be an ASA ID)\n return asset;\n }\n}\n","/**\n * AVM (Algorand) Utilities for x402 Payment Protocol\n *\n * Provides utility functions for Algod client creation, transaction encoding/decoding,\n * address validation, and network identification.\n */\n\nimport {\n decodeTransaction as decodeUnsignedTxn,\n decodeSignedTransaction as decodeSignedTxn,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport { isValidAddress } from \"@algorandfoundation/algokit-utils/common\";\nimport {\n ALGORAND_MAINNET_GENESIS_HASH,\n ALGORAND_TESTNET_GENESIS_HASH,\n ALGORAND_TESTNET_CAIP2,\n} from \"./constants\";\n\n/**\n * Encodes transaction bytes to base64 string\n *\n * @param txn - Transaction bytes (Uint8Array)\n * @returns Base64 encoded string\n */\nexport function encodeTransaction(txn: Uint8Array): string {\n return Buffer.from(txn).toString(\"base64\");\n}\n\n/**\n * Decodes a base64 encoded transaction to bytes\n *\n * @param encoded - Base64 encoded transaction string\n * @returns Transaction bytes (Uint8Array)\n */\nexport function decodeTransaction(encoded: string): Uint8Array {\n return new Uint8Array(Buffer.from(encoded, \"base64\"));\n}\n\n/**\n * Decodes a signed transaction from base64 msgpack\n *\n * @param encoded - Base64 encoded signed transaction\n * @returns Decoded signed transaction object\n */\nexport function decodeSignedTransaction(encoded: string) {\n const bytes = decodeTransaction(encoded);\n return decodeSignedTxn(bytes);\n}\n\n/**\n * Decodes an unsigned transaction from base64 msgpack\n *\n * @param encoded - Base64 encoded unsigned transaction\n * @returns Decoded transaction object\n */\nexport function decodeUnsignedTransaction(encoded: string) {\n const bytes = decodeTransaction(encoded);\n return decodeUnsignedTxn(bytes);\n}\n\n/**\n * Validates an Algorand address\n *\n * Uses isValidAddress from algokit-utils which performs full checksum validation.\n *\n * @param address - The address to validate\n * @returns True if the address is valid\n */\nexport function isValidAlgorandAddress(address: string): boolean {\n return isValidAddress(address);\n}\n\n/**\n * Gets the sender address from a transaction (signed or unsigned)\n *\n * @param txnBytes - Transaction bytes\n * @param isSigned - Whether the transaction is signed (default: true)\n * @returns Sender address string\n */\nexport function getSenderFromTransaction(txnBytes: Uint8Array, isSigned: boolean = true): string {\n if (isSigned) {\n const signedTxn = decodeSignedTxn(txnBytes);\n return signedTxn.txn.sender.toString();\n }\n const txn = decodeUnsignedTxn(txnBytes);\n return txn.sender.toString();\n}\n\n// Re-export from core for backward compatibility\nexport { convertToTokenAmount, numberToDecimalString } from \"@x402/core/utils\";\n\n/**\n * Converts atomic units to decimal amount\n *\n * @param atomicAmount - Amount in atomic units (string or bigint)\n * @param decimals - Number of decimal places\n * @returns Decimal amount as a string\n */\nexport function convertFromTokenAmount(atomicAmount: string | bigint, decimals: number): string {\n const amount = BigInt(atomicAmount);\n const divisor = BigInt(10 ** decimals);\n const intPart = amount / divisor;\n const decPart = amount % divisor;\n\n if (decPart === BigInt(0)) {\n return intPart.toString();\n }\n\n const decStr = decPart.toString().padStart(decimals, \"0\");\n // Remove trailing zeros\n const trimmedDec = decStr.replace(/0+$/, \"\");\n return `${intPart}.${trimmedDec}`;\n}\n\n/**\n * Gets the network type from a CAIP-2 identifier\n *\n * @param caip2 - CAIP-2 network identifier\n * @returns Network type (\"mainnet\" | \"testnet\") or null if unknown\n */\nexport function getNetworkFromCaip2(caip2: string): \"mainnet\" | \"testnet\" | null {\n if (!caip2.startsWith(\"algorand:\")) {\n return null;\n }\n\n const genesisHash = caip2.slice(\"algorand:\".length);\n\n if (genesisHash === ALGORAND_MAINNET_GENESIS_HASH) {\n return \"mainnet\";\n }\n if (genesisHash === ALGORAND_TESTNET_GENESIS_HASH) {\n return \"testnet\";\n }\n\n return null;\n}\n\n/**\n * Checks if a network identifier is an Algorand network\n *\n * @param network - Network identifier (CAIP-2 format)\n * @returns True if the network is an Algorand network\n */\nexport function isAlgorandNetwork(network: string): boolean {\n return network.startsWith(\"algorand:\");\n}\n\n/**\n * Checks if a network identifier is a testnet\n *\n * @param network - Network identifier (CAIP-2 format)\n * @returns True if the network is a testnet\n */\nexport function isTestnetNetwork(network: string): boolean {\n return network === ALGORAND_TESTNET_CAIP2;\n}\n\n/**\n * Gets the genesis hash from a transaction\n *\n * @param txn - The transaction object\n * @param txn.genesisHash - The genesis hash bytes\n * @returns Base64 encoded genesis hash\n */\nexport function getGenesisHashFromTransaction(txn: { genesisHash?: Uint8Array }): string {\n if (!txn.genesisHash) {\n throw new Error(\"Transaction does not have a genesis hash\");\n }\n return Buffer.from(txn.genesisHash).toString(\"base64\");\n}\n\n/**\n * Validates that all transactions in a group have the same group ID\n *\n * @param txns - Array of transaction bytes\n * @returns True if all transactions have matching group IDs\n */\nexport function validateGroupId(txns: Uint8Array[]): boolean {\n if (txns.length <= 1) {\n return true;\n }\n\n let expectedGroupId: string | null = null;\n\n for (const txnBytes of txns) {\n const txn = decodeUnsignedTxn(txnBytes);\n const groupId = txn.group ? Buffer.from(txn.group).toString(\"base64\") : null;\n\n if (expectedGroupId === null) {\n expectedGroupId = groupId;\n } else if (groupId !== expectedGroupId) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Extracts the transaction ID from signed transaction bytes\n *\n * @param signedTxnBytes - Signed transaction bytes\n * @returns Transaction ID string\n */\nexport function getTransactionId(signedTxnBytes: Uint8Array): string {\n const signedTxn = decodeSignedTxn(signedTxnBytes);\n return signedTxn.txn.txId();\n}\n\n/**\n * Checks if a signed transaction has a valid signature\n *\n * @param signedTxnBytes - Signed transaction bytes\n * @returns True if the transaction has a signature\n */\nexport function hasSignature(signedTxnBytes: Uint8Array): boolean {\n const signedTxn = decodeSignedTxn(signedTxnBytes);\n return (\n signedTxn.sig !== undefined || signedTxn.lsig !== undefined || signedTxn.msig !== undefined\n );\n}\n\n// Re-export algokit-utils types that consumers may need\nexport { Address, encodeAddress, decodeAddress } from \"@algorandfoundation/algokit-utils/common\";\n","/**\n * Algorand Network Constants for x402 AVM Implementation\n *\n * CAIP-2 Network Identifiers use the format: algorand:<genesis-hash-base64>\n * Genesis hashes uniquely identify Algorand networks.\n */\n\n// ============================================================================\n// CAIP-2 Network Identifiers (V2)\n// ============================================================================\n\n/**\n * CAIP-2 network identifier for Algorand Mainnet\n * Format: algorand:<genesis-hash-base64>\n */\nexport const ALGORAND_MAINNET_CAIP2 = \"algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=\";\n\n/**\n * CAIP-2 network identifier for Algorand Testnet\n * Format: algorand:<genesis-hash-base64>\n */\nexport const ALGORAND_TESTNET_CAIP2 = \"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=\";\n\n/**\n * All supported CAIP-2 network identifiers\n */\nexport const CAIP2_NETWORKS = [ALGORAND_MAINNET_CAIP2, ALGORAND_TESTNET_CAIP2] as const;\n\n// ============================================================================\n// Genesis Hashes\n// ============================================================================\n\n/**\n * Algorand Mainnet genesis hash (base64 encoded)\n */\nexport const ALGORAND_MAINNET_GENESIS_HASH = \"wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=\";\n\n/**\n * Algorand Testnet genesis hash (base64 encoded)\n */\nexport const ALGORAND_TESTNET_GENESIS_HASH = \"SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=\";\n\n// ============================================================================\n// USDC ASA (Algorand Standard Asset) Configuration\n// ============================================================================\n\n/**\n * USDC ASA ID on Algorand Mainnet\n *\n * @see https://algoexplorer.io/asset/31566704\n */\nexport const USDC_MAINNET_ASA_ID = \"31566704\";\n\n/**\n * USDC ASA ID on Algorand Testnet\n *\n * @see https://testnet.algoexplorer.io/asset/10458941\n */\nexport const USDC_TESTNET_ASA_ID = \"10458941\";\n\n/**\n * USDC decimals (same across all networks)\n */\nexport const USDC_DECIMALS = 6;\n\n/**\n * USDC configuration per network\n */\nexport const USDC_CONFIG: Record<string, { asaId: string; name: string; decimals: number }> = {\n [ALGORAND_MAINNET_CAIP2]: {\n asaId: USDC_MAINNET_ASA_ID,\n name: \"USDC\",\n decimals: USDC_DECIMALS,\n },\n [ALGORAND_TESTNET_CAIP2]: {\n asaId: USDC_TESTNET_ASA_ID,\n name: \"USDC\",\n decimals: USDC_DECIMALS,\n },\n};\n\n// ============================================================================\n// Transaction Limits\n// ============================================================================\n\n/**\n * Maximum reasonable fee per transaction in microAlgos (5000 µAlgo).\n *\n * Algorand transaction fees are calculated as:\n * fee = max(current_fee_per_byte * transaction_size_in_bytes, min_fee)\n *\n * Under normal (non-congested) conditions, current_fee_per_byte is 0,\n * so fee = min_fee = 1000 µAlgo (0.001 ALGO).\n *\n * During network congestion, fees can rise. This constant is set to 5x\n * the minimum fee (5000 µAlgo) as a reasonable upper bound per transaction.\n *\n * For fee payer transactions that cover an entire group via fee pooling,\n * use `maxReasonableGroupFee(groupSize)` which multiplies this per-txn\n * cap by the number of transactions in the group.\n */\nexport const MAX_REASONABLE_FEE_PER_TXN = 5000;\n\n/**\n * Calculates the maximum reasonable fee for a fee payer transaction\n * that covers an entire atomic group via fee pooling.\n *\n * @param groupSize - Number of transactions in the atomic group\n * @returns Maximum acceptable fee in microAlgos\n */\nexport function maxReasonableGroupFee(groupSize: number): number {\n return MAX_REASONABLE_FEE_PER_TXN * groupSize;\n}\n\n// Address validation: use isValidAddress() from @algorandfoundation/algokit-utils/common\n// Address length: use ALGORAND_ADDRESS_LENGTH from @algorandfoundation/algokit-utils/common\n"],"mappings":"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","names":["import_transact","import_common"]}

@@ -68,2 +68,3 @@ "use strict";

// src/utils.ts
var import_utils = require("@x402/core/utils");
var import_common2 = require("@algorandfoundation/algokit-utils/common");

@@ -70,0 +71,0 @@ function decodeTransaction(encoded) {

@@ -1,1 +0,1 @@

{"version":3,"sources":["../../../../src/exact/facilitator/index.ts","../../../../src/exact/facilitator/scheme.ts","../../../../src/types.ts","../../../../src/utils.ts","../../../../src/constants.ts","../../../../src/exact/facilitator/errors.ts"],"sourcesContent":["export { ExactAvmScheme } from \"./scheme\";\nexport * as Errors from \"./errors\";\n","/**\n * AVM Facilitator Scheme for Exact Payment Protocol\n *\n * Verifies and settles Algorand ASA transfer payments.\n */\n\nimport {\n decodeTransaction as decodeUnsignedTxn,\n decodeSignedTransaction as decodeSignedTxn,\n encodeTransactionRaw,\n encodeSignedTransaction,\n bytesForSigning,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport type { Transaction, SignedTransaction } from \"@algorandfoundation/algokit-utils/transact\";\nimport { ed25519Verifier } from \"@algorandfoundation/algokit-utils/crypto\";\nimport type {\n Network,\n PaymentPayload,\n PaymentRequirements,\n SchemeNetworkFacilitator,\n SettleResponse,\n VerifyResponse,\n} from \"@x402/core/types\";\nimport type { FacilitatorAvmSigner } from \"../../signer\";\nimport type { ExactAvmPayloadV2 } from \"../../types\";\nimport { isExactAvmPayload } from \"../../types\";\nimport { MAX_TRANSACTION_GROUP_SIZE } from \"@algorandfoundation/algokit-utils/common\";\nimport { decodeTransaction, hasSignature } from \"../../utils\";\nimport { maxReasonableGroupFee } from \"../../constants\";\nimport * as Errors from \"./errors\";\n\n/**\n * AVM facilitator implementation for the Exact payment scheme.\n *\n * Verifies atomic transaction groups and settles ASA transfers for x402 payments.\n * Supports gasless transactions by signing fee payer transactions.\n */\nexport class ExactAvmScheme implements SchemeNetworkFacilitator {\n readonly scheme = \"exact\";\n readonly caipFamily = \"algorand:*\";\n\n /**\n * Creates a new ExactAvmScheme facilitator instance.\n *\n * @param signer - The AVM signer for facilitator operations\n */\n constructor(private readonly signer: FacilitatorAvmSigner) {}\n\n /**\n * Get mechanism-specific extra data for the supported kinds endpoint.\n * For AVM, returns the feePayer address for gasless transactions.\n *\n * @param _ - The network identifier (unused, feePayer is network-agnostic)\n * @returns Extra data with feePayer address\n */\n getExtra(_: string): Record<string, unknown> | undefined {\n const addresses = this.signer.getAddresses();\n if (addresses.length === 0) {\n return undefined;\n }\n\n // Random selection distributes ALGO fee costs across multiple signer accounts,\n // preventing any single fee payer from being depleted faster than others.\n const randomIndex = Math.floor(Math.random() * addresses.length);\n return { feePayer: addresses[randomIndex] };\n }\n\n /**\n * Get signer addresses used by this facilitator.\n * Returns all addresses this facilitator can use for signing fee payer transactions.\n *\n * @param _ - The network identifier (unused, addresses are network-agnostic)\n * @returns Array of facilitator wallet addresses\n */\n getSigners(_: string): string[] {\n return [...this.signer.getAddresses()];\n }\n\n /**\n * Verifies a payment payload.\n *\n * Verification steps:\n * 1. Validate x402Version, scheme, and network\n * 2. Validate payload format and structure\n * 3. Check group size does not exceed maximum (16)\n * 4. Decode and validate transaction group\n * 5. Verify payment transaction (amount, receiver, asset)\n * 6. Prepare signed group (verify fee payer safety + sign)\n * 7. Simulate transaction group\n *\n * @param payload - The payment payload to verify\n * @param requirements - The payment requirements\n * @returns Promise resolving to verification response\n */\n async verify(\n payload: PaymentPayload,\n requirements: PaymentRequirements,\n ): Promise<VerifyResponse> {\n try {\n // Validate x402 version\n if (payload.x402Version !== 2) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidVersion,\n invalidMessage: `Expected x402Version 2, got ${payload.x402Version}`,\n };\n }\n\n // Validate scheme\n if (payload.accepted.scheme !== \"exact\" || requirements.scheme !== \"exact\") {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidScheme,\n invalidMessage: `Expected scheme \"exact\", got payload=\"${payload.accepted.scheme}\" requirements=\"${requirements.scheme}\"`,\n };\n }\n\n // Validate network\n if (payload.accepted.network !== requirements.network) {\n return {\n isValid: false,\n invalidReason: Errors.ErrNetworkMismatch,\n invalidMessage: `Network mismatch: payload=\"${payload.accepted.network}\" requirements=\"${requirements.network}\"`,\n };\n }\n\n const rawPayload = payload.payload as unknown;\n\n if (!isExactAvmPayload(rawPayload)) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidPayload,\n invalidMessage: \"Payload does not match ExactAvmPayloadV2 format\",\n };\n }\n\n const { paymentGroup, paymentIndex } = rawPayload as ExactAvmPayloadV2;\n\n if (paymentGroup.length > MAX_TRANSACTION_GROUP_SIZE) {\n return {\n isValid: false,\n invalidReason: Errors.ErrGroupSizeExceeded,\n invalidMessage: `Transaction group has ${paymentGroup.length} transactions, maximum is ${MAX_TRANSACTION_GROUP_SIZE}`,\n };\n }\n\n if (paymentIndex < 0 || paymentIndex >= paymentGroup.length) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidPaymentIndex,\n invalidMessage: `Payment index ${paymentIndex} out of bounds for group of ${paymentGroup.length}`,\n };\n }\n\n const facilitatorAddresses = this.signer.getAddresses();\n\n // Decode all transactions and validate group structure\n const decoded = this.decodeTransactionGroup(paymentGroup, facilitatorAddresses);\n if (\"error\" in decoded) return decoded.error;\n\n // Extract payer from payment transaction\n const paymentTxn = decoded.txns[paymentIndex].txn;\n const payer = paymentTxn.sender.toString();\n\n // SECURITY: Verify facilitator's signers are not transferring their own funds\n if (facilitatorAddresses.includes(payer)) {\n return {\n isValid: false,\n invalidReason: Errors.ErrFacilitatorTransferring,\n invalidMessage: \"Facilitator signer cannot be the payment sender\",\n };\n }\n\n // Payment transaction correctness\n const paymentCheck = await this.verifyPaymentTransaction(\n decoded.txns[paymentIndex],\n requirements,\n paymentGroup[paymentIndex],\n );\n if (!paymentCheck.isValid) return paymentCheck;\n\n // Verify fee payers and sign them for simulation\n const prepared = await this.prepareSignedGroup(decoded.txns, paymentGroup);\n if (\"error\" in prepared) return prepared.error;\n\n // Simulate the assembled group\n const simResult = await this.simulateTransactionGroup(\n prepared.signedTxns,\n requirements.network,\n );\n if (!simResult.isValid) return simResult;\n\n return { isValid: true, payer };\n } catch (error) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidPayload,\n invalidMessage: `Unexpected error: ${error instanceof Error ? error.message : \"Unknown\"}`,\n };\n }\n }\n\n /**\n * Settles a payment by submitting the transaction group.\n *\n * Settlement steps:\n * 1. Verify the payment first\n * 2. Decode and sign fee payer transactions (reuses shared decode/sign logic)\n * 3. Submit transaction group\n * 4. Wait for on-chain confirmation\n * 5. Return transaction ID\n *\n * @param payload - The payment payload to settle\n * @param requirements - The payment requirements\n * @returns Promise resolving to settlement response\n */\n async settle(\n payload: PaymentPayload,\n requirements: PaymentRequirements,\n ): Promise<SettleResponse> {\n // First verify the payment\n const verification = await this.verify(payload, requirements);\n if (!verification.isValid) {\n return {\n success: false,\n errorReason: verification.invalidReason,\n errorMessage: verification.invalidMessage,\n transaction: \"\",\n network: requirements.network,\n payer: verification.payer,\n };\n }\n\n const avmPayload = payload.payload as unknown as ExactAvmPayloadV2;\n const { paymentGroup, paymentIndex } = avmPayload;\n const facilitatorAddresses = this.signer.getAddresses();\n\n // Reuse shared decode logic\n const decoded = this.decodeTransactionGroup(paymentGroup, facilitatorAddresses);\n if (\"error\" in decoded) {\n return {\n success: false,\n errorReason: Errors.ErrSettleFailed,\n errorMessage: decoded.error.invalidMessage ?? decoded.error.invalidReason,\n transaction: \"\",\n network: requirements.network,\n payer: verification.payer,\n };\n }\n\n // Reuse shared sign logic\n const prepared = await this.prepareSignedGroup(decoded.txns, paymentGroup);\n if (\"error\" in prepared) {\n return {\n success: false,\n errorReason: Errors.ErrSettleFailed,\n errorMessage: prepared.error.invalidMessage ?? prepared.error.invalidReason,\n transaction: \"\",\n network: requirements.network,\n payer: verification.payer,\n };\n }\n\n // Get the payment transaction ID before submission\n const paymentTxnBytes = prepared.signedTxns[paymentIndex];\n const paymentStxn = decodeSignedTxn(paymentTxnBytes);\n const paymentTxId = paymentStxn.txn.txId();\n\n // Submit transaction group\n try {\n await this.signer.sendTransactions(prepared.signedTxns, requirements.network);\n } catch (error) {\n return {\n success: false,\n errorReason: Errors.ErrSettleFailed,\n errorMessage: `Failed to submit transaction: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n transaction: paymentTxId,\n network: requirements.network,\n payer: verification.payer,\n };\n }\n\n // Wait for on-chain confirmation\n try {\n // Wait up to 10 rounds for on-chain confirmation\n await this.signer.waitForConfirmation(paymentTxId, requirements.network, 10);\n } catch (error) {\n return {\n success: false,\n errorReason: Errors.ErrConfirmationFailed,\n errorMessage: `Transaction submitted but confirmation failed: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n transaction: paymentTxId,\n network: requirements.network,\n payer: verification.payer,\n };\n }\n\n return {\n success: true,\n transaction: paymentTxId,\n network: requirements.network,\n payer: verification.payer,\n };\n }\n\n /**\n * Decodes all transactions in the group and validates structure.\n *\n * - Signed transactions are decoded as-is\n * - Unsigned transactions are only accepted from facilitator addresses (fee payers)\n * - Verifies group ID consistency across all transactions\n *\n * @param paymentGroup - Array of base64-encoded transaction strings\n * @param facilitatorAddresses - Addresses controlled by this facilitator\n * @returns Decoded transactions or an error response\n */\n private decodeTransactionGroup(\n paymentGroup: string[],\n facilitatorAddresses: readonly string[],\n ): { txns: SignedTransaction[] } | { error: VerifyResponse } {\n const txns: SignedTransaction[] = [];\n\n for (let i = 0; i < paymentGroup.length; i++) {\n try {\n const bytes = decodeTransaction(paymentGroup[i]);\n\n try {\n const stxn = decodeSignedTxn(bytes);\n // Validate that decoding actually produced a valid signed transaction.\n // algokit-utils decodeSignedTransaction is lenient and may succeed on raw unsigned\n // bytes, returning a transaction with type \"unknown\" and missing fields.\n if (!stxn.txn.type || stxn.txn.type === \"unknown\") {\n throw new Error(\"Invalid signed transaction: missing type\");\n }\n txns.push(stxn);\n } catch {\n // Unsigned transaction — only the facilitator's fee payer txn should be unsigned\n const unsignedTxn = decodeUnsignedTxn(bytes);\n const sender = unsignedTxn.sender.toString();\n\n if (!facilitatorAddresses.includes(sender)) {\n return {\n error: {\n isValid: false,\n invalidReason: Errors.ErrUnsignedNonFacilitator,\n invalidMessage: `Unsigned transaction at index ${i} from ${sender} is not a facilitator address`,\n },\n };\n }\n\n // Wrap unsigned txn for simulation (empty signature)\n const encodedForSimulate = encodeSignedTransaction({ txn: unsignedTxn });\n txns.push(decodeSignedTxn(encodedForSimulate));\n }\n } catch {\n return {\n error: {\n isValid: false,\n invalidReason: Errors.ErrInvalidTransaction,\n invalidMessage: `Failed to decode transaction at index ${i}`,\n },\n };\n }\n }\n\n // Verify group ID consistency\n if (txns.length > 1) {\n const firstGroup = txns[0].txn.group;\n const firstGroupId = firstGroup ? Buffer.from(firstGroup).toString(\"base64\") : null;\n\n for (let i = 1; i < txns.length; i++) {\n const group = txns[i].txn.group;\n const groupId = group ? Buffer.from(group).toString(\"base64\") : null;\n if (groupId !== firstGroupId) {\n return {\n error: {\n isValid: false,\n invalidReason: Errors.ErrInvalidGroupId,\n invalidMessage: \"Transactions have inconsistent group IDs\",\n },\n };\n }\n }\n }\n\n return { txns };\n }\n\n /**\n * Verifies fee payer transactions and signs them, returning the assembled group\n * ready for simulation or submission.\n *\n * @param decodedTxns - Decoded signed transaction objects\n * @param paymentGroup - Original base64-encoded transaction strings\n * @returns Signed transaction bytes or an error response\n */\n private async prepareSignedGroup(\n decodedTxns: SignedTransaction[],\n paymentGroup: string[],\n ): Promise<{ signedTxns: Uint8Array[] } | { error: VerifyResponse }> {\n const facilitatorAddresses = this.signer.getAddresses();\n const signedTxns: Uint8Array[] = [];\n\n for (let i = 0; i < decodedTxns.length; i++) {\n const txn = decodedTxns[i].txn;\n const sender = txn.sender.toString();\n\n if (facilitatorAddresses.includes(sender)) {\n const feeCheck = this.verifyFeePayerTransaction(txn, decodedTxns.length);\n if (!feeCheck.isValid) return { error: feeCheck };\n\n try {\n const signedTxn = await this.signer.signTransaction(encodeTransactionRaw(txn), sender);\n signedTxns.push(signedTxn);\n } catch (error) {\n return {\n error: {\n isValid: false,\n invalidReason: Errors.ErrInvalidFeePayer,\n invalidMessage: `Failed to sign fee payer transaction: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n },\n };\n }\n } else {\n signedTxns.push(decodeTransaction(paymentGroup[i]));\n }\n }\n\n return { signedTxns };\n }\n\n /**\n * Simulates the transaction group and returns the verification result.\n *\n * @param signedTxns - Signed transaction bytes to simulate\n * @param network - Target network for simulation\n * @returns Verification result from simulation\n */\n private async simulateTransactionGroup(\n signedTxns: Uint8Array[],\n network: Network,\n ): Promise<VerifyResponse> {\n try {\n const simResult = (await this.signer.simulateTransactions(signedTxns, network)) as {\n txnGroups?: Array<{ failureMessage?: string }>;\n };\n\n if (simResult.txnGroups?.[0]?.failureMessage) {\n return {\n isValid: false,\n invalidReason: Errors.ErrSimulationFailed,\n invalidMessage: simResult.txnGroups[0].failureMessage,\n };\n }\n\n return { isValid: true };\n } catch (error) {\n return {\n isValid: false,\n invalidReason: Errors.ErrSimulationFailed,\n invalidMessage: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n }\n\n /**\n * Verifies the payment transaction matches requirements\n *\n * @param stxn - The signed payment transaction\n * @param requirements - Payment requirements to verify against\n * @param encodedTxn - Base64-encoded transaction for signature check\n * @returns Verification result\n */\n private async verifyPaymentTransaction(\n stxn: SignedTransaction,\n requirements: PaymentRequirements,\n encodedTxn: string,\n ): Promise<VerifyResponse> {\n const txn = stxn.txn;\n\n // Must be an asset transfer\n if (txn.type !== \"axfer\") {\n return {\n isValid: false,\n invalidReason: Errors.ErrNotAssetTransfer,\n invalidMessage: `Expected asset transfer, got \"${txn.type}\"`,\n };\n }\n\n // Access asset transfer properties — properly typed in algokit-utils v10\n const assetTransfer = txn.assetTransfer;\n\n if (!assetTransfer) {\n return {\n isValid: false,\n invalidReason: Errors.ErrNotAssetTransfer,\n invalidMessage: \"Missing assetTransfer data\",\n };\n }\n\n // Use BigInt comparison to avoid string format mismatches (e.g. \"1000\" vs \"1000.0\")\n const amount = assetTransfer.amount ?? BigInt(0);\n\n if (amount !== BigInt(requirements.amount)) {\n return {\n isValid: false,\n invalidReason: Errors.ErrAmountMismatch,\n invalidMessage: `Expected ${requirements.amount}, got ${amount.toString()}`,\n };\n }\n\n // Verify receiver address matches payTo\n const receiver = assetTransfer.receiver ? assetTransfer.receiver.toString() : \"\";\n\n if (receiver !== requirements.payTo) {\n return {\n isValid: false,\n invalidReason: Errors.ErrReceiverMismatch,\n invalidMessage: `Expected ${requirements.payTo}, got ${receiver}`,\n };\n }\n\n // Verify asset\n const assetId = assetTransfer.assetId?.toString() ?? \"\";\n\n if (assetId !== requirements.asset) {\n return {\n isValid: false,\n invalidReason: Errors.ErrAssetMismatch,\n invalidMessage: `Expected asset ${requirements.asset}, got ${assetId}`,\n };\n }\n\n // Verify signature exists\n const txnBytes = decodeTransaction(encodedTxn);\n if (!hasSignature(txnBytes)) {\n return {\n isValid: false,\n invalidReason: Errors.ErrPaymentNotSigned,\n invalidMessage: \"Payment transaction is not signed\",\n };\n }\n\n // Verify the ed25519 signature was actually made by the sender\n if (stxn.sig) {\n const signedMsg = bytesForSigning.transaction(txn);\n const isValidSig = await ed25519Verifier(stxn.sig, signedMsg, txn.sender.publicKey);\n if (!isValidSig) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidSignature,\n invalidMessage: \"Payment transaction signature does not match sender\",\n };\n }\n }\n\n return { isValid: true };\n }\n\n /**\n * Verifies a fee payer transaction is safe to sign\n *\n * @param txn - The fee payer transaction to validate\n * @param groupSize - Number of transactions in the atomic group (for fee cap calculation)\n * @returns Verification result\n */\n private verifyFeePayerTransaction(txn: Transaction, groupSize: number): VerifyResponse {\n // Must be a payment transaction (for fee payment)\n if (txn.type !== \"pay\") {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidFeePayer,\n invalidMessage: `Expected payment transaction, got ${txn.type}`,\n };\n }\n\n // Access payment fields — properly typed in algokit-utils v10\n const paymentFields = txn.payment;\n\n // Must have zero amount (self-payment for fee coverage)\n const payAmount = paymentFields?.amount ?? BigInt(0);\n if (payAmount > BigInt(0)) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidFeePayer,\n invalidMessage: \"Fee payer amount must be 0\",\n };\n }\n\n // Must be self-payment (receiver == sender)\n if (paymentFields?.receiver) {\n const receiverAddr = paymentFields.receiver.toString();\n const senderAddr = txn.sender.toString();\n if (receiverAddr !== senderAddr) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidFeePayer,\n invalidMessage: \"Fee payer receiver must be same as sender (self-payment)\",\n };\n }\n }\n\n // Must not have close remainder to\n if (paymentFields?.closeRemainderTo) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidFeePayer,\n invalidMessage: \"closeRemainderTo not allowed on fee payer\",\n };\n }\n\n // Must not have rekey to\n if (txn.rekeyTo) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidFeePayer,\n invalidMessage: \"rekeyTo not allowed on fee payer\",\n };\n }\n\n // Fee must be reasonable — during congestion fees can rise, so the cap\n // is 5x the minimum fee (5000 µAlgo) per transaction in the group.\n // The fee payer covers the entire group via Algorand's fee pooling.\n const fee = Number(txn.fee ?? 0);\n const maxFee = maxReasonableGroupFee(groupSize);\n if (fee > maxFee) {\n return {\n isValid: false,\n invalidReason: Errors.ErrFeeTooHigh,\n invalidMessage: `Fee ${fee} exceeds maximum ${maxFee} (${groupSize} txns × 5000 µAlgo)`,\n };\n }\n\n return { isValid: true };\n }\n}\n","/**\n * AVM (Algorand) Types for x402 Payment Protocol\n *\n * Defines payload structures and type guards for Algorand transactions.\n */\n\n/**\n * V2 Payload for Algorand exact payment scheme\n *\n * Contains an atomic transaction group with a designated payment transaction.\n * Transactions are encoded as base64 msgpack.\n *\n * @example\n * ```typescript\n * const payload: ExactAvmPayloadV2 = {\n * paymentGroup: [\n * \"gqNzaWfEQ...\", // Fee payer transaction (signed by facilitator)\n * \"gqNzaWfEQ...\", // ASA transfer (signed by client)\n * ],\n * paymentIndex: 1, // Payment is the second transaction\n * };\n * ```\n */\nexport interface ExactAvmPayloadV2 {\n /**\n * Array of base64-encoded msgpack transactions forming an atomic group.\n * May include unsigned transactions (for fee payer) that the facilitator will sign.\n */\n paymentGroup: string[];\n\n /**\n * Zero-based index of the payment transaction within paymentGroup.\n * This transaction must be an ASA transfer to the payTo address.\n */\n paymentIndex: number;\n}\n\n/**\n * Type guard to check if a payload is an ExactAvmPayloadV2\n *\n * @param payload - The payload to check\n * @returns True if the payload is a valid ExactAvmPayloadV2\n */\nexport function isExactAvmPayload(payload: unknown): payload is ExactAvmPayloadV2 {\n return (\n typeof payload === \"object\" &&\n payload !== null &&\n \"paymentGroup\" in payload &&\n \"paymentIndex\" in payload &&\n Array.isArray((payload as ExactAvmPayloadV2).paymentGroup) &&\n typeof (payload as ExactAvmPayloadV2).paymentIndex === \"number\" &&\n (payload as ExactAvmPayloadV2).paymentGroup.every(item => typeof item === \"string\")\n );\n}\n\n// Transaction and SignedTransaction types are provided by @algorandfoundation/algokit-utils/transact\n// Use those directly instead of custom type definitions.\n","/**\n * AVM (Algorand) Utilities for x402 Payment Protocol\n *\n * Provides utility functions for Algod client creation, transaction encoding/decoding,\n * address validation, and network identification.\n */\n\nimport {\n decodeTransaction as decodeUnsignedTxn,\n decodeSignedTransaction as decodeSignedTxn,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport { isValidAddress } from \"@algorandfoundation/algokit-utils/common\";\nimport {\n ALGORAND_MAINNET_GENESIS_HASH,\n ALGORAND_TESTNET_GENESIS_HASH,\n ALGORAND_TESTNET_CAIP2,\n} from \"./constants\";\n\n/**\n * Encodes transaction bytes to base64 string\n *\n * @param txn - Transaction bytes (Uint8Array)\n * @returns Base64 encoded string\n */\nexport function encodeTransaction(txn: Uint8Array): string {\n return Buffer.from(txn).toString(\"base64\");\n}\n\n/**\n * Decodes a base64 encoded transaction to bytes\n *\n * @param encoded - Base64 encoded transaction string\n * @returns Transaction bytes (Uint8Array)\n */\nexport function decodeTransaction(encoded: string): Uint8Array {\n return new Uint8Array(Buffer.from(encoded, \"base64\"));\n}\n\n/**\n * Decodes a signed transaction from base64 msgpack\n *\n * @param encoded - Base64 encoded signed transaction\n * @returns Decoded signed transaction object\n */\nexport function decodeSignedTransaction(encoded: string) {\n const bytes = decodeTransaction(encoded);\n return decodeSignedTxn(bytes);\n}\n\n/**\n * Decodes an unsigned transaction from base64 msgpack\n *\n * @param encoded - Base64 encoded unsigned transaction\n * @returns Decoded transaction object\n */\nexport function decodeUnsignedTransaction(encoded: string) {\n const bytes = decodeTransaction(encoded);\n return decodeUnsignedTxn(bytes);\n}\n\n/**\n * Validates an Algorand address\n *\n * Uses isValidAddress from algokit-utils which performs full checksum validation.\n *\n * @param address - The address to validate\n * @returns True if the address is valid\n */\nexport function isValidAlgorandAddress(address: string): boolean {\n return isValidAddress(address);\n}\n\n/**\n * Gets the sender address from a transaction (signed or unsigned)\n *\n * @param txnBytes - Transaction bytes\n * @param isSigned - Whether the transaction is signed (default: true)\n * @returns Sender address string\n */\nexport function getSenderFromTransaction(txnBytes: Uint8Array, isSigned: boolean = true): string {\n if (isSigned) {\n const signedTxn = decodeSignedTxn(txnBytes);\n return signedTxn.txn.sender.toString();\n }\n const txn = decodeUnsignedTxn(txnBytes);\n return txn.sender.toString();\n}\n\n/**\n * Converts a decimal amount to atomic units (token's smallest unit)\n *\n * @param decimalAmount - The decimal amount as a string (e.g., \"1.50\")\n * @param decimals - Number of decimal places (e.g., 6 for USDC)\n * @returns Amount in atomic units as a string\n *\n * @example\n * ```typescript\n * convertToTokenAmount(\"1.50\", 6) // Returns \"1500000\"\n * convertToTokenAmount(\"0.10\", 6) // Returns \"100000\"\n * ```\n */\nexport function convertToTokenAmount(decimalAmount: string, decimals: number): string {\n const amount = parseFloat(decimalAmount);\n if (isNaN(amount)) {\n throw new Error(`Invalid amount: ${decimalAmount}`);\n }\n\n // Handle decimal conversion properly\n const [intPart, decPart = \"\"] = String(amount).split(\".\");\n const paddedDec = decPart.padEnd(decimals, \"0\").slice(0, decimals);\n const tokenAmount = (intPart + paddedDec).replace(/^0+/, \"\") || \"0\";\n\n return tokenAmount;\n}\n\n/**\n * Converts atomic units to decimal amount\n *\n * @param atomicAmount - Amount in atomic units (string or bigint)\n * @param decimals - Number of decimal places\n * @returns Decimal amount as a string\n */\nexport function convertFromTokenAmount(atomicAmount: string | bigint, decimals: number): string {\n const amount = BigInt(atomicAmount);\n const divisor = BigInt(10 ** decimals);\n const intPart = amount / divisor;\n const decPart = amount % divisor;\n\n if (decPart === BigInt(0)) {\n return intPart.toString();\n }\n\n const decStr = decPart.toString().padStart(decimals, \"0\");\n // Remove trailing zeros\n const trimmedDec = decStr.replace(/0+$/, \"\");\n return `${intPart}.${trimmedDec}`;\n}\n\n/**\n * Gets the network type from a CAIP-2 identifier\n *\n * @param caip2 - CAIP-2 network identifier\n * @returns Network type (\"mainnet\" | \"testnet\") or null if unknown\n */\nexport function getNetworkFromCaip2(caip2: string): \"mainnet\" | \"testnet\" | null {\n if (!caip2.startsWith(\"algorand:\")) {\n return null;\n }\n\n const genesisHash = caip2.slice(\"algorand:\".length);\n\n if (genesisHash === ALGORAND_MAINNET_GENESIS_HASH) {\n return \"mainnet\";\n }\n if (genesisHash === ALGORAND_TESTNET_GENESIS_HASH) {\n return \"testnet\";\n }\n\n return null;\n}\n\n/**\n * Checks if a network identifier is an Algorand network\n *\n * @param network - Network identifier (CAIP-2 format)\n * @returns True if the network is an Algorand network\n */\nexport function isAlgorandNetwork(network: string): boolean {\n return network.startsWith(\"algorand:\");\n}\n\n/**\n * Checks if a network identifier is a testnet\n *\n * @param network - Network identifier (CAIP-2 format)\n * @returns True if the network is a testnet\n */\nexport function isTestnetNetwork(network: string): boolean {\n return network === ALGORAND_TESTNET_CAIP2;\n}\n\n/**\n * Gets the genesis hash from a transaction\n *\n * @param txn - The transaction object\n * @param txn.genesisHash - The genesis hash bytes\n * @returns Base64 encoded genesis hash\n */\nexport function getGenesisHashFromTransaction(txn: { genesisHash?: Uint8Array }): string {\n if (!txn.genesisHash) {\n throw new Error(\"Transaction does not have a genesis hash\");\n }\n return Buffer.from(txn.genesisHash).toString(\"base64\");\n}\n\n/**\n * Validates that all transactions in a group have the same group ID\n *\n * @param txns - Array of transaction bytes\n * @returns True if all transactions have matching group IDs\n */\nexport function validateGroupId(txns: Uint8Array[]): boolean {\n if (txns.length <= 1) {\n return true;\n }\n\n let expectedGroupId: string | null = null;\n\n for (const txnBytes of txns) {\n const txn = decodeUnsignedTxn(txnBytes);\n const groupId = txn.group ? Buffer.from(txn.group).toString(\"base64\") : null;\n\n if (expectedGroupId === null) {\n expectedGroupId = groupId;\n } else if (groupId !== expectedGroupId) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Extracts the transaction ID from signed transaction bytes\n *\n * @param signedTxnBytes - Signed transaction bytes\n * @returns Transaction ID string\n */\nexport function getTransactionId(signedTxnBytes: Uint8Array): string {\n const signedTxn = decodeSignedTxn(signedTxnBytes);\n return signedTxn.txn.txId();\n}\n\n/**\n * Checks if a signed transaction has a valid signature\n *\n * @param signedTxnBytes - Signed transaction bytes\n * @returns True if the transaction has a signature\n */\nexport function hasSignature(signedTxnBytes: Uint8Array): boolean {\n const signedTxn = decodeSignedTxn(signedTxnBytes);\n return (\n signedTxn.sig !== undefined || signedTxn.lsig !== undefined || signedTxn.msig !== undefined\n );\n}\n\n// Re-export algokit-utils types that consumers may need\nexport { Address, encodeAddress, decodeAddress } from \"@algorandfoundation/algokit-utils/common\";\n","/**\n * Algorand Network Constants for x402 AVM Implementation\n *\n * CAIP-2 Network Identifiers use the format: algorand:<genesis-hash-base64>\n * Genesis hashes uniquely identify Algorand networks.\n */\n\n// ============================================================================\n// CAIP-2 Network Identifiers (V2)\n// ============================================================================\n\n/**\n * CAIP-2 network identifier for Algorand Mainnet\n * Format: algorand:<genesis-hash-base64>\n */\nexport const ALGORAND_MAINNET_CAIP2 = \"algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=\";\n\n/**\n * CAIP-2 network identifier for Algorand Testnet\n * Format: algorand:<genesis-hash-base64>\n */\nexport const ALGORAND_TESTNET_CAIP2 = \"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=\";\n\n/**\n * All supported CAIP-2 network identifiers\n */\nexport const CAIP2_NETWORKS = [ALGORAND_MAINNET_CAIP2, ALGORAND_TESTNET_CAIP2] as const;\n\n// ============================================================================\n// Genesis Hashes\n// ============================================================================\n\n/**\n * Algorand Mainnet genesis hash (base64 encoded)\n */\nexport const ALGORAND_MAINNET_GENESIS_HASH = \"wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=\";\n\n/**\n * Algorand Testnet genesis hash (base64 encoded)\n */\nexport const ALGORAND_TESTNET_GENESIS_HASH = \"SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=\";\n\n// ============================================================================\n// USDC ASA (Algorand Standard Asset) Configuration\n// ============================================================================\n\n/**\n * USDC ASA ID on Algorand Mainnet\n *\n * @see https://algoexplorer.io/asset/31566704\n */\nexport const USDC_MAINNET_ASA_ID = \"31566704\";\n\n/**\n * USDC ASA ID on Algorand Testnet\n *\n * @see https://testnet.algoexplorer.io/asset/10458941\n */\nexport const USDC_TESTNET_ASA_ID = \"10458941\";\n\n/**\n * USDC decimals (same across all networks)\n */\nexport const USDC_DECIMALS = 6;\n\n/**\n * USDC configuration per network\n */\nexport const USDC_CONFIG: Record<string, { asaId: string; name: string; decimals: number }> = {\n [ALGORAND_MAINNET_CAIP2]: {\n asaId: USDC_MAINNET_ASA_ID,\n name: \"USDC\",\n decimals: USDC_DECIMALS,\n },\n [ALGORAND_TESTNET_CAIP2]: {\n asaId: USDC_TESTNET_ASA_ID,\n name: \"USDC\",\n decimals: USDC_DECIMALS,\n },\n};\n\n// ============================================================================\n// Transaction Limits\n// ============================================================================\n\n/**\n * Maximum reasonable fee per transaction in microAlgos (5000 µAlgo).\n *\n * Algorand transaction fees are calculated as:\n * fee = max(current_fee_per_byte * transaction_size_in_bytes, min_fee)\n *\n * Under normal (non-congested) conditions, current_fee_per_byte is 0,\n * so fee = min_fee = 1000 µAlgo (0.001 ALGO).\n *\n * During network congestion, fees can rise. This constant is set to 5x\n * the minimum fee (5000 µAlgo) as a reasonable upper bound per transaction.\n *\n * For fee payer transactions that cover an entire group via fee pooling,\n * use `maxReasonableGroupFee(groupSize)` which multiplies this per-txn\n * cap by the number of transactions in the group.\n */\nexport const MAX_REASONABLE_FEE_PER_TXN = 5000;\n\n/**\n * Calculates the maximum reasonable fee for a fee payer transaction\n * that covers an entire atomic group via fee pooling.\n *\n * @param groupSize - Number of transactions in the atomic group\n * @returns Maximum acceptable fee in microAlgos\n */\nexport function maxReasonableGroupFee(groupSize: number): number {\n return MAX_REASONABLE_FEE_PER_TXN * groupSize;\n}\n\n// Address validation: use isValidAddress() from @algorandfoundation/algokit-utils/common\n// Address length: use ALGORAND_ADDRESS_LENGTH from @algorandfoundation/algokit-utils/common\n","/**\n * AVM Facilitator error codes for verify and settle responses.\n *\n * Uses snake_case `invalid_exact_avm_*` prefix convention,\n * consistent with EVM (`invalid_exact_evm_*`) and SVM patterns.\n */\n\n// Verify errors — scheme/network/version\nexport const ErrInvalidScheme = \"invalid_exact_avm_scheme\";\nexport const ErrNetworkMismatch = \"invalid_exact_avm_network_mismatch\";\nexport const ErrInvalidVersion = \"invalid_exact_avm_invalid_version\";\n\n// Verify errors — payload structure\nexport const ErrInvalidPayload = \"invalid_exact_avm_payload\";\nexport const ErrGroupSizeExceeded = \"invalid_exact_avm_group_size_exceeded\";\nexport const ErrInvalidPaymentIndex = \"invalid_exact_avm_payment_index\";\nexport const ErrInvalidTransaction = \"invalid_exact_avm_invalid_transaction\";\nexport const ErrInvalidGroupId = \"invalid_exact_avm_invalid_group_id\";\n\n// Verify errors — payment correctness\nexport const ErrNotAssetTransfer = \"invalid_exact_avm_not_asset_transfer\";\nexport const ErrAmountMismatch = \"invalid_exact_avm_amount_mismatch\";\nexport const ErrReceiverMismatch = \"invalid_exact_avm_receiver_mismatch\";\nexport const ErrAssetMismatch = \"invalid_exact_avm_asset_mismatch\";\n\n// Verify errors — fee payer\nexport const ErrInvalidFeePayer = \"invalid_exact_avm_invalid_fee_payer\";\nexport const ErrFeeTooHigh = \"invalid_exact_avm_fee_too_high\";\n\n// Verify errors — signature\nexport const ErrPaymentNotSigned = \"invalid_exact_avm_payment_not_signed\";\nexport const ErrInvalidSignature = \"invalid_exact_avm_invalid_signature\";\n\n// Verify errors — simulation\nexport const ErrSimulationFailed = \"invalid_exact_avm_simulation_failed\";\n\n// Verify errors — facilitator safety\nexport const ErrFacilitatorTransferring = \"invalid_exact_avm_facilitator_transferring\";\n\n// Security errors\nexport const ErrUnsignedNonFacilitator = \"invalid_exact_avm_unsigned_non_facilitator\";\n\n// Settle errors\nexport const ErrSettleFailed = \"invalid_exact_avm_settlement_failed\";\nexport const ErrConfirmationFailed = \"invalid_exact_avm_confirmation_failed\";\n"],"mappings":"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","names":["import_transact","import_common","import_common","decodeSignedTxn","decodeSignedTxn","decodeUnsignedTxn"]}
{"version":3,"sources":["../../../../src/exact/facilitator/index.ts","../../../../src/exact/facilitator/scheme.ts","../../../../src/types.ts","../../../../src/utils.ts","../../../../src/constants.ts","../../../../src/exact/facilitator/errors.ts"],"sourcesContent":["export { ExactAvmScheme } from \"./scheme\";\nexport * as Errors from \"./errors\";\n","/**\n * AVM Facilitator Scheme for Exact Payment Protocol\n *\n * Verifies and settles Algorand ASA transfer payments.\n */\n\nimport {\n decodeTransaction as decodeUnsignedTxn,\n decodeSignedTransaction as decodeSignedTxn,\n encodeTransactionRaw,\n encodeSignedTransaction,\n bytesForSigning,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport type { Transaction, SignedTransaction } from \"@algorandfoundation/algokit-utils/transact\";\nimport { ed25519Verifier } from \"@algorandfoundation/algokit-utils/crypto\";\nimport type {\n Network,\n PaymentPayload,\n PaymentRequirements,\n SchemeNetworkFacilitator,\n SettleResponse,\n VerifyResponse,\n} from \"@x402/core/types\";\nimport type { FacilitatorAvmSigner } from \"../../signer\";\nimport type { ExactAvmPayloadV2 } from \"../../types\";\nimport { isExactAvmPayload } from \"../../types\";\nimport { MAX_TRANSACTION_GROUP_SIZE } from \"@algorandfoundation/algokit-utils/common\";\nimport { decodeTransaction, hasSignature } from \"../../utils\";\nimport { maxReasonableGroupFee } from \"../../constants\";\nimport * as Errors from \"./errors\";\n\n/**\n * AVM facilitator implementation for the Exact payment scheme.\n *\n * Verifies atomic transaction groups and settles ASA transfers for x402 payments.\n * Supports gasless transactions by signing fee payer transactions.\n */\nexport class ExactAvmScheme implements SchemeNetworkFacilitator {\n readonly scheme = \"exact\";\n readonly caipFamily = \"algorand:*\";\n\n /**\n * Creates a new ExactAvmScheme facilitator instance.\n *\n * @param signer - The AVM signer for facilitator operations\n */\n constructor(private readonly signer: FacilitatorAvmSigner) {}\n\n /**\n * Get mechanism-specific extra data for the supported kinds endpoint.\n * For AVM, returns the feePayer address for gasless transactions.\n *\n * @param _ - The network identifier (unused, feePayer is network-agnostic)\n * @returns Extra data with feePayer address\n */\n getExtra(_: string): Record<string, unknown> | undefined {\n const addresses = this.signer.getAddresses();\n if (addresses.length === 0) {\n return undefined;\n }\n\n // Random selection distributes ALGO fee costs across multiple signer accounts,\n // preventing any single fee payer from being depleted faster than others.\n const randomIndex = Math.floor(Math.random() * addresses.length);\n return { feePayer: addresses[randomIndex] };\n }\n\n /**\n * Get signer addresses used by this facilitator.\n * Returns all addresses this facilitator can use for signing fee payer transactions.\n *\n * @param _ - The network identifier (unused, addresses are network-agnostic)\n * @returns Array of facilitator wallet addresses\n */\n getSigners(_: string): string[] {\n return [...this.signer.getAddresses()];\n }\n\n /**\n * Verifies a payment payload.\n *\n * Verification steps:\n * 1. Validate x402Version, scheme, and network\n * 2. Validate payload format and structure\n * 3. Check group size does not exceed maximum (16)\n * 4. Decode and validate transaction group\n * 5. Verify payment transaction (amount, receiver, asset)\n * 6. Prepare signed group (verify fee payer safety + sign)\n * 7. Simulate transaction group\n *\n * @param payload - The payment payload to verify\n * @param requirements - The payment requirements\n * @returns Promise resolving to verification response\n */\n async verify(\n payload: PaymentPayload,\n requirements: PaymentRequirements,\n ): Promise<VerifyResponse> {\n try {\n // Validate x402 version\n if (payload.x402Version !== 2) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidVersion,\n invalidMessage: `Expected x402Version 2, got ${payload.x402Version}`,\n };\n }\n\n // Validate scheme\n if (payload.accepted.scheme !== \"exact\" || requirements.scheme !== \"exact\") {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidScheme,\n invalidMessage: `Expected scheme \"exact\", got payload=\"${payload.accepted.scheme}\" requirements=\"${requirements.scheme}\"`,\n };\n }\n\n // Validate network\n if (payload.accepted.network !== requirements.network) {\n return {\n isValid: false,\n invalidReason: Errors.ErrNetworkMismatch,\n invalidMessage: `Network mismatch: payload=\"${payload.accepted.network}\" requirements=\"${requirements.network}\"`,\n };\n }\n\n const rawPayload = payload.payload as unknown;\n\n if (!isExactAvmPayload(rawPayload)) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidPayload,\n invalidMessage: \"Payload does not match ExactAvmPayloadV2 format\",\n };\n }\n\n const { paymentGroup, paymentIndex } = rawPayload as ExactAvmPayloadV2;\n\n if (paymentGroup.length > MAX_TRANSACTION_GROUP_SIZE) {\n return {\n isValid: false,\n invalidReason: Errors.ErrGroupSizeExceeded,\n invalidMessage: `Transaction group has ${paymentGroup.length} transactions, maximum is ${MAX_TRANSACTION_GROUP_SIZE}`,\n };\n }\n\n if (paymentIndex < 0 || paymentIndex >= paymentGroup.length) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidPaymentIndex,\n invalidMessage: `Payment index ${paymentIndex} out of bounds for group of ${paymentGroup.length}`,\n };\n }\n\n const facilitatorAddresses = this.signer.getAddresses();\n\n // Decode all transactions and validate group structure\n const decoded = this.decodeTransactionGroup(paymentGroup, facilitatorAddresses);\n if (\"error\" in decoded) return decoded.error;\n\n // Extract payer from payment transaction\n const paymentTxn = decoded.txns[paymentIndex].txn;\n const payer = paymentTxn.sender.toString();\n\n // SECURITY: Verify facilitator's signers are not transferring their own funds\n if (facilitatorAddresses.includes(payer)) {\n return {\n isValid: false,\n invalidReason: Errors.ErrFacilitatorTransferring,\n invalidMessage: \"Facilitator signer cannot be the payment sender\",\n };\n }\n\n // Payment transaction correctness\n const paymentCheck = await this.verifyPaymentTransaction(\n decoded.txns[paymentIndex],\n requirements,\n paymentGroup[paymentIndex],\n );\n if (!paymentCheck.isValid) return paymentCheck;\n\n // Verify fee payers and sign them for simulation\n const prepared = await this.prepareSignedGroup(decoded.txns, paymentGroup);\n if (\"error\" in prepared) return prepared.error;\n\n // Simulate the assembled group\n const simResult = await this.simulateTransactionGroup(\n prepared.signedTxns,\n requirements.network,\n );\n if (!simResult.isValid) return simResult;\n\n return { isValid: true, payer };\n } catch (error) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidPayload,\n invalidMessage: `Unexpected error: ${error instanceof Error ? error.message : \"Unknown\"}`,\n };\n }\n }\n\n /**\n * Settles a payment by submitting the transaction group.\n *\n * Settlement steps:\n * 1. Verify the payment first\n * 2. Decode and sign fee payer transactions (reuses shared decode/sign logic)\n * 3. Submit transaction group\n * 4. Wait for on-chain confirmation\n * 5. Return transaction ID\n *\n * @param payload - The payment payload to settle\n * @param requirements - The payment requirements\n * @returns Promise resolving to settlement response\n */\n async settle(\n payload: PaymentPayload,\n requirements: PaymentRequirements,\n ): Promise<SettleResponse> {\n // First verify the payment\n const verification = await this.verify(payload, requirements);\n if (!verification.isValid) {\n return {\n success: false,\n errorReason: verification.invalidReason,\n errorMessage: verification.invalidMessage,\n transaction: \"\",\n network: requirements.network,\n payer: verification.payer,\n };\n }\n\n const avmPayload = payload.payload as unknown as ExactAvmPayloadV2;\n const { paymentGroup, paymentIndex } = avmPayload;\n const facilitatorAddresses = this.signer.getAddresses();\n\n // Reuse shared decode logic\n const decoded = this.decodeTransactionGroup(paymentGroup, facilitatorAddresses);\n if (\"error\" in decoded) {\n return {\n success: false,\n errorReason: Errors.ErrSettleFailed,\n errorMessage: decoded.error.invalidMessage ?? decoded.error.invalidReason,\n transaction: \"\",\n network: requirements.network,\n payer: verification.payer,\n };\n }\n\n // Reuse shared sign logic\n const prepared = await this.prepareSignedGroup(decoded.txns, paymentGroup);\n if (\"error\" in prepared) {\n return {\n success: false,\n errorReason: Errors.ErrSettleFailed,\n errorMessage: prepared.error.invalidMessage ?? prepared.error.invalidReason,\n transaction: \"\",\n network: requirements.network,\n payer: verification.payer,\n };\n }\n\n // Get the payment transaction ID before submission\n const paymentTxnBytes = prepared.signedTxns[paymentIndex];\n const paymentStxn = decodeSignedTxn(paymentTxnBytes);\n const paymentTxId = paymentStxn.txn.txId();\n\n // Submit transaction group\n try {\n await this.signer.sendTransactions(prepared.signedTxns, requirements.network);\n } catch (error) {\n return {\n success: false,\n errorReason: Errors.ErrSettleFailed,\n errorMessage: `Failed to submit transaction: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n transaction: paymentTxId,\n network: requirements.network,\n payer: verification.payer,\n };\n }\n\n // Wait for on-chain confirmation\n try {\n // Wait up to 10 rounds for on-chain confirmation\n await this.signer.waitForConfirmation(paymentTxId, requirements.network, 10);\n } catch (error) {\n return {\n success: false,\n errorReason: Errors.ErrConfirmationFailed,\n errorMessage: `Transaction submitted but confirmation failed: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n transaction: paymentTxId,\n network: requirements.network,\n payer: verification.payer,\n };\n }\n\n return {\n success: true,\n transaction: paymentTxId,\n network: requirements.network,\n payer: verification.payer,\n };\n }\n\n /**\n * Decodes all transactions in the group and validates structure.\n *\n * - Signed transactions are decoded as-is\n * - Unsigned transactions are only accepted from facilitator addresses (fee payers)\n * - Verifies group ID consistency across all transactions\n *\n * @param paymentGroup - Array of base64-encoded transaction strings\n * @param facilitatorAddresses - Addresses controlled by this facilitator\n * @returns Decoded transactions or an error response\n */\n private decodeTransactionGroup(\n paymentGroup: string[],\n facilitatorAddresses: readonly string[],\n ): { txns: SignedTransaction[] } | { error: VerifyResponse } {\n const txns: SignedTransaction[] = [];\n\n for (let i = 0; i < paymentGroup.length; i++) {\n try {\n const bytes = decodeTransaction(paymentGroup[i]);\n\n try {\n const stxn = decodeSignedTxn(bytes);\n // Validate that decoding actually produced a valid signed transaction.\n // algokit-utils decodeSignedTransaction is lenient and may succeed on raw unsigned\n // bytes, returning a transaction with type \"unknown\" and missing fields.\n if (!stxn.txn.type || stxn.txn.type === \"unknown\") {\n throw new Error(\"Invalid signed transaction: missing type\");\n }\n txns.push(stxn);\n } catch {\n // Unsigned transaction — only the facilitator's fee payer txn should be unsigned\n const unsignedTxn = decodeUnsignedTxn(bytes);\n const sender = unsignedTxn.sender.toString();\n\n if (!facilitatorAddresses.includes(sender)) {\n return {\n error: {\n isValid: false,\n invalidReason: Errors.ErrUnsignedNonFacilitator,\n invalidMessage: `Unsigned transaction at index ${i} from ${sender} is not a facilitator address`,\n },\n };\n }\n\n // Wrap unsigned txn for simulation (empty signature)\n const encodedForSimulate = encodeSignedTransaction({ txn: unsignedTxn });\n txns.push(decodeSignedTxn(encodedForSimulate));\n }\n } catch {\n return {\n error: {\n isValid: false,\n invalidReason: Errors.ErrInvalidTransaction,\n invalidMessage: `Failed to decode transaction at index ${i}`,\n },\n };\n }\n }\n\n // Verify group ID consistency\n if (txns.length > 1) {\n const firstGroup = txns[0].txn.group;\n const firstGroupId = firstGroup ? Buffer.from(firstGroup).toString(\"base64\") : null;\n\n for (let i = 1; i < txns.length; i++) {\n const group = txns[i].txn.group;\n const groupId = group ? Buffer.from(group).toString(\"base64\") : null;\n if (groupId !== firstGroupId) {\n return {\n error: {\n isValid: false,\n invalidReason: Errors.ErrInvalidGroupId,\n invalidMessage: \"Transactions have inconsistent group IDs\",\n },\n };\n }\n }\n }\n\n return { txns };\n }\n\n /**\n * Verifies fee payer transactions and signs them, returning the assembled group\n * ready for simulation or submission.\n *\n * @param decodedTxns - Decoded signed transaction objects\n * @param paymentGroup - Original base64-encoded transaction strings\n * @returns Signed transaction bytes or an error response\n */\n private async prepareSignedGroup(\n decodedTxns: SignedTransaction[],\n paymentGroup: string[],\n ): Promise<{ signedTxns: Uint8Array[] } | { error: VerifyResponse }> {\n const facilitatorAddresses = this.signer.getAddresses();\n const signedTxns: Uint8Array[] = [];\n\n for (let i = 0; i < decodedTxns.length; i++) {\n const txn = decodedTxns[i].txn;\n const sender = txn.sender.toString();\n\n if (facilitatorAddresses.includes(sender)) {\n const feeCheck = this.verifyFeePayerTransaction(txn, decodedTxns.length);\n if (!feeCheck.isValid) return { error: feeCheck };\n\n try {\n const signedTxn = await this.signer.signTransaction(encodeTransactionRaw(txn), sender);\n signedTxns.push(signedTxn);\n } catch (error) {\n return {\n error: {\n isValid: false,\n invalidReason: Errors.ErrInvalidFeePayer,\n invalidMessage: `Failed to sign fee payer transaction: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n },\n };\n }\n } else {\n signedTxns.push(decodeTransaction(paymentGroup[i]));\n }\n }\n\n return { signedTxns };\n }\n\n /**\n * Simulates the transaction group and returns the verification result.\n *\n * @param signedTxns - Signed transaction bytes to simulate\n * @param network - Target network for simulation\n * @returns Verification result from simulation\n */\n private async simulateTransactionGroup(\n signedTxns: Uint8Array[],\n network: Network,\n ): Promise<VerifyResponse> {\n try {\n const simResult = (await this.signer.simulateTransactions(signedTxns, network)) as {\n txnGroups?: Array<{ failureMessage?: string }>;\n };\n\n if (simResult.txnGroups?.[0]?.failureMessage) {\n return {\n isValid: false,\n invalidReason: Errors.ErrSimulationFailed,\n invalidMessage: simResult.txnGroups[0].failureMessage,\n };\n }\n\n return { isValid: true };\n } catch (error) {\n return {\n isValid: false,\n invalidReason: Errors.ErrSimulationFailed,\n invalidMessage: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n }\n\n /**\n * Verifies the payment transaction matches requirements\n *\n * @param stxn - The signed payment transaction\n * @param requirements - Payment requirements to verify against\n * @param encodedTxn - Base64-encoded transaction for signature check\n * @returns Verification result\n */\n private async verifyPaymentTransaction(\n stxn: SignedTransaction,\n requirements: PaymentRequirements,\n encodedTxn: string,\n ): Promise<VerifyResponse> {\n const txn = stxn.txn;\n\n // Must be an asset transfer\n if (txn.type !== \"axfer\") {\n return {\n isValid: false,\n invalidReason: Errors.ErrNotAssetTransfer,\n invalidMessage: `Expected asset transfer, got \"${txn.type}\"`,\n };\n }\n\n // Access asset transfer properties — properly typed in algokit-utils v10\n const assetTransfer = txn.assetTransfer;\n\n if (!assetTransfer) {\n return {\n isValid: false,\n invalidReason: Errors.ErrNotAssetTransfer,\n invalidMessage: \"Missing assetTransfer data\",\n };\n }\n\n // Use BigInt comparison to avoid string format mismatches (e.g. \"1000\" vs \"1000.0\")\n const amount = assetTransfer.amount ?? BigInt(0);\n\n if (amount !== BigInt(requirements.amount)) {\n return {\n isValid: false,\n invalidReason: Errors.ErrAmountMismatch,\n invalidMessage: `Expected ${requirements.amount}, got ${amount.toString()}`,\n };\n }\n\n // Verify receiver address matches payTo\n const receiver = assetTransfer.receiver ? assetTransfer.receiver.toString() : \"\";\n\n if (receiver !== requirements.payTo) {\n return {\n isValid: false,\n invalidReason: Errors.ErrReceiverMismatch,\n invalidMessage: `Expected ${requirements.payTo}, got ${receiver}`,\n };\n }\n\n // Verify asset\n const assetId = assetTransfer.assetId?.toString() ?? \"\";\n\n if (assetId !== requirements.asset) {\n return {\n isValid: false,\n invalidReason: Errors.ErrAssetMismatch,\n invalidMessage: `Expected asset ${requirements.asset}, got ${assetId}`,\n };\n }\n\n // Verify signature exists\n const txnBytes = decodeTransaction(encodedTxn);\n if (!hasSignature(txnBytes)) {\n return {\n isValid: false,\n invalidReason: Errors.ErrPaymentNotSigned,\n invalidMessage: \"Payment transaction is not signed\",\n };\n }\n\n // Verify the ed25519 signature was actually made by the sender\n if (stxn.sig) {\n const signedMsg = bytesForSigning.transaction(txn);\n const isValidSig = await ed25519Verifier(stxn.sig, signedMsg, txn.sender.publicKey);\n if (!isValidSig) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidSignature,\n invalidMessage: \"Payment transaction signature does not match sender\",\n };\n }\n }\n\n return { isValid: true };\n }\n\n /**\n * Verifies a fee payer transaction is safe to sign\n *\n * @param txn - The fee payer transaction to validate\n * @param groupSize - Number of transactions in the atomic group (for fee cap calculation)\n * @returns Verification result\n */\n private verifyFeePayerTransaction(txn: Transaction, groupSize: number): VerifyResponse {\n // Must be a payment transaction (for fee payment)\n if (txn.type !== \"pay\") {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidFeePayer,\n invalidMessage: `Expected payment transaction, got ${txn.type}`,\n };\n }\n\n // Access payment fields — properly typed in algokit-utils v10\n const paymentFields = txn.payment;\n\n // Must have zero amount (self-payment for fee coverage)\n const payAmount = paymentFields?.amount ?? BigInt(0);\n if (payAmount > BigInt(0)) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidFeePayer,\n invalidMessage: \"Fee payer amount must be 0\",\n };\n }\n\n // Must be self-payment (receiver == sender)\n if (paymentFields?.receiver) {\n const receiverAddr = paymentFields.receiver.toString();\n const senderAddr = txn.sender.toString();\n if (receiverAddr !== senderAddr) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidFeePayer,\n invalidMessage: \"Fee payer receiver must be same as sender (self-payment)\",\n };\n }\n }\n\n // Must not have close remainder to\n if (paymentFields?.closeRemainderTo) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidFeePayer,\n invalidMessage: \"closeRemainderTo not allowed on fee payer\",\n };\n }\n\n // Must not have rekey to\n if (txn.rekeyTo) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidFeePayer,\n invalidMessage: \"rekeyTo not allowed on fee payer\",\n };\n }\n\n // Fee must be reasonable — during congestion fees can rise, so the cap\n // is 5x the minimum fee (5000 µAlgo) per transaction in the group.\n // The fee payer covers the entire group via Algorand's fee pooling.\n const fee = Number(txn.fee ?? 0);\n const maxFee = maxReasonableGroupFee(groupSize);\n if (fee > maxFee) {\n return {\n isValid: false,\n invalidReason: Errors.ErrFeeTooHigh,\n invalidMessage: `Fee ${fee} exceeds maximum ${maxFee} (${groupSize} txns × 5000 µAlgo)`,\n };\n }\n\n return { isValid: true };\n }\n}\n","/**\n * AVM (Algorand) Types for x402 Payment Protocol\n *\n * Defines payload structures and type guards for Algorand transactions.\n */\n\n/**\n * V2 Payload for Algorand exact payment scheme\n *\n * Contains an atomic transaction group with a designated payment transaction.\n * Transactions are encoded as base64 msgpack.\n *\n * @example\n * ```typescript\n * const payload: ExactAvmPayloadV2 = {\n * paymentGroup: [\n * \"gqNzaWfEQ...\", // Fee payer transaction (signed by facilitator)\n * \"gqNzaWfEQ...\", // ASA transfer (signed by client)\n * ],\n * paymentIndex: 1, // Payment is the second transaction\n * };\n * ```\n */\nexport interface ExactAvmPayloadV2 {\n /**\n * Array of base64-encoded msgpack transactions forming an atomic group.\n * May include unsigned transactions (for fee payer) that the facilitator will sign.\n */\n paymentGroup: string[];\n\n /**\n * Zero-based index of the payment transaction within paymentGroup.\n * This transaction must be an ASA transfer to the payTo address.\n */\n paymentIndex: number;\n}\n\n/**\n * Type guard to check if a payload is an ExactAvmPayloadV2\n *\n * @param payload - The payload to check\n * @returns True if the payload is a valid ExactAvmPayloadV2\n */\nexport function isExactAvmPayload(payload: unknown): payload is ExactAvmPayloadV2 {\n return (\n typeof payload === \"object\" &&\n payload !== null &&\n \"paymentGroup\" in payload &&\n \"paymentIndex\" in payload &&\n Array.isArray((payload as ExactAvmPayloadV2).paymentGroup) &&\n typeof (payload as ExactAvmPayloadV2).paymentIndex === \"number\" &&\n (payload as ExactAvmPayloadV2).paymentGroup.every(item => typeof item === \"string\")\n );\n}\n\n// Transaction and SignedTransaction types are provided by @algorandfoundation/algokit-utils/transact\n// Use those directly instead of custom type definitions.\n","/**\n * AVM (Algorand) Utilities for x402 Payment Protocol\n *\n * Provides utility functions for Algod client creation, transaction encoding/decoding,\n * address validation, and network identification.\n */\n\nimport {\n decodeTransaction as decodeUnsignedTxn,\n decodeSignedTransaction as decodeSignedTxn,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport { isValidAddress } from \"@algorandfoundation/algokit-utils/common\";\nimport {\n ALGORAND_MAINNET_GENESIS_HASH,\n ALGORAND_TESTNET_GENESIS_HASH,\n ALGORAND_TESTNET_CAIP2,\n} from \"./constants\";\n\n/**\n * Encodes transaction bytes to base64 string\n *\n * @param txn - Transaction bytes (Uint8Array)\n * @returns Base64 encoded string\n */\nexport function encodeTransaction(txn: Uint8Array): string {\n return Buffer.from(txn).toString(\"base64\");\n}\n\n/**\n * Decodes a base64 encoded transaction to bytes\n *\n * @param encoded - Base64 encoded transaction string\n * @returns Transaction bytes (Uint8Array)\n */\nexport function decodeTransaction(encoded: string): Uint8Array {\n return new Uint8Array(Buffer.from(encoded, \"base64\"));\n}\n\n/**\n * Decodes a signed transaction from base64 msgpack\n *\n * @param encoded - Base64 encoded signed transaction\n * @returns Decoded signed transaction object\n */\nexport function decodeSignedTransaction(encoded: string) {\n const bytes = decodeTransaction(encoded);\n return decodeSignedTxn(bytes);\n}\n\n/**\n * Decodes an unsigned transaction from base64 msgpack\n *\n * @param encoded - Base64 encoded unsigned transaction\n * @returns Decoded transaction object\n */\nexport function decodeUnsignedTransaction(encoded: string) {\n const bytes = decodeTransaction(encoded);\n return decodeUnsignedTxn(bytes);\n}\n\n/**\n * Validates an Algorand address\n *\n * Uses isValidAddress from algokit-utils which performs full checksum validation.\n *\n * @param address - The address to validate\n * @returns True if the address is valid\n */\nexport function isValidAlgorandAddress(address: string): boolean {\n return isValidAddress(address);\n}\n\n/**\n * Gets the sender address from a transaction (signed or unsigned)\n *\n * @param txnBytes - Transaction bytes\n * @param isSigned - Whether the transaction is signed (default: true)\n * @returns Sender address string\n */\nexport function getSenderFromTransaction(txnBytes: Uint8Array, isSigned: boolean = true): string {\n if (isSigned) {\n const signedTxn = decodeSignedTxn(txnBytes);\n return signedTxn.txn.sender.toString();\n }\n const txn = decodeUnsignedTxn(txnBytes);\n return txn.sender.toString();\n}\n\n// Re-export from core for backward compatibility\nexport { convertToTokenAmount, numberToDecimalString } from \"@x402/core/utils\";\n\n/**\n * Converts atomic units to decimal amount\n *\n * @param atomicAmount - Amount in atomic units (string or bigint)\n * @param decimals - Number of decimal places\n * @returns Decimal amount as a string\n */\nexport function convertFromTokenAmount(atomicAmount: string | bigint, decimals: number): string {\n const amount = BigInt(atomicAmount);\n const divisor = BigInt(10 ** decimals);\n const intPart = amount / divisor;\n const decPart = amount % divisor;\n\n if (decPart === BigInt(0)) {\n return intPart.toString();\n }\n\n const decStr = decPart.toString().padStart(decimals, \"0\");\n // Remove trailing zeros\n const trimmedDec = decStr.replace(/0+$/, \"\");\n return `${intPart}.${trimmedDec}`;\n}\n\n/**\n * Gets the network type from a CAIP-2 identifier\n *\n * @param caip2 - CAIP-2 network identifier\n * @returns Network type (\"mainnet\" | \"testnet\") or null if unknown\n */\nexport function getNetworkFromCaip2(caip2: string): \"mainnet\" | \"testnet\" | null {\n if (!caip2.startsWith(\"algorand:\")) {\n return null;\n }\n\n const genesisHash = caip2.slice(\"algorand:\".length);\n\n if (genesisHash === ALGORAND_MAINNET_GENESIS_HASH) {\n return \"mainnet\";\n }\n if (genesisHash === ALGORAND_TESTNET_GENESIS_HASH) {\n return \"testnet\";\n }\n\n return null;\n}\n\n/**\n * Checks if a network identifier is an Algorand network\n *\n * @param network - Network identifier (CAIP-2 format)\n * @returns True if the network is an Algorand network\n */\nexport function isAlgorandNetwork(network: string): boolean {\n return network.startsWith(\"algorand:\");\n}\n\n/**\n * Checks if a network identifier is a testnet\n *\n * @param network - Network identifier (CAIP-2 format)\n * @returns True if the network is a testnet\n */\nexport function isTestnetNetwork(network: string): boolean {\n return network === ALGORAND_TESTNET_CAIP2;\n}\n\n/**\n * Gets the genesis hash from a transaction\n *\n * @param txn - The transaction object\n * @param txn.genesisHash - The genesis hash bytes\n * @returns Base64 encoded genesis hash\n */\nexport function getGenesisHashFromTransaction(txn: { genesisHash?: Uint8Array }): string {\n if (!txn.genesisHash) {\n throw new Error(\"Transaction does not have a genesis hash\");\n }\n return Buffer.from(txn.genesisHash).toString(\"base64\");\n}\n\n/**\n * Validates that all transactions in a group have the same group ID\n *\n * @param txns - Array of transaction bytes\n * @returns True if all transactions have matching group IDs\n */\nexport function validateGroupId(txns: Uint8Array[]): boolean {\n if (txns.length <= 1) {\n return true;\n }\n\n let expectedGroupId: string | null = null;\n\n for (const txnBytes of txns) {\n const txn = decodeUnsignedTxn(txnBytes);\n const groupId = txn.group ? Buffer.from(txn.group).toString(\"base64\") : null;\n\n if (expectedGroupId === null) {\n expectedGroupId = groupId;\n } else if (groupId !== expectedGroupId) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Extracts the transaction ID from signed transaction bytes\n *\n * @param signedTxnBytes - Signed transaction bytes\n * @returns Transaction ID string\n */\nexport function getTransactionId(signedTxnBytes: Uint8Array): string {\n const signedTxn = decodeSignedTxn(signedTxnBytes);\n return signedTxn.txn.txId();\n}\n\n/**\n * Checks if a signed transaction has a valid signature\n *\n * @param signedTxnBytes - Signed transaction bytes\n * @returns True if the transaction has a signature\n */\nexport function hasSignature(signedTxnBytes: Uint8Array): boolean {\n const signedTxn = decodeSignedTxn(signedTxnBytes);\n return (\n signedTxn.sig !== undefined || signedTxn.lsig !== undefined || signedTxn.msig !== undefined\n );\n}\n\n// Re-export algokit-utils types that consumers may need\nexport { Address, encodeAddress, decodeAddress } from \"@algorandfoundation/algokit-utils/common\";\n","/**\n * Algorand Network Constants for x402 AVM Implementation\n *\n * CAIP-2 Network Identifiers use the format: algorand:<genesis-hash-base64>\n * Genesis hashes uniquely identify Algorand networks.\n */\n\n// ============================================================================\n// CAIP-2 Network Identifiers (V2)\n// ============================================================================\n\n/**\n * CAIP-2 network identifier for Algorand Mainnet\n * Format: algorand:<genesis-hash-base64>\n */\nexport const ALGORAND_MAINNET_CAIP2 = \"algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=\";\n\n/**\n * CAIP-2 network identifier for Algorand Testnet\n * Format: algorand:<genesis-hash-base64>\n */\nexport const ALGORAND_TESTNET_CAIP2 = \"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=\";\n\n/**\n * All supported CAIP-2 network identifiers\n */\nexport const CAIP2_NETWORKS = [ALGORAND_MAINNET_CAIP2, ALGORAND_TESTNET_CAIP2] as const;\n\n// ============================================================================\n// Genesis Hashes\n// ============================================================================\n\n/**\n * Algorand Mainnet genesis hash (base64 encoded)\n */\nexport const ALGORAND_MAINNET_GENESIS_HASH = \"wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=\";\n\n/**\n * Algorand Testnet genesis hash (base64 encoded)\n */\nexport const ALGORAND_TESTNET_GENESIS_HASH = \"SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=\";\n\n// ============================================================================\n// USDC ASA (Algorand Standard Asset) Configuration\n// ============================================================================\n\n/**\n * USDC ASA ID on Algorand Mainnet\n *\n * @see https://algoexplorer.io/asset/31566704\n */\nexport const USDC_MAINNET_ASA_ID = \"31566704\";\n\n/**\n * USDC ASA ID on Algorand Testnet\n *\n * @see https://testnet.algoexplorer.io/asset/10458941\n */\nexport const USDC_TESTNET_ASA_ID = \"10458941\";\n\n/**\n * USDC decimals (same across all networks)\n */\nexport const USDC_DECIMALS = 6;\n\n/**\n * USDC configuration per network\n */\nexport const USDC_CONFIG: Record<string, { asaId: string; name: string; decimals: number }> = {\n [ALGORAND_MAINNET_CAIP2]: {\n asaId: USDC_MAINNET_ASA_ID,\n name: \"USDC\",\n decimals: USDC_DECIMALS,\n },\n [ALGORAND_TESTNET_CAIP2]: {\n asaId: USDC_TESTNET_ASA_ID,\n name: \"USDC\",\n decimals: USDC_DECIMALS,\n },\n};\n\n// ============================================================================\n// Transaction Limits\n// ============================================================================\n\n/**\n * Maximum reasonable fee per transaction in microAlgos (5000 µAlgo).\n *\n * Algorand transaction fees are calculated as:\n * fee = max(current_fee_per_byte * transaction_size_in_bytes, min_fee)\n *\n * Under normal (non-congested) conditions, current_fee_per_byte is 0,\n * so fee = min_fee = 1000 µAlgo (0.001 ALGO).\n *\n * During network congestion, fees can rise. This constant is set to 5x\n * the minimum fee (5000 µAlgo) as a reasonable upper bound per transaction.\n *\n * For fee payer transactions that cover an entire group via fee pooling,\n * use `maxReasonableGroupFee(groupSize)` which multiplies this per-txn\n * cap by the number of transactions in the group.\n */\nexport const MAX_REASONABLE_FEE_PER_TXN = 5000;\n\n/**\n * Calculates the maximum reasonable fee for a fee payer transaction\n * that covers an entire atomic group via fee pooling.\n *\n * @param groupSize - Number of transactions in the atomic group\n * @returns Maximum acceptable fee in microAlgos\n */\nexport function maxReasonableGroupFee(groupSize: number): number {\n return MAX_REASONABLE_FEE_PER_TXN * groupSize;\n}\n\n// Address validation: use isValidAddress() from @algorandfoundation/algokit-utils/common\n// Address length: use ALGORAND_ADDRESS_LENGTH from @algorandfoundation/algokit-utils/common\n","/**\n * AVM Facilitator error codes for verify and settle responses.\n *\n * Uses snake_case `invalid_exact_avm_*` prefix convention,\n * consistent with EVM (`invalid_exact_evm_*`) and SVM patterns.\n */\n\n// Verify errors — scheme/network/version\nexport const ErrInvalidScheme = \"invalid_exact_avm_scheme\";\nexport const ErrNetworkMismatch = \"invalid_exact_avm_network_mismatch\";\nexport const ErrInvalidVersion = \"invalid_exact_avm_invalid_version\";\n\n// Verify errors — payload structure\nexport const ErrInvalidPayload = \"invalid_exact_avm_payload\";\nexport const ErrGroupSizeExceeded = \"invalid_exact_avm_group_size_exceeded\";\nexport const ErrInvalidPaymentIndex = \"invalid_exact_avm_payment_index\";\nexport const ErrInvalidTransaction = \"invalid_exact_avm_invalid_transaction\";\nexport const ErrInvalidGroupId = \"invalid_exact_avm_invalid_group_id\";\n\n// Verify errors — payment correctness\nexport const ErrNotAssetTransfer = \"invalid_exact_avm_not_asset_transfer\";\nexport const ErrAmountMismatch = \"invalid_exact_avm_amount_mismatch\";\nexport const ErrReceiverMismatch = \"invalid_exact_avm_receiver_mismatch\";\nexport const ErrAssetMismatch = \"invalid_exact_avm_asset_mismatch\";\n\n// Verify errors — fee payer\nexport const ErrInvalidFeePayer = \"invalid_exact_avm_invalid_fee_payer\";\nexport const ErrFeeTooHigh = \"invalid_exact_avm_fee_too_high\";\n\n// Verify errors — signature\nexport const ErrPaymentNotSigned = \"invalid_exact_avm_payment_not_signed\";\nexport const ErrInvalidSignature = \"invalid_exact_avm_invalid_signature\";\n\n// Verify errors — simulation\nexport const ErrSimulationFailed = \"invalid_exact_avm_simulation_failed\";\n\n// Verify errors — facilitator safety\nexport const ErrFacilitatorTransferring = \"invalid_exact_avm_facilitator_transferring\";\n\n// Security errors\nexport const ErrUnsignedNonFacilitator = \"invalid_exact_avm_unsigned_non_facilitator\";\n\n// Settle errors\nexport const ErrSettleFailed = \"invalid_exact_avm_settlement_failed\";\nexport const ErrConfirmationFailed = \"invalid_exact_avm_confirmation_failed\";\n"],"mappings":"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","names":["import_transact","import_common","import_common","decodeSignedTxn","decodeSignedTxn","decodeUnsignedTxn"]}

@@ -27,2 +27,5 @@ "use strict";

// src/exact/server/scheme.ts
var import_utils = require("@x402/core/utils");
// src/constants.ts

@@ -47,17 +50,2 @@ var ALGORAND_MAINNET_CAIP2 = "algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=";

// src/utils.ts
var import_transact = require("@algorandfoundation/algokit-utils/transact");
var import_common = require("@algorandfoundation/algokit-utils/common");
var import_common2 = require("@algorandfoundation/algokit-utils/common");
function convertToTokenAmount(decimalAmount, decimals) {
const amount = parseFloat(decimalAmount);
if (isNaN(amount)) {
throw new Error(`Invalid amount: ${decimalAmount}`);
}
const [intPart, decPart = ""] = String(amount).split(".");
const paddedDec = decPart.padEnd(decimals, "0").slice(0, decimals);
const tokenAmount = (intPart + paddedDec).replace(/^0+/, "") || "0";
return tokenAmount;
}
// src/exact/server/scheme.ts

@@ -183,3 +171,3 @@ var ExactAvmScheme = class {

const assetInfo = this.getDefaultAsset(network);
const tokenAmount = convertToTokenAmount(amount.toString(), assetInfo.decimals);
const tokenAmount = (0, import_utils.convertToTokenAmount)((0, import_utils.numberToDecimalString)(amount), assetInfo.decimals);
return {

@@ -186,0 +174,0 @@ amount: tokenAmount,

@@ -1,1 +0,1 @@

{"version":3,"sources":["../../../../src/exact/server/index.ts","../../../../src/constants.ts","../../../../src/utils.ts","../../../../src/exact/server/scheme.ts"],"sourcesContent":["export { ExactAvmScheme } from \"./scheme\";\n","/**\n * Algorand Network Constants for x402 AVM Implementation\n *\n * CAIP-2 Network Identifiers use the format: algorand:<genesis-hash-base64>\n * Genesis hashes uniquely identify Algorand networks.\n */\n\n// ============================================================================\n// CAIP-2 Network Identifiers (V2)\n// ============================================================================\n\n/**\n * CAIP-2 network identifier for Algorand Mainnet\n * Format: algorand:<genesis-hash-base64>\n */\nexport const ALGORAND_MAINNET_CAIP2 = \"algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=\";\n\n/**\n * CAIP-2 network identifier for Algorand Testnet\n * Format: algorand:<genesis-hash-base64>\n */\nexport const ALGORAND_TESTNET_CAIP2 = \"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=\";\n\n/**\n * All supported CAIP-2 network identifiers\n */\nexport const CAIP2_NETWORKS = [ALGORAND_MAINNET_CAIP2, ALGORAND_TESTNET_CAIP2] as const;\n\n// ============================================================================\n// Genesis Hashes\n// ============================================================================\n\n/**\n * Algorand Mainnet genesis hash (base64 encoded)\n */\nexport const ALGORAND_MAINNET_GENESIS_HASH = \"wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=\";\n\n/**\n * Algorand Testnet genesis hash (base64 encoded)\n */\nexport const ALGORAND_TESTNET_GENESIS_HASH = \"SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=\";\n\n// ============================================================================\n// USDC ASA (Algorand Standard Asset) Configuration\n// ============================================================================\n\n/**\n * USDC ASA ID on Algorand Mainnet\n *\n * @see https://algoexplorer.io/asset/31566704\n */\nexport const USDC_MAINNET_ASA_ID = \"31566704\";\n\n/**\n * USDC ASA ID on Algorand Testnet\n *\n * @see https://testnet.algoexplorer.io/asset/10458941\n */\nexport const USDC_TESTNET_ASA_ID = \"10458941\";\n\n/**\n * USDC decimals (same across all networks)\n */\nexport const USDC_DECIMALS = 6;\n\n/**\n * USDC configuration per network\n */\nexport const USDC_CONFIG: Record<string, { asaId: string; name: string; decimals: number }> = {\n [ALGORAND_MAINNET_CAIP2]: {\n asaId: USDC_MAINNET_ASA_ID,\n name: \"USDC\",\n decimals: USDC_DECIMALS,\n },\n [ALGORAND_TESTNET_CAIP2]: {\n asaId: USDC_TESTNET_ASA_ID,\n name: \"USDC\",\n decimals: USDC_DECIMALS,\n },\n};\n\n// ============================================================================\n// Transaction Limits\n// ============================================================================\n\n/**\n * Maximum reasonable fee per transaction in microAlgos (5000 µAlgo).\n *\n * Algorand transaction fees are calculated as:\n * fee = max(current_fee_per_byte * transaction_size_in_bytes, min_fee)\n *\n * Under normal (non-congested) conditions, current_fee_per_byte is 0,\n * so fee = min_fee = 1000 µAlgo (0.001 ALGO).\n *\n * During network congestion, fees can rise. This constant is set to 5x\n * the minimum fee (5000 µAlgo) as a reasonable upper bound per transaction.\n *\n * For fee payer transactions that cover an entire group via fee pooling,\n * use `maxReasonableGroupFee(groupSize)` which multiplies this per-txn\n * cap by the number of transactions in the group.\n */\nexport const MAX_REASONABLE_FEE_PER_TXN = 5000;\n\n/**\n * Calculates the maximum reasonable fee for a fee payer transaction\n * that covers an entire atomic group via fee pooling.\n *\n * @param groupSize - Number of transactions in the atomic group\n * @returns Maximum acceptable fee in microAlgos\n */\nexport function maxReasonableGroupFee(groupSize: number): number {\n return MAX_REASONABLE_FEE_PER_TXN * groupSize;\n}\n\n// Address validation: use isValidAddress() from @algorandfoundation/algokit-utils/common\n// Address length: use ALGORAND_ADDRESS_LENGTH from @algorandfoundation/algokit-utils/common\n","/**\n * AVM (Algorand) Utilities for x402 Payment Protocol\n *\n * Provides utility functions for Algod client creation, transaction encoding/decoding,\n * address validation, and network identification.\n */\n\nimport {\n decodeTransaction as decodeUnsignedTxn,\n decodeSignedTransaction as decodeSignedTxn,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport { isValidAddress } from \"@algorandfoundation/algokit-utils/common\";\nimport {\n ALGORAND_MAINNET_GENESIS_HASH,\n ALGORAND_TESTNET_GENESIS_HASH,\n ALGORAND_TESTNET_CAIP2,\n} from \"./constants\";\n\n/**\n * Encodes transaction bytes to base64 string\n *\n * @param txn - Transaction bytes (Uint8Array)\n * @returns Base64 encoded string\n */\nexport function encodeTransaction(txn: Uint8Array): string {\n return Buffer.from(txn).toString(\"base64\");\n}\n\n/**\n * Decodes a base64 encoded transaction to bytes\n *\n * @param encoded - Base64 encoded transaction string\n * @returns Transaction bytes (Uint8Array)\n */\nexport function decodeTransaction(encoded: string): Uint8Array {\n return new Uint8Array(Buffer.from(encoded, \"base64\"));\n}\n\n/**\n * Decodes a signed transaction from base64 msgpack\n *\n * @param encoded - Base64 encoded signed transaction\n * @returns Decoded signed transaction object\n */\nexport function decodeSignedTransaction(encoded: string) {\n const bytes = decodeTransaction(encoded);\n return decodeSignedTxn(bytes);\n}\n\n/**\n * Decodes an unsigned transaction from base64 msgpack\n *\n * @param encoded - Base64 encoded unsigned transaction\n * @returns Decoded transaction object\n */\nexport function decodeUnsignedTransaction(encoded: string) {\n const bytes = decodeTransaction(encoded);\n return decodeUnsignedTxn(bytes);\n}\n\n/**\n * Validates an Algorand address\n *\n * Uses isValidAddress from algokit-utils which performs full checksum validation.\n *\n * @param address - The address to validate\n * @returns True if the address is valid\n */\nexport function isValidAlgorandAddress(address: string): boolean {\n return isValidAddress(address);\n}\n\n/**\n * Gets the sender address from a transaction (signed or unsigned)\n *\n * @param txnBytes - Transaction bytes\n * @param isSigned - Whether the transaction is signed (default: true)\n * @returns Sender address string\n */\nexport function getSenderFromTransaction(txnBytes: Uint8Array, isSigned: boolean = true): string {\n if (isSigned) {\n const signedTxn = decodeSignedTxn(txnBytes);\n return signedTxn.txn.sender.toString();\n }\n const txn = decodeUnsignedTxn(txnBytes);\n return txn.sender.toString();\n}\n\n/**\n * Converts a decimal amount to atomic units (token's smallest unit)\n *\n * @param decimalAmount - The decimal amount as a string (e.g., \"1.50\")\n * @param decimals - Number of decimal places (e.g., 6 for USDC)\n * @returns Amount in atomic units as a string\n *\n * @example\n * ```typescript\n * convertToTokenAmount(\"1.50\", 6) // Returns \"1500000\"\n * convertToTokenAmount(\"0.10\", 6) // Returns \"100000\"\n * ```\n */\nexport function convertToTokenAmount(decimalAmount: string, decimals: number): string {\n const amount = parseFloat(decimalAmount);\n if (isNaN(amount)) {\n throw new Error(`Invalid amount: ${decimalAmount}`);\n }\n\n // Handle decimal conversion properly\n const [intPart, decPart = \"\"] = String(amount).split(\".\");\n const paddedDec = decPart.padEnd(decimals, \"0\").slice(0, decimals);\n const tokenAmount = (intPart + paddedDec).replace(/^0+/, \"\") || \"0\";\n\n return tokenAmount;\n}\n\n/**\n * Converts atomic units to decimal amount\n *\n * @param atomicAmount - Amount in atomic units (string or bigint)\n * @param decimals - Number of decimal places\n * @returns Decimal amount as a string\n */\nexport function convertFromTokenAmount(atomicAmount: string | bigint, decimals: number): string {\n const amount = BigInt(atomicAmount);\n const divisor = BigInt(10 ** decimals);\n const intPart = amount / divisor;\n const decPart = amount % divisor;\n\n if (decPart === BigInt(0)) {\n return intPart.toString();\n }\n\n const decStr = decPart.toString().padStart(decimals, \"0\");\n // Remove trailing zeros\n const trimmedDec = decStr.replace(/0+$/, \"\");\n return `${intPart}.${trimmedDec}`;\n}\n\n/**\n * Gets the network type from a CAIP-2 identifier\n *\n * @param caip2 - CAIP-2 network identifier\n * @returns Network type (\"mainnet\" | \"testnet\") or null if unknown\n */\nexport function getNetworkFromCaip2(caip2: string): \"mainnet\" | \"testnet\" | null {\n if (!caip2.startsWith(\"algorand:\")) {\n return null;\n }\n\n const genesisHash = caip2.slice(\"algorand:\".length);\n\n if (genesisHash === ALGORAND_MAINNET_GENESIS_HASH) {\n return \"mainnet\";\n }\n if (genesisHash === ALGORAND_TESTNET_GENESIS_HASH) {\n return \"testnet\";\n }\n\n return null;\n}\n\n/**\n * Checks if a network identifier is an Algorand network\n *\n * @param network - Network identifier (CAIP-2 format)\n * @returns True if the network is an Algorand network\n */\nexport function isAlgorandNetwork(network: string): boolean {\n return network.startsWith(\"algorand:\");\n}\n\n/**\n * Checks if a network identifier is a testnet\n *\n * @param network - Network identifier (CAIP-2 format)\n * @returns True if the network is a testnet\n */\nexport function isTestnetNetwork(network: string): boolean {\n return network === ALGORAND_TESTNET_CAIP2;\n}\n\n/**\n * Gets the genesis hash from a transaction\n *\n * @param txn - The transaction object\n * @param txn.genesisHash - The genesis hash bytes\n * @returns Base64 encoded genesis hash\n */\nexport function getGenesisHashFromTransaction(txn: { genesisHash?: Uint8Array }): string {\n if (!txn.genesisHash) {\n throw new Error(\"Transaction does not have a genesis hash\");\n }\n return Buffer.from(txn.genesisHash).toString(\"base64\");\n}\n\n/**\n * Validates that all transactions in a group have the same group ID\n *\n * @param txns - Array of transaction bytes\n * @returns True if all transactions have matching group IDs\n */\nexport function validateGroupId(txns: Uint8Array[]): boolean {\n if (txns.length <= 1) {\n return true;\n }\n\n let expectedGroupId: string | null = null;\n\n for (const txnBytes of txns) {\n const txn = decodeUnsignedTxn(txnBytes);\n const groupId = txn.group ? Buffer.from(txn.group).toString(\"base64\") : null;\n\n if (expectedGroupId === null) {\n expectedGroupId = groupId;\n } else if (groupId !== expectedGroupId) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Extracts the transaction ID from signed transaction bytes\n *\n * @param signedTxnBytes - Signed transaction bytes\n * @returns Transaction ID string\n */\nexport function getTransactionId(signedTxnBytes: Uint8Array): string {\n const signedTxn = decodeSignedTxn(signedTxnBytes);\n return signedTxn.txn.txId();\n}\n\n/**\n * Checks if a signed transaction has a valid signature\n *\n * @param signedTxnBytes - Signed transaction bytes\n * @returns True if the transaction has a signature\n */\nexport function hasSignature(signedTxnBytes: Uint8Array): boolean {\n const signedTxn = decodeSignedTxn(signedTxnBytes);\n return (\n signedTxn.sig !== undefined || signedTxn.lsig !== undefined || signedTxn.msig !== undefined\n );\n}\n\n// Re-export algokit-utils types that consumers may need\nexport { Address, encodeAddress, decodeAddress } from \"@algorandfoundation/algokit-utils/common\";\n","/**\n * AVM Server Scheme for Exact Payment Protocol\n *\n * Parses prices and builds payment requirements for Algorand ASA transfers.\n */\n\nimport type {\n AssetAmount,\n Network,\n PaymentRequirements,\n Price,\n SchemeNetworkServer,\n MoneyParser,\n} from \"@x402/core/types\";\nimport { USDC_CONFIG, USDC_DECIMALS } from \"../../constants\";\nimport { convertToTokenAmount } from \"../../utils\";\n\n/**\n * AVM server implementation for the Exact payment scheme.\n *\n * Handles price parsing and payment requirements enhancement for Algorand networks.\n */\nexport class ExactAvmScheme implements SchemeNetworkServer {\n readonly scheme = \"exact\";\n private moneyParsers: MoneyParser[] = [];\n\n /**\n * Register a custom money parser in the parser chain.\n * Multiple parsers can be registered - they will be tried in registration order.\n * Each parser receives a decimal amount (e.g., 1.50 for $1.50).\n * If a parser returns null, the next parser in the chain will be tried.\n * The default parser is always the final fallback.\n *\n * @param parser - Custom function to convert amount to AssetAmount (or null to skip)\n * @returns The server instance for chaining\n *\n * @example\n * ```typescript\n * avmServer.registerMoneyParser(async (amount, network) => {\n * // Custom conversion logic for non-USDC assets\n * if (amount > 100) {\n * return { amount: (amount * 1e6).toString(), asset: \"12345678\" };\n * }\n * return null; // Use next parser\n * });\n * ```\n */\n registerMoneyParser(parser: MoneyParser): ExactAvmScheme {\n this.moneyParsers.push(parser);\n return this;\n }\n\n /**\n * Parses a price into an asset amount.\n * If price is already an AssetAmount, returns it directly.\n * If price is Money (string | number), parses to decimal and tries custom parsers.\n * Falls back to default conversion if all custom parsers return null.\n *\n * @param price - The price to parse\n * @param network - The network to use\n * @returns Promise that resolves to the parsed asset amount\n */\n async parsePrice(price: Price, network: Network): Promise<AssetAmount> {\n // If already an AssetAmount, return it directly\n if (typeof price === \"object\" && price !== null && \"amount\" in price) {\n if (!price.asset) {\n throw new Error(`Asset ID must be specified for AssetAmount on network ${network}`);\n }\n return {\n amount: price.amount,\n asset: price.asset,\n extra: price.extra || {},\n };\n }\n\n // Parse Money to decimal number\n const amount = this.parseMoneyToDecimal(price);\n\n // Try each custom money parser in order\n for (const parser of this.moneyParsers) {\n const result = await parser(amount, network);\n if (result !== null) {\n return result;\n }\n }\n\n // All custom parsers returned null, use default conversion\n return this.defaultMoneyConversion(amount, network);\n }\n\n /**\n * Build payment requirements for this scheme/network combination\n *\n * @param paymentRequirements - The base payment requirements\n * @param supportedKind - The supported kind from facilitator (contains extra data like feePayer)\n * @param supportedKind.x402Version - The x402 version\n * @param supportedKind.scheme - The logical payment scheme\n * @param supportedKind.network - The network identifier in CAIP-2 format\n * @param supportedKind.extra - Optional extra metadata (e.g., feePayer address)\n * @param extensionKeys - Extension keys supported by the facilitator\n * @returns Payment requirements ready to be sent to clients\n */\n enhancePaymentRequirements(\n paymentRequirements: PaymentRequirements,\n supportedKind: {\n x402Version: number;\n scheme: string;\n network: Network;\n extra?: Record<string, unknown>;\n },\n extensionKeys: string[],\n ): Promise<PaymentRequirements> {\n // Mark unused parameter\n void extensionKeys;\n\n // Get USDC config for the network\n const usdcConfig = USDC_CONFIG[supportedKind.network];\n const decimals = usdcConfig?.decimals ?? USDC_DECIMALS;\n\n // Build enhanced requirements with feePayer and decimals\n const enhanced: PaymentRequirements = {\n ...paymentRequirements,\n extra: {\n ...paymentRequirements.extra,\n decimals,\n },\n };\n\n // Add feePayer from supportedKind.extra if provided\n if (supportedKind.extra?.feePayer) {\n enhanced.extra = {\n ...enhanced.extra,\n feePayer: supportedKind.extra.feePayer,\n };\n }\n\n return Promise.resolve(enhanced);\n }\n\n /**\n * Parse Money (string | number) to a decimal number.\n * Handles formats like \"$1.50\", \"1.50\", 1.50, etc.\n *\n * @param money - The money value to parse\n * @returns Decimal number\n */\n private parseMoneyToDecimal(money: string | number): number {\n if (typeof money === \"number\") {\n return money;\n }\n\n // Remove $ sign and whitespace, then parse\n const cleanMoney = money.replace(/^\\$/, \"\").trim();\n const amount = parseFloat(cleanMoney);\n\n if (isNaN(amount)) {\n throw new Error(`Invalid money format: ${money}`);\n }\n\n return amount;\n }\n\n /**\n * Default money conversion implementation.\n * Converts decimal amount to the default stablecoin (USDC) on the specified network.\n *\n * @param amount - The decimal amount (e.g., 1.50)\n * @param network - The network to use\n * @returns The parsed asset amount in USDC\n */\n private defaultMoneyConversion(amount: number, network: Network): AssetAmount {\n const assetInfo = this.getDefaultAsset(network);\n const tokenAmount = convertToTokenAmount(amount.toString(), assetInfo.decimals);\n\n return {\n amount: tokenAmount,\n asset: assetInfo.asaId,\n };\n }\n\n /**\n * Get the default asset info for a network (USDC)\n *\n * @param network - The network to get asset info for\n * @returns The asset information including ASA ID, name, and decimals\n */\n private getDefaultAsset(network: Network): {\n asaId: string;\n name: string;\n decimals: number;\n } {\n const assetInfo = USDC_CONFIG[network];\n if (!assetInfo) {\n throw new Error(`No default asset configured for network ${network}`);\n }\n\n return assetInfo;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACeO,IAAM,yBAAyB;AAM/B,IAAM,yBAAyB;AA8B/B,IAAM,sBAAsB;AAO5B,IAAM,sBAAsB;AAK5B,IAAM,gBAAgB;AAKtB,IAAM,cAAiF;AAAA,EAC5F,CAAC,sBAAsB,GAAG;AAAA,IACxB,OAAO;AAAA,IACP,MAAM;AAAA,IACN,UAAU;AAAA,EACZ;AAAA,EACA,CAAC,sBAAsB,GAAG;AAAA,IACxB,OAAO;AAAA,IACP,MAAM;AAAA,IACN,UAAU;AAAA,EACZ;AACF;;;ACxEA,sBAGO;AACP,oBAA+B;AA4O/B,IAAAA,iBAAsD;AAlJ/C,SAAS,qBAAqB,eAAuB,UAA0B;AACpF,QAAM,SAAS,WAAW,aAAa;AACvC,MAAI,MAAM,MAAM,GAAG;AACjB,UAAM,IAAI,MAAM,mBAAmB,aAAa,EAAE;AAAA,EACpD;AAGA,QAAM,CAAC,SAAS,UAAU,EAAE,IAAI,OAAO,MAAM,EAAE,MAAM,GAAG;AACxD,QAAM,YAAY,QAAQ,OAAO,UAAU,GAAG,EAAE,MAAM,GAAG,QAAQ;AACjE,QAAM,eAAe,UAAU,WAAW,QAAQ,OAAO,EAAE,KAAK;AAEhE,SAAO;AACT;;;AC3FO,IAAM,iBAAN,MAAoD;AAAA,EAApD;AACL,SAAS,SAAS;AAClB,SAAQ,eAA8B,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBvC,oBAAoB,QAAqC;AACvD,SAAK,aAAa,KAAK,MAAM;AAC7B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,WAAW,OAAc,SAAwC;AAErE,QAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,YAAY,OAAO;AACpE,UAAI,CAAC,MAAM,OAAO;AAChB,cAAM,IAAI,MAAM,yDAAyD,OAAO,EAAE;AAAA,MACpF;AACA,aAAO;AAAA,QACL,QAAQ,MAAM;AAAA,QACd,OAAO,MAAM;AAAA,QACb,OAAO,MAAM,SAAS,CAAC;AAAA,MACzB;AAAA,IACF;AAGA,UAAM,SAAS,KAAK,oBAAoB,KAAK;AAG7C,eAAW,UAAU,KAAK,cAAc;AACtC,YAAM,SAAS,MAAM,OAAO,QAAQ,OAAO;AAC3C,UAAI,WAAW,MAAM;AACnB,eAAO;AAAA,MACT;AAAA,IACF;AAGA,WAAO,KAAK,uBAAuB,QAAQ,OAAO;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,2BACE,qBACA,eAMA,eAC8B;AAE9B,SAAK;AAGL,UAAM,aAAa,YAAY,cAAc,OAAO;AACpD,UAAM,WAAW,YAAY,YAAY;AAGzC,UAAM,WAAgC;AAAA,MACpC,GAAG;AAAA,MACH,OAAO;AAAA,QACL,GAAG,oBAAoB;AAAA,QACvB;AAAA,MACF;AAAA,IACF;AAGA,QAAI,cAAc,OAAO,UAAU;AACjC,eAAS,QAAQ;AAAA,QACf,GAAG,SAAS;AAAA,QACZ,UAAU,cAAc,MAAM;AAAA,MAChC;AAAA,IACF;AAEA,WAAO,QAAQ,QAAQ,QAAQ;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,oBAAoB,OAAgC;AAC1D,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO;AAAA,IACT;AAGA,UAAM,aAAa,MAAM,QAAQ,OAAO,EAAE,EAAE,KAAK;AACjD,UAAM,SAAS,WAAW,UAAU;AAEpC,QAAI,MAAM,MAAM,GAAG;AACjB,YAAM,IAAI,MAAM,yBAAyB,KAAK,EAAE;AAAA,IAClD;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,uBAAuB,QAAgB,SAA+B;AAC5E,UAAM,YAAY,KAAK,gBAAgB,OAAO;AAC9C,UAAM,cAAc,qBAAqB,OAAO,SAAS,GAAG,UAAU,QAAQ;AAE9E,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,OAAO,UAAU;AAAA,IACnB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,gBAAgB,SAItB;AACA,UAAM,YAAY,YAAY,OAAO;AACrC,QAAI,CAAC,WAAW;AACd,YAAM,IAAI,MAAM,2CAA2C,OAAO,EAAE;AAAA,IACtE;AAEA,WAAO;AAAA,EACT;AACF;","names":["import_common"]}
{"version":3,"sources":["../../../../src/exact/server/index.ts","../../../../src/exact/server/scheme.ts","../../../../src/constants.ts"],"sourcesContent":["export { ExactAvmScheme } from \"./scheme\";\n","/**\n * AVM Server Scheme for Exact Payment Protocol\n *\n * Parses prices and builds payment requirements for Algorand ASA transfers.\n */\n\nimport type {\n AssetAmount,\n Network,\n PaymentRequirements,\n Price,\n SchemeNetworkServer,\n MoneyParser,\n} from \"@x402/core/types\";\nimport { convertToTokenAmount, numberToDecimalString } from \"@x402/core/utils\";\nimport { USDC_CONFIG, USDC_DECIMALS } from \"../../constants\";\n\n/**\n * AVM server implementation for the Exact payment scheme.\n *\n * Handles price parsing and payment requirements enhancement for Algorand networks.\n */\nexport class ExactAvmScheme implements SchemeNetworkServer {\n readonly scheme = \"exact\";\n private moneyParsers: MoneyParser[] = [];\n\n /**\n * Register a custom money parser in the parser chain.\n * Multiple parsers can be registered - they will be tried in registration order.\n * Each parser receives a decimal amount (e.g., 1.50 for $1.50).\n * If a parser returns null, the next parser in the chain will be tried.\n * The default parser is always the final fallback.\n *\n * @param parser - Custom function to convert amount to AssetAmount (or null to skip)\n * @returns The server instance for chaining\n *\n * @example\n * ```typescript\n * avmServer.registerMoneyParser(async (amount, network) => {\n * // Custom conversion logic for non-USDC assets\n * if (amount > 100) {\n * return { amount: (amount * 1e6).toString(), asset: \"12345678\" };\n * }\n * return null; // Use next parser\n * });\n * ```\n */\n registerMoneyParser(parser: MoneyParser): ExactAvmScheme {\n this.moneyParsers.push(parser);\n return this;\n }\n\n /**\n * Parses a price into an asset amount.\n * If price is already an AssetAmount, returns it directly.\n * If price is Money (string | number), parses to decimal and tries custom parsers.\n * Falls back to default conversion if all custom parsers return null.\n *\n * @param price - The price to parse\n * @param network - The network to use\n * @returns Promise that resolves to the parsed asset amount\n */\n async parsePrice(price: Price, network: Network): Promise<AssetAmount> {\n // If already an AssetAmount, return it directly\n if (typeof price === \"object\" && price !== null && \"amount\" in price) {\n if (!price.asset) {\n throw new Error(`Asset ID must be specified for AssetAmount on network ${network}`);\n }\n return {\n amount: price.amount,\n asset: price.asset,\n extra: price.extra || {},\n };\n }\n\n // Parse Money to decimal number\n const amount = this.parseMoneyToDecimal(price);\n\n // Try each custom money parser in order\n for (const parser of this.moneyParsers) {\n const result = await parser(amount, network);\n if (result !== null) {\n return result;\n }\n }\n\n // All custom parsers returned null, use default conversion\n return this.defaultMoneyConversion(amount, network);\n }\n\n /**\n * Build payment requirements for this scheme/network combination\n *\n * @param paymentRequirements - The base payment requirements\n * @param supportedKind - The supported kind from facilitator (contains extra data like feePayer)\n * @param supportedKind.x402Version - The x402 version\n * @param supportedKind.scheme - The logical payment scheme\n * @param supportedKind.network - The network identifier in CAIP-2 format\n * @param supportedKind.extra - Optional extra metadata (e.g., feePayer address)\n * @param extensionKeys - Extension keys supported by the facilitator\n * @returns Payment requirements ready to be sent to clients\n */\n enhancePaymentRequirements(\n paymentRequirements: PaymentRequirements,\n supportedKind: {\n x402Version: number;\n scheme: string;\n network: Network;\n extra?: Record<string, unknown>;\n },\n extensionKeys: string[],\n ): Promise<PaymentRequirements> {\n // Mark unused parameter\n void extensionKeys;\n\n // Get USDC config for the network\n const usdcConfig = USDC_CONFIG[supportedKind.network];\n const decimals = usdcConfig?.decimals ?? USDC_DECIMALS;\n\n // Build enhanced requirements with feePayer and decimals\n const enhanced: PaymentRequirements = {\n ...paymentRequirements,\n extra: {\n ...paymentRequirements.extra,\n decimals,\n },\n };\n\n // Add feePayer from supportedKind.extra if provided\n if (supportedKind.extra?.feePayer) {\n enhanced.extra = {\n ...enhanced.extra,\n feePayer: supportedKind.extra.feePayer,\n };\n }\n\n return Promise.resolve(enhanced);\n }\n\n /**\n * Parse Money (string | number) to a decimal number.\n * Handles formats like \"$1.50\", \"1.50\", 1.50, etc.\n *\n * @param money - The money value to parse\n * @returns Decimal number\n */\n private parseMoneyToDecimal(money: string | number): number {\n if (typeof money === \"number\") {\n return money;\n }\n\n // Remove $ sign and whitespace, then parse\n const cleanMoney = money.replace(/^\\$/, \"\").trim();\n const amount = parseFloat(cleanMoney);\n\n if (isNaN(amount)) {\n throw new Error(`Invalid money format: ${money}`);\n }\n\n return amount;\n }\n\n /**\n * Default money conversion implementation.\n * Converts decimal amount to the default stablecoin (USDC) on the specified network.\n *\n * @param amount - The decimal amount (e.g., 1.50)\n * @param network - The network to use\n * @returns The parsed asset amount in USDC\n */\n private defaultMoneyConversion(amount: number, network: Network): AssetAmount {\n const assetInfo = this.getDefaultAsset(network);\n const tokenAmount = convertToTokenAmount(numberToDecimalString(amount), assetInfo.decimals);\n\n return {\n amount: tokenAmount,\n asset: assetInfo.asaId,\n };\n }\n\n /**\n * Get the default asset info for a network (USDC)\n *\n * @param network - The network to get asset info for\n * @returns The asset information including ASA ID, name, and decimals\n */\n private getDefaultAsset(network: Network): {\n asaId: string;\n name: string;\n decimals: number;\n } {\n const assetInfo = USDC_CONFIG[network];\n if (!assetInfo) {\n throw new Error(`No default asset configured for network ${network}`);\n }\n\n return assetInfo;\n }\n}\n","/**\n * Algorand Network Constants for x402 AVM Implementation\n *\n * CAIP-2 Network Identifiers use the format: algorand:<genesis-hash-base64>\n * Genesis hashes uniquely identify Algorand networks.\n */\n\n// ============================================================================\n// CAIP-2 Network Identifiers (V2)\n// ============================================================================\n\n/**\n * CAIP-2 network identifier for Algorand Mainnet\n * Format: algorand:<genesis-hash-base64>\n */\nexport const ALGORAND_MAINNET_CAIP2 = \"algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=\";\n\n/**\n * CAIP-2 network identifier for Algorand Testnet\n * Format: algorand:<genesis-hash-base64>\n */\nexport const ALGORAND_TESTNET_CAIP2 = \"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=\";\n\n/**\n * All supported CAIP-2 network identifiers\n */\nexport const CAIP2_NETWORKS = [ALGORAND_MAINNET_CAIP2, ALGORAND_TESTNET_CAIP2] as const;\n\n// ============================================================================\n// Genesis Hashes\n// ============================================================================\n\n/**\n * Algorand Mainnet genesis hash (base64 encoded)\n */\nexport const ALGORAND_MAINNET_GENESIS_HASH = \"wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=\";\n\n/**\n * Algorand Testnet genesis hash (base64 encoded)\n */\nexport const ALGORAND_TESTNET_GENESIS_HASH = \"SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=\";\n\n// ============================================================================\n// USDC ASA (Algorand Standard Asset) Configuration\n// ============================================================================\n\n/**\n * USDC ASA ID on Algorand Mainnet\n *\n * @see https://algoexplorer.io/asset/31566704\n */\nexport const USDC_MAINNET_ASA_ID = \"31566704\";\n\n/**\n * USDC ASA ID on Algorand Testnet\n *\n * @see https://testnet.algoexplorer.io/asset/10458941\n */\nexport const USDC_TESTNET_ASA_ID = \"10458941\";\n\n/**\n * USDC decimals (same across all networks)\n */\nexport const USDC_DECIMALS = 6;\n\n/**\n * USDC configuration per network\n */\nexport const USDC_CONFIG: Record<string, { asaId: string; name: string; decimals: number }> = {\n [ALGORAND_MAINNET_CAIP2]: {\n asaId: USDC_MAINNET_ASA_ID,\n name: \"USDC\",\n decimals: USDC_DECIMALS,\n },\n [ALGORAND_TESTNET_CAIP2]: {\n asaId: USDC_TESTNET_ASA_ID,\n name: \"USDC\",\n decimals: USDC_DECIMALS,\n },\n};\n\n// ============================================================================\n// Transaction Limits\n// ============================================================================\n\n/**\n * Maximum reasonable fee per transaction in microAlgos (5000 µAlgo).\n *\n * Algorand transaction fees are calculated as:\n * fee = max(current_fee_per_byte * transaction_size_in_bytes, min_fee)\n *\n * Under normal (non-congested) conditions, current_fee_per_byte is 0,\n * so fee = min_fee = 1000 µAlgo (0.001 ALGO).\n *\n * During network congestion, fees can rise. This constant is set to 5x\n * the minimum fee (5000 µAlgo) as a reasonable upper bound per transaction.\n *\n * For fee payer transactions that cover an entire group via fee pooling,\n * use `maxReasonableGroupFee(groupSize)` which multiplies this per-txn\n * cap by the number of transactions in the group.\n */\nexport const MAX_REASONABLE_FEE_PER_TXN = 5000;\n\n/**\n * Calculates the maximum reasonable fee for a fee payer transaction\n * that covers an entire atomic group via fee pooling.\n *\n * @param groupSize - Number of transactions in the atomic group\n * @returns Maximum acceptable fee in microAlgos\n */\nexport function maxReasonableGroupFee(groupSize: number): number {\n return MAX_REASONABLE_FEE_PER_TXN * groupSize;\n}\n\n// Address validation: use isValidAddress() from @algorandfoundation/algokit-utils/common\n// Address length: use ALGORAND_ADDRESS_LENGTH from @algorandfoundation/algokit-utils/common\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACcA,mBAA4D;;;ACCrD,IAAM,yBAAyB;AAM/B,IAAM,yBAAyB;AA8B/B,IAAM,sBAAsB;AAO5B,IAAM,sBAAsB;AAK5B,IAAM,gBAAgB;AAKtB,IAAM,cAAiF;AAAA,EAC5F,CAAC,sBAAsB,GAAG;AAAA,IACxB,OAAO;AAAA,IACP,MAAM;AAAA,IACN,UAAU;AAAA,EACZ;AAAA,EACA,CAAC,sBAAsB,GAAG;AAAA,IACxB,OAAO;AAAA,IACP,MAAM;AAAA,IACN,UAAU;AAAA,EACZ;AACF;;;ADzDO,IAAM,iBAAN,MAAoD;AAAA,EAApD;AACL,SAAS,SAAS;AAClB,SAAQ,eAA8B,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBvC,oBAAoB,QAAqC;AACvD,SAAK,aAAa,KAAK,MAAM;AAC7B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,WAAW,OAAc,SAAwC;AAErE,QAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,YAAY,OAAO;AACpE,UAAI,CAAC,MAAM,OAAO;AAChB,cAAM,IAAI,MAAM,yDAAyD,OAAO,EAAE;AAAA,MACpF;AACA,aAAO;AAAA,QACL,QAAQ,MAAM;AAAA,QACd,OAAO,MAAM;AAAA,QACb,OAAO,MAAM,SAAS,CAAC;AAAA,MACzB;AAAA,IACF;AAGA,UAAM,SAAS,KAAK,oBAAoB,KAAK;AAG7C,eAAW,UAAU,KAAK,cAAc;AACtC,YAAM,SAAS,MAAM,OAAO,QAAQ,OAAO;AAC3C,UAAI,WAAW,MAAM;AACnB,eAAO;AAAA,MACT;AAAA,IACF;AAGA,WAAO,KAAK,uBAAuB,QAAQ,OAAO;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,2BACE,qBACA,eAMA,eAC8B;AAE9B,SAAK;AAGL,UAAM,aAAa,YAAY,cAAc,OAAO;AACpD,UAAM,WAAW,YAAY,YAAY;AAGzC,UAAM,WAAgC;AAAA,MACpC,GAAG;AAAA,MACH,OAAO;AAAA,QACL,GAAG,oBAAoB;AAAA,QACvB;AAAA,MACF;AAAA,IACF;AAGA,QAAI,cAAc,OAAO,UAAU;AACjC,eAAS,QAAQ;AAAA,QACf,GAAG,SAAS;AAAA,QACZ,UAAU,cAAc,MAAM;AAAA,MAChC;AAAA,IACF;AAEA,WAAO,QAAQ,QAAQ,QAAQ;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,oBAAoB,OAAgC;AAC1D,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO;AAAA,IACT;AAGA,UAAM,aAAa,MAAM,QAAQ,OAAO,EAAE,EAAE,KAAK;AACjD,UAAM,SAAS,WAAW,UAAU;AAEpC,QAAI,MAAM,MAAM,GAAG;AACjB,YAAM,IAAI,MAAM,yBAAyB,KAAK,EAAE;AAAA,IAClD;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,uBAAuB,QAAgB,SAA+B;AAC5E,UAAM,YAAY,KAAK,gBAAgB,OAAO;AAC9C,UAAM,kBAAc,uCAAqB,oCAAsB,MAAM,GAAG,UAAU,QAAQ;AAE1F,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,OAAO,UAAU;AAAA,IACnB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,gBAAgB,SAItB;AACA,UAAM,YAAY,YAAY,OAAO;AACrC,QAAI,CAAC,WAAW;AACd,YAAM,IAAI,MAAM,2CAA2C,OAAO,EAAE;AAAA,IACtE;AAEA,WAAO;AAAA,EACT;AACF;","names":[]}

@@ -7,2 +7,3 @@ export { ExactAvmScheme } from './exact/client/index.js';

export { ALGORAND_MIN_TX_FEE } from '@algorandfoundation/algokit-utils/amount';
export { convertToTokenAmount } from '@x402/core/utils';
import '@x402/core/types';

@@ -183,17 +184,4 @@ import '@algorandfoundation/algokit-utils/algod-client';

declare function getSenderFromTransaction(txnBytes: Uint8Array, isSigned?: boolean): string;
/**
* Converts a decimal amount to atomic units (token's smallest unit)
*
* @param decimalAmount - The decimal amount as a string (e.g., "1.50")
* @param decimals - Number of decimal places (e.g., 6 for USDC)
* @returns Amount in atomic units as a string
*
* @example
* ```typescript
* convertToTokenAmount("1.50", 6) // Returns "1500000"
* convertToTokenAmount("0.10", 6) // Returns "100000"
* ```
*/
declare function convertToTokenAmount(decimalAmount: string, decimals: number): string;
/**
* Converts atomic units to decimal amount

@@ -259,2 +247,2 @@ *

export { ALGORAND_MAINNET_CAIP2, ALGORAND_MAINNET_GENESIS_HASH, ALGORAND_TESTNET_CAIP2, ALGORAND_TESTNET_GENESIS_HASH, CAIP2_NETWORKS, type ExactAvmPayloadV2, MAX_REASONABLE_FEE_PER_TXN, USDC_CONFIG, USDC_DECIMALS, USDC_MAINNET_ASA_ID, USDC_TESTNET_ASA_ID, convertFromTokenAmount, convertToTokenAmount, decodeSignedTransaction, decodeTransaction, decodeUnsignedTransaction, encodeTransaction, getGenesisHashFromTransaction, getNetworkFromCaip2, getSenderFromTransaction, getTransactionId, hasSignature, isAlgorandNetwork, isExactAvmPayload, isTestnetNetwork, isValidAlgorandAddress, maxReasonableGroupFee, validateGroupId };
export { ALGORAND_MAINNET_CAIP2, ALGORAND_MAINNET_GENESIS_HASH, ALGORAND_TESTNET_CAIP2, ALGORAND_TESTNET_GENESIS_HASH, CAIP2_NETWORKS, type ExactAvmPayloadV2, MAX_REASONABLE_FEE_PER_TXN, USDC_CONFIG, USDC_DECIMALS, USDC_MAINNET_ASA_ID, USDC_TESTNET_ASA_ID, convertFromTokenAmount, decodeSignedTransaction, decodeTransaction, decodeUnsignedTransaction, encodeTransaction, getGenesisHashFromTransaction, getNetworkFromCaip2, getSenderFromTransaction, getTransactionId, hasSignature, isAlgorandNetwork, isExactAvmPayload, isTestnetNetwork, isValidAlgorandAddress, maxReasonableGroupFee, validateGroupId };

@@ -38,3 +38,3 @@ "use strict";

convertFromTokenAmount: () => convertFromTokenAmount,
convertToTokenAmount: () => convertToTokenAmount,
convertToTokenAmount: () => import_utils.convertToTokenAmount,
decodeSignedTransaction: () => decodeSignedTransaction,

@@ -98,2 +98,3 @@ decodeTransaction: () => decodeTransaction,

// src/utils.ts
var import_utils = require("@x402/core/utils");
var import_common2 = require("@algorandfoundation/algokit-utils/common");

@@ -125,12 +126,2 @@ function encodeTransaction(txn) {

}
function convertToTokenAmount(decimalAmount, decimals) {
const amount = parseFloat(decimalAmount);
if (isNaN(amount)) {
throw new Error(`Invalid amount: ${decimalAmount}`);
}
const [intPart, decPart = ""] = String(amount).split(".");
const paddedDec = decPart.padEnd(decimals, "0").slice(0, decimals);
const tokenAmount = (intPart + paddedDec).replace(/^0+/, "") || "0";
return tokenAmount;
}
function convertFromTokenAmount(atomicAmount, decimals) {

@@ -137,0 +128,0 @@ const amount = BigInt(atomicAmount);

@@ -1,1 +0,1 @@

{"version":3,"sources":["../../src/index.ts","../../src/exact/client/scheme.ts","../../src/utils.ts","../../src/constants.ts","../../src/signer.ts","../../src/types.ts"],"sourcesContent":["/**\n * @module @x402/avm - x402 Payment Protocol AVM (Algorand) Implementation\n *\n * This module provides the Algorand-specific implementation of the x402 payment protocol.\n *\n * @example Client signer:\n * ```typescript\n * import { toClientAvmSigner } from \"@x402/avm\";\n *\n * const signer = toClientAvmSigner(process.env.AVM_PRIVATE_KEY!);\n * ```\n *\n * @example Facilitator signer:\n * ```typescript\n * import { toFacilitatorAvmSigner } from \"@x402/avm\";\n *\n * const signer = toFacilitatorAvmSigner(process.env.AVM_PRIVATE_KEY!);\n * ```\n */\n\n// Exact scheme client\nexport { ExactAvmScheme } from \"./exact\";\n\n// Signer helpers and interfaces\nexport {\n isAvmSignerWallet,\n toClientAvmSigner,\n toFacilitatorAvmSigner,\n getAlgokitSigner,\n ALGOKIT_SIGNER,\n} from \"./signer\";\nexport type {\n ClientAvmSigner,\n ClientAvmConfig,\n FacilitatorAvmSigner,\n FacilitatorAvmSignerConfig,\n} from \"./signer\";\n\n// Re-export algokit-utils signer types for consumers who want native interop\nexport type {\n AddressWithTransactionSigner,\n AddressWithSigners,\n TransactionSigner,\n} from \"@algorandfoundation/algokit-utils/transact\";\n\n// Types\nexport type { ExactAvmPayloadV2 } from \"./types\";\nexport { isExactAvmPayload } from \"./types\";\n\n// Constants\nexport {\n // CAIP-2 Network Identifiers\n ALGORAND_MAINNET_CAIP2,\n ALGORAND_TESTNET_CAIP2,\n CAIP2_NETWORKS,\n // Genesis Hashes\n ALGORAND_MAINNET_GENESIS_HASH,\n ALGORAND_TESTNET_GENESIS_HASH,\n // USDC Configuration\n USDC_MAINNET_ASA_ID,\n USDC_TESTNET_ASA_ID,\n USDC_DECIMALS,\n USDC_CONFIG,\n // Transaction Limits\n MAX_REASONABLE_FEE_PER_TXN,\n maxReasonableGroupFee,\n} from \"./constants\";\n\n// Re-export algokit-utils constants that consumers may need\nexport { ALGORAND_ADDRESS_LENGTH } from \"@algorandfoundation/algokit-utils/common\";\nexport { ALGORAND_MIN_TX_FEE } from \"@algorandfoundation/algokit-utils/amount\";\n\n// Utilities\nexport {\n encodeTransaction,\n decodeTransaction,\n decodeSignedTransaction,\n decodeUnsignedTransaction,\n isValidAlgorandAddress,\n getSenderFromTransaction,\n convertToTokenAmount,\n convertFromTokenAmount,\n getNetworkFromCaip2,\n isAlgorandNetwork,\n isTestnetNetwork,\n getGenesisHashFromTransaction,\n validateGroupId,\n getTransactionId,\n hasSignature,\n} from \"./utils\";\n","/**\n * AVM Client Scheme for Exact Payment Protocol\n *\n * Creates atomic transaction groups for Algorand ASA transfers.\n * Uses AlgorandClient and TransactionComposer from algokit-utils v10\n * for transaction construction, fee pooling, and group management.\n */\n\nimport { AlgorandClient } from \"@algorandfoundation/algokit-utils/algorand-client\";\nimport {\n Transaction,\n encodeTransactionRaw,\n groupTransactions,\n makeEmptyTransactionSigner,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport { microAlgo } from \"@algorandfoundation/algokit-utils/amount\";\nimport type {\n PaymentRequirements,\n SchemeNetworkClient,\n PaymentPayloadResult,\n} from \"@x402/core/types\";\nimport type { ClientAvmSigner, ClientAvmConfig } from \"../../signer\";\nimport type { ExactAvmPayloadV2 } from \"../../types\";\nimport { encodeTransaction } from \"../../utils\";\nimport { USDC_CONFIG } from \"../../constants\";\nimport { isTestnetNetwork } from \"../../utils\";\n\n/**\n * AVM client implementation for the Exact payment scheme.\n *\n * Creates atomic transaction groups with ASA transfers for x402 payments.\n * Supports optional fee payer transactions for gasless payments.\n */\nexport class ExactAvmScheme implements SchemeNetworkClient {\n readonly scheme = \"exact\";\n\n /**\n * Creates a new ExactAvmScheme instance.\n *\n * @param signer - The AVM signer for client operations\n * @param config - Optional configuration for Algod client\n */\n constructor(\n private readonly signer: ClientAvmSigner,\n private readonly config?: ClientAvmConfig,\n ) {}\n\n /**\n * Creates a payment payload for the Exact scheme.\n *\n * Constructs an atomic transaction group with:\n * - Optional fee payer transaction (if feePayer specified in requirements.extra)\n * - ASA transfer transaction to payTo address\n *\n * Uses TransactionComposer for automatic suggested params, group ID assignment,\n * and fee pooling. For sponsored (gasless) transactions, exact fees are calculated\n * from actual encoded transaction sizes using the protocol fee formula:\n * fee = max(fee_per_byte × txn_size_in_bytes, min_fee)\n *\n * @param x402Version - The x402 protocol version\n * @param paymentRequirements - The payment requirements\n * @returns Promise resolving to a payment payload result\n */\n async createPaymentPayload(\n x402Version: number,\n paymentRequirements: PaymentRequirements,\n ): Promise<PaymentPayloadResult> {\n const { amount, asset, payTo, network, extra } = paymentRequirements;\n\n const algorandClient = this.getAlgorandClient(network);\n\n // Get asset ID (from requirements or default USDC)\n const assetId = this.getAssetId(asset, network);\n\n // Get fee payer address from extra if provided\n const feePayer = extra?.feePayer as string | undefined;\n let paymentIndex = 0;\n\n // Use an empty signer for building — we sign manually after\n // (fee payer txns stay unsigned for the facilitator to sign)\n const emptySigner = makeEmptyTransactionSigner();\n\n // Build the transaction group using TransactionComposer\n const composer = algorandClient.newGroup();\n\n if (feePayer) {\n // First pass: add fee payer with a placeholder fee.\n // The actual fee will be recalculated after build() using exact transaction sizes.\n composer.addPayment({\n sender: feePayer,\n receiver: feePayer,\n amount: microAlgo(0),\n note: `x402-fee-payer-${Date.now()}`,\n signer: emptySigner,\n });\n paymentIndex = 1;\n }\n\n composer.addAssetTransfer({\n sender: this.signer.address,\n receiver: payTo,\n assetId: BigInt(assetId),\n amount: BigInt(amount),\n staticFee: feePayer ? microAlgo(0) : undefined, // 0 fee when fee payer covers\n note: `x402-payment-v${x402Version}-${Date.now()}`,\n signer: emptySigner,\n });\n\n // Build transactions (assigns group ID, suggested params, fees)\n const built = await composer.build();\n let transactions = built.transactions.map(tws => tws.txn);\n\n // For sponsored transactions: recalculate the fee payer's fee using\n // exact encoded sizes of all transactions in the group.\n // Algorand fee formula: fee = max(fee_per_byte × txn_size, min_fee)\n //\n // After changing fees, the group ID must be recomputed because it is\n // derived from each transaction's encoded bytes (which include the fee).\n if (feePayer) {\n const sp = await algorandClient.getSuggestedParams();\n const feePerByte = Number(sp.fee);\n const minFee = Number(sp.minFee);\n\n // Calculate exact fee for each transaction based on its actual encoded size\n let totalGroupFee = BigInt(0);\n for (const txn of transactions) {\n const txnSize = encodeTransactionRaw(txn).length;\n const txnFee = feePerByte > 0 ? Math.max(feePerByte * txnSize, minFee) : minFee;\n totalGroupFee += BigInt(txnFee);\n }\n\n // Strip group ID, set correct fees, then re-group.\n // groupTransactions() requires group to be absent, computes the new group hash,\n // and returns new Transaction objects with the correct group ID.\n const ungrouped = transactions.map((txn, i) => {\n const fee = i === 0 ? totalGroupFee : BigInt(0);\n return new Transaction({ ...txn, fee, group: undefined });\n });\n transactions = groupTransactions(ungrouped);\n }\n\n // Encode all transactions to raw bytes\n const encodedTxns = transactions.map(txn => encodeTransactionRaw(txn));\n\n // Determine which transactions the client should sign\n const clientIndexes = transactions\n .map((txn, i) => (txn.sender.toString() === this.signer.address ? i : -1))\n .filter(i => i !== -1);\n\n // Sign client's transactions\n const signedTxns = await this.signer.signTransactions(encodedTxns, clientIndexes);\n\n // Build payment group with signed/unsigned transactions\n const paymentGroup: string[] = encodedTxns.map((txnBytes, i) => {\n const signedTxn = signedTxns[i];\n if (signedTxn) {\n return encodeTransaction(signedTxn);\n }\n // Return unsigned transaction for facilitator to sign\n return encodeTransaction(txnBytes);\n });\n\n const payload: ExactAvmPayloadV2 = {\n paymentGroup,\n paymentIndex,\n };\n\n return {\n x402Version,\n payload: payload as unknown as Record<string, unknown>,\n };\n }\n\n /**\n * Creates or retrieves an AlgorandClient for the given network.\n *\n * @param network - Network identifier (CAIP-2 format)\n * @returns AlgorandClient instance\n */\n private getAlgorandClient(network: string): AlgorandClient {\n if (this.config?.algorandClient) {\n return this.config.algorandClient;\n }\n if (this.config?.algodUrl) {\n return AlgorandClient.fromConfig({\n algodConfig: {\n server: this.config.algodUrl,\n token: this.config.algodToken ?? \"\",\n },\n });\n }\n // Auto-detect network\n return isTestnetNetwork(network) ? AlgorandClient.testNet() : AlgorandClient.mainNet();\n }\n\n /**\n * Gets the asset ID from the requirements or defaults to USDC\n *\n * @param asset - Asset identifier from requirements\n * @param network - Network identifier\n * @returns Asset ID as string\n */\n private getAssetId(asset: string, network: string): string {\n // If asset is already a numeric string, use it directly\n if (/^\\d+$/.test(asset)) {\n return asset;\n }\n\n // Try to get from USDC config\n const usdcConfig = USDC_CONFIG[network];\n if (usdcConfig) {\n return usdcConfig.asaId;\n }\n\n // Default to the asset as-is (might be an ASA ID)\n return asset;\n }\n}\n","/**\n * AVM (Algorand) Utilities for x402 Payment Protocol\n *\n * Provides utility functions for Algod client creation, transaction encoding/decoding,\n * address validation, and network identification.\n */\n\nimport {\n decodeTransaction as decodeUnsignedTxn,\n decodeSignedTransaction as decodeSignedTxn,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport { isValidAddress } from \"@algorandfoundation/algokit-utils/common\";\nimport {\n ALGORAND_MAINNET_GENESIS_HASH,\n ALGORAND_TESTNET_GENESIS_HASH,\n ALGORAND_TESTNET_CAIP2,\n} from \"./constants\";\n\n/**\n * Encodes transaction bytes to base64 string\n *\n * @param txn - Transaction bytes (Uint8Array)\n * @returns Base64 encoded string\n */\nexport function encodeTransaction(txn: Uint8Array): string {\n return Buffer.from(txn).toString(\"base64\");\n}\n\n/**\n * Decodes a base64 encoded transaction to bytes\n *\n * @param encoded - Base64 encoded transaction string\n * @returns Transaction bytes (Uint8Array)\n */\nexport function decodeTransaction(encoded: string): Uint8Array {\n return new Uint8Array(Buffer.from(encoded, \"base64\"));\n}\n\n/**\n * Decodes a signed transaction from base64 msgpack\n *\n * @param encoded - Base64 encoded signed transaction\n * @returns Decoded signed transaction object\n */\nexport function decodeSignedTransaction(encoded: string) {\n const bytes = decodeTransaction(encoded);\n return decodeSignedTxn(bytes);\n}\n\n/**\n * Decodes an unsigned transaction from base64 msgpack\n *\n * @param encoded - Base64 encoded unsigned transaction\n * @returns Decoded transaction object\n */\nexport function decodeUnsignedTransaction(encoded: string) {\n const bytes = decodeTransaction(encoded);\n return decodeUnsignedTxn(bytes);\n}\n\n/**\n * Validates an Algorand address\n *\n * Uses isValidAddress from algokit-utils which performs full checksum validation.\n *\n * @param address - The address to validate\n * @returns True if the address is valid\n */\nexport function isValidAlgorandAddress(address: string): boolean {\n return isValidAddress(address);\n}\n\n/**\n * Gets the sender address from a transaction (signed or unsigned)\n *\n * @param txnBytes - Transaction bytes\n * @param isSigned - Whether the transaction is signed (default: true)\n * @returns Sender address string\n */\nexport function getSenderFromTransaction(txnBytes: Uint8Array, isSigned: boolean = true): string {\n if (isSigned) {\n const signedTxn = decodeSignedTxn(txnBytes);\n return signedTxn.txn.sender.toString();\n }\n const txn = decodeUnsignedTxn(txnBytes);\n return txn.sender.toString();\n}\n\n/**\n * Converts a decimal amount to atomic units (token's smallest unit)\n *\n * @param decimalAmount - The decimal amount as a string (e.g., \"1.50\")\n * @param decimals - Number of decimal places (e.g., 6 for USDC)\n * @returns Amount in atomic units as a string\n *\n * @example\n * ```typescript\n * convertToTokenAmount(\"1.50\", 6) // Returns \"1500000\"\n * convertToTokenAmount(\"0.10\", 6) // Returns \"100000\"\n * ```\n */\nexport function convertToTokenAmount(decimalAmount: string, decimals: number): string {\n const amount = parseFloat(decimalAmount);\n if (isNaN(amount)) {\n throw new Error(`Invalid amount: ${decimalAmount}`);\n }\n\n // Handle decimal conversion properly\n const [intPart, decPart = \"\"] = String(amount).split(\".\");\n const paddedDec = decPart.padEnd(decimals, \"0\").slice(0, decimals);\n const tokenAmount = (intPart + paddedDec).replace(/^0+/, \"\") || \"0\";\n\n return tokenAmount;\n}\n\n/**\n * Converts atomic units to decimal amount\n *\n * @param atomicAmount - Amount in atomic units (string or bigint)\n * @param decimals - Number of decimal places\n * @returns Decimal amount as a string\n */\nexport function convertFromTokenAmount(atomicAmount: string | bigint, decimals: number): string {\n const amount = BigInt(atomicAmount);\n const divisor = BigInt(10 ** decimals);\n const intPart = amount / divisor;\n const decPart = amount % divisor;\n\n if (decPart === BigInt(0)) {\n return intPart.toString();\n }\n\n const decStr = decPart.toString().padStart(decimals, \"0\");\n // Remove trailing zeros\n const trimmedDec = decStr.replace(/0+$/, \"\");\n return `${intPart}.${trimmedDec}`;\n}\n\n/**\n * Gets the network type from a CAIP-2 identifier\n *\n * @param caip2 - CAIP-2 network identifier\n * @returns Network type (\"mainnet\" | \"testnet\") or null if unknown\n */\nexport function getNetworkFromCaip2(caip2: string): \"mainnet\" | \"testnet\" | null {\n if (!caip2.startsWith(\"algorand:\")) {\n return null;\n }\n\n const genesisHash = caip2.slice(\"algorand:\".length);\n\n if (genesisHash === ALGORAND_MAINNET_GENESIS_HASH) {\n return \"mainnet\";\n }\n if (genesisHash === ALGORAND_TESTNET_GENESIS_HASH) {\n return \"testnet\";\n }\n\n return null;\n}\n\n/**\n * Checks if a network identifier is an Algorand network\n *\n * @param network - Network identifier (CAIP-2 format)\n * @returns True if the network is an Algorand network\n */\nexport function isAlgorandNetwork(network: string): boolean {\n return network.startsWith(\"algorand:\");\n}\n\n/**\n * Checks if a network identifier is a testnet\n *\n * @param network - Network identifier (CAIP-2 format)\n * @returns True if the network is a testnet\n */\nexport function isTestnetNetwork(network: string): boolean {\n return network === ALGORAND_TESTNET_CAIP2;\n}\n\n/**\n * Gets the genesis hash from a transaction\n *\n * @param txn - The transaction object\n * @param txn.genesisHash - The genesis hash bytes\n * @returns Base64 encoded genesis hash\n */\nexport function getGenesisHashFromTransaction(txn: { genesisHash?: Uint8Array }): string {\n if (!txn.genesisHash) {\n throw new Error(\"Transaction does not have a genesis hash\");\n }\n return Buffer.from(txn.genesisHash).toString(\"base64\");\n}\n\n/**\n * Validates that all transactions in a group have the same group ID\n *\n * @param txns - Array of transaction bytes\n * @returns True if all transactions have matching group IDs\n */\nexport function validateGroupId(txns: Uint8Array[]): boolean {\n if (txns.length <= 1) {\n return true;\n }\n\n let expectedGroupId: string | null = null;\n\n for (const txnBytes of txns) {\n const txn = decodeUnsignedTxn(txnBytes);\n const groupId = txn.group ? Buffer.from(txn.group).toString(\"base64\") : null;\n\n if (expectedGroupId === null) {\n expectedGroupId = groupId;\n } else if (groupId !== expectedGroupId) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Extracts the transaction ID from signed transaction bytes\n *\n * @param signedTxnBytes - Signed transaction bytes\n * @returns Transaction ID string\n */\nexport function getTransactionId(signedTxnBytes: Uint8Array): string {\n const signedTxn = decodeSignedTxn(signedTxnBytes);\n return signedTxn.txn.txId();\n}\n\n/**\n * Checks if a signed transaction has a valid signature\n *\n * @param signedTxnBytes - Signed transaction bytes\n * @returns True if the transaction has a signature\n */\nexport function hasSignature(signedTxnBytes: Uint8Array): boolean {\n const signedTxn = decodeSignedTxn(signedTxnBytes);\n return (\n signedTxn.sig !== undefined || signedTxn.lsig !== undefined || signedTxn.msig !== undefined\n );\n}\n\n// Re-export algokit-utils types that consumers may need\nexport { Address, encodeAddress, decodeAddress } from \"@algorandfoundation/algokit-utils/common\";\n","/**\n * Algorand Network Constants for x402 AVM Implementation\n *\n * CAIP-2 Network Identifiers use the format: algorand:<genesis-hash-base64>\n * Genesis hashes uniquely identify Algorand networks.\n */\n\n// ============================================================================\n// CAIP-2 Network Identifiers (V2)\n// ============================================================================\n\n/**\n * CAIP-2 network identifier for Algorand Mainnet\n * Format: algorand:<genesis-hash-base64>\n */\nexport const ALGORAND_MAINNET_CAIP2 = \"algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=\";\n\n/**\n * CAIP-2 network identifier for Algorand Testnet\n * Format: algorand:<genesis-hash-base64>\n */\nexport const ALGORAND_TESTNET_CAIP2 = \"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=\";\n\n/**\n * All supported CAIP-2 network identifiers\n */\nexport const CAIP2_NETWORKS = [ALGORAND_MAINNET_CAIP2, ALGORAND_TESTNET_CAIP2] as const;\n\n// ============================================================================\n// Genesis Hashes\n// ============================================================================\n\n/**\n * Algorand Mainnet genesis hash (base64 encoded)\n */\nexport const ALGORAND_MAINNET_GENESIS_HASH = \"wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=\";\n\n/**\n * Algorand Testnet genesis hash (base64 encoded)\n */\nexport const ALGORAND_TESTNET_GENESIS_HASH = \"SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=\";\n\n// ============================================================================\n// USDC ASA (Algorand Standard Asset) Configuration\n// ============================================================================\n\n/**\n * USDC ASA ID on Algorand Mainnet\n *\n * @see https://algoexplorer.io/asset/31566704\n */\nexport const USDC_MAINNET_ASA_ID = \"31566704\";\n\n/**\n * USDC ASA ID on Algorand Testnet\n *\n * @see https://testnet.algoexplorer.io/asset/10458941\n */\nexport const USDC_TESTNET_ASA_ID = \"10458941\";\n\n/**\n * USDC decimals (same across all networks)\n */\nexport const USDC_DECIMALS = 6;\n\n/**\n * USDC configuration per network\n */\nexport const USDC_CONFIG: Record<string, { asaId: string; name: string; decimals: number }> = {\n [ALGORAND_MAINNET_CAIP2]: {\n asaId: USDC_MAINNET_ASA_ID,\n name: \"USDC\",\n decimals: USDC_DECIMALS,\n },\n [ALGORAND_TESTNET_CAIP2]: {\n asaId: USDC_TESTNET_ASA_ID,\n name: \"USDC\",\n decimals: USDC_DECIMALS,\n },\n};\n\n// ============================================================================\n// Transaction Limits\n// ============================================================================\n\n/**\n * Maximum reasonable fee per transaction in microAlgos (5000 µAlgo).\n *\n * Algorand transaction fees are calculated as:\n * fee = max(current_fee_per_byte * transaction_size_in_bytes, min_fee)\n *\n * Under normal (non-congested) conditions, current_fee_per_byte is 0,\n * so fee = min_fee = 1000 µAlgo (0.001 ALGO).\n *\n * During network congestion, fees can rise. This constant is set to 5x\n * the minimum fee (5000 µAlgo) as a reasonable upper bound per transaction.\n *\n * For fee payer transactions that cover an entire group via fee pooling,\n * use `maxReasonableGroupFee(groupSize)` which multiplies this per-txn\n * cap by the number of transactions in the group.\n */\nexport const MAX_REASONABLE_FEE_PER_TXN = 5000;\n\n/**\n * Calculates the maximum reasonable fee for a fee payer transaction\n * that covers an entire atomic group via fee pooling.\n *\n * @param groupSize - Number of transactions in the atomic group\n * @returns Maximum acceptable fee in microAlgos\n */\nexport function maxReasonableGroupFee(groupSize: number): number {\n return MAX_REASONABLE_FEE_PER_TXN * groupSize;\n}\n\n// Address validation: use isValidAddress() from @algorandfoundation/algokit-utils/common\n// Address length: use ALGORAND_ADDRESS_LENGTH from @algorandfoundation/algokit-utils/common\n","/**\n * AVM (Algorand) Signer Interfaces for x402 Payment Protocol\n *\n * This module defines the signer interfaces for client and facilitator operations.\n * Use the `toClientAvmSigner` and `toFacilitatorAvmSigner` helper functions to create\n * signers from a Base64-encoded private key.\n *\n * @example Client signer:\n * ```typescript\n * import { toClientAvmSigner } from \"@x402/avm\";\n *\n * const signer = toClientAvmSigner(process.env.AVM_PRIVATE_KEY!);\n * ```\n *\n * @example Facilitator signer:\n * ```typescript\n * import { toFacilitatorAvmSigner } from \"@x402/avm\";\n *\n * const signer = toFacilitatorAvmSigner(process.env.AVM_PRIVATE_KEY!);\n * ```\n */\n\nimport { AlgorandClient } from \"@algorandfoundation/algokit-utils/algorand-client\";\nimport { ed25519Generator } from \"@algorandfoundation/algokit-utils/crypto\";\nimport {\n decodeTransaction,\n generateAddressWithSigners,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport type {\n AddressWithSigners,\n AddressWithTransactionSigner,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport { waitForConfirmation } from \"@algorandfoundation/algokit-utils/transaction\";\nimport type { Network } from \"@x402/core/types\";\nimport { ALGORAND_TESTNET_CAIP2 } from \"./constants\";\n\n/**\n * Symbol key used to attach the internal algokit-utils AddressWithSigners\n * to a ClientAvmSigner created via toClientAvmSigner().\n * This enables internal code to extract the native algokit signer for\n * use with TransactionComposer and AlgorandClient.\n */\nexport const ALGOKIT_SIGNER = Symbol(\"algokit-signer\");\n\n/**\n * Client-side signer interface for Algorand wallets\n *\n * Compatible with @txnlab/use-wallet and similar wallet libraries.\n * Used to sign payment transactions on the client side.\n */\nexport interface ClientAvmSigner {\n /**\n * The Algorand address of the signer\n */\n address: string;\n\n /**\n * Sign one or more transactions\n *\n * @param txns - Array of unsigned transactions (encoded as Uint8Array)\n * @param indexesToSign - Optional array of indexes to sign (if not provided, sign all)\n * @returns Promise resolving to array of signed transactions (null for unsigned)\n */\n signTransactions(txns: Uint8Array[], indexesToSign?: number[]): Promise<(Uint8Array | null)[]>;\n}\n\n/**\n * Configuration for client AVM operations\n */\nexport interface ClientAvmConfig {\n /**\n * Pre-configured AlgorandClient instance (takes precedence over URL/token)\n * Use AlgorandClient.testNet(), .mainNet(), .fromConfig(), etc.\n */\n algorandClient?: import(\"@algorandfoundation/algokit-utils/algorand-client\").AlgorandClient;\n\n /**\n * Algod API URL (used if algorandClient not provided)\n */\n algodUrl?: string;\n\n /**\n * Algod API token\n */\n algodToken?: string;\n}\n\n/**\n * Facilitator signer interface for Algorand operations\n *\n * Used by the facilitator to verify and settle payments.\n * Supports multiple addresses for load balancing and key rotation.\n *\n * @example Using the helper function:\n * ```typescript\n * import { toFacilitatorAvmSigner } from \"@x402/avm\";\n *\n * const signer = toFacilitatorAvmSigner(process.env.AVM_PRIVATE_KEY!);\n * ```\n */\nexport interface FacilitatorAvmSigner {\n /**\n * Get all addresses this facilitator can use as fee payers\n *\n * @returns Array of Algorand addresses\n */\n getAddresses(): readonly string[];\n\n /**\n * Sign a transaction with the signer matching the sender address\n *\n * @param txn - Transaction bytes to sign\n * @param senderAddress - Expected sender address (for verification)\n * @returns Promise resolving to signed transaction bytes\n */\n signTransaction(txn: Uint8Array, senderAddress: string): Promise<Uint8Array>;\n\n /**\n * Get Algod client for a specific network\n *\n * @param network - Network identifier (CAIP-2 or V1 format)\n * @returns AlgodClient instance from @algorandfoundation/algokit-utils\n */\n getAlgodClient(\n network: Network,\n ): import(\"@algorandfoundation/algokit-utils/algod-client\").AlgodClient;\n\n /**\n * Simulate a transaction group before submission\n *\n * @param txns - Array of signed transaction bytes\n * @param network - Network identifier\n * @returns Promise resolving to SimulateResponse\n */\n simulateTransactions(\n txns: Uint8Array[],\n network: Network,\n ): Promise<import(\"@algorandfoundation/algokit-utils/algod-client\").SimulateResponse>;\n\n /**\n * Submit signed transactions to the network\n *\n * @param signedTxns - Array of signed transaction bytes\n * @param network - Network identifier\n * @returns Promise resolving to transaction ID\n */\n sendTransactions(signedTxns: Uint8Array[], network: Network): Promise<string>;\n\n /**\n * Wait for a transaction to be confirmed\n *\n * @param txId - Transaction ID\n * @param network - Network identifier\n * @param waitRounds - Number of rounds to wait (default: 4)\n * @returns Promise resolving to PendingTransactionResponse\n */\n waitForConfirmation(\n txId: string,\n network: Network,\n waitRounds?: number,\n ): Promise<import(\"@algorandfoundation/algokit-utils/algod-client\").PendingTransactionResponse>;\n}\n\n/**\n * Configuration for creating a facilitator signer\n */\nexport interface FacilitatorAvmSignerConfig {\n /**\n * Algod URL for mainnet\n */\n mainnetUrl?: string;\n\n /**\n * Algod URL for testnet\n */\n testnetUrl?: string;\n\n /**\n * Algod API token\n */\n algodToken?: string;\n}\n\n/**\n * Type guard to check if a wallet implements ClientAvmSigner\n *\n * @param wallet - The wallet to check\n * @returns True if the wallet implements ClientAvmSigner\n */\nexport function isAvmSignerWallet(wallet: unknown): wallet is ClientAvmSigner {\n return (\n typeof wallet === \"object\" &&\n wallet !== null &&\n \"address\" in wallet &&\n typeof (wallet as ClientAvmSigner).address === \"string\" &&\n \"signTransactions\" in wallet &&\n typeof (wallet as ClientAvmSigner).signTransactions === \"function\"\n );\n}\n\n/**\n * Decodes a Base64-encoded 64-byte private key into address and raw Ed25519 signer.\n *\n * @param privateKeyBase64 - Base64-encoded 64-byte key (32-byte seed + 32-byte public key)\n * @returns Address and raw Ed25519 signer function\n */\nfunction decodePrivateKey(privateKeyBase64: string) {\n const secretKey = Buffer.from(privateKeyBase64, \"base64\");\n if (secretKey.length !== 64) {\n throw new Error(\n \"AVM private key must be a Base64-encoded 64-byte key (32-byte seed + 32-byte public key)\",\n );\n }\n const seed = secretKey.subarray(0, 32);\n return ed25519Generator(seed);\n}\n\n/**\n * Creates a ClientAvmSigner from a Base64-encoded private key.\n *\n * This is the recommended way to create a client-side AVM signer for x402 payments.\n *\n * @param privateKeyBase64 - Base64-encoded 64-byte key (32-byte seed + 32-byte public key)\n * @returns A complete ClientAvmSigner ready for use with ExactAvmScheme\n *\n * @example\n * ```typescript\n * import { toClientAvmSigner } from \"@x402/avm\";\n * import { ExactAvmScheme } from \"@x402/avm/exact/client\";\n *\n * const signer = toClientAvmSigner(process.env.AVM_PRIVATE_KEY!);\n * client.register(\"algorand:*\", new ExactAvmScheme(signer));\n * ```\n */\nexport function toClientAvmSigner(privateKeyBase64: string): ClientAvmSigner {\n const { ed25519Pubkey, rawEd25519Signer } = decodePrivateKey(privateKeyBase64);\n\n // Use algokit-utils generateAddressWithSigners for the canonical signer implementation\n const algokitSigners = generateAddressWithSigners({ ed25519Pubkey, rawEd25519Signer });\n const address = algokitSigners.addr.toString();\n\n const signer: ClientAvmSigner = {\n address,\n signTransactions: async (txns: Uint8Array[], indexesToSign?: number[]) => {\n return Promise.all(\n txns.map(async (txn, i) => {\n if (indexesToSign && !indexesToSign.includes(i)) return null;\n const decoded = decodeTransaction(txn);\n // Delegate to the algokit-utils signer (signs a single transaction in a group)\n const signedBytes = await algokitSigners.signer([decoded], [0]);\n return signedBytes[0];\n }),\n );\n },\n };\n\n // Attach the internal algokit-utils AddressWithSigners for use by internal code\n // (e.g., TransactionComposer integration in client scheme)\n Object.defineProperty(signer, ALGOKIT_SIGNER, {\n value: algokitSigners,\n enumerable: false,\n writable: false,\n });\n\n return signer;\n}\n\n/**\n * Extracts the internal algokit-utils AddressWithTransactionSigner from a ClientAvmSigner,\n * if available. Returns null for wallet-created signers that don't have an internal\n * algokit signer (e.g., signers created from browser wallet adapters).\n *\n * This is useful for internal code that needs to register the signer with\n * AlgorandClient or TransactionComposer.\n *\n * @param signer - A ClientAvmSigner instance\n * @returns The internal AddressWithTransactionSigner, or null if not available\n */\nexport function getAlgokitSigner(signer: ClientAvmSigner): AddressWithTransactionSigner | null {\n const internal = (signer as unknown as Record<symbol, unknown>)[ALGOKIT_SIGNER] as\n | AddressWithSigners\n | undefined;\n if (internal && \"addr\" in internal && \"signer\" in internal) {\n return { addr: internal.addr, signer: internal.signer };\n }\n return null;\n}\n\n/**\n * Determines if a network identifier refers to testnet.\n *\n * @param network - The network identifier (CAIP-2 format)\n * @returns True if the network is testnet\n */\nfunction isTestnet(network: string): boolean {\n return network === ALGORAND_TESTNET_CAIP2;\n}\n\n/**\n * Creates a FacilitatorAvmSigner from a Base64-encoded private key.\n *\n * This is the recommended way to create a facilitator-side AVM signer for x402 payments.\n * Uses `AlgorandClient.testNet()` / `AlgorandClient.mainNet()` from AlgoKit Utils for\n * network connectivity, with optional URL overrides via config.\n *\n * @param privateKeyBase64 - Base64-encoded 64-byte key (32-byte seed + 32-byte public key)\n * @param config - Optional configuration for custom Algod URLs\n * @returns A complete FacilitatorAvmSigner ready for use with ExactAvmScheme\n *\n * @example\n * ```typescript\n * import { toFacilitatorAvmSigner } from \"@x402/avm\";\n * import { ExactAvmScheme } from \"@x402/avm/exact/facilitator\";\n *\n * // Default (AlgoNode endpoints):\n * const signer = toFacilitatorAvmSigner(process.env.AVM_PRIVATE_KEY!);\n *\n * // With custom URLs:\n * const signer = toFacilitatorAvmSigner(process.env.AVM_PRIVATE_KEY!, {\n * testnetUrl: \"https://my-testnet-node.example.com\",\n * mainnetUrl: \"https://my-mainnet-node.example.com\",\n * });\n *\n * facilitator.register(\"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=\", new ExactAvmScheme(signer));\n * ```\n */\nexport function toFacilitatorAvmSigner(\n privateKeyBase64: string,\n config?: FacilitatorAvmSignerConfig,\n): FacilitatorAvmSigner {\n const { ed25519Pubkey, rawEd25519Signer } = decodePrivateKey(privateKeyBase64);\n\n // Use algokit-utils generateAddressWithSigners for the canonical signer implementation\n const algokitSigners = generateAddressWithSigners({ ed25519Pubkey, rawEd25519Signer });\n const address = algokitSigners.addr.toString();\n\n // Create AlgorandClient instances for each network, with optional URL overrides\n const getAlgorandClientForNetwork = (network: string) => {\n if (isTestnet(network)) {\n if (config?.testnetUrl) {\n return AlgorandClient.fromConfig({\n algodConfig: { server: config.testnetUrl, token: config.algodToken ?? \"\" },\n });\n }\n return AlgorandClient.testNet();\n }\n if (config?.mainnetUrl) {\n return AlgorandClient.fromConfig({\n algodConfig: { server: config.mainnetUrl, token: config.algodToken ?? \"\" },\n });\n }\n return AlgorandClient.mainNet();\n };\n\n // Cache AlgorandClient instances per network\n const clientCache = new Map<string, ReturnType<typeof AlgorandClient.testNet>>();\n\n const getClient = (network: string) => {\n const key = isTestnet(network) ? \"testnet\" : \"mainnet\";\n let client = clientCache.get(key);\n if (!client) {\n client = getAlgorandClientForNetwork(network);\n clientCache.set(key, client);\n }\n return client;\n };\n\n return {\n getAddresses: () => [address] as readonly string[],\n\n signTransaction: async (txn: Uint8Array, _: string) => {\n const decoded = decodeTransaction(txn);\n // Delegate to the algokit-utils signer\n const signedBytes = await algokitSigners.signer([decoded], [0]);\n return signedBytes[0];\n },\n\n getAlgodClient: (network: string) => getClient(network).client.algod,\n\n simulateTransactions: async (txns: Uint8Array[], network: string) => {\n const algod = getClient(network).client.algod;\n return await algod.simulateRawTransactions(txns);\n },\n\n sendTransactions: async (signedTxns: Uint8Array[], network: string) => {\n const algod = getClient(network).client.algod;\n const response = await algod.sendRawTransaction(signedTxns);\n return response.txId;\n },\n\n waitForConfirmation: async (txId: string, network: string, waitRounds: number = 5) => {\n const algod = getClient(network).client.algod;\n return await waitForConfirmation(txId, waitRounds, algod);\n },\n };\n}\n","/**\n * AVM (Algorand) Types for x402 Payment Protocol\n *\n * Defines payload structures and type guards for Algorand transactions.\n */\n\n/**\n * V2 Payload for Algorand exact payment scheme\n *\n * Contains an atomic transaction group with a designated payment transaction.\n * Transactions are encoded as base64 msgpack.\n *\n * @example\n * ```typescript\n * const payload: ExactAvmPayloadV2 = {\n * paymentGroup: [\n * \"gqNzaWfEQ...\", // Fee payer transaction (signed by facilitator)\n * \"gqNzaWfEQ...\", // ASA transfer (signed by client)\n * ],\n * paymentIndex: 1, // Payment is the second transaction\n * };\n * ```\n */\nexport interface ExactAvmPayloadV2 {\n /**\n * Array of base64-encoded msgpack transactions forming an atomic group.\n * May include unsigned transactions (for fee payer) that the facilitator will sign.\n */\n paymentGroup: string[];\n\n /**\n * Zero-based index of the payment transaction within paymentGroup.\n * This transaction must be an ASA transfer to the payTo address.\n */\n paymentIndex: number;\n}\n\n/**\n * Type guard to check if a payload is an ExactAvmPayloadV2\n *\n * @param payload - The payload to check\n * @returns True if the payload is a valid ExactAvmPayloadV2\n */\nexport function isExactAvmPayload(payload: unknown): payload is ExactAvmPayloadV2 {\n return (\n typeof payload === \"object\" &&\n payload !== null &&\n \"paymentGroup\" in payload &&\n \"paymentIndex\" in payload &&\n Array.isArray((payload as ExactAvmPayloadV2).paymentGroup) &&\n typeof (payload as ExactAvmPayloadV2).paymentIndex === \"number\" &&\n (payload as ExactAvmPayloadV2).paymentGroup.every(item => typeof item === \"string\")\n );\n}\n\n// Transaction and SignedTransaction types are provided by @algorandfoundation/algokit-utils/transact\n// Use those directly instead of custom type definitions.\n"],"mappings":"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","names":["import_transact","import_common","decodeSignedTxn","decodeUnsignedTxn","import_algorand_client","import_transact","import_common","import_amount"]}
{"version":3,"sources":["../../src/index.ts","../../src/exact/client/scheme.ts","../../src/utils.ts","../../src/constants.ts","../../src/signer.ts","../../src/types.ts"],"sourcesContent":["/**\n * @module @x402/avm - x402 Payment Protocol AVM (Algorand) Implementation\n *\n * This module provides the Algorand-specific implementation of the x402 payment protocol.\n *\n * @example Client signer:\n * ```typescript\n * import { toClientAvmSigner } from \"@x402/avm\";\n *\n * const signer = toClientAvmSigner(process.env.AVM_PRIVATE_KEY!);\n * ```\n *\n * @example Facilitator signer:\n * ```typescript\n * import { toFacilitatorAvmSigner } from \"@x402/avm\";\n *\n * const signer = toFacilitatorAvmSigner(process.env.AVM_PRIVATE_KEY!);\n * ```\n */\n\n// Exact scheme client\nexport { ExactAvmScheme } from \"./exact\";\n\n// Signer helpers and interfaces\nexport {\n isAvmSignerWallet,\n toClientAvmSigner,\n toFacilitatorAvmSigner,\n getAlgokitSigner,\n ALGOKIT_SIGNER,\n} from \"./signer\";\nexport type {\n ClientAvmSigner,\n ClientAvmConfig,\n FacilitatorAvmSigner,\n FacilitatorAvmSignerConfig,\n} from \"./signer\";\n\n// Re-export algokit-utils signer types for consumers who want native interop\nexport type {\n AddressWithTransactionSigner,\n AddressWithSigners,\n TransactionSigner,\n} from \"@algorandfoundation/algokit-utils/transact\";\n\n// Types\nexport type { ExactAvmPayloadV2 } from \"./types\";\nexport { isExactAvmPayload } from \"./types\";\n\n// Constants\nexport {\n // CAIP-2 Network Identifiers\n ALGORAND_MAINNET_CAIP2,\n ALGORAND_TESTNET_CAIP2,\n CAIP2_NETWORKS,\n // Genesis Hashes\n ALGORAND_MAINNET_GENESIS_HASH,\n ALGORAND_TESTNET_GENESIS_HASH,\n // USDC Configuration\n USDC_MAINNET_ASA_ID,\n USDC_TESTNET_ASA_ID,\n USDC_DECIMALS,\n USDC_CONFIG,\n // Transaction Limits\n MAX_REASONABLE_FEE_PER_TXN,\n maxReasonableGroupFee,\n} from \"./constants\";\n\n// Re-export algokit-utils constants that consumers may need\nexport { ALGORAND_ADDRESS_LENGTH } from \"@algorandfoundation/algokit-utils/common\";\nexport { ALGORAND_MIN_TX_FEE } from \"@algorandfoundation/algokit-utils/amount\";\n\n// Utilities\nexport {\n encodeTransaction,\n decodeTransaction,\n decodeSignedTransaction,\n decodeUnsignedTransaction,\n isValidAlgorandAddress,\n getSenderFromTransaction,\n convertToTokenAmount,\n convertFromTokenAmount,\n getNetworkFromCaip2,\n isAlgorandNetwork,\n isTestnetNetwork,\n getGenesisHashFromTransaction,\n validateGroupId,\n getTransactionId,\n hasSignature,\n} from \"./utils\";\n","/**\n * AVM Client Scheme for Exact Payment Protocol\n *\n * Creates atomic transaction groups for Algorand ASA transfers.\n * Uses AlgorandClient and TransactionComposer from algokit-utils v10\n * for transaction construction, fee pooling, and group management.\n */\n\nimport { AlgorandClient } from \"@algorandfoundation/algokit-utils/algorand-client\";\nimport {\n Transaction,\n encodeTransactionRaw,\n groupTransactions,\n makeEmptyTransactionSigner,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport { microAlgo } from \"@algorandfoundation/algokit-utils/amount\";\nimport type {\n PaymentRequirements,\n SchemeNetworkClient,\n PaymentPayloadResult,\n} from \"@x402/core/types\";\nimport type { ClientAvmSigner, ClientAvmConfig } from \"../../signer\";\nimport type { ExactAvmPayloadV2 } from \"../../types\";\nimport { encodeTransaction } from \"../../utils\";\nimport { USDC_CONFIG } from \"../../constants\";\nimport { isTestnetNetwork } from \"../../utils\";\n\n/**\n * AVM client implementation for the Exact payment scheme.\n *\n * Creates atomic transaction groups with ASA transfers for x402 payments.\n * Supports optional fee payer transactions for gasless payments.\n */\nexport class ExactAvmScheme implements SchemeNetworkClient {\n readonly scheme = \"exact\";\n\n /**\n * Creates a new ExactAvmScheme instance.\n *\n * @param signer - The AVM signer for client operations\n * @param config - Optional configuration for Algod client\n */\n constructor(\n private readonly signer: ClientAvmSigner,\n private readonly config?: ClientAvmConfig,\n ) {}\n\n /**\n * Creates a payment payload for the Exact scheme.\n *\n * Constructs an atomic transaction group with:\n * - Optional fee payer transaction (if feePayer specified in requirements.extra)\n * - ASA transfer transaction to payTo address\n *\n * Uses TransactionComposer for automatic suggested params, group ID assignment,\n * and fee pooling. For sponsored (gasless) transactions, exact fees are calculated\n * from actual encoded transaction sizes using the protocol fee formula:\n * fee = max(fee_per_byte × txn_size_in_bytes, min_fee)\n *\n * @param x402Version - The x402 protocol version\n * @param paymentRequirements - The payment requirements\n * @returns Promise resolving to a payment payload result\n */\n async createPaymentPayload(\n x402Version: number,\n paymentRequirements: PaymentRequirements,\n ): Promise<PaymentPayloadResult> {\n const { amount, asset, payTo, network, extra } = paymentRequirements;\n\n const algorandClient = this.getAlgorandClient(network);\n\n // Get asset ID (from requirements or default USDC)\n const assetId = this.getAssetId(asset, network);\n\n // Get fee payer address from extra if provided\n const feePayer = extra?.feePayer as string | undefined;\n let paymentIndex = 0;\n\n // Use an empty signer for building — we sign manually after\n // (fee payer txns stay unsigned for the facilitator to sign)\n const emptySigner = makeEmptyTransactionSigner();\n\n // Build the transaction group using TransactionComposer\n const composer = algorandClient.newGroup();\n\n if (feePayer) {\n // First pass: add fee payer with a placeholder fee.\n // The actual fee will be recalculated after build() using exact transaction sizes.\n composer.addPayment({\n sender: feePayer,\n receiver: feePayer,\n amount: microAlgo(0),\n note: `x402-fee-payer-${Date.now()}`,\n signer: emptySigner,\n });\n paymentIndex = 1;\n }\n\n composer.addAssetTransfer({\n sender: this.signer.address,\n receiver: payTo,\n assetId: BigInt(assetId),\n amount: BigInt(amount),\n staticFee: feePayer ? microAlgo(0) : undefined, // 0 fee when fee payer covers\n note: `x402-payment-v${x402Version}-${Date.now()}`,\n signer: emptySigner,\n });\n\n // Build transactions (assigns group ID, suggested params, fees)\n const built = await composer.build();\n let transactions = built.transactions.map(tws => tws.txn);\n\n // For sponsored transactions: recalculate the fee payer's fee using\n // exact encoded sizes of all transactions in the group.\n // Algorand fee formula: fee = max(fee_per_byte × txn_size, min_fee)\n //\n // After changing fees, the group ID must be recomputed because it is\n // derived from each transaction's encoded bytes (which include the fee).\n if (feePayer) {\n const sp = await algorandClient.getSuggestedParams();\n const feePerByte = Number(sp.fee);\n const minFee = Number(sp.minFee);\n\n // Calculate exact fee for each transaction based on its actual encoded size\n let totalGroupFee = BigInt(0);\n for (const txn of transactions) {\n const txnSize = encodeTransactionRaw(txn).length;\n const txnFee = feePerByte > 0 ? Math.max(feePerByte * txnSize, minFee) : minFee;\n totalGroupFee += BigInt(txnFee);\n }\n\n // Strip group ID, set correct fees, then re-group.\n // groupTransactions() requires group to be absent, computes the new group hash,\n // and returns new Transaction objects with the correct group ID.\n const ungrouped = transactions.map((txn, i) => {\n const fee = i === 0 ? totalGroupFee : BigInt(0);\n return new Transaction({ ...txn, fee, group: undefined });\n });\n transactions = groupTransactions(ungrouped);\n }\n\n // Encode all transactions to raw bytes\n const encodedTxns = transactions.map(txn => encodeTransactionRaw(txn));\n\n // Determine which transactions the client should sign\n const clientIndexes = transactions\n .map((txn, i) => (txn.sender.toString() === this.signer.address ? i : -1))\n .filter(i => i !== -1);\n\n // Sign client's transactions\n const signedTxns = await this.signer.signTransactions(encodedTxns, clientIndexes);\n\n // Build payment group with signed/unsigned transactions\n const paymentGroup: string[] = encodedTxns.map((txnBytes, i) => {\n const signedTxn = signedTxns[i];\n if (signedTxn) {\n return encodeTransaction(signedTxn);\n }\n // Return unsigned transaction for facilitator to sign\n return encodeTransaction(txnBytes);\n });\n\n const payload: ExactAvmPayloadV2 = {\n paymentGroup,\n paymentIndex,\n };\n\n return {\n x402Version,\n payload: payload as unknown as Record<string, unknown>,\n };\n }\n\n /**\n * Creates or retrieves an AlgorandClient for the given network.\n *\n * @param network - Network identifier (CAIP-2 format)\n * @returns AlgorandClient instance\n */\n private getAlgorandClient(network: string): AlgorandClient {\n if (this.config?.algorandClient) {\n return this.config.algorandClient;\n }\n if (this.config?.algodUrl) {\n return AlgorandClient.fromConfig({\n algodConfig: {\n server: this.config.algodUrl,\n token: this.config.algodToken ?? \"\",\n },\n });\n }\n // Auto-detect network\n return isTestnetNetwork(network) ? AlgorandClient.testNet() : AlgorandClient.mainNet();\n }\n\n /**\n * Gets the asset ID from the requirements or defaults to USDC\n *\n * @param asset - Asset identifier from requirements\n * @param network - Network identifier\n * @returns Asset ID as string\n */\n private getAssetId(asset: string, network: string): string {\n // If asset is already a numeric string, use it directly\n if (/^\\d+$/.test(asset)) {\n return asset;\n }\n\n // Try to get from USDC config\n const usdcConfig = USDC_CONFIG[network];\n if (usdcConfig) {\n return usdcConfig.asaId;\n }\n\n // Default to the asset as-is (might be an ASA ID)\n return asset;\n }\n}\n","/**\n * AVM (Algorand) Utilities for x402 Payment Protocol\n *\n * Provides utility functions for Algod client creation, transaction encoding/decoding,\n * address validation, and network identification.\n */\n\nimport {\n decodeTransaction as decodeUnsignedTxn,\n decodeSignedTransaction as decodeSignedTxn,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport { isValidAddress } from \"@algorandfoundation/algokit-utils/common\";\nimport {\n ALGORAND_MAINNET_GENESIS_HASH,\n ALGORAND_TESTNET_GENESIS_HASH,\n ALGORAND_TESTNET_CAIP2,\n} from \"./constants\";\n\n/**\n * Encodes transaction bytes to base64 string\n *\n * @param txn - Transaction bytes (Uint8Array)\n * @returns Base64 encoded string\n */\nexport function encodeTransaction(txn: Uint8Array): string {\n return Buffer.from(txn).toString(\"base64\");\n}\n\n/**\n * Decodes a base64 encoded transaction to bytes\n *\n * @param encoded - Base64 encoded transaction string\n * @returns Transaction bytes (Uint8Array)\n */\nexport function decodeTransaction(encoded: string): Uint8Array {\n return new Uint8Array(Buffer.from(encoded, \"base64\"));\n}\n\n/**\n * Decodes a signed transaction from base64 msgpack\n *\n * @param encoded - Base64 encoded signed transaction\n * @returns Decoded signed transaction object\n */\nexport function decodeSignedTransaction(encoded: string) {\n const bytes = decodeTransaction(encoded);\n return decodeSignedTxn(bytes);\n}\n\n/**\n * Decodes an unsigned transaction from base64 msgpack\n *\n * @param encoded - Base64 encoded unsigned transaction\n * @returns Decoded transaction object\n */\nexport function decodeUnsignedTransaction(encoded: string) {\n const bytes = decodeTransaction(encoded);\n return decodeUnsignedTxn(bytes);\n}\n\n/**\n * Validates an Algorand address\n *\n * Uses isValidAddress from algokit-utils which performs full checksum validation.\n *\n * @param address - The address to validate\n * @returns True if the address is valid\n */\nexport function isValidAlgorandAddress(address: string): boolean {\n return isValidAddress(address);\n}\n\n/**\n * Gets the sender address from a transaction (signed or unsigned)\n *\n * @param txnBytes - Transaction bytes\n * @param isSigned - Whether the transaction is signed (default: true)\n * @returns Sender address string\n */\nexport function getSenderFromTransaction(txnBytes: Uint8Array, isSigned: boolean = true): string {\n if (isSigned) {\n const signedTxn = decodeSignedTxn(txnBytes);\n return signedTxn.txn.sender.toString();\n }\n const txn = decodeUnsignedTxn(txnBytes);\n return txn.sender.toString();\n}\n\n// Re-export from core for backward compatibility\nexport { convertToTokenAmount, numberToDecimalString } from \"@x402/core/utils\";\n\n/**\n * Converts atomic units to decimal amount\n *\n * @param atomicAmount - Amount in atomic units (string or bigint)\n * @param decimals - Number of decimal places\n * @returns Decimal amount as a string\n */\nexport function convertFromTokenAmount(atomicAmount: string | bigint, decimals: number): string {\n const amount = BigInt(atomicAmount);\n const divisor = BigInt(10 ** decimals);\n const intPart = amount / divisor;\n const decPart = amount % divisor;\n\n if (decPart === BigInt(0)) {\n return intPart.toString();\n }\n\n const decStr = decPart.toString().padStart(decimals, \"0\");\n // Remove trailing zeros\n const trimmedDec = decStr.replace(/0+$/, \"\");\n return `${intPart}.${trimmedDec}`;\n}\n\n/**\n * Gets the network type from a CAIP-2 identifier\n *\n * @param caip2 - CAIP-2 network identifier\n * @returns Network type (\"mainnet\" | \"testnet\") or null if unknown\n */\nexport function getNetworkFromCaip2(caip2: string): \"mainnet\" | \"testnet\" | null {\n if (!caip2.startsWith(\"algorand:\")) {\n return null;\n }\n\n const genesisHash = caip2.slice(\"algorand:\".length);\n\n if (genesisHash === ALGORAND_MAINNET_GENESIS_HASH) {\n return \"mainnet\";\n }\n if (genesisHash === ALGORAND_TESTNET_GENESIS_HASH) {\n return \"testnet\";\n }\n\n return null;\n}\n\n/**\n * Checks if a network identifier is an Algorand network\n *\n * @param network - Network identifier (CAIP-2 format)\n * @returns True if the network is an Algorand network\n */\nexport function isAlgorandNetwork(network: string): boolean {\n return network.startsWith(\"algorand:\");\n}\n\n/**\n * Checks if a network identifier is a testnet\n *\n * @param network - Network identifier (CAIP-2 format)\n * @returns True if the network is a testnet\n */\nexport function isTestnetNetwork(network: string): boolean {\n return network === ALGORAND_TESTNET_CAIP2;\n}\n\n/**\n * Gets the genesis hash from a transaction\n *\n * @param txn - The transaction object\n * @param txn.genesisHash - The genesis hash bytes\n * @returns Base64 encoded genesis hash\n */\nexport function getGenesisHashFromTransaction(txn: { genesisHash?: Uint8Array }): string {\n if (!txn.genesisHash) {\n throw new Error(\"Transaction does not have a genesis hash\");\n }\n return Buffer.from(txn.genesisHash).toString(\"base64\");\n}\n\n/**\n * Validates that all transactions in a group have the same group ID\n *\n * @param txns - Array of transaction bytes\n * @returns True if all transactions have matching group IDs\n */\nexport function validateGroupId(txns: Uint8Array[]): boolean {\n if (txns.length <= 1) {\n return true;\n }\n\n let expectedGroupId: string | null = null;\n\n for (const txnBytes of txns) {\n const txn = decodeUnsignedTxn(txnBytes);\n const groupId = txn.group ? Buffer.from(txn.group).toString(\"base64\") : null;\n\n if (expectedGroupId === null) {\n expectedGroupId = groupId;\n } else if (groupId !== expectedGroupId) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Extracts the transaction ID from signed transaction bytes\n *\n * @param signedTxnBytes - Signed transaction bytes\n * @returns Transaction ID string\n */\nexport function getTransactionId(signedTxnBytes: Uint8Array): string {\n const signedTxn = decodeSignedTxn(signedTxnBytes);\n return signedTxn.txn.txId();\n}\n\n/**\n * Checks if a signed transaction has a valid signature\n *\n * @param signedTxnBytes - Signed transaction bytes\n * @returns True if the transaction has a signature\n */\nexport function hasSignature(signedTxnBytes: Uint8Array): boolean {\n const signedTxn = decodeSignedTxn(signedTxnBytes);\n return (\n signedTxn.sig !== undefined || signedTxn.lsig !== undefined || signedTxn.msig !== undefined\n );\n}\n\n// Re-export algokit-utils types that consumers may need\nexport { Address, encodeAddress, decodeAddress } from \"@algorandfoundation/algokit-utils/common\";\n","/**\n * Algorand Network Constants for x402 AVM Implementation\n *\n * CAIP-2 Network Identifiers use the format: algorand:<genesis-hash-base64>\n * Genesis hashes uniquely identify Algorand networks.\n */\n\n// ============================================================================\n// CAIP-2 Network Identifiers (V2)\n// ============================================================================\n\n/**\n * CAIP-2 network identifier for Algorand Mainnet\n * Format: algorand:<genesis-hash-base64>\n */\nexport const ALGORAND_MAINNET_CAIP2 = \"algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=\";\n\n/**\n * CAIP-2 network identifier for Algorand Testnet\n * Format: algorand:<genesis-hash-base64>\n */\nexport const ALGORAND_TESTNET_CAIP2 = \"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=\";\n\n/**\n * All supported CAIP-2 network identifiers\n */\nexport const CAIP2_NETWORKS = [ALGORAND_MAINNET_CAIP2, ALGORAND_TESTNET_CAIP2] as const;\n\n// ============================================================================\n// Genesis Hashes\n// ============================================================================\n\n/**\n * Algorand Mainnet genesis hash (base64 encoded)\n */\nexport const ALGORAND_MAINNET_GENESIS_HASH = \"wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=\";\n\n/**\n * Algorand Testnet genesis hash (base64 encoded)\n */\nexport const ALGORAND_TESTNET_GENESIS_HASH = \"SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=\";\n\n// ============================================================================\n// USDC ASA (Algorand Standard Asset) Configuration\n// ============================================================================\n\n/**\n * USDC ASA ID on Algorand Mainnet\n *\n * @see https://algoexplorer.io/asset/31566704\n */\nexport const USDC_MAINNET_ASA_ID = \"31566704\";\n\n/**\n * USDC ASA ID on Algorand Testnet\n *\n * @see https://testnet.algoexplorer.io/asset/10458941\n */\nexport const USDC_TESTNET_ASA_ID = \"10458941\";\n\n/**\n * USDC decimals (same across all networks)\n */\nexport const USDC_DECIMALS = 6;\n\n/**\n * USDC configuration per network\n */\nexport const USDC_CONFIG: Record<string, { asaId: string; name: string; decimals: number }> = {\n [ALGORAND_MAINNET_CAIP2]: {\n asaId: USDC_MAINNET_ASA_ID,\n name: \"USDC\",\n decimals: USDC_DECIMALS,\n },\n [ALGORAND_TESTNET_CAIP2]: {\n asaId: USDC_TESTNET_ASA_ID,\n name: \"USDC\",\n decimals: USDC_DECIMALS,\n },\n};\n\n// ============================================================================\n// Transaction Limits\n// ============================================================================\n\n/**\n * Maximum reasonable fee per transaction in microAlgos (5000 µAlgo).\n *\n * Algorand transaction fees are calculated as:\n * fee = max(current_fee_per_byte * transaction_size_in_bytes, min_fee)\n *\n * Under normal (non-congested) conditions, current_fee_per_byte is 0,\n * so fee = min_fee = 1000 µAlgo (0.001 ALGO).\n *\n * During network congestion, fees can rise. This constant is set to 5x\n * the minimum fee (5000 µAlgo) as a reasonable upper bound per transaction.\n *\n * For fee payer transactions that cover an entire group via fee pooling,\n * use `maxReasonableGroupFee(groupSize)` which multiplies this per-txn\n * cap by the number of transactions in the group.\n */\nexport const MAX_REASONABLE_FEE_PER_TXN = 5000;\n\n/**\n * Calculates the maximum reasonable fee for a fee payer transaction\n * that covers an entire atomic group via fee pooling.\n *\n * @param groupSize - Number of transactions in the atomic group\n * @returns Maximum acceptable fee in microAlgos\n */\nexport function maxReasonableGroupFee(groupSize: number): number {\n return MAX_REASONABLE_FEE_PER_TXN * groupSize;\n}\n\n// Address validation: use isValidAddress() from @algorandfoundation/algokit-utils/common\n// Address length: use ALGORAND_ADDRESS_LENGTH from @algorandfoundation/algokit-utils/common\n","/**\n * AVM (Algorand) Signer Interfaces for x402 Payment Protocol\n *\n * This module defines the signer interfaces for client and facilitator operations.\n * Use the `toClientAvmSigner` and `toFacilitatorAvmSigner` helper functions to create\n * signers from a Base64-encoded private key.\n *\n * @example Client signer:\n * ```typescript\n * import { toClientAvmSigner } from \"@x402/avm\";\n *\n * const signer = toClientAvmSigner(process.env.AVM_PRIVATE_KEY!);\n * ```\n *\n * @example Facilitator signer:\n * ```typescript\n * import { toFacilitatorAvmSigner } from \"@x402/avm\";\n *\n * const signer = toFacilitatorAvmSigner(process.env.AVM_PRIVATE_KEY!);\n * ```\n */\n\nimport { AlgorandClient } from \"@algorandfoundation/algokit-utils/algorand-client\";\nimport { ed25519Generator } from \"@algorandfoundation/algokit-utils/crypto\";\nimport {\n decodeTransaction,\n generateAddressWithSigners,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport type {\n AddressWithSigners,\n AddressWithTransactionSigner,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport { waitForConfirmation } from \"@algorandfoundation/algokit-utils/transaction\";\nimport type { Network } from \"@x402/core/types\";\nimport { ALGORAND_TESTNET_CAIP2 } from \"./constants\";\n\n/**\n * Symbol key used to attach the internal algokit-utils AddressWithSigners\n * to a ClientAvmSigner created via toClientAvmSigner().\n * This enables internal code to extract the native algokit signer for\n * use with TransactionComposer and AlgorandClient.\n */\nexport const ALGOKIT_SIGNER = Symbol(\"algokit-signer\");\n\n/**\n * Client-side signer interface for Algorand wallets\n *\n * Compatible with @txnlab/use-wallet and similar wallet libraries.\n * Used to sign payment transactions on the client side.\n */\nexport interface ClientAvmSigner {\n /**\n * The Algorand address of the signer\n */\n address: string;\n\n /**\n * Sign one or more transactions\n *\n * @param txns - Array of unsigned transactions (encoded as Uint8Array)\n * @param indexesToSign - Optional array of indexes to sign (if not provided, sign all)\n * @returns Promise resolving to array of signed transactions (null for unsigned)\n */\n signTransactions(txns: Uint8Array[], indexesToSign?: number[]): Promise<(Uint8Array | null)[]>;\n}\n\n/**\n * Configuration for client AVM operations\n */\nexport interface ClientAvmConfig {\n /**\n * Pre-configured AlgorandClient instance (takes precedence over URL/token)\n * Use AlgorandClient.testNet(), .mainNet(), .fromConfig(), etc.\n */\n algorandClient?: import(\"@algorandfoundation/algokit-utils/algorand-client\").AlgorandClient;\n\n /**\n * Algod API URL (used if algorandClient not provided)\n */\n algodUrl?: string;\n\n /**\n * Algod API token\n */\n algodToken?: string;\n}\n\n/**\n * Facilitator signer interface for Algorand operations\n *\n * Used by the facilitator to verify and settle payments.\n * Supports multiple addresses for load balancing and key rotation.\n *\n * @example Using the helper function:\n * ```typescript\n * import { toFacilitatorAvmSigner } from \"@x402/avm\";\n *\n * const signer = toFacilitatorAvmSigner(process.env.AVM_PRIVATE_KEY!);\n * ```\n */\nexport interface FacilitatorAvmSigner {\n /**\n * Get all addresses this facilitator can use as fee payers\n *\n * @returns Array of Algorand addresses\n */\n getAddresses(): readonly string[];\n\n /**\n * Sign a transaction with the signer matching the sender address\n *\n * @param txn - Transaction bytes to sign\n * @param senderAddress - Expected sender address (for verification)\n * @returns Promise resolving to signed transaction bytes\n */\n signTransaction(txn: Uint8Array, senderAddress: string): Promise<Uint8Array>;\n\n /**\n * Get Algod client for a specific network\n *\n * @param network - Network identifier (CAIP-2 or V1 format)\n * @returns AlgodClient instance from @algorandfoundation/algokit-utils\n */\n getAlgodClient(\n network: Network,\n ): import(\"@algorandfoundation/algokit-utils/algod-client\").AlgodClient;\n\n /**\n * Simulate a transaction group before submission\n *\n * @param txns - Array of signed transaction bytes\n * @param network - Network identifier\n * @returns Promise resolving to SimulateResponse\n */\n simulateTransactions(\n txns: Uint8Array[],\n network: Network,\n ): Promise<import(\"@algorandfoundation/algokit-utils/algod-client\").SimulateResponse>;\n\n /**\n * Submit signed transactions to the network\n *\n * @param signedTxns - Array of signed transaction bytes\n * @param network - Network identifier\n * @returns Promise resolving to transaction ID\n */\n sendTransactions(signedTxns: Uint8Array[], network: Network): Promise<string>;\n\n /**\n * Wait for a transaction to be confirmed\n *\n * @param txId - Transaction ID\n * @param network - Network identifier\n * @param waitRounds - Number of rounds to wait (default: 4)\n * @returns Promise resolving to PendingTransactionResponse\n */\n waitForConfirmation(\n txId: string,\n network: Network,\n waitRounds?: number,\n ): Promise<import(\"@algorandfoundation/algokit-utils/algod-client\").PendingTransactionResponse>;\n}\n\n/**\n * Configuration for creating a facilitator signer\n */\nexport interface FacilitatorAvmSignerConfig {\n /**\n * Algod URL for mainnet\n */\n mainnetUrl?: string;\n\n /**\n * Algod URL for testnet\n */\n testnetUrl?: string;\n\n /**\n * Algod API token\n */\n algodToken?: string;\n}\n\n/**\n * Type guard to check if a wallet implements ClientAvmSigner\n *\n * @param wallet - The wallet to check\n * @returns True if the wallet implements ClientAvmSigner\n */\nexport function isAvmSignerWallet(wallet: unknown): wallet is ClientAvmSigner {\n return (\n typeof wallet === \"object\" &&\n wallet !== null &&\n \"address\" in wallet &&\n typeof (wallet as ClientAvmSigner).address === \"string\" &&\n \"signTransactions\" in wallet &&\n typeof (wallet as ClientAvmSigner).signTransactions === \"function\"\n );\n}\n\n/**\n * Decodes a Base64-encoded 64-byte private key into address and raw Ed25519 signer.\n *\n * @param privateKeyBase64 - Base64-encoded 64-byte key (32-byte seed + 32-byte public key)\n * @returns Address and raw Ed25519 signer function\n */\nfunction decodePrivateKey(privateKeyBase64: string) {\n const secretKey = Buffer.from(privateKeyBase64, \"base64\");\n if (secretKey.length !== 64) {\n throw new Error(\n \"AVM private key must be a Base64-encoded 64-byte key (32-byte seed + 32-byte public key)\",\n );\n }\n const seed = secretKey.subarray(0, 32);\n return ed25519Generator(seed);\n}\n\n/**\n * Creates a ClientAvmSigner from a Base64-encoded private key.\n *\n * This is the recommended way to create a client-side AVM signer for x402 payments.\n *\n * @param privateKeyBase64 - Base64-encoded 64-byte key (32-byte seed + 32-byte public key)\n * @returns A complete ClientAvmSigner ready for use with ExactAvmScheme\n *\n * @example\n * ```typescript\n * import { toClientAvmSigner } from \"@x402/avm\";\n * import { ExactAvmScheme } from \"@x402/avm/exact/client\";\n *\n * const signer = toClientAvmSigner(process.env.AVM_PRIVATE_KEY!);\n * client.register(\"algorand:*\", new ExactAvmScheme(signer));\n * ```\n */\nexport function toClientAvmSigner(privateKeyBase64: string): ClientAvmSigner {\n const { ed25519Pubkey, rawEd25519Signer } = decodePrivateKey(privateKeyBase64);\n\n // Use algokit-utils generateAddressWithSigners for the canonical signer implementation\n const algokitSigners = generateAddressWithSigners({ ed25519Pubkey, rawEd25519Signer });\n const address = algokitSigners.addr.toString();\n\n const signer: ClientAvmSigner = {\n address,\n signTransactions: async (txns: Uint8Array[], indexesToSign?: number[]) => {\n return Promise.all(\n txns.map(async (txn, i) => {\n if (indexesToSign && !indexesToSign.includes(i)) return null;\n const decoded = decodeTransaction(txn);\n // Delegate to the algokit-utils signer (signs a single transaction in a group)\n const signedBytes = await algokitSigners.signer([decoded], [0]);\n return signedBytes[0];\n }),\n );\n },\n };\n\n // Attach the internal algokit-utils AddressWithSigners for use by internal code\n // (e.g., TransactionComposer integration in client scheme)\n Object.defineProperty(signer, ALGOKIT_SIGNER, {\n value: algokitSigners,\n enumerable: false,\n writable: false,\n });\n\n return signer;\n}\n\n/**\n * Extracts the internal algokit-utils AddressWithTransactionSigner from a ClientAvmSigner,\n * if available. Returns null for wallet-created signers that don't have an internal\n * algokit signer (e.g., signers created from browser wallet adapters).\n *\n * This is useful for internal code that needs to register the signer with\n * AlgorandClient or TransactionComposer.\n *\n * @param signer - A ClientAvmSigner instance\n * @returns The internal AddressWithTransactionSigner, or null if not available\n */\nexport function getAlgokitSigner(signer: ClientAvmSigner): AddressWithTransactionSigner | null {\n const internal = (signer as unknown as Record<symbol, unknown>)[ALGOKIT_SIGNER] as\n | AddressWithSigners\n | undefined;\n if (internal && \"addr\" in internal && \"signer\" in internal) {\n return { addr: internal.addr, signer: internal.signer };\n }\n return null;\n}\n\n/**\n * Determines if a network identifier refers to testnet.\n *\n * @param network - The network identifier (CAIP-2 format)\n * @returns True if the network is testnet\n */\nfunction isTestnet(network: string): boolean {\n return network === ALGORAND_TESTNET_CAIP2;\n}\n\n/**\n * Creates a FacilitatorAvmSigner from a Base64-encoded private key.\n *\n * This is the recommended way to create a facilitator-side AVM signer for x402 payments.\n * Uses `AlgorandClient.testNet()` / `AlgorandClient.mainNet()` from AlgoKit Utils for\n * network connectivity, with optional URL overrides via config.\n *\n * @param privateKeyBase64 - Base64-encoded 64-byte key (32-byte seed + 32-byte public key)\n * @param config - Optional configuration for custom Algod URLs\n * @returns A complete FacilitatorAvmSigner ready for use with ExactAvmScheme\n *\n * @example\n * ```typescript\n * import { toFacilitatorAvmSigner } from \"@x402/avm\";\n * import { ExactAvmScheme } from \"@x402/avm/exact/facilitator\";\n *\n * // Default (AlgoNode endpoints):\n * const signer = toFacilitatorAvmSigner(process.env.AVM_PRIVATE_KEY!);\n *\n * // With custom URLs:\n * const signer = toFacilitatorAvmSigner(process.env.AVM_PRIVATE_KEY!, {\n * testnetUrl: \"https://my-testnet-node.example.com\",\n * mainnetUrl: \"https://my-mainnet-node.example.com\",\n * });\n *\n * facilitator.register(\"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=\", new ExactAvmScheme(signer));\n * ```\n */\nexport function toFacilitatorAvmSigner(\n privateKeyBase64: string,\n config?: FacilitatorAvmSignerConfig,\n): FacilitatorAvmSigner {\n const { ed25519Pubkey, rawEd25519Signer } = decodePrivateKey(privateKeyBase64);\n\n // Use algokit-utils generateAddressWithSigners for the canonical signer implementation\n const algokitSigners = generateAddressWithSigners({ ed25519Pubkey, rawEd25519Signer });\n const address = algokitSigners.addr.toString();\n\n // Create AlgorandClient instances for each network, with optional URL overrides\n const getAlgorandClientForNetwork = (network: string) => {\n if (isTestnet(network)) {\n if (config?.testnetUrl) {\n return AlgorandClient.fromConfig({\n algodConfig: { server: config.testnetUrl, token: config.algodToken ?? \"\" },\n });\n }\n return AlgorandClient.testNet();\n }\n if (config?.mainnetUrl) {\n return AlgorandClient.fromConfig({\n algodConfig: { server: config.mainnetUrl, token: config.algodToken ?? \"\" },\n });\n }\n return AlgorandClient.mainNet();\n };\n\n // Cache AlgorandClient instances per network\n const clientCache = new Map<string, ReturnType<typeof AlgorandClient.testNet>>();\n\n const getClient = (network: string) => {\n const key = isTestnet(network) ? \"testnet\" : \"mainnet\";\n let client = clientCache.get(key);\n if (!client) {\n client = getAlgorandClientForNetwork(network);\n clientCache.set(key, client);\n }\n return client;\n };\n\n return {\n getAddresses: () => [address] as readonly string[],\n\n signTransaction: async (txn: Uint8Array, _: string) => {\n const decoded = decodeTransaction(txn);\n // Delegate to the algokit-utils signer\n const signedBytes = await algokitSigners.signer([decoded], [0]);\n return signedBytes[0];\n },\n\n getAlgodClient: (network: string) => getClient(network).client.algod,\n\n simulateTransactions: async (txns: Uint8Array[], network: string) => {\n const algod = getClient(network).client.algod;\n return await algod.simulateRawTransactions(txns);\n },\n\n sendTransactions: async (signedTxns: Uint8Array[], network: string) => {\n const algod = getClient(network).client.algod;\n const response = await algod.sendRawTransaction(signedTxns);\n return response.txId;\n },\n\n waitForConfirmation: async (txId: string, network: string, waitRounds: number = 5) => {\n const algod = getClient(network).client.algod;\n return await waitForConfirmation(txId, waitRounds, algod);\n },\n };\n}\n","/**\n * AVM (Algorand) Types for x402 Payment Protocol\n *\n * Defines payload structures and type guards for Algorand transactions.\n */\n\n/**\n * V2 Payload for Algorand exact payment scheme\n *\n * Contains an atomic transaction group with a designated payment transaction.\n * Transactions are encoded as base64 msgpack.\n *\n * @example\n * ```typescript\n * const payload: ExactAvmPayloadV2 = {\n * paymentGroup: [\n * \"gqNzaWfEQ...\", // Fee payer transaction (signed by facilitator)\n * \"gqNzaWfEQ...\", // ASA transfer (signed by client)\n * ],\n * paymentIndex: 1, // Payment is the second transaction\n * };\n * ```\n */\nexport interface ExactAvmPayloadV2 {\n /**\n * Array of base64-encoded msgpack transactions forming an atomic group.\n * May include unsigned transactions (for fee payer) that the facilitator will sign.\n */\n paymentGroup: string[];\n\n /**\n * Zero-based index of the payment transaction within paymentGroup.\n * This transaction must be an ASA transfer to the payTo address.\n */\n paymentIndex: number;\n}\n\n/**\n * Type guard to check if a payload is an ExactAvmPayloadV2\n *\n * @param payload - The payload to check\n * @returns True if the payload is a valid ExactAvmPayloadV2\n */\nexport function isExactAvmPayload(payload: unknown): payload is ExactAvmPayloadV2 {\n return (\n typeof payload === \"object\" &&\n payload !== null &&\n \"paymentGroup\" in payload &&\n \"paymentIndex\" in payload &&\n Array.isArray((payload as ExactAvmPayloadV2).paymentGroup) &&\n typeof (payload as ExactAvmPayloadV2).paymentIndex === \"number\" &&\n (payload as ExactAvmPayloadV2).paymentGroup.every(item => typeof item === \"string\")\n );\n}\n\n// Transaction and SignedTransaction types are provided by @algorandfoundation/algokit-utils/transact\n// Use those directly instead of custom type definitions.\n"],"mappings":"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","names":["import_transact","import_common","decodeSignedTxn","decodeUnsignedTxn","import_algorand_client","import_transact","import_common","import_amount"]}
import {
ExactAvmScheme
} from "../../chunk-IZZHPFP7.mjs";
import "../../chunk-SLF4VHVI.mjs";
} from "../../chunk-OMKK3QVS.mjs";
import "../../chunk-QEY4CIDB.mjs";
import "../../chunk-MWOLCZUZ.mjs";
export {

@@ -6,0 +7,0 @@ ExactAvmScheme

@@ -5,7 +5,9 @@ import {

import {
decodeTransaction,
hasSignature
} from "../../chunk-QEY4CIDB.mjs";
import {
__export,
decodeTransaction,
hasSignature,
maxReasonableGroupFee
} from "../../chunk-SLF4VHVI.mjs";
} from "../../chunk-MWOLCZUZ.mjs";

@@ -12,0 +14,0 @@ // src/exact/facilitator/scheme.ts

@@ -1,1 +0,1 @@

{"version":3,"sources":["../../../../src/exact/facilitator/scheme.ts","../../../../src/exact/facilitator/errors.ts"],"sourcesContent":["/**\n * AVM Facilitator Scheme for Exact Payment Protocol\n *\n * Verifies and settles Algorand ASA transfer payments.\n */\n\nimport {\n decodeTransaction as decodeUnsignedTxn,\n decodeSignedTransaction as decodeSignedTxn,\n encodeTransactionRaw,\n encodeSignedTransaction,\n bytesForSigning,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport type { Transaction, SignedTransaction } from \"@algorandfoundation/algokit-utils/transact\";\nimport { ed25519Verifier } from \"@algorandfoundation/algokit-utils/crypto\";\nimport type {\n Network,\n PaymentPayload,\n PaymentRequirements,\n SchemeNetworkFacilitator,\n SettleResponse,\n VerifyResponse,\n} from \"@x402/core/types\";\nimport type { FacilitatorAvmSigner } from \"../../signer\";\nimport type { ExactAvmPayloadV2 } from \"../../types\";\nimport { isExactAvmPayload } from \"../../types\";\nimport { MAX_TRANSACTION_GROUP_SIZE } from \"@algorandfoundation/algokit-utils/common\";\nimport { decodeTransaction, hasSignature } from \"../../utils\";\nimport { maxReasonableGroupFee } from \"../../constants\";\nimport * as Errors from \"./errors\";\n\n/**\n * AVM facilitator implementation for the Exact payment scheme.\n *\n * Verifies atomic transaction groups and settles ASA transfers for x402 payments.\n * Supports gasless transactions by signing fee payer transactions.\n */\nexport class ExactAvmScheme implements SchemeNetworkFacilitator {\n readonly scheme = \"exact\";\n readonly caipFamily = \"algorand:*\";\n\n /**\n * Creates a new ExactAvmScheme facilitator instance.\n *\n * @param signer - The AVM signer for facilitator operations\n */\n constructor(private readonly signer: FacilitatorAvmSigner) {}\n\n /**\n * Get mechanism-specific extra data for the supported kinds endpoint.\n * For AVM, returns the feePayer address for gasless transactions.\n *\n * @param _ - The network identifier (unused, feePayer is network-agnostic)\n * @returns Extra data with feePayer address\n */\n getExtra(_: string): Record<string, unknown> | undefined {\n const addresses = this.signer.getAddresses();\n if (addresses.length === 0) {\n return undefined;\n }\n\n // Random selection distributes ALGO fee costs across multiple signer accounts,\n // preventing any single fee payer from being depleted faster than others.\n const randomIndex = Math.floor(Math.random() * addresses.length);\n return { feePayer: addresses[randomIndex] };\n }\n\n /**\n * Get signer addresses used by this facilitator.\n * Returns all addresses this facilitator can use for signing fee payer transactions.\n *\n * @param _ - The network identifier (unused, addresses are network-agnostic)\n * @returns Array of facilitator wallet addresses\n */\n getSigners(_: string): string[] {\n return [...this.signer.getAddresses()];\n }\n\n /**\n * Verifies a payment payload.\n *\n * Verification steps:\n * 1. Validate x402Version, scheme, and network\n * 2. Validate payload format and structure\n * 3. Check group size does not exceed maximum (16)\n * 4. Decode and validate transaction group\n * 5. Verify payment transaction (amount, receiver, asset)\n * 6. Prepare signed group (verify fee payer safety + sign)\n * 7. Simulate transaction group\n *\n * @param payload - The payment payload to verify\n * @param requirements - The payment requirements\n * @returns Promise resolving to verification response\n */\n async verify(\n payload: PaymentPayload,\n requirements: PaymentRequirements,\n ): Promise<VerifyResponse> {\n try {\n // Validate x402 version\n if (payload.x402Version !== 2) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidVersion,\n invalidMessage: `Expected x402Version 2, got ${payload.x402Version}`,\n };\n }\n\n // Validate scheme\n if (payload.accepted.scheme !== \"exact\" || requirements.scheme !== \"exact\") {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidScheme,\n invalidMessage: `Expected scheme \"exact\", got payload=\"${payload.accepted.scheme}\" requirements=\"${requirements.scheme}\"`,\n };\n }\n\n // Validate network\n if (payload.accepted.network !== requirements.network) {\n return {\n isValid: false,\n invalidReason: Errors.ErrNetworkMismatch,\n invalidMessage: `Network mismatch: payload=\"${payload.accepted.network}\" requirements=\"${requirements.network}\"`,\n };\n }\n\n const rawPayload = payload.payload as unknown;\n\n if (!isExactAvmPayload(rawPayload)) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidPayload,\n invalidMessage: \"Payload does not match ExactAvmPayloadV2 format\",\n };\n }\n\n const { paymentGroup, paymentIndex } = rawPayload as ExactAvmPayloadV2;\n\n if (paymentGroup.length > MAX_TRANSACTION_GROUP_SIZE) {\n return {\n isValid: false,\n invalidReason: Errors.ErrGroupSizeExceeded,\n invalidMessage: `Transaction group has ${paymentGroup.length} transactions, maximum is ${MAX_TRANSACTION_GROUP_SIZE}`,\n };\n }\n\n if (paymentIndex < 0 || paymentIndex >= paymentGroup.length) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidPaymentIndex,\n invalidMessage: `Payment index ${paymentIndex} out of bounds for group of ${paymentGroup.length}`,\n };\n }\n\n const facilitatorAddresses = this.signer.getAddresses();\n\n // Decode all transactions and validate group structure\n const decoded = this.decodeTransactionGroup(paymentGroup, facilitatorAddresses);\n if (\"error\" in decoded) return decoded.error;\n\n // Extract payer from payment transaction\n const paymentTxn = decoded.txns[paymentIndex].txn;\n const payer = paymentTxn.sender.toString();\n\n // SECURITY: Verify facilitator's signers are not transferring their own funds\n if (facilitatorAddresses.includes(payer)) {\n return {\n isValid: false,\n invalidReason: Errors.ErrFacilitatorTransferring,\n invalidMessage: \"Facilitator signer cannot be the payment sender\",\n };\n }\n\n // Payment transaction correctness\n const paymentCheck = await this.verifyPaymentTransaction(\n decoded.txns[paymentIndex],\n requirements,\n paymentGroup[paymentIndex],\n );\n if (!paymentCheck.isValid) return paymentCheck;\n\n // Verify fee payers and sign them for simulation\n const prepared = await this.prepareSignedGroup(decoded.txns, paymentGroup);\n if (\"error\" in prepared) return prepared.error;\n\n // Simulate the assembled group\n const simResult = await this.simulateTransactionGroup(\n prepared.signedTxns,\n requirements.network,\n );\n if (!simResult.isValid) return simResult;\n\n return { isValid: true, payer };\n } catch (error) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidPayload,\n invalidMessage: `Unexpected error: ${error instanceof Error ? error.message : \"Unknown\"}`,\n };\n }\n }\n\n /**\n * Settles a payment by submitting the transaction group.\n *\n * Settlement steps:\n * 1. Verify the payment first\n * 2. Decode and sign fee payer transactions (reuses shared decode/sign logic)\n * 3. Submit transaction group\n * 4. Wait for on-chain confirmation\n * 5. Return transaction ID\n *\n * @param payload - The payment payload to settle\n * @param requirements - The payment requirements\n * @returns Promise resolving to settlement response\n */\n async settle(\n payload: PaymentPayload,\n requirements: PaymentRequirements,\n ): Promise<SettleResponse> {\n // First verify the payment\n const verification = await this.verify(payload, requirements);\n if (!verification.isValid) {\n return {\n success: false,\n errorReason: verification.invalidReason,\n errorMessage: verification.invalidMessage,\n transaction: \"\",\n network: requirements.network,\n payer: verification.payer,\n };\n }\n\n const avmPayload = payload.payload as unknown as ExactAvmPayloadV2;\n const { paymentGroup, paymentIndex } = avmPayload;\n const facilitatorAddresses = this.signer.getAddresses();\n\n // Reuse shared decode logic\n const decoded = this.decodeTransactionGroup(paymentGroup, facilitatorAddresses);\n if (\"error\" in decoded) {\n return {\n success: false,\n errorReason: Errors.ErrSettleFailed,\n errorMessage: decoded.error.invalidMessage ?? decoded.error.invalidReason,\n transaction: \"\",\n network: requirements.network,\n payer: verification.payer,\n };\n }\n\n // Reuse shared sign logic\n const prepared = await this.prepareSignedGroup(decoded.txns, paymentGroup);\n if (\"error\" in prepared) {\n return {\n success: false,\n errorReason: Errors.ErrSettleFailed,\n errorMessage: prepared.error.invalidMessage ?? prepared.error.invalidReason,\n transaction: \"\",\n network: requirements.network,\n payer: verification.payer,\n };\n }\n\n // Get the payment transaction ID before submission\n const paymentTxnBytes = prepared.signedTxns[paymentIndex];\n const paymentStxn = decodeSignedTxn(paymentTxnBytes);\n const paymentTxId = paymentStxn.txn.txId();\n\n // Submit transaction group\n try {\n await this.signer.sendTransactions(prepared.signedTxns, requirements.network);\n } catch (error) {\n return {\n success: false,\n errorReason: Errors.ErrSettleFailed,\n errorMessage: `Failed to submit transaction: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n transaction: paymentTxId,\n network: requirements.network,\n payer: verification.payer,\n };\n }\n\n // Wait for on-chain confirmation\n try {\n // Wait up to 10 rounds for on-chain confirmation\n await this.signer.waitForConfirmation(paymentTxId, requirements.network, 10);\n } catch (error) {\n return {\n success: false,\n errorReason: Errors.ErrConfirmationFailed,\n errorMessage: `Transaction submitted but confirmation failed: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n transaction: paymentTxId,\n network: requirements.network,\n payer: verification.payer,\n };\n }\n\n return {\n success: true,\n transaction: paymentTxId,\n network: requirements.network,\n payer: verification.payer,\n };\n }\n\n /**\n * Decodes all transactions in the group and validates structure.\n *\n * - Signed transactions are decoded as-is\n * - Unsigned transactions are only accepted from facilitator addresses (fee payers)\n * - Verifies group ID consistency across all transactions\n *\n * @param paymentGroup - Array of base64-encoded transaction strings\n * @param facilitatorAddresses - Addresses controlled by this facilitator\n * @returns Decoded transactions or an error response\n */\n private decodeTransactionGroup(\n paymentGroup: string[],\n facilitatorAddresses: readonly string[],\n ): { txns: SignedTransaction[] } | { error: VerifyResponse } {\n const txns: SignedTransaction[] = [];\n\n for (let i = 0; i < paymentGroup.length; i++) {\n try {\n const bytes = decodeTransaction(paymentGroup[i]);\n\n try {\n const stxn = decodeSignedTxn(bytes);\n // Validate that decoding actually produced a valid signed transaction.\n // algokit-utils decodeSignedTransaction is lenient and may succeed on raw unsigned\n // bytes, returning a transaction with type \"unknown\" and missing fields.\n if (!stxn.txn.type || stxn.txn.type === \"unknown\") {\n throw new Error(\"Invalid signed transaction: missing type\");\n }\n txns.push(stxn);\n } catch {\n // Unsigned transaction — only the facilitator's fee payer txn should be unsigned\n const unsignedTxn = decodeUnsignedTxn(bytes);\n const sender = unsignedTxn.sender.toString();\n\n if (!facilitatorAddresses.includes(sender)) {\n return {\n error: {\n isValid: false,\n invalidReason: Errors.ErrUnsignedNonFacilitator,\n invalidMessage: `Unsigned transaction at index ${i} from ${sender} is not a facilitator address`,\n },\n };\n }\n\n // Wrap unsigned txn for simulation (empty signature)\n const encodedForSimulate = encodeSignedTransaction({ txn: unsignedTxn });\n txns.push(decodeSignedTxn(encodedForSimulate));\n }\n } catch {\n return {\n error: {\n isValid: false,\n invalidReason: Errors.ErrInvalidTransaction,\n invalidMessage: `Failed to decode transaction at index ${i}`,\n },\n };\n }\n }\n\n // Verify group ID consistency\n if (txns.length > 1) {\n const firstGroup = txns[0].txn.group;\n const firstGroupId = firstGroup ? Buffer.from(firstGroup).toString(\"base64\") : null;\n\n for (let i = 1; i < txns.length; i++) {\n const group = txns[i].txn.group;\n const groupId = group ? Buffer.from(group).toString(\"base64\") : null;\n if (groupId !== firstGroupId) {\n return {\n error: {\n isValid: false,\n invalidReason: Errors.ErrInvalidGroupId,\n invalidMessage: \"Transactions have inconsistent group IDs\",\n },\n };\n }\n }\n }\n\n return { txns };\n }\n\n /**\n * Verifies fee payer transactions and signs them, returning the assembled group\n * ready for simulation or submission.\n *\n * @param decodedTxns - Decoded signed transaction objects\n * @param paymentGroup - Original base64-encoded transaction strings\n * @returns Signed transaction bytes or an error response\n */\n private async prepareSignedGroup(\n decodedTxns: SignedTransaction[],\n paymentGroup: string[],\n ): Promise<{ signedTxns: Uint8Array[] } | { error: VerifyResponse }> {\n const facilitatorAddresses = this.signer.getAddresses();\n const signedTxns: Uint8Array[] = [];\n\n for (let i = 0; i < decodedTxns.length; i++) {\n const txn = decodedTxns[i].txn;\n const sender = txn.sender.toString();\n\n if (facilitatorAddresses.includes(sender)) {\n const feeCheck = this.verifyFeePayerTransaction(txn, decodedTxns.length);\n if (!feeCheck.isValid) return { error: feeCheck };\n\n try {\n const signedTxn = await this.signer.signTransaction(encodeTransactionRaw(txn), sender);\n signedTxns.push(signedTxn);\n } catch (error) {\n return {\n error: {\n isValid: false,\n invalidReason: Errors.ErrInvalidFeePayer,\n invalidMessage: `Failed to sign fee payer transaction: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n },\n };\n }\n } else {\n signedTxns.push(decodeTransaction(paymentGroup[i]));\n }\n }\n\n return { signedTxns };\n }\n\n /**\n * Simulates the transaction group and returns the verification result.\n *\n * @param signedTxns - Signed transaction bytes to simulate\n * @param network - Target network for simulation\n * @returns Verification result from simulation\n */\n private async simulateTransactionGroup(\n signedTxns: Uint8Array[],\n network: Network,\n ): Promise<VerifyResponse> {\n try {\n const simResult = (await this.signer.simulateTransactions(signedTxns, network)) as {\n txnGroups?: Array<{ failureMessage?: string }>;\n };\n\n if (simResult.txnGroups?.[0]?.failureMessage) {\n return {\n isValid: false,\n invalidReason: Errors.ErrSimulationFailed,\n invalidMessage: simResult.txnGroups[0].failureMessage,\n };\n }\n\n return { isValid: true };\n } catch (error) {\n return {\n isValid: false,\n invalidReason: Errors.ErrSimulationFailed,\n invalidMessage: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n }\n\n /**\n * Verifies the payment transaction matches requirements\n *\n * @param stxn - The signed payment transaction\n * @param requirements - Payment requirements to verify against\n * @param encodedTxn - Base64-encoded transaction for signature check\n * @returns Verification result\n */\n private async verifyPaymentTransaction(\n stxn: SignedTransaction,\n requirements: PaymentRequirements,\n encodedTxn: string,\n ): Promise<VerifyResponse> {\n const txn = stxn.txn;\n\n // Must be an asset transfer\n if (txn.type !== \"axfer\") {\n return {\n isValid: false,\n invalidReason: Errors.ErrNotAssetTransfer,\n invalidMessage: `Expected asset transfer, got \"${txn.type}\"`,\n };\n }\n\n // Access asset transfer properties — properly typed in algokit-utils v10\n const assetTransfer = txn.assetTransfer;\n\n if (!assetTransfer) {\n return {\n isValid: false,\n invalidReason: Errors.ErrNotAssetTransfer,\n invalidMessage: \"Missing assetTransfer data\",\n };\n }\n\n // Use BigInt comparison to avoid string format mismatches (e.g. \"1000\" vs \"1000.0\")\n const amount = assetTransfer.amount ?? BigInt(0);\n\n if (amount !== BigInt(requirements.amount)) {\n return {\n isValid: false,\n invalidReason: Errors.ErrAmountMismatch,\n invalidMessage: `Expected ${requirements.amount}, got ${amount.toString()}`,\n };\n }\n\n // Verify receiver address matches payTo\n const receiver = assetTransfer.receiver ? assetTransfer.receiver.toString() : \"\";\n\n if (receiver !== requirements.payTo) {\n return {\n isValid: false,\n invalidReason: Errors.ErrReceiverMismatch,\n invalidMessage: `Expected ${requirements.payTo}, got ${receiver}`,\n };\n }\n\n // Verify asset\n const assetId = assetTransfer.assetId?.toString() ?? \"\";\n\n if (assetId !== requirements.asset) {\n return {\n isValid: false,\n invalidReason: Errors.ErrAssetMismatch,\n invalidMessage: `Expected asset ${requirements.asset}, got ${assetId}`,\n };\n }\n\n // Verify signature exists\n const txnBytes = decodeTransaction(encodedTxn);\n if (!hasSignature(txnBytes)) {\n return {\n isValid: false,\n invalidReason: Errors.ErrPaymentNotSigned,\n invalidMessage: \"Payment transaction is not signed\",\n };\n }\n\n // Verify the ed25519 signature was actually made by the sender\n if (stxn.sig) {\n const signedMsg = bytesForSigning.transaction(txn);\n const isValidSig = await ed25519Verifier(stxn.sig, signedMsg, txn.sender.publicKey);\n if (!isValidSig) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidSignature,\n invalidMessage: \"Payment transaction signature does not match sender\",\n };\n }\n }\n\n return { isValid: true };\n }\n\n /**\n * Verifies a fee payer transaction is safe to sign\n *\n * @param txn - The fee payer transaction to validate\n * @param groupSize - Number of transactions in the atomic group (for fee cap calculation)\n * @returns Verification result\n */\n private verifyFeePayerTransaction(txn: Transaction, groupSize: number): VerifyResponse {\n // Must be a payment transaction (for fee payment)\n if (txn.type !== \"pay\") {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidFeePayer,\n invalidMessage: `Expected payment transaction, got ${txn.type}`,\n };\n }\n\n // Access payment fields — properly typed in algokit-utils v10\n const paymentFields = txn.payment;\n\n // Must have zero amount (self-payment for fee coverage)\n const payAmount = paymentFields?.amount ?? BigInt(0);\n if (payAmount > BigInt(0)) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidFeePayer,\n invalidMessage: \"Fee payer amount must be 0\",\n };\n }\n\n // Must be self-payment (receiver == sender)\n if (paymentFields?.receiver) {\n const receiverAddr = paymentFields.receiver.toString();\n const senderAddr = txn.sender.toString();\n if (receiverAddr !== senderAddr) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidFeePayer,\n invalidMessage: \"Fee payer receiver must be same as sender (self-payment)\",\n };\n }\n }\n\n // Must not have close remainder to\n if (paymentFields?.closeRemainderTo) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidFeePayer,\n invalidMessage: \"closeRemainderTo not allowed on fee payer\",\n };\n }\n\n // Must not have rekey to\n if (txn.rekeyTo) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidFeePayer,\n invalidMessage: \"rekeyTo not allowed on fee payer\",\n };\n }\n\n // Fee must be reasonable — during congestion fees can rise, so the cap\n // is 5x the minimum fee (5000 µAlgo) per transaction in the group.\n // The fee payer covers the entire group via Algorand's fee pooling.\n const fee = Number(txn.fee ?? 0);\n const maxFee = maxReasonableGroupFee(groupSize);\n if (fee > maxFee) {\n return {\n isValid: false,\n invalidReason: Errors.ErrFeeTooHigh,\n invalidMessage: `Fee ${fee} exceeds maximum ${maxFee} (${groupSize} txns × 5000 µAlgo)`,\n };\n }\n\n return { isValid: true };\n }\n}\n","/**\n * AVM Facilitator error codes for verify and settle responses.\n *\n * Uses snake_case `invalid_exact_avm_*` prefix convention,\n * consistent with EVM (`invalid_exact_evm_*`) and SVM patterns.\n */\n\n// Verify errors — scheme/network/version\nexport const ErrInvalidScheme = \"invalid_exact_avm_scheme\";\nexport const ErrNetworkMismatch = \"invalid_exact_avm_network_mismatch\";\nexport const ErrInvalidVersion = \"invalid_exact_avm_invalid_version\";\n\n// Verify errors — payload structure\nexport const ErrInvalidPayload = \"invalid_exact_avm_payload\";\nexport const ErrGroupSizeExceeded = \"invalid_exact_avm_group_size_exceeded\";\nexport const ErrInvalidPaymentIndex = \"invalid_exact_avm_payment_index\";\nexport const ErrInvalidTransaction = \"invalid_exact_avm_invalid_transaction\";\nexport const ErrInvalidGroupId = \"invalid_exact_avm_invalid_group_id\";\n\n// Verify errors — payment correctness\nexport const ErrNotAssetTransfer = \"invalid_exact_avm_not_asset_transfer\";\nexport const ErrAmountMismatch = \"invalid_exact_avm_amount_mismatch\";\nexport const ErrReceiverMismatch = \"invalid_exact_avm_receiver_mismatch\";\nexport const ErrAssetMismatch = \"invalid_exact_avm_asset_mismatch\";\n\n// Verify errors — fee payer\nexport const ErrInvalidFeePayer = \"invalid_exact_avm_invalid_fee_payer\";\nexport const ErrFeeTooHigh = \"invalid_exact_avm_fee_too_high\";\n\n// Verify errors — signature\nexport const ErrPaymentNotSigned = \"invalid_exact_avm_payment_not_signed\";\nexport const ErrInvalidSignature = \"invalid_exact_avm_invalid_signature\";\n\n// Verify errors — simulation\nexport const ErrSimulationFailed = \"invalid_exact_avm_simulation_failed\";\n\n// Verify errors — facilitator safety\nexport const ErrFacilitatorTransferring = \"invalid_exact_avm_facilitator_transferring\";\n\n// Security errors\nexport const ErrUnsignedNonFacilitator = \"invalid_exact_avm_unsigned_non_facilitator\";\n\n// Settle errors\nexport const ErrSettleFailed = \"invalid_exact_avm_settlement_failed\";\nexport const ErrConfirmationFailed = \"invalid_exact_avm_confirmation_failed\";\n"],"mappings":"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","names":[]}
{"version":3,"sources":["../../../../src/exact/facilitator/scheme.ts","../../../../src/exact/facilitator/errors.ts"],"sourcesContent":["/**\n * AVM Facilitator Scheme for Exact Payment Protocol\n *\n * Verifies and settles Algorand ASA transfer payments.\n */\n\nimport {\n decodeTransaction as decodeUnsignedTxn,\n decodeSignedTransaction as decodeSignedTxn,\n encodeTransactionRaw,\n encodeSignedTransaction,\n bytesForSigning,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport type { Transaction, SignedTransaction } from \"@algorandfoundation/algokit-utils/transact\";\nimport { ed25519Verifier } from \"@algorandfoundation/algokit-utils/crypto\";\nimport type {\n Network,\n PaymentPayload,\n PaymentRequirements,\n SchemeNetworkFacilitator,\n SettleResponse,\n VerifyResponse,\n} from \"@x402/core/types\";\nimport type { FacilitatorAvmSigner } from \"../../signer\";\nimport type { ExactAvmPayloadV2 } from \"../../types\";\nimport { isExactAvmPayload } from \"../../types\";\nimport { MAX_TRANSACTION_GROUP_SIZE } from \"@algorandfoundation/algokit-utils/common\";\nimport { decodeTransaction, hasSignature } from \"../../utils\";\nimport { maxReasonableGroupFee } from \"../../constants\";\nimport * as Errors from \"./errors\";\n\n/**\n * AVM facilitator implementation for the Exact payment scheme.\n *\n * Verifies atomic transaction groups and settles ASA transfers for x402 payments.\n * Supports gasless transactions by signing fee payer transactions.\n */\nexport class ExactAvmScheme implements SchemeNetworkFacilitator {\n readonly scheme = \"exact\";\n readonly caipFamily = \"algorand:*\";\n\n /**\n * Creates a new ExactAvmScheme facilitator instance.\n *\n * @param signer - The AVM signer for facilitator operations\n */\n constructor(private readonly signer: FacilitatorAvmSigner) {}\n\n /**\n * Get mechanism-specific extra data for the supported kinds endpoint.\n * For AVM, returns the feePayer address for gasless transactions.\n *\n * @param _ - The network identifier (unused, feePayer is network-agnostic)\n * @returns Extra data with feePayer address\n */\n getExtra(_: string): Record<string, unknown> | undefined {\n const addresses = this.signer.getAddresses();\n if (addresses.length === 0) {\n return undefined;\n }\n\n // Random selection distributes ALGO fee costs across multiple signer accounts,\n // preventing any single fee payer from being depleted faster than others.\n const randomIndex = Math.floor(Math.random() * addresses.length);\n return { feePayer: addresses[randomIndex] };\n }\n\n /**\n * Get signer addresses used by this facilitator.\n * Returns all addresses this facilitator can use for signing fee payer transactions.\n *\n * @param _ - The network identifier (unused, addresses are network-agnostic)\n * @returns Array of facilitator wallet addresses\n */\n getSigners(_: string): string[] {\n return [...this.signer.getAddresses()];\n }\n\n /**\n * Verifies a payment payload.\n *\n * Verification steps:\n * 1. Validate x402Version, scheme, and network\n * 2. Validate payload format and structure\n * 3. Check group size does not exceed maximum (16)\n * 4. Decode and validate transaction group\n * 5. Verify payment transaction (amount, receiver, asset)\n * 6. Prepare signed group (verify fee payer safety + sign)\n * 7. Simulate transaction group\n *\n * @param payload - The payment payload to verify\n * @param requirements - The payment requirements\n * @returns Promise resolving to verification response\n */\n async verify(\n payload: PaymentPayload,\n requirements: PaymentRequirements,\n ): Promise<VerifyResponse> {\n try {\n // Validate x402 version\n if (payload.x402Version !== 2) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidVersion,\n invalidMessage: `Expected x402Version 2, got ${payload.x402Version}`,\n };\n }\n\n // Validate scheme\n if (payload.accepted.scheme !== \"exact\" || requirements.scheme !== \"exact\") {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidScheme,\n invalidMessage: `Expected scheme \"exact\", got payload=\"${payload.accepted.scheme}\" requirements=\"${requirements.scheme}\"`,\n };\n }\n\n // Validate network\n if (payload.accepted.network !== requirements.network) {\n return {\n isValid: false,\n invalidReason: Errors.ErrNetworkMismatch,\n invalidMessage: `Network mismatch: payload=\"${payload.accepted.network}\" requirements=\"${requirements.network}\"`,\n };\n }\n\n const rawPayload = payload.payload as unknown;\n\n if (!isExactAvmPayload(rawPayload)) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidPayload,\n invalidMessage: \"Payload does not match ExactAvmPayloadV2 format\",\n };\n }\n\n const { paymentGroup, paymentIndex } = rawPayload as ExactAvmPayloadV2;\n\n if (paymentGroup.length > MAX_TRANSACTION_GROUP_SIZE) {\n return {\n isValid: false,\n invalidReason: Errors.ErrGroupSizeExceeded,\n invalidMessage: `Transaction group has ${paymentGroup.length} transactions, maximum is ${MAX_TRANSACTION_GROUP_SIZE}`,\n };\n }\n\n if (paymentIndex < 0 || paymentIndex >= paymentGroup.length) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidPaymentIndex,\n invalidMessage: `Payment index ${paymentIndex} out of bounds for group of ${paymentGroup.length}`,\n };\n }\n\n const facilitatorAddresses = this.signer.getAddresses();\n\n // Decode all transactions and validate group structure\n const decoded = this.decodeTransactionGroup(paymentGroup, facilitatorAddresses);\n if (\"error\" in decoded) return decoded.error;\n\n // Extract payer from payment transaction\n const paymentTxn = decoded.txns[paymentIndex].txn;\n const payer = paymentTxn.sender.toString();\n\n // SECURITY: Verify facilitator's signers are not transferring their own funds\n if (facilitatorAddresses.includes(payer)) {\n return {\n isValid: false,\n invalidReason: Errors.ErrFacilitatorTransferring,\n invalidMessage: \"Facilitator signer cannot be the payment sender\",\n };\n }\n\n // Payment transaction correctness\n const paymentCheck = await this.verifyPaymentTransaction(\n decoded.txns[paymentIndex],\n requirements,\n paymentGroup[paymentIndex],\n );\n if (!paymentCheck.isValid) return paymentCheck;\n\n // Verify fee payers and sign them for simulation\n const prepared = await this.prepareSignedGroup(decoded.txns, paymentGroup);\n if (\"error\" in prepared) return prepared.error;\n\n // Simulate the assembled group\n const simResult = await this.simulateTransactionGroup(\n prepared.signedTxns,\n requirements.network,\n );\n if (!simResult.isValid) return simResult;\n\n return { isValid: true, payer };\n } catch (error) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidPayload,\n invalidMessage: `Unexpected error: ${error instanceof Error ? error.message : \"Unknown\"}`,\n };\n }\n }\n\n /**\n * Settles a payment by submitting the transaction group.\n *\n * Settlement steps:\n * 1. Verify the payment first\n * 2. Decode and sign fee payer transactions (reuses shared decode/sign logic)\n * 3. Submit transaction group\n * 4. Wait for on-chain confirmation\n * 5. Return transaction ID\n *\n * @param payload - The payment payload to settle\n * @param requirements - The payment requirements\n * @returns Promise resolving to settlement response\n */\n async settle(\n payload: PaymentPayload,\n requirements: PaymentRequirements,\n ): Promise<SettleResponse> {\n // First verify the payment\n const verification = await this.verify(payload, requirements);\n if (!verification.isValid) {\n return {\n success: false,\n errorReason: verification.invalidReason,\n errorMessage: verification.invalidMessage,\n transaction: \"\",\n network: requirements.network,\n payer: verification.payer,\n };\n }\n\n const avmPayload = payload.payload as unknown as ExactAvmPayloadV2;\n const { paymentGroup, paymentIndex } = avmPayload;\n const facilitatorAddresses = this.signer.getAddresses();\n\n // Reuse shared decode logic\n const decoded = this.decodeTransactionGroup(paymentGroup, facilitatorAddresses);\n if (\"error\" in decoded) {\n return {\n success: false,\n errorReason: Errors.ErrSettleFailed,\n errorMessage: decoded.error.invalidMessage ?? decoded.error.invalidReason,\n transaction: \"\",\n network: requirements.network,\n payer: verification.payer,\n };\n }\n\n // Reuse shared sign logic\n const prepared = await this.prepareSignedGroup(decoded.txns, paymentGroup);\n if (\"error\" in prepared) {\n return {\n success: false,\n errorReason: Errors.ErrSettleFailed,\n errorMessage: prepared.error.invalidMessage ?? prepared.error.invalidReason,\n transaction: \"\",\n network: requirements.network,\n payer: verification.payer,\n };\n }\n\n // Get the payment transaction ID before submission\n const paymentTxnBytes = prepared.signedTxns[paymentIndex];\n const paymentStxn = decodeSignedTxn(paymentTxnBytes);\n const paymentTxId = paymentStxn.txn.txId();\n\n // Submit transaction group\n try {\n await this.signer.sendTransactions(prepared.signedTxns, requirements.network);\n } catch (error) {\n return {\n success: false,\n errorReason: Errors.ErrSettleFailed,\n errorMessage: `Failed to submit transaction: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n transaction: paymentTxId,\n network: requirements.network,\n payer: verification.payer,\n };\n }\n\n // Wait for on-chain confirmation\n try {\n // Wait up to 10 rounds for on-chain confirmation\n await this.signer.waitForConfirmation(paymentTxId, requirements.network, 10);\n } catch (error) {\n return {\n success: false,\n errorReason: Errors.ErrConfirmationFailed,\n errorMessage: `Transaction submitted but confirmation failed: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n transaction: paymentTxId,\n network: requirements.network,\n payer: verification.payer,\n };\n }\n\n return {\n success: true,\n transaction: paymentTxId,\n network: requirements.network,\n payer: verification.payer,\n };\n }\n\n /**\n * Decodes all transactions in the group and validates structure.\n *\n * - Signed transactions are decoded as-is\n * - Unsigned transactions are only accepted from facilitator addresses (fee payers)\n * - Verifies group ID consistency across all transactions\n *\n * @param paymentGroup - Array of base64-encoded transaction strings\n * @param facilitatorAddresses - Addresses controlled by this facilitator\n * @returns Decoded transactions or an error response\n */\n private decodeTransactionGroup(\n paymentGroup: string[],\n facilitatorAddresses: readonly string[],\n ): { txns: SignedTransaction[] } | { error: VerifyResponse } {\n const txns: SignedTransaction[] = [];\n\n for (let i = 0; i < paymentGroup.length; i++) {\n try {\n const bytes = decodeTransaction(paymentGroup[i]);\n\n try {\n const stxn = decodeSignedTxn(bytes);\n // Validate that decoding actually produced a valid signed transaction.\n // algokit-utils decodeSignedTransaction is lenient and may succeed on raw unsigned\n // bytes, returning a transaction with type \"unknown\" and missing fields.\n if (!stxn.txn.type || stxn.txn.type === \"unknown\") {\n throw new Error(\"Invalid signed transaction: missing type\");\n }\n txns.push(stxn);\n } catch {\n // Unsigned transaction — only the facilitator's fee payer txn should be unsigned\n const unsignedTxn = decodeUnsignedTxn(bytes);\n const sender = unsignedTxn.sender.toString();\n\n if (!facilitatorAddresses.includes(sender)) {\n return {\n error: {\n isValid: false,\n invalidReason: Errors.ErrUnsignedNonFacilitator,\n invalidMessage: `Unsigned transaction at index ${i} from ${sender} is not a facilitator address`,\n },\n };\n }\n\n // Wrap unsigned txn for simulation (empty signature)\n const encodedForSimulate = encodeSignedTransaction({ txn: unsignedTxn });\n txns.push(decodeSignedTxn(encodedForSimulate));\n }\n } catch {\n return {\n error: {\n isValid: false,\n invalidReason: Errors.ErrInvalidTransaction,\n invalidMessage: `Failed to decode transaction at index ${i}`,\n },\n };\n }\n }\n\n // Verify group ID consistency\n if (txns.length > 1) {\n const firstGroup = txns[0].txn.group;\n const firstGroupId = firstGroup ? Buffer.from(firstGroup).toString(\"base64\") : null;\n\n for (let i = 1; i < txns.length; i++) {\n const group = txns[i].txn.group;\n const groupId = group ? Buffer.from(group).toString(\"base64\") : null;\n if (groupId !== firstGroupId) {\n return {\n error: {\n isValid: false,\n invalidReason: Errors.ErrInvalidGroupId,\n invalidMessage: \"Transactions have inconsistent group IDs\",\n },\n };\n }\n }\n }\n\n return { txns };\n }\n\n /**\n * Verifies fee payer transactions and signs them, returning the assembled group\n * ready for simulation or submission.\n *\n * @param decodedTxns - Decoded signed transaction objects\n * @param paymentGroup - Original base64-encoded transaction strings\n * @returns Signed transaction bytes or an error response\n */\n private async prepareSignedGroup(\n decodedTxns: SignedTransaction[],\n paymentGroup: string[],\n ): Promise<{ signedTxns: Uint8Array[] } | { error: VerifyResponse }> {\n const facilitatorAddresses = this.signer.getAddresses();\n const signedTxns: Uint8Array[] = [];\n\n for (let i = 0; i < decodedTxns.length; i++) {\n const txn = decodedTxns[i].txn;\n const sender = txn.sender.toString();\n\n if (facilitatorAddresses.includes(sender)) {\n const feeCheck = this.verifyFeePayerTransaction(txn, decodedTxns.length);\n if (!feeCheck.isValid) return { error: feeCheck };\n\n try {\n const signedTxn = await this.signer.signTransaction(encodeTransactionRaw(txn), sender);\n signedTxns.push(signedTxn);\n } catch (error) {\n return {\n error: {\n isValid: false,\n invalidReason: Errors.ErrInvalidFeePayer,\n invalidMessage: `Failed to sign fee payer transaction: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n },\n };\n }\n } else {\n signedTxns.push(decodeTransaction(paymentGroup[i]));\n }\n }\n\n return { signedTxns };\n }\n\n /**\n * Simulates the transaction group and returns the verification result.\n *\n * @param signedTxns - Signed transaction bytes to simulate\n * @param network - Target network for simulation\n * @returns Verification result from simulation\n */\n private async simulateTransactionGroup(\n signedTxns: Uint8Array[],\n network: Network,\n ): Promise<VerifyResponse> {\n try {\n const simResult = (await this.signer.simulateTransactions(signedTxns, network)) as {\n txnGroups?: Array<{ failureMessage?: string }>;\n };\n\n if (simResult.txnGroups?.[0]?.failureMessage) {\n return {\n isValid: false,\n invalidReason: Errors.ErrSimulationFailed,\n invalidMessage: simResult.txnGroups[0].failureMessage,\n };\n }\n\n return { isValid: true };\n } catch (error) {\n return {\n isValid: false,\n invalidReason: Errors.ErrSimulationFailed,\n invalidMessage: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n }\n\n /**\n * Verifies the payment transaction matches requirements\n *\n * @param stxn - The signed payment transaction\n * @param requirements - Payment requirements to verify against\n * @param encodedTxn - Base64-encoded transaction for signature check\n * @returns Verification result\n */\n private async verifyPaymentTransaction(\n stxn: SignedTransaction,\n requirements: PaymentRequirements,\n encodedTxn: string,\n ): Promise<VerifyResponse> {\n const txn = stxn.txn;\n\n // Must be an asset transfer\n if (txn.type !== \"axfer\") {\n return {\n isValid: false,\n invalidReason: Errors.ErrNotAssetTransfer,\n invalidMessage: `Expected asset transfer, got \"${txn.type}\"`,\n };\n }\n\n // Access asset transfer properties — properly typed in algokit-utils v10\n const assetTransfer = txn.assetTransfer;\n\n if (!assetTransfer) {\n return {\n isValid: false,\n invalidReason: Errors.ErrNotAssetTransfer,\n invalidMessage: \"Missing assetTransfer data\",\n };\n }\n\n // Use BigInt comparison to avoid string format mismatches (e.g. \"1000\" vs \"1000.0\")\n const amount = assetTransfer.amount ?? BigInt(0);\n\n if (amount !== BigInt(requirements.amount)) {\n return {\n isValid: false,\n invalidReason: Errors.ErrAmountMismatch,\n invalidMessage: `Expected ${requirements.amount}, got ${amount.toString()}`,\n };\n }\n\n // Verify receiver address matches payTo\n const receiver = assetTransfer.receiver ? assetTransfer.receiver.toString() : \"\";\n\n if (receiver !== requirements.payTo) {\n return {\n isValid: false,\n invalidReason: Errors.ErrReceiverMismatch,\n invalidMessage: `Expected ${requirements.payTo}, got ${receiver}`,\n };\n }\n\n // Verify asset\n const assetId = assetTransfer.assetId?.toString() ?? \"\";\n\n if (assetId !== requirements.asset) {\n return {\n isValid: false,\n invalidReason: Errors.ErrAssetMismatch,\n invalidMessage: `Expected asset ${requirements.asset}, got ${assetId}`,\n };\n }\n\n // Verify signature exists\n const txnBytes = decodeTransaction(encodedTxn);\n if (!hasSignature(txnBytes)) {\n return {\n isValid: false,\n invalidReason: Errors.ErrPaymentNotSigned,\n invalidMessage: \"Payment transaction is not signed\",\n };\n }\n\n // Verify the ed25519 signature was actually made by the sender\n if (stxn.sig) {\n const signedMsg = bytesForSigning.transaction(txn);\n const isValidSig = await ed25519Verifier(stxn.sig, signedMsg, txn.sender.publicKey);\n if (!isValidSig) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidSignature,\n invalidMessage: \"Payment transaction signature does not match sender\",\n };\n }\n }\n\n return { isValid: true };\n }\n\n /**\n * Verifies a fee payer transaction is safe to sign\n *\n * @param txn - The fee payer transaction to validate\n * @param groupSize - Number of transactions in the atomic group (for fee cap calculation)\n * @returns Verification result\n */\n private verifyFeePayerTransaction(txn: Transaction, groupSize: number): VerifyResponse {\n // Must be a payment transaction (for fee payment)\n if (txn.type !== \"pay\") {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidFeePayer,\n invalidMessage: `Expected payment transaction, got ${txn.type}`,\n };\n }\n\n // Access payment fields — properly typed in algokit-utils v10\n const paymentFields = txn.payment;\n\n // Must have zero amount (self-payment for fee coverage)\n const payAmount = paymentFields?.amount ?? BigInt(0);\n if (payAmount > BigInt(0)) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidFeePayer,\n invalidMessage: \"Fee payer amount must be 0\",\n };\n }\n\n // Must be self-payment (receiver == sender)\n if (paymentFields?.receiver) {\n const receiverAddr = paymentFields.receiver.toString();\n const senderAddr = txn.sender.toString();\n if (receiverAddr !== senderAddr) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidFeePayer,\n invalidMessage: \"Fee payer receiver must be same as sender (self-payment)\",\n };\n }\n }\n\n // Must not have close remainder to\n if (paymentFields?.closeRemainderTo) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidFeePayer,\n invalidMessage: \"closeRemainderTo not allowed on fee payer\",\n };\n }\n\n // Must not have rekey to\n if (txn.rekeyTo) {\n return {\n isValid: false,\n invalidReason: Errors.ErrInvalidFeePayer,\n invalidMessage: \"rekeyTo not allowed on fee payer\",\n };\n }\n\n // Fee must be reasonable — during congestion fees can rise, so the cap\n // is 5x the minimum fee (5000 µAlgo) per transaction in the group.\n // The fee payer covers the entire group via Algorand's fee pooling.\n const fee = Number(txn.fee ?? 0);\n const maxFee = maxReasonableGroupFee(groupSize);\n if (fee > maxFee) {\n return {\n isValid: false,\n invalidReason: Errors.ErrFeeTooHigh,\n invalidMessage: `Fee ${fee} exceeds maximum ${maxFee} (${groupSize} txns × 5000 µAlgo)`,\n };\n }\n\n return { isValid: true };\n }\n}\n","/**\n * AVM Facilitator error codes for verify and settle responses.\n *\n * Uses snake_case `invalid_exact_avm_*` prefix convention,\n * consistent with EVM (`invalid_exact_evm_*`) and SVM patterns.\n */\n\n// Verify errors — scheme/network/version\nexport const ErrInvalidScheme = \"invalid_exact_avm_scheme\";\nexport const ErrNetworkMismatch = \"invalid_exact_avm_network_mismatch\";\nexport const ErrInvalidVersion = \"invalid_exact_avm_invalid_version\";\n\n// Verify errors — payload structure\nexport const ErrInvalidPayload = \"invalid_exact_avm_payload\";\nexport const ErrGroupSizeExceeded = \"invalid_exact_avm_group_size_exceeded\";\nexport const ErrInvalidPaymentIndex = \"invalid_exact_avm_payment_index\";\nexport const ErrInvalidTransaction = \"invalid_exact_avm_invalid_transaction\";\nexport const ErrInvalidGroupId = \"invalid_exact_avm_invalid_group_id\";\n\n// Verify errors — payment correctness\nexport const ErrNotAssetTransfer = \"invalid_exact_avm_not_asset_transfer\";\nexport const ErrAmountMismatch = \"invalid_exact_avm_amount_mismatch\";\nexport const ErrReceiverMismatch = \"invalid_exact_avm_receiver_mismatch\";\nexport const ErrAssetMismatch = \"invalid_exact_avm_asset_mismatch\";\n\n// Verify errors — fee payer\nexport const ErrInvalidFeePayer = \"invalid_exact_avm_invalid_fee_payer\";\nexport const ErrFeeTooHigh = \"invalid_exact_avm_fee_too_high\";\n\n// Verify errors — signature\nexport const ErrPaymentNotSigned = \"invalid_exact_avm_payment_not_signed\";\nexport const ErrInvalidSignature = \"invalid_exact_avm_invalid_signature\";\n\n// Verify errors — simulation\nexport const ErrSimulationFailed = \"invalid_exact_avm_simulation_failed\";\n\n// Verify errors — facilitator safety\nexport const ErrFacilitatorTransferring = \"invalid_exact_avm_facilitator_transferring\";\n\n// Security errors\nexport const ErrUnsignedNonFacilitator = \"invalid_exact_avm_unsigned_non_facilitator\";\n\n// Settle errors\nexport const ErrSettleFailed = \"invalid_exact_avm_settlement_failed\";\nexport const ErrConfirmationFailed = \"invalid_exact_avm_confirmation_failed\";\n"],"mappings":"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","names":[]}
import {
USDC_CONFIG,
USDC_DECIMALS,
convertToTokenAmount
} from "../../chunk-SLF4VHVI.mjs";
USDC_DECIMALS
} from "../../chunk-MWOLCZUZ.mjs";
// src/exact/server/scheme.ts
import { convertToTokenAmount, numberToDecimalString } from "@x402/core/utils";
var ExactAvmScheme = class {

@@ -127,3 +127,3 @@ constructor() {

const assetInfo = this.getDefaultAsset(network);
const tokenAmount = convertToTokenAmount(amount.toString(), assetInfo.decimals);
const tokenAmount = convertToTokenAmount(numberToDecimalString(amount), assetInfo.decimals);
return {

@@ -130,0 +130,0 @@ amount: tokenAmount,

@@ -1,1 +0,1 @@

{"version":3,"sources":["../../../../src/exact/server/scheme.ts"],"sourcesContent":["/**\n * AVM Server Scheme for Exact Payment Protocol\n *\n * Parses prices and builds payment requirements for Algorand ASA transfers.\n */\n\nimport type {\n AssetAmount,\n Network,\n PaymentRequirements,\n Price,\n SchemeNetworkServer,\n MoneyParser,\n} from \"@x402/core/types\";\nimport { USDC_CONFIG, USDC_DECIMALS } from \"../../constants\";\nimport { convertToTokenAmount } from \"../../utils\";\n\n/**\n * AVM server implementation for the Exact payment scheme.\n *\n * Handles price parsing and payment requirements enhancement for Algorand networks.\n */\nexport class ExactAvmScheme implements SchemeNetworkServer {\n readonly scheme = \"exact\";\n private moneyParsers: MoneyParser[] = [];\n\n /**\n * Register a custom money parser in the parser chain.\n * Multiple parsers can be registered - they will be tried in registration order.\n * Each parser receives a decimal amount (e.g., 1.50 for $1.50).\n * If a parser returns null, the next parser in the chain will be tried.\n * The default parser is always the final fallback.\n *\n * @param parser - Custom function to convert amount to AssetAmount (or null to skip)\n * @returns The server instance for chaining\n *\n * @example\n * ```typescript\n * avmServer.registerMoneyParser(async (amount, network) => {\n * // Custom conversion logic for non-USDC assets\n * if (amount > 100) {\n * return { amount: (amount * 1e6).toString(), asset: \"12345678\" };\n * }\n * return null; // Use next parser\n * });\n * ```\n */\n registerMoneyParser(parser: MoneyParser): ExactAvmScheme {\n this.moneyParsers.push(parser);\n return this;\n }\n\n /**\n * Parses a price into an asset amount.\n * If price is already an AssetAmount, returns it directly.\n * If price is Money (string | number), parses to decimal and tries custom parsers.\n * Falls back to default conversion if all custom parsers return null.\n *\n * @param price - The price to parse\n * @param network - The network to use\n * @returns Promise that resolves to the parsed asset amount\n */\n async parsePrice(price: Price, network: Network): Promise<AssetAmount> {\n // If already an AssetAmount, return it directly\n if (typeof price === \"object\" && price !== null && \"amount\" in price) {\n if (!price.asset) {\n throw new Error(`Asset ID must be specified for AssetAmount on network ${network}`);\n }\n return {\n amount: price.amount,\n asset: price.asset,\n extra: price.extra || {},\n };\n }\n\n // Parse Money to decimal number\n const amount = this.parseMoneyToDecimal(price);\n\n // Try each custom money parser in order\n for (const parser of this.moneyParsers) {\n const result = await parser(amount, network);\n if (result !== null) {\n return result;\n }\n }\n\n // All custom parsers returned null, use default conversion\n return this.defaultMoneyConversion(amount, network);\n }\n\n /**\n * Build payment requirements for this scheme/network combination\n *\n * @param paymentRequirements - The base payment requirements\n * @param supportedKind - The supported kind from facilitator (contains extra data like feePayer)\n * @param supportedKind.x402Version - The x402 version\n * @param supportedKind.scheme - The logical payment scheme\n * @param supportedKind.network - The network identifier in CAIP-2 format\n * @param supportedKind.extra - Optional extra metadata (e.g., feePayer address)\n * @param extensionKeys - Extension keys supported by the facilitator\n * @returns Payment requirements ready to be sent to clients\n */\n enhancePaymentRequirements(\n paymentRequirements: PaymentRequirements,\n supportedKind: {\n x402Version: number;\n scheme: string;\n network: Network;\n extra?: Record<string, unknown>;\n },\n extensionKeys: string[],\n ): Promise<PaymentRequirements> {\n // Mark unused parameter\n void extensionKeys;\n\n // Get USDC config for the network\n const usdcConfig = USDC_CONFIG[supportedKind.network];\n const decimals = usdcConfig?.decimals ?? USDC_DECIMALS;\n\n // Build enhanced requirements with feePayer and decimals\n const enhanced: PaymentRequirements = {\n ...paymentRequirements,\n extra: {\n ...paymentRequirements.extra,\n decimals,\n },\n };\n\n // Add feePayer from supportedKind.extra if provided\n if (supportedKind.extra?.feePayer) {\n enhanced.extra = {\n ...enhanced.extra,\n feePayer: supportedKind.extra.feePayer,\n };\n }\n\n return Promise.resolve(enhanced);\n }\n\n /**\n * Parse Money (string | number) to a decimal number.\n * Handles formats like \"$1.50\", \"1.50\", 1.50, etc.\n *\n * @param money - The money value to parse\n * @returns Decimal number\n */\n private parseMoneyToDecimal(money: string | number): number {\n if (typeof money === \"number\") {\n return money;\n }\n\n // Remove $ sign and whitespace, then parse\n const cleanMoney = money.replace(/^\\$/, \"\").trim();\n const amount = parseFloat(cleanMoney);\n\n if (isNaN(amount)) {\n throw new Error(`Invalid money format: ${money}`);\n }\n\n return amount;\n }\n\n /**\n * Default money conversion implementation.\n * Converts decimal amount to the default stablecoin (USDC) on the specified network.\n *\n * @param amount - The decimal amount (e.g., 1.50)\n * @param network - The network to use\n * @returns The parsed asset amount in USDC\n */\n private defaultMoneyConversion(amount: number, network: Network): AssetAmount {\n const assetInfo = this.getDefaultAsset(network);\n const tokenAmount = convertToTokenAmount(amount.toString(), assetInfo.decimals);\n\n return {\n amount: tokenAmount,\n asset: assetInfo.asaId,\n };\n }\n\n /**\n * Get the default asset info for a network (USDC)\n *\n * @param network - The network to get asset info for\n * @returns The asset information including ASA ID, name, and decimals\n */\n private getDefaultAsset(network: Network): {\n asaId: string;\n name: string;\n decimals: number;\n } {\n const assetInfo = USDC_CONFIG[network];\n if (!assetInfo) {\n throw new Error(`No default asset configured for network ${network}`);\n }\n\n return assetInfo;\n }\n}\n"],"mappings":";;;;;;;AAsBO,IAAM,iBAAN,MAAoD;AAAA,EAApD;AACL,SAAS,SAAS;AAClB,SAAQ,eAA8B,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBvC,oBAAoB,QAAqC;AACvD,SAAK,aAAa,KAAK,MAAM;AAC7B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,WAAW,OAAc,SAAwC;AAErE,QAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,YAAY,OAAO;AACpE,UAAI,CAAC,MAAM,OAAO;AAChB,cAAM,IAAI,MAAM,yDAAyD,OAAO,EAAE;AAAA,MACpF;AACA,aAAO;AAAA,QACL,QAAQ,MAAM;AAAA,QACd,OAAO,MAAM;AAAA,QACb,OAAO,MAAM,SAAS,CAAC;AAAA,MACzB;AAAA,IACF;AAGA,UAAM,SAAS,KAAK,oBAAoB,KAAK;AAG7C,eAAW,UAAU,KAAK,cAAc;AACtC,YAAM,SAAS,MAAM,OAAO,QAAQ,OAAO;AAC3C,UAAI,WAAW,MAAM;AACnB,eAAO;AAAA,MACT;AAAA,IACF;AAGA,WAAO,KAAK,uBAAuB,QAAQ,OAAO;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,2BACE,qBACA,eAMA,eAC8B;AAE9B,SAAK;AAGL,UAAM,aAAa,YAAY,cAAc,OAAO;AACpD,UAAM,WAAW,YAAY,YAAY;AAGzC,UAAM,WAAgC;AAAA,MACpC,GAAG;AAAA,MACH,OAAO;AAAA,QACL,GAAG,oBAAoB;AAAA,QACvB;AAAA,MACF;AAAA,IACF;AAGA,QAAI,cAAc,OAAO,UAAU;AACjC,eAAS,QAAQ;AAAA,QACf,GAAG,SAAS;AAAA,QACZ,UAAU,cAAc,MAAM;AAAA,MAChC;AAAA,IACF;AAEA,WAAO,QAAQ,QAAQ,QAAQ;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,oBAAoB,OAAgC;AAC1D,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO;AAAA,IACT;AAGA,UAAM,aAAa,MAAM,QAAQ,OAAO,EAAE,EAAE,KAAK;AACjD,UAAM,SAAS,WAAW,UAAU;AAEpC,QAAI,MAAM,MAAM,GAAG;AACjB,YAAM,IAAI,MAAM,yBAAyB,KAAK,EAAE;AAAA,IAClD;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,uBAAuB,QAAgB,SAA+B;AAC5E,UAAM,YAAY,KAAK,gBAAgB,OAAO;AAC9C,UAAM,cAAc,qBAAqB,OAAO,SAAS,GAAG,UAAU,QAAQ;AAE9E,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,OAAO,UAAU;AAAA,IACnB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,gBAAgB,SAItB;AACA,UAAM,YAAY,YAAY,OAAO;AACrC,QAAI,CAAC,WAAW;AACd,YAAM,IAAI,MAAM,2CAA2C,OAAO,EAAE;AAAA,IACtE;AAEA,WAAO;AAAA,EACT;AACF;","names":[]}
{"version":3,"sources":["../../../../src/exact/server/scheme.ts"],"sourcesContent":["/**\n * AVM Server Scheme for Exact Payment Protocol\n *\n * Parses prices and builds payment requirements for Algorand ASA transfers.\n */\n\nimport type {\n AssetAmount,\n Network,\n PaymentRequirements,\n Price,\n SchemeNetworkServer,\n MoneyParser,\n} from \"@x402/core/types\";\nimport { convertToTokenAmount, numberToDecimalString } from \"@x402/core/utils\";\nimport { USDC_CONFIG, USDC_DECIMALS } from \"../../constants\";\n\n/**\n * AVM server implementation for the Exact payment scheme.\n *\n * Handles price parsing and payment requirements enhancement for Algorand networks.\n */\nexport class ExactAvmScheme implements SchemeNetworkServer {\n readonly scheme = \"exact\";\n private moneyParsers: MoneyParser[] = [];\n\n /**\n * Register a custom money parser in the parser chain.\n * Multiple parsers can be registered - they will be tried in registration order.\n * Each parser receives a decimal amount (e.g., 1.50 for $1.50).\n * If a parser returns null, the next parser in the chain will be tried.\n * The default parser is always the final fallback.\n *\n * @param parser - Custom function to convert amount to AssetAmount (or null to skip)\n * @returns The server instance for chaining\n *\n * @example\n * ```typescript\n * avmServer.registerMoneyParser(async (amount, network) => {\n * // Custom conversion logic for non-USDC assets\n * if (amount > 100) {\n * return { amount: (amount * 1e6).toString(), asset: \"12345678\" };\n * }\n * return null; // Use next parser\n * });\n * ```\n */\n registerMoneyParser(parser: MoneyParser): ExactAvmScheme {\n this.moneyParsers.push(parser);\n return this;\n }\n\n /**\n * Parses a price into an asset amount.\n * If price is already an AssetAmount, returns it directly.\n * If price is Money (string | number), parses to decimal and tries custom parsers.\n * Falls back to default conversion if all custom parsers return null.\n *\n * @param price - The price to parse\n * @param network - The network to use\n * @returns Promise that resolves to the parsed asset amount\n */\n async parsePrice(price: Price, network: Network): Promise<AssetAmount> {\n // If already an AssetAmount, return it directly\n if (typeof price === \"object\" && price !== null && \"amount\" in price) {\n if (!price.asset) {\n throw new Error(`Asset ID must be specified for AssetAmount on network ${network}`);\n }\n return {\n amount: price.amount,\n asset: price.asset,\n extra: price.extra || {},\n };\n }\n\n // Parse Money to decimal number\n const amount = this.parseMoneyToDecimal(price);\n\n // Try each custom money parser in order\n for (const parser of this.moneyParsers) {\n const result = await parser(amount, network);\n if (result !== null) {\n return result;\n }\n }\n\n // All custom parsers returned null, use default conversion\n return this.defaultMoneyConversion(amount, network);\n }\n\n /**\n * Build payment requirements for this scheme/network combination\n *\n * @param paymentRequirements - The base payment requirements\n * @param supportedKind - The supported kind from facilitator (contains extra data like feePayer)\n * @param supportedKind.x402Version - The x402 version\n * @param supportedKind.scheme - The logical payment scheme\n * @param supportedKind.network - The network identifier in CAIP-2 format\n * @param supportedKind.extra - Optional extra metadata (e.g., feePayer address)\n * @param extensionKeys - Extension keys supported by the facilitator\n * @returns Payment requirements ready to be sent to clients\n */\n enhancePaymentRequirements(\n paymentRequirements: PaymentRequirements,\n supportedKind: {\n x402Version: number;\n scheme: string;\n network: Network;\n extra?: Record<string, unknown>;\n },\n extensionKeys: string[],\n ): Promise<PaymentRequirements> {\n // Mark unused parameter\n void extensionKeys;\n\n // Get USDC config for the network\n const usdcConfig = USDC_CONFIG[supportedKind.network];\n const decimals = usdcConfig?.decimals ?? USDC_DECIMALS;\n\n // Build enhanced requirements with feePayer and decimals\n const enhanced: PaymentRequirements = {\n ...paymentRequirements,\n extra: {\n ...paymentRequirements.extra,\n decimals,\n },\n };\n\n // Add feePayer from supportedKind.extra if provided\n if (supportedKind.extra?.feePayer) {\n enhanced.extra = {\n ...enhanced.extra,\n feePayer: supportedKind.extra.feePayer,\n };\n }\n\n return Promise.resolve(enhanced);\n }\n\n /**\n * Parse Money (string | number) to a decimal number.\n * Handles formats like \"$1.50\", \"1.50\", 1.50, etc.\n *\n * @param money - The money value to parse\n * @returns Decimal number\n */\n private parseMoneyToDecimal(money: string | number): number {\n if (typeof money === \"number\") {\n return money;\n }\n\n // Remove $ sign and whitespace, then parse\n const cleanMoney = money.replace(/^\\$/, \"\").trim();\n const amount = parseFloat(cleanMoney);\n\n if (isNaN(amount)) {\n throw new Error(`Invalid money format: ${money}`);\n }\n\n return amount;\n }\n\n /**\n * Default money conversion implementation.\n * Converts decimal amount to the default stablecoin (USDC) on the specified network.\n *\n * @param amount - The decimal amount (e.g., 1.50)\n * @param network - The network to use\n * @returns The parsed asset amount in USDC\n */\n private defaultMoneyConversion(amount: number, network: Network): AssetAmount {\n const assetInfo = this.getDefaultAsset(network);\n const tokenAmount = convertToTokenAmount(numberToDecimalString(amount), assetInfo.decimals);\n\n return {\n amount: tokenAmount,\n asset: assetInfo.asaId,\n };\n }\n\n /**\n * Get the default asset info for a network (USDC)\n *\n * @param network - The network to get asset info for\n * @returns The asset information including ASA ID, name, and decimals\n */\n private getDefaultAsset(network: Network): {\n asaId: string;\n name: string;\n decimals: number;\n } {\n const assetInfo = USDC_CONFIG[network];\n if (!assetInfo) {\n throw new Error(`No default asset configured for network ${network}`);\n }\n\n return assetInfo;\n }\n}\n"],"mappings":";;;;;;AAcA,SAAS,sBAAsB,6BAA6B;AAQrD,IAAM,iBAAN,MAAoD;AAAA,EAApD;AACL,SAAS,SAAS;AAClB,SAAQ,eAA8B,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBvC,oBAAoB,QAAqC;AACvD,SAAK,aAAa,KAAK,MAAM;AAC7B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,WAAW,OAAc,SAAwC;AAErE,QAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,YAAY,OAAO;AACpE,UAAI,CAAC,MAAM,OAAO;AAChB,cAAM,IAAI,MAAM,yDAAyD,OAAO,EAAE;AAAA,MACpF;AACA,aAAO;AAAA,QACL,QAAQ,MAAM;AAAA,QACd,OAAO,MAAM;AAAA,QACb,OAAO,MAAM,SAAS,CAAC;AAAA,MACzB;AAAA,IACF;AAGA,UAAM,SAAS,KAAK,oBAAoB,KAAK;AAG7C,eAAW,UAAU,KAAK,cAAc;AACtC,YAAM,SAAS,MAAM,OAAO,QAAQ,OAAO;AAC3C,UAAI,WAAW,MAAM;AACnB,eAAO;AAAA,MACT;AAAA,IACF;AAGA,WAAO,KAAK,uBAAuB,QAAQ,OAAO;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,2BACE,qBACA,eAMA,eAC8B;AAE9B,SAAK;AAGL,UAAM,aAAa,YAAY,cAAc,OAAO;AACpD,UAAM,WAAW,YAAY,YAAY;AAGzC,UAAM,WAAgC;AAAA,MACpC,GAAG;AAAA,MACH,OAAO;AAAA,QACL,GAAG,oBAAoB;AAAA,QACvB;AAAA,MACF;AAAA,IACF;AAGA,QAAI,cAAc,OAAO,UAAU;AACjC,eAAS,QAAQ;AAAA,QACf,GAAG,SAAS;AAAA,QACZ,UAAU,cAAc,MAAM;AAAA,MAChC;AAAA,IACF;AAEA,WAAO,QAAQ,QAAQ,QAAQ;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,oBAAoB,OAAgC;AAC1D,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO;AAAA,IACT;AAGA,UAAM,aAAa,MAAM,QAAQ,OAAO,EAAE,EAAE,KAAK;AACjD,UAAM,SAAS,WAAW,UAAU;AAEpC,QAAI,MAAM,MAAM,GAAG;AACjB,YAAM,IAAI,MAAM,yBAAyB,KAAK,EAAE;AAAA,IAClD;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,uBAAuB,QAAgB,SAA+B;AAC5E,UAAM,YAAY,KAAK,gBAAgB,OAAO;AAC9C,UAAM,cAAc,qBAAqB,sBAAsB,MAAM,GAAG,UAAU,QAAQ;AAE1F,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,OAAO,UAAU;AAAA,IACnB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,gBAAgB,SAItB;AACA,UAAM,YAAY,YAAY,OAAO;AACrC,QAAI,CAAC,WAAW;AACd,YAAM,IAAI,MAAM,2CAA2C,OAAO,EAAE;AAAA,IACtE;AAEA,WAAO;AAAA,EACT;AACF;","names":[]}

@@ -7,2 +7,3 @@ export { ExactAvmScheme } from './exact/client/index.mjs';

export { ALGORAND_MIN_TX_FEE } from '@algorandfoundation/algokit-utils/amount';
export { convertToTokenAmount } from '@x402/core/utils';
import '@x402/core/types';

@@ -183,17 +184,4 @@ import '@algorandfoundation/algokit-utils/algod-client';

declare function getSenderFromTransaction(txnBytes: Uint8Array, isSigned?: boolean): string;
/**
* Converts a decimal amount to atomic units (token's smallest unit)
*
* @param decimalAmount - The decimal amount as a string (e.g., "1.50")
* @param decimals - Number of decimal places (e.g., 6 for USDC)
* @returns Amount in atomic units as a string
*
* @example
* ```typescript
* convertToTokenAmount("1.50", 6) // Returns "1500000"
* convertToTokenAmount("0.10", 6) // Returns "100000"
* ```
*/
declare function convertToTokenAmount(decimalAmount: string, decimals: number): string;
/**
* Converts atomic units to decimal amount

@@ -259,2 +247,2 @@ *

export { ALGORAND_MAINNET_CAIP2, ALGORAND_MAINNET_GENESIS_HASH, ALGORAND_TESTNET_CAIP2, ALGORAND_TESTNET_GENESIS_HASH, CAIP2_NETWORKS, type ExactAvmPayloadV2, MAX_REASONABLE_FEE_PER_TXN, USDC_CONFIG, USDC_DECIMALS, USDC_MAINNET_ASA_ID, USDC_TESTNET_ASA_ID, convertFromTokenAmount, convertToTokenAmount, decodeSignedTransaction, decodeTransaction, decodeUnsignedTransaction, encodeTransaction, getGenesisHashFromTransaction, getNetworkFromCaip2, getSenderFromTransaction, getTransactionId, hasSignature, isAlgorandNetwork, isExactAvmPayload, isTestnetNetwork, isValidAlgorandAddress, maxReasonableGroupFee, validateGroupId };
export { ALGORAND_MAINNET_CAIP2, ALGORAND_MAINNET_GENESIS_HASH, ALGORAND_TESTNET_CAIP2, ALGORAND_TESTNET_GENESIS_HASH, CAIP2_NETWORKS, type ExactAvmPayloadV2, MAX_REASONABLE_FEE_PER_TXN, USDC_CONFIG, USDC_DECIMALS, USDC_MAINNET_ASA_ID, USDC_TESTNET_ASA_ID, convertFromTokenAmount, decodeSignedTransaction, decodeTransaction, decodeUnsignedTransaction, encodeTransaction, getGenesisHashFromTransaction, getNetworkFromCaip2, getSenderFromTransaction, getTransactionId, hasSignature, isAlgorandNetwork, isExactAvmPayload, isTestnetNetwork, isValidAlgorandAddress, maxReasonableGroupFee, validateGroupId };
import {
ExactAvmScheme
} from "./chunk-IZZHPFP7.mjs";
} from "./chunk-OMKK3QVS.mjs";
import {

@@ -8,12 +8,2 @@ isExactAvmPayload

import {
ALGORAND_MAINNET_CAIP2,
ALGORAND_MAINNET_GENESIS_HASH,
ALGORAND_TESTNET_CAIP2,
ALGORAND_TESTNET_GENESIS_HASH,
CAIP2_NETWORKS,
MAX_REASONABLE_FEE_PER_TXN,
USDC_CONFIG,
USDC_DECIMALS,
USDC_MAINNET_ASA_ID,
USDC_TESTNET_ASA_ID,
convertFromTokenAmount,

@@ -33,5 +23,17 @@ convertToTokenAmount,

isValidAlgorandAddress,
maxReasonableGroupFee,
validateGroupId
} from "./chunk-SLF4VHVI.mjs";
} from "./chunk-QEY4CIDB.mjs";
import {
ALGORAND_MAINNET_CAIP2,
ALGORAND_MAINNET_GENESIS_HASH,
ALGORAND_TESTNET_CAIP2,
ALGORAND_TESTNET_GENESIS_HASH,
CAIP2_NETWORKS,
MAX_REASONABLE_FEE_PER_TXN,
USDC_CONFIG,
USDC_DECIMALS,
USDC_MAINNET_ASA_ID,
USDC_TESTNET_ASA_ID,
maxReasonableGroupFee
} from "./chunk-MWOLCZUZ.mjs";

@@ -38,0 +40,0 @@ // src/signer.ts

@@ -1,1 +0,1 @@

{"version":3,"sources":["../../src/signer.ts","../../src/index.ts"],"sourcesContent":["/**\n * AVM (Algorand) Signer Interfaces for x402 Payment Protocol\n *\n * This module defines the signer interfaces for client and facilitator operations.\n * Use the `toClientAvmSigner` and `toFacilitatorAvmSigner` helper functions to create\n * signers from a Base64-encoded private key.\n *\n * @example Client signer:\n * ```typescript\n * import { toClientAvmSigner } from \"@x402/avm\";\n *\n * const signer = toClientAvmSigner(process.env.AVM_PRIVATE_KEY!);\n * ```\n *\n * @example Facilitator signer:\n * ```typescript\n * import { toFacilitatorAvmSigner } from \"@x402/avm\";\n *\n * const signer = toFacilitatorAvmSigner(process.env.AVM_PRIVATE_KEY!);\n * ```\n */\n\nimport { AlgorandClient } from \"@algorandfoundation/algokit-utils/algorand-client\";\nimport { ed25519Generator } from \"@algorandfoundation/algokit-utils/crypto\";\nimport {\n decodeTransaction,\n generateAddressWithSigners,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport type {\n AddressWithSigners,\n AddressWithTransactionSigner,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport { waitForConfirmation } from \"@algorandfoundation/algokit-utils/transaction\";\nimport type { Network } from \"@x402/core/types\";\nimport { ALGORAND_TESTNET_CAIP2 } from \"./constants\";\n\n/**\n * Symbol key used to attach the internal algokit-utils AddressWithSigners\n * to a ClientAvmSigner created via toClientAvmSigner().\n * This enables internal code to extract the native algokit signer for\n * use with TransactionComposer and AlgorandClient.\n */\nexport const ALGOKIT_SIGNER = Symbol(\"algokit-signer\");\n\n/**\n * Client-side signer interface for Algorand wallets\n *\n * Compatible with @txnlab/use-wallet and similar wallet libraries.\n * Used to sign payment transactions on the client side.\n */\nexport interface ClientAvmSigner {\n /**\n * The Algorand address of the signer\n */\n address: string;\n\n /**\n * Sign one or more transactions\n *\n * @param txns - Array of unsigned transactions (encoded as Uint8Array)\n * @param indexesToSign - Optional array of indexes to sign (if not provided, sign all)\n * @returns Promise resolving to array of signed transactions (null for unsigned)\n */\n signTransactions(txns: Uint8Array[], indexesToSign?: number[]): Promise<(Uint8Array | null)[]>;\n}\n\n/**\n * Configuration for client AVM operations\n */\nexport interface ClientAvmConfig {\n /**\n * Pre-configured AlgorandClient instance (takes precedence over URL/token)\n * Use AlgorandClient.testNet(), .mainNet(), .fromConfig(), etc.\n */\n algorandClient?: import(\"@algorandfoundation/algokit-utils/algorand-client\").AlgorandClient;\n\n /**\n * Algod API URL (used if algorandClient not provided)\n */\n algodUrl?: string;\n\n /**\n * Algod API token\n */\n algodToken?: string;\n}\n\n/**\n * Facilitator signer interface for Algorand operations\n *\n * Used by the facilitator to verify and settle payments.\n * Supports multiple addresses for load balancing and key rotation.\n *\n * @example Using the helper function:\n * ```typescript\n * import { toFacilitatorAvmSigner } from \"@x402/avm\";\n *\n * const signer = toFacilitatorAvmSigner(process.env.AVM_PRIVATE_KEY!);\n * ```\n */\nexport interface FacilitatorAvmSigner {\n /**\n * Get all addresses this facilitator can use as fee payers\n *\n * @returns Array of Algorand addresses\n */\n getAddresses(): readonly string[];\n\n /**\n * Sign a transaction with the signer matching the sender address\n *\n * @param txn - Transaction bytes to sign\n * @param senderAddress - Expected sender address (for verification)\n * @returns Promise resolving to signed transaction bytes\n */\n signTransaction(txn: Uint8Array, senderAddress: string): Promise<Uint8Array>;\n\n /**\n * Get Algod client for a specific network\n *\n * @param network - Network identifier (CAIP-2 or V1 format)\n * @returns AlgodClient instance from @algorandfoundation/algokit-utils\n */\n getAlgodClient(\n network: Network,\n ): import(\"@algorandfoundation/algokit-utils/algod-client\").AlgodClient;\n\n /**\n * Simulate a transaction group before submission\n *\n * @param txns - Array of signed transaction bytes\n * @param network - Network identifier\n * @returns Promise resolving to SimulateResponse\n */\n simulateTransactions(\n txns: Uint8Array[],\n network: Network,\n ): Promise<import(\"@algorandfoundation/algokit-utils/algod-client\").SimulateResponse>;\n\n /**\n * Submit signed transactions to the network\n *\n * @param signedTxns - Array of signed transaction bytes\n * @param network - Network identifier\n * @returns Promise resolving to transaction ID\n */\n sendTransactions(signedTxns: Uint8Array[], network: Network): Promise<string>;\n\n /**\n * Wait for a transaction to be confirmed\n *\n * @param txId - Transaction ID\n * @param network - Network identifier\n * @param waitRounds - Number of rounds to wait (default: 4)\n * @returns Promise resolving to PendingTransactionResponse\n */\n waitForConfirmation(\n txId: string,\n network: Network,\n waitRounds?: number,\n ): Promise<import(\"@algorandfoundation/algokit-utils/algod-client\").PendingTransactionResponse>;\n}\n\n/**\n * Configuration for creating a facilitator signer\n */\nexport interface FacilitatorAvmSignerConfig {\n /**\n * Algod URL for mainnet\n */\n mainnetUrl?: string;\n\n /**\n * Algod URL for testnet\n */\n testnetUrl?: string;\n\n /**\n * Algod API token\n */\n algodToken?: string;\n}\n\n/**\n * Type guard to check if a wallet implements ClientAvmSigner\n *\n * @param wallet - The wallet to check\n * @returns True if the wallet implements ClientAvmSigner\n */\nexport function isAvmSignerWallet(wallet: unknown): wallet is ClientAvmSigner {\n return (\n typeof wallet === \"object\" &&\n wallet !== null &&\n \"address\" in wallet &&\n typeof (wallet as ClientAvmSigner).address === \"string\" &&\n \"signTransactions\" in wallet &&\n typeof (wallet as ClientAvmSigner).signTransactions === \"function\"\n );\n}\n\n/**\n * Decodes a Base64-encoded 64-byte private key into address and raw Ed25519 signer.\n *\n * @param privateKeyBase64 - Base64-encoded 64-byte key (32-byte seed + 32-byte public key)\n * @returns Address and raw Ed25519 signer function\n */\nfunction decodePrivateKey(privateKeyBase64: string) {\n const secretKey = Buffer.from(privateKeyBase64, \"base64\");\n if (secretKey.length !== 64) {\n throw new Error(\n \"AVM private key must be a Base64-encoded 64-byte key (32-byte seed + 32-byte public key)\",\n );\n }\n const seed = secretKey.subarray(0, 32);\n return ed25519Generator(seed);\n}\n\n/**\n * Creates a ClientAvmSigner from a Base64-encoded private key.\n *\n * This is the recommended way to create a client-side AVM signer for x402 payments.\n *\n * @param privateKeyBase64 - Base64-encoded 64-byte key (32-byte seed + 32-byte public key)\n * @returns A complete ClientAvmSigner ready for use with ExactAvmScheme\n *\n * @example\n * ```typescript\n * import { toClientAvmSigner } from \"@x402/avm\";\n * import { ExactAvmScheme } from \"@x402/avm/exact/client\";\n *\n * const signer = toClientAvmSigner(process.env.AVM_PRIVATE_KEY!);\n * client.register(\"algorand:*\", new ExactAvmScheme(signer));\n * ```\n */\nexport function toClientAvmSigner(privateKeyBase64: string): ClientAvmSigner {\n const { ed25519Pubkey, rawEd25519Signer } = decodePrivateKey(privateKeyBase64);\n\n // Use algokit-utils generateAddressWithSigners for the canonical signer implementation\n const algokitSigners = generateAddressWithSigners({ ed25519Pubkey, rawEd25519Signer });\n const address = algokitSigners.addr.toString();\n\n const signer: ClientAvmSigner = {\n address,\n signTransactions: async (txns: Uint8Array[], indexesToSign?: number[]) => {\n return Promise.all(\n txns.map(async (txn, i) => {\n if (indexesToSign && !indexesToSign.includes(i)) return null;\n const decoded = decodeTransaction(txn);\n // Delegate to the algokit-utils signer (signs a single transaction in a group)\n const signedBytes = await algokitSigners.signer([decoded], [0]);\n return signedBytes[0];\n }),\n );\n },\n };\n\n // Attach the internal algokit-utils AddressWithSigners for use by internal code\n // (e.g., TransactionComposer integration in client scheme)\n Object.defineProperty(signer, ALGOKIT_SIGNER, {\n value: algokitSigners,\n enumerable: false,\n writable: false,\n });\n\n return signer;\n}\n\n/**\n * Extracts the internal algokit-utils AddressWithTransactionSigner from a ClientAvmSigner,\n * if available. Returns null for wallet-created signers that don't have an internal\n * algokit signer (e.g., signers created from browser wallet adapters).\n *\n * This is useful for internal code that needs to register the signer with\n * AlgorandClient or TransactionComposer.\n *\n * @param signer - A ClientAvmSigner instance\n * @returns The internal AddressWithTransactionSigner, or null if not available\n */\nexport function getAlgokitSigner(signer: ClientAvmSigner): AddressWithTransactionSigner | null {\n const internal = (signer as unknown as Record<symbol, unknown>)[ALGOKIT_SIGNER] as\n | AddressWithSigners\n | undefined;\n if (internal && \"addr\" in internal && \"signer\" in internal) {\n return { addr: internal.addr, signer: internal.signer };\n }\n return null;\n}\n\n/**\n * Determines if a network identifier refers to testnet.\n *\n * @param network - The network identifier (CAIP-2 format)\n * @returns True if the network is testnet\n */\nfunction isTestnet(network: string): boolean {\n return network === ALGORAND_TESTNET_CAIP2;\n}\n\n/**\n * Creates a FacilitatorAvmSigner from a Base64-encoded private key.\n *\n * This is the recommended way to create a facilitator-side AVM signer for x402 payments.\n * Uses `AlgorandClient.testNet()` / `AlgorandClient.mainNet()` from AlgoKit Utils for\n * network connectivity, with optional URL overrides via config.\n *\n * @param privateKeyBase64 - Base64-encoded 64-byte key (32-byte seed + 32-byte public key)\n * @param config - Optional configuration for custom Algod URLs\n * @returns A complete FacilitatorAvmSigner ready for use with ExactAvmScheme\n *\n * @example\n * ```typescript\n * import { toFacilitatorAvmSigner } from \"@x402/avm\";\n * import { ExactAvmScheme } from \"@x402/avm/exact/facilitator\";\n *\n * // Default (AlgoNode endpoints):\n * const signer = toFacilitatorAvmSigner(process.env.AVM_PRIVATE_KEY!);\n *\n * // With custom URLs:\n * const signer = toFacilitatorAvmSigner(process.env.AVM_PRIVATE_KEY!, {\n * testnetUrl: \"https://my-testnet-node.example.com\",\n * mainnetUrl: \"https://my-mainnet-node.example.com\",\n * });\n *\n * facilitator.register(\"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=\", new ExactAvmScheme(signer));\n * ```\n */\nexport function toFacilitatorAvmSigner(\n privateKeyBase64: string,\n config?: FacilitatorAvmSignerConfig,\n): FacilitatorAvmSigner {\n const { ed25519Pubkey, rawEd25519Signer } = decodePrivateKey(privateKeyBase64);\n\n // Use algokit-utils generateAddressWithSigners for the canonical signer implementation\n const algokitSigners = generateAddressWithSigners({ ed25519Pubkey, rawEd25519Signer });\n const address = algokitSigners.addr.toString();\n\n // Create AlgorandClient instances for each network, with optional URL overrides\n const getAlgorandClientForNetwork = (network: string) => {\n if (isTestnet(network)) {\n if (config?.testnetUrl) {\n return AlgorandClient.fromConfig({\n algodConfig: { server: config.testnetUrl, token: config.algodToken ?? \"\" },\n });\n }\n return AlgorandClient.testNet();\n }\n if (config?.mainnetUrl) {\n return AlgorandClient.fromConfig({\n algodConfig: { server: config.mainnetUrl, token: config.algodToken ?? \"\" },\n });\n }\n return AlgorandClient.mainNet();\n };\n\n // Cache AlgorandClient instances per network\n const clientCache = new Map<string, ReturnType<typeof AlgorandClient.testNet>>();\n\n const getClient = (network: string) => {\n const key = isTestnet(network) ? \"testnet\" : \"mainnet\";\n let client = clientCache.get(key);\n if (!client) {\n client = getAlgorandClientForNetwork(network);\n clientCache.set(key, client);\n }\n return client;\n };\n\n return {\n getAddresses: () => [address] as readonly string[],\n\n signTransaction: async (txn: Uint8Array, _: string) => {\n const decoded = decodeTransaction(txn);\n // Delegate to the algokit-utils signer\n const signedBytes = await algokitSigners.signer([decoded], [0]);\n return signedBytes[0];\n },\n\n getAlgodClient: (network: string) => getClient(network).client.algod,\n\n simulateTransactions: async (txns: Uint8Array[], network: string) => {\n const algod = getClient(network).client.algod;\n return await algod.simulateRawTransactions(txns);\n },\n\n sendTransactions: async (signedTxns: Uint8Array[], network: string) => {\n const algod = getClient(network).client.algod;\n const response = await algod.sendRawTransaction(signedTxns);\n return response.txId;\n },\n\n waitForConfirmation: async (txId: string, network: string, waitRounds: number = 5) => {\n const algod = getClient(network).client.algod;\n return await waitForConfirmation(txId, waitRounds, algod);\n },\n };\n}\n","/**\n * @module @x402/avm - x402 Payment Protocol AVM (Algorand) Implementation\n *\n * This module provides the Algorand-specific implementation of the x402 payment protocol.\n *\n * @example Client signer:\n * ```typescript\n * import { toClientAvmSigner } from \"@x402/avm\";\n *\n * const signer = toClientAvmSigner(process.env.AVM_PRIVATE_KEY!);\n * ```\n *\n * @example Facilitator signer:\n * ```typescript\n * import { toFacilitatorAvmSigner } from \"@x402/avm\";\n *\n * const signer = toFacilitatorAvmSigner(process.env.AVM_PRIVATE_KEY!);\n * ```\n */\n\n// Exact scheme client\nexport { ExactAvmScheme } from \"./exact\";\n\n// Signer helpers and interfaces\nexport {\n isAvmSignerWallet,\n toClientAvmSigner,\n toFacilitatorAvmSigner,\n getAlgokitSigner,\n ALGOKIT_SIGNER,\n} from \"./signer\";\nexport type {\n ClientAvmSigner,\n ClientAvmConfig,\n FacilitatorAvmSigner,\n FacilitatorAvmSignerConfig,\n} from \"./signer\";\n\n// Re-export algokit-utils signer types for consumers who want native interop\nexport type {\n AddressWithTransactionSigner,\n AddressWithSigners,\n TransactionSigner,\n} from \"@algorandfoundation/algokit-utils/transact\";\n\n// Types\nexport type { ExactAvmPayloadV2 } from \"./types\";\nexport { isExactAvmPayload } from \"./types\";\n\n// Constants\nexport {\n // CAIP-2 Network Identifiers\n ALGORAND_MAINNET_CAIP2,\n ALGORAND_TESTNET_CAIP2,\n CAIP2_NETWORKS,\n // Genesis Hashes\n ALGORAND_MAINNET_GENESIS_HASH,\n ALGORAND_TESTNET_GENESIS_HASH,\n // USDC Configuration\n USDC_MAINNET_ASA_ID,\n USDC_TESTNET_ASA_ID,\n USDC_DECIMALS,\n USDC_CONFIG,\n // Transaction Limits\n MAX_REASONABLE_FEE_PER_TXN,\n maxReasonableGroupFee,\n} from \"./constants\";\n\n// Re-export algokit-utils constants that consumers may need\nexport { ALGORAND_ADDRESS_LENGTH } from \"@algorandfoundation/algokit-utils/common\";\nexport { ALGORAND_MIN_TX_FEE } from \"@algorandfoundation/algokit-utils/amount\";\n\n// Utilities\nexport {\n encodeTransaction,\n decodeTransaction,\n decodeSignedTransaction,\n decodeUnsignedTransaction,\n isValidAlgorandAddress,\n getSenderFromTransaction,\n convertToTokenAmount,\n convertFromTokenAmount,\n getNetworkFromCaip2,\n isAlgorandNetwork,\n isTestnetNetwork,\n getGenesisHashFromTransaction,\n validateGroupId,\n getTransactionId,\n hasSignature,\n} from \"./utils\";\n"],"mappings":"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","names":["decodeTransaction","decodeTransaction"]}
{"version":3,"sources":["../../src/signer.ts","../../src/index.ts"],"sourcesContent":["/**\n * AVM (Algorand) Signer Interfaces for x402 Payment Protocol\n *\n * This module defines the signer interfaces for client and facilitator operations.\n * Use the `toClientAvmSigner` and `toFacilitatorAvmSigner` helper functions to create\n * signers from a Base64-encoded private key.\n *\n * @example Client signer:\n * ```typescript\n * import { toClientAvmSigner } from \"@x402/avm\";\n *\n * const signer = toClientAvmSigner(process.env.AVM_PRIVATE_KEY!);\n * ```\n *\n * @example Facilitator signer:\n * ```typescript\n * import { toFacilitatorAvmSigner } from \"@x402/avm\";\n *\n * const signer = toFacilitatorAvmSigner(process.env.AVM_PRIVATE_KEY!);\n * ```\n */\n\nimport { AlgorandClient } from \"@algorandfoundation/algokit-utils/algorand-client\";\nimport { ed25519Generator } from \"@algorandfoundation/algokit-utils/crypto\";\nimport {\n decodeTransaction,\n generateAddressWithSigners,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport type {\n AddressWithSigners,\n AddressWithTransactionSigner,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport { waitForConfirmation } from \"@algorandfoundation/algokit-utils/transaction\";\nimport type { Network } from \"@x402/core/types\";\nimport { ALGORAND_TESTNET_CAIP2 } from \"./constants\";\n\n/**\n * Symbol key used to attach the internal algokit-utils AddressWithSigners\n * to a ClientAvmSigner created via toClientAvmSigner().\n * This enables internal code to extract the native algokit signer for\n * use with TransactionComposer and AlgorandClient.\n */\nexport const ALGOKIT_SIGNER = Symbol(\"algokit-signer\");\n\n/**\n * Client-side signer interface for Algorand wallets\n *\n * Compatible with @txnlab/use-wallet and similar wallet libraries.\n * Used to sign payment transactions on the client side.\n */\nexport interface ClientAvmSigner {\n /**\n * The Algorand address of the signer\n */\n address: string;\n\n /**\n * Sign one or more transactions\n *\n * @param txns - Array of unsigned transactions (encoded as Uint8Array)\n * @param indexesToSign - Optional array of indexes to sign (if not provided, sign all)\n * @returns Promise resolving to array of signed transactions (null for unsigned)\n */\n signTransactions(txns: Uint8Array[], indexesToSign?: number[]): Promise<(Uint8Array | null)[]>;\n}\n\n/**\n * Configuration for client AVM operations\n */\nexport interface ClientAvmConfig {\n /**\n * Pre-configured AlgorandClient instance (takes precedence over URL/token)\n * Use AlgorandClient.testNet(), .mainNet(), .fromConfig(), etc.\n */\n algorandClient?: import(\"@algorandfoundation/algokit-utils/algorand-client\").AlgorandClient;\n\n /**\n * Algod API URL (used if algorandClient not provided)\n */\n algodUrl?: string;\n\n /**\n * Algod API token\n */\n algodToken?: string;\n}\n\n/**\n * Facilitator signer interface for Algorand operations\n *\n * Used by the facilitator to verify and settle payments.\n * Supports multiple addresses for load balancing and key rotation.\n *\n * @example Using the helper function:\n * ```typescript\n * import { toFacilitatorAvmSigner } from \"@x402/avm\";\n *\n * const signer = toFacilitatorAvmSigner(process.env.AVM_PRIVATE_KEY!);\n * ```\n */\nexport interface FacilitatorAvmSigner {\n /**\n * Get all addresses this facilitator can use as fee payers\n *\n * @returns Array of Algorand addresses\n */\n getAddresses(): readonly string[];\n\n /**\n * Sign a transaction with the signer matching the sender address\n *\n * @param txn - Transaction bytes to sign\n * @param senderAddress - Expected sender address (for verification)\n * @returns Promise resolving to signed transaction bytes\n */\n signTransaction(txn: Uint8Array, senderAddress: string): Promise<Uint8Array>;\n\n /**\n * Get Algod client for a specific network\n *\n * @param network - Network identifier (CAIP-2 or V1 format)\n * @returns AlgodClient instance from @algorandfoundation/algokit-utils\n */\n getAlgodClient(\n network: Network,\n ): import(\"@algorandfoundation/algokit-utils/algod-client\").AlgodClient;\n\n /**\n * Simulate a transaction group before submission\n *\n * @param txns - Array of signed transaction bytes\n * @param network - Network identifier\n * @returns Promise resolving to SimulateResponse\n */\n simulateTransactions(\n txns: Uint8Array[],\n network: Network,\n ): Promise<import(\"@algorandfoundation/algokit-utils/algod-client\").SimulateResponse>;\n\n /**\n * Submit signed transactions to the network\n *\n * @param signedTxns - Array of signed transaction bytes\n * @param network - Network identifier\n * @returns Promise resolving to transaction ID\n */\n sendTransactions(signedTxns: Uint8Array[], network: Network): Promise<string>;\n\n /**\n * Wait for a transaction to be confirmed\n *\n * @param txId - Transaction ID\n * @param network - Network identifier\n * @param waitRounds - Number of rounds to wait (default: 4)\n * @returns Promise resolving to PendingTransactionResponse\n */\n waitForConfirmation(\n txId: string,\n network: Network,\n waitRounds?: number,\n ): Promise<import(\"@algorandfoundation/algokit-utils/algod-client\").PendingTransactionResponse>;\n}\n\n/**\n * Configuration for creating a facilitator signer\n */\nexport interface FacilitatorAvmSignerConfig {\n /**\n * Algod URL for mainnet\n */\n mainnetUrl?: string;\n\n /**\n * Algod URL for testnet\n */\n testnetUrl?: string;\n\n /**\n * Algod API token\n */\n algodToken?: string;\n}\n\n/**\n * Type guard to check if a wallet implements ClientAvmSigner\n *\n * @param wallet - The wallet to check\n * @returns True if the wallet implements ClientAvmSigner\n */\nexport function isAvmSignerWallet(wallet: unknown): wallet is ClientAvmSigner {\n return (\n typeof wallet === \"object\" &&\n wallet !== null &&\n \"address\" in wallet &&\n typeof (wallet as ClientAvmSigner).address === \"string\" &&\n \"signTransactions\" in wallet &&\n typeof (wallet as ClientAvmSigner).signTransactions === \"function\"\n );\n}\n\n/**\n * Decodes a Base64-encoded 64-byte private key into address and raw Ed25519 signer.\n *\n * @param privateKeyBase64 - Base64-encoded 64-byte key (32-byte seed + 32-byte public key)\n * @returns Address and raw Ed25519 signer function\n */\nfunction decodePrivateKey(privateKeyBase64: string) {\n const secretKey = Buffer.from(privateKeyBase64, \"base64\");\n if (secretKey.length !== 64) {\n throw new Error(\n \"AVM private key must be a Base64-encoded 64-byte key (32-byte seed + 32-byte public key)\",\n );\n }\n const seed = secretKey.subarray(0, 32);\n return ed25519Generator(seed);\n}\n\n/**\n * Creates a ClientAvmSigner from a Base64-encoded private key.\n *\n * This is the recommended way to create a client-side AVM signer for x402 payments.\n *\n * @param privateKeyBase64 - Base64-encoded 64-byte key (32-byte seed + 32-byte public key)\n * @returns A complete ClientAvmSigner ready for use with ExactAvmScheme\n *\n * @example\n * ```typescript\n * import { toClientAvmSigner } from \"@x402/avm\";\n * import { ExactAvmScheme } from \"@x402/avm/exact/client\";\n *\n * const signer = toClientAvmSigner(process.env.AVM_PRIVATE_KEY!);\n * client.register(\"algorand:*\", new ExactAvmScheme(signer));\n * ```\n */\nexport function toClientAvmSigner(privateKeyBase64: string): ClientAvmSigner {\n const { ed25519Pubkey, rawEd25519Signer } = decodePrivateKey(privateKeyBase64);\n\n // Use algokit-utils generateAddressWithSigners for the canonical signer implementation\n const algokitSigners = generateAddressWithSigners({ ed25519Pubkey, rawEd25519Signer });\n const address = algokitSigners.addr.toString();\n\n const signer: ClientAvmSigner = {\n address,\n signTransactions: async (txns: Uint8Array[], indexesToSign?: number[]) => {\n return Promise.all(\n txns.map(async (txn, i) => {\n if (indexesToSign && !indexesToSign.includes(i)) return null;\n const decoded = decodeTransaction(txn);\n // Delegate to the algokit-utils signer (signs a single transaction in a group)\n const signedBytes = await algokitSigners.signer([decoded], [0]);\n return signedBytes[0];\n }),\n );\n },\n };\n\n // Attach the internal algokit-utils AddressWithSigners for use by internal code\n // (e.g., TransactionComposer integration in client scheme)\n Object.defineProperty(signer, ALGOKIT_SIGNER, {\n value: algokitSigners,\n enumerable: false,\n writable: false,\n });\n\n return signer;\n}\n\n/**\n * Extracts the internal algokit-utils AddressWithTransactionSigner from a ClientAvmSigner,\n * if available. Returns null for wallet-created signers that don't have an internal\n * algokit signer (e.g., signers created from browser wallet adapters).\n *\n * This is useful for internal code that needs to register the signer with\n * AlgorandClient or TransactionComposer.\n *\n * @param signer - A ClientAvmSigner instance\n * @returns The internal AddressWithTransactionSigner, or null if not available\n */\nexport function getAlgokitSigner(signer: ClientAvmSigner): AddressWithTransactionSigner | null {\n const internal = (signer as unknown as Record<symbol, unknown>)[ALGOKIT_SIGNER] as\n | AddressWithSigners\n | undefined;\n if (internal && \"addr\" in internal && \"signer\" in internal) {\n return { addr: internal.addr, signer: internal.signer };\n }\n return null;\n}\n\n/**\n * Determines if a network identifier refers to testnet.\n *\n * @param network - The network identifier (CAIP-2 format)\n * @returns True if the network is testnet\n */\nfunction isTestnet(network: string): boolean {\n return network === ALGORAND_TESTNET_CAIP2;\n}\n\n/**\n * Creates a FacilitatorAvmSigner from a Base64-encoded private key.\n *\n * This is the recommended way to create a facilitator-side AVM signer for x402 payments.\n * Uses `AlgorandClient.testNet()` / `AlgorandClient.mainNet()` from AlgoKit Utils for\n * network connectivity, with optional URL overrides via config.\n *\n * @param privateKeyBase64 - Base64-encoded 64-byte key (32-byte seed + 32-byte public key)\n * @param config - Optional configuration for custom Algod URLs\n * @returns A complete FacilitatorAvmSigner ready for use with ExactAvmScheme\n *\n * @example\n * ```typescript\n * import { toFacilitatorAvmSigner } from \"@x402/avm\";\n * import { ExactAvmScheme } from \"@x402/avm/exact/facilitator\";\n *\n * // Default (AlgoNode endpoints):\n * const signer = toFacilitatorAvmSigner(process.env.AVM_PRIVATE_KEY!);\n *\n * // With custom URLs:\n * const signer = toFacilitatorAvmSigner(process.env.AVM_PRIVATE_KEY!, {\n * testnetUrl: \"https://my-testnet-node.example.com\",\n * mainnetUrl: \"https://my-mainnet-node.example.com\",\n * });\n *\n * facilitator.register(\"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=\", new ExactAvmScheme(signer));\n * ```\n */\nexport function toFacilitatorAvmSigner(\n privateKeyBase64: string,\n config?: FacilitatorAvmSignerConfig,\n): FacilitatorAvmSigner {\n const { ed25519Pubkey, rawEd25519Signer } = decodePrivateKey(privateKeyBase64);\n\n // Use algokit-utils generateAddressWithSigners for the canonical signer implementation\n const algokitSigners = generateAddressWithSigners({ ed25519Pubkey, rawEd25519Signer });\n const address = algokitSigners.addr.toString();\n\n // Create AlgorandClient instances for each network, with optional URL overrides\n const getAlgorandClientForNetwork = (network: string) => {\n if (isTestnet(network)) {\n if (config?.testnetUrl) {\n return AlgorandClient.fromConfig({\n algodConfig: { server: config.testnetUrl, token: config.algodToken ?? \"\" },\n });\n }\n return AlgorandClient.testNet();\n }\n if (config?.mainnetUrl) {\n return AlgorandClient.fromConfig({\n algodConfig: { server: config.mainnetUrl, token: config.algodToken ?? \"\" },\n });\n }\n return AlgorandClient.mainNet();\n };\n\n // Cache AlgorandClient instances per network\n const clientCache = new Map<string, ReturnType<typeof AlgorandClient.testNet>>();\n\n const getClient = (network: string) => {\n const key = isTestnet(network) ? \"testnet\" : \"mainnet\";\n let client = clientCache.get(key);\n if (!client) {\n client = getAlgorandClientForNetwork(network);\n clientCache.set(key, client);\n }\n return client;\n };\n\n return {\n getAddresses: () => [address] as readonly string[],\n\n signTransaction: async (txn: Uint8Array, _: string) => {\n const decoded = decodeTransaction(txn);\n // Delegate to the algokit-utils signer\n const signedBytes = await algokitSigners.signer([decoded], [0]);\n return signedBytes[0];\n },\n\n getAlgodClient: (network: string) => getClient(network).client.algod,\n\n simulateTransactions: async (txns: Uint8Array[], network: string) => {\n const algod = getClient(network).client.algod;\n return await algod.simulateRawTransactions(txns);\n },\n\n sendTransactions: async (signedTxns: Uint8Array[], network: string) => {\n const algod = getClient(network).client.algod;\n const response = await algod.sendRawTransaction(signedTxns);\n return response.txId;\n },\n\n waitForConfirmation: async (txId: string, network: string, waitRounds: number = 5) => {\n const algod = getClient(network).client.algod;\n return await waitForConfirmation(txId, waitRounds, algod);\n },\n };\n}\n","/**\n * @module @x402/avm - x402 Payment Protocol AVM (Algorand) Implementation\n *\n * This module provides the Algorand-specific implementation of the x402 payment protocol.\n *\n * @example Client signer:\n * ```typescript\n * import { toClientAvmSigner } from \"@x402/avm\";\n *\n * const signer = toClientAvmSigner(process.env.AVM_PRIVATE_KEY!);\n * ```\n *\n * @example Facilitator signer:\n * ```typescript\n * import { toFacilitatorAvmSigner } from \"@x402/avm\";\n *\n * const signer = toFacilitatorAvmSigner(process.env.AVM_PRIVATE_KEY!);\n * ```\n */\n\n// Exact scheme client\nexport { ExactAvmScheme } from \"./exact\";\n\n// Signer helpers and interfaces\nexport {\n isAvmSignerWallet,\n toClientAvmSigner,\n toFacilitatorAvmSigner,\n getAlgokitSigner,\n ALGOKIT_SIGNER,\n} from \"./signer\";\nexport type {\n ClientAvmSigner,\n ClientAvmConfig,\n FacilitatorAvmSigner,\n FacilitatorAvmSignerConfig,\n} from \"./signer\";\n\n// Re-export algokit-utils signer types for consumers who want native interop\nexport type {\n AddressWithTransactionSigner,\n AddressWithSigners,\n TransactionSigner,\n} from \"@algorandfoundation/algokit-utils/transact\";\n\n// Types\nexport type { ExactAvmPayloadV2 } from \"./types\";\nexport { isExactAvmPayload } from \"./types\";\n\n// Constants\nexport {\n // CAIP-2 Network Identifiers\n ALGORAND_MAINNET_CAIP2,\n ALGORAND_TESTNET_CAIP2,\n CAIP2_NETWORKS,\n // Genesis Hashes\n ALGORAND_MAINNET_GENESIS_HASH,\n ALGORAND_TESTNET_GENESIS_HASH,\n // USDC Configuration\n USDC_MAINNET_ASA_ID,\n USDC_TESTNET_ASA_ID,\n USDC_DECIMALS,\n USDC_CONFIG,\n // Transaction Limits\n MAX_REASONABLE_FEE_PER_TXN,\n maxReasonableGroupFee,\n} from \"./constants\";\n\n// Re-export algokit-utils constants that consumers may need\nexport { ALGORAND_ADDRESS_LENGTH } from \"@algorandfoundation/algokit-utils/common\";\nexport { ALGORAND_MIN_TX_FEE } from \"@algorandfoundation/algokit-utils/amount\";\n\n// Utilities\nexport {\n encodeTransaction,\n decodeTransaction,\n decodeSignedTransaction,\n decodeUnsignedTransaction,\n isValidAlgorandAddress,\n getSenderFromTransaction,\n convertToTokenAmount,\n convertFromTokenAmount,\n getNetworkFromCaip2,\n isAlgorandNetwork,\n isTestnetNetwork,\n getGenesisHashFromTransaction,\n validateGroupId,\n getTransactionId,\n hasSignature,\n} from \"./utils\";\n"],"mappings":"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","names":["decodeTransaction","decodeTransaction"]}
{
"name": "@x402/avm",
"version": "2.10.0",
"version": "2.11.0",
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
"types": "./dist/cjs/index.d.ts",
"scripts": {
"start": "tsx --env-file=.env index.ts",
"build": "tsup",
"test": "vitest run",
"test:integration": "vitest run --config vitest.integration.config.ts",
"test:watch": "vitest",
"watch": "tsc --watch",
"format": "prettier -c .prettierrc --write \"**/*.{ts,js,cjs,json,md}\"",
"format:check": "prettier -c .prettierrc --check \"**/*.{ts,js,cjs,json,md}\"",
"lint": "eslint . --ext .ts --fix",
"lint:check": "eslint . --ext .ts"
},
"keywords": [

@@ -49,3 +37,3 @@ "x402",

"@algorandfoundation/algokit-utils": "10.0.0-alpha.46",
"@x402/core": "workspace:~"
"@x402/core": "~2.11.0"
},

@@ -96,3 +84,15 @@ "exports": {

"dist"
]
}
],
"scripts": {
"start": "tsx --env-file=.env index.ts",
"build": "tsup",
"test": "vitest run",
"test:integration": "vitest run --config vitest.integration.config.ts",
"test:watch": "vitest",
"watch": "tsc --watch",
"format": "prettier -c .prettierrc --write \"**/*.{ts,js,cjs,json,md}\"",
"format:check": "prettier -c .prettierrc --check \"**/*.{ts,js,cjs,json,md}\"",
"lint": "eslint . --ext .ts --fix",
"lint:check": "eslint . --ext .ts"
}
}

@@ -1,2 +0,2 @@

# @x402/avm
# @x402/avm [![npm version](https://img.shields.io/npm/v/%40x402%2Favm.svg)](https://www.npmjs.com/package/@x402/avm)

@@ -3,0 +3,0 @@ AVM (Algorand Virtual Machine) implementation of the x402 payment protocol using the **Exact** payment scheme with ASA (Algorand Standard Asset) transfers.

import {
USDC_CONFIG,
encodeTransaction,
isTestnetNetwork
} from "./chunk-SLF4VHVI.mjs";
// src/exact/client/scheme.ts
import { AlgorandClient } from "@algorandfoundation/algokit-utils/algorand-client";
import {
Transaction,
encodeTransactionRaw,
groupTransactions,
makeEmptyTransactionSigner
} from "@algorandfoundation/algokit-utils/transact";
import { microAlgo } from "@algorandfoundation/algokit-utils/amount";
var ExactAvmScheme = class {
/**
* Creates a new ExactAvmScheme instance.
*
* @param signer - The AVM signer for client operations
* @param config - Optional configuration for Algod client
*/
constructor(signer, config) {
this.signer = signer;
this.config = config;
this.scheme = "exact";
}
/**
* Creates a payment payload for the Exact scheme.
*
* Constructs an atomic transaction group with:
* - Optional fee payer transaction (if feePayer specified in requirements.extra)
* - ASA transfer transaction to payTo address
*
* Uses TransactionComposer for automatic suggested params, group ID assignment,
* and fee pooling. For sponsored (gasless) transactions, exact fees are calculated
* from actual encoded transaction sizes using the protocol fee formula:
* fee = max(fee_per_byte × txn_size_in_bytes, min_fee)
*
* @param x402Version - The x402 protocol version
* @param paymentRequirements - The payment requirements
* @returns Promise resolving to a payment payload result
*/
async createPaymentPayload(x402Version, paymentRequirements) {
const { amount, asset, payTo, network, extra } = paymentRequirements;
const algorandClient = this.getAlgorandClient(network);
const assetId = this.getAssetId(asset, network);
const feePayer = extra?.feePayer;
let paymentIndex = 0;
const emptySigner = makeEmptyTransactionSigner();
const composer = algorandClient.newGroup();
if (feePayer) {
composer.addPayment({
sender: feePayer,
receiver: feePayer,
amount: microAlgo(0),
note: `x402-fee-payer-${Date.now()}`,
signer: emptySigner
});
paymentIndex = 1;
}
composer.addAssetTransfer({
sender: this.signer.address,
receiver: payTo,
assetId: BigInt(assetId),
amount: BigInt(amount),
staticFee: feePayer ? microAlgo(0) : void 0,
// 0 fee when fee payer covers
note: `x402-payment-v${x402Version}-${Date.now()}`,
signer: emptySigner
});
const built = await composer.build();
let transactions = built.transactions.map((tws) => tws.txn);
if (feePayer) {
const sp = await algorandClient.getSuggestedParams();
const feePerByte = Number(sp.fee);
const minFee = Number(sp.minFee);
let totalGroupFee = BigInt(0);
for (const txn of transactions) {
const txnSize = encodeTransactionRaw(txn).length;
const txnFee = feePerByte > 0 ? Math.max(feePerByte * txnSize, minFee) : minFee;
totalGroupFee += BigInt(txnFee);
}
const ungrouped = transactions.map((txn, i) => {
const fee = i === 0 ? totalGroupFee : BigInt(0);
return new Transaction({ ...txn, fee, group: void 0 });
});
transactions = groupTransactions(ungrouped);
}
const encodedTxns = transactions.map((txn) => encodeTransactionRaw(txn));
const clientIndexes = transactions.map((txn, i) => txn.sender.toString() === this.signer.address ? i : -1).filter((i) => i !== -1);
const signedTxns = await this.signer.signTransactions(encodedTxns, clientIndexes);
const paymentGroup = encodedTxns.map((txnBytes, i) => {
const signedTxn = signedTxns[i];
if (signedTxn) {
return encodeTransaction(signedTxn);
}
return encodeTransaction(txnBytes);
});
const payload = {
paymentGroup,
paymentIndex
};
return {
x402Version,
payload
};
}
/**
* Creates or retrieves an AlgorandClient for the given network.
*
* @param network - Network identifier (CAIP-2 format)
* @returns AlgorandClient instance
*/
getAlgorandClient(network) {
if (this.config?.algorandClient) {
return this.config.algorandClient;
}
if (this.config?.algodUrl) {
return AlgorandClient.fromConfig({
algodConfig: {
server: this.config.algodUrl,
token: this.config.algodToken ?? ""
}
});
}
return isTestnetNetwork(network) ? AlgorandClient.testNet() : AlgorandClient.mainNet();
}
/**
* Gets the asset ID from the requirements or defaults to USDC
*
* @param asset - Asset identifier from requirements
* @param network - Network identifier
* @returns Asset ID as string
*/
getAssetId(asset, network) {
if (/^\d+$/.test(asset)) {
return asset;
}
const usdcConfig = USDC_CONFIG[network];
if (usdcConfig) {
return usdcConfig.asaId;
}
return asset;
}
};
export {
ExactAvmScheme
};
//# sourceMappingURL=chunk-IZZHPFP7.mjs.map
{"version":3,"sources":["../../src/exact/client/scheme.ts"],"sourcesContent":["/**\n * AVM Client Scheme for Exact Payment Protocol\n *\n * Creates atomic transaction groups for Algorand ASA transfers.\n * Uses AlgorandClient and TransactionComposer from algokit-utils v10\n * for transaction construction, fee pooling, and group management.\n */\n\nimport { AlgorandClient } from \"@algorandfoundation/algokit-utils/algorand-client\";\nimport {\n Transaction,\n encodeTransactionRaw,\n groupTransactions,\n makeEmptyTransactionSigner,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport { microAlgo } from \"@algorandfoundation/algokit-utils/amount\";\nimport type {\n PaymentRequirements,\n SchemeNetworkClient,\n PaymentPayloadResult,\n} from \"@x402/core/types\";\nimport type { ClientAvmSigner, ClientAvmConfig } from \"../../signer\";\nimport type { ExactAvmPayloadV2 } from \"../../types\";\nimport { encodeTransaction } from \"../../utils\";\nimport { USDC_CONFIG } from \"../../constants\";\nimport { isTestnetNetwork } from \"../../utils\";\n\n/**\n * AVM client implementation for the Exact payment scheme.\n *\n * Creates atomic transaction groups with ASA transfers for x402 payments.\n * Supports optional fee payer transactions for gasless payments.\n */\nexport class ExactAvmScheme implements SchemeNetworkClient {\n readonly scheme = \"exact\";\n\n /**\n * Creates a new ExactAvmScheme instance.\n *\n * @param signer - The AVM signer for client operations\n * @param config - Optional configuration for Algod client\n */\n constructor(\n private readonly signer: ClientAvmSigner,\n private readonly config?: ClientAvmConfig,\n ) {}\n\n /**\n * Creates a payment payload for the Exact scheme.\n *\n * Constructs an atomic transaction group with:\n * - Optional fee payer transaction (if feePayer specified in requirements.extra)\n * - ASA transfer transaction to payTo address\n *\n * Uses TransactionComposer for automatic suggested params, group ID assignment,\n * and fee pooling. For sponsored (gasless) transactions, exact fees are calculated\n * from actual encoded transaction sizes using the protocol fee formula:\n * fee = max(fee_per_byte × txn_size_in_bytes, min_fee)\n *\n * @param x402Version - The x402 protocol version\n * @param paymentRequirements - The payment requirements\n * @returns Promise resolving to a payment payload result\n */\n async createPaymentPayload(\n x402Version: number,\n paymentRequirements: PaymentRequirements,\n ): Promise<PaymentPayloadResult> {\n const { amount, asset, payTo, network, extra } = paymentRequirements;\n\n const algorandClient = this.getAlgorandClient(network);\n\n // Get asset ID (from requirements or default USDC)\n const assetId = this.getAssetId(asset, network);\n\n // Get fee payer address from extra if provided\n const feePayer = extra?.feePayer as string | undefined;\n let paymentIndex = 0;\n\n // Use an empty signer for building — we sign manually after\n // (fee payer txns stay unsigned for the facilitator to sign)\n const emptySigner = makeEmptyTransactionSigner();\n\n // Build the transaction group using TransactionComposer\n const composer = algorandClient.newGroup();\n\n if (feePayer) {\n // First pass: add fee payer with a placeholder fee.\n // The actual fee will be recalculated after build() using exact transaction sizes.\n composer.addPayment({\n sender: feePayer,\n receiver: feePayer,\n amount: microAlgo(0),\n note: `x402-fee-payer-${Date.now()}`,\n signer: emptySigner,\n });\n paymentIndex = 1;\n }\n\n composer.addAssetTransfer({\n sender: this.signer.address,\n receiver: payTo,\n assetId: BigInt(assetId),\n amount: BigInt(amount),\n staticFee: feePayer ? microAlgo(0) : undefined, // 0 fee when fee payer covers\n note: `x402-payment-v${x402Version}-${Date.now()}`,\n signer: emptySigner,\n });\n\n // Build transactions (assigns group ID, suggested params, fees)\n const built = await composer.build();\n let transactions = built.transactions.map(tws => tws.txn);\n\n // For sponsored transactions: recalculate the fee payer's fee using\n // exact encoded sizes of all transactions in the group.\n // Algorand fee formula: fee = max(fee_per_byte × txn_size, min_fee)\n //\n // After changing fees, the group ID must be recomputed because it is\n // derived from each transaction's encoded bytes (which include the fee).\n if (feePayer) {\n const sp = await algorandClient.getSuggestedParams();\n const feePerByte = Number(sp.fee);\n const minFee = Number(sp.minFee);\n\n // Calculate exact fee for each transaction based on its actual encoded size\n let totalGroupFee = BigInt(0);\n for (const txn of transactions) {\n const txnSize = encodeTransactionRaw(txn).length;\n const txnFee = feePerByte > 0 ? Math.max(feePerByte * txnSize, minFee) : minFee;\n totalGroupFee += BigInt(txnFee);\n }\n\n // Strip group ID, set correct fees, then re-group.\n // groupTransactions() requires group to be absent, computes the new group hash,\n // and returns new Transaction objects with the correct group ID.\n const ungrouped = transactions.map((txn, i) => {\n const fee = i === 0 ? totalGroupFee : BigInt(0);\n return new Transaction({ ...txn, fee, group: undefined });\n });\n transactions = groupTransactions(ungrouped);\n }\n\n // Encode all transactions to raw bytes\n const encodedTxns = transactions.map(txn => encodeTransactionRaw(txn));\n\n // Determine which transactions the client should sign\n const clientIndexes = transactions\n .map((txn, i) => (txn.sender.toString() === this.signer.address ? i : -1))\n .filter(i => i !== -1);\n\n // Sign client's transactions\n const signedTxns = await this.signer.signTransactions(encodedTxns, clientIndexes);\n\n // Build payment group with signed/unsigned transactions\n const paymentGroup: string[] = encodedTxns.map((txnBytes, i) => {\n const signedTxn = signedTxns[i];\n if (signedTxn) {\n return encodeTransaction(signedTxn);\n }\n // Return unsigned transaction for facilitator to sign\n return encodeTransaction(txnBytes);\n });\n\n const payload: ExactAvmPayloadV2 = {\n paymentGroup,\n paymentIndex,\n };\n\n return {\n x402Version,\n payload: payload as unknown as Record<string, unknown>,\n };\n }\n\n /**\n * Creates or retrieves an AlgorandClient for the given network.\n *\n * @param network - Network identifier (CAIP-2 format)\n * @returns AlgorandClient instance\n */\n private getAlgorandClient(network: string): AlgorandClient {\n if (this.config?.algorandClient) {\n return this.config.algorandClient;\n }\n if (this.config?.algodUrl) {\n return AlgorandClient.fromConfig({\n algodConfig: {\n server: this.config.algodUrl,\n token: this.config.algodToken ?? \"\",\n },\n });\n }\n // Auto-detect network\n return isTestnetNetwork(network) ? AlgorandClient.testNet() : AlgorandClient.mainNet();\n }\n\n /**\n * Gets the asset ID from the requirements or defaults to USDC\n *\n * @param asset - Asset identifier from requirements\n * @param network - Network identifier\n * @returns Asset ID as string\n */\n private getAssetId(asset: string, network: string): string {\n // If asset is already a numeric string, use it directly\n if (/^\\d+$/.test(asset)) {\n return asset;\n }\n\n // Try to get from USDC config\n const usdcConfig = USDC_CONFIG[network];\n if (usdcConfig) {\n return usdcConfig.asaId;\n }\n\n // Default to the asset as-is (might be an ASA ID)\n return asset;\n }\n}\n"],"mappings":"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","names":[]}
var __defProp = Object.defineProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// src/constants.ts
var ALGORAND_MAINNET_CAIP2 = "algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=";
var ALGORAND_TESTNET_CAIP2 = "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=";
var CAIP2_NETWORKS = [ALGORAND_MAINNET_CAIP2, ALGORAND_TESTNET_CAIP2];
var ALGORAND_MAINNET_GENESIS_HASH = "wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=";
var ALGORAND_TESTNET_GENESIS_HASH = "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=";
var USDC_MAINNET_ASA_ID = "31566704";
var USDC_TESTNET_ASA_ID = "10458941";
var USDC_DECIMALS = 6;
var USDC_CONFIG = {
[ALGORAND_MAINNET_CAIP2]: {
asaId: USDC_MAINNET_ASA_ID,
name: "USDC",
decimals: USDC_DECIMALS
},
[ALGORAND_TESTNET_CAIP2]: {
asaId: USDC_TESTNET_ASA_ID,
name: "USDC",
decimals: USDC_DECIMALS
}
};
var MAX_REASONABLE_FEE_PER_TXN = 5e3;
function maxReasonableGroupFee(groupSize) {
return MAX_REASONABLE_FEE_PER_TXN * groupSize;
}
// src/utils.ts
import {
decodeTransaction as decodeUnsignedTxn,
decodeSignedTransaction as decodeSignedTxn
} from "@algorandfoundation/algokit-utils/transact";
import { isValidAddress } from "@algorandfoundation/algokit-utils/common";
import { Address, encodeAddress, decodeAddress } from "@algorandfoundation/algokit-utils/common";
function encodeTransaction(txn) {
return Buffer.from(txn).toString("base64");
}
function decodeTransaction(encoded) {
return new Uint8Array(Buffer.from(encoded, "base64"));
}
function decodeSignedTransaction(encoded) {
const bytes = decodeTransaction(encoded);
return decodeSignedTxn(bytes);
}
function decodeUnsignedTransaction(encoded) {
const bytes = decodeTransaction(encoded);
return decodeUnsignedTxn(bytes);
}
function isValidAlgorandAddress(address) {
return isValidAddress(address);
}
function getSenderFromTransaction(txnBytes, isSigned = true) {
if (isSigned) {
const signedTxn = decodeSignedTxn(txnBytes);
return signedTxn.txn.sender.toString();
}
const txn = decodeUnsignedTxn(txnBytes);
return txn.sender.toString();
}
function convertToTokenAmount(decimalAmount, decimals) {
const amount = parseFloat(decimalAmount);
if (isNaN(amount)) {
throw new Error(`Invalid amount: ${decimalAmount}`);
}
const [intPart, decPart = ""] = String(amount).split(".");
const paddedDec = decPart.padEnd(decimals, "0").slice(0, decimals);
const tokenAmount = (intPart + paddedDec).replace(/^0+/, "") || "0";
return tokenAmount;
}
function convertFromTokenAmount(atomicAmount, decimals) {
const amount = BigInt(atomicAmount);
const divisor = BigInt(10 ** decimals);
const intPart = amount / divisor;
const decPart = amount % divisor;
if (decPart === BigInt(0)) {
return intPart.toString();
}
const decStr = decPart.toString().padStart(decimals, "0");
const trimmedDec = decStr.replace(/0+$/, "");
return `${intPart}.${trimmedDec}`;
}
function getNetworkFromCaip2(caip2) {
if (!caip2.startsWith("algorand:")) {
return null;
}
const genesisHash = caip2.slice("algorand:".length);
if (genesisHash === ALGORAND_MAINNET_GENESIS_HASH) {
return "mainnet";
}
if (genesisHash === ALGORAND_TESTNET_GENESIS_HASH) {
return "testnet";
}
return null;
}
function isAlgorandNetwork(network) {
return network.startsWith("algorand:");
}
function isTestnetNetwork(network) {
return network === ALGORAND_TESTNET_CAIP2;
}
function getGenesisHashFromTransaction(txn) {
if (!txn.genesisHash) {
throw new Error("Transaction does not have a genesis hash");
}
return Buffer.from(txn.genesisHash).toString("base64");
}
function validateGroupId(txns) {
if (txns.length <= 1) {
return true;
}
let expectedGroupId = null;
for (const txnBytes of txns) {
const txn = decodeUnsignedTxn(txnBytes);
const groupId = txn.group ? Buffer.from(txn.group).toString("base64") : null;
if (expectedGroupId === null) {
expectedGroupId = groupId;
} else if (groupId !== expectedGroupId) {
return false;
}
}
return true;
}
function getTransactionId(signedTxnBytes) {
const signedTxn = decodeSignedTxn(signedTxnBytes);
return signedTxn.txn.txId();
}
function hasSignature(signedTxnBytes) {
const signedTxn = decodeSignedTxn(signedTxnBytes);
return signedTxn.sig !== void 0 || signedTxn.lsig !== void 0 || signedTxn.msig !== void 0;
}
export {
__export,
ALGORAND_MAINNET_CAIP2,
ALGORAND_TESTNET_CAIP2,
CAIP2_NETWORKS,
ALGORAND_MAINNET_GENESIS_HASH,
ALGORAND_TESTNET_GENESIS_HASH,
USDC_MAINNET_ASA_ID,
USDC_TESTNET_ASA_ID,
USDC_DECIMALS,
USDC_CONFIG,
MAX_REASONABLE_FEE_PER_TXN,
maxReasonableGroupFee,
encodeTransaction,
decodeTransaction,
decodeSignedTransaction,
decodeUnsignedTransaction,
isValidAlgorandAddress,
getSenderFromTransaction,
convertToTokenAmount,
convertFromTokenAmount,
getNetworkFromCaip2,
isAlgorandNetwork,
isTestnetNetwork,
getGenesisHashFromTransaction,
validateGroupId,
getTransactionId,
hasSignature
};
//# sourceMappingURL=chunk-SLF4VHVI.mjs.map
{"version":3,"sources":["../../src/constants.ts","../../src/utils.ts"],"sourcesContent":["/**\n * Algorand Network Constants for x402 AVM Implementation\n *\n * CAIP-2 Network Identifiers use the format: algorand:<genesis-hash-base64>\n * Genesis hashes uniquely identify Algorand networks.\n */\n\n// ============================================================================\n// CAIP-2 Network Identifiers (V2)\n// ============================================================================\n\n/**\n * CAIP-2 network identifier for Algorand Mainnet\n * Format: algorand:<genesis-hash-base64>\n */\nexport const ALGORAND_MAINNET_CAIP2 = \"algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=\";\n\n/**\n * CAIP-2 network identifier for Algorand Testnet\n * Format: algorand:<genesis-hash-base64>\n */\nexport const ALGORAND_TESTNET_CAIP2 = \"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=\";\n\n/**\n * All supported CAIP-2 network identifiers\n */\nexport const CAIP2_NETWORKS = [ALGORAND_MAINNET_CAIP2, ALGORAND_TESTNET_CAIP2] as const;\n\n// ============================================================================\n// Genesis Hashes\n// ============================================================================\n\n/**\n * Algorand Mainnet genesis hash (base64 encoded)\n */\nexport const ALGORAND_MAINNET_GENESIS_HASH = \"wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=\";\n\n/**\n * Algorand Testnet genesis hash (base64 encoded)\n */\nexport const ALGORAND_TESTNET_GENESIS_HASH = \"SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=\";\n\n// ============================================================================\n// USDC ASA (Algorand Standard Asset) Configuration\n// ============================================================================\n\n/**\n * USDC ASA ID on Algorand Mainnet\n *\n * @see https://algoexplorer.io/asset/31566704\n */\nexport const USDC_MAINNET_ASA_ID = \"31566704\";\n\n/**\n * USDC ASA ID on Algorand Testnet\n *\n * @see https://testnet.algoexplorer.io/asset/10458941\n */\nexport const USDC_TESTNET_ASA_ID = \"10458941\";\n\n/**\n * USDC decimals (same across all networks)\n */\nexport const USDC_DECIMALS = 6;\n\n/**\n * USDC configuration per network\n */\nexport const USDC_CONFIG: Record<string, { asaId: string; name: string; decimals: number }> = {\n [ALGORAND_MAINNET_CAIP2]: {\n asaId: USDC_MAINNET_ASA_ID,\n name: \"USDC\",\n decimals: USDC_DECIMALS,\n },\n [ALGORAND_TESTNET_CAIP2]: {\n asaId: USDC_TESTNET_ASA_ID,\n name: \"USDC\",\n decimals: USDC_DECIMALS,\n },\n};\n\n// ============================================================================\n// Transaction Limits\n// ============================================================================\n\n/**\n * Maximum reasonable fee per transaction in microAlgos (5000 µAlgo).\n *\n * Algorand transaction fees are calculated as:\n * fee = max(current_fee_per_byte * transaction_size_in_bytes, min_fee)\n *\n * Under normal (non-congested) conditions, current_fee_per_byte is 0,\n * so fee = min_fee = 1000 µAlgo (0.001 ALGO).\n *\n * During network congestion, fees can rise. This constant is set to 5x\n * the minimum fee (5000 µAlgo) as a reasonable upper bound per transaction.\n *\n * For fee payer transactions that cover an entire group via fee pooling,\n * use `maxReasonableGroupFee(groupSize)` which multiplies this per-txn\n * cap by the number of transactions in the group.\n */\nexport const MAX_REASONABLE_FEE_PER_TXN = 5000;\n\n/**\n * Calculates the maximum reasonable fee for a fee payer transaction\n * that covers an entire atomic group via fee pooling.\n *\n * @param groupSize - Number of transactions in the atomic group\n * @returns Maximum acceptable fee in microAlgos\n */\nexport function maxReasonableGroupFee(groupSize: number): number {\n return MAX_REASONABLE_FEE_PER_TXN * groupSize;\n}\n\n// Address validation: use isValidAddress() from @algorandfoundation/algokit-utils/common\n// Address length: use ALGORAND_ADDRESS_LENGTH from @algorandfoundation/algokit-utils/common\n","/**\n * AVM (Algorand) Utilities for x402 Payment Protocol\n *\n * Provides utility functions for Algod client creation, transaction encoding/decoding,\n * address validation, and network identification.\n */\n\nimport {\n decodeTransaction as decodeUnsignedTxn,\n decodeSignedTransaction as decodeSignedTxn,\n} from \"@algorandfoundation/algokit-utils/transact\";\nimport { isValidAddress } from \"@algorandfoundation/algokit-utils/common\";\nimport {\n ALGORAND_MAINNET_GENESIS_HASH,\n ALGORAND_TESTNET_GENESIS_HASH,\n ALGORAND_TESTNET_CAIP2,\n} from \"./constants\";\n\n/**\n * Encodes transaction bytes to base64 string\n *\n * @param txn - Transaction bytes (Uint8Array)\n * @returns Base64 encoded string\n */\nexport function encodeTransaction(txn: Uint8Array): string {\n return Buffer.from(txn).toString(\"base64\");\n}\n\n/**\n * Decodes a base64 encoded transaction to bytes\n *\n * @param encoded - Base64 encoded transaction string\n * @returns Transaction bytes (Uint8Array)\n */\nexport function decodeTransaction(encoded: string): Uint8Array {\n return new Uint8Array(Buffer.from(encoded, \"base64\"));\n}\n\n/**\n * Decodes a signed transaction from base64 msgpack\n *\n * @param encoded - Base64 encoded signed transaction\n * @returns Decoded signed transaction object\n */\nexport function decodeSignedTransaction(encoded: string) {\n const bytes = decodeTransaction(encoded);\n return decodeSignedTxn(bytes);\n}\n\n/**\n * Decodes an unsigned transaction from base64 msgpack\n *\n * @param encoded - Base64 encoded unsigned transaction\n * @returns Decoded transaction object\n */\nexport function decodeUnsignedTransaction(encoded: string) {\n const bytes = decodeTransaction(encoded);\n return decodeUnsignedTxn(bytes);\n}\n\n/**\n * Validates an Algorand address\n *\n * Uses isValidAddress from algokit-utils which performs full checksum validation.\n *\n * @param address - The address to validate\n * @returns True if the address is valid\n */\nexport function isValidAlgorandAddress(address: string): boolean {\n return isValidAddress(address);\n}\n\n/**\n * Gets the sender address from a transaction (signed or unsigned)\n *\n * @param txnBytes - Transaction bytes\n * @param isSigned - Whether the transaction is signed (default: true)\n * @returns Sender address string\n */\nexport function getSenderFromTransaction(txnBytes: Uint8Array, isSigned: boolean = true): string {\n if (isSigned) {\n const signedTxn = decodeSignedTxn(txnBytes);\n return signedTxn.txn.sender.toString();\n }\n const txn = decodeUnsignedTxn(txnBytes);\n return txn.sender.toString();\n}\n\n/**\n * Converts a decimal amount to atomic units (token's smallest unit)\n *\n * @param decimalAmount - The decimal amount as a string (e.g., \"1.50\")\n * @param decimals - Number of decimal places (e.g., 6 for USDC)\n * @returns Amount in atomic units as a string\n *\n * @example\n * ```typescript\n * convertToTokenAmount(\"1.50\", 6) // Returns \"1500000\"\n * convertToTokenAmount(\"0.10\", 6) // Returns \"100000\"\n * ```\n */\nexport function convertToTokenAmount(decimalAmount: string, decimals: number): string {\n const amount = parseFloat(decimalAmount);\n if (isNaN(amount)) {\n throw new Error(`Invalid amount: ${decimalAmount}`);\n }\n\n // Handle decimal conversion properly\n const [intPart, decPart = \"\"] = String(amount).split(\".\");\n const paddedDec = decPart.padEnd(decimals, \"0\").slice(0, decimals);\n const tokenAmount = (intPart + paddedDec).replace(/^0+/, \"\") || \"0\";\n\n return tokenAmount;\n}\n\n/**\n * Converts atomic units to decimal amount\n *\n * @param atomicAmount - Amount in atomic units (string or bigint)\n * @param decimals - Number of decimal places\n * @returns Decimal amount as a string\n */\nexport function convertFromTokenAmount(atomicAmount: string | bigint, decimals: number): string {\n const amount = BigInt(atomicAmount);\n const divisor = BigInt(10 ** decimals);\n const intPart = amount / divisor;\n const decPart = amount % divisor;\n\n if (decPart === BigInt(0)) {\n return intPart.toString();\n }\n\n const decStr = decPart.toString().padStart(decimals, \"0\");\n // Remove trailing zeros\n const trimmedDec = decStr.replace(/0+$/, \"\");\n return `${intPart}.${trimmedDec}`;\n}\n\n/**\n * Gets the network type from a CAIP-2 identifier\n *\n * @param caip2 - CAIP-2 network identifier\n * @returns Network type (\"mainnet\" | \"testnet\") or null if unknown\n */\nexport function getNetworkFromCaip2(caip2: string): \"mainnet\" | \"testnet\" | null {\n if (!caip2.startsWith(\"algorand:\")) {\n return null;\n }\n\n const genesisHash = caip2.slice(\"algorand:\".length);\n\n if (genesisHash === ALGORAND_MAINNET_GENESIS_HASH) {\n return \"mainnet\";\n }\n if (genesisHash === ALGORAND_TESTNET_GENESIS_HASH) {\n return \"testnet\";\n }\n\n return null;\n}\n\n/**\n * Checks if a network identifier is an Algorand network\n *\n * @param network - Network identifier (CAIP-2 format)\n * @returns True if the network is an Algorand network\n */\nexport function isAlgorandNetwork(network: string): boolean {\n return network.startsWith(\"algorand:\");\n}\n\n/**\n * Checks if a network identifier is a testnet\n *\n * @param network - Network identifier (CAIP-2 format)\n * @returns True if the network is a testnet\n */\nexport function isTestnetNetwork(network: string): boolean {\n return network === ALGORAND_TESTNET_CAIP2;\n}\n\n/**\n * Gets the genesis hash from a transaction\n *\n * @param txn - The transaction object\n * @param txn.genesisHash - The genesis hash bytes\n * @returns Base64 encoded genesis hash\n */\nexport function getGenesisHashFromTransaction(txn: { genesisHash?: Uint8Array }): string {\n if (!txn.genesisHash) {\n throw new Error(\"Transaction does not have a genesis hash\");\n }\n return Buffer.from(txn.genesisHash).toString(\"base64\");\n}\n\n/**\n * Validates that all transactions in a group have the same group ID\n *\n * @param txns - Array of transaction bytes\n * @returns True if all transactions have matching group IDs\n */\nexport function validateGroupId(txns: Uint8Array[]): boolean {\n if (txns.length <= 1) {\n return true;\n }\n\n let expectedGroupId: string | null = null;\n\n for (const txnBytes of txns) {\n const txn = decodeUnsignedTxn(txnBytes);\n const groupId = txn.group ? Buffer.from(txn.group).toString(\"base64\") : null;\n\n if (expectedGroupId === null) {\n expectedGroupId = groupId;\n } else if (groupId !== expectedGroupId) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Extracts the transaction ID from signed transaction bytes\n *\n * @param signedTxnBytes - Signed transaction bytes\n * @returns Transaction ID string\n */\nexport function getTransactionId(signedTxnBytes: Uint8Array): string {\n const signedTxn = decodeSignedTxn(signedTxnBytes);\n return signedTxn.txn.txId();\n}\n\n/**\n * Checks if a signed transaction has a valid signature\n *\n * @param signedTxnBytes - Signed transaction bytes\n * @returns True if the transaction has a signature\n */\nexport function hasSignature(signedTxnBytes: Uint8Array): boolean {\n const signedTxn = decodeSignedTxn(signedTxnBytes);\n return (\n signedTxn.sig !== undefined || signedTxn.lsig !== undefined || signedTxn.msig !== undefined\n );\n}\n\n// Re-export algokit-utils types that consumers may need\nexport { Address, encodeAddress, decodeAddress } from \"@algorandfoundation/algokit-utils/common\";\n"],"mappings":"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","names":[]}