[PAXUSD] ERC20 Token Smart Contract Technical Assessment

[PAXUSD] ERC20 Token Smart Contract Technical Assessment

General Information

Risk Summary

  • Does the contract implement the ERC20 token standards? Yes.
  • Risk analysis: MEDIUM.

Technical Information

Formal Verification Considerations:

  • Does transfer have simple semantics? Yes.
  • Does transferFrom have simple semantics? Yes.
  • Can balances be arbitrarily modified by some actor? Yes, the contract can freeze/unfreeze any on-chain address.
  • Are there any external calls? No.

Testnet Information

Contract Logic Summary


PAX uses OpenZeppelin’s AdminUpgradeabilityProxy upgradeability pattern. This pattern uses two contracts: a proxy contract and an implementation contract. The proxy contract receives calls and delegatecalls onward to the implementation contract. The proxy contract address doesn’t change, and should PAX need to change (“upgrade”) the underlying implementation contract, they can do so without having to point external users to a new contract address.

PAX briefly documents their usage of the pattern here.


The contract includes a beta implementation variation of EIP-865 that allows for gas-less transactions. This allows a user to delegate a token transfer to a third party, which will pay for the transaction using native Ether, and takes the commensurate fee in tokens. PAX’s implementation is such that only approved parties are allowed to transfer PAXUSD on behalf of a user. There is an additional betaDelegatedTransferBatch() function that allows for the aforementioned action to be done in batches.

betaDelegateTransfer() functionality can be found in the code beginning here.

Administrative Addresses

Difference between owner and supplyController:

  • owner can pause the contract (pausing transfers and approvals of the PAX token) – this functionality is implemented in the contract following OpenZeppelin’s Ownable and Pausable code patterns.
  • supplyController can mint and burn tokens, and per PAXUSD documentation, this functionality is “based on teh actual movement of cash in and out of the reserve based on requests for the purchase and redemption of PAX.”

It’s important to note that these are currently set to same address but occupy two different address variables within the implementation smart contract. When calling initialize() (a one-time use function) these two address variables are set to msg.sender. These two addresses could be different in the future.

Inheritance Structure

PAXUSD’s contract logic, excluding the upgradeability patterns, is all encompassed within the PAXImplementationV2.sol contract. The contract inherits no other contracts.

Contract Risk Summary

This is a medium risk contract. The ERC20 function are implemented to industry standard, it uses a SafeMath library to prevent over/underflows, and there is no inheritance. However, the non-standard features outlined in the technical information section of this assessment introduce additional risk.

Supporting Materials

Sūrya’s Description Report (tool: Surya)

Files Description Table

File Name SHA-1 Hash
PAXImplementationV2.sol 13e131ed1364d4c72e203804a8f0545bfab6a557

Contracts Description Table

Contract Type Bases
Function Name Visibility Mutability Modifiers
PAXImplementationV2 Implementation
initialize Public :exclamation: :stop_sign: NO❗️
Public :exclamation: :stop_sign: NO❗️
initializeDomainSeparator Public :exclamation: :stop_sign: NO❗️
totalSupply Public :exclamation: NO❗️
transfer Public :exclamation: :stop_sign: whenNotPaused
balanceOf Public :exclamation: NO❗️
transferFrom Public :exclamation: :stop_sign: whenNotPaused
approve Public :exclamation: :stop_sign: whenNotPaused
allowance Public :exclamation: NO❗️
proposeOwner Public :exclamation: :stop_sign: onlyOwner
disregardProposeOwner Public :exclamation: :stop_sign: NO❗️
claimOwnership Public :exclamation: :stop_sign: NO❗️
reclaimPAX External :exclamation: :stop_sign: onlyOwner
pause Public :exclamation: :stop_sign: onlyOwner
unpause Public :exclamation: :stop_sign: onlyOwner
setAssetProtectionRole Public :exclamation: :stop_sign: NO❗️
freeze Public :exclamation: :stop_sign: onlyAssetProtectionRole
unfreeze Public :exclamation: :stop_sign: onlyAssetProtectionRole
wipeFrozenAddress Public :exclamation: :stop_sign: onlyAssetProtectionRole
isFrozen Public :exclamation: NO❗️
setSupplyController Public :exclamation: :stop_sign: NO❗️
increaseSupply Public :exclamation: :stop_sign: onlySupplyController
decreaseSupply Public :exclamation: :stop_sign: onlySupplyController
nextSeqOf Public :exclamation: NO❗️
betaDelegatedTransfer Public :exclamation: :stop_sign: NO❗️
_betaDelegatedTransfer Internal :lock: :stop_sign: whenNotPaused
betaDelegatedTransferBatch Public :exclamation: :stop_sign: NO❗️
isWhitelistedBetaDelegate Public :exclamation: NO❗️
setBetaDelegateWhitelister Public :exclamation: :stop_sign: NO❗️
whitelistBetaDelegate Public :exclamation: :stop_sign: onlyBetaDelegateWhitelister
unwhitelistBetaDelegate Public :exclamation: :stop_sign: onlyBetaDelegateWhitelister


Symbol Meaning
:stop_sign: Function can modify state
:dollar: Function is payable

Misc notes

totalSupply() returns 0