-
Notifications
You must be signed in to change notification settings - Fork 1
Zodiac test2 #4
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
base: master
Are you sure you want to change the base?
Zodiac test2 #4
Changes from all commits
9084e18
ad5e9d2
e236359
9b1bda0
1612d21
19127eb
ea81bdf
f1450d1
88297c0
11a429b
2e2d8aa
ec66841
dbdab93
206e10f
10423cd
6396911
92da525
1682efb
d654aff
123f84d
8849dc3
fce9a25
a891e9a
21d3c00
649b1c1
ad4abd1
af999d6
4cd44f4
c3ed623
ea04fa0
0edf11d
1b4b2ee
2462295
64ab0f8
a8733fb
f3e3c65
fe068d1
da08997
4c86452
70af4eb
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 |
|---|---|---|
| @@ -1,9 +1,28 @@ | ||
| node_modules | ||
| .vscode | ||
| .env | ||
| coverage | ||
| coverage.json | ||
| typechain | ||
| .DS_Store | ||
| node_modules | ||
| venv | ||
| getPrivateKeys.js | ||
|
|
||
| # Hardhat files | ||
| artifacts-hardhat | ||
| cache-hardhat | ||
|
|
||
| #Hardhat files | ||
| # Forge files | ||
| artifacts-foundry | ||
| cache-foundry | ||
|
|
||
| # Slither cache | ||
| cache | ||
| artifacts | ||
|
|
||
| # autogenerated types | ||
| typechain | ||
| dist | ||
|
|
||
| # Coverage | ||
| coverage | ||
| coverage.json | ||
| lcov.info | ||
| report |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| { | ||
| "require": "ts-node/register/files", | ||
| "timeout": 20000 | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| v18 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| module.exports = { | ||
| skipFiles: [ | ||
| 'util/ABDKMath', // ABDKMath has issues compiling in solcover | ||
| 'fakes', // Ignore fakes | ||
| ] | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| { | ||
| "extends": "solhint:recommended", | ||
| "rules": { | ||
| "func-visibility": ["warn",{"ignoreConstructors":true}], | ||
| "compiler-version": ["error","^0.8.17"], | ||
| "reason-string": ["warn",{"maxLength":32}] | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,240 @@ | ||
| pragma solidity 0.8.18; | ||
| // SPDX-License-Identifier: AGPL-3.0-or-later | ||
|
|
||
| import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; | ||
| import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; | ||
| import {Counters} from "@openzeppelin/contracts/utils/Counters.sol"; | ||
|
|
||
| /** | ||
| * @notice interfaced from Relic.sol to obtain | ||
| * information from Relic.sol regarding enclave type. | ||
| */ | ||
| interface IRelic { | ||
| function getRelicInfos(uint256 enclaves) external returns (uint256); | ||
| } | ||
|
|
||
| /** | ||
| Intended contract specs: | ||
| * 1) Interface with Relic and Shards | ||
| * 2) Implement EIP 712 to concatenate signed message, expected deadline and expected nonce into a hash | ||
| * 3) Recover the address of the account calling the function using the digest and signature through this hash | ||
| * 4) Map Shards to each enclave | ||
| * 5) Allow a verified signer to mint the chosen Enclave Shard | ||
|
|
||
| */ | ||
| /** | ||
| * @notice interfaced from Shards.sol to obtain address, token Id, amount owned and stored data | ||
| * for future use | ||
| */ | ||
| interface IShards { | ||
| function partnerMint( | ||
| address account, | ||
| uint256 id, | ||
| uint256 amount, | ||
| bytes memory data | ||
| ) external; | ||
| } | ||
|
|
||
| /** | ||
| * @title This contract aims to allow a user to mint | ||
| * an Enclave Shard corresponding to the Enclave quest | ||
| * upon reaching the winning state of Path of the Temple. | ||
| * It uses EIP712 to verify that a user has signed the hashed message | ||
| */ | ||
| contract PathOfTheTemplarShard is Ownable { | ||
| IShards private SHARDS; | ||
| IRelic private RELIC; | ||
| //Mapping SHARD_ID to the individual enclaves | ||
| uint256[] public SHARD_ID = [2, 3, 4, 5, 6]; | ||
| string[] public ENCLAVE = [ | ||
| "", | ||
|
Collaborator
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. what's the empty string for? |
||
| "chaosEnclave", | ||
| "mysteryEnclave", | ||
| "logicEnclave", | ||
| "structureEnclave", | ||
| "orderEnclave" | ||
| ]; | ||
|
|
||
| using Counters for Counters.Counter; | ||
|
|
||
| // Defining the CONSTANTS which are of bytes32 type | ||
| bytes32 constant MINTREQUEST_TYPEHASH = | ||
| keccak256("MintRequest(address signer,uint256 deadline,uint256 nonce)"); | ||
| bytes32 constant EIP712DOMAIN_TYPEHASH = | ||
|
Collaborator
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. add
Collaborator
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. as seen here |
||
| keccak256("EIP712Domain(string name,string version,uint256 chainId)"); | ||
| bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); | ||
| bytes32 public immutable DOMAIN_SEPARATOR; | ||
|
|
||
| // MintRequest struct has variables of account, deadline and nonce types. | ||
| struct MintRequest { | ||
| address account; | ||
| uint256 deadline; | ||
| uint256 nonce; | ||
| } | ||
|
|
||
| // Defining EIP712 Domain struct with variables of name, version and chain id. | ||
| struct EIP712Domain { | ||
| string name; | ||
| string version; | ||
| uint256 chainId; | ||
| } | ||
|
|
||
| //error definition for when msg.sender is not signer | ||
| error MintForbidden(); | ||
| error InvalidMint(address account); | ||
|
|
||
| // error definitions for passing checks related to the hashed message and signature | ||
| error DeadlineExpired(uint256 lateBy); | ||
| error InvalidNonce(address account); | ||
| error InvalidSignature(address account); | ||
|
|
||
| // MinterSet event takes the minter and value as parameters once an address calling the setMinter function | ||
| // passes the check to be updated with the minter role | ||
| event MinterSet(address indexed minter, bool value); | ||
|
|
||
| // modifier applied so that if the account calling the function is not the minter, the tx will | ||
| // revert with the Mint Forbidden message. | ||
| modifier canMint() { | ||
| if (!minters[msg.sender]) { | ||
| revert MintForbidden(); | ||
| } | ||
|
|
||
| _; | ||
| } | ||
| //mapping role for minting to the address calling mint function if check is passed | ||
| mapping(address => bool) public minters; | ||
| //mapping the Shard ID from its declared array to the Enclave names | ||
| mapping(uint256 => string) public shardToEnclave; | ||
| // mapping address to nonces for incremental counter | ||
| mapping(address => Counters.Counter) public nonces; | ||
|
|
||
| // Constructor occurs just once during deployment of contract | ||
| // original deployer is granted the default admin role | ||
| // Shards and domain separator constant is initialised | ||
| // using name, version and Arbitrum Goerli chainID. | ||
| constructor() { | ||
|
Collaborator
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. you have not set |
||
| if (msg.sender != owner()) { | ||
|
Collaborator
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 don't think this check is necessary |
||
| revert("Only contract owner can deploy"); | ||
| } | ||
|
|
||
| DOMAIN_SEPARATOR = hash( | ||
| EIP712Domain({ | ||
| name: "PathOfTheTemplarShard", | ||
| version: "1", | ||
| chainId: 421613 | ||
| }) | ||
| ); | ||
| } | ||
|
|
||
| // setMintRequest grants the address calling this function the ability to mint if the check | ||
| // using EIP712 standard below are passed (with signature verification, deadline and nonce) | ||
| function mintShard( | ||
| MintRequest calldata request, | ||
| bytes calldata signature, | ||
| uint256 _shardIndex | ||
| ) external { | ||
| _relayMintRequestFor(request, signature); | ||
| if (_shardIndex < SHARD_ID.length || _shardIndex > SHARD_ID.length) { | ||
|
Collaborator
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. if you choose to maintain |
||
| revert InvalidMint(msg.sender); | ||
| } | ||
| SHARDS.partnerMint(msg.sender, SHARD_ID[_shardIndex], 1, ""); | ||
|
Collaborator
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. you have not set |
||
| } | ||
|
|
||
| //for loop checks if the Enclave name matches the Shard ID | ||
| function establishMapping() public { | ||
| // Establish the mapping between SHARD_ID and ENCLAVE | ||
|
Collaborator
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. gas: |
||
| for (uint256 i = 2; i < SHARD_ID.length; i++) { | ||
|
Collaborator
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. this You could also do this in the constructor as it's a one-time thing |
||
| shardToEnclave[SHARD_ID[i]] = ENCLAVE[i]; | ||
| } | ||
| } | ||
|
|
||
| // Shard Id corresponding to Enclave can be viewed publically by anyone calling this function | ||
| function getEnclaveForShard( | ||
| uint256 shardId | ||
| ) public view returns (string memory) { | ||
| return shardToEnclave[shardId]; | ||
| } | ||
|
|
||
| // Only contract owner may call the minter role for an account that has passed the check. | ||
| // the minter role corresponding to the account that passed is stored in value. | ||
| function setMinter(address account, bool value) external onlyOwner { | ||
| minters[account] = value; | ||
| //emit the new role for the account | ||
| emit MinterSet(account, value); | ||
| } | ||
|
|
||
| // Function takes two parameters request and signature | ||
| function _relayMintRequestFor( | ||
| MintRequest calldata request, | ||
| bytes calldata signature | ||
| ) internal { | ||
| //concatenates the three values into a hash readable as a digest via a keccak256 hash function | ||
| bytes32 digest = keccak256( | ||
| abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, hash(request)) | ||
| ); | ||
| // stores address into signer and error recovery in err and use recover to verify digest and signature | ||
| // is from address calling function | ||
| (address signer, ECDSA.RecoverError err) = ECDSA.tryRecover( | ||
| digest, | ||
| signature | ||
| ); | ||
| // Check for error in signature recovery process | ||
| if (err != ECDSA.RecoverError.NoError) { | ||
| revert InvalidSignature(request.account); | ||
| } | ||
| // Check for error if deadline is expired | ||
|
Collaborator
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: I would put checks at beginning of function |
||
| if (block.timestamp > request.deadline) | ||
| revert DeadlineExpired(block.timestamp - request.deadline); | ||
| // Check for error if signer is whitelisted minter | ||
| if (!minters[signer]) revert InvalidSignature(request.account); | ||
| // Checks for error if nonce is valid for requesting address | ||
| if (_useNonce(request.account) != request.nonce) | ||
| revert InvalidNonce(request.account); | ||
| } | ||
|
|
||
| /** | ||
| * "Consume a nonce": return the current value and increment. This would be done by the dev wallet | ||
| * which is owner of the contract (ie trusted TEMPLE EOA) | ||
| */ | ||
| function _useNonce(address _owner) internal returns (uint256 current) { | ||
| Counters.Counter storage nonce = nonces[_owner]; | ||
| current = nonce.current(); | ||
| nonce.increment(); | ||
| } | ||
|
|
||
| /** | ||
| * @dev hash function stores custom data type QuestCompletedMessageReq as input | ||
| * in a bytes32 hash from data input values of name, version and chainId. | ||
| */ | ||
| function hash(EIP712Domain memory _input) internal pure returns (bytes32) { | ||
| return | ||
| keccak256( | ||
| abi.encode( | ||
| EIP712DOMAIN_TYPEHASH, | ||
| keccak256(bytes(_input.name)), | ||
| keccak256(bytes(_input.version)), | ||
| _input.chainId | ||
| ) | ||
|
Collaborator
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. add verifyingAddress |
||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * @dev functions creates a hash from signer, deadline and nonce and returns it as a bytes32 hash. | ||
| */ | ||
| function hash(MintRequest memory _input) internal pure returns (bytes32) { | ||
| return | ||
| keccak256( | ||
| abi.encode( | ||
| MINTREQUEST_TYPEHASH, | ||
| _input.account, | ||
| _input.deadline, | ||
| _input.nonce | ||
| ) | ||
| ); | ||
| } | ||
|
|
||
| // function creates a hash from input and returns it as a bytes32 hash | ||
| function hash(uint256[] memory _input) internal pure returns (bytes32) { | ||
| return keccak256(abi.encodePacked(_input)); | ||
| } | ||
| } | ||
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.
nit: put interfaces in an external file