Increase user engagement and retention by sponsoring transactions
Overview
You'll find API endpoints and key usage notes below. If you ever need help with an integration question, you can reach out to us.
Use Cases
Shinami's Gas Station API lets you easily sponsor transactions for your users. APT is pulled from a fund you create to power these transactions, managed in Shinami's dashboard.
Shinami facilitates sponsored transactions based on logic determined by you, the app developer. Examples include sponsoring each user's initial transaction(s) for better onboarding and sponsoring transactions of a particular type that you want to encourage.
Authentication, Rate Limits, and Error Handling
Authentication
You authenticate via an access key passed in a header ('X-Api-Key: ACCESS_KEY') or in the request url (https://api.shinami.com/aptos/gas/v1/ACCESS_KEY
). We recommend using a request header and not putting access keys in your request URLs for reduced visibility (in logs, etc). These steps are done automatically by our TypeScript SDK.
For more information, including how to set up API access keys, see our Authentication and API Keys guide.
Call Shinami's Gas Station from your backend server
Shinami Gas Station does not support CORS requests, so you will get a CORS error if you make requests from your frontend. Use your backend server to integrate with Shinami's Gas Station. This limits exposure of your sponsorship access 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.
For a tutorial on how to combine frontend signing with backend sponsorship, see our Frontend signing + backend sponsorship tutorial.
Rate Limits
Gas Station access keys have a rate limit of 10 QPS. When you surpass this, we return a JSON-RPC error code -32010
. We recommend implementing retries with a backoff to handle any rate limits errors that arise. You can also consider batching multiple Move calls into a Move script.
Error Handling
See our Error Reference for guidance on the errors you may receive from our services, including a section on errors specific to the Aptos Gas Station API.
Methods
gas_sponsorTransaction
Description
Sponsor a transaction by having your Shinami Gas Station fund act as the feePayer.
Gas Station requests must come from your backend (the service doesn't have CORS support). For an overview of all the ways to integrate frontend signing with backend sponsorship, see our Gas Station Tutorial Appendix, including tips on serializing and deserializing the necessary data types as you pass them between FE and BE.
Gas Station fund required: You need a Gas Station fund with APT in order to sponsorship transactions. For information on how to set this up, see the Gas Station section of our FAQ.
Shinami sponsorship fees: See our FAQ.
Request Parameters
Name | Type | Description |
---|---|---|
rawTransaction | TypeScript SDK: SimpleTransaction | MultiAgentTransaction cURL: String | byte array | TypeScript SDK: An AnyRawTransaction (the result of a successful call to aptos.transaction.build.simple() or aptos.transaction.build.multiAgent() ). For a code example of building and sponsoring a transaction, see the Appendix.cURL: BCS-serialized RawTransaction component of an AnyRawTransaction. Can be passed either as an unsigned byte array or a hex string. For an example of creating this in Python, see the Aptos Python SDK: rawTransaction tab of our Example Request Templates.Note: the transaction's expiration timestamp must be set to a time within the next hour, in seconds. Otherwise, our service will not sponsor the transaction. |
cURL-only secondarySignerAddresses | String[] | Optional array of additional signer addresses. Must be in the exact order. Required for multi-agent transactions. |
Example Request Template
The TypeScript example uses the Shinami Clients SDK.
Replace all instances of {{name}}
with the actual value for that name
import { GasStationClient } from "@shinami/clients/aptos";
const gas = new GasStationClient("{{gasStationAccessKey}}");
await gas.sponsorTransaction(transaction);
curl https://api.shinami.com/aptos/gas/v1/ \
-X POST \
-H 'X-API-Key: {{gasStationAccessKey}}' \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc":"2.0",
"method":"gas_sponsorTransaction",
"params":[
"{{rawTransaction}}",
["{{secondarySignerOne}}"]
],
"id":1
}'
curl https://api.shinami.com/aptos/gas/v1/ \
-X POST \
-H 'X-API-Key: {{gasStationAccessKey}}' \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc":"2.0",
"method":"gas_sponsorTransaction",
"params":[
"0x3de05e9561457529d7b46a93bfa78d9927a403e51a61dc65c5d9ea3e6ec319e80000000000000000008302a11ceb0b060000000701000602060a03101104210405252a074f42089101200000000100020103040100010004080001050304010002060607000107080501000002020202060c060c020b000108010b0001080101080102060c03010b000109000001060c010502050b000109000a6170746f735f636f696e04636f696e067369676e657204436f696e094170746f73436f696e0877697468647261770a616464726573735f6f66076465706f7369740000000000000000000000000000000000000000000000000000000000000001000001110a0006640000000000000038000c020a0106c80000000000000038000c030b0111010b0238010b0011010b033801020000400d0300000000006400000000000000e31b56660000000002",
["0x7e3a6349ad8a43a6b7c3307b4b05d9008548a11673f440af5e516a9bc5114b3d"]
],
"id":1
}'
# Here is an example of preparing a `RawTransaction` to be sent
# as the `rawTransaction` parameter
# Imports used in the below code snippet
from aptos_sdk.async_client import RestClient
from aptos_sdk.bcs import Serializer
...
## Set up your transaction payload (`txPayload`) of type `TransactionPayload`
## Also the `senderAccount`, which is of type `Account`
...
rest_client = RestClient(APTOS_TESTNET_NODE_URL)
raw_transaction = await rest_client.create_bcs_transaction(senderAccount, txPayload))
# Option 1: send as an unsigned byte array
shinami_rawTransaction = list(raw_transaction.to_bytes())
# Option 2: send as a hex string
transaction_serializer = Serializer()
raw_transaction.serialize(transaction_serializer)
shinami_rawTransaction = transaction_serializer.output().hex()
Example Response
{
public_key: { key: { data: [Object] } },
signature: { data: { data: [Object] } }
}
{
"jsonrpc":"2.0",
"result":{
"feePayer":{
"address":"0x2cdcec66a48b596c923024102feda5ee759197fe844c5ba56013d3b9a99287b6",
"signature":[0,32,87,185,212,148,211,30,213,111,144,35,98,238,246,233,212,250,141,34,227,144,171,146,27,203,22,120,200,236,145,94,203,207,64,3,238,3,81,139,240,173,30,31,146,34,145,151,191,54,93,34,206,249,77,214,168,228,65,165,220,65,174,220,137,196,173,168,37,116,136,85,216,217,121,46,1,205,38,147,183,201,252,246,40,62,127,220,20,246,41,244,5,209,109,224,114,189,3]
}
},
"id":1
}
TypeScript SDK Response Fields
Note transaction.feePayerAddress is updated in-place upon a successful return.
Type | Description |
---|---|
AccountAuthenticator | AccountAuthenticator of the sponsor, to be included in the request to submit the transaction to the chain. |
cURL Response Fields
Name | Type | Description |
---|---|---|
feePayer.address | String | Fee payer account address. If desired you can use this to set the transaction's feePayerAddress before obtaining sender and any secondary signer signatures. |
feePayer.signature | Unsigned byte array | BCS-serialized AccountAuthenticator of the fee payer, as an array of unsigned bytes. |
gas_sponsorAndSubmitSignedTransaction
Description
This method first sponsors a transaction with your Shinami Gas Station fund and then submits the transaction to an Aptos fullnode for execution. Therefore, you need to obtain all necessary signatures - with wildcard fee payer address 0x0
- before calling this method.
Gas Station requests must come from your backend (the service doesn't have CORS support). For an overview of all the ways to integrate frontend signing with backend sponsorship, see our Gas Station Tutorial Appendix, including tips on serializing and deserializing the necessary data types as you pass them between FE and BE.
Access Key Requirements: In addition to Gas Station rights, making this request requires an access key with Node Service rights for the Aptos network you wish to submit the transaction on. See how to make one here.
Gas Station fund required: You need a Gas Station fund with APT in order to sponsorship transactions. For information on how to set this up, see the Gas Station section of our FAQ.
Shinami sponsorship fees: See our FAQ.
Request Parameters
Name | Type | Description |
---|---|---|
rawTransaction | SDK SimpleTransaction | MultiAgentTransaction cURL Hex string | Unsigned byte array. For an example of creating this in Python, see the Aptos Python SDK tab of our Example Request Templates. | TypeScript SDK: (The result of a successful call to aptos.transaction.build.simple() or aptos.transaction.build.multiAgent() ). For a code example of building and sponsoring a transaction, see the Appendix .cURL: BCS-serialized RawTransaction component of an AnyRawTransaction. Can be passed either as an unsigned byte array or a hex string. Note: the transaction's expiration timestamp must be set to a time within the next hour, in seconds. Otherwise, our service will not sponsor the transaction. |
senderSignature | SDK AccountAuthenticator cURL BCS-serialized AccountAuthenticator . Can be passed either as an unsigned byte array or a hex string. For an example of creating this in Python, see the Aptos Python SDK tab of our Example Request Templates. | The signature of the sender over the transaction with wildcard fee payer address (0x0 ). |
secondarySigners | SDK AccountAuthenticator[] cURL ["address" : <hex string>, "signature": <BCS-serialized AccountAuthenticator>(Can be passed either as an unsigned byte array or a hex string.)] | Optional array of additional signers and their signatures. Must be in the exact order. Required for multi-agent transactions |
Example Request Template
The TypeScript example uses the Shinami Clients SDK.
Replace all instances of {{name}}
with the actual value for that name
import { GasStationClient } from "@shinami/clients/aptos";
const gas = new GasStationClient("{{gasStationAccessKey}}");
await gas.sponsorAndSubmitSignedTransaction(
{{transaction}},
{{senderSignature}},
{{[secondarySignatureOne, secondarySignatureTwo...]}}
);
curl https://api.shinami.com/aptos/gas/v1/ \
-X POST \
-H 'X-API-Key: {{gasStationAccessKey}}' \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc":"2.0",
"method":"gas_sponsorAndSubmitSignedTransaction",
"params":[
"{{rawTransaction}}",
"{{senderSignature}}",
[{"address": "{{secondarySenderAddress}}","signature": "{{secondaryAddressSignature}}"}]
],
"id":1
}'
curl https://api.shinami.com/aptos/gas/v1/ \
-X POST \
-H 'X-API-Key: {{gasStationAccessKey}}' \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc":"2.0",
"method":"gas_sponsorAndSubmitSignedTransaction",
"params":[
"0x8a59591db8c95f7065cc1476c913e0015bea279cc8ba852d97f9269f181b3ecc0000000000000000008302a11ceb0b060000000701000602060a03101104210405252a074f42089101200000000100020103040100010004080001050304010002060607000107080501000002020202060c060c020b000108010b0001080101080102060c03010b000109000001060c010502050b000109000a6170746f735f636f696e04636f696e067369676e657204436f696e094170746f73436f696e0877697468647261770a616464726573735f6f66076465706f7369740000000000000000000000000000000000000000000000000000000000000001000001110a0006640000000000000038000c020a0106c80000000000000038000c030b0111010b0238010b0011010b033801020000400d0300000000006400000000000000af1551660000000002",
"0x0200202775e36c43afe1cd6b49514aaee0377fe60d2a154c74774248b7248d1554555800400c3c374f5a16084ca25c7f5f6105fe32e3b6acddbe7e4d369791c12fd7efd7838209e114c2d212bd31d7b3f38b5fa737145051659c18205ec3a32195aa02d406",
[{
"address": "0x5f4a07084ae99c7d208ad4d6dc0315f271b8e2b915b38ee241e3b26e18885fa2",
"signature": "0x0200204faef99c8a1b3b1511348081f3b73fe59550121f6081bfaefa4f8a55e61533bd0040efb3618c7e31ea3073f61e0f9798353979cb5ed13a6f8d52f5bf3791e54d3c760d223ec0721557254f821f1d5fecab96b2ad085a82e27ae1995b939876f3260e"
}]
],
"id":1
}'
# Here is an example of preparing a `RawTransaction` to be sent as the `rawTransaction` parameter
# and an AccountAuthenticator to be sent as the `senderSignature` parameter.
# Imports used in the below code snippet
from aptos_sdk.async_client import RestClient
from aptos_sdk.bcs import Serializer
from aptos_sdk.transactions import FeePayerRawTransaction
...
## Set up your transaction payload (`txPayload`) of type `TransactionPayload`
## Also the `senderAccount`, which is of type `Account`
...
rest_client = RestClient(APTOS_TESTNET_NODE_URL)
## RawTransaction
raw_transaction = await rest_client.create_bcs_transaction(senderAccount, txPayload))
# Option 1: send as an unsigned byte array
shinami_rawTransaction = list(raw_transaction.to_bytes())
# Option 2: send as a hex string
transaction_serializer = Serializer()
raw_transaction.serialize(transaction_serializer)
shinami_rawTransaction = transaction_serializer.output().hex()
## sender's AccountAuthenticator
# Build a fee payer transaction and have the sender sign it
fee_payer_transaction = FeePayerRawTransaction(raw_transaction, [], None)
sender_authenticator = senderAccount.sign_transaction(fee_payer_transaction)
auth_serializer = Serializer()
sender_authenticator.serialize(auth_serializer)
# Option 1: send as an unsigned byte array
shinami_senderSignature = list(auth_serializer.to_bytes())
# Option 2: send as a hex string
shinami_senderSignature = auth_serializer.output().hex()
Example Response
{
hash: '0xc0f973af799eaa463a9a00f22b917e25c512e4e46ae765e9683030d1bad93436',
sender: '0xadf5e46c8096d4587032dbe35cb261134ad062d28a247da2c8a6b28097944415',
sequence_number: '7',
max_gas_amount: '200000',
gas_unit_price: '100',
expiration_timestamp_secs: '1717631961',
payload: {
function: '0xc13c3641ba3fc36e6a62f56e5a4b8a1f651dc5d9dc280bd349d5e4d0266d0817::message::set_message',
type_arguments: [],
arguments: [ 'hello' ],
type: 'entry_function_payload'
},
signature: {
sender: {
public_key: '0x8df7fae1ff10bb4135d700c1d787f4b8f1896001fcf78e19e059530c138f2f67',
signature: '0xbfd09c753b98f3b04798f8ee902cc08d9770be5951d23df5cc5323ef671894b7328d66b91a02b3561293fa3d6e200ff6f2f35969781f19bd2b595be7ab074304',
type: 'ed25519_signature'
},
secondary_signer_addresses: [],
secondary_signers: [],
fee_payer_address: '0x2cdcec66a48b596c923024102feda5ee759197fe844c5ba56013d3b9a99287b6',
fee_payer_signer: {
public_key: '0x57b9d494d31ed56f902362eef6e9d4fa8d22e390ab921bcb1678c8ec915ecbcf',
signature: '0x097a5c04894e874485f8d10b6092bd9cef3c4eede1d524d9b676cb06267d3dce4388742c38f03e8d65e2ead408ed32fdb735c9b50ad96cf0260fc2c5c1a2430e',
type: 'ed25519_signature'
},
type: 'fee_payer_signature'
}
}
{
"id" : 1,
"jsonrpc" : "2.0",
"result" : {
"pendingTransaction" : {
"expiration_timestamp_secs" : "1716589253",
"gas_unit_price" : "100",
"hash" : "0x7740a3baee841c049d79a5aee0021dc8a98f65d72854b94dbb79a8dfa4381b20",
"max_gas_amount" : "200000",
"payload" : {
"arguments" : [
"Test message"
],
"function" : "0xc13c3641ba3fc36e6a62f56e5a4b8a1f651dc5d9dc280bd349d5e4d0266d0817::message::set_message",
"type" : "entry_function_payload",
"type_arguments" : []
},
"sender" : "0xd0c8e3d35d0ff8912d74aab5ef42442b8425de5e4504639f7612bcf3c3876442",
"sequence_number" : "0",
"signature" : {
"fee_payer_address" : "0x11045a6f53a6e9064fd92b45038649c334fd1afd60848a090facab14e07cf70a",
"fee_payer_signer" : {
"public_key" : "0x7dfd1009d1dff08cd60e900c1eccda628808016e5885bc82a66f4d97ba959594",
"signature" : "0x96c29f29f4925b6083794c16d289b6168135a63f1c95ed0e5a273bd923aeaf0a6efc3c5ff2963fff83b72b8f3786ecb1f5b1439072086d1f58e3e88bd2b18e05",
"type" : "ed25519_signature"
},
"secondary_signer_addresses" : [],
"secondary_signers" : [],
"sender" : {
"public_key" : "0x6cf81b2b0adcd10c6d10edc3a85fa093dfa4eae890076528ea8edc4eed3b9a35",
"signature" : "0x9a331c9189b4ef008418e887ef0ee25382b2a1f74fa0c725b3e9432c2ea7fa37899f8e8490f202c521a52dda3509822c11de3c5b834d6111adf6d0bc397f9a02",
"type" : "single_key_signature"
},
"type" : "fee_payer_signature"
}
}
}
}
{
"id" : 1,
"jsonrpc" : "2.0",
"result" : {
"pendingTransaction" : {
"expiration_timestamp_secs" : "1716589999",
"gas_unit_price" : "100",
"hash" : "0x47e3b8632257f91611be18f999a776745f6542298a15c2ca6016c9cfc88a751b",
"max_gas_amount" : "200000",
"payload" : {
"arguments" : [],
"code" : {
"abi" : {
"generic_type_params" : [],
"is_entry" : true,
"is_view" : false,
"name" : "main",
"params" : [
"&signer",
"&signer"
],
"return" : [],
"visibility" : "public"
},
"bytecode" : "0xa11ceb0b060000000701000602060a03101104210405252a074f42089101200000000100020103040100010004080001050304010002060607000107080501000002020202060c060c020b000108010b0001080101080102060c03010b000109000001060c010502050b000109000a6170746f735f636f696e04636f696e067369676e657204436f696e094170746f73436f696e0877697468647261770a616464726573735f6f66076465706f7369740000000000000000000000000000000000000000000000000000000000000001000001110a0006640000000000000038000c020a0106c80000000000000038000c030b0111010b0238010b0011010b03380102"
},
"type" : "script_payload",
"type_arguments" : []
},
"sender" : "0x8a59591db8c95f7065cc1476c913e0015bea279cc8ba852d97f9269f181b3ecc",
"sequence_number" : "0",
"signature" : {
"fee_payer_address" : "0x221351e64436aa7b6732c791a3e66c6cb8386b2a2a27c6ab17a67f66aa449335",
"fee_payer_signer" : {
"public_key" : "0xdb3031396202fb5a21c5c3f100986a5ad9e9484c12bd5bae8e7c916dff4bf33a",
"signature" : "0xceb7b95aaa7d2557ff98824ea4fd98e124db2d6b87dcb75d1b9f5b33c3362a5a4c3fe8975c434edbfd2985acc9e9e4aadd3430b7a47dc668c522db2dd7116207",
"type" : "ed25519_signature"
},
"secondary_signer_addresses" : [
"0x5f4a07084ae99c7d208ad4d6dc0315f271b8e2b915b38ee241e3b26e18885fa2"
],
"secondary_signers" : [
{
"public_key" : "0x4faef99c8a1b3b1511348081f3b73fe59550121f6081bfaefa4f8a55e61533bd",
"signature" : "0xefb3618c7e31ea3073f61e0f9798353979cb5ed13a6f8d52f5bf3791e54d3c760d223ec0721557254f821f1d5fecab96b2ad085a82e27ae1995b939876f3260e",
"type" : "single_key_signature"
}
],
"sender" : {
"public_key" : "0x2775e36c43afe1cd6b49514aaee0377fe60d2a154c74774248b7248d15545558",
"signature" : "0x0c3c374f5a16084ca25c7f5f6105fe32e3b6acddbe7e4d369791c12fd7efd7838209e114c2d212bd31d7b3f38b5fa737145051659c18205ec3a32195aa02d406",
"type" : "single_key_signature"
},
"type" : "fee_payer_signature"
}
}
}
}
Response Fields
Type | Description |
---|---|
pendingTransactionResponse | The submitted transaction waiting in the Fullnode's mempool. Same as PendingTransactionResponse . |
gas_getFund
Description
Retrieve information about a Gas Station fund. Since each Gas Station API access key is tied to exactly one fund, we return information tied to the key you use to authenticate the request.
Request Parameters
None.
Example Request Template
The TypeScript example uses the Shinami Clients SDK.
Replace all instances of {{name}}
with the actual value for that name
import { GasStationClient } from "@shinami/clients/aptos";
const gas = new GasStationClient("{{gasStationAccessKey}}");
await gas.getFund();
curl https://api.shinami.com/aptos/gas/v1/ \
-X POST \
-H 'X-API-Key: {{gasStationAccessKey}}' \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc":"2.0",
"method":"gas_getFund",
"params":[],
"id":1
}'
Example Response
{
name: 'test_fund',
balance: 1030412796,
inFlight: 0,
network: 'APTOS_TESTNET'
}
{
"id" : 1,
"jsonrpc" : "2.0",
"result" : {
"balance" : 0,
"depositAddress" : null, // see comment in "Respone Fields" section below
"inFlight" : 0,
"name" : "Mainnet fund two",
"network" : "APTOS_MAINNET"
}
}
Response Fields
Name | Type | Description |
---|---|---|
name | string | The name of the fund tied to this API access key. |
network | string | The network the fund is associated with. |
balance | integer | Available balance of the the fund in Octas. |
inFlight | integer | The portion of the fund balance that is currently locked for use with active sponsorships. Shown in Octas. |
depositAddress | string | null | A string representing a Sui address if a deposit address has been created for the fund. null if no deposit address has been created. If you do not have a deposit address, follow the steps in our in our FAQ to generate one. |
gas_encodeAndSponsorTransaction
Description
Sponsors a JSON transaction request by providing fee payer info, and encodes it into message bytes for signing. Intended to be used only by clients without BCS encoding capability. If you're using the Aptos TypeScript SDK you should use sponsorTransaction instead.
The full transaction flow for using this API is as follows:
- Construct the transaction JSON.
- Call this API to get the encoded message bytes for signing, as well as fee payer address and signature.
- Sender (and any secondary signers) sign the message bytes.
- Submit the transaction JSON together with all signatures to Aptos fullnode for execution (an example of this call and response is show in the
cURL - submit sponsored tx example
tabs.
Access Key Requirements: In addition to Gas Station rights, making this request requires an access key with Node Service rights for the Aptos network you wish to submit the transaction on. See how to make one here.
Gas Station fund required: You need a Gas Station fund with APT in order to sponsorship transactions. For information on how to set this up, see the Gas Station section of our FAQ.
Shinami sponsorship fees: See our FAQ.
Request Parameters
Name | Type | Description |
---|---|---|
submission | object | EncodeSubmissionRequest , same as the request body on Aptos fullnode REST API /transactions/encode_submission (see documentation here).Note: the transaction's expiration timestamp must be set to a time within the next hour, in seconds. Otherwise, our service will not sponsor the transaction. |
Example Request Template
Replace all instances of {{name}}
with the actual value for that name
curl https://api.shinami.com/aptos/gas/v1/ \
-X POST \
-H 'X-API-Key: {{gasStationAccessKey}}' \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc":"2.0",
"method":"gas_encodeAndSponsorTransaction",
"params":[{
"sender": "{{senderAddress}}",
"sequence_number": "{{sequenceNumber}}",
"max_gas_amount": "{{maxGasAmount}}",
"gas_unit_price": "{{gasUnitPrice}}",
"expiration_timestamp_secs": "{{timestamp}}",
"payload": {
"type": "{{payloadType}}",
"function": "{{functionId}}",
"type_arguments": [
"{{typeArgs}}"
],
"arguments": [
{{arguments}}
]
},
"secondary_signers": [
{{secondarySignerAddresses}}
]
}],
"id":1
}'
curl https://api.shinami.com/aptos/gas/v1/ \
-X POST \
-H 'X-API-Key: {{gasStationAccessKey}}' \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc":"2.0",
"method":"gas_encodeAndSponsorTransaction",
"params":[{
"sender": "0xadf5e46c8096d4587032dbe35cb261134ad062d28a247da2c8a6b28097944415",
"sequence_number": "7",
"max_gas_amount": "200000",
"gas_unit_price": "100",
"expiration_timestamp_secs": "1717606333",
"payload": {
"type": "entry_function_payload",
"function": "0xc13c3641ba3fc36e6a62f56e5a4b8a1f651dc5d9dc280bd349d5e4d0266d0817::message::set_message",
"type_arguments": [],
"arguments": [
"hello"
]
},
"secondary_signers": []
}],
"id":1
}'
## This is a an example of the request to an Apto fullnode that you
## make after getting the response from Shinami's gas_encodeAndSponsorTransaction
## and then generating the sender sig on the transactionSigningMessage returned.
curl --request POST \
--url https://api.testnet.aptoslabs.com/v1/transactions \
--header 'Content-Type: application/json' \
-d '{
"sender": "0xadf5e46c8096d4587032dbe35cb261134ad062d28a247da2c8a6b28097944415",
"sequence_number": "9",
"max_gas_amount": "200000",
"gas_unit_price": "100",
"expiration_timestamp_secs": "1718671754",
"payload": {
"type": "entry_function_payload",
"function": "0xc13c3641ba3fc36e6a62f56e5a4b8a1f651dc5d9dc280bd349d5e4d0266d0817::message::set_message",
"type_arguments": [],
"arguments": [
"hello"
]
},
"signature": {
"type": "fee_payer_signature",
"sender": {
"type":"ed25519_signature",
"public_key":"0x8df7fae1ff10bb4135d700c1d787f4b8f1896001fcf78e19e059530c138f2f67",
"signature":"0x4b9bc91c2c6886c25a007e660bc7c457f851fe68a90896f5e3905f5ac2c256af918cf2afc3237d70ad24360ee9e86bdf053a5d7d85ea59d60bd387c3d67dd102"
},
"secondary_signer_addresses" : [],
"secondary_signers" : [],
"fee_payer_address": "0x11045a6f53a6e9064fd92b45038649c334fd1afd60848a090facab14e07cf70a",
"fee_payer_signer": {
"type":"ed25519_signature",
"public_key":"0x7dfd1009d1dff08cd60e900c1eccda628808016e5885bc82a66f4d97ba959594",
"signature":"0x743b061bf0cc3629df36962a7374682f026fad09d0a51a0138dcbc9b6f4cf748f853807b2a139a90466b9653685b136542a7cd0f8725038a2c060f0edfd96507"
}
}
}'
Example Response
{
"jsonrpc":"2.0",
"result":{
"transactionSigningMessage":[94,250,60,79,2,248,58,15,75,45,105,252,149,198,7,204,2,130,92,196,231,190,83,110,240,153,45,240,80,217,230,124,1,173,245,228,108,128,150,212,88,112,50,219,227,92,178,97,19,74,208,98,210,138,36,125,162,200,166,178,128,151,148,68,21,7,0,0,0,0,0,0,0,2,193,60,54,65,186,63,195,110,106,98,245,110,90,75,138,31,101,29,197,217,220,40,11,211,73,213,228,208,38,109,8,23,7,109,101,115,115,97,103,101,11,115,101,116,95,109,101,115,115,97,103,101,0,1,6,5,104,101,108,108,111,64,13,3,0,0,0,0,0,100,0,0,0,0,0,0,0,189,151,96,102,0,0,0,0,2,0,44,220,236,102,164,139,89,108,146,48,36,16,47,237,165,238,117,145,151,254,132,76,91,165,96,19,211,185,169,146,135,182],
"feePayerAddress":"0x2cdcec66a48b596c923024102feda5ee759197fe844c5ba56013d3b9a99287b6",
"feePayerSignature":{
"type":"ed25519_signature",
"public_key":"0x57b9d494d31ed56f902362eef6e9d4fa8d22e390ab921bcb1678c8ec915ecbcf",
"signature":"0x894063384997bfa3d29e42e01d9306297784a14b67baf102d501c6f3cfacd784c8dc7ae3f5abd8e3620b09011b09f9db044d25900217e9923c15f7d7876da000"
}
},
"id":1
}
{
"expiration_timestamp_secs" : "1718671754",
"gas_unit_price" : "100",
"hash" : "0xa37344e691317b9d034f0995ad693090c35fe46d69fc4c8b1821ff24373cea81",
"max_gas_amount" : "200000",
"payload" : {
"arguments" : [
"hello"
],
"function" : "0xc13c3641ba3fc36e6a62f56e5a4b8a1f651dc5d9dc280bd349d5e4d0266d0817::message::set_message",
"type" : "entry_function_payload",
"type_arguments" : []
},
"sender" : "0xadf5e46c8096d4587032dbe35cb261134ad062d28a247da2c8a6b28097944415",
"sequence_number" : "9",
"signature" : {
"fee_payer_address" : "0x11045a6f53a6e9064fd92b45038649c334fd1afd60848a090facab14e07cf70a",
"fee_payer_signer" : {
"public_key" : "0x7dfd1009d1dff08cd60e900c1eccda628808016e5885bc82a66f4d97ba959594",
"signature" : "0x743b061bf0cc3629df36962a7374682f026fad09d0a51a0138dcbc9b6f4cf748f853807b2a139a90466b9653685b136542a7cd0f8725038a2c060f0edfd96507",
"type" : "ed25519_signature"
},
"secondary_signer_addresses" : [],
"secondary_signers" : [],
"sender" : {
"public_key" : "0x8df7fae1ff10bb4135d700c1d787f4b8f1896001fcf78e19e059530c138f2f67",
"signature" : "0x4b9bc91c2c6886c25a007e660bc7c457f851fe68a90896f5e3905f5ac2c256af918cf2afc3237d70ad24360ee9e86bdf053a5d7d85ea59d60bd387c3d67dd102",
"type" : "ed25519_signature"
},
"type" : "fee_payer_signature"
}
}
Response Fields
Name | Type | Description |
---|---|---|
transactionSigningMessage | Unsigned byte array | Encoded message bytes for the transaction. To be signed by the sender and all secondary signers. |
feePayerAddress | Hex string | Fee payer account address. To be used to fill in signature.fee_payer_address property in the JSON transaction submission API. |
feePayerSignature | object | AccountSignature of the fee payer. To be used to fill in signature.fee_payer_signer property in the JSON transaction submission API. |
Appendix
How to build, sponsor, sign, and submit a transaction
This section shows an example of how to build, sponsor, sign, and submit a simple transaction using the Shinami Clients SDK and the Aptos TS SDK. For a high-level image of the sponsorship flow see here. For multiple end-to-end examples, see our Gas Station TypeScript Tutorial.
Key requirements
- Build your transaction with the fee payer address set to
0x0
(this is what happens when you setwithFeePayer: true
as shown below). The sender and any secondary signers can sign the transaction with this address, or with the actual feePayer's address you obtain from Shinami upon a successful sponsorship. - The transaction's expiration timestamp must be set to a time within the next hour (as is required by our Gas Station). When you control the generation of the sender's signature, as with an embedded wallet you control for the user, you can likely use the SDK default, which is 20 seconds from now. When you need to wait on a signature you don't control, as with a connected wallet where the user must approve a signing message, you can explicitly set it as in the example below.
Replace all instances of {{name}}
with the actual value for that name
// 1. Import the required dependencies
import { GasStationClient } from "@shinami/clients/aptos";
import { Account, Aptos, AptosConfig, Network } from "@aptos-labs/ts-sdk";
// 2. Create an Aptos client and generate an account to act as the sender
const aptos = new Aptos(new AptosConfig({ network: Network.TESTNET }));
// 3. Generarte a sender. In production, you won't be using a newly generated account
// each time. The sender will often be a wallet the user connected or an embedded
// wallet you control for the user. This just makes for a shorter example.
const sender = Account.generate({});
// 4. Create a Shinami Gas Station client for sponsoring our transactions.
const SHINAMI_TESTNET_GAS_KEY = "{{APTOS_TESTNET_GAS_STATION_ACCESS_KEY}}";
const gasStationClient = new GasStationClient(SHINAMI_TESTNET_GAS_KEY);
// 5. Build a transaction
// This transaction makes a function call to a module we've deployed on Testnet
const FIVE_MINUTES_FROM_NOW_IN_SECONDS = Math.floor(Date.now() / 1000) + (5 * 60);
const transaction = await aptos.transaction.build.simple({
sender: sender.accountAddress,
withFeePayer: true,
data: {
function: "0xc13c3641ba3fc36e6a62f56e5a4b8a1f651dc5d9dc280bd349d5e4d0266d0817::message::set_message",
functionArguments: ["test message"]
},
options: {
expireTimestamp: FIVE_MINUTES_FROM_NOW_IN_SECONDS
}
});
// 6. Obtain the sender's signature
const senderAuthenticator = aptos.transaction.sign({
signer: sender,
transaction
});
//
// OPTION 1: SPONSOR AND SUBMIT IN ONE REQUEST
//
// 7. Ask Shinami to sponsor and submit the transaction
const pendingTx = await gasStationClient.sponsorAndSubmitSignedTransaction(
transaction,
senderAuthenticator
);
//
// OPTION 2: SPONSOR AND SUBMIT IN TWO REQUESTS
//
// 7. Send the transaction to Shinami for sponsorship
const feePayerAuthenticator = await gasStationClient.sponsorTransaction(transaction);
// Note that our SDK updates the transaction's feePayer address on a successful sponsorship.
// The sender and any secondary signers can sign the transaction with the actual feePayer's
// address or with the special `0x0` feePayer address given when the feePayer transaction is
// built (which we do above). However, the transaction must be submitted with the actual
// feePayer's address. If you don't use our TS SDK, you must explictly set it with the
// `feePayer.address` value we return when you ask for a sponsorship.
console.log("transaction.feePayerAddress post-sponsorship:", transaction.feePayerAddress);
// 8. Submit the transaction to the Aptos blockchain
const pendingTx = await aptos.transaction.submit.simple({
transaction,
senderAuthenticator,
feePayerAuthenticator,
});