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

Commit 5756e7f

Browse files
committed
token-cli: convert unwrap and sync_native to client
also allow client to control its own nativeness (nativity?)
1 parent 953e06b commit 5756e7f

File tree

2 files changed

+93
-73
lines changed

2 files changed

+93
-73
lines changed

token/cli/src/main.rs

Lines changed: 69 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,22 @@ fn token_client_from_config(
363363
}
364364
}
365365

366+
fn native_token_client_from_config(config: &Config<'_>) -> Token<ProgramRpcClientSendTransaction> {
367+
let token = Token::new_native(
368+
config.program_client.clone(),
369+
&config.program_id,
370+
config.fee_payer.clone(),
371+
);
372+
373+
if let (Some(nonce_account), Some(nonce_authority)) =
374+
(config.nonce_account, config.nonce_authority)
375+
{
376+
token.with_nonce(&nonce_account, &nonce_authority)
377+
} else {
378+
token
379+
}
380+
}
381+
366382
#[allow(clippy::too_many_arguments)]
367383
async fn command_create_token(
368384
config: &Config<'_>,
@@ -508,7 +524,7 @@ async fn command_create_account(
508524
let token = token_client_from_config(config, &token_pubkey);
509525
let mut extensions = vec![];
510526

511-
let (account, associated) = if let Some(account) = maybe_account {
527+
let (account, is_associated) = if let Some(account) = maybe_account {
512528
(account, false)
513529
} else {
514530
(token.get_associated_token_address(&owner), true)
@@ -518,7 +534,7 @@ async fn command_create_account(
518534

519535
if !config.sign_only {
520536
if let Some(account_data) = config.program_client.get_account(account).await? {
521-
if account_data.owner != system_program::id() || !associated {
537+
if account_data.owner != system_program::id() || !is_associated {
522538
return Err(format!("Error: Account already exists: {}", account).into());
523539
}
524540
}
@@ -531,7 +547,7 @@ async fn command_create_account(
531547
config.program_id
532548
)
533549
.into());
534-
} else if associated {
550+
} else if is_associated {
535551
println_display(
536552
config,
537553
"Note: --immutable specified, but Token-2022 ATAs are always immutable".to_string(),
@@ -541,7 +557,7 @@ async fn command_create_account(
541557
}
542558
}
543559

544-
let res = if associated {
560+
let res = if is_associated {
545561
token.create_associated_token_account(&owner).await
546562
} else {
547563
let signer = bulk_signers
@@ -1189,17 +1205,6 @@ async fn command_thaw(
11891205
})
11901206
}
11911207

1192-
// XXX TODO remove this when functionality moved into client
1193-
fn native_mint(program_id: &Pubkey) -> Result<Pubkey, Error> {
1194-
if program_id == &spl_token_2022::id() {
1195-
Ok(spl_token_2022::native_mint::id())
1196-
} else if program_id == &spl_token::id() {
1197-
Ok(spl_token::native_mint::id())
1198-
} else {
1199-
Err(format!("Error: unknown token program id {}", program_id).into())
1200-
}
1201-
}
1202-
12031208
async fn command_wrap(
12041209
config: &Config<'_>,
12051210
sol: f64,
@@ -1208,15 +1213,12 @@ async fn command_wrap(
12081213
bulk_signers: BulkSigners,
12091214
) -> CommandResult {
12101215
let lamports = sol_to_lamports(sol);
1211-
// XXX TODO i think i want a Token fn `new_native` that encapsulates the `native_mint` logic in one place
1212-
// also a function on it `is_native` maybe
1213-
let native_mint = native_mint(&config.program_id)?;
1214-
let token = token_client_from_config(config, &native_mint);
1216+
let token = native_token_client_from_config(config);
12151217

12161218
let account = wrapped_sol_account.unwrap_or_else(|| {
12171219
get_associated_token_address_with_program_id(
12181220
&wallet_address,
1219-
&native_mint,
1221+
token.get_address(),
12201222
&config.program_id,
12211223
)
12221224
});
@@ -1251,19 +1253,22 @@ async fn command_wrap(
12511253
async fn command_unwrap(
12521254
config: &Config<'_>,
12531255
wallet_address: Pubkey,
1254-
account: Option<Pubkey>,
1256+
maybe_account: Option<Pubkey>,
12551257
bulk_signers: BulkSigners,
12561258
) -> CommandResult {
1257-
let use_associated_account = account.is_none();
1258-
let native_mint = native_mint(&config.program_id)?;
1259-
let account = account.unwrap_or_else(|| {
1259+
let use_associated_account = maybe_account.is_none();
1260+
let token = native_token_client_from_config(config);
1261+
1262+
let account = maybe_account.unwrap_or_else(|| {
12601263
get_associated_token_address_with_program_id(
12611264
&wallet_address,
1262-
&native_mint,
1265+
token.get_address(),
12631266
&config.program_id,
12641267
)
12651268
});
1269+
12661270
println_display(config, format!("Unwrapping {}", account));
1271+
12671272
if !config.sign_only {
12681273
let lamports = config.rpc_client.get_balance(&account).await?;
12691274
if lamports == 0 {
@@ -1273,30 +1278,29 @@ async fn command_unwrap(
12731278
return Err(format!("No wrapped SOL in {}", account).into());
12741279
}
12751280
}
1281+
12761282
println_display(
12771283
config,
12781284
format!(" Amount: {} SOL", lamports_to_sol(lamports)),
12791285
);
1286+
1287+
if !use_associated_account {
1288+
let account_data = config.get_account_checked(&account).await?;
1289+
let account_state = StateWithExtensionsOwned::<Account>::unpack(account_data.data)?;
1290+
1291+
if account_state.base.mint != *token.get_address() {
1292+
return Err(format!("{} is not a native token account", account).into());
1293+
}
1294+
}
12801295
}
1296+
12811297
println_display(config, format!(" Recipient: {}", &wallet_address));
12821298

1283-
let instructions = vec![close_account(
1284-
&config.program_id,
1285-
&account,
1286-
&wallet_address,
1287-
&wallet_address,
1288-
&config.multisigner_pubkeys,
1289-
)?];
1290-
let tx_return = handle_tx(
1291-
&CliSignerInfo {
1292-
signers: bulk_signers,
1293-
},
1294-
config,
1295-
false,
1296-
0,
1297-
instructions,
1298-
)
1299-
.await?;
1299+
let res = token
1300+
.close_account(&account, &wallet_address, &wallet_address, &bulk_signers)
1301+
.await?;
1302+
1303+
let tx_return = finish_tx(config, &res, false).await?;
13001304
Ok(match tx_return {
13011305
TransactionReturnData::CliSignature(signature) => {
13021306
config.output_format.formatted_string(&signature)
@@ -1811,25 +1815,20 @@ async fn command_gc(
18111815
Ok(result)
18121816
}
18131817

1814-
async fn command_sync_native(
1815-
native_account_address: Pubkey,
1816-
bulk_signers: Vec<Arc<dyn Signer>>,
1817-
config: &Config<'_>,
1818-
) -> CommandResult {
1818+
async fn command_sync_native(config: &Config<'_>, native_account_address: Pubkey) -> CommandResult {
1819+
let token = native_token_client_from_config(config);
1820+
18191821
if !config.sign_only {
1820-
config.get_account_checked(&native_account_address).await?;
1822+
let account_data = config.get_account_checked(&native_account_address).await?;
1823+
let account_state = StateWithExtensionsOwned::<Account>::unpack(account_data.data)?;
1824+
1825+
if account_state.base.mint != *token.get_address() {
1826+
return Err(format!("{} is not a native token account", native_account_address).into());
1827+
}
18211828
}
18221829

1823-
let tx_return = handle_tx(
1824-
&CliSignerInfo {
1825-
signers: bulk_signers,
1826-
},
1827-
config,
1828-
false,
1829-
0,
1830-
vec![sync_native(&config.program_id, &native_account_address)?],
1831-
)
1832-
.await?;
1830+
let res = token.sync_native(&native_account_address).await?;
1831+
let tx_return = finish_tx(config, &res, false).await?;
18331832
Ok(match tx_return {
18341833
TransactionReturnData::CliSignature(signature) => {
18351834
config.output_format.formatted_string(&signature)
@@ -3437,8 +3436,7 @@ async fn process_command<'a>(
34373436
.await
34383437
}
34393438
(CommandName::SyncNative, arg_matches) => {
3440-
let program_id = config.program_id;
3441-
let native_mint = native_mint(&program_id)?;
3439+
let native_mint = *native_token_client_from_config(config).get_address();
34423440
let address = config
34433441
.associated_token_address_for_token_or_override(
34443442
arg_matches,
@@ -3447,7 +3445,7 @@ async fn process_command<'a>(
34473445
Some(native_mint),
34483446
)
34493447
.await;
3450-
command_sync_native(address, bulk_signers, config).await
3448+
command_sync_native(config, address).await
34513449
}
34523450
(CommandName::EnableRequiredTransferMemos, arg_matches) => {
34533451
let (owner_signer, owner) =
@@ -3599,6 +3597,16 @@ mod tests {
35993597
tempfile::NamedTempFile,
36003598
};
36013599

3600+
fn native_mint(program_id: &Pubkey) -> Result<Pubkey, Error> {
3601+
if program_id == &spl_token_2022::id() {
3602+
Ok(spl_token_2022::native_mint::id())
3603+
} else if program_id == &spl_token::id() {
3604+
Ok(spl_token::native_mint::id())
3605+
} else {
3606+
Err(format!("Error: unknown token program id {}", program_id).into())
3607+
}
3608+
}
3609+
36023610
fn clone_keypair(keypair: &Keypair) -> Keypair {
36033611
Keypair::from_bytes(&keypair.to_bytes()).unwrap()
36043612
}

token/client/src/token.rs

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,16 @@ impl<T> fmt::Debug for Token<T> {
217217
}
218218
}
219219

220+
fn native_mint(program_id: &Pubkey) -> Pubkey {
221+
if program_id == &spl_token_2022::id() {
222+
spl_token_2022::native_mint::id()
223+
} else if program_id == &spl_token::id() {
224+
spl_token::native_mint::id()
225+
} else {
226+
panic!("Unrecognized token program id: {}", program_id);
227+
}
228+
}
229+
220230
impl<T> Token<T>
221231
where
222232
T: SendTransaction,
@@ -238,6 +248,18 @@ where
238248
}
239249
}
240250

251+
pub fn new_native(
252+
client: Arc<dyn ProgramClient<T>>,
253+
program_id: &Pubkey,
254+
payer: Arc<dyn Signer>,
255+
) -> Self {
256+
Self::new(client, program_id, &native_mint(program_id), payer)
257+
}
258+
259+
pub fn is_native(&self) -> bool {
260+
self.pubkey == native_mint(&self.program_id)
261+
}
262+
241263
/// Get token address.
242264
pub fn get_address(&self) -> &Pubkey {
243265
&self.pubkey
@@ -443,7 +465,7 @@ where
443465
program_id: &Pubkey,
444466
payer: Arc<dyn Signer>,
445467
) -> TokenResult<Self> {
446-
let token = Self::new(client, program_id, &native_mint(program_id), payer);
468+
let token = Self::new_native(client, program_id, payer);
447469
token
448470
.process_ixs::<[&dyn Signer; 0]>(
449471
&[instruction::create_native_mint(
@@ -928,7 +950,7 @@ where
928950
lamports: u64,
929951
immutable_owner: bool,
930952
) -> TokenResult<Vec<Instruction>> {
931-
if self.pubkey != native_mint(&self.program_id) {
953+
if !self.is_native() {
932954
return Err(TokenError::AccountInvalidMint);
933955
}
934956

@@ -2006,13 +2028,3 @@ where
20062028
.await
20072029
}
20082030
}
2009-
2010-
fn native_mint(program_id: &Pubkey) -> Pubkey {
2011-
if program_id == &spl_token_2022::id() {
2012-
spl_token_2022::native_mint::id()
2013-
} else if program_id == &spl_token::id() {
2014-
spl_token::native_mint::id()
2015-
} else {
2016-
panic!("Unrecognized token program id: {}", program_id);
2017-
}
2018-
}

0 commit comments

Comments
 (0)