refactor: migrate codebase to foundry-template (#8)

Closes #3
This commit is contained in:
r4bbit
2023-09-12 16:21:51 +02:00
committed by GitHub
parent ea04d950ee
commit 4d63c8448a
39 changed files with 907 additions and 1702 deletions

View File

@@ -1,7 +0,0 @@
{
"presets": ["es2015", "stage-2"],
"plugins": [
"add-module-exports"
]
}

19
.editorconfig Normal file
View File

@@ -0,0 +1,19 @@
# EditorConfig http://EditorConfig.org
# top-most EditorConfig file
root = true
# All files
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.sol]
indent_size = 4
[*.tree]
indent_size = 1

11
.env.example Normal file
View File

@@ -0,0 +1,11 @@
export API_KEY_ALCHEMY="YOUR_API_KEY_ALCHEMY"
export API_KEY_ARBISCAN="YOUR_API_KEY_ARBISCAN"
export API_KEY_BSCSCAN="YOUR_API_KEY_BSCSCAN"
export API_KEY_ETHERSCAN="YOUR_API_KEY_ETHERSCAN"
export API_KEY_GNOSISSCAN="YOUR_API_KEY_GNOSISSCAN"
export API_KEY_INFURA="YOUR_API_KEY_INFURA"
export API_KEY_OPTIMISTIC_ETHERSCAN="YOUR_API_KEY_OPTIMISTIC_ETHERSCAN"
export API_KEY_POLYGONSCAN="YOUR_API_KEY_POLYGONSCAN"
export API_KEY_SNOWTRACE="YOUR_API_KEY_SNOWTRACE"
export MNEMONIC="YOUR_MNEMONIC"
export FOUNDRY_PROFILE="default"

View File

@@ -1,7 +0,0 @@
{
"extends": "airbnb",
"rules": {
"jsx-a11y/href-no-hash": "off",
"jsx-a11y/anchor-is-valid": ["warn", { "aspects": ["invalidHref"] }]
}
}

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
lib/** linguist-vendored

12
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,12 @@
## Description
Describe the changes made in your pull request here.
## Checklist
Ensure you completed **all of the steps** below before submitting your pull request:
- [ ] Added natspec comments?
- [ ] Ran `forge snapshot`?
- [ ] Ran `pnpm lint`?
- [ ] Ran `forge test`?

119
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,119 @@
name: "CI"
env:
API_KEY_ALCHEMY: ${{ secrets.API_KEY_ALCHEMY }}
FOUNDRY_PROFILE: "ci"
on:
workflow_dispatch:
pull_request:
push:
branches:
- "main"
jobs:
lint:
runs-on: "ubuntu-latest"
steps:
- name: "Check out the repo"
uses: "actions/checkout@v3"
with:
submodules: "recursive"
- name: "Install Foundry"
uses: "foundry-rs/foundry-toolchain@v1"
- name: "Install Pnpm"
uses: "pnpm/action-setup@v2"
with:
version: "8"
- name: "Install Node.js"
uses: "actions/setup-node@v3"
with:
cache: "pnpm"
node-version: "lts/*"
- name: "Install the Node.js dependencies"
run: "pnpm install"
- name: "Lint the contracts"
run: "pnpm lint"
- name: "Add lint summary"
run: |
echo "## Lint result" >> $GITHUB_STEP_SUMMARY
echo "✅ Passed" >> $GITHUB_STEP_SUMMARY
build:
runs-on: "ubuntu-latest"
steps:
- name: "Check out the repo"
uses: "actions/checkout@v3"
with:
submodules: "recursive"
- name: "Install Foundry"
uses: "foundry-rs/foundry-toolchain@v1"
- name: "Build the contracts and print their size"
run: "forge build --sizes"
- name: "Add build summary"
run: |
echo "## Build result" >> $GITHUB_STEP_SUMMARY
echo "✅ Passed" >> $GITHUB_STEP_SUMMARY
test:
needs: ["lint", "build"]
runs-on: "ubuntu-latest"
steps:
- name: "Check out the repo"
uses: "actions/checkout@v3"
with:
submodules: "recursive"
- name: "Install Foundry"
uses: "foundry-rs/foundry-toolchain@v1"
- name: "Show the Foundry config"
run: "forge config"
- name: "Generate a fuzz seed that changes weekly to avoid burning through RPC allowance"
run: >
echo "FOUNDRY_FUZZ_SEED=$(
echo $(($EPOCHSECONDS - $EPOCHSECONDS % 604800))
)" >> $GITHUB_ENV
- name: "Run the tests"
run: "forge test"
- name: "Add test summary"
run: |
echo "## Tests result" >> $GITHUB_STEP_SUMMARY
echo "✅ Passed" >> $GITHUB_STEP_SUMMARY
coverage:
needs: ["lint", "build"]
runs-on: "ubuntu-latest"
steps:
- name: "Check out the repo"
uses: "actions/checkout@v3"
with:
submodules: "recursive"
- name: "Install Foundry"
uses: "foundry-rs/foundry-toolchain@v1"
- name: "Generate the coverage report using the unit and the integration tests"
run: 'forge coverage --match-path "test/**/*.sol" --report lcov'
- name: "Upload coverage report to Codecov"
uses: "codecov/codecov-action@v3"
with:
files: "./lcov.info"
- name: "Add coverage summary"
run: |
echo "## Coverage result" >> $GITHUB_STEP_SUMMARY
echo "✅ Uploaded to Codecov" >> $GITHUB_STEP_SUMMARY

18
.gitignore vendored
View File

@@ -1 +1,17 @@
node_modules/
# directories
cache
node_modules
out
# files
*.env
*.log
.DS_Store
.pnp.*
lcov.info
yarn.lock
# broadcasts
!broadcast
broadcast/*
broadcast/*/31337/

4
.gitmodules vendored Normal file
View File

@@ -0,0 +1,4 @@
[submodule "lib/forge-std"]
branch = "v1"
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std

18
.prettierignore Normal file
View File

@@ -0,0 +1,18 @@
# directories
broadcast
cache
lib
node_modules
out
# files
*.env
*.log
.DS_Store
.pnp.*
lcov.info
package-lock.json
pnpm-lock.yaml
yarn.lock
slither.config.json

7
.prettierrc.yml Normal file
View File

@@ -0,0 +1,7 @@
bracketSpacing: true
printWidth: 120
proseWrap: "always"
singleQuote: false
tabWidth: 2
trailingComma: "all"
useTabs: false

13
.solhint.json Normal file
View File

@@ -0,0 +1,13 @@
{
"extends": "solhint:recommended",
"rules": {
"code-complexity": ["error", 8],
"compiler-version": ["error", ">=0.8.19"],
"func-name-mixedcase": "off",
"func-visibility": ["error", { "ignoreConstructors": true }],
"max-line-length": ["error", 120],
"named-parameters-mapping": "warn",
"no-console": "off",
"not-rely-on-time": "off"
}
}

View File

@@ -1,3 +0,0 @@
language: 'node_js'
node_js:
- '8'

10
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,10 @@
{
"[solidity]": {
"editor.defaultFormatter": "NomicFoundation.hardhat-solidity"
},
"[toml]": {
"editor.defaultFormatter": "tamasfe.even-better-toml"
},
"npm.exclude": "**/lib/**",
"solidity.formatter": "forge"
}

15
PROPERTIES.md Normal file
View File

@@ -0,0 +1,15 @@
## Protocol properties and invariants
Below is a list of all documented properties and invariants of this project that must hold true.
- **Property** - Describes the property of the project / protocol that should ultimately be tested and formaly verified.
- **Type** - Properties are split into 5 main types: **Valid State**, **State Transition**, **Variable Transition**,
**High-Level Property**, **Unit Test**
- **Risk** - One of **High**, **Medium** and **Low**, depending on the property's risk factor
- **Tested** - Whether this property has been (fuzz) tested
| **Property** | **Type** | **Risk** | **Tested** |
| ------------ | -------- | -------- | ---------- |
| | | | |
| | | | |
| | | | |

View File

@@ -1,7 +0,0 @@
/* This is an autogenerated file. DO NOT EDIT MANUALLY */
exports.ControlledAbi = [{"constant":false,"inputs":[{"name":"_newController","type":"address"}],"name":"changeController","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"controller","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]
exports.ControlledByteCode = "0x6060604052341561000f57600080fd5b60008054600160a060020a033316600160a060020a03199091161790556101668061003b6000396000f30060606040526004361061004b5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416633cebb8238114610050578063f77c47911461007e575b600080fd5b341561005b57600080fd5b61007c73ffffffffffffffffffffffffffffffffffffffff600435166100ba565b005b341561008957600080fd5b61009161011e565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b6000543373ffffffffffffffffffffffffffffffffffffffff9081169116146100e257600080fd5b6000805473ffffffffffffffffffffffffffffffffffffffff191673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60005473ffffffffffffffffffffffffffffffffffffffff16815600a165627a7a72305820348e33213b8644e828ed3b4249fa8ae33458cd699d8de08bf42e589b2b9fe1eb0029"
exports.ControlledRuntimeByteCode = "0x60606040526004361061004b5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416633cebb8238114610050578063f77c47911461007e575b600080fd5b341561005b57600080fd5b61007c73ffffffffffffffffffffffffffffffffffffffff600435166100ba565b005b341561008957600080fd5b61009161011e565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b6000543373ffffffffffffffffffffffffffffffffffffffff9081169116146100e257600080fd5b6000805473ffffffffffffffffffffffffffffffffffffffff191673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60005473ffffffffffffffffffffffffffffffffffffffff16815600a165627a7a72305820348e33213b8644e828ed3b4249fa8ae33458cd699d8de08bf42e589b2b9fe1eb0029"
exports._solcVersion = "0.4.18+commit.9cf6e910.Emscripten.clang"
exports._sha256 = "0xcd25f1d20c06711ec5147fce5dc8518b5374f61f2313d4035c3650bd229e9de0"

View File

@@ -1,19 +0,0 @@
//File: ./contracts/Controlled.sol
pragma solidity ^0.4.18;
contract Controlled {
/// @notice The address of the controller is the only address that can call
/// a function with this modifier
modifier onlyController { require(msg.sender == controller); _; }
address public controller;
function Controlled() public { controller = msg.sender;}
/// @notice Changes the controller of the contract
/// @param _newController The new controller of the contract
function changeController(address _newController) public onlyController {
controller = _newController;
}
}

File diff suppressed because one or more lines are too long

View File

@@ -1,613 +0,0 @@
//File: contracts/Controlled.sol
pragma solidity ^0.4.18;
contract Controlled {
/// @notice The address of the controller is the only address that can call
/// a function with this modifier
modifier onlyController { require(msg.sender == controller); _; }
address public controller;
function Controlled() public { controller = msg.sender;}
/// @notice Changes the controller of the contract
/// @param _newController The new controller of the contract
function changeController(address _newController) public onlyController {
controller = _newController;
}
}
//File: contracts/TokenController.sol
pragma solidity ^0.4.18;
/// @dev The token controller contract must implement these functions
contract TokenController {
/// @notice Called when `_owner` sends ether to the MiniMe Token contract
/// @param _owner The address that sent the ether to create tokens
/// @return True if the ether is accepted, false if it throws
function proxyPayment(address _owner) public payable returns(bool);
/// @notice Notifies the controller about a token transfer allowing the
/// controller to react if desired
/// @param _from The origin of the transfer
/// @param _to The destination of the transfer
/// @param _amount The amount of the transfer
/// @return False if the controller does not authorize the transfer
function onTransfer(address _from, address _to, uint _amount) public returns(bool);
/// @notice Notifies the controller about an approval allowing the
/// controller to react if desired
/// @param _owner The address that calls `approve()`
/// @param _spender The spender in the `approve()` call
/// @param _amount The amount in the `approve()` call
/// @return False if the controller does not authorize the approval
function onApprove(address _owner, address _spender, uint _amount) public
returns(bool);
}
//File: ./contracts/MiniMeToken.sol
pragma solidity ^0.4.18;
/*
Copyright 2016, Jordi Baylina
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/// @title MiniMeToken Contract
/// @author Jordi Baylina
/// @dev This token contract's goal is to make it easy for anyone to clone this
/// token using the token distribution at a given block, this will allow DAO's
/// and DApps to upgrade their features in a decentralized manner without
/// affecting the original token
/// @dev It is ERC20 compliant, but still needs to under go further testing.
contract ApproveAndCallFallBack {
function receiveApproval(address from, uint256 _amount, address _token, bytes _data) public;
}
/// @dev The actual token contract, the default controller is the msg.sender
/// that deploys the contract, so usually this token will be deployed by a
/// token controller contract, which Giveth will call a "Campaign"
contract MiniMeToken is Controlled {
string public name; //The Token's name: e.g. DigixDAO Tokens
uint8 public decimals; //Number of decimals of the smallest unit
string public symbol; //An identifier: e.g. REP
string public version = 'MMT_0.2'; //An arbitrary versioning scheme
/// @dev `Checkpoint` is the structure that attaches a block number to a
/// given value, the block number attached is the one that last changed the
/// value
struct Checkpoint {
// `fromBlock` is the block number that the value was generated from
uint128 fromBlock;
// `value` is the amount of tokens at a specific block number
uint128 value;
}
// `parentToken` is the Token address that was cloned to produce this token;
// it will be 0x0 for a token that was not cloned
MiniMeToken public parentToken;
// `parentSnapShotBlock` is the block number from the Parent Token that was
// used to determine the initial distribution of the Clone Token
uint public parentSnapShotBlock;
// `creationBlock` is the block number that the Clone Token was created
uint public creationBlock;
// `balances` is the map that tracks the balance of each address, in this
// contract when the balance changes the block number that the change
// occurred is also included in the map
mapping (address => Checkpoint[]) balances;
// `allowed` tracks any extra transfer rights as in all ERC20 tokens
mapping (address => mapping (address => uint256)) allowed;
// Tracks the history of the `totalSupply` of the token
Checkpoint[] totalSupplyHistory;
// Flag that determines if the token is transferable or not.
bool public transfersEnabled;
// The factory used to create new clone tokens
MiniMeTokenFactory public tokenFactory;
////////////////
// Constructor
////////////////
/// @notice Constructor to create a MiniMeToken
/// @param _tokenFactory The address of the MiniMeTokenFactory contract that
/// will create the Clone token contracts, the token factory needs to be
/// deployed first
/// @param _parentToken Address of the parent token, set to 0x0 if it is a
/// new token
/// @param _parentSnapShotBlock Block of the parent token that will
/// determine the initial distribution of the clone token, set to 0 if it
/// is a new token
/// @param _tokenName Name of the new token
/// @param _decimalUnits Number of decimals of the new token
/// @param _tokenSymbol Token Symbol for the new token
/// @param _transfersEnabled If true, tokens will be able to be transferred
function MiniMeToken(
address _tokenFactory,
address _parentToken,
uint _parentSnapShotBlock,
string _tokenName,
uint8 _decimalUnits,
string _tokenSymbol,
bool _transfersEnabled
) public {
tokenFactory = MiniMeTokenFactory(_tokenFactory);
name = _tokenName; // Set the name
decimals = _decimalUnits; // Set the decimals
symbol = _tokenSymbol; // Set the symbol
parentToken = MiniMeToken(_parentToken);
parentSnapShotBlock = _parentSnapShotBlock;
transfersEnabled = _transfersEnabled;
creationBlock = block.number;
}
///////////////////
// ERC20 Methods
///////////////////
/// @notice Send `_amount` tokens to `_to` from `msg.sender`
/// @param _to The address of the recipient
/// @param _amount The amount of tokens to be transferred
/// @return Whether the transfer was successful or not
function transfer(address _to, uint256 _amount) public returns (bool success) {
require(transfersEnabled);
doTransfer(msg.sender, _to, _amount);
return true;
}
/// @notice Send `_amount` tokens to `_to` from `_from` on the condition it
/// is approved by `_from`
/// @param _from The address holding the tokens being transferred
/// @param _to The address of the recipient
/// @param _amount The amount of tokens to be transferred
/// @return True if the transfer was successful
function transferFrom(address _from, address _to, uint256 _amount
) public returns (bool success) {
// The controller of this contract can move tokens around at will,
// this is important to recognize! Confirm that you trust the
// controller of this contract, which in most situations should be
// another open source smart contract or 0x0
if (msg.sender != controller) {
require(transfersEnabled);
// The standard ERC 20 transferFrom functionality
require(allowed[_from][msg.sender] >= _amount);
allowed[_from][msg.sender] -= _amount;
}
doTransfer(_from, _to, _amount);
return true;
}
/// @dev This is the actual transfer function in the token contract, it can
/// only be called by other functions in this contract.
/// @param _from The address holding the tokens being transferred
/// @param _to The address of the recipient
/// @param _amount The amount of tokens to be transferred
/// @return True if the transfer was successful
function doTransfer(address _from, address _to, uint _amount
) internal {
if (_amount == 0) {
Transfer(_from, _to, _amount); // Follow the spec to louch the event when transfer 0
return;
}
require(parentSnapShotBlock < block.number);
// Do not allow transfer to 0x0 or the token contract itself
require((_to != 0) && (_to != address(this)));
// If the amount being transfered is more than the balance of the
// account the transfer throws
var previousBalanceFrom = balanceOfAt(_from, block.number);
require(previousBalanceFrom >= _amount);
// Alerts the token controller of the transfer
if (isContract(controller)) {
require(TokenController(controller).onTransfer(_from, _to, _amount));
}
// First update the balance array with the new value for the address
// sending the tokens
updateValueAtNow(balances[_from], previousBalanceFrom - _amount);
// Then update the balance array with the new value for the address
// receiving the tokens
var previousBalanceTo = balanceOfAt(_to, block.number);
require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow
updateValueAtNow(balances[_to], previousBalanceTo + _amount);
// An event to make the transfer easy to find on the blockchain
Transfer(_from, _to, _amount);
}
/// @param _owner The address that's balance is being requested
/// @return The balance of `_owner` at the current block
function balanceOf(address _owner) public constant returns (uint256 balance) {
return balanceOfAt(_owner, block.number);
}
/// @notice `msg.sender` approves `_spender` to spend `_amount` tokens on
/// its behalf. This is a modified version of the ERC20 approve function
/// to be a little bit safer
/// @param _spender The address of the account able to transfer the tokens
/// @param _amount The amount of tokens to be approved for transfer
/// @return True if the approval was successful
function approve(address _spender, uint256 _amount) public returns (bool success) {
require(transfersEnabled);
// To change the approve amount you first have to reduce the addresses`
// allowance to zero by calling `approve(_spender,0)` if it is not
// already 0 to mitigate the race condition described here:
// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
require((_amount == 0) || (allowed[msg.sender][_spender] == 0));
// Alerts the token controller of the approve function call
if (isContract(controller)) {
require(TokenController(controller).onApprove(msg.sender, _spender, _amount));
}
allowed[msg.sender][_spender] = _amount;
Approval(msg.sender, _spender, _amount);
return true;
}
/// @dev This function makes it easy to read the `allowed[]` map
/// @param _owner The address of the account that owns the token
/// @param _spender The address of the account able to transfer the tokens
/// @return Amount of remaining tokens of _owner that _spender is allowed
/// to spend
function allowance(address _owner, address _spender
) public constant returns (uint256 remaining) {
return allowed[_owner][_spender];
}
/// @notice `msg.sender` approves `_spender` to send `_amount` tokens on
/// its behalf, and then a function is triggered in the contract that is
/// being approved, `_spender`. This allows users to use their tokens to
/// interact with contracts in one function call instead of two
/// @param _spender The address of the contract able to transfer the tokens
/// @param _amount The amount of tokens to be approved for transfer
/// @return True if the function call was successful
function approveAndCall(address _spender, uint256 _amount, bytes _extraData
) public returns (bool success) {
require(approve(_spender, _amount));
ApproveAndCallFallBack(_spender).receiveApproval(
msg.sender,
_amount,
this,
_extraData
);
return true;
}
/// @dev This function makes it easy to get the total number of tokens
/// @return The total number of tokens
function totalSupply() public constant returns (uint) {
return totalSupplyAt(block.number);
}
////////////////
// Query balance and totalSupply in History
////////////////
/// @dev Queries the balance of `_owner` at a specific `_blockNumber`
/// @param _owner The address from which the balance will be retrieved
/// @param _blockNumber The block number when the balance is queried
/// @return The balance at `_blockNumber`
function balanceOfAt(address _owner, uint _blockNumber) public constant
returns (uint) {
// These next few lines are used when the balance of the token is
// requested before a check point was ever created for this token, it
// requires that the `parentToken.balanceOfAt` be queried at the
// genesis block for that token as this contains initial balance of
// this token
if ((balances[_owner].length == 0)
|| (balances[_owner][0].fromBlock > _blockNumber)) {
if (address(parentToken) != 0) {
return parentToken.balanceOfAt(_owner, min(_blockNumber, parentSnapShotBlock));
} else {
// Has no parent
return 0;
}
// This will return the expected balance during normal situations
} else {
return getValueAt(balances[_owner], _blockNumber);
}
}
/// @notice Total amount of tokens at a specific `_blockNumber`.
/// @param _blockNumber The block number when the totalSupply is queried
/// @return The total amount of tokens at `_blockNumber`
function totalSupplyAt(uint _blockNumber) public constant returns(uint) {
// These next few lines are used when the totalSupply of the token is
// requested before a check point was ever created for this token, it
// requires that the `parentToken.totalSupplyAt` be queried at the
// genesis block for this token as that contains totalSupply of this
// token at this block number.
if ((totalSupplyHistory.length == 0)
|| (totalSupplyHistory[0].fromBlock > _blockNumber)) {
if (address(parentToken) != 0) {
return parentToken.totalSupplyAt(min(_blockNumber, parentSnapShotBlock));
} else {
return 0;
}
// This will return the expected totalSupply during normal situations
} else {
return getValueAt(totalSupplyHistory, _blockNumber);
}
}
////////////////
// Clone Token Method
////////////////
/// @notice Creates a new clone token with the initial distribution being
/// this token at `_snapshotBlock`
/// @param _cloneTokenName Name of the clone token
/// @param _cloneDecimalUnits Number of decimals of the smallest unit
/// @param _cloneTokenSymbol Symbol of the clone token
/// @param _snapshotBlock Block when the distribution of the parent token is
/// copied to set the initial distribution of the new clone token;
/// if the block is zero than the actual block, the current block is used
/// @param _transfersEnabled True if transfers are allowed in the clone
/// @return The address of the new MiniMeToken Contract
function createCloneToken(
string _cloneTokenName,
uint8 _cloneDecimalUnits,
string _cloneTokenSymbol,
uint _snapshotBlock,
bool _transfersEnabled
) public returns(address) {
if (_snapshotBlock == 0) _snapshotBlock = block.number;
MiniMeToken cloneToken = tokenFactory.createCloneToken(
this,
_snapshotBlock,
_cloneTokenName,
_cloneDecimalUnits,
_cloneTokenSymbol,
_transfersEnabled
);
cloneToken.changeController(msg.sender);
// An event to make the token easy to find on the blockchain
NewCloneToken(address(cloneToken), _snapshotBlock);
return address(cloneToken);
}
////////////////
// Generate and destroy tokens
////////////////
/// @notice Generates `_amount` tokens that are assigned to `_owner`
/// @param _owner The address that will be assigned the new tokens
/// @param _amount The quantity of tokens generated
/// @return True if the tokens are generated correctly
function generateTokens(address _owner, uint _amount
) public onlyController returns (bool) {
uint curTotalSupply = totalSupply();
require(curTotalSupply + _amount >= curTotalSupply); // Check for overflow
uint previousBalanceTo = balanceOf(_owner);
require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow
updateValueAtNow(totalSupplyHistory, curTotalSupply + _amount);
updateValueAtNow(balances[_owner], previousBalanceTo + _amount);
Transfer(0, _owner, _amount);
return true;
}
/// @notice Burns `_amount` tokens from `_owner`
/// @param _owner The address that will lose the tokens
/// @param _amount The quantity of tokens to burn
/// @return True if the tokens are burned correctly
function destroyTokens(address _owner, uint _amount
) onlyController public returns (bool) {
uint curTotalSupply = totalSupply();
require(curTotalSupply >= _amount);
uint previousBalanceFrom = balanceOf(_owner);
require(previousBalanceFrom >= _amount);
updateValueAtNow(totalSupplyHistory, curTotalSupply - _amount);
updateValueAtNow(balances[_owner], previousBalanceFrom - _amount);
Transfer(_owner, 0, _amount);
return true;
}
////////////////
// Enable tokens transfers
////////////////
/// @notice Enables token holders to transfer their tokens freely if true
/// @param _transfersEnabled True if transfers are allowed in the clone
function enableTransfers(bool _transfersEnabled) public onlyController {
transfersEnabled = _transfersEnabled;
}
////////////////
// Internal helper functions to query and set a value in a snapshot array
////////////////
/// @dev `getValueAt` retrieves the number of tokens at a given block number
/// @param checkpoints The history of values being queried
/// @param _block The block number to retrieve the value at
/// @return The number of tokens being queried
function getValueAt(Checkpoint[] storage checkpoints, uint _block
) constant internal returns (uint) {
if (checkpoints.length == 0) return 0;
// Shortcut for the actual value
if (_block >= checkpoints[checkpoints.length-1].fromBlock)
return checkpoints[checkpoints.length-1].value;
if (_block < checkpoints[0].fromBlock) return 0;
// Binary search of the value in the array
uint min = 0;
uint max = checkpoints.length-1;
while (max > min) {
uint mid = (max + min + 1)/ 2;
if (checkpoints[mid].fromBlock<=_block) {
min = mid;
} else {
max = mid-1;
}
}
return checkpoints[min].value;
}
/// @dev `updateValueAtNow` used to update the `balances` map and the
/// `totalSupplyHistory`
/// @param checkpoints The history of data being updated
/// @param _value The new number of tokens
function updateValueAtNow(Checkpoint[] storage checkpoints, uint _value
) internal {
if ((checkpoints.length == 0)
|| (checkpoints[checkpoints.length -1].fromBlock < block.number)) {
Checkpoint storage newCheckPoint = checkpoints[ checkpoints.length++ ];
newCheckPoint.fromBlock = uint128(block.number);
newCheckPoint.value = uint128(_value);
} else {
Checkpoint storage oldCheckPoint = checkpoints[checkpoints.length-1];
oldCheckPoint.value = uint128(_value);
}
}
/// @dev Internal function to determine if an address is a contract
/// @param _addr The address being queried
/// @return True if `_addr` is a contract
function isContract(address _addr) constant internal returns(bool) {
uint size;
if (_addr == 0) return false;
assembly {
size := extcodesize(_addr)
}
return size>0;
}
/// @dev Helper function to return a min betwen the two uints
function min(uint a, uint b) pure internal returns (uint) {
return a < b ? a : b;
}
/// @notice The fallback function: If the contract's controller has not been
/// set to 0, then the `proxyPayment` method is called which relays the
/// ether and creates tokens as described in the token controller contract
function () public payable {
require(isContract(controller));
require(TokenController(controller).proxyPayment.value(msg.value)(msg.sender));
}
//////////
// Safety Methods
//////////
/// @notice This method can be used by the controller to extract mistakenly
/// sent tokens to this contract.
/// @param _token The address of the token contract that you want to recover
/// set to 0 in case you want to extract ether.
function claimTokens(address _token) public onlyController {
if (_token == 0x0) {
controller.transfer(this.balance);
return;
}
MiniMeToken token = MiniMeToken(_token);
uint balance = token.balanceOf(this);
token.transfer(controller, balance);
ClaimedTokens(_token, controller, balance);
}
////////////////
// Events
////////////////
event ClaimedTokens(address indexed _token, address indexed _controller, uint _amount);
event Transfer(address indexed _from, address indexed _to, uint256 _amount);
event NewCloneToken(address indexed _cloneToken, uint _snapshotBlock);
event Approval(
address indexed _owner,
address indexed _spender,
uint256 _amount
);
}
////////////////
// MiniMeTokenFactory
////////////////
/// @dev This contract is used to generate clone contracts from a contract.
/// In solidity this is the way to create a contract from a contract of the
/// same class
contract MiniMeTokenFactory {
/// @notice Update the DApp by creating a new token with new functionalities
/// the msg.sender becomes the controller of this clone token
/// @param _parentToken Address of the token being cloned
/// @param _snapshotBlock Block of the parent token that will
/// determine the initial distribution of the clone token
/// @param _tokenName Name of the new token
/// @param _decimalUnits Number of decimals of the new token
/// @param _tokenSymbol Token Symbol for the new token
/// @param _transfersEnabled If true, tokens will be able to be transferred
/// @return The address of the new token contract
function createCloneToken(
address _parentToken,
uint _snapshotBlock,
string _tokenName,
uint8 _decimalUnits,
string _tokenSymbol,
bool _transfersEnabled
) public returns (MiniMeToken) {
MiniMeToken newToken = new MiniMeToken(
this,
_parentToken,
_snapshotBlock,
_tokenName,
_decimalUnits,
_tokenSymbol,
_transfersEnabled
);
newToken.changeController(msg.sender);
return newToken;
}
}

File diff suppressed because one or more lines are too long

View File

@@ -1,800 +0,0 @@
//File: contracts/Controlled.sol
pragma solidity ^0.4.18;
contract Controlled {
/// @notice The address of the controller is the only address that can call
/// a function with this modifier
modifier onlyController { require(msg.sender == controller); _; }
address public controller;
function Controlled() public { controller = msg.sender;}
/// @notice Changes the controller of the contract
/// @param _newController The new controller of the contract
function changeController(address _newController) public onlyController {
controller = _newController;
}
}
//File: contracts/TokenController.sol
pragma solidity ^0.4.18;
/// @dev The token controller contract must implement these functions
contract TokenController {
/// @notice Called when `_owner` sends ether to the MiniMe Token contract
/// @param _owner The address that sent the ether to create tokens
/// @return True if the ether is accepted, false if it throws
function proxyPayment(address _owner) public payable returns(bool);
/// @notice Notifies the controller about a token transfer allowing the
/// controller to react if desired
/// @param _from The origin of the transfer
/// @param _to The destination of the transfer
/// @param _amount The amount of the transfer
/// @return False if the controller does not authorize the transfer
function onTransfer(address _from, address _to, uint _amount) public returns(bool);
/// @notice Notifies the controller about an approval allowing the
/// controller to react if desired
/// @param _owner The address that calls `approve()`
/// @param _spender The spender in the `approve()` call
/// @param _amount The amount in the `approve()` call
/// @return False if the controller does not authorize the approval
function onApprove(address _owner, address _spender, uint _amount) public
returns(bool);
}
//File: contracts/MiniMeToken.sol
pragma solidity ^0.4.18;
/*
Copyright 2016, Jordi Baylina
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/// @title MiniMeToken Contract
/// @author Jordi Baylina
/// @dev This token contract's goal is to make it easy for anyone to clone this
/// token using the token distribution at a given block, this will allow DAO's
/// and DApps to upgrade their features in a decentralized manner without
/// affecting the original token
/// @dev It is ERC20 compliant, but still needs to under go further testing.
contract ApproveAndCallFallBack {
function receiveApproval(address from, uint256 _amount, address _token, bytes _data) public;
}
/// @dev The actual token contract, the default controller is the msg.sender
/// that deploys the contract, so usually this token will be deployed by a
/// token controller contract, which Giveth will call a "Campaign"
contract MiniMeToken is Controlled {
string public name; //The Token's name: e.g. DigixDAO Tokens
uint8 public decimals; //Number of decimals of the smallest unit
string public symbol; //An identifier: e.g. REP
string public version = 'MMT_0.2'; //An arbitrary versioning scheme
/// @dev `Checkpoint` is the structure that attaches a block number to a
/// given value, the block number attached is the one that last changed the
/// value
struct Checkpoint {
// `fromBlock` is the block number that the value was generated from
uint128 fromBlock;
// `value` is the amount of tokens at a specific block number
uint128 value;
}
// `parentToken` is the Token address that was cloned to produce this token;
// it will be 0x0 for a token that was not cloned
MiniMeToken public parentToken;
// `parentSnapShotBlock` is the block number from the Parent Token that was
// used to determine the initial distribution of the Clone Token
uint public parentSnapShotBlock;
// `creationBlock` is the block number that the Clone Token was created
uint public creationBlock;
// `balances` is the map that tracks the balance of each address, in this
// contract when the balance changes the block number that the change
// occurred is also included in the map
mapping (address => Checkpoint[]) balances;
// `allowed` tracks any extra transfer rights as in all ERC20 tokens
mapping (address => mapping (address => uint256)) allowed;
// Tracks the history of the `totalSupply` of the token
Checkpoint[] totalSupplyHistory;
// Flag that determines if the token is transferable or not.
bool public transfersEnabled;
// The factory used to create new clone tokens
MiniMeTokenFactory public tokenFactory;
////////////////
// Constructor
////////////////
/// @notice Constructor to create a MiniMeToken
/// @param _tokenFactory The address of the MiniMeTokenFactory contract that
/// will create the Clone token contracts, the token factory needs to be
/// deployed first
/// @param _parentToken Address of the parent token, set to 0x0 if it is a
/// new token
/// @param _parentSnapShotBlock Block of the parent token that will
/// determine the initial distribution of the clone token, set to 0 if it
/// is a new token
/// @param _tokenName Name of the new token
/// @param _decimalUnits Number of decimals of the new token
/// @param _tokenSymbol Token Symbol for the new token
/// @param _transfersEnabled If true, tokens will be able to be transferred
function MiniMeToken(
address _tokenFactory,
address _parentToken,
uint _parentSnapShotBlock,
string _tokenName,
uint8 _decimalUnits,
string _tokenSymbol,
bool _transfersEnabled
) public {
tokenFactory = MiniMeTokenFactory(_tokenFactory);
name = _tokenName; // Set the name
decimals = _decimalUnits; // Set the decimals
symbol = _tokenSymbol; // Set the symbol
parentToken = MiniMeToken(_parentToken);
parentSnapShotBlock = _parentSnapShotBlock;
transfersEnabled = _transfersEnabled;
creationBlock = block.number;
}
///////////////////
// ERC20 Methods
///////////////////
/// @notice Send `_amount` tokens to `_to` from `msg.sender`
/// @param _to The address of the recipient
/// @param _amount The amount of tokens to be transferred
/// @return Whether the transfer was successful or not
function transfer(address _to, uint256 _amount) public returns (bool success) {
require(transfersEnabled);
doTransfer(msg.sender, _to, _amount);
return true;
}
/// @notice Send `_amount` tokens to `_to` from `_from` on the condition it
/// is approved by `_from`
/// @param _from The address holding the tokens being transferred
/// @param _to The address of the recipient
/// @param _amount The amount of tokens to be transferred
/// @return True if the transfer was successful
function transferFrom(address _from, address _to, uint256 _amount
) public returns (bool success) {
// The controller of this contract can move tokens around at will,
// this is important to recognize! Confirm that you trust the
// controller of this contract, which in most situations should be
// another open source smart contract or 0x0
if (msg.sender != controller) {
require(transfersEnabled);
// The standard ERC 20 transferFrom functionality
require(allowed[_from][msg.sender] >= _amount);
allowed[_from][msg.sender] -= _amount;
}
doTransfer(_from, _to, _amount);
return true;
}
/// @dev This is the actual transfer function in the token contract, it can
/// only be called by other functions in this contract.
/// @param _from The address holding the tokens being transferred
/// @param _to The address of the recipient
/// @param _amount The amount of tokens to be transferred
/// @return True if the transfer was successful
function doTransfer(address _from, address _to, uint _amount
) internal {
if (_amount == 0) {
Transfer(_from, _to, _amount); // Follow the spec to louch the event when transfer 0
return;
}
require(parentSnapShotBlock < block.number);
// Do not allow transfer to 0x0 or the token contract itself
require((_to != 0) && (_to != address(this)));
// If the amount being transfered is more than the balance of the
// account the transfer throws
var previousBalanceFrom = balanceOfAt(_from, block.number);
require(previousBalanceFrom >= _amount);
// Alerts the token controller of the transfer
if (isContract(controller)) {
require(TokenController(controller).onTransfer(_from, _to, _amount));
}
// First update the balance array with the new value for the address
// sending the tokens
updateValueAtNow(balances[_from], previousBalanceFrom - _amount);
// Then update the balance array with the new value for the address
// receiving the tokens
var previousBalanceTo = balanceOfAt(_to, block.number);
require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow
updateValueAtNow(balances[_to], previousBalanceTo + _amount);
// An event to make the transfer easy to find on the blockchain
Transfer(_from, _to, _amount);
}
/// @param _owner The address that's balance is being requested
/// @return The balance of `_owner` at the current block
function balanceOf(address _owner) public constant returns (uint256 balance) {
return balanceOfAt(_owner, block.number);
}
/// @notice `msg.sender` approves `_spender` to spend `_amount` tokens on
/// its behalf. This is a modified version of the ERC20 approve function
/// to be a little bit safer
/// @param _spender The address of the account able to transfer the tokens
/// @param _amount The amount of tokens to be approved for transfer
/// @return True if the approval was successful
function approve(address _spender, uint256 _amount) public returns (bool success) {
require(transfersEnabled);
// To change the approve amount you first have to reduce the addresses`
// allowance to zero by calling `approve(_spender,0)` if it is not
// already 0 to mitigate the race condition described here:
// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
require((_amount == 0) || (allowed[msg.sender][_spender] == 0));
// Alerts the token controller of the approve function call
if (isContract(controller)) {
require(TokenController(controller).onApprove(msg.sender, _spender, _amount));
}
allowed[msg.sender][_spender] = _amount;
Approval(msg.sender, _spender, _amount);
return true;
}
/// @dev This function makes it easy to read the `allowed[]` map
/// @param _owner The address of the account that owns the token
/// @param _spender The address of the account able to transfer the tokens
/// @return Amount of remaining tokens of _owner that _spender is allowed
/// to spend
function allowance(address _owner, address _spender
) public constant returns (uint256 remaining) {
return allowed[_owner][_spender];
}
/// @notice `msg.sender` approves `_spender` to send `_amount` tokens on
/// its behalf, and then a function is triggered in the contract that is
/// being approved, `_spender`. This allows users to use their tokens to
/// interact with contracts in one function call instead of two
/// @param _spender The address of the contract able to transfer the tokens
/// @param _amount The amount of tokens to be approved for transfer
/// @return True if the function call was successful
function approveAndCall(address _spender, uint256 _amount, bytes _extraData
) public returns (bool success) {
require(approve(_spender, _amount));
ApproveAndCallFallBack(_spender).receiveApproval(
msg.sender,
_amount,
this,
_extraData
);
return true;
}
/// @dev This function makes it easy to get the total number of tokens
/// @return The total number of tokens
function totalSupply() public constant returns (uint) {
return totalSupplyAt(block.number);
}
////////////////
// Query balance and totalSupply in History
////////////////
/// @dev Queries the balance of `_owner` at a specific `_blockNumber`
/// @param _owner The address from which the balance will be retrieved
/// @param _blockNumber The block number when the balance is queried
/// @return The balance at `_blockNumber`
function balanceOfAt(address _owner, uint _blockNumber) public constant
returns (uint) {
// These next few lines are used when the balance of the token is
// requested before a check point was ever created for this token, it
// requires that the `parentToken.balanceOfAt` be queried at the
// genesis block for that token as this contains initial balance of
// this token
if ((balances[_owner].length == 0)
|| (balances[_owner][0].fromBlock > _blockNumber)) {
if (address(parentToken) != 0) {
return parentToken.balanceOfAt(_owner, min(_blockNumber, parentSnapShotBlock));
} else {
// Has no parent
return 0;
}
// This will return the expected balance during normal situations
} else {
return getValueAt(balances[_owner], _blockNumber);
}
}
/// @notice Total amount of tokens at a specific `_blockNumber`.
/// @param _blockNumber The block number when the totalSupply is queried
/// @return The total amount of tokens at `_blockNumber`
function totalSupplyAt(uint _blockNumber) public constant returns(uint) {
// These next few lines are used when the totalSupply of the token is
// requested before a check point was ever created for this token, it
// requires that the `parentToken.totalSupplyAt` be queried at the
// genesis block for this token as that contains totalSupply of this
// token at this block number.
if ((totalSupplyHistory.length == 0)
|| (totalSupplyHistory[0].fromBlock > _blockNumber)) {
if (address(parentToken) != 0) {
return parentToken.totalSupplyAt(min(_blockNumber, parentSnapShotBlock));
} else {
return 0;
}
// This will return the expected totalSupply during normal situations
} else {
return getValueAt(totalSupplyHistory, _blockNumber);
}
}
////////////////
// Clone Token Method
////////////////
/// @notice Creates a new clone token with the initial distribution being
/// this token at `_snapshotBlock`
/// @param _cloneTokenName Name of the clone token
/// @param _cloneDecimalUnits Number of decimals of the smallest unit
/// @param _cloneTokenSymbol Symbol of the clone token
/// @param _snapshotBlock Block when the distribution of the parent token is
/// copied to set the initial distribution of the new clone token;
/// if the block is zero than the actual block, the current block is used
/// @param _transfersEnabled True if transfers are allowed in the clone
/// @return The address of the new MiniMeToken Contract
function createCloneToken(
string _cloneTokenName,
uint8 _cloneDecimalUnits,
string _cloneTokenSymbol,
uint _snapshotBlock,
bool _transfersEnabled
) public returns(address) {
if (_snapshotBlock == 0) _snapshotBlock = block.number;
MiniMeToken cloneToken = tokenFactory.createCloneToken(
this,
_snapshotBlock,
_cloneTokenName,
_cloneDecimalUnits,
_cloneTokenSymbol,
_transfersEnabled
);
cloneToken.changeController(msg.sender);
// An event to make the token easy to find on the blockchain
NewCloneToken(address(cloneToken), _snapshotBlock);
return address(cloneToken);
}
////////////////
// Generate and destroy tokens
////////////////
/// @notice Generates `_amount` tokens that are assigned to `_owner`
/// @param _owner The address that will be assigned the new tokens
/// @param _amount The quantity of tokens generated
/// @return True if the tokens are generated correctly
function generateTokens(address _owner, uint _amount
) public onlyController returns (bool) {
uint curTotalSupply = totalSupply();
require(curTotalSupply + _amount >= curTotalSupply); // Check for overflow
uint previousBalanceTo = balanceOf(_owner);
require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow
updateValueAtNow(totalSupplyHistory, curTotalSupply + _amount);
updateValueAtNow(balances[_owner], previousBalanceTo + _amount);
Transfer(0, _owner, _amount);
return true;
}
/// @notice Burns `_amount` tokens from `_owner`
/// @param _owner The address that will lose the tokens
/// @param _amount The quantity of tokens to burn
/// @return True if the tokens are burned correctly
function destroyTokens(address _owner, uint _amount
) onlyController public returns (bool) {
uint curTotalSupply = totalSupply();
require(curTotalSupply >= _amount);
uint previousBalanceFrom = balanceOf(_owner);
require(previousBalanceFrom >= _amount);
updateValueAtNow(totalSupplyHistory, curTotalSupply - _amount);
updateValueAtNow(balances[_owner], previousBalanceFrom - _amount);
Transfer(_owner, 0, _amount);
return true;
}
////////////////
// Enable tokens transfers
////////////////
/// @notice Enables token holders to transfer their tokens freely if true
/// @param _transfersEnabled True if transfers are allowed in the clone
function enableTransfers(bool _transfersEnabled) public onlyController {
transfersEnabled = _transfersEnabled;
}
////////////////
// Internal helper functions to query and set a value in a snapshot array
////////////////
/// @dev `getValueAt` retrieves the number of tokens at a given block number
/// @param checkpoints The history of values being queried
/// @param _block The block number to retrieve the value at
/// @return The number of tokens being queried
function getValueAt(Checkpoint[] storage checkpoints, uint _block
) constant internal returns (uint) {
if (checkpoints.length == 0) return 0;
// Shortcut for the actual value
if (_block >= checkpoints[checkpoints.length-1].fromBlock)
return checkpoints[checkpoints.length-1].value;
if (_block < checkpoints[0].fromBlock) return 0;
// Binary search of the value in the array
uint min = 0;
uint max = checkpoints.length-1;
while (max > min) {
uint mid = (max + min + 1)/ 2;
if (checkpoints[mid].fromBlock<=_block) {
min = mid;
} else {
max = mid-1;
}
}
return checkpoints[min].value;
}
/// @dev `updateValueAtNow` used to update the `balances` map and the
/// `totalSupplyHistory`
/// @param checkpoints The history of data being updated
/// @param _value The new number of tokens
function updateValueAtNow(Checkpoint[] storage checkpoints, uint _value
) internal {
if ((checkpoints.length == 0)
|| (checkpoints[checkpoints.length -1].fromBlock < block.number)) {
Checkpoint storage newCheckPoint = checkpoints[ checkpoints.length++ ];
newCheckPoint.fromBlock = uint128(block.number);
newCheckPoint.value = uint128(_value);
} else {
Checkpoint storage oldCheckPoint = checkpoints[checkpoints.length-1];
oldCheckPoint.value = uint128(_value);
}
}
/// @dev Internal function to determine if an address is a contract
/// @param _addr The address being queried
/// @return True if `_addr` is a contract
function isContract(address _addr) constant internal returns(bool) {
uint size;
if (_addr == 0) return false;
assembly {
size := extcodesize(_addr)
}
return size>0;
}
/// @dev Helper function to return a min betwen the two uints
function min(uint a, uint b) pure internal returns (uint) {
return a < b ? a : b;
}
/// @notice The fallback function: If the contract's controller has not been
/// set to 0, then the `proxyPayment` method is called which relays the
/// ether and creates tokens as described in the token controller contract
function () public payable {
require(isContract(controller));
require(TokenController(controller).proxyPayment.value(msg.value)(msg.sender));
}
//////////
// Safety Methods
//////////
/// @notice This method can be used by the controller to extract mistakenly
/// sent tokens to this contract.
/// @param _token The address of the token contract that you want to recover
/// set to 0 in case you want to extract ether.
function claimTokens(address _token) public onlyController {
if (_token == 0x0) {
controller.transfer(this.balance);
return;
}
MiniMeToken token = MiniMeToken(_token);
uint balance = token.balanceOf(this);
token.transfer(controller, balance);
ClaimedTokens(_token, controller, balance);
}
////////////////
// Events
////////////////
event ClaimedTokens(address indexed _token, address indexed _controller, uint _amount);
event Transfer(address indexed _from, address indexed _to, uint256 _amount);
event NewCloneToken(address indexed _cloneToken, uint _snapshotBlock);
event Approval(
address indexed _owner,
address indexed _spender,
uint256 _amount
);
}
////////////////
// MiniMeTokenFactory
////////////////
/// @dev This contract is used to generate clone contracts from a contract.
/// In solidity this is the way to create a contract from a contract of the
/// same class
contract MiniMeTokenFactory {
/// @notice Update the DApp by creating a new token with new functionalities
/// the msg.sender becomes the controller of this clone token
/// @param _parentToken Address of the token being cloned
/// @param _snapshotBlock Block of the parent token that will
/// determine the initial distribution of the clone token
/// @param _tokenName Name of the new token
/// @param _decimalUnits Number of decimals of the new token
/// @param _tokenSymbol Token Symbol for the new token
/// @param _transfersEnabled If true, tokens will be able to be transferred
/// @return The address of the new token contract
function createCloneToken(
address _parentToken,
uint _snapshotBlock,
string _tokenName,
uint8 _decimalUnits,
string _tokenSymbol,
bool _transfersEnabled
) public returns (MiniMeToken) {
MiniMeToken newToken = new MiniMeToken(
this,
_parentToken,
_snapshotBlock,
_tokenName,
_decimalUnits,
_tokenSymbol,
_transfersEnabled
);
newToken.changeController(msg.sender);
return newToken;
}
}
//File: ./contracts/SampleCampaign-TokenController.sol
pragma solidity ^0.4.6;
/*
Copyright 2017, Jordi Baylina
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/// @title MilestoneTracker Contract
/// @author Jordi Baylina
/// @dev This contract controls the issuance of tokens for the MiniMe Token
/// Contract. This version specifically acts as a Campaign manager for raising
/// funds for non-profit causes, but it can be customized for any variety of
/// purposes.
/// @dev `Owned` is a base level contract that assigns an `owner` that can be
/// later changed
contract Owned {
/// @dev `owner` is the only address that can call a function with this
/// modifier
modifier onlyOwner { require (msg.sender == owner); _; }
address public owner;
/// @notice The Constructor assigns the message sender to be `owner`
function Owned() { owner = msg.sender;}
/// @notice `owner` can step down and assign some other address to this role
/// @param _newOwner The address of the new owner. 0x0 can be used to create
/// an unowned neutral vault, however that cannot be undone
function changeOwner(address _newOwner) onlyOwner {
owner = _newOwner;
}
}
/// @dev This is designed to control the issuance of a MiniMe Token for a
/// non-profit Campaign. This contract effectively dictates the terms of the
/// funding round.
contract Campaign is TokenController, Owned {
uint public startFundingTime; // In UNIX Time Format
uint public endFundingTime; // In UNIX Time Format
uint public maximumFunding; // In wei
uint public totalCollected; // In wei
MiniMeToken public tokenContract; // The new token for this Campaign
address public vaultAddress; // The address to hold the funds donated
/// @notice 'Campaign()' initiates the Campaign by setting its funding
/// parameters
/// @dev There are several checks to make sure the parameters are acceptable
/// @param _startFundingTime The UNIX time that the Campaign will be able to
/// start receiving funds
/// @param _endFundingTime The UNIX time that the Campaign will stop being able
/// to receive funds
/// @param _maximumFunding In wei, the Maximum amount that the Campaign can
/// receive (currently the max is set at 10,000 ETH for the beta)
/// @param _vaultAddress The address that will store the donated funds
/// @param _tokenAddress Address of the token contract this contract controls
function Campaign(
uint _startFundingTime,
uint _endFundingTime,
uint _maximumFunding,
address _vaultAddress,
address _tokenAddress
) {
require ((_endFundingTime >= now) && // Cannot end in the past
(_endFundingTime > _startFundingTime) &&
(_maximumFunding <= 10000 ether) && // The Beta is limited
(_vaultAddress != 0)); // To prevent burning ETH
startFundingTime = _startFundingTime;
endFundingTime = _endFundingTime;
maximumFunding = _maximumFunding;
tokenContract = MiniMeToken(_tokenAddress);// The Deployed Token Contract
vaultAddress = _vaultAddress;
}
/// @dev The fallback function is called when ether is sent to the contract, it
/// simply calls `doPayment()` with the address that sent the ether as the
/// `_owner`. Payable is a required solidity modifier for functions to receive
/// ether, without this modifier functions will throw if ether is sent to them
function () payable {
doPayment(msg.sender);
}
/////////////////
// TokenController interface
/////////////////
/// @notice `proxyPayment()` allows the caller to send ether to the Campaign and
/// have the tokens created in an address of their choosing
/// @param _owner The address that will hold the newly created tokens
function proxyPayment(address _owner) payable returns(bool) {
doPayment(_owner);
return true;
}
/// @notice Notifies the controller about a transfer, for this Campaign all
/// transfers are allowed by default and no extra notifications are needed
/// @param _from The origin of the transfer
/// @param _to The destination of the transfer
/// @param _amount The amount of the transfer
/// @return False if the controller does not authorize the transfer
function onTransfer(address _from, address _to, uint _amount) returns(bool) {
return true;
}
/// @notice Notifies the controller about an approval, for this Campaign all
/// approvals are allowed by default and no extra notifications are needed
/// @param _owner The address that calls `approve()`
/// @param _spender The spender in the `approve()` call
/// @param _amount The amount in the `approve()` call
/// @return False if the controller does not authorize the approval
function onApprove(address _owner, address _spender, uint _amount)
returns(bool)
{
return true;
}
/// @dev `doPayment()` is an internal function that sends the ether that this
/// contract receives to the `vault` and creates tokens in the address of the
/// `_owner` assuming the Campaign is still accepting funds
/// @param _owner The address that will hold the newly created tokens
function doPayment(address _owner) internal {
// First check that the Campaign is allowed to receive this donation
require ((now >= startFundingTime) &&
(now <= endFundingTime) &&
(tokenContract.controller() != 0) && // Extra check
(msg.value != 0) &&
(totalCollected + msg.value <= maximumFunding));
//Track how much the Campaign has collected
totalCollected += msg.value;
//Send the ether to the vault
require (vaultAddress.send(msg.value));
// Creates an equal amount of tokens as ether sent. The new tokens are created
// in the `_owner` address
require (tokenContract.generateTokens(_owner, msg.value));
return;
}
/// @notice `finalizeFunding()` ends the Campaign by calling setting the
/// controller to 0, thereby ending the issuance of new tokens and stopping the
/// Campaign from receiving more ether
/// @dev `finalizeFunding()` can only be called after the end of the funding period.
function finalizeFunding() {
require(now >= endFundingTime);
tokenContract.changeController(0);
}
/// @notice `onlyOwner` changes the location that ether is sent
/// @param _newVaultAddress The address that will receive the ether sent to this
/// Campaign
function setVault(address _newVaultAddress) onlyOwner {
vaultAddress = _newVaultAddress;
}
}

View File

@@ -1,7 +0,0 @@
/* This is an autogenerated file. DO NOT EDIT MANUALLY */
exports.TokenControllerAbi = [{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"onTransfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"},{"name":"_amount","type":"uint256"}],"name":"onApprove","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_owner","type":"address"}],"name":"proxyPayment","outputs":[{"name":"","type":"bool"}],"payable":true,"stateMutability":"payable","type":"function"}]
exports.TokenControllerByteCode = "0x"
exports.TokenControllerRuntimeByteCode = "0x"
exports._solcVersion = "0.4.18+commit.9cf6e910.Emscripten.clang"
exports._sha256 = "0x4ab21dd789d6619432629f4e930c7f6cd05cd149e14a8127f99c7c83f2c4377f"

View File

@@ -1,28 +0,0 @@
//File: ./contracts/TokenController.sol
pragma solidity ^0.4.18;
/// @dev The token controller contract must implement these functions
contract TokenController {
/// @notice Called when `_owner` sends ether to the MiniMe Token contract
/// @param _owner The address that sent the ether to create tokens
/// @return True if the ether is accepted, false if it throws
function proxyPayment(address _owner) public payable returns(bool);
/// @notice Notifies the controller about a token transfer allowing the
/// controller to react if desired
/// @param _from The origin of the transfer
/// @param _to The destination of the transfer
/// @param _amount The amount of the transfer
/// @return False if the controller does not authorize the transfer
function onTransfer(address _from, address _to, uint _amount) public returns(bool);
/// @notice Notifies the controller about an approval allowing the
/// controller to react if desired
/// @param _owner The address that calls `approve()`
/// @param _spender The spender in the `approve()` call
/// @param _amount The amount in the `approve()` call
/// @return False if the controller does not authorize the approval
function onApprove(address _owner, address _spender, uint _amount) public
returns(bool);
}

28
codecov.yml Normal file
View File

@@ -0,0 +1,28 @@
codecov:
require_ci_to_pass: false
comment: false
ignore:
- "script"
- "test"
coverage:
status:
project:
default:
# advanced settings
# Prevents PR from being blocked with a reduction in coverage.
# Note, if we want to re-enable this, a `threshold` value can be used
# allow coverage to drop by x% while still posting a success status.
# `informational`: https://docs.codecov.com/docs/commit-status#informational
# `threshold`: https://docs.codecov.com/docs/commit-status#threshold
informational: true
patch:
default:
# advanced settings
# Prevents PR from being blocked with a reduction in coverage.
# Note, if we want to re-enable this, a `threshold` value can be used
# allow coverage to drop by x% while still posting a success status.
# `informational`: https://docs.codecov.com/docs/commit-status#informational
# `threshold`: https://docs.codecov.com/docs/commit-status#threshold
informational: true

49
env.js
View File

@@ -1,49 +0,0 @@
"use strict";
var Web3 = require('web3');
// create an instance of web3 using the HTTP provider.
// NOTE in mist web3 is already available, so check first if its available before instantiating
var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
var BigNumber = require('bignumber.js');
var eth = web3.eth;
var async = require('async');
var MiniMeToken = require('./dist/minimetoken.js');
var gcb = function(err, res) {
if (err) {
console.log("ERROR: "+err);
} else {
console.log(JSON.stringify(res,null,2));
}
}
var minimeToken;
function deployExample(cb) {
cb = cb || gcb;
async.series([
function(cb) {
MiniMeToken.deploy(web3, {
tokenName: "MiniMe Test Token",
decimalUnits: 18,
tokenSymbol: "MMT",
}, function(err, _minimeToken) {
if (err) return err;
minimeToken = _minimeToken;
console.log("Minime Token: " + minimeToken.contract.address);
cb();
});
},
function(cb) {
minimeToken.generateTokens({
owner: eth.accounts[ 1 ],
amount: 10,
from: eth.accounts[ 0 ],
},cb);
},
], cb);
}

55
foundry.toml Normal file
View File

@@ -0,0 +1,55 @@
# Full reference https://github.com/foundry-rs/foundry/tree/master/config
[profile.default]
auto_detect_solc = false
block_timestamp = 1_680_220_800 # March 31, 2023 at 00:00 GMT
bytecode_hash = "none"
cbor_metadata = false
evm_version = "paris"
fuzz = { runs = 1_000 }
gas_reports = ["*"]
libs = ["lib"]
optimizer = true
optimizer_runs = 10_000
out = "out"
script = "script"
solc = "0.8.19"
src = "contracts"
test = "test"
[profile.ci]
fuzz = { runs = 10_000 }
verbosity = 4
[etherscan]
arbitrum_one = { key = "${API_KEY_ARBISCAN}" }
avalanche = { key = "${API_KEY_SNOWTRACE}" }
bnb_smart_chain = { key = "${API_KEY_BSCSCAN}" }
gnosis_chain = { key = "${API_KEY_GNOSISSCAN}" }
goerli = { key = "${API_KEY_ETHERSCAN}" }
mainnet = { key = "${API_KEY_ETHERSCAN}" }
optimism = { key = "${API_KEY_OPTIMISTIC_ETHERSCAN}" }
polygon = { key = "${API_KEY_POLYGONSCAN}" }
sepolia = { key = "${API_KEY_ETHERSCAN}" }
[fmt]
bracket_spacing = true
int_types = "long"
line_length = 120
multiline_func_header = "all"
number_underscore = "thousands"
quote_style = "double"
tab_width = 4
wrap_comments = true
[rpc_endpoints]
arbitrum_one = "https://arbitrum-mainnet.infura.io/v3/${API_KEY_INFURA}"
avalanche = "https://avalanche-mainnet.infura.io/v3/${API_KEY_INFURA}"
bnb_smart_chain = "https://bsc-dataseed.binance.org"
gnosis_chain = "https://rpc.gnosischain.com"
goerli = "https://goerli.infura.io/v3/${API_KEY_INFURA}"
localhost = "http://localhost:8545"
mainnet = "https://eth-mainnet.g.alchemy.com/v2/${API_KEY_ALCHEMY}"
optimism = "https://optimism-mainnet.infura.io/v3/${API_KEY_INFURA}"
polygon = "https://polygon-mainnet.infura.io/v3/${API_KEY_INFURA}"
sepolia = "https://sepolia.infura.io/v3/${API_KEY_INFURA}"

View File

@@ -1,3 +0,0 @@
exports.MiniMeToken = require('./js/minimetoken');
exports.MiniMeTokenFactory = require('./js/minimetokenfactory');
exports.MiniMeTokenState = require('./js/minimetokenstate');

View File

@@ -1,14 +0,0 @@
const ethConnector = require("ethconnector");
const path = require("path");
ethConnector.compile(
path.join(__dirname, "../contracts/MiniMeToken.sol"),
path.join(__dirname, "../contracts/MiniMeToken.sol.js"),
(err) => {
if (err) {
console.log(err);
process.exit(1);
} else {
process.exit(0);
}
});

View File

@@ -1,5 +0,0 @@
const MiniMeTokenAbi = require('../build/MiniMeToken.sol').MiniMeTokenAbi;
const MiniMeTokenByteCode = require('../build/MiniMeToken.sol').MiniMeTokenByteCode;
const generateClass = require('eth-contract-class').default;
module.exports = generateClass(MiniMeTokenAbi, MiniMeTokenByteCode);

View File

@@ -1,5 +0,0 @@
const MiniMeTokenFactoryAbi = require('../build/MiniMeToken.sol').MiniMeTokenFactoryAbi;
const MiniMeTokenFactoryByteCode = require('../build/MiniMeToken.sol').MiniMeTokenFactoryByteCode;
const generateClass = require('eth-contract-class').default;
module.exports = generateClass(MiniMeTokenFactoryAbi, MiniMeTokenFactoryByteCode);

View File

@@ -1,44 +0,0 @@
class MiniMeTokenState {
constructor(minimeToken) {
this.$token = minimeToken;
}
async getState() {
const st = {
balances: {},
};
const res = await Promise.all([
this.$token.name(),
this.$token.decimals(),
this.$token.controller(),
this.$token.totalSupply(),
this.$token.parentToken(),
this.$token.controller(),
this.$token.parentSnapShotBlock(),
this.$token.$web3.eth.getAccounts(),
]);
st.name = res[0];
st.decimals = res[1];
st.controller = res[2];
st.totalSupply = res[3];
st.parentToken = res[4];
st.controller = res[5];
st.parentSnapShotBlock = res[6];
const accounts = res[7];
const calls = accounts.map(account => this.$token.balanceOf(account));
const balances = await Promise.all(calls);
for (let i = 0; i < accounts.length; i += 1) {
st.balances[accounts[i]] = balances[i];
}
return st;
}
}
module.exports = MiniMeTokenState;

1
lib/forge-std vendored Submodule

Submodule lib/forge-std added at 74cfb77e30

View File

@@ -1,51 +1,30 @@
{
"name": "minimetoken",
"version": "0.2.0",
"description": "MiniMe contract",
"main": "dist/minimetoken.js",
"directories": {
"test": "test"
},
"scripts": {
"build": "solcpiler",
"test": "solcpiler; mocha --harmony"
},
"repository": {
"type": "git",
"url": "https://github.com/Giveth/minime.git"
},
"keywords": [
"dao",
"solidity",
"token",
"charity",
"smart",
"contract",
"minime",
"giveth",
"ethereum"
],
"author": "Jordi Baylina",
"license": "GPL-3.0",
"bugs": {
"url": "https://github.com/Giveth/minimi/issues"
},
"homepage": "https://github.com/Giveth/minime",
"dependencies": {
"eth-contract-class": "0.0.6"
"name": "@vacp2p/foundry-template",
"description": "Foundry-based template for developing Solidity smart contracts used by Vac",
"version": "1.0.0",
"author": {
"name": "0x-r4bbit",
"url": "https://github.com/vacp2p"
},
"devDependencies": {
"babel-eslint": "^7.2.3",
"eslint-config-airbnb": "^15.0.1",
"eslint-plugin-import": "^2.6.0",
"eslint-plugin-jsx-a11y": "^6.0.2",
"eslint-plugin-react": "^7.1.0",
"ethereumjs-testrpc": "git://github.com/perissology/testrpc.git#81216dbc",
"lerna": "^2.2.0",
"random-bytes": "^1.0.0",
"mocha": "^3.5.0",
"solcpiler": "0.0.6",
"web3": "git://github.com/perissology/web3.js.git#all_fixes",
"chai": "^4.1.0"
"prettier": "^3.0.0",
"solhint-community": "^3.6.0"
},
"keywords": [
"blockchain",
"ethereum",
"forge",
"foundry",
"smart-contracts",
"solidity",
"template"
],
"private": true,
"scripts": {
"clean": "rm -rf cache out",
"lint": "pnpm lint:sol && pnpm prettier:check",
"lint:sol": "forge fmt --check && pnpm solhint {script,src,test}/**/*.sol",
"prettier:check": "prettier --check **/*.{json,md,yml} --ignore-path=.prettierignore",
"prettier:write": "prettier --write **/*.{json,md,yml} --ignore-path=.prettierignore"
}
}

452
pnpm-lock.yaml generated Normal file
View File

@@ -0,0 +1,452 @@
lockfileVersion: '6.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
devDependencies:
prettier:
specifier: ^3.0.0
version: 3.0.0
solhint-community:
specifier: ^3.6.0
version: 3.6.0
packages:
/@babel/code-frame@7.22.5:
resolution: {integrity: sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/highlight': 7.22.5
dev: true
/@babel/helper-validator-identifier@7.22.5:
resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/highlight@7.22.5:
resolution: {integrity: sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-validator-identifier': 7.22.5
chalk: 2.4.2
js-tokens: 4.0.0
dev: true
/@solidity-parser/parser@0.16.1:
resolution: {integrity: sha512-PdhRFNhbTtu3x8Axm0uYpqOy/lODYQK+MlYSgqIsq2L8SFYEHJPHNUiOTAJbDGzNjjr1/n9AcIayxafR/fWmYw==}
dependencies:
antlr4ts: 0.5.0-alpha.4
dev: true
/ajv@6.12.6:
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
dependencies:
fast-deep-equal: 3.1.3
fast-json-stable-stringify: 2.1.0
json-schema-traverse: 0.4.1
uri-js: 4.4.1
dev: true
/ajv@8.12.0:
resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==}
dependencies:
fast-deep-equal: 3.1.3
json-schema-traverse: 1.0.0
require-from-string: 2.0.2
uri-js: 4.4.1
dev: true
/ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
dev: true
/ansi-styles@3.2.1:
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
engines: {node: '>=4'}
dependencies:
color-convert: 1.9.3
dev: true
/ansi-styles@4.3.0:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
dependencies:
color-convert: 2.0.1
dev: true
/antlr4@4.13.0:
resolution: {integrity: sha512-zooUbt+UscjnWyOrsuY/tVFL4rwrAGwOivpQmvmUDE22hy/lUA467Rc1rcixyRwcRUIXFYBwv7+dClDSHdmmew==}
engines: {node: '>=16'}
dev: true
/antlr4ts@0.5.0-alpha.4:
resolution: {integrity: sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==}
dev: true
/argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
dev: true
/ast-parents@0.0.1:
resolution: {integrity: sha512-XHusKxKz3zoYk1ic8Un640joHbFMhbqneyoZfoKnEGtf2ey9Uh/IdpcQplODdO/kENaMIWsD0nJm4+wX3UNLHA==}
dev: true
/astral-regex@2.0.0:
resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
engines: {node: '>=8'}
dev: true
/balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
dev: true
/brace-expansion@2.0.1:
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
dependencies:
balanced-match: 1.0.2
dev: true
/callsites@3.1.0:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'}
dev: true
/chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'}
dependencies:
ansi-styles: 3.2.1
escape-string-regexp: 1.0.5
supports-color: 5.5.0
dev: true
/chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
dependencies:
ansi-styles: 4.3.0
supports-color: 7.2.0
dev: true
/color-convert@1.9.3:
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
dependencies:
color-name: 1.1.3
dev: true
/color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
dependencies:
color-name: 1.1.4
dev: true
/color-name@1.1.3:
resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
dev: true
/color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
dev: true
/commander@10.0.1:
resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
engines: {node: '>=14'}
dev: true
/cosmiconfig@8.2.0:
resolution: {integrity: sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==}
engines: {node: '>=14'}
dependencies:
import-fresh: 3.3.0
js-yaml: 4.1.0
parse-json: 5.2.0
path-type: 4.0.0
dev: true
/emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
dev: true
/error-ex@1.3.2:
resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
dependencies:
is-arrayish: 0.2.1
dev: true
/escape-string-regexp@1.0.5:
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
engines: {node: '>=0.8.0'}
dev: true
/fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
dev: true
/fast-diff@1.3.0:
resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==}
dev: true
/fast-json-stable-stringify@2.1.0:
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
dev: true
/fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
dev: true
/glob@8.1.0:
resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
engines: {node: '>=12'}
dependencies:
fs.realpath: 1.0.0
inflight: 1.0.6
inherits: 2.0.4
minimatch: 5.1.6
once: 1.4.0
dev: true
/has-flag@3.0.0:
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
engines: {node: '>=4'}
dev: true
/has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
dev: true
/ignore@5.2.4:
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
engines: {node: '>= 4'}
dev: true
/import-fresh@3.3.0:
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
engines: {node: '>=6'}
dependencies:
parent-module: 1.0.1
resolve-from: 4.0.0
dev: true
/inflight@1.0.6:
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
dependencies:
once: 1.4.0
wrappy: 1.0.2
dev: true
/inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
dev: true
/is-arrayish@0.2.1:
resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
dev: true
/is-fullwidth-code-point@3.0.0:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
dev: true
/js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
dev: true
/js-yaml@4.1.0:
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
hasBin: true
dependencies:
argparse: 2.0.1
dev: true
/json-parse-even-better-errors@2.3.1:
resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
dev: true
/json-schema-traverse@0.4.1:
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
dev: true
/json-schema-traverse@1.0.0:
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
dev: true
/lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
dev: true
/lodash.truncate@4.4.2:
resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==}
dev: true
/lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
dev: true
/minimatch@5.1.6:
resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
engines: {node: '>=10'}
dependencies:
brace-expansion: 2.0.1
dev: true
/once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
dependencies:
wrappy: 1.0.2
dev: true
/parent-module@1.0.1:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'}
dependencies:
callsites: 3.1.0
dev: true
/parse-json@5.2.0:
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
engines: {node: '>=8'}
dependencies:
'@babel/code-frame': 7.22.5
error-ex: 1.3.2
json-parse-even-better-errors: 2.3.1
lines-and-columns: 1.2.4
dev: true
/path-type@4.0.0:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
engines: {node: '>=8'}
dev: true
/pluralize@8.0.0:
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
engines: {node: '>=4'}
dev: true
/prettier@2.8.8:
resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==}
engines: {node: '>=10.13.0'}
hasBin: true
requiresBuild: true
dev: true
optional: true
/prettier@3.0.0:
resolution: {integrity: sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==}
engines: {node: '>=14'}
hasBin: true
dev: true
/punycode@2.3.0:
resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
engines: {node: '>=6'}
dev: true
/require-from-string@2.0.2:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
dev: true
/resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
dev: true
/semver@6.3.1:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
dev: true
/slice-ansi@4.0.0:
resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==}
engines: {node: '>=10'}
dependencies:
ansi-styles: 4.3.0
astral-regex: 2.0.0
is-fullwidth-code-point: 3.0.0
dev: true
/solhint-community@3.6.0:
resolution: {integrity: sha512-3WGi8nB9VSdC7B3xawktFoQkJEgX6rsUe7jWZJteDBdix+tAOGN+2ZhRr7Cyv1s+h5BRLSsNOXoh7Vg9KEeQbg==}
hasBin: true
dependencies:
'@solidity-parser/parser': 0.16.1
ajv: 6.12.6
antlr4: 4.13.0
ast-parents: 0.0.1
chalk: 4.1.2
commander: 10.0.1
cosmiconfig: 8.2.0
fast-diff: 1.3.0
glob: 8.1.0
ignore: 5.2.4
js-yaml: 4.1.0
lodash: 4.17.21
pluralize: 8.0.0
semver: 6.3.1
strip-ansi: 6.0.1
table: 6.8.1
text-table: 0.2.0
optionalDependencies:
prettier: 2.8.8
dev: true
/string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
dependencies:
emoji-regex: 8.0.0
is-fullwidth-code-point: 3.0.0
strip-ansi: 6.0.1
dev: true
/strip-ansi@6.0.1:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'}
dependencies:
ansi-regex: 5.0.1
dev: true
/supports-color@5.5.0:
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
engines: {node: '>=4'}
dependencies:
has-flag: 3.0.0
dev: true
/supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
dependencies:
has-flag: 4.0.0
dev: true
/table@6.8.1:
resolution: {integrity: sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==}
engines: {node: '>=10.0.0'}
dependencies:
ajv: 8.12.0
lodash.truncate: 4.4.2
slice-ansi: 4.0.0
string-width: 4.2.3
strip-ansi: 6.0.1
dev: true
/text-table@0.2.0:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
dev: true
/uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
dependencies:
punycode: 2.3.0
dev: true
/wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
dev: true

1
remappings.txt Normal file
View File

@@ -0,0 +1 @@
forge-std/=lib/forge-std/src/

41
script/Base.s.sol Normal file
View File

@@ -0,0 +1,41 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19 <=0.9.0;
import { Script } from "forge-std/Script.sol";
abstract contract BaseScript is Script {
/// @dev Included to enable compilation of the script without a $MNEMONIC environment variable.
string internal constant TEST_MNEMONIC = "test test test test test test test test test test test junk";
/// @dev Needed for the deterministic deployments.
bytes32 internal constant ZERO_SALT = bytes32(0);
/// @dev The address of the transaction broadcaster.
address internal broadcaster;
/// @dev Used to derive the broadcaster's address if $ETH_FROM is not defined.
string internal mnemonic;
/// @dev Initializes the transaction broadcaster like this:
///
/// - If $ETH_FROM is defined, use it.
/// - Otherwise, derive the broadcaster address from $MNEMONIC.
/// - If $MNEMONIC is not defined, default to a test mnemonic.
///
/// The use case for $ETH_FROM is to specify the broadcaster key and its address via the command line.
constructor() {
address from = vm.envOr({ name: "ETH_FROM", defaultValue: address(0) });
if (from != address(0)) {
broadcaster = from;
} else {
mnemonic = vm.envOr({ name: "MNEMONIC", defaultValue: TEST_MNEMONIC });
(broadcaster,) = deriveRememberKey({ mnemonic: mnemonic, index: 0 });
}
}
modifier broadcast() {
vm.startBroadcast(broadcaster);
_;
vm.stopBroadcast();
}
}

11
script/Deploy.s.sol Normal file
View File

@@ -0,0 +1,11 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.19 <=0.9.0;
import { BaseScript } from "./Base.s.sol";
import { DeploymentConfig } from "./DeploymentConfig.s.sol";
contract Deploy is BaseScript {
function run() public returns (DeploymentConfig deploymentConfig) {
deploymentConfig = new DeploymentConfig(broadcaster);
}
}

View File

@@ -0,0 +1,39 @@
//// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.19 <=0.9.0;
import { Script } from "forge-std/Script.sol";
contract DeploymentConfig is Script {
error DeploymentConfig_InvalidDeployerAddress();
error DeploymentConfig_NoConfigForChain(uint256);
struct NetworkConfig {
address deployer;
}
NetworkConfig public activeNetworkConfig;
address private deployer;
constructor(address _broadcaster) {
if (block.chainid == 31_337) {
activeNetworkConfig = getOrCreateAnvilEthConfig();
} else {
revert DeploymentConfig_NoConfigForChain(block.chainid);
}
if (_broadcaster == address(0)) revert DeploymentConfig_InvalidDeployerAddress();
deployer = _broadcaster;
}
function getOrCreateAnvilEthConfig() public view returns (NetworkConfig memory) {
return NetworkConfig({ deployer: deployer });
}
// This function is a hack to have it excluded by `forge coverage` until
// https://github.com/foundry-rs/foundry/issues/2988 is fixed.
// See: https://github.com/foundry-rs/foundry/issues/2988#issuecomment-1437784542
// for more info.
// solhint-disable-next-line
function test() public { }
}

8
slither.config.json Normal file
View File

@@ -0,0 +1,8 @@
{
"detectors_to_exclude": "naming-convention,reentrancy-events,solc-version,timestamp",
"filter_paths": "(lib|test)",
"solc_remaps": [
"@openzeppelin/contracts=lib/openzeppelin-contracts/contracts/",
"forge-std/=lib/forge-std/src/"
]
}