Skip to content

Commit 69aec99

Browse files
committed
add initial implementation
1 parent f5d214d commit 69aec99

18 files changed

Lines changed: 42382 additions & 1 deletion

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
ETHERSCAN_API_KEY=ABC123ABC123ABC123ABC123ABC123ABC1
2+
ROPSTEN_URL=https://eth-ropsten.alchemyapi.io/v2/<YOUR ALCHEMY KEY>
3+
PRIVATE_KEY=0xabc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc1

.eslintignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules
2+
artifacts
3+
cache
4+
coverage

.eslintrc.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
module.exports = {
2+
env: {
3+
browser: false,
4+
es2021: true,
5+
mocha: true,
6+
node: true,
7+
},
8+
plugins: ["@typescript-eslint"],
9+
extends: [
10+
"standard",
11+
"plugin:prettier/recommended",
12+
"plugin:node/recommended",
13+
],
14+
parser: "@typescript-eslint/parser",
15+
parserOptions: {
16+
ecmaVersion: 12,
17+
},
18+
rules: {
19+
"node/no-unsupported-features/es-syntax": [
20+
"error",
21+
{ ignores: ["modules"] },
22+
],
23+
},
24+
};

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
node_modules
2+
.env
3+
coverage
4+
coverage.json
5+
typechain
6+
7+
#Hardhat files
8+
cache
9+
artifacts

.npmignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
hardhat.config.ts
2+
scripts
3+
test

.prettierignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules
2+
artifacts
3+
cache
4+
coverage*
5+
gasReporterOutput.json

.solhint.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"extends": "solhint:recommended",
3+
"rules": {
4+
"compiler-version": ["error", "^0.8.0"],
5+
"func-visibility": ["warn", { "ignoreConstructors": true }]
6+
}
7+
}

.solhintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

README.md

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,46 @@
1-
# ERC721-Permit
1+
# Advanced Sample Hardhat Project
2+
3+
This project demonstrates an advanced Hardhat use case, integrating other tools commonly used alongside Hardhat in the ecosystem.
4+
5+
The project comes with a sample contract, a test for that contract, a sample script that deploys that contract, and an example of a task implementation, which simply lists the available accounts. It also comes with a variety of other tools, preconfigured to work with the project code.
6+
7+
Try running some of the following tasks:
8+
9+
```shell
10+
npx hardhat accounts
11+
npx hardhat compile
12+
npx hardhat clean
13+
npx hardhat test
14+
npx hardhat node
15+
npx hardhat help
16+
REPORT_GAS=true npx hardhat test
17+
npx hardhat coverage
18+
npx hardhat run scripts/deploy.ts
19+
TS_NODE_FILES=true npx ts-node scripts/deploy.ts
20+
npx eslint '**/*.{js,ts}'
21+
npx eslint '**/*.{js,ts}' --fix
22+
npx prettier '**/*.{json,sol,md}' --check
23+
npx prettier '**/*.{json,sol,md}' --write
24+
npx solhint 'contracts/**/*.sol'
25+
npx solhint 'contracts/**/*.sol' --fix
26+
```
27+
28+
# Etherscan verification
29+
30+
To try out Etherscan verification, you first need to deploy a contract to an Ethereum network that's supported by Etherscan, such as Ropsten.
31+
32+
In this project, copy the .env.example file to a file named .env, and then edit it to fill in the details. Enter your Etherscan API key, your Ropsten node URL (eg from Alchemy), and the private key of the account which will send the deployment transaction. With a valid .env file in place, first deploy your contract:
33+
34+
```shell
35+
hardhat run --network ropsten scripts/sample-script.ts
36+
```
37+
38+
Then, copy the deployment address and paste it in to replace `DEPLOYED_CONTRACT_ADDRESS` in this command:
39+
40+
```shell
41+
npx hardhat verify --network ropsten DEPLOYED_CONTRACT_ADDRESS "Hello, Hardhat!"
42+
```
43+
44+
# Performance optimizations
45+
46+
For faster runs of your tests and scripts, consider skipping ts-node's type checking by setting the environment variable `TS_NODE_TRANSPILE_ONLY` to `1` in hardhat's environment. For more details see [the documentation](https://hardhat.org/guides/typescript.html#performance-optimizations).

contracts/ERC721Permit.sol

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
5+
import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";
6+
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
7+
import "@openzeppelin/contracts/interfaces/IERC1271.sol";
8+
import "@openzeppelin/contracts/utils/Counters.sol";
9+
10+
import "./interfaces/IERC721Permit.sol";
11+
12+
contract ERC721Permit is IERC721Permit, ERC721, EIP712 {
13+
using Counters for Counters.Counter;
14+
15+
mapping(uint256 => Counters.Counter) private _nonces;
16+
17+
// solhint-disable-next-line var-name-mixedcase
18+
bytes32 private immutable _PERMIT_TYPEHASH =
19+
keccak256(
20+
"Permit(address spender,uint256 tokenId,uint256 nonce,uint256 deadline)"
21+
);
22+
23+
constructor(string memory name, string memory symbol)
24+
ERC721(name, symbol)
25+
EIP712(name, "1")
26+
{
27+
this;
28+
}
29+
30+
function nonces(uint256 tokenId)
31+
external
32+
view
33+
virtual
34+
override
35+
returns (uint256)
36+
{
37+
return _nonces[tokenId].current();
38+
}
39+
40+
// solhint-disable-next-line func-name-mixedcase
41+
function DOMAIN_SEPARATOR() external view override returns (bytes32) {
42+
return _domainSeparatorV4();
43+
}
44+
45+
function supportsInterface(bytes4 interfaceId)
46+
public
47+
view
48+
virtual
49+
override(IERC165, ERC721)
50+
returns (bool)
51+
{
52+
return
53+
interfaceId == type(IERC721Permit).interfaceId || // 0x5604e225
54+
super.supportsInterface(interfaceId);
55+
}
56+
57+
function permit(
58+
address spender,
59+
uint256 tokenId,
60+
uint256 deadline,
61+
bytes memory signature
62+
) external override {
63+
_permit(spender, tokenId, deadline, signature);
64+
}
65+
66+
function _transfer(
67+
address from,
68+
address to,
69+
uint256 tokenId
70+
) internal virtual override {
71+
_nonces[tokenId].increment();
72+
super._transfer(from, to, tokenId);
73+
}
74+
75+
function _permit(
76+
address spender,
77+
uint256 tokenId,
78+
uint256 deadline,
79+
bytes memory signature
80+
) internal virtual {
81+
// solhint-disable-next-line not-rely-on-time
82+
require(block.timestamp <= deadline, "ERC721Permit: expired deadline");
83+
84+
bytes32 structHash = keccak256(
85+
abi.encode(
86+
_PERMIT_TYPEHASH,
87+
spender,
88+
tokenId,
89+
_nonces[tokenId].current(),
90+
deadline
91+
)
92+
);
93+
bytes32 hash = _hashTypedDataV4(structHash);
94+
95+
(address signer, ) = ECDSA.tryRecover(hash, signature);
96+
bool isValidEOASignature = signer != address(0) &&
97+
_isApprovedOrOwner(signer, tokenId);
98+
99+
require(
100+
isValidEOASignature ||
101+
_isValidContractERC1271Signature(ownerOf(tokenId), hash, signature) ||
102+
_isValidContractERC1271Signature(getApproved(tokenId), hash, signature),
103+
"ERC721Permit: invalid signature"
104+
);
105+
106+
_approve(spender, tokenId);
107+
}
108+
109+
function _isValidContractERC1271Signature(
110+
address signer,
111+
bytes32 hash,
112+
bytes memory signature
113+
) private view returns (bool) {
114+
(bool success, bytes memory result) = signer.staticcall(
115+
abi.encodeWithSelector(
116+
IERC1271.isValidSignature.selector,
117+
hash,
118+
signature
119+
)
120+
);
121+
return (success &&
122+
result.length == 32 &&
123+
abi.decode(result, (bytes4)) == IERC1271.isValidSignature.selector);
124+
}
125+
}

0 commit comments

Comments
 (0)