'use strict';

var index = require('./index-a58e9bd5.cjs.prod.js');
var transactions = require('./transactions-63baf7f6.cjs.prod.js');
var ethers = require('ethers');
var invariant = require('tiny-invariant');
var signature = require('./signature-793a9bb6.cjs.prod.js');
var zod = require('zod');
var assertEnabled = require('./assertEnabled-304710df.cjs.prod.js');
var contractAppuri = require('./contract-appuri-9d0579a6.cjs.prod.js');
var deployContractDeterministic = require('./deployContractDeterministic-e5db32b0.cjs.prod.js');

function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }

var invariant__default = /*#__PURE__*/_interopDefault(invariant);

let AdminFlag = /*#__PURE__*/function (AdminFlag) {
  AdminFlag[AdminFlag["None"] = 0] = "None";
  AdminFlag[AdminFlag["AddAdmin"] = 1] = "AddAdmin";
  AdminFlag[AdminFlag["RemoveAdmin"] = 2] = "RemoveAdmin";
  return AdminFlag;
}({});
const DEFAULT_PERMISSIONS = {
  // eslint-disable-next-line better-tree-shaking/no-top-level-side-effects
  startDate: ethers.BigNumber.from(0),
  // eslint-disable-next-line better-tree-shaking/no-top-level-side-effects
  expirationDate: ethers.BigNumber.from(0),
  approvedCallTargets: [],
  nativeTokenLimitPerTransaction: "0"
};
const SignerPermissionsSchema = /* @__PURE__ */(() => zod.z.object({
  startDate: assertEnabled.StartDateSchema,
  expirationDate: assertEnabled.EndDateSchema,
  nativeTokenLimitPerTransaction: index.AmountSchema.default(0),
  approvedCallTargets: zod.z.union([zod.z.array(index.AddressOrEnsSchema), zod.z.literal("*")])
}))();
const PermissionSnapshotSchema = /* @__PURE__ */(() => zod.z.array(zod.z.object({
  signer: index.AddressOrEnsSchema,
  makeAdmin: zod.z.boolean(),
  permissions: SignerPermissionsSchema
})))();
const SignerPermissionRequestV1 = [{
  name: "signer",
  type: "address"
}, {
  name: "approvedTargets",
  type: "address[]"
}, {
  name: "nativeTokenLimitPerTransaction",
  type: "uint256"
}, {
  name: "permissionStartTimestamp",
  type: "uint128"
}, {
  name: "permissionEndTimestamp",
  type: "uint128"
}, {
  name: "reqValidityStartTimestamp",
  type: "uint128"
}, {
  name: "reqValidityEndTimestamp",
  type: "uint128"
}, {
  name: "uid",
  type: "bytes32"
}];
const SignerPermissionRequest = [{
  name: "signer",
  type: "address"
}, {
  name: "isAdmin",
  type: "uint8"
}, {
  name: "approvedTargets",
  type: "address[]"
}, {
  name: "nativeTokenLimitPerTransaction",
  type: "uint256"
}, {
  name: "permissionStartTimestamp",
  type: "uint128"
}, {
  name: "permissionEndTimestamp",
  type: "uint128"
}, {
  name: "reqValidityStartTimestamp",
  type: "uint128"
}, {
  name: "reqValidityEndTimestamp",
  type: "uint128"
}, {
  name: "uid",
  type: "bytes32"
}];

class AccountPermissions {
  featureName = index.FEATURE_ACCOUNT_PERMISSIONS.name;
  constructor(contractWrapper) {
    this.contractWrapper = contractWrapper;
  }
  getAddress() {
    return this.contractWrapper.address;
  }

  /*********************************
   * HELPER FUNCTIONS
   ********************************/

  hasDuplicateSigners(snapshot) {
    const checkedSigner = {};
    const signers = snapshot.map(item => item.signer);
    for (const signer of signers) {
      if (!checkedSigner[signer]) {
        checkedSigner[signer] = true;
      } else {
        return true;
      }
    }
    return false;
  }

  /**
   * Format the access restrictions for a given role
   *
   * @param restrictions - The access restrictions for a given role
   * @returns formatted role restrictions
   *
   */
  parseSignerPermissionsStruct(permissions) {
    return {
      startDate: new Date(parseInt(permissions.startTimestamp.toString()) * 1000),
      expirationDate: new Date(parseInt(permissions.endTimestamp.toString()) * 1000),
      nativeTokenLimitPerTransaction: ethers.BigNumber.from(permissions.nativeTokenLimitPerTransaction),
      approvedCallTargets: permissions.approvedTargets
    };
  }
  async sendSignerPermissionRequest(signerAddress, permissions, adminFlag) {
    const {
      payload,
      signature
    } = await this.generatePayload(signerAddress, permissions, adminFlag);
    return transactions.Transaction.fromContractWrapper({
      contractWrapper: this.contractWrapper,
      method: "setPermissionsForSigner",
      args: [payload, signature]
    });
  }

  /**
   * Generate and sign a payload to grant or revoke a signer's access to the account.
   *
   * @param signer - The address of the signer
   * @param roleAction - The address of the signer
   * @returns The generated payload and signature produced on signing that payload.
   *
   */
  async generatePayload(signerAddress, permissions, isAdmin) {
    // Get payload struct.
    const payload = {
      signer: signerAddress,
      isAdmin: isAdmin.valueOf(),
      approvedTargets: permissions.approvedCallTargets === "*" ? [index.AddressZero] : permissions.approvedCallTargets,
      nativeTokenLimitPerTransaction: ethers.utils.parseEther(permissions.nativeTokenLimitPerTransaction),
      permissionStartTimestamp: permissions.startDate,
      permissionEndTimestamp: permissions.expirationDate,
      reqValidityStartTimestamp: 0,
      // Req validity ends 10 years from now.
      reqValidityEndTimestamp: ethers.BigNumber.from(Math.floor(new Date(Date.now() + 1000 * 60 * 60 * 24 * 365 * 10).getTime() / 1000)),
      uid: signature.resolveOrGenerateId(undefined)
    };

    // Generate signature
    const chainId = await this.contractWrapper.getChainID();
    const connectedSigner = this.contractWrapper.getSigner();
    invariant__default["default"](connectedSigner, "No signer available");
    const signature$1 = await this.contractWrapper.signTypedData(connectedSigner, {
      name: "Account",
      version: "1",
      chainId,
      verifyingContract: this.getAddress()
    }, {
      SignerPermissionRequest
    }, payload);
    return {
      payload,
      signature: signature$1
    };
  }
  async generateLegacyPayload(signerAddress, permissions) {
    if (permissions.approvedCallTargets === "*") {
      throw new Error("Wildcard call targets are not supported on legacy account permissions contract, please deploy an updated contract factory.");
    }
    // legacy account permissions contract
    // admin is set only via EOA
    // signer permissions are set via EOA or admin but no wildcard
    const payload = {
      signer: signerAddress,
      approvedTargets: permissions.approvedCallTargets,
      nativeTokenLimitPerTransaction: ethers.utils.parseEther(permissions.nativeTokenLimitPerTransaction),
      permissionStartTimestamp: permissions.startDate,
      permissionEndTimestamp: permissions.expirationDate,
      reqValidityStartTimestamp: 0,
      // Req validity ends 10 years from now.
      reqValidityEndTimestamp: ethers.BigNumber.from(Math.floor(new Date(Date.now() + 1000 * 60 * 60 * 24 * 365 * 10).getTime() / 1000)),
      uid: signature.resolveOrGenerateId(undefined)
    };
    const chainId = await this.contractWrapper.getChainID();
    const connectedSigner = this.contractWrapper.getSigner();
    invariant__default["default"](connectedSigner, "No signer available");
    const signature$1 = await this.contractWrapper.signTypedData(connectedSigner, {
      name: "Account",
      version: "1",
      chainId,
      verifyingContract: this.getAddress()
    }, {
      SignerPermissionRequest: SignerPermissionRequestV1
    }, payload);
    return {
      payload,
      signature: signature$1
    };
  }

  /*********************************
   * READ FUNCTIONS
   ********************************/

  /**
   * Get whether a signer is an admin on the account.
   *
   * @example
   * ```javascript
   * const isAdmin = await contract.account.isAdmin(signer);
   * ```
   * @param signer - The address of a signer of the account.
   * @returns whether a signer is an admin on the account.
   *
   * @twfeature AccountPermissions
   */
  async isAdmin(signerAddress) {
    const resolvedSignerAddress = await index.resolveAddress(signerAddress);
    return await this.contractWrapper.read("isAdmin", [resolvedSignerAddress]);
  }

  /**
   * Get whether a signer has permissions to use the account.
   *
   * @example
   * ```javascript
   * const isAdmin = await contract.account.isSigner(signer);
   * ```
   * @param signer - The address of a signer of the account.
   * @returns whether a signer has permissions to use the account.
   *
   * @twfeature AccountPermissions
   */
  async isSigner(signerAddress) {
    const resolvedSignerAddress = await index.resolveAddress(signerAddress);
    return await this.contractWrapper.read("isActiveSigner", [resolvedSignerAddress]);
  }

  /**
   * Get all admins of the account.
   *
   * @example
   * ```javascript
   * const allAdmins = await contract.account.getAllAdmins();
   * ```
   *
   * @returns All admins of the account.
   *
   * @twfeature AccountPermissions
   */
  async getAllAdmins() {
    return await this.contractWrapper.read("getAllAdmins", []);
  }

  /**
   * Get all (non-admin) signers with permissions to use the account.
   *
   * @example
   * ```javascript
   * const allSigners = await contract.account.getAllSigners();
   * ```
   *
   * @returns All (non-admin) signers with permissions to use the account.
   *
   * @twfeature AccountPermissions
   */
  async getAllSigners() {
    const activeSignersWithPerms = await this.contractWrapper.read("getAllActiveSigners", []);
    return await Promise.all(activeSignersWithPerms.map(async signerWithPermissions => {
      const signer = signerWithPermissions.signer;
      const permissions = this.parseSignerPermissionsStruct(signerWithPermissions);
      return {
        signer,
        permissions
      };
    }));
  }

  /**
   * Get all admins and non-admin signers with permissions to use the account.
   *
   * @example
   * ```javascript
   * const allAdminsAndSigners = await contract.account.getAllAdminsAndSigners();
   * ```
   *
   * @returns All admins and non-admin signers with permissions to use the account.
   *
   * @twfeature AccountPermissions
   */
  async getAllAdminsAndSigners() {
    const allAdmins = await this.getAllAdmins();
    const transformedAdmins = allAdmins.map(admin => {
      return {
        isAdmin: true,
        signer: admin,
        permissions: {
          startDate: new Date(0),
          expirationDate: new Date(0),
          nativeTokenLimitPerTransaction: ethers.BigNumber.from(0),
          approvedCallTargets: []
        }
      };
    });
    const allSigners = await this.getAllSigners();
    return [...transformedAdmins, ...allSigners];
  }

  /*********************************
   * WRITE FUNCTIONS
   ********************************/

  /**
   * Grant an address admin access to the account.
   *
   * @remarks Grants an address admin access to the account. The admin will have complete authority over the account.
   *
   * @param signer - The address to be granted admin access to the account.
   *
   * @example
   * ```javascript
   * const tx = await contract.account.grantAdminAccess(signer);
   * const receipt = tx.receipt();
   * ```
   *
   * @twfeature AccountPermissions
   */
  grantAdminPermissions = /* @__PURE__ */transactions.buildTransactionFunction(async signerAddress => {
    const resolvedSignerAddress = await index.resolveAddress(signerAddress);
    return await this.sendSignerPermissionRequest(resolvedSignerAddress, DEFAULT_PERMISSIONS, AdminFlag.AddAdmin);
  });

  /**
   * Revoke an address' admin access to the account.
   *
   * @remarks Revokes an address' admin access to the account.
   *
   * @param signer - The address of an admin of the account.
   *
   * @example
   * ```javascript
   * const tx = await contract.account.revokeAdminAccess(signer);
   * const receipt = tx.receipt();
   * ```
   *
   * @twfeature AccountPermissions
   */
  revokeAdminPermissions = /* @__PURE__ */transactions.buildTransactionFunction(async signerAddress => {
    const resolvedSignerAddress = await index.resolveAddress(signerAddress);
    return await this.sendSignerPermissionRequest(resolvedSignerAddress, DEFAULT_PERMISSIONS, AdminFlag.RemoveAdmin);
  });

  /**
   * Grant a signer permissions to use the account.
   *
   * @remarks Grants a signer permissions to use the account.
   *
   * @param signer - The signer to be granted permissions to use the account.
   * @param permissions - The permissions to be applied to the signer's use of the account.
   *
   * @example
   * ```javascript
   * const tx = await contract.account.grantPermissions(signer, permissions);
   * const receipt = tx.receipt();
   * ```
   *
   * @twfeature AccountPermissions
   */
  grantPermissions = /* @__PURE__ */transactions.buildTransactionFunction(async (signerAddress, permissions) => {
    const resolvedSignerAddress = await index.resolveAddress(signerAddress);
    const resolvedPermissions = await SignerPermissionsSchema.parseAsync(permissions);
    return await this.sendSignerPermissionRequest(resolvedSignerAddress, resolvedPermissions, AdminFlag.None);
  });

  /**
   * Update the permissions of a signer for using the account.
   *
   * @remarks Updates the permissions of a signer for using the account.
   *
   * @param signer - The signer whose permissions to use the account are to be updated.
   * @param permissions - The permissions to be applied to the signer's use of the account.
   *
   * @example
   * ```javascript
   * const tx = await contract.account.updatePermissions(signer, permissions);
   * const receipt = tx.receipt();
   * ```
   *
   * @twfeature AccountPermissions
   */
  updatePermissions = /* @__PURE__ */transactions.buildTransactionFunction(async (signerAddress, permissions) => {
    const resolvedSignerAddress = await index.resolveAddress(signerAddress);
    const resolvedPermissions = await SignerPermissionsSchema.parseAsync(permissions);
    if (await this.isAdmin(resolvedSignerAddress)) {
      throw new Error("Signer is already an admin. Cannot update permissions of an existing admin.");
    }
    if (!(await this.isSigner(resolvedSignerAddress))) {
      throw new Error("Signer does not already have permissions. You can grant permissions using `grantPermissions`.");
    }
    return await this.sendSignerPermissionRequest(resolvedSignerAddress, resolvedPermissions, AdminFlag.None);
  });

  /**
   * Revoke a scoped access address to the account
   *
   * @remarks Revokes an address' access to the account.
   *
   * @param signer - The address whose access to the account is to be revoked.
   *
   * @example
   * ```javascript
   * const tx = await contract.account.revokeAccess(signer);
   * const receipt = tx.receipt();
   * ```
   *
   * @twfeature AccountPermissions
   */
  revokeAccess = /* @__PURE__ */transactions.buildTransactionFunction(async signerAddress => {
    const resolvedSignerAddress = await index.resolveAddress(signerAddress);
    return await this.sendSignerPermissionRequest(resolvedSignerAddress, {
      startDate: ethers.BigNumber.from(0),
      expirationDate: ethers.BigNumber.from(0),
      approvedCallTargets: [],
      nativeTokenLimitPerTransaction: "0"
    }, AdminFlag.None);
  });

  /**
   * Approve an address as a call target for a given signer on the account
   *
   * @remarks Approves an address as a call target for a given signer on the account.
   *
   * @param signer - A signer with restricted access to the account.
   * @param target - The address to approve as a call target for the signer.
   *
   * @example
   * ```javascript
   * const tx = await contract.account.approveTargetForSigner(signer, target);
   * const receipt = tx.receipt();
   * ```
   *
   * @twfeature AccountPermissions
   */
  approveTargetForSigner = /* @__PURE__ */transactions.buildTransactionFunction(async (signerAddress, target) => {
    const resolvedSignerAddress = await index.resolveAddress(signerAddress);
    const resolvedTarget = await index.resolveAddress(target);
    if (await this.isAdmin(resolvedSignerAddress)) {
      throw new Error("Signer is already an admin. Cannot approve targets for an admin.");
    }
    if (!(await this.isSigner(resolvedSignerAddress))) {
      throw new Error("Signer does not already have permissions. You can grant permissions using `grantPermissions`.");
    }
    const permissions = await this.contractWrapper.read("getPermissionsForSigner", [resolvedSignerAddress]);
    if (permissions.approvedTargets.includes(target)) {
      throw new Error("Target is already approved");
    }
    const newTargets = [...permissions.approvedTargets, resolvedTarget];
    return await this.sendSignerPermissionRequest(resolvedSignerAddress, {
      startDate: ethers.BigNumber.from(permissions.startTimestamp),
      expirationDate: ethers.BigNumber.from(permissions.endTimestamp),
      approvedCallTargets: newTargets,
      nativeTokenLimitPerTransaction: permissions.nativeTokenLimitPerTransaction.toString()
    }, AdminFlag.None);
  });

  /**
   * Disapprove an address as a call target for a given signer on the account
   *
   * @remarks Disapprove an address as a call target for a given signer on the account.
   *
   * @param signer - A signer with restricted access to the account.
   * @param target - The address to disapprove as a call target for the signer.
   *
   * @example
   * ```javascript
   * const tx = await contract.account.disapproveTargetForSigner(signer, target);
   * const receipt = tx.receipt();
   * ```
   *
   * @twfeature AccountPermissions
   */
  disapproveTargetForSigner = /* @__PURE__ */transactions.buildTransactionFunction(async (signerAddress, target) => {
    const resolvedSignerAddress = await index.resolveAddress(signerAddress);
    const resolvedTarget = await index.resolveAddress(target);
    if (await this.isAdmin(resolvedSignerAddress)) {
      throw new Error("Signer is already an admin. Cannot approve targets for an admin.");
    }
    if (!(await this.isSigner(resolvedSignerAddress))) {
      throw new Error("Signer does not already have permissions. You can grant permissions using `grantPermissions`.");
    }
    const permissions = await this.contractWrapper.read("getPermissionsForSigner", [resolvedSignerAddress]);
    if (!permissions.approvedTargets.includes(resolvedTarget)) {
      throw new Error("Target is currently not approved");
    }
    const newTargets = permissions.approvedTargets.filter(approvedTarget => ethers.utils.getAddress(approvedTarget) !== ethers.utils.getAddress(resolvedTarget));
    return await this.sendSignerPermissionRequest(resolvedSignerAddress, {
      startDate: ethers.BigNumber.from(permissions.startTimestamp),
      expirationDate: ethers.BigNumber.from(permissions.endTimestamp),
      approvedCallTargets: newTargets,
      nativeTokenLimitPerTransaction: permissions.nativeTokenLimitPerTransaction.toString()
    }, AdminFlag.None);
  });

  /**
   * Set the account's entire snapshot of permissions.
   *
   * @remarks Sets the account's entire snapshot of permissions.
   *
   * @param permissionSnapshot - the snapshot to set as the account's entire permission snapshot.
   *
   * @example
   * ```javascript
   * const tx = await contract.account.setAccess(permissionSnapshot);
   * const receipt = tx.receipt();
   * ```
   *
   * @twfeature AccountPermissions
   */
  resetAllPermissions = /* @__PURE__ */transactions.buildTransactionFunction(async permissionSnapshot => {
    const resolvedSnapshot = await PermissionSnapshotSchema.parseAsync(permissionSnapshot);

    /**
     * All cases
     *
     * - Add new admin :check:
     * - Remove current admin :check:
     * - Add new scoped :check:
     * - Remove current scoped :check:
     * - Update current scoped :check:
     * - Current admin - new scoped :check:
     * - Current scoped - new admin :check:
     **/

    // No duplicate signers in input!
    if (this.hasDuplicateSigners(resolvedSnapshot)) {
      throw new Error("Duplicate signers found in input.");
    }
    const removeAdminData = [];
    const addOrUpdateSignerData = [];
    const removeSignerData = [];

    // Remove all existing admins not included in the passed snapshot.
    const allAdmins = await this.getAllAdmins();
    const allToMakeAdmin = resolvedSnapshot.filter(item => item.makeAdmin).map(item => item.signer);
    allAdmins.forEach(async admin => {
      if (!allToMakeAdmin.includes(admin)) {
        const data = (await this.sendSignerPermissionRequest(admin, DEFAULT_PERMISSIONS, AdminFlag.RemoveAdmin)).encode();
        removeAdminData.push(data);
      }
    });

    // Remove all existing signers not included in the passed snapshot.
    const allSigners = await this.getAllSigners();
    const allToMakeSigners = resolvedSnapshot.filter(item => {
      return !item.makeAdmin;
    }).map(item => item.signer);
    await Promise.all(allSigners.map(async item => {
      if (!allToMakeSigners.includes(item.signer)) {
        const data = (await this.sendSignerPermissionRequest(item.signer, DEFAULT_PERMISSIONS, AdminFlag.None)).encode();
        removeSignerData.push(data);
      }
    }));
    for (const member of resolvedSnapshot) {
      // Add new admin
      if (member.makeAdmin) {
        (await this.sendSignerPermissionRequest(member.signer, DEFAULT_PERMISSIONS, AdminFlag.AddAdmin)).encode();
      } else {
        // Add new scoped
        const data = (await this.sendSignerPermissionRequest(member.signer, member.permissions, AdminFlag.None)).encode();
        addOrUpdateSignerData.push(data);
      }
    }
    const data = [];
    removeAdminData.forEach(item => {
      data.push(item);
    });
    removeSignerData.forEach(item => {
      data.push(item);
    });
    addOrUpdateSignerData.forEach(item => {
      data.push(item);
    });
    return transactions.Transaction.fromContractWrapper({
      contractWrapper: this.contractWrapper,
      method: "multicall",
      args: [data]
    });
  });
}

/**
 * Interact with ERC-4337 accounts
 * @remarks Exposes useful functions available on account contracts.
 * @example
 * ```javascript
 * const contract = await sdk.getContract("{{contract_address}}");
 * await contract.account.getAllAdminsAndSigners();
 * ```
 * @public
 */
class Account {
  featureName = index.FEATURE_ACCOUNT.name;
  constructor(contractWrapper) {
    this.contractWrapper = contractWrapper;
    this.accountPermissions = this.detectAccountPermissions();
  }
  detectAccountPermissions() {
    if (contractAppuri.detectContractFeature(this.contractWrapper, "AccountPermissions") || contractAppuri.detectContractFeature(this.contractWrapper, "AccountPermissionsV1")) {
      return new AccountPermissions(this.contractWrapper);
    }
    return undefined;
  }
  getAddress() {
    return this.contractWrapper.address;
  }

  /*********************************
   * READ FUNCTIONS
   ********************************/

  /**
   * Get whether a signer is an admin on the account.
   *
   * @example
   * ```javascript
   * const isAdmin = await contract.account.isAdmin(signer);
   * ```
   * @param signer - The address of a signer of the account.
   * @returns whether a signer is an admin on the account.
   *
   * @twfeature AccountPermissions
   */
  async isAdmin(signerAddress) {
    return assertEnabled.assertEnabled(this.accountPermissions, index.FEATURE_ACCOUNT_PERMISSIONS).isAdmin(signerAddress);
  }

  /**
   * Get whether a signer has permissions to use the account.
   *
   * @example
   * ```javascript
   * const isAdmin = await contract.account.isSigner(signer);
   * ```
   * @param signer - The address of a signer of the account.
   * @returns whether a signer has permissions to use the account.
   *
   * @twfeature AccountPermissions
   */
  async isSigner(signerAddress) {
    return assertEnabled.assertEnabled(this.accountPermissions, index.FEATURE_ACCOUNT_PERMISSIONS).isSigner(signerAddress);
  }

  /**
   * Get all admins of the account.
   *
   * @example
   * ```javascript
   * const allAdmins = await contract.account.getAllAdmins();
   * ```
   *
   * @returns All admins of the account.
   *
   * @twfeature AccountPermissions
   */
  async getAllAdmins() {
    return assertEnabled.assertEnabled(this.accountPermissions, index.FEATURE_ACCOUNT_PERMISSIONS).getAllAdmins();
  }

  /**
   * Get all (non-admin) signers with permissions to use the account.
   *
   * @example
   * ```javascript
   * const allSigners = await contract.account.getAllSigners();
   * ```
   *
   * @returns All (non-admin) signers with permissions to use the account.
   *
   * @twfeature AccountPermissions
   */
  async getAllSigners() {
    return assertEnabled.assertEnabled(this.accountPermissions, index.FEATURE_ACCOUNT_PERMISSIONS).getAllSigners();
  }

  /**
   * Get all admins and non-admin signers with permissions to use the account.
   *
   * @example
   * ```javascript
   * const allAdminsAndSigners = await contract.account.getAllAdminsAndSigners();
   * ```
   *
   * @returns All admins and non-admin signers with permissions to use the account.
   *
   * @twfeature AccountPermissions
   */
  async getAllAdminsAndSigners() {
    return assertEnabled.assertEnabled(this.accountPermissions, index.FEATURE_ACCOUNT_PERMISSIONS).getAllAdminsAndSigners();
  }

  /*********************************
   * WRITE FUNCTIONS
   ********************************/

  /**
   * Grant an address admin access to the account.
   *
   * @remarks Grants an address admin access to the account. The admin will have complete authority over the account.
   *
   * @param signer - The address to be granted admin access to the account.
   *
   * @example
   * ```javascript
   * const tx = await contract.account.grantAdminAccess(signer);
   * const receipt = tx.receipt();
   * ```
   *
   * @twfeature AccountPermissions
   */
  grantAdminPermissions = /* @__PURE__ */transactions.buildTransactionFunction(async signerAddress => {
    return assertEnabled.assertEnabled(this.accountPermissions, index.FEATURE_ACCOUNT_PERMISSIONS).grantAdminPermissions.prepare(signerAddress);
  });

  /**
   * Revoke an address' admin access to the account.
   *
   * @remarks Revokes an address' admin access to the account.
   *
   * @param signer - The address of an admin of the account.
   *
   * @example
   * ```javascript
   * const tx = await contract.account.revokeAdminAccess(signer);
   * const receipt = tx.receipt();
   * ```
   *
   * @twfeature AccountPermissions
   */
  revokeAdminPermissions = /* @__PURE__ */transactions.buildTransactionFunction(async signerAddress => {
    return assertEnabled.assertEnabled(this.accountPermissions, index.FEATURE_ACCOUNT_PERMISSIONS).revokeAdminPermissions.prepare(signerAddress);
  });

  /**
   * Grant a signer permissions to use the account.
   *
   * @remarks Grants a signer permissions to use the account.
   *
   * @param signer - The signer to be granted permissions to use the account.
   * @param permissions - The permissions to be applied to the signer's use of the account.
   *
   * @example
   * ```javascript
   * const tx = await contract.account.grantPermissions(signer, permissions);
   * const receipt = tx.receipt();
   * ```
   *
   * @twfeature AccountPermissions
   */
  grantPermissions = /* @__PURE__ */transactions.buildTransactionFunction(async (signerAddress, permissions) => {
    return assertEnabled.assertEnabled(this.accountPermissions, index.FEATURE_ACCOUNT_PERMISSIONS).grantPermissions.prepare(signerAddress, permissions);
  });

  /**
   * Update the permissions of a signer for using the account.
   *
   * @remarks Updates the permissions of a signer for using the account.
   *
   * @param signer - The signer whose permissions to use the account are to be updated.
   * @param permissions - The permissions to be applied to the signer's use of the account.
   *
   * @example
   * ```javascript
   * const tx = await contract.account.updateAccess(signer, restrictions);
   * const receipt = tx.receipt();
   * ```
   *
   * @twfeature AccountPermissions
   */
  updatePermissions = /* @__PURE__ */transactions.buildTransactionFunction(async (signerAddress, permissions) => {
    return assertEnabled.assertEnabled(this.accountPermissions, index.FEATURE_ACCOUNT_PERMISSIONS).updatePermissions.prepare(signerAddress, permissions);
  });

  /**
   * Revoke a scoped access address to the account
   *
   * @remarks Revokes an address' access to the account.
   *
   * @param signer - The address whose access to the account is to be revoked.
   *
   * @example
   * ```javascript
   * const tx = await contract.account.revokeAccess(signer);
   * const receipt = tx.receipt();
   * ```
   *
   * @twfeature AccountPermissions
   */
  revokeAccess = /* @__PURE__ */transactions.buildTransactionFunction(async signerAddress => {
    return assertEnabled.assertEnabled(this.accountPermissions, index.FEATURE_ACCOUNT_PERMISSIONS).revokeAccess.prepare(signerAddress);
  });

  /**
   * Approve an address as a call target for a given signer on the account
   *
   * @remarks Approves an address as a call target for a given signer on the account.
   *
   * @param signer - A signer with restricted access to the account.
   * @param target - The address to approve as a call target for the signer.
   *
   * @example
   * ```javascript
   * const tx = await contract.account.approveTargetForSigner(signer, target);
   * const receipt = tx.receipt();
   * ```
   *
   * @twfeature AccountPermissions
   */
  approveTargetForSigner = /* @__PURE__ */transactions.buildTransactionFunction(async (signerAddress, target) => {
    return assertEnabled.assertEnabled(this.accountPermissions, index.FEATURE_ACCOUNT_PERMISSIONS).approveTargetForSigner.prepare(signerAddress, target);
  });

  /**
   * Disapprove an address as a call target for a given signer on the account
   *
   * @remarks Disapprove an address as a call target for a given signer on the account.
   *
   * @param signer - A signer with restricted access to the account.
   * @param target - The address to disapprove as a call target for the signer.
   *
   * @example
   * ```javascript
   * const tx = await contract.account.disapproveTargetForSigner(signer, target);
   * const receipt = tx.receipt();
   * ```
   *
   * @twfeature AccountPermissions
   */
  disapproveTargetForSigner = /* @__PURE__ */transactions.buildTransactionFunction(async (signerAddress, target) => {
    return assertEnabled.assertEnabled(this.accountPermissions, index.FEATURE_ACCOUNT_PERMISSIONS).disapproveTargetForSigner.prepare(signerAddress, target);
  });

  /**
   * Set the account's entire snapshot of permissions.
   *
   * @remarks Sets the account's entire snapshot of permissions.
   *
   * @param permissionSnapshot - the snapshot to set as the account's entire permission snapshot.
   *
   * @example
   * ```javascript
   * const tx = await contract.account.setAccess(permissionSnapshot);
   * const receipt = tx.receipt();
   * ```
   *
   * @twfeature AccountPermissions
   */
  resetAllPermissions = /* @__PURE__ */transactions.buildTransactionFunction(async permissionSnapshot => {
    return assertEnabled.assertEnabled(this.accountPermissions, index.FEATURE_ACCOUNT_PERMISSIONS).resetAllPermissions.prepare(permissionSnapshot);
  });
}

/**
 * Interact with ERC-4337 account factory contracts
 * @remarks Exposes useful functions available on the account factory contract.
 * @example
 * ```javascript
 * const contract = await sdk.getContract("{{contract_address}}");
 * await contract.accountFactory.predictAccountAddress(walletAddress);
 * ```
 * @public
 */
class AccountFactory {
  featureName = index.FEATURE_ACCOUNT_FACTORY.name;

  // utilities

  constructor(contractWrapper) {
    this.contractWrapper = contractWrapper;
    this.events = new contractAppuri.ContractEvents(this.contractWrapper);
  }
  getAddress() {
    return this.contractWrapper.address;
  }

  /*********************************
   * READ FUNCTIONS
   *******************************/

  /**
   * Get the deterministic address of the account that will be created
   *
   * @example
   * ```javascript
   * const accountAddress = await contract.accountFactory.predictAccountAddress(admin);
   * ```
   * @param admin - The admin of the account.
   * @param extraData - (Optional) Extra data to be passed to the account on creation.
   * @returns The deterministic address of the account that will be created for the given admin.
   *
   * @twfeature AccountFactory
   */
  async predictAccountAddress(admin, extraData) {
    let data = ethers.utils.toUtf8Bytes("");
    if (extraData) {
      data = extraData;
    }
    return this.contractWrapper.read("getAddress", [admin, data]);
  }

  /**
   * Get all accounts on which the given signer has authority
   *
   * @example
   * ```javascript
   * const allAccounts = await contract.accountFactory.getAssociatedAccounts(admin);
   * ```
   * @param signer - The account address.
   * @returns All accounts on which the given signer has authority.
   *
   * @twfeature AccountFactory
   */
  async getAssociatedAccounts(signer) {
    return this.contractWrapper.read("getAccountsOfSigner", [signer]);
  }

  /**
   * Get all accounts
   *
   * @example
   * ```javascript
   * const allAccounts = await contract.accountFactory.getAllAccounts();
   * ```
   *
   * @returns All accounts created via the account factory.
   *
   * @twfeature AccountFactory
   */
  async getAllAccounts() {
    return await this.contractWrapper.read("getAllAccounts", []);
  }

  /**
   * Check if a account has been deployed for the given admin
   *
   * @param admin - The admin of the account.
   * @param extraData - (Optional) Extra data to be passed to the account on creation.
   * @returns whether the account has been deployed for the given admin.
   */
  async isAccountDeployed(admin, extraData) {
    const addr = await this.predictAccountAddress(admin, extraData);
    return index.isContractDeployed(addr, this.contractWrapper.getProvider());
  }

  /*********************************
   * WRITE FUNCTIONS
   *******************************/

  /**
   * Create a account
   *
   * @remarks Create a account for an admin. The admin will have complete authority over the account.
   *
   * @param admin - The admin of the account.
   * @param extraData - (Optional) Extra data to be passed to the account on creation.
   *
   * @example
   * ```javascript
   * const tx = await contract.accountFactory.createAccount(admin, extraData);
   * const receipt = tx.receipt();
   * const accountAddress = tx.address;
   * ```
   *
   * @twfeature AccountFactory
   */
  createAccount = /* @__PURE__ */transactions.buildTransactionFunction(async (accountAdmin, extraData) => {
    if (await this.isAccountDeployed(accountAdmin, extraData)) {
      throw new Error(`Account already deployed for admin: ${accountAdmin}`);
    }
    let data = ethers.utils.toUtf8Bytes("");
    if (extraData) {
      data = extraData;
    }
    return transactions.Transaction.fromContractWrapper({
      contractWrapper: this.contractWrapper,
      method: "createAccount",
      args: [accountAdmin, data],
      parse: receipt => {
        const event = this.contractWrapper.parseLogs("AccountCreated", receipt?.logs);
        return {
          address: event[0].args.account,
          receipt
        };
      }
    });
  });
}

/**
 * @internal
 * @param abi - The abi to extract events from
 * @param metadata - The metadata to extract comments from
 */
function extractEventsFromAbi(abi, metadata) {
  const parsedAbi = index.AbiSchema.parse(abi || []);
  const events = parsedAbi.filter(el => el.type === "event");
  const parsed = [];
  for (const e of events) {
    const doc = index.extractCommentFromMetadata(e.name, metadata, "events");
    parsed.push({
      inputs: e.inputs || [],
      outputs: e.outputs || [],
      name: e.name || "unknown",
      comment: doc
    });
  }
  return parsed;
}

/**
 * Handles publish metadata for a contract
 * @internal
 */
class ContractPublishedMetadata {
  constructor(contractWrapper, storage) {
    this.contractWrapper = contractWrapper;
    this.storage = storage;
  }

  /**
   * Get the published metadata for this contract
   * @public
   */
  async get() {
    if (this._cachedMetadata) {
      return this._cachedMetadata;
    }
    this._cachedMetadata = await index.fetchContractMetadataFromAddress(this.contractWrapper.address, this.contractWrapper.getProvider(), this.storage, this.contractWrapper.options);
    return this._cachedMetadata;
  }

  /**
   * @public
   */
  async extractSources() {
    const publishedMetadata = await this.get();
    return index.fetchSourceFilesFromMetadata(publishedMetadata, this.storage);
  }

  /**
   * @public
   */
  async extractFunctions() {
    let publishedMetadata;
    try {
      publishedMetadata = await this.get();
    } catch (e) {
      // ignore for built-in contracts
    }
    // to construct a contract we already **have** to have the abi on the contract wrapper, so there is no reason to look fetch it again (means this function can become synchronous as well!)
    return index.extractFunctionsFromAbi(index.AbiSchema.parse(this.contractWrapper.abi), publishedMetadata?.metadata);
  }

  /**
   * @public
   */
  async extractEvents() {
    let publishedMetadata;
    try {
      publishedMetadata = await this.get();
    } catch (e) {
      // ignore for built-in contracts
    }
    // to construct a contract we already **have** to have the abi on the contract wrapper, so there is no reason to look fetch it again (means this function can become synchronous as well!)
    return extractEventsFromAbi(index.AbiSchema.parse(this.contractWrapper.abi), publishedMetadata?.metadata);
  }
}

class ExtensionManager {
  featureName = index.FEATURE_DYNAMIC_CONTRACT.name;
  constructor(contractWrapper) {
    this.contractWrapper = contractWrapper;
  }
  getAddress() {
    return this.contractWrapper.readContract.address;
  }

  /** ******************************
   * READ FUNCTIONS
   *******************************/

  async getAll() {
    const extensions = await this.contractWrapper.readContract.getAllExtensions();
    return extensions;
  }
  async get(extensionName) {
    const extension = await this.contractWrapper.readContract.getExtension(extensionName);
    return extension;
  }
  async getExtensionAddress(extensionName) {
    const extension = await this.get(extensionName);
    return extension.metadata.implementation;
  }
  async getAllFunctions(extensionName) {
    const extension = await this.get(extensionName);
    return extension.functions;
  }
  async getExtensionForFunction(functionInput) {
    let selector = functionInput.functionSelector;
    if (!selector) {
      invariant__default["default"](functionInput.functionSignature, "Atleast one of function selector and signature must be provided");
      selector = ethers.utils.id(functionInput.functionSignature).substring(0, 10);
    }
    const extensionMetadata = await this.contractWrapper.readContract.getMetadataForFunction(selector);
    return extensionMetadata;
  }
  async getExtensionAddressForFunction(functionInput) {
    const extensionMetadata = await this.getExtensionForFunction(functionInput);
    return extensionMetadata.implementation;
  }

  /** ******************************
   * WRITE FUNCTIONS
   *******************************/

  /**
   * Adds an extension to the contract
   */
  add = /* @__PURE__ */transactions.buildTransactionFunction(async inputArgs => {
    return transactions.Transaction.fromContractWrapper({
      contractWrapper: this.contractWrapper,
      method: "addExtension",
      args: [inputArgs.extension],
      parse: async receipt => {
        const events = this.contractWrapper.parseLogs("ExtensionAdded", receipt.logs);
        if (events.length < 1) {
          throw new Error("No ExtensionAdded event found");
        }
        const extensionAbi = inputArgs.extensionAbi ? index.AbiSchema.parse(inputArgs.extensionAbi) : (await index.fetchContractMetadataFromAddress(inputArgs.extension.metadata.implementation, this.contractWrapper.getProvider(), this.contractWrapper.storage)).abi;
        const abiToAdd = this.filterAbiForAdd(extensionAbi, inputArgs.extension);
        const updatedAbi = index.joinABIs([index.AbiSchema.parse(this.contractWrapper.abi), abiToAdd]);
        this.contractWrapper.updateAbi(updatedAbi);
        return receipt;
      }
    });
  });

  /**
   * Adds a deployed extension to the contract
   */
  addDeployed = /* @__PURE__ */transactions.buildTransactionFunction(async inputArgs => {
    let extensionAbi = inputArgs.extensionAbi;
    if (!extensionAbi) {
      const metadata = await index.fetchContractMetadataFromAddress(inputArgs.extensionAddress, this.contractWrapper.getProvider(), this.contractWrapper.storage, this.contractWrapper.options);
      extensionAbi = metadata.abi;
    }
    invariant__default["default"](extensionAbi, "Require extension ABI");
    let extensionMetadataUri = "";
    if (inputArgs.extensionMetadata) {
      if (typeof inputArgs.extensionMetadata === "string") {
        extensionMetadataUri = inputArgs.extensionMetadata;
      } else {
        const parsedMetadata = await index.CommonContractSchema.parseAsync(inputArgs.extensionMetadata);
        extensionMetadataUri = await this.contractWrapper.storage.upload(parsedMetadata);
      }
    }
    const extensionFunctions = deployContractDeterministic.generateExtensionFunctions(index.AbiSchema.parse(extensionAbi));
    const extension = {
      metadata: {
        name: inputArgs.extensionName,
        metadataURI: extensionMetadataUri,
        implementation: inputArgs.extensionAddress
      },
      functions: extensionFunctions
    };
    return this.add.prepare({
      extension,
      extensionAbi
    });
  });

  /**
   * Adds a published extension to the contract, and deploys it deterministically if necessary
   */
  addPublished = /* @__PURE__ */transactions.buildTransactionFunction(async inputArgs => {
    const version = inputArgs.version || "latest";
    const {
      deployedExtensionAddress,
      extensionMetadata
    } = await this.deployExtension(inputArgs.extensionName, inputArgs.publisherAddress || index.THIRDWEB_DEPLOYER, version);
    return this.addDeployed.prepare({
      extensionName: inputArgs.extensionName,
      extensionAddress: deployedExtensionAddress,
      extensionMetadata: inputArgs.extensionMetadataOverride || extensionMetadata
    });
  });
  replace = /* @__PURE__ */transactions.buildTransactionFunction(async inputArgs => {
    return transactions.Transaction.fromContractWrapper({
      contractWrapper: this.contractWrapper,
      method: "replaceExtension",
      args: [inputArgs.extension],
      parse: async receipt => {
        const events = this.contractWrapper.parseLogs("ExtensionReplaced", receipt.logs);
        if (events.length < 1) {
          throw new Error("No ExtensionReplaced event found");
        }
        const extensionAbi = inputArgs.extensionAbi ? index.AbiSchema.parse(inputArgs.extensionAbi) : (await index.fetchContractMetadataFromAddress(inputArgs.extension.metadata.implementation, this.contractWrapper.getProvider(), this.contractWrapper.storage)).abi;
        const contractAbi = this.filterAbiForRemove(index.AbiSchema.parse(this.contractWrapper.abi), extensionAbi);
        const abiToAdd = this.filterAbiForAdd(extensionAbi, inputArgs.extension);
        const updatedAbi = index.joinABIs([contractAbi, abiToAdd]);
        this.contractWrapper.updateAbi(updatedAbi);
        return receipt;
      }
    });
  });
  replaceDeployed = /* @__PURE__ */transactions.buildTransactionFunction(async inputArgs => {
    let extensionAbi = inputArgs.extensionAbi;
    if (!extensionAbi) {
      const metadata = await index.fetchContractMetadataFromAddress(inputArgs.extensionAddress, this.contractWrapper.getProvider(), this.contractWrapper.storage, this.contractWrapper.options);
      extensionAbi = metadata.abi;
    }
    invariant__default["default"](extensionAbi, "Require extension ABI");
    let extensionMetadataUri = "";
    if (inputArgs.extensionMetadata) {
      if (typeof inputArgs.extensionMetadata === "string") {
        extensionMetadataUri = inputArgs.extensionMetadata;
      } else {
        const parsedMetadata = await index.CommonContractSchema.parseAsync(inputArgs.extensionMetadata);
        extensionMetadataUri = await this.contractWrapper.storage.upload(parsedMetadata);
      }
    }
    const extensionFunctions = deployContractDeterministic.generateExtensionFunctions(index.AbiSchema.parse(extensionAbi));
    const extension = {
      metadata: {
        name: inputArgs.extensionName,
        metadataURI: extensionMetadataUri,
        implementation: inputArgs.extensionAddress
      },
      functions: extensionFunctions
    };
    return this.replace.prepare({
      extension,
      extensionAbi
    });
  });
  replacePublished = /* @__PURE__ */transactions.buildTransactionFunction(async inputArgs => {
    const version = inputArgs.version || "latest";
    const {
      deployedExtensionAddress,
      extensionMetadata
    } = await this.deployExtension(inputArgs.extensionName, inputArgs.publisherAddress || index.THIRDWEB_DEPLOYER, version);
    return this.replaceDeployed.prepare({
      extensionName: inputArgs.extensionName,
      extensionAddress: deployedExtensionAddress,
      extensionMetadata: inputArgs.extensionMetadataOverride || extensionMetadata
    });
  });
  remove = /* @__PURE__ */transactions.buildTransactionFunction(async inputArgs => {
    const extensionAddress = await this.getExtensionAddress(inputArgs.extensionName);
    return transactions.Transaction.fromContractWrapper({
      contractWrapper: this.contractWrapper,
      method: "removeExtension",
      args: [inputArgs.extensionName],
      parse: async receipt => {
        const events = this.contractWrapper.parseLogs("ExtensionRemoved", receipt.logs);
        if (events.length < 1) {
          throw new Error("No ExtensionRemoved event found");
        }
        const extensionAbi = (await index.fetchContractMetadataFromAddress(extensionAddress, this.contractWrapper.getProvider(), this.contractWrapper.storage)).abi;
        const updatedAbi = this.filterAbiForRemove(index.AbiSchema.parse(this.contractWrapper.abi), extensionAbi);
        this.contractWrapper.updateAbi(updatedAbi);
        return receipt;
      }
    });
  });

  /** ******************************
   * Internal / private
   *******************************/

  filterAbiForAdd(extensionAbi, extension) {
    const extensionAbiInterface = new ethers.utils.Interface(extensionAbi);
    const extensionFunctionSelectors = extension.functions.map(fn => fn.functionSelector);
    const filtered = extensionAbi.filter(item => {
      const fnFragment = Object.values(new ethers.utils.Interface([item]).functions);
      if (fnFragment.length === 0) {
        return false;
      }
      const fnSigHash = extensionAbiInterface.getSighash(fnFragment[0]);
      return extensionFunctionSelectors.includes(fnSigHash);
    });
    return filtered;
  }
  filterAbiForRemove(fullAbi, abiToRemove) {
    const fullAbiInterface = new ethers.utils.Interface(fullAbi);
    const interfaceToRemove = new ethers.utils.Interface(abiToRemove);
    const functionsToRemove = Object.values(interfaceToRemove.functions).map(fn => interfaceToRemove.getSighash(fn));
    const filtered = fullAbi.filter(item => {
      const fnFragment = Object.values(new ethers.utils.Interface([item]).functions);
      if (fnFragment.length === 0) {
        return false;
      }
      const fnSigHash = fullAbiInterface.getSighash(fnFragment[0]);
      return !functionsToRemove.includes(fnSigHash);
    });
    return filtered;
  }
  async deployExtension(extensionName, publisherAddress) {
    let version = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "latest";
    const published = await index.fetchPublishedContractFromPolygon(publisherAddress, extensionName, version, this.contractWrapper.storage, this.contractWrapper.options.clientId, this.contractWrapper.options.secretKey);
    const deploymentInfo = await deployContractDeterministic.getDeploymentInfo(published.metadataUri, this.contractWrapper.storage, this.contractWrapper.getProvider(), "", this.contractWrapper.options.clientId, this.contractWrapper.options.secretKey);
    const implementationAddress = deploymentInfo.find(i => i.type === "implementation")?.transaction.predictedAddress;

    // deploy infra + plugins + implementation using a throwaway Deployer contract

    // filter out already deployed contracts (data is empty)
    const transactionsToSend = deploymentInfo.filter(i => i.transaction.data && i.transaction.data.length > 0);
    const transactionsforDirectDeploy = transactionsToSend.filter(i => {
      return i.type !== "infra";
    }).map(i => i.transaction);
    const transactionsForThrowawayDeployer = transactionsToSend.filter(i => {
      return i.type === "infra";
    }).map(i => i.transaction);
    const signer = this.contractWrapper.getSigner();
    invariant__default["default"](signer, "Signer is required");

    // deploy via throwaway deployer, multiple infra contracts in one transaction
    await deployContractDeterministic.deployWithThrowawayDeployer(signer, transactionsForThrowawayDeployer, {});

    // send each transaction directly to Create2 factory
    // process txns one at a time
    for (const tx of transactionsforDirectDeploy) {
      try {
        await deployContractDeterministic.deployContractDeterministic(signer, tx);
      } catch (e) {
        console.debug(`Error deploying contract at ${tx.predictedAddress}`, e?.message);
      }
    }
    return {
      deployedExtensionAddress: implementationAddress,
      extensionMetadata: published.metadataUri
    };
  }
}

/**
 * @public
 */
class Airdrop20 {
  featureName = index.FEATURE_AIRDROP_ERC20.name;
  constructor(contractWrapper) {
    this.contractWrapper = contractWrapper;
  }

  /**
   * @internal
   */
  getAddress() {
    return this.contractWrapper.address;
  }

  /** ******************************
   * WRITE FUNCTIONS
   *******************************/

  /**
   * Perform airdrop of ERC20 tokens
   *
   * @example
   * ```javascript
   * // Airdrop content array, with recipients and token amounts
   * const contents = [
   *      {
   *        recipient: "0xabc...", // first recipient address
   *        amount: "10" // number of tokens in wei units
   *      },
   *      {
   *        recipient: "0x123...", // second recipient address
   *        amount: "20" // number of tokens in wei units
   *      }
   *   ]
   *
   * const tokenAddress = "0x..." // Address of the ERC20 token being airdropped
   * const tokenOwner = "0x..." // Address of the owner of the tokens being airdropped
   *
   * const output = await contract.airdrop20.drop(tokenAddress, tokenOwner, contents);
   *
   * // the `output` return value above contains:
   * //     - count of successful and failed drops
   * //     - array containing failed drops, if any
   *
   * ```
   * @param tokenAddress - Address of the ERC20 token being airdropped
   * @param tokenOwner - Address of the owner of the tokens being airdropped
   * @param contents - Array of airdrop contents
   *
   * @returns An array of recipients for who the airdrop failed (empty means all transfers were successful)
   * @twfeature AirdropERC20
   */
  drop = /* @__PURE__ */transactions.buildTransactionFunction(async (tokenAddress, tokenOwner, contents) => {
    return transactions.Transaction.fromContractWrapper({
      contractWrapper: this.contractWrapper,
      method: "airdropERC20",
      args: [tokenAddress, tokenOwner, contents],
      parse: receipt => {
        const events = this.contractWrapper.parseLogs("AirdropFailed", receipt.logs);
        const failedDrops = events.map(e => {
          return {
            recipient: e.args.recipient,
            amount: e.args.amount.toString()
          };
        });
        return {
          successfulDropCount: contents.length - failedDrops.length,
          failedDropCount: failedDrops.length,
          failedDrops
        };
      }
    });
  });
}

/**
 * @public
 */
class Airdrop721 {
  featureName = index.FEATURE_AIRDROP_ERC721.name;
  constructor(contractWrapper) {
    this.contractWrapper = contractWrapper;
  }

  /**
   * @internal
   */
  getAddress() {
    return this.contractWrapper.address;
  }

  /** ******************************
   * WRITE FUNCTIONS
   *******************************/

  /**
   * Perform airdrop of ERC721 tokens
   *
   * @example
   * ```javascript
   * // Airdrop content array, with recipients and tokenIds
   * const contents = [
   *      {
   *        recipient: "0xabc...", // first recipient address
   *        tokenId: 0
   *      },
   *      {
   *        recipient: "0x123...", // second recipient address
   *        tokenId: 2
   *      }
   *   ]
   *
   * const tokenAddress = "0x..." // Address of the ERC721 token being airdropped
   * const tokenOwner = "0x..." // Address of the owner of the tokens being airdropped
   *
   * const output = await contract.airdrop721.drop(tokenAddress, tokenOwner, contents);
   *
   * // the `output` return value above contains:
   * //     - count of successful and failed drops
   * //     - array containing failed drops, if any
   *
   * ```
   * @param tokenAddress - Address of the ERC721 token being airdropped
   * @param tokenOwner - Address of the owner of the tokens being airdropped
   * @param contents - Array of recipients and tokenIds to airdrop
   *
   * @returns An array of recipients for who the airdrop failed (empty means all transfers were successful)
   * @twfeature AirdropERC721
   */
  drop = /* @__PURE__ */transactions.buildTransactionFunction(async (tokenAddress, tokenOwner, contents) => {
    return transactions.Transaction.fromContractWrapper({
      contractWrapper: this.contractWrapper,
      method: "airdropERC721",
      args: [tokenAddress, tokenOwner, contents],
      parse: receipt => {
        const events = this.contractWrapper.parseLogs("AirdropFailed", receipt.logs);
        const failedDrops = events.map(e => {
          return {
            recipient: e.args.recipient,
            tokenId: e.args.tokenId.toNumber()
          };
        });
        return {
          successfulDropCount: contents.length - failedDrops.length,
          failedDropCount: failedDrops.length,
          failedDrops
        };
      }
    });
  });
}

/**
 * @public
 */
class Airdrop1155 {
  featureName = index.FEATURE_AIRDROP_ERC1155.name;
  constructor(contractWrapper) {
    this.contractWrapper = contractWrapper;
  }

  /**
   * @internal
   */
  getAddress() {
    return this.contractWrapper.address;
  }

  /** ******************************
   * WRITE FUNCTIONS
   *******************************/

  /**
   * Perform airdrop of ERC1155 tokens
   *
   * @example
   * ```javascript
   * // Airdrop content array, with recipients and tokenIds
   * const contents = [
   *      {
   *        recipient: "0xabc...", // first recipient address
   *        tokenId: 0,
   *        amount: "10" // number of tokens
   *      },
   *      {
   *        recipient: "0x123...", // second recipient address
   *        tokenId: 0
   *        amount: "20" // number of tokens
   *      }
   *   ]
   *
   * const tokenAddress = "0x..." // Address of the ERC1155 token being airdropped
   * const tokenOwner = "0x..." // Address of the owner of the tokens being airdropped
   *
   * const output = await contract.airdrop1155.drop(tokenAddress, tokenOwner, contents);
   *
   * // the `output` return value above contains:
   * //     - count of successful and failed drops
   * //     - array containing failed drops, if any
   *
   * ```
   * @param tokenAddress - Address of the ERC1155 token being airdropped
   * @param tokenOwner - Address of the owner of the tokens being airdropped
   * @param contents - Array of recipients and tokenIds to airdrop
   *
   * @returns An array of recipients for who the airdrop failed (empty means all transfers were successful)
   * @twfeature AirdropERC1155
   */
  drop = /* @__PURE__ */transactions.buildTransactionFunction(async (tokenAddress, tokenOwner, contents) => {
    return transactions.Transaction.fromContractWrapper({
      contractWrapper: this.contractWrapper,
      method: "airdropERC1155",
      args: [tokenAddress, tokenOwner, contents],
      parse: receipt => {
        const events = this.contractWrapper.parseLogs("AirdropFailed", receipt.logs);
        const failedDrops = events.map(e => {
          return {
            recipient: e.args.recipient,
            tokenId: e.args.tokenId.toNumber(),
            amount: e.args.amount.toString()
          };
        });
        return {
          successfulDropCount: contents.length - failedDrops.length,
          failedDropCount: failedDrops.length,
          failedDrops
        };
      }
    });
  });
}

exports.Account = Account;
exports.AccountFactory = AccountFactory;
exports.AdminFlag = AdminFlag;
exports.Airdrop1155 = Airdrop1155;
exports.Airdrop20 = Airdrop20;
exports.Airdrop721 = Airdrop721;
exports.ContractPublishedMetadata = ContractPublishedMetadata;
exports.DEFAULT_PERMISSIONS = DEFAULT_PERMISSIONS;
exports.ExtensionManager = ExtensionManager;
exports.PermissionSnapshotSchema = PermissionSnapshotSchema;
exports.SignerPermissionRequest = SignerPermissionRequest;
exports.SignerPermissionRequestV1 = SignerPermissionRequestV1;
exports.SignerPermissionsSchema = SignerPermissionsSchema;
exports.extractEventsFromAbi = extractEventsFromAbi;
