uvd-x402-sdk 2.11.1 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/adapters/index.d.mts +1 -1
  2. package/dist/adapters/index.d.ts +1 -1
  3. package/dist/adapters/index.js.map +1 -1
  4. package/dist/adapters/index.mjs.map +1 -1
  5. package/dist/backend/index.d.mts +1 -1
  6. package/dist/backend/index.d.ts +1 -1
  7. package/dist/backend/index.js.map +1 -1
  8. package/dist/backend/index.mjs.map +1 -1
  9. package/dist/{index-C60c_e5z.d.mts → index-C6Vxnneo.d.mts} +1 -1
  10. package/dist/{index-VIOUicmO.d.ts → index-DmJGKD9r.d.ts} +1 -1
  11. package/dist/{index-D-dO_FoP.d.mts → index-fIhvHqCQ.d.mts} +18 -22
  12. package/dist/{index-D-dO_FoP.d.ts → index-fIhvHqCQ.d.ts} +18 -22
  13. package/dist/index.d.mts +56 -2
  14. package/dist/index.d.ts +56 -2
  15. package/dist/index.js +59 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/index.mjs +58 -1
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/providers/algorand/index.d.mts +11 -5
  20. package/dist/providers/algorand/index.d.ts +11 -5
  21. package/dist/providers/algorand/index.js +142 -27
  22. package/dist/providers/algorand/index.js.map +1 -1
  23. package/dist/providers/algorand/index.mjs +142 -27
  24. package/dist/providers/algorand/index.mjs.map +1 -1
  25. package/dist/providers/evm/index.d.mts +1 -1
  26. package/dist/providers/evm/index.d.ts +1 -1
  27. package/dist/providers/evm/index.js.map +1 -1
  28. package/dist/providers/evm/index.mjs.map +1 -1
  29. package/dist/providers/near/index.d.mts +1 -1
  30. package/dist/providers/near/index.d.ts +1 -1
  31. package/dist/providers/near/index.js.map +1 -1
  32. package/dist/providers/near/index.mjs.map +1 -1
  33. package/dist/providers/solana/index.d.mts +1 -1
  34. package/dist/providers/solana/index.d.ts +1 -1
  35. package/dist/providers/solana/index.js.map +1 -1
  36. package/dist/providers/solana/index.mjs.map +1 -1
  37. package/dist/providers/stellar/index.d.mts +1 -1
  38. package/dist/providers/stellar/index.d.ts +1 -1
  39. package/dist/providers/stellar/index.js.map +1 -1
  40. package/dist/providers/stellar/index.mjs.map +1 -1
  41. package/dist/react/index.d.mts +3 -3
  42. package/dist/react/index.d.ts +3 -3
  43. package/dist/react/index.js.map +1 -1
  44. package/dist/react/index.mjs.map +1 -1
  45. package/dist/utils/index.d.mts +1 -1
  46. package/dist/utils/index.d.ts +1 -1
  47. package/dist/utils/index.js.map +1 -1
  48. package/dist/utils/index.mjs.map +1 -1
  49. package/package.json +1 -1
  50. package/src/facilitator.ts +106 -0
  51. package/src/index.ts +4 -0
  52. package/src/providers/algorand/index.ts +122 -32
  53. package/src/types/index.ts +18 -22
@@ -674,6 +674,50 @@ function chainToCAIP2(chainName) {
674
674
  return chainName;
675
675
  }
676
676
 
677
+ // src/facilitator.ts
678
+ var FACILITATOR_ADDRESSES = {
679
+ /**
680
+ * Solana facilitator address (fee payer)
681
+ * Used for: Paying transaction fees on Solana
682
+ */
683
+ solana: "F742C4VfFLQ9zRQyithoj5229ZgtX2WqKCSFKgH2EThq",
684
+ /**
685
+ * Algorand facilitator address (fee payer)
686
+ * Used for: Signing Transaction 0 (fee tx) in atomic groups
687
+ * Note: This is derived from the facilitator's Algorand mnemonic
688
+ */
689
+ algorand: "SXHRBXS22SKKXHXK44DTQMWN2SXK3SFJWBDAQZGF4DRPW7PNFAUM2GYFAQ",
690
+ /**
691
+ * Algorand testnet facilitator address
692
+ */
693
+ "algorand-testnet": "SXHRBXS22SKKXHXK44DTQMWN2SXK3SFJWBDAQZGF4DRPW7PNFAUM2GYFAQ",
694
+ /**
695
+ * EVM facilitator address
696
+ * Used for: Submitting EIP-3009 transferWithAuthorization transactions
697
+ * Note: Same address across all EVM chains
698
+ */
699
+ evm: "0x7c5F3AdB0C7775968Bc7e7cF61b27fECf2e2b500",
700
+ /**
701
+ * Stellar facilitator address
702
+ * Used for: Signing soroban authorization entries
703
+ */
704
+ stellar: "GDUTDNV53WQPOB2JUZPO6SXH4LVT7CJSLCMLFQ7W4CNAXGIX7XYMCNP2",
705
+ /**
706
+ * NEAR facilitator address
707
+ * Used for: Relaying meta-transactions
708
+ */
709
+ near: "uvd-facilitator.near"
710
+ };
711
+ function getFacilitatorAddress(chainName, networkType) {
712
+ const exactMatch = FACILITATOR_ADDRESSES[chainName];
713
+ if (exactMatch) {
714
+ return exactMatch;
715
+ }
716
+ {
717
+ return FACILITATOR_ADDRESSES.algorand;
718
+ }
719
+ }
720
+
677
721
  // src/providers/algorand/index.ts
678
722
  function uint8ArrayToBase64(bytes) {
679
723
  let binary = "";
@@ -877,11 +921,13 @@ var AlgorandProvider = class {
877
921
  }
878
922
  }
879
923
  /**
880
- * Create Algorand ASA transfer payment
924
+ * Create Algorand atomic group payment (GoPlausible x402-avm spec)
925
+ *
926
+ * Transaction structure (atomic group):
927
+ * - Transaction 0: Fee payment (UNSIGNED) - facilitator -> facilitator, covers all fees
928
+ * - Transaction 1: ASA transfer (SIGNED) - client -> merchant
881
929
  *
882
- * Transaction structure:
883
- * 1. ASA Transfer from user to recipient
884
- * 2. Facilitator pays transaction fees
930
+ * The facilitator signs transaction 0 and submits the complete atomic group.
885
931
  */
886
932
  async signPayment(paymentInfo, chainConfig) {
887
933
  await loadAlgorandDeps();
@@ -894,40 +940,86 @@ var AlgorandProvider = class {
894
940
  const algodClient = await this.getAlgodClient(chainConfig);
895
941
  const recipient = paymentInfo.recipients?.algorand || paymentInfo.recipient;
896
942
  const assetId = parseInt(chainConfig.usdc.address, 10);
943
+ const chainName = chainConfig?.name || "algorand";
944
+ const facilitatorAddress = paymentInfo.facilitator || getFacilitatorAddress(chainName);
945
+ if (!facilitatorAddress) {
946
+ throw new X402Error(
947
+ "Facilitator address not configured for Algorand",
948
+ "PAYMENT_FAILED"
949
+ );
950
+ }
897
951
  const amount = Math.floor(parseFloat(paymentInfo.amount) * 1e6);
898
952
  try {
899
953
  const suggestedParams = await algodClient.getTransactionParams().do();
900
- const txn = algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject({
954
+ const feeTxn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({
955
+ sender: facilitatorAddress,
956
+ receiver: facilitatorAddress,
957
+ // self-transfer
958
+ amount: 0,
959
+ suggestedParams: {
960
+ ...suggestedParams,
961
+ fee: 2e3,
962
+ // Covers both transactions (1000 each)
963
+ flatFee: true
964
+ }
965
+ });
966
+ const paymentTxn = algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject({
901
967
  sender: this.address,
902
968
  receiver: recipient,
903
969
  amount: BigInt(amount),
904
970
  assetIndex: assetId,
905
- suggestedParams,
971
+ suggestedParams: {
972
+ ...suggestedParams,
973
+ fee: 0,
974
+ // Fee paid by transaction 0
975
+ flatFee: true
976
+ },
906
977
  note: new TextEncoder().encode("x402 payment via uvd-x402-sdk")
907
978
  });
908
- let signedTxn;
979
+ const txnGroup = algosdk.assignGroupID([feeTxn, paymentTxn]);
980
+ const unsignedFeeTxnBytes = algosdk.encodeUnsignedTransaction(txnGroup[0]);
981
+ const unsignedFeeTxnBase64 = uint8ArrayToBase64(unsignedFeeTxnBytes);
982
+ let signedPaymentTxnBytes;
909
983
  if (this.walletType === "lute" && this.luteWallet) {
910
- const txnBase64 = uint8ArrayToBase64(txn.toByte());
911
- const signedTxns = await this.luteWallet.signTxns([{ txn: txnBase64 }]);
912
- if (!signedTxns || signedTxns.length === 0 || !signedTxns[0]) {
984
+ const feeTxnBase64 = uint8ArrayToBase64(txnGroup[0].toByte());
985
+ const paymentTxnBase64 = uint8ArrayToBase64(txnGroup[1].toByte());
986
+ const signedTxns = await this.luteWallet.signTxns([
987
+ { txn: feeTxnBase64, signers: [] },
988
+ // Don't sign - facilitator will
989
+ { txn: paymentTxnBase64 }
990
+ // Sign this one
991
+ ]);
992
+ if (!signedTxns || signedTxns.length < 2 || !signedTxns[1]) {
913
993
  throw new X402Error("No signed transaction returned", "SIGNATURE_REJECTED");
914
994
  }
915
- signedTxn = Uint8Array.from(atob(signedTxns[0]), (c) => c.charCodeAt(0));
995
+ const signedResult = signedTxns[1];
996
+ signedPaymentTxnBytes = this.decodeSignedTxn(signedResult);
916
997
  } else if (this.walletType === "pera" && this.peraWallet) {
917
- const signedTxns = await this.peraWallet.signTransaction([[{ txn }]]);
918
- if (!signedTxns || signedTxns.length === 0) {
998
+ const signedTxns = await this.peraWallet.signTransaction([
999
+ [
1000
+ { txn: txnGroup[0], signers: [] },
1001
+ // Don't sign - facilitator will
1002
+ { txn: txnGroup[1] }
1003
+ // Sign this one
1004
+ ]
1005
+ ]);
1006
+ if (!signedTxns || signedTxns.length < 2 || !signedTxns[1]) {
919
1007
  throw new X402Error("No signed transaction returned", "SIGNATURE_REJECTED");
920
1008
  }
921
- signedTxn = signedTxns[0];
1009
+ signedPaymentTxnBytes = signedTxns[1];
922
1010
  } else {
923
1011
  throw new X402Error("No wallet available for signing", "WALLET_NOT_CONNECTED");
924
1012
  }
1013
+ const signedPaymentTxnBase64 = uint8ArrayToBase64(signedPaymentTxnBytes);
925
1014
  const payload = {
926
- from: this.address,
927
- to: recipient,
928
- amount: amount.toString(),
929
- assetId,
930
- signedTxn: uint8ArrayToBase64(signedTxn)
1015
+ paymentIndex: 1,
1016
+ // Index of the payment transaction in the group
1017
+ paymentGroup: [
1018
+ unsignedFeeTxnBase64,
1019
+ // Transaction 0: unsigned fee tx
1020
+ signedPaymentTxnBase64
1021
+ // Transaction 1: signed payment tx
1022
+ ]
931
1023
  };
932
1024
  return JSON.stringify(payload);
933
1025
  } catch (error) {
@@ -946,6 +1038,29 @@ var AlgorandProvider = class {
946
1038
  );
947
1039
  }
948
1040
  }
1041
+ /**
1042
+ * Decode signed transaction from wallet response (handles various formats)
1043
+ */
1044
+ decodeSignedTxn(signedResult) {
1045
+ if (signedResult instanceof Uint8Array) {
1046
+ return signedResult;
1047
+ } else if (typeof signedResult === "string") {
1048
+ try {
1049
+ return Uint8Array.from(atob(signedResult), (c) => c.charCodeAt(0));
1050
+ } catch {
1051
+ const standardBase64 = signedResult.replace(/-/g, "+").replace(/_/g, "/");
1052
+ return Uint8Array.from(atob(standardBase64), (c) => c.charCodeAt(0));
1053
+ }
1054
+ } else if (ArrayBuffer.isView(signedResult)) {
1055
+ return new Uint8Array(
1056
+ signedResult.buffer,
1057
+ signedResult.byteOffset,
1058
+ signedResult.byteLength
1059
+ );
1060
+ } else {
1061
+ throw new X402Error("Unexpected signed transaction format", "PAYMENT_FAILED");
1062
+ }
1063
+ }
949
1064
  /**
950
1065
  * Encode Algorand payment as X-PAYMENT header
951
1066
  *
@@ -956,14 +1071,15 @@ var AlgorandProvider = class {
956
1071
  */
957
1072
  encodePaymentHeader(paymentPayload, chainConfig, version = 1) {
958
1073
  const payload = JSON.parse(paymentPayload);
959
- const networkName = chainConfig?.name || "algorand";
1074
+ let networkName;
1075
+ if (chainConfig?.name === "algorand-testnet") {
1076
+ networkName = "algorand-testnet";
1077
+ } else {
1078
+ networkName = "algorand-mainnet";
1079
+ }
960
1080
  const payloadData = {
961
- from: payload.from,
962
- to: payload.to,
963
- amount: payload.amount,
964
- assetId: payload.assetId,
965
- signedTxn: payload.signedTxn,
966
- ...payload.note && { note: payload.note }
1081
+ paymentIndex: payload.paymentIndex,
1082
+ paymentGroup: payload.paymentGroup
967
1083
  };
968
1084
  const x402Payload = version === 2 ? {
969
1085
  x402Version: 2,
@@ -975,7 +1091,6 @@ var AlgorandProvider = class {
975
1091
  x402Version: 1,
976
1092
  scheme: "exact",
977
1093
  network: networkName,
978
- // Plain chain name for v1
979
1094
  payload: payloadData
980
1095
  };
981
1096
  return btoa(JSON.stringify(x402Payload));