Skip to content

Commit

Permalink
Add timestamp, rename to "multiplier"
Browse files Browse the repository at this point in the history
  • Loading branch information
joncinque committed Nov 21, 2024
1 parent 772f34f commit 4afb6e6
Show file tree
Hide file tree
Showing 6 changed files with 282 additions and 102 deletions.
26 changes: 17 additions & 9 deletions token/client/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ pub enum ExtensionInitializationParams {
},
ScaledUiAmountConfig {
authority: Option<Pubkey>,
scale: f64,
multiplier: f64,
},
}
impl ExtensionInitializationParams {
Expand Down Expand Up @@ -322,9 +322,15 @@ impl ExtensionInitializationParams {
authority,
member_address,
),
Self::ScaledUiAmountConfig { authority, scale } => {
scaled_ui_amount::instruction::initialize(token_program_id, mint, authority, scale)
}
Self::ScaledUiAmountConfig {
authority,
multiplier,
} => scaled_ui_amount::instruction::initialize(
token_program_id,
mint,
authority,
multiplier,
),
}
}
}
Expand Down Expand Up @@ -1814,23 +1820,25 @@ where
.await
}

/// Update scale
pub async fn update_scale<S: Signers>(
/// Update multiplier
pub async fn update_multiplier<S: Signers>(
&self,
authority: &Pubkey,
new_scale: f64,
new_multiplier: f64,
new_multiplier_effective_timestamp: i64,
signing_keypairs: &S,
) -> TokenResult<T::Output> {
let signing_pubkeys = signing_keypairs.pubkeys();
let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys);

self.process_ixs(
&[scaled_ui_amount::instruction::update_scale(
&[scaled_ui_amount::instruction::update_multiplier(
&self.program_id,
self.get_address(),
authority,
&multisig_signers,
new_scale,
new_multiplier,
new_multiplier_effective_timestamp,
)?],
signing_keypairs,
)
Expand Down
91 changes: 72 additions & 19 deletions token/program-2022-test/tests/scaled_ui_amount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ use {

#[tokio::test]
async fn success_initialize() {
for (scale, authority) in [
for (multiplier, authority) in [
(f64::MIN_POSITIVE, None),
(f64::MAX, Some(Pubkey::new_unique())),
] {
let mut context = TestContext::new().await;
context
.init_token_with_mint(vec![ExtensionInitializationParams::ScaledUiAmountConfig {
authority,
scale,
multiplier,
}])
.await
.unwrap();
Expand All @@ -50,42 +50,95 @@ async fn success_initialize() {
let state = token.get_mint_info().await.unwrap();
let extension = state.get_extension::<ScaledUiAmountConfig>().unwrap();
assert_eq!(Option::<Pubkey>::from(extension.authority), authority,);
assert_eq!(f64::from(extension.scale), scale);
assert_eq!(f64::from(extension.multiplier), multiplier);
assert_eq!(f64::from(extension.new_multiplier), multiplier);
assert_eq!(i64::from(extension.new_multiplier_effective_timestamp), 0);
}
}

#[tokio::test]
async fn update_scale() {
async fn fail_initialize_with_interest_bearing() {
let authority = None;
let mut context = TestContext::new().await;
let err = context
.init_token_with_mint(vec![
ExtensionInitializationParams::ScaledUiAmountConfig {
authority,
multiplier: 1.0,
},
ExtensionInitializationParams::InterestBearingConfig {
rate_authority: None,
rate: 0,
},
])
.await
.unwrap_err();
assert_eq!(
err,
TokenClientError::Client(Box::new(TransportError::TransactionError(
TransactionError::InstructionError(
3,
InstructionError::Custom(TokenError::InvalidExtensionCombination as u32)
)
)))
);
}

#[tokio::test]
async fn update_multiplier() {
let authority = Keypair::new();
let initial_scale = 5.0;
let initial_multiplier = 5.0;
let mut context = TestContext::new().await;
context
.init_token_with_mint(vec![ExtensionInitializationParams::ScaledUiAmountConfig {
authority: Some(authority.pubkey()),
scale: initial_scale,
multiplier: initial_multiplier,
}])
.await
.unwrap();
let TokenContext { token, .. } = context.token_context.take().unwrap();

let state = token.get_mint_info().await.unwrap();
let extension = state.get_extension::<ScaledUiAmountConfig>().unwrap();
assert_eq!(f64::from(extension.scale), initial_scale);
assert_eq!(f64::from(extension.multiplier), initial_multiplier);
assert_eq!(f64::from(extension.new_multiplier), initial_multiplier);

// correct
let new_scale = 10.0;
let new_multiplier = 10.0;
token
.update_multiplier(&authority.pubkey(), new_multiplier, 0, &[&authority])
.await
.unwrap();
let state = token.get_mint_info().await.unwrap();
let extension = state.get_extension::<ScaledUiAmountConfig>().unwrap();
assert_eq!(f64::from(extension.multiplier), new_multiplier);
assert_eq!(f64::from(extension.new_multiplier), new_multiplier);
assert_eq!(i64::from(extension.new_multiplier_effective_timestamp), 0);

// correct in the future
let newest_multiplier = 100.0;
token
.update_scale(&authority.pubkey(), new_scale, &[&authority])
.update_multiplier(
&authority.pubkey(),
newest_multiplier,
i64::MAX,
&[&authority],
)
.await
.unwrap();
let state = token.get_mint_info().await.unwrap();
let extension = state.get_extension::<ScaledUiAmountConfig>().unwrap();
assert_eq!(f64::from(extension.scale), new_scale);
assert_eq!(f64::from(extension.multiplier), new_multiplier);
assert_eq!(f64::from(extension.new_multiplier), newest_multiplier);
assert_eq!(
i64::from(extension.new_multiplier_effective_timestamp),
i64::MAX
);

// wrong signer
let wrong_signer = Keypair::new();
let err = token
.update_scale(&wrong_signer.pubkey(), 1.0, &[&wrong_signer])
.update_multiplier(&wrong_signer.pubkey(), 1.0, 0, &[&wrong_signer])
.await
.unwrap_err();
assert_eq!(
Expand All @@ -102,12 +155,12 @@ async fn update_scale() {
#[tokio::test]
async fn set_authority() {
let authority = Keypair::new();
let initial_scale = 500.0;
let initial_multiplier = 500.0;
let mut context = TestContext::new().await;
context
.init_token_with_mint(vec![ExtensionInitializationParams::ScaledUiAmountConfig {
authority: Some(authority.pubkey()),
scale: initial_scale,
multiplier: initial_multiplier,
}])
.await
.unwrap();
Expand All @@ -132,11 +185,11 @@ async fn set_authority() {
Some(new_authority.pubkey()).try_into().unwrap(),
);
token
.update_scale(&new_authority.pubkey(), 10.0, &[&new_authority])
.update_multiplier(&new_authority.pubkey(), 10.0, 0, &[&new_authority])
.await
.unwrap();
let err = token
.update_scale(&authority.pubkey(), 100.0, &[&authority])
.update_multiplier(&authority.pubkey(), 100.0, 0, &[&authority])
.await
.unwrap_err();
assert_eq!(
Expand Down Expand Up @@ -166,7 +219,7 @@ async fn set_authority() {

// now all fail
let err = token
.update_scale(&new_authority.pubkey(), 50.0, &[&new_authority])
.update_multiplier(&new_authority.pubkey(), 50.0, 0, &[&new_authority])
.await
.unwrap_err();
assert_eq!(
Expand All @@ -179,7 +232,7 @@ async fn set_authority() {
)))
);
let err = token
.update_scale(&authority.pubkey(), 5.5, &[&authority])
.update_multiplier(&authority.pubkey(), 5.5, 0, &[&authority])
.await
.unwrap_err();
assert_eq!(
Expand Down Expand Up @@ -256,11 +309,11 @@ async fn amount_conversions() {
context,
token_context: None,
};
let initial_scale = 5.0;
let initial_multiplier = 5.0;
context
.init_token_with_mint(vec![ExtensionInitializationParams::ScaledUiAmountConfig {
authority: Some(authority.pubkey()),
scale: initial_scale,
multiplier: initial_multiplier,
}])
.await
.unwrap();
Expand Down
52 changes: 36 additions & 16 deletions token/program-2022/src/extension/scaled_ui_amount/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
use {
crate::{
check_program_account,
extension::scaled_ui_amount::PodF64,
extension::scaled_ui_amount::{PodF64, UnixTimestamp},
instruction::{encode_instruction, TokenInstruction},
},
bytemuck::{Pod, Zeroable},
Expand Down Expand Up @@ -41,23 +41,27 @@ pub enum ScaledUiAmountMintInstruction {
/// Data expected by this instruction:
/// `crate::extension::scaled_ui_amount::instruction::InitializeInstructionData`
Initialize,
/// Update the scale. Only supported for mints that include the
/// Update the multiplier. Only supported for mints that include the
/// `ScaledUiAmount` extension.
///
/// The authority provides a new multiplier and a unix timestamp on which
/// it should take effect. If the timestamp is before the current time,
/// immediately sets the multiplier.
///
/// Accounts expected by this instruction:
///
/// * Single authority
/// 0. `[writable]` The mint.
/// 1. `[signer]` The mint scale authority.
/// 1. `[signer]` The multiplier authority.
///
/// * Multisignature authority
/// 0. `[writable]` The mint.
/// 1. `[]` The mint's multisignature scale authority.
/// 1. `[]` The mint's multisignature multiplier authority.
/// 2. `..2+M` `[signer]` M signer accounts.
///
/// Data expected by this instruction:
/// `crate::extension::scaled_ui_amount::PodF64`
UpdateScale,
/// `crate::extension::scaled_ui_amount::instruction::UpdateMultiplierInstructionData`
UpdateMultiplier,
}

/// Data expected by `ScaledUiAmountMint::Initialize`
Expand All @@ -66,18 +70,30 @@ pub enum ScaledUiAmountMintInstruction {
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct InitializeInstructionData {
/// The public key for the account that can update the scale
/// The public key for the account that can update the multiplier
pub authority: OptionalNonZeroPubkey,
/// The initial scale
pub scale: PodF64,
/// The initial multiplier
pub multiplier: PodF64,
}

/// Data expected by `ScaledUiAmountMint::UpdateMultiplier`
#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))]
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct UpdateMultiplierInstructionData {
/// The new multiplier
pub multiplier: PodF64,
/// Timestamp at which the new multiplier will take effect
pub effective_timestamp: UnixTimestamp,
}

/// Create an `Initialize` instruction
pub fn initialize(
token_program_id: &Pubkey,
mint: &Pubkey,
authority: Option<Pubkey>,
scale: f64,
multiplier: f64,
) -> Result<Instruction, ProgramError> {
check_program_account(token_program_id)?;
let accounts = vec![AccountMeta::new(*mint, false)];
Expand All @@ -88,18 +104,19 @@ pub fn initialize(
ScaledUiAmountMintInstruction::Initialize,
&InitializeInstructionData {
authority: authority.try_into()?,
scale: scale.into(),
multiplier: multiplier.into(),
},
))
}

/// Create an `UpdateScale` instruction
pub fn update_scale(
/// Create an `UpdateMultiplier` instruction
pub fn update_multiplier(
token_program_id: &Pubkey,
mint: &Pubkey,
authority: &Pubkey,
signers: &[&Pubkey],
scale: f64,
multiplier: f64,
effective_timestamp: i64,
) -> Result<Instruction, ProgramError> {
check_program_account(token_program_id)?;
let mut accounts = vec![
Expand All @@ -113,7 +130,10 @@ pub fn update_scale(
token_program_id,
accounts,
TokenInstruction::ScaledUiAmountExtension,
ScaledUiAmountMintInstruction::UpdateScale,
&PodF64::from(scale),
ScaledUiAmountMintInstruction::UpdateMultiplier,
&UpdateMultiplierInstructionData {
effective_timestamp: effective_timestamp.into(),
multiplier: multiplier.into(),
},
))
}
Loading

0 comments on commit 4afb6e6

Please sign in to comment.