'use strict';

var assertEnabled = require('./assertEnabled-4fdb739e.cjs.prod.js');
var contractAppuri = require('./contract-appuri-bfcc1b54.cjs.prod.js');
var index = require('./index-9e6c7a37.cjs.prod.js');
var airdropErc1155 = require('./airdrop-erc1155-d461138b.cjs.prod.js');
var fetchCurrencyValue = require('./fetchCurrencyValue-94271459.cjs.prod.js');
var contractInterceptor = require('./contract-interceptor-c926beb5.cjs.prod.js');
var contractOwner = require('./contract-owner-6cfa616d.cjs.prod.js');
var contractPlatformFee = require('./contract-platform-fee-0ccc60ec.cjs.prod.js');
var contractRoles = require('./contract-roles-3f9fbb76.cjs.prod.js');
var contractSales = require('./contract-sales-e52689a8.cjs.prod.js');
var erc1155 = require('./erc-1155-0bf18de5.cjs.prod.js');
var erc20 = require('./erc-20-1fad76d9.cjs.prod.js');
var erc721 = require('./erc-721-ddd48b2c.cjs.prod.js');
var marketplacev3Offers = require('./marketplacev3-offers-01207a18.cjs.prod.js');
var transactions = require('./transactions-2031b21a.cjs.prod.js');
require('ethers');
require('zod');
require('@thirdweb-dev/storage');
require('@thirdweb-dev/chains');
require('@thirdweb-dev/crypto');
require('tiny-invariant');
require('bn.js');
require('bs58');
require('@thirdweb-dev/contracts-js/dist/abis/IBurnableERC20.json');
require('@thirdweb-dev/contracts-js/dist/abis/IDrop.json');
require('@thirdweb-dev/contracts-js/dist/abis/IDropERC20_V2.json');
require('@thirdweb-dev/contracts-js/dist/abis/IDropSinglePhase.json');
require('@thirdweb-dev/contracts-js/dist/abis/IDropSinglePhase_V1.json');
require('@thirdweb-dev/contracts-js/dist/abis/IERC20.json');
require('@thirdweb-dev/contracts-js/dist/abis/IERC20Permit.json');
require('@thirdweb-dev/contracts-js/dist/abis/IMintableERC20.json');
require('@thirdweb-dev/contracts-js/dist/abis/IMulticall.json');
require('@thirdweb-dev/contracts-js/dist/abis/ISignatureMintERC20.json');
require('@thirdweb-dev/contracts-js/dist/abis/IERC20Metadata.json');
require('@thirdweb-dev/contracts-js/dist/abis/IBurnableERC721.json');
require('@thirdweb-dev/contracts-js/dist/abis/IClaimableERC721.json');
require('@thirdweb-dev/contracts-js/dist/abis/IDelayedReveal.json');
require('@thirdweb-dev/contracts-js/dist/abis/IDropERC721_V3.json');
require('@thirdweb-dev/contracts-js/dist/abis/IERC721.json');
require('@thirdweb-dev/contracts-js/dist/abis/IERC721Enumerable.json');
require('@thirdweb-dev/contracts-js/dist/abis/IERC721AQueryableUpgradeable.json');
require('@thirdweb-dev/contracts-js/dist/abis/IERC721Supply.json');
require('@thirdweb-dev/contracts-js/dist/abis/ILazyMint.json');
require('@thirdweb-dev/contracts-js/dist/abis/IMintableERC721.json');
require('@thirdweb-dev/contracts-js/dist/abis/ISignatureMintERC721.json');
require('@thirdweb-dev/contracts-js/dist/abis/ISignatureMintERC721_V1.json');
require('@thirdweb-dev/contracts-js/dist/abis/LazyMintWithTier_V1.json');
require('@thirdweb-dev/contracts-js/dist/abis/SharedMetadata.json');
require('@thirdweb-dev/contracts-js/dist/abis/zora_IERC721Drop.json');
require('@thirdweb-dev/contracts-js/dist/abis/ILoyaltyCard.json');
require('@thirdweb-dev/contracts-js/dist/abis/INFTMetadata.json');
require('@thirdweb-dev/contracts-js/dist/abis/IERC721Metadata.json');
require('@thirdweb-dev/contracts-js/dist/abis/IBurnableERC1155.json');
require('@thirdweb-dev/contracts-js/dist/abis/IClaimableERC1155.json');
require('@thirdweb-dev/contracts-js/dist/abis/IDrop1155.json');
require('@thirdweb-dev/contracts-js/dist/abis/IDropERC1155_V2.json');
require('@thirdweb-dev/contracts-js/dist/abis/IDropSinglePhase1155.json');
require('@thirdweb-dev/contracts-js/dist/abis/IDropSinglePhase1155_V1.json');
require('@thirdweb-dev/contracts-js/dist/abis/IERC1155.json');
require('@thirdweb-dev/contracts-js/dist/abis/IERC1155Metadata.json');
require('@thirdweb-dev/contracts-js/dist/abis/IERC1155Supply.json');
require('@thirdweb-dev/contracts-js/dist/abis/IERC1155Enumerable.json');
require('@thirdweb-dev/contracts-js/dist/abis/IMintableERC1155.json');
require('@thirdweb-dev/contracts-js/dist/abis/ISignatureMintERC1155.json');
require('@thirdweb-dev/contracts-js/dist/abis/IERC2771Context.json');
require('@thirdweb-dev/contracts-js/dist/abis/IAppURI.json');
require('@thirdweb-dev/contracts-js/dist/abis/IContractMetadata.json');
require('@thirdweb-dev/contracts-js/dist/abis/IDirectListings.json');
require('@thirdweb-dev/contracts-js/dist/abis/IEnglishAuctions.json');
require('@thirdweb-dev/contracts-js/dist/abis/IOffers.json');
require('@thirdweb-dev/contracts-js/dist/abis/IPackVRFDirect.json');
require('@thirdweb-dev/contracts-js/dist/abis/IPermissions.json');
require('@thirdweb-dev/contracts-js/dist/abis/IPermissionsEnumerable.json');
require('@thirdweb-dev/contracts-js/dist/abis/IPlatformFee.json');
require('@thirdweb-dev/contracts-js/dist/abis/IPrimarySale.json');
require('@thirdweb-dev/contracts-js/dist/abis/IRoyalty.json');
require('@thirdweb-dev/contracts-js/dist/abis/Ownable.json');
require('@thirdweb-dev/contracts-js/dist/abis/IAirdropERC20.json');
require('@thirdweb-dev/contracts-js/dist/abis/IAirdropERC721.json');
require('@thirdweb-dev/contracts-js/dist/abis/IAirdropERC1155.json');
require('@thirdweb-dev/contracts-js/dist/abis/IAccountFactoryCore.json');
require('@thirdweb-dev/contracts-js/dist/abis/IAccountPermissions.json');
require('@thirdweb-dev/contracts-js/dist/abis/IAccountPermissions_V1.json');
require('@thirdweb-dev/contracts-js/dist/abis/IAccount.json');
require('ethers/lib/utils');
require('eventemitter3');
require('./signature-f1b68b14.cjs.prod.js');
require('./normalizePriceValue-d8989cf3.cjs.prod.js');
require('@thirdweb-dev/merkletree');
require('./setErc20Allowance-e5d5499e.cjs.prod.js');
require('uuid');
require('./deployContractDeterministic-49525f35.cjs.prod.js');
require('./QueryParams-ac240562.cjs.prod.js');
require('fast-deep-equal');
require('./drop-claim-conditions-1e3d706d.cjs.prod.js');
require('./cleanCurrencyAddress-e04a066f.cjs.prod.js');
require('./marketplace-f0f6f479.cjs.prod.js');

/**
 * Custom contract dynamic class with feature detection
 *
 * @example
 *
 * ```javascript
 * import { ThirdwebSDK } from "@thirdweb-dev/sdk";
 *
 * const sdk = new ThirdwebSDK(provider);
 * const contract = await sdk.getContract("{{contract_address}}");
 *
 * // call any function in your contract
 * await contract.call("myCustomFunction", [param1, param2]);
 *
 * // if your contract follows an ERC standard, contract.ercXYZ will be present
 * const allNFTs = await contract.erc721.getAll()
 *
 * // if your contract extends a particular contract extension, the corresponding function will be available
 * const tx = await contract.erc721.mint({
 *     name: "Cool NFT",
 *     image: readFileSync("some_image.png"),
 *   });
 * ```
 *
 * @contract
 * @public
 */
class SmartContract {
  // utilities

  get abi() {
    return index.AbiSchema.parse(this.contractWrapper.abi || []);
  }

  /**
   * Handle royalties
   */
  get royalties() {
    return assertEnabled.assertEnabled(this.detectRoyalties(), index.FEATURE_ROYALTY);
  }

  /**
   * Handle permissions
   */
  get roles() {
    return assertEnabled.assertEnabled(this.detectRoles(), index.FEATURE_PERMISSIONS);
  }

  /**
   * Handle primary sales
   */
  get sales() {
    return assertEnabled.assertEnabled(this.detectPrimarySales(), index.FEATURE_PRIMARY_SALE);
  }

  /**
   * Handle platform fees
   */
  get platformFees() {
    return assertEnabled.assertEnabled(this.detectPlatformFees(), index.FEATURE_PLATFORM_FEE);
  }

  /**
   * Set and get the owner of the contract
   */
  get owner() {
    return assertEnabled.assertEnabled(this.detectOwnable(), index.FEATURE_OWNER);
  }

  /**
   * Auto-detects ERC20 standard functions.
   */
  get erc20() {
    return assertEnabled.assertEnabled(this.detectErc20(), index.FEATURE_TOKEN);
  }

  /**
   * Auto-detects ERC721 standard functions.
   */
  get erc721() {
    return assertEnabled.assertEnabled(this.detectErc721(), index.FEATURE_NFT);
  }

  /**
   * Auto-detects ERC1155 standard functions.
   */
  get erc1155() {
    return assertEnabled.assertEnabled(this.detectErc1155(), index.FEATURE_EDITION);
  }

  /**
   * Auto-detects AppURI standard functions.
   */
  get app() {
    return assertEnabled.assertEnabled(this.detectApp(), index.FEATURE_APPURI);
  }

  /**
   * Direct listings
   * @remarks Create and manage direct listings in your marketplace.
   * ```javascript
   * // Data of the listing you want to create
   * const listing = {
   *   // address of the contract the asset you want to list is on
   *   assetContractAddress: "0x...",
   *   // token ID of the asset you want to list
   *   tokenId: "0",
   *   // how many of the asset you want to list
   *   quantity: 1,
   *   // address of the currency contract that will be used to pay for the listing
   *   currencyContractAddress: NATIVE_TOKEN_ADDRESS,
   *   // The price to pay per unit of NFTs listed.
   *   pricePerToken: 1.5,
   *   // when should the listing open up for offers
   *   startTimestamp: new Date(Date.now()),
   *   // how long the listing will be open for
   *   endTimestamp: new Date(Date.now() + 5 * 24 * 60 * 60 * 1000),
   *   // Whether the listing is reserved for a specific set of buyers.
   *   isReservedListing: false
   * }
   *
   * const tx = await contract.directListings.createListing(listing);
   * const receipt = tx.receipt; // the transaction receipt
   * const id = tx.id; // the id of the newly created listing
   *
   * // And on the buyers side:
   * // The ID of the listing you want to buy from
   * const listingId = 0;
   * // Quantity of the asset you want to buy
   * const quantityDesired = 1;
   *
   * await contract.directListings.buyFromListing(listingId, quantityDesired);
   * ```
   */
  get directListings() {
    return assertEnabled.assertEnabled(this.detectDirectListings(), index.FEATURE_DIRECT_LISTINGS);
  }
  /**
   * Auctions
   * @remarks Create and manage auctions in your marketplace.
   * @example
   * ```javascript
   * // Data of the auction you want to create
   * const auction = {
   *   // address of the contract of the asset you want to auction
   *   assetContractAddress: "0x...",
   *   // token ID of the asset you want to auction
   *   tokenId: "0",
   *   // how many of the asset you want to auction
   *   quantity: 1,
   *   // address of the currency contract that will be used to pay for the auctioned tokens
   *   currencyContractAddress: NATIVE_TOKEN_ADDRESS,
   *   // the minimum bid that will be accepted for the token
   *   minimumBidAmount: "1.5",
   *   // how much people would have to bid to instantly buy the asset
   *   buyoutBidAmount: "10",
   *   // If a bid is made less than these many seconds before expiration, the expiration time is increased by this.
   *   timeBufferInSeconds: "1000",
   *   // A bid must be at least this much bps greater than the current winning bid
   *   bidBufferBps: "100", // 100 bps stands for 1%
   *   // when should the auction open up for bidding
   *   startTimestamp: new Date(Date.now()),
   *   // end time of auction
   *   endTimestamp: new Date(Date.now() + 5 * 24 * 60 * 60 * 1000),
   * }
   *
   * const tx = await contract.englishAuctions.createAuction(auction);
   * const receipt = tx.receipt; // the transaction receipt
   * const id = tx.id; // the id of the newly created auction
   *
   * // And on the buyers side:
   * // The auction ID of the asset you want to bid on
   * const auctionId = 0;
   * // The total amount you are willing to bid for auctioned tokens
   * const bidAmount = 1;
   *
   * await contract.englishAuctions.makeBid(auctionId, bidAmount);
   * ```
   */
  get englishAuctions() {
    return assertEnabled.assertEnabled(this.detectEnglishAuctions(), index.FEATURE_ENGLISH_AUCTIONS);
  }

  /**
   * Offers
   * @remarks Make and manage offers.
   * @example
   * ```javascript
   * // Data of the offer you want to make
   * const offer = {
   *   // address of the contract the asset you want to make an offer for
   *   assetContractAddress: "0x...",
   *   // token ID of the asset you want to buy
   *   tokenId: "0",
   *   // how many of the asset you want to buy
   *   quantity: 1,
   *   // address of the currency contract that you offer to pay in
   *   currencyContractAddress: NATIVE_TOKEN_ADDRESS,
   *   // Total price you offer to pay for the mentioned token(s)
   *   totalPrice: "1.5",
   *   // Offer valid until
   *   endTimestamp: new Date(),
   * }
   *
   * const tx = await contract.offers.makeOffer(offer);
   * const receipt = tx.receipt; // the transaction receipt
   * const id = tx.id; // the id of the newly created offer
   *
   * // And on the seller's side:
   * // The ID of the offer you want to accept
   * const offerId = 0;
   * await contract.offers.acceptOffer(offerId);
   * ```
   */
  get offers() {
    return assertEnabled.assertEnabled(this.detectOffers(), index.FEATURE_OFFERS);
  }
  get airdrop20() {
    return assertEnabled.assertEnabled(this.detectAirdrop20(), index.FEATURE_AIRDROP_ERC20);
  }
  get airdrop721() {
    return assertEnabled.assertEnabled(this.detectAirdrop721(), index.FEATURE_AIRDROP_ERC721);
  }
  get airdrop1155() {
    return assertEnabled.assertEnabled(this.detectAirdrop1155(), index.FEATURE_AIRDROP_ERC1155);
  }

  /**
   * Account Factory
   *
   * @remarks Create accounts and fetch data about them.
   * @example
   * ```javascript
   *
   * // Predict the address of the account that will be created for an admin.
   * const deterministicAddress = await contract.accountFactory.predictAccountAddress(admin, extraData);
   *
   * // Create accounts
   * const tx = await contract.accountFactory.createAccount(admin, extraData);
   * // the same as `deterministicAddress`
   * const accountAddress = tx.address;
   *
   * // Get all accounts created by the factory
   * const allAccounts = await contract.accountFactory.getAllAccounts();
   *
   * // Get all accounts on which a signer has been given authority.
   * const associatedAccounts = await contract.accountFactory.getAssociatedAccounts(signer);
   *
   * // Get all signers who have been given authority on a account.
   * const associatedSigners = await contract.accountFactory.getAssociatedSigners(accountAddress);
   *
   * // Check whether a account has already been created for a given admin.
   * const isAccountDeployed = await contract.accountFactory.isAccountDeployed(admin, extraData);
   * ```
   */
  get accountFactory() {
    return assertEnabled.assertEnabled(this.detectAccountFactory(), index.FEATURE_ACCOUNT_FACTORY);
  }

  // TODO documentation
  get account() {
    return assertEnabled.assertEnabled(this.detectAccount(), index.FEATURE_ACCOUNT);
  }
  get extensions() {
    return assertEnabled.assertEnabled(this.detectBaseRouter(), index.FEATURE_DYNAMIC_CONTRACT);
  }
  get chainId() {
    return this._chainId;
  }
  constructor(network, address, abi, storage) {
    let options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
    let chainId = arguments.length > 5 ? arguments[5] : undefined;
    let contractWrapper = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : new index.ContractWrapper(network, address, abi, options, storage);
    this._chainId = chainId;
    this.storage = storage;
    this.contractWrapper = contractWrapper;
    this.events = new contractAppuri.ContractEvents(this.contractWrapper);
    this.encoder = new fetchCurrencyValue.ContractEncoder(this.contractWrapper);
    this.interceptor = new contractInterceptor.ContractInterceptor(this.contractWrapper);
    this.estimator = new contractAppuri.GasCostEstimator(this.contractWrapper);
    this.publishedMetadata = new airdropErc1155.ContractPublishedMetadata(this.contractWrapper, this.storage);
    this.metadata = new contractAppuri.ContractMetadata(this.contractWrapper, index.CustomContractSchema, this.storage);
  }
  onNetworkUpdated(network) {
    this.contractWrapper.updateSignerOrProvider(network);
  }
  getAddress() {
    return this.contractWrapper.address;
  }

  /**
   * Prepare a transaction for sending
   */
  prepare(method, args, overrides) {
    return transactions.Transaction.fromContractWrapper({
      contractWrapper: this.contractWrapper,
      method,
      args,
      overrides
    });
  }

  /**
   * Call any function on this contract
   * @example
   * ```javascript
   * // read functions will return the data from the contract
   * const myValue = await contract.call("myReadFunction");
   * console.log(myValue);
   *
   * // write functions will return the transaction receipt
   * const tx = await contract.call("myWriteFunction", [arg1, arg2]);
   * const receipt = tx.receipt;
   *
   * // Optionally override transaction options
   * await contract.call("myWriteFunction", [arg1, arg2], {
   *  gasLimit: 1000000, // override default gas limit
   *  value: ethers.utils.parseEther("0.1"), // send 0.1 ether with the contract call
   * };
   * ```
   * @param functionName - the name of the function to call
   * @param args - the arguments of the function
   */
  async call(functionName, args, overrides) {
    return this.contractWrapper.call(functionName, args, overrides);
  }

  /** ********************
   * FEATURE DETECTION
   * ********************/

  detectRoyalties() {
    if (contractAppuri.detectContractFeature(this.contractWrapper, "Royalty")) {
      // ContractMetadata is stateless, it's fine to create a new one here
      // This also makes it not order dependent in the feature detection process
      const metadata = new contractAppuri.ContractMetadata(this.contractWrapper, index.CustomContractSchema, this.storage);
      return new contractOwner.ContractRoyalty(this.contractWrapper, metadata);
    }
    return undefined;
  }
  detectRoles() {
    if (contractAppuri.detectContractFeature(this.contractWrapper, "Permissions")) {
      return new contractRoles.ContractRoles(this.contractWrapper, index.ALL_ROLES);
    }
    return undefined;
  }
  detectPrimarySales() {
    if (contractAppuri.detectContractFeature(this.contractWrapper, "PrimarySale")) {
      return new contractSales.ContractPrimarySale(this.contractWrapper);
    }
    return undefined;
  }
  detectPlatformFees() {
    if (contractAppuri.detectContractFeature(this.contractWrapper, "PlatformFee")) {
      return new contractPlatformFee.ContractPlatformFee(this.contractWrapper);
    }
    return undefined;
  }
  detectErc20() {
    if (contractAppuri.detectContractFeature(this.contractWrapper, "ERC20")) {
      return new erc20.Erc20(this.contractWrapper, this.storage, this.chainId);
    }
    return undefined;
  }
  detectErc721() {
    if (contractAppuri.detectContractFeature(this.contractWrapper, "ERC721")) {
      return new erc721.Erc721(this.contractWrapper, this.storage, this.chainId);
    }
    return undefined;
  }
  detectErc1155() {
    if (contractAppuri.detectContractFeature(this.contractWrapper, "ERC1155")) {
      return new erc1155.Erc1155(this.contractWrapper, this.storage, this.chainId);
    }
    return undefined;
  }
  detectOwnable() {
    if (contractAppuri.detectContractFeature(this.contractWrapper, "Ownable")) {
      return new contractOwner.ContractOwner(this.contractWrapper);
    }
    return undefined;
  }
  detectApp() {
    const metadata = new contractAppuri.ContractMetadata(this.contractWrapper, index.CustomContractSchema, this.storage);
    if (contractAppuri.detectContractFeature(this.contractWrapper, "AppURI")) {
      return new contractAppuri.ContractAppURI(this.contractWrapper, metadata, this.storage);
    } else if (contractAppuri.detectContractFeature(this.contractWrapper, "ContractMetadata")) {
      return new contractAppuri.ContractAppURI(this.contractWrapper, metadata, this.storage);
    }
    return undefined;
  }
  detectDirectListings() {
    if (contractAppuri.detectContractFeature(this.contractWrapper, "DirectListings")) {
      return new marketplacev3Offers.MarketplaceV3DirectListings(this.contractWrapper, this.storage);
    }
    return undefined;
  }
  detectEnglishAuctions() {
    if (contractAppuri.detectContractFeature(this.contractWrapper, "EnglishAuctions")) {
      return new marketplacev3Offers.MarketplaceV3EnglishAuctions(this.contractWrapper, this.storage);
    }
    return undefined;
  }
  detectOffers() {
    if (contractAppuri.detectContractFeature(this.contractWrapper, "Offers")) {
      return new marketplacev3Offers.MarketplaceV3Offers(this.contractWrapper, this.storage);
    }
    return undefined;
  }
  detectBaseRouter() {
    if (contractAppuri.detectContractFeature(this.contractWrapper, index.FEATURE_DYNAMIC_CONTRACT.name)) {
      return new airdropErc1155.ExtensionManager(this.contractWrapper);
    }
    return undefined;
  }
  detectAirdrop20() {
    if (contractAppuri.detectContractFeature(this.contractWrapper, "AirdropERC20")) {
      return new airdropErc1155.Airdrop20(this.contractWrapper);
    }
    return undefined;
  }
  detectAirdrop721() {
    if (contractAppuri.detectContractFeature(this.contractWrapper, "AirdropERC721")) {
      return new airdropErc1155.Airdrop721(this.contractWrapper);
    }
    return undefined;
  }
  detectAirdrop1155() {
    if (contractAppuri.detectContractFeature(this.contractWrapper, "AirdropERC1155")) {
      return new airdropErc1155.Airdrop1155(this.contractWrapper);
    }
    return undefined;
  }

  // ========== Account features ==========

  detectAccountFactory() {
    if (contractAppuri.detectContractFeature(this.contractWrapper, index.FEATURE_ACCOUNT_FACTORY.name)) {
      return new airdropErc1155.AccountFactory(this.contractWrapper);
    }
    return undefined;
  }
  detectAccount() {
    if (contractAppuri.detectContractFeature(this.contractWrapper, index.FEATURE_ACCOUNT.name)) {
      return new airdropErc1155.Account(this.contractWrapper);
    }
    return undefined;
  }
}

exports.SmartContract = SmartContract;
