apostle/v1.ts

import { ethers } from "ethers";
import { erc20Allowance } from "@evo/api/ethers/erc20";
import { pad0xBegin } from "@evo/utils/common/utils";
import { toHexAndPadLeft, padLeft } from "@evo/utils/ethers/utils";
import {
  getContractAddressAndAbiByContractName,
  triggerContractByContractName,
  viewContractByContractName,
} from "@evo/utils/ethers/contractHelper";
import type { ElementType } from "@evo/types";
import type { CallbackType } from "@evo/utils/ethers/contractHelper";
import { LandId } from "@evo/config/constants";
import { TransactionResponse } from "@ethersproject/providers";
import { getAddressesByLandId } from "@evo/utils/ethers/addressHelper";

/**
 * Apostle Allowance To Clock Auction
 * @param provider Ethers provider
 * @param from From
 * @param callback Callback
 * @returns Promise<string | undefined>
 */
export const apostleAllowanceToClockAuction = async (
  landId: LandId,
  provider: ethers.providers.Provider,
  from: string,
  callback?: CallbackType
): Promise<string | undefined> => {
  const spender = getContractAddressAndAbiByContractName(landId, provider, "apostleAuction").address;
  if (spender) {
    return erc20Allowance(landId, provider, "ring", from, spender as string);
  } else {
    callback &&
      callback.errorCallback({ error: new Error("failed to call apostleAllowanceToClockAuction. spender undefined") });
  }
};

/**
 * Apostle Bid
 * @param signer Ethers signer
 * @param tokenId Apostle token id
 * @param referrer Refer address
 * @param amountMax RING Amount
 * @param callback Callback
 */
export const apostleBidWithToken = (
  landId: LandId,
  signer: ethers.Signer,
  tokenId: string,
  referrer: string,
  amountMax: string,
  callback?: CallbackType
): Promise<TransactionResponse> => {
  const finalReferrer =
    referrer && ethers.utils.isAddress(referrer) ? referrer : "0x0000000000000000000000000000000000000000";

  if (landId === LandId.ETHEREUM || landId === LandId._ETHEREUM) {
    const data = referrer
      ? `${pad0xBegin(tokenId)}${ethers.utils.hexZeroPad(referrer, 32).slice(2)}`
      : pad0xBegin(tokenId);

    const addresses = getAddressesByLandId(landId);
    if (!addresses || !addresses.APOSTLE_CLOCK_AUCTION) {
      throw "address error";
    }

    return triggerContractByContractName(
      landId,
      signer,
      "ring",
      "transfer(address,uint256,bytes)",
      [addresses.APOSTLE_CLOCK_AUCTION, amountMax, data],
      callback
    );
  }

  return triggerContractByContractName(
    landId,
    signer,
    "apostleClockAuctionV3",
    "bidWithToken",
    [pad0xBegin(tokenId), finalReferrer, amountMax],
    callback
  );
};

/**
 * Apostle Breed - Apostle reproduction in own
 * @param signer Ethers signer
 * @param tokenId Token id
 * @param targetTokenId Target token id
 * @param amountMax Amount max
 * @param callback Callback
 */
export const apostleBreed = (
  landId: LandId,
  signer: ethers.Signer,
  tokenId: string,
  targetTokenId: string,
  amountMax: string,
  callback?: CallbackType
): Promise<TransactionResponse> => {
  if (landId === LandId.ETHEREUM || landId === LandId._ETHEREUM) {
    const addresses = getAddressesByLandId(landId);
    if (!addresses || !addresses.APOSTLE_BASE) {
      throw "address error";
    }

    return triggerContractByContractName(
      landId,
      signer,
      "ring",
      "transfer(address,uint256,bytes)",
      [
        addresses.APOSTLE_BASE,
        amountMax,
        `${pad0xBegin(tokenId)}${ethers.utils.hexZeroPad(pad0xBegin(targetTokenId), 32).slice(2)}`,
      ],
      callback
    );
  }

  return triggerContractByContractName(
    landId,
    signer,
    "apostleBaseV3",
    "breedWithAuto",
    [pad0xBegin(tokenId), pad0xBegin(targetTokenId), amountMax],
    callback
  );
};

/**
 * Apostle Breed Bid - Apostle reproduction
 * @param signer Ethers signer
 * @param tokenId Token id
 * @param targetTokenId Target token id
 * @param amountMax Amount max (bidPrice + autoBirthFee)
 * @param callback Callback
 */
export const apostleBreedBid = (
  landId: LandId,
  signer: ethers.Signer,
  tokenId: string,
  targetTokenId: string,
  amountMax: string,
  callback?: CallbackType
): Promise<TransactionResponse> => {
  if (landId === LandId.ETHEREUM || landId === LandId._ETHEREUM) {
    const addresses = getAddressesByLandId(landId);
    if (!addresses || !addresses.SIRING_CLOCK_AUCTION) {
      throw "address error";
    }

    return triggerContractByContractName(
      landId,
      signer,
      "ring",
      "transfer(address,uint256,bytes)",
      [
        addresses.SIRING_CLOCK_AUCTION,
        amountMax,
        `${pad0xBegin(tokenId)}${ethers.utils.hexZeroPad(pad0xBegin(targetTokenId), 32).slice(2)}`,
      ],
      callback
    );
  }

  return triggerContractByContractName(
    landId,
    signer,
    "apostleSiringAuctionV3",
    "bidWithToken",
    [pad0xBegin(tokenId), pad0xBegin(targetTokenId), amountMax],
    callback
  );
};

/**
 * Apostle Hire Bid - Bid apostle on Renting
 * @param signer Ethers signer
 * @param tokenId Apostle token id
 * @param amountMax Amount max (bid price)
 * @param callback Callback
 */
export const apostleHireBid = (
  landId: LandId,
  signer: ethers.Signer,
  tokenId: string,
  amountMax: string,
  callback?: CallbackType
): Promise<TransactionResponse> => {
  if (landId === LandId.ETHEREUM || landId === LandId._ETHEREUM) {
    const addresses = getAddressesByLandId(landId);
    if (!addresses || !addresses.TOKEN_USE) {
      throw "address error";
    }

    return triggerContractByContractName(
      landId,
      signer,
      "ring",
      "transfer(address,uint256,bytes)",
      [addresses.TOKEN_USE, amountMax, pad0xBegin(tokenId)],
      callback
    );
  }

  return triggerContractByContractName(
    landId,
    signer,
    "apostleTokenUseV2",
    "takeTokenUseOffer",
    [pad0xBegin(tokenId), amountMax],
    callback
  );
};

/**
 * Apostle Born Without Element
 * @param signer Ethers signer
 * @param motherTokenId Mother token id
 * @param callback Callback
 */
export const apostleBorn = (
  landId: LandId,
  signer: ethers.Signer,
  motherTokenId: string,
  callback?: CallbackType
): Promise<TransactionResponse> => {
  return triggerContractByContractName(
    landId,
    signer,
    "apostleBaseV3",
    "giveBirth",
    [pad0xBegin(motherTokenId), ethers.utils.hexZeroPad("0x", 20), 0, 0],
    callback
  );
};

/**
 * Apostle Born And Enhance
 * @param signer Ethers signer
 * @param motherTokenId Mother token id
 * @param element Element
 * @param level Level
 * @param levelUnitPrice Level unit price
 * @param callback Callback
 * @returns Void
 */
export const apostleBornAndEnhance = async (
  landId: LandId,
  signer: ethers.Signer,
  motherTokenId: string,
  element: ElementType,
  level: number,
  levelUnitPrice: string,
  callback?: CallbackType
): Promise<TransactionResponse> => {
  const elementAddress = getContractAddressAndAbiByContractName(landId, signer, element).address;
  const cost = ethers.BigNumber.from(level).mul(ethers.BigNumber.from(levelUnitPrice)).toString();

  if (landId === LandId.ETHEREUM || landId === LandId._ETHEREUM) {
    const addresses = getAddressesByLandId(landId);
    if (!addresses || !addresses.APOSTLE_BASE) {
      throw "address error";
    }

    return triggerContractByContractName(
      landId,
      signer,
      element,
      "transfer(address,uint256,bytes)",
      [
        addresses.APOSTLE_BASE,
        cost,
        `${pad0xBegin(motherTokenId)}${ethers.utils.hexZeroPad(ethers.utils.hexlify(level), 32).slice(2)}`,
      ],
      callback
    );
  }

  return triggerContractByContractName(
    landId,
    signer,
    "apostleBaseV3",
    "giveBirth",
    [pad0xBegin(motherTokenId), elementAddress, level, cost],
    callback
  );
};

/**
 * Returns the current price of an auction.
 * @param provider Ethers signer
 * @param tokenId Apostle Token Id
 * @param callback Callback
 * @returns any
 */
export const apostleGetCurrentPriceByTokenId = async (
  landId: LandId,
  provider: ethers.providers.Provider,
  tokenId: string,
  callback?: CallbackType
): Promise<string> => {
  const results = await viewContractByContractName(
    landId,
    provider,
    "apostleClockAuctionV3",
    "getCurrentPriceInToken",
    [pad0xBegin(tokenId)],
    callback
  );
  return (results[0] as ethers.BigNumber).toHexString();
};

/**
 * Returns the current siring price of an auction.
 * @param provider Ethers signer
 * @param tokenId Token Id
 * @param callback Callback
 * @returns any
 */
export const apostleGetCurrentSiringPriceByTokenId = async (
  landId: LandId,
  provider: ethers.providers.Provider,
  tokenId: string,
  callback?: CallbackType
): Promise<string> => {
  const results = await viewContractByContractName(
    landId,
    provider,
    "apostleSiringAuctionV3",
    "getCurrentPriceInToken",
    [pad0xBegin(tokenId)],
    callback
  );
  return (results[0] as ethers.BigNumber).toHexString();
};

/**
 * Sell Apostle asset
 * @param signer Ethers signer
 * @param from
 * @param tokenId - Apostle tokenId
 * @param start - start price
 * @param end - end price
 * @param duration - bid duration time in second
 * @param callback Callback
 * @returns {Promise<void>}
 */
export const apostleAskWithToken = async (
  landId: LandId,
  signer: ethers.Signer,
  from: string,
  tokenId: string,
  startPrice: string,
  endPrice: string,
  duration: string,
  callback?: CallbackType
): Promise<TransactionResponse> => {
  const _from = padLeft(from.slice(2), 64, "0");
  const _start = toHexAndPadLeft(startPrice).slice(2);
  const _end = toHexAndPadLeft(endPrice).slice(2);
  const _duration = toHexAndPadLeft(duration).slice(2);
  const data = `0x${_start}${_end}${_duration}${_from}`;
  const apostleClockAuctionV3 = getContractAddressAndAbiByContractName(landId, signer, "apostleClockAuctionV3");

  return triggerContractByContractName(
    landId,
    signer,
    "objectOwnership",
    "approveAndCall",
    [apostleClockAuctionV3.address, pad0xBegin(tokenId), data],
    callback
  );
};

/**
 * Cancel the auction by apostle token ID
 * @param landId Land Id
 * @param signer Signer
 * @param tokenId apostle token Id
 * @param callback Callback
 * @returns any
 */
export const apostleCancelAskWithToken = (
  landId: LandId,
  signer: ethers.Signer,
  tokenId: string,
  callback?: CallbackType
): Promise<TransactionResponse> => {
  return triggerContractByContractName(
    landId,
    signer,
    "apostleAuction",
    "cancelAuction",
    [pad0xBegin(tokenId)],
    callback
  );
};

/**
 * Renting apostles to work
 * @param landId Land Id
 * @param signer Signer
 * @param resourceAddress Resource contract address
 * @param tokenId Apostle tokenId
 * @param price Hire price
 * @param duration Duration in second
 * @param callback Callback
 */
export const apostleHire = async (
  landId: LandId,
  signer: ethers.Signer,
  resourceAddress: string,
  tokenId: string,
  price: string,
  duration: string,
  callback?: CallbackType
): Promise<TransactionResponse> => {
  const _price = toHexAndPadLeft(price).slice(2);
  const _duration = toHexAndPadLeft(duration).slice(2);
  const _resourceAddress = padLeft(resourceAddress.slice(2), 64, "0");
  const data = `0x${_duration}${_price}${_resourceAddress}`;
  const apostleTokenUseV2Contract = getContractAddressAndAbiByContractName(landId, signer, "apostleTokenUseV2");

  return triggerContractByContractName(
    landId,
    signer,
    "objectOwnership",
    "approveAndCall",
    [apostleTokenUseV2Contract.address, pad0xBegin(tokenId), data],
    callback
  );
};

/**
 * Cancel an apostle on Renting
 * @param landId Land Id
 * @param signer Signer
 * @param tokenId Apostle tokenId
 * @param callback Callback
 * @returns any
 */
export const apostleCancelHire = (
  landId: LandId,
  signer: ethers.Signer,
  tokenId: string,
  callback?: CallbackType
): Promise<TransactionResponse> => {
  return triggerContractByContractName(
    landId,
    signer,
    "apostleTokenUseV2",
    "cancelTokenUseOffer",
    [pad0xBegin(tokenId)],
    callback
  );
};

/**
 * Let apostle go to work
 * @param landId Land Id
 * @param signer Signer
 * @param apostleTokenId Apostle token Id
 * @param landTokenId Land token Id
 * @param elementContractAddress Element contract address
 * @param callback Callback
 * @returns any
 */
export const apostleWork = (
  landId: LandId,
  signer: ethers.Signer,
  apostleTokenId: string,
  landTokenId: string,
  elementContractAddress: string,
  callback?: CallbackType
): Promise<TransactionResponse> => {
  return triggerContractByContractName(
    landId,
    signer,
    "apostleLandResource",
    "startMining",
    [pad0xBegin(apostleTokenId), pad0xBegin(landTokenId), elementContractAddress],
    callback
  );
};

/**
 * Stop apostle mining
 * @param landId Land Id
 * @param signer Signer
 * @param tokenId Apostle tokenId
 * @param callback Callback
 * @returns any
 */
export const apostleStopWork = async (
  landId: LandId,
  signer: ethers.Signer,
  tokenId: string,
  callback?: CallbackType
): Promise<TransactionResponse> => {
  return triggerContractByContractName(
    landId,
    signer,
    "apostleLandResource",
    "stopMining",
    [pad0xBegin(tokenId)],
    callback
  );
};

/**
 * Get apostle breed fee
 * @param landId Land Id
 * @param provider Provider
 * @param callback Callback
 * @returns fee in string
 */
export const apostleGetAutoBreedFee = async (
  landId: LandId,
  provider: ethers.providers.Provider,
  callback?: CallbackType
): Promise<string> => {
  const results = await viewContractByContractName(
    landId,
    provider,
    "settingsRegistry",
    "uintOf",
    ["0x55494e545f4155544f42495254485f4645450000000000000000000000000000"], // UINT_AUTOBIRTH_FEE
    callback
  );
  return (results[0] as ethers.BigNumber).toHexString();
};

/**
 * Receive apostle
 * @param landId Land Id
 * @param signer Singer
 * @param tokenId Apostle tokenId
 * @param callback Callback
 * @returns any
 */
export const apostleClaim = async (
  landId: LandId,
  signer: ethers.Signer,
  tokenId: string,
  callback?: CallbackType
): Promise<TransactionResponse> => {
  return triggerContractByContractName(
    landId,
    signer,
    "apostleAuction",
    "claimApostleAsset",
    [pad0xBegin(tokenId)],
    callback
  );
};

/**
 * Apostle Breed Auction
 * @param landId Land Id
 * @param signer Signer
 * @param from 
 * @param tokenId Apostle tokenId
 * @param startPrice Start price
 * @param endPrice End price
 * @param duration Auction duration time in second
 * @param callback Callback
 * @returns any
 */
export const apostleBreedAsk = async (
  landId: LandId,
  signer: ethers.Signer,
  from: string,
  tokenId: string,
  startPrice: string,
  endPrice: string,
  duration: string,
  callback?: CallbackType
): Promise<TransactionResponse> => {
  const _from = padLeft(from.slice(2), 64, "0");
  const _start = toHexAndPadLeft(startPrice).slice(2);
  const _end = toHexAndPadLeft(endPrice).slice(2);
  const _duration = toHexAndPadLeft(duration).slice(2);
  const data = `0x${_start}${_end}${_duration}${_from}`;
  const apostleSiringAuctionV3Contract = getContractAddressAndAbiByContractName(
    landId,
    signer,
    "apostleSiringAuctionV3"
  );
  return triggerContractByContractName(
    landId,
    signer,
    "objectOwnership",
    "approveAndCall",
    [apostleSiringAuctionV3Contract.address, pad0xBegin(tokenId), data],
    callback
  );
};

/**
 * Cancel Apostle Breed Auction
 * @param landId Land Id
 * @param signer Signer
 * @param tokenId Apostle tokenId
 * @param callback Callback
 * @returns any
 */
export const apostleCancelBreedAsk = async (
  landId: LandId,
  signer: ethers.Signer,
  tokenId: string,
  callback?: CallbackType
): Promise<TransactionResponse> => {
  return triggerContractByContractName(
    landId,
    signer,
    "apostleSiringAuctionV3",
    "cancelAuction",
    [pad0xBegin(tokenId)],
    callback
  );
};

/**
 * Transfer Apsotle
 * @param landId Land Id
 * @param signer Signer
 * @param from Apostle owner
 * @param to Recipient
 * @param tokenId Apostle tokenId
 * @param callback Callback
 * @returns any
 */
export const apostleTransfer = async (
  landId: LandId,
  signer: ethers.Signer,
  from: string,
  to: string,
  tokenId: string,
  callback?: CallbackType
): Promise<TransactionResponse> => {
  return triggerContractByContractName(
    landId,
    signer,
    "objectOwnership",
    "transferFrom",
    [from, to, pad0xBegin(tokenId)],
    callback
  );
};

/**
 * Claim an apostle that expires at work
 * @param landId Land Id
 * @param signer Signer
 * @param tokenId Apostle tokenId
 * @param callback Callback
 * @returns any
 */
export const apostleClaimHire = async (
  landId: LandId,
  signer: ethers.Signer,
  tokenId: string,
  callback?: CallbackType
): Promise<TransactionResponse> => {
  return triggerContractByContractName(
    landId,
    signer,
    "apostleTokenUseV2",
    "removeTokenUseAndActivity",
    [pad0xBegin(tokenId)],
    callback
  );
};