Overview
Shinami’s Gas Station only supports integration with your app’s backend (no CORS support) for security reasons. This limits exposure of your Gas Station API keys. If these keys are leaked, bad actors have the ability to sponsor transactions from your fund until it has been drained or you disable the key in our dashboard. This guide will help you integrate a connected browser wallet with Shinami Gas Station transaction sponsorship. We show possible integration flow diagrams with links to associated sample code. If you have not already set up a Shinami Gas Station Fund and Access Key, and sent a successful sponsorship request, see our Gas Station tutorial. It’s useful to run the intial example to understand sponsorship before you connect the frontend, and the code repo you clone includes the code for this tutorial as well. Finally, after you review the images and code below, if you get stuck on something and have a question feel free to reach out to us.Examples
Sample app overview

Aptos Labs wallet-adapter-react library and the Shinami TypeScript SDK. It integrates well with Razor Chrome extension wallet out of the box. We’ve included it in our shinamicorp/shinami-examples repo here. It’s not meant as a starter template for a production app (as an example its API endpoints have no authentication mechanism). Instead, it’s meant to show you a very simple working example so you can understand the core concepts involved. It includes a README.md file to help you get it up and running quickly if you want to see a working example.
Consider obtaining the user signature pre-sponsorship
You should think about whether building the transaction on the frontend or backend is a better fit for your app. The same goes for where to submit the transaction for execution. All combinations work, as long as you ask for sponsorship from your backend. In general, it’s beneficial to have the user sign the transaction before you ask for a sponsorship. This is because if you ask for a sponsorship and then the user fails to sign, you’ll still pay our small, minimum fee for creating a sponsorship that goes unused. This means that the user will sign against the0x0 placeholder feePayer address generated when you build a feePayer transaction, and then the transaction you submit will need to have the actual feepayer’s address set (done automatically by our TypeScript SDK).
Build and sign on the FE, sponsor and submit on the BE
This example shows how to build and sign a transaction on the frontend, then send it to your backed for Shinami Gas Station sponsorship and submission to a Movement node. Signing is done with a connected browser wallet. Image
connectedWalletTxFEBuildBESubmit on the frontend and sponsorAndSubmitTx on the backend.
- Frontend: Build a feePayer transaction (sample code).
- Frontend: Obtain the sender’s signature over the transaction (sample code).
- Frontend: Send a request to your app’s backend to sponsor and submit the transaction. This request will include serialized versions of the transaction and the sender’s AccountAuthenticator (sample code).
- Backend: Send a
gas_sponsorAndSubmitSignedTransactionrequest to Shinami’s Gas Station (with the de-serialized transaction and sender AccountAuthenticator) (sample code). - Shinami: Shinami will sponsor the transaction and then submit it with the sender and feePayer signatures, returning the
PendingTransactionResponseto your backend if successful. - Backend: Handle the response as needed.
- Backend: Send a response to the frontend (sample code).
- Frontend: Handle the response (this is the response to the request in Step 3). In our sample app, we poll a Movement node until it has a record of the transaction and then print the user’s message that was included in the transaction (sample code).
Build and sponsor on the BE, sign and submit on the FE
This example shows how to build and sponsor a transaction on your backend, then sign and submit it on your frontend. Signing is done with a connected browser wallet. Image
connectedWalletTxBEBuildFESubmit on the FE and buildAndSponsorTx on the BE.
- Frontend: Send a request to your app’s backend to build and sponsor a transaction. Include any data from the FE needed to build the transaction. In our case, that’s the sender’s address and the message the user submitted in the form on the page (sample code).
- Backend: Build a feePayer transaction (sample code).
- Backend: Make a
gas_sponsorTransactionrequest to Shinami’s Gas Station to sponsor the transaction (sample code). - Backend or Frontend: Set the transaction’s
feePayerAddressto the feePayer address value returned from the Gas Station sponsorship request (our TypeScript SDK sets this for you automatically and so does not explicitly return it to you). The sender can sign the transaction with either the actual feePayer address or the special0x0address assigned when you create a feePayer transaction. However, you must ensure the actual feePayer’s address is set on the transaction before you submit it to the Movement blockchain. If not, you’ll get anINVALID_SIGNATUREerror because the feePayer’s signature was over a transaction with the feePayer address but the submitted transaction still has the0x0address. - Backend: Return the serialized transaction, feePayer AccountAuthenticator, and (optionally) feePayer’s address to the frontend (sample code).
- Frontend: Deserialize the transaction and obtain the sender’s signature over the transaction (sample code).
- Frontend: Submit the transaction, along with the sender and (deserialized) feePayer signatures, to a Movement node (sample code).
- Frontend: Handle the response (a
PendingTransactionResponseif successful). In our sample app, we poll a Movement node until it has a record of the transaction and then print the user’s message that was included in the transaction (sample code).
Other flows in our sample app
- Build and sign on the FE, sponsor on the BE, submit on the FE.
- Build and sponsor on the BE, sign on the FE, submit on the BE.
Appendix
Common issues
INVALID_SIGNATURE error when submitting the transaction
- Make sure the transaction you’re submitting has been updated to use the feePayer’s address (instead of the
0x0that gets set when a feePayer transaction is built).