"use strict";
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());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DefaultTransactionAPI = void 0;
const constants_1 = require("../utils/constants");
class DefaultTransactionAPI {
    constructor(api, account) {
        this.api = api;
        this.account = account;
    }
    setAccount(account) {
        this.account = account;
    }
    removeAccount() {
        this.account = undefined;
    }
    getAccount() {
        return this.account;
    }
    sendLogged(transaction, successEventType, onlyInBlock) {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.account === undefined) {
                return Promise.reject(new Error(constants_1.ACCOUNT_NOT_SET_ERROR_MESSAGE));
            }
            return DefaultTransactionAPI.sendLogged(this.api, this.account, transaction, successEventType, onlyInBlock);
        });
    }
    static sendLogged(api, account, transaction, successEventType, onlyInBlock) {
        return __awaiter(this, void 0, void 0, function* () {
            const { unsubscribe, result } = yield new Promise((resolve, reject) => {
                let unsubscribe;
                // When passing { nonce: -1 } to signAndSend the API will use system.accountNextIndex to determine the nonce
                transaction
                    .signAndSend(account, { nonce: -1 }, (result) => callback({ unsubscribe, result }))
                    .then((u) => (unsubscribe = u))
                    .catch((error) => reject(error));
                function callback(callbackObject) {
                    const status = callbackObject.result.status;
                    if (onlyInBlock) {
                        if (status.isInBlock) {
                            resolve(callbackObject);
                        }
                    }
                    else {
                        if (status.isFinalized) {
                            resolve(callbackObject);
                        }
                    }
                }
            });
            if (onlyInBlock) {
                console.log(`Transaction included at blockHash ${result.status.asInBlock}`);
            }
            else {
                console.log(`Transaction finalized at blockHash ${result.status.asFinalized}`);
            }
            unsubscribe(result);
            // Print all events for debugging
            DefaultTransactionAPI.printEvents(api, result.events);
            const dispatchError = result.dispatchError;
            if (dispatchError) {
                // Construct error message
                let message = "The transaction failed.";
                // Runtime error in one of the parachain modules
                if (dispatchError.isModule) {
                    // for module errors, we have the section indexed, lookup
                    const decoded = api.registry.findMetaError(dispatchError.asModule);
                    const { docs, name, section } = decoded;
                    message = message.concat(` The error code is ${section}.${name}. ${docs.join(" ")}`);
                    // Bad origin
                }
                else if (dispatchError.isBadOrigin) {
                    message = message.concat(` The error is caused by using an incorrect account.
                The error code is BadOrigin ${dispatchError}.`);
                }
                // Other, CannotLookup, no extra info
                else {
                    message = message.concat(` The error is ${dispatchError}.`);
                }
                console.log(message);
                return Promise.reject(new Error(message));
            }
            return result;
        });
    }
    static printEvents(api, events) {
        let foundErrorEvent = false;
        let errorMessage = "";
        events
            .map(({ event }) => event.data)
            .forEach((eventData) => {
            if (DefaultTransactionAPI.isDispatchError(eventData)) {
                try {
                    const { docs, name, section } = api.registry.findMetaError(eventData.asModule);
                    if ((docs === null || docs === void 0 ? void 0 : docs.length) > 0) {
                        errorMessage = `${section}.${name}: ${docs.join(" ")}`;
                    }
                    else {
                        errorMessage = `${section}.${name}`;
                    }
                    foundErrorEvent = true;
                }
                catch (err) {
                    errorMessage = "Error. Could not find transaction failure details.";
                }
            }
        });
        if (!foundErrorEvent) {
            events.forEach(({ phase, event: { data, method, section } }) => {
                console.log(`\t' ${phase}: ${section}.${method}:: ${data}`);
            });
        }
        else if (!constants_1.IGNORED_ERROR_MESSAGES.includes(errorMessage)) {
            throw new Error(errorMessage);
        }
    }
    static waitForEvent(api, event, timeoutMs) {
        return __awaiter(this, void 0, void 0, function* () {
            // Use this function with a timeout.
            // Unless the awaited event occurs, this Promise will never resolve.
            let timeoutHandle;
            const timeoutPromise = new Promise((_, reject) => {
                timeoutHandle = setTimeout(() => reject(), timeoutMs);
            });
            yield Promise.race([
                new Promise((resolve, _reject) => {
                    api.query.system.events((eventsVec) => {
                        const events = eventsVec.toArray();
                        if (this.doesArrayContainEvent(events, event)) {
                            resolve();
                        }
                    });
                }),
                timeoutPromise,
            ]).then((_) => {
                clearTimeout(timeoutHandle);
            });
            return true;
        });
    }
    static isDispatchError(eventData) {
        return eventData.isModule !== undefined;
    }
    static doesArrayContainEvent(events, eventType) {
        for (const { event } of events) {
            if (eventType.is(event)) {
                return true;
            }
        }
        return false;
    }
}
exports.DefaultTransactionAPI = DefaultTransactionAPI;
//# sourceMappingURL=transaction.js.map