"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const types_1 = require("../../polkadot/types");
const sinon_1 = __importDefault(require("sinon"));
const internal_1 = require("../../internal");
const testUtils_1 = require("../../testUtils");
const mocks_1 = require("../../testUtils/mocks");
const types_2 = require("../../types");
const utils_1 = require("../../types/utils");
const utilsConversionModule = __importStar(require("../../utils/conversion"));
describe('Polymesh Transaction Base class', () => {
    let context;
    beforeAll(() => {
        mocks_1.dsMockUtils.initMocks();
        mocks_1.entityMockUtils.initMocks();
    });
    beforeEach(() => {
        context = mocks_1.dsMockUtils.getContextInstance();
    });
    const txSpec = {
        signer: 'signer',
        isCritical: false,
        fee: new bignumber_js_1.default(100),
    };
    afterEach(() => {
        mocks_1.dsMockUtils.reset();
        mocks_1.entityMockUtils.reset();
    });
    afterAll(() => {
        mocks_1.dsMockUtils.cleanup();
        mocks_1.entityMockUtils.cleanup();
    });
    describe('method: run', () => {
        beforeEach(() => {
            mocks_1.dsMockUtils.createRpcStub('chain', 'getBlock', {
                returnValue: mocks_1.dsMockUtils.createMockSignedBlock({
                    block: {
                        header: {
                            number: mocks_1.dsMockUtils.createMockCompact(mocks_1.dsMockUtils.createMockU32(1)),
                            parentHash: 'hash',
                            stateRoot: 'hash',
                            extrinsicsRoot: 'hash',
                        },
                    },
                }),
            });
        });
        test('should execute the underlying transaction with the provided arguments, setting the tx and block hash when finished', () => __awaiter(void 0, void 0, void 0, function* () {
            const transaction = mocks_1.dsMockUtils.createTxStub('asset', 'registerTicker', {
                autoResolve: false,
            });
            const args = utils_1.tuple('A_TICKER');
            const tx = new internal_1.PolymeshTransaction(Object.assign(Object.assign({}, txSpec), { transaction,
                args }), context);
            tx.run();
            mocks_1.dsMockUtils.updateTxStatus(transaction, mocks_1.dsMockUtils.MockTxStatus.InBlock);
            yield testUtils_1.fakePromise();
            mocks_1.dsMockUtils.updateTxStatus(transaction, mocks_1.dsMockUtils.MockTxStatus.Succeeded);
            yield testUtils_1.fakePromise();
            sinon_1.default.assert.calledWith(transaction, ...args);
            expect(tx.blockHash).toBeDefined();
            expect(tx.blockNumber).toBeDefined();
            expect(tx.txHash).toBeDefined();
            expect(tx.status).toBe(types_2.TransactionStatus.Succeeded);
        }));
        test('should unwrap PostTransactionValue arguments', () => __awaiter(void 0, void 0, void 0, function* () {
            const transaction = mocks_1.dsMockUtils.createTxStub('asset', 'registerTicker', {
                autoResolve: false,
            });
            const ticker = 'A_DIFFERENT_TICKER';
            const postTransactionTicker = new internal_1.PostTransactionValue(() => __awaiter(void 0, void 0, void 0, function* () { return ticker; }));
            yield postTransactionTicker.run({});
            const args = utils_1.tuple(postTransactionTicker);
            const tx = new internal_1.PolymeshTransaction(Object.assign(Object.assign({}, txSpec), { transaction,
                args }), context);
            tx.run();
            mocks_1.dsMockUtils.updateTxStatus(transaction, mocks_1.dsMockUtils.MockTxStatus.InBlock);
            yield testUtils_1.fakePromise();
            mocks_1.dsMockUtils.updateTxStatus(transaction, mocks_1.dsMockUtils.MockTxStatus.Succeeded);
            yield testUtils_1.fakePromise();
            sinon_1.default.assert.calledWith(transaction, ticker);
            expect(tx.blockHash).toBeDefined();
            expect(tx.blockNumber).toBeDefined();
            expect(tx.txHash).toBeDefined();
            expect(tx.status).toBe(types_2.TransactionStatus.Succeeded);
        }));
        test('should update the transaction status', () => __awaiter(void 0, void 0, void 0, function* () {
            const transaction = mocks_1.dsMockUtils.createTxStub('asset', 'registerTicker', {
                autoResolve: false,
            });
            const args = utils_1.tuple('ANOTHER_TICKER');
            const tx = new internal_1.PolymeshTransaction(Object.assign(Object.assign({}, txSpec), { transaction,
                args }), context);
            expect(tx.status).toBe(types_2.TransactionStatus.Idle);
            tx.run();
            expect(tx.status).toBe(types_2.TransactionStatus.Unapproved);
            mocks_1.dsMockUtils.updateTxStatus(transaction, mocks_1.dsMockUtils.MockTxStatus.Ready);
            yield testUtils_1.fakePromise();
            expect(tx.status).toBe(types_2.TransactionStatus.Running);
            mocks_1.dsMockUtils.updateTxStatus(transaction, mocks_1.dsMockUtils.MockTxStatus.Intermediate);
            yield testUtils_1.fakePromise();
            expect(tx.status).toBe(types_2.TransactionStatus.Running);
            mocks_1.dsMockUtils.updateTxStatus(transaction, mocks_1.dsMockUtils.MockTxStatus.InBlock);
            yield testUtils_1.fakePromise();
            expect(tx.status).toBe(types_2.TransactionStatus.Running);
            mocks_1.dsMockUtils.updateTxStatus(transaction, mocks_1.dsMockUtils.MockTxStatus.Succeeded);
            yield testUtils_1.fakePromise();
            expect(tx.status).toBe(types_2.TransactionStatus.Succeeded);
        }));
        test('should resolve all postValues', () => __awaiter(void 0, void 0, void 0, function* () {
            const transaction = mocks_1.dsMockUtils.createTxStub('asset', 'registerTicker');
            const args = utils_1.tuple('YET_ANOTHER_TICKER');
            const firstStub = sinon_1.default.stub().resolves(1);
            const secondStub = sinon_1.default.stub().resolves('someString');
            const postTransactionValues = [
                { run: firstStub },
                { run: secondStub },
            ];
            const tx = new internal_1.PolymeshTransaction(Object.assign(Object.assign({}, txSpec), { transaction,
                args,
                postTransactionValues }), context);
            yield tx.run();
            sinon_1.default.assert.calledOnce(firstStub);
            sinon_1.default.assert.calledOnce(secondStub);
        }));
        test('should throw an error when the transaction is aborted', () => __awaiter(void 0, void 0, void 0, function* () {
            const transaction = mocks_1.dsMockUtils.createTxStub('asset', 'registerTicker', {
                autoResolve: mocks_1.dsMockUtils.MockTxStatus.Aborted,
            });
            const args = utils_1.tuple('IT_HURTS');
            const tx = new internal_1.PolymeshTransaction(Object.assign(Object.assign({}, txSpec), { transaction,
                args }), context);
            yield expect(tx.run()).rejects.toThrow('The transaction was removed from the transaction pool. This might mean that it was malformed (nonce too large/nonce too small/duplicated or invalid transaction)');
            expect(tx.status).toBe(types_2.TransactionStatus.Aborted);
        }));
        test('should throw an error when the transaction fails', () => __awaiter(void 0, void 0, void 0, function* () {
            let transaction = mocks_1.dsMockUtils.createTxStub('asset', 'registerTicker', { autoResolve: false });
            const args = utils_1.tuple('PLEASE_MAKE_IT_STOP');
            let tx = new internal_1.PolymeshTransaction(Object.assign(Object.assign({}, txSpec), { transaction,
                args }), context);
            let runPromise = tx.run();
            mocks_1.dsMockUtils.updateTxStatus(transaction, mocks_1.dsMockUtils.MockTxStatus.Failed, mocks_1.dsMockUtils.TxFailReason.BadOrigin);
            yield expect(runPromise).rejects.toThrow('Bad origin');
            expect(tx.status).toBe(types_2.TransactionStatus.Failed);
            transaction = mocks_1.dsMockUtils.createTxStub('asset', 'registerTicker', { autoResolve: false });
            tx = new internal_1.PolymeshTransaction(Object.assign(Object.assign({}, txSpec), { transaction,
                args }), context);
            runPromise = tx.run();
            mocks_1.dsMockUtils.updateTxStatus(transaction, mocks_1.dsMockUtils.MockTxStatus.Failed, mocks_1.dsMockUtils.TxFailReason.CannotLookup);
            yield expect(runPromise).rejects.toThrow('Could not lookup information required to validate the transaction');
            expect(tx.status).toBe(types_2.TransactionStatus.Failed);
            transaction = mocks_1.dsMockUtils.createTxStub('asset', 'registerTicker', { autoResolve: false });
            tx = new internal_1.PolymeshTransaction(Object.assign(Object.assign({}, txSpec), { transaction,
                args }), context);
            runPromise = tx.run();
            mocks_1.dsMockUtils.updateTxStatus(transaction, mocks_1.dsMockUtils.MockTxStatus.Failed, mocks_1.dsMockUtils.TxFailReason.Other);
            yield expect(runPromise).rejects.toThrow('Unknown error');
            expect(tx.status).toBe(types_2.TransactionStatus.Failed);
            transaction = mocks_1.dsMockUtils.createTxStub('asset', 'registerTicker', { autoResolve: false });
            tx = new internal_1.PolymeshTransaction(Object.assign(Object.assign({}, txSpec), { transaction,
                args }), context);
            runPromise = tx.run();
            mocks_1.dsMockUtils.updateTxStatus(transaction, mocks_1.dsMockUtils.MockTxStatus.Failed, mocks_1.dsMockUtils.TxFailReason.Module);
            yield expect(runPromise).rejects.toThrow('someModule.SomeError: This is very bad');
            expect(tx.status).toBe(types_2.TransactionStatus.Failed);
        }));
        test('should throw an error when the transaction is rejected', () => __awaiter(void 0, void 0, void 0, function* () {
            const transaction = mocks_1.dsMockUtils.createTxStub('asset', 'registerTicker', {
                autoResolve: mocks_1.dsMockUtils.MockTxStatus.Rejected,
            });
            const args = utils_1.tuple('THIS_IS_THE_LAST_ONE_I_SWEAR');
            const tx = new internal_1.PolymeshTransaction(Object.assign(Object.assign({}, txSpec), { transaction,
                args }), context);
            yield expect(tx.run()).rejects.toThrow('The user canceled the transaction signature');
            expect(tx.status).toBe(types_2.TransactionStatus.Rejected);
        }));
    });
    describe('method: onStatusChange', () => {
        test("should execute a callback when the transaction's status changes", () => __awaiter(void 0, void 0, void 0, function* () {
            const transaction = mocks_1.dsMockUtils.createTxStub('asset', 'registerTicker');
            const args = utils_1.tuple('I_HAVE_LOST_THE_WILL_TO_LIVE');
            const tx = new internal_1.PolymeshTransaction(Object.assign(Object.assign({}, txSpec), { transaction,
                args }), context);
            const listenerStub = sinon_1.default.stub();
            tx.onStatusChange(t => listenerStub(t.status));
            yield tx.run();
            sinon_1.default.assert.calledWith(listenerStub.firstCall, types_2.TransactionStatus.Unapproved);
            sinon_1.default.assert.calledWith(listenerStub.secondCall, types_2.TransactionStatus.Running);
            sinon_1.default.assert.calledWith(listenerStub.thirdCall, types_2.TransactionStatus.Succeeded);
        }));
        test('should return an unsubscribe function', () => __awaiter(void 0, void 0, void 0, function* () {
            const transaction = mocks_1.dsMockUtils.createTxStub('asset', 'registerTicker', {
                autoResolve: false,
            });
            const args = utils_1.tuple('THE_ONLY_THING_THAT_KEEPS_ME_GOING_IS_THE_HOPE_OF_FULL_COVERAGE');
            const tx = new internal_1.PolymeshTransaction(Object.assign(Object.assign({}, txSpec), { transaction,
                args }), context);
            const listenerStub = sinon_1.default.stub();
            const unsub = tx.onStatusChange(t => listenerStub(t.status));
            tx.run();
            yield testUtils_1.fakePromise();
            unsub();
            sinon_1.default.assert.calledWith(listenerStub.firstCall, types_2.TransactionStatus.Unapproved);
            sinon_1.default.assert.calledWith(listenerStub.secondCall, types_2.TransactionStatus.Running);
            sinon_1.default.assert.callCount(listenerStub, 2);
        }));
    });
    describe('method: getFees', () => {
        let balanceToBigNumberStub;
        let protocolFees;
        let gasFees;
        let rawGasFees;
        beforeAll(() => {
            balanceToBigNumberStub = sinon_1.default.stub(utilsConversionModule, 'balanceToBigNumber');
            protocolFees = [new bignumber_js_1.default(250), new bignumber_js_1.default(150)];
            gasFees = [5, 10];
            rawGasFees = gasFees.map(mocks_1.dsMockUtils.createMockBalance);
        });
        beforeEach(() => {
            context.getProtocolFees
                .withArgs({ tag: types_1.TxTags.asset.RegisterTicker })
                .resolves(protocolFees[0]);
            context.getProtocolFees.withArgs({ tag: types_1.TxTags.asset.CreateAsset }).resolves(protocolFees[1]);
            rawGasFees.forEach((rawGasFee, index) => balanceToBigNumberStub.withArgs(rawGasFee).returns(new bignumber_js_1.default(gasFees[index])));
        });
        test('should fetch (if missing) and return transaction fees', () => __awaiter(void 0, void 0, void 0, function* () {
            const tx1 = mocks_1.dsMockUtils.createTxStub('asset', 'registerTicker', { gas: rawGasFees[0] });
            const tx2 = mocks_1.dsMockUtils.createTxStub('asset', 'createAsset', { gas: rawGasFees[1] });
            mocks_1.dsMockUtils.createTxStub('utility', 'batchAtomic', { gas: rawGasFees[1] });
            const args = utils_1.tuple('OH_GOD_NO_IT_IS_BACK');
            let tx = new internal_1.PolymeshTransaction(Object.assign(Object.assign({}, txSpec), { transaction: tx1, args, fee: undefined }), context);
            let result = yield tx.getFees();
            expect(result === null || result === void 0 ? void 0 : result.protocol).toEqual(new bignumber_js_1.default(250));
            expect(result === null || result === void 0 ? void 0 : result.gas).toEqual(new bignumber_js_1.default(5));
            tx = new internal_1.PolymeshTransaction(Object.assign(Object.assign({}, txSpec), { transaction: tx1, args, fee: undefined, feeMultiplier: 2 }), context);
            result = yield tx.getFees();
            expect(result === null || result === void 0 ? void 0 : result.protocol).toEqual(new bignumber_js_1.default(500));
            expect(result === null || result === void 0 ? void 0 : result.gas).toEqual(new bignumber_js_1.default(5));
            tx = new internal_1.PolymeshTransaction(Object.assign(Object.assign({}, txSpec), { fee: new bignumber_js_1.default(protocolFees[1]), transaction: tx2, args }), context);
            result = yield tx.getFees();
            expect(result === null || result === void 0 ? void 0 : result.protocol).toEqual(new bignumber_js_1.default(150));
            expect(result === null || result === void 0 ? void 0 : result.gas).toEqual(new bignumber_js_1.default(10));
            tx = new internal_1.PolymeshTransaction(Object.assign(Object.assign({}, txSpec), { fee: new bignumber_js_1.default(protocolFees[1]), transaction: tx2, args }), context);
            result = yield tx.getFees();
            expect(result === null || result === void 0 ? void 0 : result.protocol).toEqual(new bignumber_js_1.default(150));
            expect(result === null || result === void 0 ? void 0 : result.gas).toEqual(new bignumber_js_1.default(10));
            tx = new internal_1.PolymeshTransactionBatch(Object.assign(Object.assign({}, txSpec), { fee: undefined, transactions: [
                    {
                        transaction: tx1,
                        args,
                    },
                    {
                        transaction: tx2,
                        args,
                    },
                ] }), context);
            result = yield tx.getFees();
            expect(result === null || result === void 0 ? void 0 : result.protocol).toEqual(new bignumber_js_1.default(400));
            expect(result === null || result === void 0 ? void 0 : result.gas).toEqual(new bignumber_js_1.default(10));
        }));
        test('should return null if the transaction arguments are not ready', () => __awaiter(void 0, void 0, void 0, function* () {
            const transaction = mocks_1.dsMockUtils.createTxStub('asset', 'registerTicker', {
                gas: rawGasFees[0],
            });
            const args = utils_1.tuple('WILL_IT_EVER_BE_OVER?');
            const tx = new internal_1.PolymeshTransaction(Object.assign(Object.assign({}, txSpec), { transaction, args: args.map(arg => new internal_1.PostTransactionValue(() => __awaiter(void 0, void 0, void 0, function* () { return arg; }))) }), context);
            const result = yield tx.getFees();
            expect(result).toBe(null);
        }));
    });
    describe('method: getPayingAccount', () => {
        test('should return null if the current Account should pay', () => __awaiter(void 0, void 0, void 0, function* () {
            const transaction = mocks_1.dsMockUtils.createTxStub('asset', 'registerTicker');
            const args = utils_1.tuple('SOMETHING');
            const tx = new internal_1.PolymeshTransaction(Object.assign(Object.assign({}, txSpec), { transaction,
                args }), context);
            const result = yield tx.getPayingAccount();
            expect(result).toBeNull();
        }));
        test('should return null if the transaction ignores subsidy', () => __awaiter(void 0, void 0, void 0, function* () {
            const transaction = mocks_1.dsMockUtils.createTxStub('relayer', 'removePayingKey');
            const account = mocks_1.entityMockUtils.getAccountInstance();
            const allowance = new bignumber_js_1.default(100);
            context.accountSubsidy.resolves({
                subsidizer: account,
                allowance,
            });
            const args = utils_1.tuple('SOMETHING_ELSE');
            const tx = new internal_1.PolymeshTransaction(Object.assign(Object.assign({}, txSpec), { transaction,
                args }), context);
            const result = yield tx.getPayingAccount();
            expect(result).toBeNull();
        }));
        test('should return a null allowance if the transaction is paid for a fixed third party', () => __awaiter(void 0, void 0, void 0, function* () {
            const transaction = mocks_1.dsMockUtils.createTxStub('asset', 'registerTicker');
            const account = mocks_1.entityMockUtils.getAccountInstance();
            const paidForBy = mocks_1.entityMockUtils.getIdentityInstance({
                getPrimaryAccount: {
                    account,
                    permissions: {
                        assets: null,
                        portfolios: null,
                        transactions: null,
                        transactionGroups: [],
                    },
                },
            });
            const args = utils_1.tuple('SOMETHING');
            const tx = new internal_1.PolymeshTransaction(Object.assign(Object.assign({}, txSpec), { transaction,
                args,
                paidForBy }), context);
            const result = yield tx.getPayingAccount();
            expect(result === null || result === void 0 ? void 0 : result.account.address).toEqual(account.address);
            expect(result === null || result === void 0 ? void 0 : result.allowance).toBeNull();
        }));
        test('should return the account and allowance if the transaction is being subsidized', () => __awaiter(void 0, void 0, void 0, function* () {
            const transaction = mocks_1.dsMockUtils.createTxStub('asset', 'registerTicker');
            const account = mocks_1.entityMockUtils.getAccountInstance();
            const allowance = new bignumber_js_1.default(100);
            context.accountSubsidy.resolves({
                subsidizer: account,
                allowance,
            });
            const args = utils_1.tuple('SOMETHING');
            let tx = new internal_1.PolymeshTransaction(Object.assign(Object.assign({}, txSpec), { transaction,
                args }), context);
            let result = yield tx.getPayingAccount();
            expect(result === null || result === void 0 ? void 0 : result.account).toEqual(account);
            expect(result === null || result === void 0 ? void 0 : result.allowance).toBe(allowance);
            tx = new internal_1.PolymeshTransactionBatch(Object.assign(Object.assign({}, txSpec), { transactions: [
                    {
                        transaction,
                        args,
                    },
                ] }), context);
            result = yield tx.getPayingAccount();
            expect(result === null || result === void 0 ? void 0 : result.account).toEqual(account);
            expect(result === null || result === void 0 ? void 0 : result.allowance).toBe(allowance);
        }));
    });
});
//# sourceMappingURL=PolymeshTransactionBase.js.map