WPOKT UX/UI Smart Contract Scoping Document

Overview

The Pocket Foundation needs a capable ERC20 in order to bridge their native POKT token to EVM compatible chains.

Tech Stack

  • Foundry
  • OpenZeppelin SC Library
  • wPOKT
    • ERC20
    • IERC20
    • ERC20Permit
    • AccessControl
  • Minter Contract
    • Ownable
    • IwPOKT Interface

wPOKT Core Functions (18 hours)

  • AccessControl Wallets:

    • DEFAULT_ADMIN_ROLE: granted to a Safe multisig controlled by POKT Foundation + POKT community members
    • MINTER_ROLE: granted to Minter contract which is owned by the Copper sharded wallet
    • PAUSER_ROLE: granted to POKT Foundation member
  • Custom AccessControl Functions:

    • mint(address user, uint256 amount, uint256 nonce) external onlyRole(MINTER_ROLE) only callable by MINTER_ROLE contract. Increments mapping(address => uint256) private _userNonces; and enforces require(nonce == _userNonces[user]++) - _userNonces[user] = nonce++; prevents accidental double minting from the backend by preventing any duplicate data from successfully minting. Checks feeFlag and if feeFlag == true mints a portion of the new tokens to the feeCollector address. Reverts if mintPaused == true

    • batchMint(address[] users, uint256[] amounts, uint256[] nonces) external onlyRole(MINTER_ROLE) calls the mint function in a loop

    • changeFee(uint256 newFeePoints, address feeCollector) external onlyRole(DEFAULT_ADMIN_ROLE) only callable by DEFAULT_ADMIN_ROLE and is bounded by MAX_FEE if (newFeePoints > 0) feeFlag = true; if (newFeePoints == 0) - feeFlag = false;

    • pauseMint(bool status) external onlyRole(PAUSER_ROLE) only callable by PAUSER_ROLE wallets. Sets the mintPaused = status; flag to block or allow minting

    • setMintCooldown(uint256 newLimit, uint256 newLimitIncreasePerSecond) external onlyRole(DEFAULT_ADMIN_ROLE only callable by the DEFAULT_ADMIN_ROLE. Sets newLimit which is the maximum number of tokens that can be minted after complete cooldown. Sets newLimitIncreasePerSecond which is the rate that new tokens can be minted per second. We changed from the LIDO implementation because it assumes that it's contract is deployed to Ethereum mainnet - block times can vary widely crosschain so we should be using block.timestamp rather than the block.number

  • Custom Mutative Public functions:

    • burn(uint256 amount, address receiver) emits receiver address log data for consumption at the Copper backend
  • Custom View Functions:

    • calculateFee(uint256 amount) public view returns (uint256 fee) uses BASIS_POINTS and feePoints by fee = (amount * feePoints) / BASIS_POINTS. Requires that amount % BASIS_POINTS == 0 to prevent rounding errors and "dust" accumulation

    • getCurrentLimit() public view returns (uint256 limit) derives the current mintable token limit at the current timestamp

Minter Contract (6 hours)

  • Sets IwPKT immutable wPKT; interface in constructor.

  • Custom onlyOwner functions:

    • callMint(address user, uint256 amount, uint256 nonce) public onlyOwner and simply passes the data into the wPKT token's mint function require(wPKT.mint(user, amount, nonce);
    • batchMint(address[] users, uint256[] amounts, uint256[] nonces) external onlyOwner requires that the length of all arrays match then makes call to wPKT token's mint function in a loop

Audit Readiness (26 hours)

  • Foundry test coverage > 90%
  • Deployment script
  • Fuzz testing
  • Invariant testing
  • Full Audit Readiness
  • Testnet deployment
Select a repo