-
Notifications
You must be signed in to change notification settings - Fork 11.8k
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
rbac mintable erc721 token with signature bouncer #950
Changes from all commits
e772f35
580905d
1ba6c31
bd44dc8
0d09184
2337a89
926fa3f
8365b90
2155044
f6e680a
d2549fc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
pragma solidity ^0.4.23; | ||
|
||
|
||
contract AutoIncrementing { | ||
uint256 internal nextId_ = 0; | ||
|
||
function nextId() internal returns (uint256) { | ||
uint256 thisId = nextId_; | ||
nextId_ = nextId_ + 1; | ||
return thisId; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
pragma solidity ^0.4.23; | ||
|
||
import "../token/ERC721/MintableERC721Token.sol"; | ||
import "./SignatureBouncer.sol"; | ||
import "../AutoIncrementing.sol"; | ||
|
||
|
||
/** | ||
* @title AutoIncrementingERC721Minter | ||
* @author Matt Condon (@shrugs) | ||
* @dev An ERc721Minter that generates auto-incrementing `tokenId`s. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: lowercase |
||
*/ | ||
contract AutoIncrementingERC721Minter is AutoIncrementing, SignatureBouncer { | ||
MintableERC721Token public token; | ||
|
||
constructor(MintableERC721Token _token) | ||
public | ||
{ | ||
token = _token; | ||
} | ||
|
||
function mint(bytes _sig, string _tokenURI) | ||
public | ||
returns (uint256) | ||
{ | ||
require( | ||
isValidMintSignature( | ||
msg.sender, | ||
_sig, | ||
_tokenURI | ||
) | ||
); | ||
|
||
uint256 _tokenId = nextId(); | ||
token.mint(msg.sender, _tokenId, _tokenURI); | ||
return _tokenId; | ||
} | ||
|
||
function isValidMintSignature( | ||
address _address, | ||
bytes _sig, | ||
string _tokenURI | ||
) | ||
internal | ||
view | ||
returns (bool) | ||
{ | ||
return isValidDataHash( | ||
keccak256( | ||
address(this), | ||
_address, | ||
_tokenURI | ||
), | ||
_sig | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
pragma solidity ^0.4.23; | ||
|
||
import "../token/ERC721/MintableERC721Token.sol"; | ||
import "./SignatureBouncer.sol"; | ||
|
||
|
||
/** | ||
* @title ERC721Minter | ||
* @author Matt Condon (@shrugs) | ||
* @dev A SignatureBouncer that allows users to mint themselves a MintableERC721Token | ||
* @dev iff they have a valid signature from a bouncer. | ||
* @dev 1. Deploy a MintableERC721Token and ERC721Minter. | ||
* @dev 2. Make ERC721Minter a minter of the token using `addMinter`. | ||
* @dev 3. Make your server a bouncer of ERC721Minter using `addBouncer`. | ||
* @dev 4. Generate a valid bouncer signature (address(this) + msg.sender + tokenId + tokenURI) | ||
* @dev and submit it to the ERC721Minter using `mint` | ||
*/ | ||
contract ERC721Minter is SignatureBouncer { | ||
MintableERC721Token public token; | ||
|
||
constructor(MintableERC721Token _token) | ||
public | ||
{ | ||
token = _token; | ||
} | ||
|
||
function mint(bytes _sig, uint256 _tokenId, string _tokenURI) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The What do you think about making it always the last argument? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
public | ||
returns (uint256) | ||
{ | ||
require( | ||
isValidMintSignature( | ||
msg.sender, | ||
_sig, | ||
_tokenId, | ||
_tokenURI | ||
) | ||
); | ||
|
||
token.mint(msg.sender, _tokenId, _tokenURI); | ||
return _tokenId; | ||
} | ||
|
||
function isValidMintSignature( | ||
address _address, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd prefer a more relevant name here. I can only think of |
||
bytes _sig, | ||
uint256 _tokenId, | ||
string _tokenURI | ||
) | ||
internal | ||
view | ||
returns (bool) | ||
{ | ||
return isValidDataHash( | ||
keccak256( | ||
address(this), | ||
_address, | ||
_tokenId, | ||
_tokenURI | ||
), | ||
_sig | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
pragma solidity ^0.4.23; | ||
|
||
|
||
/** | ||
* @title NonceTracker | ||
* @author Matt Condon (@shrugs) | ||
* @dev A simple way to keep track of nonces and restrict access. | ||
* @dev Use the `withAccess` modifier to restrict access by address. | ||
* @dev Use the `withMaxAccess` modifier to restrict access by address up to a max amount | ||
* @dev For example, withAccess(msg.sender, 1) will only allow once-per-address. | ||
* @dev You can also accept nonces from users (as part of a hash you verify). | ||
*/ | ||
contract NonceTracker { | ||
mapping(address => uint256) private nonces; | ||
|
||
modifier withAccess(address _address, uint256 _nonce) | ||
{ | ||
access(_address, _nonce); | ||
_; | ||
} | ||
|
||
/** | ||
* @dev use withMaxAccess to restrict access per-address for a maxiumum | ||
* @dev number of times | ||
*/ | ||
modifier withMaxAccess(address _address, uint256 _maxNonce) | ||
{ | ||
// require that we haven't accessed this resource too much | ||
require(nonce(_address) < _maxNonce); | ||
// access it once more by incrementing the nonce | ||
access(_address, nonce(_address) + 1); | ||
// allow access | ||
_; | ||
} | ||
|
||
function nonce(address _address) | ||
public | ||
view | ||
returns (uint256) | ||
{ | ||
return nonces[_address]; | ||
} | ||
|
||
/** | ||
* @dev call this function when accepting a nonce | ||
* @dev throws if nonce is not strictly greater than previous nonce | ||
*/ | ||
function access(address _address, uint256 _nonce) | ||
internal | ||
{ | ||
require(_nonce > nonces[_address]); | ||
nonces[_address] = _nonce; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
pragma solidity ^0.4.23; | ||
|
||
import "../token/ERC721/MintableERC721Token.sol"; | ||
import "../token/ERC721/DefaultTokenURI.sol"; | ||
|
||
|
||
contract DefaultTokenURIMock is DefaultTokenURI, MintableERC721Token { | ||
|
||
constructor(string _name, string _symbol, string _tokenURI) | ||
MintableERC721Token(_name, _symbol) | ||
DefaultTokenURI(_tokenURI) | ||
public | ||
{ | ||
|
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
pragma solidity ^0.4.23; | ||
|
||
import "../access/NonceTracker.sol"; | ||
|
||
|
||
contract NonceTrackerImpl is NonceTracker { | ||
|
||
modifier onlyValidInputs(uint256 _nonce) | ||
{ | ||
require(true); | ||
// ^ you'd implement this using something like SignatureBouncer | ||
_; | ||
} | ||
|
||
function canDoThisOnce() | ||
withMaxAccess(msg.sender, 1) | ||
public | ||
{ | ||
|
||
} | ||
|
||
function canDoThisTwice() | ||
withMaxAccess(msg.sender, 2) | ||
public | ||
{ | ||
} | ||
|
||
function cantDoThisAtAll() | ||
withMaxAccess(msg.sender, 0) | ||
public | ||
{ | ||
require(nonce(msg.sender) <= 0); | ||
} | ||
|
||
function withAcceptedNonce(uint256 _nonce) | ||
onlyValidInputs(_nonce) | ||
withAccess(msg.sender, _nonce) | ||
public | ||
{ | ||
|
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
pragma solidity ^0.4.23; | ||
|
||
import "./RBAC.sol"; | ||
|
||
|
||
/** | ||
* @title RBACMintable | ||
* @author Matt Condon (@shrugs) | ||
* @dev Mintable logic using RBAC. | ||
* @dev You must add any logic for addMinter/removeMinter yourself because | ||
* @dev security concerns will vary. | ||
*/ | ||
contract RBACMintable is RBAC { | ||
string public constant ROLE_MINTER = "minter"; | ||
|
||
modifier onlyMinter() { | ||
checkRole(msg.sender, ROLE_MINTER); | ||
_; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
pragma solidity ^0.4.23; | ||
|
||
import "./RBAC.sol"; | ||
|
||
|
||
/** | ||
* @title RBACOwnable | ||
* @author Matt Condon (@shrugs) | ||
* @dev Ownable logic using RBAC. | ||
* @dev Use RBACOwnable if you could have many different owners and you're ok with | ||
* @dev the security profile of any owner being able to add another owner. | ||
* @dev Only difference from Ownable.sol is that the owners are not stored as public variables. | ||
*/ | ||
contract RBACOwnable is RBAC { | ||
string public constant ROLE_OWNER = "owner"; | ||
|
||
constructor() | ||
public | ||
{ | ||
addRole(msg.sender, ROLE_OWNER); | ||
} | ||
|
||
modifier onlyOwner() { | ||
checkRole(msg.sender, ROLE_OWNER); | ||
_; | ||
} | ||
|
||
function addOwner(address _owner) | ||
onlyOwner | ||
public | ||
{ | ||
addRole(_owner, ROLE_OWNER); | ||
} | ||
|
||
function removeOwner(address _owner) | ||
onlyOwner | ||
public | ||
{ | ||
removeRole(_owner, ROLE_OWNER); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
pragma solidity ^0.4.23; | ||
|
||
import "./ERC721Token.sol"; | ||
|
||
|
||
contract DefaultTokenURI is ERC721Token { | ||
string private tokenURI_ = ""; | ||
|
||
constructor(string _tokenURI) | ||
public | ||
{ | ||
require(bytes(_tokenURI).length > 0); | ||
tokenURI_ = _tokenURI; | ||
} | ||
|
||
/** | ||
* @dev Returns a default URI for every tokenId unless a specific URI is set | ||
* @param _tokenId uint256 ID of the token to query | ||
*/ | ||
function tokenURI(uint256 _tokenId) | ||
public | ||
view | ||
returns (string) | ||
{ | ||
if (bytes(tokenURIs[_tokenId]).length != 0) { | ||
return super.tokenURI(_tokenId); | ||
} | ||
|
||
return tokenURI_; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should make this
private
so that derived contracts cannot access/modify directly.