var __awaiter = this && this.__awaiter || function (thisArg, _arguments, P, generator) {
  function adopt(value) {
    return value instanceof P ? value : new P(function (resolve) {
      resolve(value);
    });
  }

  return new (P || (P = Promise))(function (resolve, reject) {
    function fulfilled(value) {
      try {
        step(generator.next(value));
      } catch (e) {
        reject(e);
      }
    }

    function rejected(value) {
      try {
        step(generator["throw"](value));
      } catch (e) {
        reject(e);
      }
    }

    function step(result) {
      result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
    }

    step((generator = generator.apply(thisArg, _arguments || [])).next());
  });
}; // For ESM compatibility


import pkg from 'js-sha3';
const {
  keccak256,
  sha3_256
} = pkg;
import { hexToBytes, intToHex, makeHexString, assertHexString } from "./hex.js";
import { assertBytes } from "./bytes.js";
const ETH_ADDR_BYTES_LENGTH = 20;
const ETH_ADDR_HEX_LENGTH = 40;
export function makeEthAddress(address) {
  if (typeof address === 'string') {
    const hexAddr = makeHexString(address, ETH_ADDR_HEX_LENGTH);
    const ownerBytes = hexToBytes(hexAddr);
    assertBytes(ownerBytes, ETH_ADDR_BYTES_LENGTH);
    return ownerBytes;
  } else if (address instanceof Uint8Array) {
    assertBytes(address, ETH_ADDR_BYTES_LENGTH);
    return address;
  }

  throw new TypeError('Invalid EthAddress');
}
export function makeHexEthAddress(address) {
  try {
    return makeHexString(address, ETH_ADDR_HEX_LENGTH);
  } catch (e) {
    if (e instanceof TypeError) {
      e.message = `Invalid HexEthAddress: ${e.message}`;
    }

    throw e;
  }
}
/**
 * Check if this is all caps or small caps eth address (=address without checksum)
 *
 * @param address Ethereum address as hex string
 */

function isEthAddrCaseIns(address) {
  // Check it's string, all small caps or all all caps hex and 40 chars long without the `0x` prefix
  return typeof address === 'string' && (/^(0x|0X)?[0-9a-f]{40}$/.test(address) || /^(0x|0X)?[0-9A-F]{40}$/.test(address));
}
/**
 * Check if this is checksummed ethereum address
 *
 * @param address Ethereum address as hex string
 */


function isValidChecksummedEthAddress(address) {
  try {
    // Check for valid case insensitive hex type string, 40 chars
    const addr = makeHexString(address, ETH_ADDR_HEX_LENGTH); // Check the checksum

    const addressHash = keccak256(addr.toLowerCase());

    for (let i = 0; i < 40; i += 1) {
      // the nth letter should be uppercase if the nth digit of casemap is 1
      if (parseInt(addressHash[i], 16) > 7 && addr[i].toUpperCase() !== addr[i] || parseInt(addressHash[i], 16) <= 7 && addr[i].toLowerCase() !== addr[i]) {
        return false;
      }
    }

    return true;
  } catch (e) {
    if (e instanceof TypeError) {
      return false;
    }

    throw e;
  }
}
/**
 * Check if is valid ethereum address
 *
 * Pretty much typed version from web3js
 * https://github.com/ChainSafe/web3.js/blob/1.x/packages/web3-utils/src/utils.js
 *
 * @param address  Ethereum address as hex string
 *
 * @return True if is valid eth address
 */


export function isHexEthAddress(address) {
  return isEthAddrCaseIns(address) || isValidChecksummedEthAddress(address);
}
/**
 * Convert big-endian hex or number to little-endian.
 * Note: Before conversion it is automatically padded to even length hexstring
 *
 * @param bigEndian Big-endian hex string or number to convert
 * @param pad       Length to which the string should be padded before conversion (defaul: 2)
 *
 * @return little-endian encoded hexstring
 */

export function toLittleEndian(bigEndian, pad = 2) {
  var _a;

  if (!(Number.isInteger(pad) && pad >= 2 && pad % 2 === 0)) {
    throw new TypeError('minimal padding for conversion needs to be positive even integer');
  }

  let hexRep;
  if (typeof bigEndian === 'string') hexRep = makeHexString(bigEndian);else if (typeof bigEndian === 'number') hexRep = intToHex(bigEndian);else throw new TypeError('incorrect input type');
  hexRep = hexRep.padStart(pad, '0'); // Extend to an even length hexstring

  if (hexRep.length % 2 !== 0) hexRep = hexRep.padStart(hexRep.length + 1, '0'); // Match all two pairs in the hexstring, reverse the pairs and join it again

  const littleEndian = (_a = hexRep.match(/../g)) === null || _a === void 0 ? void 0 : _a.reverse().join('');
  if (littleEndian) return littleEndian;
  throw new Error('failed to convert');
}
/**
 * Convert little-endian hex or number to big-endian
 * Note: Before conversion it is automatically padded to even length hexstring
 *
 * @param littleEndian Little-endian hex string or number to convert
 * @param pad          Length to which the string should be padded before conversion (defaul: 2)
 *
 * @return big-endian encoded hexstring
 */

export function fromLittleEndian(littleEndian, pad = 2) {
  // It's a reversible function
  return toLittleEndian(littleEndian, pad);
}

function assertEthAddress(ethAddress) {
  if (!isHexEthAddress(ethAddress)) throw new TypeError('invalid ETH address');
}

function assertSwarmNetworkId(networkId) {
  if (Number.isInteger(networkId && networkId > 0 && networkId < Number.MAX_SAFE_INTEGER)) {
    throw new TypeError('swarm network id must be positive integer');
  }
}
/**
 * Get swarm overlay address from public ethereum address and swarm network id
 *
 * @param ethAddress  Public ethereum address
 * @param networkId   Swarm network id
 *
 * @return Swarm overlay address
 */


export function ethToSwarmAddress(ethAddress, networkId = 1) {
  assertEthAddress(ethAddress);
  assertSwarmNetworkId(networkId);
  const hex = `${makeHexString(ethAddress)}${toLittleEndian(networkId, 16)}`;
  assertHexString(hex);
  const overlayAddress = sha3_256(hexToBytes(hex));
  return overlayAddress;
}
/**
 * Function that takes Ethereum EIP-1193 compatible provider and create an Signer instance that
 * uses `personal_sign` method to sign requested data.
 *
 * @param provider Injected web3 provider like window.ethereum or other compatible with EIP-1193
 * @param ethAddress Optional address of the account which the data should be signed with. If not specified `eth_requestAccounts` request is used to get the account address.
 */

export function makeEthereumWalletSigner(provider, ethAddress) {
  return __awaiter(this, void 0, void 0, function* () {
    let executorFnc;

    if (typeof provider !== 'object' || provider === null) {
      throw new TypeError('We need JsonRPC provider object!');
    }

    if (provider.request) {
      executorFnc = provider.request;
    } else if (provider.sendAsync) {
      executorFnc = provider.sendAsync;
    } else {
      throw new Error('Incompatible interface of given provider!');
    }

    if (!ethAddress) {
      ethAddress = (yield executorFnc({
        method: 'eth_requestAccounts'
      }))[0];
    }

    const bytesEthAddress = makeEthAddress(ethAddress);
    const hexEthAddress = makeHexEthAddress(ethAddress);
    return {
      address: bytesEthAddress,
      sign: data => __awaiter(this, void 0, void 0, function* () {
        const result = yield executorFnc({
          jsonrpc: '2.0',
          method: 'personal_sign',
          params: ['0x' + data.hex(), '0x' + hexEthAddress]
        });
        return result;
      })
    };
  });
}