Skip to content

Commit ef5a0f3

Browse files
authored
Merge pull request #6581 from BitGo/COIN-4967
feat(wrw): added api key for full node txn
2 parents 3249a6f + 7b301db commit ef5a0f3

File tree

3 files changed

+159
-101
lines changed

3 files changed

+159
-101
lines changed

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

Lines changed: 130 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ export interface SolRecoveryOptions extends MPCRecoveryOptions {
157157
// destination address where token should be sent before closing the ATA address
158158
recoveryDestinationAtaAddress?: string;
159159
programId?: string; // programId of the token
160+
apiKey?: string; // API key for node requests
160161
}
161162

162163
export interface SolConsolidationRecoveryOptions extends MPCConsolidationRecoveryOptions {
@@ -165,6 +166,7 @@ export interface SolConsolidationRecoveryOptions extends MPCConsolidationRecover
165166
secretKey: string;
166167
};
167168
tokenContractAddress?: string;
169+
apiKey?: string; // API key for node requests
168170
}
169171

170172
const HEX_REGEX = /^[0-9a-fA-F]+$/;
@@ -534,16 +536,22 @@ export class Sol extends BaseCoin {
534536
});
535537
}
536538

537-
protected getPublicNodeUrl(): string {
539+
protected getPublicNodeUrl(apiKey?: string): string {
540+
if (apiKey) {
541+
return Environments[this.bitgo.getEnv()].solAlchemyNodeUrl + `/${apiKey}`;
542+
}
538543
return Environments[this.bitgo.getEnv()].solNodeUrl;
539544
}
540545

541546
/**
542547
* Make a request to one of the public SOL nodes available
543548
* @param params.payload
544549
*/
545-
protected async getDataFromNode(params: { payload?: Record<string, unknown> }): Promise<request.Response> {
546-
const nodeUrl = this.getPublicNodeUrl();
550+
protected async getDataFromNode(
551+
params: { payload?: Record<string, unknown> },
552+
apiKey?: string
553+
): Promise<request.Response> {
554+
const nodeUrl = this.getPublicNodeUrl(apiKey);
547555
try {
548556
return await request.post(nodeUrl).send(params.payload);
549557
} catch (e) {
@@ -552,92 +560,107 @@ export class Sol extends BaseCoin {
552560
throw new Error(`Unable to call endpoint: '/' from node: ${nodeUrl}`);
553561
}
554562

555-
protected async getBlockhash(): Promise<string> {
556-
const response = await this.getDataFromNode({
557-
payload: {
558-
id: '1',
559-
jsonrpc: '2.0',
560-
method: 'getLatestBlockhash',
561-
params: [
562-
{
563-
commitment: 'finalized',
564-
},
565-
],
563+
protected async getBlockhash(apiKey?: string): Promise<string> {
564+
const response = await this.getDataFromNode(
565+
{
566+
payload: {
567+
id: '1',
568+
jsonrpc: '2.0',
569+
method: 'getLatestBlockhash',
570+
params: [
571+
{
572+
commitment: 'finalized',
573+
},
574+
],
575+
},
566576
},
567-
});
577+
apiKey
578+
);
568579
if (response.status !== 200) {
569580
throw new Error('Account not found');
570581
}
571582

572583
return response.body.result.value.blockhash;
573584
}
574585

575-
protected async getFeeForMessage(message: string): Promise<number> {
576-
const response = await this.getDataFromNode({
577-
payload: {
578-
id: '1',
579-
jsonrpc: '2.0',
580-
method: 'getFeeForMessage',
581-
params: [
582-
message,
583-
{
584-
commitment: 'finalized',
585-
},
586-
],
586+
protected async getFeeForMessage(message: string, apiKey?: string): Promise<number> {
587+
const response = await this.getDataFromNode(
588+
{
589+
payload: {
590+
id: '1',
591+
jsonrpc: '2.0',
592+
method: 'getFeeForMessage',
593+
params: [
594+
message,
595+
{
596+
commitment: 'finalized',
597+
},
598+
],
599+
},
587600
},
588-
});
601+
apiKey
602+
);
589603
if (response.status !== 200) {
590604
throw new Error('Account not found');
591605
}
592606

593607
return response.body.result.value;
594608
}
595609

596-
protected async getRentExemptAmount(): Promise<number> {
597-
const response = await this.getDataFromNode({
598-
payload: {
599-
jsonrpc: '2.0',
600-
id: '1',
601-
method: 'getMinimumBalanceForRentExemption',
602-
params: [165],
610+
protected async getRentExemptAmount(apiKey?: string): Promise<number> {
611+
const response = await this.getDataFromNode(
612+
{
613+
payload: {
614+
jsonrpc: '2.0',
615+
id: '1',
616+
method: 'getMinimumBalanceForRentExemption',
617+
params: [165],
618+
},
603619
},
604-
});
620+
apiKey
621+
);
605622
if (response.status !== 200 || response.error) {
606623
throw new Error(JSON.stringify(response.error));
607624
}
608625

609626
return response.body.result;
610627
}
611628

612-
protected async getAccountBalance(pubKey: string): Promise<number> {
613-
const response = await this.getDataFromNode({
614-
payload: {
615-
id: '1',
616-
jsonrpc: '2.0',
617-
method: 'getBalance',
618-
params: [pubKey],
629+
protected async getAccountBalance(pubKey: string, apiKey?: string): Promise<number> {
630+
const response = await this.getDataFromNode(
631+
{
632+
payload: {
633+
id: '1',
634+
jsonrpc: '2.0',
635+
method: 'getBalance',
636+
params: [pubKey],
637+
},
619638
},
620-
});
639+
apiKey
640+
);
621641
if (response.status !== 200) {
622642
throw new Error('Account not found');
623643
}
624644
return response.body.result.value;
625645
}
626646

627-
protected async getAccountInfo(pubKey: string): Promise<SolDurableNonceFromNode> {
628-
const response = await this.getDataFromNode({
629-
payload: {
630-
id: '1',
631-
jsonrpc: '2.0',
632-
method: 'getAccountInfo',
633-
params: [
634-
pubKey,
635-
{
636-
encoding: 'jsonParsed',
637-
},
638-
],
647+
protected async getAccountInfo(pubKey: string, apiKey?: string): Promise<SolDurableNonceFromNode> {
648+
const response = await this.getDataFromNode(
649+
{
650+
payload: {
651+
id: '1',
652+
jsonrpc: '2.0',
653+
method: 'getAccountInfo',
654+
params: [
655+
pubKey,
656+
{
657+
encoding: 'jsonParsed',
658+
},
659+
],
660+
},
639661
},
640-
});
662+
apiKey
663+
);
641664
if (response.status !== 200) {
642665
throw new Error('Account not found');
643666
}
@@ -647,26 +670,29 @@ export class Sol extends BaseCoin {
647670
};
648671
}
649672

650-
protected async getTokenAccountsByOwner(pubKey = '', programId = ''): Promise<[] | TokenAccount[]> {
651-
const response = await this.getDataFromNode({
652-
payload: {
653-
id: '1',
654-
jsonrpc: '2.0',
655-
method: 'getTokenAccountsByOwner',
656-
params: [
657-
pubKey,
658-
{
659-
programId:
660-
programId.toString().toLowerCase() === TOKEN_2022_PROGRAM_ID.toString().toLowerCase()
661-
? TOKEN_2022_PROGRAM_ID.toString()
662-
: TOKEN_PROGRAM_ID.toString(),
663-
},
664-
{
665-
encoding: 'jsonParsed',
666-
},
667-
],
673+
protected async getTokenAccountsByOwner(pubKey = '', programId = '', apiKey?: string): Promise<[] | TokenAccount[]> {
674+
const response = await this.getDataFromNode(
675+
{
676+
payload: {
677+
id: '1',
678+
jsonrpc: '2.0',
679+
method: 'getTokenAccountsByOwner',
680+
params: [
681+
pubKey,
682+
{
683+
programId:
684+
programId.toString().toLowerCase() === TOKEN_2022_PROGRAM_ID.toString().toLowerCase()
685+
? TOKEN_2022_PROGRAM_ID.toString()
686+
: TOKEN_PROGRAM_ID.toString(),
687+
},
688+
{
689+
encoding: 'jsonParsed',
690+
},
691+
],
692+
},
668693
},
669-
});
694+
apiKey
695+
);
670696
if (response.status !== 200) {
671697
throw new Error('Account not found');
672698
}
@@ -682,20 +708,23 @@ export class Sol extends BaseCoin {
682708
return [];
683709
}
684710

685-
protected async getTokenAccountInfo(pubKey: string): Promise<TokenAccount> {
686-
const response = await this.getDataFromNode({
687-
payload: {
688-
id: '1',
689-
jsonrpc: '2.0',
690-
method: 'getAccountInfo',
691-
params: [
692-
pubKey,
693-
{
694-
encoding: 'jsonParsed',
695-
},
696-
],
711+
protected async getTokenAccountInfo(pubKey: string, apiKey?: string): Promise<TokenAccount> {
712+
const response = await this.getDataFromNode(
713+
{
714+
payload: {
715+
id: '1',
716+
jsonrpc: '2.0',
717+
method: 'getAccountInfo',
718+
params: [
719+
pubKey,
720+
{
721+
encoding: 'jsonParsed',
722+
},
723+
],
724+
},
697725
},
698-
});
726+
apiKey
727+
);
699728
if (response.status !== 200) {
700729
throw new Error('Account not found');
701730
}
@@ -791,21 +820,21 @@ export class Sol extends BaseCoin {
791820
const accountId = MPC.deriveUnhardened(bitgoKey, currPath).slice(0, 64);
792821
const bs58EncodedPublicKey = new SolKeyPair({ pub: accountId }).getAddress();
793822

794-
balance = await this.getAccountBalance(bs58EncodedPublicKey);
823+
balance = await this.getAccountBalance(bs58EncodedPublicKey, params.apiKey);
795824

796825
const factory = this.getBuilder();
797826
const walletCoin = this.getChain();
798827

799828
let txBuilder;
800-
let blockhash = await this.getBlockhash();
829+
let blockhash = await this.getBlockhash(params.apiKey);
801830
let rentExemptAmount;
802831
let authority = '';
803832
let totalFee = new BigNumber(0);
804833
let totalFeeForTokenRecovery = new BigNumber(0);
805834

806835
// check for possible token recovery, recover the token provide by user
807836
if (params.tokenContractAddress) {
808-
const tokenAccounts = await this.getTokenAccountsByOwner(bs58EncodedPublicKey, params.programId);
837+
const tokenAccounts = await this.getTokenAccountsByOwner(bs58EncodedPublicKey, params.programId, params.apiKey);
809838
if (tokenAccounts.length !== 0) {
810839
// there exists token accounts on the given address, but need to check certain conditions:
811840
// 1. if there is a recoverable balance
@@ -826,7 +855,7 @@ export class Sol extends BaseCoin {
826855
}
827856

828857
if (recovereableTokenAccounts.length !== 0) {
829-
rentExemptAmount = await this.getRentExemptAmount();
858+
rentExemptAmount = await this.getRentExemptAmount(params.apiKey);
830859

831860
txBuilder = factory
832861
.getTokenTransferBuilder()
@@ -838,7 +867,8 @@ export class Sol extends BaseCoin {
838867
// need to get all token accounts of the recipient address and need to create them if they do not exist
839868
const recipientTokenAccounts = await this.getTokenAccountsByOwner(
840869
params.recoveryDestination,
841-
params.programId
870+
params.programId,
871+
params.apiKey
842872
);
843873

844874
for (const tokenAccount of recovereableTokenAccounts) {
@@ -894,7 +924,7 @@ export class Sol extends BaseCoin {
894924
}
895925

896926
if (params.durableNonce) {
897-
const durableNonceInfo = await this.getAccountInfo(params.durableNonce.publicKey);
927+
const durableNonceInfo = await this.getAccountInfo(params.durableNonce.publicKey, params.apiKey);
898928
blockhash = durableNonceInfo.blockhash;
899929
authority = durableNonceInfo.authority;
900930

@@ -908,7 +938,7 @@ export class Sol extends BaseCoin {
908938
const unsignedTransactionWithoutFee = (await txBuilder.build()) as Transaction;
909939
const serializedMessage = unsignedTransactionWithoutFee.solTransaction.serializeMessage().toString('base64');
910940

911-
const baseFee = await this.getFeeForMessage(serializedMessage);
941+
const baseFee = await this.getFeeForMessage(serializedMessage, params.apiKey);
912942
const feePerSignature = params.durableNonce ? baseFee / 2 : baseFee;
913943
totalFee = totalFee.plus(new BigNumber(baseFee));
914944
totalFeeForTokenRecovery = totalFeeForTokenRecovery.plus(new BigNumber(baseFee));
@@ -1118,7 +1148,7 @@ export class Sol extends BaseCoin {
11181148
throw new Error('invalid recoveryDestinationAtaAddress');
11191149
}
11201150

1121-
blockhash = await this.getBlockhash();
1151+
blockhash = await this.getBlockhash(params.apiKey);
11221152

11231153
txBuilder = factory
11241154
.getTokenTransferBuilder()
@@ -1128,7 +1158,7 @@ export class Sol extends BaseCoin {
11281158
.feePayer(bs58EncodedPublicKey);
11291159
const unsignedTransaction = (await txBuilder.build()) as Transaction;
11301160
const serializedMessage = unsignedTransaction.solTransaction.serializeMessage().toString('base64');
1131-
const feePerSignature = await this.getFeeForMessage(serializedMessage);
1161+
const feePerSignature = await this.getFeeForMessage(serializedMessage, params.apiKey);
11321162
const baseFee = params.durableNonce ? feePerSignature * 2 : feePerSignature;
11331163
const totalFee = new BigNumber(baseFee);
11341164
if (totalFee.gt(accountBalance)) {
@@ -1159,7 +1189,7 @@ export class Sol extends BaseCoin {
11591189

11601190
// after recovering the token amount, attempting to close the ATA address
11611191
if (params.closeAtaAddress) {
1162-
blockhash = await this.getBlockhash();
1192+
blockhash = await this.getBlockhash(params.apiKey);
11631193

11641194
const ataCloseBuilder = () => {
11651195
const txBuilder = factory.getCloseAtaInitializationBuilder();
@@ -1316,6 +1346,7 @@ export class Sol extends BaseCoin {
13161346
secretKey: params.durableNonces.secretKey,
13171347
},
13181348
tokenContractAddress: params.tokenContractAddress,
1349+
apiKey: params.apiKey,
13191350
};
13201351

13211352
let recoveryTransaction;

0 commit comments

Comments
 (0)