Skip to content

Commit fd831a2

Browse files
committed
chore(sdk-coin-sol): remove statics dependency from sol module
Ticket: WIN-5679
1 parent 1facd02 commit fd831a2

File tree

13 files changed

+377
-56
lines changed

13 files changed

+377
-56
lines changed

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

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,15 @@ export class AtaInitializationBuilder extends TransactionBuilder {
115115
}
116116
validateOwnerAddress(recipient.ownerAddress);
117117
const token = getSolTokenFromTokenName(recipient.tokenName);
118-
if (!token) {
118+
let tokenAddress: string;
119+
if (token) {
120+
tokenAddress = token.tokenAddress;
121+
} else if (recipient.tokenAddress) {
122+
tokenAddress = recipient.tokenAddress;
123+
} else {
119124
throw new BuildTransactionError('Invalid transaction: invalid token name, got: ' + recipient.tokenName);
120125
}
121-
validateMintAddress(token.tokenAddress);
126+
validateMintAddress(tokenAddress);
122127

123128
this._tokenAssociateRecipients.push(recipient);
124129
return this;
@@ -141,25 +146,33 @@ export class AtaInitializationBuilder extends TransactionBuilder {
141146
await Promise.all(
142147
this._tokenAssociateRecipients.map(async (recipient) => {
143148
const token = getSolTokenFromTokenName(recipient.tokenName);
144-
if (!token) {
149+
let tokenAddress: string;
150+
let programId: string;
151+
if (token) {
152+
tokenAddress = token.tokenAddress;
153+
programId = token.programId;
154+
} else if (recipient.tokenAddress && recipient.programId) {
155+
tokenAddress = recipient.tokenAddress;
156+
programId = recipient.programId;
157+
} else {
145158
throw new BuildTransactionError('Invalid transaction: invalid token name, got: ' + recipient.tokenName);
146159
}
147160

148161
// Use the provided ataAddress if it exists, otherwise calculate it
149162
let ataPk = recipient.ataAddress;
150163
if (!ataPk) {
151-
ataPk = await getAssociatedTokenAccountAddress(token.tokenAddress, recipient.ownerAddress);
164+
ataPk = await getAssociatedTokenAccountAddress(tokenAddress, recipient.ownerAddress);
152165
}
153166

154167
this._instructionsData.push({
155168
type: InstructionBuilderTypes.CreateAssociatedTokenAccount,
156169
params: {
157-
mintAddress: token.tokenAddress,
170+
mintAddress: tokenAddress,
158171
ataAddress: ataPk,
159172
ownerAddress: recipient.ownerAddress,
160173
payerAddress: this._sender,
161174
tokenName: recipient.tokenName,
162-
programId: token.programId,
175+
programId: programId,
163176
},
164177
});
165178
})

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ export interface TokenTransfer {
7272
amount: string;
7373
tokenName: string;
7474
sourceAddress: string;
75+
tokenAddress?: string;
76+
decimalPlaces?: number;
77+
programId?: string;
7578
};
7679
}
7780

@@ -182,4 +185,6 @@ export class TokenAssociateRecipient {
182185
ownerAddress: string;
183186
tokenName: string;
184187
ataAddress?: string;
188+
tokenAddress?: string;
189+
programId?: string;
185190
}

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

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,21 +52,22 @@ import { getInstructionType } from './utils';
5252
export function instructionParamsFactory(
5353
type: TransactionType,
5454
instructions: TransactionInstruction[],
55-
coinName?: string
55+
coinName?: string,
56+
instructionMetadata?: InstructionParams[]
5657
): InstructionParams[] {
5758
switch (type) {
5859
case TransactionType.WalletInitialization:
5960
return parseWalletInitInstructions(instructions);
6061
case TransactionType.Send:
61-
return parseSendInstructions(instructions);
62+
return parseSendInstructions(instructions, instructionMetadata);
6263
case TransactionType.StakingActivate:
6364
return parseStakingActivateInstructions(instructions);
6465
case TransactionType.StakingDeactivate:
6566
return parseStakingDeactivateInstructions(instructions, coinName);
6667
case TransactionType.StakingWithdraw:
6768
return parseStakingWithdrawInstructions(instructions);
6869
case TransactionType.AssociatedTokenAccountInitialization:
69-
return parseAtaInitInstructions(instructions);
70+
return parseAtaInitInstructions(instructions, instructionMetadata);
7071
case TransactionType.CloseAssociatedTokenAccount:
7172
return parseAtaCloseInstructions(instructions);
7273
case TransactionType.StakingAuthorize:
@@ -120,7 +121,8 @@ function parseWalletInitInstructions(instructions: TransactionInstruction[]): Ar
120121
* @returns {InstructionParams[]} An array containing instruction params for Send tx
121122
*/
122123
function parseSendInstructions(
123-
instructions: TransactionInstruction[]
124+
instructions: TransactionInstruction[],
125+
instructionMetadata?: InstructionParams[]
124126
): Array<Nonce | Memo | Transfer | TokenTransfer | AtaInit | AtaClose | SetPriorityFee> {
125127
const instructionData: Array<Nonce | Memo | Transfer | TokenTransfer | AtaInit | AtaClose | SetPriorityFee> = [];
126128
for (const instruction of instructions) {
@@ -160,7 +162,7 @@ function parseSendInstructions(
160162
} else {
161163
tokenTransferInstruction = decodeTransferCheckedInstruction(instruction, TOKEN_2022_PROGRAM_ID);
162164
}
163-
const tokenName = findTokenName(tokenTransferInstruction.keys.mint.pubkey.toString());
165+
const tokenName = findTokenName(tokenTransferInstruction.keys.mint.pubkey.toString(), instructionMetadata);
164166
const tokenTransfer: TokenTransfer = {
165167
type: InstructionBuilderTypes.TokenTransfer,
166168
params: {
@@ -175,7 +177,7 @@ function parseSendInstructions(
175177
break;
176178
case ValidInstructionTypesEnum.InitializeAssociatedTokenAccount:
177179
const mintAddress = instruction.keys[ataInitInstructionKeysIndexes.MintAddress].pubkey.toString();
178-
const mintTokenName = findTokenName(mintAddress);
180+
const mintTokenName = findTokenName(mintAddress, instructionMetadata);
179181

180182
const ataInit: AtaInit = {
181183
type: InstructionBuilderTypes.CreateAssociatedTokenAccount,
@@ -652,7 +654,10 @@ const closeAtaInstructionKeysIndexes = {
652654
* @param {TransactionInstruction[]} instructions - an array of supported Solana instructions
653655
* @returns {InstructionParams[]} An array containing instruction params for Send tx
654656
*/
655-
function parseAtaInitInstructions(instructions: TransactionInstruction[]): Array<AtaInit | Memo | Nonce> {
657+
function parseAtaInitInstructions(
658+
instructions: TransactionInstruction[],
659+
instructionMetadata?: InstructionParams[]
660+
): Array<AtaInit | Memo | Nonce> {
656661
const instructionData: Array<AtaInit | Memo | Nonce> = [];
657662
let memo: Memo | undefined;
658663

@@ -675,7 +680,7 @@ function parseAtaInitInstructions(instructions: TransactionInstruction[]): Array
675680
break;
676681
case ValidInstructionTypesEnum.InitializeAssociatedTokenAccount:
677682
const mintAddress = instruction.keys[ataInitInstructionKeysIndexes.MintAddress].pubkey.toString();
678-
const tokenName = findTokenName(mintAddress);
683+
const tokenName = findTokenName(mintAddress, instructionMetadata);
679684

680685
const ataInit: AtaInit = {
681686
type: InstructionBuilderTypes.CreateAssociatedTokenAccount,
@@ -831,7 +836,7 @@ function parseStakingAuthorizeRawInstructions(instructions: TransactionInstructi
831836
return instructionData;
832837
}
833838

834-
function findTokenName(mintAddress: string): string {
839+
function findTokenName(mintAddress: string, instructionMetadata?: InstructionParams[]): string {
835840
let token: string | undefined;
836841

837842
coins.forEach((value, key) => {
@@ -840,6 +845,21 @@ function findTokenName(mintAddress: string): string {
840845
}
841846
});
842847

848+
if (!token && instructionMetadata) {
849+
instructionMetadata.forEach((instruction) => {
850+
if (
851+
instruction.type === InstructionBuilderTypes.CreateAssociatedTokenAccount &&
852+
instruction.params.mintAddress === mintAddress
853+
) {
854+
token = instruction.params.tokenName;
855+
} else if (
856+
instruction.type === InstructionBuilderTypes.TokenTransfer &&
857+
instruction.params.tokenAddress === mintAddress
858+
) {
859+
token = instruction.params.tokenName;
860+
}
861+
});
862+
}
843863
assert(token);
844864

845865
return token;

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

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { coins, SolCoin } from '@bitgo/statics';
1+
import { SolCoin } from '@bitgo/statics';
22
import {
33
createAssociatedTokenAccountInstruction,
44
createCloseAccountInstruction,
@@ -35,6 +35,7 @@ import {
3535
WalletInit,
3636
SetPriorityFee,
3737
} from './iface';
38+
import { getSolTokenFromTokenName } from './utils';
3839

3940
/**
4041
* Construct Solana instructions from instructions params
@@ -157,28 +158,43 @@ function tokenTransferInstruction(data: TokenTransfer): TransactionInstruction[]
157158
assert(amount, 'Missing amount param');
158159
assert(tokenName, 'Missing token name');
159160
assert(sourceAddress, 'Missing ata address');
160-
const token = coins.get(data.params.tokenName);
161-
assert(token instanceof SolCoin);
161+
const token = getSolTokenFromTokenName(data.params.tokenName);
162+
let tokenAddress: string;
163+
let programId: string | undefined;
164+
let decimalPlaces: number;
165+
if (token) {
166+
assert(token instanceof SolCoin);
167+
tokenAddress = token.tokenAddress;
168+
decimalPlaces = token.decimalPlaces;
169+
programId = token.programId;
170+
} else if (data.params.tokenAddress && data.params.decimalPlaces) {
171+
tokenAddress = data.params.tokenAddress;
172+
decimalPlaces = data.params.decimalPlaces;
173+
programId = data.params.programId;
174+
} else {
175+
throw new Error('Invalid token name, got:' + data.params.tokenName);
176+
}
177+
162178
let transferInstruction: TransactionInstruction;
163-
if (token.programId === TOKEN_2022_PROGRAM_ID.toString()) {
179+
if (programId === TOKEN_2022_PROGRAM_ID.toString()) {
164180
transferInstruction = createTransferCheckedInstruction(
165181
new PublicKey(sourceAddress),
166-
new PublicKey(token.tokenAddress),
182+
new PublicKey(tokenAddress),
167183
new PublicKey(toAddress),
168184
new PublicKey(fromAddress),
169185
BigInt(amount),
170-
token.decimalPlaces,
186+
decimalPlaces,
171187
[],
172188
TOKEN_2022_PROGRAM_ID
173189
);
174190
} else {
175191
transferInstruction = createTransferCheckedInstruction(
176192
new PublicKey(sourceAddress),
177-
new PublicKey(token.tokenAddress),
193+
new PublicKey(tokenAddress),
178194
new PublicKey(toAddress),
179195
new PublicKey(fromAddress),
180196
BigInt(amount),
181-
token.decimalPlaces
197+
decimalPlaces
182198
);
183199
}
184200
return [transferInstruction];

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

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { BaseCoin as CoinConfig, SolCoin } from '@bitgo/statics';
1+
import { BaseCoin as CoinConfig } from '@bitgo/statics';
22
import { BuildTransactionError, TransactionType } from '@bitgo/sdk-core';
33
import { Transaction } from './transaction';
44
import {
@@ -19,6 +19,9 @@ export interface SendParams {
1919
address: string;
2020
amount: string;
2121
tokenName: string;
22+
tokenAddress?: string;
23+
programId?: string;
24+
decimalPlaces?: number;
2225
}
2326

2427
const UNSIGNED_BIGINT_MAX = BigInt('18446744073709551615');
@@ -69,7 +72,7 @@ export class TokenTransferBuilder extends TransactionBuilder {
6972
* @param {string} params.tokenName - name of token that is intended to send
7073
* @returns {TransactionBuilder} This transaction builder
7174
*/
72-
send({ address, amount, tokenName }: SendParams): this {
75+
send({ address, amount, tokenName, tokenAddress, programId, decimalPlaces }: SendParams): this {
7376
validateAddress(address, 'address');
7477
if (!amount || !isValidAmount(amount)) {
7578
throw new BuildTransactionError('Invalid or missing amount, got: ' + amount);
@@ -78,7 +81,7 @@ export class TokenTransferBuilder extends TransactionBuilder {
7881
throw new BuildTransactionError(`input amount ${amount} exceeds big int limit ${UNSIGNED_BIGINT_MAX}`);
7982
}
8083

81-
this._sendParams.push({ address, amount, tokenName: tokenName });
84+
this._sendParams.push({ address, amount, tokenName: tokenName, tokenAddress, programId, decimalPlaces });
8285
return this;
8386
}
8487

@@ -92,10 +95,15 @@ export class TokenTransferBuilder extends TransactionBuilder {
9295
createAssociatedTokenAccount(recipient: TokenAssociateRecipient): this {
9396
validateOwnerAddress(recipient.ownerAddress);
9497
const token = getSolTokenFromTokenName(recipient.tokenName);
95-
if (!token) {
98+
let tokenAddress: string;
99+
if (token) {
100+
tokenAddress = token.tokenAddress;
101+
} else if (recipient.tokenAddress) {
102+
tokenAddress = recipient.tokenAddress;
103+
} else {
96104
throw new BuildTransactionError('Invalid token name, got: ' + recipient.tokenName);
97105
}
98-
validateMintAddress(token.tokenAddress);
106+
validateMintAddress(tokenAddress);
99107

100108
this._createAtaParams.push(recipient);
101109
return this;
@@ -107,16 +115,35 @@ export class TokenTransferBuilder extends TransactionBuilder {
107115
const sendInstructions = await Promise.all(
108116
this._sendParams.map(async (sendParams: SendParams): Promise<TokenTransfer> => {
109117
const coin = getSolTokenFromTokenName(sendParams.tokenName);
110-
assert(coin instanceof SolCoin);
111-
const sourceAddress = await getAssociatedTokenAccountAddress(coin.tokenAddress, this._sender);
118+
let tokenAddress: string;
119+
let tokenName: string;
120+
let programId: string | undefined;
121+
let decimals: number | undefined;
122+
if (coin) {
123+
tokenAddress = coin.tokenAddress;
124+
tokenName = coin.name;
125+
programId = coin.programId;
126+
decimals = coin.decimalPlaces;
127+
} else if (sendParams.tokenAddress) {
128+
tokenAddress = sendParams.tokenAddress;
129+
tokenName = sendParams.tokenName;
130+
programId = sendParams.programId;
131+
decimals = sendParams.decimalPlaces;
132+
} else {
133+
throw new Error(`Could not determine token information for ${sendParams.tokenName}`);
134+
}
135+
const sourceAddress = await getAssociatedTokenAccountAddress(tokenAddress, this._sender);
112136
return {
113137
type: InstructionBuilderTypes.TokenTransfer,
114138
params: {
115139
fromAddress: this._sender,
116140
toAddress: sendParams.address,
117141
amount: sendParams.amount,
118-
tokenName: coin.name,
142+
tokenName: tokenName,
119143
sourceAddress: sourceAddress,
144+
tokenAddress: tokenAddress,
145+
programId: programId,
146+
decimalPlaces: decimals,
120147
},
121148
};
122149
})
@@ -127,20 +154,35 @@ export class TokenTransferBuilder extends TransactionBuilder {
127154
const createAtaInstructions = await Promise.all(
128155
uniqueCreateAtaParams.map(async (recipient: TokenAssociateRecipient): Promise<AtaInit> => {
129156
const coin = getSolTokenFromTokenName(recipient.tokenName);
130-
assert(coin instanceof SolCoin);
157+
let tokenAddress: string;
158+
let tokenName: string;
159+
let programId: string | undefined;
160+
if (coin) {
161+
tokenName = coin.name;
162+
tokenAddress = coin.tokenAddress;
163+
programId = coin.programId;
164+
} else if (recipient.tokenAddress) {
165+
tokenName = recipient.tokenName;
166+
tokenAddress = recipient.tokenAddress;
167+
programId = recipient.programId;
168+
} else {
169+
throw new Error(`Could not determine token information for ${recipient.tokenName}`);
170+
}
171+
131172
// Use the provided ataAddress if it exists, otherwise calculate it
132173
let ataAddress = recipient.ataAddress;
133174
if (!ataAddress) {
134-
ataAddress = await getAssociatedTokenAccountAddress(coin.tokenAddress, recipient.ownerAddress);
175+
ataAddress = await getAssociatedTokenAccountAddress(tokenAddress, recipient.ownerAddress);
135176
}
136177
return {
137178
type: InstructionBuilderTypes.CreateAssociatedTokenAccount,
138179
params: {
139180
ownerAddress: recipient.ownerAddress,
140-
tokenName: coin.name,
141-
mintAddress: coin.tokenAddress,
181+
mintAddress: tokenAddress,
142182
ataAddress,
143183
payerAddress: this._sender,
184+
tokenName: tokenName,
185+
programId: programId,
144186
},
145187
};
146188
})

0 commit comments

Comments
 (0)