Skip to content

Commit 6e5bf67

Browse files
authored
Merge pull request #498 from AltudePlatform/account-compression-program
Account compression program
2 parents 632c6be + 6feec68 commit 6e5bf67

5 files changed

Lines changed: 884 additions & 1 deletion

File tree

Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
using Solnet.Programs.Abstract;
2+
using Solnet.Programs.AccountCompression;
3+
using Solnet.Programs.Utilities;
4+
using Solnet.Rpc.Models;
5+
using Solnet.Wallet;
6+
using System;
7+
using System.Collections.Generic;
8+
using static Solnet.Programs.Models.Stake.State;
9+
10+
11+
namespace Solnet.Programs
12+
{
13+
/// <summary>
14+
/// Implements the Stake Program methods.
15+
/// <remarks>
16+
/// For more information see:
17+
/// https://docs.rs/spl-account-compression/latest/spl_account_compression/instruction/index.html
18+
/// https://github.com/solana-program/account-compression/blob/ac-mainnet-tag/account-compression/sdk/src/instructions/index.ts
19+
/// </remarks>
20+
/// </summary>
21+
public static class AccountCompressionProgram
22+
{
23+
/// <summary>
24+
/// The public key of the Stake Program.
25+
/// </summary>
26+
public static PublicKey ProgramIdKey = new ("compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq");
27+
28+
/// <summary>
29+
/// The public key of the account compression program.
30+
/// </summary>
31+
public static readonly PublicKey ConfigKey = new("ComprConfig11111111111111111111111111111111");
32+
/// <summary>
33+
/// The program's name.
34+
/// </summary>
35+
private const string ProgramName = "Account Compression Program";
36+
37+
38+
39+
/// <summary>
40+
/// Creates an instruction to append a leaf to a Merkle tree.
41+
/// </summary>
42+
/// <param name="merkleTree"></param>
43+
/// <param name="authority"></param>
44+
/// <param name="leaf"></param>
45+
/// <returns></returns>
46+
public static TransactionInstruction Append(
47+
PublicKey merkleTree,
48+
PublicKey authority,
49+
byte[] leaf)
50+
{
51+
List<AccountMeta> keys = new()
52+
{
53+
AccountMeta.Writable(authority, true), // Authority (signer)
54+
AccountMeta.Writable(merkleTree, false), // Merkle tree account (writable)
55+
};
56+
57+
58+
return new TransactionInstruction
59+
{
60+
ProgramId = ProgramIdKey.KeyBytes,
61+
Keys = keys,
62+
Data = AccountCompressionProgramData.EncodeAppendData(leaf)
63+
};
64+
}
65+
/// <summary>
66+
/// /// Creates an instruction to close an empty Merkle tree and transfer its lamports to a recipient.
67+
/// </summary>
68+
/// <param name="merkleTree"></param>
69+
/// <param name="authority"></param>
70+
/// <param name="recipient"></param>
71+
/// <returns></returns>
72+
public static TransactionInstruction CloseEmptyTree(
73+
PublicKey merkleTree,
74+
PublicKey authority,
75+
PublicKey recipient
76+
)
77+
{
78+
List<AccountMeta> keys = new()
79+
{
80+
AccountMeta.Writable(merkleTree, false), // Merkle tree account (writable)
81+
AccountMeta.Writable(authority, true), // Authority (signer)
82+
AccountMeta.ReadOnly(recipient, false) // recipient
83+
};
84+
85+
86+
return new TransactionInstruction
87+
{
88+
ProgramId = ProgramIdKey.KeyBytes,
89+
Keys = keys,
90+
Data = AccountCompressionProgramData.EncodeCloseEmptyTreeData()
91+
};
92+
}
93+
/// <summary>
94+
/// /// Creates an instruction to initialize an empty Merkle tree.
95+
/// </summary>
96+
/// <param name="merkleTree"></param>
97+
/// <param name="authority"></param>
98+
/// <param name="maxDepth"></param>
99+
/// <param name="maxBufferSize"></param>
100+
/// <returns></returns>
101+
public static TransactionInstruction InitEmptyMerkleTree(
102+
PublicKey merkleTree,
103+
PublicKey authority,
104+
byte maxDepth, byte maxBufferSize
105+
)
106+
{
107+
List<AccountMeta> keys = new()
108+
{
109+
AccountMeta.Writable(merkleTree, false), // Merkle tree account (writable)
110+
AccountMeta.Writable(authority, true), // Authority (signer)
111+
};
112+
113+
114+
return new TransactionInstruction
115+
{
116+
ProgramId = ProgramIdKey.KeyBytes,
117+
Keys = keys,
118+
Data = AccountCompressionProgramData.EncodeInitEmptyMerkleTreeData(maxDepth, maxBufferSize)
119+
};
120+
}
121+
/// <summary>
122+
/// /// Creates an instruction to replace a leaf in a Merkle tree.
123+
/// </summary>
124+
/// <param name="merkleTree"></param>
125+
/// <param name="authority"></param>
126+
/// <param name="newLeaf"></param>
127+
/// <param name="previousLeaf"></param>
128+
/// <param name="root"></param>
129+
/// <param name="index"></param>
130+
/// <returns></returns>
131+
public static TransactionInstruction ReplaceLeaf(
132+
PublicKey merkleTree,
133+
PublicKey authority,
134+
byte[] newLeaf,
135+
byte[] previousLeaf,
136+
byte[] root,
137+
uint index
138+
)
139+
{
140+
List<AccountMeta> keys = new()
141+
{
142+
AccountMeta.Writable(merkleTree, false), // Merkle tree account (writable)
143+
AccountMeta.Writable(authority, true), // Authority (signer)
144+
};
145+
146+
147+
return new TransactionInstruction
148+
{
149+
ProgramId = ProgramIdKey.KeyBytes,
150+
Keys = keys,
151+
Data = AccountCompressionProgramData.EncodeReplaceLeafData(newLeaf, previousLeaf, root, index)
152+
};
153+
}
154+
/// <summary>
155+
/// /// Creates an instruction to insert or append a leaf to a Merkle tree.
156+
/// </summary>
157+
/// <param name="merkleTree"></param>
158+
/// <param name="authority"></param>
159+
/// <param name="Leaf"></param>
160+
/// <param name="root"></param>
161+
/// <param name="index"></param>
162+
/// <returns></returns>
163+
public static TransactionInstruction InsertOrAppend(
164+
PublicKey merkleTree,
165+
PublicKey authority,
166+
byte[] Leaf,
167+
byte[] root,
168+
uint index
169+
)
170+
{
171+
List<AccountMeta> keys = new()
172+
{
173+
AccountMeta.Writable(merkleTree, false), // Merkle tree account (writable)
174+
AccountMeta.Writable(authority, true), // Authority (signer)
175+
};
176+
177+
178+
return new TransactionInstruction
179+
{
180+
ProgramId = ProgramIdKey.KeyBytes,
181+
Keys = keys,
182+
Data = AccountCompressionProgramData.EncodeInsertOrAppendData(Leaf, root, index)
183+
};
184+
}
185+
/// <summary>
186+
/// Creates an instruction to transfer authority of a Merkle tree.
187+
/// </summary>
188+
/// <param name="merkleTree"></param>
189+
/// <param name="authority"></param>
190+
/// <param name="newAuthority"></param>
191+
/// <returns></returns>
192+
public static TransactionInstruction TransferAuthority(
193+
PublicKey merkleTree,
194+
PublicKey authority,
195+
PublicKey newAuthority
196+
)
197+
{
198+
List<AccountMeta> keys = new()
199+
{
200+
AccountMeta.Writable(merkleTree, false), // Merkle tree account (writable)
201+
AccountMeta.Writable(authority, true), // Authority (signer)
202+
AccountMeta.ReadOnly(newAuthority, false), //new Authority
203+
204+
};
205+
206+
207+
return new TransactionInstruction
208+
{
209+
ProgramId = ProgramIdKey.KeyBytes,
210+
Keys = keys,
211+
Data = AccountCompressionProgramData.EncodeTransferAuthorityData(newAuthority)
212+
};
213+
}
214+
/// <summary>
215+
/// Creates an instruction to verify a leaf in a Merkle tree.
216+
/// </summary>
217+
/// <param name="merkleTree"></param>
218+
/// <param name="authority"></param>
219+
/// <param name="root"></param>
220+
/// <param name="leaf"></param>
221+
/// <param name="index"></param>
222+
/// <returns></returns>
223+
public static TransactionInstruction VerifyLeaf(
224+
PublicKey merkleTree,
225+
PublicKey authority,
226+
byte[] root, byte[] leaf, uint index
227+
)
228+
{
229+
List<AccountMeta> keys = new()
230+
{
231+
AccountMeta.Writable(merkleTree, false), // Merkle tree account (writable)
232+
AccountMeta.Writable(authority, true), // Authority (signer)
233+
234+
};
235+
236+
237+
return new TransactionInstruction
238+
{
239+
ProgramId = ProgramIdKey.KeyBytes,
240+
Keys = keys,
241+
Data = AccountCompressionProgramData.EncodeVerifyLeafData(root, leaf, index)
242+
};
243+
}
244+
/// <summary>
245+
/// Decodes the instruction data for the <see cref="AccountCompressionProgram"/>.
246+
/// </summary>
247+
/// <param name="data"></param>
248+
/// <param name="keys"></param>
249+
/// <param name="keyIndices"></param>
250+
/// <returns></returns>
251+
public static DecodedInstruction Decode(ReadOnlySpan<byte> data, IList<PublicKey> keys, byte[] keyIndices)
252+
{
253+
uint instruction = data.GetU32(AccountCompressionProgramData.MethodOffset);
254+
255+
if (!Enum.IsDefined(typeof(NameServiceInstructions.Values), instruction))
256+
{
257+
return new()
258+
{
259+
PublicKey = ProgramIdKey,
260+
InstructionName = "Unknown Instruction",
261+
ProgramName = ProgramName,
262+
Values = new Dictionary<string, object>(),
263+
InnerInstructions = new List<DecodedInstruction>()
264+
};
265+
}
266+
267+
AccountCompressionProgramInstructions.Values instructionValue = (AccountCompressionProgramInstructions.Values)instruction;
268+
269+
DecodedInstruction decodedInstruction = new()
270+
{
271+
PublicKey = ProgramIdKey,
272+
InstructionName = AccountCompressionProgramInstructions.Names[instructionValue],
273+
ProgramName = ProgramName,
274+
Values = new Dictionary<string, object>() { },
275+
InnerInstructions = new List<DecodedInstruction>()
276+
};
277+
278+
switch (instructionValue)
279+
{
280+
case AccountCompressionProgramInstructions.Values.Append:
281+
AccountCompressionProgramData.DecodeAppendLeafData(decodedInstruction, data, keys, keyIndices);
282+
break;
283+
case AccountCompressionProgramInstructions.Values.InsertOrAppend:
284+
AccountCompressionProgramData.DecodeInsertOrAppendData(decodedInstruction, data, keys, keyIndices);
285+
break;
286+
case AccountCompressionProgramInstructions.Values.VerifyLeaf:
287+
AccountCompressionProgramData.DecodeVerifyLeafData(decodedInstruction, data, keys, keyIndices);
288+
break;
289+
case AccountCompressionProgramInstructions.Values.InitEmptyMerkleTree:
290+
AccountCompressionProgramData.DecodeInitEmptyMerkleTreeData(decodedInstruction, data, keys, keyIndices);
291+
break;
292+
case AccountCompressionProgramInstructions.Values.CloseEmptyTree:
293+
AccountCompressionProgramData.DecodeCloseEmptyTreeData(decodedInstruction, data, keys, keyIndices);
294+
break;
295+
case AccountCompressionProgramInstructions.Values.TransferAuthority:
296+
AccountCompressionProgramData.DecodeTransferAuthorityInstruction(decodedInstruction, data, keys, keyIndices);
297+
break;
298+
case AccountCompressionProgramInstructions.Values.ReplaceLeaf:
299+
AccountCompressionProgramData.DecodeReplaceLeafData(decodedInstruction, data, keys, keyIndices);
300+
break;
301+
302+
}
303+
return decodedInstruction;
304+
}
305+
}
306+
}

0 commit comments

Comments
 (0)