Skip to content

Commit 4acffc6

Browse files
fix(sdk-coin-sol): fix ata creation for sol 2022 token
TICKET: WIN-5670
1 parent 76dd8c7 commit 4acffc6

File tree

5 files changed

+92
-14
lines changed

5 files changed

+92
-14
lines changed

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

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
decodeCloseAccountInstruction,
1313
getAssociatedTokenAddress,
1414
TOKEN_PROGRAM_ID,
15+
TOKEN_2022_PROGRAM_ID,
1516
} from '@solana/spl-token';
1617
import {
1718
Keypair,
@@ -475,6 +476,29 @@ export function getSolTokenFromAddress(tokenAddress: string, network: BaseNetwor
475476
return undefined;
476477
}
477478

479+
/**
480+
* Get the statics coin object matching a given Solana token address if it exists
481+
*
482+
* @param tokenAddress The token address to match against
483+
* @param network Solana Mainnet or Testnet
484+
* @returns statics BaseCoin object for the matching token
485+
*/
486+
export function getSolTokenFromAddressOnly(tokenAddress: string): Readonly<BaseCoin> | undefined {
487+
const tokens = coins.filter((coin) => {
488+
if (coin instanceof SolCoin) {
489+
return coin.tokenAddress.toLowerCase() === tokenAddress.toLowerCase();
490+
}
491+
return false;
492+
});
493+
const tokensArray = tokens.map((token) => token);
494+
if (tokensArray.length >= 1) {
495+
// there should never be two tokens with the same contract address, so we assert that here
496+
assert(tokensArray.length === 1);
497+
return tokensArray[0];
498+
}
499+
return undefined;
500+
}
501+
478502
/**
479503
* Get the solana token object from token name
480504
* @param tokenName The token name to match against
@@ -505,17 +529,26 @@ export async function getAssociatedTokenAccountAddress(
505529
ownerAddress: string,
506530
allowOwnerOffCurve = false
507531
): Promise<string> {
532+
const mintPublicKey = new PublicKey(tokenMintAddress);
508533
const ownerPublicKey = new PublicKey(ownerAddress);
509534

510535
// tokenAddress are not on ed25519 curve, so they can't be used as ownerAddress
511536
if (!allowOwnerOffCurve && !PublicKey.isOnCurve(ownerPublicKey.toBuffer())) {
512537
throw new UtilsError('Invalid ownerAddress - address off ed25519 curve, got: ' + ownerAddress);
513538
}
514-
const ataAddress = await getAssociatedTokenAddress(
515-
new PublicKey(tokenMintAddress),
516-
ownerPublicKey,
517-
allowOwnerOffCurve
518-
);
539+
540+
const coin = getSolTokenFromAddressOnly(tokenMintAddress);
541+
if (!coin || !(coin instanceof SolCoin)) {
542+
throw new UtilsError(`Token not found or not a Solana token: ${tokenMintAddress}`);
543+
}
544+
545+
let ataAddress: PublicKey;
546+
const programId = coin.programId;
547+
if (programId === TOKEN_2022_PROGRAM_ID.toString()) {
548+
ataAddress = await getAssociatedTokenAddress(mintPublicKey, ownerPublicKey, false, TOKEN_2022_PROGRAM_ID);
549+
} else {
550+
ataAddress = await getAssociatedTokenAddress(mintPublicKey, ownerPublicKey, allowOwnerOffCurve);
551+
}
519552
return ataAddress.toString();
520553
}
521554

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,11 @@ export const authAccount = {
115115
prv: 'Vd4PFt2YsjZFbyW6ej5bCK46QRptKyBe6Aq2FPxpynBzqXL5RUt3L3Y4FXyKq9igfshKFskv5yGqdrgvRknKbLk',
116116
};
117117

118+
export const sol2022AuthAccount = {
119+
pub: 'GZmjWFJT9dUnpWNqJabnRMq4twyqCr9kXFc3zMGePma9',
120+
prv: 'Vd4PFt2YsjZFbyW6ej5bCK46QRptKyBe6Aq2FPxpynBzqXL5RUt3L3Y4FXyKq9igfshKFskv5yGqdrgvRknKbLk',
121+
};
122+
118123
export const authAccount2 = {
119124
pub: '7nSM6jwFRGP5BDZQBcgBgpPNfSB2PZKUyLBS6SrJP9kx',
120125
prv: '5ecNbERFDGwouBRNv8G5P2e4rwQpKK7DeveahDoz168hkiuUNt6MdmzVDLPGtnSX9hugboCzbKLoNsuaWDFEs7UY',
@@ -325,6 +330,7 @@ export const tokenTransfers = {
325330
owner: 'i34VuFZY1Lz8Tg9ajAqq5MNjE3ES32uVR8oPFFtLd9F',
326331
nameUSDC: 'tsol:usdc',
327332
mintUSDC: 'F4uLeXJoFz3hw13MposuwaQbMcZbCjqvEGPPeRRB1Byf',
333+
mintAI16Z: 'HeLp6NuQkmYB4pYWo2zYs22mESHXPQYzXbB8n4V98jwC',
328334
sourceUSDC: '2fyhC1YbqaYszkUQw2YGNRVkr2abr69UwFXVCjz4Q5f5',
329335
sourceRAY: 'EF3VmejtM8sAznwsiYAwF1LA7N2tRAgFUvoVjF3MMwzG',
330336
sourceSRM: '2P6cevaY3RLoQdW6xiduwePFqZCCqM6mTzNr3rpJbzKX',

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,22 @@ describe('SOL util library', function () {
315315
});
316316
});
317317

318+
describe('getAssociatedTokenAccountAddress for sol 2022 token', function () {
319+
const ai16ZMintAddress = testData.tokenTransfers.mintAI16Z;
320+
const tokenAddress = 'JC2HAixRNrhBgNjCsV2PBfZEdyitEnWqJPNGokFD8xSA';
321+
it('should succeed for native address as owner address', async function () {
322+
const ownerAddress = testData.sol2022AuthAccount.pub;
323+
const result = await Utils.getAssociatedTokenAccountAddress(ai16ZMintAddress, ownerAddress);
324+
result.should.be.equal(tokenAddress);
325+
});
326+
it('should fail for token address as owner address', async function () {
327+
const invalidOwnerAddress = tokenAddress;
328+
await Utils.getAssociatedTokenAccountAddress(ai16ZMintAddress, invalidOwnerAddress).should.be.rejectedWith(
329+
'Invalid ownerAddress - address off ed25519 curve, got: ' + invalidOwnerAddress
330+
);
331+
});
332+
});
333+
318334
describe('matchTransactionTypeByInstructionsOrder', function () {
319335
describe('Activate stake instructions', function () {
320336
it('should match staking activate instructions', function () {

modules/statics/src/account.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ import {
88
CELO_TOKEN_FEATURES,
99
} from './coinFeatures';
1010

11+
/**
12+
* This is the program id against sol tokem program.
13+
*/
14+
export enum ProgramID {
15+
TokenProgramId = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
16+
Token2022ProgramId = 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb',
17+
}
18+
1119
export interface AccountConstructorOptions {
1220
id: string;
1321
fullName: string;
@@ -97,6 +105,7 @@ export interface EosCoinConstructorOptions extends AccountConstructorOptions {
97105
export interface SolCoinConstructorOptions extends AccountConstructorOptions {
98106
tokenAddress: string;
99107
contractAddress: string;
108+
programId: string;
100109
}
101110

102111
export interface AdaCoinConstructorOptions extends AccountConstructorOptions {
@@ -366,13 +375,15 @@ export class EosCoin extends AccountCoinToken {
366375
export class SolCoin extends AccountCoinToken {
367376
public tokenAddress: string;
368377
public contractAddress: string;
378+
public programId: string;
369379
constructor(options: SolCoinConstructorOptions) {
370380
super({
371381
...options,
372382
});
373383

374384
this.tokenAddress = options.contractAddress;
375385
this.contractAddress = options.contractAddress;
386+
this.programId = options.programId;
376387
}
377388
}
378389

@@ -1610,6 +1621,7 @@ export function solToken(
16101621
contractAddress: string,
16111622
asset: UnderlyingAsset,
16121623
features: CoinFeature[] = [...AccountCoin.DEFAULT_FEATURES, CoinFeature.REQUIRES_RESERVE],
1624+
programId: ProgramID = ProgramID.TokenProgramId,
16131625
prefix = '',
16141626
suffix: string = name.toUpperCase(),
16151627
network: AccountNetwork = Networks.main.sol,
@@ -1626,6 +1638,7 @@ export function solToken(
16261638
prefix,
16271639
suffix,
16281640
features,
1641+
programId,
16291642
decimalPlaces,
16301643
asset,
16311644
isToken: true,
@@ -1658,6 +1671,7 @@ export function tsolToken(
16581671
contractAddress: string,
16591672
asset: UnderlyingAsset,
16601673
features: CoinFeature[] = [...AccountCoin.DEFAULT_FEATURES, CoinFeature.REQUIRES_RESERVE],
1674+
programId = ProgramID.TokenProgramId,
16611675
prefix = '',
16621676
suffix: string = name.toUpperCase(),
16631677
network: AccountNetwork = Networks.test.sol
@@ -1671,6 +1685,7 @@ export function tsolToken(
16711685
contractAddress,
16721686
asset,
16731687
features,
1688+
programId,
16741689
prefix,
16751690
suffix,
16761691
network

modules/statics/src/coins/solTokens.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { solToken, tsolToken } from '../account';
1+
import { ProgramID, solToken, tsolToken } from '../account';
22
import { CoinFeature, UnderlyingAsset } from '../base';
33
import {
44
SOL_TOKEN_FEATURES,
@@ -75,7 +75,8 @@ export const solTokens = [
7575
'HeLp6NuQkmYB4pYWo2zYs22mESHXPQYzXbB8n4V98jwC', // https://solscan.io/token/HeLp6NuQkmYB4pYWo2zYs22mESHXPQYzXbB8n4V98jwC
7676
'HeLp6NuQkmYB4pYWo2zYs22mESHXPQYzXbB8n4V98jwC',
7777
UnderlyingAsset['sol:ai16z'],
78-
SOL_TOKEN_FEATURES
78+
SOL_TOKEN_FEATURES,
79+
ProgramID.Token2022ProgramId
7980
),
8081
solToken(
8182
'd6bee044-de5b-4ce2-8ed2-dbae6f23e728',
@@ -2471,7 +2472,8 @@ export const solTokens = [
24712472
CoinFeature.CUSTODY_BITGO_FRANKFURT,
24722473
CoinFeature.CUSTODY_BITGO_GERMANY,
24732474
CoinFeature.CUSTODY_BITGO_SINGAPORE,
2474-
]
2475+
],
2476+
ProgramID.Token2022ProgramId
24752477
),
24762478
solToken(
24772479
'b4e9b281-952e-4b75-8d57-8666e0b4a33e',
@@ -2486,7 +2488,8 @@ export const solTokens = [
24862488
CoinFeature.CUSTODY_BITGO_FRANKFURT,
24872489
CoinFeature.CUSTODY_BITGO_GERMANY,
24882490
CoinFeature.CUSTODY_BITGO_SINGAPORE,
2489-
]
2491+
],
2492+
ProgramID.Token2022ProgramId
24902493
),
24912494
solToken(
24922495
'cf7d8a35-11ea-46dc-80a5-e0bed443a18c',
@@ -2501,7 +2504,8 @@ export const solTokens = [
25012504
CoinFeature.CUSTODY_BITGO_FRANKFURT,
25022505
CoinFeature.CUSTODY_BITGO_GERMANY,
25032506
CoinFeature.CUSTODY_BITGO_SINGAPORE,
2504-
]
2507+
],
2508+
ProgramID.Token2022ProgramId
25052509
),
25062510
solToken(
25072511
'694e2cc5-cc12-4fa9-9447-3904ac7a2c57',
@@ -2516,7 +2520,8 @@ export const solTokens = [
25162520
CoinFeature.CUSTODY_BITGO_FRANKFURT,
25172521
CoinFeature.CUSTODY_BITGO_GERMANY,
25182522
CoinFeature.CUSTODY_BITGO_SINGAPORE,
2519-
]
2523+
],
2524+
ProgramID.Token2022ProgramId
25202525
),
25212526
solToken(
25222527
'6dd706b0-4764-48b1-b45d-e78a98e609b3',
@@ -2531,7 +2536,8 @@ export const solTokens = [
25312536
CoinFeature.CUSTODY_BITGO_FRANKFURT,
25322537
CoinFeature.CUSTODY_BITGO_GERMANY,
25332538
CoinFeature.CUSTODY_BITGO_SINGAPORE,
2534-
]
2539+
],
2540+
ProgramID.Token2022ProgramId
25352541
),
25362542
solToken(
25372543
'679055e6-e8cc-4b91-906c-0689de806a59',
@@ -2566,7 +2572,8 @@ export const solTokens = [
25662572
'4MmJVdwYN8LwvbGeCowYjSx7KoEi6BJWg8XXnW4fDDp6',
25672573
'4MmJVdwYN8LwvbGeCowYjSx7KoEi6BJWg8XXnW4fDDp6',
25682574
UnderlyingAsset['sol:tbill'],
2569-
SOL_TOKEN_FEATURES
2575+
SOL_TOKEN_FEATURES,
2576+
ProgramID.Token2022ProgramId
25702577
),
25712578
solToken(
25722579
'88814831-9cae-4174-8d1c-380aeb3fe55a',
@@ -2576,7 +2583,8 @@ export const solTokens = [
25762583
'DghpMkatCiUsofbTmid3M3kAbDTPqDwKiYHnudXeGG52',
25772584
'DghpMkatCiUsofbTmid3M3kAbDTPqDwKiYHnudXeGG52',
25782585
UnderlyingAsset['sol:eurcv'],
2579-
SOL_TOKEN_FEATURES
2586+
SOL_TOKEN_FEATURES,
2587+
ProgramID.Token2022ProgramId
25802588
),
25812589
solToken(
25822590
'9a89cbd8-f097-4ab8-a5ea-f594c44cb33e',

0 commit comments

Comments
 (0)