Abstract gas fees for EOA transactions
curl --request POST \
--url https://grid.squads.xyz/api/v0/grid/transactions/eoa/gas-abstraction \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'X-Grid-Environment: <x-grid-environment>' \
--data '
{
"transaction": "<string>"
}
'import requests
url = "https://grid.squads.xyz/api/v0/grid/transactions/eoa/gas-abstraction"
payload = { "transaction": "<string>" }
headers = {
"X-Grid-Environment": "<x-grid-environment>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'X-Grid-Environment': '<x-grid-environment>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({transaction: '<string>'})
};
fetch('https://grid.squads.xyz/api/v0/grid/transactions/eoa/gas-abstraction', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://grid.squads.xyz/api/v0/grid/transactions/eoa/gas-abstraction",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'transaction' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"X-Grid-Environment: <x-grid-environment>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://grid.squads.xyz/api/v0/grid/transactions/eoa/gas-abstraction"
payload := strings.NewReader("{\n \"transaction\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Grid-Environment", "<x-grid-environment>")
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://grid.squads.xyz/api/v0/grid/transactions/eoa/gas-abstraction")
.header("X-Grid-Environment", "<x-grid-environment>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"transaction\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://grid.squads.xyz/api/v0/grid/transactions/eoa/gas-abstraction")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Grid-Environment"] = '<x-grid-environment>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"transaction\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"transaction": "<string>",
"fee": {
"amount": "<string>",
"amount_decimal": "<string>",
"currency": "usd",
"sol_equivalent": {
"amount": "<string>",
"amount_decimal": "<string>"
}
}
}{
"details": [
{
"code": "<string>",
"field": "<string>",
"message": "<string>"
}
],
"message": "<string>",
"timestamp": "<string>"
}{
"details": [
{
"code": "<string>",
"field": "<string>",
"message": "<string>"
}
],
"message": "<string>",
"timestamp": "<string>"
}{
"details": [
{
"code": "<string>",
"field": "<string>",
"message": "<string>"
}
],
"message": "<string>",
"timestamp": "<string>"
}{
"details": [
{
"code": "<string>",
"field": "<string>",
"message": "<string>"
}
],
"message": "<string>",
"timestamp": "<string>"
}API Reference
Abstract Transaction Fees
Sponsor gas fees for standard Solana wallet transactions using Grid paymaster
POST
/
transactions
/
eoa
/
gas-abstraction
Abstract gas fees for EOA transactions
curl --request POST \
--url https://grid.squads.xyz/api/v0/grid/transactions/eoa/gas-abstraction \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'X-Grid-Environment: <x-grid-environment>' \
--data '
{
"transaction": "<string>"
}
'import requests
url = "https://grid.squads.xyz/api/v0/grid/transactions/eoa/gas-abstraction"
payload = { "transaction": "<string>" }
headers = {
"X-Grid-Environment": "<x-grid-environment>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'X-Grid-Environment': '<x-grid-environment>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({transaction: '<string>'})
};
fetch('https://grid.squads.xyz/api/v0/grid/transactions/eoa/gas-abstraction', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://grid.squads.xyz/api/v0/grid/transactions/eoa/gas-abstraction",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'transaction' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"X-Grid-Environment: <x-grid-environment>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://grid.squads.xyz/api/v0/grid/transactions/eoa/gas-abstraction"
payload := strings.NewReader("{\n \"transaction\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Grid-Environment", "<x-grid-environment>")
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://grid.squads.xyz/api/v0/grid/transactions/eoa/gas-abstraction")
.header("X-Grid-Environment", "<x-grid-environment>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"transaction\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://grid.squads.xyz/api/v0/grid/transactions/eoa/gas-abstraction")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Grid-Environment"] = '<x-grid-environment>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"transaction\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"transaction": "<string>",
"fee": {
"amount": "<string>",
"amount_decimal": "<string>",
"currency": "usd",
"sol_equivalent": {
"amount": "<string>",
"amount_decimal": "<string>"
}
}
}{
"details": [
{
"code": "<string>",
"field": "<string>",
"message": "<string>"
}
],
"message": "<string>",
"timestamp": "<string>"
}{
"details": [
{
"code": "<string>",
"field": "<string>",
"message": "<string>"
}
],
"message": "<string>",
"timestamp": "<string>"
}{
"details": [
{
"code": "<string>",
"field": "<string>",
"message": "<string>"
}
],
"message": "<string>",
"timestamp": "<string>"
}{
"details": [
{
"code": "<string>",
"field": "<string>",
"message": "<string>"
}
],
"message": "<string>",
"timestamp": "<string>"
}The Gas Abstraction endpoint enables fee sponsorship for standard Solana wallet transactions through the Grid paymaster system. This service removes the requirement for users to hold SOL tokens for transaction fees, creating a seamless user experience.
Custom identifiers enable you to:
Solution: Verify your transaction is properly serialized and base64-encoded.
Solution: Ensure
Solution: Implement retry logic with exponential backoff.
This endpoint is exclusively for standard Solana wallets (keypair-based
accounts). For Squads smart accounts with built-in gas abstraction, see Grid
v1 Cost Efficiency.
How It Works
The gas abstraction process follows these steps:- Transaction Submission: Send your unsigned, base64-encoded transaction to the endpoint
- Fee Calculation: The paymaster computes the required fees in your specified currency (defaults to USDC)
- Paymaster Signing: The paymaster adds its signature to sponsor the fees
- User Signing: Add the user’s signature to the paymaster-signed transaction
- Blockchain Submission: Submit the fully signed transaction to Solana
Implementation Guide
1
Prepare Your Unsigned Transaction
Create your versioned transaction without signing it:
import {
VersionedTransaction,
TransactionMessage,
SystemProgram,
PublicKey,
Connection,
LAMPORTS_PER_SOL,
} from "@solana/web3.js";
const connection = new Connection("https://api.mainnet-beta.solana.com");
// Create transaction instructions (example: SOL transfer)
const transferInstruction = SystemProgram.transfer({
fromPubkey: senderPublicKey,
toPubkey: recipientPublicKey,
lamports: 0.1 * LAMPORTS_PER_SOL,
});
// Get recent blockhash and build transaction message
const { blockhash } = await connection.getLatestBlockhash();
const messageV0 = new TransactionMessage({
payerKey: senderPublicKey,
recentBlockhash: blockhash,
instructions: [transferInstruction],
}).compileToV0Message();
// Create versioned transaction (unsigned)
const transaction = new VersionedTransaction(messageV0);
// Serialize to base64
const serializedTransaction = Buffer.from(
transaction.serialize()
).toString("base64");
2
Submit to Gas Abstraction API
Send your unsigned transaction to the endpoint:
const response = await fetch(
"https://grid.squads.xyz/api/v0/grid/transactions/eoa/gas-abstraction",
{
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"X-Grid-Environment": "production", // or 'sandbox' for devnet
"Content-Type": "application/json",
},
body: JSON.stringify({
transaction: serializedTransaction,
}),
}
);
const result = await response.json();
// Review fee information
console.log(`Fee: ${result.fee.amount_decimal} ${result.fee.currency}`);
console.log(
`SOL equivalent: ${result.fee.sol_equivalent.amount_decimal} SOL`
);
3
Sign and Submit to Blockchain
Deserialize the paymaster-signed transaction, add the user’s signature, and submit:
import { VersionedTransaction } from "@solana/web3.js";
// Decode the paymaster-signed transaction
const signedTransactionBuffer = Buffer.from(result.transaction, "base64");
const transaction = VersionedTransaction.deserialize(
signedTransactionBuffer
);
// Add user's signature
transaction.sign([userKeypair]);
// Submit to blockchain
const signature = await connection.sendTransaction(transaction);
console.log("Transaction signature:", signature);
// Confirm transaction
const confirmation = await connection.confirmTransaction(signature);
console.log(
"Status:",
confirmation.value.err ? "Failed" : "Confirmed"
);
Fee Structure
Fee Calculation
The total fee consists of:- Compute Units: Based on transaction complexity and computational requirements
- Rent Fees: For account creation or rent-exempt balance requirements
- Currency Conversion: Fees quoted in your specified currency (default: USDC)
Billing Details
- Sandbox Environment: Devnet transactions are processed but not billed
- Production Environment: Mainnet transactions are recorded and billed according to your Grid API agreement
- Fee Components: Transaction, priority, and rent fees are included in billing
Custom Identifiers
Use the optionalcustomIdentifier query parameter to group and track related transactions:
const response = await fetch(
"https://grid.squads.xyz/api/v0/grid/transactions/eoa/gas-abstraction?customIdentifier=user-123-session-abc",
{
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"X-Grid-Environment": "production",
"Content-Type": "application/json",
},
body: JSON.stringify({
transaction: serializedTransaction,
}),
}
);
- Group transactions by user, session, or feature
- Query fee history by identifier
- Track usage patterns and costs per category
Transaction Validation
Transactions must:- Be valid Solana transactions that can be simulated successfully
- Use versioned transaction format
- Be properly serialized to base64
- Not be pre-signed
Error Handling
Common error scenarios:Invalid Transaction Format
{
"code": "INVALID_TRANSACTION",
"message": "tried to parse invalid transaction"
}
Invalid Environment Header
{
"code": "INVALID_ENVIRONMENT",
"message": "Environment must be either 'sandbox' or 'production'"
}
X-Grid-Environment header is set to either sandbox or production.
Service Unavailable
{
"code": "SERVICE_UNAVAILABLE",
"message": "The service is temporarily unavailable. Please try again."
}
Security Best Practices
- API Key Protection: Never expose your Grid API key in client-side code or public repositories
- Environment Separation: Use sandbox for testing, production only for live transactions
- Transaction Verification: Always validate the returned signed transaction matches your intent
- Fee Monitoring: Implement alerts for unexpected fee spikes
- Rate Limiting: Implement appropriate rate limiting to avoid overwhelming the API
Authorizations
API Key for authentication
Headers
The environment you're using. Can be sandbox or production.
Query Parameters
Optional custom identifier for tracking purposes
Body
application/json
Base64-encoded transaction to process for fee abstraction
Was this page helpful?