Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Contract Updates #4

Merged
merged 7 commits into from
Jan 11, 2021
Merged
Show file tree
Hide file tree
Changes from 4 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
36 changes: 36 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
module.exports = {
// https://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy
// This option interrupts the configuration hierarchy at this file
// Remove this if you have an higher level ESLint config file (it usually happens into a monorepos)
root: true,

parserOptions: {
extraFileExtensions: [''],
tsconfigRootDir: __dirname,
ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
sourceType: 'module', // Allows for the use of imports
},

env: {
node: true,
},

// Rules order is important
extends: [
// https://github.com/prettier/eslint-config-prettier#installation
// usage with Prettier, provided by 'eslint-config-prettier'.
'prettier',
],

plugins: [],

globals: {
process: true,
},

// add your custom rules here
rules: {
// allow debugger during development only
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
},
};
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ node_modules
# slither-flat puts flattended *.sol files here
crytic-export

#Buidler files
# Hardhat files
cache
artifacts
6 changes: 6 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/artifacts
/cache
/build
/node_modules
/coverage
*.hbs
4 changes: 2 additions & 2 deletions .solhint.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"extends": "solhint:recommended",
"rules": {
"compiler-version": ["warn", "0.6.7"],
"reason-string": ["warn", {"maxLength": 255}]
"compiler-version": ["warn", "^0.6.12"],
"reason-string": ["warn", { "maxLength": 255 }]
}
}
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Smart contracts for Gitcoin personal token functionality.

## Deployed Contracts

The current personal token factory contracts are deployed on Mainnet and Rinkeby at [0x75589C2e56095c80f63EC773509f033aC595c34e](https://etherscan.io/address/0x75589C2e56095c80f63EC773509f033aC595c34e)
The current personal token factory contracts are deployed on Rinkeby at [0x4FB8Ad81754a5330165277a2655d47e99A7F598E](https://rinkeby.etherscan.io/address/0x4FB8Ad81754a5330165277a2655d47e99A7F598E)

## Development

Expand All @@ -22,7 +22,7 @@ To deploy a new factory contract:
MNEMONIC_MAINNET="your mnemonic for production"
ETHERSCAN_API_KEY=yourEtherscanApiKey
```
2. Open `buidler.config.js` and configure the object for the network you want to deploy to
1. If you want to change the gas price, derivation path, or other paramters, see the [Buidler docs](https://buidler.dev/config/#networks-configuration) for details
2. Open `hardhat.config.js` and configure the object for the network you want to deploy to
1. If you want to change the gas price, derivation path, or other paramters, see the [Hardhat docs](https://hardhat.org/config/#networks-configuration) for details
3. Run `yarn run deploy-dev` or `yarn run deploy-prod` to compile and deploy the contracts to the desired network
4. Once deployment is complete, be sure to update this README with the new deployment address, and verify the contract on Etherscan
50 changes: 0 additions & 50 deletions buidler.config.js

This file was deleted.

118 changes: 93 additions & 25 deletions contracts/PToken.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.6.7;
pragma solidity ^0.6.12;

import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/SafeERC20.sol";
Expand All @@ -9,71 +9,139 @@ contract PToken is ERC20UpgradeSafe, OwnableUpgradeSafe {
using SafeMath for uint256;
using SafeERC20 for IERC20;

IERC20 public acceptedToken; // ERC20 that is used for purchasing pToken
uint256 public price; // The amount of aToken to purchase pToken
/// @notice ERC20 that is used for purchasing pTokens
IERC20 public acceptedToken;

event Initialized(address owner, uint256 price, uint256 supply);
/// @notice Price per pToken, denominated in units of `acceptedToken`
uint256 public price;

event Purchased(address buyer, uint256 cost, uint256 amountReceived);
/// @dev Emitted when pToken is initialized
event Initialized(uint256 price, uint256 supply);

event Redeemed(address seller, uint256 amountRedeemed);
/// @dev Emitted when pTokens are purchased
event Purchased(address buyer, uint256 cost, uint256 amount);

event PriceUpdated(address owner, uint256 newPrice);
/// @dev Emitted when pTokens are redeemed
event Redeemed(address seller, uint256 amount);

event Minted(address owner, uint256 amountMinted);
/// @dev Emitted when pToken price is changed
event PriceUpdated(address owner, uint256 newPrice);

event Burned(address owner, uint256 amountBurned);
/**
* @dev Modifier that implements transfer restrictions
*/
modifier restricted(address _from, address _to) {
// This contract can send tokens to anyone. If the sender is anyone except this contract, the
// only allowed recipient is this contract
if (_from != address(this)) {
require(_to == address(this), "PToken: Invalid recipient");
mds1 marked this conversation as resolved.
Show resolved Hide resolved
}
_;
}

/**
* @notice Replaces contructor since pTokens are deployed as minimal proxies
* @param _name Token name
* @param _symbol Token symbol
* @param _price Price per token, denominated in _acceptedERC20 units
* @param _initialSupply Initial token supply
* @param _acceptedERC20 Address of token used to purchase these tokens
*/
function initializePtoken(
string memory _name,
string memory _symbol,
uint256 _price,
uint256 _initialSupply,
address _acceptedERC20
) public initializer {
) external initializer {
// Initializers
__Ownable_init();
__ERC20_init(_name, _symbol);

// Set contract state
acceptedToken = IERC20(_acceptedERC20);
price = _price;
_mint(address(this), _initialSupply);

emit Initialized(msg.sender, _price, _initialSupply);
// Mint initial supply to owner
_mint(address(this), _initialSupply);
emit Initialized(_price, _initialSupply);
}

function purchase(uint256 _amount) public {
/**
* @notice Purchase pTokens from owner
* @param _amount Amount of pTokens to purchase
*/
function purchase(uint256 _amount) external {
uint256 _allowance = acceptedToken.allowance(msg.sender, address(this));
uint256 _cost = price.mul(_amount).div(10**18);
require(_allowance >= _cost, "PToken: Not enough token allowance");

acceptedToken.safeTransferFrom(msg.sender, owner(), _cost);
require(this.transfer(msg.sender, _amount), "PToken: Transfer during purchase failed");

emit Purchased(msg.sender, _cost, _amount);
}

function redeem(uint256 _amount) public {
/**
* @notice Redeem pTokens
* @param _amount Amount of pTokens to redeem
*/
function redeem(uint256 _amount) external {
require(transfer(address(this), _amount), "PToken: Transfer during redemption failed");

emit Redeemed(msg.sender, _amount);
}

function updatePrice(uint256 _newPrice) public onlyOwner {
/**
* @notice Update purchase price of pTokens
* @param _newPrice New pToken price, denominated in _acceptedERC20 units
*/
function updatePrice(uint256 _newPrice) external onlyOwner {
price = _newPrice;

emit PriceUpdated(msg.sender, _newPrice);
}

// Allow only the owner to mint to this pool and not to other accounts
function mint(uint256 _amount) public onlyOwner {
/**
* @notice Allows owner to mint more pTokens to this contract
* @param _amount Amount of pTokens to mint
*/
function mint(uint256 _amount) external onlyOwner {
_mint(address(this), _amount);

emit Minted(msg.sender, _amount);
}

// Allow only the owner to burn from this pool and not other accounts
function burn(uint256 _amount) public onlyOwner {
/**
* @notice Allows owner to burn pTokens from this contract
* @param _amount Amount of pTokens to burn
*/
function burn(uint256 _amount) external onlyOwner {
_burn(address(this), _amount);
}

/**
* @notice Moves `_amount` tokens from the caller's account to the `_to` address
* @param _to Address to send pTokens to
* @param _amount Amount of pTokens to send
*/
function transfer(address _to, uint256 _amount)
public
override
restricted(msg.sender, _to)
returns (bool)
{
return super.transfer(_to, _amount);
}

emit Burned(msg.sender, _amount);
/**
* @notice Moves `_amount` tokens from `_from` to `_to`, where the `_amount` is then
* deducted from the caller's allowance.
* @dev Only allows transfer of pTokens between this contract and purchaser
* @param _from Address to send pTokens from
* @param _to Address to send pTokens to
* @param _amount Amount of pTokens to send
*/
function transferFrom(
address _from,
address _to,
uint256 _amount
) public override restricted(_from, _to) returns (bool) {
return super.transferFrom(_from, _to, _amount);
}
}
31 changes: 20 additions & 11 deletions contracts/PTokenFactory.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.6.7;
pragma solidity ^0.6.12;

import "./PToken.sol";
import "./ProxyFactory.sol";
Expand All @@ -21,24 +21,33 @@ contract PTokenFactory is ProxyFactory {
emit PTokenLogicDeployed(address(ptokenLogicContract));
}

/**
* @notice Creats a pToken for the calleer
* @param _name Token name
* @param _symbol Token symbol
* @param _price Price per token, denominated in _acceptedERC20 units
* @param _initialSupply Initial token supply
* @param _acceptedERC20 Address of token used to purchase these tokens
*/
function createPToken(
string memory _name,
string memory _symbol,
uint256 _cost,
uint256 _supply,
uint256 _price,
uint256 _initialSupply,
address _acceptedERC20
) public {
require(address(userPTokens[msg.sender]) == address(0), "PToken: User token already exists");

// Deploy PToken contract and call initialization function
bytes memory payload = abi.encodeWithSignature(
"initializePtoken(string,string,uint256,uint256,address)",
_name,
_symbol,
_cost,
_supply,
_acceptedERC20
);
bytes memory payload =
abi.encodeWithSignature(
"initializePtoken(string,string,uint256,uint256,address)",
_name,
_symbol,
_price,
_initialSupply,
_acceptedERC20
);
address newPtokenContract = deployMinimal(ptokenLogic, payload);

// Update state
Expand Down
2 changes: 1 addition & 1 deletion contracts/ProxyFactory.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity ^0.6.7;
pragma solidity ^0.6.12;

contract ProxyFactory {
/**
Expand Down
Loading