1
1
use borsh:: { BorshDeserialize , BorshSerialize } ;
2
- use light_hasher:: { DataHasher , Hasher } ;
3
2
use light_sdk:: {
4
- account:: LightAccount ,
5
- cpi:: { CpiAccounts , CpiAccountsConfig , CpiInputs } ,
3
+ cpi:: { CpiAccounts , CpiAccountsConfig } ,
6
4
error:: LightSdkError ,
7
- instruction:: {
8
- account_meta:: { CompressedAccountMeta , CompressedAccountMetaTrait } ,
9
- ValidityProof ,
10
- } ,
5
+ instruction:: { account_meta:: CompressedAccountMeta , ValidityProof } ,
11
6
LightDiscriminator , LightHasher ,
12
7
} ;
13
- use solana_program:: {
14
- account_info:: AccountInfo , clock:: Clock , msg, program:: invoke_signed, pubkey:: Pubkey ,
15
- rent:: Rent , system_instruction, sysvar:: Sysvar ,
16
- } ;
17
-
18
- pub const SLOTS_UNTIL_COMPRESSION : u64 = 100 ;
8
+ use solana_program:: account_info:: AccountInfo ;
19
9
20
- /// Account structure for the decompressed PDA
21
- #[ derive( Clone , Debug , Default , BorshDeserialize , BorshSerialize ) ]
22
- pub struct DecompressedPdaAccount {
23
- /// The compressed account address this PDA was derived from
24
- pub compressed_address : [ u8 ; 32 ] ,
25
- /// Slot when this account was last written
26
- pub last_written_slot : u64 ,
27
- /// Number of slots until this account can be compressed again
28
- pub slots_until_compression : u64 ,
29
- /// The actual account data
30
- pub data : [ u8 ; 31 ] ,
31
- /// Flag to indicate if this is a decompressed account
32
- pub is_decompressed : bool ,
33
- }
10
+ use crate :: sdk:: decompress_idempotent:: decompress_idempotent;
34
11
35
- /// Compressed account structure with decompression flag
36
- #[ derive(
37
- Clone , Debug , Default , LightHasher , LightDiscriminator , BorshDeserialize , BorshSerialize ,
38
- ) ]
39
- pub struct DecompressedMarkerAccount {
40
- /// Flag to indicate this account has been decompressed
41
- pub is_decompressed : bool ,
42
- }
12
+ pub const SLOTS_UNTIL_COMPRESSION : u64 = 100 ;
43
13
44
- /// Decompresses a compressed account into a PDA
45
- /// The PDA is derived from the compressed account's address and other seeds
14
+ /// Decompresses a compressed account into a PDA idempotently.
46
15
pub fn decompress_to_pda (
47
16
accounts : & [ AccountInfo ] ,
48
17
instruction_data : & [ u8 ] ,
49
18
) -> Result < ( ) , LightSdkError > {
50
- msg ! ( "Decompressing compressed account to PDA" ) ;
51
-
52
19
let mut instruction_data = instruction_data;
53
20
let instruction_data = DecompressToPdaInstructionData :: deserialize ( & mut instruction_data)
54
21
. map_err ( |_| LightSdkError :: Borsh ) ?;
@@ -59,157 +26,75 @@ pub fn decompress_to_pda(
59
26
let rent_payer = & accounts[ 2 ] ; // Account that pays for PDA rent
60
27
let system_program = & accounts[ 3 ] ;
61
28
62
- // Derive PDA from compressed address
63
- let compressed_address = instruction_data. compressed_account . meta . address ;
64
- let ( pda_pubkey, pda_bump) = Pubkey :: find_program_address (
65
- & [
66
- b"decompressed_pda" ,
67
- & compressed_address,
68
- & instruction_data. additional_seed ,
69
- ] ,
70
- & crate :: ID ,
71
- ) ;
72
-
73
- // Verify PDA matches
74
- if pda_pubkey != * pda_account. key {
75
- msg ! ( "Invalid PDA pubkey" ) ;
76
- return Err ( LightSdkError :: ConstraintViolation ) ;
77
- }
78
-
79
- // Get current slot
80
- let clock = Clock :: get ( ) . map_err ( |_| LightSdkError :: Borsh ) ?;
81
- let current_slot = clock. slot ;
82
-
83
- // Calculate space needed for PDA
84
- let space = std:: mem:: size_of :: < DecompressedPdaAccount > ( ) + 8 ; // +8 for discriminator
85
-
86
- // Get minimum rent
87
- let rent = Rent :: get ( ) . map_err ( |_| LightSdkError :: Borsh ) ?;
88
- let minimum_balance = rent. minimum_balance ( space) ;
89
-
90
- // Create PDA account (rent payer pays for the PDA creation)
91
- let create_account_ix = system_instruction:: create_account (
92
- rent_payer. key ,
93
- pda_account. key ,
94
- minimum_balance,
95
- space as u64 ,
96
- & crate :: ID ,
97
- ) ;
98
-
99
- let signer_seeds = & [
100
- b"decompressed_pda" . as_ref ( ) ,
101
- compressed_address. as_ref ( ) ,
102
- instruction_data. additional_seed . as_ref ( ) ,
103
- & [ pda_bump] ,
104
- ] ;
105
-
106
- invoke_signed (
107
- & create_account_ix,
108
- & [
109
- rent_payer. clone ( ) ,
110
- pda_account. clone ( ) ,
111
- system_program. clone ( ) ,
112
- ] ,
113
- & [ signer_seeds] ,
114
- ) ?;
115
-
116
- // Initialize PDA with decompressed data
117
- let decompressed_pda = DecompressedPdaAccount {
118
- compressed_address,
119
- last_written_slot : current_slot,
120
- slots_until_compression : SLOTS_UNTIL_COMPRESSION ,
121
- data : instruction_data. compressed_account . data ,
122
- is_decompressed : true ,
123
- } ;
124
-
125
- // Write data to PDA
126
- decompressed_pda
127
- . serialize ( & mut & mut pda_account. try_borrow_mut_data ( ) ?[ 8 ..] )
128
- . map_err ( |_| LightSdkError :: Borsh ) ?;
129
-
130
- // Write discriminator
131
- pda_account. try_borrow_mut_data ( ) ?[ ..8 ] . copy_from_slice ( b"decomppd" ) ;
132
-
133
- // Now handle the compressed account side
134
- // Create a marker account that indicates this compressed account has been decompressed
135
- let marker_account = LightAccount :: < ' _ , DecompressedMarkerAccount > :: new_mut (
136
- & crate :: ID ,
137
- & instruction_data. compressed_account . meta ,
138
- DecompressedMarkerAccount {
139
- is_decompressed : true ,
140
- } ,
141
- ) ?;
142
-
143
- // Set up CPI accounts for light system program
144
- let mut config = CpiAccountsConfig :: new ( crate :: LIGHT_CPI_SIGNER ) ;
145
- config. sol_pool_pda = false ;
146
- config. sol_compression_recipient = true ; // We need to decompress SOL to the PDA
147
-
29
+ // Cpi accounts
148
30
let cpi_accounts = CpiAccounts :: new_with_config (
149
31
fee_payer,
150
32
& accounts[ instruction_data. system_accounts_offset as usize ..] ,
151
- config ,
33
+ CpiAccountsConfig :: new ( crate :: LIGHT_CPI_SIGNER ) ,
152
34
) ;
153
35
154
- // Create CPI inputs with decompression
155
- let mut cpi_inputs = CpiInputs :: new (
156
- instruction_data. proof ,
157
- vec ! [ marker_account. to_account_info( ) ?] ,
158
- ) ;
36
+ // Custom seeds for PDA derivation
37
+ let custom_seeds: Vec < & [ u8 ] > = vec ! [ b"decompressed_pda" ] ;
159
38
160
- // Set decompression parameters
161
- // Transfer all lamports from compressed account to the PDA
162
- let lamports_to_decompress = instruction_data
163
- . compressed_account
164
- . meta
165
- . get_lamports ( )
166
- . unwrap_or ( 0 ) ;
167
-
168
- cpi_inputs. compress_or_decompress_lamports = Some ( lamports_to_decompress) ;
169
- cpi_inputs. is_compress = false ; // This is decompression
39
+ // Call the SDK function to decompress idempotently
40
+ // this inits pda_account if not already initialized
41
+ decompress_idempotent :: < MyPdaAccount > (
42
+ pda_account,
43
+ Some ( & instruction_data. compressed_account . meta ) ,
44
+ & instruction_data. compressed_account . data ,
45
+ instruction_data. proof ,
46
+ cpi_accounts,
47
+ & crate :: ID ,
48
+ rent_payer,
49
+ system_program,
50
+ & custom_seeds,
51
+ & instruction_data. additional_seed ,
52
+ ) ?;
170
53
171
- // Invoke light system program
172
- cpi_inputs. invoke_light_system_program ( cpi_accounts) ?;
54
+ // do something with pda_account...
173
55
174
- msg ! ( "Successfully decompressed account to PDA" ) ;
175
56
Ok ( ( ) )
176
57
}
177
58
178
59
#[ derive( Clone , Debug , Default , BorshDeserialize , BorshSerialize ) ]
179
60
pub struct DecompressToPdaInstructionData {
180
61
pub proof : ValidityProof ,
181
- pub compressed_account : DecompressMyCompressedAccount ,
182
- pub additional_seed : [ u8 ; 32 ] , // Additional seed for PDA derivation
62
+ pub compressed_account : MyCompressedAccount ,
63
+ pub additional_seed : [ u8 ; 32 ] , // ... some seed
183
64
pub system_accounts_offset : u8 ,
184
65
}
185
66
67
+ // just a wrapper
186
68
#[ derive( Clone , Debug , Default , BorshDeserialize , BorshSerialize ) ]
187
- pub struct DecompressMyCompressedAccount {
69
+ pub struct MyCompressedAccount {
188
70
pub meta : CompressedAccountMeta ,
189
- pub data : [ u8 ; 31 ] ,
71
+ pub data : MyPdaAccount ,
190
72
}
191
73
192
- // Implement required traits for DecompressedPdaAccount
193
- impl DataHasher for DecompressedPdaAccount {
194
- fn hash < H : Hasher > ( & self ) -> Result < [ u8 ; 32 ] , light_hasher:: HasherError > {
195
- let mut bytes = vec ! [ ] ;
196
- self . serialize ( & mut bytes) . unwrap ( ) ;
197
- H :: hashv ( & [ & bytes] )
198
- }
199
- }
200
-
201
- impl LightDiscriminator for DecompressedPdaAccount {
202
- const LIGHT_DISCRIMINATOR : [ u8 ; 8 ] = [ 0xDE , 0xC0 , 0x11 , 0x9D , 0xA0 , 0x00 , 0x00 , 0x00 ] ;
203
- const LIGHT_DISCRIMINATOR_SLICE : & ' static [ u8 ] =
204
- & [ 0xDE , 0xC0 , 0x11 , 0x9D , 0xA0 , 0x00 , 0x00 , 0x00 ] ;
74
+ /// Account structure for the PDA
75
+ #[ derive(
76
+ Clone , Debug , LightHasher , LightDiscriminator , Default , BorshDeserialize , BorshSerialize ,
77
+ ) ]
78
+ pub struct MyPdaAccount {
79
+ /// Slot when this account was last written
80
+ pub last_written_slot : u64 ,
81
+ /// Number of slots after last_written_slot until this account can be compressed again
82
+ pub slots_until_compression : u64 ,
83
+ /// The actual account data
84
+ pub data : [ u8 ; 31 ] ,
205
85
}
206
86
207
- impl crate :: sdk:: compress_pda:: PdaTimingData for DecompressedPdaAccount {
87
+ // We require this trait to be implemented for the custom PDA account.
88
+ impl crate :: sdk:: compress_pda:: PdaTimingData for MyPdaAccount {
208
89
fn last_touched_slot ( & self ) -> u64 {
209
90
self . last_written_slot
210
91
}
211
92
212
93
fn slots_buffer ( & self ) -> u64 {
213
94
self . slots_until_compression
214
95
}
96
+
97
+ fn set_last_written_slot ( & mut self , slot : u64 ) {
98
+ self . last_written_slot = slot;
99
+ }
215
100
}
0 commit comments