import {
route,
executeMultipleRoutes,
balances,
assets,
getRouteWithGasOnReceive
} from "@skip-go/client";
class GasOnReceiveManager {
private readonly GAS_AMOUNTS_USD = {
cosmos: 0.10,
evm_l2: 2.00
};
async shouldEnableGasOnReceive(
destinationChainId: string,
destinationAddress: string,
destinationAssetDenom: string
): Promise<boolean> {
// Check if chain is supported
if (!this.isChainSupported(destinationChainId)) {
return false;
}
// Don't enable if destination asset is already a gas token
if (await this.isGasToken(destinationChainId, destinationAssetDenom)) {
return false;
}
// Check user's gas balance
const hasGas = await this.checkGasBalance(destinationChainId, destinationAddress);
return !hasGas;
}
private isChainSupported(chainId: string): boolean {
// Solana chains not supported for destination
const unsupportedDestChains = ["solana", "solana-devnet"];
// Ethereum mainnet and Sepolia not supported as source
const unsupportedSourceChains = ["1", "11155111"];
// For this example, checking destination support
return !unsupportedDestChains.includes(chainId);
}
private async isGasToken(chainId: string, denom: string): boolean {
const chainAssets = await assets({ chainId });
const gasTokens = chainAssets.chain?.feeAssets || [];
return gasTokens.some(token => token.denom === denom);
}
private async checkGasBalance(
chainId: string,
address: string
): Promise<boolean> {
const balanceResponse = await balances({
chains: { [chainId]: { address } }
});
const nativeDenom = await this.getNativeDenom(chainId);
const balance = balanceResponse?.chains?.[chainId]?.denoms?.[nativeDenom];
// Check if user has any balance
return balance?.amount && balance.amount !== "0";
}
async executeSwapWithGasOnReceive(
params: {
amountIn: string;
sourceAsset: { chainId: string; denom: string };
destAsset: { chainId: string; denom: string };
userAddresses: Array<{ chainId: string; address: string }>;
enableGasOnReceive: boolean;
signers: any;
}
) {
const {
amountIn,
sourceAsset,
destAsset,
userAddresses,
enableGasOnReceive,
signers
} = params;
// Get initial route
const originalRoute = await route({
amountIn,
sourceAssetChainId: sourceAsset.chainId,
sourceAssetDenom: sourceAsset.denom,
destAssetChainId: destAsset.chainId,
destAssetDenom: destAsset.denom
});
if (enableGasOnReceive) {
// Use automatic splitting
const { mainRoute, gasRoute } = await getRouteWithGasOnReceive({
routeResponse: originalRoute,
routeRequest: {
amountIn,
sourceAssetChainId: sourceAsset.chainId,
sourceAssetDenom: sourceAsset.denom,
destAssetChainId: destAsset.chainId,
destAssetDenom: destAsset.denom
}
});
if (gasRoute) {
// Execute both routes
await executeMultipleRoutes({
route: { mainRoute, feeRoute: gasRoute },
userAddresses: {
mainRoute: userAddresses,
feeRoute: userAddresses
},
slippageTolerancePercent: {
mainRoute: "1",
feeRoute: "10" // Higher tolerance for gas route
},
...signers,
onRouteStatusUpdated: this.handleRouteStatus
});
} else {
// Execute just the main route
await executeRoute({
route: mainRoute,
userAddresses,
slippageTolerancePercent: "1",
...signers
});
}
} else {
// Execute original route without gas
await executeRoute({
route: originalRoute,
userAddresses,
slippageTolerancePercent: "1",
...signers
});
}
}
private handleRouteStatus(status: RouteStatus) {
if (status.status === "completed") {
console.log("Swap completed successfully");
}
// Check gas route status
const gasRoute = status.relatedRoutes?.find(r => r.routeKey === "feeRoute");
if (gasRoute?.status === "failed") {
console.warn("Gas provision failed, but main swap continues");
} else if (gasRoute?.status === "completed") {
console.log("Gas tokens received successfully");
}
}
}