import { tokenSpecifics } from "./tokenSpecifics";
import { BigNumber } from "@ethersproject/bignumber";
import { Biconomy } from "../meta-tx/biconomy";
import { prepareDataSignatureParameters } from "../utils/signature";
import { nativeMetaTransactionsIface, alternativeNonceIface } from "./interface";
import { getName as getERC20Name } from "../erc20/handler";
import { erc20Iface } from "../erc20/interface";
const ERC712_VERSION = "1"; // Is consistent with all implementations of child tokens on Polygon
export async function getNonce(args) {
    try {
        const result = await args.web3Lib.call({
            to: args.contractAddress,
            data: nativeMetaTransactionsIface.encodeFunctionData("getNonce", [
                args.user
            ])
        });
        const [nonce] = 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 = await args.web3Lib.call({
            to: args.contractAddress,
            data: alternativeNonceIface.encodeFunctionData("nonces", [args.user])
        });
        const [nonce] = alternativeNonceIface.decodeFunctionResult("nonces", result);
        return String(nonce);
    }
}
// Implementation
export async function signNativeMetaTx(args) {
    const metaTransactionType = [
        { name: "nonce", type: "uint256" },
        { name: "from", type: "address" },
        { name: "functionSignature", type: "bytes" }
    ];
    const customSignatureType = {
        MetaTransaction: metaTransactionType
    };
    const signerAddress = await args.web3Lib.getSignerAddress();
    const message = {
        nonce: args.nonce.toString(),
        from: signerAddress,
        functionSignature: args.functionSignature
    };
    const baseParams = {
        ...args,
        verifyingContractAddress: args.metaTxHandlerAddress,
        customSignatureType,
        customDomainData: args.domain,
        primaryType: "MetaTransaction",
        message
    };
    if (args.returnTypedDataToSign) {
        const structuredData = await prepareDataSignatureParameters({
            ...baseParams,
            returnTypedDataToSign: true
        });
        return {
            ...structuredData,
            functionName: args.functionName,
            functionSignature: args.functionSignature
        };
    }
    const signature = await prepareDataSignatureParameters({
        ...baseParams,
        returnTypedDataToSign: false
    });
    return {
        functionName: args.functionName,
        functionSignature: args.functionSignature,
        ...signature
    };
}
// Implementation
export async function signNativeMetaTxApproveExchangeToken(args) {
    const domain = {
        name: await getERC20Name({
            contractAddress: args.exchangeToken,
            web3Lib: args.web3Lib
        }),
        version: tokenSpecifics(args.chainId, args.exchangeToken)?.ERC712_VERSION ||
            ERC712_VERSION
    };
    const nonce = await getNonce({
        contractAddress: args.exchangeToken,
        user: args.user,
        web3Lib: args.web3Lib
    });
    const functionName = "approve(address,uint256)";
    const functionSignature = 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({ ...baseArgs, returnTypedDataToSign: true });
    }
    return signNativeMetaTx({ ...baseArgs, returnTypedDataToSign: false });
}
export async function relayNativeMetaTransaction(args) {
    const { chainId, contractAddress, metaTx } = args;
    const biconomy = new Biconomy(metaTx.config.relayerUrl, metaTx.config.apiKey, metaTx.config.apiId);
    const relayTxResponse = await 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: async () => {
            const waitResponse = await biconomy.wait({
                networkId: chainId,
                transactionHash: relayTxResponse.txHash
            });
            const txHash = waitResponse.data.newHash;
            const txReceipt = await args.web3LibAdapter.getTransactionReceipt(txHash);
            return {
                to: txReceipt?.to || contractAddress,
                from: txReceipt?.from || metaTx.params.userAddress,
                transactionHash: txHash,
                logs: txReceipt?.logs || [],
                effectiveGasPrice: BigNumber.from(waitResponse.data.newGasPrice),
                blockNumber: txReceipt.blockNumber,
                gasUsed: txReceipt.gasUsed
            };
        },
        hash: relayTxResponse.txHash
    };
}
// Implementation
export async function executeNativeMetaTransaction(args) {
    const data = 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