import { utils, envConfigs } from "@bosonprotocol/common";
import { storeMetadataOnTheGraph } from "../offers/storage";
import { BigNumber } from "@ethersproject/bignumber";
import { hexlify } from "@ethersproject/bytes";
import { encodeCreateSeller, encodeOptInToSellerUpdate, encodeUpdateSeller } from "../accounts/interface";
import { bosonExchangeCommitHandlerIface, bosonExchangeHandlerIface, encodeCommitToBuyerOffer, encodeCreateOfferAndCommit } from "../exchanges/interface";
import { bosonOfferHandlerIface, encodeCreateOffer, encodeCreateOfferBatch, encodeReserveRange } from "../offers/interface";
import { prepareDataSignatureParameters, rebuildSignature } from "../utils/signature";
import { Biconomy } from "./biconomy";
import { metaTransactionsHandlerIface } from "./interface";
import { isAddress } from "@ethersproject/address";
import { AddressZero } from "@ethersproject/constants";
import { encodeDepositFunds, encodeWithdrawFunds } from "../funds/interface";
import { bosonDisputeHandlerIface } from "../disputes/interface";
import { encodeCreateGroup } from "../groups/interface";
import { encodeCommitToConditionalOfferAndRedeemVoucher, encodeCommitToOfferAndRedeemVoucher, encodeCreateOfferCommitAndRedeem, encodeCreateOfferWithCondition } from "../orchestration/interface";
import { encodeCallExternalContract, encodePreMint, encodeSetApprovalForAll, encodeSetApprovalForAllToContract } from "../voucher/interface";
import { keccak256 } from "@ethersproject/keccak256";
import { id } from "@ethersproject/hash";
import { defaultAbiCoder } from "@ethersproject/abi";
import { encodeTransferAuthorizationEntry } from "../erc20/handler";
import { getNonce, verifyEIP712 } from "../forwarder/handler";
import { isTrustedForwarder } from "../voucher/handler";
import { findCollectionSalt } from "../accounts/handler";
import { storeMetadataItems } from "../metadata/storeMetadataItems";
// Implementation
export async function signMetaTx(args) {
    const metaTransactionType = [
        { name: "nonce", type: "uint256" },
        { name: "from", type: "address" },
        { name: "contractAddress", type: "address" },
        { name: "functionName", type: "string" },
        { name: "functionSignature", type: "bytes" }
    ];
    const customSignatureType = {
        MetaTransaction: metaTransactionType
    };
    const signerAddress = await args.web3Lib.getSignerAddress();
    const message = {
        nonce: args.nonce.toString(),
        from: signerAddress,
        contractAddress: args.metaTxHandlerAddress,
        functionName: args.functionName,
        functionSignature: args.functionSignature
    };
    if (args.returnTypedDataToSign) {
        const structuredData = await prepareDataSignatureParameters({
            ...args,
            verifyingContractAddress: args.metaTxHandlerAddress,
            customSignatureType,
            primaryType: "MetaTransaction",
            message,
            returnTypedDataToSign: true
        });
        return {
            ...structuredData,
            functionName: args.functionName,
            functionSignature: args.functionSignature
        };
    }
    const signature = await prepareDataSignatureParameters({
        ...args,
        verifyingContractAddress: args.metaTxHandlerAddress,
        customSignatureType,
        primaryType: "MetaTransaction",
        message,
        returnTypedDataToSign: false
    });
    return {
        functionName: args.functionName,
        functionSignature: args.functionSignature,
        ...signature
    };
}
export async function signVoucherMetaTx(args) {
    const forwardType = [
        { name: "from", type: "address" },
        { name: "to", type: "address" },
        { name: "nonce", type: "uint256" },
        { name: "data", type: "bytes" }
    ];
    const customSignatureType = {
        EIP712Domain: [
            { name: "name", type: "string" },
            { name: "version", type: "string" },
            { name: "chainId", type: "uint256" },
            { name: "verifyingContract", type: "address" }
        ],
        ForwardRequest: forwardType
    };
    const signerAddress = await args.web3Lib.getSignerAddress();
    const chainId = await args.web3Lib.getChainId();
    const nonce = await getNonce({
        contractAddress: args.forwarderAddress,
        user: signerAddress,
        web3Lib: args.web3Lib,
        forwarderAbi: args.forwarderAbi
    });
    const message = {
        from: signerAddress,
        to: args.bosonVoucherAddress,
        nonce,
        data: args.functionSignature
    };
    const signature = await prepareDataSignatureParameters({
        ...args,
        verifyingContractAddress: args.forwarderAddress,
        customSignatureType,
        primaryType: "ForwardRequest",
        message,
        customDomainData: {
            name: "MockForwarder",
            version: "0.0.1",
            chainId,
            salt: undefined
        },
        returnTypedDataToSign: false
    });
    return {
        to: message.to,
        functionSignature: args.functionSignature,
        request: message,
        ...signature
    };
}
export async function signBiconomyVoucherMetaTx(args) {
    const customSignatureType = {
        EIP712Domain: [
            { name: "name", type: "string" },
            { name: "version", type: "string" },
            // { name: "chainId", type: "uint256" },
            // { name: "verifyingContract", type: "address" }
            { name: "verifyingContract", type: "address" },
            { name: "salt", type: "bytes32" }
        ],
        ERC20ForwardRequest: [
            { name: "from", type: "address" },
            { name: "to", type: "address" },
            { name: "token", type: "address" },
            { name: "txGas", type: "uint256" },
            { name: "tokenGasPrice", type: "uint256" },
            { name: "batchId", type: "uint256" },
            { name: "batchNonce", type: "uint256" },
            { name: "deadline", type: "uint256" },
            { name: "data", type: "bytes" }
        ]
    };
    const signerAddress = await args.web3Lib.getSignerAddress();
    const chainId = await args.web3Lib.getChainId();
    // Check which forwarder needs to be used for the contract
    const biconomyForwarderDomainDetails = await new Biconomy(args.relayerUrl).getForwarderDomainDetails({ chainId });
    const biconomyForwarderDomainData = await new Promise(async (resolve, reject) => {
        try {
            for (const bFDD of Object.values(biconomyForwarderDomainDetails)) {
                const ret = await isTrustedForwarder({
                    forwarder: bFDD.verifyingContract,
                    contractAddress: args.bosonVoucherAddress,
                    web3Lib: args.web3Lib
                });
                if (ret) {
                    resolve(bFDD);
                }
            }
            resolve(undefined);
        }
        catch (e) {
            reject(e);
        }
    });
    if (!biconomyForwarderDomainData) {
        throw `Unable to find the trusted forwarder for BosonVoucher contract ${args.bosonVoucherAddress}`;
    }
    const nonce = await getNonce({
        contractAddress: biconomyForwarderDomainData.verifyingContract,
        user: signerAddress,
        web3Lib: args.web3Lib,
        batchId: args.batchId,
        forwarderAbi: args.forwarderAbi
    });
    const message = {
        from: signerAddress,
        to: args.bosonVoucherAddress,
        token: "0x0000000000000000000000000000000000000000",
        txGas: args.txGas,
        tokenGasPrice: "0",
        batchId: args.batchId,
        batchNonce: nonce,
        deadline: Math.floor(Date.now() / 1000 + 3600),
        data: args.functionSignature
    };
    const signatureParams = await prepareDataSignatureParameters({
        ...args,
        chainId,
        verifyingContractAddress: biconomyForwarderDomainData.verifyingContract,
        customSignatureType,
        primaryType: "ERC20ForwardRequest",
        message,
        customDomainData: {
            ...biconomyForwarderDomainData
            // chainId
            // salt: undefined
        },
        returnTypedDataToSign: false
    });
    const signature = signatureParams.signature;
    const getDomainSeparator = async () => {
        const domainData = biconomyForwarderDomainData;
        const domainSeparator = keccak256(defaultAbiCoder.encode(["bytes32", "bytes32", "bytes32", "address", "bytes32"], [
            id("EIP712Domain(string name,string version,address verifyingContract,bytes32 salt)"),
            id(domainData.name),
            id(domainData.version),
            domainData.verifyingContract,
            domainData.salt
        ]));
        return domainSeparator;
    };
    const domainSeparator = await getDomainSeparator();
    // verify signature
    const signatureVerified = await verifyEIP712({
        request: message,
        contractAddress: biconomyForwarderDomainData.verifyingContract,
        web3Lib: args.web3Lib,
        domainSeparator,
        forwarderAbi: args.forwarderAbi,
        signature
    });
    if (!signatureVerified) {
        throw `Signature is not verified`;
    }
    return {
        to: message.to,
        domainSeparator,
        request: message,
        ...signatureParams,
        signature,
        functionSignature: args.functionSignature
    };
}
export async function relayBiconomyMetaTransaction(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.request,
            metaTx.params.domainSeparator,
            metaTx.params.signature
        ],
        from: metaTx.params.userAddress,
        signatureType: "EIP712_SIGN"
    });
    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 signMetaTxCreateSeller(args) {
    await Promise.all([args.createSellerArgs.contractUri, args.createSellerArgs.metadataUri].map((metadataUri) => storeMetadataOnTheGraph({
        metadataUriOrHash: metadataUri,
        metadataStorage: args.metadataStorage,
        theGraphStorage: args.theGraphStorage
    })));
    const collectionSalt = await findCollectionSalt({
        sellerToCreate: args.createSellerArgs,
        contractAddress: args.metaTxHandlerAddress,
        ...args
    });
    const signMetaTxArgs = {
        ...args,
        functionName: "createSeller((uint256,address,address,address,address,bool,string),(uint256,uint8),(string,uint256,bytes32))",
        functionSignature: encodeCreateSeller(args.createSellerArgs, collectionSalt)
    };
    if (args.returnTypedDataToSign) {
        return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: true });
    }
    return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: false });
}
// Implementation
export async function signMetaTxUpdateSeller(args) {
    await storeMetadataOnTheGraph({
        metadataUriOrHash: args.updateSellerArgs.metadataUri,
        metadataStorage: args.metadataStorage,
        theGraphStorage: args.theGraphStorage
    });
    const signMetaTxArgs = {
        ...args,
        functionName: "updateSeller((uint256,address,address,address,address,bool,string),(uint256,uint8))",
        functionSignature: encodeUpdateSeller(args.updateSellerArgs)
    };
    if (args.returnTypedDataToSign) {
        return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: true });
    }
    return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: false });
}
// Implementation
export async function signMetaTxOptInToSellerUpdate(args) {
    const signMetaTxArgs = {
        ...args,
        functionName: "optInToSellerUpdate(uint256,uint8[])",
        functionSignature: encodeOptInToSellerUpdate(args.optInToSellerUpdateArgs)
    };
    if (args.returnTypedDataToSign) {
        return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: true });
    }
    return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: false });
}
// Implementation
export async function signMetaTxCreateOffer(args) {
    utils.validation.createOfferArgsSchema.validateSync(args.createOfferArgs, {
        abortEarly: false
    });
    await storeMetadataOnTheGraph({
        metadataUriOrHash: args.createOfferArgs.metadataUri,
        metadataStorage: args.metadataStorage,
        theGraphStorage: args.theGraphStorage
    });
    await storeMetadataItems({
        ...args,
        createOffersArgs: [args.createOfferArgs]
    });
    const signMetaTxArgs = {
        ...args,
        functionName: "createOffer((uint256,uint256,uint256,uint256,uint256,uint256,address,uint8,uint8,string,string,bool,uint256,(address[],uint256[])[],uint256),(uint256,uint256,uint256,uint256),(uint256,uint256,uint256),(uint256,address),uint256,uint256)",
        functionSignature: encodeCreateOffer(args.createOfferArgs)
    };
    if (args.returnTypedDataToSign) {
        return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: true });
    }
    return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: false });
}
// Implementation
export async function signMetaTxCreateOfferBatch(args) {
    for (const offerToCreate of args.createOffersArgs) {
        utils.validation.createOfferArgsSchema.validateSync(offerToCreate, {
            abortEarly: false
        });
    }
    await Promise.all(args.createOffersArgs.map((offerToCreate) => storeMetadataOnTheGraph({
        metadataUriOrHash: offerToCreate.metadataUri,
        metadataStorage: args.metadataStorage,
        theGraphStorage: args.theGraphStorage
    })));
    await storeMetadataItems(args);
    const signMetaTxArgs = {
        ...args,
        functionName: "createOfferBatch((uint256,uint256,uint256,uint256,uint256,uint256,address,uint8,uint8,string,string,bool,uint256,(address[],uint256[])[],uint256)[],(uint256,uint256,uint256,uint256)[],(uint256,uint256,uint256)[],(uint256,address)[],uint256[],uint256[])",
        functionSignature: encodeCreateOfferBatch(args.createOffersArgs)
    };
    if (args.returnTypedDataToSign) {
        return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: true });
    }
    return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: false });
}
// Implementation
export async function signMetaTxVoidOffer(args) {
    const signMetaTxArgs = {
        ...args,
        functionName: "voidOffer(uint256)",
        functionSignature: bosonOfferHandlerIface.encodeFunctionData("voidOffer", [
            args.offerId
        ])
    };
    if (args.returnTypedDataToSign) {
        return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: true });
    }
    return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: false });
}
// Implementation
export async function signMetaTxVoidOfferBatch(args) {
    const signMetaTxArgs = {
        ...args,
        functionName: "voidOfferBatch(uint256[])",
        functionSignature: bosonOfferHandlerIface.encodeFunctionData("voidOfferBatch", [args.offerIds])
    };
    if (args.returnTypedDataToSign) {
        return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: true });
    }
    return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: false });
}
// Implementation
export async function signMetaTxExtendOffer(args) {
    const signMetaTxArgs = {
        ...args,
        functionName: "extendOffer(uint256,uint256)",
        functionSignature: bosonOfferHandlerIface.encodeFunctionData("extendOffer", [args.offerId, args.validUntil])
    };
    if (args.returnTypedDataToSign) {
        return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: true });
    }
    return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: false });
}
// Implementation
export async function signMetaTxExtendOfferBatch(args) {
    const signMetaTxArgs = {
        ...args,
        functionName: "extendOfferBatch(uint256[],uint256)",
        functionSignature: bosonOfferHandlerIface.encodeFunctionData("extendOfferBatch", [args.offerIds, args.validUntil])
    };
    if (args.returnTypedDataToSign) {
        return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: true });
    }
    return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: false });
}
// Implementation
export async function signMetaTxCompleteExchangeBatch(args) {
    const signMetaTxArgs = {
        ...args,
        functionName: "completeExchangeBatch(uint256[])",
        functionSignature: bosonExchangeHandlerIface.encodeFunctionData("completeExchangeBatch", [args.exchangeIds])
    };
    if (args.returnTypedDataToSign) {
        return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: true });
    }
    return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: false });
}
// Implementation
export async function signMetaTxExpireVoucher(args) {
    const signMetaTxArgs = {
        ...args,
        functionName: "expireVoucher(uint256)",
        functionSignature: bosonExchangeHandlerIface.encodeFunctionData("expireVoucher", [args.exchangeId])
    };
    if (args.returnTypedDataToSign) {
        return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: true });
    }
    return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: false });
}
// Implementation
export async function signMetaTxRevokeVoucher(args) {
    const signMetaTxArgs = {
        ...args,
        functionName: "revokeVoucher(uint256)",
        functionSignature: bosonExchangeHandlerIface.encodeFunctionData("revokeVoucher", [args.exchangeId])
    };
    if (args.returnTypedDataToSign) {
        return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: true });
    }
    return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: false });
}
// Implementation
export async function signMetaTxCreateGroup(args) {
    const signMetaTxArgs = {
        ...args,
        functionName: "createGroup((uint256,uint256,uint256[]),(uint8,uint8,address,uint8,uint256,uint256,uint256,uint256))",
        functionSignature: encodeCreateGroup(args.createGroupArgs)
    };
    if (args.returnTypedDataToSign) {
        return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: true });
    }
    return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: false });
}
// Implementation
export async function signMetaTxReserveRange(args) {
    const signMetaTxArgs = {
        ...args,
        functionName: "reserveRange(uint256,uint256,address)",
        functionSignature: encodeReserveRange(args.offerId, args.length, args.to)
    };
    if (args.returnTypedDataToSign) {
        return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: true });
    }
    return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: false });
}
function isLocal(chainId) {
    const localConfigs = envConfigs["local"];
    return localConfigs.some((localConfig) => localConfig.chainId === chainId);
}
export async function signMetaTxPreMint(args) {
    const functionSignature = encodePreMint(args.offerId, args.amount);
    if (isLocal(args.chainId)) {
        return signVoucherMetaTx({
            ...args,
            forwarderAddress: args.forwarderAddress,
            functionSignature,
            forwarderAbi: args.forwarderAbi
        });
    }
    const txGas = 200000 + BigNumber.from(args.amount).mul(2500).toNumber(); // ~(180000 + 2250*N) estimation on 2023/02/03
    return signBiconomyVoucherMetaTx({
        ...args,
        functionSignature,
        forwarderAbi: args.forwarderAbi,
        batchId: args.batchId || "0",
        txGas
    });
}
export async function signMetaTxSetApprovalForAll(args) {
    const functionSignature = encodeSetApprovalForAll(args.operator, args.approved);
    if (isLocal(args.chainId)) {
        return signVoucherMetaTx({
            ...args,
            forwarderAddress: args.forwarderAddress,
            functionSignature,
            forwarderAbi: args.forwarderAbi
        });
    }
    const txGas = 100000; // ~70000 estimation on 2023/02/03
    return signBiconomyVoucherMetaTx({
        ...args,
        functionSignature,
        forwarderAbi: args.forwarderAbi,
        batchId: args.batchId || "0",
        txGas
    });
}
export async function signMetaTxSetApprovalForAllToContract(args, overrides = {}) {
    const functionSignature = encodeSetApprovalForAllToContract(args.operator, args.approved);
    if (isLocal(args.chainId)) {
        return signVoucherMetaTx({
            ...args,
            forwarderAddress: args.forwarderAddress,
            functionSignature,
            forwarderAbi: args.forwarderAbi
        });
    }
    const txGas = overrides.txGas || 100000; // TODO: estimate the gas needed
    return signBiconomyVoucherMetaTx({
        ...args,
        functionSignature,
        forwarderAbi: args.forwarderAbi,
        batchId: args.batchId || "0",
        txGas
    });
}
export async function signMetaTxCallExternalContract(args, overrides = {}) {
    const functionSignature = encodeCallExternalContract(args.to, args.data);
    if (isLocal(args.chainId)) {
        return signVoucherMetaTx({
            ...args,
            forwarderAddress: args.forwarderAddress,
            functionSignature,
            forwarderAbi: args.forwarderAbi
        });
    }
    const txGas = overrides.txGas || 500000; // TODO: estimate the gas needed
    return signBiconomyVoucherMetaTx({
        ...args,
        functionSignature,
        forwarderAbi: args.forwarderAbi,
        batchId: args.batchId || "0",
        txGas
    });
}
// Implementation
export async function signMetaTxCreateOfferWithCondition(args) {
    utils.validation.createOfferArgsSchema.validateSync(args.offerToCreate, {
        abortEarly: false
    });
    await storeMetadataOnTheGraph({
        metadataUriOrHash: args.offerToCreate.metadataUri,
        metadataStorage: args.metadataStorage,
        theGraphStorage: args.theGraphStorage
    });
    await storeMetadataItems({
        ...args,
        createOffersArgs: [args.offerToCreate]
    });
    const signMetaTxArgs = {
        ...args,
        functionName: "createOfferWithCondition((uint256,uint256,uint256,uint256,uint256,uint256,address,uint8,uint8,string,string,bool,uint256,(address[],uint256[])[],uint256),(uint256,uint256,uint256,uint256),(uint256,uint256,uint256),(uint256,address),(uint8,uint8,address,uint8,uint256,uint256,uint256,uint256),uint256,uint256)",
        functionSignature: encodeCreateOfferWithCondition(args.offerToCreate, args.condition)
    };
    if (args.returnTypedDataToSign) {
        return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: true });
    }
    return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: false });
}
// Implementation
export async function signMetaTxCommitToOffer(args) {
    const functionName = "commitToOffer(address,uint256)";
    const offerType = [
        { name: "buyer", type: "address" },
        { name: "offerId", type: "uint256" }
    ];
    const metaTransactionType = [
        { name: "nonce", type: "uint256" },
        { name: "from", type: "address" },
        { name: "contractAddress", type: "address" },
        { name: "functionName", type: "string" },
        { name: "offerDetails", type: "MetaTxOfferDetails" }
    ];
    const customSignatureType = {
        MetaTxCommitToOffer: metaTransactionType,
        MetaTxOfferDetails: offerType
    };
    const buyerAddress = await args.web3Lib.getSignerAddress();
    const message = {
        nonce: args.nonce.toString(),
        from: buyerAddress,
        contractAddress: args.metaTxHandlerAddress,
        functionName,
        offerDetails: {
            buyer: buyerAddress,
            offerId: args.offerId.toString()
        }
    };
    const functionSignature = bosonExchangeCommitHandlerIface.encodeFunctionData("commitToOffer", [buyerAddress, args.offerId]);
    if (args.returnTypedDataToSign) {
        const structuredData = await prepareDataSignatureParameters({
            ...args,
            verifyingContractAddress: args.metaTxHandlerAddress,
            customSignatureType,
            primaryType: "MetaTxCommitToOffer",
            message,
            returnTypedDataToSign: true
        });
        return { ...structuredData, functionName, functionSignature };
    }
    const signatureParams = await prepareDataSignatureParameters({
        ...args,
        verifyingContractAddress: args.metaTxHandlerAddress,
        customSignatureType,
        primaryType: "MetaTxCommitToOffer",
        message,
        returnTypedDataToSign: false
    });
    return {
        ...signatureParams,
        functionName,
        functionSignature
    };
}
// Implementation
export async function signMetaTxCommitToConditionalOffer(args) {
    const functionName = "commitToConditionalOffer(address,uint256,uint256)";
    const offerType = [
        { name: "buyer", type: "address" },
        { name: "offerId", type: "uint256" },
        { name: "tokenId", type: "uint256" }
    ];
    const metaTransactionType = [
        { name: "nonce", type: "uint256" },
        { name: "from", type: "address" },
        { name: "contractAddress", type: "address" },
        { name: "functionName", type: "string" },
        { name: "offerDetails", type: "MetaTxConditionalOfferDetails" }
    ];
    const customSignatureType = {
        MetaTxCommitToConditionalOffer: metaTransactionType,
        MetaTxConditionalOfferDetails: offerType
    };
    const buyerAddress = await args.web3Lib.getSignerAddress();
    const message = {
        nonce: args.nonce.toString(),
        from: buyerAddress,
        contractAddress: args.metaTxHandlerAddress,
        functionName,
        offerDetails: {
            buyer: buyerAddress,
            offerId: args.offerId.toString(),
            tokenId: args.tokenId.toString()
        }
    };
    const functionSignature = bosonExchangeCommitHandlerIface.encodeFunctionData("commitToConditionalOffer", [buyerAddress, args.offerId, args.tokenId]);
    if (args.returnTypedDataToSign) {
        const structuredData = await prepareDataSignatureParameters({
            ...args,
            verifyingContractAddress: args.metaTxHandlerAddress,
            customSignatureType,
            primaryType: "MetaTxCommitToConditionalOffer",
            message,
            returnTypedDataToSign: true
        });
        return { ...structuredData, functionName, functionSignature };
    }
    const signatureParams = await prepareDataSignatureParameters({
        ...args,
        verifyingContractAddress: args.metaTxHandlerAddress,
        customSignatureType,
        primaryType: "MetaTxCommitToConditionalOffer",
        message,
        returnTypedDataToSign: false
    });
    return {
        ...signatureParams,
        functionName,
        functionSignature
    };
}
// Implementation
export async function signMetaTxCommitToBuyerOffer(args) {
    const functionName = "commitToBuyerOffer(uint256,(uint256,(address[],uint256[]),address))";
    const signMetaTxArgs = {
        ...args,
        functionName,
        functionSignature: encodeCommitToBuyerOffer(args.offerId, args.sellerParams)
    };
    if (args.returnTypedDataToSign) {
        return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: true });
    }
    return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: false });
}
// Implementation
export async function signMetaTxCreateOfferAndCommit(args) {
    utils.validation.createOfferAndCommitArgsSchema.validateSync(args.createOfferAndCommitArgs, { abortEarly: false });
    await storeMetadataOnTheGraph({
        metadataUriOrHash: args.createOfferAndCommitArgs.metadataUri,
        metadataStorage: args.metadataStorage,
        theGraphStorage: args.theGraphStorage
    });
    await storeMetadataItems({
        ...args,
        createOffersArgs: [args.createOfferAndCommitArgs]
    });
    const signMetaTxArgs = {
        ...args,
        functionName: "createOfferAndCommit(((uint256,uint256,uint256,uint256,uint256,uint256,address,uint8,uint8,string,string,bool,uint256,(address[],uint256[])[],uint256),(uint256,uint256,uint256,uint256),(uint256,uint256,uint256),(uint256,address),(uint8,uint8,address,uint8,uint256,uint256,uint256,uint256),uint256,uint256,bool),address,address,bytes,uint256,(uint256,(address[],uint256[]),address))",
        functionSignature: encodeCreateOfferAndCommit(args.createOfferAndCommitArgs)
    };
    if (args.returnTypedDataToSign) {
        return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: true });
    }
    return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: false });
}
// Implementation
export async function signMetaTxCommitToOfferAndRedeemVoucher(args) {
    const signMetaTxArgs = {
        ...args,
        functionName: "commitToOfferAndRedeemVoucher(uint256)",
        functionSignature: encodeCommitToOfferAndRedeemVoucher(args.offerId)
    };
    if (args.returnTypedDataToSign) {
        return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: true });
    }
    return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: false });
}
// Implementation
export async function signMetaTxCommitToConditionalOfferAndRedeemVoucher(args) {
    const signMetaTxArgs = {
        ...args,
        functionName: "commitToConditionalOfferAndRedeemVoucher(uint256,uint256)",
        functionSignature: encodeCommitToConditionalOfferAndRedeemVoucher(args.offerId, args.tokenId)
    };
    if (args.returnTypedDataToSign) {
        return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: true });
    }
    return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: false });
}
// Implementation
export async function signMetaTxCreateOfferCommitAndRedeem(args) {
    utils.validation.createOfferAndCommitArgsSchema.validateSync(args.createOfferAndCommitArgs, { abortEarly: false });
    await storeMetadataOnTheGraph({
        metadataUriOrHash: args.createOfferAndCommitArgs.metadataUri,
        metadataStorage: args.metadataStorage,
        theGraphStorage: args.theGraphStorage
    });
    await storeMetadataItems({
        ...args,
        createOffersArgs: [args.createOfferAndCommitArgs]
    });
    const signMetaTxArgs = {
        ...args,
        functionName: "createOfferCommitAndRedeem(((uint256,uint256,uint256,uint256,uint256,uint256,address,uint8,uint8,string,string,bool,uint256,(address[],uint256[])[],uint256),(uint256,uint256,uint256,uint256),(uint256,uint256,uint256),(uint256,address),(uint8,uint8,address,uint8,uint256,uint256,uint256,uint256),uint256,uint256,bool),address,bytes,uint256)",
        functionSignature: encodeCreateOfferCommitAndRedeem(args.createOfferAndCommitArgs)
    };
    if (args.returnTypedDataToSign) {
        return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: true });
    }
    return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: false });
}
// Implementation
export async function signMetaTxCancelVoucher(args) {
    return makeExchangeMetaTxSigner("cancelVoucher(uint256)")(args);
}
// Implementation
export async function signMetaTxRedeemVoucher(args) {
    return makeExchangeMetaTxSigner("redeemVoucher(uint256)")(args);
}
// Implementation
export async function signMetaTxCompleteExchange(args) {
    return makeExchangeMetaTxSigner("completeExchange(uint256)")(args);
}
// Implementation
export async function signMetaTxRetractDispute(args) {
    return makeExchangeMetaTxSigner("retractDispute(uint256)", bosonDisputeHandlerIface)(args);
}
// Implementation
export async function signMetaTxEscalateDispute(args) {
    return makeExchangeMetaTxSigner("escalateDispute(uint256)", bosonDisputeHandlerIface)(args);
}
// Implementation
export async function signMetaTxRaiseDispute(args) {
    return makeExchangeMetaTxSigner("raiseDispute(uint256)", bosonDisputeHandlerIface)(args);
}
// Implementation
export async function signMetaTxResolveDispute(args) {
    const functionName = "resolveDispute(uint256,uint256,bytes)";
    const counterpartySig = typeof args.counterpartySig === "string"
        ? args.counterpartySig
        : rebuildSignature(args.counterpartySig);
    const disputeResolutionType = [
        { name: "exchangeId", type: "uint256" },
        { name: "buyerPercentBasisPoints", type: "uint256" },
        { name: "signature", type: "bytes" }
    ];
    const metaTransactionType = [
        { name: "nonce", type: "uint256" },
        { name: "from", type: "address" },
        { name: "contractAddress", type: "address" },
        { name: "functionName", type: "string" },
        { name: "disputeResolutionDetails", type: "MetaTxDisputeResolutionDetails" }
    ];
    const customSignatureType = {
        MetaTxDisputeResolution: metaTransactionType,
        MetaTxDisputeResolutionDetails: disputeResolutionType
    };
    const message = {
        nonce: args.nonce.toString(),
        from: await args.web3Lib.getSignerAddress(),
        contractAddress: args.metaTxHandlerAddress,
        functionName,
        disputeResolutionDetails: {
            exchangeId: args.exchangeId.toString(),
            buyerPercentBasisPoints: args.buyerPercent.toString(),
            signature: counterpartySig
        }
    };
    const functionSignature = bosonDisputeHandlerIface.encodeFunctionData(
    // remove params in brackets from string
    functionName.replace(/\(([^)]*)\)[^(]*$/, ""), [args.exchangeId, args.buyerPercent, counterpartySig]);
    if (args.returnTypedDataToSign) {
        const structuredData = await prepareDataSignatureParameters({
            ...args,
            verifyingContractAddress: args.metaTxHandlerAddress,
            customSignatureType,
            primaryType: "MetaTxDisputeResolution",
            message,
            returnTypedDataToSign: true
        });
        return { ...structuredData, functionName, functionSignature };
    }
    const signatureParams = await prepareDataSignatureParameters({
        ...args,
        verifyingContractAddress: args.metaTxHandlerAddress,
        customSignatureType,
        primaryType: "MetaTxDisputeResolution",
        message,
        returnTypedDataToSign: false
    });
    return {
        ...signatureParams,
        functionName,
        functionSignature
    };
}
// Implementation
export async function signMetaTxExtendDisputeTimeout(args) {
    const signMetaTxArgs = {
        ...args,
        functionName: "extendDisputeTimeout(uint256,uint256)",
        functionSignature: bosonDisputeHandlerIface.encodeFunctionData("extendDisputeTimeout", [args.exchangeId, args.newTimeout])
    };
    if (args.returnTypedDataToSign) {
        return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: true });
    }
    return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: false });
}
// Implementation
export async function signMetaTxWithdrawFunds(args) {
    const functionName = "withdrawFunds(uint256,address[],uint256[])";
    const fundType = [
        { name: "entityId", type: "uint256" },
        { name: "tokenList", type: "address[]" },
        { name: "tokenAmounts", type: "uint256[]" }
    ];
    const metaTransactionType = [
        { name: "nonce", type: "uint256" },
        { name: "from", type: "address" },
        { name: "contractAddress", type: "address" },
        { name: "functionName", type: "string" },
        { name: "fundDetails", type: "MetaTxFundDetails" }
    ];
    const customSignatureType = {
        MetaTxFund: metaTransactionType,
        MetaTxFundDetails: fundType
    };
    const message = {
        nonce: args.nonce.toString(),
        from: await args.web3Lib.getSignerAddress(),
        contractAddress: args.metaTxHandlerAddress,
        functionName,
        fundDetails: {
            entityId: args.entityId.toString(),
            tokenList: args.tokenList,
            tokenAmounts: args.tokenAmounts.map((bn) => bn.toString())
        }
    };
    const functionSignature = encodeWithdrawFunds(args.entityId, args.tokenList, args.tokenAmounts);
    if (args.returnTypedDataToSign) {
        const structuredData = await prepareDataSignatureParameters({
            ...args,
            verifyingContractAddress: args.metaTxHandlerAddress,
            customSignatureType,
            primaryType: "MetaTxFund",
            message,
            returnTypedDataToSign: true
        });
        return { ...structuredData, functionName, functionSignature };
    }
    const signatureParams = await prepareDataSignatureParameters({
        ...args,
        verifyingContractAddress: args.metaTxHandlerAddress,
        customSignatureType,
        primaryType: "MetaTxFund",
        message,
        returnTypedDataToSign: false
    });
    return {
        ...signatureParams,
        functionName,
        functionSignature
    };
}
// Implementation
export async function signMetaTxDepositFunds(args) {
    if (!isAddress(args.fundsTokenAddress)) {
        throw new Error(`Invalid fundsTokenAddress: ${args.fundsTokenAddress}`);
    }
    if (args.fundsTokenAddress === AddressZero) {
        throw new Error(`Meta transaction can't be used to deposit native currency`);
    }
    const signMetaTxArgs = {
        ...args,
        functionName: "depositFunds(uint256,address,uint256)",
        functionSignature: encodeDepositFunds(args.entityId, args.fundsTokenAddress, args.fundsAmount)
    };
    if (args.returnTypedDataToSign) {
        return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: true });
    }
    return signMetaTx({ ...signMetaTxArgs, returnTypedDataToSign: false });
}
function makeExchangeMetaTxSigner(functionName, handlerIface = bosonExchangeHandlerIface) {
    return async function signExchangeMetaTx(args) {
        const exchangeType = [{ name: "exchangeId", type: "uint256" }];
        const metaTransactionType = [
            { name: "nonce", type: "uint256" },
            { name: "from", type: "address" },
            { name: "contractAddress", type: "address" },
            { name: "functionName", type: "string" },
            { name: "exchangeDetails", type: "MetaTxExchangeDetails" }
        ];
        const customSignatureType = {
            MetaTxExchange: metaTransactionType,
            MetaTxExchangeDetails: exchangeType
        };
        const buyerAddress = await args.web3Lib.getSignerAddress();
        const message = {
            nonce: args.nonce.toString(),
            from: buyerAddress,
            contractAddress: args.metaTxHandlerAddress,
            functionName,
            exchangeDetails: {
                exchangeId: args.exchangeId.toString()
            }
        };
        const functionSignature = handlerIface.encodeFunctionData(
        // remove params in brackets from string
        functionName.replace(/\(([^)]*)\)[^(]*$/, ""), [args.exchangeId]);
        if (args.returnTypedDataToSign) {
            const structuredData = await prepareDataSignatureParameters({
                ...args,
                verifyingContractAddress: args.metaTxHandlerAddress,
                customSignatureType,
                primaryType: "MetaTxExchange",
                message,
                returnTypedDataToSign: true
            });
            return { ...structuredData, functionName, functionSignature };
        }
        const signatureParams = await prepareDataSignatureParameters({
            ...args,
            verifyingContractAddress: args.metaTxHandlerAddress,
            customSignatureType,
            primaryType: "MetaTxExchange",
            message,
            returnTypedDataToSign: false
        });
        return {
            ...signatureParams,
            functionName,
            functionSignature
        };
    };
}
export async function relayMetaTransaction(args) {
    const { chainId, contractAddress, metaTx } = args;
    const biconomy = new Biconomy(metaTx.config.relayerUrl, metaTx.config.apiKey, metaTx.config.apiId);
    const baseParams = [
        metaTx.params.userAddress,
        metaTx.params.functionName,
        metaTx.params.functionSignature,
        metaTx.params.nonce,
        rebuildSignature({
            r: metaTx.params.sigR.toString(),
            s: metaTx.params.sigS.toString(),
            v: Number(metaTx.params.sigV)
        })
    ];
    const params = metaTx.params.transferAuthorizations?.length
        ? [
            ...baseParams,
            metaTx.params.transferAuthorizations.map(encodeTransferAuthorizationEntry)
        ]
        : baseParams;
    const relayTxResponse = await biconomy.relayTransaction({
        to: contractAddress,
        params,
        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 executeMetaTransaction(args) {
    const data = metaTransactionsHandlerIface.encodeFunctionData("executeMetaTransaction", [
        args.userAddress,
        args.functionName,
        args.functionSignature,
        args.nonce,
        rebuildSignature({
            r: hexlify(args.sigR),
            s: hexlify(args.sigS),
            v: Number(args.sigV)
        })
    ]);
    const tx = { to: args.contractAddress, data };
    return args.returnTxInfo ? tx : args.web3Lib.sendTransaction(tx);
}
// Implementation
export async function executeMetaTransactionWithTokenTransferAuthorization(args) {
    const data = metaTransactionsHandlerIface.encodeFunctionData("executeMetaTransactionWithTokenTransferAuthorization", [
        args.userAddress,
        args.functionName,
        args.functionSignature,
        args.nonce,
        rebuildSignature({
            r: hexlify(args.sigR),
            s: hexlify(args.sigS),
            v: Number(args.sigV)
        }),
        args.transferAuthorizations.map(encodeTransferAuthorizationEntry)
    ]);
    const tx = { to: args.contractAddress, data };
    return args.returnTxInfo ? tx : args.web3Lib.sendTransaction(tx);
}
export async function getResubmitted(args) {
    const { chainId, metaTx } = args;
    const biconomy = new Biconomy(metaTx.config.relayerUrl, metaTx.config.apiKey, metaTx.config.apiId);
    const retriedHashesResponse = await biconomy.getResubmitted({
        networkId: chainId,
        transactionHash: metaTx.originalHash
    });
    return retriedHashesResponse.data;
}
//# sourceMappingURL=handler.js.map