// SPDX-License-Identifier: MIT

///////////////////////////////////////////////////////////////////////////////////
//                                                                               //
//  .......................   KONGO GOLDEN APE SOCIETY   ......................  //
//  ...........................................................................  //
//  ...................................&***&...................................  //
//  ...................................*****...................................  //
//  ...................................*****...................................  //
//  ....................&**&..........*&*&*&*..........&**&....................  //
//  ....................****&.......&****&**&*&.......&****....................  //
//  ....................&***&.....&&*****&*****&......&***&....................  //
//  .....................****&&&&********&********&&&*****.....................  //
//  .....................**********...&**.**...&**********.....................  //
//  .....................**********&..&**.**&..***********.....................  //
//  .....................*********************************.....................  //
//  ....................&******&&&...............&&&******&....................  //
//  ....................*&&.............................&&*....................  //
//  ............................&&&&&&&&&&&&&&&&&&&&...........................  //
//  ......................&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&......................  //
//  ...................&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&...................  //
//  .................&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&................  //
//  ...............&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&..............  //
//  .............&&&&&&...........&&&&&&&&&&&&&&&&..........&&&&&&&............  //
//  ............&&&&&&..&&&****&&...&&&&&&&&&&&&...&&****&&...&&&&&&...........  //
//  ...............&..&***********&..&........&..&***********...&..............  //
//  .................&*************&...&&&&&....**************.................  //
//  ......&...&&..&..***************...&&&&&&..&***************..&..&...&......  //
//  ...&&.....&&&&&..***************..&&&&&&.&..**************&..&&&&.....&&...  //
//  .&&.......&&&&&&..*************...&&&&&&&&..&*************..&&&&&.......&&.  //
//  .&&..&&...&&&&&&&..&*********&...&&&&&&&&&&...&*********&..&&&&&&...&.&..&.  //
//  .&&..&&...&&&&&&&&....&&&&&....&&&&&&&&&&&&&&....&&&&&....&&&&&&&&..&&...&.  //
//  ..&&......&&&&&&&&&&&.......&&&&&&&&&&&&&&&&&&&........&&&&&&&&&&&......&&.  //
//  ...&&.....&&&&&&&&&&&&&&&&&&...................&&&&&&&&&&&&&&&&&&&....&&...  //
//  ..........&&&&&&&&&&&&&&&&&.....................&&&&&&&&&&&&&&&&&..........  //
//  ..........&&&&&&&&&&&&&&&&&...&&&&&&...&&&&&&...&&&&&&&&&&&&&&&&&..........  //
//  ..........&.&&&&&&&&&&&&&&&&..&&&&&&&.&&&&&&&..&&&&&&&&&&&&&&&&&&..........  //
//  ...........&&&&&&&&&&.&&&&&&&&&&&&&&&.&&&&&&&&&&&&&&&.&&&&&&&&&&...........  //
//  ...........&&&&&&&&&..&&&&&&&&&&&&&.&.&&&&&&&&&&&&&&&..&&&&&&&&&...........  //
//  ............&&&&&&&..&&&&&&&&&&&&&&&&.&&&&&&&&&&&&&&&&..&&&&&&&............  //
//  ..............&&.&..&&&&&&&&&&&.............&&&&&&&&&.&..&&&&..............  //
//  ................&...&&&&&&..........&&&..........&&&&&&...&................  //
//  ...................&&&&&...&&&&&&&&&&&&&&&&&&&&&...&&&&&...................  //
//  ...................&&&&&.&&&&&&&&&&&&&&&&&&&&&&&&&.&&&&&...................  //
//  ...................&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&...................  //
//  ....................&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&.&....................  //
//  .....................&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&.....................  //
//  ......................&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&......................  //
//  .........................&&&&&&&&&&&&&&&&&&&&&&&&&&........................  //
//  ............................&&&&&&&&&&&&&&&&&&&............................  //
//  ....................................&&.....................................  //
//  ...........................................................................  //
//  .............................................................by Optryx Labs  //
//                                                                               //
///////////////////////////////////////////////////////////////////////////////////

// File @openzeppelin/contracts/utils/Context.sol@v4.7.3
pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
	function _msgSender() internal view virtual returns (address) {
		return msg.sender;
	}

	function _msgData() internal view virtual returns (bytes calldata) {
		return msg.data;
	}
}

// File @openzeppelin/contracts/utils/introspection/IERC165.sol@v4.7.3
pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
	/**
	 * @dev Returns true if this contract implements the interface defined by
	 * `interfaceId`. See the corresponding
	 * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
	 * to learn more about how these ids are created.
	 *
	 * This function call must use less than 30 000 gas.
	 */
	function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// File @openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol@v4.7.3
pragma solidity ^0.8.0;

/**
 * @dev _Available since v3.1._
 */
interface IERC1155Receiver is IERC165 {
	/**
	 * @dev Handles the receipt of a single ERC1155 token type. This function is
	 * called at the end of a `safeTransferFrom` after the balance has been updated.
	 *
	 * NOTE: To accept the transfer, this must return
	 * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
	 * (i.e. 0xf23a6e61, or its own function selector).
	 *
	 * @param operator The address which initiated the transfer (i.e. msg.sender)
	 * @param from The address which previously owned the token
	 * @param id The ID of the token being transferred
	 * @param value The amount of tokens being transferred
	 * @param data Additional data with no specified format
	 * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
	 */
	function onERC1155Received(
		address operator,
		address from,
		uint256 id,
		uint256 value,
		bytes calldata data
	) external returns (bytes4);

	/**
	 * @dev Handles the receipt of a multiple ERC1155 token types. This function
	 * is called at the end of a `safeBatchTransferFrom` after the balances have
	 * been updated.
	 *
	 * NOTE: To accept the transfer(s), this must return
	 * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
	 * (i.e. 0xbc197c81, or its own function selector).
	 *
	 * @param operator The address which initiated the batch transfer (i.e. msg.sender)
	 * @param from The address which previously owned the token
	 * @param ids An array containing ids of each token being transferred (order and length must match values array)
	 * @param values An array containing amounts of each token being transferred (order and length must match ids array)
	 * @param data Additional data with no specified format
	 * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
	 */
	function onERC1155BatchReceived(
		address operator,
		address from,
		uint256[] calldata ids,
		uint256[] calldata values,
		bytes calldata data
	) external returns (bytes4);
}

// File @openzeppelin/contracts/utils/Strings.sol@v4.7.3
pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library Strings {
	bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
	uint8 private constant _ADDRESS_LENGTH = 20;

	/**
	 * @dev Converts a `uint256` to its ASCII `string` decimal representation.
	 */
	function toString(uint256 value) internal pure returns (string memory) {
		// Inspired by OraclizeAPI's implementation - MIT licence
		// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

		if (value == 0) {
			return "0";
		}
		uint256 temp = value;
		uint256 digits;
		while (temp != 0) {
			digits++;
			temp /= 10;
		}
		bytes memory buffer = new bytes(digits);
		while (value != 0) {
			digits -= 1;
			buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
			value /= 10;
		}
		return string(buffer);
	}
}

// File @openzeppelin/contracts/utils/Address.sol@v4.7.3
pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
	/**
	 * @dev Returns true if `account` is a contract.
	 *
	 * [IMPORTANT]
	 * ====
	 * It is unsafe to assume that an address for which this function returns
	 * false is an externally-owned account (EOA) and not a contract.
	 *
	 * Among others, `isContract` will return false for the following
	 * types of addresses:
	 *
	 *  - an externally-owned account
	 *  - a contract in construction
	 *  - an address where a contract will be created
	 *  - an address where a contract lived, but was destroyed
	 * ====
	 *
	 * [IMPORTANT]
	 * ====
	 * You shouldn't rely on `isContract` to protect against flash loan attacks!
	 *
	 * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
	 * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
	 * constructor.
	 * ====
	 */
	function isContract(address account) internal view returns (bool) {
		// This method relies on extcodesize/address.code.length, which returns 0
		// for contracts in construction, since the code is only stored at the end
		// of the constructor execution.

		return account.code.length > 0;
	}

	/**
	 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
	 * `recipient`, forwarding all available gas and reverting on errors.
	 *
	 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
	 * of certain opcodes, possibly making contracts go over the 2300 gas limit
	 * imposed by `transfer`, making them unable to receive funds via
	 * `transfer`. {sendValue} removes this limitation.
	 *
	 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
	 *
	 * IMPORTANT: because control is transferred to `recipient`, care must be
	 * taken to not create reentrancy vulnerabilities. Consider using
	 * {ReentrancyGuard} or the
	 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
	 */
	function sendValue(address payable recipient, uint256 amount) internal {
		require(
			address(this).balance >= amount,
			"Address: insufficient balance"
		);

		(bool success, ) = recipient.call{value: amount}("");
		require(
			success,
			"Address: unable to send value, recipient may have reverted"
		);
	}

	/**
	 * @dev Performs a Solidity function call using a low level `call`. A
	 * plain `call` is an unsafe replacement for a function call: use this
	 * function instead.
	 *
	 * If `target` reverts with a revert reason, it is bubbled up by this
	 * function (like regular Solidity function calls).
	 *
	 * Returns the raw returned data. To convert to the expected return value,
	 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
	 *
	 * Requirements:
	 *
	 * - `target` must be a contract.
	 * - calling `target` with `data` must not revert.
	 *
	 * _Available since v3.1._
	 */
	function functionCall(address target, bytes memory data)
		internal
		returns (bytes memory)
	{
		return functionCall(target, data, "Address: low-level call failed");
	}

	/**
	 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
	 * `errorMessage` as a fallback revert reason when `target` reverts.
	 *
	 * _Available since v3.1._
	 */
	function functionCall(
		address target,
		bytes memory data,
		string memory errorMessage
	) internal returns (bytes memory) {
		return functionCallWithValue(target, data, 0, errorMessage);
	}

	/**
	 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
	 * but also transferring `value` wei to `target`.
	 *
	 * Requirements:
	 *
	 * - the calling contract must have an ETH balance of at least `value`.
	 * - the called Solidity function must be `payable`.
	 *
	 * _Available since v3.1._
	 */
	function functionCallWithValue(
		address target,
		bytes memory data,
		uint256 value
	) internal returns (bytes memory) {
		return
			functionCallWithValue(
				target,
				data,
				value,
				"Address: low-level call with value failed"
			);
	}

	/**
	 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
	 * with `errorMessage` as a fallback revert reason when `target` reverts.
	 *
	 * _Available since v3.1._
	 */
	function functionCallWithValue(
		address target,
		bytes memory data,
		uint256 value,
		string memory errorMessage
	) internal returns (bytes memory) {
		require(
			address(this).balance >= value,
			"Address: insufficient balance for call"
		);
		require(isContract(target), "Address: call to non-contract");

		(bool success, bytes memory returndata) = target.call{value: value}(
			data
		);
		return verifyCallResult(success, returndata, errorMessage);
	}

	/**
	 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
	 * but performing a static call.
	 *
	 * _Available since v3.3._
	 */
	function functionStaticCall(address target, bytes memory data)
		internal
		view
		returns (bytes memory)
	{
		return
			functionStaticCall(
				target,
				data,
				"Address: low-level static call failed"
			);
	}

	/**
	 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
	 * but performing a static call.
	 *
	 * _Available since v3.3._
	 */
	function functionStaticCall(
		address target,
		bytes memory data,
		string memory errorMessage
	) internal view returns (bytes memory) {
		require(isContract(target), "Address: static call to non-contract");

		(bool success, bytes memory returndata) = target.staticcall(data);
		return verifyCallResult(success, returndata, errorMessage);
	}

	/**
	 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
	 * but performing a delegate call.
	 *
	 * _Available since v3.4._
	 */
	function functionDelegateCall(address target, bytes memory data)
		internal
		returns (bytes memory)
	{
		return
			functionDelegateCall(
				target,
				data,
				"Address: low-level delegate call failed"
			);
	}

	/**
	 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
	 * but performing a delegate call.
	 *
	 * _Available since v3.4._
	 */
	function functionDelegateCall(
		address target,
		bytes memory data,
		string memory errorMessage
	) internal returns (bytes memory) {
		require(isContract(target), "Address: delegate call to non-contract");

		(bool success, bytes memory returndata) = target.delegatecall(data);
		return verifyCallResult(success, returndata, errorMessage);
	}

	/**
	 * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
	 * revert reason using the provided one.
	 *
	 * _Available since v4.3._
	 */
	function verifyCallResult(
		bool success,
		bytes memory returndata,
		string memory errorMessage
	) internal pure returns (bytes memory) {
		if (success) {
			return returndata;
		} else {
			// Look for revert reason and bubble it up if present
			if (returndata.length > 0) {
				// The easiest way to bubble the revert reason is using memory via assembly
				/// @solidity memory-safe-assembly
				assembly {
					let returndata_size := mload(returndata)
					revert(add(32, returndata), returndata_size)
				}
			} else {
				revert(errorMessage);
			}
		}
	}
}

// File @openzeppelin/contracts/token/ERC1155/IERC1155.sol@v4.7.3
pragma solidity ^0.8.0;

/**
 * @dev Required interface of an ERC1155 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-1155[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155 is IERC165 {
	/**
	 * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
	 */
	event TransferSingle(
		address indexed operator,
		address indexed from,
		address indexed to,
		uint256 id,
		uint256 value
	);

	/**
	 * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
	 * transfers.
	 */
	event TransferBatch(
		address indexed operator,
		address indexed from,
		address indexed to,
		uint256[] ids,
		uint256[] values
	);

	/**
	 * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
	 * `approved`.
	 */
	event ApprovalForAll(
		address indexed account,
		address indexed operator,
		bool approved
	);

	/**
	 * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
	 *
	 * If an {URI} event was emitted for `id`, the standard
	 * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
	 * returned by {IERC1155MetadataURI-uri}.
	 */
	event URI(string value, uint256 indexed id);

	/**
	 * @dev Returns the amount of tokens of token type `id` owned by `account`.
	 *
	 * Requirements:
	 *
	 * - `account` cannot be the zero address.
	 */
	function balanceOf(address account, uint256 id)
		external
		view
		returns (uint256);

	/**
	 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
	 *
	 * Requirements:
	 *
	 * - `accounts` and `ids` must have the same length.
	 */
	function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
		external
		view
		returns (uint256[] memory);

	/**
	 * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
	 *
	 * Emits an {ApprovalForAll} event.
	 *
	 * Requirements:
	 *
	 * - `operator` cannot be the caller.
	 */
	function setApprovalForAll(address operator, bool approved) external;

	/**
	 * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
	 *
	 * See {setApprovalForAll}.
	 */
	function isApprovedForAll(address account, address operator)
		external
		view
		returns (bool);

	/**
	 * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
	 *
	 * Emits a {TransferSingle} event.
	 *
	 * Requirements:
	 *
	 * - `to` cannot be the zero address.
	 * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
	 * - `from` must have a balance of tokens of type `id` of at least `amount`.
	 * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
	 * acceptance magic value.
	 */
	function safeTransferFrom(
		address from,
		address to,
		uint256 id,
		uint256 amount,
		bytes calldata data
	) external;

	/**
	 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
	 *
	 * Emits a {TransferBatch} event.
	 *
	 * Requirements:
	 *
	 * - `ids` and `amounts` must have the same length.
	 * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
	 * acceptance magic value.
	 */
	function safeBatchTransferFrom(
		address from,
		address to,
		uint256[] calldata ids,
		uint256[] calldata amounts,
		bytes calldata data
	) external;
}

// File @openzeppelin/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol@v4.7.3
pragma solidity ^0.8.0;

/**
 * @dev Interface of the optional ERC1155MetadataExtension interface, as defined
 * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155MetadataURI is IERC1155 {
	/**
	 * @dev Returns the URI for token type `id`.
	 *
	 * If the `\{id\}` substring is present in the URI, it must be replaced by
	 * clients with the actual token type ID.
	 */
	function uri(uint256 id) external view returns (string memory);
}

// File @openzeppelin/contracts/utils/introspection/ERC165.sol@v4.7.3
pragma solidity ^0.8.0;

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
	/**
	 * @dev See {IERC165-supportsInterface}.
	 */
	function supportsInterface(bytes4 interfaceId)
		public
		view
		virtual
		override
		returns (bool)
	{
		return interfaceId == type(IERC165).interfaceId;
	}
}

// File @openzeppelin/contracts/access/Ownable.sol@v4.7.3
pragma solidity ^0.8.0;

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
	address private _owner;

	event OwnershipTransferred(
		address indexed previousOwner,
		address indexed newOwner
	);

	/**
	 * @dev Initializes the contract setting the deployer as the initial owner.
	 */
	constructor() {
		_transferOwnership(_msgSender());
	}

	/**
	 * @dev Throws if called by any account other than the owner.
	 */
	modifier onlyOwner() {
		_checkOwner();
		_;
	}

	/**
	 * @dev Returns the address of the current owner.
	 */
	function owner() public view virtual returns (address) {
		return _owner;
	}

	/**
	 * @dev Throws if the sender is not the owner.
	 */
	function _checkOwner() internal view virtual {
		require(owner() == _msgSender(), "Ownable: caller is not the owner");
	}

	/**
	 * @dev Leaves the contract without owner. It will not be possible to call
	 * `onlyOwner` functions anymore. Can only be called by the current owner.
	 *
	 * NOTE: Renouncing ownership will leave the contract without an owner,
	 * thereby removing any functionality that is only available to the owner.
	 */
	function renounceOwnership() public virtual onlyOwner {
		_transferOwnership(address(0));
	}

	/**
	 * @dev Transfers ownership of the contract to a new account (`newOwner`).
	 * Can only be called by the current owner.
	 */
	function transferOwnership(address newOwner) public virtual onlyOwner {
		require(
			newOwner != address(0),
			"Ownable: new owner is the zero address"
		);
		_transferOwnership(newOwner);
	}

	/**
	 * @dev Transfers ownership of the contract to a new account (`newOwner`).
	 * Internal function without access restriction.
	 */
	function _transferOwnership(address newOwner) internal virtual {
		address oldOwner = _owner;
		_owner = newOwner;
		emit OwnershipTransferred(oldOwner, newOwner);
	}
}

pragma solidity ^0.8.0;

/**
 * @dev Implementation of the basic standard multi-token.
 * See https://eips.ethereum.org/EIPS/eip-1155
 * Originally based on code by Enjin: https://github.com/enjin/erc-1155
 *
 * _Available since v3.1._
 */
contract KGASBaseERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
	using Address for address;
	using Strings for uint256;

	// Mapping from token ID to account balances
	mapping(uint256 => mapping(address => uint256)) private _balances;

	// Mapping from account to operator approvals
	mapping(address => mapping(address => bool)) private _operatorApprovals;

	// Optional base URI
	string private _baseURI = "";

	// Optional mapping for token URIs
	mapping(uint256 => string) private _tokenURIs;

	// Current number of ERC1155 minted tokens
	uint256 public _tokenCount;

	constructor() {
		_tokenCount = 0;
	}

	/**
	 * @dev See {IERC165-supportsInterface}.
	 */
	function supportsInterface(bytes4 interfaceId)
		public
		view
		virtual
		override(ERC165, IERC165)
		returns (bool)
	{
		return
			interfaceId == type(IERC1155).interfaceId ||
			interfaceId == type(IERC1155MetadataURI).interfaceId ||
			super.supportsInterface(interfaceId);
	}

	/**
	 * @dev See {IERC1155MetadataURI-uri}.
	 *
	 * This implementation returns the concatenation of the `_baseURI`
	 * and the token-specific uri if the latter is set
	 *
	 * This enables the following behaviors:
	 *
	 * - if `_tokenURIs[tokenId]` is set, then the result is the concatenation
	 *   of `_baseURI` and `_tokenURIs[tokenId]` (keep in mind that `_baseURI`
	 *   is empty per default);
	 *
	 * - if `_tokenURIs[tokenId]` is NOT set then we fallback to `super.uri()`
	 *   which in most cases will contain `ERC1155._uri`;
	 *
	 * - if `_tokenURIs[tokenId]` is NOT set, and if the parents do not have a
	 *   uri value set, then the result is empty.
	 */
	function uri(uint256 tokenId)
		public
		view
		virtual
		override
		returns (string memory)
	{
		string memory tokenURI = _tokenURIs[tokenId];

		// If token URI is set, concatenate base URI and tokenURI (via abi.encodePacked).
		return
			bytes(tokenURI).length > 0
				? string(abi.encodePacked(_baseURI, tokenURI))
				: "";
	}

	/**
	 * @dev Sets `tokenURI` as the tokenURI of `tokenId`.
	 */
	function _setURI(uint256 tokenId, string memory tokenURI) internal virtual {
		_tokenURIs[tokenId] = tokenURI;
		emit URI(uri(tokenId), tokenId);
	}

	/**
	 * @dev Sets `baseURI` as the `_baseURI` for all tokens
	 */
	function _setBaseURI(string memory baseURI) internal virtual {
		_baseURI = baseURI;
	}

	/**
	 * @dev See {IERC1155-balanceOf}.
	 *
	 * Requirements:
	 *
	 * - `account` cannot be the zero address.
	 */
	function balanceOf(address account, uint256 id)
		public
		view
		virtual
		override
		returns (uint256)
	{
		require(
			account != address(0),
			"ERC1155: address zero is not a valid owner"
		);
		return _balances[id][account];
	}

	/**
	 * @dev See {IERC1155-balanceOfBatch}.
	 *
	 * Requirements:
	 *
	 * - `accounts` and `ids` must have the same length.
	 */
	function balanceOfBatch(address[] memory accounts, uint256[] memory ids)
		public
		view
		virtual
		override
		returns (uint256[] memory)
	{
		require(
			accounts.length == ids.length,
			"ERC1155: accounts and ids length mismatch"
		);

		uint256[] memory batchBalances = new uint256[](accounts.length);

		for (uint256 i = 0; i < accounts.length; ++i) {
			batchBalances[i] = balanceOf(accounts[i], ids[i]);
		}

		return batchBalances;
	}

	/**
	 * @dev See {IERC1155-setApprovalForAll}.
	 */
	function setApprovalForAll(address operator, bool approved)
		public
		virtual
		override
	{
		_setApprovalForAll(_msgSender(), operator, approved);
	}

	/**
	 * @dev See {IERC1155-isApprovedForAll}.
	 */
	function isApprovedForAll(address account, address operator)
		public
		view
		virtual
		override
		returns (bool)
	{
		return _operatorApprovals[account][operator];
	}

	/**
	 * @dev See {IERC1155-safeTransferFrom}.
	 */
	function safeTransferFrom(
		address from,
		address to,
		uint256 id,
		uint256 amount,
		bytes memory data
	) public virtual override {
		require(
			from == _msgSender() || isApprovedForAll(from, _msgSender()),
			"ERC1155: caller is not token owner nor approved"
		);
		_safeTransferFrom(from, to, id, amount, data);
	}

	/**
	 * @dev See {IERC1155-safeBatchTransferFrom}.
	 */
	function safeBatchTransferFrom(
		address from,
		address to,
		uint256[] memory ids,
		uint256[] memory amounts,
		bytes memory data
	) public virtual override {
		require(
			from == _msgSender() || isApprovedForAll(from, _msgSender()),
			"ERC1155: caller is not token owner nor approved"
		);
		_safeBatchTransferFrom(from, to, ids, amounts, data);
	}

	/**
	 * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
	 *
	 * Emits a {TransferSingle} event.
	 *
	 * Requirements:
	 *
	 * - `to` cannot be the zero address.
	 * - `from` must have a balance of tokens of type `id` of at least `amount`.
	 * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
	 * acceptance magic value.
	 */
	function _safeTransferFrom(
		address from,
		address to,
		uint256 id,
		uint256 amount,
		bytes memory data
	) internal virtual {
		require(to != address(0), "ERC1155: transfer to the zero address");

		address operator = _msgSender();

		uint256 fromBalance = _balances[id][from];
		require(
			fromBalance >= amount,
			"ERC1155: insufficient balance for transfer"
		);
		unchecked {
			_balances[id][from] = fromBalance - amount;
		}
		_balances[id][to] += amount;

		emit TransferSingle(operator, from, to, id, amount);

		_doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
	}

	/**
	 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
	 *
	 * Emits a {TransferBatch} event.
	 *
	 * Requirements:
	 *
	 * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
	 * acceptance magic value.
	 */
	function _safeBatchTransferFrom(
		address from,
		address to,
		uint256[] memory ids,
		uint256[] memory amounts,
		bytes memory data
	) internal virtual {
		require(
			ids.length == amounts.length,
			"ERC1155: ids and amounts length mismatch"
		);
		require(to != address(0), "ERC1155: transfer to the zero address");

		address operator = _msgSender();

		for (uint256 i = 0; i < ids.length; ++i) {
			uint256 id = ids[i];
			uint256 amount = amounts[i];

			uint256 fromBalance = _balances[id][from];
			require(
				fromBalance >= amount,
				"ERC1155: insufficient balance for transfer"
			);
			unchecked {
				_balances[id][from] = fromBalance - amount;
			}
			_balances[id][to] += amount;
		}

		emit TransferBatch(operator, from, to, ids, amounts);

		_doSafeBatchTransferAcceptanceCheck(
			operator,
			from,
			to,
			ids,
			amounts,
			data
		);
	}

	/**
	 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
	 *
	 * Emits a {TransferBatch} event.
	 *
	 * Requirements:
	 *
	 * - `ids` and `amounts` must have the same length.
	 * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
	 * acceptance magic value.
	 */
	function _mintBatch(
		address to,
		uint256[] memory amounts,
		string memory cid,
		bytes memory data
	) internal virtual {
		require(to != address(0), "ERC1155: mint to the zero address");

		uint256 n = amounts.length;
		uint256[] memory ids = new uint256[](n);

		for (uint256 i = 0; i < n; i++) {
			uint256 id = _tokenCount + i;
			ids[i] = id;
			_balances[id][to] += amounts[i];
			_setURI(
				id,
				string(abi.encodePacked(cid, "/", Strings.toString(id)))
			);
		}

		_tokenCount = _tokenCount + n;

		address operator = _msgSender();

		emit TransferBatch(operator, address(0), to, ids, amounts);

		_doSafeBatchTransferAcceptanceCheck(
			operator,
			address(0),
			to,
			ids,
			amounts,
			data
		);
	}

	/**
	 * @dev Approve `operator` to operate on all of `owner` tokens
	 *
	 * Emits an {ApprovalForAll} event.
	 */
	function _setApprovalForAll(
		address owner,
		address operator,
		bool approved
	) internal virtual {
		require(owner != operator, "ERC1155: setting approval status for self");
		_operatorApprovals[owner][operator] = approved;
		emit ApprovalForAll(owner, operator, approved);
	}

	function _doSafeTransferAcceptanceCheck(
		address operator,
		address from,
		address to,
		uint256 id,
		uint256 amount,
		bytes memory data
	) private {
		if (to.isContract()) {
			try
				IERC1155Receiver(to).onERC1155Received(
					operator,
					from,
					id,
					amount,
					data
				)
			returns (bytes4 response) {
				if (response != IERC1155Receiver.onERC1155Received.selector) {
					revert("ERC1155: ERC1155Receiver rejected tokens");
				}
			} catch Error(string memory reason) {
				revert(reason);
			} catch {
				revert("ERC1155: transfer to non ERC1155Receiver implementer");
			}
		}
	}

	function _doSafeBatchTransferAcceptanceCheck(
		address operator,
		address from,
		address to,
		uint256[] memory ids,
		uint256[] memory amounts,
		bytes memory data
	) private {
		if (to.isContract()) {
			try
				IERC1155Receiver(to).onERC1155BatchReceived(
					operator,
					from,
					ids,
					amounts,
					data
				)
			returns (bytes4 response) {
				if (
					response != IERC1155Receiver.onERC1155BatchReceived.selector
				) {
					revert("ERC1155: ERC1155Receiver rejected tokens");
				}
			} catch Error(string memory reason) {
				revert(reason);
			} catch {
				revert("ERC1155: transfer to non ERC1155Receiver implementer");
			}
		}
	}
}

pragma solidity ^0.8.16;

contract KongoGoldenApeSociety is KGASBaseERC1155, Ownable {
	uint256 public MAX_SUPPLY = 500;

	constructor() KGASBaseERC1155() {
		_setBaseURI("ipfs://");
	}

	function name() public pure returns (string memory) {
		return "Kongo Golden Ape Society";
	}

	function symbol() public pure returns (string memory) {
		return "KGAS";
	}

	function setBaseUri(string memory newBaseUri) public onlyOwner {
		_setBaseURI(newBaseUri);
	}

	function setUri(uint256 id, string memory cid) public onlyOwner {
		_setURI(id, string.concat(cid, "/", Strings.toString(id)));
	}

	function setUriBatch(uint256[] memory ids, string memory cid)
		public
		onlyOwner
	{
		for (uint256 i = 0; i < ids.length; i++) {
			uint256 id = ids[i];
			_setURI(id, string.concat(cid, "/", Strings.toString(id)));
		}
	}

	function ownerMintBatch(uint256[] memory amounts, string memory cid)
		public
		onlyOwner
	{
		require(
			_tokenCount + amounts.length <= MAX_SUPPLY,
			"Cannot mint. It would exceed max supply"
		);

		_mintBatch(msg.sender, amounts, cid, "");
	}
}