Pinkslip Finance - Smart Contract Audit Report
Summary
Pinkslip Finance is building a new ERC-20 token with a crowdsale platform, a racing game, and a yield farming platform that ties it all together.
We reviewed Pinkslip Finance's PinkslipToken, PinkslipSale, PinkslipNFT, PinkslipRacing, PinkslipTicket, and PinkslipStake contracts using code provided to us by the project team.
Notes on Individual Contracts:
PinkslipToken contract:PinkslipSale contract:
- The total supply of the Pinkslip token is set to 30 million tokens, and is minted to the owner on initialization.
- No mint functions are present, but any user may burn their own tokens or those for which they are given approval.
- Transfers are initially locked and must be unlocked by the owner; users on the lock whitelist are exempt.
- The owner is able to release the lock to enable transfers at any time; once unlocked, transfers may not be locked again.
- The owner can enforce a max transaction amount only once for a period of 24 hours; whitelisted users are exempt from the max transaction amount.
- The owner is able to add or remove any address from the whitelist and the lock whitelist at any time.
PinkslipNFT contract:
- The crowdsale is active until the hard cap of 500 ETH has been met, or unless the owner has manually paused the system.
- While the crowdsale is active, users must deposit at least 0.1 ETH (up to a total contribution limit of 3 ETH per user) and in return will receive 10,909 tokens per ETH deposited.
- Any amount of ETH deposited over the hard cap is returned to the user, and the remaining amount is transferred to the funding address controlled by the team.
- The owner can pause or unpause the crowdsale at any time.
- The owner can transfer all of the crowdsale tokens in the contract to the funding address controlled by the team at any time.
PinkslipRacing contract:
- Anyone can use the PinkslipNFT contract to purchase up to 3 NFTs representing a car that can be used in the PinkslipRacing contract.
- Alternatively, any wallet or contract address with the Minter role can mint any valid car to any wallet address.
- There are 3 types of cars that users can purchase; prices vary depending on car type and are paid in the fee token defined on initialization.
- Users can purchase an unlimited amount of cars of type 1 or 2, but there is a limited supply of 1,000 cars of type 3 can ever be minted.
- A portion of the fee token is transferred to the staking fees wallet and the remainder is sent to the platform fees wallet; both wallets are controlled by the team.
- The owner has the default Admin role and the Minter role.
- The owner can set the prices for the car types to any value at any time.
PinkslipTicket contract:
- Anyone can create a proposal for a race in at most 1 of 50 slots, given the slot is available and the user is not currently in a slot.
- The race creator must provide the ID of the Pinkslip NFT car they wish to use and wager in the race and the amount they wish to wager as a reward for winning the race; the wager amount must be at least 5 tokens.
- The race creator must ensure they have enough balance to cover the wager amount, the contract address is approved for at least the wager amount, they are the owner of the Pinkslip NFT car, the contract address is approved for the Pinkslip NFT car, and the end date of the race (24 hours from creation) has not passed in order to secure the race slot.
- If the race slot has not been secured, it can be overwritten by another user.
- Only one user can accept another user's race proposal as long as the end time of the race has not passed and the proper approvals are in place.
- On acceptance, the winner is randomly selected and will receive the wagered amount and Pinkslip NFT car.
- The randomness function used to determine the winner relies on some predictable variables that can be manipulated by miners to some extent. This is common, albiet not best practice, but the probability of miners maliciously changing these variables is extremley low.
- Odds of winning the race also varies depending on the cars chosen by both players; car type 1 grants the user 3 points, car type 2 grants 5 points, and car type 3 grants the user 7 points.
- Users are able to check the car type of the race creator prior to accepting a race.
- The race creator is able to cancel their race proposal as long as the end time has not passed and the race has not been accepted yet.
PinkslipStake contract:
- Users can exchange 20 Pinkslip NFT cars for 1 Pinkslip Staking Pass NFT that is needed in order to participate in the PinkslipStake staking contract.
- Alternatively, a user can purchase a Pinkslip Staking Pass NFT for 5 ETH; the ETH is sent to the fee wallet controlled by the team.
- Any wallet or contract address with the Minter role can mint a Pinkslip Staking Pass NFT to any wallet address at any time.
- The owner has the default Admin role and the minter role.
- The owner can set the price for a Pinkslip Staking Pass NFT to any value at any time.
- Users must deposit at most 1 Pinkslip Staking Pass NFT and will recieve 1000 shares in the staking pool in return.
- On withdrawals, the user forfeits their shares in the staking pool and receives rewards in Pinkslip Token.
- There is a minimum stake time enforced by the contract in order to receive all accrued rewards. In the event that the user chooses to withdraw before the minimum stake time has elapsed, half of the reward amount is burned and the remaining half is transferred to the user.
- Rewards are recalculated during any function call and can also be kicked off manually at any time.
- The owner can set the minimum stake time to any value up to 365 days at any time; initially, it is set to 30 days.
- SafeMath is utilized to prevent overflows.
Audit Findings Summary:
- No security issues from outside attackers were identified.
- Ensure trust in the team as they have notable control in the ecosystem.
- Date: August 16th, 2021.
- Updated: November 7th, 2021 to include PinkslipStaking minimum stake time requirement.
External Threat Results
Vulnerability Category | Notes | Result |
---|---|---|
Arbitrary Storage Write | N/A | PASS |
Arbitrary Jump | N/A | PASS |
Delegate Call to Untrusted Contract | N/A | PASS |
Dependence on Predictable Variables | Decisions are made based on the block.number and block.difficulty environment variables which can be manipulated by a malicious miner. This is extremley unlikely to occur. | WARNING |
Deprecated Opcodes | N/A | PASS |
Ether Thief | N/A | PASS |
Exceptions | N/A | PASS |
External Calls | N/A | PASS |
Integer Over/Underflow | N/A | PASS |
Multiple Sends | N/A | PASS |
Suicide | N/A | PASS |
State Change External Calls | N/A | Pass |
Unchecked Retval | N/A | PASS |
User Supplied Assertion | N/A | PASS |
Critical Solidity Compiler | N/A | PASS |
Overall Contract Safety | PASS |
Pinkslip Token Contract
($) = payable function
# = non-constant function
Int = Internal
Ext = External
Pub = Public
+ [Int] IERC20
- [Ext] totalSupply
- [Ext] balanceOf
- [Ext] transfer #
- [Ext] allowance
- [Ext] approve #
- [Ext] transferFrom #
+ Context
- [Int] _msgSender
- [Int] _msgData
+ [Lib] SafeMath
- [Int] tryAdd
- [Int] trySub
- [Int] tryMul
- [Int] tryDiv
- [Int] tryMod
- [Int] add
- [Int] sub
- [Int] mul
- [Int] div
- [Int] mod
- [Int] sub
- [Int] div
- [Int] mod
+ Ownable (Context)
- [Int] #
- [Pub] owner
- [Pub] renounceOwnership #
- modifiers: onlyOwner
- [Pub] transferOwnership #
- modifiers: onlyOwner
+ PinkslipToken (Context, IERC20, Ownable)
- [Pub] #
- [Pub] name
- [Pub] symbol
- [Pub] decimals
- [Pub] totalSupply
- [Pub] balanceOf
- [Pub] transfer #
- [Pub] allowance
- [Pub] approve #
- [Pub] transferFrom #
- [Pub] increaseAllowance #
- [Pub] decreaseAllowance #
- [Pub] burn #
- [Pub] burnFrom #
- [Ext] addToWhitelist #
- modifiers: onlyOwner
- [Ext] removeFromWhitelist #
- modifiers: onlyOwner
- [Ext] setMaxTxDate #
- modifiers: onlyOwner
- [Int] _transfer #
- [Int] _burn #
- [Int] _approve #
- [Ext] addToLockWhitelist #
- modifiers: onlyOwner
- [Ext] removeFromLockWhitelist #
- modifiers: onlyOwner
- [Ext] release #
- modifiers: onlyOwner
Pinkslip Sale Contract
($) = payable function
# = non-constant function
Int = Internal
Ext = External
Pub = Public
+ [Lib] SafeMath
- [Int] tryAdd
- [Int] trySub
- [Int] tryMul
- [Int] tryDiv
- [Int] tryMod
- [Int] add
- [Int] sub
- [Int] mul
- [Int] div
- [Int] mod
- [Int] sub
- [Int] div
- [Int] mod
+ ReentrancyGuard
- [Int] #
+ Context
- [Int] _msgSender
- [Int] _msgData
+ Ownable (Context)
- [Int] #
- [Pub] owner
- [Pub] renounceOwnership #
- modifiers: onlyOwner
- [Pub] transferOwnership #
- modifiers: onlyOwner
+ [Int] IERC20
- [Ext] totalSupply
- [Ext] balanceOf
- [Ext] transfer #
- [Ext] allowance
- [Ext] approve #
- [Ext] transferFrom #
+ [Lib] Address
- [Int] isContract
- [Int] sendValue #
- [Int] functionCall #
- [Int] functionCall #
- [Int] functionCallWithValue #
- [Int] functionCallWithValue #
- [Int] functionStaticCall
- [Int] functionStaticCall
- [Int] functionDelegateCall #
- [Int] functionDelegateCall #
- [Prv] _verifyCallResult
+ PinkslipSale (ReentrancyGuard, Ownable)
- [Pub] #
- [Ext] getToken
- [Ext] ($)
- [Int] sell #
- modifiers: nonReentrant,whenCrowdsaleAlive
- [Ext] totalTokensNeeded
- [Ext] stop #
- modifiers: onlyOwner
- [Ext] unstop #
- modifiers: onlyOwner
- [Ext] returnUnsold #
- modifiers: nonReentrant,onlyOwner
- [Pub] getTime
- [Pub] isActive
- [Pub] isSuccessful
Pinkslip NFT and Racing Contracts
($) = payable function
# = non-constant function
Int = Internal
Ext = External
Pub = Public
+ Context
- [Int] _msgSender
- [Int] _msgData
+ Ownable (Context)
- [Int] #
- [Pub] owner
- [Pub] renounceOwnership #
- modifiers: onlyOwner
- [Pub] transferOwnership #
- modifiers: onlyOwner
+ [Lib] SafeMath
- [Int] tryAdd
- [Int] trySub
- [Int] tryMul
- [Int] tryDiv
- [Int] tryMod
- [Int] add
- [Int] sub
- [Int] mul
- [Int] div
- [Int] mod
- [Int] sub
- [Int] div
- [Int] mod
+ ReentrancyGuard
- [Int] #
+ [Int] IERC20
- [Ext] totalSupply
- [Ext] balanceOf
- [Ext] transfer #
- [Ext] allowance
- [Ext] approve #
- [Ext] transferFrom #
+ [Int] IERC165
- [Ext] supportsInterface
+ [Int] IERC721 (IERC165)
- [Ext] balanceOf
- [Ext] ownerOf
- [Ext] safeTransferFrom #
- [Ext] transferFrom #
- [Ext] approve #
- [Ext] getApproved
- [Ext] setApprovalForAll #
- [Ext] isApprovedForAll
- [Ext] safeTransferFrom #
+ [Int] IERC721Metadata (IERC721)
- [Ext] name
- [Ext] symbol
- [Ext] tokenURI
+ [Int] IERC721Enumerable (IERC721)
- [Ext] totalSupply
- [Ext] tokenOfOwnerByIndex
- [Ext] tokenByIndex
+ [Int] IERC721Receiver
- [Ext] onERC721Received #
+ ERC165 (IERC165)
- [Int] #
- [Pub] supportsInterface
- [Int] _registerInterface #
+ [Lib] Address
- [Int] isContract
- [Int] sendValue #
- [Int] functionCall #
- [Int] functionCall #
- [Int] functionCallWithValue #
- [Int] functionCallWithValue #
- [Int] functionStaticCall
- [Int] functionStaticCall
- [Int] functionDelegateCall #
- [Int] functionDelegateCall #
- [Prv] _verifyCallResult
+ [Lib] EnumerableSet
- [Prv] _add #
- [Prv] _remove #
- [Prv] _contains
- [Prv] _length
- [Prv] _at
- [Int] add #
- [Int] remove #
- [Int] contains
- [Int] length
- [Int] at
- [Int] add #
- [Int] remove #
- [Int] contains
- [Int] length
- [Int] at
- [Int] add #
- [Int] remove #
- [Int] contains
- [Int] length
- [Int] at
+ [Lib] EnumerableMap
- [Prv] _set #
- [Prv] _remove #
- [Prv] _contains
- [Prv] _length
- [Prv] _at
- [Prv] _tryGet
- [Prv] _get
- [Prv] _get
- [Int] set #
- [Int] remove #
- [Int] contains
- [Int] length
- [Int] at
- [Int] tryGet
- [Int] get
- [Int] get
+ [Lib] Strings
- [Int] toString
+ ERC721 (Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable)
- [Pub] #
- [Pub] balanceOf
- [Pub] ownerOf
- [Pub] name
- [Pub] symbol
- [Pub] tokenURI
- [Pub] baseURI
- [Pub] tokenOfOwnerByIndex
- [Pub] totalSupply
- [Pub] tokenByIndex
- [Pub] approve #
- [Pub] getApproved
- [Pub] setApprovalForAll #
- [Pub] isApprovedForAll
- [Pub] transferFrom #
- [Pub] safeTransferFrom #
- [Pub] safeTransferFrom #
- [Int] _safeTransfer #
- [Int] _exists
- [Int] _isApprovedOrOwner
- [Int] _safeMint #
- [Int] _safeMint #
- [Int] _mint #
- [Int] _burn #
- [Int] _transfer #
- [Int] _setTokenURI #
- [Int] _setBaseURI #
- [Prv] _checkOnERC721Received #
- [Int] _approve #
- [Int] _beforeTokenTransfer #
+ [Lib] Counters
- [Int] current
- [Int] increment #
- [Int] decrement #
+ AccessControl (Context)
- [Pub] hasRole
- [Pub] getRoleMemberCount
- [Pub] getRoleMember
- [Pub] getRoleAdmin
- [Pub] grantRole #
- [Pub] revokeRole #
- [Pub] renounceRole #
- [Int] _setupRole #
- [Int] _setRoleAdmin #
- [Prv] _grantRole #
- [Prv] _revokeRole #
+ PinkslipNFT (ERC721, AccessControl, Ownable)
- [Pub] #
- modifiers: ERC721
- [Pub] mint #
- [Pub] buy #
- [Pub] carType
- [Int] _setCarType #
- [Pub] setFeesDestinators #
- modifiers: onlyOwner
- [Pub] setFeesPercentages #
- modifiers: onlyOwner
- [Pub] setFeesAmounts #
- modifiers: onlyOwner
+ PinkslipRacing (Ownable, ReentrancyGuard)
- [Pub] #
- [Pub] createRace #
- modifiers: slotAvailable
- [Pub] cancel #
- modifiers: inProgress
- [Pub] accept #
- modifiers: inProgress
- [Pub] isSlotAvailable
- [Pub] allSlotsStatus
- [Int] _getTime
- [Int] _randomNumber
- [Int] _chancesByType
Pinkslip NFT, Ticket, and Stake Contracts
($) = payable function
# = non-constant function
Int = Internal
Ext = External
Pub = Public
+ [Lib] SafeMath
- [Int] tryAdd
- [Int] trySub
- [Int] tryMul
- [Int] tryDiv
- [Int] tryMod
- [Int] add
- [Int] sub
- [Int] mul
- [Int] div
- [Int] mod
- [Int] sub
- [Int] div
- [Int] mod
+ Context
- [Int] _msgSender
- [Int] _msgData
+ Ownable (Context)
- [Int] #
- [Pub] owner
- [Pub] renounceOwnership #
- modifiers: onlyOwner
- [Pub] transferOwnership #
- modifiers: onlyOwner
+ [Int] IERC20
- [Ext] totalSupply
- [Ext] balanceOf
- [Ext] transfer #
- [Ext] allowance
- [Ext] approve #
- [Ext] transferFrom #
+ [Int] IERC165
- [Ext] supportsInterface
+ [Int] IERC721 (IERC165)
- [Ext] balanceOf
- [Ext] ownerOf
- [Ext] safeTransferFrom #
- [Ext] transferFrom #
- [Ext] approve #
- [Ext] getApproved
- [Ext] setApprovalForAll #
- [Ext] isApprovedForAll
- [Ext] safeTransferFrom #
+ [Int] IERC721Metadata (IERC721)
- [Ext] name
- [Ext] symbol
- [Ext] tokenURI
+ [Int] IERC721Enumerable (IERC721)
- [Ext] totalSupply
- [Ext] tokenOfOwnerByIndex
- [Ext] tokenByIndex
+ [Int] IERC721Receiver
- [Ext] onERC721Received #
+ ERC165 (IERC165)
- [Int] #
- [Pub] supportsInterface
- [Int] _registerInterface #
+ [Lib] Address
- [Int] isContract
- [Int] sendValue #
- [Int] functionCall #
- [Int] functionCall #
- [Int] functionCallWithValue #
- [Int] functionCallWithValue #
- [Int] functionStaticCall
- [Int] functionStaticCall
- [Int] functionDelegateCall #
- [Int] functionDelegateCall #
- [Prv] _verifyCallResult
+ [Lib] EnumerableSet
- [Prv] _add #
- [Prv] _remove #
- [Prv] _contains
- [Prv] _length
- [Prv] _at
- [Int] add #
- [Int] remove #
- [Int] contains
- [Int] length
- [Int] at
- [Int] add #
- [Int] remove #
- [Int] contains
- [Int] length
- [Int] at
- [Int] add #
- [Int] remove #
- [Int] contains
- [Int] length
- [Int] at
+ [Lib] EnumerableMap
- [Prv] _set #
- [Prv] _remove #
- [Prv] _contains
- [Prv] _length
- [Prv] _at
- [Prv] _tryGet
- [Prv] _get
- [Prv] _get
- [Int] set #
- [Int] remove #
- [Int] contains
- [Int] length
- [Int] at
- [Int] tryGet
- [Int] get
- [Int] get
+ [Lib] Strings
- [Int] toString
+ ERC721 (Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable)
- [Pub] #
- [Pub] balanceOf
- [Pub] ownerOf
- [Pub] name
- [Pub] symbol
- [Pub] tokenURI
- [Pub] baseURI
- [Pub] tokenOfOwnerByIndex
- [Pub] totalSupply
- [Pub] tokenByIndex
- [Pub] approve #
- [Pub] getApproved
- [Pub] setApprovalForAll #
- [Pub] isApprovedForAll
- [Pub] transferFrom #
- [Pub] safeTransferFrom #
- [Pub] safeTransferFrom #
- [Int] _safeTransfer #
- [Int] _exists
- [Int] _isApprovedOrOwner
- [Int] _safeMint #
- [Int] _safeMint #
- [Int] _mint #
- [Int] _burn #
- [Int] _transfer #
- [Int] _setTokenURI #
- [Int] _setBaseURI #
- [Prv] _checkOnERC721Received #
- [Int] _approve #
- [Int] _beforeTokenTransfer #
+ [Lib] Counters
- [Int] current
- [Int] increment #
- [Int] decrement #
+ AccessControl (Context)
- [Pub] hasRole
- [Pub] getRoleMemberCount
- [Pub] getRoleMember
- [Pub] getRoleAdmin
- [Pub] grantRole #
- [Pub] revokeRole #
- [Pub] renounceRole #
- [Int] _setupRole #
- [Int] _setRoleAdmin #
- [Prv] _grantRole #
- [Prv] _revokeRole #
+ PinkslipNFT (ERC721, AccessControl, Ownable)
- [Pub] #
- modifiers: ERC721
- [Pub] mint #
- [Pub] buy #
- [Pub] carType
- [Int] _setCarType #
- [Pub] setFeesDestinators #
- modifiers: onlyOwner
- [Pub] setFeesPercentages #
- modifiers: onlyOwner
- [Pub] setFeesAmounts #
- modifiers: onlyOwner
+ PinkslipTicket (ERC721, AccessControl, Ownable)
- [Pub] #
- modifiers: ERC721
- [Pub] mint #
- [Pub] buy ($)
- [Pub] exchange #
- [Pub] setFeeAddress #
- modifiers: onlyOwner
- [Pub] setFeeAmount #
- modifiers: onlyOwner
+ PinkslipStake (Ownable)
- [Pub] #
- [Ext] pendingReward
- [Pub] updatePool #
- [Pub] deposit #
- [Pub] withdraw #