"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());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const EpochTokenLocker = artifacts.require("EpochTokenLocker");
const EpochTokenLockerTestInterface = artifacts.require("EpochTokenLockerTestInterface");
const MockContract = artifacts.require("MockContract");
const ERC20Interface = artifacts.require("ERC20");
const truffle_assertions_1 = __importDefault(require("truffle-assertions"));
const utilities_1 = require("./utilities");
contract("EpochTokenLocker", (accounts) => __awaiter(void 0, void 0, void 0, function* () {
    const [user_1, user_2] = accounts;
    describe("deposit()", () => {
        it("processes a deposit and stores it in the pendingDeposits", () => __awaiter(void 0, void 0, void 0, function* () {
            const epochTokenLocker = yield EpochTokenLocker.new();
            const ERC20 = yield MockContract.new();
            yield ERC20.givenAnyReturnBool(true);
            const currentStateIndex = yield epochTokenLocker.getCurrentBatchId();
            yield epochTokenLocker.deposit(ERC20.address, 100);
            const pendingDeposit = yield epochTokenLocker.getPendingDeposit(user_1, ERC20.address);
            assert.equal(pendingDeposit[0].toNumber(), 100);
            assert(pendingDeposit[1].eq(currentStateIndex));
        }));
        it("throws, if transferFrom fails", () => __awaiter(void 0, void 0, void 0, function* () {
            const epochTokenLocker = yield EpochTokenLocker.new();
            const ERC20 = yield MockContract.new();
            yield ERC20.givenAnyRevert();
            yield truffle_assertions_1.default.reverts(epochTokenLocker.deposit(ERC20.address, 100), "SafeERC20: low-level call failed");
        }));
        it("adds two deposits, if they are deposited during same batchId", () => __awaiter(void 0, void 0, void 0, function* () {
            const epochTokenLocker = yield EpochTokenLocker.new();
            const ERC20 = yield MockContract.new();
            yield ERC20.givenAnyReturnBool(true);
            yield epochTokenLocker.deposit(ERC20.address, 100);
            yield epochTokenLocker.deposit(ERC20.address, 100);
            assert.equal((yield epochTokenLocker.getPendingDeposit(user_1, ERC20.address))[0].toNumber(), 200);
        }));
        it("does not consolidate two deposits, if they are not deposited during same batchId", () => __awaiter(void 0, void 0, void 0, function* () {
            const epochTokenLocker = yield EpochTokenLocker.new();
            const ERC20 = yield MockContract.new();
            yield ERC20.givenAnyReturnBool(true);
            yield epochTokenLocker.deposit(ERC20.address, 100);
            yield utilities_1.closeAuction(epochTokenLocker);
            yield epochTokenLocker.deposit(ERC20.address, 200);
            const currentStateIndex = yield epochTokenLocker.getCurrentBatchId();
            const pendingDeposit = yield epochTokenLocker.getPendingDeposit(user_1, ERC20.address);
            assert.equal(pendingDeposit[0].toNumber(), 200);
            assert(pendingDeposit[1].eq(currentStateIndex));
        }));
    });
    describe("requestWithdraw()", () => {
        it("processes a withdraw request", () => __awaiter(void 0, void 0, void 0, function* () {
            const epochTokenLocker = yield EpochTokenLocker.new();
            const ERC20 = yield MockContract.new();
            yield ERC20.givenAnyReturnBool(true);
            const currentStateIndex = yield epochTokenLocker.getCurrentBatchId();
            yield epochTokenLocker.requestWithdraw(ERC20.address, 100);
            const pendingWithdraw = yield epochTokenLocker.getPendingWithdraw(user_1, ERC20.address);
            assert.equal(pendingWithdraw[0].toNumber(), 100);
            assert(pendingWithdraw[1].eq(currentStateIndex));
        }));
        it("processes a withdraw request, if previous withdraw request is valid", () => __awaiter(void 0, void 0, void 0, function* () {
            const epochTokenLocker = yield EpochTokenLocker.new();
            const ERC20 = yield MockContract.new();
            yield ERC20.givenAnyReturnBool(true);
            yield epochTokenLocker.requestWithdraw(ERC20.address, 100);
            yield utilities_1.closeAuction(epochTokenLocker);
            yield epochTokenLocker.requestWithdraw(ERC20.address, 100);
            // checking that the transfer in withdraw wased
            const token = yield ERC20Interface.new();
            const withdrawTransfer = token.contract.methods
                .transfer(accounts[0], 0)
                .encodeABI();
            assert.equal((yield ERC20.invocationCountForCalldata.call(withdrawTransfer)).toNumber(), 1);
        }));
    });
    describe("requestFutureWithdraw()", () => {
        it("rejects futureWithdraw request from the past", () => __awaiter(void 0, void 0, void 0, function* () {
            const epochTokenLocker = yield EpochTokenLocker.new();
            const ERC20 = yield MockContract.new();
            yield ERC20.givenAnyReturnBool(true);
            const currentStateIndex = (yield epochTokenLocker.getCurrentBatchId()).toNumber();
            yield truffle_assertions_1.default.reverts(epochTokenLocker.requestFutureWithdraw(ERC20.address, 100, currentStateIndex - 1), "Request cannot be made in the past");
        }));
        it("processes a future withdraw request", () => __awaiter(void 0, void 0, void 0, function* () {
            const epochTokenLocker = yield EpochTokenLocker.new();
            const ERC20 = yield MockContract.new();
            yield ERC20.givenAnyReturnBool(true);
            const currentStateIndex = (yield epochTokenLocker.getCurrentBatchId()).toNumber();
            yield epochTokenLocker.requestFutureWithdraw(ERC20.address, 100, currentStateIndex + 1);
            const pendingWithdraw = yield epochTokenLocker.getPendingWithdraw(user_1, ERC20.address);
            assert.equal(pendingWithdraw[0].toNumber(), 100);
            assert.equal(pendingWithdraw[1].toNumber(), currentStateIndex + 1);
        }));
    });
    describe("withdraw()", () => {
        it("processes a deposit, then processes a withdraw request and withdraws in next batchId", () => __awaiter(void 0, void 0, void 0, function* () {
            const epochTokenLocker = yield EpochTokenLocker.new();
            const ERC20 = yield MockContract.new();
            yield ERC20.givenAnyReturnBool(true);
            yield epochTokenLocker.deposit(ERC20.address, 100);
            yield utilities_1.closeAuction(epochTokenLocker);
            assert.equal((yield epochTokenLocker.getBalance(user_1, ERC20.address)).toNumber(), 100);
            yield epochTokenLocker.requestWithdraw(ERC20.address, 100);
            yield utilities_1.closeAuction(epochTokenLocker);
            yield epochTokenLocker.withdraw(user_1, ERC20.address);
            const pendingWithdraw = yield epochTokenLocker.getPendingWithdraw(user_1, ERC20.address);
            assert.equal(pendingWithdraw[0].toNumber(), 0);
            assert.equal(pendingWithdraw[1].toNumber(), 0);
            const token = yield ERC20Interface.new();
            const depositTransfer = token.contract.methods
                .transfer(accounts[0], 100)
                .encodeABI();
            assert.equal((yield ERC20.invocationCountForCalldata.call(depositTransfer)).toNumber(), 1);
        }));
        it("processes a deposit, then processes a withdraw request and withdraws fails in current batchId", () => __awaiter(void 0, void 0, void 0, function* () {
            const epochTokenLocker = yield EpochTokenLocker.new();
            const ERC20 = yield MockContract.new();
            yield ERC20.givenAnyReturnBool(true);
            yield epochTokenLocker.deposit(ERC20.address, 100);
            yield utilities_1.closeAuction(epochTokenLocker);
            assert.equal((yield epochTokenLocker.getBalance(user_1, ERC20.address)).toNumber(), 100);
            yield epochTokenLocker.requestWithdraw(ERC20.address, 100);
            yield truffle_assertions_1.default.reverts(epochTokenLocker.withdraw(user_1, ERC20.address), "withdraw was not registered previously");
        }));
        it("processes a withdraw request and withdraws only available amounts", () => __awaiter(void 0, void 0, void 0, function* () {
            const epochTokenLocker = yield EpochTokenLocker.new();
            const ERC20 = yield MockContract.new();
            yield ERC20.givenAnyReturnBool(true);
            yield epochTokenLocker.deposit(ERC20.address, 50);
            yield utilities_1.closeAuction(epochTokenLocker);
            yield epochTokenLocker.requestWithdraw(ERC20.address, 100);
            yield utilities_1.closeAuction(epochTokenLocker);
            yield epochTokenLocker.withdraw(user_1, ERC20.address);
            const token = yield ERC20Interface.new();
            const depositTransfer = token.contract.methods
                .transfer(accounts[0], 50)
                .encodeABI();
            assert.equal((yield ERC20.invocationCountForCalldata.call(depositTransfer)).toNumber(), 1);
        }));
        it("throws, if the withdraw wased, also there has been a credit for the account in this batch", () => __awaiter(void 0, void 0, void 0, function* () {
            const epochTokenLocker = yield EpochTokenLockerTestInterface.new();
            const ERC20 = yield MockContract.new();
            yield ERC20.givenAnyReturnBool(true);
            yield epochTokenLocker.requestWithdraw(ERC20.address, 100, {
                from: user_1,
            });
            yield utilities_1.closeAuction(epochTokenLocker);
            yield epochTokenLocker.addBalanceAndBlockWithdrawForThisBatchTest(user_1, ERC20.address, 100);
            const batchId = yield epochTokenLocker.getCurrentBatchId();
            assert.equal((yield epochTokenLocker.lastCreditBatchId(user_1, ERC20.address)).toNumber(), batchId.toNumber());
            yield truffle_assertions_1.default.reverts(epochTokenLocker.withdraw(user_1, ERC20.address), "Withdraw not possible for token that is traded in the current auction");
        }));
    });
    describe("getBalance()", () => {
        it("returns just the balance, if there are no pending deposits and withdraws", () => __awaiter(void 0, void 0, void 0, function* () {
            const epochTokenLocker = yield EpochTokenLocker.new();
            const ERC20 = yield MockContract.new();
            yield ERC20.givenAnyReturnBool(true);
            yield epochTokenLocker.deposit(ERC20.address, 100);
            yield utilities_1.closeAuction(epochTokenLocker);
            assert.equal((yield epochTokenLocker.getBalance(user_1, ERC20.address)).toNumber(), 100);
        }));
        it("returns just the balance, if there are no pending deposit from a previous time and no withdraws", () => __awaiter(void 0, void 0, void 0, function* () {
            const epochTokenLocker = yield EpochTokenLocker.new();
            const ERC20 = yield MockContract.new();
            yield ERC20.givenAnyReturnBool(true);
            yield epochTokenLocker.deposit(ERC20.address, 100);
            assert.equal((yield epochTokenLocker.getBalance(user_1, ERC20.address)).toNumber(), 0);
        }));
        it("returns just the balance + pending deposit, if there are no withdraws", () => __awaiter(void 0, void 0, void 0, function* () {
            const epochTokenLocker = yield EpochTokenLocker.new();
            const ERC20 = yield MockContract.new();
            yield ERC20.givenAnyReturnBool(true);
            yield epochTokenLocker.deposit(ERC20.address, 100);
            yield utilities_1.closeAuction(epochTokenLocker);
            assert.equal((yield epochTokenLocker.getBalance(user_1, ERC20.address)).toNumber(), 100);
        }));
        it("returns just the balance + pending deposit - depending withdraws", () => __awaiter(void 0, void 0, void 0, function* () {
            const epochTokenLocker = yield EpochTokenLocker.new();
            const ERC20 = yield MockContract.new();
            yield ERC20.givenAnyReturnBool(true);
            yield epochTokenLocker.deposit(ERC20.address, 100);
            yield epochTokenLocker.requestWithdraw(ERC20.address, 50);
            yield utilities_1.closeAuction(epochTokenLocker);
            assert.equal((yield epochTokenLocker.getBalance(user_1, ERC20.address)).toNumber(), 50);
        }));
        it("returns just the balance + pending deposit - depending withdraws and protects overflows", () => __awaiter(void 0, void 0, void 0, function* () {
            const epochTokenLocker = yield EpochTokenLocker.new();
            const ERC20 = yield MockContract.new();
            yield ERC20.givenAnyReturnBool(true);
            yield epochTokenLocker.deposit(ERC20.address, 100);
            yield epochTokenLocker.requestWithdraw(ERC20.address, 150);
            yield utilities_1.closeAuction(epochTokenLocker);
            assert.equal((yield epochTokenLocker.getBalance(user_1, ERC20.address)).toNumber(), 0);
        }));
        it("returns just the balance + pending deposit if withdraw was made in same batchId", () => __awaiter(void 0, void 0, void 0, function* () {
            const epochTokenLocker = yield EpochTokenLocker.new();
            const ERC20 = yield MockContract.new();
            yield ERC20.givenAnyReturnBool(true);
            yield epochTokenLocker.deposit(ERC20.address, 100);
            yield utilities_1.closeAuction(epochTokenLocker);
            yield epochTokenLocker.requestWithdraw(ERC20.address, 150);
            assert.equal((yield epochTokenLocker.getBalance(user_1, ERC20.address)).toNumber(), 100);
        }));
    });
    describe("addBalance()", () => {
        it("modifies the balance by adding", () => __awaiter(void 0, void 0, void 0, function* () {
            const epochTokenLocker = yield EpochTokenLockerTestInterface.new();
            const ERC20 = yield MockContract.new();
            yield epochTokenLocker.addBalanceTest(user_1, ERC20.address, 100);
            assert.equal((yield epochTokenLocker.getBalance(user_1, ERC20.address)).toNumber(), 100);
        }));
    });
    describe("addBalanceAndBlockWithdrawForThisBatch()", () => {
        it("does not postpone a withdrawRequest for a future epoch", () => __awaiter(void 0, void 0, void 0, function* () {
            const epochTokenLocker = yield EpochTokenLockerTestInterface.new();
            const ERC20 = yield MockContract.new();
            const currentStateIndex = yield epochTokenLocker.getCurrentBatchId();
            yield epochTokenLocker.requestWithdraw(ERC20.address, 100, {
                from: user_1,
            });
            yield epochTokenLocker.addBalanceAndBlockWithdrawForThisBatchTest(user_1, ERC20.address, 100);
            assert.equal((yield epochTokenLocker.getPendingWithdraw(user_1, ERC20.address))[1].toNumber(), currentStateIndex.toNumber(), "State index updated incorrectly during to addBalanceAndBlockWithdrawForThisBatchTest");
        }));
        it("blocks withdraws for this epoch", () => __awaiter(void 0, void 0, void 0, function* () {
            const epochTokenLocker = yield EpochTokenLockerTestInterface.new();
            const ERC20 = yield MockContract.new();
            yield ERC20.givenAnyReturnBool(true);
            const currentStateIndex = yield epochTokenLocker.getCurrentBatchId();
            yield epochTokenLocker.requestWithdraw(ERC20.address, 100, {
                from: user_1,
            });
            yield utilities_1.closeAuction(epochTokenLocker);
            yield epochTokenLocker.addBalanceAndBlockWithdrawForThisBatchTest(user_1, ERC20.address, 100);
            const batchId = yield epochTokenLocker.getCurrentBatchId();
            assert.equal((yield epochTokenLocker.lastCreditBatchId(user_1, ERC20.address)).toNumber(), batchId.toNumber());
            assert.equal((yield epochTokenLocker.getPendingWithdraw(user_1, ERC20.address))[1].toNumber(), currentStateIndex.toNumber(), "PendingWithdrawBatchNumber not set correctly");
        }));
    });
    describe("subtractBalance()", () => {
        it("modifies the balance by subtraction", () => __awaiter(void 0, void 0, void 0, function* () {
            const epochTokenLocker = yield EpochTokenLockerTestInterface.new();
            const ERC20 = yield MockContract.new();
            yield epochTokenLocker.addBalanceTest(user_1, ERC20.address, 100);
            yield epochTokenLocker.subtractBalanceTest(user_1, ERC20.address, 50);
            assert.equal((yield epochTokenLocker.getBalance(user_1, ERC20.address)).toNumber(), 50);
        }));
        it("modifies the balance by subtracting on behalf of someone else", () => __awaiter(void 0, void 0, void 0, function* () {
            const epochTokenLocker = yield EpochTokenLockerTestInterface.new();
            const ERC20 = yield MockContract.new();
            yield ERC20.givenAnyReturnBool(true);
            yield epochTokenLocker.deposit(ERC20.address, 100, { from: user_2 });
            yield utilities_1.closeAuction(epochTokenLocker);
            yield epochTokenLocker.subtractBalanceTest(user_2, ERC20.address, 50);
            assert.equal((yield epochTokenLocker.getBalance(user_2, ERC20.address)).toNumber(), 50);
        }));
        it("throws in case of underflow", () => __awaiter(void 0, void 0, void 0, function* () {
            const epochTokenLocker = yield EpochTokenLockerTestInterface.new();
            const ERC20 = yield MockContract.new();
            yield truffle_assertions_1.default.reverts(epochTokenLocker.subtractBalanceTest(user_1, ERC20.address, 50));
        }));
    });
}));
