From e807122a50bf8954f2261eb82bd71a764dcd6fa5 Mon Sep 17 00:00:00 2001 From: Ivaylo Nikolov Date: Mon, 8 Jul 2024 17:44:02 +0300 Subject: [PATCH 1/5] test: add max auto associate tests Signed-off-by: Ivaylo Nikolov --- .../TokenAssociateIntegrationTest.js | 744 +++++++++++++++++- 1 file changed, 742 insertions(+), 2 deletions(-) diff --git a/test/integration/TokenAssociateIntegrationTest.js b/test/integration/TokenAssociateIntegrationTest.js index 56bfe956b..ffc2f008d 100644 --- a/test/integration/TokenAssociateIntegrationTest.js +++ b/test/integration/TokenAssociateIntegrationTest.js @@ -1,12 +1,19 @@ import { + AccountAllowanceApproveTransaction, AccountBalanceQuery, AccountCreateTransaction, - AccountInfoQuery, + AccountUpdateTransaction, Hbar, + NftId, + AccountInfoQuery, PrivateKey, Status, TokenAssociateTransaction, TokenCreateTransaction, + TokenMintTransaction, + TokenType, + TransactionId, + TransferTransaction, } from "../../src/exports.js"; import IntegrationTestEnv from "./client/NodeIntegrationTestEnv.js"; @@ -14,7 +21,7 @@ describe("TokenAssociate", function () { let env; before(async function () { - env = await IntegrationTestEnv.new(); + env = await IntegrationTestEnv.new({ balance: 1000 }); }); it("should be executable", async function () { @@ -129,6 +136,739 @@ describe("TokenAssociate", function () { } }); + describe("Max Auto Associations", function () { + let receiverKey, receiverId; + const TOKEN_SUPPLY = 100; + + beforeEach(async function () { + receiverKey = PrivateKey.generateECDSA(); + const receiverAccountCreateTx = await new AccountCreateTransaction() + .setKey(receiverKey) + .freezeWith(env.client) + .sign(receiverKey); + receiverId = ( + await ( + await receiverAccountCreateTx.execute(env.client) + ).getReceipt(env.client) + ).accountId; + }); + + describe("Limited Auto Associations", function () { + it("should revert FT transfer when no auto associations left", async function () { + this.timeout(12000); + // update account to have one auto association + const accountUpdateTx = await new AccountUpdateTransaction() + .setAccountId(receiverId) + .setMaxAutomaticTokenAssociations(1) + .freezeWith(env.client) + .sign(receiverKey); + + await ( + await accountUpdateTx.execute(env.client) + ).getReceipt(env.client); + + const tokenCreateTransaction = + await new TokenCreateTransaction() + .setTokenType(TokenType.FungibleCommon) + .setTokenName("FFFFF") + .setTokenSymbol("ffff") + .setInitialSupply(TOKEN_SUPPLY) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setFreezeKey(env.operatorKey) + .setWipeKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId } = await tokenCreateTransaction.getReceipt( + env.client, + ); + + const tokenCreateTransaction2 = + await new TokenCreateTransaction() + .setTokenType(TokenType.FungibleCommon) + .setTokenName("FFFFF") + .setTokenSymbol("ffff") + .setInitialSupply(TOKEN_SUPPLY) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setFreezeKey(env.operatorKey) + .setWipeKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId: tokenId2 } = + await tokenCreateTransaction2.getReceipt(env.client); + + const sendTokenToReceiverTx = await new TransferTransaction() + .addTokenTransfer(tokenId, env.operatorId, -TOKEN_SUPPLY) + .addTokenTransfer(tokenId, receiverId, TOKEN_SUPPLY) + .execute(env.client); + + await sendTokenToReceiverTx.getReceipt(env.client); + + const sendTokenToReceiverTx2 = await new TransferTransaction() + .addTokenTransfer(tokenId2, env.operatorId, -TOKEN_SUPPLY) + .addTokenTransfer(tokenId2, receiverId, TOKEN_SUPPLY) + .freezeWith(env.client) + .execute(env.client); + + let err = false; + + try { + await sendTokenToReceiverTx2.getReceipt(env.client); + } catch (error) { + err = error + .toString() + .includes(Status.NoRemainingAutomaticAssociations); + } + + if (!err) { + throw new Error( + "Token transfer did not error with NO_REMAINING_AUTOMATIC_ASSOCIATIONS", + ); + } + }); + + it("should revert NFTs transfer when no auto associations left", async function () { + this.timeout(120000); + const accountUpdateTx = await new AccountUpdateTransaction() + .setAccountId(receiverId) + .setMaxAutomaticTokenAssociations(1) + .freezeWith(env.client) + .sign(receiverKey); + + await ( + await accountUpdateTx.execute(env.client) + ).getReceipt(env.client); + + // create token 1 + const tokenCreateTransaction = + await new TokenCreateTransaction() + .setTokenType(TokenType.NonFungibleUnique) + .setTokenName("FFFFF") + .setTokenSymbol("ffff") + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId } = await tokenCreateTransaction.getReceipt( + env.client, + ); + + // mint a token in token 1 + const tokenMintSignedTransaction = + await new TokenMintTransaction() + .setTokenId(tokenId) + .setMetadata([Buffer.from("-")]) + .execute(env.client); + + await tokenMintSignedTransaction.getReceipt(env.client); + + // transfer the token to receiver + + const transferTxSign = await new TransferTransaction() + .addNftTransfer(tokenId, 1, env.operatorId, receiverId) + .execute(env.client); + + await transferTxSign.getReceipt(env.client); + + // create token 2 + const tokenCreateTransaction2 = + await new TokenCreateTransaction() + .setTokenType(TokenType.NonFungibleUnique) + .setTokenName("FFFFF") + .setTokenSymbol("ffff") + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId: tokenId2 } = + await tokenCreateTransaction2.getReceipt(env.client); + + // mint token 2 + const tokenMintSignedTransaction2 = + await new TokenMintTransaction() + .setTokenId(tokenId2) + .addMetadata(Buffer.from("-")) + .execute(env.client); + + await tokenMintSignedTransaction2.getReceipt(env.client); + + let err = false; + + try { + const transferToken2Response = + await new TransferTransaction() + .addNftTransfer( + tokenId2, + 1, + env.operatorId, + receiverId, + ) + .execute(env.client); + + await transferToken2Response.getReceipt(env.client); + } catch (error) { + err = error + .toString() + .includes(Status.NoRemainingAutomaticAssociations); + } + + if (!err) { + throw new Error( + "Token transfer did not error with NO_REMAINING_AUTOMATIC_ASSOCIATIONS", + ); + } + }); + + it("should contain sent balance when transfering FT to account with manual token association", async function () { + this.timeout(12000); + const tokenCreateTransaction = + await new TokenCreateTransaction() + .setTokenType(TokenType.FungibleCommon) + .setTokenName("FFFFF") + .setTokenSymbol("ffff") + .setInitialSupply(TOKEN_SUPPLY) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setFreezeKey(env.operatorKey) + .setWipeKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId } = await tokenCreateTransaction.getReceipt( + env.client, + ); + + const tokenAssociateTransaction = + await new TokenAssociateTransaction() + .setAccountId(receiverId) + .setTokenIds([tokenId]) + .freezeWith(env.client) + .sign(receiverKey); + + await ( + await tokenAssociateTransaction.execute(env.client) + ).getReceipt(env.client); + + const tokenMintTx = await new TokenMintTransaction() + .setTokenId(tokenId) + .freezeWith(env.client) + .sign(env.operatorKey); + + await ( + await tokenMintTx.execute(env.client) + ).getReceipt(env.client); + + const sendTokenToReceiverTx = await new TransferTransaction() + .addTokenTransfer(tokenId, env.operatorId, -TOKEN_SUPPLY) + .addTokenTransfer(tokenId, receiverId, TOKEN_SUPPLY) + .execute(env.client); + + await sendTokenToReceiverTx.getReceipt(env.client); + + const tokenBalance = await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client); + + expect(tokenBalance.tokens.get(tokenId).toInt()).to.be.equal( + TOKEN_SUPPLY, + ); + }); + + it("should contain sent balance when transfering NFT to account with manual token association", async function () { + this.timeout(12000); + const tokenCreateTransaction = + await new TokenCreateTransaction() + .setTokenType(TokenType.NonFungibleUnique) + .setTokenName("FFFFF") + .setTokenSymbol("ffff") + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId } = await tokenCreateTransaction.getReceipt( + env.client, + ); + + const tokenAssociateTransaction = + await new TokenAssociateTransaction() + .setAccountId(receiverId) + .setTokenIds([tokenId]) + .freezeWith(env.client) + .sign(receiverKey); + + await ( + await tokenAssociateTransaction.execute(env.client) + ).getReceipt(env.client); + + const tokenMintTx = await new TokenMintTransaction() + .setTokenId(tokenId) + .setMetadata([Buffer.from("-")]) + .freezeWith(env.client) + .sign(env.operatorKey); + + await ( + await tokenMintTx.execute(env.client) + ).getReceipt(env.client); + + const sendTokenToReceiverTx = await new TransferTransaction() + .addNftTransfer(tokenId, 1, env.operatorId, receiverId) + .execute(env.client); + + await sendTokenToReceiverTx.getReceipt(env.client); + + const tokenBalance = await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client); + + expect(tokenBalance.tokens.get(tokenId).toInt()).to.be.equal(1); + }); + }); + + describe("Unlimited Auto Associations", function () { + it("receiver should contain FTs when transfering to account with unlimited auto associations", async function () { + this.timeout(12000); + const tokenCreateResponse = await new TokenCreateTransaction() + .setTokenType(TokenType.FungibleCommon) + .setTokenName("ffff") + .setTokenSymbol("F") + .setInitialSupply(TOKEN_SUPPLY) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setFreezeKey(env.operatorKey) + .setWipeKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId } = await tokenCreateResponse.getReceipt( + env.client, + ); + + const tokenCreateResponse2 = await new TokenCreateTransaction() + .setTokenType(TokenType.FungibleCommon) + .setTokenName("ffff") + .setTokenSymbol("F") + .setInitialSupply(TOKEN_SUPPLY) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setFreezeKey(env.operatorKey) + .setWipeKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId: tokenId2 } = + await tokenCreateResponse2.getReceipt(env.client); + + const updateUnlimitedAutomaticAssociations = + await new AccountUpdateTransaction() + .setAccountId(receiverId) + .setMaxAutomaticTokenAssociations(-1) + .freezeWith(env.client) + .sign(receiverKey); + + await ( + await updateUnlimitedAutomaticAssociations.execute( + env.client, + ) + ).getReceipt(env.client); + + const tokenTransferResponse = await new TransferTransaction() + .addTokenTransfer(tokenId, env.operatorId, -TOKEN_SUPPLY) + .addTokenTransfer(tokenId, receiverId, TOKEN_SUPPLY) + .execute(env.client); + + await tokenTransferResponse.getReceipt(env.client); + + const tokenTransferResponse2 = await new TransferTransaction() + .addTokenTransfer(tokenId2, env.operatorId, -TOKEN_SUPPLY) + .addTokenTransfer(tokenId2, receiverId, TOKEN_SUPPLY) + .execute(env.client); + + await tokenTransferResponse2.getReceipt(env.client); + + const newTokenBalance = ( + await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client) + ).tokens.get(tokenId); + + const newTokenBalance2 = ( + await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client) + ).tokens.get(tokenId2); + + expect(newTokenBalance.toInt()).to.equal(TOKEN_SUPPLY); + expect(newTokenBalance2.toInt()).to.equal(TOKEN_SUPPLY); + }); + + it("receiver should contain NFTs when transfering to account with unlimited auto associations", async function () { + this.timeout(12000); + const tokenCreateResponse = await new TokenCreateTransaction() + .setTokenType(TokenType.NonFungibleUnique) + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId } = await tokenCreateResponse.getReceipt( + env.client, + ); + + const tokenCreateResponse2 = await new TokenCreateTransaction() + .setTokenType(TokenType.NonFungibleUnique) + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId: tokenId2 } = + await tokenCreateResponse2.getReceipt(env.client); + + const mintTokenTx = await new TokenMintTransaction() + .setTokenId(tokenId) + .setMetadata([Buffer.from("-")]) + .execute(env.client); + + await mintTokenTx.getReceipt(env.client); + + const mintTokenTx2 = await new TokenMintTransaction() + .setTokenId(tokenId2) + .setMetadata([Buffer.from("-")]) + .execute(env.client); + + await mintTokenTx2.getReceipt(env.client); + + const updateUnlimitedAutomaticAssociations = + await new AccountUpdateTransaction() + .setAccountId(receiverId) + .setMaxAutomaticTokenAssociations(-1) + .freezeWith(env.client) + .sign(receiverKey); + + await ( + await updateUnlimitedAutomaticAssociations.execute( + env.client, + ) + ).getReceipt(env.client); + + const tokenTransferResponse = await new TransferTransaction() + .addNftTransfer(tokenId, 1, env.operatorId, receiverId) + .execute(env.client); + + await tokenTransferResponse.getReceipt(env.client); + + const tokenTransferResponse2 = await new TransferTransaction() + .addNftTransfer(tokenId2, 1, env.operatorId, receiverId) + .execute(env.client); + + await tokenTransferResponse2.getReceipt(env.client); + + const newTokenBalance = ( + await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client) + ).tokens.get(tokenId); + + const newTokenBalance2 = ( + await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client) + ).tokens.get(tokenId2); + + expect(newTokenBalance.toInt()).to.equal(1); + expect(newTokenBalance2.toInt()).to.equal(1); + }); + + it("receiver should have token balance even if it has given allowance to spender", async function () { + this.timeout(12000); + const spenderKey = PrivateKey.generateECDSA(); + + const unlimitedAutoAssociationTx = + await new AccountUpdateTransaction() + .setAccountId(receiverId) + .setMaxAutomaticTokenAssociations(-1) + .freezeWith(env.client) + .sign(receiverKey); + + await ( + await unlimitedAutoAssociationTx.execute(env.client) + ).getReceipt(env.client); + + const spenderAccountCreateTx = + await new AccountCreateTransaction() + .setKey(spenderKey) + .setInitialBalance(new Hbar(1)) + .execute(env.client); + + const spenderId = ( + await spenderAccountCreateTx.getReceipt(env.client) + ).accountId; + + const tokenCreateResponse = await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setInitialSupply(TOKEN_SUPPLY) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId } = await tokenCreateResponse.getReceipt( + env.client, + ); + + const tokenTransferHalfSupply = await new TransferTransaction() + .addTokenTransfer(tokenId, env.operatorId, -TOKEN_SUPPLY) + .addTokenTransfer(tokenId, receiverId, TOKEN_SUPPLY) + .execute(env.client); + + await tokenTransferHalfSupply.getReceipt(env.client); + + const tokenAllowanceTx = + await new AccountAllowanceApproveTransaction() + .approveTokenAllowance( + tokenId, + receiverId, + spenderId, + 1, + ) + .freezeWith(env.client) + .sign(receiverKey); + + await ( + await tokenAllowanceTx.execute(env.client) + ).getReceipt(env.client); + + const tokenBalanceReceiver = await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client); + + const tokenBalanceSpender = await new AccountBalanceQuery() + .setAccountId(spenderId) + .execute(env.client); + + const tokenBalanceTreasury = await new AccountBalanceQuery() + .setAccountId(env.operatorId) + .execute(env.client); + + expect( + tokenBalanceReceiver.tokens.get(tokenId).toInt(), + ).to.equal(TOKEN_SUPPLY); + + expect(tokenBalanceSpender.tokens.get(tokenId)).to.equal(null); + + expect( + tokenBalanceTreasury.tokens.get(tokenId).toInt(), + ).to.equal(0); + }); + + it("receiver should have nft even if it has given allowance to spender", async function () { + this.timeout(12000); + const spenderKey = PrivateKey.generateECDSA(); + + const unlimitedAutoAssociationReceiverTx = + await new AccountUpdateTransaction() + .setAccountId(receiverId) + .setMaxAutomaticTokenAssociations(-1) + .freezeWith(env.client) + .sign(receiverKey); + + await ( + await unlimitedAutoAssociationReceiverTx.execute(env.client) + ).getReceipt(env.client); + + const spenderAccountCreateTx = + await new AccountCreateTransaction() + .setKey(spenderKey) + .setInitialBalance(new Hbar(1)) + .setMaxAutomaticTokenAssociations(-1) + .execute(env.client); + + const spenderId = ( + await spenderAccountCreateTx.getReceipt(env.client) + ).accountId; + + const tokenCreateResponse = await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId } = await tokenCreateResponse.getReceipt( + env.client, + ); + + await ( + await new TokenMintTransaction() + .setTokenId(tokenId) + .setMetadata([Buffer.from("-")]) + .execute(env.client) + ).getReceipt(env.client); + + const nftId = new NftId(tokenId, 1); + const nftAllowanceTx = + await new AccountAllowanceApproveTransaction() + .approveTokenNftAllowance( + nftId, + env.operatorId, + spenderId, + ) + .execute(env.client); + + await nftAllowanceTx.getReceipt(env.client); + + // Generate TransactionId from spender's account id in order + // for the transaction to be to be executed on behalf of the spender + const onBehalfOfTransactionId = + TransactionId.generate(spenderId); + + const nftTransferToReceiver = await new TransferTransaction() + .addApprovedNftTransfer(nftId, env.operatorId, receiverId) + .setTransactionId(onBehalfOfTransactionId) + .freezeWith(env.client) + .sign(spenderKey); + + await ( + await nftTransferToReceiver.execute(env.client) + ).getReceipt(env.client); + + const tokenBalanceReceiver = await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client); + + const tokenBalanceSpender = await new AccountBalanceQuery() + .setAccountId(spenderId) + .execute(env.client); + + const tokenBalanceTreasury = await new AccountBalanceQuery() + .setAccountId(env.operatorId) + .execute(env.client); + + expect( + tokenBalanceReceiver.tokens.get(tokenId).toInt(), + ).to.equal(1); + + expect(tokenBalanceSpender.tokens.get(tokenId)).to.equal(null); + + expect( + tokenBalanceTreasury.tokens.get(tokenId).toInt(), + ).to.equal(0); + }); + + it("receiver with unlimited auto associations should have FTs with decimal when sender transfers FTs", async function () { + const tokenCreateResponse = await new TokenCreateTransaction() + .setTokenType(TokenType.FungibleCommon) + .setTokenName("FFFFFFF") + .setTokenSymbol("fff") + .setDecimals(3) + .setInitialSupply(TOKEN_SUPPLY) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setFreezeKey(env.operatorKey) + .setWipeKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId } = await tokenCreateResponse.getReceipt( + env.client, + ); + + const receiverKey = PrivateKey.generateECDSA(); + const receiverAccountResponse = + await new AccountCreateTransaction() + .setKey(receiverKey) + .setMaxAutomaticTokenAssociations(-1) + .setInitialBalance(new Hbar(1)) + .execute(env.client); + + const { accountId: receiverAccountId } = + await receiverAccountResponse.getReceipt(env.client); + + await ( + await new TokenAssociateTransaction() + .setAccountId(receiverAccountId) + .setTokenIds([tokenId]) + .freezeWith(env.client) + .sign(receiverKey) + ).execute(env.client); + + const tokenTransferResponse = await new TransferTransaction() + .addTokenTransfer(tokenId, env.operatorId, -TOKEN_SUPPLY) + .addTokenTransfer(tokenId, receiverAccountId, TOKEN_SUPPLY) + .execute(env.client); + + await tokenTransferResponse.getReceipt(env.client); + + const receiverBalance = ( + await new AccountBalanceQuery() + .setAccountId(receiverAccountId) + .execute(env.client) + ).tokens + .get(tokenId) + .toInt(); + + expect(receiverBalance).to.equal(TOKEN_SUPPLY); + }); + + it("should revert when auto association is set to <-1", async function () { + let err = false; + + try { + const accountUpdateTx = await new AccountUpdateTransaction() + .setAccountId(receiverId) + .setMaxAutomaticTokenAssociations(-2) + .freezeWith(env.client) + .sign(receiverKey); + await ( + await accountUpdateTx.execute(env.client) + ).getReceipt(env.client); + } catch (error) { + err = error + .toString() + .includes(Status.InvalidMaxAutoAssociations); + } + + if (!err) { + throw new Error("Token association did not error"); + } + + try { + const key = PrivateKey.generateECDSA(); + const accountCreateInvalidAutoAssociation = + await new AccountCreateTransaction() + .setKey(key) + .setMaxAutomaticTokenAssociations(-2) + .execute(env.client); + + await accountCreateInvalidAutoAssociation.getReceipt( + env.client, + ); + } catch (error) { + err = error + .toString() + .includes(Status.InvalidMaxAutoAssociations); + } + + if (!err) { + throw new Error("Token association did not error"); + } + }); + }); + }); + after(async function () { await env.close(); }); From 7f1ff5ebd01b4b24d6f42b66a42ebe0ea4d5bdeb Mon Sep 17 00:00:00 2001 From: Ivaylo Nikolov Date: Tue, 9 Jul 2024 12:29:54 +0300 Subject: [PATCH 2/5] test(style): timeout consistency Signed-off-by: Ivaylo Nikolov --- test/integration/TokenAssociateIntegrationTest.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/integration/TokenAssociateIntegrationTest.js b/test/integration/TokenAssociateIntegrationTest.js index ffc2f008d..267d4556f 100644 --- a/test/integration/TokenAssociateIntegrationTest.js +++ b/test/integration/TokenAssociateIntegrationTest.js @@ -155,7 +155,7 @@ describe("TokenAssociate", function () { describe("Limited Auto Associations", function () { it("should revert FT transfer when no auto associations left", async function () { - this.timeout(12000); + this.timeout(120000); // update account to have one auto association const accountUpdateTx = await new AccountUpdateTransaction() .setAccountId(receiverId) @@ -325,7 +325,7 @@ describe("TokenAssociate", function () { }); it("should contain sent balance when transfering FT to account with manual token association", async function () { - this.timeout(12000); + this.timeout(120000); const tokenCreateTransaction = await new TokenCreateTransaction() .setTokenType(TokenType.FungibleCommon) @@ -380,7 +380,7 @@ describe("TokenAssociate", function () { }); it("should contain sent balance when transfering NFT to account with manual token association", async function () { - this.timeout(12000); + this.timeout(120000); const tokenCreateTransaction = await new TokenCreateTransaction() .setTokenType(TokenType.NonFungibleUnique) @@ -432,7 +432,7 @@ describe("TokenAssociate", function () { describe("Unlimited Auto Associations", function () { it("receiver should contain FTs when transfering to account with unlimited auto associations", async function () { - this.timeout(12000); + this.timeout(120000); const tokenCreateResponse = await new TokenCreateTransaction() .setTokenType(TokenType.FungibleCommon) .setTokenName("ffff") @@ -508,7 +508,7 @@ describe("TokenAssociate", function () { }); it("receiver should contain NFTs when transfering to account with unlimited auto associations", async function () { - this.timeout(12000); + this.timeout(120000); const tokenCreateResponse = await new TokenCreateTransaction() .setTokenType(TokenType.NonFungibleUnique) .setTokenName("ffff") @@ -590,7 +590,7 @@ describe("TokenAssociate", function () { }); it("receiver should have token balance even if it has given allowance to spender", async function () { - this.timeout(12000); + this.timeout(120000); const spenderKey = PrivateKey.generateECDSA(); const unlimitedAutoAssociationTx = @@ -673,7 +673,7 @@ describe("TokenAssociate", function () { }); it("receiver should have nft even if it has given allowance to spender", async function () { - this.timeout(12000); + this.timeout(120000); const spenderKey = PrivateKey.generateECDSA(); const unlimitedAutoAssociationReceiverTx = From 89af7d711645d3a02bc13db1b62d6877563d2115 Mon Sep 17 00:00:00 2001 From: Ivaylo Nikolov Date: Tue, 9 Jul 2024 12:44:13 +0300 Subject: [PATCH 3/5] test: using transfer_amount const instead of total_supply when transfering tokens Signed-off-by: Ivaylo Nikolov --- .../TokenAssociateIntegrationTest.js | 55 ++++++++++++------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/test/integration/TokenAssociateIntegrationTest.js b/test/integration/TokenAssociateIntegrationTest.js index 267d4556f..2b41821dc 100644 --- a/test/integration/TokenAssociateIntegrationTest.js +++ b/test/integration/TokenAssociateIntegrationTest.js @@ -138,7 +138,8 @@ describe("TokenAssociate", function () { describe("Max Auto Associations", function () { let receiverKey, receiverId; - const TOKEN_SUPPLY = 100; + const TOKEN_SUPPLY = 100, + TRANSFER_AMOUNT = 10; beforeEach(async function () { receiverKey = PrivateKey.generateECDSA(); @@ -201,15 +202,19 @@ describe("TokenAssociate", function () { await tokenCreateTransaction2.getReceipt(env.client); const sendTokenToReceiverTx = await new TransferTransaction() - .addTokenTransfer(tokenId, env.operatorId, -TOKEN_SUPPLY) - .addTokenTransfer(tokenId, receiverId, TOKEN_SUPPLY) + .addTokenTransfer(tokenId, env.operatorId, -TRANSFER_AMOUNT) + .addTokenTransfer(tokenId, receiverId, TRANSFER_AMOUNT) .execute(env.client); await sendTokenToReceiverTx.getReceipt(env.client); const sendTokenToReceiverTx2 = await new TransferTransaction() - .addTokenTransfer(tokenId2, env.operatorId, -TOKEN_SUPPLY) - .addTokenTransfer(tokenId2, receiverId, TOKEN_SUPPLY) + .addTokenTransfer( + tokenId2, + env.operatorId, + -TRANSFER_AMOUNT, + ) + .addTokenTransfer(tokenId2, receiverId, TRANSFER_AMOUNT) .freezeWith(env.client) .execute(env.client); @@ -364,8 +369,8 @@ describe("TokenAssociate", function () { ).getReceipt(env.client); const sendTokenToReceiverTx = await new TransferTransaction() - .addTokenTransfer(tokenId, env.operatorId, -TOKEN_SUPPLY) - .addTokenTransfer(tokenId, receiverId, TOKEN_SUPPLY) + .addTokenTransfer(tokenId, env.operatorId, -TRANSFER_AMOUNT) + .addTokenTransfer(tokenId, receiverId, TRANSFER_AMOUNT) .execute(env.client); await sendTokenToReceiverTx.getReceipt(env.client); @@ -375,7 +380,7 @@ describe("TokenAssociate", function () { .execute(env.client); expect(tokenBalance.tokens.get(tokenId).toInt()).to.be.equal( - TOKEN_SUPPLY, + TRANSFER_AMOUNT, ); }); @@ -478,15 +483,19 @@ describe("TokenAssociate", function () { ).getReceipt(env.client); const tokenTransferResponse = await new TransferTransaction() - .addTokenTransfer(tokenId, env.operatorId, -TOKEN_SUPPLY) - .addTokenTransfer(tokenId, receiverId, TOKEN_SUPPLY) + .addTokenTransfer(tokenId, env.operatorId, -TRANSFER_AMOUNT) + .addTokenTransfer(tokenId, receiverId, TRANSFER_AMOUNT) .execute(env.client); await tokenTransferResponse.getReceipt(env.client); const tokenTransferResponse2 = await new TransferTransaction() - .addTokenTransfer(tokenId2, env.operatorId, -TOKEN_SUPPLY) - .addTokenTransfer(tokenId2, receiverId, TOKEN_SUPPLY) + .addTokenTransfer( + tokenId2, + env.operatorId, + -TRANSFER_AMOUNT, + ) + .addTokenTransfer(tokenId2, receiverId, TRANSFER_AMOUNT) .execute(env.client); await tokenTransferResponse2.getReceipt(env.client); @@ -503,8 +512,8 @@ describe("TokenAssociate", function () { .execute(env.client) ).tokens.get(tokenId2); - expect(newTokenBalance.toInt()).to.equal(TOKEN_SUPPLY); - expect(newTokenBalance2.toInt()).to.equal(TOKEN_SUPPLY); + expect(newTokenBalance.toInt()).to.equal(TRANSFER_AMOUNT); + expect(newTokenBalance2.toInt()).to.equal(TRANSFER_AMOUNT); }); it("receiver should contain NFTs when transfering to account with unlimited auto associations", async function () { @@ -628,8 +637,8 @@ describe("TokenAssociate", function () { ); const tokenTransferHalfSupply = await new TransferTransaction() - .addTokenTransfer(tokenId, env.operatorId, -TOKEN_SUPPLY) - .addTokenTransfer(tokenId, receiverId, TOKEN_SUPPLY) + .addTokenTransfer(tokenId, env.operatorId, -TRANSFER_AMOUNT) + .addTokenTransfer(tokenId, receiverId, TRANSFER_AMOUNT) .execute(env.client); await tokenTransferHalfSupply.getReceipt(env.client); @@ -663,13 +672,13 @@ describe("TokenAssociate", function () { expect( tokenBalanceReceiver.tokens.get(tokenId).toInt(), - ).to.equal(TOKEN_SUPPLY); + ).to.equal(TRANSFER_AMOUNT); expect(tokenBalanceSpender.tokens.get(tokenId)).to.equal(null); expect( tokenBalanceTreasury.tokens.get(tokenId).toInt(), - ).to.equal(0); + ).to.equal(TOKEN_SUPPLY - TRANSFER_AMOUNT); }); it("receiver should have nft even if it has given allowance to spender", async function () { @@ -806,8 +815,12 @@ describe("TokenAssociate", function () { ).execute(env.client); const tokenTransferResponse = await new TransferTransaction() - .addTokenTransfer(tokenId, env.operatorId, -TOKEN_SUPPLY) - .addTokenTransfer(tokenId, receiverAccountId, TOKEN_SUPPLY) + .addTokenTransfer(tokenId, env.operatorId, -TRANSFER_AMOUNT) + .addTokenTransfer( + tokenId, + receiverAccountId, + TRANSFER_AMOUNT, + ) .execute(env.client); await tokenTransferResponse.getReceipt(env.client); @@ -820,7 +833,7 @@ describe("TokenAssociate", function () { .get(tokenId) .toInt(); - expect(receiverBalance).to.equal(TOKEN_SUPPLY); + expect(receiverBalance).to.equal(TRANSFER_AMOUNT); }); it("should revert when auto association is set to <-1", async function () { From 860ae56200ec807703b18c91495488d7a4943363 Mon Sep 17 00:00:00 2001 From: Ivaylo Nikolov Date: Tue, 9 Jul 2024 16:10:12 +0300 Subject: [PATCH 4/5] test: use serial ids instead of hardcoded integers Signed-off-by: Ivaylo Nikolov --- .../TokenAssociateIntegrationTest.js | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/test/integration/TokenAssociateIntegrationTest.js b/test/integration/TokenAssociateIntegrationTest.js index 2b41821dc..dad168963 100644 --- a/test/integration/TokenAssociateIntegrationTest.js +++ b/test/integration/TokenAssociateIntegrationTest.js @@ -269,12 +269,19 @@ describe("TokenAssociate", function () { .setMetadata([Buffer.from("-")]) .execute(env.client); - await tokenMintSignedTransaction.getReceipt(env.client); + const { serials } = await tokenMintSignedTransaction.getReceipt( + env.client, + ); // transfer the token to receiver const transferTxSign = await new TransferTransaction() - .addNftTransfer(tokenId, 1, env.operatorId, receiverId) + .addNftTransfer( + tokenId, + serials[0], + env.operatorId, + receiverId, + ) .execute(env.client); await transferTxSign.getReceipt(env.client); @@ -300,7 +307,9 @@ describe("TokenAssociate", function () { .addMetadata(Buffer.from("-")) .execute(env.client); - await tokenMintSignedTransaction2.getReceipt(env.client); + const serials2 = ( + await tokenMintSignedTransaction2.getReceipt(env.client) + ).serials; let err = false; @@ -309,7 +318,7 @@ describe("TokenAssociate", function () { await new TransferTransaction() .addNftTransfer( tokenId2, - 1, + serials2[0], env.operatorId, receiverId, ) @@ -417,12 +426,17 @@ describe("TokenAssociate", function () { .freezeWith(env.client) .sign(env.operatorKey); - await ( + const { serials } = await ( await tokenMintTx.execute(env.client) ).getReceipt(env.client); const sendTokenToReceiverTx = await new TransferTransaction() - .addNftTransfer(tokenId, 1, env.operatorId, receiverId) + .addNftTransfer( + tokenId, + serials[0], + env.operatorId, + receiverId, + ) .execute(env.client); await sendTokenToReceiverTx.getReceipt(env.client); @@ -548,7 +562,7 @@ describe("TokenAssociate", function () { .setMetadata([Buffer.from("-")]) .execute(env.client); - await mintTokenTx.getReceipt(env.client); + const { serials } = await mintTokenTx.getReceipt(env.client); const mintTokenTx2 = await new TokenMintTransaction() .setTokenId(tokenId2) @@ -571,7 +585,12 @@ describe("TokenAssociate", function () { ).getReceipt(env.client); const tokenTransferResponse = await new TransferTransaction() - .addNftTransfer(tokenId, 1, env.operatorId, receiverId) + .addNftTransfer( + tokenId, + serials[0], + env.operatorId, + receiverId, + ) .execute(env.client); await tokenTransferResponse.getReceipt(env.client); From b5ca9e4f0736df5544716c20c03da6a09eb52b72 Mon Sep 17 00:00:00 2001 From: Ivaylo Nikolov Date: Tue, 9 Jul 2024 16:55:42 +0300 Subject: [PATCH 5/5] test: approve before transfer for token association Signed-off-by: Ivaylo Nikolov --- .../TokenAssociateIntegrationTest.js | 65 +++++++++---------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/test/integration/TokenAssociateIntegrationTest.js b/test/integration/TokenAssociateIntegrationTest.js index dad168963..5d31f007d 100644 --- a/test/integration/TokenAssociateIntegrationTest.js +++ b/test/integration/TokenAssociateIntegrationTest.js @@ -368,15 +368,6 @@ describe("TokenAssociate", function () { await tokenAssociateTransaction.execute(env.client) ).getReceipt(env.client); - const tokenMintTx = await new TokenMintTransaction() - .setTokenId(tokenId) - .freezeWith(env.client) - .sign(env.operatorKey); - - await ( - await tokenMintTx.execute(env.client) - ).getReceipt(env.client); - const sendTokenToReceiverTx = await new TransferTransaction() .addTokenTransfer(tokenId, env.operatorId, -TRANSFER_AMOUNT) .addTokenTransfer(tokenId, receiverId, TRANSFER_AMOUNT) @@ -620,21 +611,10 @@ describe("TokenAssociate", function () { it("receiver should have token balance even if it has given allowance to spender", async function () { this.timeout(120000); const spenderKey = PrivateKey.generateECDSA(); - - const unlimitedAutoAssociationTx = - await new AccountUpdateTransaction() - .setAccountId(receiverId) - .setMaxAutomaticTokenAssociations(-1) - .freezeWith(env.client) - .sign(receiverKey); - - await ( - await unlimitedAutoAssociationTx.execute(env.client) - ).getReceipt(env.client); - const spenderAccountCreateTx = await new AccountCreateTransaction() .setKey(spenderKey) + .setMaxAutomaticTokenAssociations(-1) .setInitialBalance(new Hbar(1)) .execute(env.client); @@ -642,6 +622,17 @@ describe("TokenAssociate", function () { await spenderAccountCreateTx.getReceipt(env.client) ).accountId; + const unlimitedAutoAssociationReceiverTx = + await new AccountUpdateTransaction() + .setAccountId(receiverId) + .setMaxAutomaticTokenAssociations(-1) + .freezeWith(env.client) + .sign(receiverKey); + + await ( + await unlimitedAutoAssociationReceiverTx.execute(env.client) + ).getReceipt(env.client); + const tokenCreateResponse = await new TokenCreateTransaction() .setTokenName("ffff") .setTokenSymbol("F") @@ -655,26 +646,34 @@ describe("TokenAssociate", function () { env.client, ); - const tokenTransferHalfSupply = await new TransferTransaction() - .addTokenTransfer(tokenId, env.operatorId, -TRANSFER_AMOUNT) - .addTokenTransfer(tokenId, receiverId, TRANSFER_AMOUNT) - .execute(env.client); - - await tokenTransferHalfSupply.getReceipt(env.client); - const tokenAllowanceTx = await new AccountAllowanceApproveTransaction() .approveTokenAllowance( tokenId, - receiverId, + env.operatorId, spenderId, - 1, + TRANSFER_AMOUNT, ) + .execute(env.client); + + await tokenAllowanceTx.getReceipt(env.client); + + const onBehalfOfTransactionId = + TransactionId.generate(spenderId); + const tokenTransferApprovedSupply = + await new TransferTransaction() + .setTransactionId(onBehalfOfTransactionId) + .addApprovedTokenTransfer( + tokenId, + env.operatorId, + -TRANSFER_AMOUNT, + ) + .addTokenTransfer(tokenId, receiverId, TRANSFER_AMOUNT) .freezeWith(env.client) - .sign(receiverKey); + .sign(spenderKey); await ( - await tokenAllowanceTx.execute(env.client) + await tokenTransferApprovedSupply.execute(env.client) ).getReceipt(env.client); const tokenBalanceReceiver = await new AccountBalanceQuery() @@ -855,7 +854,7 @@ describe("TokenAssociate", function () { expect(receiverBalance).to.equal(TRANSFER_AMOUNT); }); - it("should revert when auto association is set to <-1", async function () { + it("should revert when auto association is set to less than -1", async function () { let err = false; try {