Documentación: spare-sdk

Documentación

spare-sdk

Suma una microdonación en USDC por transacción a cualquier app de Stellar. La donación va directo a la cuenta de la fundación; Spare nunca retiene fondos. Solo USDC de Circle, solo fundaciones con dominio verificado.

Instalar

pnpm add spare-sdk @stellar/stellar-sdk

@stellar/stellar-sdk es una peer dependency: instálala junto a spare-sdk (una sola copia compartida con tu app).

Transacción clásica: addDonation

import { addDonation, HorizonAccountPort } from "spare-sdk";

const result = await addDonation(unsignedTx, {
  foundation: { account: "G...", domain: "foundation.org" }, // must be listed in its stellar.toml
  amount: "0.50",            // or: roundUp: true (only when the purchase is USDC)
  appId: "my-app",           // <= 22 ASCII bytes, memo becomes "spare:my-app"
  maxAmount: "5",            // your cap per donation
  accounts: new HorizonAccountPort("https://horizon.stellar.org"),
});

switch (result.status) {
  case "offered":      // ask the user "Add US$<result.donation.amount>?", then sign result.transaction
  case "not-offered":  // sign the original tx; result.reason says why (e.g. "insufficient-balance")
  case "passthrough":  // signed / fee-bump / multisig / extra signers / Soroban: sign the original tx
}

offered devuelve una transacción nueva sin firmar (hash nuevo, comisión subida en una operación, memo puesto solo si estaba vacío y ningún destino exige uno). No se agrega nada sin el sí explícito de la persona.

Redondeo

Solo cuando toda la transacción es una compra en USDC de Circle (pagos, pathPaymentStrictSend, ofertas de venta). El SDK informa lo que usó, así que muestra estos valores en vez de recalcular: donation.purchase / purchaseStroops (la compra en USDC) y roundedTotal / roundedTotalStroops (compra más donación). Solo vienen definidos en el redondeo.

  • Ya es entero (por ejemplo 5.00): round-up-already-whole.
  • pathPaymentStrictReceive, operaciones en XLM u otro activo, o operaciones no modeladas en la misma transacción: round-up-needs-usdc-purchase (el gasto real no se conoce de antemano, así que Spare nunca redondea sendMax).

Una transacción Soroban tiene una sola operación, así que la donación es un pago aparte después del éxito. Necesita una segunda firma: avísale a la persona antes de que acepte.

import { donateAfter } from "spare-sdk";

const follow = await donateAfter(
  { successful: true, sourceAccount: userAccount, hash },
  { foundation, amount: "0.50", appId: "my-app", maxAmount: "5",
    accounts, networkPassphrase: Networks.PUBLIC },
);
if (follow.status === "offered") { /* confirm, then sign follow.transaction */ }

El seguimiento es un único pago (umbral medio). Si la cuenta de la persona necesita más que su llave maestra para firmarlo, no se ofrece nada (multisig).

Resultados y códigos de motivo

EstadoSignificado
offeredUna transaction nueva sin firmar, la información de donation y el fee. Pregunta a la persona y luego fírmala.
not-offeredLa transacción está bien pero no se debe ofrecer donación. reason dice por qué. Firma la original.
passthroughSpare no puede modificar la transacción de forma segura. Queda intacta. Firma la original.

Motivos de passthrough

MotivoSignificado
fee-bumpLa transacción es un fee-bump.
already-signedLa transacción ya tiene firmas.
sorobanUna transacción Soroban: usa donateAfter.
multiple-sourcesMás de una cuenta de origen.
multisigLa llave maestra sola no alcanza el umbral de la transacción: medio, o alto con setOptions / accountMerge.
extra-signersEstá puesta la precondición extraSigners de CAP-21.

Motivos de not-offered

MotivoSignificado
invalid-optionsLas opciones están mal formadas, incluido un baseFee mal formado.
invalid-app-idappId no es válido (como máximo 22 bytes ASCII).
invalid-amountEl monto no es un monto USDC válido.
invalid-foundationLa fundación está mal formada.
amount-over-capLa donación supera tu maxAmount.
round-up-needs-usdc-purchaseEl redondeo necesita una compra en USDC de Circle (ver arriba).
round-up-already-wholeLa compra ya es un monto entero.
foundation-not-verifiedLa cuenta de la fundación no está listada en el stellar.toml de su sitio.
foundation-trustline-missingLa fundación no tiene trustline de USDC.
foundation-trustline-unauthorizedLa trustline de USDC de la fundación no está autorizada.
foundation-trustline-fullNo queda espacio bajo el límite de la trustline de la fundación.
user-trustline-missingLa persona no tiene trustline de USDC.
user-trustline-unauthorizedLa trustline de USDC de la persona no está autorizada.
unsupported-operationLa transacción tiene operaciones que pueden gastar fondos de formas que Spare no modela, por ejemplo manageBuyOffer, createClaimableBalance, liquidityPoolDeposit, accountMerge.
multisigSolo desde donateAfter: el seguimiento necesita más que la llave maestra.
insufficient-balanceNo alcanza el USDC, o el XLM para la comisión más el XLM que envía la transacción.
account-lookup-failedNo se pudo cargar la cuenta.
main-tx-not-successfulSolo donateAfter: la transacción principal no resultó bien.

DonationInfo

CampoDescripción
amountMonto en USDC, 7 decimales. Muéstraselo a la persona antes de firmar.
amountStroopsEl mismo monto en stroops (bigint).
foundationLa cuenta de la fundación.
attributedFalso cuando la transacción ya tenía un memo, así que la donación no se puede etiquetar para la app.
purchase, purchaseStroopsSolo redondeo: la compra en USDC que la transacción ya hace.
roundedTotal, roundedTotalStroopsSolo redondeo: la compra más la donación.

Opciones

OpciónDescripción
foundation{ account, domain }: la cuenta G... y el hostname cuyo stellar.toml debe listarla.
appIdComo máximo 22 bytes ASCII. El memo queda como spare:<appId>.
maxAmountTu tope por donación, en USDC.
accountsUn AccountPort (por ejemplo HorizonAccountPort).
fetchOpcional. Fetch inyectable, por defecto el global.
baseFeeOpcional. Comisión por operación en stroops, solo dígitos, mínimo 100.

Puerto de cuenta

AccountPort.loadAccount(id) devuelve un AccountSnapshot, o null cuando la cuenta no existe. HorizonAccountPort lo llena desde Horizon (headroom = límite - saldo - pasivos de compra; memoRequired desde la entrada de datos config.memo_required). Spare decide sobre múltiples firmas con masterWeight y thresholds por transacción.

CampoDescripción
sequenceNúmero de secuencia actual (la próxima transacción usa sequence + 1).
xlmAvailableXLM disponible para comisiones: saldo menos reserva y pasivos de venta.
usdcTrustline de USDC de Circle { balance, authorized, headroom }, o null si no hay.
masterWeightPeso de la llave propia de la cuenta; 0 cuando está desactivada.
thresholds{ low, medium, high }, de 0 a 255.
memoRequiredSEP-29: la cuenta espera un memo en los pagos entrantes.

Memo, SEP-29 y comisiones

Si el destino de un pago, pago con ruta o merge tiene config.memo_required, Spare no pone su memo, así que el checkMemoRequired de tu app sigue funcionando. La donación igual se ofrece, con donation.attributed === false. Un destino que no se puede leer se trata como memo requerido.

baseFee es por operación, solo dígitos, mínimo 100 stroops. Un valor mal formado da not-offered / invalid-options; nunca lanza error. Una transacción con comisión 0 sube a 100 por operación, y el mismo número se usa para la revisión de saldo y para la transacción construida.

Utilidades

  • verifyFoundation(foundation) y listVerifiedFoundations(candidates): la revisión SEP-1 de ACCOUNTS (solo https, sin redirecciones, 5 s de timeout, cuerpo limitado a 100 KB).
  • parseUsdc, formatStroops, roundUpToWholeDollar: utilidades de montos.
  • isValidAppId, memoFor: validación del app id y el memo.
  • USDC_ISSUER, USDC_CODE: las constantes de USDC de Circle.
  • El acceso a la red (fetch, AccountPort) es inyectable.