Skip to content

Commit 2b6a07e

Browse files
Merge pull request #5025 from BitGo/COIN-1879
fix: replace getFees rpc call with getFeeForMessage
2 parents bd67abf + b59e4a6 commit 2b6a07e

File tree

3 files changed

+88
-36
lines changed

3 files changed

+88
-36
lines changed

modules/sdk-coin-sol/src/sol.ts

Lines changed: 56 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -553,20 +553,25 @@ export class Sol extends BaseCoin {
553553
return response.body.result.value.blockhash;
554554
}
555555

556-
/** TODO Update to getFeeForMessage and make necssary changes in fee calculation, GetFees is deprecated */
557-
protected async getFees(): Promise<number> {
556+
protected async getFeeForMessage(message: string): Promise<number> {
558557
const response = await this.getDataFromNode({
559558
payload: {
560559
id: '1',
561560
jsonrpc: '2.0',
562-
method: 'getFees',
561+
method: 'getFeeForMessage',
562+
params: [
563+
message,
564+
{
565+
commitment: 'finalized',
566+
},
567+
],
563568
},
564569
});
565570
if (response.status !== 200) {
566571
throw new Error('Account not found');
567572
}
568573

569-
return response.body.result.value.feeCalculator.lamportsPerSignature;
574+
return response.body.result.value;
570575
}
571576

572577
protected async getRentExemptAmount(): Promise<number> {
@@ -758,19 +763,13 @@ export class Sol extends BaseCoin {
758763
// Build the transaction
759764
const MPC = await EDDSAMethods.getInitializedMpcInstance();
760765
let balance = 0;
761-
const feePerSignature = await this.getFees();
762-
const baseFee = params.durableNonce ? feePerSignature * 2 : feePerSignature;
763-
let totalFee = new BigNumber(baseFee);
764766

765767
const index = params.index || 0;
766768
const currPath = params.seed ? getDerivationPath(params.seed) + `/${index}` : `m/${index}`;
767769
const accountId = MPC.deriveUnhardened(bitgoKey, currPath).slice(0, 64);
768770
const bs58EncodedPublicKey = new SolKeyPair({ pub: accountId }).getAddress();
769771

770772
balance = await this.getAccountBalance(bs58EncodedPublicKey);
771-
if (totalFee.gt(balance)) {
772-
throw Error('Did not find address with funds to recover');
773-
}
774773

775774
const factory = this.getBuilder();
776775
const walletCoin = this.getChain();
@@ -779,6 +778,8 @@ export class Sol extends BaseCoin {
779778
let blockhash = await this.getBlockhash();
780779
let rentExemptAmount;
781780
let authority = '';
781+
let totalFee = new BigNumber(0);
782+
let totalFeeForTokenRecovery = new BigNumber(0);
782783

783784
if (params.durableNonce) {
784785
const durableNonceInfo = await this.getAccountInfo(params.durableNonce.publicKey);
@@ -815,7 +816,6 @@ export class Sol extends BaseCoin {
815816
.getTokenTransferBuilder()
816817
.nonce(blockhash)
817818
.sender(bs58EncodedPublicKey)
818-
.fee({ amount: feePerSignature })
819819
.associatedTokenAccountRent(rentExemptAmount.toString())
820820
.feePayer(bs58EncodedPublicKey);
821821

@@ -849,16 +849,9 @@ export class Sol extends BaseCoin {
849849
tokenName: tokenName,
850850
});
851851
// add rent exempt amount to total fee for each token account that has to be created
852-
totalFee = totalFee.plus(rentExemptAmount);
852+
totalFeeForTokenRecovery = totalFeeForTokenRecovery.plus(rentExemptAmount);
853853
}
854854
}
855-
856-
// there are recoverable token accounts, need to check if there is sufficient native solana to recover tokens
857-
if (new BigNumber(balance).lt(totalFee)) {
858-
throw Error(
859-
'Not enough funds to pay for recover tokens fees, have: ' + balance + ' need: ' + totalFee.toString()
860-
);
861-
}
862855
} else {
863856
throw Error('Not enough token funds to recover');
864857
}
@@ -867,14 +860,11 @@ export class Sol extends BaseCoin {
867860
throw Error('Did not find token account to recover tokens, please check token account');
868861
}
869862
} else {
870-
const netAmount = new BigNumber(balance).minus(totalFee);
871-
872863
txBuilder = factory
873864
.getTransferBuilder()
874865
.nonce(blockhash)
875866
.sender(bs58EncodedPublicKey)
876-
.send({ address: params.recoveryDestination, amount: netAmount.toString() })
877-
.fee({ amount: feePerSignature })
867+
.send({ address: params.recoveryDestination, amount: balance.toString() })
878868
.feePayer(bs58EncodedPublicKey);
879869
}
880870

@@ -885,6 +875,17 @@ export class Sol extends BaseCoin {
885875
});
886876
}
887877

878+
// build the transaction without fee
879+
const unsignedTransactionWithoutFee = (await txBuilder.build()) as Transaction;
880+
const serializedMessage = unsignedTransactionWithoutFee.solTransaction.serializeMessage().toString('base64');
881+
882+
const feePerSignature = await this.getFeeForMessage(serializedMessage);
883+
const baseFee = params.durableNonce ? feePerSignature * 2 : feePerSignature;
884+
totalFee = totalFee.plus(new BigNumber(baseFee));
885+
totalFeeForTokenRecovery = totalFeeForTokenRecovery.plus(new BigNumber(baseFee));
886+
if (totalFee.gt(balance)) {
887+
throw Error('Did not find address with funds to recover');
888+
}
888889
if (!isUnsignedSweep) {
889890
// Sign the txn
890891
if (!params.userKey) {
@@ -899,6 +900,26 @@ export class Sol extends BaseCoin {
899900
throw new Error('missing wallet passphrase');
900901
}
901902

903+
if (params.tokenContractAddress) {
904+
totalFeeForTokenRecovery = totalFeeForTokenRecovery.plus(new BigNumber(baseFee));
905+
// Check if there is sufficient native solana to recover tokens
906+
if (new BigNumber(balance).lt(totalFeeForTokenRecovery)) {
907+
throw Error(
908+
'Not enough funds to pay for recover tokens fees, have: ' +
909+
balance +
910+
' need: ' +
911+
totalFeeForTokenRecovery.toString()
912+
);
913+
}
914+
txBuilder.fee({ amount: feePerSignature });
915+
} else {
916+
totalFee = new BigNumber(baseFee);
917+
const netAmount = new BigNumber(balance).minus(totalFee);
918+
txBuilder
919+
.send({ address: params.recoveryDestination, amount: netAmount.toString() })
920+
.fee({ amount: feePerSignature });
921+
}
922+
// build the transaction with fee
902923
const unsignedTransaction = (await txBuilder.build()) as Transaction;
903924

904925
const userKey = params.userKey.replace(/\s/g, '');
@@ -966,7 +987,7 @@ export class Sol extends BaseCoin {
966987
}
967988
const spendAmount = completedTransaction.inputs.length === 1 ? completedTransaction.inputs[0].value : 0;
968989
const parsedTx = { inputs: inputs, outputs: outputs, spendAmount: spendAmount, type: '' };
969-
const feeInfo = { fee: totalFee.toNumber(), feeString: totalFee.toString() };
990+
const feeInfo = { fee: totalFeeForTokenRecovery.toNumber(), feeString: totalFee.toString() };
970991
const coinSpecific = { commonKeychain: bitgoKey };
971992
if (isUnsignedSweep) {
972993
const transaction: MPCTx = {
@@ -1021,19 +1042,13 @@ export class Sol extends BaseCoin {
10211042
// Build the transaction
10221043
const MPC = await EDDSAMethods.getInitializedMpcInstance();
10231044
let balance = 0;
1024-
const feePerSignature = await this.getFees();
1025-
const baseFee = params.durableNonce ? feePerSignature * 2 : feePerSignature;
1026-
const totalFee = new BigNumber(baseFee);
10271045

10281046
const index = params.index || 0;
10291047
const currPath = params.seed ? getDerivationPath(params.seed) + `/${index}` : `m/${index}`;
10301048
const accountId = MPC.deriveUnhardened(bitgoKey, currPath).slice(0, 64);
10311049
const bs58EncodedPublicKey = new SolKeyPair({ pub: accountId }).getAddress();
10321050

1033-
balance = await this.getAccountBalance(bs58EncodedPublicKey);
1034-
if (totalFee.gt(balance)) {
1035-
throw Error('Did not find address with funds to recover');
1036-
}
1051+
const accountBalance = await this.getAccountBalance(bs58EncodedPublicKey);
10371052

10381053
balance = await this.getAccountBalance(params.closeAtaAddress);
10391054
if (balance <= 0) {
@@ -1069,9 +1084,17 @@ export class Sol extends BaseCoin {
10691084
.getTokenTransferBuilder()
10701085
.nonce(blockhash)
10711086
.sender(bs58EncodedPublicKey)
1072-
.fee({ amount: feePerSignature })
10731087
.associatedTokenAccountRent(rentExemptAmount.toString())
10741088
.feePayer(bs58EncodedPublicKey);
1089+
const unsignedTransaction = (await txBuilder.build()) as Transaction;
1090+
const serializedMessage = unsignedTransaction.solTransaction.serializeMessage().toString('base64');
1091+
const feePerSignature = await this.getFeeForMessage(serializedMessage);
1092+
const baseFee = params.durableNonce ? feePerSignature * 2 : feePerSignature;
1093+
const totalFee = new BigNumber(baseFee);
1094+
if (totalFee.gt(accountBalance)) {
1095+
throw Error('Did not find address with funds to recover');
1096+
}
1097+
txBuilder.fee({ amount: feePerSignature });
10751098

10761099
const network = this.getNetwork();
10771100
const token = getSolTokenFromAddress(tokenInfo.info.mint, network);
@@ -1238,6 +1261,7 @@ export class Sol extends BaseCoin {
12381261
const durableNoncePubKeysLength = params.durableNonces.publicKeys.length;
12391262
const consolidationTransactions: any[] = [];
12401263
let lastScanIndex = startIdx;
1264+
12411265
for (let i = startIdx; i < endIdx; i++) {
12421266
const recoverParams = {
12431267
userKey: params.userKey,

modules/sdk-coin-sol/test/fixtures/sol.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,21 @@ const getFeesResponse = {
108108
},
109109
};
110110

111+
const getFeesForMessageResponse = {
112+
status: 200,
113+
body: {
114+
jsonrpc: '2.0',
115+
result: {
116+
context: {
117+
apiVersion: '2.0.13',
118+
slot: 332103639,
119+
},
120+
value: 5000,
121+
},
122+
id: 1,
123+
},
124+
};
125+
111126
const getMinimumBalanceForRentExemptionResponse = {
112127
status: 200,
113128
body: {
@@ -475,6 +490,7 @@ const getTokenAccountsByOwnerResponse3 = {
475490
export const SolResponses = {
476491
getBlockhashResponse,
477492
getFeesResponse,
493+
getFeesForMessageResponse,
478494
getAccountBalanceResponse,
479495
getTokenInfoResponse,
480496
getAccountInfoResponse,

modules/sdk-coin-sol/test/unit/sol.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,10 +1454,16 @@ describe('SOL:', function () {
14541454
payload: {
14551455
id: '1',
14561456
jsonrpc: '2.0',
1457-
method: 'getFees',
1457+
method: 'getFeeForMessage',
1458+
params: [
1459+
sinon.match.string,
1460+
{
1461+
commitment: 'finalized',
1462+
},
1463+
],
14581464
},
14591465
})
1460-
.resolves(testData.SolResponses.getFeesResponse);
1466+
.resolves(testData.SolResponses.getFeesForMessageResponse);
14611467
callBack
14621468
.withArgs({
14631469
payload: {
@@ -2049,10 +2055,16 @@ describe('SOL:', function () {
20492055
payload: {
20502056
id: '1',
20512057
jsonrpc: '2.0',
2052-
method: 'getFees',
2058+
method: 'getFeeForMessage',
2059+
params: [
2060+
sinon.match.string,
2061+
{
2062+
commitment: 'finalized',
2063+
},
2064+
],
20532065
},
20542066
})
2055-
.resolves(testData.SolResponses.getFeesResponse);
2067+
.resolves(testData.SolResponses.getFeesForMessageResponse);
20562068
callBack
20572069
.withArgs({
20582070
payload: {

0 commit comments

Comments
 (0)