fix(stablecoin): require controller initialization signer

This commit is contained in:
Ricardo Guilherme Schmidt 2026-06-29 15:03:39 -03:00
parent 5626f3af76
commit 7e4e9eeaf0
No known key found for this signature in database
GPG Key ID: 1396EA17DE132FFE
5 changed files with 41 additions and 6 deletions

View File

@ -125,7 +125,7 @@
{
"name": "stablecoin_definition",
"writable": false,
"signer": false,
"signer": true,
"init": false
},
{

View File

@ -27,6 +27,10 @@ impl Keys {
fn user_stablecoin_holding() -> PrivateKey {
PrivateKey::try_new([43; 32]).expect("valid private key")
}
fn stablecoin_definition() -> PrivateKey {
PrivateKey::try_new([44; 32]).expect("valid private key")
}
}
impl Ids {
@ -51,7 +55,9 @@ impl Ids {
}
fn stablecoin_definition() -> AccountId {
AccountId::new([6; 32])
AccountId::from(&PublicKey::new_from_private_key(
&Keys::stablecoin_definition(),
))
}
fn price_feed() -> AccountId {
@ -486,11 +492,12 @@ fn stablecoin_redemption_controller_initializes_and_updates_from_price_feed() {
Ids::stablecoin_definition(),
Ids::price_feed(),
],
vec![],
vec![current_nonce(&state, Ids::stablecoin_definition())],
initialize,
)
.unwrap();
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
let witness_set =
public_transaction::WitnessSet::for_message(&message, &[&Keys::stablecoin_definition()]);
let tx = PublicTransaction::new(message, witness_set);
state
.transition_from_public_transaction(&tx, 0, current_timestamp)

View File

@ -91,7 +91,7 @@ pub enum Instruction {
/// Required accounts (3):
/// - Redemption controller account (uninitialized, address must match
/// `compute_redemption_controller_pda(self_program_id, stablecoin_definition, price_feed)`)
/// - Stablecoin token definition account (initialized fungible token)
/// - Stablecoin token definition account (authorized initialized fungible token)
/// - Oracle price feed account (initialized; must decode as the configured
/// stablecoin/collateral market price)
///

View File

@ -130,6 +130,7 @@ mod stablecoin {
ctx: ProgramContext,
#[account(init)]
controller: AccountWithMetadata,
#[account(signer)]
stablecoin_definition: AccountWithMetadata,
price_feed: AccountWithMetadata,
collateral_definition_id: AccountId,

View File

@ -16,6 +16,7 @@ const CONTROLLER_GAIN_SCALE_I128: i128 = {
/// Initialize the redemption-rate feedback controller for one stablecoin/feed pair.
///
/// # Panics
/// - `stablecoin_definition` is not authorized.
/// - `controller` is already initialized.
/// - `controller.account_id` does not match the stablecoin/feed PDA.
/// - `stablecoin_definition` is uninitialized or not a fungible token definition.
@ -39,6 +40,10 @@ pub fn initialize_redemption_controller(
max_price_feed_age: u64,
current_timestamp: u64,
) -> Vec<AccountPostState> {
assert!(
stablecoin_definition.is_authorized,
"Stablecoin definition authorization is missing"
);
assert_eq!(
controller.account,
Account::default(),
@ -380,7 +385,7 @@ mod tests {
}),
nonce: Nonce(0),
},
is_authorized: false,
is_authorized: true,
account_id: stablecoin_definition_id(),
}
}
@ -489,6 +494,28 @@ mod tests {
assert_eq!(controller.last_update_timestamp, 100);
}
#[test]
#[should_panic(expected = "Stablecoin definition authorization is missing")]
fn initialize_redemption_controller_requires_stablecoin_definition_authorization() {
let mut stablecoin_definition = stablecoin_definition_account();
stablecoin_definition.is_authorized = false;
initialize_redemption_controller(
uninit_controller_account(),
stablecoin_definition,
price_feed_account(1_000, 100),
STABLECOIN_PROGRAM_ID,
collateral_definition_id(),
1_000,
CONTROLLER_GAIN_SCALE,
0,
1_000,
500,
10,
100,
);
}
#[test]
fn update_redemption_controller_uses_live_price_feed() {
let post_states = update_redemption_controller(