Gasless Stablecoin Transfers
Gasless stablecoin transfers are currently only available on Testnet. They will be available on Mainnet in the future.
Chain-specific gas fees can introduce several points of friction to end users and complicate institutional compliance for asset trading or payments. Eliminating chain-specific gas is a major simplification for regulated institutions to conduct payments without the burden of holding digital assets that might fluctuate in value.
Gasless stablecoin transfers on Sui enable peer-to-peer payments of qualified stablecoins to execute without paying gas fees in SUI. The sender of the transaction does not need to hold SUI in their wallet. When the network is congested, the network prioritizes transactions that pay gas fees over gasless stablecoin transfers.
Eligible stablecoins
Stablecoins qualified for gasless stablecoin transfers can be found here.
What gasless stablecoin transfers don't cover
Gasless stablecoin transfers are narrow by design. A transaction qualifies only when:
- The PTB consists of allowlisted
balanceorcoinoperations on an allowlisted stablecoin type. The primary one is0x2::balance::send_funds<T>. A few related helpers (balance::redeem_funds,coin::send_funds,coin::into_balance, and so on) are also allowed and might appear in resolved transactions. - Gas is paid from the sender's address balance.
gasPaymentis empty andgasPrice = 0. - No objects are written during the transaction, and all input coins are consumed or converted to address balances.
If the user is sending SUI, sending a non-allowlisted token, or executing any other Move call (NFT mint, swap, app interaction), the gasless stablecoin transfer does not apply and the transaction requires a normal gas payment.
Supporting gasless stablecoin transfers
Sui gasless stablecoin transfers are an extension of the Address Balances feature, enabled by calling the Address Balances Move API send_funds() with gas=0 and gas_budget=0.
The TypeScript SDK automatically detects gasless stablecoin transfers when using the gRPC or GraphQL transports. Learn more in the TypeScript SDK documentation.
Gasless stablecoin transfers are limited to the Move functions withdrawal_split, redeem_funds, and send_funds. Gasless stablecoin transfers cannot be used to write any object.
Integrating gasless stablecoins into wallets
To integrate gasless stablecoins into wallets, wallet builders need to detect eligible transfers and construct them with @mysten/sui. To do this, build a PTB that calls 0x2::balance::send_funds<T> with a Balance<T> from tx.balance(), then submit it through SuiGrpcClient. The SDK detects eligibility during simulate, sets gasPrice = 0 and gasBudget = 0, and pays gas from the sender's address balance.
import { SuiGrpcClient } from '@mysten/sui/grpc';
import { Transaction } from '@mysten/sui/transactions';
const USDC = '0xa1ec7fc00a6f40db9693ad1415d0c193ad3906494428cf252621037bd7117e29::usdc::USDC';
const client = new SuiGrpcClient({
network: 'testnet',
baseUrl: 'https://fullnode.testnet.sui.io:443',
});
const tx = new Transaction();
tx.setSender(senderAddress);
tx.moveCall({
target: '0x2::balance::send_funds',
typeArguments: [USDC],
arguments: [
tx.balance({ type: USDC, balance: 1_000_000n }), // 1 USDC
tx.pure.address(recipient),
],
});
const result = await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
});
If your wallet builds transactions with gRPC but executes through a different transport, pass the gRPC client to build so the gas price still gets set correctly:
const bytes = await tx.build({ client: grpcClient });
SuiGraphQLClient works the same way as gRPC for this flow.
JSON-RPC fallback
If you must use SuiJsonRpcClient, the SDK cannot detect eligibility for you. Set the price and payment yourself, but only after confirming the coin type is on the allowlist:
const tx = new Transaction();
tx.setSender(senderAddress);
// Manually set gas price to 0. Only do this when you know the coin type is on the allow list
tx.setGasPrice(0);
tx.moveCall({
target: '0x2::balance::send_funds',
typeArguments: [USDC],
arguments: [tx.balance({ type: USDC, balance: 1_000_000n }), tx.pure.address(recipient)],
});
If the coin type isn't allowlisted or the PTB shape isn't eligible, the transaction fails at validation.