Overview
In this tutorial you’ll learn how to sponsor transactions with Shinami’s Gas Station and submit them for execution on the Sui blockchain. We also cover key tips for using our Gas Station. For our examples, we’ll be constructing our programmable transactions using the Sui TypeScript SDK provided by Mysten. We’ll also use the Shinami Clients SDK to make JSON-RPC calls to Shinami Gas Station for transaction sponsorship and Node Service for transaction execution. This is not meant as a complete template for production code, but as a way to show you how to perform all the requests our Gas Station API supports.What is a sponsored transaction?
When callingsui_executeTransactionBlock
a Base64 encoded string is passed in which encodes the transactions to be executed, the sender address, and the gas information for the transaction block. This unit is called a TransactionData
object. Along with one or more signatures, it’s what’s needed to write to the chain.
When a transaction is being sponsored, the sponsor sends Shinami Gas Station two parts of a TransactionData
object: the sender address and the transaction block to be executed - with gas information omitted this is considered a TransactionKind
object. Gas Station attaches a GasData
object from your Gas Station fund that will pay for the transaction. Then, it produces the BCS-serialized, Base64-encoded string that represents the full TransactionData
object, and sends it back to you along with the sponsor signature authorizing this transaction. From there, the last piece needed to call sui_executeTransactionBlock
is the sender’s signature over the TransactionData
. We’ll go through all of this below.
Requests must be from your BE
Our Gas Station does not support CORS, so if you attempt to make requests to it from your frontend you’ll get a CORS error. We do this because exposing a Gas Station API key on the frontend is a security risk - an attacker could drain the SUI in your Gas Station fund associated with the key by using it to sponsor transactions. For an example flow of combining frontend signing and/or transaction inputs with backend sponsorship, see our Frontend signing + backend sponsorship tutorial.Understanding errors
Check the error code and message of any errors you get. We outline common errors in our Error Reference - make sure to check out the section specific to Sui Gas Station as well as the “General Error Codes” section at the top that apply to all Shinami services.Usage and Billing FAQ
Make sure to check out the Sui Gas Station product usage FAQ and billing FAQ in our Help Center for answers to common questions.Required setup
Notes:
- This tutorial requires a Shinami account. If you do not have one, you can sign up here.
- We take you through the process step-by-step below, but if you get stuck you can reach out to us.
1. Create a Shinami Gas Station fund on Testnet
See the Sui Gas Station page of our Help Center for guidance on how to set up a fund on Testnet if you don’t already have one. When you create a Testnet fund, we add some SUI to it so you can test immediately.2. Clone the github repo
Clone the shinami-examples github repo, cd into theshinami-examples/sui/typescript/backend_examples
directory, and run npm install
to install the dependencies for running the code. Run tsc
in your terminal, and if the command isn’t found run npm install typescript --save-dev
(see other options here ) . If you need to install npm and Node.js, see here. Below, we’ll be using the gas_station.ts
file in the shinami-examples/sui/typescript/backend_examples/src
directory.
3: Create an API access key and copy it into the file.
Create a key You use API access keys to interact with Shinami’s services. For this tutorial, we’ll create one access key that has bothNode Service
and Gas Station
rights for Testnet (you could also make two keys, one with only Node Service
rights and one with only Gas Station
rights).
Create your keys in our US East - us1
region as this code code uses the US East - us1
service URLs since all services used in this tutorial are deployed there (unlike Tokyo - apac1
). See our Authentication and API Keys guide for info on how to set up a key with rights to multiple services.
Add it to the gas_station.ts file
Once you have your key, use it as the value for the GAS_AND_NODE_TESTNET_ACCESS_KEY
constant. You’ll see that we immediately use it to instantiate a Gas Station client (for sponsoring transactions) and a Node client (for executing them).
4. Generate a new Keypair and log its secret key for subsequent runs
We need a Sui address to act as the sender, so we’ll generate a new KeyPair. On the first run, the secret key will be printed to the console. We’ll reuse the same address for all runs, so you can see all the history in one account.Notes:Your app should have its own way to manage accounts and keys. This is just meant as a convenience when doing your initial tests with Gas Station using our tutorial code. Some examples require the sender to have a SUI coin, so maintaining a consistent sender is required.
- Run the code by running the
tsc
command in a terminal at the root of theshinami-examples/sui/typescript/backend_examples
directory to transpile the.ts
files in the/src
directory to JavaScript. - Run
node build/gas_station.js
to run the resulting file. - A secret key will be printed to the console, e.g.
secretKey: suiprivkey...
value. - Before running the code again, replace the call to
generateSecretKey()
with the value of the secret key.
5. Open your Shinami dashboard
Technically not required, but we recommend visiting the “Completed transactions” tab of the Sui Gas Station page of your Shinami dashboard. This tab, and the “In flight transactions” tab (for sponsored transactions that haven’t yet been executed) can be helpful when testing.
Code examples
Overview
Below, we’ll review some of our sample code functions and how to run them. At a high-level, you’ll uncomment just one function that builds a Shinami typeGaslessTransaction
for sponsorship - e.g. clockMoveCallGaslessTransaction()
in the code block below. Then, save any changes you made to the file, run tsc
in the shinami-examples/sui/typescript/backend_examples
directory to transpile the code to JavaScript, and finally run node build/gas_station.js
to run the resulting file. The functions that take arguments require setting values for the associated constant (generally the object id of a SUI coin or other object owned by the sender).
At a high level, we do the same three things each time:
- Generate a transaction without gas info (Step 5).
- Sponsor, sign, and submit the transaction to the Sui blockchain (Step 6).
- Poll our Full node until the transaction is check-pointed and received by the node, then print the status (Step 7).
Sponsor, sign, and execute flow
ThesponsorAndExecuteTransactionForKeyPairSender
does the following:
- Asks Shinami Gas Station to sponsor the transaction we generated above using the Gas Station fund tied to the access key you’re using. Gas Station returns the full transaction - now with gas payment information - as well as the sponsor signature. We use Shinami’s auto-budgeting feature because we omit a
gasBudget
. To learn more about auto-budgeting (and manual budgeting) see the Appendix. - Generates the sender signature.
- Submits the full transaction data, along with the sender and sponsor signatures to the Sui blockchain.
Sponsor and execute a Move call
Understand the code TheclockMoveCallGaslessTransaction
function builds a TransactionBlock that calls a function on a Move module we’ve deployed to Testnet. This is a very simple Move module based on a sample Move project from Mysten. We’re calling its one function, access
, which takes a read-only reference to the sui::clock::Clock
instance located at address 0x6
as its single parameter. The function emits a single event that contains the current timestamp obtained from the Clock
instance. We print the GaslessTransaction to the console so you can see it’s shape.
Note:building transaction blocks creates requests to your Node Service account, which counts towards your access key’s QPS and your daily Node Service request count (and so your bill if you’re on a Pay-as-you-go plan). For more information on how Sui’s programmable transaction blocks are built, see our Transaction.build() guide.
tsc
in the shinami-examples/sui/typescript/backend_examples
directory to transpile the code to JavaScript. Then, run node build/gas_station.js
. You’ll see a lot of output in the console - we added a lot of printing so you can see the shape of API call responses and function results. Of course, once you understand them you can comment out the console.log()
statements.
View the transaction in an explorer
The last thing printed to the console is the digest of the transaction you sponsored. It will be the same value as the txDigest
returned by our gasStationClient.sponsorTransactionBlock
request, since at that point the digest is created over the full TransactionData that will be submitted to the chain (which includes transaction operations, gas object, and sender address). Your digest will of course be different than my example since the sponsorship gas object and the sender are different:
TimeEvent
as mentioned above:

Appendix
You can’t access the gas coin in a sponsored transaction
In a sponsored transaction, you cannot use the gas object provided by Shinami for other purposes. For example, you cannot writeconst [coin] = txb.splitCoins(txb.gas,[txb.pure(100)]);
because it’s accessing txb.gas
. If you try to sponsor a TransactionKind that uses the gas object you will get a JSON-RPC -32602 error.
Check a fund’s balance and deposit more SUI in the fund
You can use ourgas_getFund
endpoint to check the balance of the Gas Station fund tied to the request’s API access key. We generate a deposit address for the fund when you look it up in the Shinami dashboard - go ahead and do that now (see how). If you don’t, the depositAddress
will be null
- which is why we guard against it in the code below.
Understand the checkFundBalanceAndDepositIfNeeded
Note:In order to run the
checkFundBalanceAndDepositIfNeeded
function, you’ll need to have a SUI coin in the address controlled by the KeyPair we’re using as a sender. It takes that coin’s object id as its lone argument. A simple options is to use the faucet to send 1 SUI to that address.- Sets a minimum fund balance (in MIST), above which it will make a deposit.
- Calls
getFund
to obtain the fund information. - Checks that it has a deposit address and if the MIST available for new sponsorships is less than the minimum fund balance. We subtract
inFlight
frombalance
, becauseinFlight
is what’s already reserved for your existing, active sponsorships. - If the conditions are met, it creates a
GaslessTransaction
that transfers the coin passed into the function to your fund. Otherwise, it returnsunderfined
so we know not to poll the Full node for a transaction.
{{name}}
with the actual value for that name
- Set the value of
SUI_COIN_TO_DEPOSIT_ID
to the object ID of a SUI coin owned by the sender. This function is one of the reasons we started the tutorial by having you hard code a private key: so that you had a fixed sender you could send a SUI coin to. - Make sure the
checkFundBalanceAndDepositIfNeeded
function is the only sample code function uncommented.
tsc
in the shinami-examples/sui/typescript/backend_examples
directory to transpile the code to JavaScript. Then, run node build/gas_station.js
. You can look up the digest printed to the console in a Sui explorer like Suivision or Suiscan (make sure you’ve set the explorer to Testnet first). You can also view the deposit history of your fund on the “Funds” tab of the Sui Gas Station page of your Shinami Dashboard.
Tips for setting your sponsorship budget
Auto-budgeting When you omit thegasBudget
parameter in a sponsorship request using our Gas Station API or our Invisible Wallet API, we estimate the transaction cost for you. We then add a buffer of 5% for non-shared objects and 25% for shared objects. The larger buffer for shared objects is because in the time between sponsorship and execution, shared objects can change in a way that increases their transaction cost due to transactions from other apps and individuals. Therefore, we encourage you to execute sponsored transactions quickly if possible.
While we believe our buffers work well in most cases, we encourage you to monitor the success rate of your auto-budgeted transactions to gauge whether your specific use-case requires manually setting an even larger gasBudget.
As a part of auto-budgeting, we put your transactionBytes
through a sui_dryRunTransactionBlock request as a free service before we attempt to sponsor it. This call will generate error messages for certain invalid transactions, such as if the transactionBytes
are transferring an object that’s not owned by the sender
address you provide. We’ll return these errors back to you, which should be the same as if you had made a sui_dryRunTransactionBlock
request yourself. We do not do this step if you manually budget, so any issues that would be caught by sui_dryRunTransactionBlock
will instead produce an error when you try to execute the transaction.
Manual budgeting
When you provide a value for the gasBudget
parameter, remember that:
- The budget set for a transaction along with a fee will be “held” from your gas credits fund while the transaction is in-flight. Funds withheld cannot be used for new sponsorships. Our sponsorship fee is also a percentage of your gas budget (see full explanation in the “Sui Gas Station” tab of the Billing page in your Shinami dashboard. So, you don’t want to set your gas budget unnecessarily high.
- However, if your budget is set too low for the chain to process, the transaction will fail, but gas credits will still be deducted for the computation cost of running the failed transaction on the Sui blockchain. Note that the reference gas price and storage price can change over time, so a similar transaction’s cost one day might be different on another day.
sui_devInspectTransactionBlock
to estimate the gas cost needed by the transaction, but note that this request can produce additional requests that affect your billing. Also, sui_devInspectTransactionBlock
does not catch all transaction execution errors and should not be used for testing the correctness of a transaction. sui_dryRunTransactionBlock
is better for testing transaction correctness, but requires an attached gas object and also can produce extra requests that affect your billing.
There’s an overview of how gas fees are calculated here for more detail.
Other Transaction examples
There are more types of transactions than a Move function call like we did above. Below are some additional examples you can sponsor and execute. To run any one of them, you need to:- Uncomment only that function in Step 5.
- Set the required values for the constants that represent its arguments
splitCoinOwnedByGaslessTransaction
. Note that
tsc
in the shinami-examples/sui/typescript/backend_examples
directory to transpile the code to JavaScript. Then, run node build/gas_station.js
. You can look up the digest printed to the console in a Sui explorer like Suivision or Suiscan (make sure you’ve set the explorer to Testnet first).
Another way to build a TransactionKind
Above, we called theclockMoveCallGaslessTransaction
function, which uses the buildGaslessTransaction
helper function in our TypeScript SDK to build the transaction bytes and turn the bytes into a Base64 string in one request. If you have a need, you can also break those two steps up. The clockMoveCallGaslessTransactionAlternateVersion()
does this by:
- Creating a new
Transaction
and populating it with the Move call. - Building the transaction (with
onlyTransactionKind: true
because this will be a sponsored transaction). - Converting the result to a Base64 string
- Returning a
GaslessTransaction
.
Note:building transaction blocks creates requests to your Node Service account, which counts towards your access key’s QPS and your daily Node Service request count (and so your bill if you’re on a Pay-as-you-go plan). For more information on how Sui’s programmable transaction blocks are built, see our TransactionBlock.build() guide.
clockMoveCallGaslessTransactionAlternateVersion
is the only function un-commented in Step 5:
tsc
in the shinami-examples/sui/typescript/backend_examples
directory to transpile the code to JavaScript. Then, run node build/gas_station.js
. You can look up the digest printed to the console in a Sui explorer like Suivision or Suiscan (make sure you’ve set the explorer to Testnet first).
Check the status of a sponsorship
Why you may not need to check the status of a sponsorship For many use cases, you will not need to check the status of your sponsorships. This is because when you get a successful response from thegas_sponsorTransactionBlock
request, you will immediately get a sender signature and submit it to our Node Service for execution (and therefore you’ll know the status).
Even in the case when a long time elapses between sponsoring the transaction and submitting it to the Sui network, there may not be a need to check the status. If the transaction is executed more than an hour after sponsorship, there will be an error with the associated gas object (because we enforce a 1 hour TTL). If you and the user still want to proceed with the transaction, you can just go through the process again to re-build it and re-sponsor it.
Finally, we return the transaction digest of the sponsored transaction in the txDigest
response field. This can be used with a sui_getTransactionBlock
request of our Sui Node JSON-RPC service to check on the status of the transaction. So, you have the option to check on the status of the transaction directly, rather than indirectly through checking the attached sponsorship.
How to check the status of a sponsorship
All of that said, there may be certain cases where your application needs to check the status of a sponsorship. To use our method for this:
- Uncomment out the
await checkSponsorshipStatusExample();
line beneath thecheckSponsorshipStatusExample
function, as shown below. - Save the changes, transpile the code to JavaScript by running the
tsc
command, and then run the resulting file withnode build/gas_station.js
Note:Note that the status will likely be
IN_FLIGHT
even if you’ve just executed the transaction. This is because it can take a little time after execution for our system to register that the gas object for the transaction has been used. You can read about the possible sponsorship statuses on our Sui Gas Station API doc.Getting the lowest latency possible
Options to consider Generally, customers don’t have latency concerns with our Gas Station. At the time of writing this, our Mainnet p95 sponsorship latency across all customers in the last month was 102.7ms, with an average of 45.5ms. This is the latency from when a request hits our server to when we return a response. This latency includes sponsorships that use our auto-budgeting (the vast majority) and those that don’t. If your use case requires the lowest latency possible, you can try one or both of the options below. The size of the savings varies a lot depending on your location and your transactions, so you should test with and without these behaviors and only use one if it’s meaningful for you. Also, note that the latencies you see on Testnet may not be the same on Mainnet (but should give you an idea of the magnitude of the savings you can achieve).- Build your transaction offline. This can save a few requests that would otherwise get made to a Fullnode client via the
Transaction.build()
request. Note, though, that those requests are extremely fast for us to fulfill once they hit our servers - often sub 10ms - so this is only a latency concern if your servers are geographically far from one of our Fullnode regions. - Manually budget your transaction by providing a
gasBudget
value. This skips thesui_dryRunTransactionBlock
request we do as a part of auto-budgeting to determine the right gas budget for your transaction. Thissui_dryRunTransactionBlock
request also catches some transaction errors pre-sponsorship, so you’ll lose this check. We generally recommend auto-budgeting because it’s fast and sets an ideal budget for you, but if you need the lowest latency possible, manual budgeting speeds things up. How much depends on the complexity of your transactions. Again, our recent p95 for sponsorships- including those where we auto-budget and run asui_dryRunTransactionBlock
- was just over 100ms on Mainnet. Still, we see some customers whosesui_dryRunTransactionBlock
p95 latency by itself is > 100ms. So, if you’re sponsoring very complex transactions, you could save a meaningful amount of time with manual budgeting.- If you choose to do manual budgeting, you still have the option to do a
sui_dryRunTransactionBlock
before requesting sponsorship so that you maintain low latency from the point of asking for a sponsorship onward and you still get the benefits of error checks on your transaction and better gas estimation.
- If you choose to do manual budgeting, you still have the option to do a
clockMoveCallGaslessTransactionOfflineBuildManualBudget()
. The method:
- Provides all the information needed about the input object offline (see this doc for more info).
- Builds the transaction without a Full node client and sets the gas budget manually.
clockMoveCallGaslessTransactionAlternateVersion
is the only function un-commented in Step 5:
tsc
in the shinami-examples/sui/typescript/backend_examples
directory to transpile the code to JavaScript. Then, run node build/gas_station.js
. You can look up the digest printed to the console in a Sui explorer like Suivision or Suiscan (make sure you’ve set the explorer to Testnet first).