Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync repo to audited commit hash #3

Merged
merged 4 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2024 Paxos Technology Solutions, LLC
Copyright (c) 2021 Paxos Technology Solutions, LLC

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
41 changes: 41 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
.PHONY:all
all: setup fmt compile generate-bin test-contracts-coverage

.PHONY:clean
clean:
@rm -r build/ || true

##################
# Code
##################

.PHONY:setup
setup:
yarn install --ignore-optional

.PHONY:fmt
fmt:
@npm run solhint

.PHONY:ganache
ganache:
@npm run ganache

.PHONY:compile
compile:
@npm run compile

.PHONY:generate-bin
generate-bin: compile
@npm run generate-abi
@npm run generate-bin

# compile is needed as a dependency here to ensure the zos-lib based tests work
.PHONY:test-contracts
test-contracts: compile
@npm test

# TODO: get tests to pass in coverage env
.PHONY:test-contracts-coverage
test-contracts-coverage:
@npm run coverage
57 changes: 34 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,27 @@ Paxos-issued USD-collateralized ERC20 stablecoin public smart contract repositor

https://github.com/paxosglobal/paxos-token-contract

### Roles
### Roles and Addresses

| Role |
| ------------------------------ |
| DEFAULT_ADMIN_ROLE |
| PAUSE_ROLE |
| ASSET_PROTECTION_ROLE |
| SUPPLY_CONTROLLER_MANAGER_ROLE |
| SUPPLY_CONTROLLER_ROLE |
| Role | Address |
| ------------------------------ | ------------------------------------------ |
| DEFAULT_ADMIN_ROLE | TBD |
| PAUSE_ROLE | TBD |
| ASSET_PROTECTION_ROLE | TBD |
| SUPPLY_CONTROLLER_MANAGER_ROLE | TBD |
| SUPPLY_CONTROLLER_ROLE | TBD |

To guard against centralized control, the addresses above utilize multisignature contracts ([source](https://github.com/paxosglobal/simple-multisig)). Any change requires the presence of a quorum of signers in the same physical location, ensuring that no individual signer can unilaterally influence a change.

### ABI and Addresses
### ABI, Address, and Verification

The contract abi is in `PaxosToken.abi`, which is the implementation contract abi.
The contract abi is in `PaxosToken.abi`, which is the imlementation contract abi.

Interaction with token is done at the address of the proxy. Deployed token addresses can be found in
the [Paxos docs](https://docs.paxos.com/stablecoin).
Interaction with token is done at the address of the proxy. Below is the table which list out all our contract addresses.

| Token | Proxy-address | Implementation-Address |
| :-------------: |:-------------:| :-----:|
| USDP | 0x000000000000000000000000000000000000000 | 0x000000000000000000000000000000000000000 |

## Contract Specification

Expand Down Expand Up @@ -88,6 +91,10 @@ While paused, the supply controller retains the ability to mint and burn tokens.

### Asset Protection Role

Paxos Trust Company is regulated by the New York Department of Financial Services (NYDFS). As required by the regulator,
Paxos must have a role for asset protection to freeze or seize the assets of a criminal party when required to do so by
law, including by court order or other legal process.

The `ASSET_PROTECTION_ROLE` can freeze and unfreeze the token balance of any address on chain.
It can also wipe the balance of an address after it is frozen
to allow the appropriate authorities to seize the backing assets.
Expand All @@ -98,7 +105,7 @@ via `isFrozen(address who)`.

### Delegate Transfer

To facilitate gas-less transactions, we have implemented [EIP-3009](https://eips.ethereum.org/EIPS/eip-3009) and [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612).
To facilitate gas-less transactions, we have implemented [EIP-3009](https://eips.ethereum.org/EIPS/eip-3009) and (EIP-2612)[https://eips.ethereum.org/EIPS/eip-2612].

#### EIP-3009
The public functions, `transferWithAuthorization` and `transferWithAuthorizationBatch` (for multiple transfers request), allows a spender(delegate) to transfer tokens on behalf of the sender, with condition that a signature, conforming to [EIP-712](https://eips.ethereum.org/EIPS/eip-712), is provided by the respective sender.
Expand Down Expand Up @@ -168,16 +175,7 @@ _in the context of the proxy storage_. This way the implementation pointer can
be changed to a different implementation contract while still keeping the same
data and contract address, which are really for the proxy contract.

USDP and PYUSD use `AdminUpgradeabilityProxy` from OpenZeppelin.

USDG and SupplyControl use `UUPSUpgradeable` from OpenZeppelin.

`UUPSUpgradeable` is a newer proxy pattern which
has some advantages over `AdminUpgradeabilityProxy`. One issue with `AdminUpgradeabilityProxy` is the proxy admin
cannot call any of the implementation functions which means the proxy admin must be a separate address
from the DEFAULT_ADMIN_ROLE. This is not an issue with `UUPSUpgradeable`. Another advantage is updating
the proxy admin in `UUPSUpgradeable` is a two step process due to using OpenZeppelin's AccessControlDefaultAdmin.
However, in `AdminUpgradeabilityProxy` it's one step which is more dangerous.
The proxy used here is AdminUpgradeabilityProxy from ZeppelinOS.

## Upgrade Process

Expand All @@ -188,6 +186,19 @@ it can all be done in one transaction. You must first deploy a copy of the new i
contract, which is automatically paused by its constructor to help avoid accidental calls directly
to the proxy contract.

## Bytecode verification
[comment]: <> (TODO: ref: `@` -> Add implementation address for line 142)

The proxy contract and implementation contracts are verified on etherscan at the following links:
https://etherscan.io/token/0x6c3ea9036406852006290770bedfcaba0e23a0e8
https://etherscan.io/token/`@`

Because the implementation address in the proxy is a private variable,
verifying that this is the proxy being used requires reading contract
storage directly. The contract storage slot address is `0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3`.

`npx hardhat --network ${NETWORK} run scripts/getImplementationAddress.js`

## Contract Tests
Install dependencies:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,17 @@ library SafeMath {
}
}

// File: contracts/PaxosToken.sol
// File: contracts/XYZImplementation.sol

pragma solidity ^0.4.24;
pragma experimental "v0.5.0";



/**
* @title PaxosToken
* @title XYZImplementation
* @dev this contract is a Pausable ERC20 token with Burn and Mint
* controlled by a central SupplyController. By implementing PaxosToken
* controlled by a central SupplyController. By implementing XYZImplementation
* this contract also includes external methods for setting
* a new implementation contract for the Proxy.
* NOTE: The storage defined here will actually be held in the Proxy
Expand All @@ -48,7 +48,7 @@ pragma experimental "v0.5.0";
* Any call to transfer against this contract should fail
* with insufficient funds since no tokens will be issued there.
*/
contract PaxosToken {
contract XYZImplementation {

/**
* MATH
Expand All @@ -66,8 +66,8 @@ contract PaxosToken {
// ERC20 BASIC DATA
mapping(address => uint256) internal balances;
uint256 internal totalSupply_;
string public constant name = "PaxosToken USD"; // solium-disable-line
string public constant symbol = "PaxosToken"; // solium-disable-line uppercase
string public constant name = "Hopper"; // solium-disable-line
string public constant symbol = "XYZ"; // solium-disable-line uppercase
uint8 public constant decimals = 6; // solium-disable-line uppercase

// ERC20 DATA
Expand Down Expand Up @@ -383,11 +383,11 @@ contract PaxosToken {
}

/**
* @dev Reclaim all tokens at the contract address.
* This sends the tokens that this contract add holding to the owner.
* @dev Reclaim all XYZ at the contract address.
* This sends the XYZ tokens that this contract add holding to the owner.
* Note: this is not affected by freeze constraints.
*/
function reclaimToken() external onlyOwner {
function reclaimXYZ() external onlyOwner {
uint256 _balance = balances[this];
balances[this] = 0;
balances[owner] = balances[owner].add(_balance);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -487,15 +487,15 @@ abstract contract EIP3009 is PaxosBaseAbstract, EIP712Domain {
}
}

// File: contracts/PaxosToken.sol
// File: contracts/XYZImplementation.sol

pragma solidity ^0.8.17;


/**
* @title PaxosToken
* @title XYZImplementation
* @dev this contract is a Pausable ERC20 token with Burn and Mint
* controlled by a central SupplyController. By implementing PaxosToken
* controlled by a central SupplyController. By implementing XYZImplementation
* this contract also includes external methods for setting
* a new implementation contract for the Proxy.
* NOTE: The storage defined here will actually be held in the Proxy
Expand All @@ -504,7 +504,7 @@ pragma solidity ^0.8.17;
* Any call to transfer against this contract should fail
* with insufficient funds since no tokens will be issued there.
*/
contract PaxosToken is PaxosBaseAbstract{
contract XYZImplementation is PaxosBaseAbstract{

/**
* DATA
Expand All @@ -516,8 +516,8 @@ contract PaxosToken is PaxosBaseAbstract{
// ERC20 BASIC DATA
mapping(address => uint256) internal balances;
uint256 internal totalSupply_;
string public constant name = "PaxosToken USD"; // solhint-disable-line const-name-snakecase
string public constant symbol = "PaxosToken"; // solhint-disable-line const-name-snakecase
string public constant name = "XYZ USD"; // solhint-disable-line const-name-snakecase
string public constant symbol = "XYZ"; // solhint-disable-line const-name-snakecase
uint8 public constant decimals = 6; // solhint-disable-line const-name-snakecase

// ERC20 DATA
Expand Down Expand Up @@ -556,7 +556,7 @@ contract PaxosToken is PaxosBaseAbstract{
// solhint-disable-next-line var-name-mixedcase
bytes32 public EIP712_DOMAIN_HASH_DEPRECATED;
// Storage gap: https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#storage-gaps
uint256[25] __gap_PaxosToken;
uint256[25] __gap_XYZImplementation;

/**
* EVENTS
Expand Down Expand Up @@ -828,11 +828,11 @@ contract PaxosToken is PaxosBaseAbstract{
}

/**
* @dev Reclaim all tokens at the contract address.
* This sends the tokens that this contract add holding to the owner.
* @dev Reclaim all XYZ at the contract address.
* This sends the XYZ tokens that this contract add holding to the owner.
* Note: this is not affected by freeze constraints.
*/
function reclaimToken() external onlyOwner {
function reclaimXYZ() external onlyOwner {
uint256 _balance = balances[address(this)];
balances[address(this)] = 0;
balances[owner] += _balance;
Expand Down Expand Up @@ -1046,7 +1046,7 @@ contract PaxosToken is PaxosBaseAbstract{

}

// File: contracts/PaxosTokenV1.sol
// File: contracts/XYZImplementationV1.sol

pragma solidity ^0.8.17;

Expand All @@ -1055,9 +1055,9 @@ pragma solidity ^0.8.17;


/**
* @title PaxosTokenV1
* @title XYZImplementationV1
* @dev this contract is a Pausable ERC20 token with Burn and Mint
* controlled by a central SupplyController. By implementing PaxosTokenV1
* controlled by a central SupplyController. By implementing XYZImplementationV1
* this contract also includes external methods for setting
* a new implementation contract for the Proxy.
* NOTE: The storage defined here will actually be held in the Proxy
Expand All @@ -1066,7 +1066,7 @@ pragma solidity ^0.8.17;
* Any call to transfer against this contract should fail
* with insufficient funds since no tokens will be issued there.
*/
contract PaxosTokenV1 is PaxosToken, EIP2612, EIP3009 {
contract XYZImplementationV1 is XYZImplementation, EIP2612, EIP3009 {
constructor() {
initializeEIP712DomainSeparator();
}
Expand All @@ -1075,6 +1075,6 @@ contract PaxosTokenV1 is PaxosToken, EIP2612, EIP3009 {
* @dev To be called when upgrading the contract using upgradeAndCall and during initialization of contract.
*/
function initializeEIP712DomainSeparator() public {
DOMAIN_SEPARATOR = EIP712.makeDomainSeparator("PaxosToken", "1");
DOMAIN_SEPARATOR = EIP712.makeDomainSeparator("XYZ", "1");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,23 @@ import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils
import { PaxosTokenV2 } from "./../PaxosTokenV2.sol";

/**
* @title USDG Smart contract
* @title USDX Smart contract
* @dev This contract is a {PaxosTokenV2-PaxosTokenV2} ERC20 token.
* @custom:security-contact smart-contract-security@paxos.com
*/
contract USDG is PaxosTokenV2, UUPSUpgradeable {
contract USDX is PaxosTokenV2, UUPSUpgradeable {
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return "Global Dollar";
return "USD Token";
}

/**
* @dev Returns the symbol of the token.
*/
function symbol() public view virtual override returns (string memory) {
return "USDG";
return "USDX";
}

/**
Expand Down
3 changes: 1 addition & 2 deletions scripts/deploy.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { ethers } = require("hardhat");
const { PrintDeployerDetails, PrintContractDetails, ValidateEnvironmentVariables } = require('./utils');
const { PrintDeployerDetails, PrintContractDetails } = require('./utils');

const { TOKEN_ADMIN_ADDRESS, TOKEN_OWNER_ADDRESS, PAUSER_ADDRESS, ASSET_PROTECTOR_ADDRESS, TOKEN_CONTRACT_NAME } = process.env;

Expand All @@ -14,7 +14,6 @@ const initializerArgs = [
]

async function main() {
ValidateEnvironmentVariables([...initializerArgs, TOKEN_CONTRACT_NAME])
PrintDeployerDetails();

console.log("\nDeploying Implementation contract...")
Expand Down
3 changes: 1 addition & 2 deletions scripts/deployImplementation.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
const { ethers } = require("hardhat");
const { TOKEN_CONTRACT_NAME} = process.env;

const { PrintDeployerDetails, PrintContractDetails, ValidateEnvironmentVariables } = require('./utils');
const { PrintDeployerDetails, PrintContractDetails } = require('./utils');

async function main() {
ValidateEnvironmentVariables([TOKEN_CONTRACT_NAME])
PrintDeployerDetails();

console.log("\nDeploying Implementation contract...")
Expand Down
Loading