"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.encodeTransferAuthorizationEntry = encodeTransferAuthorizationEntry;
exports.approve = approve;
exports.getAllowance = getAllowance;
exports.getDecimals = getDecimals;
exports.getSymbol = getSymbol;
exports.getName = getName;
exports.ensureAllowance = ensureAllowance;
exports.balanceOf = balanceOf;
exports.signReceiveWithErc3009Authorization = signReceiveWithErc3009Authorization;
exports.signReceiveWithErc2612Permit = signReceiveWithErc2612Permit;
exports.signReceiveWithPermit2 = signReceiveWithPermit2;
exports.signReceiveWithDaiPermit = signReceiveWithDaiPermit;
const abi_1 = require("@ethersproject/abi");
const bignumber_1 = require("@ethersproject/bignumber");
const bytes_1 = require("@ethersproject/bytes");
const random_1 = require("@ethersproject/random");
const interface_1 = require("./interface");
const interface_2 = require("../native-meta-tx/interface");
const signature_1 = require("../utils/signature");
const TRANSFER_STRATEGY_ID = {
    ERC3009: 1,
    EIP2612: 2,
    Permit2: 3,
    DAIPermit: 4
};
function encodeTransferAuthorizationEntry(auth) {
    let innerData;
    switch (auth.strategy) {
        case "ERC3009":
            innerData = abi_1.defaultAbiCoder.encode(["uint256", "uint256", "bytes32", "uint8", "bytes32", "bytes32"], [
                auth.data.validAfter,
                auth.data.validBefore,
                auth.data.nonce,
                auth.v,
                auth.r,
                auth.s
            ]);
            break;
        case "EIP2612":
            innerData = abi_1.defaultAbiCoder.encode(["uint256", "uint8", "bytes32", "bytes32"], [auth.data.deadline, auth.v, auth.r, auth.s]);
            break;
        case "Permit2":
            innerData = abi_1.defaultAbiCoder.encode(["uint256", "uint256", "bytes"], [auth.data.nonce, auth.data.deadline, auth.signature]);
            break;
        case "DAIPermit":
            innerData = abi_1.defaultAbiCoder.encode(["uint256", "uint256", "uint8", "bytes32", "bytes32"], [auth.data.nonce, auth.data.expiry, auth.v, auth.r, auth.s]);
            break;
    }
    return abi_1.defaultAbiCoder.encode(["uint8", "bytes"], [TRANSFER_STRATEGY_ID[auth.strategy], innerData]);
}
// Implementation
function approve(args) {
    return __awaiter(this, void 0, void 0, function* () {
        const transactionRequest = Object.assign(Object.assign({}, args.txRequest), { to: args.contractAddress, data: interface_1.erc20Iface.encodeFunctionData("approve", [args.spender, args.value]) });
        if (args.returnTxInfo) {
            return transactionRequest;
        }
        else {
            return args.web3Lib.sendTransaction(transactionRequest);
        }
    });
}
function getAllowance(args) {
    return __awaiter(this, void 0, void 0, function* () {
        const result = yield args.web3Lib.call({
            to: args.contractAddress,
            data: interface_1.erc20Iface.encodeFunctionData("allowance", [args.owner, args.spender])
        });
        const [allowance] = interface_1.erc20Iface.decodeFunctionResult("allowance", result);
        return String(allowance);
    });
}
function getDecimals(args) {
    return __awaiter(this, void 0, void 0, function* () {
        const result = yield args.web3Lib.call({
            to: args.contractAddress,
            data: interface_1.erc20Iface.encodeFunctionData("decimals", [])
        });
        const [decimals] = interface_1.erc20Iface.decodeFunctionResult("decimals", result);
        return Number(decimals);
    });
}
function getSymbol(args) {
    return __awaiter(this, void 0, void 0, function* () {
        const result = yield args.web3Lib.call({
            to: args.contractAddress,
            data: interface_1.erc20Iface.encodeFunctionData("symbol", [])
        });
        const [symbols] = interface_1.erc20Iface.decodeFunctionResult("symbol", result);
        return String(symbols);
    });
}
function getName(args) {
    return __awaiter(this, void 0, void 0, function* () {
        const result = yield args.web3Lib.call({
            to: args.contractAddress,
            data: interface_1.erc20Iface.encodeFunctionData("name", [])
        });
        const [name] = interface_1.erc20Iface.decodeFunctionResult("name", result);
        return String(name);
    });
}
function ensureAllowance(args) {
    return __awaiter(this, void 0, void 0, function* () {
        if (args.returnTxInfo) {
            return;
        }
        const allowance = yield getAllowance(args);
        if (bignumber_1.BigNumber.from(allowance).lt(args.value)) {
            const approveTx = yield approve(Object.assign(Object.assign({}, args), { returnTxInfo: false }));
            yield approveTx.wait();
        }
    });
}
function balanceOf(args) {
    return __awaiter(this, void 0, void 0, function* () {
        const result = yield args.web3Lib.call({
            to: args.contractAddress,
            data: interface_1.erc20Iface.encodeFunctionData("balanceOf", [args.owner])
        });
        const [balance] = interface_1.erc20Iface.decodeFunctionResult("balanceOf", result);
        return String(balance);
    });
}
// Implementation
function signReceiveWithErc3009Authorization(args) {
    return __awaiter(this, void 0, void 0, function* () {
        const nonce = (0, bytes_1.hexlify)((0, random_1.randomBytes)(32));
        const customSignatureType = {
            EIP712Domain: [
                { name: "name", type: "string" },
                { name: "version", type: "string" },
                { name: "chainId", type: "uint256" },
                { name: "verifyingContract", type: "address" }
            ],
            ReceiveWithAuthorization: [
                { name: "from", type: "address" },
                { name: "to", type: "address" },
                { name: "value", type: "uint256" },
                { name: "validAfter", type: "uint256" },
                { name: "validBefore", type: "uint256" },
                { name: "nonce", type: "bytes32" }
            ]
        };
        const customDomainData = {
            name: args.tokenDomain.name,
            version: args.tokenDomain.version,
            chainId: args.chainId,
            salt: undefined
        };
        const message = {
            from: args.user,
            to: args.spender,
            value: args.value.toString(),
            validAfter: args.validAfter.toString(),
            validBefore: args.validBefore.toString(),
            nonce
        };
        const baseParams = {
            web3Lib: args.web3Lib,
            chainId: args.chainId,
            verifyingContractAddress: args.exchangeToken,
            customSignatureType,
            customDomainData,
            primaryType: "ReceiveWithAuthorization",
            message
        };
        if (args.returnTypedDataToSign) {
            return (0, signature_1.prepareDataSignatureParameters)(Object.assign(Object.assign({}, baseParams), { returnTypedDataToSign: true }));
        }
        const sig = yield (0, signature_1.prepareDataSignatureParameters)(Object.assign(Object.assign({}, baseParams), { returnTypedDataToSign: false }));
        return Object.assign(Object.assign({}, sig), { strategy: "ERC3009", data: {
                validAfter: args.validAfter,
                validBefore: args.validBefore,
                nonce
            } });
    });
}
// Implementation
function signReceiveWithErc2612Permit(args) {
    return __awaiter(this, void 0, void 0, function* () {
        const nonceResult = yield args.web3Lib.call({
            to: args.exchangeToken,
            data: interface_2.alternativeNonceIface.encodeFunctionData("nonces", [args.user])
        });
        const [nonce] = interface_2.alternativeNonceIface.decodeFunctionResult("nonces", nonceResult);
        const customSignatureType = {
            EIP712Domain: [
                { name: "name", type: "string" },
                { name: "version", type: "string" },
                { name: "chainId", type: "uint256" },
                { name: "verifyingContract", type: "address" }
            ],
            Permit: [
                { name: "owner", type: "address" },
                { name: "spender", type: "address" },
                { name: "value", type: "uint256" },
                { name: "nonce", type: "uint256" },
                { name: "deadline", type: "uint256" }
            ]
        };
        const customDomainData = {
            name: args.tokenDomain.name,
            version: args.tokenDomain.version,
            chainId: args.chainId,
            salt: undefined
        };
        const message = {
            owner: args.user,
            spender: args.spender,
            value: args.value.toString(),
            nonce: nonce.toString(),
            deadline: args.deadline.toString()
        };
        const baseParams = {
            web3Lib: args.web3Lib,
            chainId: args.chainId,
            verifyingContractAddress: args.exchangeToken,
            customSignatureType,
            customDomainData,
            primaryType: "Permit",
            message
        };
        if (args.returnTypedDataToSign) {
            return (0, signature_1.prepareDataSignatureParameters)(Object.assign(Object.assign({}, baseParams), { returnTypedDataToSign: true }));
        }
        const sig = yield (0, signature_1.prepareDataSignatureParameters)(Object.assign(Object.assign({}, baseParams), { returnTypedDataToSign: false }));
        return Object.assign(Object.assign({}, sig), { strategy: "EIP2612", data: { deadline: args.deadline } });
    });
}
// Implementation
function signReceiveWithPermit2(args) {
    return __awaiter(this, void 0, void 0, function* () {
        var _a;
        const permit2Nonce = (_a = args.permit2Nonce) !== null && _a !== void 0 ? _a : bignumber_1.BigNumber.from((0, random_1.randomBytes)(32));
        const customSignatureType = {
            EIP712Domain: [
                { name: "name", type: "string" },
                { name: "chainId", type: "uint256" },
                { name: "verifyingContract", type: "address" }
            ],
            PermitTransferFrom: [
                { name: "permitted", type: "TokenPermissions" },
                { name: "spender", type: "address" },
                { name: "nonce", type: "uint256" },
                { name: "deadline", type: "uint256" }
            ],
            TokenPermissions: [
                { name: "token", type: "address" },
                { name: "amount", type: "uint256" }
            ]
        };
        const customDomainData = {
            name: "Permit2",
            chainId: args.chainId,
            version: undefined,
            salt: undefined
        };
        const message = {
            permitted: {
                token: args.exchangeToken,
                amount: args.value.toString()
            },
            spender: args.spender,
            nonce: permit2Nonce.toString(),
            deadline: args.deadline.toString()
        };
        const baseParams = {
            web3Lib: args.web3Lib,
            chainId: args.chainId,
            verifyingContractAddress: args.permit2Address,
            customSignatureType,
            customDomainData,
            primaryType: "PermitTransferFrom",
            message
        };
        if (args.returnTypedDataToSign) {
            return (0, signature_1.prepareDataSignatureParameters)(Object.assign(Object.assign({}, baseParams), { returnTypedDataToSign: true }));
        }
        const sig = yield (0, signature_1.prepareDataSignatureParameters)(Object.assign(Object.assign({}, baseParams), { returnTypedDataToSign: false }));
        return Object.assign(Object.assign({}, sig), { strategy: "Permit2", data: { nonce: permit2Nonce, deadline: args.deadline } });
    });
}
// Implementation
function signReceiveWithDaiPermit(args) {
    return __awaiter(this, void 0, void 0, function* () {
        const nonceResult = yield args.web3Lib.call({
            to: args.exchangeToken,
            data: interface_2.alternativeNonceIface.encodeFunctionData("nonces", [args.user])
        });
        const [nonce] = interface_2.alternativeNonceIface.decodeFunctionResult("nonces", nonceResult);
        const customSignatureType = {
            EIP712Domain: [
                { name: "name", type: "string" },
                { name: "version", type: "string" },
                { name: "chainId", type: "uint256" },
                { name: "verifyingContract", type: "address" }
            ],
            Permit: [
                { name: "holder", type: "address" },
                { name: "spender", type: "address" },
                { name: "nonce", type: "uint256" },
                { name: "expiry", type: "uint256" },
                { name: "allowed", type: "bool" }
            ]
        };
        const customDomainData = {
            name: args.tokenDomain.name,
            version: "1",
            chainId: args.chainId,
            salt: undefined
        };
        const message = {
            holder: args.user,
            spender: args.spender,
            nonce: nonce.toString(),
            expiry: args.expiry.toString(),
            allowed: true
        };
        const baseParams = {
            web3Lib: args.web3Lib,
            chainId: args.chainId,
            verifyingContractAddress: args.exchangeToken,
            customSignatureType,
            customDomainData,
            primaryType: "Permit",
            message
        };
        if (args.returnTypedDataToSign) {
            return (0, signature_1.prepareDataSignatureParameters)(Object.assign(Object.assign({}, baseParams), { returnTypedDataToSign: true }));
        }
        const sig = yield (0, signature_1.prepareDataSignatureParameters)(Object.assign(Object.assign({}, baseParams), { returnTypedDataToSign: false }));
        return Object.assign(Object.assign({}, sig), { strategy: "DAIPermit", data: { nonce: nonce.toString(), expiry: args.expiry } });
    });
}
//# sourceMappingURL=handler.js.map