add integratin tests wip

This commit is contained in:
Sergio Chouhy 2025-09-15 18:09:28 -03:00
parent 5979d8d0cf
commit 473a5fd98b
3 changed files with 173 additions and 7 deletions

View File

@ -172,6 +172,47 @@ pub async fn test_success_move_to_another_account() {
info!("Success!");
}
pub async fn test_success_token_program() {
let wallet_config = fetch_config().unwrap();
wallet::execute_subcommand(Command::RegisterAccount {})
.await
.unwrap();
wallet::execute_subcommand(Command::RegisterAccount {})
.await
.unwrap();
wallet::execute_subcommand(Command::RegisterAccount {})
.await
.unwrap();
let persistent_accounts = fetch_persistent_accounts().unwrap();
let mut new_persistent_accounts_addr = Vec::new();
for per_acc in persistent_accounts {
if (per_acc.address.to_string() != ACC_RECEIVER)
&& (per_acc.address.to_string() != ACC_SENDER)
{
new_persistent_accounts_addr.push(per_acc.address.to_string());
}
}
let [definition_addr, supply_addr, other_addr] = new_persistent_accounts_addr
.try_into()
.expect("Failed to produce new account, not present in persistent accounts");
let command = Command::NewTokenDefinition {
definition_addr,
supply_addr,
name: "name".to_string(),
total_supply: 37,
};
wallet::execute_subcommand(command).await.unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
}
pub async fn test_failure() {
let command = Command::SendNativeTokenTransfer {
from: ACC_SENDER.to_string(),

View File

@ -3,14 +3,27 @@ use nssa_core::{
program::{ProgramInput, read_nssa_inputs, write_nssa_outputs},
};
/// [type (1) || amount (16) || name (6)]
type Instruction = [u8; 23];
// The token program has two functions:
// 1. New token definition.
// Arguments to this function are:
// * Two **default** accounts: [definition_account, holding_account].
// The first default account will be populated with the token definition account values. The second account will
// be set to a token holding account for the new token, holding the entire total supply.
// * An instruction data of 23-bytes, indicating the total supply and the token name, with
// the following layout:
// [0x00 || total_supply (little-endian 16 bytes) || name (6 bytes)]
// The name cannot be equal to [0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
// 2. Token transfer
// Arguments to this function are:
// * Two accounts: [sender_account, recipient_account].
// * An instruction data byte string of length 23, indicating the total supply and the token name, with the following layout
// [0x01 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 || 0x00 || 0x00 || 0x00].
const TOKEN_DEFINITION_TYPE: u8 = 0;
const TOKEN_DEFINITION_SIZE: usize = 23;
const TOKEN_DEFINITION_DATA_SIZE: usize = 23;
const TOKEN_HOLDING_TYPE: u8 = 1;
const TOKEN_HOLDING_SIZE: usize = 49;
const TOKEN_HOLDING_DATA_SIZE: usize = 49;
struct TokenDefinition {
account_type: u8,
@ -26,7 +39,7 @@ struct TokenHolding {
impl TokenDefinition {
fn into_data(self) -> Vec<u8> {
let mut bytes = [0; TOKEN_DEFINITION_SIZE];
let mut bytes = [0; TOKEN_DEFINITION_DATA_SIZE];
bytes[0] = self.account_type;
bytes[1..7].copy_from_slice(&self.name);
bytes[7..].copy_from_slice(&self.total_supply.to_le_bytes());
@ -44,7 +57,7 @@ impl TokenHolding {
}
fn parse(data: &[u8]) -> Option<Self> {
if data.len() != TOKEN_HOLDING_SIZE && data[0] != TOKEN_HOLDING_TYPE {
if data.len() != TOKEN_HOLDING_DATA_SIZE && data[0] != TOKEN_HOLDING_TYPE {
None
} else {
let account_type = data[0];
@ -59,7 +72,7 @@ impl TokenHolding {
}
fn into_data(self) -> Data {
let mut bytes = [0; TOKEN_HOLDING_SIZE];
let mut bytes = [0; TOKEN_HOLDING_DATA_SIZE];
bytes[0] = self.account_type;
bytes[1..33].copy_from_slice(&self.definition_id.to_bytes());
bytes[33..].copy_from_slice(&self.balance.to_le_bytes());
@ -147,6 +160,8 @@ fn new_definition(pre_states: Vec<AccountWithMetadata>, name: [u8; 6], total_sup
);
}
type Instruction = [u8; 23];
fn main() {
let ProgramInput {
pre_states,

View File

@ -126,6 +126,63 @@ impl WalletCore {
}
}
pub async fn send_new_token_definition(
&self,
definition_address: Address,
supply_address: Address,
name: [u8; 6],
total_supply: u128,
) -> Result<SendTxResponse, ExecutionFailureKind> {
let addresses = vec![definition_address, supply_address];
let program_id = nssa::program::Program::token().id();
// Instruction must be: [0x00 || total_supply (little-endian 16 bytes) || name (6 bytes)]
let mut instruction = [0; 23];
instruction[1..17].copy_from_slice(&total_supply.to_le_bytes());
instruction[17..].copy_from_slice(&name);
let message =
nssa::public_transaction::Message::try_new(program_id, addresses, vec![], instruction)
.unwrap();
let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[]);
let tx = nssa::PublicTransaction::new(message, witness_set);
Ok(self.sequencer_client.send_tx(tx).await?)
}
pub async fn send_token_transfer(
&self,
sender_address: Address,
recipient_address: Address,
amount: u128,
) -> Result<SendTxResponse, ExecutionFailureKind> {
let addresses = vec![sender_address, recipient_address];
let program_id = nssa::program::Program::token().id();
// Instruction must be: [0x01 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 || 0x00 || 0x00 || 0x00].
let mut instruction = [0; 23];
instruction[0] = 0x01;
instruction[1..17].copy_from_slice(&amount.to_le_bytes());
let Ok(nonces) = self.get_accounts_nonces(vec![sender_address]).await else {
return Err(ExecutionFailureKind::SequencerError);
};
let message =
nssa::public_transaction::Message::try_new(program_id, addresses, nonces, instruction)
.unwrap();
let Some(signing_key) = self
.storage
.user_data
.get_account_signing_key(&sender_address)
else {
return Err(ExecutionFailureKind::KeyNotFoundError);
};
let witness_set =
nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]);
let tx = nssa::PublicTransaction::new(message, witness_set);
Ok(self.sequencer_client.send_tx(tx).await?)
}
///Get account balance
pub async fn get_account_balance(&self, acc: Address) -> Result<u128> {
Ok(self
@ -201,6 +258,24 @@ pub enum Command {
#[arg(short, long)]
addr: String,
},
NewTokenDefinition {
#[arg(short, long)]
definition_addr: String,
#[arg(short, long)]
supply_addr: String,
#[arg(short, long)]
name: String,
#[arg(short, long)]
total_supply: u128,
},
TokenTransfer {
#[arg(short, long)]
sender_addr: String,
#[arg(short, long)]
recipient_addr: String,
#[arg(short, long)]
balance_to_move: u128,
},
}
///To execute commands, env var NSSA_WALLET_HOME_DIR must be set into directory with config
@ -264,6 +339,41 @@ pub async fn execute_subcommand(command: Command) -> Result<()> {
let account: HumanReadableAccount = wallet_core.get_account(addr).await?.into();
println!("{}", serde_json::to_string(&account).unwrap());
}
Command::NewTokenDefinition {
definition_addr,
supply_addr,
name,
total_supply,
} => {
let name = name.as_bytes();
if name.len() > 6 {
// TODO: return error
panic!();
}
let mut name_bytes = [0; 6];
name_bytes[..name.len()].copy_from_slice(name);
wallet_core
.send_new_token_definition(
definition_addr.parse().unwrap(),
supply_addr.parse().unwrap(),
name_bytes,
total_supply,
)
.await?;
}
Command::TokenTransfer {
sender_addr,
recipient_addr,
balance_to_move,
} => {
wallet_core
.send_token_transfer(
sender_addr.parse().unwrap(),
recipient_addr.parse().unwrap(),
balance_to_move,
)
.await?;
}
}
Ok(())