Sponsored Transactions
A Sui sponsored transaction is one where a Sui address (the sponsor's) pays the gas fees for a transaction that another address (the user's) initializes. You can use sponsored transactions to cover the fees for users on your site or app so that they don't get charged for them. This removes a significant obstacle that web 2.0 users encounter when entering web3, as they often have to purchase tokens to perform a transaction on chain. For example, you could sponsor gamers' early transactions to increase conversion rates.
Sponsored transactions also facilitate asset management as you don't need to maintain multiple accounts with SUI tokens to transfer funds.
You can use Sui sponsored transactions to:
- Sponsor (pay gas fees for) a transaction a user initiates.
- Sponsor transactions you initiate as the sponsor.
- Provide a wildcard
GasData
object to users. The object covers the gas fees for a user transaction. TheGasData
object covers any fee amount determined for the transaction as long as the budget is sufficient.
Potential risks using sponsored transactions
The most significant potential risk when using sponsored transactions is equivocation. In some cases under certain conditions, a sponsored transaction can result in all associated owned objects, including gas in a locked state when examined by Sui validators. To avoid double spending, validators lock objects as they validate transactions. An equivocation occurs when an owned object's pair (ObjectID
, SequenceNumber
) is concurrently used in multiple non-finalized transactions.
To equivocate, either the user or the sponsor signs and submits another transaction that attempts to manipulate an owned object in the original transaction. Because only the object owner can use an owned object, only the user and sponsor can cause this condition.
Create a user-initiated sponsored transaction
A user-initiated sponsored transaction involves the following steps:
- A user initializes a
GasLessTransactionData
transaction. - The user sends
GasLessTransactionData
to the sponsor. - The sponsor validates the transaction, constructs
TransactionData
with gas fees, and then signsTransactionData
. - The sponsor sends the signed
TransactionData
and the sponsorSignature
back to the user. - The user verifies and then signs
TransactionData
and sends the dual-signed transaction to Sui network through a Full node or the sponsor.
GasLessTransactionData
GasLessTransactionData
is basically TransactionData
without GasData
. It is not a sui-core
data structure, but it is only an interface between user and sponsor.
The following example constructs a GasLessTransactionData
object.
pub struct GasLessTransactionData {
pub kind: TransactionKind,
sender: SuiAddress,
…
}
Create a sponsor-initiated sponsored transaction
A sponsor-initiated sponsored transaction involves the following steps:
- A sponsor constructs a
TransactionData
object that contains the transaction details and associated gas fee data. The sponsor signs it to generate aSignature
before sending it to a user. You can send the unsignedTransactionData
via email, SMS, or an application interface. - The user checks the transaction and signs it to generate the second
Signature
for the transaction. - The user submits the dual-signed transaction to a Sui Full node or sponsor to execute it.
You can use a sponsor-initiated sponsored transaction as an advertiser, or to incentivize specific user actions without requiring the user to pay for gas fees.
Create sponsored transactions using a GasData object
To use a GasData
object to sponsor the gas fees for a transaction, create a GasData
object that covers the fees determined for the transaction. This is similar to providing a blank check to a user that can be used only to cover gas fees. The user doesn't need to know how much the fee is or approve it.
A sponsor transaction using a GasData
object involves the following steps:
- The sponsor provides a
GasData
object to a user. - The user constructs
TransactionData
and signs it to generate aSignature
. - The user sends the
TransactionData
and theSignature
to the sponsor. - The sponsor confirms the
TransactionData
and then signs it. - The sponsor submits the dual-signed
TransactionData
to a Full node to execute the transaction.
Create a Sui gas station
On Sui, a gas station is a concept to describe where you set up processes to sponsor user transactions. You can customize a Sui gas station to support the specific user-facing functionality you need. Some example use cases for a Sui gas station include:
- Monitor real-time gas prices on the network to determine the gas price that the station provides.
- Track usage of gas provided to users on the network.
- Gas pool management, such as using specific gas objects to minimize costs or reduce the risk of a large amount of locked objects that remain illiquid while locked.
Authorization and rate limiting
Depending on the nature of your gas station, you can apply different authorization rules to avoid being spammed by bad actors. Possible policies include:
- Rate limit gas requests per account or per IP address
- Only accept requests with a valid authorization header, which has separate rate limits
Abuse detection
For all gas objects that you provide as a sponsor, you should track if users ever try to equivocate and lock objects. If you detect such behavior, block the user or requester accordingly.
Code examples to create a Sui gas station
The following Rust SDK code examples demonstrate how to implement a Sui gas station that supports each of the sponsored transaction types described previously.
User-initiated sponsored transactions
Use the API endpoint to receive GaslessTransaction
transactions and return a sole-signed SenderSignedData
object.
pub fn request_gas_and_signature(gasless_tx: GaslessTransaction) -> Result<SenderSignedData, Error>;
Sponsored transactions with GasData objects
Use the API endpoint to receive sole-signed SenderSignedData
and return the result of the transaction.
pub fn submit_sole_signed_transaction(sole_signed_data: SenderSignedData) -> Result<(Transaction, CertifiedTransactionEffects), Error>;
Alternatively, use the API endpoint to return a GasData object.
pub fn request_gas(/*requirement data*/) -> Result<GasData, Error>;
User and sponsor-initiated transaction
Use the API endpoint to receive dual-signed SenderSignedData
and return the result of the transaction.
pub fn submit_dual_signed_transaction(dual_signed_data: SenderSignedData) -> Result<(Transaction, CertifiedTransactionEffects), Error>;
For user and sponsor-initiated transactions, users can submit the dual-signed transaction via either a sponsor or a Full node.
Sponsored transaction data structure
The following code block describes the TransactionData
structure for sponsored transactions and GasObject
. You can view the source code in the Sui GitHub repository.
TransactionData
Structure
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
pub struct TransactionDataV1 {
pub kind: TransactionKind,
pub sender: SuiAddress,
pub gas_data: GasData,
pub expiration: TransactionExpiration,
}
GasData
Structure
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
pub struct GasData {
pub payment: Vec<ObjectRef>,
pub owner: SuiAddress,
pub price: u64,
pub budget: u64,
}
To learn more about transactions in Sui, see Transactions.