@swapper-finance/sdk
Version:
JavaScript SDK form Swapper
150 lines (132 loc) • 4.91 kB
text/typescript
import { BigNumber, ethers } from "ethers";
import { logger } from "./logger";
/**
* Calculate the gas cashback from a transaction - how much USDC was saved from the estimated cost
* @param receipt The transaction receipt
* @param estimatedGasCost The estimated gas cost from the route
* @param tokenDecimals The decimals of the cash token
* @param smartWalletAddress The user's smart wallet address
* @param knownAddresses The address not to be included in the cashback calculation
* @returns Information about the gas cashback amount
*/
interface GasCashbackResult {
estimatedGasCost: BigNumber;
actualGasCost: BigNumber;
cashbackAmount: BigNumber;
}
export const calculateGasCashback = async (
receipt: ethers.providers.TransactionReceipt,
estimatedGasCost: BigNumber,
tokenAddress: `0x${string}`,
tokenDecimals: number = 6,
smartWalletAddress: string,
knownAddresses?: string[],
): Promise<GasCashbackResult> => {
try {
// Normalize addresses
const normalizedSmartWallet = smartWalletAddress.toLowerCase();
const normalizedKnownAddresses =
knownAddresses?.map((address) => address.toLowerCase()) || [];
// Find all USDC transfers FROM the smart wallet
const walletTransfers = receipt.logs.filter((log) => {
try {
// Filter for ERC20 Transfer events
const topics = log.topics || [];
// Check if this is a Transfer event
const isTransferEvent =
topics[0] === ethers.utils.id("Transfer(address,address,uint256)");
// Check if transfer is for USDC token
const isUsdcToken =
log.address.toLowerCase() === tokenAddress.toLowerCase();
// Check if sender is the smart wallet (topic[1] contains the sender address)
let isFromSmartWallet = false;
if (topics.length >= 2 && topics[1]) {
// Decode the sender address from the event (first topic after event signature)
const sender = ethers.utils
.getAddress("0x" + topics[1].substring(26))
.toLowerCase();
isFromSmartWallet = sender === normalizedSmartWallet;
}
// Check if recipient is NOT the swap target
let isNotToSwapTarget = true;
if (
normalizedKnownAddresses.length > 0 &&
topics.length >= 3 &&
topics[2]
) {
// Decode the recipient address from the event
const recipient = ethers.utils
.getAddress("0x" + topics[2].substring(26))
.toLowerCase();
isNotToSwapTarget = !normalizedKnownAddresses.includes(recipient);
}
return (
isTransferEvent &&
isUsdcToken &&
isFromSmartWallet &&
isNotToSwapTarget
);
} catch (e) {
console.error("Error parsing transfer event:", e);
return false;
}
});
// Get the total USDC spent on gas (all transfers from smart wallet excluding to swap target)
let actualGasCost = BigNumber.from(0);
if (walletTransfers.length > 0) {
// Sum all USDC transfers from the wallet
actualGasCost = walletTransfers.reduce((total, transferLog) => {
try {
const transferData = ethers.utils.defaultAbiCoder.decode(
["uint256"],
transferLog.data,
);
return total.add(transferData[0]);
} catch (e) {
console.error("Error decoding transfer amount:", e);
return total;
}
}, BigNumber.from(0));
}
// Calculate the cashback amount (how much USDC was saved)
const cashbackAmount = estimatedGasCost.sub(actualGasCost);
// Format for logging
const formattedEstimate = ethers.utils.formatUnits(
estimatedGasCost,
tokenDecimals,
);
const formattedActual = ethers.utils.formatUnits(
actualGasCost,
tokenDecimals,
);
const formattedCashback = ethers.utils.formatUnits(
cashbackAmount.lt(0) ? BigNumber.from(0) : cashbackAmount,
tokenDecimals,
);
// Log the results
logger.log(`Gas cost estimate: ${formattedEstimate} USDC`);
logger.log(`Actual gas cost: ${formattedActual} USDC`);
logger.log(`Gas cashback: ${formattedCashback} USDC`);
if (walletTransfers.length === 0) {
logger.warn(
`No USDC transfers from smart wallet (${normalizedSmartWallet}) found in transaction`,
);
} else {
logger.log(
`Found ${walletTransfers.length} USDC transfers from smart wallet for gas payments`,
);
}
return {
estimatedGasCost,
actualGasCost,
cashbackAmount: cashbackAmount.lt(0) ? BigNumber.from(0) : cashbackAmount,
};
} catch (error) {
console.error("Failed to calculate gas cashback:", error);
return {
estimatedGasCost,
actualGasCost: BigNumber.from(0),
cashbackAmount: BigNumber.from(0),
};
}
};