mirror of
https://github.com/logos-blockchain/lez-programs.git
synced 2026-07-26 16:53:12 +00:00
refactor: gate mint/set_authority via lez-authority with explicit signer account
This commit is contained in:
parent
029f617737
commit
175c9d256c
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -2251,13 +2251,6 @@ dependencies = [
|
|||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lez-authority"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"borsh",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.186"
|
version = "0.2.186"
|
||||||
|
|||||||
@ -139,6 +139,12 @@
|
|||||||
"signer": true,
|
"signer": true,
|
||||||
"init": false
|
"init": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "authority_account",
|
||||||
|
"writable": false,
|
||||||
|
"signer": false,
|
||||||
|
"init": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "user_holding_account",
|
"name": "user_holding_account",
|
||||||
"writable": true,
|
"writable": true,
|
||||||
@ -197,6 +203,12 @@
|
|||||||
"writable": false,
|
"writable": false,
|
||||||
"signer": false,
|
"signer": false,
|
||||||
"init": false
|
"init": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "authority_account",
|
||||||
|
"writable": false,
|
||||||
|
"signer": false,
|
||||||
|
"init": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"args": [
|
"args": [
|
||||||
|
|||||||
@ -28,6 +28,10 @@ impl Keys {
|
|||||||
fn recipient_key() -> PrivateKey {
|
fn recipient_key() -> PrivateKey {
|
||||||
PrivateKey::try_new([12; 32]).expect("valid private key")
|
PrivateKey::try_new([12; 32]).expect("valid private key")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn authority_key() -> PrivateKey {
|
||||||
|
PrivateKey::try_new([13; 32]).expect("valid private key")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ids {
|
impl Ids {
|
||||||
@ -50,6 +54,10 @@ impl Ids {
|
|||||||
fn recipient() -> AccountId {
|
fn recipient() -> AccountId {
|
||||||
AccountId::from(&PublicKey::new_from_private_key(&Keys::recipient_key()))
|
AccountId::from(&PublicKey::new_from_private_key(&Keys::recipient_key()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn authority() -> AccountId {
|
||||||
|
AccountId::from(&PublicKey::new_from_private_key(&Keys::authority_key()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Accounts {
|
impl Accounts {
|
||||||
@ -62,7 +70,7 @@ impl Accounts {
|
|||||||
total_supply: 1_000_000_u128,
|
total_supply: 1_000_000_u128,
|
||||||
metadata_id: None,
|
metadata_id: None,
|
||||||
mint_authority: Some(
|
mint_authority: Some(
|
||||||
Ids::token_definition()
|
Ids::authority()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("AccountId is always 32 bytes"),
|
.expect("AccountId is always 32 bytes"),
|
||||||
@ -81,7 +89,7 @@ impl Accounts {
|
|||||||
total_supply: 1_000_000_u128,
|
total_supply: 1_000_000_u128,
|
||||||
metadata_id: None,
|
metadata_id: None,
|
||||||
mint_authority: Some(
|
mint_authority: Some(
|
||||||
Ids::token_definition()
|
Ids::authority()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("AccountId is always 32 bytes"),
|
.expect("AccountId is always 32 bytes"),
|
||||||
@ -114,6 +122,15 @@ impl Accounts {
|
|||||||
nonce: Nonce(0),
|
nonce: Nonce(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn authority_init() -> Account {
|
||||||
|
Account {
|
||||||
|
program_owner: Ids::token_program(),
|
||||||
|
balance: 0_u128,
|
||||||
|
data: Data::default(),
|
||||||
|
nonce: Nonce(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deploy_token(state: &mut V03State) {
|
fn deploy_token(state: &mut V03State) {
|
||||||
@ -130,6 +147,7 @@ fn state_for_token_tests() -> V03State {
|
|||||||
state.force_insert_account(Ids::token_definition(), Accounts::token_definition_init());
|
state.force_insert_account(Ids::token_definition(), Accounts::token_definition_init());
|
||||||
state.force_insert_account(Ids::holder(), Accounts::holder_init());
|
state.force_insert_account(Ids::holder(), Accounts::holder_init());
|
||||||
state.force_insert_account(Ids::recipient(), Accounts::recipient_init());
|
state.force_insert_account(Ids::recipient(), Accounts::recipient_init());
|
||||||
|
state.force_insert_account(Ids::authority(), Accounts::authority_init());
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,6 +156,7 @@ fn state_for_token_tests_without_recipient() -> V03State {
|
|||||||
deploy_token(&mut state);
|
deploy_token(&mut state);
|
||||||
state.force_insert_account(Ids::token_definition(), Accounts::token_definition_init());
|
state.force_insert_account(Ids::token_definition(), Accounts::token_definition_init());
|
||||||
state.force_insert_account(Ids::holder(), Accounts::holder_init());
|
state.force_insert_account(Ids::holder(), Accounts::holder_init());
|
||||||
|
state.force_insert_account(Ids::authority(), Accounts::authority_init());
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -429,7 +448,7 @@ fn token_burn() {
|
|||||||
total_supply: 800_000_u128,
|
total_supply: 800_000_u128,
|
||||||
metadata_id: None,
|
metadata_id: None,
|
||||||
mint_authority: Some(
|
mint_authority: Some(
|
||||||
Ids::token_definition()
|
Ids::authority()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("AccountId is always 32 bytes")
|
.expect("AccountId is always 32 bytes")
|
||||||
@ -463,13 +482,14 @@ fn token_mint() {
|
|||||||
|
|
||||||
let message = public_transaction::Message::try_new(
|
let message = public_transaction::Message::try_new(
|
||||||
Ids::token_program(),
|
Ids::token_program(),
|
||||||
vec![Ids::token_definition(), Ids::holder()],
|
vec![Ids::token_definition(), Ids::authority(), Ids::holder()],
|
||||||
vec![Nonce(0)],
|
vec![Nonce(0)],
|
||||||
instruction,
|
instruction,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&Keys::def_key()]);
|
let witness_set =
|
||||||
|
public_transaction::WitnessSet::for_message(&message, &[&Keys::authority_key()]);
|
||||||
|
|
||||||
let tx = PublicTransaction::new(message, witness_set);
|
let tx = PublicTransaction::new(message, witness_set);
|
||||||
state.transition_from_public_transaction(&tx, 0, 0).unwrap();
|
state.transition_from_public_transaction(&tx, 0, 0).unwrap();
|
||||||
@ -484,13 +504,13 @@ fn token_mint() {
|
|||||||
total_supply: 1_500_000_u128,
|
total_supply: 1_500_000_u128,
|
||||||
metadata_id: None,
|
metadata_id: None,
|
||||||
mint_authority: Some(
|
mint_authority: Some(
|
||||||
Ids::token_definition()
|
Ids::authority()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("AccountId is always 32 bytes")
|
.expect("AccountId is always 32 bytes")
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
nonce: Nonce(1),
|
nonce: Nonce(0),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -522,7 +542,7 @@ fn token_mint_rejects_foreign_owned_definition() {
|
|||||||
|
|
||||||
let message = public_transaction::Message::try_new(
|
let message = public_transaction::Message::try_new(
|
||||||
Ids::token_program(),
|
Ids::token_program(),
|
||||||
vec![Ids::token_definition(), Ids::recipient()],
|
vec![Ids::token_definition(), Ids::authority(), Ids::recipient()],
|
||||||
vec![Nonce(0), Nonce(0)],
|
vec![Nonce(0), Nonce(0)],
|
||||||
instruction,
|
instruction,
|
||||||
)
|
)
|
||||||
@ -530,7 +550,7 @@ fn token_mint_rejects_foreign_owned_definition() {
|
|||||||
|
|
||||||
let witness_set = public_transaction::WitnessSet::for_message(
|
let witness_set = public_transaction::WitnessSet::for_message(
|
||||||
&message,
|
&message,
|
||||||
&[&Keys::def_key(), &Keys::recipient_key()],
|
&[&Keys::authority_key(), &Keys::recipient_key()],
|
||||||
);
|
);
|
||||||
|
|
||||||
let tx = PublicTransaction::new(message, witness_set);
|
let tx = PublicTransaction::new(message, witness_set);
|
||||||
@ -556,13 +576,14 @@ fn token_mint_fresh_public_recipient_requires_authorization() {
|
|||||||
|
|
||||||
let message = public_transaction::Message::try_new(
|
let message = public_transaction::Message::try_new(
|
||||||
Ids::token_program(),
|
Ids::token_program(),
|
||||||
vec![Ids::token_definition(), Ids::recipient()],
|
vec![Ids::token_definition(), Ids::authority(), Ids::recipient()],
|
||||||
vec![Nonce(0)],
|
vec![Nonce(0)],
|
||||||
instruction,
|
instruction,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&Keys::def_key()]);
|
let witness_set =
|
||||||
|
public_transaction::WitnessSet::for_message(&message, &[&Keys::authority_key()]);
|
||||||
|
|
||||||
let tx = PublicTransaction::new(message, witness_set);
|
let tx = PublicTransaction::new(message, witness_set);
|
||||||
assert!(state.transition_from_public_transaction(&tx, 0, 0).is_err());
|
assert!(state.transition_from_public_transaction(&tx, 0, 0).is_err());
|
||||||
@ -587,7 +608,7 @@ fn token_mint_fresh_authorized_public_recipient() {
|
|||||||
|
|
||||||
let message = public_transaction::Message::try_new(
|
let message = public_transaction::Message::try_new(
|
||||||
Ids::token_program(),
|
Ids::token_program(),
|
||||||
vec![Ids::token_definition(), Ids::recipient()],
|
vec![Ids::token_definition(), Ids::authority(), Ids::recipient()],
|
||||||
vec![Nonce(0), Nonce(0)],
|
vec![Nonce(0), Nonce(0)],
|
||||||
instruction,
|
instruction,
|
||||||
)
|
)
|
||||||
@ -595,7 +616,7 @@ fn token_mint_fresh_authorized_public_recipient() {
|
|||||||
|
|
||||||
let witness_set = public_transaction::WitnessSet::for_message(
|
let witness_set = public_transaction::WitnessSet::for_message(
|
||||||
&message,
|
&message,
|
||||||
&[&Keys::def_key(), &Keys::recipient_key()],
|
&[&Keys::authority_key(), &Keys::recipient_key()],
|
||||||
);
|
);
|
||||||
|
|
||||||
let tx = PublicTransaction::new(message, witness_set);
|
let tx = PublicTransaction::new(message, witness_set);
|
||||||
@ -611,13 +632,13 @@ fn token_mint_fresh_authorized_public_recipient() {
|
|||||||
total_supply: 1_500_000_u128,
|
total_supply: 1_500_000_u128,
|
||||||
metadata_id: None,
|
metadata_id: None,
|
||||||
mint_authority: Some(
|
mint_authority: Some(
|
||||||
Ids::token_definition()
|
Ids::authority()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("AccountId is always 32 bytes")
|
.expect("AccountId is always 32 bytes")
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
nonce: Nonce(1),
|
nonce: Nonce(0),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -993,11 +1014,10 @@ fn token_new_fungible_definition_with_authority() {
|
|||||||
fn token_set_authority_revoke() {
|
fn token_set_authority_revoke() {
|
||||||
let mut state = V03State::new_with_genesis_accounts(&[], vec![], 0);
|
let mut state = V03State::new_with_genesis_accounts(&[], vec![], 0);
|
||||||
deploy_token(&mut state);
|
deploy_token(&mut state);
|
||||||
let authority_key: [u8; 32] = Ids::token_definition()
|
let authority_key: [u8; 32] = Ids::authority()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("AccountId is always 32 bytes");
|
.expect("AccountId is always 32 bytes");
|
||||||
|
|
||||||
// Create token with authority
|
// Create token with authority
|
||||||
let instruction = token_core::Instruction::NewFungibleDefinitionWithAuthority {
|
let instruction = token_core::Instruction::NewFungibleDefinitionWithAuthority {
|
||||||
name: String::from("AuthCoin"),
|
name: String::from("AuthCoin"),
|
||||||
@ -1017,18 +1037,23 @@ fn token_set_authority_revoke() {
|
|||||||
);
|
);
|
||||||
let tx = PublicTransaction::new(message, witness_set);
|
let tx = PublicTransaction::new(message, witness_set);
|
||||||
state.transition_from_public_transaction(&tx, 0, 0).unwrap();
|
state.transition_from_public_transaction(&tx, 0, 0).unwrap();
|
||||||
|
|
||||||
|
// Seed the authority account so it can sign the revoke
|
||||||
|
state.force_insert_account(Ids::authority(), Accounts::authority_init());
|
||||||
|
|
||||||
// Revoke authority
|
// Revoke authority
|
||||||
let instruction = token_core::Instruction::SetAuthority {
|
let instruction = token_core::Instruction::SetAuthority {
|
||||||
new_authority: None,
|
new_authority: None,
|
||||||
};
|
};
|
||||||
let message = public_transaction::Message::try_new(
|
let message = public_transaction::Message::try_new(
|
||||||
Ids::token_program(),
|
Ids::token_program(),
|
||||||
vec![Ids::token_definition()],
|
vec![Ids::token_definition(), Ids::authority()],
|
||||||
vec![Nonce(1)],
|
vec![Nonce(0)],
|
||||||
instruction,
|
instruction,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&Keys::def_key()]);
|
let witness_set =
|
||||||
|
public_transaction::WitnessSet::for_message(&message, &[&Keys::authority_key()]);
|
||||||
let tx = PublicTransaction::new(message, witness_set);
|
let tx = PublicTransaction::new(message, witness_set);
|
||||||
state.transition_from_public_transaction(&tx, 0, 0).unwrap();
|
state.transition_from_public_transaction(&tx, 0, 0).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -1042,7 +1067,7 @@ fn token_set_authority_revoke() {
|
|||||||
metadata_id: None,
|
metadata_id: None,
|
||||||
mint_authority: None,
|
mint_authority: None,
|
||||||
}),
|
}),
|
||||||
nonce: Nonce(2),
|
nonce: Nonce(1),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,3 +9,4 @@ workspace = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0-rc6", features = ["host"], package = "lee_core" }
|
nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0-rc6", features = ["host"], package = "lee_core" }
|
||||||
token_core = { path = "core" }
|
token_core = { path = "core" }
|
||||||
|
lez-authority = { path = "../../lez-authority" }
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
#![cfg_attr(not(test), no_main)]
|
#![cfg_attr(not(test), no_main)]
|
||||||
|
|
||||||
use spel_framework::prelude::*;
|
|
||||||
use spel_framework::context::ProgramContext;
|
|
||||||
use nssa_core::account::AccountWithMetadata;
|
use nssa_core::account::AccountWithMetadata;
|
||||||
|
use spel_framework::context::ProgramContext;
|
||||||
|
use spel_framework::prelude::*;
|
||||||
|
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
risc0_zkvm::guest::entry!(main);
|
risc0_zkvm::guest::entry!(main);
|
||||||
@ -25,11 +25,10 @@ mod token {
|
|||||||
recipient: AccountWithMetadata,
|
recipient: AccountWithMetadata,
|
||||||
amount_to_transfer: u128,
|
amount_to_transfer: u128,
|
||||||
) -> SpelResult {
|
) -> SpelResult {
|
||||||
Ok(spel_framework::SpelOutput::execute(token_program::transfer::transfer(
|
Ok(spel_framework::SpelOutput::execute(
|
||||||
sender,
|
token_program::transfer::transfer(sender, recipient, amount_to_transfer),
|
||||||
recipient,
|
vec![],
|
||||||
amount_to_transfer,
|
))
|
||||||
), vec![]))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new fungible token definition without metadata.
|
/// Create a new fungible token definition without metadata.
|
||||||
@ -111,11 +110,10 @@ mod token {
|
|||||||
user_holding_account: AccountWithMetadata,
|
user_holding_account: AccountWithMetadata,
|
||||||
amount_to_burn: u128,
|
amount_to_burn: u128,
|
||||||
) -> SpelResult {
|
) -> SpelResult {
|
||||||
Ok(spel_framework::SpelOutput::execute(token_program::burn::burn(
|
Ok(spel_framework::SpelOutput::execute(
|
||||||
definition_account,
|
token_program::burn::burn(definition_account, user_holding_account, amount_to_burn),
|
||||||
user_holding_account,
|
vec![],
|
||||||
amount_to_burn,
|
))
|
||||||
), vec![]))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mint new tokens to the holder's account.
|
/// Mint new tokens to the holder's account.
|
||||||
@ -125,19 +123,22 @@ mod token {
|
|||||||
ctx: ProgramContext,
|
ctx: ProgramContext,
|
||||||
#[account(mut, signer)]
|
#[account(mut, signer)]
|
||||||
definition_account: AccountWithMetadata,
|
definition_account: AccountWithMetadata,
|
||||||
#[account(mut)]
|
authority_account: AccountWithMetadata,
|
||||||
user_holding_account: AccountWithMetadata,
|
user_holding_account: AccountWithMetadata,
|
||||||
amount_to_mint: u128,
|
amount_to_mint: u128,
|
||||||
) -> SpelResult {
|
) -> SpelResult {
|
||||||
Ok(spel_framework::SpelOutput::execute(token_program::mint::mint(
|
Ok(spel_framework::SpelOutput::execute(
|
||||||
definition_account,
|
token_program::mint::mint(
|
||||||
user_holding_account,
|
definition_account,
|
||||||
amount_to_mint,
|
authority_account,
|
||||||
ctx.self_program_id,
|
user_holding_account,
|
||||||
), vec![]))
|
amount_to_mint,
|
||||||
|
ctx.self_program_id,
|
||||||
|
),
|
||||||
|
vec![],
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Create a new fungible token definition with a mint authority.
|
/// Create a new fungible token definition with a mint authority.
|
||||||
/// Unlike NewFungibleDefinition, this allows minting additional tokens later.
|
/// Unlike NewFungibleDefinition, this allows minting additional tokens later.
|
||||||
#[instruction]
|
#[instruction]
|
||||||
@ -165,11 +166,13 @@ mod token {
|
|||||||
#[instruction]
|
#[instruction]
|
||||||
pub fn set_authority(
|
pub fn set_authority(
|
||||||
definition_account: AccountWithMetadata,
|
definition_account: AccountWithMetadata,
|
||||||
|
authority_account: AccountWithMetadata,
|
||||||
new_authority: Option<[u8; 32]>,
|
new_authority: Option<[u8; 32]>,
|
||||||
) -> SpelResult {
|
) -> SpelResult {
|
||||||
Ok(spel_framework::SpelOutput::execute(
|
Ok(spel_framework::SpelOutput::execute(
|
||||||
token_program::set_authority::set_authority(
|
token_program::set_authority::set_authority(
|
||||||
definition_account,
|
definition_account,
|
||||||
|
authority_account,
|
||||||
new_authority,
|
new_authority,
|
||||||
),
|
),
|
||||||
vec![],
|
vec![],
|
||||||
@ -185,9 +188,9 @@ mod token {
|
|||||||
#[account(init, signer)]
|
#[account(init, signer)]
|
||||||
printed_account: AccountWithMetadata,
|
printed_account: AccountWithMetadata,
|
||||||
) -> SpelResult {
|
) -> SpelResult {
|
||||||
Ok(spel_framework::SpelOutput::execute(token_program::print_nft::print_nft(
|
Ok(spel_framework::SpelOutput::execute(
|
||||||
master_account,
|
token_program::print_nft::print_nft(master_account, printed_account),
|
||||||
printed_account,
|
vec![],
|
||||||
), vec![]))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
use lez_authority::AuthoritySlot;
|
||||||
use nssa_core::{
|
use nssa_core::{
|
||||||
account::{Account, AccountWithMetadata, Data},
|
account::{Account, AccountWithMetadata, Data},
|
||||||
program::{AccountPostState, Claim, ProgramId},
|
program::{AccountPostState, Claim, ProgramId},
|
||||||
@ -6,14 +7,11 @@ use token_core::{TokenDefinition, TokenHolding};
|
|||||||
|
|
||||||
pub fn mint(
|
pub fn mint(
|
||||||
definition_account: AccountWithMetadata,
|
definition_account: AccountWithMetadata,
|
||||||
|
authority_account: AccountWithMetadata,
|
||||||
user_holding_account: AccountWithMetadata,
|
user_holding_account: AccountWithMetadata,
|
||||||
amount_to_mint: u128,
|
amount_to_mint: u128,
|
||||||
token_program_id: ProgramId,
|
token_program_id: ProgramId,
|
||||||
) -> Vec<AccountPostState> {
|
) -> Vec<AccountPostState> {
|
||||||
assert!(
|
|
||||||
definition_account.is_authorized,
|
|
||||||
"Definition authorization is missing"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
definition_account.account.program_owner, token_program_id,
|
definition_account.account.program_owner, token_program_id,
|
||||||
"Token definition must be owned by token program"
|
"Token definition must be owned by token program"
|
||||||
@ -22,19 +20,22 @@ pub fn mint(
|
|||||||
let mut definition = TokenDefinition::try_from(&definition_account.account.data)
|
let mut definition = TokenDefinition::try_from(&definition_account.account.data)
|
||||||
.expect("Token Definition account must be valid");
|
.expect("Token Definition account must be valid");
|
||||||
|
|
||||||
// LP-0013: enforce mint authority — minting is only allowed if mint_authority is Some.
|
// LP-0013 / RFP-001: gate minting through lez-authority. The authority_account
|
||||||
|
// is the signer and must match the stored mint authority.
|
||||||
if let TokenDefinition::Fungible { mint_authority, .. } = &definition {
|
if let TokenDefinition::Fungible { mint_authority, .. } = &definition {
|
||||||
match mint_authority {
|
assert!(
|
||||||
None => panic!("Mint authority has been revoked; this token has a fixed supply"),
|
authority_account.is_authorized,
|
||||||
Some(authority_key) => {
|
"Mint authority must sign the transaction"
|
||||||
assert_eq!(
|
);
|
||||||
definition_account.account_id.as_ref(),
|
let signer: [u8; 32] = authority_account
|
||||||
authority_key,
|
.account_id
|
||||||
"Signer is not the mint authority"
|
.as_ref()
|
||||||
);
|
.try_into()
|
||||||
}
|
.expect("AccountId is always 32 bytes");
|
||||||
}
|
let slot = AuthoritySlot(*mint_authority);
|
||||||
|
slot.check(signer).expect("Mint authority check failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut holding = if user_holding_account.account == Account::default() {
|
let mut holding = if user_holding_account.account == Account::default() {
|
||||||
TokenHolding::zeroized_from_definition(definition_account.account_id, &definition)
|
TokenHolding::zeroized_from_definition(definition_account.account_id, &definition)
|
||||||
} else {
|
} else {
|
||||||
@ -86,6 +87,7 @@ pub fn mint(
|
|||||||
|
|
||||||
vec![
|
vec![
|
||||||
AccountPostState::new(definition_post),
|
AccountPostState::new(definition_post),
|
||||||
|
AccountPostState::new(authority_account.account),
|
||||||
AccountPostState::new_claimed_if_default(holding_post, Claim::Authorized),
|
AccountPostState::new_claimed_if_default(holding_post, Claim::Authorized),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,38 +1,33 @@
|
|||||||
|
use lez_authority::AuthoritySlot;
|
||||||
use nssa_core::{
|
use nssa_core::{
|
||||||
account::{AccountWithMetadata, Data},
|
account::{AccountWithMetadata, Data},
|
||||||
program::AccountPostState,
|
program::AccountPostState,
|
||||||
};
|
};
|
||||||
use token_core::TokenDefinition;
|
use token_core::TokenDefinition;
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn set_authority(
|
pub fn set_authority(
|
||||||
definition_account: AccountWithMetadata,
|
definition_account: AccountWithMetadata,
|
||||||
|
authority_account: AccountWithMetadata,
|
||||||
new_authority: Option<[u8; 32]>,
|
new_authority: Option<[u8; 32]>,
|
||||||
) -> Vec<AccountPostState> {
|
) -> Vec<AccountPostState> {
|
||||||
assert!(
|
|
||||||
definition_account.is_authorized,
|
|
||||||
"Definition account authorization is missing; only the mint authority can call SetAuthority"
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut definition = TokenDefinition::try_from(&definition_account.account.data)
|
let mut definition = TokenDefinition::try_from(&definition_account.account.data)
|
||||||
.expect("Token Definition account must be valid");
|
.expect("Token Definition account must be valid");
|
||||||
|
|
||||||
match &mut definition {
|
match &mut definition {
|
||||||
TokenDefinition::Fungible { mint_authority, .. } => {
|
TokenDefinition::Fungible { mint_authority, .. } => {
|
||||||
match mint_authority {
|
assert!(
|
||||||
None => {
|
authority_account.is_authorized,
|
||||||
panic!("Mint authority already revoked; supply is permanently fixed");
|
"Mint authority must sign the transaction"
|
||||||
}
|
);
|
||||||
Some(authority_key) => {
|
let signer: [u8; 32] = authority_account
|
||||||
// Validate caller matches the stored mint authority key
|
.account_id
|
||||||
assert_eq!(
|
.as_ref()
|
||||||
definition_account.account_id.as_ref(),
|
.try_into()
|
||||||
authority_key.as_ref(),
|
.expect("AccountId is always 32 bytes");
|
||||||
"Signer does not match the stored mint authority"
|
let mut slot = AuthoritySlot(*mint_authority);
|
||||||
);
|
slot.set(signer, new_authority)
|
||||||
*mint_authority = new_authority;
|
.expect("SetAuthority failed");
|
||||||
}
|
*mint_authority = slot.0;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
TokenDefinition::NonFungible { .. } => {
|
TokenDefinition::NonFungible { .. } => {
|
||||||
panic!("SetAuthority is not supported for Non-Fungible Tokens");
|
panic!("SetAuthority is not supported for Non-Fungible Tokens");
|
||||||
@ -42,5 +37,8 @@ pub fn set_authority(
|
|||||||
let mut definition_post = definition_account.account;
|
let mut definition_post = definition_account.account;
|
||||||
definition_post.data = Data::from(&definition);
|
definition_post.data = Data::from(&definition);
|
||||||
|
|
||||||
vec![AccountPostState::new(definition_post)]
|
vec![
|
||||||
|
AccountPostState::new(definition_post),
|
||||||
|
AccountPostState::new(authority_account.account),
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,6 +51,16 @@ impl AccountForTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A signed authority account whose ID matches the [15; 32] mint authority
|
||||||
|
/// used by definition_account_auth() / definition_account_mint().
|
||||||
|
fn authority_account_auth() -> AccountWithMetadata {
|
||||||
|
AccountWithMetadata {
|
||||||
|
account: Account::default(),
|
||||||
|
is_authorized: true,
|
||||||
|
account_id: IdForTests::pool_definition_id(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn definition_account_foreign_owner() -> AccountWithMetadata {
|
fn definition_account_foreign_owner() -> AccountWithMetadata {
|
||||||
AccountWithMetadata {
|
AccountWithMetadata {
|
||||||
account: Account {
|
account: Account {
|
||||||
@ -904,6 +914,7 @@ fn test_mint_not_valid_holding_account() {
|
|||||||
let holding_account = AccountForTests::definition_account_without_auth();
|
let holding_account = AccountForTests::definition_account_without_auth();
|
||||||
let _post_states = mint(
|
let _post_states = mint(
|
||||||
definition_account,
|
definition_account,
|
||||||
|
AccountForTests::authority_account_auth(),
|
||||||
holding_account,
|
holding_account,
|
||||||
BalanceForTests::mint_success(),
|
BalanceForTests::mint_success(),
|
||||||
TOKEN_PROGRAM_ID,
|
TOKEN_PROGRAM_ID,
|
||||||
@ -917,6 +928,7 @@ fn test_mint_not_valid_definition_account() {
|
|||||||
let holding_account = AccountForTests::holding_same_definition_without_authorization();
|
let holding_account = AccountForTests::holding_same_definition_without_authorization();
|
||||||
let _post_states = mint(
|
let _post_states = mint(
|
||||||
definition_account,
|
definition_account,
|
||||||
|
AccountForTests::authority_account_auth(),
|
||||||
holding_account,
|
holding_account,
|
||||||
BalanceForTests::mint_success(),
|
BalanceForTests::mint_success(),
|
||||||
TOKEN_PROGRAM_ID,
|
TOKEN_PROGRAM_ID,
|
||||||
@ -924,12 +936,19 @@ fn test_mint_not_valid_definition_account() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "Definition authorization is missing")]
|
#[should_panic(expected = "Mint authority must sign the transaction")]
|
||||||
fn test_mint_missing_authorization() {
|
fn test_mint_missing_authorization() {
|
||||||
let definition_account = AccountForTests::definition_account_without_auth();
|
let definition_account = AccountForTests::definition_account_auth();
|
||||||
let holding_account = AccountForTests::holding_same_definition_without_authorization();
|
let holding_account = AccountForTests::holding_same_definition_without_authorization();
|
||||||
|
// authority account that is NOT signed
|
||||||
|
let unsigned_authority = AccountWithMetadata {
|
||||||
|
account: Account::default(),
|
||||||
|
is_authorized: false,
|
||||||
|
account_id: IdForTests::pool_definition_id(),
|
||||||
|
};
|
||||||
let _post_states = mint(
|
let _post_states = mint(
|
||||||
definition_account,
|
definition_account,
|
||||||
|
unsigned_authority,
|
||||||
holding_account,
|
holding_account,
|
||||||
BalanceForTests::mint_success(),
|
BalanceForTests::mint_success(),
|
||||||
TOKEN_PROGRAM_ID,
|
TOKEN_PROGRAM_ID,
|
||||||
@ -943,6 +962,7 @@ fn test_mint_rejects_foreign_owned_definition() {
|
|||||||
let holding_account = AccountForTests::holding_account_uninit();
|
let holding_account = AccountForTests::holding_account_uninit();
|
||||||
let _post_states = mint(
|
let _post_states = mint(
|
||||||
definition_account,
|
definition_account,
|
||||||
|
AccountForTests::authority_account_auth(),
|
||||||
holding_account,
|
holding_account,
|
||||||
BalanceForTests::mint_success(),
|
BalanceForTests::mint_success(),
|
||||||
TOKEN_PROGRAM_ID,
|
TOKEN_PROGRAM_ID,
|
||||||
@ -952,10 +972,12 @@ fn test_mint_rejects_foreign_owned_definition() {
|
|||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "Mismatch Token Definition and Token Holding")]
|
#[should_panic(expected = "Mismatch Token Definition and Token Holding")]
|
||||||
fn test_mint_mismatched_token_definition() {
|
fn test_mint_mismatched_token_definition() {
|
||||||
|
//
|
||||||
let definition_account = AccountForTests::definition_account_auth();
|
let definition_account = AccountForTests::definition_account_auth();
|
||||||
let holding_account = AccountForTests::holding_different_definition();
|
let holding_account = AccountForTests::holding_different_definition();
|
||||||
let _post_states = mint(
|
let _post_states = mint(
|
||||||
definition_account,
|
definition_account,
|
||||||
|
AccountForTests::authority_account_auth(),
|
||||||
holding_account,
|
holding_account,
|
||||||
BalanceForTests::mint_success(),
|
BalanceForTests::mint_success(),
|
||||||
TOKEN_PROGRAM_ID,
|
TOKEN_PROGRAM_ID,
|
||||||
@ -968,12 +990,13 @@ fn test_mint_success() {
|
|||||||
let holding_account = AccountForTests::holding_same_definition_without_authorization();
|
let holding_account = AccountForTests::holding_same_definition_without_authorization();
|
||||||
let post_states = mint(
|
let post_states = mint(
|
||||||
definition_account,
|
definition_account,
|
||||||
|
AccountForTests::authority_account_auth(),
|
||||||
holding_account,
|
holding_account,
|
||||||
BalanceForTests::mint_success(),
|
BalanceForTests::mint_success(),
|
||||||
TOKEN_PROGRAM_ID,
|
TOKEN_PROGRAM_ID,
|
||||||
);
|
);
|
||||||
|
|
||||||
let [def_post, holding_post] = post_states.try_into().unwrap();
|
let [def_post, _authority_post, holding_post] = post_states.try_into().unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*def_post.account(),
|
*def_post.account(),
|
||||||
@ -993,12 +1016,13 @@ fn test_mint_uninit_holding_success() {
|
|||||||
let holding_account = AccountForTests::holding_account_uninit();
|
let holding_account = AccountForTests::holding_account_uninit();
|
||||||
let post_states = mint(
|
let post_states = mint(
|
||||||
definition_account,
|
definition_account,
|
||||||
|
AccountForTests::authority_account_auth(),
|
||||||
holding_account,
|
holding_account,
|
||||||
BalanceForTests::mint_success(),
|
BalanceForTests::mint_success(),
|
||||||
TOKEN_PROGRAM_ID,
|
TOKEN_PROGRAM_ID,
|
||||||
);
|
);
|
||||||
|
|
||||||
let [def_post, holding_post] = post_states.try_into().unwrap();
|
let [def_post, _authority_post, holding_post] = post_states.try_into().unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*def_post.account(),
|
*def_post.account(),
|
||||||
@ -1019,6 +1043,7 @@ fn test_mint_total_supply_overflow() {
|
|||||||
let holding_account = AccountForTests::holding_same_definition_without_authorization();
|
let holding_account = AccountForTests::holding_same_definition_without_authorization();
|
||||||
let _post_states = mint(
|
let _post_states = mint(
|
||||||
definition_account,
|
definition_account,
|
||||||
|
AccountForTests::authority_account_auth(),
|
||||||
holding_account,
|
holding_account,
|
||||||
BalanceForTests::mint_overflow(),
|
BalanceForTests::mint_overflow(),
|
||||||
TOKEN_PROGRAM_ID,
|
TOKEN_PROGRAM_ID,
|
||||||
@ -1032,6 +1057,7 @@ fn test_mint_holding_account_overflow() {
|
|||||||
let holding_account = AccountForTests::holding_same_definition_without_authorization_overflow();
|
let holding_account = AccountForTests::holding_same_definition_without_authorization_overflow();
|
||||||
let _post_states = mint(
|
let _post_states = mint(
|
||||||
definition_account,
|
definition_account,
|
||||||
|
AccountForTests::authority_account_auth(),
|
||||||
holding_account,
|
holding_account,
|
||||||
BalanceForTests::mint_overflow(),
|
BalanceForTests::mint_overflow(),
|
||||||
TOKEN_PROGRAM_ID,
|
TOKEN_PROGRAM_ID,
|
||||||
@ -1045,6 +1071,7 @@ fn test_mint_cannot_mint_unmintable_tokens() {
|
|||||||
let holding_account = AccountForTests::holding_account_master_nft();
|
let holding_account = AccountForTests::holding_account_master_nft();
|
||||||
let _post_states = mint(
|
let _post_states = mint(
|
||||||
definition_account,
|
definition_account,
|
||||||
|
AccountForTests::authority_account_auth(),
|
||||||
holding_account,
|
holding_account,
|
||||||
BalanceForTests::mint_success(),
|
BalanceForTests::mint_success(),
|
||||||
TOKEN_PROGRAM_ID,
|
TOKEN_PROGRAM_ID,
|
||||||
@ -1364,24 +1391,6 @@ mod authority_tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn def_without_auth_flag() -> AccountWithMetadata {
|
|
||||||
AccountWithMetadata {
|
|
||||||
account: Account {
|
|
||||||
program_owner: [5_u32; 8],
|
|
||||||
balance: 0_u128,
|
|
||||||
data: Data::from(&TokenDefinition::Fungible {
|
|
||||||
name: String::from("test"),
|
|
||||||
total_supply: 100_000_u128,
|
|
||||||
metadata_id: None,
|
|
||||||
mint_authority: Some(AUTHORITY),
|
|
||||||
}),
|
|
||||||
nonce: 0_u128.into(),
|
|
||||||
},
|
|
||||||
is_authorized: false,
|
|
||||||
account_id: AccountId::new([15; 32]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn holding_account() -> AccountWithMetadata {
|
fn holding_account() -> AccountWithMetadata {
|
||||||
AccountWithMetadata {
|
AccountWithMetadata {
|
||||||
account: Account {
|
account: Account {
|
||||||
@ -1398,15 +1407,34 @@ mod authority_tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Signed authority matching the [15; 32] stored mint authority.
|
||||||
|
fn authority_signer() -> AccountWithMetadata {
|
||||||
|
AccountWithMetadata {
|
||||||
|
account: Account::default(),
|
||||||
|
is_authorized: true,
|
||||||
|
account_id: AccountId::new([15; 32]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A different signer (Bob) — NOT the current authority.
|
||||||
|
fn wrong_authority_signer() -> AccountWithMetadata {
|
||||||
|
AccountWithMetadata {
|
||||||
|
account: Account::default(),
|
||||||
|
is_authorized: true,
|
||||||
|
account_id: AccountId::new([99; 32]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn mint_with_authority_succeeds() {
|
fn mint_with_authority_succeeds() {
|
||||||
let post_states = mint(
|
let post_states = mint(
|
||||||
def_with_authority(),
|
def_with_authority(),
|
||||||
|
authority_signer(),
|
||||||
holding_account(),
|
holding_account(),
|
||||||
50_000,
|
50_000,
|
||||||
TOKEN_PROGRAM_ID,
|
TOKEN_PROGRAM_ID,
|
||||||
);
|
);
|
||||||
let [def_post, holding_post] = post_states.try_into().unwrap();
|
let [def_post, _authority_post, holding_post] = post_states.try_into().unwrap();
|
||||||
|
|
||||||
let def = TokenDefinition::try_from(&def_post.account().data).unwrap();
|
let def = TokenDefinition::try_from(&def_post.account().data).unwrap();
|
||||||
let holding = TokenHolding::try_from(&holding_post.account().data).unwrap();
|
let holding = TokenHolding::try_from(&holding_post.account().data).unwrap();
|
||||||
@ -1429,10 +1457,11 @@ mod authority_tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "Mint authority has been revoked; this token has a fixed supply")]
|
#[should_panic(expected = "Mint authority check failed")]
|
||||||
fn mint_with_revoked_authority_fails() {
|
fn mint_with_revoked_authority_fails() {
|
||||||
let _ = mint(
|
let _ = mint(
|
||||||
def_with_authority_revoked(),
|
def_with_authority_revoked(),
|
||||||
|
authority_signer(),
|
||||||
holding_account(),
|
holding_account(),
|
||||||
50_000,
|
50_000,
|
||||||
TOKEN_PROGRAM_ID,
|
TOKEN_PROGRAM_ID,
|
||||||
@ -1440,10 +1469,16 @@ mod authority_tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "Definition authorization is missing")]
|
#[should_panic(expected = "Mint authority must sign the transaction")]
|
||||||
fn mint_without_is_authorized_fails() {
|
fn mint_without_is_authorized_fails() {
|
||||||
|
let unsigned_authority = AccountWithMetadata {
|
||||||
|
account: Account::default(),
|
||||||
|
is_authorized: false,
|
||||||
|
account_id: AccountId::new([15; 32]),
|
||||||
|
};
|
||||||
let _ = mint(
|
let _ = mint(
|
||||||
def_without_auth_flag(),
|
def_with_authority(),
|
||||||
|
unsigned_authority,
|
||||||
holding_account(),
|
holding_account(),
|
||||||
50_000,
|
50_000,
|
||||||
TOKEN_PROGRAM_ID,
|
TOKEN_PROGRAM_ID,
|
||||||
@ -1453,8 +1488,8 @@ mod authority_tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn set_authority_rotates_to_new_key() {
|
fn set_authority_rotates_to_new_key() {
|
||||||
let new_key = [7_u8; 32];
|
let new_key = [7_u8; 32];
|
||||||
let post_states = set_authority(def_with_authority(), Some(new_key));
|
let post_states = set_authority(def_with_authority(), authority_signer(), Some(new_key));
|
||||||
let [def_post] = post_states.try_into().unwrap();
|
let [def_post, _authority_post] = post_states.try_into().unwrap();
|
||||||
|
|
||||||
let def = TokenDefinition::try_from(&def_post.account().data).unwrap();
|
let def = TokenDefinition::try_from(&def_post.account().data).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
@ -1463,10 +1498,22 @@ mod authority_tests {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "Mint authority check failed")]
|
||||||
|
fn mint_with_wrong_signer_fails() {
|
||||||
|
let _ = mint(
|
||||||
|
def_with_authority(),
|
||||||
|
wrong_authority_signer(),
|
||||||
|
holding_account(),
|
||||||
|
50_000,
|
||||||
|
TOKEN_PROGRAM_ID,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn set_authority_revokes_permanently() {
|
fn set_authority_revokes_permanently() {
|
||||||
let post_states = set_authority(def_with_authority(), None);
|
let post_states = set_authority(def_with_authority(), authority_signer(), None);
|
||||||
let [def_post] = post_states.try_into().unwrap();
|
let [def_post, _authority_post] = post_states.try_into().unwrap();
|
||||||
|
|
||||||
let def = TokenDefinition::try_from(&def_post.account().data).unwrap();
|
let def = TokenDefinition::try_from(&def_post.account().data).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
@ -1479,22 +1526,41 @@ mod authority_tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "Mint authority already revoked; supply is permanently fixed")]
|
#[should_panic(expected = "SetAuthority failed")]
|
||||||
fn set_authority_on_revoked_fails() {
|
fn set_authority_on_revoked_fails() {
|
||||||
let _ = set_authority(def_with_authority_revoked(), Some([7_u8; 32]));
|
let _ = set_authority(
|
||||||
|
def_with_authority_revoked(),
|
||||||
|
authority_signer(),
|
||||||
|
Some([7_u8; 32]),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "Definition account authorization is missing")]
|
#[should_panic(expected = "Mint authority must sign the transaction")]
|
||||||
fn set_authority_without_is_authorized_fails() {
|
fn set_authority_without_is_authorized_fails() {
|
||||||
let _ = set_authority(def_without_auth_flag(), Some([7_u8; 32]));
|
let unsigned_authority = AccountWithMetadata {
|
||||||
|
account: Account::default(),
|
||||||
|
is_authorized: false,
|
||||||
|
account_id: AccountId::new([15; 32]),
|
||||||
|
};
|
||||||
|
let _ = set_authority(def_with_authority(), unsigned_authority, Some([7_u8; 32]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "SetAuthority failed")]
|
||||||
|
fn set_authority_wrong_signer_fails() {
|
||||||
|
let _ = set_authority(
|
||||||
|
def_with_authority(),
|
||||||
|
wrong_authority_signer(),
|
||||||
|
Some([7_u8; 32]),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn set_authority_rotate_then_old_cannot_mint() {
|
fn set_authority_rotate_then_old_cannot_mint() {
|
||||||
let new_key = [7_u8; 32];
|
let new_key = [7_u8; 32];
|
||||||
let post_states = set_authority(def_with_authority(), Some(new_key));
|
let post_states = set_authority(def_with_authority(), authority_signer(), Some(new_key));
|
||||||
let [def_post] = post_states.try_into().unwrap();
|
let [def_post, _authority_post] = post_states.try_into().unwrap();
|
||||||
|
|
||||||
let def = TokenDefinition::try_from(&def_post.account().data).unwrap();
|
let def = TokenDefinition::try_from(&def_post.account().data).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
|
|||||||
@ -46,6 +46,22 @@ TOKEN_BIN="${TOKEN_BIN:-$LEZ_PROGRAMS/target/riscv-guest/token-methods/token-gue
|
|||||||
DEMO_DIR="${DEMO_DIR:-$(pwd)}"
|
DEMO_DIR="${DEMO_DIR:-$(pwd)}"
|
||||||
WALLET_DIR="${WALLET_DIR:-$DEMO_DIR/.scaffold/wallet}"
|
WALLET_DIR="${WALLET_DIR:-$DEMO_DIR/.scaffold/wallet}"
|
||||||
|
|
||||||
|
# Convert a base58 "Public/..." account_id to the 64-char hex form
|
||||||
|
# that SPEL expects for [u8; 32] args (e.g. --mint-authority).
|
||||||
|
b58_to_hex() {
|
||||||
|
local id="${1#Public/}" # strip the Public/ prefix
|
||||||
|
id="${id#Private/}" # strip Private/ if present
|
||||||
|
python3 -c "
|
||||||
|
import sys
|
||||||
|
s = sys.argv[1]
|
||||||
|
alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
||||||
|
num = 0
|
||||||
|
for c in s:
|
||||||
|
num = num * 58 + alphabet.index(c)
|
||||||
|
print(num.to_bytes(32, 'big').hex())
|
||||||
|
" "$id"
|
||||||
|
}
|
||||||
|
|
||||||
echo "================================================================"
|
echo "================================================================"
|
||||||
echo " LP-0013: Token Program Mint Authority — End-to-End Demo"
|
echo " LP-0013: Token Program Mint Authority — End-to-End Demo"
|
||||||
echo " RISC0_DEV_MODE=${RISC0_DEV_MODE:-not set}"
|
echo " RISC0_DEV_MODE=${RISC0_DEV_MODE:-not set}"
|
||||||
@ -66,17 +82,18 @@ lgs wallet topup 2>&1 | grep -E "complete|funded|Address" || true
|
|||||||
echo " Wallet funded."
|
echo " Wallet funded."
|
||||||
|
|
||||||
echo "[3/7] Creating token accounts..."
|
echo "[3/7] Creating token accounts..."
|
||||||
DEF_RESULT=$(lgs wallet -- account new --public 2>&1)
|
DEF_RESULT=$(lgs wallet -- account new public 2>&1)
|
||||||
DEF_ID=$(echo "$DEF_RESULT" | grep -oE '[0-9a-f]{64}' | head -1)
|
DEF_ID=$(echo "$DEF_RESULT" | grep -oE 'account_id [^ ]+' | awk '{print $2}')
|
||||||
SUPPLY_RESULT=$(lgs wallet -- account new --public 2>&1)
|
SUPPLY_RESULT=$(lgs wallet -- account new public 2>&1)
|
||||||
SUPPLY_ID=$(echo "$SUPPLY_RESULT" | grep -oE '[0-9a-f]{64}' | head -1)
|
SUPPLY_ID=$(echo "$SUPPLY_RESULT" | grep -oE 'account_id [^ ]+' | awk '{print $2}')
|
||||||
RECIPIENT_RESULT=$(lgs wallet -- account new --public 2>&1)
|
RECIPIENT_RESULT=$(lgs wallet -- account new public 2>&1)
|
||||||
RECIPIENT_ID=$(echo "$RECIPIENT_RESULT" | grep -oE '[0-9a-f]{64}' | head -1)
|
RECIPIENT_ID=$(echo "$RECIPIENT_RESULT" | grep -oE 'account_id [^ ]+' | awk '{print $2}')
|
||||||
echo " Definition account: $DEF_ID"
|
echo " Definition account: $DEF_ID"
|
||||||
echo " Supply account: $SUPPLY_ID"
|
echo " Supply account: $SUPPLY_ID"
|
||||||
echo " Recipient account: $RECIPIENT_ID"
|
echo " Recipient account: $RECIPIENT_ID"
|
||||||
|
|
||||||
echo "[4/7] Creating token with mint authority..."
|
echo "[4/7] Creating token with mint authority..."
|
||||||
|
DEF_ID_HEX=$(b58_to_hex "$DEF_ID")
|
||||||
NSSA_WALLET_HOME_DIR="$WALLET_DIR" \
|
NSSA_WALLET_HOME_DIR="$WALLET_DIR" \
|
||||||
${TIMEOUT:+$TIMEOUT 30} "$SPEL" --idl "$IDL" --program "$TOKEN_BIN" \
|
${TIMEOUT:+$TIMEOUT 30} "$SPEL" --idl "$IDL" --program "$TOKEN_BIN" \
|
||||||
-- new-fungible-definition-with-authority \
|
-- new-fungible-definition-with-authority \
|
||||||
@ -84,7 +101,7 @@ ${TIMEOUT:+$TIMEOUT 30} "$SPEL" --idl "$IDL" --program "$TOKEN_BIN" \
|
|||||||
--holding-target-account "$SUPPLY_ID" \
|
--holding-target-account "$SUPPLY_ID" \
|
||||||
--name "DemoCoin" \
|
--name "DemoCoin" \
|
||||||
--initial-supply 1000000 \
|
--initial-supply 1000000 \
|
||||||
--mint-authority "$DEF_ID"
|
--mint-authority "$DEF_ID_HEX"
|
||||||
echo " Token 'DemoCoin' submitted. Initial supply: 1,000,000"
|
echo " Token 'DemoCoin' submitted. Initial supply: 1,000,000"
|
||||||
|
|
||||||
sleep 2
|
sleep 2
|
||||||
@ -94,6 +111,7 @@ NSSA_WALLET_HOME_DIR="$WALLET_DIR" \
|
|||||||
${TIMEOUT:+$TIMEOUT 30} "$SPEL" --idl "$IDL" --program "$TOKEN_BIN" \
|
${TIMEOUT:+$TIMEOUT 30} "$SPEL" --idl "$IDL" --program "$TOKEN_BIN" \
|
||||||
-- mint \
|
-- mint \
|
||||||
--definition-account "$DEF_ID" \
|
--definition-account "$DEF_ID" \
|
||||||
|
--authority-account "$DEF_ID" \
|
||||||
--user-holding-account "$RECIPIENT_ID" \
|
--user-holding-account "$RECIPIENT_ID" \
|
||||||
--amount-to-mint 500000
|
--amount-to-mint 500000
|
||||||
echo " Mint transaction submitted. New total supply: 1,500,000"
|
echo " Mint transaction submitted. New total supply: 1,500,000"
|
||||||
@ -105,6 +123,7 @@ NSSA_WALLET_HOME_DIR="$WALLET_DIR" \
|
|||||||
${TIMEOUT:+$TIMEOUT 30} "$SPEL" --idl "$IDL" --program "$TOKEN_BIN" \
|
${TIMEOUT:+$TIMEOUT 30} "$SPEL" --idl "$IDL" --program "$TOKEN_BIN" \
|
||||||
-- set-authority \
|
-- set-authority \
|
||||||
--definition-account "$DEF_ID" \
|
--definition-account "$DEF_ID" \
|
||||||
|
--authority-account "$DEF_ID" \
|
||||||
--new-authority none
|
--new-authority none
|
||||||
echo " Authority revoked. Supply permanently fixed at 1,500,000"
|
echo " Authority revoked. Supply permanently fixed at 1,500,000"
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user