Skip to content

Commit fd05d40

Browse files
Merge pull request #6236 from BitGo/WIN-5670-build
fix(sdk-coin-sol): fix enable token for sol 2022 program
2 parents 5b1b752 + f03487d commit fd05d40

File tree

8 files changed

+188
-20
lines changed

8 files changed

+188
-20
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ export class AtaInitializationBuilder extends TransactionBuilder {
159159
ownerAddress: recipient.ownerAddress,
160160
payerAddress: this._sender,
161161
tokenName: recipient.tokenName,
162+
programId: token.programId,
162163
},
163164
});
164165
})

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,14 @@ export interface SetPriorityFee {
128128

129129
export interface AtaInit {
130130
type: InstructionBuilderTypes.CreateAssociatedTokenAccount;
131-
params: { mintAddress: string; ataAddress: string; ownerAddress: string; payerAddress: string; tokenName: string };
131+
params: {
132+
mintAddress: string;
133+
ataAddress: string;
134+
ownerAddress: string;
135+
payerAddress: string;
136+
tokenName: string;
137+
programId?: string;
138+
};
132139
}
133140

134141
export interface AtaClose {

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { decodeTransferCheckedInstruction } from '@solana/spl-token';
1+
import {
2+
DecodedTransferCheckedInstruction,
3+
decodeTransferCheckedInstruction,
4+
TOKEN_2022_PROGRAM_ID,
5+
} from '@solana/spl-token';
26
import {
37
AllocateParams,
48
AssignParams,
@@ -150,7 +154,12 @@ function parseSendInstructions(
150154
instructionData.push(transfer);
151155
break;
152156
case ValidInstructionTypesEnum.TokenTransfer:
153-
const tokenTransferInstruction = decodeTransferCheckedInstruction(instruction);
157+
let tokenTransferInstruction: DecodedTransferCheckedInstruction;
158+
if (instruction.programId.toString() !== TOKEN_2022_PROGRAM_ID.toString()) {
159+
tokenTransferInstruction = decodeTransferCheckedInstruction(instruction);
160+
} else {
161+
tokenTransferInstruction = decodeTransferCheckedInstruction(instruction, TOKEN_2022_PROGRAM_ID);
162+
}
154163
const tokenName = findTokenName(tokenTransferInstruction.keys.mint.pubkey.toString());
155164
const tokenTransfer: TokenTransfer = {
156165
type: InstructionBuilderTypes.TokenTransfer,

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

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
createAssociatedTokenAccountInstruction,
44
createCloseAccountInstruction,
55
createTransferCheckedInstruction,
6+
TOKEN_2022_PROGRAM_ID,
67
} from '@solana/spl-token';
78
import {
89
Authorized,
@@ -158,14 +159,28 @@ function tokenTransferInstruction(data: TokenTransfer): TransactionInstruction[]
158159
assert(sourceAddress, 'Missing ata address');
159160
const token = coins.get(data.params.tokenName);
160161
assert(token instanceof SolCoin);
161-
const transferInstruction = createTransferCheckedInstruction(
162-
new PublicKey(sourceAddress),
163-
new PublicKey(token.tokenAddress),
164-
new PublicKey(toAddress),
165-
new PublicKey(fromAddress),
166-
BigInt(amount),
167-
token.decimalPlaces
168-
);
162+
let transferInstruction: TransactionInstruction;
163+
if (token.programId === TOKEN_2022_PROGRAM_ID.toString()) {
164+
transferInstruction = createTransferCheckedInstruction(
165+
new PublicKey(sourceAddress),
166+
new PublicKey(token.tokenAddress),
167+
new PublicKey(toAddress),
168+
new PublicKey(fromAddress),
169+
BigInt(amount),
170+
token.decimalPlaces,
171+
[],
172+
TOKEN_2022_PROGRAM_ID
173+
);
174+
} else {
175+
transferInstruction = createTransferCheckedInstruction(
176+
new PublicKey(sourceAddress),
177+
new PublicKey(token.tokenAddress),
178+
new PublicKey(toAddress),
179+
new PublicKey(fromAddress),
180+
BigInt(amount),
181+
token.decimalPlaces
182+
);
183+
}
169184
return [transferInstruction];
170185
}
171186

@@ -340,19 +355,30 @@ function stakingWithdrawInstruction(data: StakingWithdraw): TransactionInstructi
340355
*/
341356
function createATAInstruction(data: AtaInit): TransactionInstruction[] {
342357
const {
343-
params: { mintAddress, ataAddress, ownerAddress, payerAddress },
358+
params: { mintAddress, ataAddress, ownerAddress, payerAddress, programId },
344359
} = data;
345360
assert(mintAddress, 'Missing mintAddress param');
346361
assert(ataAddress, 'Missing ataAddress param');
347362
assert(ownerAddress, 'Missing ownerAddress param');
348363
assert(payerAddress, 'Missing payerAddress param');
349364

350-
const associatedTokenAccountInstruction = createAssociatedTokenAccountInstruction(
351-
new PublicKey(payerAddress),
352-
new PublicKey(ataAddress),
353-
new PublicKey(ownerAddress),
354-
new PublicKey(mintAddress)
355-
);
365+
let associatedTokenAccountInstruction: TransactionInstruction;
366+
if (programId && programId === TOKEN_2022_PROGRAM_ID.toString()) {
367+
associatedTokenAccountInstruction = createAssociatedTokenAccountInstruction(
368+
new PublicKey(payerAddress),
369+
new PublicKey(ataAddress),
370+
new PublicKey(ownerAddress),
371+
new PublicKey(mintAddress),
372+
TOKEN_2022_PROGRAM_ID
373+
);
374+
} else {
375+
associatedTokenAccountInstruction = createAssociatedTokenAccountInstruction(
376+
new PublicKey(payerAddress),
377+
new PublicKey(ataAddress),
378+
new PublicKey(ownerAddress),
379+
new PublicKey(mintAddress)
380+
);
381+
}
356382
return [associatedTokenAccountInstruction];
357383
}
358384

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ export class TransferBuilderV2 extends TransactionBuilder {
164164
mintAddress: coin.tokenAddress,
165165
ataAddress: recipientTokenAddress,
166166
payerAddress: this._sender,
167+
programId: coin.programId,
167168
},
168169
};
169170
})

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,8 +333,14 @@ export function getInstructionType(instruction: TransactionInstruction): ValidIn
333333
case SystemProgram.programId.toString():
334334
return SystemInstruction.decodeInstructionType(instruction);
335335
case TOKEN_PROGRAM_ID.toString():
336+
case TOKEN_2022_PROGRAM_ID.toString():
336337
try {
337-
const decodedInstruction = decodeCloseAccountInstruction(instruction);
338+
let decodedInstruction;
339+
if (instruction.programId.toString() !== TOKEN_2022_PROGRAM_ID.toString()) {
340+
decodedInstruction = decodeCloseAccountInstruction(instruction);
341+
} else {
342+
decodedInstruction = decodeCloseAccountInstruction(instruction, TOKEN_2022_PROGRAM_ID);
343+
}
338344
if (decodedInstruction && decodedInstruction.data.instruction === 9) {
339345
return 'CloseAssociatedTokenAccount';
340346
}

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,17 @@ export const associatedTokenAccounts = {
295295
],
296296
};
297297

298+
export const associatedTokenAccountsForSol2022 = {
299+
mint: 'tsol:t22mint',
300+
mintId: '5NR1bQwLWqjbkhbQ1hx72HKJybbuvwkDnUZNoAZ2VhW6',
301+
accounts: [
302+
{
303+
pub: 'ANjpai13LdUuqTeHoJd72tosapYofAWNk95U7vind9dd',
304+
ata: 'GQEPA5QK43RDD9xE1wN52aVgnH5acoPr4mMrEoob7KWq',
305+
},
306+
],
307+
};
308+
298309
export const ATA_INIT_UNSIGNED_TX =
299310
'AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAUHAGymKVqOJEQemBHH67uu8ISJV4rtwTejLrjw7VSeW6eRPnWyN8hzxKVQ4TDLI3w9B+9bjXlvcVElL/sY0IDBtgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjJclj04kifG7PRApFI4NgwtaE5na/xCEBI572Nvp+FnG+nrzvtutOj1l82qryXQxsbvkwtL24OR8pgIDRS9dYQan1RcZLFxRIYzJTD1K8X9Y2u4Im6H9ROPb2YoAAAAABt324ddloZPZy+FGzut5rBy0he1fWzeROoz1hX7/AKnjMtr5L6vs6LY/96RABeX9/Zr6FYdWthxalfkEs7jQgQEDBwABAAQCBgUA';
300311
export const ATA_INIT_UNSIGNED_TX_WITH_MEMO =
@@ -339,6 +350,16 @@ export const tokenTransfers = {
339350
amount: 300000,
340351
};
341352

353+
export const sol2022TokenTransfers = {
354+
owner: 'ANjpai13LdUuqTeHoJd72tosapYofAWNk95U7vind9dd',
355+
name: 'tsol:t22mint',
356+
mint: '5NR1bQwLWqjbkhbQ1hx72HKJybbuvwkDnUZNoAZ2VhW6',
357+
memo: 'test memo',
358+
decimals: 9,
359+
amount: 300000,
360+
source: 'GQEPA5QK43RDD9xE1wN52aVgnH5acoPr4mMrEoob7KWq',
361+
};
362+
342363
export const TOKEN_TRANSFER_TO_NATIVE_UNSIGNED_TX_HEX =
343364
'0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010002052fe1aa39950c065cedb380ffbd710ad66cf8029ac349b1803f4867c78069952d63b0bbb8476e7a39a463cccc4c3d6d3be5a9eb683cdf9b0e3a7c408e657aebf0619d87c29fcae057cbbb0bec48e6bc6afb95bfbda13189af77f561ffcd93951ed10389fbcee528f208611dccc734b31092540cb2b8d58d100f2eaa2cedb4da5e06ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a9fc112c1d986ab5f98b5bfc458083c826fc85c247930b2a4c338f0d297ad5519c010404010302000a0c010000000000000009';
344365
export const TOKEN_TRANSFER_UNSIGNED_TX_WITH_MEMO =

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

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ import { InstructionParams } from '../../src/lib/iface';
66
import { InstructionBuilderTypes, MEMO_PROGRAM_PK } from '../../src/lib/constants';
77
import { PublicKey, SystemProgram, TransactionInstruction } from '@solana/web3.js';
88
import BigNumber from 'bignumber.js';
9-
import { createAssociatedTokenAccountInstruction, createTransferCheckedInstruction } from '@solana/spl-token';
9+
import {
10+
createAssociatedTokenAccountInstruction,
11+
createTransferCheckedInstruction,
12+
TOKEN_2022_PROGRAM_ID,
13+
} from '@solana/spl-token';
1014

1115
describe('Instruction Parser Tests: ', function () {
1216
describe('Succeed ', function () {
@@ -135,6 +139,67 @@ describe('Instruction Parser Tests: ', function () {
135139
should.deepEqual(result, instructionsData);
136140
});
137141

142+
it('Send sol 2022 token tx instructions', () => {
143+
const authAccount = testData.authAccount.pub;
144+
const nonceAccount = testData.nonceAccount.pub;
145+
const amount = testData.sol2022TokenTransfers.amount;
146+
const memo = testData.sol2022TokenTransfers.memo;
147+
const decimals = testData.sol2022TokenTransfers.decimals;
148+
const name = testData.sol2022TokenTransfers.name;
149+
const mint = testData.sol2022TokenTransfers.mint;
150+
const owner = testData.sol2022TokenTransfers.owner;
151+
const source = testData.sol2022TokenTransfers.source;
152+
153+
// nonce
154+
const nonceAdvanceParams: InstructionParams = {
155+
type: InstructionBuilderTypes.NonceAdvance,
156+
params: { walletNonceAddress: nonceAccount, authWalletAddress: authAccount },
157+
};
158+
const nonceAdvanceInstruction = SystemProgram.nonceAdvance({
159+
noncePubkey: new PublicKey(nonceAccount),
160+
authorizedPubkey: new PublicKey(authAccount),
161+
});
162+
163+
// token transfer
164+
const transferParams = {
165+
type: InstructionBuilderTypes.TokenTransfer,
166+
params: {
167+
fromAddress: owner,
168+
toAddress: nonceAccount,
169+
amount: amount.toString(),
170+
tokenName: name,
171+
sourceAddress: source,
172+
},
173+
};
174+
const transferInstruction = createTransferCheckedInstruction(
175+
new PublicKey(source),
176+
new PublicKey(mint),
177+
new PublicKey(nonceAccount),
178+
new PublicKey(owner),
179+
amount,
180+
decimals,
181+
[],
182+
TOKEN_2022_PROGRAM_ID
183+
);
184+
185+
// memo
186+
const memoParams: InstructionParams = {
187+
type: InstructionBuilderTypes.Memo,
188+
params: { memo },
189+
};
190+
191+
const memoInstruction = new TransactionInstruction({
192+
keys: [],
193+
programId: new PublicKey(MEMO_PROGRAM_PK),
194+
data: Buffer.from(memo),
195+
});
196+
197+
const instructions = [nonceAdvanceInstruction, transferInstruction, memoInstruction];
198+
const instructionsData = [nonceAdvanceParams, transferParams, memoParams];
199+
const result = instructionParamsFactory(TransactionType.Send, instructions);
200+
should.deepEqual(result, instructionsData);
201+
});
202+
138203
it('multi ATA init tx instructions', () => {
139204
const ataParams = [
140205
{
@@ -172,6 +237,38 @@ describe('Instruction Parser Tests: ', function () {
172237
const result = instructionParamsFactory(TransactionType.AssociatedTokenAccountInitialization, ataInstructions);
173238
should.deepEqual(result, createATAParams);
174239
});
240+
it('sol 2022 ATA init tx instructions', () => {
241+
const ataParams = [
242+
{
243+
mintAddress: testData.associatedTokenAccountsForSol2022.mintId,
244+
ownerAddress: testData.associatedTokenAccountsForSol2022.accounts[0].pub,
245+
payerAddress: testData.associatedTokenAccountsForSol2022.accounts[0].pub,
246+
ataAddress: testData.associatedTokenAccountsForSol2022.accounts[0].ata,
247+
},
248+
];
249+
250+
const ataInstructions: TransactionInstruction[] = [];
251+
const createATAParams: InstructionParams[] = [];
252+
253+
ataParams.forEach((param) => {
254+
ataInstructions.push(
255+
createAssociatedTokenAccountInstruction(
256+
new PublicKey(param.payerAddress),
257+
new PublicKey(param.ataAddress),
258+
new PublicKey(param.ownerAddress),
259+
new PublicKey(param.mintAddress),
260+
TOKEN_2022_PROGRAM_ID
261+
)
262+
);
263+
264+
createATAParams.push({
265+
type: InstructionBuilderTypes.CreateAssociatedTokenAccount,
266+
params: { ...param, tokenName: 'tsol:t22mint' },
267+
});
268+
});
269+
const result = instructionParamsFactory(TransactionType.AssociatedTokenAccountInitialization, ataInstructions);
270+
should.deepEqual(result, createATAParams);
271+
});
175272
});
176273
describe('Fail ', function () {
177274
it('Invalid type', () => {

0 commit comments

Comments
 (0)