diff --git a/src/Solnet.Programs/AccountCompression/AccountCompressionProgram.cs b/src/Solnet.Programs/AccountCompression/AccountCompressionProgram.cs
new file mode 100644
index 00000000..494ca694
--- /dev/null
+++ b/src/Solnet.Programs/AccountCompression/AccountCompressionProgram.cs
@@ -0,0 +1,306 @@
+using Solnet.Programs.Abstract;
+using Solnet.Programs.AccountCompression;
+using Solnet.Programs.Utilities;
+using Solnet.Rpc.Models;
+using Solnet.Wallet;
+using System;
+using System.Collections.Generic;
+using static Solnet.Programs.Models.Stake.State;
+
+
+namespace Solnet.Programs
+{
+ ///
+ /// Implements the Stake Program methods.
+ ///
+ /// For more information see:
+ /// https://docs.rs/spl-account-compression/latest/spl_account_compression/instruction/index.html
+ /// https://github.com/solana-program/account-compression/blob/ac-mainnet-tag/account-compression/sdk/src/instructions/index.ts
+ ///
+ ///
+ public static class AccountCompressionProgram
+ {
+ ///
+ /// The public key of the Stake Program.
+ ///
+ public static PublicKey ProgramIdKey = new ("compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq");
+
+ ///
+ /// The public key of the account compression program.
+ ///
+ public static readonly PublicKey ConfigKey = new("ComprConfig11111111111111111111111111111111");
+ ///
+ /// The program's name.
+ ///
+ private const string ProgramName = "Account Compression Program";
+
+
+
+ ///
+ /// Creates an instruction to append a leaf to a Merkle tree.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static TransactionInstruction Append(
+ PublicKey merkleTree,
+ PublicKey authority,
+ byte[] leaf)
+ {
+ List keys = new()
+ {
+ AccountMeta.Writable(authority, true), // Authority (signer)
+ AccountMeta.Writable(merkleTree, false), // Merkle tree account (writable)
+ };
+
+
+ return new TransactionInstruction
+ {
+ ProgramId = ProgramIdKey.KeyBytes,
+ Keys = keys,
+ Data = AccountCompressionProgramData.EncodeAppendData(leaf)
+ };
+ }
+ ///
+ /// /// Creates an instruction to close an empty Merkle tree and transfer its lamports to a recipient.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static TransactionInstruction CloseEmptyTree(
+ PublicKey merkleTree,
+ PublicKey authority,
+ PublicKey recipient
+ )
+ {
+ List keys = new()
+ {
+ AccountMeta.Writable(merkleTree, false), // Merkle tree account (writable)
+ AccountMeta.Writable(authority, true), // Authority (signer)
+ AccountMeta.ReadOnly(recipient, false) // recipient
+ };
+
+
+ return new TransactionInstruction
+ {
+ ProgramId = ProgramIdKey.KeyBytes,
+ Keys = keys,
+ Data = AccountCompressionProgramData.EncodeCloseEmptyTreeData()
+ };
+ }
+ ///
+ /// /// Creates an instruction to initialize an empty Merkle tree.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static TransactionInstruction InitEmptyMerkleTree(
+ PublicKey merkleTree,
+ PublicKey authority,
+ byte maxDepth, byte maxBufferSize
+ )
+ {
+ List keys = new()
+ {
+ AccountMeta.Writable(merkleTree, false), // Merkle tree account (writable)
+ AccountMeta.Writable(authority, true), // Authority (signer)
+ };
+
+
+ return new TransactionInstruction
+ {
+ ProgramId = ProgramIdKey.KeyBytes,
+ Keys = keys,
+ Data = AccountCompressionProgramData.EncodeInitEmptyMerkleTreeData(maxDepth, maxBufferSize)
+ };
+ }
+ ///
+ /// /// Creates an instruction to replace a leaf in a Merkle tree.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static TransactionInstruction ReplaceLeaf(
+ PublicKey merkleTree,
+ PublicKey authority,
+ byte[] newLeaf,
+ byte[] previousLeaf,
+ byte[] root,
+ uint index
+ )
+ {
+ List keys = new()
+ {
+ AccountMeta.Writable(merkleTree, false), // Merkle tree account (writable)
+ AccountMeta.Writable(authority, true), // Authority (signer)
+ };
+
+
+ return new TransactionInstruction
+ {
+ ProgramId = ProgramIdKey.KeyBytes,
+ Keys = keys,
+ Data = AccountCompressionProgramData.EncodeReplaceLeafData(newLeaf, previousLeaf, root, index)
+ };
+ }
+ ///
+ /// /// Creates an instruction to insert or append a leaf to a Merkle tree.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static TransactionInstruction InsertOrAppend(
+ PublicKey merkleTree,
+ PublicKey authority,
+ byte[] Leaf,
+ byte[] root,
+ uint index
+ )
+ {
+ List keys = new()
+ {
+ AccountMeta.Writable(merkleTree, false), // Merkle tree account (writable)
+ AccountMeta.Writable(authority, true), // Authority (signer)
+ };
+
+
+ return new TransactionInstruction
+ {
+ ProgramId = ProgramIdKey.KeyBytes,
+ Keys = keys,
+ Data = AccountCompressionProgramData.EncodeInsertOrAppendData(Leaf, root, index)
+ };
+ }
+ ///
+ /// Creates an instruction to transfer authority of a Merkle tree.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static TransactionInstruction TransferAuthority(
+ PublicKey merkleTree,
+ PublicKey authority,
+ PublicKey newAuthority
+ )
+ {
+ List keys = new()
+ {
+ AccountMeta.Writable(merkleTree, false), // Merkle tree account (writable)
+ AccountMeta.Writable(authority, true), // Authority (signer)
+ AccountMeta.ReadOnly(newAuthority, false), //new Authority
+
+ };
+
+
+ return new TransactionInstruction
+ {
+ ProgramId = ProgramIdKey.KeyBytes,
+ Keys = keys,
+ Data = AccountCompressionProgramData.EncodeTransferAuthorityData(newAuthority)
+ };
+ }
+ ///
+ /// Creates an instruction to verify a leaf in a Merkle tree.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static TransactionInstruction VerifyLeaf(
+ PublicKey merkleTree,
+ PublicKey authority,
+ byte[] root, byte[] leaf, uint index
+ )
+ {
+ List keys = new()
+ {
+ AccountMeta.Writable(merkleTree, false), // Merkle tree account (writable)
+ AccountMeta.Writable(authority, true), // Authority (signer)
+
+ };
+
+
+ return new TransactionInstruction
+ {
+ ProgramId = ProgramIdKey.KeyBytes,
+ Keys = keys,
+ Data = AccountCompressionProgramData.EncodeVerifyLeafData(root, leaf, index)
+ };
+ }
+ ///
+ /// Decodes the instruction data for the .
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static DecodedInstruction Decode(ReadOnlySpan data, IList keys, byte[] keyIndices)
+ {
+ uint instruction = data.GetU32(AccountCompressionProgramData.MethodOffset);
+
+ if (!Enum.IsDefined(typeof(NameServiceInstructions.Values), instruction))
+ {
+ return new()
+ {
+ PublicKey = ProgramIdKey,
+ InstructionName = "Unknown Instruction",
+ ProgramName = ProgramName,
+ Values = new Dictionary(),
+ InnerInstructions = new List()
+ };
+ }
+
+ AccountCompressionProgramInstructions.Values instructionValue = (AccountCompressionProgramInstructions.Values)instruction;
+
+ DecodedInstruction decodedInstruction = new()
+ {
+ PublicKey = ProgramIdKey,
+ InstructionName = AccountCompressionProgramInstructions.Names[instructionValue],
+ ProgramName = ProgramName,
+ Values = new Dictionary() { },
+ InnerInstructions = new List()
+ };
+
+ switch (instructionValue)
+ {
+ case AccountCompressionProgramInstructions.Values.Append:
+ AccountCompressionProgramData.DecodeAppendLeafData(decodedInstruction, data, keys, keyIndices);
+ break;
+ case AccountCompressionProgramInstructions.Values.InsertOrAppend:
+ AccountCompressionProgramData.DecodeInsertOrAppendData(decodedInstruction, data, keys, keyIndices);
+ break;
+ case AccountCompressionProgramInstructions.Values.VerifyLeaf:
+ AccountCompressionProgramData.DecodeVerifyLeafData(decodedInstruction, data, keys, keyIndices);
+ break;
+ case AccountCompressionProgramInstructions.Values.InitEmptyMerkleTree:
+ AccountCompressionProgramData.DecodeInitEmptyMerkleTreeData(decodedInstruction, data, keys, keyIndices);
+ break;
+ case AccountCompressionProgramInstructions.Values.CloseEmptyTree:
+ AccountCompressionProgramData.DecodeCloseEmptyTreeData(decodedInstruction, data, keys, keyIndices);
+ break;
+ case AccountCompressionProgramInstructions.Values.TransferAuthority:
+ AccountCompressionProgramData.DecodeTransferAuthorityInstruction(decodedInstruction, data, keys, keyIndices);
+ break;
+ case AccountCompressionProgramInstructions.Values.ReplaceLeaf:
+ AccountCompressionProgramData.DecodeReplaceLeafData(decodedInstruction, data, keys, keyIndices);
+ break;
+
+ }
+ return decodedInstruction;
+ }
+ }
+}
diff --git a/src/Solnet.Programs/AccountCompression/AccountCompressionProgramData.cs b/src/Solnet.Programs/AccountCompression/AccountCompressionProgramData.cs
new file mode 100644
index 00000000..aec1c52c
--- /dev/null
+++ b/src/Solnet.Programs/AccountCompression/AccountCompressionProgramData.cs
@@ -0,0 +1,413 @@
+using Solnet.Programs.Utilities;
+using Solnet.Wallet;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using static Solnet.Programs.Models.Stake.State;
+
+namespace Solnet.Programs.AccountCompression
+{
+ ///
+ /// Represents the instruction data for the .
+ ///
+ internal static class AccountCompressionProgramData
+ {
+ ///
+ /// The offset for the instruction discriminator in the instruction data.
+ ///
+ internal const int MethodOffset = 0;
+ ///
+ /// Encode the Append instruction data
+ ///
+ /// 32-byte leaf data
+ /// Encoded byte array for the instruction data
+ public static byte[] EncodeAppendData(byte[] leaf)
+ {
+ if (leaf == null || leaf.Length != 32)
+ throw new ArgumentException("Leaf must be 32 bytes");
+
+ byte[] data = new byte[4 + 32]; // 4 bytes discriminator + 32 bytes leaf
+ int offset = 0;
+
+ // Write the 4-byte discriminator
+ BitConverter.GetBytes((uint)AccountCompressionProgramInstructions.Values.Append).CopyTo(data, MethodOffset);
+ offset += 4;
+
+ // Write the 32-byte leaf data
+ Buffer.BlockCopy(leaf, 0, data, offset, 32);
+
+ return data;
+ }
+ ///
+ /// Encodes the CloseEmptyTree instruction data.
+ ///
+ ///
+ internal static byte[] EncodeCloseEmptyTreeData()
+ {
+ // Only the discriminator (first 4 bytes) is needed
+ var data = new byte[4];
+
+ // You should replace this with the actual discriminator used by your Anchor program
+ // For example: discriminator = Hash("global:close_empty_tree").Take(8) or a known constant
+ uint discriminator = (uint)AccountCompressionProgramInstructions.Values.CloseEmptyTree;
+
+ data.WriteU32(discriminator, 0);
+ return data;
+ }
+ ///
+ /// Encodes the InitEmptyMerkleTree instruction data.
+ ///
+ ///
+ ///
+ ///
+ internal static byte[] EncodeInitEmptyMerkleTreeData(byte maxDepth, byte maxBufferSize)
+ {
+ // Total size = 4 (discriminator) + 1 (maxDepth) + 1 (maxBufferSize) = 6 bytes
+ byte[] data = new byte[6];
+ int offset = 0;
+
+ // 1. Instruction Discriminator (4 bytes)
+ BitConverter.GetBytes((uint)AccountCompressionProgramInstructions.Values.InitEmptyMerkleTree)
+ .CopyTo(data, MethodOffset);
+ offset += 4;
+
+ // 2. Max Depth (1 byte)
+ data[offset++] = maxDepth;
+
+ // 3. Max Buffer Size (1 byte)
+ data[offset] = maxBufferSize;
+
+ return data;
+ }
+ ///
+ /// Encodes the ReplaceLeaf instruction data.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static byte[] EncodeReplaceLeafData(
+ byte[] newLeaf,
+ byte[] previousLeaf,
+ byte[] root,
+ uint index
+ )
+ {
+ // Validate inputs
+ if (newLeaf == null || newLeaf.Length != 32) throw new ArgumentException("newLeaf must be 32 bytes");
+ if (previousLeaf == null || previousLeaf.Length != 32) throw new ArgumentException("previousLeaf must be 32 bytes");
+ if (root == null || root.Length != 32) throw new ArgumentException("root must be 32 bytes");
+
+ // Total size: 4 (discriminator) + 32 (root) + 32 (previousLeaf) + 32 (newLeaf) + 4 (index) = 104 bytes
+ byte[] data = new byte[104];
+ int offset = 0;
+
+ // 1. Instruction Discriminator (4 bytes)
+ BitConverter.GetBytes((uint)AccountCompressionProgramInstructions.Values.ReplaceLeaf)
+ .CopyTo(data, MethodOffset);
+ offset += 4;
+
+ // 2. Root [32 bytes]
+ Buffer.BlockCopy(root, 0, data, offset, 32);
+ offset += 32;
+
+ // 3. Previous Leaf [32 bytes]
+ Buffer.BlockCopy(previousLeaf, 0, data, offset, 32);
+ offset += 32;
+
+ // 4. New Leaf [32 bytes]
+ Buffer.BlockCopy(newLeaf, 0, data, offset, 32);
+ offset += 32;
+
+ // 5. Index [4 bytes]
+ BitConverter.GetBytes(index).CopyTo(data, offset);
+
+ return data;
+ }
+
+ ///
+ /// Encodes the InsertOrAppend instruction data.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static byte[] EncodeInsertOrAppendData(
+ byte[] Leaf,
+ byte[] root,
+ uint index
+ )
+ {
+ // Validate inputs
+ if (root == null || root.Length != 32) throw new ArgumentException("root must be 32 bytes");
+
+ // Total size: 4 (discriminator) + 32 (root) + 32 (Leaf) + 4 (index) = 104 bytes
+ byte[] data = new byte[72];
+ int offset = 0;
+
+ // 1. Instruction Discriminator (4 bytes)
+ BitConverter.GetBytes((uint)AccountCompressionProgramInstructions.Values.InsertOrAppend)
+ .CopyTo(data, MethodOffset);
+ offset += 4;
+
+ // 2. Root [32 bytes]
+ Buffer.BlockCopy(root, 0, data, offset, 32);
+ offset += 32;
+
+ // 3. Leaf [32 bytes]
+ Buffer.BlockCopy(Leaf, 0, data, offset, 32);
+ offset += 32;
+
+ // 4. Index [4 bytes]
+ BitConverter.GetBytes(index).CopyTo(data, offset);
+
+ return data;
+ }
+
+ ///
+ /// Encodes the TransferAuthority instruction data.
+ ///
+ ///
+ ///
+ ///
+ internal static byte[] EncodeTransferAuthorityData(PublicKey newAuthority)
+ {
+ if (newAuthority is null) throw new ArgumentNullException(nameof(newAuthority));
+
+ byte[] data = new byte[36];
+
+ // 1. Add instruction discriminator
+ data.WriteU32((uint)AccountCompressionProgramInstructions.Values.TransferAuthority,MethodOffset);
+
+ // 2. Add new authority public key (32 bytes)
+ data.WritePubKey(newAuthority, 4);
+
+ return data;
+ }
+ ///
+ /// Encodes the VerifyLeaf instruction data.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static byte[] EncodeVerifyLeafData(byte[] root, byte[] leaf, uint index)
+ {
+ if (root == null || root.Length != 32) throw new ArgumentException("Root must be 32 bytes", nameof(root));
+ if (leaf == null || leaf.Length != 32) throw new ArgumentException("Leaf must be 32 bytes", nameof(leaf));
+
+ // Total size: 4 (discriminator) + 32 (root) + 32 (leaf) + 4 (index) = 72 bytes
+ byte[] data = new byte[72];
+ int offset = 0;
+
+ // 1. Instruction Discriminator (4 bytes)
+ BitConverter.GetBytes((uint)AccountCompressionProgramInstructions.Values.VerifyLeaf).CopyTo(data, MethodOffset);
+ offset += 4;
+
+ // 2. Root (32 bytes)
+ Buffer.BlockCopy(root, 0, data, offset, 32);
+ offset += 32;
+
+ // 3. Leaf (32 bytes)
+ Buffer.BlockCopy(leaf, 0, data, offset, 32);
+ offset += 32;
+
+ // 4. Index (4 bytes)
+ BitConverter.GetBytes(index).CopyTo(data, offset);
+
+ return data;
+ }
+ ///
+ /// Decodes the AppendLeaf instruction data.
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static void DecodeAppendLeafData(
+ DecodedInstruction decodedInstruction,
+ ReadOnlySpan data,
+ IList keys,
+ byte[] keyIndices)
+ {
+ // Decode accounts
+ decodedInstruction.Values.Add("authority", keys[keyIndices[0]]);
+ decodedInstruction.Values.Add("merkle_tree", keys[keyIndices[1]]);
+
+ // Instruction data offsets:
+ // 0..4 = discriminator
+ // 4..36 = leaf (32 bytes)
+ var leafBytes = data.GetBytes(4, 32);
+ decodedInstruction.Values.Add("leaf", leafBytes);
+ }
+ ///
+ /// Decodes the CloseEmptyTree instruction data.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static void DecodeCloseEmptyTreeData(
+ DecodedInstruction decodedInstruction,
+ ReadOnlySpan data,
+ IList keys,
+ byte[] keyIndices)
+ {
+ if (data.Length < 4) throw new ArgumentException("Instruction data too short.");
+
+ uint discriminator = BitConverter.ToUInt32(data.Slice(0, 4));
+
+ decodedInstruction.Values ??= new Dictionary();
+ decodedInstruction.Values.Add("instruction", discriminator);
+
+ // Match accounts: [merkle_tree, authority, recipient]
+ if (keyIndices.Length < 3) throw new ArgumentException("Insufficient account keys for CloseEmptyTree");
+
+ decodedInstruction.Values.Add("merkle_tree", keys[keyIndices[0]]);
+ decodedInstruction.Values.Add("authority", keys[keyIndices[1]]);
+ decodedInstruction.Values.Add("recipient", keys[keyIndices[2]]);
+ }
+ ///
+ /// Decodes the InitEmptyMerkleTree instruction data.
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static void DecodeInitEmptyMerkleTreeData(
+ DecodedInstruction decodedInstruction,
+ ReadOnlySpan data,
+ IList keys,
+ byte[] keyIndices)
+ {
+ // Accounts used
+ decodedInstruction.Values.Add("merkle_tree", keys[keyIndices[0]]);
+ decodedInstruction.Values.Add("authority", keys[keyIndices[1]]);
+
+ // Data: [4 bytes discriminator][1 byte maxDepth][1 byte maxBufferSize]
+ decodedInstruction.Values.Add("max_depth", data[4]);
+ decodedInstruction.Values.Add("max_buffer_size", data[5]);
+ }
+ ///
+ /// Decodes the ReplaceLeaf instruction data.
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static void DecodeReplaceLeafData(
+ DecodedInstruction decodedInstruction,
+ ReadOnlySpan data,
+ IList keys,
+ byte[] keyIndices)
+ {
+ // Decode accounts
+
+ decodedInstruction.Values.Add("merkle_tree", keys[keyIndices[0]]);
+ decodedInstruction.Values.Add("authority", keys[keyIndices[1]]);
+
+ // 0..4 = discriminator
+ // 4..36 = root
+ // 36..68 = previousLeaf
+ // 68..100 = newLeaf
+ // 100..104 = index (uint32)
+
+ decodedInstruction.Values.Add("root", data.GetBytes(4, 32));
+ decodedInstruction.Values.Add("previous_leaf", data.GetBytes(36, 32));
+ decodedInstruction.Values.Add("new_leaf", data.GetBytes(68, 32));
+ decodedInstruction.Values.Add("index", data.GetU32(100));
+ }
+ ///
+ /// Decodes the InsertOrAppend instruction data.
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static void DecodeInsertOrAppendData(
+ DecodedInstruction decodedInstruction,
+ ReadOnlySpan data,
+ IList keys,
+ byte[] keyIndices)
+ {
+ // Decode accounts
+
+ decodedInstruction.Values.Add("merkle_tree", keys[keyIndices[0]]);
+ decodedInstruction.Values.Add("authority", keys[keyIndices[1]]);
+
+ // 0..4 = discriminator
+ // 4..36 = root
+ // 36..68 = previousLeaf
+ // 68..100 = newLeaf
+ // 100..104 = index (uint32)
+
+ decodedInstruction.Values.Add("root", data.GetBytes(4,32));
+ decodedInstruction.Values.Add("leaf", data.GetBytes(36,32));
+ decodedInstruction.Values.Add("index", data.GetU32(68));
+ }
+ ///
+ /// Decodes the TransferAuthority instruction data.
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static void DecodeTransferAuthorityInstruction(
+ DecodedInstruction decodedInstruction,
+ ReadOnlySpan data,
+ IList keys,
+ byte[] keyIndices)
+ {
+ // Decode accounts
+
+ decodedInstruction.Values.Add("merkle_tree", keys[keyIndices[0]]);
+ decodedInstruction.Values.Add("authority", keys[keyIndices[1]]);
+ decodedInstruction.Values.Add("new_authority", keys[keyIndices[2]]);
+
+ // Instruction data offsets:
+ // 0..4 = discriminator
+ // 4..36 = newAuthority public key
+ var newAuthority = data.GetPubKey(4);
+ decodedInstruction.Values.Add("new_authority", newAuthority);
+ }
+ ///
+ /// Decodes the VerifyLeaf instruction data.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static void DecodeVerifyLeafData(
+ DecodedInstruction decodedInstruction,
+ ReadOnlySpan data,
+ IList keys,
+ byte[] keyIndices)
+ {
+ // This instruction typically has only one key: merkleTree
+ decodedInstruction.Values.Add("merkle_tree", keys[keyIndices[0]]);
+
+ // Decode data
+ if (data.Length < 72) throw new ArgumentException("Invalid data length for VerifyLeaf");
+
+ var root = data.GetBytes(4,32);
+ var leaf = data.GetBytes(36,32);
+ var index = data.GetU32(68);
+
+ decodedInstruction.Values.Add("root", root);
+ decodedInstruction.Values.Add("leaf", leaf);
+ decodedInstruction.Values.Add("index", index);
+ }
+ }
+
+
+}
diff --git a/src/Solnet.Programs/AccountCompression/AccountCompressionProgramInstructions.cs b/src/Solnet.Programs/AccountCompression/AccountCompressionProgramInstructions.cs
new file mode 100644
index 00000000..0958c4a0
--- /dev/null
+++ b/src/Solnet.Programs/AccountCompression/AccountCompressionProgramInstructions.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Solnet.Programs
+{
+ ///
+ /// Represents the instruction types for the ??? along with a friendly name so as not to use reflection ???.
+ ///
+ /// For more information see:
+ /// https://docs.rs/spl-account-compression/latest/spl_account_compression/instruction/index.html
+ /// https://github.com/solana-program/account-compression/blob/ac-mainnet-tag/account-compression/sdk/src/instructions/index.ts
+ ///
+ ///
+ internal static class AccountCompressionProgramInstructions
+ {
+ ///
+ /// Represents the user-friendly names for the instruction types for the .
+ ///
+ internal static readonly Dictionary Names = new()
+ {
+ { Values.Append, "Append Merkle Tree" },
+ { Values.CloseEmptyTree, "Close Empty Tree" },
+ { Values.InitEmptyMerkleTree, "Init Empty Merkle Tree" },
+ { Values.ReplaceLeaf, "Replace Leaf" },
+ { Values.InsertOrAppend, "Insert Or Append Leaf" },
+ { Values.TransferAuthority, "Transfer Authority" },
+ { Values.VerifyLeaf, "Verify Leaf" }
+ };
+ ///
+ /// Represents the instruction types for the .
+ ///
+ internal enum Values: uint
+ {
+ Append = 0,
+ CloseEmptyTree = 1,
+ InitEmptyMerkleTree = 2,
+ ReplaceLeaf = 3,
+ InsertOrAppend = 4,
+ TransferAuthority = 5,
+ VerifyLeaf = 6
+ }
+ }
+}
diff --git a/src/Solnet.Programs/InstructionDecoder.cs b/src/Solnet.Programs/InstructionDecoder.cs
index a6d060f7..2aaa6a58 100644
--- a/src/Solnet.Programs/InstructionDecoder.cs
+++ b/src/Solnet.Programs/InstructionDecoder.cs
@@ -1,4 +1,5 @@
-using Solnet.Programs.TokenSwap;
+using Solnet.Programs.AccountCompression;
+using Solnet.Programs.TokenSwap;
using Solnet.Rpc.Builders;
using Solnet.Rpc.Models;
using Solnet.Wallet;
@@ -38,6 +39,7 @@ static InstructionDecoder()
InstructionDictionary.Add(NameServiceProgram.ProgramIdKey, NameServiceProgram.Decode);
InstructionDictionary.Add(SharedMemoryProgram.ProgramIdKey, SharedMemoryProgram.Decode);
InstructionDictionary.Add(StakeProgram.ProgramIdKey, StakeProgram.Decode);
+ InstructionDictionary.Add(AccountCompressionProgram.ProgramIdKey, AccountCompressionProgram.Decode);
}
///
diff --git a/test/Solnet.Programs.Test/AccountCompressionProgramTest.cs b/test/Solnet.Programs.Test/AccountCompressionProgramTest.cs
new file mode 100644
index 00000000..6ddac027
--- /dev/null
+++ b/test/Solnet.Programs.Test/AccountCompressionProgramTest.cs
@@ -0,0 +1,116 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Solnet.Wallet;
+using System.Linq;
+
+namespace Solnet.Programs.Test
+{
+ [TestClass]
+ public class AccountCompressionProgramTest
+ {
+ private static readonly PublicKey MerkleTree = new("11111111111111111111111111111111");
+ private static readonly PublicKey Authority = new("22222222222222222222222222222222");
+ private static readonly PublicKey Recipient = new("33333333333333333333333333333333");
+ private static readonly PublicKey NewAuthority = new("44444444444444444444444444444444");
+
+ [TestMethod]
+ public void Append_CreatesCorrectInstruction()
+ {
+ var leaf = Enumerable.Range(0, 32).Select(i => (byte)i).ToArray();
+ var instr = AccountCompressionProgram.Append(MerkleTree, Authority, leaf);
+
+ CollectionAssert.AreEqual(AccountCompressionProgram.ProgramIdKey.KeyBytes, instr.ProgramId);
+ Assert.AreEqual(2, instr.Keys.Count);
+ Assert.IsTrue(instr.Keys[0].IsWritable && instr.Keys[0].IsSigner); // Authority
+ Assert.IsTrue(instr.Keys[1].IsWritable && !instr.Keys[1].IsSigner); // MerkleTree
+ Assert.AreEqual(36, instr.Data.Length);
+ }
+
+ [TestMethod]
+ public void CloseEmptyTree_CreatesCorrectInstruction()
+ {
+ var instr = AccountCompressionProgram.CloseEmptyTree(MerkleTree, Authority, Recipient);
+
+ CollectionAssert.AreEqual(AccountCompressionProgram.ProgramIdKey.KeyBytes, instr.ProgramId);
+ Assert.AreEqual(3, instr.Keys.Count);
+ Assert.IsTrue(instr.Keys[0].IsWritable); // MerkleTree
+ Assert.IsTrue(instr.Keys[1].IsWritable && instr.Keys[1].IsSigner); // Authority
+ Assert.IsFalse(instr.Keys[2].IsWritable); // Recipient
+ Assert.AreEqual(4, instr.Data.Length);
+ }
+
+ [TestMethod]
+ public void InitEmptyMerkleTree_CreatesCorrectInstruction()
+ {
+ byte maxDepth = 10, maxBufferSize = 20;
+ var instr = AccountCompressionProgram.InitEmptyMerkleTree(MerkleTree, Authority, maxDepth, maxBufferSize);
+
+ CollectionAssert.AreEqual(AccountCompressionProgram.ProgramIdKey.KeyBytes, instr.ProgramId);
+ Assert.AreEqual(2, instr.Keys.Count);
+ Assert.IsTrue(instr.Keys[0].IsWritable); // MerkleTree
+ Assert.IsTrue(instr.Keys[1].IsWritable && instr.Keys[1].IsSigner); // Authority
+ Assert.AreEqual(6, instr.Data.Length);
+ }
+
+ [TestMethod]
+ public void ReplaceLeaf_CreatesCorrectInstruction()
+ {
+ var newLeaf = Enumerable.Repeat((byte)1, 32).ToArray();
+ var prevLeaf = Enumerable.Repeat((byte)2, 32).ToArray();
+ var root = Enumerable.Repeat((byte)3, 32).ToArray();
+ uint index = 42;
+
+ var instr = AccountCompressionProgram.ReplaceLeaf(MerkleTree, Authority, newLeaf, prevLeaf, root, index);
+
+ CollectionAssert.AreEqual(AccountCompressionProgram.ProgramIdKey.KeyBytes, instr.ProgramId);
+ Assert.AreEqual(2, instr.Keys.Count);
+ Assert.IsTrue(instr.Keys[0].IsWritable); // MerkleTree
+ Assert.IsTrue(instr.Keys[1].IsWritable && instr.Keys[1].IsSigner); // Authority
+ Assert.AreEqual(104, instr.Data.Length);
+ }
+
+ [TestMethod]
+ public void InsertOrAppend_CreatesCorrectInstruction()
+ {
+ var leaf = Enumerable.Repeat((byte)1, 32).ToArray();
+ var root = Enumerable.Repeat((byte)2, 32).ToArray();
+ uint index = 99;
+
+ var instr = AccountCompressionProgram.InsertOrAppend(MerkleTree, Authority, leaf, root, index);
+
+ CollectionAssert.AreEqual(AccountCompressionProgram.ProgramIdKey.KeyBytes, instr.ProgramId);
+ Assert.AreEqual(2, instr.Keys.Count);
+ Assert.IsTrue(instr.Keys[0].IsWritable); // MerkleTree
+ Assert.IsTrue(instr.Keys[1].IsWritable && instr.Keys[1].IsSigner); // Authority
+ Assert.AreEqual(72, instr.Data.Length);
+ }
+
+ [TestMethod]
+ public void TransferAuthority_CreatesCorrectInstruction()
+ {
+ var instr = AccountCompressionProgram.TransferAuthority(MerkleTree, Authority, NewAuthority);
+
+ CollectionAssert.AreEqual(AccountCompressionProgram.ProgramIdKey.KeyBytes, instr.ProgramId);
+ Assert.AreEqual(3, instr.Keys.Count);
+ Assert.IsTrue(instr.Keys[0].IsWritable); // MerkleTree
+ Assert.IsTrue(instr.Keys[1].IsWritable && instr.Keys[1].IsSigner); // Authority
+ Assert.IsFalse(instr.Keys[2].IsWritable); // NewAuthority
+ Assert.AreEqual(36, instr.Data.Length);
+ }
+
+ [TestMethod]
+ public void VerifyLeaf_CreatesCorrectInstruction()
+ {
+ var root = Enumerable.Repeat((byte)1, 32).ToArray();
+ var leaf = Enumerable.Repeat((byte)2, 32).ToArray();
+ uint index = 123;
+
+ var instr = AccountCompressionProgram.VerifyLeaf(MerkleTree, Authority, root, leaf, index);
+
+ CollectionAssert.AreEqual(AccountCompressionProgram.ProgramIdKey.KeyBytes, instr.ProgramId);
+ Assert.AreEqual(2, instr.Keys.Count);
+ Assert.IsTrue(instr.Keys[0].IsWritable); // MerkleTree
+ Assert.IsTrue(instr.Keys[1].IsWritable && instr.Keys[1].IsSigner); // Authority
+ Assert.AreEqual(72, instr.Data.Length);
+ }
+ }
+}