Skip to content

Commit

Permalink
HIP-904: Allow max_automatic_token_association to accept -1
Browse files Browse the repository at this point in the history
  • Loading branch information
RickyLB authored Jul 31, 2024
1 parent 6aa8a4f commit 9b4f497
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 33 deletions.
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

0 comments on commit 9b4f497

Please sign in to comment.