|
1 | 1 | import assert from "assert";
|
2 |
| -import { getComputeUnitEstimateForTransactionMessageFactory, TransactionSigner } from "@solana/kit"; |
3 |
| -import { SVMProvider, SolanaVoidSigner, getFillRelayTx } from "../../arch/svm"; |
| 2 | +import { |
| 3 | + getComputeUnitEstimateForTransactionMessageFactory, |
| 4 | + TransactionSigner, |
| 5 | + fetchEncodedAccount, |
| 6 | + isSome, |
| 7 | +} from "@solana/kit"; |
| 8 | +import { SVMProvider, SolanaVoidSigner, getFillRelayTx, toAddress, getAssociatedTokenAddress } from "../../arch/svm"; |
4 | 9 | import { Coingecko } from "../../coingecko";
|
5 | 10 | import { CHAIN_IDs } from "../../constants";
|
6 | 11 | import { getGasPriceEstimate } from "../../gasPriceOracle";
|
7 | 12 | import { RelayData } from "../../interfaces";
|
8 | 13 | import { Address, BigNumber, BigNumberish, SvmAddress, TransactionCostEstimate, toBN } from "../../utils";
|
9 | 14 | import { Logger, QueryInterface, getDefaultRelayer } from "../relayFeeCalculator";
|
10 | 15 | import { SymbolMappingType } from "./";
|
| 16 | +import { TOKEN_PROGRAM_ADDRESS } from "@solana-program/token"; |
| 17 | +import { TOKEN_2022_PROGRAM_ADDRESS, getTokenSize, fetchMint, Extension } from "@solana-program/token-2022"; |
11 | 18 |
|
12 | 19 | /**
|
13 | 20 | * A special QueryBase implementation for SVM used for querying gas costs, token prices, and decimals of various tokens
|
@@ -76,24 +83,49 @@ export class SvmQuery implements QueryInterface {
|
76 | 83 | repaymentAddress
|
77 | 84 | );
|
78 | 85 |
|
79 |
| - const [computeUnitsConsumed, gasPriceEstimate] = await Promise.all([ |
| 86 | + const [computeUnitsConsumed, gasPriceEstimate, tokenAccountInfo] = await Promise.all([ |
80 | 87 | toBN(await this.computeUnitEstimator(fillRelayTx)),
|
81 | 88 | getGasPriceEstimate(this.provider, {
|
82 | 89 | unsignedTx: fillRelayTx,
|
83 | 90 | baseFeeMultiplier: options.baseFeeMultiplier,
|
84 | 91 | priorityFeeMultiplier: options.priorityFeeMultiplier,
|
85 | 92 | }),
|
| 93 | + this.provider.getAccountInfo(toAddress(outputToken), { encoding: "base58" }).send(), |
86 | 94 | ]);
|
87 | 95 |
|
| 96 | + // If the owner of the token account is not the token program, then we can assume that it is the 2022 token program address, in which |
| 97 | + // case we need to determine the extensions the token has to properly calculate rent exemption. |
| 98 | + const tokenOwner = tokenAccountInfo!.value!.owner; |
| 99 | + assert( |
| 100 | + tokenOwner === TOKEN_2022_PROGRAM_ADDRESS || tokenOwner === TOKEN_PROGRAM_ADDRESS, |
| 101 | + `${outputToken} has invalid token account owner ${tokenOwner}.` |
| 102 | + ); |
| 103 | + const recipientAta = await getAssociatedTokenAddress(recipient, outputToken, tokenOwner); |
| 104 | + const encodedAta = await fetchEncodedAccount(this.provider, recipientAta); |
| 105 | + |
88 | 106 | // We can cast the gas price estimate to an SvmGasPriceEstimate here since the oracle should always
|
89 | 107 | // query the Solana adapter.
|
90 | 108 | const gasPrice = gasPriceEstimate.baseFee.add(
|
91 | 109 | gasPriceEstimate.microLamportsPerComputeUnit.mul(computeUnitsConsumed).div(toBN(1_000_000)) // 1_000_000 microLamports/lamport.
|
92 | 110 | );
|
| 111 | + let tokenGasCost = gasPrice; |
| 112 | + |
| 113 | + // If the ATA does not exist, we need to factor the rent amount into the token gas cost. |
| 114 | + if (!encodedAta.exists) { |
| 115 | + // If the ATA is a non-2022 token, then it will always have a fixed size of 165. |
| 116 | + let extensions: Extension[] | undefined = undefined; |
| 117 | + if (tokenOwner === TOKEN_2022_PROGRAM_ADDRESS) { |
| 118 | + const mint = await fetchMint(this.provider, toAddress(outputToken)); |
| 119 | + extensions = isSome(mint.data.extensions) ? mint.data.extensions.value : undefined; |
| 120 | + } |
| 121 | + const tokenAccountSize = getTokenSize(extensions); |
| 122 | + const rentCostInLamports = await this.provider.getMinimumBalanceForRentExemption(BigInt(tokenAccountSize)).send(); |
| 123 | + tokenGasCost = tokenGasCost.add(toBN(Number(rentCostInLamports))); |
| 124 | + } |
93 | 125 |
|
94 | 126 | return {
|
95 | 127 | nativeGasCost: computeUnitsConsumed,
|
96 |
| - tokenGasCost: gasPrice, |
| 128 | + tokenGasCost, |
97 | 129 | gasPrice,
|
98 | 130 | };
|
99 | 131 | }
|
|
0 commit comments