Skip to content
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
27 changes: 27 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
version: 2.1

orbs:
node: circleci/[email protected]

defaults: &defaults
working_directory: ~/repo
docker:
- image: cimg/node:20.17.0

jobs:
lint and build:
<<: *defaults
steps:
- checkout
- node/install-packages:
pkg-manager: yarn
- run: yarn build
- run: yarn lint
- persist_to_workspace:
root: ~/repo
paths: [.]
workflows:
version: 2
build:
jobs:
- lint and build
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ bin
cache
artifacts
typechain/
.env
*.env*

# OpenZeppelin
.openzeppelin/dev-*.json
Expand Down
25 changes: 25 additions & 0 deletions contracts/ERC20Mock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT
/* solhint-disable */
pragma solidity ^0.8.3;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

/**
* @title ERC20Mock
* @dev Mock contract for testing purposes, allowing minting of tokens. Used as mock of MEOW token,
* which is the main payment token in ZNS.
*/
contract ERC20Mock is ERC20 {
constructor(
string memory name_,
string memory symbol_
) ERC20(name_, symbol_) {}

function mint(address account, uint256 amount) public {
_mint(account, amount);
}

function decimals() public pure override returns (uint8) {
return 6;
}
}
259 changes: 259 additions & 0 deletions contracts/ZeroDAOTokenV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;

// Slight modifiations from base Open Zeppelin Contracts
// Consult /oz/README.md for more information
import "./oz/ERC20Upgradeable.sol";
import "./oz/ERC20SnapshotUpgradeable.sol";
import "./oz/ERC20PausableUpgradeable.sol";

import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

contract ZeroDAOTokenV2 is
OwnableUpgradeable,
ERC20Upgradeable,
ERC20PausableUpgradeable,
ERC20SnapshotUpgradeable
{
using SafeERC20 for IERC20;

event AuthorizedSnapshotter(address account);
event DeauthorizedSnapshotter(address account);
event ERC20TokenWithdrawn(
IERC20 indexed token,
address indexed to,
uint256 indexed amount
);

// Mapping which stores all addresses allowed to snapshot
mapping(address => bool) authorizedToSnapshot;

function initialize(
string memory name,
string memory symbol
) public initializer {
__Ownable_init();
__ERC20_init(name, symbol);
__ERC20Snapshot_init();
__ERC20Pausable_init();
}

// Call this on the implementation contract (not the proxy)
function initializeImplementation() public initializer {
__Ownable_init();
_pause();
}

/**
* Mints new tokens.
* @param account the account to mint the tokens for
* @param amount the amount of tokens to mint.
*/
function mint(address account, uint256 amount) external onlyOwner {
_mint(account, amount);
}

/**
* Burns tokens from an address.
* @param account the account to mint the tokens for
* @param amount the amount of tokens to mint.
*/
function burn(address account, uint256 amount) external onlyOwner {
_burn(account, amount);
}

/**
* Pauses the token contract preventing any token mint/transfer/burn operations.
* Can only be called if the contract is unpaused.
*/
function pause() external onlyOwner {
_pause();
}

/**
* Unpauses the token contract preventing any token mint/transfer/burn operations
* Can only be called if the contract is paused.
*/
function unpause() external onlyOwner {
_unpause();
}

/**
* Creates a token balance snapshot. Ideally this would be called by the
* controlling DAO whenever a proposal is made.
*/
function snapshot() external returns (uint256) {
require(
authorizedToSnapshot[_msgSender()] || _msgSender() == owner(),
"zDAOToken: Not authorized to snapshot"
);
return _snapshot();
}

/**
* Authorizes an account to take snapshots
* @param account The account to authorize
*/
function authorizeSnapshotter(address account) external onlyOwner {
require(
!authorizedToSnapshot[account],
"zDAOToken: Account already authorized"
);

authorizedToSnapshot[account] = true;
emit AuthorizedSnapshotter(account);
}

/**
* Deauthorizes an account to take snapshots
* @param account The account to de-authorize
*/
function deauthorizeSnapshotter(address account) external onlyOwner {
require(authorizedToSnapshot[account], "zDAOToken: Account not authorized");

authorizedToSnapshot[account] = false;
emit DeauthorizedSnapshotter(account);
}

/**
* Withdraws ERC20 tokens that are stuck in this contract.
* @param token The ERC20 token contract address to withdraw
* @param to The address to send the tokens to
* @param amount The amount of tokens to withdraw (0 means withdraw all available)
*/
function withdrawERC20(
IERC20 token,
address to,
uint256 amount
) external onlyOwner {
require(
address(token) != address(0),
"zDAOToken: Token address cannot be zero"
);
require(
address(token) != address(this),
"zDAOToken: Token address cannot be this token"
);
require(to != address(0), "zDAOToken: Recipient address cannot be zero");

uint256 withdrawAmount;
if (amount == 0) {
// If amount is 0, withdraw all available tokens
withdrawAmount = token.balanceOf(address(this));
} else {
// Otherwise, ensure the requested amount doesn't exceed the contract's balance
withdrawAmount = amount;
}

token.safeTransfer(to, withdrawAmount);

emit ERC20TokenWithdrawn(token, to, withdrawAmount);
}

/**
* Utility function to transfer tokens to many addresses at once.
* @param recipients The addresses to send tokens to
* @param amount The amount of tokens to send
* @return Boolean if the transfer was a success
*/
function transferBulk(
address[] calldata recipients,
uint256 amount
) external returns (bool) {
address sender = _msgSender();

uint256 total = amount * recipients.length;
require(
_balances[sender] >= total,
"ERC20: transfer amount exceeds balance"
);

require(!paused(), "ERC20Pausable: token transfer while paused");

_balances[sender] -= total;
_updateAccountSnapshot(sender);

for (uint256 i = 0; i < recipients.length; ++i) {
address recipient = recipients[i];
require(recipient != address(0), "ERC20: transfer to the zero address");

// Note: _beforeTokenTransfer isn't called here
// This function emulates what it would do (paused and snapshot)

_balances[recipient] += amount;

_updateAccountSnapshot(recipient);

emit Transfer(sender, recipient, amount);
}

return true;
}

/**
* Utility function to transfer tokens to many addresses at once.
* @param sender The address to send the tokens from
* @param recipients The addresses to send tokens to
* @param amount The amount of tokens to send
* @return Boolean if the transfer was a success
*/
function transferFromBulk(
address sender,
address[] calldata recipients,
uint256 amount
) external returns (bool) {
require(!paused(), "ERC20Pausable: token transfer while paused");

uint256 total = amount * recipients.length;
require(
_balances[sender] >= total,
"ERC20: transfer amount exceeds balance"
);

// Ensure enough allowance
uint256 currentAllowance = _allowances[sender][_msgSender()];
require(
currentAllowance >= total,
"ERC20: transfer total exceeds allowance"
);
_approve(sender, _msgSender(), currentAllowance - total);

_balances[sender] -= total;
_updateAccountSnapshot(sender);

for (uint256 i = 0; i < recipients.length; ++i) {
address recipient = recipients[i];
require(recipient != address(0), "ERC20: transfer to the zero address");

// Note: _beforeTokenTransfer isn't called here
// This function emulates what it would do (paused and snapshot)

_balances[recipient] += amount;

_updateAccountSnapshot(recipient);

emit Transfer(sender, recipient, amount);
}

return true;
}

function _beforeTokenTransfer(
address from,
address to,
uint256 amount
)
internal
virtual
override(
ERC20PausableUpgradeable,
ERC20SnapshotUpgradeable,
ERC20Upgradeable
)
{
super._beforeTokenTransfer(from, to, amount);
}
}
Loading