use assert_matches::assert_matches;
use hedera::{
Hbar,
Status,
TokenCreateTransaction,
TokenMintTransaction,
TokenSupplyType,
TokenType,
};
use time::{
Duration,
OffsetDateTime,
};
use crate::account::Account;
use crate::common::{
setup_nonfree,
TestEnvironment,
};
use crate::token::{
CreateFungibleToken,
FungibleToken,
Key,
Nft,
TokenKeys,
};
const TOKEN_KEYS: TokenKeys = TokenKeys { supply: Some(Key::Owner), ..TokenKeys::DEFAULT };
#[tokio::test]
async fn basic() -> anyhow::Result<()> {
const INITIAL_SUPPLY: u64 = 1_000_000;
let Some(TestEnvironment { config: _, client }) = setup_nonfree() else {
return Ok(());
};
let account = Account::create(Hbar::new(0), &client).await?;
let token = super::FungibleToken::create(
&client,
&account,
CreateFungibleToken { initial_supply: INITIAL_SUPPLY, keys: TOKEN_KEYS },
)
.await?;
let receipt = TokenMintTransaction::new()
.amount(10)
.token_id(token.id)
.sign(account.key.clone())
.execute(&client)
.await?
.get_receipt(&client)
.await?;
assert_eq!(receipt.total_supply, INITIAL_SUPPLY + 10);
token.burn(&client, INITIAL_SUPPLY + 10).await?;
token.delete(&client).await?;
account.delete(&client).await?;
Ok(())
}
#[tokio::test]
async fn over_supply_limit_fails() -> anyhow::Result<()> {
let Some(TestEnvironment { config: _, client }) = setup_nonfree() else {
return Ok(());
};
let account = Account::create(Hbar::new(0), &client).await?;
let token_id = TokenCreateTransaction::new()
.name("ffff")
.symbol("F")
.token_supply_type(hedera::TokenSupplyType::Finite)
.max_supply(5)
.treasury_account_id(account.id)
.admin_key(account.key.public_key())
.supply_key(account.key.public_key())
.expiration_time(OffsetDateTime::now_utc() + Duration::minutes(5))
.sign(account.key.clone())
.execute(&client)
.await?
.get_receipt(&client)
.await?
.token_id
.unwrap();
let token = FungibleToken { id: token_id, owner: account.clone() };
let res = TokenMintTransaction::new()
.token_id(token.id)
.amount(6)
.sign(account.key.clone())
.execute(&client)
.await?
.get_receipt(&client)
.await;
assert_matches!(
res,
Err(hedera::Error::ReceiptStatus { status: Status::TokenMaxSupplyReached, .. })
);
token.delete(&client).await?;
account.delete(&client).await?;
Ok(())
}
#[tokio::test]
async fn missing_token_id_fails() -> anyhow::Result<()> {
let Some(TestEnvironment { config: _, client }) = setup_nonfree() else {
return Ok(());
};
let res = TokenMintTransaction::new().amount(6).execute(&client).await;
assert_matches!(
res,
Err(hedera::Error::TransactionPreCheckStatus { status: Status::InvalidTokenId, .. })
);
Ok(())
}
#[tokio::test]
async fn zero() -> anyhow::Result<()> {
const INITIAL_SUPPLY: u64 = 1_000_000;
let Some(TestEnvironment { config: _, client }) = setup_nonfree() else {
return Ok(());
};
let account = Account::create(Hbar::new(0), &client).await?;
let token = super::FungibleToken::create(
&client,
&account,
CreateFungibleToken { initial_supply: INITIAL_SUPPLY, keys: TOKEN_KEYS },
)
.await?;
let receipt = TokenMintTransaction::new()
.token_id(token.id)
.sign(account.key.clone())
.execute(&client)
.await?
.get_receipt(&client)
.await?;
assert_eq!(receipt.total_supply, INITIAL_SUPPLY);
token.burn(&client, INITIAL_SUPPLY).await?;
token.delete(&client).await?;
account.delete(&client).await?;
Ok(())
}
#[tokio::test]
async fn missing_supply_key_sig_fails() -> anyhow::Result<()> {
let Some(TestEnvironment { config: _, client }) = setup_nonfree() else {
return Ok(());
};
let account = Account::create(Hbar::new(0), &client).await?;
let token = super::FungibleToken::create(
&client,
&account,
CreateFungibleToken { initial_supply: 0, keys: TOKEN_KEYS },
)
.await?;
let res = TokenMintTransaction::new()
.token_id(token.id)
.amount(10)
.execute(&client)
.await?
.get_receipt(&client)
.await;
assert_matches!(
res,
Err(hedera::Error::ReceiptStatus { status: Status::InvalidSignature, .. })
);
token.delete(&client).await?;
account.delete(&client).await?;
Ok(())
}
#[tokio::test]
async fn nfts() -> anyhow::Result<()> {
let Some(TestEnvironment { config: _, client }) = setup_nonfree() else {
return Ok(());
};
let account = Account::create(Hbar::new(0), &client).await?;
let token_id = TokenCreateTransaction::new()
.name("ffff")
.symbol("F")
.treasury_account_id(account.id)
.admin_key(account.key.public_key())
.supply_key(account.key.public_key())
.token_type(TokenType::NonFungibleUnique)
.token_supply_type(TokenSupplyType::Finite)
.max_supply(5000)
.expiration_time(OffsetDateTime::now_utc() + Duration::minutes(5))
.sign(account.key.clone())
.execute(&client)
.await?
.get_receipt(&client)
.await?
.token_id
.unwrap();
let token = Nft { id: token_id, owner: account.clone() };
let mint_receipt = TokenMintTransaction::new()
.token_id(token_id)
.metadata((0..10).map(|it| [it]))
.sign(account.key.clone())
.execute(&client)
.await?
.get_receipt(&client)
.await?;
assert_eq!(mint_receipt.serials.len(), 10);
token.burn(&client, mint_receipt.serials).await?;
token.delete(&client).await?;
account.delete(&client).await?;
Ok(())
}
#[tokio::test]
async fn nft_metadata_too_long_fails() -> anyhow::Result<()> {
let Some(TestEnvironment { config: _, client }) = setup_nonfree() else {
return Ok(());
};
let account = Account::create(Hbar::new(0), &client).await?;
let token_id = TokenCreateTransaction::new()
.name("ffff")
.symbol("F")
.treasury_account_id(account.id)
.admin_key(account.key.public_key())
.supply_key(account.key.public_key())
.token_type(TokenType::NonFungibleUnique)
.token_supply_type(TokenSupplyType::Finite)
.max_supply(5000)
.expiration_time(OffsetDateTime::now_utc() + Duration::minutes(5))
.sign(account.key.clone())
.execute(&client)
.await?
.get_receipt(&client)
.await?
.token_id
.unwrap();
let token = Nft { id: token_id, owner: account.clone() };
let res = TokenMintTransaction::new()
.token_id(token_id)
.metadata([[1; 101]])
.sign(account.key.clone())
.execute(&client)
.await?
.get_receipt(&client)
.await;
assert_matches!(
res,
Err(hedera::Error::ReceiptStatus { status: Status::MetadataTooLong, .. })
);
token.delete(&client).await?;
account.delete(&client).await?;
Ok(())
}