sememe 0.0.0-stage → 0.4.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2777 -2
  3. package/bin/sememe.mjs +556 -0
  4. package/index.mjs +48 -0
  5. package/interface/ata-ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL.json +62 -0
  6. package/interface/native-11111111111111111111111111111111.json +226 -0
  7. package/interface/native-ComputeBudget111111111111111111111111111111.json +72 -0
  8. package/interface/raydium-675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8.json +352 -0
  9. package/interface/token-TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA.json +334 -0
  10. package/interface/token-TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb.json +535 -0
  11. package/interface/token-metadata.json +132 -0
  12. package/package.json +71 -4
  13. package/src/account-list.mjs +308 -0
  14. package/src/account-union.mjs +523 -0
  15. package/src/account.mjs +199 -0
  16. package/src/acquire.mjs +145 -0
  17. package/src/adopt.mjs +206 -0
  18. package/src/ata-interface.mjs +276 -0
  19. package/src/ata.mjs +223 -0
  20. package/src/base58.mjs +96 -0
  21. package/src/canonical.mjs +24 -0
  22. package/src/cli-surface.mjs +300 -0
  23. package/src/codec.mjs +1118 -0
  24. package/src/conformance.mjs +431 -0
  25. package/src/corpus/harvest-compute-budget-2026-10-31.json +19122 -0
  26. package/src/corpus/harvest-system-2026-10-31.json +26090 -0
  27. package/src/decode.mjs +286 -0
  28. package/src/dialect.mjs +180 -0
  29. package/src/discriminators.mjs +192 -0
  30. package/src/drift.mjs +172 -0
  31. package/src/gap.mjs +205 -0
  32. package/src/lighthouse.mjs +168 -0
  33. package/src/lock.mjs +440 -0
  34. package/src/native-audit.mjs +56 -0
  35. package/src/native-corpus.mjs +57 -0
  36. package/src/native-interface.mjs +252 -0
  37. package/src/native.mjs +324 -0
  38. package/src/peer.mjs +95 -0
  39. package/src/program-id.mjs +74 -0
  40. package/src/pull.mjs +117 -0
  41. package/src/rawle.mjs +454 -0
  42. package/src/raydium-interface.mjs +747 -0
  43. package/src/raydium.mjs +391 -0
  44. package/src/receipt-digest.mjs +84 -0
  45. package/src/receipt-name.mjs +128 -0
  46. package/src/receipt-order.mjs +56 -0
  47. package/src/shank.mjs +73 -0
  48. package/src/short-forms.mjs +209 -0
  49. package/src/store-read.mjs +253 -0
  50. package/src/store-write.mjs +46 -0
  51. package/src/token-interface.mjs +394 -0
  52. package/src/token-metadata-interface.mjs +371 -0
  53. package/src/token-metadata.mjs +317 -0
  54. package/src/token.mjs +443 -0
  55. package/src/union.mjs +428 -0
  56. package/vendor/anza-solana-sdk/8db406e2edfafa25939ec782b77b5b6f30de164d/LICENSE.txt +201 -0
  57. package/vendor/anza-solana-sdk/8db406e2edfafa25939ec782b77b5b6f30de164d/compute-budget-interface/src/lib.rs +93 -0
  58. package/vendor/anza-solana-sdk/8db406e2edfafa25939ec782b77b5b6f30de164d/system-interface/src/instruction.rs +1899 -0
  59. package/vendor/lighthouse/4c579479c98635e419b1b167f08be02a71604a71/LICENSE +21 -0
  60. package/vendor/lighthouse/4c579479c98635e419b1b167f08be02a71604a71/lighthouse.json +2769 -0
  61. package/vendor/raydium-amm/91739d8d9f5cf347e0ce14ebe4d2bb7069fb31b6/LICENSE +201 -0
  62. package/vendor/raydium-amm/91739d8d9f5cf347e0ce14ebe4d2bb7069fb31b6/program/src/instruction.rs +1189 -0
  63. package/vendor/raydium-amm/91739d8d9f5cf347e0ce14ebe4d2bb7069fb31b6/program/src/state.rs +1348 -0
  64. package/vendor/spl-associated-token-account/fbf5379e7398ac8b231c73ee8a9ff677b43dd0ec/LICENSE +201 -0
  65. package/vendor/spl-associated-token-account/fbf5379e7398ac8b231c73ee8a9ff677b43dd0ec/interface/src/instruction.rs +169 -0
  66. package/vendor/spl-associated-token-account/fbf5379e7398ac8b231c73ee8a9ff677b43dd0ec/interface/src/lib.rs +12 -0
  67. package/vendor/spl-associated-token-account/fbf5379e7398ac8b231c73ee8a9ff677b43dd0ec/program/src/processor.rs +308 -0
  68. package/vendor/spl-token/737788ff5bcc3f1f44cfb222f25483187650be76/LICENSE.txt +176 -0
  69. package/vendor/spl-token/737788ff5bcc3f1f44cfb222f25483187650be76/interface/src/instruction.rs +1963 -0
  70. package/vendor/spl-token-2022/5251dcc07ecbcb721d6c4f0231090defadf00311/LICENSE.txt +176 -0
  71. package/vendor/spl-token-2022/5251dcc07ecbcb721d6c4f0231090defadf00311/interface/src/instruction.rs +2868 -0
  72. package/vendor/spl-token-metadata-interface/7038969ec74a89f1afdb9bc97c112d4d26fead9a/LICENSE +201 -0
  73. package/vendor/spl-token-metadata-interface/7038969ec74a89f1afdb9bc97c112d4d26fead9a/interface/src/error.rs +58 -0
  74. package/vendor/spl-token-metadata-interface/7038969ec74a89f1afdb9bc97c112d4d26fead9a/interface/src/instruction.rs +596 -0
  75. package/vendor/spl-token-metadata-interface/7038969ec74a89f1afdb9bc97c112d4d26fead9a/interface/src/lib.rs +21 -0
  76. package/vendor/spl-token-metadata-interface/7038969ec74a89f1afdb9bc97c112d4d26fead9a/interface/src/state.rs +229 -0
@@ -0,0 +1,596 @@
1
+ //! Instruction types
2
+
3
+ use {
4
+ crate::state::Field,
5
+ alloc::{string::String, vec, vec::Vec},
6
+ borsh::{BorshDeserialize, BorshSerialize},
7
+ solana_address::Address,
8
+ solana_instruction::{account_meta::AccountMeta, Instruction},
9
+ solana_nullable::MaybeNull,
10
+ solana_program_error::ProgramError,
11
+ spl_discriminator::{discriminator::ArrayDiscriminator, SplDiscriminate},
12
+ };
13
+
14
+ #[cfg(feature = "serde-traits")]
15
+ use {
16
+ serde::{Deserialize, Serialize},
17
+ serde_with::DisplayFromStr,
18
+ };
19
+
20
+ /// Initialization instruction data
21
+ #[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))]
22
+ #[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))]
23
+ #[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, SplDiscriminate)]
24
+ #[discriminator_hash_input("spl_token_metadata_interface:initialize_account")]
25
+ pub struct Initialize {
26
+ /// Longer name of the token
27
+ pub name: String,
28
+ /// Shortened symbol of the token
29
+ pub symbol: String,
30
+ /// URI pointing to more metadata (image, video, etc.)
31
+ pub uri: String,
32
+ }
33
+
34
+ /// Update field instruction data
35
+ #[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))]
36
+ #[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))]
37
+ #[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, SplDiscriminate)]
38
+ #[discriminator_hash_input("spl_token_metadata_interface:updating_field")]
39
+ pub struct UpdateField {
40
+ /// Field to update in the metadata
41
+ pub field: Field,
42
+ /// Value to write for the field
43
+ pub value: String,
44
+ }
45
+
46
+ /// Remove key instruction data
47
+ #[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))]
48
+ #[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))]
49
+ #[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, SplDiscriminate)]
50
+ #[discriminator_hash_input("spl_token_metadata_interface:remove_key_ix")]
51
+ pub struct RemoveKey {
52
+ /// If the idempotent flag is set to true, then the instruction will not
53
+ /// error if the key does not exist
54
+ pub idempotent: bool,
55
+ /// Key to remove in the additional metadata portion
56
+ pub key: String,
57
+ }
58
+
59
+ /// Update authority instruction data
60
+ #[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, SplDiscriminate)]
61
+ #[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))]
62
+ #[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))]
63
+ #[discriminator_hash_input("spl_token_metadata_interface:update_the_authority")]
64
+ pub struct UpdateAuthority {
65
+ /// New authority for the token metadata, or unset if `None`
66
+ #[cfg_attr(
67
+ feature = "serde-traits",
68
+ serde(with = "serde_with::As::<Option<DisplayFromStr>>")
69
+ )]
70
+ pub new_authority: MaybeNull<Address>,
71
+ }
72
+
73
+ /// Instruction data for Emit
74
+ #[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))]
75
+ #[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))]
76
+ #[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, SplDiscriminate)]
77
+ #[discriminator_hash_input("spl_token_metadata_interface:emitter")]
78
+ pub struct Emit {
79
+ /// Start of range of data to emit
80
+ pub start: Option<u64>,
81
+ /// End of range of data to emit
82
+ pub end: Option<u64>,
83
+ }
84
+
85
+ /// All instructions that must be implemented in the token-metadata interface
86
+ #[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))]
87
+ #[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))]
88
+ #[derive(Clone, Debug, PartialEq)]
89
+ pub enum TokenMetadataInstruction {
90
+ /// Initializes a TLV entry with the basic token-metadata fields.
91
+ ///
92
+ /// Assumes that the provided mint is an SPL token mint, that the metadata
93
+ /// account is allocated and assigned to the program, and that the metadata
94
+ /// account has enough lamports to cover the rent-exempt reserve.
95
+ ///
96
+ /// Accounts expected by this instruction:
97
+ ///
98
+ /// 0. `[w]` Metadata
99
+ /// 1. `[]` Update authority
100
+ /// 2. `[]` Mint
101
+ /// 3. `[s]` Mint authority
102
+ ///
103
+ /// Data: `Initialize` data, name / symbol / uri strings
104
+ Initialize(Initialize),
105
+
106
+ /// Updates a field in a token-metadata account.
107
+ ///
108
+ /// The field can be one of the required fields (name, symbol, URI), or a
109
+ /// totally new field denoted by a "key" string.
110
+ ///
111
+ /// By the end of the instruction, the metadata account must be properly
112
+ /// re-sized based on the new size of the TLV entry.
113
+ /// * If the new size is larger, the program must first reallocate to
114
+ /// avoid overwriting other TLV entries.
115
+ /// * If the new size is smaller, the program must reallocate at the end
116
+ /// so that it's possible to iterate over TLV entries
117
+ ///
118
+ /// Accounts expected by this instruction:
119
+ ///
120
+ /// 0. `[w]` Metadata account
121
+ /// 1. `[s]` Update authority
122
+ ///
123
+ /// Data: `UpdateField` data, specifying the new field and value. If the
124
+ /// field does not exist on the account, it will be created. If the
125
+ /// field does exist, it will be overwritten.
126
+ UpdateField(UpdateField),
127
+
128
+ /// Removes a key-value pair in a token-metadata account.
129
+ ///
130
+ /// This only applies to additional fields, and not the base name / symbol /
131
+ /// URI fields.
132
+ ///
133
+ /// By the end of the instruction, the metadata account must be properly
134
+ /// re-sized at the end based on the new size of the TLV entry.
135
+ ///
136
+ /// Accounts expected by this instruction:
137
+ ///
138
+ /// 0. `[w]` Metadata account
139
+ /// 1. `[s]` Update authority
140
+ ///
141
+ /// Data: the string key to remove. If the idempotent flag is set to false,
142
+ /// returns an error if the key is not present
143
+ RemoveKey(RemoveKey),
144
+
145
+ /// Updates the token-metadata authority
146
+ ///
147
+ /// Accounts expected by this instruction:
148
+ ///
149
+ /// 0. `[w]` Metadata account
150
+ /// 1. `[s]` Current update authority
151
+ ///
152
+ /// Data: the new authority. Can be unset using a `None` value
153
+ UpdateAuthority(UpdateAuthority),
154
+
155
+ /// Emits the token-metadata as return data
156
+ ///
157
+ /// The format of the data emitted follows exactly the `TokenMetadata`
158
+ /// struct, but it's possible that the account data is stored in another
159
+ /// format by the program.
160
+ ///
161
+ /// With this instruction, a program that implements the token-metadata
162
+ /// interface can return `TokenMetadata` without adhering to the specific
163
+ /// byte layout of the `TokenMetadata` struct in any accounts.
164
+ ///
165
+ /// Accounts expected by this instruction:
166
+ ///
167
+ /// 0. `[]` Metadata account
168
+ Emit(Emit),
169
+ }
170
+ /// Validates that a Borsh-encoded `String` starting at `*cursor` declares a
171
+ /// length that fits within `rest`, advancing `*cursor` past it on success.
172
+ ///
173
+ /// `TokenMetadataInstruction::unpack` calls this before `try_from_slice` so a
174
+ /// forged length prefix can't trigger an oversized allocation that aborts on
175
+ /// the SBF heap. See <https://github.com/solana-program/token-2022/issues/1152>.
176
+ fn check_borsh_string(rest: &[u8], cursor: &mut usize) -> Result<(), ProgramError> {
177
+ let len_end = cursor
178
+ .checked_add(core::mem::size_of::<u32>())
179
+ .ok_or(ProgramError::InvalidInstructionData)?;
180
+ let len_bytes = rest
181
+ .get(*cursor..len_end)
182
+ .ok_or(ProgramError::InvalidInstructionData)?;
183
+ let len = u32::from_le_bytes(len_bytes.try_into().unwrap()) as usize;
184
+ let str_end = len_end
185
+ .checked_add(len)
186
+ .ok_or(ProgramError::InvalidInstructionData)?;
187
+ if str_end > rest.len() {
188
+ return Err(ProgramError::InvalidInstructionData);
189
+ }
190
+ *cursor = str_end;
191
+ Ok(())
192
+ }
193
+
194
+ impl TokenMetadataInstruction {
195
+ /// Unpacks a byte buffer into a
196
+ /// [`TokenMetadataInstruction`](enum.TokenMetadataInstruction.html).
197
+ pub fn unpack(input: &[u8]) -> Result<Self, ProgramError> {
198
+ if input.len() < ArrayDiscriminator::LENGTH {
199
+ return Err(ProgramError::InvalidInstructionData);
200
+ }
201
+ let (discriminator, rest) = input.split_at(ArrayDiscriminator::LENGTH);
202
+ Ok(match discriminator {
203
+ Initialize::SPL_DISCRIMINATOR_SLICE => {
204
+ let mut cursor = 0usize;
205
+ check_borsh_string(rest, &mut cursor)?; // name
206
+ check_borsh_string(rest, &mut cursor)?; // symbol
207
+ check_borsh_string(rest, &mut cursor)?; // uri
208
+ let data = Initialize::try_from_slice(rest)?;
209
+ Self::Initialize(data)
210
+ }
211
+ UpdateField::SPL_DISCRIMINATOR_SLICE => {
212
+ let mut cursor = 0usize;
213
+ // `Field` is a Borsh enum: 1-byte variant tag, and only the
214
+ // `Key` variant (tag 3) carries a nested string.
215
+ let tag = *rest
216
+ .get(cursor)
217
+ .ok_or(ProgramError::InvalidInstructionData)?;
218
+ cursor = cursor
219
+ .checked_add(1)
220
+ .ok_or(ProgramError::InvalidInstructionData)?;
221
+ if tag == 3 {
222
+ check_borsh_string(rest, &mut cursor)?; // Field::Key(String)
223
+ }
224
+ check_borsh_string(rest, &mut cursor)?; // value
225
+ let data = UpdateField::try_from_slice(rest)?;
226
+ Self::UpdateField(data)
227
+ }
228
+ RemoveKey::SPL_DISCRIMINATOR_SLICE => {
229
+ let mut cursor = 1usize; // skip the 1-byte `idempotent` bool
230
+ check_borsh_string(rest, &mut cursor)?; // key
231
+ let data = RemoveKey::try_from_slice(rest)?;
232
+ Self::RemoveKey(data)
233
+ }
234
+ UpdateAuthority::SPL_DISCRIMINATOR_SLICE => {
235
+ let data = UpdateAuthority::try_from_slice(rest)?;
236
+ Self::UpdateAuthority(data)
237
+ }
238
+ Emit::SPL_DISCRIMINATOR_SLICE => {
239
+ let data = Emit::try_from_slice(rest)?;
240
+ Self::Emit(data)
241
+ }
242
+ _ => return Err(ProgramError::InvalidInstructionData),
243
+ })
244
+ }
245
+
246
+ /// Packs a [`TokenMetadataInstruction`](enum.TokenMetadataInstruction.html)
247
+ /// into a byte buffer.
248
+ pub fn pack(&self) -> Vec<u8> {
249
+ let mut buf = vec![];
250
+ match self {
251
+ Self::Initialize(data) => {
252
+ buf.extend_from_slice(Initialize::SPL_DISCRIMINATOR_SLICE);
253
+ buf.append(&mut borsh::to_vec(data).unwrap());
254
+ }
255
+ Self::UpdateField(data) => {
256
+ buf.extend_from_slice(UpdateField::SPL_DISCRIMINATOR_SLICE);
257
+ buf.append(&mut borsh::to_vec(data).unwrap());
258
+ }
259
+ Self::RemoveKey(data) => {
260
+ buf.extend_from_slice(RemoveKey::SPL_DISCRIMINATOR_SLICE);
261
+ buf.append(&mut borsh::to_vec(data).unwrap());
262
+ }
263
+ Self::UpdateAuthority(data) => {
264
+ buf.extend_from_slice(UpdateAuthority::SPL_DISCRIMINATOR_SLICE);
265
+ buf.append(&mut borsh::to_vec(data).unwrap());
266
+ }
267
+ Self::Emit(data) => {
268
+ buf.extend_from_slice(Emit::SPL_DISCRIMINATOR_SLICE);
269
+ buf.append(&mut borsh::to_vec(data).unwrap());
270
+ }
271
+ };
272
+ buf
273
+ }
274
+ }
275
+
276
+ /// Creates an `Initialize` instruction
277
+ #[allow(clippy::too_many_arguments)]
278
+ pub fn initialize(
279
+ program_id: &Address,
280
+ metadata: &Address,
281
+ update_authority: &Address,
282
+ mint: &Address,
283
+ mint_authority: &Address,
284
+ name: String,
285
+ symbol: String,
286
+ uri: String,
287
+ ) -> Instruction {
288
+ let data = TokenMetadataInstruction::Initialize(Initialize { name, symbol, uri });
289
+ Instruction {
290
+ program_id: *program_id,
291
+ accounts: vec![
292
+ AccountMeta::new(*metadata, false),
293
+ AccountMeta::new_readonly(*update_authority, false),
294
+ AccountMeta::new_readonly(*mint, false),
295
+ AccountMeta::new_readonly(*mint_authority, true),
296
+ ],
297
+ data: data.pack(),
298
+ }
299
+ }
300
+
301
+ /// Creates an `UpdateField` instruction
302
+ pub fn update_field(
303
+ program_id: &Address,
304
+ metadata: &Address,
305
+ update_authority: &Address,
306
+ field: Field,
307
+ value: String,
308
+ ) -> Instruction {
309
+ let data = TokenMetadataInstruction::UpdateField(UpdateField { field, value });
310
+ Instruction {
311
+ program_id: *program_id,
312
+ accounts: vec![
313
+ AccountMeta::new(*metadata, false),
314
+ AccountMeta::new_readonly(*update_authority, true),
315
+ ],
316
+ data: data.pack(),
317
+ }
318
+ }
319
+
320
+ /// Creates a `RemoveKey` instruction
321
+ pub fn remove_key(
322
+ program_id: &Address,
323
+ metadata: &Address,
324
+ update_authority: &Address,
325
+ key: String,
326
+ idempotent: bool,
327
+ ) -> Instruction {
328
+ let data = TokenMetadataInstruction::RemoveKey(RemoveKey { key, idempotent });
329
+ Instruction {
330
+ program_id: *program_id,
331
+ accounts: vec![
332
+ AccountMeta::new(*metadata, false),
333
+ AccountMeta::new_readonly(*update_authority, true),
334
+ ],
335
+ data: data.pack(),
336
+ }
337
+ }
338
+
339
+ /// Creates an `UpdateAuthority` instruction
340
+ pub fn update_authority(
341
+ program_id: &Address,
342
+ metadata: &Address,
343
+ current_authority: &Address,
344
+ new_authority: MaybeNull<Address>,
345
+ ) -> Instruction {
346
+ let data = TokenMetadataInstruction::UpdateAuthority(UpdateAuthority { new_authority });
347
+ Instruction {
348
+ program_id: *program_id,
349
+ accounts: vec![
350
+ AccountMeta::new(*metadata, false),
351
+ AccountMeta::new_readonly(*current_authority, true),
352
+ ],
353
+ data: data.pack(),
354
+ }
355
+ }
356
+
357
+ /// Creates an `Emit` instruction
358
+ pub fn emit(
359
+ program_id: &Address,
360
+ metadata: &Address,
361
+ start: Option<u64>,
362
+ end: Option<u64>,
363
+ ) -> Instruction {
364
+ let data = TokenMetadataInstruction::Emit(Emit { start, end });
365
+ Instruction {
366
+ program_id: *program_id,
367
+ accounts: vec![AccountMeta::new_readonly(*metadata, false)],
368
+ data: data.pack(),
369
+ }
370
+ }
371
+
372
+ #[cfg(test)]
373
+ mod test {
374
+ #[cfg(feature = "serde-traits")]
375
+ use core::str::FromStr;
376
+ use {
377
+ super::*,
378
+ crate::NAMESPACE,
379
+ alloc::{format, string::ToString, vec},
380
+ solana_sha256_hasher::hashv,
381
+ };
382
+
383
+ fn check_pack_unpack<T: BorshSerialize>(
384
+ instruction: TokenMetadataInstruction,
385
+ discriminator: &[u8],
386
+ data: T,
387
+ ) {
388
+ let mut expect = vec![];
389
+ expect.extend_from_slice(discriminator.as_ref());
390
+ expect.append(&mut borsh::to_vec(&data).unwrap());
391
+ let packed = instruction.pack();
392
+ assert_eq!(packed, expect);
393
+ let unpacked = TokenMetadataInstruction::unpack(&expect).unwrap();
394
+ assert_eq!(unpacked, instruction);
395
+ }
396
+
397
+ #[test]
398
+ fn initialize_pack() {
399
+ let name = "My test token";
400
+ let symbol = "TEST";
401
+ let uri = "http://test.test";
402
+ let data = Initialize {
403
+ name: name.to_string(),
404
+ symbol: symbol.to_string(),
405
+ uri: uri.to_string(),
406
+ };
407
+ let check = TokenMetadataInstruction::Initialize(data.clone());
408
+ let preimage = hashv(&[format!("{NAMESPACE}:initialize_account").as_bytes()]);
409
+ let discriminator = &preimage.as_ref()[..ArrayDiscriminator::LENGTH];
410
+ check_pack_unpack(check, discriminator, data);
411
+ }
412
+
413
+ #[test]
414
+ fn update_field_pack() {
415
+ let field = "MyTestField";
416
+ let value = "http://test.uri";
417
+ let data = UpdateField {
418
+ field: Field::Key(field.to_string()),
419
+ value: value.to_string(),
420
+ };
421
+ let check = TokenMetadataInstruction::UpdateField(data.clone());
422
+ let preimage = hashv(&[format!("{NAMESPACE}:updating_field").as_bytes()]);
423
+ let discriminator = &preimage.as_ref()[..ArrayDiscriminator::LENGTH];
424
+ check_pack_unpack(check, discriminator, data);
425
+ }
426
+
427
+ #[test]
428
+ fn remove_key_pack() {
429
+ let data = RemoveKey {
430
+ key: "MyTestField".to_string(),
431
+ idempotent: true,
432
+ };
433
+ let check = TokenMetadataInstruction::RemoveKey(data.clone());
434
+ let preimage = hashv(&[format!("{NAMESPACE}:remove_key_ix").as_bytes()]);
435
+ let discriminator = &preimage.as_ref()[..ArrayDiscriminator::LENGTH];
436
+ check_pack_unpack(check, discriminator, data);
437
+ }
438
+
439
+ #[test]
440
+ fn update_authority_pack() {
441
+ let data = UpdateAuthority {
442
+ new_authority: MaybeNull::default(),
443
+ };
444
+ let check = TokenMetadataInstruction::UpdateAuthority(data.clone());
445
+ let preimage = hashv(&[format!("{NAMESPACE}:update_the_authority").as_bytes()]);
446
+ let discriminator = &preimage.as_ref()[..ArrayDiscriminator::LENGTH];
447
+ check_pack_unpack(check, discriminator, data);
448
+ }
449
+
450
+ #[test]
451
+ fn emit_pack() {
452
+ let data = Emit {
453
+ start: None,
454
+ end: Some(10),
455
+ };
456
+ let check = TokenMetadataInstruction::Emit(data.clone());
457
+ let preimage = hashv(&[format!("{NAMESPACE}:emitter").as_bytes()]);
458
+ let discriminator = &preimage.as_ref()[..ArrayDiscriminator::LENGTH];
459
+ check_pack_unpack(check, discriminator, data);
460
+ }
461
+
462
+ #[test]
463
+ fn fail_unpack_initialize_with_forged_name_length() {
464
+ let mut input = vec![];
465
+ input.extend_from_slice(Initialize::SPL_DISCRIMINATOR_SLICE);
466
+ input.extend_from_slice(&u32::MAX.to_le_bytes()); // forged name length, no bytes follow
467
+ assert_eq!(
468
+ TokenMetadataInstruction::unpack(&input).unwrap_err(),
469
+ ProgramError::InvalidInstructionData,
470
+ );
471
+ }
472
+
473
+ #[test]
474
+ fn fail_unpack_update_field_with_forged_value_length() {
475
+ let mut input = vec![];
476
+ input.extend_from_slice(UpdateField::SPL_DISCRIMINATOR_SLICE);
477
+ input.push(0u8); // Field::Name (tag 0, no nested string)
478
+ input.extend_from_slice(&u32::MAX.to_le_bytes()); // forged value length
479
+ assert_eq!(
480
+ TokenMetadataInstruction::unpack(&input).unwrap_err(),
481
+ ProgramError::InvalidInstructionData,
482
+ );
483
+ }
484
+
485
+ #[test]
486
+ fn fail_unpack_remove_key_with_forged_key_length() {
487
+ let mut input = vec![];
488
+ input.extend_from_slice(RemoveKey::SPL_DISCRIMINATOR_SLICE);
489
+ input.push(0u8); // idempotent = false
490
+ input.extend_from_slice(&u32::MAX.to_le_bytes()); // forged key length
491
+ assert_eq!(
492
+ TokenMetadataInstruction::unpack(&input).unwrap_err(),
493
+ ProgramError::InvalidInstructionData,
494
+ );
495
+ }
496
+
497
+ #[cfg(feature = "serde-traits")]
498
+ #[test]
499
+ fn initialize_serde() {
500
+ let data = Initialize {
501
+ name: "Token Name".to_string(),
502
+ symbol: "TST".to_string(),
503
+ uri: "uri.test".to_string(),
504
+ };
505
+ let ix = TokenMetadataInstruction::Initialize(data);
506
+ let serialized = serde_json::to_string(&ix).unwrap();
507
+ let serialized_expected =
508
+ "{\"initialize\":{\"name\":\"Token Name\",\"symbol\":\"TST\",\"uri\":\"uri.test\"}}";
509
+ assert_eq!(&serialized, serialized_expected);
510
+
511
+ let deserialized = serde_json::from_str::<TokenMetadataInstruction>(&serialized).unwrap();
512
+ assert_eq!(ix, deserialized);
513
+ }
514
+
515
+ #[cfg(feature = "serde-traits")]
516
+ #[test]
517
+ fn update_field_serde() {
518
+ let data = UpdateField {
519
+ field: Field::Key("MyField".to_string()),
520
+ value: "my field value".to_string(),
521
+ };
522
+ let ix = TokenMetadataInstruction::UpdateField(data);
523
+ let serialized = serde_json::to_string(&ix).unwrap();
524
+ let serialized_expected =
525
+ "{\"updateField\":{\"field\":{\"key\":\"MyField\"},\"value\":\"my field value\"}}";
526
+ assert_eq!(&serialized, serialized_expected);
527
+
528
+ let deserialized = serde_json::from_str::<TokenMetadataInstruction>(&serialized).unwrap();
529
+ assert_eq!(ix, deserialized);
530
+ }
531
+
532
+ #[cfg(feature = "serde-traits")]
533
+ #[test]
534
+ fn remove_key_serde() {
535
+ let data = RemoveKey {
536
+ key: "MyTestField".to_string(),
537
+ idempotent: true,
538
+ };
539
+ let ix = TokenMetadataInstruction::RemoveKey(data);
540
+ let serialized = serde_json::to_string(&ix).unwrap();
541
+ let serialized_expected = "{\"removeKey\":{\"idempotent\":true,\"key\":\"MyTestField\"}}";
542
+ assert_eq!(&serialized, serialized_expected);
543
+
544
+ let deserialized = serde_json::from_str::<TokenMetadataInstruction>(&serialized).unwrap();
545
+ assert_eq!(ix, deserialized);
546
+ }
547
+
548
+ #[cfg(feature = "serde-traits")]
549
+ #[test]
550
+ fn update_authority_serde() {
551
+ let update_authority_option: Option<Address> =
552
+ Some(Address::from_str("4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM").unwrap());
553
+ let update_authority: MaybeNull<Address> = update_authority_option.try_into().unwrap();
554
+ let data = UpdateAuthority {
555
+ new_authority: update_authority,
556
+ };
557
+ let ix = TokenMetadataInstruction::UpdateAuthority(data);
558
+ let serialized = serde_json::to_string(&ix).unwrap();
559
+ let serialized_expected = "{\"updateAuthority\":{\"newAuthority\":\"4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM\"}}";
560
+ assert_eq!(&serialized, serialized_expected);
561
+
562
+ let deserialized = serde_json::from_str::<TokenMetadataInstruction>(&serialized).unwrap();
563
+ assert_eq!(ix, deserialized);
564
+ }
565
+
566
+ #[cfg(feature = "serde-traits")]
567
+ #[test]
568
+ fn update_authority_serde_with_none() {
569
+ let data = UpdateAuthority {
570
+ new_authority: MaybeNull::default(),
571
+ };
572
+ let ix = TokenMetadataInstruction::UpdateAuthority(data);
573
+ let serialized = serde_json::to_string(&ix).unwrap();
574
+ let serialized_expected = "{\"updateAuthority\":{\"newAuthority\":null}}";
575
+ assert_eq!(&serialized, serialized_expected);
576
+
577
+ let deserialized = serde_json::from_str::<TokenMetadataInstruction>(&serialized).unwrap();
578
+ assert_eq!(ix, deserialized);
579
+ }
580
+
581
+ #[cfg(feature = "serde-traits")]
582
+ #[test]
583
+ fn emit_serde() {
584
+ let data = Emit {
585
+ start: None,
586
+ end: Some(10),
587
+ };
588
+ let ix = TokenMetadataInstruction::Emit(data);
589
+ let serialized = serde_json::to_string(&ix).unwrap();
590
+ let serialized_expected = "{\"emit\":{\"start\":null,\"end\":10}}";
591
+ assert_eq!(&serialized, serialized_expected);
592
+
593
+ let deserialized = serde_json::from_str::<TokenMetadataInstruction>(&serialized).unwrap();
594
+ assert_eq!(ix, deserialized);
595
+ }
596
+ }
@@ -0,0 +1,21 @@
1
+ //! Crate defining an interface for token-metadata
2
+
3
+ #![allow(clippy::arithmetic_side_effects)]
4
+ #![no_std]
5
+ #![deny(missing_docs)]
6
+ #![cfg_attr(not(test), forbid(unsafe_code))]
7
+
8
+ extern crate alloc;
9
+
10
+ pub mod error;
11
+ pub mod instruction;
12
+ pub mod state;
13
+
14
+ // Export current sdk types for downstream users building with a different sdk
15
+ // version Export borsh for downstream users
16
+ pub use {
17
+ borsh, solana_address, solana_borsh, solana_instruction, solana_nullable, solana_program_error,
18
+ };
19
+
20
+ /// Namespace for all programs implementing token-metadata
21
+ pub const NAMESPACE: &str = "spl_token_metadata_interface";