"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.getNonce = getNonce;
exports.signNativeMetaTx = signNativeMetaTx;
exports.signNativeMetaTxApproveExchangeToken = signNativeMetaTxApproveExchangeToken;
exports.relayNativeMetaTransaction = relayNativeMetaTransaction;
exports.executeNativeMetaTransaction = executeNativeMetaTransaction;
const tokenSpecifics_1 = require("./tokenSpecifics");
const bignumber_1 = require("@ethersproject/bignumber");
const biconomy_1 = require("../meta-tx/biconomy");
const signature_1 = require("../utils/signature");
const interface_1 = require("./interface");
const handler_1 = require("../erc20/handler");
const interface_2 = require("../erc20/interface");
const ERC712_VERSION = "1"; // Is consistent with all implementations of child tokens on Polygon
function getNonce(args) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const result = yield args.web3Lib.call({
                to: args.contractAddress,
                data: interface_1.nativeMetaTransactionsIface.encodeFunctionData("getNonce", [
                    args.user
                ])
            });
            const [nonce] = interface_1.nativeMetaTransactionsIface.decodeFunctionResult("getNonce", result);
            return String(nonce);
        }
        catch (e) {
            console.warn(`Calling getNonce() for contract ${args.contractAddress} has failed. Try with 'nonces' instead`);
            // If so, call 'nonces()' instead (USDC case, for instance)
            const result = yield args.web3Lib.call({
                to: args.contractAddress,
                data: interface_1.alternativeNonceIface.encodeFunctionData("nonces", [args.user])
            });
            const [nonce] = interface_1.alternativeNonceIface.decodeFunctionResult("nonces", result);
            return String(nonce);
        }
    });
}
// Implementation
function signNativeMetaTx(args) {
    return __awaiter(this, void 0, void 0, function* () {
        const metaTransactionType = [
            { name: "nonce", type: "uint256" },
            { name: "from", type: "address" },
            { name: "functionSignature", type: "bytes" }
        ];
        const customSignatureType = {
            MetaTransaction: metaTransactionType
        };
        const signerAddress = yield args.web3Lib.getSignerAddress();
        const message = {
            nonce: args.nonce.toString(),
            from: signerAddress,
            functionSignature: args.functionSignature
        };
        const baseParams = Object.assign(Object.assign({}, args), { verifyingContractAddress: args.metaTxHandlerAddress, customSignatureType, customDomainData: args.domain, primaryType: "MetaTransaction", message });
        if (args.returnTypedDataToSign) {
            const structuredData = yield (0, signature_1.prepareDataSignatureParameters)(Object.assign(Object.assign({}, baseParams), { returnTypedDataToSign: true }));
            return Object.assign(Object.assign({}, structuredData), { functionName: args.functionName, functionSignature: args.functionSignature });
        }
        const signature = yield (0, signature_1.prepareDataSignatureParameters)(Object.assign(Object.assign({}, baseParams), { returnTypedDataToSign: false }));
        return Object.assign({ functionName: args.functionName, functionSignature: args.functionSignature }, signature);
    });
}
// Implementation
function signNativeMetaTxApproveExchangeToken(args) {
    return __awaiter(this, void 0, void 0, function* () {
        var _a;
        const domain = {
            name: yield (0, handler_1.getName)({
                contractAddress: args.exchangeToken,
                web3Lib: args.web3Lib
            }),
            version: ((_a = (0, tokenSpecifics_1.tokenSpecifics)(args.chainId, args.exchangeToken)) === null || _a === void 0 ? void 0 : _a.ERC712_VERSION) ||
                ERC712_VERSION
        };
        const nonce = yield getNonce({
            contractAddress: args.exchangeToken,
            user: args.user,
            web3Lib: args.web3Lib
        });
        const functionName = "approve(address,uint256)";
        const functionSignature = interface_2.erc20Iface.encodeFunctionData("approve", [
            args.spender,
            args.value
        ]);
        const baseArgs = {
            web3Lib: args.web3Lib,
            metaTxHandlerAddress: args.exchangeToken,
            chainId: args.chainId,
            nonce,
            functionName,
            functionSignature,
            domain
        };
        if (args.returnTypedDataToSign) {
            return signNativeMetaTx(Object.assign(Object.assign({}, baseArgs), { returnTypedDataToSign: true }));
        }
        return signNativeMetaTx(Object.assign(Object.assign({}, baseArgs), { returnTypedDataToSign: false }));
    });
}
function relayNativeMetaTransaction(args) {
    return __awaiter(this, void 0, void 0, function* () {
        const { chainId, contractAddress, metaTx } = args;
        const biconomy = new biconomy_1.Biconomy(metaTx.config.relayerUrl, metaTx.config.apiKey, metaTx.config.apiId);
        const relayTxResponse = yield biconomy.relayTransaction({
            to: contractAddress,
            params: [
                metaTx.params.userAddress,
                metaTx.params.functionSignature,
                metaTx.params.sigR,
                metaTx.params.sigS,
                metaTx.params.sigV
            ],
            from: metaTx.params.userAddress
        });
        return {
            wait: () => __awaiter(this, void 0, void 0, function* () {
                const waitResponse = yield biconomy.wait({
                    networkId: chainId,
                    transactionHash: relayTxResponse.txHash
                });
                const txHash = waitResponse.data.newHash;
                const txReceipt = yield args.web3LibAdapter.getTransactionReceipt(txHash);
                return {
                    to: (txReceipt === null || txReceipt === void 0 ? void 0 : txReceipt.to) || contractAddress,
                    from: (txReceipt === null || txReceipt === void 0 ? void 0 : txReceipt.from) || metaTx.params.userAddress,
                    transactionHash: txHash,
                    logs: (txReceipt === null || txReceipt === void 0 ? void 0 : txReceipt.logs) || [],
                    effectiveGasPrice: bignumber_1.BigNumber.from(waitResponse.data.newGasPrice),
                    blockNumber: txReceipt.blockNumber,
                    gasUsed: txReceipt.gasUsed
                };
            }),
            hash: relayTxResponse.txHash
        };
    });
}
// Implementation
function executeNativeMetaTransaction(args) {
    return __awaiter(this, void 0, void 0, function* () {
        const data = interface_1.nativeMetaTransactionsIface.encodeFunctionData("executeMetaTransaction", [args.userAddress, args.functionSignature, args.sigR, args.sigS, args.sigV]);
        const tx = { to: args.contractAddress, data };
        return args.returnTxInfo ? tx : args.web3Lib.sendTransaction(tx);
    });
}
//# sourceMappingURL=handler.js.map