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

USDC v2.2 #357

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
/**
* SPDX-License-Identifier: MIT
*
* Copyright (c) 2018 zOS Global Limited.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
Expand All @@ -24,13 +22,13 @@

pragma solidity 0.6.12;

import { FiatTokenV3 } from "../v3/FiatTokenV3.sol";
import { FiatTokenV2_2 } from "../v2/FiatTokenV2_2.sol";

/**
* @title UpgradedFiatTokenNewFieldsTest
* @dev ERC20 Token backed by fiat reserves
*/
contract UpgradedFiatTokenNewFieldsV3Test is FiatTokenV3 {
contract UpgradedFiatTokenNewFieldsV2_2Test is FiatTokenV2_2 {
bool public newBool;
address public newAddress;
uint256 public newUint;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
/**
* SPDX-License-Identifier: MIT
*
* Copyright (c) 2018 zOS Global Limited.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
Expand All @@ -24,12 +22,12 @@

pragma solidity 0.6.12;

import { FiatTokenV3 } from "../v3/FiatTokenV3.sol";
import { FiatTokenV2_2 } from "../v2/FiatTokenV2_2.sol";

/**
* @title UpgradedFiatToken
* @dev ERC20 Token backed by fiat reserves
*/
contract UpgradedFiatTokenV3 is FiatTokenV3 {
contract UpgradedFiatTokenV2_2 is FiatTokenV2_2 {

}
1 change: 1 addition & 0 deletions contracts/v1/FiatTokenV1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable {
* @return A boolean that indicates if the operation was successful.
*/
function mint(address _to, uint256 _amount)
virtual
external
whenNotPaused
onlyMinters
Expand Down
54 changes: 42 additions & 12 deletions contracts/v3/FiatTokenV3.sol → contracts/v2/FiatTokenV2_2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,15 @@ import { EIP712 } from "../util/EIP712.sol";
// solhint-disable func-name-mixedcase

/**
* @title FiatTokenV3
* @title FiatTokenV2_2
* @dev ERC20 Token backed by fiat reserves
*/
contract FiatTokenV3 is FiatTokenV2_1 {

contract FiatTokenV2_2 is FiatTokenV2_1 {
/**
* @notice Initialize v3
* @notice Initialize v2.2
*/
function initializeV3(address[] calldata accountsToBlacklist) external {
require(_initializedVersion == 2, "v3 initialized out of order");
function initializeV2_2(address[] calldata accountsToBlacklist) external {
require(_initializedVersion == 2, "v2.2 initialized out of order");

// re-add previously blacklisted accounts to the blacklist
// by setting the high bit of their balance to 1
Expand All @@ -48,8 +47,6 @@ contract FiatTokenV3 is FiatTokenV2_1 {
// additionally blacklist the contract address itself
_blacklist(address(this));

DOMAIN_SEPARATOR = EIP712.makeDomainSeparator(name, "3");

_initializedVersion = 3;
}

Expand Down Expand Up @@ -291,10 +288,43 @@ contract FiatTokenV3 is FiatTokenV2_1 {
}

/**
* @notice Version string for the EIP712 domain separator
* @return Version string
* @dev Function to mint tokens
* @param _to The address that will receive the minted tokens.
* @param _amount The amount of tokens to mint. Must be less than or equal
* to the minterAllowance of the caller.
* @return A boolean that indicates if the operation was successful.
*/
function version() external override view returns (string memory) {
return "3";
function mint(address _to, uint256 _amount)
override
external
whenNotPaused
onlyMinters
notBlacklisted(msg.sender)
notBlacklisted(_to)
returns (bool)
{
require(_to != address(0), "FiatToken: mint to the zero address");
require(_amount > 0, "FiatToken: mint amount not greater than 0");

uint256 mintingAllowedAmount = minterAllowed[msg.sender];
require(
_amount <= mintingAllowedAmount,
"FiatToken: mint amount exceeds minterAllowance"
);

uint256 newTotalSupply = totalSupply_.add(_amount);
// The supply cap ensures that no account can be unintentionally blacklisted
// with a high balance
// Hardcoding the value here as opposed to setting it in storage since it's
// expected to be static and this avoids an SLOAD
require(newTotalSupply <= (uint256(1) << 255) - 1, "mint causes total supply to supply cap");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, nice, so avoid doing this in transfer since no one can ever have a balance > totalSupply. So only mint gets more expensive.


totalSupply_ = newTotalSupply;

balances[_to] = balances[_to].add(_amount);
minterAllowed[msg.sender] = mintingAllowedAmount.sub(_amount);
emit Mint(msg.sender, _to, _amount);
emit Transfer(address(0), _to, _amount);
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,20 @@ pragma solidity 0.6.12;
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Ownable } from "../../v1/Ownable.sol";
import { FiatTokenV3 } from "../FiatTokenV3.sol";
import { FiatTokenV2_2 } from "../FiatTokenV2_2.sol";
import { FiatTokenProxy } from "../../v1/FiatTokenProxy.sol";
import { V2UpgraderHelper } from "../../v2/upgrader/V2UpgraderHelper.sol";

/**
* @title V3 Upgrader
* @notice Performs USDC v3 upgrade
* @dev Read docs/v3_upgrade.md
* @title v2.2 Upgrader
* @notice Performs USDC v2.2 upgrade
* @dev docs TBD
*/
contract V3Upgrader is Ownable {
contract V2_2Upgrader is Ownable {
using SafeMath for uint256;

FiatTokenProxy private _proxy;
FiatTokenV3 private _implementation;
FiatTokenV2_2 private _implementation;
address private _newProxyAdmin;
string private _newName;
V2UpgraderHelper private _helper;
Expand All @@ -47,13 +47,13 @@ contract V3Upgrader is Ownable {
/**
* @notice Constructor
* @param proxy FiatTokenProxy contract
* @param implementation FiatTokenV3 implementation contract
* @param implementation FiatTokenV2_2 implementation contract
* @param newProxyAdmin Grantee of proxy admin role after upgrade
* @param accountsToBlacklist Accounts to re-add to the blacklist
*/
constructor(
FiatTokenProxy proxy,
FiatTokenV3 implementation,
FiatTokenV2_2 implementation,
address newProxyAdmin,
address[] memory accountsToBlacklist
) public Ownable() {
Expand All @@ -73,7 +73,7 @@ contract V3Upgrader is Ownable {
}

/**
* @notice The address of the FiatTokenV3 implementation contract
* @notice The address of the FiatTokenV2_2 implementation contract
* @return Contract address
*/
function implementation() external view returns (address) {
Expand Down Expand Up @@ -117,7 +117,7 @@ contract V3Upgrader is Ownable {

// Check that this contract sufficient funds to run the tests
uint256 contractBal = _helper.balanceOf(address(this));
require(contractBal >= 2e5, "V3Upgrader: 0.2 USDC needed");
require(contractBal >= 2e5, "V2_2Upgrader: 0.2 USDC needed");

uint256 callerBal = _helper.balanceOf(msg.sender);

Expand All @@ -137,52 +137,52 @@ contract V3Upgrader is Ownable {
// Transfer proxy admin role
_proxy.changeAdmin(_newProxyAdmin);

// Initialize V3 contract
FiatTokenV3 v3 = FiatTokenV3(address(_proxy));
v3.initializeV3(_accountsToBlacklist);
// Initialize V2_2 contract
FiatTokenV2_2 v2_2 = FiatTokenV2_2(address(_proxy));
v2_2.initializeV2_2(_accountsToBlacklist);

// Sanity test
// Check metadata
require(
keccak256(bytes(name)) == keccak256(bytes(v3.name())) &&
keccak256(bytes(symbol)) == keccak256(bytes(v3.symbol())) &&
decimals == v3.decimals() &&
keccak256(bytes(currency)) == keccak256(bytes(v3.currency())) &&
masterMinter == v3.masterMinter() &&
owner == v3.owner() &&
pauser == v3.pauser() &&
blacklister == v3.blacklister(),
"V3Upgrader: metadata test failed"
keccak256(bytes(name)) == keccak256(bytes(v2_2.name())) &&
keccak256(bytes(symbol)) == keccak256(bytes(v2_2.symbol())) &&
decimals == v2_2.decimals() &&
keccak256(bytes(currency)) == keccak256(bytes(v2_2.currency())) &&
masterMinter == v2_2.masterMinter() &&
owner == v2_2.owner() &&
pauser == v2_2.pauser() &&
blacklister == v2_2.blacklister(),
"V2_2Upgrader: metadata test failed"
);

// Test balanceOf
require(
v3.balanceOf(address(this)) == contractBal,
"V3Upgrader: balanceOf test failed"
v2_2.balanceOf(address(this)) == contractBal,
"V2_2Upgrader: balanceOf test failed"
);

// Test transfer
require(
v3.transfer(msg.sender, 1e5) &&
v3.balanceOf(msg.sender) == callerBal.add(1e5) &&
v3.balanceOf(address(this)) == contractBal.sub(1e5),
"V3Upgrader: transfer test failed"
v2_2.transfer(msg.sender, 1e5) &&
v2_2.balanceOf(msg.sender) == callerBal.add(1e5) &&
v2_2.balanceOf(address(this)) == contractBal.sub(1e5),
"V2_2Upgrader: transfer test failed"
);

// Test approve/transferFrom
require(
v3.approve(address(_helper), 1e5) &&
v3.allowance(address(this), address(_helper)) == 1e5 &&
v2_2.approve(address(_helper), 1e5) &&
v2_2.allowance(address(this), address(_helper)) == 1e5 &&
_helper.transferFrom(address(this), msg.sender, 1e5) &&
v3.allowance(address(this), msg.sender) == 0 &&
v3.balanceOf(msg.sender) == callerBal.add(2e5) &&
v3.balanceOf(address(this)) == contractBal.sub(2e5),
"V3Upgrader: approve/transferFrom test failed"
v2_2.allowance(address(this), msg.sender) == 0 &&
v2_2.balanceOf(msg.sender) == callerBal.add(2e5) &&
v2_2.balanceOf(address(this)) == contractBal.sub(2e5),
"V2_2Upgrader: approve/transferFrom test failed"
);

// Check that addresses that should be blacklisted are
for (uint256 i = 0; i < _accountsToBlacklist.length; i++) {
require(v3.isBlacklisted(_accountsToBlacklist[i]), "V3Upgrader: blacklist test failed");
require(v2_2.isBlacklisted(_accountsToBlacklist[i]), "V2_2Upgrader: blacklist test failed");
}

// Transfer any remaining USDC to the caller
Expand All @@ -202,7 +202,7 @@ contract V3Upgrader is Ownable {
if (balance > 0) {
require(
usdc.transfer(msg.sender, balance),
"V3Upgrader: failed to withdraw USDC"
"V2_2Upgrader: failed to withdraw USDC"
);
}
}
Expand Down
39 changes: 21 additions & 18 deletions scripts/gas_usage_v2_vs_v3.js → gas_usage_v2_1_vs_v2_2.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
// run with `yarn truffle exec scripts/gas_usage_v2_vs_v3.js`
// run with `yarn truffle exec scripts/gas_usage_v2_1_vs_v2_2.js`

const FiatTokenV2_1 = artifacts.require("FiatTokenV2_1");
const FiatTokenV3 = artifacts.require("FiatTokenV3");
const FiatTokenV2_2 = artifacts.require("FiatTokenV2_2");

module.exports = async function (callback) {
try {
const [fiatTokenOwner, alice, bob] = await web3.eth.getAccounts();
const mintAmount = 50e6;
const transferAmount = 10e6;

const fiatTokenV3 = await initializeV3(fiatTokenOwner);
const fiatTokenV2_2 = await initializeV2_2(fiatTokenOwner);
const fiatTokenV2_1 = await initializeV2_1(fiatTokenOwner);

await fiatTokenV3.mint(alice, mintAmount, { from: fiatTokenOwner });
const transferTxV3 = await fiatTokenV3.transfer(bob, transferAmount, {
await FiatTokenV2_2.mint(alice, mintAmount, { from: fiatTokenOwner });
const transferTxV2_2 = await FiatTokenV2_2.transfer(bob, transferAmount, {
from: alice,
});
console.log("V3 transfer gas usage:", transferTxV3.receipt.gasUsed);
console.log("V2.2 transfer gas usage:", transferTxV2_2.receipt.gasUsed);

await fiatTokenV2_1.mint(alice, mintAmount, {
from: fiatTokenOwner,
Expand All @@ -26,25 +26,28 @@ module.exports = async function (callback) {
});
console.log("V2.1 transfer gas usage:", transferTxV2_1.receipt.gasUsed);

const approvalTxV3 = await fiatTokenV3.approve(bob, transferAmount, {
const approvalTxV2_2 = await fiatTokenV2_2.approve(bob, transferAmount, {
from: alice,
});
console.log("V3 approve gas usage:", approvalTxV3.receipt.gasUsed);
console.log("V2.2 approve gas usage:", approvalTxV2_2.receipt.gasUsed);

const approvalTxV2_1 = await fiatTokenV2_1.approve(bob, transferAmount, {
from: alice,
});
console.log("V2.1 approve gas usage:", approvalTxV2_1.receipt.gasUsed);

const transferFromTxV3 = await fiatTokenV3.transferFrom(
const transferFromTxV2_2 = await fiatTokenV2_2.transferFrom(
alice,
bob,
transferAmount,
{
from: bob,
}
);
console.log("V3 transferFrom gas usage:", transferFromTxV3.receipt.gasUsed);
console.log(
"V2.2 transferFrom gas usage:",
transferFromTxV2_2.receipt.gasUsed
);

const transferFromTxV2_1 = await fiatTokenV2_1.transferFrom(
alice,
Expand All @@ -65,10 +68,10 @@ module.exports = async function (callback) {
callback();
};

async function initializeV3(fiatTokenOwner) {
const fiatTokenV3 = await FiatTokenV3.new();
async function initializeV2_2(fiatTokenOwner) {
const fiatTokenV2_2 = await FiatTokenV2_2.new();

await fiatTokenV3.initialize(
await fiatTokenV2_2.initialize(
"USD Coin",
"USDC",
"USD",
Expand All @@ -78,15 +81,15 @@ async function initializeV3(fiatTokenOwner) {
fiatTokenOwner,
fiatTokenOwner
);
await fiatTokenV3.initializeV2("USD Coin", { from: fiatTokenOwner });
await fiatTokenV3.initializeV2_1(fiatTokenOwner, { from: fiatTokenOwner });
await fiatTokenV3.initializeV3([], { from: fiatTokenOwner });
await fiatTokenV2_2.initializeV2("USD Coin", { from: fiatTokenOwner });
await fiatTokenV2_2.initializeV2_1(fiatTokenOwner, { from: fiatTokenOwner });
await fiatTokenV2_2.initializeV2_2([], { from: fiatTokenOwner });

await fiatTokenV3.configureMinter(fiatTokenOwner, 1000000e6, {
await fiatTokenV2_2.configureMinter(fiatTokenOwner, 1000000e6, {
from: fiatTokenOwner,
});

return fiatTokenV3;
return fiatTokenV2_2;
}

async function initializeV2_1(fiatTokenOwner) {
Expand Down
Loading