LCOV - code coverage report
Current view: top level - src/contracts - TicketNFT.sol (source / functions) Hit Total Coverage
Test: TicketNFT Coverage Lines: 71 71 100.0 %
Date: 2023-02-21 16:00:17 Functions: 14 14 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 52 52 100.0 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: UNLICENSED
       2                 :            : pragma solidity ^0.8.10;
       3                 :            : 
       4                 :            : import "../interfaces/IERC20.sol";
       5                 :            : import "../interfaces/ITicketNFT.sol";
       6                 :            : import "../interfaces/IPrimaryMarket.sol";
       7                 :            : import "../interfaces/ISecondaryMarket.sol";
       8                 :            : 
       9                 :            : contract TicketNFT is ITicketNFT, IPrimaryMarket, ISecondaryMarket {
      10                 :            :     address _admin;
      11                 :            :     uint256 _totalSupply;
      12                 :            :     IERC20 public _purchaseToken;
      13                 :            :     mapping(uint256 => address) _approvals;
      14                 :            :     mapping(address => uint256) _balances;
      15                 :            :     mapping(uint256 => uint256) public _expiryTimes;
      16                 :            :     mapping(uint256 => string) _holderNames;
      17                 :            :     mapping(uint256 => address) _holders;
      18                 :            :     mapping(uint256 => address) _listers;
      19                 :            :     mapping(uint256 => uint256) _prices;
      20                 :            :     mapping(uint256 => bool) _ticketUsed;
      21                 :            :     uint256 immutable _purchasePrice = 100e18;
      22                 :            :     uint256 immutable _saleFee = 50;
      23                 :            : 
      24                 :            :     constructor(IERC20 purchaseToken) {
      25                 :            :         _admin = msg.sender;
      26                 :            :         _totalSupply = 0;
      27                 :            :         _purchaseToken = purchaseToken;
      28                 :            :     }
      29                 :            : 
      30                 :            :     function mint(address holder, string memory holderName) external {
      31         [ +  + ]:         39 :         require(
      32                 :            :             msg.sender == address(this),
      33                 :            :             "mint: can only be called by primary market"
      34                 :            :         ); // Limits to being called by this contract and only purchase function calls mint
      35                 :         38 :         uint256 ticketId = _totalSupply + 1;
      36                 :         38 :         _balances[holder] += 1;
      37                 :         38 :         _expiryTimes[ticketId] = block.timestamp + 10 days;
      38                 :         38 :         _holderNames[ticketId] = holderName;
      39                 :         38 :         _holders[ticketId] = holder;
      40                 :         38 :         _ticketUsed[ticketId] = false;
      41                 :         38 :         _totalSupply = ticketId;
      42                 :         38 :         emit Transfer(address(0), holder, ticketId);
      43                 :            :     }
      44                 :            : 
      45                 :            :     function balanceOf(address holder) external view returns (uint256) {
      46                 :         48 :         return _balances[holder];
      47                 :            :     }
      48                 :            : 
      49                 :            :     function holderOf(uint256 ticketID) external view returns (address) {
      50         [ +  + ]:         17 :         require(ticketID <= _totalSupply, "holderOf: ticket doesn't exist");
      51                 :         16 :         return _holders[ticketID];
      52                 :            :     }
      53                 :            : 
      54                 :            :     function transferFrom(
      55                 :            :         address from,
      56                 :            :         address to,
      57                 :            :         uint256 ticketID
      58                 :            :     ) external {
      59         [ +  + ]:         22 :         require(from != address(0), "transferFrom: from cannot be 0");
      60         [ +  + ]:         20 :         require(to != address(0), "transferFrom: to cannot be 0");
      61         [ +  + ]:         18 :         require(
      62                 :            :             msg.sender == _holders[ticketID] ||
      63                 :            :                 msg.sender == _approvals[ticketID] ||
      64                 :            :                 (msg.sender == address(this) &&
      65                 :            :                     (from == _holders[ticketID] ||
      66                 :            :                         from == _approvals[ticketID])),
      67                 :            :             "transferFrom: msg.sender must be current holder or approved sender"
      68                 :            :         );
      69         [ +  + ]:         16 :         require(
      70                 :            :             from == _holders[ticketID],
      71                 :            :             "transferFrom: ticket not owned by from"
      72                 :            :         );
      73                 :         14 :         _approvals[ticketID] = address(0);
      74                 :         14 :         _balances[from] -= 1;
      75                 :         14 :         _balances[to] += 1;
      76                 :         14 :         _holders[ticketID] = to;
      77                 :         14 :         emit Transfer(from, to, ticketID);
      78                 :         14 :         emit Approval(to, address(0), ticketID);
      79                 :            :     }
      80                 :            : 
      81                 :            :     function approve(address to, uint256 ticketID) external {
      82         [ +  + ]:          6 :         require(ticketID <= _totalSupply, "approve: ticket doesn't exist");
      83         [ +  + ]:          5 :         require(
      84                 :            :             msg.sender == _holders[ticketID],
      85                 :            :             "approve: msg.sender doesn't own ticket"
      86                 :            :         );
      87                 :          4 :         _approvals[ticketID] = to;
      88                 :          4 :         emit Approval(msg.sender, to, ticketID);
      89                 :            :     }
      90                 :            : 
      91                 :            :     function getApproved(uint256 ticketID) external view returns (address) {
      92         [ +  + ]:         14 :         require(ticketID <= _totalSupply, "getApproved: ticket doesn't exist");
      93                 :         13 :         return _approvals[ticketID];
      94                 :            :     }
      95                 :            : 
      96                 :            :     function holderNameOf(uint256 ticketID)
      97                 :            :         external
      98                 :            :         view
      99                 :            :         returns (string memory)
     100                 :            :     {
     101         [ +  + ]:         13 :         require(ticketID <= _totalSupply, "holderNameOf: ticket doesn't exist");
     102                 :         12 :         return _holderNames[ticketID];
     103                 :            :     }
     104                 :            : 
     105                 :            :     function updateHolderName(uint256 ticketID, string calldata newName)
     106                 :            :         external
     107                 :            :     {
     108         [ +  + ]:          3 :         require(
     109                 :            :             ticketID <= _totalSupply,
     110                 :            :             "updateHolderName: ticket doesn't exist"
     111                 :            :         );
     112         [ +  + ]:          2 :         require(
     113                 :            :             msg.sender == _holders[ticketID],
     114                 :            :             "updateHolderName: msg.sender doesn't own ticket"
     115                 :            :         );
     116                 :          1 :         _holderNames[ticketID] = newName;
     117                 :            :     }
     118                 :            : 
     119                 :            :     function setUsed(uint256 ticketID) external {
     120         [ +  + ]:          8 :         require(msg.sender == _admin, "setUsed: only admin can setUsed");
     121         [ +  + ]:          7 :         require(ticketID <= _totalSupply, "setUsed: ticket doesn't exist");
     122         [ +  + ]:          6 :         require(_ticketUsed[ticketID] == false, "setUsed: ticket already used");
     123         [ +  + ]:          5 :         require(
     124                 :            :             _expiryTimes[ticketID] >= block.timestamp,
     125                 :            :             "setUsed: ticket expired"
     126                 :            :         );
     127                 :          4 :         _ticketUsed[ticketID] = true;
     128                 :            :     }
     129                 :            : 
     130                 :            :     function isExpiredOrUsed(uint256 ticketID) external view returns (bool) {
     131         [ +  + ]:         18 :         require(
     132                 :            :             ticketID <= _totalSupply,
     133                 :            :             "isExpiredOrUsed: ticket doesn't exist"
     134                 :            :         );
     135                 :         17 :         return (_ticketUsed[ticketID] ||
     136                 :            :             _expiryTimes[ticketID] < block.timestamp);
     137                 :            :     }
     138                 :            : 
     139                 :            :     function admin() external view returns (address) {
     140                 :          1 :         return _admin;
     141                 :            :     }
     142                 :            : 
     143                 :            :     function purchase(string memory holderName) external {
     144         [ +  + ]:         40 :         require(_totalSupply < 1000, "purchase: maximum tickets reached");
     145         [ +  + ]:         39 :         require(
     146                 :            :             _purchaseToken.allowance(msg.sender, address(this)) >=
     147                 :            :                 _purchasePrice,
     148                 :            :             "purchase: insufficient token allowance"
     149                 :            :         );
     150                 :         38 :         _purchaseToken.transferFrom(msg.sender, _admin, _purchasePrice);
     151                 :         37 :         this.mint(msg.sender, holderName);
     152                 :         37 :         emit Purchase(msg.sender, holderName);
     153                 :            :     }
     154                 :            : 
     155                 :            :     function listTicket(uint256 ticketID, uint256 price) external {
     156         [ +  + ]:         13 :         require(
     157                 :            :             msg.sender == _holders[ticketID],
     158                 :            :             "listTicket: msg.sender doesn't own ticket"
     159                 :            :         );
     160         [ +  + ]:         12 :         require(
     161                 :            :             _ticketUsed[ticketID] == false &&
     162                 :            :                 _expiryTimes[ticketID] >= block.timestamp,
     163                 :            :             "listTicket: ticket is expired/used"
     164                 :            :         );
     165         [ +  + ]:         10 :         require(price > 0, "listTicket: price cannot be 0");
     166                 :          9 :         _listers[ticketID] = msg.sender;
     167                 :          9 :         _prices[ticketID] = price;
     168                 :          9 :         this.transferFrom(msg.sender, address(this), ticketID);
     169                 :          9 :         emit Listing(ticketID, msg.sender, price);
     170                 :            :     }
     171                 :            : 
     172                 :            :     function purchase(uint256 ticketID, string calldata name) external {
     173         [ +  + ]:          6 :         require(
     174                 :            :             _listers[ticketID] != address(0) && _prices[ticketID] != 0,
     175                 :            :             "purchase: ticket is not listed, missing lister address or price"
     176                 :            :         );
     177                 :          5 :         uint256 ticketPrice = _prices[ticketID];
     178                 :          5 :         uint256 ticketFee = _saleFee * (ticketPrice / 1000);
     179                 :          5 :         uint256 ticketRevenue = ticketPrice - ticketFee;
     180         [ +  + ]:          5 :         require(
     181                 :            :             _ticketUsed[ticketID] == false &&
     182                 :            :                 _expiryTimes[ticketID] >= block.timestamp,
     183                 :            :             "purchase: ticket is expired/used"
     184                 :            :         );
     185         [ +  + ]:          3 :         require(
     186                 :            :             _purchaseToken.allowance(msg.sender, address(this)) >= ticketPrice,
     187                 :            :             "purchase: insufficient token allowance"
     188                 :            :         );
     189                 :          2 :         _purchaseToken.transferFrom(
     190                 :            :             msg.sender,
     191                 :            :             _listers[ticketID],
     192                 :            :             ticketRevenue
     193                 :            :         );
     194                 :          1 :         _purchaseToken.transferFrom(msg.sender, _admin, ticketFee);
     195                 :          1 :         this.transferFrom(address(this), msg.sender, ticketID);
     196                 :          1 :         _listers[ticketID] = address(0);
     197                 :          1 :         _prices[ticketID] = 0;
     198                 :          1 :         _holderNames[ticketID] = name;
     199                 :          1 :         emit Purchase(msg.sender, ticketID, ticketPrice, name);
     200                 :            :     }
     201                 :            : 
     202                 :            :     function delistTicket(uint256 ticketID) external {
     203         [ +  + ]:          3 :         require(
     204                 :            :             msg.sender == _listers[ticketID],
     205                 :            :             "delistTicket: msg.sender didn't list ticket"
     206                 :            :         );
     207                 :          2 :         _listers[ticketID] = address(0);
     208                 :          2 :         _prices[ticketID] = 0;
     209                 :          2 :         this.transferFrom(address(this), msg.sender, ticketID);
     210                 :          2 :         emit Delisting(ticketID);
     211                 :            :     }
     212                 :            : }

Generated by: LCOV version 1.14