Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HIP-904: Allow max_automatic_token_association to accept -1 #806

Merged
merged 4 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions src/account/account_create_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ pub struct AccountCreateTransactionData {
/// The maximum number of tokens that an Account can be implicitly associated with.
///
/// Defaults to `0`. Allows up to a maximum value of `1000`.
max_automatic_token_associations: u16,
/// If the value is set to `-1`, unlimited automatic token associations are allowed.
max_automatic_token_associations: i32,

// notably *not* a PublicKey.
/// A 20-byte EVM address to be used as the account's alias.
Expand Down Expand Up @@ -201,12 +202,12 @@ impl AccountCreateTransaction {
///
/// Defaults to `0`. Allows up to a maximum value of `1000`.
#[must_use]
pub fn get_max_automatic_token_associations(&self) -> u16 {
pub fn get_max_automatic_token_associations(&self) -> i32 {
self.data().max_automatic_token_associations
}

/// Sets the maximum number of tokens that an Account can be implicitly associated with.
pub fn max_automatic_token_associations(&mut self, amount: u16) -> &mut Self {
pub fn max_automatic_token_associations(&mut self, amount: i32) -> &mut Self {
self.data_mut().max_automatic_token_associations = amount;
self
}
Expand Down Expand Up @@ -326,7 +327,7 @@ impl FromProtobuf<services::CryptoCreateTransactionBody> for AccountCreateTransa
auto_renew_period: pb.auto_renew_period.map(Into::into),
auto_renew_account_id: None,
account_memo: pb.memo,
max_automatic_token_associations: pb.max_automatic_token_associations as u16,
max_automatic_token_associations: pb.max_automatic_token_associations,
alias,
staked_id: Option::from_protobuf(pb.staked_id)?,
decline_staking_reward: pb.decline_reward,
Expand Down Expand Up @@ -410,7 +411,7 @@ mod tests {
const STAKED_ACCOUNT_ID: AccountId = AccountId::new(0, 0, 3);
const STAKED_NODE_ID: u64 = 4;
const ALIAS: EvmAddress = EvmAddress(hex!("5c562e90feaf0eebd33ea75d21024f249d451417"));
const MAX_AUTOMATIC_TOKEN_ASSOCIATIONS: u16 = 100;
const MAX_AUTOMATIC_TOKEN_ASSOCIATIONS: i32 = 100;

fn make_transaction() -> AccountCreateTransaction {
let mut tx = AccountCreateTransaction::new_for_tests();
Expand Down Expand Up @@ -699,7 +700,7 @@ mod tests {
realm_id: None,
new_realm_admin_key: None,
memo: ACCOUNT_MEMO.to_owned(),
max_automatic_token_associations: MAX_AUTOMATIC_TOKEN_ASSOCIATIONS as i32,
max_automatic_token_associations: MAX_AUTOMATIC_TOKEN_ASSOCIATIONS,
decline_reward: false,
alias: ALIAS.to_bytes().to_vec(),
staked_id: Some(services::crypto_create_transaction_body::StakedId::StakedAccountId(
Expand Down
15 changes: 7 additions & 8 deletions src/account/account_update_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ pub struct AccountUpdateTransactionData {
/// The maximum number of tokens that an Account can be implicitly associated with.
///
/// Defaults to `0`. Allows up to a maximum value of `1000`.
///
max_automatic_token_associations: Option<u16>,
/// If the value is set to `-1`, unlimited automatic token associations are allowed.
max_automatic_token_associations: Option<i32>,

/// ID of the account or node to which this account is staking, if any.
staked_id: Option<StakedId>,
Expand Down Expand Up @@ -227,12 +227,13 @@ impl AccountUpdateTransaction {

/// Returns the maximum number of tokens that an Account can be implicitly associated with.
#[must_use]
pub fn get_max_automatic_token_associations(&self) -> Option<u16> {
pub fn get_max_automatic_token_associations(&self) -> Option<i32> {
self.data().max_automatic_token_associations
}

/// Sets the maximum number of tokens that an Account can be implicitly associated with.
pub fn max_automatic_token_associations(&mut self, amount: u16) -> &mut Self {
///
pub fn max_automatic_token_associations(&mut self, amount: i32) -> &mut Self {
self.data_mut().max_automatic_token_associations = Some(amount);
self
}
Expand Down Expand Up @@ -353,9 +354,7 @@ impl FromProtobuf<services::CryptoUpdateTransactionBody> for AccountUpdateTransa
proxy_account_id: Option::from_protobuf(pb.proxy_account_id)?,
expiration_time: pb.expiration_time.map(Into::into),
account_memo: pb.memo,
max_automatic_token_associations: pb
.max_automatic_token_associations
.map(|it| it as u16),
max_automatic_token_associations: pb.max_automatic_token_associations,
staked_id: Option::from_protobuf(pb.staked_id)?,
decline_staking_reward: pb.decline_reward,
})
Expand Down Expand Up @@ -445,7 +444,7 @@ mod tests {
};

const RECEIVER_SIGNATURE_REQUIRED: bool = false;
const MAX_AUTOMATIC_TOKEN_ASSOCIATIONS: u16 = 100;
const MAX_AUTOMATIC_TOKEN_ASSOCIATIONS: i32 = 100;
const ACCOUNT_MEMO: &str = "Some memo";
const STAKED_ACCOUNT_ID: AccountId = AccountId::new(0, 0, 3);
const STAKED_NODE_ID: u64 = 4;
Expand Down
6 changes: 3 additions & 3 deletions src/contract/contract_create_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,14 +161,14 @@ impl ContractCreateFlow {

/// Retunrs the maximum number of tokens that the contract can be automatically associated with.
#[must_use]
pub fn get_max_automatic_token_associations(&self) -> u32 {
pub fn get_max_automatic_token_associations(&self) -> i32 {
self.contract_data.max_automatic_token_associations
}

/// Sets the maximum number of tokens that the contract can be automatically associated with.
pub fn max_automatic_token_associations(
&mut self,
max_automatic_token_associations: u32,
max_automatic_token_associations: i32,
) -> &mut Self {
self.contract_data.max_automatic_token_associations = max_automatic_token_associations;

Expand Down Expand Up @@ -382,7 +382,7 @@ struct ContractData {
constructor_parameters: Vec<u8>,
gas: u64,
initial_balance: Hbar,
max_automatic_token_associations: u32,
max_automatic_token_associations: i32,
decline_staking_reward: bool,
admin_key: Option<Key>,
// proxy_account_id: Option<AccountId>
Expand Down
14 changes: 7 additions & 7 deletions src/contract/contract_create_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ pub struct ContractCreateTransactionData {

contract_memo: String,

max_automatic_token_associations: u32,
max_automatic_token_associations: i32,

auto_renew_account_id: Option<AccountId>,

Expand Down Expand Up @@ -196,12 +196,12 @@ impl ContractCreateTransaction {

/// Returns the maximum number of tokens that the contract can be automatically associated with.
#[must_use]
pub fn get_max_automatic_token_associations(&self) -> u32 {
pub fn get_max_automatic_token_associations(&self) -> i32 {
self.data().max_automatic_token_associations
}

/// Sets the maximum number of tokens that this contract can be automatically associated with.
pub fn max_automatic_token_associations(&mut self, max: u32) -> &mut Self {
pub fn max_automatic_token_associations(&mut self, max: i32) -> &mut Self {
self.data_mut().max_automatic_token_associations = max;
self
}
Expand Down Expand Up @@ -327,7 +327,7 @@ impl FromProtobuf<services::ContractCreateTransactionBody> for ContractCreateTra
auto_renew_period: pb_getf!(pb, auto_renew_period)?.into(),
constructor_parameters: pb.constructor_parameters,
contract_memo: pb.memo,
max_automatic_token_associations: pb.max_automatic_token_associations as u32,
max_automatic_token_associations: pb.max_automatic_token_associations,
auto_renew_account_id: Option::from_protobuf(pb.auto_renew_account_id)?,
staked_id: Option::from_protobuf(pb.staked_id)?,
decline_staking_reward: pb.decline_reward,
Expand Down Expand Up @@ -385,7 +385,7 @@ impl ToProtobuf for ContractCreateTransactionData {
realm_id: None,
new_realm_admin_key: None,
memo: self.contract_memo.clone(),
max_automatic_token_associations: self.max_automatic_token_associations as i32,
max_automatic_token_associations: self.max_automatic_token_associations,
auto_renew_account_id,
decline_reward: self.decline_staking_reward,
initcode_source,
Expand Down Expand Up @@ -429,7 +429,7 @@ mod tests {

const GAS: u64 = 0;
const INITIAL_BALANCE: Hbar = Hbar::from_tinybars(1000);
const MAX_AUTOMATIC_TOKEN_ASSOCIATIONS: u32 = 101;
const MAX_AUTOMATIC_TOKEN_ASSOCIATIONS: i32 = 101;
const AUTO_RENEW_PERIOD: Duration = Duration::hours(10);
const CONSTRUCTOR_PARAMETERS: [u8; 5] = [10, 11, 12, 13, 25];
const AUTO_RENEW_ACCOUNT_ID: AccountId = AccountId::new(0, 0, 30);
Expand Down Expand Up @@ -729,7 +729,7 @@ mod tests {
realm_id: None,
new_realm_admin_key: None,
memo: String::new(),
max_automatic_token_associations: MAX_AUTOMATIC_TOKEN_ASSOCIATIONS as i32,
max_automatic_token_associations: MAX_AUTOMATIC_TOKEN_ASSOCIATIONS,
decline_reward: false,
staked_id: Some(services::contract_create_transaction_body::StakedId::StakedAccountId(
STAKED_ACCOUNT_ID.to_protobuf(),
Expand Down
14 changes: 6 additions & 8 deletions src/contract/contract_update_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ pub struct ContractUpdateTransactionData {

contract_memo: Option<String>,

max_automatic_token_associations: Option<u32>,
max_automatic_token_associations: Option<i32>,

auto_renew_account_id: Option<AccountId>,

Expand Down Expand Up @@ -138,12 +138,12 @@ impl ContractUpdateTransaction {

/// Returns the maximum number of tokens that this contract can be automatically associated with.
#[must_use]
pub fn get_max_automatic_token_associations(&self) -> Option<u32> {
pub fn get_max_automatic_token_associations(&self) -> Option<i32> {
self.data().max_automatic_token_associations
}

/// Sets the maximum number of tokens that this contract can be automatically associated with.
pub fn max_automatic_token_associations(&mut self, max: u32) -> &mut Self {
pub fn max_automatic_token_associations(&mut self, max: i32) -> &mut Self {
self.data_mut().max_automatic_token_associations = Some(max);
self
}
Expand Down Expand Up @@ -268,9 +268,7 @@ impl FromProtobuf<services::ContractUpdateTransactionBody> for ContractUpdateTra
contract_memo: pb.memo_field.map(|it| match it {
MemoField::Memo(it) | MemoField::MemoWrapper(it) => it,
}),
max_automatic_token_associations: pb
.max_automatic_token_associations
.map(|it| it as u32),
max_automatic_token_associations: pb.max_automatic_token_associations,
auto_renew_account_id: Option::from_protobuf(pb.auto_renew_account_id)?,
proxy_account_id: Option::from_protobuf(pb.proxy_account_id)?,
staked_id: Option::from_protobuf(pb.staked_id)?,
Expand Down Expand Up @@ -365,7 +363,7 @@ mod tests {

const CONTRACT_ID: ContractId = ContractId::new(0, 0, 5007);

const MAX_AUTOMATIC_TOKEN_ASSOCIATIONS: u32 = 101;
const MAX_AUTOMATIC_TOKEN_ASSOCIATIONS: i32 = 101;
const AUTO_RENEW_PERIOD: Duration = Duration::days(1);
const CONTRACT_MEMO: &str = "3";
const EXPIRATION_TIME: OffsetDateTime =
Expand Down Expand Up @@ -693,7 +691,7 @@ mod tests {
admin_key: Some(admin_key().to_protobuf()),
proxy_account_id: Some(PROXY_ACCOUNT_ID.to_protobuf()),
auto_renew_period: Some(AUTO_RENEW_PERIOD.to_protobuf()),
max_automatic_token_associations: Some(MAX_AUTOMATIC_TOKEN_ASSOCIATIONS as _),
max_automatic_token_associations: Some(MAX_AUTOMATIC_TOKEN_ASSOCIATIONS),
auto_renew_account_id: Some(AUTO_RENEW_ACCOUNT_ID.to_protobuf()),
decline_reward: None,
memo_field: Some(services::contract_update_transaction_body::MemoField::MemoWrapper(
Expand Down
26 changes: 26 additions & 0 deletions tests/e2e/account/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,3 +389,29 @@ async fn alias_with_receiver_sig_required_missing_signature_fails() -> anyhow::R

Ok(())
}

#[tokio::test]
async fn cannot_create_account_with_invalid_negative_max_auto_token_assocation(
) -> anyhow::Result<()> {
let Some(TestEnvironment { config: _, client }) = setup_nonfree() else {
return Ok(());
};

let key = PrivateKey::generate_ed25519();

let res = AccountCreateTransaction::new()
.key(key.public_key())
.max_automatic_token_associations(-2)
.execute(&client)
.await;

assert_matches::assert_matches!(
res,
Err(hedera::Error::TransactionPreCheckStatus {
status: hedera::Status::InvalidMaxAutoAssociations,
..
})
);

Ok(())
}
66 changes: 66 additions & 0 deletions tests/e2e/account/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use hedera::{
Hbar,
Key,
PrivateKey,
TokenCreateTransaction,
TransferTransaction,
};
use time::Duration;

Expand Down Expand Up @@ -84,3 +86,67 @@ async fn missing_account_id_fails() -> anyhow::Result<()> {

Ok(())
}

#[tokio::test]
async fn cannot_update_max_token_association_to_lower_value_fails() -> anyhow::Result<()> {
let Some(TestEnvironment { config: _, client }) = setup_nonfree() else {
return Ok(());
};

let account_key = PrivateKey::generate_ed25519();

// Create account with max token associations of 1
let account_id = AccountCreateTransaction::new()
.key(account_key.public_key())
.max_automatic_token_associations(1)
.execute(&client)
.await?
.get_receipt(&client)
.await?
.account_id
.unwrap();

// Create token
let token_id = TokenCreateTransaction::new()
.name("ffff")
.symbol("F")
.initial_supply(100_000)
.treasury_account_id(client.get_operator_account_id().unwrap())
.admin_key(client.get_operator_public_key().unwrap())
.execute(&client)
.await?
.get_receipt(&client)
.await?
.token_id
.unwrap();

// Associate token with account
_ = TransferTransaction::new()
.token_transfer(token_id, client.get_operator_account_id().unwrap(), -10)
.token_transfer(token_id, account_id, 10)
.execute(&client)
.await?
.get_receipt(&client)
.await?;

// Update account max token associations to 0
let res = AccountUpdateTransaction::new()
.account_id(account_id)
.max_automatic_token_associations(0)
.freeze_with(&client)?
.sign(account_key)
.execute(&client)
.await?
.get_receipt(&client)
.await;

assert_matches::assert_matches!(
res,
Err(hedera::Error::ReceiptStatus {
status: hedera::Status::ExistingAutomaticAssociationsExceedGivenLimit,
..
})
);

Ok(())
}
Loading
Loading