Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v4 fix: send usdc,usdt issues on sol OK-25951 #4212

Merged
merged 3 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 47 additions & 11 deletions packages/engine/src/vaults/impl/sol/Vault.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
getAssociatedTokenAddressSync,
} from '@solana/spl-token';
import {
ComputeBudgetProgram,
PublicKey,
SYSVAR_INSTRUCTIONS_PUBKEY,
SystemInstruction,
Expand Down Expand Up @@ -84,6 +85,7 @@ import {
import { ClientSol, PARAMS_ENCODINGS } from './sdk';
import settings from './settings';
import {
MIN_PRIORITY_FEE,
TOKEN_AUTH_RULES_ID,
TOKEN_METADATA_PROGRAM_ID,
masterEditionAddress,
Expand Down Expand Up @@ -121,6 +123,7 @@ import type {
} from '../../types';
import type {
AssociatedTokenInfo,
IEncodedTxSol,
INativeTxSol,
ParsedAccountInfo,
} from './types';
Expand Down Expand Up @@ -584,16 +587,16 @@ export default class Vault extends VaultBase {
let lastRpcErrorMessage = '';
const maxRetryTimes = 5;
const client = await this.getClient();
const accountAddress = await this.getAccountAddress();
const transferInfo = transferInfos[0];
const { from, to: firstReceiver, isNFT } = transferInfo;

const source = new PublicKey(from);
const nativeTx = new Transaction();

const doGetFee = async () => {
const doGetRecentBlockHash = async () => {
try {
const [, recentBlockhash] = await client.getFees();
return recentBlockhash;
return await client.getLatestBlockHash();
} catch (error: any) {
const rpcErrorData = error?.data as
| {
Expand All @@ -612,16 +615,30 @@ export default class Vault extends VaultBase {
retryTime += 1;
if (retryTime > maxRetryTimes) {
throw new Error(
`Solana getFees retry times exceeded: ${lastRpcErrorMessage || ''}`,
`Solana getLatestBlockHash retry times exceeded: ${
lastRpcErrorMessage || ''
}`,
);
}
const recentBlockhash = await doGetFee();
nativeTx.recentBlockhash = recentBlockhash;
const resp = await doGetRecentBlockHash();
nativeTx.recentBlockhash = resp?.blockhash;
nativeTx.lastValidBlockHeight = resp?.lastValidBlockHeight;
await wait(1000);
} while (!nativeTx.recentBlockhash);

nativeTx.feePayer = source;

// To make sure tx can be processed
const prioritizationFee = await client.getRecentMaxPrioritizationFees([
accountAddress,
]);

const addPriorityFee = ComputeBudgetProgram.setComputeUnitPrice({
microLamports: Math.max(MIN_PRIORITY_FEE, prioritizationFee),
});

nativeTx.add(addPriorityFee);

for (let i = 0; i < transferInfos.length; i += 1) {
const {
token: tokenAddress,
Expand Down Expand Up @@ -1237,13 +1254,28 @@ export default class Vault extends VaultBase {
return client.getFeePricePerUnit();
}

override async fetchFeeInfo(encodedTx: IEncodedTx): Promise<IFeeInfo> {
const [network, { prices }, nativeTx] = await Promise.all([
override async fetchFeeInfo(encodedTx: IEncodedTxSol): Promise<IFeeInfo> {
const client = await this.getClient();
const nativeTx = await this.helper.parseToNativeTx(encodedTx);
const isVersionedTransaction = nativeTx instanceof VersionedTransaction;
let message = '';
if (isVersionedTransaction) {
message = Buffer.from(nativeTx.message.serialize()).toString('base64');
} else {
message = (nativeTx as Transaction)
.compileMessage()
.serialize()
.toString('base64');
}
const [network, feePerSig] = await Promise.all([
this.getNetwork(),
this.engine.getGasInfo(this.networkId),
this.helper.parseToNativeTx(encodedTx),
client.getFeesForMessage(message),
]);

const prices = [
new BigNumber(feePerSig).shiftedBy(-network.feeDecimals).toFixed(),
];

return {
nativeSymbol: network.symbol,
nativeDecimals: network.decimals,
Expand Down Expand Up @@ -1486,7 +1518,11 @@ export default class Vault extends VaultBase {
transaction,
);
const client = await this.getClient();
[, nativeTx.recentBlockhash] = await client.getFees();
const { blockhash, lastValidBlockHeight } =
await client.getLatestBlockHash();

nativeTx.recentBlockhash = blockhash;
nativeTx.lastValidBlockHeight = lastValidBlockHeight;

return bs58.encode(nativeTx.serialize({ requireAllSignatures: false }));
}
Expand Down
45 changes: 45 additions & 0 deletions packages/engine/src/vaults/impl/sol/sdk/ClientSol.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// eslint-disable-next-line @typescript-eslint/naming-convention
import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
import BigNumber from 'bignumber.js';
import { map, max } from 'lodash';

import { BaseClient } from '@onekeyhq/engine/src/client/BaseClient';
import { JsonRPCRequest } from '@onekeyhq/shared/src/request/JsonRPCRequest';
Expand All @@ -24,8 +25,11 @@ export enum RPC_METHODS {
GET_ACCOUNT_INFO = 'getAccountInfo',
GET_TOKEN_ACCOUNTS_BY_OWNER = 'getTokenAccountsByOwner',
GET_FEES = 'getFees',
GET_FEES_FOR_MESSAGE = 'getFeeForMessage',
GET_RECENT_PRIORITIZATION_FEES = 'getRecentPrioritizationFees',
GET_TRANSACTION = 'getTransaction',
GET_MINIMUM_BALANCE_FOR_RENT_EXEMPTION = 'getMinimumBalanceForRentExemption',
GET_LATEST_BLOCK_HASH = 'getLatestBlockhash',
}
// eslint-disable-next-line @typescript-eslint/naming-convention
export enum PARAMS_ENCODINGS {
Expand Down Expand Up @@ -214,6 +218,47 @@ export class ClientSol extends BaseClient {
return [feePerSig, recentBlockhash];
}

async getFeesForMessage(message: string) {
const resp = await this.rpc.call<{
value: number;
}>(RPC_METHODS.GET_FEES_FOR_MESSAGE, [
message,
{
commitment: 'confirmed',
},
]);

return resp.value;
}

async getRecentPrioritizationFees(accountAddresses: string[]) {
const resp = await this.rpc.call<
{ slot: number; prioritizationFee: number }[]
>(RPC_METHODS.GET_RECENT_PRIORITIZATION_FEES, [accountAddresses]);

return resp;
}

async getRecentMaxPrioritizationFees(accountAddress: string[]) {
const resp = await this.getRecentPrioritizationFees(accountAddress);
return max(map(resp, 'prioritizationFee')) || 0;
}

async getLatestBlockHash() {
const resp = await this.rpc.call<{
value: { blockhash: string; lastValidBlockHeight: number };
}>(RPC_METHODS.GET_LATEST_BLOCK_HASH, [
{
commitment: 'confirmed',
},
]);

return {
blockhash: resp.value.blockhash,
lastValidBlockHeight: resp.value.lastValidBlockHeight,
};
}

async getTransactionStatuses(
txids: string[],
): Promise<Array<TransactionStatus | undefined>> {
Expand Down
2 changes: 2 additions & 0 deletions packages/engine/src/vaults/impl/sol/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export const TOKEN_AUTH_RULES_ID = new PublicKey(
'auth9SigNpDKz4sJJ1DfCTuZrZNSAgh9sFD3rboVmgg',
);

export const MIN_PRIORITY_FEE = 1000;

export function metadataAddress(mint: PublicKey): PublicKey {
return PublicKey.findProgramAddressSync(
[
Expand Down
Loading