"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const vector_types_1 = require("@connext/vector-types");
const vector_utils_1 = require("@connext/vector-utils");
const constants_1 = require("@ethersproject/constants");
const providers_1 = require("@ethersproject/providers");
const ethers_1 = require("ethers");
const utils_1 = require("ethers/lib/utils");
const sinon_1 = require("sinon");
const uuid_1 = require("uuid");
const ethService_1 = require("./ethService");
let storeMock;
let signer;
let ethService;
let provider1337;
let provider1338;
let sendTxWithRetriesMock;
let approveMock;
let getCodeMock;
let getOnchainBalanceMock;
let waitForConfirmation;
let getGasPrice;
let channelState;
const assertResult = (result, isError, unwrappedVal) => {
    var _a;
    if (isError) {
        vector_utils_1.expect(result.isError).to.be.true;
        if (unwrappedVal) {
            vector_utils_1.expect((_a = result.getError()) === null || _a === void 0 ? void 0 : _a.message).to.be.eq(unwrappedVal);
        }
    }
    else {
        vector_utils_1.expect(result.isError).to.be.false;
        if (unwrappedVal) {
            vector_utils_1.expect(result.getValue()).to.deep.eq(unwrappedVal);
        }
    }
};
const txResponse = {
    chainId: 1337,
    confirmations: 1,
    data: "0x",
    from: constants_1.AddressZero,
    gasLimit: constants_1.One,
    gasPrice: constants_1.One,
    hash: vector_utils_1.mkHash(),
    nonce: 1,
    value: constants_1.Zero,
    wait: () => Promise.resolve({}),
};
const txReceipt = {
    blockHash: vector_utils_1.mkHash("0xabc"),
    blockNumber: 123,
    byzantium: true,
    confirmations: 1,
    contractAddress: vector_utils_1.mkAddress("0xa"),
    cumulativeGasUsed: ethers_1.BigNumber.from(21000),
    from: txResponse.from,
    gasUsed: ethers_1.BigNumber.from(21000),
    logs: [],
    logsBloom: "0x",
    to: vector_utils_1.mkAddress("0xbbb"),
    transactionHash: txResponse.hash,
    transactionIndex: 1,
    status: 1,
};
const { log } = vector_utils_1.getTestLoggers("ethService");
describe("ethService unit test", () => {
    beforeEach(() => {
        storeMock = sinon_1.createStubInstance(vector_utils_1.MemoryStoreService);
        signer = sinon_1.createStubInstance(vector_utils_1.ChannelSigner);
        signer.connect.returns(signer);
        signer._isSigner = true;
        const _provider = sinon_1.createStubInstance(providers_1.JsonRpcProvider);
        _provider.getTransaction.resolves(txResponse);
        provider1337 = _provider;
        provider1338 = _provider;
        signer.provider = provider1337;
        ethService = new ethService_1.EthereumChainService(storeMock, {
            1337: provider1337,
            1338: provider1338,
        }, signer, log);
        getCodeMock = sinon_1.stub(ethService, "getCode").resolves(vector_types_1.Result.ok("0x"));
        approveMock = sinon_1.stub(ethService, "approveTokens").resolves(vector_types_1.Result.ok(txReceipt));
        getOnchainBalanceMock = sinon_1.stub(ethService, "getOnchainBalance").resolves(vector_types_1.Result.ok(ethers_1.BigNumber.from("100")));
        getGasPrice = sinon_1.stub(ethService, "getGasPrice").resolves(vector_types_1.Result.ok(utils_1.parseUnits("1", "gwei")));
        const test = vector_utils_1.createTestChannelState("create");
        channelState = test.channel;
        channelState.networkContext.chainId = 1337;
        signer.getAddress.resolves(channelState.alice);
    });
    afterEach(() => {
        sinon_1.restore();
        sinon_1.reset();
    });
    describe("sendDeployChannelTx", () => {
        beforeEach(() => {
            sendTxWithRetriesMock = sinon_1.stub(ethService, "sendTxWithRetries").resolves(vector_types_1.Result.ok(txReceipt));
        });
        it("errors if cannot get a signer", async () => {
            channelState.networkContext.chainId = 1234;
            const result = await ethService.sendDeployChannelTx(channelState);
            assertResult(result, true, vector_types_1.ChainError.reasons.SignerNotFound);
        });
        it("errors if multisig code cannot be retrieved", async () => {
            getCodeMock.resolves(vector_types_1.Result.fail(new vector_types_1.ChainError("getCode error")));
            const result = await ethService.sendDeployChannelTx(channelState);
            assertResult(result, true, "getCode error");
        });
        it("errors if multisig is already deployed", async () => {
            getCodeMock.resolves(vector_types_1.Result.ok(vector_utils_1.mkHash("0xabc")));
            const result = await ethService.sendDeployChannelTx(channelState);
            assertResult(result, true, vector_types_1.ChainError.reasons.MultisigDeployed);
        });
        it("errors if multisig deployment fails without deposit", async () => {
            sendTxWithRetriesMock.resolves(vector_types_1.Result.fail(new vector_types_1.ChainError(vector_types_1.ChainError.reasons.TxReverted)));
            const result = await ethService.sendDeployChannelTx(channelState);
            assertResult(result, true, vector_types_1.ChainError.reasons.TxReverted);
        });
        it("errors if multisig deployment returns nothing", async () => {
            sendTxWithRetriesMock.resolves(vector_types_1.Result.ok(undefined));
            const result = await ethService.sendDeployChannelTx(channelState);
            assertResult(result, true, vector_types_1.ChainError.reasons.MultisigDeployed);
        });
        it("errors if deposit and is not alice", async () => {
            signer.getAddress.resolves(channelState.bob);
            const result = await ethService.sendDeployChannelTx(channelState, {
                amount: "1",
                assetId: constants_1.AddressZero,
            });
            assertResult(result, true, vector_types_1.ChainError.reasons.FailedToDeploy);
        });
        it("errors if deposit and cannot get onchain balance", async () => {
            getOnchainBalanceMock.resolves(vector_types_1.Result.fail(new vector_types_1.ChainError(vector_types_1.ChainError.reasons.TxNotFound)));
            const result = await ethService.sendDeployChannelTx(channelState, {
                amount: "1",
                assetId: constants_1.AddressZero,
            });
            assertResult(result, true, vector_types_1.ChainError.reasons.TxNotFound);
        });
        it("errors if deposit and not enough onchain balance", async () => {
            getOnchainBalanceMock.resolves(vector_types_1.Result.ok(ethers_1.BigNumber.from("9")));
            const result = await ethService.sendDeployChannelTx(channelState, {
                amount: "10",
                assetId: constants_1.AddressZero,
            });
            assertResult(result, true, vector_types_1.ChainError.reasons.NotEnoughFunds);
        });
        it("errors if error on approve", async () => {
            approveMock.resolves(vector_types_1.Result.fail(new vector_types_1.ChainError(vector_types_1.ChainError.reasons.NotEnoughFunds)));
            const result = await ethService.sendDeployChannelTx(channelState, {
                amount: "1",
                assetId: vector_utils_1.mkAddress("0xa"),
            });
            assertResult(result, true, vector_types_1.ChainError.reasons.NotEnoughFunds);
        });
        it("happy: alice can deploy channel without deposit", async () => {
            const result = await ethService.sendDeployChannelTx(channelState);
            assertResult(result, false, txReceipt);
            const call = sendTxWithRetriesMock.getCall(0);
            vector_utils_1.expect(call.args[0]).to.eq(channelState.channelAddress);
            vector_utils_1.expect(call.args[1]).to.eq(channelState.networkContext.chainId);
            vector_utils_1.expect(call.args[2]).to.eq(vector_types_1.TransactionReason.deploy);
        });
        it("happy: bob can deploy channel without deposit", async () => {
            signer.getAddress.resolves(channelState.bob);
            const result = await ethService.sendDeployChannelTx(channelState);
            assertResult(result, false, txReceipt);
            const call = sendTxWithRetriesMock.getCall(0);
            vector_utils_1.expect(call.args[0]).to.eq(channelState.channelAddress);
            vector_utils_1.expect(call.args[1]).to.eq(channelState.networkContext.chainId);
            vector_utils_1.expect(call.args[2]).to.eq(vector_types_1.TransactionReason.deploy);
        });
        it("happy: calls createChannelAndDepositAlice with native asset if 0x000... deposit", async () => {
            const result = await ethService.sendDeployChannelTx(channelState, {
                amount: "1",
                assetId: constants_1.AddressZero,
            });
            assertResult(result, false, txReceipt);
            const call = sendTxWithRetriesMock.getCall(0);
            vector_utils_1.expect(call.args[0]).to.eq(channelState.channelAddress);
            vector_utils_1.expect(call.args[1]).to.eq(channelState.networkContext.chainId);
            vector_utils_1.expect(call.args[2]).to.eq(vector_types_1.TransactionReason.deployWithDepositAlice);
        });
        it("happy: calls createChannelAndDepositAlice with tokens if token deposit", async () => {
            const result = await ethService.sendDeployChannelTx(channelState, {
                amount: "1",
                assetId: vector_utils_1.mkAddress("0xa"),
            });
            assertResult(result, false);
            const approveCall = approveMock.getCall(0);
            vector_utils_1.expect(approveCall.args[0]).to.eq(channelState.channelAddress);
            vector_utils_1.expect(approveCall.args[1]).to.eq(channelState.networkContext.channelFactoryAddress);
            vector_utils_1.expect(approveCall.args[2]).to.eq(channelState.alice);
            vector_utils_1.expect(approveCall.args[3]).to.eq("1");
            vector_utils_1.expect(approveCall.args[4]).to.eq(vector_utils_1.mkAddress("0xa"));
            vector_utils_1.expect(approveCall.args[5]).to.eq(channelState.networkContext.chainId);
            const call = sendTxWithRetriesMock.getCall(0);
            vector_utils_1.expect(call.args[0]).to.eq(channelState.channelAddress);
            vector_utils_1.expect(call.args[1]).to.eq(channelState.networkContext.chainId);
            vector_utils_1.expect(call.args[2]).to.eq("deployWithDepositAlice");
        });
    });
    describe("sendWithdrawTx", () => {
        const minTx = {
            data: vector_utils_1.mkBytes32("0xabc"),
            to: constants_1.AddressZero,
            value: 0,
        };
        let sendDeployChannelTxMock;
        beforeEach(() => {
            sendDeployChannelTxMock = sinon_1.stub(ethService, "sendDeployChannelTx").resolves(vector_types_1.Result.ok(txReceipt));
            sendTxWithRetriesMock = sinon_1.stub(ethService, "sendTxWithRetries").resolves(vector_types_1.Result.ok(txReceipt));
        });
        it("errors if cannot get a signer", async () => {
            channelState.networkContext.chainId = 1234;
            const result = await ethService.sendWithdrawTx(channelState, minTx);
            assertResult(result, true, vector_types_1.ChainError.reasons.SignerNotFound);
        });
        it("errors if multisig code cannot be retrieved", async () => {
            getCodeMock.resolves(vector_types_1.Result.fail(new vector_types_1.ChainError("getCode error")));
            const result = await ethService.sendWithdrawTx(channelState, minTx);
            assertResult(result, true, "getCode error");
        });
        it("errors if channel deployment fails", async () => {
            sendDeployChannelTxMock.resolves(vector_types_1.Result.fail(new vector_types_1.ChainError(vector_types_1.ChainError.reasons.NotEnoughFunds)));
            const result = await ethService.sendWithdrawTx(channelState, minTx);
            assertResult(result, true, vector_types_1.ChainError.reasons.FailedToDeploy);
        });
        it("errors if deploy tx throws an error", async () => {
            sendDeployChannelTxMock.resolves(vector_types_1.Result.fail(new vector_types_1.ChainError(vector_types_1.ChainError.reasons.TxReverted)));
            const result = await ethService.sendWithdrawTx(channelState, minTx);
            assertResult(result, true, vector_types_1.ChainError.reasons.FailedToDeploy);
        });
        it("happy: if channel is deployed, send withdrawal tx", async () => {
            getCodeMock.resolves(vector_types_1.Result.ok(vector_utils_1.mkHash("0xabc")));
            const result = await ethService.sendWithdrawTx(channelState, minTx);
            vector_utils_1.expect(sendDeployChannelTxMock.callCount).to.eq(0);
            assertResult(result, false, txReceipt);
        });
        it("happy: if channel is not deployed, deploy channel then send withdrawal tx", async () => {
            const result = await ethService.sendWithdrawTx(channelState, minTx);
            vector_utils_1.expect(sendDeployChannelTxMock.callCount).to.eq(1);
            vector_utils_1.expect(sendDeployChannelTxMock.getCall(0).firstArg).to.deep.eq(channelState);
            assertResult(result, false, txReceipt);
        });
    });
    describe("sendDepositTx", () => {
        let sendDeployChannelTxMock;
        let sendDepositATxMock;
        let sendDepositBTxMock;
        beforeEach(() => {
            sendDeployChannelTxMock = sinon_1.stub(ethService, "sendDeployChannelTx").resolves(vector_types_1.Result.ok(txReceipt));
            sendDepositATxMock = sinon_1.stub(ethService, "sendDepositATx").resolves(vector_types_1.Result.ok(txReceipt));
            sendDepositBTxMock = sinon_1.stub(ethService, "sendDepositBTx").resolves(vector_types_1.Result.ok(txReceipt));
            sendTxWithRetriesMock = sinon_1.stub(ethService, "sendTxWithRetries").resolves(vector_types_1.Result.ok(txReceipt));
        });
        it("errors if cannot get a signer", async () => {
            channelState.networkContext.chainId = 1234;
            const result = await ethService.sendDepositTx(channelState, channelState.alice, "1", constants_1.AddressZero);
            assertResult(result, true, vector_types_1.ChainError.reasons.SignerNotFound);
        });
        it("errors if sender is not in channel", async () => {
            const result = await ethService.sendDepositTx(channelState, vector_utils_1.mkAddress("0xababab"), "1", constants_1.AddressZero);
            assertResult(result, true, vector_types_1.ChainError.reasons.SenderNotInChannel);
        });
        it("errors if deposit amount is negative", async () => {
            const result = await ethService.sendDepositTx(channelState, channelState.alice, "-1", constants_1.AddressZero);
            assertResult(result, true, vector_types_1.ChainError.reasons.NegativeDepositAmount);
        });
        it("errors if multisig code cannot be retrieved", async () => {
            getCodeMock.resolves(vector_types_1.Result.fail(new vector_types_1.ChainError("getCode error")));
            const result = await ethService.sendDepositTx(channelState, channelState.alice, "1", constants_1.AddressZero);
            assertResult(result, true, "getCode error");
        });
        it("errors if onchain balance returns an error", async () => {
            getCodeMock.resolves(vector_types_1.Result.ok(vector_utils_1.mkHash("0xabc")));
            getOnchainBalanceMock.resolves(vector_types_1.Result.fail(new Error("getOnchainBalance error")));
            const result = await ethService.sendDepositTx(channelState, channelState.alice, "1", constants_1.AddressZero);
            assertResult(result, true, "getOnchainBalance error");
        });
        it("errors if channel is deployed and onchain balance is < send amount", async () => {
            getCodeMock.resolves(vector_types_1.Result.ok(vector_utils_1.mkHash("0xabc")));
            getOnchainBalanceMock.resolves(vector_types_1.Result.ok(ethers_1.BigNumber.from(5)));
            const result = await ethService.sendDepositTx(channelState, channelState.alice, "6", constants_1.AddressZero);
            assertResult(result, true, vector_types_1.ChainError.reasons.NotEnoughFunds);
        });
        it("happy: alice deploys channel with deposit if not deployed", async () => {
            const result = await ethService.sendDepositTx(channelState, channelState.alice, "1", constants_1.AddressZero);
            assertResult(result, false, txReceipt);
            vector_utils_1.expect(sendDeployChannelTxMock.callCount).to.eq(1);
            const call = sendDeployChannelTxMock.getCall(0);
            vector_utils_1.expect(call.args[0]).to.deep.eq(channelState);
            vector_utils_1.expect(call.args[1]).to.deep.eq({ amount: "1", assetId: constants_1.AddressZero });
        });
        it("happy: alice calls sendDepositATx if multisig is deployed", async () => {
            getCodeMock.resolves(vector_types_1.Result.ok(vector_utils_1.mkHash("0xabc")));
            const result = await ethService.sendDepositTx(channelState, channelState.alice, "1", constants_1.AddressZero);
            assertResult(result, false, txReceipt);
            vector_utils_1.expect(sendDepositATxMock.callCount).to.eq(1);
            const call = sendDepositATxMock.getCall(0);
            vector_utils_1.expect(call.args[0]).to.deep.eq(channelState);
            vector_utils_1.expect(call.args[1]).to.deep.eq("1");
            vector_utils_1.expect(call.args[2]).to.deep.eq(constants_1.AddressZero);
        });
        it("happy: bob calls sendDepositBTx if multisig is deployed", async () => {
            getCodeMock.resolves(vector_types_1.Result.ok(vector_utils_1.mkHash("0xabc")));
            const result = await ethService.sendDepositTx(channelState, channelState.bob, "1", constants_1.AddressZero);
            assertResult(result, false, txReceipt);
            vector_utils_1.expect(sendDepositBTxMock.callCount).to.eq(1);
            const call = sendDepositBTxMock.getCall(0);
            vector_utils_1.expect(call.args[0]).to.deep.eq(channelState);
            vector_utils_1.expect(call.args[1]).to.deep.eq("1");
            vector_utils_1.expect(call.args[2]).to.deep.eq(constants_1.AddressZero);
        });
        it("happy: bob calls sendDepositBTx if multisig is not deployed", async () => {
            const result = await ethService.sendDepositTx(channelState, channelState.bob, "1", constants_1.AddressZero);
            assertResult(result, false, txReceipt);
            vector_utils_1.expect(sendDepositBTxMock.callCount).to.eq(1);
            const call = sendDepositBTxMock.getCall(0);
            vector_utils_1.expect(call.args[0]).to.deep.eq(channelState);
            vector_utils_1.expect(call.args[1]).to.deep.eq("1");
            vector_utils_1.expect(call.args[2]).to.deep.eq(constants_1.AddressZero);
        });
    });
    describe("sendTxWithRetries", () => {
        let sendAndConfirmTx;
        beforeEach(() => {
            sendAndConfirmTx = sinon_1.stub(ethService, "sendAndConfirmTx").resolves(vector_types_1.Result.ok(txReceipt));
        });
        it("errors if sendTxAndParseResponse errors", async () => {
            sendAndConfirmTx.resolves(vector_types_1.Result.fail(new vector_types_1.ChainError(vector_types_1.ChainError.reasons.NotEnoughFunds)));
            const result = await ethService.sendTxWithRetries(channelState.channelAddress, channelState.networkContext.chainId, "allowance", () => {
                return Promise.resolve(txResponse);
            });
            assertResult(result, true, vector_types_1.ChainError.reasons.NotEnoughFunds);
        });
        it("retries if it's a retryable error", async () => {
            sendAndConfirmTx
                .onFirstCall()
                .resolves(vector_types_1.Result.fail(new vector_types_1.ChainError('processing response error (body="{"jsonrpc":"2.0","error":{"code":-32000,"message":"Block information is incomplete while ancient block sync is still in progress, before it\'s finished we can\'t determine the existence of requested item."},"id":14890}\n", error={"code":-32000}, requestBody="{"method":"eth_getTransactionReceipt","params":["0x8731c46fafd569bb65c6c26cd3960ad418d88310a41a03c5c4f4a0dcce15cd8a"],"id":14890,"jsonrpc":"2.0"}", requestMethod="POST", url="https://rpc.xdaichain.com/", code=SERVER_ERROR, version=web/5.1.0)')));
            sendAndConfirmTx.resolves(vector_types_1.Result.ok(txReceipt));
            const result = await ethService.sendTxWithRetries(channelState.channelAddress, channelState.networkContext.chainId, "allowance", () => {
                return Promise.resolve(txResponse);
            });
            assertResult(result, false, txReceipt);
        });
        it("happy: should work when sendTxAndParseResponse works on the first try", async () => {
            const result = await ethService.sendTxWithRetries(channelState.channelAddress, channelState.networkContext.chainId, "allowance", () => {
                return Promise.resolve(txResponse);
            });
            assertResult(result, false, txReceipt);
        });
    });
    describe("sendAndConfirmTx", () => {
        beforeEach(() => {
            waitForConfirmation = sinon_1.stub(ethService, "waitForConfirmation");
        });
        it("if txFn returns undefined, returns undefined", async () => {
            const result = await ethService.sendAndConfirmTx(constants_1.AddressZero, 1337, "allowance", async () => {
                return undefined;
            }, ethers_1.BigNumber.from(10000));
            assertResult(result, false, undefined);
        });
        it("if txFn errors, returns error", async () => {
            const result = await ethService.sendAndConfirmTx(constants_1.AddressZero, 1337, "allowance", async () => {
                throw new Error("Boooo");
            }, ethers_1.BigNumber.from(10000));
            assertResult(result, true, "Boooo");
        });
        it("if txFn errors, with not enough funds, return special error", async () => {
            const result = await ethService.sendAndConfirmTx(constants_1.AddressZero, 1337, "allowance", async () => {
                throw new Error("sender doesn't have enough funds");
            }, ethers_1.BigNumber.from(10000));
            assertResult(result, true, vector_types_1.ChainError.reasons.NotEnoughFunds);
        });
        it("if receipt status == 0, saves response with error", async () => {
            const badReceipt = Object.assign(Object.assign({}, txReceipt), { status: 0 });
            waitForConfirmation.resolves(badReceipt);
            const result = await ethService.sendAndConfirmTx(constants_1.AddressZero, 1337, "allowance", async () => {
                return txResponse;
            });
            vector_utils_1.expect(storeMock.saveTransactionAttempt.callCount).eq(1);
            const saveTransactionAttemptCall = storeMock.saveTransactionAttempt.getCall(0);
            const id = saveTransactionAttemptCall.args[0];
            vector_utils_1.expect(saveTransactionAttemptCall.args[1]).eq(constants_1.AddressZero);
            vector_utils_1.expect(saveTransactionAttemptCall.args[2]).eq("allowance");
            vector_utils_1.expect(saveTransactionAttemptCall.args[3]).deep.eq(txResponse);
            vector_utils_1.expect(storeMock.saveTransactionFailure.callCount).eq(1);
            const saveTransactionFailureCall = storeMock.saveTransactionFailure.getCall(0);
            vector_utils_1.expect(saveTransactionFailureCall.args[0]).eq(id);
            vector_utils_1.expect(saveTransactionFailureCall.args[1]).eq(vector_types_1.ChainError.reasons.TxReverted);
            vector_utils_1.expect(saveTransactionFailureCall.args[2]).eq(badReceipt);
            assertResult(result, true, vector_types_1.ChainError.reasons.TxReverted);
        });
        it("if receipt wait fn errors, saves response with error", async () => {
            const ERROR_MSG = "Booooo";
            const error = new Error(ERROR_MSG);
            waitForConfirmation.rejects(error);
            const result = await ethService.sendAndConfirmTx(constants_1.AddressZero, 1337, "allowance", async () => {
                return txResponse;
            });
            vector_utils_1.expect(storeMock.saveTransactionAttempt.callCount).eq(1);
            const saveTransactionAttemptCall = storeMock.saveTransactionAttempt.getCall(0);
            const id = saveTransactionAttemptCall.args[0];
            vector_utils_1.expect(saveTransactionAttemptCall.args[1]).eq(constants_1.AddressZero);
            vector_utils_1.expect(saveTransactionAttemptCall.args[2]).eq("allowance");
            vector_utils_1.expect(saveTransactionAttemptCall.args[3]).deep.eq(txResponse);
            vector_utils_1.expect(storeMock.saveTransactionFailure.callCount).eq(1);
            const saveTransactionFailureCall = storeMock.saveTransactionFailure.getCall(0);
            vector_utils_1.expect(saveTransactionFailureCall.args[0]).eq(id);
            vector_utils_1.expect(saveTransactionFailureCall.args[1]).eq(ERROR_MSG);
            assertResult(result, true, ERROR_MSG);
        });
        it("retries transaction with higher gas price", async () => {
            const newTx = Object.assign(Object.assign({}, txResponse), { hash: vector_utils_1.mkHash("0xddd") });
            const newReceipt = Object.assign(Object.assign({}, txReceipt), { transactionHash: newTx.hash });
            waitForConfirmation.onFirstCall().rejects(new vector_types_1.ChainError(vector_types_1.ChainError.retryableTxErrors.ConfirmationTimeout));
            waitForConfirmation.onSecondCall().resolves(newReceipt);
            let receivedNonce = -1;
            let firstGasPrice = ethers_1.BigNumber.from(-1);
            let secondGasPrice = ethers_1.BigNumber.from(-1);
            const result = await ethService.sendAndConfirmTx(constants_1.AddressZero, 1337, "allowance", async (gasPrice, nonce) => {
                if (nonce) {
                    receivedNonce = nonce;
                    secondGasPrice = gasPrice;
                    return newTx;
                }
                firstGasPrice = gasPrice;
                return txResponse;
            });
            vector_utils_1.expect(receivedNonce === txResponse.nonce, "nonce passed into callback was not the same as original tx nonce");
            vector_utils_1.expect(secondGasPrice > firstGasPrice, "second gas price should be larger than first");
            vector_utils_1.expect(storeMock.saveTransactionAttempt.callCount).eq(2);
            const saveTransactionAttemptCall = storeMock.saveTransactionAttempt.getCall(0);
            const id = saveTransactionAttemptCall.args[0];
            vector_utils_1.expect(saveTransactionAttemptCall.args[1]).eq(constants_1.AddressZero);
            vector_utils_1.expect(saveTransactionAttemptCall.args[2]).eq("allowance");
            vector_utils_1.expect(saveTransactionAttemptCall.args[3]).deep.eq(txResponse);
            const saveTransactionAttemptCall2 = storeMock.saveTransactionAttempt.getCall(1);
            vector_utils_1.expect(saveTransactionAttemptCall2.args[0]).eq(id);
            vector_utils_1.expect(saveTransactionAttemptCall2.args[1]).eq(constants_1.AddressZero);
            vector_utils_1.expect(saveTransactionAttemptCall2.args[2]).eq("allowance");
            vector_utils_1.expect(saveTransactionAttemptCall2.args[3]).deep.eq(newTx);
            vector_utils_1.expect(storeMock.saveTransactionReceipt.callCount).eq(1);
            const saveTransactionReceiptCall = storeMock.saveTransactionReceipt.getCall(0);
            vector_utils_1.expect(saveTransactionReceiptCall.args[0]).eq(id);
            vector_utils_1.expect(saveTransactionReceiptCall.args[1]).eq(newReceipt);
            assertResult(result, false, newReceipt);
        });
        it("stops trying to send if at max gas price", async () => {
            getGasPrice.resolves(vector_types_1.Result.ok(ethService_1.BIG_GAS_PRICE.sub(1)));
            waitForConfirmation.onFirstCall().rejects(new vector_types_1.ChainError(vector_types_1.ChainError.retryableTxErrors.ConfirmationTimeout));
            const result = await ethService.sendAndConfirmTx(constants_1.AddressZero, 1337, "allowance", async () => {
                return txResponse;
            });
            vector_utils_1.expect(storeMock.saveTransactionAttempt.callCount).eq(1);
            const saveTransactionAttemptCall = storeMock.saveTransactionAttempt.getCall(0);
            const id = saveTransactionAttemptCall.args[0];
            vector_utils_1.expect(saveTransactionAttemptCall.args[1]).eq(constants_1.AddressZero);
            vector_utils_1.expect(saveTransactionAttemptCall.args[2]).eq("allowance");
            vector_utils_1.expect(saveTransactionAttemptCall.args[3]).deep.eq(txResponse);
            vector_utils_1.expect(storeMock.saveTransactionFailure.callCount).eq(1);
            const saveTransactionFailureCall = storeMock.saveTransactionFailure.getCall(0);
            vector_utils_1.expect(saveTransactionFailureCall.args[0]).eq(id);
            vector_utils_1.expect(saveTransactionFailureCall.args[1]).eq(vector_types_1.ChainError.reasons.MaxGasPriceReached);
            assertResult(result, true, vector_types_1.ChainError.reasons.MaxGasPriceReached);
        });
        it("happy: saves responses if confirmation happens on first loop", async () => {
            waitForConfirmation.resolves(txReceipt);
            const result = await ethService.sendAndConfirmTx(constants_1.AddressZero, 1337, "allowance", async () => {
                return txResponse;
            });
            vector_utils_1.expect(storeMock.saveTransactionAttempt.callCount).eq(1);
            const saveTransactionAttemptCall = storeMock.saveTransactionAttempt.getCall(0);
            const id = saveTransactionAttemptCall.args[0];
            vector_utils_1.expect(saveTransactionAttemptCall.args[1]).eq(constants_1.AddressZero);
            vector_utils_1.expect(saveTransactionAttemptCall.args[2]).eq("allowance");
            vector_utils_1.expect(saveTransactionAttemptCall.args[3]).deep.eq(txResponse);
            vector_utils_1.expect(storeMock.saveTransactionReceipt.callCount).eq(1);
            const saveTransactionReceiptCall = storeMock.saveTransactionReceipt.getCall(0);
            vector_utils_1.expect(saveTransactionReceiptCall.args[0]).eq(id);
            vector_utils_1.expect(saveTransactionReceiptCall.args[1]).eq(txReceipt);
            assertResult(result, false, txReceipt);
        });
    });
    describe("waitForConfirmation", () => {
        it("should return an error if there is no provider for chain", async () => {
            await vector_utils_1.expect(ethService.waitForConfirmation(111, txResponse)).to.eventually.be.rejectedWith(vector_types_1.ChainError.reasons.ProviderNotFound);
        });
        it("should wait for the required amount of confirmations", async () => {
            provider1337.getTransactionReceipt.onFirstCall().resolves(Object.assign(Object.assign({}, txReceipt), { confirmations: 0 }));
            provider1337.getTransactionReceipt.onSecondCall().resolves(Object.assign(Object.assign({}, txReceipt), { confirmations: 0 }));
            provider1337.getTransactionReceipt.onThirdCall().resolves(txReceipt);
            const res = await ethService.waitForConfirmation(1337, txResponse);
            vector_utils_1.expect(res).to.deep.eq(txReceipt);
            vector_utils_1.expect(provider1337.getTransactionReceipt.callCount).to.eq(3);
        });
        it("should error with a timeout error if it is past the confirmation time", async () => {
            provider1337.getTransactionReceipt.onThirdCall().resolves(undefined);
            await vector_utils_1.expect(ethService.waitForConfirmation(1337, txResponse)).to.eventually.be.rejectedWith(vector_types_1.ChainError.retryableTxErrors.ConfirmationTimeout);
        });
    });
    describe("revitalizeTxs", () => {
        it("should resubmit and monitor active txs", async () => {
            const storedTx = {
                channelAddress: vector_utils_1.mkAddress("0xa"),
                reason: "allowance",
                status: "submitted",
                to: vector_utils_1.mkAddress(),
                chainId: txResponse.chainId,
                data: txResponse.data,
                from: txResponse.from,
                id: uuid_1.v4(),
                nonce: txResponse.nonce,
                attempts: [
                    {
                        transactionHash: txResponse.hash,
                        gasLimit: txResponse.gasLimit.toString(),
                        gasPrice: txResponse.gasPrice.toString(),
                        createdAt: new Date(),
                    },
                ],
                value: txResponse.value.toString(),
            };
            const storedTxs = [storedTx, storedTx, storedTx, storedTx];
            storeMock.getActiveTransactions.resolves(storedTxs);
            const getTxResponseFromHash = sinon_1.stub(ethService, "getTxResponseFromHash");
            getTxResponseFromHash.onFirstCall().resolves(vector_types_1.Result.fail(new vector_types_1.ChainError(vector_types_1.ChainError.reasons.TxNotFound)));
            getTxResponseFromHash.onSecondCall().resolves(vector_types_1.Result.ok({ response: txResponse, receipt: txReceipt }));
            getTxResponseFromHash.onThirdCall().resolves(vector_types_1.Result.ok({ response: txResponse }));
            getTxResponseFromHash.resolves(vector_types_1.Result.ok({ response: txResponse }));
            const sendTxWithRetries = sinon_1.stub(ethService, "sendTxWithRetries").resolves(vector_types_1.Result.ok(txReceipt));
            await ethService.revitalizeTxs();
            vector_utils_1.expect(storeMock.saveTransactionReceipt.callCount).to.eq(1);
            vector_utils_1.expect(sendTxWithRetries.callCount).to.eq(2);
        });
    });
});
//# sourceMappingURL=ethService.spec.js.map