From 5712828c1dee2396adbd7bd7319219edf2dc6ff8 Mon Sep 17 00:00:00 2001 From: Vu Vo Date: Tue, 8 Feb 2022 17:37:18 +0700 Subject: [PATCH 1/9] verify closeRemainderTo, remove auth/spend address when close account --- packages/runtime/src/ctx.ts | 22 ++++++++++++++++++++-- packages/runtime/src/errors/errors-list.ts | 6 ++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/packages/runtime/src/ctx.ts b/packages/runtime/src/ctx.ts index 568ee8bcc..d98643a99 100644 --- a/packages/runtime/src/ctx.ts +++ b/packages/runtime/src/ctx.ts @@ -156,6 +156,7 @@ export class Ctx implements Context { this.assertAccBalAboveMin(fromAccount.address); if (txParam.payFlags.closeRemainderTo) { + this.verifyCloseRemainderTo(txParam); const closeRemToAcc = this.getAccount(txParam.payFlags.closeRemainderTo); closeRemToAcc.amount += fromAccount.amount; // transfer funds of sender to closeRemTo account @@ -374,13 +375,24 @@ export class Ctx implements Context { } } + /** + * Verify closeRemainderTo field is different with fromAccountAddr + * @param txParam transaction param + */ + verifyCloseRemainderTo (txParam: types.ExecParams): void { + if (!txParam.payFlags.closeRemainderTo) return; + if (txParam.payFlags.closeRemainderTo === webTx.getFromAddress(txParam)) { + throw new RuntimeError(RUNTIME_ERRORS.GENERAL.INVALID_CLOSE_REMAINDER_TO); + } + } + /** * Deduct transaction fee from sender account. * @param sender Sender address * @param index Index of current tx being processed in tx group */ deductFee (sender: AccountAddress, index: number, params: types.TxParams): void { - let fee: bigint = BigInt(this.gtxs[index].fee as number); + let fee: bigint = BigInt(this.gtxs[index].fee); // If flatFee boolean is not set, change fee value if (!params.flatFee && params.totalFee === undefined) { fee = BigInt(Math.max(ALGORAND_MIN_TX_FEE, Number(this.gtxs[index].fee))); @@ -413,6 +425,8 @@ export class Ctx implements Context { toAssetHolding.amount += BigInt(txParam.amount); if (txParam.payFlags.closeRemainderTo) { + this.verifyCloseRemainderTo(txParam); + const closeToAddr = txParam.payFlags.closeRemainderTo; if (fromAccountAddr === fromAssetHolding.creator) { throw new RuntimeError(RUNTIME_ERRORS.ASA.CANNOT_CLOSE_ASSET_BY_CREATOR); @@ -749,7 +763,11 @@ export class Ctx implements Context { break; } } - + // if closeRemainderTo field occur in txParam + // we will change rekeyTo field to webTx.getFromAddress(txParam) + if (txParam.payFlags.closeRemainderTo) { + txParam.payFlags.rekeyTo = webTx.getFromAddress(txParam); + } // apply rekey after pass all logic this.rekeyTo(txParam); diff --git a/packages/runtime/src/errors/errors-list.ts b/packages/runtime/src/errors/errors-list.ts index d365d1bb6..84ef706e0 100644 --- a/packages/runtime/src/errors/errors-list.ts +++ b/packages/runtime/src/errors/errors-list.ts @@ -456,6 +456,12 @@ const runtimeGeneralErrors = { message: "Should have been authorized by %spend% but was actually authorized by %signer%", title: "Invalid spend account.", description: "Invalid spend account" + }, + INVALID_CLOSE_REMAINDER_TO: { + number: 1507, + message: "Transaction cannot close account to its sender", + title: "Transaction cannot close account to its sender.", + description: "Transaction cannot close account to its sender" } }; From 47ceb8e1306d6912d78c394fa8b57758d077efbe Mon Sep 17 00:00:00 2001 From: Vu Vo Date: Tue, 8 Feb 2022 18:04:22 +0700 Subject: [PATCH 2/9] add test for closeRemainder on Asset Transfer transaction --- .../integration/{ => rekey}/rekey-inner-tx.ts | 10 ++--- .../{ => rekey}/rekey-transaction.ts | 8 ++-- packages/runtime/test/src/runtime.ts | 40 +++++++++++++++---- 3 files changed, 42 insertions(+), 16 deletions(-) rename packages/runtime/test/integration/{ => rekey}/rekey-inner-tx.ts (93%) rename packages/runtime/test/integration/{ => rekey}/rekey-transaction.ts (98%) diff --git a/packages/runtime/test/integration/rekey-inner-tx.ts b/packages/runtime/test/integration/rekey/rekey-inner-tx.ts similarity index 93% rename from packages/runtime/test/integration/rekey-inner-tx.ts rename to packages/runtime/test/integration/rekey/rekey-inner-tx.ts index 2d8cf3123..1af6363b8 100644 --- a/packages/runtime/test/integration/rekey-inner-tx.ts +++ b/packages/runtime/test/integration/rekey/rekey-inner-tx.ts @@ -2,11 +2,11 @@ import { types } from "@algo-builder/web"; import { getApplicationAddress } from "algosdk"; import { assert } from "chai"; -import { RUNTIME_ERRORS } from "../../src/errors/errors-list"; -import { AccountStore, Runtime } from "../../src/index"; -import { DeployedAppTxReceipt } from "../../src/types"; -import { useFixture } from "../helpers/integration"; -import { expectRuntimeError } from "../helpers/runtime-errors"; +import { RUNTIME_ERRORS } from "../../../src/errors/errors-list"; +import { AccountStore, Runtime } from "../../../src/index"; +import { DeployedAppTxReceipt } from "../../../src/types"; +import { useFixture } from "../../helpers/integration"; +import { expectRuntimeError } from "../../helpers/runtime-errors"; // default initial balance const baseBalance = 20e9; diff --git a/packages/runtime/test/integration/rekey-transaction.ts b/packages/runtime/test/integration/rekey/rekey-transaction.ts similarity index 98% rename from packages/runtime/test/integration/rekey-transaction.ts rename to packages/runtime/test/integration/rekey/rekey-transaction.ts index 8a8c74952..4b9c1afe2 100644 --- a/packages/runtime/test/integration/rekey-transaction.ts +++ b/packages/runtime/test/integration/rekey/rekey-transaction.ts @@ -2,10 +2,10 @@ import { types } from "@algo-builder/web"; import { LogicSigAccount } from "algosdk"; import { assert } from "chai"; -import { RUNTIME_ERRORS } from "../../src/errors/errors-list"; -import { AccountStore, Runtime } from "../../src/index"; -import { useFixture } from "../helpers/integration"; -import { expectRuntimeError } from "../helpers/runtime-errors"; +import { RUNTIME_ERRORS } from "../../../src/errors/errors-list"; +import { AccountStore, Runtime } from "../../../src/index"; +import { useFixture } from "../../helpers/integration"; +import { expectRuntimeError } from "../../helpers/runtime-errors"; // default initial balance const baseBalance = 20e9; diff --git a/packages/runtime/test/src/runtime.ts b/packages/runtime/test/src/runtime.ts index 6d73a72cc..86bf13f2f 100644 --- a/packages/runtime/test/src/runtime.ts +++ b/packages/runtime/test/src/runtime.ts @@ -7,6 +7,7 @@ import { AccountStore } from "../../src/account"; import { RUNTIME_ERRORS } from "../../src/errors/errors-list"; import { ASSET_CREATION_FEE } from "../../src/lib/constants"; import { Runtime } from "../../src/runtime"; +import { AccountStoreI } from "../../src/types"; import { useFixture } from "../helpers/integration"; import { expectRuntimeError } from "../helpers/runtime-errors"; import { elonMuskAccount } from "../mocks/account"; @@ -279,13 +280,13 @@ describe("Algorand Standard Assets", function () { const johnAssetHolding = john.getAssetHolding(assetId); assert.isDefined(johnAssetHolding); - assert.equal(johnAssetHolding?.amount as bigint, 5912599999515n); + assert.equal(johnAssetHolding?.amount, 5912599999515n); // opt-in for alice runtime.optIntoASA(assetId, alice.address, {}); const aliceAssetHolding = alice.getAssetHolding(assetId); assert.isDefined(aliceAssetHolding); - assert.equal(aliceAssetHolding?.amount as bigint, 0n); + assert.equal(aliceAssetHolding?.amount, 0n); }); it("should opt-in to asset using asset transfer transaction", () => { @@ -307,7 +308,7 @@ describe("Algorand Standard Assets", function () { syncAccounts(); const aliceAssetHolding = alice.getAssetHolding(assetId); - assert.equal(aliceAssetHolding?.amount as bigint, 0n); + assert.equal(aliceAssetHolding?.amount, 0n); // verfiy min balance is also raised assert.equal(alice.minBalance, prevAliceMinBal + ASSET_CREATION_FEE); }); @@ -338,8 +339,8 @@ describe("Algorand Standard Assets", function () { assert.isDefined(res); runtime.optIntoASA(assetId, alice.address, {}); - const initialJohnAssets = john.getAssetHolding(assetId)?.amount as bigint; - const initialAliceAssets = alice.getAssetHolding(assetId)?.amount as bigint; + const initialJohnAssets = john.getAssetHolding(assetId)?.amount; + const initialAliceAssets = alice.getAssetHolding(assetId)?.amount; assert.isDefined(initialJohnAssets); assert.isDefined(initialAliceAssets); @@ -395,8 +396,8 @@ describe("Algorand Standard Assets", function () { syncAccounts(); assert.equal(alice.minBalance, initialAliceMinBalance + ASSET_CREATION_FEE); // alice min balance raised after opt-in - const initialJohnAssets = john.getAssetHolding(assetId)?.amount as bigint; - const initialAliceAssets = alice.getAssetHolding(assetId)?.amount as bigint; + const initialJohnAssets = john.getAssetHolding(assetId)?.amount; + const initialAliceAssets = alice.getAssetHolding(assetId)?.amount; assert.isDefined(initialJohnAssets); assert.isDefined(initialAliceAssets); @@ -414,6 +415,31 @@ describe("Algorand Standard Assets", function () { assert.equal(alice.minBalance, initialAliceMinBalance); // min balance should decrease to initial value after opt-out }); + it("should throw error if closeRemainderTo is fromAccountAddr", () => { + const res = runtime.getAssetDef(assetId); + assert.isDefined(res); + runtime.optIntoASA(assetId, alice.address, {}); + + // transfer few assets to alice + runtime.executeTx({ + ...assetTransferParam, + toAccountAddr: alice.address, + amount: 30n + }); + + // throw error because closeReaminderTo invalid. + expectRuntimeError( + () => runtime.executeTx({ + ...assetTransferParam, + sign: types.SignType.SecretKey, + fromAccount: alice.account, + toAccountAddr: alice.address, + payFlags: { totalFee: 1000, closeRemainderTo: alice.address } // transfer all assets of alice => john (using closeRemTo) + }), + RUNTIME_ERRORS.GENERAL.INVALID_CLOSE_REMAINDER_TO + ); + }); + it("should throw error if trying to close asset holding of asset creator account", () => { const res = runtime.getAssetDef(assetId); assert.isDefined(res); From a86b7f29d32771e22383495739be38060a3687c5 Mon Sep 17 00:00:00 2001 From: Vu Vo Date: Tue, 8 Feb 2022 18:25:51 +0700 Subject: [PATCH 3/9] add test for closeRemainder on ALGO transfer Tx --- packages/runtime/test/src/runtime.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime/test/src/runtime.ts b/packages/runtime/test/src/runtime.ts index 7f11e29c1..c5b336845 100644 --- a/packages/runtime/test/src/runtime.ts +++ b/packages/runtime/test/src/runtime.ts @@ -24,7 +24,7 @@ describe("Transfer Algo Transaction", function () { let runtime: Runtime; - function syncAccounts() : void { + function syncAccounts (): void { alice = runtime.getAccount(alice.address); bob = runtime.getAccount(bob.address); } From 0bdeeab0857d12eb86ad4b6ab75ab4a6b3231522 Mon Sep 17 00:00:00 2001 From: Vu Vo Date: Tue, 8 Feb 2022 22:28:50 +0700 Subject: [PATCH 4/9] add test case for close 'rekey account' --- .../integration/rekey/rekey-transaction.ts | 175 ++++++++++++++---- 1 file changed, 137 insertions(+), 38 deletions(-) diff --git a/packages/runtime/test/integration/rekey/rekey-transaction.ts b/packages/runtime/test/integration/rekey/rekey-transaction.ts index 4b9c1afe2..6e62c49c7 100644 --- a/packages/runtime/test/integration/rekey/rekey-transaction.ts +++ b/packages/runtime/test/integration/rekey/rekey-transaction.ts @@ -1,3 +1,4 @@ +/* eslint-disable sonarjs/no-duplicate-string */ import { types } from "@algo-builder/web"; import { LogicSigAccount } from "algosdk"; import { assert } from "chai"; @@ -141,46 +142,63 @@ describe("Re-keying transactions", function () { ); }); - describe("Rekey an already rekeyed account", function () { - this.beforeEach(() => { - txParams = { - type: types.TransactionType.TransferAlgo, - sign: types.SignType.SecretKey, - fromAccount: bob.account, - fromAccountAddr: alice.address, - toAccountAddr: bob.address, - amountMicroAlgos: amount, - payFlags: { totalFee: fee, rekeyTo: lsigAccount.address } - }; - - runtime.executeTx(txParams); - syncAccounts(); - }); - - it("Check spend key", function () { - assert.equal(alice.getSpendAddress(), lsigAccount.address); - }); + it("Can rekey account again", () => { + txParams = { + type: types.TransactionType.TransferAlgo, + sign: types.SignType.SecretKey, + fromAccount: bob.account, + fromAccountAddr: alice.address, + toAccountAddr: bob.address, + amountMicroAlgos: amount, + payFlags: { totalFee: fee, rekeyTo: lsigAccount.address } + }; + + runtime.executeTx(txParams); + syncAccounts(); + // check spend address + assert.equal(alice.getSpendAddress(), lsigAccount.address); + }); + + it("Can Rekey again back to orginal account", () => { + txParams = { + type: types.TransactionType.TransferAlgo, + sign: types.SignType.SecretKey, + fromAccount: bob.account, + fromAccountAddr: alice.address, + toAccountAddr: bob.address, + amountMicroAlgos: amount, + payFlags: { totalFee: fee, rekeyTo: alice.address } + }; + + runtime.executeTx(txParams); + syncAccounts(); + // check spend address + assert.equal(alice.getSpendAddress(), alice.address); }); - describe("Rekey again back to orginal account", function () { - this.beforeEach(() => { - txParams = { - type: types.TransactionType.TransferAlgo, - sign: types.SignType.SecretKey, - fromAccount: bob.account, - fromAccountAddr: alice.address, - toAccountAddr: bob.address, - amountMicroAlgos: amount, - payFlags: { totalFee: fee, rekeyTo: alice.address } - }; - - runtime.executeTx(txParams); - syncAccounts(); - }); - - it("Check spend key", function () { - assert.equal(alice.getSpendAddress(), alice.address); - }); + it("close account should remove spend/auth address", () => { + const aliceBalanceBefore = alice.balance(); + const bobBalanceBefore = bob.balance(); + + txParams = { + type: types.TransactionType.TransferAlgo, + sign: types.SignType.SecretKey, + fromAccount: bob.account, + fromAccountAddr: alice.address, + toAccountAddr: bob.address, + amountMicroAlgos: 0, + payFlags: { + totalFee: fee, + closeRemainderTo: bob.address + } + }; + runtime.executeTx(txParams); + syncAccounts(); + + // check account state after clsoe + assert.equal(alice.balance(), 0n); + assert.equal(bob.balance(), aliceBalanceBefore + bobBalanceBefore - BigInt(fee)); + assert.equal(alice.getSpendAddress(), alice.address); }); }); @@ -268,6 +286,33 @@ describe("Re-keying transactions", function () { rekeyMessageError(alice.getSpendAddress(), john.address) ); }); + + it("close account should remove spend/auth address", () => { + const aliceBalanceBefore = alice.balance(); + const bobBalanceBefore = bob.balance(); + + // close alice account to bob + txParams = { + type: types.TransactionType.TransferAlgo, + sign: types.SignType.LogicSignature, + fromAccountAddr: alice.address, + toAccountAddr: bob.address, + amountMicroAlgos: 0n, + lsig: lsig, + payFlags: { + totalFee: fee, + closeRemainderTo: bob.address + } + }; + + runtime.executeTx(txParams); + syncAccounts(); + + // check account state after close + assert.equal(alice.balance(), 0n); + assert.equal(bob.balance(), aliceBalanceBefore + bobBalanceBefore - BigInt(fee)); + assert.equal(alice.getSpendAddress(), alice.address); + }); }); describe("Lsig to Lsig", function () { @@ -337,6 +382,33 @@ describe("Re-keying transactions", function () { rekeyMessageError(lsigAccount.getSpendAddress(), lsigAccount.address) ); }); + + it("close account should remove spend/auth address", () => { + const lsigAccountBalanceBefore = lsigAccount.balance(); + const aliceBalanceBefore = alice.balance(); + + // close lsig account to alice + txParams = { + type: types.TransactionType.TransferAlgo, + sign: types.SignType.LogicSignature, + fromAccountAddr: lsigAccount.address, + toAccountAddr: bob.address, + amountMicroAlgos: 0n, + lsig: cloneLsig, + payFlags: { + totalFee: fee, + closeRemainderTo: alice.address + } + }; + + runtime.executeTx(txParams); + syncAccounts(); + + // check account state after close + assert.equal(lsigAccount.balance(), 0n); + assert.equal(alice.balance(), lsigAccountBalanceBefore + aliceBalanceBefore - BigInt(fee)); + assert.equal(lsigAccount.getSpendAddress(), lsigAccount.address); + }); }); describe("Lsig to account", function () { @@ -409,6 +481,33 @@ describe("Re-keying transactions", function () { assert.equal(lsigAccount.getSpendAddress(), bob.address); }); + + it("close account should remove auth/spend address", () => { + // balance before rekey + const lsigBalanceBefore = lsigAccount.balance(); + const aliceBalanceBefore = alice.balance(); + // transfer ALGO use lsig + txParams = { + type: types.TransactionType.TransferAlgo, + sign: types.SignType.SecretKey, + fromAccount: bob.account, + fromAccountAddr: lsigAccount.address, + toAccountAddr: alice.address, + amountMicroAlgos: 0n, + payFlags: { + totalFee: fee, + closeRemainderTo: alice.address + } + }; + + runtime.executeTx(txParams); + + // check account state after close + syncAccounts(); + assert.equal(lsigAccount.balance(), 0n); + assert.equal(alice.balance(), lsigBalanceBefore + aliceBalanceBefore - BigInt(fee)); + assert.equal(lsigAccount.getSpendAddress(), lsigAccount.address); + }); }); describe("Rekey by another Tx type", function () { From 85580674d44abf581232784d1964fc1f52c247d2 Mon Sep 17 00:00:00 2001 From: Vu Vo Date: Tue, 8 Feb 2022 22:33:10 +0700 Subject: [PATCH 5/9] add test case use closeRemainderTo and rekeyTo in same transaction --- packages/runtime/test/src/runtime.ts | 31 +++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/packages/runtime/test/src/runtime.ts b/packages/runtime/test/src/runtime.ts index c5b336845..ae72c5e8d 100644 --- a/packages/runtime/test/src/runtime.ts +++ b/packages/runtime/test/src/runtime.ts @@ -21,18 +21,21 @@ describe("Transfer Algo Transaction", function () { let alice: AccountStoreI; let bob: AccountStoreI; + let alan: AccountStoreI; let runtime: Runtime; function syncAccounts (): void { alice = runtime.getAccount(alice.address); bob = runtime.getAccount(bob.address); + alan = runtime.getAccount(alan.address); } this.beforeEach(() => { alice = new AccountStore(minBalance); bob = new AccountStore(minBalance); - runtime = new Runtime([alice, bob]); + alan = new AccountStore(minBalance); + runtime = new Runtime([alice, bob, alan]); }); it("Transfer ALGO from alice to bob", () => { @@ -80,6 +83,32 @@ describe("Transfer Algo Transaction", function () { assert.equal(initialAliceBalance + initialBobBalance - BigInt(fee), bob.balance()); }); + it("should ignore rekey when use with closeRemainderTo", () => { + const initialAliceBalance = alice.balance(); + const initialBobBalance = bob.balance(); + + const ALGOTransferTxParam: types.AlgoTransferParam = { + type: types.TransactionType.TransferAlgo, + sign: types.SignType.SecretKey, + fromAccount: alice.account, + toAccountAddr: bob.address, + amountMicroAlgos: 0n, + payFlags: { + totalFee: fee, + closeRemainderTo: bob.address, + rekeyTo: alan.address + } + }; + + runtime.executeTx(ALGOTransferTxParam); + + syncAccounts(); + assert.equal(alice.balance(), 0n); + assert.equal(initialAliceBalance + initialBobBalance - BigInt(fee), bob.balance()); + // spend/auth address of alice not changed. + assert.equal(alice.getSpendAddress(), alice.address); + }); + it("should throw error if closeRemainderTo is fromAccountAddr", () => { // throw error because closeReaminderTo invalid. expectRuntimeError( From 3add690bba7be12c035770773d297690422757fe Mon Sep 17 00:00:00 2001 From: Vu Vo Date: Wed, 9 Feb 2022 01:24:42 +0700 Subject: [PATCH 6/9] create verify transfer aware duplicate code --- packages/runtime/src/ctx.ts | 4 +- .../integration/rekey/rekey-transaction.ts | 476 +++++++----------- 2 files changed, 175 insertions(+), 305 deletions(-) diff --git a/packages/runtime/src/ctx.ts b/packages/runtime/src/ctx.ts index e4ac15f24..9aefda9ca 100644 --- a/packages/runtime/src/ctx.ts +++ b/packages/runtime/src/ctx.ts @@ -376,7 +376,7 @@ export class Ctx implements Context { } /** - * Verify closeRemainderTo field is different with fromAccountAddr + * Verify closeRemainderTo field is different than fromAccountAddr * @param txParam transaction param */ verifyCloseRemainderTo (txParam: types.ExecParams): void { @@ -392,7 +392,7 @@ export class Ctx implements Context { * @param index Index of current tx being processed in tx group */ deductFee (sender: AccountAddress, index: number, params: types.TxParams): void { - let fee: bigint = BigInt(this.gtxs[index].fee as number); + let fee: bigint = BigInt(this.gtxs[index].fee); // If flatFee boolean is not set, change fee value if (!params.flatFee && params.totalFee === undefined) { fee = BigInt(Math.max(ALGORAND_MIN_TX_FEE, Number(this.gtxs[index].fee))); diff --git a/packages/runtime/test/integration/rekey/rekey-transaction.ts b/packages/runtime/test/integration/rekey/rekey-transaction.ts index 6e62c49c7..a7b1a41d7 100644 --- a/packages/runtime/test/integration/rekey/rekey-transaction.ts +++ b/packages/runtime/test/integration/rekey/rekey-transaction.ts @@ -2,19 +2,21 @@ import { types } from "@algo-builder/web"; import { LogicSigAccount } from "algosdk"; import { assert } from "chai"; +import { Address } from "cluster"; import { RUNTIME_ERRORS } from "../../../src/errors/errors-list"; import { AccountStore, Runtime } from "../../../src/index"; +import { AccountStoreI } from "../../../src/types"; import { useFixture } from "../../helpers/integration"; import { expectRuntimeError } from "../../helpers/runtime-errors"; // default initial balance const baseBalance = 20e9; // default fee -const fee = 1000; +const FEE = 1000; // default amount use for transfer -const amount = 1000n; +const DEFAULT_AMOUNT = 1000n; function rekeyMessageError (spend: string, signer: string): string { return `Should have been authorized by ${spend} but was actually authorized by ${signer}`; @@ -23,12 +25,12 @@ function rekeyMessageError (spend: string, signer: string): string { describe("Re-keying transactions", function () { useFixture('basic-teal'); - let master: AccountStore; - let alice: AccountStore; - let bob: AccountStore; - let john: AccountStore; - let lsigAccount: AccountStore; - let cloneLsigAccount: AccountStore; + let master: AccountStoreI; + let alice: AccountStoreI; + let bob: AccountStoreI; + let john: AccountStoreI; + let lsigAccount: AccountStoreI; + let cloneLsigAccount: AccountStoreI; let lsig: LogicSigAccount; let cloneLsig: LogicSigAccount; @@ -46,6 +48,143 @@ describe("Re-keying transactions", function () { cloneLsigAccount = runtime.getAccount(cloneLsig.address()); } + function mkTxAlgoTransferFromLsig ( + runtime: Runtime, + lsig: LogicSigAccount, + from: AccountStoreI, + to: AccountStoreI, + amount: bigint | number, + payFlags: types.TxParams + ): void { + const txParam: types.AlgoTransferParam = { + type: types.TransactionType.TransferAlgo, + sign: types.SignType.LogicSignature, + lsig: lsig, + fromAccountAddr: from.address, + toAccountAddr: to.address, + amountMicroAlgos: amount, + payFlags: payFlags + }; + + runtime.executeTx(txParam); + syncAccounts(); + } + + function mkTxAlgoTransferFromAccount ( + runtime: Runtime, + signer: AccountStoreI, + from: AccountStoreI, + to: AccountStoreI, + algoAmount: bigint, + payFlags: types.TxParams + ): void { + const txParam: types.AlgoTransferParam = { + type: types.TransactionType.TransferAlgo, + sign: types.SignType.SecretKey, + fromAccount: signer.account, + fromAccountAddr: from.address, + toAccountAddr: to.address, + amountMicroAlgos: algoAmount, + payFlags: payFlags + }; + runtime.executeTx(txParam); + syncAccounts(); + }; + + function rekeyFromAccount ( + runtime: Runtime, signer: AccountStoreI, from: AccountStoreI, to: AccountStoreI + ): void { + mkTxAlgoTransferFromAccount( + runtime, signer, from, from, 0n, + { + totalFee: FEE, + rekeyTo: to.address + } + ); + } + + function rekeyFromLsig ( + runtime: Runtime, lsig: LogicSigAccount, from: AccountStoreI, to: AccountStoreI + ): void { + mkTxAlgoTransferFromLsig( + runtime, lsig, from, from, 0n, + { + totalFee: FEE, + rekeyTo: to.address + } + ); + } + + function closeWithAccount ( + runtime: Runtime, signer: AccountStoreI, from: AccountStoreI, closeTo: AccountStoreI + ): void { + mkTxAlgoTransferFromAccount( + runtime, signer, from, from, 0n, + { + totalFee: FEE, + closeRemainderTo: closeTo.address + } + ); + } + + function closeWithLsig ( + runtime: Runtime, lsig: LogicSigAccount, from: AccountStoreI, closeTo: AccountStoreI + ): void { + mkTxAlgoTransferFromLsig( + runtime, lsig, from, from, 0n, + { + totalFee: FEE, + closeRemainderTo: closeTo.address + } + ); + } + + function verifyTransferAlgoAuthByAccount ( + runtime: Runtime, signer: AccountStoreI, from: AccountStoreI, to: AccountStoreI, algoAmount: bigint + ): void { + // balance before rekey + const fromAccountBalanceBefore = from.balance(); + const toAccountBalanceBefore = to.balance(); + + // transfer ALGO by spend account + mkTxAlgoTransferFromAccount( + runtime, signer, from, to, algoAmount, + { + totalFee: FEE + } + ); + + const fromAccountBalanceAfter = runtime.getAccount(from.address).balance(); + const toAccountBalanceAfter = runtime.getAccount(to.address).balance(); + + // transaction fee paid by `from account` not `signer` + assert.equal(fromAccountBalanceBefore, fromAccountBalanceAfter + algoAmount + BigInt(FEE)); + assert.equal(toAccountBalanceBefore + algoAmount, toAccountBalanceAfter); + } + + function verifyTransferAlgoAuthByLsig ( + runtime: Runtime, lsig: LogicSigAccount, from: AccountStoreI, to: AccountStoreI, amount: bigint + ): void { + // balance before rekey + const fromAccountBalanceBefore = from.balance(); + const toAccountBalanceBefore = to.balance(); + + // transfer ALGO by spend account + mkTxAlgoTransferFromLsig( + runtime, lsig, from, to, amount, + { + totalFee: FEE + } + ); + + const fromAccountBalanceAfter = runtime.getAccount(from.address).balance(); + const toAccountBalanceAfter = runtime.getAccount(to.address).balance(); + + // transaction fee paid by `from account` not `signer` + assert.equal(fromAccountBalanceBefore, fromAccountBalanceAfter + amount + BigInt(FEE)); + assert.equal(toAccountBalanceBefore + amount, toAccountBalanceAfter); + } + this.beforeEach(() => { // accounts master = new AccountStore(baseBalance); @@ -75,20 +214,7 @@ describe("Re-keying transactions", function () { describe("Account to account", function () { this.beforeEach(() => { - txParams = { - type: types.TransactionType.TransferAlgo, - sign: types.SignType.SecretKey, - fromAccount: alice.account, - toAccountAddr: alice.address, - amountMicroAlgos: 0, - payFlags: { - totalFee: fee, - rekeyTo: bob.address - } - }; - - runtime.executeTx(txParams); - syncAccounts(); + rekeyFromAccount(runtime, alice, alice, bob); }); it("Spend address of alice account should changed to bob account", function () { @@ -97,81 +223,26 @@ describe("Re-keying transactions", function () { }); it("Should transfer ALGO by spend account", function () { - syncAccounts(); // balance before rekey - const aliceBalanceBefore = alice.balance(); - const bobBalanceBefore = bob.balance(); - - // transfer ALGO by spend account - txParams = { - type: types.TransactionType.TransferAlgo, - sign: types.SignType.SecretKey, - fromAccount: bob.account, - fromAccountAddr: alice.address, - toAccountAddr: bob.address, - amountMicroAlgos: amount, - payFlags: { totalFee: fee } - }; - - runtime.executeTx(txParams); - - // check balance of alice and bob after transfer - syncAccounts(); - const aliceBalanceAfter = alice.balance(); - const bobBalanceAfter = bob.balance(); - - // transaction fee paid by `from account` not `signer` - assert.equal(aliceBalanceBefore, aliceBalanceAfter + amount + BigInt(fee)); - assert.equal(bobBalanceBefore + amount, bobBalanceAfter); + verifyTransferAlgoAuthByAccount(runtime, bob, alice, bob, DEFAULT_AMOUNT); }); it("Should fail because signer account is invalid spend address", function () { - txParams = { - type: types.TransactionType.TransferAlgo, - sign: types.SignType.SecretKey, - fromAccount: alice.account, - toAccountAddr: bob.address, - amountMicroAlgos: amount, - payFlags: { totalFee: fee } - }; - expectRuntimeError( - () => runtime.executeTx(txParams), + () => mkTxAlgoTransferFromAccount(runtime, alice, alice, bob, DEFAULT_AMOUNT, { totalFee: FEE }), RUNTIME_ERRORS.GENERAL.INVALID_AUTH_ACCOUNT, rekeyMessageError(alice.getSpendAddress(), alice.address) ); }); it("Can rekey account again", () => { - txParams = { - type: types.TransactionType.TransferAlgo, - sign: types.SignType.SecretKey, - fromAccount: bob.account, - fromAccountAddr: alice.address, - toAccountAddr: bob.address, - amountMicroAlgos: amount, - payFlags: { totalFee: fee, rekeyTo: lsigAccount.address } - }; - - runtime.executeTx(txParams); - syncAccounts(); + rekeyFromAccount(runtime, bob, alice, lsigAccount); // check spend address assert.equal(alice.getSpendAddress(), lsigAccount.address); }); it("Can Rekey again back to orginal account", () => { - txParams = { - type: types.TransactionType.TransferAlgo, - sign: types.SignType.SecretKey, - fromAccount: bob.account, - fromAccountAddr: alice.address, - toAccountAddr: bob.address, - amountMicroAlgos: amount, - payFlags: { totalFee: fee, rekeyTo: alice.address } - }; - - runtime.executeTx(txParams); - syncAccounts(); + rekeyFromAccount(runtime, bob, alice, alice); // check spend address assert.equal(alice.getSpendAddress(), alice.address); }); @@ -180,24 +251,10 @@ describe("Re-keying transactions", function () { const aliceBalanceBefore = alice.balance(); const bobBalanceBefore = bob.balance(); - txParams = { - type: types.TransactionType.TransferAlgo, - sign: types.SignType.SecretKey, - fromAccount: bob.account, - fromAccountAddr: alice.address, - toAccountAddr: bob.address, - amountMicroAlgos: 0, - payFlags: { - totalFee: fee, - closeRemainderTo: bob.address - } - }; - runtime.executeTx(txParams); - syncAccounts(); - + closeWithAccount(runtime, bob, alice, bob); // check account state after clsoe assert.equal(alice.balance(), 0n); - assert.equal(bob.balance(), aliceBalanceBefore + bobBalanceBefore - BigInt(fee)); + assert.equal(bob.balance(), aliceBalanceBefore + bobBalanceBefore - BigInt(FEE)); assert.equal(alice.getSpendAddress(), alice.address); }); }); @@ -205,17 +262,7 @@ describe("Re-keying transactions", function () { describe("Account to Lsig", function () { this.beforeEach(() => { // create rekey transaction - txParams = { - type: types.TransactionType.TransferAlgo, - sign: types.SignType.SecretKey, - fromAccount: alice.account, - toAccountAddr: alice.address, - amountMicroAlgos: 0, - payFlags: { totalFee: fee, rekeyTo: lsigAccount.address } - }; - - runtime.executeTx(txParams); - syncAccounts(); + rekeyFromAccount(runtime, alice, alice, lsigAccount); }); it("spend address of alice account should be lsig address", () => { @@ -224,64 +271,20 @@ describe("Re-keying transactions", function () { }); it("Transfer ALGO by valid spend account", () => { - // balance before rekey - const aliceBalanceBefore = alice.balance(); - const bobBalanceBefore = bob.balance(); - - // transfer ALGO use lsig - txParams = { - type: types.TransactionType.TransferAlgo, - sign: types.SignType.LogicSignature, - fromAccountAddr: alice.address, - toAccountAddr: bob.address, - amountMicroAlgos: amount, - lsig: lsig, - payFlags: { totalFee: fee } - }; - - runtime.executeTx(txParams); - - // check balance of alice and bob after transfer - syncAccounts(); - const aliceBalanceAfter = alice.balance(); - const bobBalanceAfter = bob.balance(); - - // transaction fee will paid by `from account` not `signer account` - assert.equal(aliceBalanceBefore, aliceBalanceAfter + amount + BigInt(fee)); - assert.equal(bobBalanceBefore + amount, bobBalanceAfter); + verifyTransferAlgoAuthByLsig(runtime, lsig, alice, bob, DEFAULT_AMOUNT); }); it("Should failed because cloneLsig is invalid spend address of alice account", () => { - txParams = { - type: types.TransactionType.TransferAlgo, - sign: types.SignType.LogicSignature, - fromAccountAddr: alice.address, - toAccountAddr: bob.address, - amountMicroAlgos: amount, - lsig: cloneLsig, - payFlags: { totalFee: fee } - }; - expectRuntimeError( - () => runtime.executeTx(txParams), + () => mkTxAlgoTransferFromLsig(runtime, cloneLsig, alice, bob, DEFAULT_AMOUNT, { totalFee: FEE }), RUNTIME_ERRORS.GENERAL.INVALID_AUTH_ACCOUNT, rekeyMessageError(alice.getSpendAddress(), cloneLsigAccount.address) ); }); it("Should failed: when use another account", () => { - txParams = { - type: types.TransactionType.TransferAlgo, - sign: types.SignType.SecretKey, - fromAccount: john.account, - fromAccountAddr: alice.address, - toAccountAddr: bob.address, - amountMicroAlgos: amount, - payFlags: { totalFee: fee } - }; - expectRuntimeError( - () => runtime.executeTx(txParams), + () => mkTxAlgoTransferFromAccount(runtime, john, alice, bob, DEFAULT_AMOUNT, { totalFee: FEE }), RUNTIME_ERRORS.GENERAL.INVALID_AUTH_ACCOUNT, rekeyMessageError(alice.getSpendAddress(), john.address) ); @@ -292,45 +295,18 @@ describe("Re-keying transactions", function () { const bobBalanceBefore = bob.balance(); // close alice account to bob - txParams = { - type: types.TransactionType.TransferAlgo, - sign: types.SignType.LogicSignature, - fromAccountAddr: alice.address, - toAccountAddr: bob.address, - amountMicroAlgos: 0n, - lsig: lsig, - payFlags: { - totalFee: fee, - closeRemainderTo: bob.address - } - }; - - runtime.executeTx(txParams); - syncAccounts(); + closeWithLsig(runtime, lsig, alice, bob); // check account state after close assert.equal(alice.balance(), 0n); - assert.equal(bob.balance(), aliceBalanceBefore + bobBalanceBefore - BigInt(fee)); + assert.equal(bob.balance(), aliceBalanceBefore + bobBalanceBefore - BigInt(FEE)); assert.equal(alice.getSpendAddress(), alice.address); }); }); describe("Lsig to Lsig", function () { this.beforeEach(() => { - txParams = { - type: types.TransactionType.TransferAlgo, - sign: types.SignType.LogicSignature, - fromAccountAddr: lsig.address(), - toAccountAddr: lsig.address(), - amountMicroAlgos: 0, - lsig: lsig, - payFlags: { - totalFee: fee, - rekeyTo: cloneLsigAccount.address - } - }; - runtime.executeTx(txParams); - syncAccounts(); + rekeyFromLsig(runtime, lsig, lsigAccount, cloneLsigAccount); }); it("Spend address of lsig should be cloneLsig address", () => { @@ -339,45 +315,12 @@ describe("Re-keying transactions", function () { }); it("Transfer ALGO by valid spend account", () => { - // balance before rekey - const lsigBalanceBefore = lsigAccount.balance(); - const aliceBalanceBefore = alice.balance(); - // transfer ALGO use lsig - txParams = { - type: types.TransactionType.TransferAlgo, - sign: types.SignType.LogicSignature, - fromAccountAddr: lsigAccount.address, - toAccountAddr: alice.address, - amountMicroAlgos: amount, - lsig: cloneLsig, - payFlags: { totalFee: fee } - }; - - runtime.executeTx(txParams); - - // check balance of alice and bob after transfer - syncAccounts(); - const lsigBalanceAfter = lsigAccount.balance(); - const aliceBalanceAfter = alice.balance(); - - // transaction fee will paid by `from account` not `signer account` - assert.equal(lsigBalanceBefore, lsigBalanceAfter + amount + BigInt(fee)); - assert.equal(aliceBalanceBefore + amount, aliceBalanceAfter); + verifyTransferAlgoAuthByLsig(runtime, cloneLsig, lsigAccount, alice, DEFAULT_AMOUNT); }); it("Should failed if signer is invalid spend account", () => { - txParams = { - type: types.TransactionType.TransferAlgo, - sign: types.SignType.LogicSignature, - fromAccountAddr: lsigAccount.address, - toAccountAddr: alice.address, - amountMicroAlgos: amount, - lsig: lsig, - payFlags: { totalFee: fee } - }; - expectRuntimeError( - () => runtime.executeTx(txParams), + () => mkTxAlgoTransferFromLsig(runtime, lsig, lsigAccount, alice, DEFAULT_AMOUNT, { totalFee: FEE }), RUNTIME_ERRORS.GENERAL.INVALID_AUTH_ACCOUNT, rekeyMessageError(lsigAccount.getSpendAddress(), lsigAccount.address) ); @@ -388,25 +331,10 @@ describe("Re-keying transactions", function () { const aliceBalanceBefore = alice.balance(); // close lsig account to alice - txParams = { - type: types.TransactionType.TransferAlgo, - sign: types.SignType.LogicSignature, - fromAccountAddr: lsigAccount.address, - toAccountAddr: bob.address, - amountMicroAlgos: 0n, - lsig: cloneLsig, - payFlags: { - totalFee: fee, - closeRemainderTo: alice.address - } - }; - - runtime.executeTx(txParams); - syncAccounts(); - + closeWithLsig(runtime, cloneLsig, lsigAccount, alice); // check account state after close assert.equal(lsigAccount.balance(), 0n); - assert.equal(alice.balance(), lsigAccountBalanceBefore + aliceBalanceBefore - BigInt(fee)); + assert.equal(alice.balance(), lsigAccountBalanceBefore + aliceBalanceBefore - BigInt(FEE)); assert.equal(lsigAccount.getSpendAddress(), lsigAccount.address); }); }); @@ -414,20 +342,7 @@ describe("Re-keying transactions", function () { describe("Lsig to account", function () { this.beforeEach(() => { // create rekey transaction - txParams = { - type: types.TransactionType.TransferAlgo, - sign: types.SignType.LogicSignature, - fromAccountAddr: lsig.address(), - toAccountAddr: lsig.address(), - amountMicroAlgos: 0, - lsig: lsig, - payFlags: { - totalFee: fee, - rekeyTo: bob.address - } - }; - runtime.executeTx(txParams); - syncAccounts(); + rekeyFromLsig(runtime, lsig, lsigAccount, bob); }); it("Spend address of lsig should change to bob", () => { @@ -436,45 +351,14 @@ describe("Re-keying transactions", function () { }); it("Transfer ALGO by spend account", () => { - // balance before rekey - const lsigBalanceBefore = lsigAccount.balance(); - const aliceBalanceBefore = alice.balance(); - // transfer ALGO use lsig - txParams = { - type: types.TransactionType.TransferAlgo, - sign: types.SignType.SecretKey, - fromAccount: bob.account, - fromAccountAddr: lsigAccount.address, - toAccountAddr: alice.address, - amountMicroAlgos: amount, - payFlags: { totalFee: fee } - }; - - runtime.executeTx(txParams); - - // check balance of alice and bob after transfer - syncAccounts(); - const lsigBalanceAfter = lsigAccount.balance(); - const aliceBalanceAfter = alice.balance(); - - // transaction fee will paid by `from account` not `signer account` - assert.equal(lsigBalanceBefore, lsigBalanceAfter + amount + BigInt(fee)); - assert.equal(aliceBalanceBefore + amount, aliceBalanceAfter); + verifyTransferAlgoAuthByAccount(runtime, bob, lsigAccount, alice, DEFAULT_AMOUNT); }); it("Should failed if alice is invalid spend address of lsig address", () => { - txParams = { - type: types.TransactionType.TransferAlgo, - sign: types.SignType.SecretKey, - fromAccount: alice.account, - fromAccountAddr: lsigAccount.address, - toAccountAddr: alice.address, - amountMicroAlgos: amount, - payFlags: { totalFee: fee } - }; - expectRuntimeError( - () => runtime.executeTx(txParams), + () => mkTxAlgoTransferFromAccount( + runtime, alice, lsigAccount, alice, DEFAULT_AMOUNT, { totalFee: FEE } + ), RUNTIME_ERRORS.GENERAL.INVALID_AUTH_ACCOUNT, rekeyMessageError(lsigAccount.getSpendAddress(), alice.address) ); @@ -483,29 +367,15 @@ describe("Re-keying transactions", function () { }); it("close account should remove auth/spend address", () => { - // balance before rekey + // balance before close const lsigBalanceBefore = lsigAccount.balance(); const aliceBalanceBefore = alice.balance(); - // transfer ALGO use lsig - txParams = { - type: types.TransactionType.TransferAlgo, - sign: types.SignType.SecretKey, - fromAccount: bob.account, - fromAccountAddr: lsigAccount.address, - toAccountAddr: alice.address, - amountMicroAlgos: 0n, - payFlags: { - totalFee: fee, - closeRemainderTo: alice.address - } - }; - - runtime.executeTx(txParams); + closeWithAccount(runtime, bob, lsigAccount, alice); // check account state after close syncAccounts(); assert.equal(lsigAccount.balance(), 0n); - assert.equal(alice.balance(), lsigBalanceBefore + aliceBalanceBefore - BigInt(fee)); + assert.equal(alice.balance(), lsigBalanceBefore + aliceBalanceBefore - BigInt(FEE)); assert.equal(lsigAccount.getSpendAddress(), lsigAccount.address); }); }); From 8f568bd0e9ac798f098e5165c138ab26ff49d61c Mon Sep 17 00:00:00 2001 From: Vu Vo Date: Wed, 9 Feb 2022 02:12:44 +0700 Subject: [PATCH 7/9] add verify close account helper --- packages/runtime/src/ctx.ts | 2 +- .../integration/rekey/rekey-transaction.ts | 89 +++++++++++-------- 2 files changed, 53 insertions(+), 38 deletions(-) diff --git a/packages/runtime/src/ctx.ts b/packages/runtime/src/ctx.ts index 9aefda9ca..78c57b174 100644 --- a/packages/runtime/src/ctx.ts +++ b/packages/runtime/src/ctx.ts @@ -392,7 +392,7 @@ export class Ctx implements Context { * @param index Index of current tx being processed in tx group */ deductFee (sender: AccountAddress, index: number, params: types.TxParams): void { - let fee: bigint = BigInt(this.gtxs[index].fee); + let fee: bigint = BigInt(this.gtxs[index].fee as number); // If flatFee boolean is not set, change fee value if (!params.flatFee && params.totalFee === undefined) { fee = BigInt(Math.max(ALGORAND_MIN_TX_FEE, Number(this.gtxs[index].fee))); diff --git a/packages/runtime/test/integration/rekey/rekey-transaction.ts b/packages/runtime/test/integration/rekey/rekey-transaction.ts index a7b1a41d7..9580a69bb 100644 --- a/packages/runtime/test/integration/rekey/rekey-transaction.ts +++ b/packages/runtime/test/integration/rekey/rekey-transaction.ts @@ -48,6 +48,7 @@ describe("Re-keying transactions", function () { cloneLsigAccount = runtime.getAccount(cloneLsig.address()); } + // create transfe algo transaction from lsig function mkTxAlgoTransferFromLsig ( runtime: Runtime, lsig: LogicSigAccount, @@ -70,6 +71,7 @@ describe("Re-keying transactions", function () { syncAccounts(); } + // create algo transfer transaction from normal account(use secret key) function mkTxAlgoTransferFromAccount ( runtime: Runtime, signer: AccountStoreI, @@ -91,6 +93,7 @@ describe("Re-keying transactions", function () { syncAccounts(); }; + // rekey normal account function rekeyFromAccount ( runtime: Runtime, signer: AccountStoreI, from: AccountStoreI, to: AccountStoreI ): void { @@ -103,6 +106,7 @@ describe("Re-keying transactions", function () { ); } + // rekey lsig account function rekeyFromLsig ( runtime: Runtime, lsig: LogicSigAccount, from: AccountStoreI, to: AccountStoreI ): void { @@ -115,6 +119,7 @@ describe("Re-keying transactions", function () { ); } + // close Account when auth/spend is account function closeWithAccount ( runtime: Runtime, signer: AccountStoreI, from: AccountStoreI, closeTo: AccountStoreI ): void { @@ -127,6 +132,7 @@ describe("Re-keying transactions", function () { ); } + // close Account when auth/spend is lsig function closeWithLsig ( runtime: Runtime, lsig: LogicSigAccount, from: AccountStoreI, closeTo: AccountStoreI ): void { @@ -139,6 +145,7 @@ describe("Re-keying transactions", function () { ); } + // verify transfer algo when auth/spend address is normal account function verifyTransferAlgoAuthByAccount ( runtime: Runtime, signer: AccountStoreI, from: AccountStoreI, to: AccountStoreI, algoAmount: bigint ): void { @@ -162,6 +169,7 @@ describe("Re-keying transactions", function () { assert.equal(toAccountBalanceBefore + algoAmount, toAccountBalanceAfter); } + // verify transfer algo when auth/spend address is lsig function verifyTransferAlgoAuthByLsig ( runtime: Runtime, lsig: LogicSigAccount, from: AccountStoreI, to: AccountStoreI, amount: bigint ): void { @@ -185,6 +193,46 @@ describe("Re-keying transactions", function () { assert.equal(toAccountBalanceBefore + amount, toAccountBalanceAfter); } + // verify close account when auth/spend address is normal account + function verifyCloseByAccount ( + runtime: Runtime, + signer: AccountStoreI, + from: AccountStoreI, + closeTo: AccountStoreI + ): void { + const fromBalanceBefore = from.balance(); + const closeToBalanceBefore = closeTo.balance(); + + closeWithAccount(runtime, signer, from, closeTo); + // sync account + from = runtime.getAccount(from.address); + closeTo = runtime.getAccount(closeTo.address); + // check account state after clsoe + assert.equal(from.balance(), 0n); + assert.equal(closeTo.balance(), fromBalanceBefore + closeToBalanceBefore - BigInt(FEE)); + assert.equal(from.getSpendAddress(), from.address); + } + + // verify close account when auth/spend address is lsig + function verifyCloseByLsig ( + runtime: Runtime, + lsig: LogicSigAccount, + from: AccountStoreI, + closeTo: AccountStoreI + ): void { + const fromBalanceBefore = from.balance(); + const closeToBalanceBefore = closeTo.balance(); + + closeWithLsig(runtime, lsig, from, closeTo); + // sync account + from = runtime.getAccount(from.address); + closeTo = runtime.getAccount(closeTo.address); + // check account state after clsoe + assert.equal(from.balance(), 0n); + assert.equal(closeTo.balance(), fromBalanceBefore + closeToBalanceBefore - BigInt(FEE)); + assert.equal(from.getSpendAddress(), from.address); + } + this.beforeEach(() => { // accounts master = new AccountStore(baseBalance); @@ -248,14 +296,7 @@ describe("Re-keying transactions", function () { }); it("close account should remove spend/auth address", () => { - const aliceBalanceBefore = alice.balance(); - const bobBalanceBefore = bob.balance(); - - closeWithAccount(runtime, bob, alice, bob); - // check account state after clsoe - assert.equal(alice.balance(), 0n); - assert.equal(bob.balance(), aliceBalanceBefore + bobBalanceBefore - BigInt(FEE)); - assert.equal(alice.getSpendAddress(), alice.address); + verifyCloseByAccount(runtime, bob, alice, bob); }); }); @@ -291,16 +332,7 @@ describe("Re-keying transactions", function () { }); it("close account should remove spend/auth address", () => { - const aliceBalanceBefore = alice.balance(); - const bobBalanceBefore = bob.balance(); - - // close alice account to bob - closeWithLsig(runtime, lsig, alice, bob); - - // check account state after close - assert.equal(alice.balance(), 0n); - assert.equal(bob.balance(), aliceBalanceBefore + bobBalanceBefore - BigInt(FEE)); - assert.equal(alice.getSpendAddress(), alice.address); + verifyCloseByLsig(runtime, lsig, alice, bob); }); }); @@ -327,15 +359,7 @@ describe("Re-keying transactions", function () { }); it("close account should remove spend/auth address", () => { - const lsigAccountBalanceBefore = lsigAccount.balance(); - const aliceBalanceBefore = alice.balance(); - - // close lsig account to alice - closeWithLsig(runtime, cloneLsig, lsigAccount, alice); - // check account state after close - assert.equal(lsigAccount.balance(), 0n); - assert.equal(alice.balance(), lsigAccountBalanceBefore + aliceBalanceBefore - BigInt(FEE)); - assert.equal(lsigAccount.getSpendAddress(), lsigAccount.address); + verifyCloseByLsig(runtime, cloneLsig, lsigAccount, alice); }); }); @@ -367,16 +391,7 @@ describe("Re-keying transactions", function () { }); it("close account should remove auth/spend address", () => { - // balance before close - const lsigBalanceBefore = lsigAccount.balance(); - const aliceBalanceBefore = alice.balance(); - - closeWithAccount(runtime, bob, lsigAccount, alice); - // check account state after close - syncAccounts(); - assert.equal(lsigAccount.balance(), 0n); - assert.equal(alice.balance(), lsigBalanceBefore + aliceBalanceBefore - BigInt(FEE)); - assert.equal(lsigAccount.getSpendAddress(), lsigAccount.address); + verifyCloseByAccount(runtime, bob, lsigAccount, alice); }); }); From 13e3ba444cb67959587717ffa1eb33dfafa944ef Mon Sep 17 00:00:00 2001 From: Vu Vo Date: Wed, 9 Feb 2022 16:59:23 +0700 Subject: [PATCH 8/9] fix review --- .../test/integration/rekey/rekey-transaction.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/runtime/test/integration/rekey/rekey-transaction.ts b/packages/runtime/test/integration/rekey/rekey-transaction.ts index 9580a69bb..e8046d420 100644 --- a/packages/runtime/test/integration/rekey/rekey-transaction.ts +++ b/packages/runtime/test/integration/rekey/rekey-transaction.ts @@ -155,10 +155,7 @@ describe("Re-keying transactions", function () { // transfer ALGO by spend account mkTxAlgoTransferFromAccount( - runtime, signer, from, to, algoAmount, - { - totalFee: FEE - } + runtime, signer, from, to, algoAmount, { totalFee: FEE } ); const fromAccountBalanceAfter = runtime.getAccount(from.address).balance(); @@ -179,10 +176,7 @@ describe("Re-keying transactions", function () { // transfer ALGO by spend account mkTxAlgoTransferFromLsig( - runtime, lsig, from, to, amount, - { - totalFee: FEE - } + runtime, lsig, from, to, amount, { totalFee: FEE } ); const fromAccountBalanceAfter = runtime.getAccount(from.address).balance(); @@ -194,6 +188,7 @@ describe("Re-keying transactions", function () { } // verify close account when auth/spend address is normal account + // close `from` account to `closeTo` account function verifyCloseByAccount ( runtime: Runtime, signer: AccountStoreI, @@ -214,6 +209,7 @@ describe("Re-keying transactions", function () { } // verify close account when auth/spend address is lsig + // close `from` account to `closeTo` account function verifyCloseByLsig ( runtime: Runtime, lsig: LogicSigAccount, @@ -253,7 +249,7 @@ describe("Re-keying transactions", function () { syncAccounts(); }); - it("Validate spend address after init", () => { + it("Validate account state before apply rekey transaciton", () => { assert.equal(alice.getSpendAddress(), alice.address); assert.equal(bob.getSpendAddress(), bob.address); assert.equal(lsigAccount.getSpendAddress(), lsigAccount.address); @@ -296,6 +292,7 @@ describe("Re-keying transactions", function () { }); it("close account should remove spend/auth address", () => { + // close account alice to bob verifyCloseByAccount(runtime, bob, alice, bob); }); }); From 7163a56cdc088ede77135717a340cfd1976582a4 Mon Sep 17 00:00:00 2001 From: Vu Vo Date: Wed, 9 Feb 2022 17:11:41 +0700 Subject: [PATCH 9/9] update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52f7dedd8..5a5eee9a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,11 @@ Features, Bug Fixes, Breaking Changes, Deprecated - Used app account instead of `deposit_lsig` in `examples/dao` - Support RekeyTo field in the inner transaction for TEAL v6. +### Bug fixes + +- Return error when closeRemainderTo and fromAccountAddr is the same. +- When close account should remove auth/spend address. Fixed in [#575](https://github.com/scale-it/algo-builder/pull/575). + ### Infrastructure - Support for run command `setup-master-account` and `sandbox-setup-master-account` more than one time.