In-Depth: Sponsored Transactions for Developers

Sponsored transactions were added to the Stacks blockchain to enable developers and/or infrastructure operators to pay for users to call into their smart contracts, even if users do not hold STX to pay for fees. (See SIP-005).

The process consists of two steps: 1) signing by user and 2) signing by sponsor

Signing Transaction by User

Sponsored transactions are build in the same way as normal transactions. The only difference is that fees are set to 0 and the sponsored flag is set to true. The sponsored transaction is then send to the user's wallet for confirmation. The wallet can't broadcast the transaction, instead it is returned to the app and needs to be handled separately.

An example with @stacks/connect library looks like the following (see also send-many example):

const txOptions = {...};
txOptions.fee = 0;
txOptions.sponsored = true;

openContractCall({...txOptions,
        onFinish: (finishedTxData) => handleSignedSponsoredTransaction(finishTxData)
    }
)

Signing Transaction by Sponsor

The signed transaction is returned from the wallet as part of the finished transaction data. The authentication type of the transaction will be of type Sponsored

data.stacksTransaction.auth.authType === AuthType.Sponsored;

The serialized transaction (data.txRaw) can then be sent to a server for signing by the sponsor. There, the sponsor sets the fee for the transaction, add the sponsor's nonce and signs the transaction with the private key.

An example using the javascript library @stacks/transactions on a server looks like the following (see also not-sponsoring example):

import {deserializeTransaction, sponsorTransaction} from '@stacks/transactions';

...
const transaction = deserializeTransaction(txRaw);
const feeEstimate = await estimateTransactionFeeWithFallback(transaction, network);
const sponsorNonce = nonce; // next nonce of sponsor account

const sponsoredTx = await sponsorTransaction({
    sponsorPrivateKey: env.SPONSOR_PRIVATE_KEY,
    transaction,
    network,
    fee,
    sponsorNonce: nonce,
});

const result = await broadcastTransaction(sponsoredTx);

Webservices for Sponsoring Transactions

As mentioned above the transaction signing by sponsor usually happens on a server that holds the private key of the sponsor. There are two open source projects that implemented such a webservice that provide both the same API:

  • Tx2 service: basic service that allows to sponsor send-many calls for $NOTHING, uses single sponsoring address.
  • Xverse service: advanced service that allows to sponsor nft transfers, uses a randomly selected address from a list of sponsoring addresses.

API Reference

/v1/sponsor (POST)

The request body contains the user signed transaction as json property tx. The transaction must be created as a sponsored transaction. The service will sign the transaction as the sponsor and broadcast it.

Example

Request:

curl -X POST http://localhost:3000/v1/sponsor \
 -H "Content-Type: application/json" \
 -d '{"tx": "feedc00d1...user-signed-transaction-hex"}'

Result:

{
  "txid": "c001c0de...tx-id",
  "rawTx": "feedc00d2...sponsored-transaction-hex"
}

info

/v1/info (GET)

The request returns information about the service in JSON. The result is a list of addresses used by the service for sponsoring transactions.

For tx2, the result contains also the minimum fees for NOT sponsoring and the balance of the sponsor.

Example
curl -X GET http://localhost:3000/v1/info'

Result:

{
  "active": "true",
  "sponsor_addresses": ["SP1234...", "SP5678..."]
}