diff --git a/src/integration-test/java/com/lmax/solana4j/programs/SystemProgramIntegrationTest.java b/src/integration-test/java/com/lmax/solana4j/programs/SystemProgramIntegrationTest.java new file mode 100644 index 0000000..bd1761e --- /dev/null +++ b/src/integration-test/java/com/lmax/solana4j/programs/SystemProgramIntegrationTest.java @@ -0,0 +1,41 @@ +package com.lmax.solana4j.programs; + +import com.lmax.solana4j.base.IntegrationTestBase; +import com.lmax.solana4j.base.ParameterizedMessageEncodingTest; +import org.junit.jupiter.api.BeforeEach; + +public class SystemProgramIntegrationTest extends IntegrationTestBase +{ + @BeforeEach + void beforeEachTest() + { + solana.createKeyPair("payer"); + solana.airdrop("payer", "100"); + } + + @ParameterizedMessageEncodingTest + void shouldCreateNonce(final String messageEncoding) + { + solana.setMessageEncoding(messageEncoding); + + solana.createKeyPair("account"); + solana.createKeyPair("authority"); + + solana.airdrop("address: authority", "amountSol: 10"); + solana.createNonceAccount("account", "authority", "payer"); + } + + @ParameterizedMessageEncodingTest + void shouldAdvanceNonce(final String messageEncoding) + { + solana.setMessageEncoding(messageEncoding); + + solana.createKeyPair("account"); + solana.createKeyPair("authority"); + + solana.airdrop("address: authority", "amountSol: 10"); + solana.createNonceAccount("account", "authority", "payer"); + + solana.advanceNonce("account", "authority", "payer"); + } +} diff --git a/src/integration-test/java/com/lmax/solana4j/programs/TokenProgramIntegrationTest.java b/src/integration-test/java/com/lmax/solana4j/programs/TokenProgramIntegrationTest.java index 4544aab..f8d551d 100644 --- a/src/integration-test/java/com/lmax/solana4j/programs/TokenProgramIntegrationTest.java +++ b/src/integration-test/java/com/lmax/solana4j/programs/TokenProgramIntegrationTest.java @@ -43,18 +43,6 @@ void shouldMintToToken(final String messageEncoding, final String tokenProgram) solana.tokenBalance("tokenAccount", "0.0000000000000001"); } - @ParameterizedTokenTest - void shouldCreateNonce(final String messageEncoding, final String tokenProgram) - { - solana.setMessageEncoding(messageEncoding); - - solana.createKeyPair("account"); - solana.createKeyPair("authority"); - - solana.airdrop("address: authority", "amountSol: 10"); - solana.createNonceAccount("account", "authority", "payer", tokenProgram); - } - @ParameterizedTokenTest void shouldTransferToken(final String messageEncoding, final String tokenProgram) { diff --git a/src/main/java/com/lmax/solana4j/programs/TokenProgram.java b/src/main/java/com/lmax/solana4j/programs/TokenProgram.java index 45e8a5b..ae6c2b2 100644 --- a/src/main/java/com/lmax/solana4j/programs/TokenProgram.java +++ b/src/main/java/com/lmax/solana4j/programs/TokenProgram.java @@ -64,7 +64,7 @@ public static TokenProgram factory(final TransactionBuilderBase tb) } - public T createInitializeAccountInstruction(final PublicKey account, final PublicKey mint, final PublicKey owner) + public T createInitializeTokenAccountInstruction(final PublicKey account, final PublicKey mint, final PublicKey owner) { tb.append(ib -> ib .program(programId) @@ -77,7 +77,7 @@ public T createInitializeAccountInstruction(final PublicKey account, final Publi return (T) this; } - public T createInitializeMintInstruction( + public T createInitializeMintAccountInstruction( final PublicKey tokenMintAddress, final byte decimals, final PublicKey mintAuthority, diff --git a/src/test-support/java/com/lmax/solana4j/SolanaDriver.java b/src/test-support/java/com/lmax/solana4j/SolanaDriver.java index 6081c2e..b085c30 100644 --- a/src/test-support/java/com/lmax/solana4j/SolanaDriver.java +++ b/src/test-support/java/com/lmax/solana4j/SolanaDriver.java @@ -235,6 +235,25 @@ public String createNonceAccount( return solanaApi.sendTransaction(transactionBlob, Commitment.FINALIZED); } + public String advanceNonce( + final TestKeyPair account, + final TestKeyPair authority, + final TestKeyPair payer, + final List addressLookupTables) + { + final Blockhash blockhash = solanaApi.getRecentBlockHash(); + + final String transactionBlob = getTransactionFactory().advanceNonce( + account.getSolana4jPublicKey(), + authority.getSolana4jPublicKey(), + Solana.blockhash(blockhash.getBytes()), + payer.getSolana4jPublicKey(), + List.of(payer, authority), + addressLookupTables); + + return solanaApi.sendTransaction(transactionBlob, Commitment.FINALIZED); + } + public String tokenTransfer( final TokenProgram tokenProgram, final TestKeyPair from, diff --git a/src/test-support/java/com/lmax/solana4j/SolanaNodeDsl.java b/src/test-support/java/com/lmax/solana4j/SolanaNodeDsl.java index e542268..eea95ff 100644 --- a/src/test-support/java/com/lmax/solana4j/SolanaNodeDsl.java +++ b/src/test-support/java/com/lmax/solana4j/SolanaNodeDsl.java @@ -320,7 +320,6 @@ public void createNonceAccount(final String... args) new RequiredArg("account"), new RequiredArg("authority"), new RequiredArg("payer"), - new OptionalArg("tokenProgram").setAllowedValues("Token", "Token2022").setDefault("Token"), new OptionalArg("addressLookupTables").setAllowMultipleValues() ); @@ -335,4 +334,26 @@ public void createNonceAccount(final String... args) new Waiter().waitFor(new IsNotNullAssertion<>(() -> solanaDriver.getTransactionResponse(transactionSignature, FINALIZED).getTransaction())); } + + public void advanceNonce(final String... args) + { + final DslParams params = DslParams.create( + args, + new RequiredArg("account"), + new RequiredArg("authority"), + new RequiredArg("payer"), + new OptionalArg("addressLookupTables").setAllowMultipleValues() + ); + + final TestKeyPair account = testContext.getKeyPair(params.value("account")); + final TestKeyPair authority = testContext.getKeyPair(params.value("authority")); + final TestKeyPair payer = testContext.getKeyPair(params.value("payer")); + final List addressLookupTables = params.valuesAsList("addressLookupTables").stream() + .map(testContext::getAddressLookupTable) + .toList(); + + final String transactionSignature = solanaDriver.advanceNonce(account, authority, payer, addressLookupTables); + + new Waiter().waitFor(new IsNotNullAssertion<>(() -> solanaDriver.getTransactionResponse(transactionSignature, FINALIZED).getTransaction())); + } } diff --git a/src/test-support/java/com/lmax/solana4j/transaction/LegacyTransactionFactory.java b/src/test-support/java/com/lmax/solana4j/transaction/LegacyTransactionFactory.java index 315dfa6..cffc52e 100644 --- a/src/test-support/java/com/lmax/solana4j/transaction/LegacyTransactionFactory.java +++ b/src/test-support/java/com/lmax/solana4j/transaction/LegacyTransactionFactory.java @@ -204,7 +204,7 @@ public String createMintAccount( accountSpan, tokenProgram.getProgram())) .instructions(builder -> tokenProgram.getFactory().factory(builder) - .createInitializeMintInstruction( + .createInitializeMintAccountInstruction( account, (byte) decimals, mintAuthority, @@ -287,7 +287,7 @@ public String initializeTokenAccount( accountSpan, tokenProgram.getProgram())) .instructions(legacyTransactionBuilder -> tokenProgram.getFactory().factory(legacyTransactionBuilder) - .createInitializeAccountInstruction( + .createInitializeTokenAccountInstruction( account, mint, owner)) @@ -393,6 +393,36 @@ public String extendAddressLookupTable( return base58encode(buffer); } + @Override + public String advanceNonce( + final PublicKey account, + final PublicKey authority, + final Blockhash blockhash, + final PublicKey payer, + final List signers, + final List addressLookupTables) + { + final ByteBuffer buffer = ByteBuffer.allocate(Solana.MAX_MESSAGE_SIZE); + Solana.builder(buffer) + .legacy() + .recent(blockhash) + .instructions(tb -> SystemProgram.factory(tb) + .nonceAdvance(account, authority)) + .payer(payer) + .seal() + .unsigned() + .build(); + + final SignedMessageBuilder signedMessageBuilder = Solana.forSigning(buffer); + for (final TestKeyPair signer : signers) + { + signedMessageBuilder.by(signer.getSolana4jPublicKey(), new BouncyCastleSigner(signer.getPrivateKeyBytes())); + } + + signedMessageBuilder.build(); + return base58encode(buffer); + } + private static String base58encode(final ByteBuffer bytes) { return Base58.encode(ByteBufferPrimitiveArray.copy(bytes)); diff --git a/src/test-support/java/com/lmax/solana4j/transaction/TransactionFactory.java b/src/test-support/java/com/lmax/solana4j/transaction/TransactionFactory.java index 606419e..9661b86 100644 --- a/src/test-support/java/com/lmax/solana4j/transaction/TransactionFactory.java +++ b/src/test-support/java/com/lmax/solana4j/transaction/TransactionFactory.java @@ -115,4 +115,12 @@ String extendAddressLookupTable( PublicKey payer, List signers, List addressLookupTables); + + String advanceNonce( + PublicKey account, + PublicKey authority, + Blockhash blockhash, + PublicKey payer, + List signers, + List addressLookupTables); } diff --git a/src/test-support/java/com/lmax/solana4j/transaction/V0TransactionFactory.java b/src/test-support/java/com/lmax/solana4j/transaction/V0TransactionFactory.java index 423124c..a1e121c 100644 --- a/src/test-support/java/com/lmax/solana4j/transaction/V0TransactionFactory.java +++ b/src/test-support/java/com/lmax/solana4j/transaction/V0TransactionFactory.java @@ -201,7 +201,7 @@ public String createMintAccount( accountSpan, tokenProgram.getProgram())) .instructions(builder -> tokenProgram.getFactory().factory(builder) - .createInitializeMintInstruction( + .createInitializeMintAccountInstruction( account, (byte) decimals, mintAuthority, @@ -285,7 +285,7 @@ public String initializeTokenAccount( accountSpan, tokenProgram.getProgram())) .instructions(legacyTransactionBuilder -> tokenProgram.getFactory().factory(legacyTransactionBuilder) - .createInitializeAccountInstruction( + .createInitializeTokenAccountInstruction( account, mint, owner)) @@ -394,6 +394,37 @@ public String extendAddressLookupTable( return base58encode(buffer); } + @Override + public String advanceNonce( + final PublicKey account, + final PublicKey authority, + final Blockhash blockhash, + final PublicKey payer, + final List signers, + final List addressLookupTables) + { + final ByteBuffer buffer = ByteBuffer.allocate(Solana.MAX_MESSAGE_SIZE); + Solana.builder(buffer) + .v0() + .recent(blockhash) + .instructions(tb -> SystemProgram.factory(tb) + .nonceAdvance(account, authority)) + .payer(payer) + .lookups(addressLookupTables) + .seal() + .unsigned() + .build(); + + final SignedMessageBuilder signedMessageBuilder = Solana.forSigning(buffer); + for (final TestKeyPair signer : signers) + { + signedMessageBuilder.by(signer.getSolana4jPublicKey(), new BouncyCastleSigner(signer.getPrivateKeyBytes())); + } + + signedMessageBuilder.build(); + return base58encode(buffer); + } + private static String base58encode(final ByteBuffer bytes) { return Base58.encode(ByteBufferPrimitiveArray.copy(bytes));