mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-01-07 15:53:14 +00:00
updates
This commit is contained in:
parent
22440cf95d
commit
1b7afbecdc
@ -38,7 +38,6 @@ use nssa_core::{
|
|||||||
// * An instruction data byte string of length 23, indicating the balance to mint with the folloiwng layout
|
// * An instruction data byte string of length 23, indicating the balance to mint with the folloiwng layout
|
||||||
// [0x04 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 || 0x00 || 0x00 || 0x00].
|
// [0x04 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 || 0x00 || 0x00 || 0x00].
|
||||||
|
|
||||||
|
|
||||||
enum TokenStandard {
|
enum TokenStandard {
|
||||||
FungibleTokenHolding,
|
FungibleTokenHolding,
|
||||||
FungibleAssetHolding,
|
FungibleAssetHolding,
|
||||||
@ -55,49 +54,120 @@ fn helper_token_standard_constructor(selection: TokenStandard) -> u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const TOKEN_DEFINITION_TYPE: u8 = 0;
|
const TOKEN_DEFINITION_TYPE: u8 = 0;
|
||||||
const TOKEN_DEFINITION_DATA_SIZE: usize = 55; //23;
|
const TOKEN_DEFINITION_DATA_SIZE: usize = 55;
|
||||||
|
|
||||||
const TOKEN_HOLDING_TYPE: u8 = 1;
|
const TOKEN_HOLDING_TYPE: u8 = 1;
|
||||||
const TOKEN_HOLDING_DATA_SIZE: usize = 49;
|
const TOKEN_HOLDING_DATA_SIZE: usize = 49;
|
||||||
const CURRENT_VERSION: u8 = 1;
|
const CURRENT_VERSION: u8 = 1;
|
||||||
|
|
||||||
|
const TOKEN_METADATA_DATA_SIZE: usize = 463;
|
||||||
|
|
||||||
//TODO: pub probably not necessary
|
//TODO: pub probably not necessary
|
||||||
pub type StringU8 = Vec<u8>;
|
pub type StringU8 = Vec<u8>;
|
||||||
|
|
||||||
struct TokenDefinition {
|
struct TokenDefinition {
|
||||||
account_type: u8,
|
account_type: u8, //specifies Token Standard?
|
||||||
name: [u8; 6],
|
name: [u8; 6],
|
||||||
total_supply: u128,
|
total_supply: u128,
|
||||||
metadata_id: AccountId,
|
metadata_id: AccountId,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TokenHolding {
|
struct TokenHolding {
|
||||||
account_type: u8,
|
account_type: u8, //(potentially for edition/master edition/printable)
|
||||||
definition_id: AccountId,
|
definition_id: AccountId,
|
||||||
balance: u128,
|
balance: u128,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//BAH fixed data size is kind of needed...
|
||||||
|
//TODO need implement
|
||||||
struct TokenMetadata {
|
struct TokenMetadata {
|
||||||
account_type: u8, //Not sure if necessary
|
account_type: u8, //Not sure if necessary
|
||||||
version: u8,
|
version: u8,
|
||||||
definition_id: AccountId,
|
definition_id: AccountId,
|
||||||
uri: StringU8,
|
uri: [u8; 200], //TODO: add to specs; this is the limit Solana uses
|
||||||
creators: StringU8,
|
creators: [u8; 250], //TODO: double check this value;
|
||||||
primary_sale_date: StringU8, //Maybe do this as a block number?
|
primary_sale_date: u64, //BlockId
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO remove any unwraps
|
||||||
|
|
||||||
|
impl TokenMetadata {
|
||||||
|
fn into_data(self) -> Data {
|
||||||
|
let mut bytes = Vec::<u8>::new();
|
||||||
|
bytes.extend_from_slice(&[self.account_type]);
|
||||||
|
bytes.extend_from_slice(&[self.version]);
|
||||||
|
bytes.extend_from_slice(&self.definition_id.to_bytes());
|
||||||
|
bytes.extend_from_slice(&self.uri);
|
||||||
|
bytes.extend_from_slice(&self.creators);
|
||||||
|
bytes.extend_from_slice(&self.primary_sale_date.to_le_bytes());
|
||||||
|
|
||||||
|
if bytes.len() != TOKEN_METADATA_DATA_SIZE {
|
||||||
|
panic!("Invalid Token Definition data");
|
||||||
|
}
|
||||||
|
|
||||||
|
Data::try_from(bytes).expect("Invalid data")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(data: &Data) -> Option<Self> {
|
||||||
|
let data = Vec::<u8>::from(data.clone());
|
||||||
|
|
||||||
|
if data.len() != TOKEN_METADATA_DATA_SIZE {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let account_type = data[0];
|
||||||
|
let version = data[1];
|
||||||
|
let definition_id = AccountId::new(
|
||||||
|
data[2..34]
|
||||||
|
.try_into()
|
||||||
|
.expect("Token Program expects valid Account Id for Metadata"),
|
||||||
|
);
|
||||||
|
let uri: [u8; 200] = data[34..234]
|
||||||
|
.try_into()
|
||||||
|
.expect("Token Program expects valid uri for Metadata");
|
||||||
|
let creators: [u8; 250] = data[234..484]
|
||||||
|
.try_into()
|
||||||
|
.expect("Token Program expects valid creators for Metadata");
|
||||||
|
let primary_sale_date = u64::from_le_bytes(
|
||||||
|
data[484..TOKEN_METADATA_DATA_SIZE]
|
||||||
|
.try_into()
|
||||||
|
.expect("Token Program expects valid blockid for Metadata"),
|
||||||
|
);
|
||||||
|
Some(Self {
|
||||||
|
account_type,
|
||||||
|
version,
|
||||||
|
definition_id,
|
||||||
|
uri,
|
||||||
|
creators,
|
||||||
|
primary_sale_date,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TokenDefinition {
|
impl TokenDefinition {
|
||||||
fn into_data(self) -> Vec<u8> {
|
fn into_data(self) -> Data {
|
||||||
let mut bytes = [0; TOKEN_DEFINITION_DATA_SIZE];
|
let mut bytes = Vec::<u8>::new();
|
||||||
bytes[0] = self.account_type;
|
bytes.extend_from_slice(&[self.account_type]);
|
||||||
bytes[1..7].copy_from_slice(&self.name);
|
bytes.extend_from_slice(&self.name);
|
||||||
bytes[7..23].copy_from_slice(&self.total_supply.to_le_bytes());
|
bytes.extend_from_slice(&self.total_supply.to_le_bytes());
|
||||||
bytes[23..].copy_from_slice(&self.metadata_id.to_bytes());
|
bytes.extend_from_slice(&self.metadata_id.to_bytes());
|
||||||
bytes.into()
|
|
||||||
|
if bytes.len() != TOKEN_DEFINITION_DATA_SIZE {
|
||||||
|
panic!("Invalid Token Definition data");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(data: &[u8]) -> Option<Self> {
|
Data::try_from(bytes).expect("Invalid data")
|
||||||
if data.len() != TOKEN_DEFINITION_DATA_SIZE || data[0] != TOKEN_DEFINITION_TYPE {
|
}
|
||||||
|
|
||||||
|
//STATUS: TODO
|
||||||
|
fn parse(data: &Data) -> Option<Self> {
|
||||||
|
let data = Vec::<u8>::from(data.clone());
|
||||||
|
|
||||||
|
//TODO: TOKEN_DEFINITION_TYPE might be silly
|
||||||
|
//Should make sure it's a valid option
|
||||||
|
//Removed the check for data[0] check
|
||||||
|
//Can be included
|
||||||
|
if data.len() != TOKEN_DEFINITION_DATA_SIZE {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let account_type = data[0];
|
let account_type = data[0];
|
||||||
@ -107,7 +177,11 @@ impl TokenDefinition {
|
|||||||
.try_into()
|
.try_into()
|
||||||
.expect("Total supply must be 16 bytes little-endian"),
|
.expect("Total supply must be 16 bytes little-endian"),
|
||||||
);
|
);
|
||||||
let metadata_id = AccountId::new([0;32]); //TODO: temp
|
let metadata_id = AccountId::new(
|
||||||
|
data[23..TOKEN_DEFINITION_DATA_SIZE]
|
||||||
|
.try_into()
|
||||||
|
.expect("Token Program expects valid Account Id for Metadata"),
|
||||||
|
);
|
||||||
Some(Self {
|
Some(Self {
|
||||||
account_type,
|
account_type,
|
||||||
name,
|
name,
|
||||||
@ -119,6 +193,8 @@ impl TokenDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TokenHolding {
|
impl TokenHolding {
|
||||||
|
//STATUS: TODO
|
||||||
|
//account type matters
|
||||||
fn new(definition_id: &AccountId) -> Self {
|
fn new(definition_id: &AccountId) -> Self {
|
||||||
Self {
|
Self {
|
||||||
account_type: TOKEN_HOLDING_TYPE,
|
account_type: TOKEN_HOLDING_TYPE,
|
||||||
@ -127,7 +203,12 @@ impl TokenHolding {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(data: &[u8]) -> Option<Self> {
|
//STATUS: TODO
|
||||||
|
fn parse(data: &Data) -> Option<Self> {
|
||||||
|
let data = Vec::<u8>::from(data.clone());
|
||||||
|
|
||||||
|
//TODO: holding type matters
|
||||||
|
//e.g., royalties payments for NFTs?
|
||||||
if data.len() != TOKEN_HOLDING_DATA_SIZE || data[0] != TOKEN_HOLDING_TYPE {
|
if data.len() != TOKEN_HOLDING_DATA_SIZE || data[0] != TOKEN_HOLDING_TYPE {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@ -143,6 +224,7 @@ impl TokenHolding {
|
|||||||
.try_into()
|
.try_into()
|
||||||
.expect("balance must be 16 bytes little-endian"),
|
.expect("balance must be 16 bytes little-endian"),
|
||||||
);
|
);
|
||||||
|
|
||||||
Some(Self {
|
Some(Self {
|
||||||
definition_id,
|
definition_id,
|
||||||
balance,
|
balance,
|
||||||
@ -150,15 +232,25 @@ impl TokenHolding {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//STATUS: fixed
|
||||||
fn into_data(self) -> Data {
|
fn into_data(self) -> Data {
|
||||||
let mut bytes = [0; TOKEN_HOLDING_DATA_SIZE];
|
let mut bytes = Vec::<u8>::new();
|
||||||
bytes[0] = self.account_type;
|
bytes.extend_from_slice(&[self.account_type]);
|
||||||
bytes[1..33].copy_from_slice(&self.definition_id.to_bytes());
|
bytes.extend_from_slice(&self.definition_id.to_bytes());
|
||||||
bytes[33..].copy_from_slice(&self.balance.to_le_bytes());
|
bytes.extend_from_slice(&self.balance.to_le_bytes());
|
||||||
bytes.into()
|
|
||||||
|
if bytes.len() != TOKEN_HOLDING_DATA_SIZE {
|
||||||
|
panic!("Invalid Token Holding data");
|
||||||
|
}
|
||||||
|
|
||||||
|
Data::try_from(bytes).expect("Invalid data")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: transfer for NFTs could/should have built in payments for royalties
|
||||||
|
// This probably would need a different transfer program...
|
||||||
|
// or branching logic
|
||||||
|
// Minus royalties issue-> this function is fine
|
||||||
fn transfer(pre_states: &[AccountWithMetadata], balance_to_move: u128) -> Vec<AccountPostState> {
|
fn transfer(pre_states: &[AccountWithMetadata], balance_to_move: u128) -> Vec<AccountPostState> {
|
||||||
if pre_states.len() != 2 {
|
if pre_states.len() != 2 {
|
||||||
panic!("Invalid number of input accounts");
|
panic!("Invalid number of input accounts");
|
||||||
@ -166,8 +258,13 @@ fn transfer(pre_states: &[AccountWithMetadata], balance_to_move: u128) -> Vec<Ac
|
|||||||
let sender = &pre_states[0];
|
let sender = &pre_states[0];
|
||||||
let recipient = &pre_states[1];
|
let recipient = &pre_states[1];
|
||||||
|
|
||||||
|
if !sender.is_authorized {
|
||||||
|
panic!("Sender authorization is missing");
|
||||||
|
}
|
||||||
|
|
||||||
let mut sender_holding =
|
let mut sender_holding =
|
||||||
TokenHolding::parse(&sender.account.data).expect("Invalid sender data");
|
TokenHolding::parse(&sender.account.data).expect("Invalid sender data");
|
||||||
|
|
||||||
let mut recipient_holding = if recipient.account == Account::default() {
|
let mut recipient_holding = if recipient.account == Account::default() {
|
||||||
TokenHolding::new(&sender_holding.definition_id)
|
TokenHolding::new(&sender_holding.definition_id)
|
||||||
} else {
|
} else {
|
||||||
@ -182,11 +279,10 @@ fn transfer(pre_states: &[AccountWithMetadata], balance_to_move: u128) -> Vec<Ac
|
|||||||
panic!("Insufficient balance");
|
panic!("Insufficient balance");
|
||||||
}
|
}
|
||||||
|
|
||||||
if !sender.is_authorized {
|
sender_holding.balance -= sender_holding
|
||||||
panic!("Sender authorization is missing");
|
.balance
|
||||||
}
|
.checked_sub(balance_to_move)
|
||||||
|
.expect("Checked above");
|
||||||
sender_holding.balance -= balance_to_move;
|
|
||||||
recipient_holding.balance = recipient_holding
|
recipient_holding.balance = recipient_holding
|
||||||
.balance
|
.balance
|
||||||
.checked_add(balance_to_move)
|
.checked_add(balance_to_move)
|
||||||
@ -213,36 +309,17 @@ fn transfer(pre_states: &[AccountWithMetadata], balance_to_move: u128) -> Vec<Ac
|
|||||||
vec![sender_post, recipient_post]
|
vec![sender_post, recipient_post]
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: move supplemental functions to the bottom (above tests)
|
//TODO: use new_definition to only mint FungibleTokens
|
||||||
fn valid_total_supply_for_token_standard(total_supply: u128, token_standard: u8) -> bool {
|
// e.g. no Metadata
|
||||||
if token_standard == helper_token_standard_constructor(TokenStandard::NonFungibleHolding) &&
|
|
||||||
total_supply != 1 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_definition(
|
fn new_definition(
|
||||||
pre_states: &[AccountWithMetadata],
|
pre_states: &[AccountWithMetadata],
|
||||||
name: [u8; 6],
|
name: [u8; 6],
|
||||||
total_supply: u128,
|
total_supply: u128,
|
||||||
//token_standard: u8, //TODO: uncomment out; currently ommitted to not break tests
|
|
||||||
//how to determine accounttype
|
|
||||||
) -> Vec<AccountPostState> {
|
) -> Vec<AccountPostState> {
|
||||||
//Additional account needed in some cases.
|
|
||||||
//Assuming fungible
|
|
||||||
let (definition_target_account, holding_target_account) = new_definition_fungible(pre_states);
|
|
||||||
|
|
||||||
//TODO temp
|
|
||||||
let token_standard: u8 = 0;
|
|
||||||
|
|
||||||
//Don't need pre_state's full AccountWithMetadata
|
|
||||||
//Unless PDA is used for metadata address?
|
|
||||||
|
|
||||||
if pre_states.len() != 2 {
|
if pre_states.len() != 2 {
|
||||||
panic!("Invalid number of input accounts");
|
panic!("Invalid number of input accounts");
|
||||||
}
|
}
|
||||||
|
|
||||||
let definition_target_account = &pre_states[0];
|
let definition_target_account = &pre_states[0];
|
||||||
let holding_target_account = &pre_states[1];
|
let holding_target_account = &pre_states[1];
|
||||||
|
|
||||||
@ -254,35 +331,19 @@ fn new_definition(
|
|||||||
panic!("Holding target account must have default values");
|
panic!("Holding target account must have default values");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nonfungible must have total supply 1
|
|
||||||
//TODO add a test
|
|
||||||
//Ideally, we could use TokenStandard enum for this
|
|
||||||
if !valid_total_supply_for_token_standard(total_supply, token_standard) {
|
|
||||||
panic!("Invalid total supply for the specified token supply");
|
|
||||||
}
|
|
||||||
|
|
||||||
let token_definition = TokenDefinition {
|
let token_definition = TokenDefinition {
|
||||||
account_type: TOKEN_DEFINITION_TYPE, //TODO use token standard
|
account_type: helper_token_standard_constructor(TokenStandard::FungibleTokenHolding),
|
||||||
name,
|
name,
|
||||||
total_supply,
|
total_supply,
|
||||||
metadata_id: AccountId::new([0;32]), //TODO placeholder
|
metadata_id: AccountId::new([0; 32]),
|
||||||
};
|
};
|
||||||
|
|
||||||
let token_holding = TokenHolding {
|
let token_holding = TokenHolding {
|
||||||
account_type: TOKEN_HOLDING_TYPE, //TODO use token standard?
|
account_type: TOKEN_HOLDING_TYPE,
|
||||||
definition_id: definition_target_account.account_id.clone(),
|
definition_id: definition_target_account.account_id.clone(),
|
||||||
balance: total_supply,
|
balance: total_supply,
|
||||||
};
|
};
|
||||||
|
|
||||||
let token_metadata = TokenMetadata {
|
|
||||||
account_type: TOKEN_HOLDING_TYPE, //TODO temp
|
|
||||||
definition_id: definition_target_account.account_id.clone(),
|
|
||||||
version: CURRENT_VERSION,
|
|
||||||
uri: Vec::<u8>::new(),
|
|
||||||
creators: Vec::<u8>::new(),
|
|
||||||
primary_sale_date: Vec::<u8>::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut definition_target_account_post = definition_target_account.account.clone();
|
let mut definition_target_account_post = definition_target_account.account.clone();
|
||||||
definition_target_account_post.data = token_definition.into_data();
|
definition_target_account_post.data = token_definition.into_data();
|
||||||
|
|
||||||
@ -295,12 +356,89 @@ fn new_definition(
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_definition_fungible(pre_states: &[AccountWithMetadata]) -> (Account, Account) {
|
fn new_definition_with_metadata(
|
||||||
if pre_states.len() != 2 {
|
pre_states: &[AccountWithMetadata],
|
||||||
|
name: [u8; 6],
|
||||||
|
total_supply: u128,
|
||||||
|
token_standard: u8,
|
||||||
|
uri: &[u8; 200],
|
||||||
|
creators: &[u8; 250],
|
||||||
|
blockid: u64,
|
||||||
|
) -> Vec<AccountPostState> {
|
||||||
|
if pre_states.len() != 3 {
|
||||||
panic!("Invalid number of input accounts");
|
panic!("Invalid number of input accounts");
|
||||||
}
|
}
|
||||||
|
|
||||||
(pre_states[0].account.clone(), pre_states[1].account.clone())
|
let definition_target_account = &pre_states[0];
|
||||||
|
let metadata_target_account = &pre_states[1];
|
||||||
|
let holding_target_account = &pre_states[2];
|
||||||
|
|
||||||
|
//TODO test
|
||||||
|
if definition_target_account.account != Account::default() {
|
||||||
|
panic!("Definition target account must have default values");
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO test
|
||||||
|
if metadata_target_account.account != Account::default() {
|
||||||
|
panic!("Metadata target account must have default values");
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO test
|
||||||
|
if holding_target_account.account != Account::default() {
|
||||||
|
panic!("Holding target account must have default values");
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO test
|
||||||
|
if !valid_total_supply_for_token_standard(total_supply, token_standard) {
|
||||||
|
panic!("Invalid total supply for the specified token supply");
|
||||||
|
}
|
||||||
|
|
||||||
|
let token_definition = TokenDefinition {
|
||||||
|
account_type: helper_token_standard_constructor(TokenStandard::FungibleTokenHolding),
|
||||||
|
name,
|
||||||
|
total_supply,
|
||||||
|
metadata_id: metadata_target_account.account_id.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let token_holding = TokenHolding {
|
||||||
|
account_type: TOKEN_HOLDING_TYPE,
|
||||||
|
definition_id: definition_target_account.account_id.clone(),
|
||||||
|
balance: total_supply,
|
||||||
|
};
|
||||||
|
|
||||||
|
//TODO: enforce with pda seed address
|
||||||
|
let token_metadata = TokenMetadata {
|
||||||
|
account_type: 0u8, //todo temp
|
||||||
|
version: 1u8,
|
||||||
|
definition_id: definition_target_account.account_id.clone(),
|
||||||
|
uri: uri.clone(),
|
||||||
|
creators: creators.clone(),
|
||||||
|
primary_sale_date: blockid,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut definition_target_account_post = definition_target_account.account.clone();
|
||||||
|
definition_target_account_post.data = token_definition.into_data();
|
||||||
|
|
||||||
|
let mut holding_target_account_post = holding_target_account.account.clone();
|
||||||
|
holding_target_account_post.data = token_holding.into_data();
|
||||||
|
|
||||||
|
let mut metadata_target_account_post = metadata_target_account.account.clone();
|
||||||
|
metadata_target_account_post.data = token_metadata.into_data();
|
||||||
|
|
||||||
|
vec![
|
||||||
|
AccountPostState::new_claimed(definition_target_account_post),
|
||||||
|
AccountPostState::new_claimed(holding_target_account_post),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn valid_total_supply_for_token_standard(total_supply: u128, token_standard: u8) -> bool {
|
||||||
|
if token_standard == helper_token_standard_constructor(TokenStandard::NonFungibleHolding)
|
||||||
|
&& total_supply != 1
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize_account(pre_states: &[AccountWithMetadata]) -> Vec<AccountPostState> {
|
fn initialize_account(pre_states: &[AccountWithMetadata]) -> Vec<AccountPostState> {
|
||||||
@ -341,17 +479,17 @@ fn burn(pre_states: &[AccountWithMetadata], balance_to_burn: u128) -> Vec<Accoun
|
|||||||
let definition = &pre_states[0];
|
let definition = &pre_states[0];
|
||||||
let user_holding = &pre_states[1];
|
let user_holding = &pre_states[1];
|
||||||
|
|
||||||
let definition_values =
|
if !user_holding.is_authorized {
|
||||||
TokenDefinition::parse(&definition.account.data).expect("Definition account must be valid");
|
panic!("Authorization is missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
let definition_values = TokenDefinition::parse(&definition.account.data)
|
||||||
|
.expect("Token Definition account must be valid");
|
||||||
let user_values = TokenHolding::parse(&user_holding.account.data)
|
let user_values = TokenHolding::parse(&user_holding.account.data)
|
||||||
.expect("Token Holding account must be valid");
|
.expect("Token Holding account must be valid");
|
||||||
|
|
||||||
if definition.account_id != user_values.definition_id {
|
if definition.account_id != user_values.definition_id {
|
||||||
panic!("Mismatch token definition and token holding");
|
panic!("Mismatch Token Definition and Token Holding");
|
||||||
}
|
|
||||||
|
|
||||||
if !user_holding.is_authorized {
|
|
||||||
panic!("Authorization is missing");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if user_values.balance < balance_to_burn {
|
if user_values.balance < balance_to_burn {
|
||||||
@ -364,7 +502,10 @@ fn burn(pre_states: &[AccountWithMetadata], balance_to_burn: u128) -> Vec<Accoun
|
|||||||
post_user_holding.data = TokenHolding::into_data(TokenHolding {
|
post_user_holding.data = TokenHolding::into_data(TokenHolding {
|
||||||
account_type: user_values.account_type,
|
account_type: user_values.account_type,
|
||||||
definition_id: user_values.definition_id,
|
definition_id: user_values.definition_id,
|
||||||
balance: user_values.balance - balance_to_burn,
|
balance: user_values
|
||||||
|
.balance
|
||||||
|
.checked_sub(balance_to_burn)
|
||||||
|
.expect("Checked above"),
|
||||||
});
|
});
|
||||||
|
|
||||||
post_definition.data = TokenDefinition::into_data(TokenDefinition {
|
post_definition.data = TokenDefinition::into_data(TokenDefinition {
|
||||||
@ -383,6 +524,16 @@ fn burn(pre_states: &[AccountWithMetadata], balance_to_burn: u128) -> Vec<Accoun
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: temp
|
||||||
|
fn is_mintable(account_type: u8) -> bool {
|
||||||
|
if account_type == helper_token_standard_constructor(TokenStandard::NonFungibleHolding) {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status of function: fixed
|
||||||
fn mint_additional_supply(
|
fn mint_additional_supply(
|
||||||
pre_states: &[AccountWithMetadata],
|
pre_states: &[AccountWithMetadata],
|
||||||
amount_to_mint: u128,
|
amount_to_mint: u128,
|
||||||
@ -404,11 +555,16 @@ fn mint_additional_supply(
|
|||||||
let token_holding_values: TokenHolding = if token_holding.account == Account::default() {
|
let token_holding_values: TokenHolding = if token_holding.account == Account::default() {
|
||||||
TokenHolding::new(&definition.account_id)
|
TokenHolding::new(&definition.account_id)
|
||||||
} else {
|
} else {
|
||||||
|
//remove clone
|
||||||
TokenHolding::parse(&token_holding.account.data).expect("Holding account must be valid")
|
TokenHolding::parse(&token_holding.account.data).expect("Holding account must be valid")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if !is_mintable(definition_values.account_type) {
|
||||||
|
panic!("Token Definition's standard does not permit minting additional supply");
|
||||||
|
}
|
||||||
|
|
||||||
if definition.account_id != token_holding_values.definition_id {
|
if definition.account_id != token_holding_values.definition_id {
|
||||||
panic!("Mismatch token definition and token holding");
|
panic!("Mismatch Token Definition and Token Holding");
|
||||||
}
|
}
|
||||||
|
|
||||||
let token_holding_post_data = TokenHolding {
|
let token_holding_post_data = TokenHolding {
|
||||||
@ -534,12 +690,13 @@ fn main() {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use nssa_core::account::{Account, AccountId, AccountWithMetadata};
|
use nssa_core::account::{Account, AccountId, AccountWithMetadata, Data};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
TOKEN_DEFINITION_DATA_SIZE, TOKEN_DEFINITION_TYPE, TOKEN_HOLDING_DATA_SIZE,
|
TOKEN_DEFINITION_DATA_SIZE, TOKEN_DEFINITION_TYPE, TOKEN_HOLDING_DATA_SIZE,
|
||||||
TOKEN_HOLDING_TYPE, TokenDefinition, TokenHolding, burn, initialize_account,
|
TOKEN_HOLDING_TYPE, TokenDefinition, TokenHolding, TokenStandard, burn,
|
||||||
mint_additional_supply, new_definition, transfer,
|
helper_token_standard_constructor, initialize_account, mint_additional_supply,
|
||||||
|
new_definition, transfer,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[should_panic(expected = "Invalid number of input accounts")]
|
#[should_panic(expected = "Invalid number of input accounts")]
|
||||||
@ -618,6 +775,7 @@ mod tests {
|
|||||||
let _post_states = new_definition(&pre_states, [0xca, 0xfe, 0xca, 0xfe, 0xca, 0xfe], 10);
|
let _post_states = new_definition(&pre_states, [0xca, 0xfe, 0xca, 0xfe, 0xca, 0xfe], 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
#[test]
|
#[test]
|
||||||
fn test_new_definition_with_valid_inputs_succeeds() {
|
fn test_new_definition_with_valid_inputs_succeeds() {
|
||||||
let pre_states = vec![
|
let pre_states = vec![
|
||||||
@ -656,6 +814,7 @@ mod tests {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
#[should_panic(expected = "Invalid number of input accounts")]
|
#[should_panic(expected = "Invalid number of input accounts")]
|
||||||
#[test]
|
#[test]
|
||||||
@ -699,7 +858,8 @@ mod tests {
|
|||||||
AccountWithMetadata {
|
AccountWithMetadata {
|
||||||
account: Account {
|
account: Account {
|
||||||
// First byte should be `TOKEN_HOLDING_TYPE` for token holding accounts
|
// First byte should be `TOKEN_HOLDING_TYPE` for token holding accounts
|
||||||
data: vec![invalid_type; TOKEN_HOLDING_DATA_SIZE],
|
data: Data::try_from(vec![invalid_type; TOKEN_HOLDING_DATA_SIZE])
|
||||||
|
.expect("Invalid data"),
|
||||||
..Account::default()
|
..Account::default()
|
||||||
},
|
},
|
||||||
is_authorized: true,
|
is_authorized: true,
|
||||||
@ -714,6 +874,7 @@ mod tests {
|
|||||||
let _post_states = transfer(&pre_states, 10);
|
let _post_states = transfer(&pre_states, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
#[should_panic(expected = "Invalid sender data")]
|
#[should_panic(expected = "Invalid sender data")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_transfer_invalid_data_size_should_fail_1() {
|
fn test_transfer_invalid_data_size_should_fail_1() {
|
||||||
@ -920,7 +1081,7 @@ mod tests {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
enum BalanceEnum {
|
enum BalanceEnum {
|
||||||
InitSupply,
|
InitSupply,
|
||||||
HoldingBalance,
|
HoldingBalance,
|
||||||
@ -948,12 +1109,14 @@ mod tests {
|
|||||||
DefinitionAccountMint,
|
DefinitionAccountMint,
|
||||||
HoldingSameDefMint,
|
HoldingSameDefMint,
|
||||||
HoldingSameDefAuthLargeBalance,
|
HoldingSameDefAuthLargeBalance,
|
||||||
|
DefinitionAccountAuthNotMintable,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum IdEnum {
|
enum IdEnum {
|
||||||
PoolDefinitionId,
|
PoolDefinitionId,
|
||||||
PoolDefinitionIdDiff,
|
PoolDefinitionIdDiff,
|
||||||
HoldingId,
|
HoldingId,
|
||||||
|
MetadataId,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn helper_account_constructor(selection: AccountsEnum) -> AccountWithMetadata {
|
fn helper_account_constructor(selection: AccountsEnum) -> AccountWithMetadata {
|
||||||
@ -966,7 +1129,7 @@ mod tests {
|
|||||||
account_type: TOKEN_DEFINITION_TYPE,
|
account_type: TOKEN_DEFINITION_TYPE,
|
||||||
name: [2; 6],
|
name: [2; 6],
|
||||||
total_supply: helper_balance_constructor(BalanceEnum::InitSupply),
|
total_supply: helper_balance_constructor(BalanceEnum::InitSupply),
|
||||||
metadata_id: AccountId::new([0;32]),
|
metadata_id: helper_id_constructor(IdEnum::MetadataId),
|
||||||
}),
|
}),
|
||||||
nonce: 0,
|
nonce: 0,
|
||||||
},
|
},
|
||||||
@ -981,7 +1144,7 @@ mod tests {
|
|||||||
account_type: TOKEN_DEFINITION_TYPE,
|
account_type: TOKEN_DEFINITION_TYPE,
|
||||||
name: [2; 6],
|
name: [2; 6],
|
||||||
total_supply: helper_balance_constructor(BalanceEnum::InitSupply),
|
total_supply: helper_balance_constructor(BalanceEnum::InitSupply),
|
||||||
metadata_id: AccountId::new([0;32]),
|
metadata_id: helper_id_constructor(IdEnum::MetadataId),
|
||||||
}),
|
}),
|
||||||
nonce: 0,
|
nonce: 0,
|
||||||
},
|
},
|
||||||
@ -1052,7 +1215,7 @@ mod tests {
|
|||||||
account_type: TOKEN_DEFINITION_TYPE,
|
account_type: TOKEN_DEFINITION_TYPE,
|
||||||
name: [2; 6],
|
name: [2; 6],
|
||||||
total_supply: helper_balance_constructor(BalanceEnum::InitSupplyBurned),
|
total_supply: helper_balance_constructor(BalanceEnum::InitSupplyBurned),
|
||||||
metadata_id: AccountId::new([0;32]),
|
metadata_id: helper_id_constructor(IdEnum::MetadataId),
|
||||||
}),
|
}),
|
||||||
nonce: 0,
|
nonce: 0,
|
||||||
},
|
},
|
||||||
@ -1114,7 +1277,7 @@ mod tests {
|
|||||||
account_type: TOKEN_DEFINITION_TYPE,
|
account_type: TOKEN_DEFINITION_TYPE,
|
||||||
name: [2; 6],
|
name: [2; 6],
|
||||||
total_supply: helper_balance_constructor(BalanceEnum::InitSupplyMint),
|
total_supply: helper_balance_constructor(BalanceEnum::InitSupplyMint),
|
||||||
metadata_id: AccountId::new([0;32]),
|
metadata_id: helper_id_constructor(IdEnum::MetadataId),
|
||||||
}),
|
}),
|
||||||
nonce: 0,
|
nonce: 0,
|
||||||
},
|
},
|
||||||
@ -1135,6 +1298,23 @@ mod tests {
|
|||||||
is_authorized: true,
|
is_authorized: true,
|
||||||
account_id: helper_id_constructor(IdEnum::PoolDefinitionId),
|
account_id: helper_id_constructor(IdEnum::PoolDefinitionId),
|
||||||
},
|
},
|
||||||
|
AccountsEnum::DefinitionAccountAuthNotMintable => AccountWithMetadata {
|
||||||
|
account: Account {
|
||||||
|
program_owner: [5u32; 8],
|
||||||
|
balance: 0u128,
|
||||||
|
data: TokenDefinition::into_data(TokenDefinition {
|
||||||
|
account_type: helper_token_standard_constructor(
|
||||||
|
TokenStandard::NonFungibleHolding,
|
||||||
|
),
|
||||||
|
name: [2; 6],
|
||||||
|
total_supply: helper_balance_constructor(BalanceEnum::InitSupplyMint),
|
||||||
|
metadata_id: helper_id_constructor(IdEnum::MetadataId),
|
||||||
|
}),
|
||||||
|
nonce: 0,
|
||||||
|
},
|
||||||
|
is_authorized: true,
|
||||||
|
account_id: helper_id_constructor(IdEnum::PoolDefinitionId),
|
||||||
|
},
|
||||||
_ => panic!("Invalid selection"),
|
_ => panic!("Invalid selection"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1160,6 +1340,7 @@ mod tests {
|
|||||||
IdEnum::PoolDefinitionId => AccountId::new([15; 32]),
|
IdEnum::PoolDefinitionId => AccountId::new([15; 32]),
|
||||||
IdEnum::PoolDefinitionIdDiff => AccountId::new([16; 32]),
|
IdEnum::PoolDefinitionIdDiff => AccountId::new([16; 32]),
|
||||||
IdEnum::HoldingId => AccountId::new([17; 32]),
|
IdEnum::HoldingId => AccountId::new([17; 32]),
|
||||||
|
IdEnum::MetadataId => AccountId::new([42; 32]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1176,7 +1357,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "Mismatch token definition and token holding")]
|
#[should_panic(expected = "Mismatch Token Definition and Token Holding")]
|
||||||
fn test_burn_mismatch_def() {
|
fn test_burn_mismatch_def() {
|
||||||
let pre_states = vec![
|
let pre_states = vec![
|
||||||
helper_account_constructor(AccountsEnum::DefinitionAccountAuth),
|
helper_account_constructor(AccountsEnum::DefinitionAccountAuth),
|
||||||
@ -1290,7 +1471,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[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 pre_states = vec![
|
let pre_states = vec![
|
||||||
helper_account_constructor(AccountsEnum::DefinitionAccountAuth),
|
helper_account_constructor(AccountsEnum::DefinitionAccountAuth),
|
||||||
@ -1375,4 +1556,19 @@ mod tests {
|
|||||||
helper_balance_constructor(BalanceEnum::MintOverflow),
|
helper_balance_constructor(BalanceEnum::MintOverflow),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(
|
||||||
|
expected = "Token Definition's standard does not permit minting additional supply"
|
||||||
|
)]
|
||||||
|
fn test_mint_cannot_mint_unmintable_tokens() {
|
||||||
|
let pre_states = vec![
|
||||||
|
helper_account_constructor(AccountsEnum::DefinitionAccountAuthNotMintable),
|
||||||
|
helper_account_constructor(AccountsEnum::HoldingSameDefNotAuth),
|
||||||
|
];
|
||||||
|
let _post_states = mint_additional_supply(
|
||||||
|
&pre_states,
|
||||||
|
helper_balance_constructor(BalanceEnum::MintSuccess),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user