Skip to content

Commit

Permalink
Derive AssociatedTokenAddresses just like we do all the other addresses
Browse files Browse the repository at this point in the history
  • Loading branch information
ml-james committed Sep 27, 2024
1 parent 5c391ab commit 767204d
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 177 deletions.
14 changes: 0 additions & 14 deletions src/main/java/com/lmax/solana4j/Solana.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.lmax.solana4j;

import com.lmax.solana4j.api.AddressLookupTable;
import com.lmax.solana4j.api.AssociatedTokenAddress;
import com.lmax.solana4j.api.Blockhash;
import com.lmax.solana4j.api.InnerTransactionBuilder;
import com.lmax.solana4j.api.Message;
Expand Down Expand Up @@ -121,19 +120,6 @@ public static ProgramDerivedAddress programDerivedAddress(final List<byte[]> see
return SolanaEncoding.deriveProgramAddress(seeds, programId);
}

/**
* Derives an associated token address for the given owner, mint, and token program account.
*
* @param owner the public key of the owner
* @param mint the public key of the mint
* @param tokenProgramAccount the public key of the token program account
* @return a new instance of {@link AssociatedTokenAddress}
*/
public static AssociatedTokenAddress associatedTokenAddress(final PublicKey owner, final PublicKey mint, final PublicKey tokenProgramAccount)
{
return SolanaEncoding.deriveAssociatedTokenAddress(owner, mint, tokenProgramAccount);
}

/**
* Creates a new blockhash from the given byte array.
*
Expand Down
27 changes: 0 additions & 27 deletions src/main/java/com/lmax/solana4j/api/AssociatedTokenAddress.java

This file was deleted.

This file was deleted.

14 changes: 0 additions & 14 deletions src/main/java/com/lmax/solana4j/encoding/SolanaEncoding.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@


import com.lmax.solana4j.api.AddressLookupTable;
import com.lmax.solana4j.api.AssociatedTokenAddress;
import com.lmax.solana4j.api.Blockhash;
import com.lmax.solana4j.api.Destination;
import com.lmax.solana4j.api.InnerTransactionBuilder;
Expand Down Expand Up @@ -98,19 +97,6 @@ public static ProgramDerivedAddress deriveProgramAddress(final List<byte[]> seed
return SolanaProgramDerivedAddress.deriveProgramAddress(seeds, programId);
}

/**
* Derives an associated token address for the given owner, mint, and token program account.
*
* @param owner the public key of the owner
* @param mint the public key of the mint
* @param tokenProgramAccount the public key of the token program account
* @return a new instance of {@link AssociatedTokenAddress}
*/
public static AssociatedTokenAddress deriveAssociatedTokenAddress(final PublicKey owner, final PublicKey mint, final PublicKey tokenProgramAccount)
{
return SolanaAssociatedTokenAddress.deriveAssociatedTokenAddress(owner, mint, tokenProgramAccount);
}

/**
* Creates a new blockhash from the given byte array.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
import com.lmax.solana4j.api.ProgramDerivedAddress;
import com.lmax.solana4j.api.PublicKey;
import com.lmax.solana4j.api.TransactionBuilder;
import com.lmax.solana4j.encoding.SolanaEncoding;
import org.bitcoinj.core.Base58;

import java.nio.ByteOrder;
import java.util.List;

import static com.lmax.solana4j.encoding.SysVar.RENT;
import static com.lmax.solana4j.programs.SystemProgram.SYSTEM_PROGRAM_ACCOUNT;
import static java.util.Objects.requireNonNull;

/**
* Program for managing associated token accounts on the Solana blockchain.
Expand Down Expand Up @@ -97,4 +100,25 @@ public AssociatedTokenProgram createAssociatedToken(

return this;
}

/**
* Derives a program address for a given owner, token program account, and mint.
* This method generates a program-derived address based on the combination of the owner's public key,
* the token program account's public key, and the mint's public key, using the associated token program account.
*
* @param owner the owner's public key; must not be null
* @param tokenProgramAccount the token program account's public key; must not be null
* @param mint the mint's public key; must not be null
* @return the derived program address based on the provided owner, token program account, and mint public keys
* @throws NullPointerException if the owner, tokenProgramAccount, or mint is null
*/
public static ProgramDerivedAddress deriveAddress(final PublicKey owner, final PublicKey tokenProgramAccount, final PublicKey mint)
{
requireNonNull(owner, "The owner public key must be specified, but was null");
requireNonNull(tokenProgramAccount, "The token program public key must be specified, but was null");
requireNonNull(mint, "The mint public key must be specified, but was null");

return SolanaEncoding.deriveProgramAddress(List.of(owner.bytes(), tokenProgramAccount.bytes(), mint.bytes()), ASSOCIATED_TOKEN_PROGRAM_ACCOUNT);
}

}
5 changes: 2 additions & 3 deletions src/test-support/java/com/lmax/solana4j/SolanaNodeDsl.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import com.lmax.simpledsl.api.RepeatingGroup;
import com.lmax.simpledsl.api.RequiredArg;
import com.lmax.solana4j.api.AddressLookupTable;
import com.lmax.solana4j.api.AssociatedTokenAddress;
import com.lmax.solana4j.api.ProgramDerivedAddress;
import com.lmax.solana4j.api.PublicKey;
import com.lmax.solana4j.assertion.Condition;
Expand All @@ -17,6 +16,7 @@
import com.lmax.solana4j.domain.TokenProgram;
import com.lmax.solana4j.encoding.SolanaEncoding;
import com.lmax.solana4j.programs.AddressLookupTableProgram;
import com.lmax.solana4j.programs.AssociatedTokenProgram;
import com.lmax.solana4j.programs.BpfLoaderUpgradeableProgram;
import com.lmax.solana4j.programs.SystemProgram;
import com.lmax.solana4j.solanaclient.api.AccountInfo;
Expand All @@ -36,7 +36,6 @@
import static com.lmax.solana4j.assertion.Condition.isEqualTo;
import static com.lmax.solana4j.assertion.Condition.isNotNull;
import static com.lmax.solana4j.assertion.Condition.isTrue;
import static com.lmax.solana4j.encoding.SolanaEncoding.deriveAssociatedTokenAddress;
import static com.lmax.solana4j.programs.SystemProgram.MINT_ACCOUNT_LENGTH;
import static com.lmax.solana4j.programs.SystemProgram.NONCE_ACCOUNT_LENGTH;
import static com.lmax.solana4j.programs.TokenProgram.ACCOUNT_LAYOUT_SPAN;
Expand Down Expand Up @@ -581,7 +580,7 @@ public void createAssociatedTokenAccount(final String... args)
final TokenProgram tokenProgram = TokenProgram.fromName(params.value("tokenProgram"));
final TestPublicKey owner = testContext.data(TestDataType.TEST_PUBLIC_KEY).lookup(params.value("owner"));

final AssociatedTokenAddress associatedTokenAddress = deriveAssociatedTokenAddress(owner.getSolana4jPublicKey(), tokenMint.getSolana4jPublicKey(), tokenProgram.getProgram());
final ProgramDerivedAddress associatedTokenAddress = AssociatedTokenProgram.deriveAddress(owner.getSolana4jPublicKey(), tokenProgram.getProgram(), tokenMint.getSolana4jPublicKey());
testContext.data(TestDataType.TEST_PUBLIC_KEY).store(params.value("associatedTokenAddress"), new TestPublicKey(associatedTokenAddress.address().bytes()));

final TestKeyPair payer = testContext.data(TestDataType.TEST_KEY_PAIR).lookup(params.value("payer"));
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.lmax.solana4j.programs;

import com.lmax.solana4j.Solana;
import com.lmax.solana4j.api.ProgramDerivedAddress;
import com.lmax.solana4j.api.PublicKey;
import org.junit.jupiter.api.Test;

import static com.lmax.solana4j.Solana4jTestHelper.ACCOUNT1;
import static com.lmax.solana4j.Solana4jTestHelper.ACCOUNT2;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;

public class AssociatedTokenProgramTest
{
@Test
void throwsOnNullOwnerKey()
{
assertThatThrownBy(() ->
AssociatedTokenProgram.deriveAddress(null, Solana.account(ACCOUNT1), Solana.account(ACCOUNT2)))
.isInstanceOf(NullPointerException.class);
}

@Test
void throwsOnNullMintKey()
{
assertThatThrownBy(() ->
AssociatedTokenProgram.deriveAddress(Solana.account(ACCOUNT1), null, Solana.account(ACCOUNT2)))
.isInstanceOf(NullPointerException.class);
}

@Test
void throwsOnNullTokenProgramKey()
{
assertThatThrownBy(() ->
AssociatedTokenProgram.deriveAddress(Solana.account(ACCOUNT1), null, Solana.account(ACCOUNT2)))
.isInstanceOf(NullPointerException.class);
}

@Test
void shouldCreateAssociatedTokenAddress()
{
final PublicKey owner = Solana.account(ACCOUNT1);
final PublicKey mint = Solana.account(ACCOUNT2);

final ProgramDerivedAddress associatedTokenAddress = AssociatedTokenProgram.deriveAddress(
owner,
TokenProgram.PROGRAM_ACCOUNT,
mint);

assertThat(associatedTokenAddress.programId()).isEqualTo(AssociatedTokenProgram.ASSOCIATED_TOKEN_PROGRAM_ACCOUNT);
assertThat(associatedTokenAddress.address().base58()).isEqualTo("BtHaGVQ4uRjgxGSoADNkPLxQUVBFhv3GaLbrU5jQnbmh");
assertThat(associatedTokenAddress.nonce()).isEqualTo(255);
}

@Test
void shouldCreateAssociatedToken2022Address()
{
final PublicKey owner = Solana.account(ACCOUNT1);
final PublicKey mint = Solana.account(ACCOUNT2);

final ProgramDerivedAddress associatedTokenAddress = AssociatedTokenProgram.deriveAddress(
owner,
Token2022Program.PROGRAM_ACCOUNT,
mint);

assertThat(associatedTokenAddress.programId()).isEqualTo(AssociatedTokenProgram.ASSOCIATED_TOKEN_PROGRAM_ACCOUNT);
assertThat(associatedTokenAddress.address().base58()).isEqualTo("6TZpgbVr5gCryZfwJEEgTjp88DU4LWDkVA68ACUdC9gK");
assertThat(associatedTokenAddress.nonce()).isEqualTo(253);
}
}

0 comments on commit 767204d

Please sign in to comment.