Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit 562c935

Browse files
committed
token 2022: add GroupPointer extension
1 parent 6384308 commit 562c935

File tree

8 files changed

+593
-2
lines changed

8 files changed

+593
-2
lines changed

token/client/src/token.rs

+38-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ use {
4040
self, account_info::WithheldTokensInfo, ConfidentialTransferFeeAmount,
4141
ConfidentialTransferFeeConfig,
4242
},
43-
cpi_guard, default_account_state, interest_bearing_mint, memo_transfer,
43+
cpi_guard, default_account_state, group_pointer, interest_bearing_mint, memo_transfer,
4444
metadata_pointer, transfer_fee, transfer_hook, BaseStateWithExtensions, ExtensionType,
4545
StateWithExtensionsOwned,
4646
},
@@ -178,6 +178,10 @@ pub enum ExtensionInitializationParams {
178178
authority: Option<Pubkey>,
179179
withdraw_withheld_authority_elgamal_pubkey: PodElGamalPubkey,
180180
},
181+
GroupPointer {
182+
authority: Option<Pubkey>,
183+
group_address: Option<Pubkey>,
184+
},
181185
}
182186
impl ExtensionInitializationParams {
183187
/// Get the extension type associated with the init params
@@ -195,6 +199,7 @@ impl ExtensionInitializationParams {
195199
Self::ConfidentialTransferFeeConfig { .. } => {
196200
ExtensionType::ConfidentialTransferFeeConfig
197201
}
202+
Self::GroupPointer { .. } => ExtensionType::GroupPointer,
198203
}
199204
}
200205
/// Generate an appropriate initialization instruction for the given mint
@@ -286,6 +291,15 @@ impl ExtensionInitializationParams {
286291
withdraw_withheld_authority_elgamal_pubkey,
287292
)
288293
}
294+
Self::GroupPointer {
295+
authority,
296+
group_address,
297+
} => group_pointer::instruction::initialize(
298+
token_program_id,
299+
mint,
300+
authority,
301+
group_address,
302+
),
289303
}
290304
}
291305
}
@@ -1666,6 +1680,29 @@ where
16661680
.await
16671681
}
16681682

1683+
/// Update group pointer address
1684+
pub async fn update_group_address<S: Signers>(
1685+
&self,
1686+
authority: &Pubkey,
1687+
new_group_address: Option<Pubkey>,
1688+
signing_keypairs: &S,
1689+
) -> TokenResult<T::Output> {
1690+
let signing_pubkeys = signing_keypairs.pubkeys();
1691+
let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);
1692+
1693+
self.process_ixs(
1694+
&[group_pointer::instruction::update(
1695+
&self.program_id,
1696+
self.get_address(),
1697+
authority,
1698+
&multisig_signers,
1699+
new_group_address,
1700+
)?],
1701+
signing_keypairs,
1702+
)
1703+
.await
1704+
}
1705+
16691706
/// Update confidential transfer mint
16701707
pub async fn confidential_transfer_update_mint<S: Signers>(
16711708
&self,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
#![cfg(feature = "test-sbf")]
2+
3+
mod program_test;
4+
use {
5+
program_test::TestContext,
6+
solana_program_test::{processor, tokio, ProgramTest},
7+
solana_sdk::{
8+
instruction::InstructionError, pubkey::Pubkey, signature::Signer, signer::keypair::Keypair,
9+
transaction::TransactionError, transport::TransportError,
10+
},
11+
spl_token_2022::{
12+
error::TokenError,
13+
extension::{group_pointer::GroupPointer, BaseStateWithExtensions},
14+
instruction,
15+
processor::Processor,
16+
},
17+
spl_token_client::token::{ExtensionInitializationParams, TokenError as TokenClientError},
18+
std::{convert::TryInto, sync::Arc},
19+
};
20+
21+
fn setup_program_test() -> ProgramTest {
22+
let mut program_test = ProgramTest::default();
23+
program_test.prefer_bpf(false);
24+
program_test.add_program(
25+
"spl_token_2022",
26+
spl_token_2022::id(),
27+
processor!(Processor::process),
28+
);
29+
program_test
30+
}
31+
32+
async fn setup(mint: Keypair, group_address: &Pubkey, authority: &Pubkey) -> TestContext {
33+
let program_test = setup_program_test();
34+
35+
let context = program_test.start_with_context().await;
36+
let context = Arc::new(tokio::sync::Mutex::new(context));
37+
let mut context = TestContext {
38+
context,
39+
token_context: None,
40+
};
41+
context
42+
.init_token_with_mint_keypair_and_freeze_authority(
43+
mint,
44+
vec![ExtensionInitializationParams::GroupPointer {
45+
authority: Some(*authority),
46+
group_address: Some(*group_address),
47+
}],
48+
None,
49+
)
50+
.await
51+
.unwrap();
52+
context
53+
}
54+
55+
#[tokio::test]
56+
async fn success_init() {
57+
let authority = Pubkey::new_unique();
58+
let group_address = Pubkey::new_unique();
59+
let mint_keypair = Keypair::new();
60+
let token = setup(mint_keypair, &group_address, &authority)
61+
.await
62+
.token_context
63+
.take()
64+
.unwrap()
65+
.token;
66+
67+
let state = token.get_mint_info().await.unwrap();
68+
assert!(state.base.is_initialized);
69+
let extension = state.get_extension::<GroupPointer>().unwrap();
70+
assert_eq!(extension.authority, Some(authority).try_into().unwrap());
71+
assert_eq!(
72+
extension.group_address,
73+
Some(group_address).try_into().unwrap()
74+
);
75+
}
76+
77+
#[tokio::test]
78+
async fn fail_init_all_none() {
79+
let mut program_test = ProgramTest::default();
80+
program_test.prefer_bpf(false);
81+
program_test.add_program(
82+
"spl_token_2022",
83+
spl_token_2022::id(),
84+
processor!(Processor::process),
85+
);
86+
let context = program_test.start_with_context().await;
87+
let context = Arc::new(tokio::sync::Mutex::new(context));
88+
let mut context = TestContext {
89+
context,
90+
token_context: None,
91+
};
92+
let err = context
93+
.init_token_with_mint_keypair_and_freeze_authority(
94+
Keypair::new(),
95+
vec![ExtensionInitializationParams::GroupPointer {
96+
authority: None,
97+
group_address: None,
98+
}],
99+
None,
100+
)
101+
.await
102+
.unwrap_err();
103+
assert_eq!(
104+
err,
105+
TokenClientError::Client(Box::new(TransportError::TransactionError(
106+
TransactionError::InstructionError(
107+
1,
108+
InstructionError::Custom(TokenError::InvalidInstruction as u32)
109+
)
110+
)))
111+
);
112+
}
113+
114+
#[tokio::test]
115+
async fn set_authority() {
116+
let authority = Keypair::new();
117+
let group_address = Pubkey::new_unique();
118+
let mint_keypair = Keypair::new();
119+
let token = setup(mint_keypair, &group_address, &authority.pubkey())
120+
.await
121+
.token_context
122+
.take()
123+
.unwrap()
124+
.token;
125+
let new_authority = Keypair::new();
126+
127+
// fail, wrong signature
128+
let wrong = Keypair::new();
129+
let err = token
130+
.set_authority(
131+
token.get_address(),
132+
&wrong.pubkey(),
133+
Some(&new_authority.pubkey()),
134+
instruction::AuthorityType::GroupPointer,
135+
&[&wrong],
136+
)
137+
.await
138+
.unwrap_err();
139+
assert_eq!(
140+
err,
141+
TokenClientError::Client(Box::new(TransportError::TransactionError(
142+
TransactionError::InstructionError(
143+
0,
144+
InstructionError::Custom(TokenError::OwnerMismatch as u32)
145+
)
146+
)))
147+
);
148+
149+
// success
150+
token
151+
.set_authority(
152+
token.get_address(),
153+
&authority.pubkey(),
154+
Some(&new_authority.pubkey()),
155+
instruction::AuthorityType::GroupPointer,
156+
&[&authority],
157+
)
158+
.await
159+
.unwrap();
160+
let state = token.get_mint_info().await.unwrap();
161+
let extension = state.get_extension::<GroupPointer>().unwrap();
162+
assert_eq!(
163+
extension.authority,
164+
Some(new_authority.pubkey()).try_into().unwrap(),
165+
);
166+
167+
// set to none
168+
token
169+
.set_authority(
170+
token.get_address(),
171+
&new_authority.pubkey(),
172+
None,
173+
instruction::AuthorityType::GroupPointer,
174+
&[&new_authority],
175+
)
176+
.await
177+
.unwrap();
178+
let state = token.get_mint_info().await.unwrap();
179+
let extension = state.get_extension::<GroupPointer>().unwrap();
180+
assert_eq!(extension.authority, None.try_into().unwrap(),);
181+
182+
// fail set again
183+
let err = token
184+
.set_authority(
185+
token.get_address(),
186+
&new_authority.pubkey(),
187+
Some(&authority.pubkey()),
188+
instruction::AuthorityType::GroupPointer,
189+
&[&new_authority],
190+
)
191+
.await
192+
.unwrap_err();
193+
assert_eq!(
194+
err,
195+
TokenClientError::Client(Box::new(TransportError::TransactionError(
196+
TransactionError::InstructionError(
197+
0,
198+
InstructionError::Custom(TokenError::AuthorityTypeNotSupported as u32)
199+
)
200+
)))
201+
);
202+
}
203+
204+
#[tokio::test]
205+
async fn update_group_address() {
206+
let authority = Keypair::new();
207+
let group_address = Pubkey::new_unique();
208+
let mint_keypair = Keypair::new();
209+
let token = setup(mint_keypair, &group_address, &authority.pubkey())
210+
.await
211+
.token_context
212+
.take()
213+
.unwrap()
214+
.token;
215+
let new_group_address = Pubkey::new_unique();
216+
217+
// fail, wrong signature
218+
let wrong = Keypair::new();
219+
let err = token
220+
.update_group_address(&wrong.pubkey(), Some(new_group_address), &[&wrong])
221+
.await
222+
.unwrap_err();
223+
assert_eq!(
224+
err,
225+
TokenClientError::Client(Box::new(TransportError::TransactionError(
226+
TransactionError::InstructionError(
227+
0,
228+
InstructionError::Custom(TokenError::OwnerMismatch as u32)
229+
)
230+
)))
231+
);
232+
233+
// success
234+
token
235+
.update_group_address(&authority.pubkey(), Some(new_group_address), &[&authority])
236+
.await
237+
.unwrap();
238+
let state = token.get_mint_info().await.unwrap();
239+
let extension = state.get_extension::<GroupPointer>().unwrap();
240+
assert_eq!(
241+
extension.group_address,
242+
Some(new_group_address).try_into().unwrap(),
243+
);
244+
245+
// set to none
246+
token
247+
.update_group_address(&authority.pubkey(), None, &[&authority])
248+
.await
249+
.unwrap();
250+
let state = token.get_mint_info().await.unwrap();
251+
let extension = state.get_extension::<GroupPointer>().unwrap();
252+
assert_eq!(extension.group_address, None.try_into().unwrap(),);
253+
}

0 commit comments

Comments
 (0)