erc721/index.ts

import { ethers } from "ethers";
import { getAbiObjectOwnership, getAbiERC721, getAbiERC721Collection } from "@evo/utils/ethers/abiHelper";
import type { CallbackType } from "@evo/utils/ethers/contractHelper";
import { pad0xBegin } from "@evo/utils/common/utils";
import { triggerContract, viewContract, getContract } from "@evo/utils/ethers/contractHelper";
import { TransactionResponse } from "@ethersproject/providers";

/**
 * ERC721 Owner of
 * @param provider Provider
 * @param tokenAddress ERC721 contract address
 * @param tokenId Token Id
 * @param callback Callback
 * @returns The address of owner
 */
export const erc721OwnerOf = async (
  provider: ethers.providers.Provider,
  tokenAddress: string,
  tokenId: string,
  callback?: CallbackType
): Promise<string> => {
  const contract = await getContract(provider, tokenAddress, await getAbiObjectOwnership());

  const results = await viewContract(contract, "ownerOf", [tokenId], callback);
  return results.length ? results[0] : "";
};

/**
 * Query if an address is an authorized operator for another address
 * @param provider Provider
 * @param owner The address that owns the NFTs
 * @param spender The address that acts on behalf of the owner
 * @param tokenContractAddress ERC721 contract address
 * @param callback Callback
 * @returns any
 */
export const erc721IsApprovedForAll = async (
  provider: ethers.providers.Provider,
  owner: string,
  spender: string,
  tokenContractAddress: string,
  callback?: CallbackType
): Promise<boolean> => {
  const contract = await getContract(provider, tokenContractAddress, await getAbiERC721Collection());
  const results = await viewContract(contract, "isApprovedForAll", [owner, spender]);
  return results.length ? results[0] : false;
};

/**
 * Returns whether `spender` is allowed to manage `tokenId`.
 * @param provider Provider
 * @param spender The address that acts on behalf of the owner
 * @param tokenContractAddress The factory of tokenId.
 * @param tokenId ERC721 token Id;
 * @param callback Callback
 * @returns any
 */
export const erc721IsApprovedOrOwner = async (
  provider: ethers.providers.Provider,
  spender: string,
  tokenContractAddress: string,
  tokenId: string,
  callback?: CallbackType
): Promise<boolean> => {
  const contract = await getContract(provider, tokenContractAddress, await getAbiERC721());

  const owner = await viewContract(contract, "ownerOf", [pad0xBegin(tokenId)]);

  if (!owner._owner) {
    throw new Error("erc721IsApprovedOrOwner::Token Id does not exist!");
  }

  const approvedAddress = await viewContract(contract, "getApproved", [pad0xBegin(tokenId)]);
  const isApprovedForAll = await erc721IsApprovedForAll(provider, owner._owner, spender, tokenContractAddress);

  return (
    owner._owner?.toLowerCase() === spender.toLowerCase() ||
    approvedAddress._approved?.toLowerCase() === spender.toLowerCase() ||
    isApprovedForAll
  );
};

/**
 * Change or reaffirm the approved address for all of NFT
 * @param signer Signer
 * @param contractAddress The factory of tokenId.
 * @param spender The address that acts on behalf of the owner
 * @param approved Approved or Cancel Approved
 * @param callback Callback
 * @returns any
 */
export const erc721SetApprovalForAll = async (
  signer: ethers.Signer,
  contractAddress: string,
  spender: string,
  approved: boolean,
  callback?: CallbackType
): Promise<TransactionResponse> => {
  const contract = await getContract(signer, contractAddress, await getAbiERC721Collection());
  return triggerContract(contract, "setApprovalForAll", [spender, approved], callback);
};