diff --git a/contracts/community/Administration.sol b/contracts/community/Administration.sol index 2a2cfc0..a38a569 100644 --- a/contracts/community/Administration.sol +++ b/contracts/community/Administration.sol @@ -1,10 +1,12 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.9; +pragma solidity ^0.8.17; -abstract contract Administration { +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +abstract contract Administration is Initializable { mapping(address => bool) private _admins; - constructor() { + function _initializeAdministration() internal virtual initializer { _admins[msg.sender] = true; } @@ -35,4 +37,4 @@ abstract contract Administration { function deleteAdmin(address _deleteAdmin) external onlyAdmins { _admins[_deleteAdmin] = false; } -} \ No newline at end of file +} diff --git a/contracts/community/InteractCommunityToken.sol b/contracts/community/InteractCommunityToken.sol new file mode 100644 index 0000000..f30e099 --- /dev/null +++ b/contracts/community/InteractCommunityToken.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import "./Administration.sol"; +import "./MintManager.sol"; +import "./interfaces/IHenkakuToken.sol"; + +abstract contract InteractCommunityToken is Administration, MintManager { + address public communityToken; + + function _initializeInteractCommunityToken(address _communityToken) internal virtual initializer { + communityToken = _communityToken; + } + + function setCommunityToken(address _communityToken) external onlyAdmins { + communityToken = _communityToken; + } + + function _batchTransferCommunityToken( + uint256 totalAmount, + uint256[] memory _amounts, + address[] memory _to + ) internal { + _checkCommunityTokenBalance(totalAmount); + + uint256[] memory amounts = _amounts; + uint256 amountsLength = amounts.length; + require(amountsLength == _to.length, "amounts and to length mismatch"); + + for (uint256 i = 0; i < amountsLength; ) { + bool sent = IHenkakuToken(communityToken).transferFrom(msg.sender, _to[i], amounts[i]); + require(sent, "Ticket: ERC20 Token transfer failed"); + + unchecked { + ++i; + } + } + } + + function _checkCommunityTokenBalance(uint256 _requiredAmount) internal view { + require( + IHenkakuToken(communityToken).balanceOf(msg.sender) >= _requiredAmount, + "Ticket: Insufficient ERC20 token" + ); + } +} diff --git a/contracts/community/InteractHenkakuToken.sol b/contracts/community/InteractHenkakuToken.sol deleted file mode 100644 index bdd5a73..0000000 --- a/contracts/community/InteractHenkakuToken.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.9; - -import "./Administration.sol"; -import "./MintManager.sol"; -import "./interfaces/IHenkakuToken.sol"; - -abstract contract InteractHenakuToken is Administration, MintManager { - address public henkakuV2; - - constructor(address _henkakuV2) { - henkakuV2 = _henkakuV2; - } - - function transferHenkakuV2(uint256 _amount, address _to) internal { - _checkHenkakuV2Balance(_amount); - bool sent = IHenkakuToken(henkakuV2).transferFrom(msg.sender, _to, _amount); - require(sent, "Ticket: ERC20 token transfer failed"); - } - - function batchTransferHenkakuV2(uint256 totalPrice, uint256[] memory _amounts, address[] memory _to) internal { - _checkHenkakuV2Balance(totalPrice); - - uint256[] memory amounts = _amounts; - uint256 amountsLength = amounts.length; - require(amountsLength == _to.length, "amounts and to length mismatch"); - - for (uint256 i = 0; i < amountsLength; ) { - bool sent = IHenkakuToken(henkakuV2).transferFrom(msg.sender, _to[i], amounts[i]); - require(sent, "Ticket: ERC20 Token transfer failed"); - - unchecked { - ++i; - } - } - } - - function _checkHenkakuV2Balance(uint256 _requiredAmount) internal view { - require(IHenkakuToken(henkakuV2).balanceOf(msg.sender) >= _requiredAmount, "Ticket: Insufficient ERC20 token"); - } -} diff --git a/contracts/community/MintManager.sol b/contracts/community/MintManager.sol index 53f98bf..ea63dc4 100644 --- a/contracts/community/MintManager.sol +++ b/contracts/community/MintManager.sol @@ -1,13 +1,11 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.9; +pragma solidity ^0.8.17; import "./Administration.sol"; abstract contract MintManager is Administration { bool public mintable; - constructor() {} - function switchMintable() external onlyAdmins { mintable = !mintable; } diff --git a/contracts/community/Ticket.sol b/contracts/community/Ticket.sol index d3d001e..5111525 100644 --- a/contracts/community/Ticket.sol +++ b/contracts/community/Ticket.sol @@ -1,13 +1,21 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.9; +pragma solidity ^0.8.17; -import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155SupplyUpgradeable.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "./Administration.sol"; -import "./InteractHenkakuToken.sol"; +import "./InteractCommunityToken.sol"; import "./MintManager.sol"; -contract Ticket is ERC1155, ERC1155Supply, Administration, MintManager, InteractHenakuToken { +contract Ticket is + Initializable, + ERC1155Upgradeable, + ERC1155SupplyUpgradeable, + Administration, + MintManager, + InteractCommunityToken +{ //@dev count up tokenId from 0 using Counters for Counters.Counter; Counters.Counter private _tokenIds; @@ -35,9 +43,15 @@ contract Ticket is ERC1155, ERC1155Supply, Administration, MintManager, Interact error InvalidParams(string); /** - * @param uri: metadata uri * @param creator: creator's wallet address + * @param open_blockTimestamp: open block timestamp for sales period + * @param close_blockTimestamp: close block timestamp for sales period * @param maxSupply: max supply number of token + * @param id: event id + * @param price: price of ticket + * @param uri: metadata uri + * @param sharesAmounts: shares amount of shareholders + * @param shareholdersAddresses: shareholders' wallet addresses */ struct TicketInfo { address creator; @@ -51,22 +65,21 @@ contract Ticket is ERC1155, ERC1155Supply, Administration, MintManager, Interact address[] shareholdersAddresses; } - TicketInfo[] private registeredTickets; + TicketInfo[] private _registeredTickets; - constructor( - string memory _name, - string memory _symbol, - address _communityToken - ) ERC1155("") MintManager() InteractHenakuToken(_communityToken) { + function initialize(string memory _name, string memory _symbol, address _communityToken) public initializer { name = _name; symbol = _symbol; - registeredTickets.push(TicketInfo(address(0), 0, 0, 0, 0, 0, "", new uint256[](0), new address[](0))); + _registeredTickets.push(TicketInfo(address(0), 0, 0, 0, 0, 0, "", new uint256[](0), new address[](0))); _tokenIds.increment(); + + super._initializeAdministration(); + super._initializeInteractCommunityToken(_communityToken); } - modifier onlyHenkakuHolders() { - _checkHenkakuV2Balance(1); + modifier onlyCommunityTokenHolders() { + _checkCommunityTokenBalance(1); _; } @@ -91,7 +104,7 @@ contract Ticket is ERC1155, ERC1155Supply, Administration, MintManager, Interact uint64 _close_blockTimestamp, address[] memory _shareholdersAddresses, uint256[] memory _sharesAmounts - ) external onlyHenkakuHolders { + ) external onlyCommunityTokenHolders { if ( _maxSupply == 0 || keccak256(bytes(_metaDataURL)) == keccak256(bytes("")) || @@ -100,7 +113,7 @@ contract Ticket is ERC1155, ERC1155Supply, Administration, MintManager, Interact uint256 tokenId = _tokenIds.current(); ownerOfRegisteredIds[msg.sender].push(tokenId); - registeredTickets.push( + _registeredTickets.push( TicketInfo( msg.sender, _open_blockTimestamp, @@ -132,13 +145,13 @@ contract Ticket is ERC1155, ERC1155Supply, Administration, MintManager, Interact // @return all registered TicketInfo function retrieveAllTickets() public view returns (TicketInfo[] memory) { - return registeredTickets; + return _registeredTickets; } // @return registered TicketInfo by tokenId function retrieveRegisteredTicket(uint256 _tokenId) public view returns (TicketInfo memory) { - require(registeredTickets.length > _tokenId, "Ticket: not available"); - return registeredTickets[_tokenId]; + require(_registeredTickets.length > _tokenId, "Ticket: not available"); + return _registeredTickets[_tokenId]; } // @return registered TicketInfo by address @@ -148,7 +161,7 @@ contract Ticket is ERC1155, ERC1155Supply, Administration, MintManager, Interact TicketInfo[] memory _ownerOfRegisteredTickets = new TicketInfo[](_ownerOfRegisteredIdsLength); for (uint256 i = 0; i < _ownerOfRegisteredIdsLength; ) { - TicketInfo memory _registeredTicket = registeredTickets[_ownerOfRegisteredIds[i]]; + TicketInfo memory _registeredTicket = _registeredTickets[_ownerOfRegisteredIds[i]]; _ownerOfRegisteredTickets[i] = _registeredTicket; unchecked { ++i; @@ -158,7 +171,7 @@ contract Ticket is ERC1155, ERC1155Supply, Administration, MintManager, Interact } // @dev mint function - function mint(uint256 _tokenId) external onlyHenkakuHolders { + function mint(uint256 _tokenId) external onlyCommunityTokenHolders { require(mintable, "Ticket: Not mintable"); require(balanceOf(msg.sender, _tokenId) == 0, "Ticket: You already have this ticket"); @@ -169,7 +182,7 @@ contract Ticket is ERC1155, ERC1155Supply, Administration, MintManager, Interact ownerOfMintedIds[msg.sender].push(_tokenId); - batchTransferHenkakuV2(ticket.price, ticket.sharesAmounts, ticket.shareholdersAddresses); + _batchTransferCommunityToken(ticket.price, ticket.sharesAmounts, ticket.shareholdersAddresses); _mint(msg.sender, _tokenId, 1, ""); @@ -185,7 +198,7 @@ contract Ticket is ERC1155, ERC1155Supply, Administration, MintManager, Interact TicketInfo[] memory _ownerOfMintedTickets = new TicketInfo[](_ownerOfMintedIdsLength); for (uint256 i = 0; i < _ownerOfMintedIdsLength; ) { - _ownerOfMintedTickets[i] = registeredTickets[_ownerOfMintedIds[i]]; + _ownerOfMintedTickets[i] = _registeredTickets[_ownerOfMintedIds[i]]; unchecked { ++i; } @@ -195,7 +208,7 @@ contract Ticket is ERC1155, ERC1155Supply, Administration, MintManager, Interact } // @return token metadata uri - function uri(uint256 _tokenId) public view override(ERC1155) returns (string memory) { + function uri(uint256 _tokenId) public view override(ERC1155Upgradeable) returns (string memory) { return retrieveRegisteredTicket(_tokenId).uri; } @@ -211,7 +224,7 @@ contract Ticket is ERC1155, ERC1155Supply, Administration, MintManager, Interact uint256[] memory _ids, uint256[] memory _amounts, bytes memory _data - ) internal virtual override(ERC1155, ERC1155Supply) { - ERC1155Supply._beforeTokenTransfer(_operator, _from, _to, _ids, _amounts, _data); + ) internal virtual override(ERC1155Upgradeable, ERC1155SupplyUpgradeable) { + ERC1155SupplyUpgradeable._beforeTokenTransfer(_operator, _from, _to, _ids, _amounts, _data); } } diff --git a/contracts/community/external/henkaku-v2/HenkakuToken.sol b/contracts/community/external/erc20/CommunityToken.sol similarity index 92% rename from contracts/community/external/henkaku-v2/HenkakuToken.sol rename to contracts/community/external/erc20/CommunityToken.sol index a98d0da..b6493c5 100644 --- a/contracts/community/external/henkaku-v2/HenkakuToken.sol +++ b/contracts/community/external/erc20/CommunityToken.sol @@ -6,14 +6,14 @@ import "hardhat/console.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; -contract HenkakuToken is ERC20, Ownable { - uint256 private maxSupply = 1_000_000_000e18; // 1 billion henkaku +contract CommunityToken is ERC20, Ownable { + uint256 private maxSupply = 1_000_000_000e18; // 1 billion Community mapping(address => bool) private whitelist; - address public gateKeeper; // multisig contract address managed by henkaku community + address public gateKeeper; // multisig contract address managed by Community community address public dev; // EOA which updates whitelist systematically like using crontab. bool unlock; - constructor() ERC20("HENKAKU", "HENKAKU") { + constructor() ERC20("Community", "Community") { unlock = false; } diff --git a/contracts/community/interfaces/IHenkakuToken.sol b/contracts/community/interfaces/IHenkakuToken.sol index b5861fe..824abae 100644 --- a/contracts/community/interfaces/IHenkakuToken.sol +++ b/contracts/community/interfaces/IHenkakuToken.sol @@ -2,30 +2,55 @@ pragma solidity ^0.8.9; interface IHenkakuToken { - function addWhitelistUser ( address user ) external; - function addWhitelistUsers ( address[] memory users ) external; - function allowance ( address owner, address spender ) external view returns ( uint256 ); - function approve ( address spender, uint256 amount ) external returns ( bool ); - function balanceOf ( address account ) external view returns ( uint256 ); - function burn ( address _of, uint256 amount ) external; - function decimals ( ) external view returns ( uint8 ); - function decreaseAllowance ( address spender, uint256 subtractedValue ) external returns ( bool ); - function dev ( ) external view returns ( address ); - function gateKeeper ( ) external view returns ( address ); - function increaseAllowance ( address spender, uint256 addedValue ) external returns ( bool ); - function isAllowed ( address user ) external view returns ( bool ); - function mint ( address _to, uint256 amount ) external; - function name ( ) external view returns ( string memory ); - function owner ( ) external view returns ( address ); - function removeWhitelistUser ( address user ) external; - function removeWhitelistUsers ( address[] memory users ) external; - function renounceOwnership ( ) external; - function setDevAddress ( address user ) external; - function setGateKeeper ( address user ) external; - function symbol ( ) external view returns ( string memory ); - function totalSupply ( ) external view returns ( uint256 ); - function transfer ( address to, uint256 amount ) external returns ( bool ); - function transferFrom ( address from, address to, uint256 amount ) external returns ( bool ); - function transferOwnership ( address newOwner ) external; - function unLock ( ) external; + function addWhitelistUser(address user) external; + + function addWhitelistUsers(address[] memory users) external; + + function allowance(address owner, address spender) external view returns (uint256); + + function approve(address spender, uint256 amount) external returns (bool); + + function balanceOf(address account) external view returns (uint256); + + function burn(address _of, uint256 amount) external; + + function decimals() external view returns (uint8); + + function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool); + + function dev() external view returns (address); + + function gateKeeper() external view returns (address); + + function increaseAllowance(address spender, uint256 addedValue) external returns (bool); + + function isAllowed(address user) external view returns (bool); + + function mint(address _to, uint256 amount) external; + + function name() external view returns (string memory); + + function owner() external view returns (address); + + function removeWhitelistUser(address user) external; + + function removeWhitelistUsers(address[] memory users) external; + + function renounceOwnership() external; + + function setDevAddress(address user) external; + + function setGateKeeper(address user) external; + + function symbol() external view returns (string memory); + + function totalSupply() external view returns (uint256); + + function transfer(address to, uint256 amount) external returns (bool); + + function transferFrom(address from, address to, uint256 amount) external returns (bool); + + function transferOwnership(address newOwner) external; + + function unLock() external; } diff --git a/contracts/community/test/TicketV2.sol b/contracts/community/test/TicketV2.sol new file mode 100644 index 0000000..8bc7ac6 --- /dev/null +++ b/contracts/community/test/TicketV2.sol @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155SupplyUpgradeable.sol"; +import "@openzeppelin/contracts/utils/Counters.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import "../Administration.sol"; +import "../InteractCommunityToken.sol"; +import "../MintManager.sol"; + +contract TicketV2 is + Initializable, + ERC1155Upgradeable, + ERC1155SupplyUpgradeable, + Administration, + MintManager, + InteractCommunityToken +{ + //@dev count up tokenId from 0 + using Counters for Counters.Counter; + Counters.Counter private _tokenIds; + + string public name; + string public symbol; + + mapping(address => uint256[]) private ownerOfRegisteredIds; + mapping(address => uint256[]) private ownerOfMintedIds; + + //@dev Declare Event to emit + event RegisterTicket( + address indexed creator, + uint64 open_blockTimestamp, + uint64 close_blockTimestamp, + uint64 maxSupply, + uint256 tokenId, + uint256 price, + string metaDataURL, + uint256[] sharesAmounts, + address[] shareholdersAddresses + ); + event Mint(address indexed minter, uint256 indexed tokenId); + + error InvalidParams(string); + + /** + * @param creator: creator's wallet address + * @param open_blockTimestamp: open block timestamp for sales period + * @param close_blockTimestamp: close block timestamp for sales period + * @param maxSupply: max supply number of token + * @param id: event id + * @param price: price of ticket + * @param uri: metadata uri + * @param sharesAmounts: shares amount of shareholders + * @param shareholdersAddresses: shareholders' wallet addresses + */ + struct TicketInfo { + address creator; + uint64 open_blockTimestamp; + uint64 close_blockTimestamp; + uint64 maxSupply; + uint256 id; + uint256 price; + string uri; + uint256[] sharesAmounts; + address[] shareholdersAddresses; + } + + TicketInfo[] private _registeredTickets; + + function initialize(string memory _name, string memory _symbol, address _communityToken) public initializer { + name = _name; + symbol = _symbol; + + _registeredTickets.push(TicketInfo(address(0), 0, 0, 0, 0, 0, "", new uint256[](0), new address[](0))); + _tokenIds.increment(); + + super._initializeAdministration(); + super._initializeInteractCommunityToken(_communityToken); + } + + modifier onlyCommunityTokenHolders() { + _checkCommunityTokenBalance(1); + _; + } + + function _getTotalSharesAmounts(uint256[] memory _sharesAmounts) internal pure returns (uint256) { + uint256[] memory sharedAmounts = _sharesAmounts; + uint256 sharesAmountsLength = sharedAmounts.length; + uint256 totalSharesAmounts = 0; + for (uint256 i = 0; i < sharesAmountsLength; ) { + totalSharesAmounts = totalSharesAmounts + sharedAmounts[i]; + unchecked { + ++i; + } + } + return totalSharesAmounts; + } + + function registerTicket( + uint64 _maxSupply, + string calldata _metaDataURL, + uint256 _price, + uint64 _open_blockTimestamp, + uint64 _close_blockTimestamp, + address[] memory _shareholdersAddresses, + uint256[] memory _sharesAmounts + ) external onlyCommunityTokenHolders { + if ( + _maxSupply == 0 || + keccak256(bytes(_metaDataURL)) == keccak256(bytes("")) || + _getTotalSharesAmounts(_sharesAmounts) != _price + ) revert InvalidParams("Ticket: invalid params"); + + uint256 tokenId = _tokenIds.current(); + ownerOfRegisteredIds[msg.sender].push(tokenId); + _registeredTickets.push( + TicketInfo( + msg.sender, + _open_blockTimestamp, + _close_blockTimestamp, + _maxSupply, + tokenId, + _price, + _metaDataURL, + _sharesAmounts, + _shareholdersAddresses + ) + ); + _tokenIds.increment(); + + // @dev Emit registeredTicket + // @param address, tokenId, URL of meta data, max supply + emit RegisterTicket( + msg.sender, + _open_blockTimestamp, + _close_blockTimestamp, + _maxSupply, + tokenId, + _price, + _metaDataURL, + _sharesAmounts, + _shareholdersAddresses + ); + } + + // @return all registered TicketInfo + function retrieveAllTickets() public view returns (TicketInfo[] memory) { + return _registeredTickets; + } + + // @return registered TicketInfo by tokenId + function retrieveRegisteredTicket(uint256 _tokenId) public view returns (TicketInfo memory) { + require(_registeredTickets.length > _tokenId, "Ticket: not available"); + return _registeredTickets[_tokenId]; + } + + // @return registered TicketInfo by address + function retrieveRegisteredTickets(address _address) public view returns (TicketInfo[] memory) { + uint256[] memory _ownerOfRegisteredIds = ownerOfRegisteredIds[_address]; + uint256 _ownerOfRegisteredIdsLength = _ownerOfRegisteredIds.length; + TicketInfo[] memory _ownerOfRegisteredTickets = new TicketInfo[](_ownerOfRegisteredIdsLength); + + for (uint256 i = 0; i < _ownerOfRegisteredIdsLength; ) { + TicketInfo memory _registeredTicket = _registeredTickets[_ownerOfRegisteredIds[i]]; + _ownerOfRegisteredTickets[i] = _registeredTicket; + unchecked { + ++i; + } + } + return _ownerOfRegisteredTickets; + } + + // @dev mint function + function mint(uint256 _tokenId) external onlyCommunityTokenHolders { + require(mintable, "Ticket: Not mintable"); + require(balanceOf(msg.sender, _tokenId) == 0, "Ticket: You already have this ticket"); + + TicketInfo memory ticket = retrieveRegisteredTicket(_tokenId); + require(ticket.open_blockTimestamp <= block.timestamp, "Ticket: Not open yet"); + require(ticket.close_blockTimestamp >= block.timestamp, "Ticket: Already closed"); + require(ticket.maxSupply > totalSupply(_tokenId), "Ticket: Mint limit reached"); + + ownerOfMintedIds[msg.sender].push(_tokenId); + + _batchTransferCommunityToken(ticket.price, ticket.sharesAmounts, ticket.shareholdersAddresses); + + _mint(msg.sender, _tokenId, 1, ""); + + // @dev Emit mint event + // @param address, tokenId + emit Mint(msg.sender, _tokenId); + } + + // @return holding tokenIds with address + function retrieveMintedTickets(address _address) public view returns (TicketInfo[] memory) { + uint256[] memory _ownerOfMintedIds = ownerOfMintedIds[_address]; + uint256 _ownerOfMintedIdsLength = _ownerOfMintedIds.length; + TicketInfo[] memory _ownerOfMintedTickets = new TicketInfo[](_ownerOfMintedIdsLength); + + for (uint256 i = 0; i < _ownerOfMintedIdsLength; ) { + _ownerOfMintedTickets[i] = _registeredTickets[_ownerOfMintedIds[i]]; + unchecked { + ++i; + } + } + + return _ownerOfMintedTickets; + } + + // @return token metadata uri + function uri(uint256 _tokenId) public view override(ERC1155Upgradeable) returns (string memory) { + return retrieveRegisteredTicket(_tokenId).uri; + } + + // @return token metadata uri + function tokenURI(uint256 _tokenId) public view returns (string memory) { + return retrieveRegisteredTicket(_tokenId).uri; + } + + function _beforeTokenTransfer( + address _operator, + address _from, + address _to, + uint256[] memory _ids, + uint256[] memory _amounts, + bytes memory _data + ) internal virtual override(ERC1155Upgradeable, ERC1155SupplyUpgradeable) { + ERC1155SupplyUpgradeable._beforeTokenTransfer(_operator, _from, _to, _ids, _amounts, _data); + } + + function newFunction() public pure returns (uint256) { + return 1; + } +} diff --git a/hardhat.config.ts b/hardhat.config.ts index b626d11..64b6298 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -1,12 +1,21 @@ import * as dotenv from 'dotenv' import { HardhatUserConfig } from 'hardhat/config' import '@nomicfoundation/hardhat-toolbox' +import '@openzeppelin/hardhat-upgrades' import 'hardhat-gas-reporter' dotenv.config() const config: HardhatUserConfig = { - solidity: '0.8.17', + solidity: { + version: '0.8.17', + settings: { + optimizer: { + enabled: true, + runs: 5000, + }, + }, + }, networks: { polygon: { url: process.env.POLYGON_ALCHEMY_KEY!, diff --git a/package.json b/package.json index 70bc624..1ded011 100644 --- a/package.json +++ b/package.json @@ -43,5 +43,9 @@ "ts-node": ">=8.0.0", "typechain": "^8.1.0", "typescript": ">=4.5.0" + }, + "dependencies": { + "@openzeppelin/contracts-upgradeable": "^4.9.0", + "@openzeppelin/hardhat-upgrades": "^1.27.0" } } diff --git a/test/community_ticket.ts b/test/community_ticket.ts index fbaf0f6..2109b1f 100644 --- a/test/community_ticket.ts +++ b/test/community_ticket.ts @@ -1,13 +1,12 @@ import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' import { expect } from 'chai' import { ethers } from 'hardhat' -import { HenkakuToken, HenkakuToken__factory, Ticket, Ticket__factory } from '../typechain-types' -import { BigNumber } from 'ethers' -import { deployAndDistributeHenkakuV2, deployTicket } from './helper/deploy' +import { CommunityToken, Ticket } from '../typechain-types' +import { deployAndDistributeCommunityToken, deployTicket } from './helper/deploy' describe('RegisterTicket', () => { let TicketContract: Ticket - let HenkakuTokenContract: HenkakuToken + let CommunityTokenContract: CommunityToken let deployer: SignerWithAddress let creator: SignerWithAddress let user1: SignerWithAddress @@ -17,12 +16,12 @@ describe('RegisterTicket', () => { before(async () => { ;[deployer, creator, user1, user2, user3, outsider] = await ethers.getSigners() - HenkakuTokenContract = await deployAndDistributeHenkakuV2({ + CommunityTokenContract = await deployAndDistributeCommunityToken({ deployer, addresses: [creator.address, user1.address, user2.address, user3.address, deployer.address], amount: ethers.utils.parseEther('1000'), }) - TicketContract = await deployTicket(HenkakuTokenContract.address) + TicketContract = await deployTicket(CommunityTokenContract.address) }) it('register creative', async () => { @@ -45,7 +44,7 @@ describe('RegisterTicket', () => { expect(getRegisteredTicket.creator).to.equal(ethers.constants.AddressZero) expect(getRegisteredTicket.maxSupply).to.equal(0) - await HenkakuTokenContract.connect(creator).approve(TicketContract.address, ethers.utils.parseEther('200')) + await CommunityTokenContract.connect(creator).approve(TicketContract.address, ethers.utils.parseEther('200')) // Register the first Ticket // 1つ目の年賀状(_tokenIdが1)を登録 @@ -53,42 +52,69 @@ describe('RegisterTicket', () => { // @dev test emit register creative await expect( - TicketContract.connect(creator).registerTicket(2, 'ipfs://test1', 100, now, now + 1000000000000, [creator.address, deployer.address], [60, 40]) + TicketContract.connect(creator).registerTicket( + 2, + 'ipfs://test1', + 100, + now, + now + 1000000000000, + [creator.address, deployer.address], + [60, 40] + ) ) .to.emit(TicketContract, 'RegisterTicket') - .withArgs(creator.address, now, now + 1000000000000, 2, 1, 100, 'ipfs://test1', [60, 40], [creator.address, deployer.address]) + .withArgs( + creator.address, + now, + now + 1000000000000, + 2, + 1, + 100, + 'ipfs://test1', + [60, 40], + [creator.address, deployer.address] + ) tokenURI = await TicketContract.uri(1) expect(tokenURI).equal('ipfs://test1') getAllRegisteredTickets = await TicketContract.retrieveAllTickets() - // expect(getAllRegisteredTickets.length).to.equal(2) - // expect(getAllRegisteredTickets[1].uri).to.equal('ipfs://test1') - // expect(getAllRegisteredTickets[1].creator).to.equal(creator.address) - // expect(getAllRegisteredTickets[1].maxSupply).to.equal(2) + expect(getAllRegisteredTickets.length).to.equal(2) + expect(getAllRegisteredTickets[1].uri).to.equal('ipfs://test1') + expect(getAllRegisteredTickets[1].creator).to.equal(creator.address) + expect(getAllRegisteredTickets[1].maxSupply).to.equal(2) getRegisteredTicket = await TicketContract.retrieveRegisteredTicket(1) - // expect(getRegisteredTicket.uri).to.equal('ipfs://test1') - // expect(getRegisteredTicket.creator).to.equal(creator.address) - // expect(getRegisteredTicket.maxSupply).to.equal(2) + expect(getRegisteredTicket.uri).to.equal('ipfs://test1') + expect(getRegisteredTicket.creator).to.equal(creator.address) + expect(getRegisteredTicket.maxSupply).to.equal(2) const registeredTickets = await TicketContract.retrieveRegisteredTickets(creator.address) - // expect(registeredTickets[0].uri).to.equal('ipfs://test1') - // expect(registeredTickets[0].creator).to.equal(creator.address) - // expect(registeredTickets[0].maxSupply).to.equal(2) + expect(registeredTickets[0].uri).to.equal('ipfs://test1') + expect(registeredTickets[0].creator).to.equal(creator.address) + expect(registeredTickets[0].maxSupply).to.equal(2) }) it('revert register creative', async () => { // @dev test revert register creative - expect(await HenkakuTokenContract.balanceOf(outsider.address)).to.equal(0) - await expect(TicketContract.connect(outsider).registerTicket(2, 'ipfs://test1', 100, 0, 0, [outsider.address, deployer.address], [60, 40])).to.be.revertedWith('Ticket: Insufficient HenkakuV2 token') + expect(await CommunityTokenContract.balanceOf(outsider.address)).to.equal(0) + await expect( + TicketContract.connect(outsider).registerTicket( + 2, + 'ipfs://test1', + 100, + 0, + 0, + [outsider.address, deployer.address], + [60, 40] + ) + ).to.be.revertedWith('Ticket: Insufficient ERC20 token') }) - }) describe('MintTicket', () => { let TicketContract: Ticket - let HenkakuTokenContract: HenkakuToken + let CommunityTokenContract: CommunityToken let deployer: SignerWithAddress let creator: SignerWithAddress let user1: SignerWithAddress @@ -99,59 +125,77 @@ describe('MintTicket', () => { before(async () => { ;[deployer, creator, user1, user2, user3, user4, outsider] = await ethers.getSigners() - HenkakuTokenContract = await deployAndDistributeHenkakuV2({ + CommunityTokenContract = await deployAndDistributeCommunityToken({ deployer, addresses: [creator.address, user1.address, user2.address, user3.address, deployer.address], amount: ethers.utils.parseEther('1000'), }) - TicketContract = await deployTicket(HenkakuTokenContract.address) - await HenkakuTokenContract.connect(user1).approve(TicketContract.address, ethers.utils.parseEther('1000')) - await HenkakuTokenContract.connect(user2).approve(TicketContract.address, ethers.utils.parseEther('1000')) + TicketContract = await deployTicket(CommunityTokenContract.address) + await CommunityTokenContract.connect(user1).approve(TicketContract.address, ethers.utils.parseEther('1000')) + await CommunityTokenContract.connect(user2).approve(TicketContract.address, ethers.utils.parseEther('1000')) let now = (await ethers.provider.getBlock('latest')).timestamp - expect(await TicketContract.connect(creator).registerTicket( - 2, - 'ipfs://test1', - 100, - now, - now + 1000000000000, - [creator.address, deployer.address], - [60, 40] - )).not.to.be.reverted - - expect(await TicketContract.connect(creator).registerTicket( - 2, - 'ipfs://test1', - 100, - now, - now + 1000000000000, - [creator.address, deployer.address], - [60, 40] - )).not.to.be.reverted - - expect(await TicketContract.connect(creator).registerTicket( - 1, - 'ipfs://test1', - 100, - now + 1000000000000, - now + 1000000000000, - [creator.address, deployer.address], - [60, 40] - )).not.to.be.reverted - - expect(await TicketContract.connect(creator).registerTicket(1, 'ipfs://test1', 100, now, 0, [creator.address, deployer.address], [60, 40])).not.to.be.reverted - - expect(await TicketContract.connect(creator).registerTicket( - 1, - 'ipfs://test1', - 100, - now, - now + 1000000000000, - [creator.address, deployer.address], - [60, 40] - )).not.to.be.reverted + expect( + await TicketContract.connect(creator).registerTicket( + 2, + 'ipfs://test1', + 100, + now, + now + 1000000000000, + [creator.address, deployer.address], + [60, 40] + ) + ).not.to.be.reverted + + expect( + await TicketContract.connect(creator).registerTicket( + 2, + 'ipfs://test1', + 100, + now, + now + 1000000000000, + [creator.address, deployer.address], + [60, 40] + ) + ).not.to.be.reverted + + expect( + await TicketContract.connect(creator).registerTicket( + 1, + 'ipfs://test1', + 100, + now + 1000000000000, + now + 1000000000000, + [creator.address, deployer.address], + [60, 40] + ) + ).not.to.be.reverted + + expect( + await TicketContract.connect(creator).registerTicket( + 1, + 'ipfs://test1', + 100, + now, + 0, + [creator.address, deployer.address], + [60, 40] + ) + ).not.to.be.reverted + + expect( + await TicketContract.connect(creator).registerTicket( + 1, + 'ipfs://test1', + 100, + now, + now + 1000000000000, + [creator.address, deployer.address], + [60, 40] + ) + ).not.to.be.reverted }) it('mint ticket', async () => { @@ -215,20 +259,20 @@ describe('MintTicket', () => { await expect(TicketContract.connect(user3).mint(4)).to.be.revertedWith('Ticket: Already closed') }) - it('failed with insufficient Henkaku Token', async () => { - await expect(TicketContract.connect(user4).mint(5)).to.be.revertedWith('Ticket: Insufficient HenkakuV2 token') + it('failed with insufficient ERC20 Token', async () => { + await expect(TicketContract.connect(user4).mint(5)).to.be.revertedWith('Ticket: Insufficient ERC20 token') }) it('revert register creative', async () => { // @dev test revert register creative - expect(await HenkakuTokenContract.balanceOf(outsider.address)).to.equal(0) - await expect(TicketContract.connect(outsider).mint(5)).to.be.revertedWith('Ticket: Insufficient HenkakuV2 token') + expect(await CommunityTokenContract.balanceOf(outsider.address)).to.equal(0) + await expect(TicketContract.connect(outsider).mint(5)).to.be.revertedWith('Ticket: Insufficient ERC20 token') }) }) describe('CheckMintable', () => { let TicketContract: Ticket - let HenkakuTokenContract: HenkakuToken + let CommunityTokenContract: CommunityToken let deployer: SignerWithAddress let creator: SignerWithAddress let user1: SignerWithAddress @@ -237,12 +281,12 @@ describe('CheckMintable', () => { before(async () => { ;[deployer, creator, user1, user2, user3] = await ethers.getSigners() - HenkakuTokenContract = await deployAndDistributeHenkakuV2({ + CommunityTokenContract = await deployAndDistributeCommunityToken({ deployer, addresses: [creator.address, user1.address, user2.address, deployer.address], amount: ethers.utils.parseEther('100'), }) - TicketContract = await deployTicket(HenkakuTokenContract.address) + TicketContract = await deployTicket(CommunityTokenContract.address) }) it('initial mintable flag is false', async () => { @@ -338,9 +382,9 @@ describe('CheckMintable', () => { }) }) -describe('check henkaku token transfer', () => { +describe('check community token transfer', () => { let TicketContract: Ticket - let HenkakuTokenContract: HenkakuToken + let CommunityTokenContract: CommunityToken let deployer: SignerWithAddress let creator: SignerWithAddress let user1: SignerWithAddress @@ -348,54 +392,56 @@ describe('check henkaku token transfer', () => { before(async () => { ;[deployer, creator, user1, user2] = await ethers.getSigners() - HenkakuTokenContract = await deployAndDistributeHenkakuV2({ + CommunityTokenContract = await deployAndDistributeCommunityToken({ deployer, addresses: [creator.address, user1.address, user2.address, deployer.address], amount: ethers.utils.parseEther('1000'), }) - TicketContract = await deployTicket(HenkakuTokenContract.address) + TicketContract = await deployTicket(CommunityTokenContract.address) - await HenkakuTokenContract.connect(user1).approve(TicketContract.address, ethers.utils.parseEther('1000')) - await HenkakuTokenContract.connect(user2).approve(TicketContract.address, ethers.utils.parseEther('1000')) + await CommunityTokenContract.connect(user1).approve(TicketContract.address, ethers.utils.parseEther('1000')) + await CommunityTokenContract.connect(user2).approve(TicketContract.address, ethers.utils.parseEther('1000')) let now = (await ethers.provider.getBlock('latest')).timestamp - expect(await TicketContract.connect(creator).registerTicket( - 1, - 'ipfs://test1', - ethers.utils.parseEther('100'), - now, - now + 1000000000000, - [creator.address, deployer.address], - [ethers.utils.parseEther('60'), ethers.utils.parseEther('40')] - )).not.to.be.reverted + expect( + await TicketContract.connect(creator).registerTicket( + 1, + 'ipfs://test1', + ethers.utils.parseEther('100'), + now, + now + 1000000000000, + [creator.address, deployer.address], + [ethers.utils.parseEther('60'), ethers.utils.parseEther('40')] + ) + ).not.to.be.reverted await TicketContract.connect(deployer).switchMintable() }) it('success to transfer ticket', async () => { - expect(await HenkakuTokenContract.balanceOf(user1.address)).to.be.equal(ethers.utils.parseEther('1000')) - expect(await HenkakuTokenContract.balanceOf(creator.address)).to.be.equal(ethers.utils.parseEther('1000')) - expect(await HenkakuTokenContract.balanceOf(deployer.address)).to.be.equal(ethers.utils.parseEther('1000')) + expect(await CommunityTokenContract.balanceOf(user1.address)).to.be.equal(ethers.utils.parseEther('1000')) + expect(await CommunityTokenContract.balanceOf(creator.address)).to.be.equal(ethers.utils.parseEther('1000')) + expect(await CommunityTokenContract.balanceOf(deployer.address)).to.be.equal(ethers.utils.parseEther('1000')) const tx = await TicketContract.connect(user1).mint(1) await tx.wait() - expect(await HenkakuTokenContract.balanceOf(user1.address)).to.be.equal(ethers.utils.parseEther('900')) - expect(await HenkakuTokenContract.balanceOf(creator.address)).to.be.equal(ethers.utils.parseEther('1060')) - expect(await HenkakuTokenContract.balanceOf(deployer.address)).to.be.equal(ethers.utils.parseEther('1040')) + expect(await CommunityTokenContract.balanceOf(user1.address)).to.be.equal(ethers.utils.parseEther('900')) + expect(await CommunityTokenContract.balanceOf(creator.address)).to.be.equal(ethers.utils.parseEther('1060')) + expect(await CommunityTokenContract.balanceOf(deployer.address)).to.be.equal(ethers.utils.parseEther('1040')) }) - it('fail to mint and henkaku token is not transfered', async () => { + it('fail to mint and community token is not transfered', async () => { await expect(TicketContract.connect(user2).mint(1)).revertedWith('Ticket: Mint limit reached') - expect(await HenkakuTokenContract.balanceOf(user2.address)).to.be.equal(ethers.utils.parseEther('1000')) - expect(await HenkakuTokenContract.balanceOf(creator.address)).to.be.equal(ethers.utils.parseEther('1060')) - expect(await HenkakuTokenContract.balanceOf(deployer.address)).to.be.equal(ethers.utils.parseEther('1040')) + expect(await CommunityTokenContract.balanceOf(user2.address)).to.be.equal(ethers.utils.parseEther('1000')) + expect(await CommunityTokenContract.balanceOf(creator.address)).to.be.equal(ethers.utils.parseEther('1060')) + expect(await CommunityTokenContract.balanceOf(deployer.address)).to.be.equal(ethers.utils.parseEther('1040')) }) }) describe('check timestamp', () => { let TicketContract: Ticket - let HenkakuTokenContract: HenkakuToken + let CommunityTokenContract: CommunityToken let deployer: SignerWithAddress let creator: SignerWithAddress let user1: SignerWithAddress @@ -420,19 +466,19 @@ describe('check timestamp', () => { before(async () => { ;[deployer, creator, user1, user2] = await ethers.getSigners() - HenkakuTokenContract = await deployAndDistributeHenkakuV2({ + CommunityTokenContract = await deployAndDistributeCommunityToken({ deployer, addresses: [creator.address, user1.address, user2.address, deployer.address], amount: ethers.utils.parseEther('100'), }) - TicketContract = await deployTicket(HenkakuTokenContract.address) + TicketContract = await deployTicket(CommunityTokenContract.address) }) }) describe('after minting term', () => { let TicketContract: Ticket - let HenkakuTokenContract: HenkakuToken + let CommunityTokenContract: CommunityToken let deployer: SignerWithAddress let creator: SignerWithAddress let user1: SignerWithAddress @@ -457,11 +503,11 @@ describe('after minting term', () => { before(async () => { ;[deployer, creator, user1, user2] = await ethers.getSigners() - HenkakuTokenContract = await deployAndDistributeHenkakuV2({ + CommunityTokenContract = await deployAndDistributeCommunityToken({ deployer, addresses: [creator.address, user1.address, user2.address, deployer.address], amount: ethers.utils.parseEther('100'), }) - TicketContract = await deployTicket(HenkakuTokenContract.address) + TicketContract = await deployTicket(CommunityTokenContract.address) }) }) diff --git a/test/helper/deploy.ts b/test/helper/deploy.ts index 840d3d6..20cdbd6 100644 --- a/test/helper/deploy.ts +++ b/test/helper/deploy.ts @@ -1,29 +1,31 @@ import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' import { BigNumber } from 'ethers' -import { HenkakuToken, HenkakuToken__factory, Ticket__factory } from '../../typechain-types' -import { ethers } from 'hardhat' +import { CommunityToken, CommunityToken__factory, Ticket, Ticket__factory } from '../../typechain-types' +import { ethers, upgrades } from 'hardhat' -export const deployTicket = async (henkakuTokenAddr: string) => { +export const deployTicket = async (communityTokenAddr: string) => { const TicketFactory = (await ethers.getContractFactory('Ticket')) as Ticket__factory - const TicketContract = await TicketFactory.deploy('Henkaku Ticket', 'HNJ', henkakuTokenAddr) + const TicketContract = await upgrades.deployProxy(TicketFactory, ['CommunityTicket', 'CT', communityTokenAddr], { + initializer: 'initialize', + }) await TicketContract.deployed() - return TicketContract + return TicketContract as Ticket } -export const deployAndDistributeHenkakuV2: (params: { +export const deployAndDistributeCommunityToken: (params: { deployer: SignerWithAddress addresses: string[] amount: BigNumber -}) => Promise = async ({ deployer, addresses, amount }) => { - const HenkakuV2Factory = (await ethers.getContractFactory('HenkakuToken')) as HenkakuToken__factory - const HenkakuV2Contract = await HenkakuV2Factory.connect(deployer).deploy() - await HenkakuV2Contract.deployed() +}) => Promise = async ({ deployer, addresses, amount }) => { + const CommunityTokenFactory = (await ethers.getContractFactory('CommunityToken')) as CommunityToken__factory + const CommunityTokenContract = await CommunityTokenFactory.connect(deployer).deploy() + await CommunityTokenContract.deployed() - await HenkakuV2Contract.addWhitelistUsers(addresses) + await CommunityTokenContract.addWhitelistUsers(addresses) for (const address of addresses) { - await HenkakuV2Contract.mint(address, amount) + await CommunityTokenContract.mint(address, amount) } - return HenkakuV2Contract + return CommunityTokenContract } diff --git a/test/public_ticket.ts b/test/public_ticket.ts deleted file mode 100644 index 019d9e1..0000000 --- a/test/public_ticket.ts +++ /dev/null @@ -1,436 +0,0 @@ -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { expect } from 'chai' -import { ethers } from 'hardhat' -import { PublicTicket, PublicTicket__factory } from '../typechain-types' - -const open_blockTimestamp: number = 1672498800 -const close_blockTimestamp: number = 1704034800 - -describe('RegisterTicket', () => { - let TicketContract: PublicTicket - let deployer: SignerWithAddress - let creator: SignerWithAddress - let user1: SignerWithAddress - let user2: SignerWithAddress - let user3: SignerWithAddress - - before(async () => { - ;[deployer, creator, user1, user2, user3] = await ethers.getSigners() - const TicketFactory = (await ethers.getContractFactory('PublicTicket')) as PublicTicket__factory - TicketContract = await TicketFactory.connect(deployer).deploy( - 'Henkaku Ticket', - 'HNJ', - open_blockTimestamp, - close_blockTimestamp, - deployer.address - ) - await TicketContract.deployed() - await TicketContract.addAdmins([creator.address]) - }) - - it('register creative', async () => { - // Check the contents of tokenId #0, which is the default missing value. - // デフォルト値の欠番としたtokenId #0の内容を確認 - let tokenURI - tokenURI = await TicketContract.uri(0) - expect(tokenURI).equal('') - - let getAllRegisteredTickets - getAllRegisteredTickets = await TicketContract.retrieveAllTickets() - expect(getAllRegisteredTickets.length).to.equal(1) - expect(getAllRegisteredTickets[0].uri).to.equal('') - expect(getAllRegisteredTickets[0].creator).to.equal(ethers.constants.AddressZero) - expect(getAllRegisteredTickets[0].maxSupply).to.equal(0) - - let getRegisteredTicket - getRegisteredTicket = await TicketContract.retrieveRegisteredTicket(0) - expect(getRegisteredTicket.uri).to.equal('') - expect(getRegisteredTicket.creator).to.equal(ethers.constants.AddressZero) - expect(getRegisteredTicket.maxSupply).to.equal(0) - - // @dev test emit register creative - await expect(TicketContract.connect(creator).registerTicket(2, 'ipfs://test1')) - .to.emit(TicketContract, 'RegisterTicket') - .withArgs(creator.address, 1, 'ipfs://test1', 2) - - tokenURI = await TicketContract.uri(1) - expect(tokenURI).equal('ipfs://test1') - - getAllRegisteredTickets = await TicketContract.retrieveAllTickets() - expect(getAllRegisteredTickets.length).to.equal(2) - expect(getAllRegisteredTickets[1].uri).to.equal('ipfs://test1') - expect(getAllRegisteredTickets[1].creator).to.equal(creator.address) - expect(getAllRegisteredTickets[1].maxSupply).to.equal(2) - - getRegisteredTicket = await TicketContract.retrieveRegisteredTicket(1) - expect(getRegisteredTicket.uri).to.equal('ipfs://test1') - expect(getRegisteredTicket.creator).to.equal(creator.address) - expect(getRegisteredTicket.maxSupply).to.equal(2) - - const registeredTickets = await TicketContract.retrieveRegisteredTickets(creator.address) - expect(registeredTickets[0].uri).to.equal('ipfs://test1') - expect(registeredTickets[0].creator).to.equal(creator.address) - expect(registeredTickets[0].maxSupply).to.equal(2) - }) -}) - -describe('MintTicket', () => { - let TicketContract: PublicTicket - let deployer: SignerWithAddress - let creator: SignerWithAddress - let user1: SignerWithAddress - let user2: SignerWithAddress - let user3: SignerWithAddress - let user4: SignerWithAddress - - before(async () => { - ;[deployer, creator, user1, user2, user3, user4] = await ethers.getSigners() - - const TicketFactory = (await ethers.getContractFactory('PublicTicket')) as PublicTicket__factory - TicketContract = await TicketFactory.deploy( - 'Henkaku Ticket', - 'HNJ', - open_blockTimestamp, - close_blockTimestamp, - deployer.address - ) - await TicketContract.deployed() - await TicketContract.addAdmins([creator.address]) - await TicketContract.connect(creator).registerTicket(2, 'ipfs://test1') - }) - - it('mint ticket', async () => { - await TicketContract.connect(deployer).switchMintable() - - await TicketContract.connect(user1).mint(1) - let balance = await TicketContract.connect(user1).balanceOf(user1.address, 1) - expect(balance).to.equal(1) - - // @dev test emit mint - await expect(TicketContract.connect(user2).mint(1)).to.emit(TicketContract, 'Mint').withArgs(user2.address, 1) - - //await TicketContract.connect(user2).mint(1) - balance = await TicketContract.connect(user2).balanceOf(user2.address, 1) - expect(balance).to.equal(1) - }) - - it('retrieve minted ticket', async () => { - // URIs - let mintedTicketInfo = await TicketContract.connect(user1).retrieveMintedTickets(user1.address) - expect(mintedTicketInfo.length).equal(1) - expect(mintedTicketInfo[0].uri).to.equal('ipfs://test1') - // Register the second Ticket - // 2つ目(_tokenIdが1)の年賀状を登録 - await TicketContract.connect(creator).registerTicket(2, 'ipfs://test1') - - // // user1が年賀状を2枚め(_tokenIdが2)をミント - await TicketContract.connect(user1).mint(2) - mintedTicketInfo = await TicketContract.connect(user1).retrieveMintedTickets(user1.address) - - expect(mintedTicketInfo.length).equal(2) - expect(mintedTicketInfo[0].id).to.equal(1) - expect(mintedTicketInfo[1].id).to.equal(2) - expect(mintedTicketInfo[0].uri).to.equal('ipfs://test1') - expect(mintedTicketInfo[1].uri).to.equal('ipfs://test1') - - mintedTicketInfo = await TicketContract.connect(user2).retrieveMintedTickets(user2.address) - - expect(mintedTicketInfo.length).equal(1) - expect(mintedTicketInfo[0].id).to.equal(1) - expect(mintedTicketInfo[0].uri).to.equal('ipfs://test1') - }) - - it('mint batch tickets', async () => { - // Register the third Ticket - // 3つ目(_tokenIdが2)の年賀状を登録 - await TicketContract.connect(creator).registerTicket(2, 'ipfs://test4') - // Register the fourth Ticket - // 4つ目(_tokenIdが3)の年賀状を登録 - await TicketContract.connect(creator).registerTicket(2, 'ipfs://test4') - - // @dev test emit mint batch - await expect(await TicketContract.connect(user3).mintBatch([3, 4])) - .to.emit(TicketContract, 'MintBatch') - .withArgs(user3.address, [3, 4]) - //await TicketContract.connect(user3).mintBatch([3, 4]) - - let balance - balance = await TicketContract.connect(user3).balanceOf(user3.address, 3) - expect(balance).to.equal(1) - - balance = await TicketContract.connect(user3).balanceOf(user3.address, 4) - expect(balance).to.equal(1) - - let mintedTicketInfo = await TicketContract.connect(user3).retrieveMintedTickets(user3.address) - - expect(mintedTicketInfo.length).equal(2) - expect(mintedTicketInfo[0].id).to.equal(3) - expect(mintedTicketInfo[1].id).to.equal(4) - expect(mintedTicketInfo[0].uri).to.equal('ipfs://test4') - expect(mintedTicketInfo[1].uri).to.equal('ipfs://test4') - }) - - it('mint batch failed with already have', async () => { - // Confirmed that even with the mintBatch function, it is not possible to mint more than two Tickets. - // mintBatch関数でも同じ年賀状を2つ以上ミント出来ないことを確認 - await expect(TicketContract.connect(user3).mintBatch([3, 4])).to.be.revertedWith( - 'Ticket: You already have this ticket' - ) - - // Confirm that balance, etc. has not changed. - // balance等が変わっていないことを確認 - let balance - balance = await TicketContract.connect(user3).balanceOf(user3.address, 3) - expect(balance).to.equal(1) - - balance = await TicketContract.connect(user3).balanceOf(user3.address, 4) - expect(balance).to.equal(1) - - let mintedTicketInfo = await TicketContract.connect(user3).retrieveMintedTickets(user3.address) - - expect(mintedTicketInfo.length).equal(2) - expect(mintedTicketInfo[0].id).to.equal(3) - expect(mintedTicketInfo[1].id).to.equal(4) - expect(mintedTicketInfo[0].uri).to.equal('ipfs://test4') - expect(mintedTicketInfo[1].uri).to.equal('ipfs://test4') - }) - - it('failed with unavailable', async () => { - // await expect(TicketContract.connect(user2).mint(1)).to.be.revertedWith('Ticket: not available') - await expect(TicketContract.connect(user2).mint(999)).to.be.revertedWith('Ticket: not available') - }) - - it('failed with already have', async () => { - await expect(TicketContract.connect(user2).mint(1)).to.be.revertedWith('Ticket: You already have this ticket') - }) - - it('failed with mint limit', async () => { - await expect(TicketContract.connect(user3).mint(1)).to.be.revertedWith('Ticket: Mint limit reached') - }) - - it('failed with mint tokenId #0', async () => { - await expect(TicketContract.connect(user3).mint(0)).to.be.revertedWith('Ticket: Mint limit reached') - }) -}) - -describe('CheckMintable', () => { - let TicketContract: PublicTicket - let deployer: SignerWithAddress - let creator: SignerWithAddress - let user1: SignerWithAddress - let user2: SignerWithAddress - let user3: SignerWithAddress - - before(async () => { - ;[deployer, creator, user1, user2, user3] = await ethers.getSigners() - const TicketFactory = (await ethers.getContractFactory('PublicTicket')) as PublicTicket__factory - TicketContract = await TicketFactory.deploy( - 'Henkaku Ticket', - 'HNJ', - open_blockTimestamp, - close_blockTimestamp, - deployer.address - ) - await TicketContract.deployed() - }) - - it('initial mintable flag is false', async () => { - const mintable = await TicketContract.mintable() - expect(mintable).to.equal(false) - }) - - it('initial admin is deployer', async () => { - const admin = await TicketContract.isAdmin(deployer.address) - expect(admin).to.equal(true) - }) - - it('initial admin is only deployer', async () => { - const admin = await TicketContract.isAdmin(user1.address) - expect(admin).to.equal(false) - }) - - it('only admins can add new admins', async () => { - const newAdmins = [user1.address, user2.address] - await expect(TicketContract.connect(creator).addAdmins(newAdmins)).to.be.revertedWith('Admins only') - }) - - it('add a new admin', async () => { - const newAdmins = [creator.address] - - let isAdmin - isAdmin = await TicketContract.isAdmin(creator.address) - expect(isAdmin).to.equal(false) - - const addAdmins = await TicketContract.connect(deployer).addAdmins(newAdmins) - await addAdmins.wait() - - isAdmin = await TicketContract.isAdmin(creator.address) - expect(isAdmin).to.equal(true) - }) - - it('add new admins', async () => { - const newAdmins = [user1.address, user2.address] - - let isAdmin - isAdmin = await TicketContract.isAdmin(user1.address) - expect(isAdmin).to.equal(false) - isAdmin = await TicketContract.isAdmin(user2.address) - expect(isAdmin).to.equal(false) - - const addAdmins = await TicketContract.connect(deployer).addAdmins(newAdmins) - await addAdmins.wait() - - isAdmin = await TicketContract.isAdmin(user1.address) - expect(isAdmin).to.equal(true) - isAdmin = await TicketContract.isAdmin(user2.address) - expect(isAdmin).to.equal(true) - }) - - it('delete an admin', async () => { - let isAdmin - isAdmin = await TicketContract.isAdmin(user2.address) - expect(isAdmin).to.equal(true) - - const deleteAdmin = await TicketContract.connect(deployer).deleteAdmin(user2.address) - await deleteAdmin.wait() - - isAdmin = await TicketContract.isAdmin(user2.address) - expect(isAdmin).to.equal(false) - }) - - it('switch mintable flag', async () => { - let mintable - mintable = await TicketContract.mintable() - expect(mintable).to.equal(false) - - let switchMintable - switchMintable = await TicketContract.connect(deployer).switchMintable() - - mintable = await TicketContract.mintable() - expect(mintable).to.equal(true) - - switchMintable = await TicketContract.connect(deployer).switchMintable() - - mintable = await TicketContract.mintable() - expect(mintable).to.equal(false) - }) - - it('only admins can switch mintable flag', async () => { - let mintable - mintable = await TicketContract.mintable() - expect(mintable).to.equal(false) - - await expect(TicketContract.connect(user3).switchMintable()).to.be.revertedWith('Admins only') - - mintable = await TicketContract.mintable() - expect(mintable).to.equal(false) - }) -}) - -describe('check timestamp', () => { - let TicketContract: PublicTicket - let deployer: SignerWithAddress - let creator: SignerWithAddress - let user1: SignerWithAddress - let user2: SignerWithAddress - - const day = 24 * 60 * 60 - const hour = 60 * 60 - const minute = 60 - const second = 1 - - const calcRemainingTime = (time: number) => { - const remainingTime = time - - const days = Math.floor(remainingTime / day) - const hours = Math.floor((remainingTime % day) / hour) - const minutes = Math.floor((remainingTime % hour) / minute) - const seconds = Math.floor((remainingTime % minute) / second) - const returnTime = `${days}日 ${hours}時間 ${minutes}分 ${seconds}秒` - - return returnTime - } - - before(async () => { - ;[deployer, creator, user1, user2] = await ethers.getSigners() - const TicketFactory = (await ethers.getContractFactory('PublicTicket')) as PublicTicket__factory - TicketContract = await TicketFactory.deploy( - 'Henkaku Ticket', - 'HNJ', - open_blockTimestamp, - close_blockTimestamp, - deployer.address - ) - await TicketContract.deployed() - }) - - it('check remaining open time', async () => { - const checkRemainingOpenTime = await TicketContract.callStatic.checkRemainingOpenTime() - expect(checkRemainingOpenTime.toNumber()).to.below(4676081) - }) - - it('check remaining close time', async () => { - const checkRemainingCloseTime = await TicketContract.callStatic.checkRemainingCloseTime() - expect(checkRemainingCloseTime.toNumber()).to.below(36212059) - }) -}) - -describe('after minting term', () => { - let TicketContract: PublicTicket - let deployer: SignerWithAddress - let creator: SignerWithAddress - let user1: SignerWithAddress - let user2: SignerWithAddress - - const day = 24 * 60 * 60 - const hour = 60 * 60 - const minute = 60 - const second = 1 - - const calcRemainingTime = (time: number) => { - const remainingTime = time - - const days = Math.floor(remainingTime / day) - const hours = Math.floor((remainingTime % day) / hour) - const minutes = Math.floor((remainingTime % hour) / minute) - const seconds = Math.floor((remainingTime % minute) / second) - const returnTime = `${days}日 ${hours}時間 ${minutes}分 ${seconds}秒` - - return returnTime - } - - before(async () => { - ;[deployer, creator, user1, user2] = await ethers.getSigners() - const TicketFactory = (await ethers.getContractFactory('PublicTicket')) as PublicTicket__factory - TicketContract = await TicketFactory.deploy('Henkaku Ticket', 'HNJ', 946652400, 946652400, deployer.address) - await TicketContract.deployed() - await TicketContract.addAdmins([creator.address]) - }) - - it('check remaining open time', async () => { - const checkRemainingOpenTime = await TicketContract.callStatic.checkRemainingOpenTime() - expect(checkRemainingOpenTime.toNumber()).to.equal(0) - }) - - it('check remaining close time', async () => { - const checkRemainingCloseTime = await TicketContract.callStatic.checkRemainingCloseTime() - expect(checkRemainingCloseTime.toNumber()).to.equal(0) - }) - - it('failed with minting time', async () => { - const checkRemainingOpenTime = await TicketContract.callStatic.checkRemainingOpenTime() - - const checkRemainingCloseTime = await TicketContract.callStatic.checkRemainingCloseTime() - await TicketContract.connect(creator).registerTicket(1, 'ipfs://test1') - const tokenURI = await TicketContract.uri(1) - expect(tokenURI).equal('ipfs://test1') - - let mintable - mintable = await TicketContract.mintable() - expect(mintable).to.equal(false) - - if (checkRemainingOpenTime || (!checkRemainingCloseTime && !mintable)) { - await expect(TicketContract.connect(user1).mint(1)).to.be.revertedWith('Ticket: Not mintable') - } - }) -}) diff --git a/test/public_ticket_mx.ts b/test/public_ticket_mx.ts deleted file mode 100644 index 3278103..0000000 --- a/test/public_ticket_mx.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { ethers as hardhatEthers } from 'hardhat' -import { ethers } from 'ethers' -import { Forwarder, Forwarder__factory, PublicTicket, PublicTicket__factory } from '../typechain-types' -import ethSignUtil from 'eth-sig-util' -import { expect } from 'chai' - -const open_blockTimestamp: number = 0 -const close_blockTimestamp: number = 2704034800 - -describe('Mint via Fowarder', () => { - let TicketContract: PublicTicket - let ForwarderContract: Forwarder - let deployer: SignerWithAddress - let creator: SignerWithAddress - let user1: SignerWithAddress - - before(async () => { - ;[deployer, creator, user1] = await hardhatEthers.getSigners() - const ForwarderFactory = (await hardhatEthers.getContractFactory('Forwarder')) as Forwarder__factory - ForwarderContract = await ForwarderFactory.connect(deployer).deploy() - await ForwarderContract.deployed() - const TicketFactory = (await hardhatEthers.getContractFactory('PublicTicket')) as PublicTicket__factory - TicketContract = await TicketFactory.connect(deployer).deploy( - 'Henkaku Ticket', - 'HNJ', - open_blockTimestamp, - close_blockTimestamp, - ForwarderContract.address - ) - await TicketContract.deployed() - - await ForwarderContract.whitelistTarget(TicketContract.address, true) - const x = TicketContract.interface.encodeFunctionData('mint', [1]).substring(0, 10) - await ForwarderContract.whitelistMethod(TicketContract.address, x, true) - - await TicketContract.addAdmins([creator.address]) - await TicketContract.connect(creator).registerTicket(100, 'https://test.com') - }) - - it('mint Ticket', async () => { - const from = user1.address - const data = TicketContract.interface.encodeFunctionData('mint', [1]) - const to = TicketContract.address - - const { request, signature } = await signMetaTxRequest(user1.provider, ForwarderContract, { - to, - from, - data, - }) - - await ForwarderContract.execute(request, signature) - - const balance = await TicketContract.balanceOf(user1.address, 1) - expect(balance.toNumber()).to.equal(1) - }) - - it('tx fail when execute not allowed target', async () => { - const from = user1.address - const data = TicketContract.interface.encodeFunctionData('mint', [[1]]) - - const { request, signature } = await signMetaTxRequest(user1.provider, ForwarderContract, { - to: '0xcbEAF3BDe82155F56486Fb5a1072cb8baAf547cc', - from, - data, - }) - - await expect(ForwarderContract.execute(request, signature)).to.be.rejectedWith( - 'Forwarder: signature does not match request' - ) - }) - - it('tx fail when execute not allowed method', async () => { - const from = user1.address - const data = TicketContract.interface.encodeFunctionData('mintBatch', [[1]]) - const to = TicketContract.address - - const { request, signature } = await signMetaTxRequest(user1.provider, ForwarderContract, { - to, - from, - data, - }) - - await expect(ForwarderContract.execute(request, signature)).to.be.rejectedWith( - 'Forwarder: signature does not match request' - ) - }) -}) - -const EIP712Domain = [ - { name: 'name', type: 'string' }, - { name: 'version', type: 'string' }, - { name: 'chainId', type: 'uint256' }, - { name: 'verifyingContract', type: 'address' }, -] - -// forwarder request struct -// ref: lib/openzeppelin-contracts/contracts/metatx/MinimalForwarder.sol -const ForwardRequest = [ - { name: 'from', type: 'address' }, - { name: 'to', type: 'address' }, - { name: 'value', type: 'uint256' }, - { name: 'gas', type: 'uint256' }, - { name: 'nonce', type: 'uint256' }, - { name: 'data', type: 'bytes' }, -] - -const getMetaTxTypeData = (chainId: number, verifyingContract: string) => { - // Specification of the eth_signTypedData JSON RPC - return { - types: { - EIP712Domain, - ForwardRequest, - }, - domain: { - name: 'Forwarder', - version: '0.0.1', - chainId, - verifyingContract, - }, - primaryType: 'ForwardRequest', - } -} - -const signTypeData = async (signer: any, from: string, data: any) => { - if (typeof signer === 'string') { - const privateKey = Buffer.from(signer.replace(/^0x/, ''), 'hex') - return ethSignUtil.signTypedMessage(privateKey, { data }) - } - - const [method, argData] = ['eth_signTypedData_v4', JSON.stringify(data)] - return await signer.send(method, [from, argData]) -} - -export const buildRequest = async (forwarder: ethers.Contract, input: any) => { - const nonce = await forwarder.getNonce(input.from).then((nonce: { toString: () => any }) => nonce.toString()) - return { value: 0, gas: 1e6, nonce, ...input } -} - -export const buildTypedData = async (forwarder: ethers.Contract, request: any) => { - const chainId = await forwarder.provider.getNetwork().then((n) => n.chainId) - const typeData = getMetaTxTypeData(chainId, forwarder.address) - return { ...typeData, message: request } -} - -export const signMetaTxRequest = async (signer: any, forwarder: ethers.Contract, input: any) => { - const request = await buildRequest(forwarder, input) - const toSign = await buildTypedData(forwarder, request) - const signature = await signTypeData(signer, input.from, toSign) - return { signature, request } -} diff --git a/test/upgrade.ts b/test/upgrade.ts new file mode 100644 index 0000000..bf18015 --- /dev/null +++ b/test/upgrade.ts @@ -0,0 +1,110 @@ +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import { CommunityToken, Ticket, TicketV2 } from '../typechain-types' +import { ethers, upgrades } from 'hardhat' +import { deployAndDistributeCommunityToken, deployTicket } from './helper/deploy' +import { expect } from 'chai' + +describe('RegisterTicket', () => { + let TicketContract: Ticket | TicketV2 + let CommunityTokenContract: CommunityToken + let deployer: SignerWithAddress + let creator: SignerWithAddress + let user1: SignerWithAddress + let user2: SignerWithAddress + let user3: SignerWithAddress + + before(async () => { + ;[deployer, creator, user1, user2, user3] = await ethers.getSigners() + CommunityTokenContract = await deployAndDistributeCommunityToken({ + deployer, + addresses: [creator.address, user1.address, user2.address, user3.address, deployer.address], + amount: ethers.utils.parseEther('1000'), + }) + TicketContract = await deployTicket(CommunityTokenContract.address) + }) + + it('register creative', async () => { + let now = (await ethers.provider.getBlock('latest')).timestamp + + // @dev test emit register creative + await expect( + TicketContract.connect(creator).registerTicket( + 2, + 'ipfs://test1', + 100, + now, + now + 1000000000000, + [creator.address, deployer.address], + [60, 40] + ) + ) + .to.emit(TicketContract, 'RegisterTicket') + .withArgs( + creator.address, + now, + now + 1000000000000, + 2, + 1, + 100, + 'ipfs://test1', + [60, 40], + [creator.address, deployer.address] + ) + + let tokenURI = await TicketContract.uri(1) + expect(tokenURI).equal('ipfs://test1') + + let getAllRegisteredTickets = await TicketContract.retrieveAllTickets() + expect(getAllRegisteredTickets.length).to.equal(2) + expect(getAllRegisteredTickets[1].uri).to.equal('ipfs://test1') + expect(getAllRegisteredTickets[1].creator).to.equal(creator.address) + expect(getAllRegisteredTickets[1].maxSupply).to.equal(2) + + let getRegisteredTicket = await TicketContract.retrieveRegisteredTicket(1) + expect(getRegisteredTicket.uri).to.equal('ipfs://test1') + expect(getRegisteredTicket.creator).to.equal(creator.address) + expect(getRegisteredTicket.maxSupply).to.equal(2) + + const registeredTickets = await TicketContract.retrieveRegisteredTickets(creator.address) + expect(registeredTickets[0].uri).to.equal('ipfs://test1') + expect(registeredTickets[0].creator).to.equal(creator.address) + expect(registeredTickets[0].maxSupply).to.equal(2) + }) + + it('upgrade ticket contract', async () => { + const TicketV2Factory = await ethers.getContractFactory('TicketV2') + const TicketV2 = (await upgrades.upgradeProxy(TicketContract.address, TicketV2Factory)) as TicketV2 + + await TicketV2.deployed() + + expect(TicketV2.address).to.equal(TicketContract.address) + + TicketContract = TicketV2 + }) + + it('new function', async () => { + const val = await (TicketContract as TicketV2).newFunction() + expect(val).to.equal(1) + }) + + it('retrieve registered ticket v1', async () => { + let tokenURI = await TicketContract.uri(1) + expect(tokenURI).equal('ipfs://test1') + + let getAllRegisteredTickets = await TicketContract.retrieveAllTickets() + expect(getAllRegisteredTickets.length).to.equal(2) + expect(getAllRegisteredTickets[1].uri).to.equal('ipfs://test1') + expect(getAllRegisteredTickets[1].creator).to.equal(creator.address) + expect(getAllRegisteredTickets[1].maxSupply).to.equal(2) + + let getRegisteredTicket = await TicketContract.retrieveRegisteredTicket(1) + expect(getRegisteredTicket.uri).to.equal('ipfs://test1') + expect(getRegisteredTicket.creator).to.equal(creator.address) + expect(getRegisteredTicket.maxSupply).to.equal(2) + + const registeredTickets = await TicketContract.retrieveRegisteredTickets(creator.address) + expect(registeredTickets[0].uri).to.equal('ipfs://test1') + expect(registeredTickets[0].creator).to.equal(creator.address) + expect(registeredTickets[0].maxSupply).to.equal(2) + }) +}) diff --git a/yarn.lock b/yarn.lock index 4ac42ab..6730a7b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,38 @@ # yarn lockfile v1 +"@aws-crypto/sha256-js@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-1.2.2.tgz#02acd1a1fda92896fc5a28ec7c6e164644ea32fc" + integrity sha512-Nr1QJIbW/afYYGzYvrF70LtaHrIRtd4TNAglX8BvlfxJLZ45SAmueIKYl5tWoNBPzp65ymXGFK0Bb1vZUpuc9g== + dependencies: + "@aws-crypto/util" "^1.2.2" + "@aws-sdk/types" "^3.1.0" + tslib "^1.11.1" + +"@aws-crypto/util@^1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-1.2.2.tgz#b28f7897730eb6538b21c18bd4de22d0ea09003c" + integrity sha512-H8PjG5WJ4wz0UXAFXeJjWCW1vkvIJ3qUUD+rGRwJ2/hj+xT58Qle2MTql/2MGzkU+1JLAFuR6aJpLAjHwhmwwg== + dependencies: + "@aws-sdk/types" "^3.1.0" + "@aws-sdk/util-utf8-browser" "^3.0.0" + tslib "^1.11.1" + +"@aws-sdk/types@^3.1.0": + version "3.347.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.347.0.tgz#4affe91de36ef227f6375d64a6efda8d4ececd5d" + integrity sha512-GkCMy79mdjU9OTIe5KT58fI/6uqdf8UmMdWqVHmFJ+UpEzOci7L/uw4sOXWo7xpPzLs6cJ7s5ouGZW4GRPmHFA== + dependencies: + tslib "^2.5.0" + +"@aws-sdk/util-utf8-browser@^3.0.0": + version "3.259.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz#3275a6f5eb334f96ca76635b961d3c50259fd9ff" + integrity sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw== + dependencies: + tslib "^2.3.1" + "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -9,7 +41,7 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.0.0-beta.146", "@ethersproject/abi@^5.0.9", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.4.7", "@ethersproject/abi@^5.7.0": +"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.0.0-beta.146", "@ethersproject/abi@^5.0.9", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.4.7", "@ethersproject/abi@^5.6.3", "@ethersproject/abi@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== @@ -659,11 +691,41 @@ table "^6.8.0" undici "^5.4.0" +"@openzeppelin/contracts-upgradeable@^4.9.0": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.9.0.tgz#70aaef469c8ac5bb0ff781480f3d321cbf7be3a8" + integrity sha512-+6i2j6vr2fdudTqkBvG+UOosankukxYzg3WN1nqU7ijjQ5A4osWaD3ip6CEz6YvDoSdZgcFVZoiGr7zRlUUoZw== + "@openzeppelin/contracts@^4.7.3": version "4.8.0" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.8.0.tgz#6854c37df205dd2c056bdfa1b853f5d732109109" integrity sha512-AGuwhRRL+NaKx73WKRNzeCxOCOCxpaqF+kp8TJ89QzAipSwZy/NoflkWaL9bywXFRhIzXt8j38sfF7KBKCPWLw== +"@openzeppelin/hardhat-upgrades@^1.27.0": + version "1.27.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/hardhat-upgrades/-/hardhat-upgrades-1.27.0.tgz#0e304041d72c97979c76466137b48733120270fd" + integrity sha512-+OwrHWDz9tzpmBev6t2CtZM2tRpy/ybhg5e3GIgyeqTxK2vUV40dxIxO6lie+qKeJHZ2RIdDwvSTSiCEJif+fA== + dependencies: + "@openzeppelin/upgrades-core" "^1.26.2" + chalk "^4.1.0" + debug "^4.1.1" + defender-base-client "^1.44.0" + platform-deploy-client "^0.6.0" + proper-lockfile "^4.1.1" + +"@openzeppelin/upgrades-core@^1.26.2": + version "1.26.2" + resolved "https://registry.yarnpkg.com/@openzeppelin/upgrades-core/-/upgrades-core-1.26.2.tgz#6ac3d16dca21d9bd76bd55a8a34121b4a78dd2c2" + integrity sha512-TJORrgyun5qflPos/47P3j61gDw+7W+tEirSBOYRxfVL1WGjX1n8iaLrijPIqzyeS1MKguN1nckAMspQ4SKrxw== + dependencies: + cbor "^8.0.0" + chalk "^4.1.0" + compare-versions "^5.0.0" + debug "^4.1.1" + ethereumjs-util "^7.0.3" + proper-lockfile "^4.1.1" + solidity-ast "^0.4.15" + "@rollup/plugin-commonjs@^24.0.0": version "24.0.0" resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-24.0.0.tgz#fb7cf4a6029f07ec42b25daa535c75b05a43f75c" @@ -1054,6 +1116,17 @@ amazon-cognito-identity-js@^4.3.3: isomorphic-unfetch "^3.0.0" js-cookie "^2.2.1" +amazon-cognito-identity-js@^6.0.1: + version "6.2.0" + resolved "https://registry.yarnpkg.com/amazon-cognito-identity-js/-/amazon-cognito-identity-js-6.2.0.tgz#99e96666944429cb8f67b62e4cf7ad77fbe71ad0" + integrity sha512-9Fxrp9+MtLdsJvqOwSaE3ll+pneICeuE3pwj2yDkiyGNWuHx97b8bVLR2bOgfDmDJnY0Hq8QoeXtwdM4aaXJjg== + dependencies: + "@aws-crypto/sha256-js" "1.2.2" + buffer "4.9.2" + fast-base64-decode "^1.0.0" + isomorphic-unfetch "^3.0.0" + js-cookie "^2.2.1" + amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" @@ -1459,7 +1532,7 @@ catering@^2.1.0, catering@^2.1.1: resolved "https://registry.yarnpkg.com/catering/-/catering-2.1.1.tgz#66acba06ed5ee28d5286133982a927de9a04b510" integrity sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w== -cbor@^8.1.0: +cbor@^8.0.0, cbor@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.1.0.tgz#cfc56437e770b73417a2ecbfc9caf6b771af60d5" integrity sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg== @@ -1671,6 +1744,11 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== +compare-versions@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-5.0.3.tgz#a9b34fea217472650ef4a2651d905f42c28ebfd7" + integrity sha512-4UZlZP8Z99MGEY+Ovg/uJxJuvoXuN4M6B3hKaiackiHrgzQFEe3diJi1mf1PNHbFujM7FvLrK2bpgIaImbtZ1A== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -1826,6 +1904,17 @@ defender-base-client@1.37.0: lodash "^4.17.19" node-fetch "^2.6.0" +defender-base-client@^1.44.0: + version "1.44.0" + resolved "https://registry.yarnpkg.com/defender-base-client/-/defender-base-client-1.44.0.tgz#afe724447c0f9177b999b70b9f14dd70d61d5a7a" + integrity sha512-8ZgGA93+FlxNwG9LN1nu/Au5AyCKwAWJGNf0VLiPmh4GX/Nali/7kv72K+OtZgGxTLtKDKfgN4cnhEZwfrc8dg== + dependencies: + amazon-cognito-identity-js "^6.0.1" + async-retry "^1.3.3" + axios "^0.21.2" + lodash "^4.17.19" + node-fetch "^2.6.0" + define-properties@^1.1.2, define-properties@^1.1.3, define-properties@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" @@ -2134,7 +2223,7 @@ ethereumjs-util@^6.0.0, ethereumjs-util@^6.2.1: ethjs-util "0.1.6" rlp "^2.2.3" -ethereumjs-util@^7.1.0, ethereumjs-util@^7.1.4: +ethereumjs-util@^7.0.3, ethereumjs-util@^7.1.0, ethereumjs-util@^7.1.4: version "7.1.5" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz#9ecf04861e4fbbeed7465ece5f23317ad1129181" integrity sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg== @@ -2614,6 +2703,11 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== +graceful-fs@^4.2.4: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + growl@1.10.5: version "1.10.5" resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" @@ -3872,6 +3966,17 @@ pify@^4.0.1: resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== +platform-deploy-client@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/platform-deploy-client/-/platform-deploy-client-0.6.0.tgz#1f7a59d0cbae2e78f65ad253d7cc1aff67e3de0e" + integrity sha512-mBfnOvF2gb9acGJjlXBQ6VOAkKFRdljsNKHUVY5xKqzKP2PNh/RqCIvi5AR5NqLMrQ3XaMIwRvmwAjtGw7JhYg== + dependencies: + "@ethersproject/abi" "^5.6.3" + axios "^0.21.2" + defender-base-client "^1.44.0" + lodash "^4.17.19" + node-fetch "^2.6.0" + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -3894,6 +3999,15 @@ promise@^8.0.0: dependencies: asap "~2.0.6" +proper-lockfile@^4.1.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-4.1.2.tgz#c8b9de2af6b2f1601067f98e01ac66baa223141f" + integrity sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA== + dependencies: + graceful-fs "^4.2.4" + retry "^0.12.0" + signal-exit "^3.0.2" + psl@^1.1.28: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" @@ -4104,6 +4218,11 @@ retry@0.13.1: resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== + reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" @@ -4298,6 +4417,11 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" +signal-exit@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -4327,6 +4451,11 @@ solc@0.7.3: semver "^5.5.0" tmp "0.0.33" +solidity-ast@^0.4.15: + version "0.4.49" + resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.49.tgz#ecba89d10c0067845b7848c3a3e8cc61a4fc5b82" + integrity sha512-Pr5sCAj1SFqzwFZw1HPKSq0PehlQNdM8GwKyAVYh2DOn7/cCK8LUKD1HeHnKtTgBW7hi9h4nnnan7hpAg5RhWQ== + solidity-coverage@^0.8.0: version "0.8.2" resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.8.2.tgz#bc39604ab7ce0a3fa7767b126b44191830c07813" @@ -4676,11 +4805,16 @@ ts-node@>=8.0.0: v8-compile-cache-lib "^3.0.1" yn "3.1.1" -tslib@^1.9.3: +tslib@^1.11.1, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.3.1, tslib@^2.5.0: + version "2.5.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" + integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== + tsort@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/tsort/-/tsort-0.0.1.tgz#e2280f5e817f8bf4275657fd0f9aebd44f5a2786"