Skip to content

Commit dfe6e7d

Browse files
authored
Add timestamp based Governor and Votes clock options (#347)
1 parent 8d9189d commit dfe6e7d

26 files changed

+927
-74
lines changed

packages/core/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## 0.4.3 (2024-04-08)
4+
5+
- Add timestamp based Governor and Votes clock options. ([#347](https://github.com/OpenZeppelin/contracts-wizard/pull/347))
6+
37
## 0.4.2 (2024-02-22)
48

59
- Add code comments for compatible OpenZeppelin Contracts versions. ([#331](https://github.com/OpenZeppelin/contracts-wizard/pull/331))

packages/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@openzeppelin/wizard",
3-
"version": "0.4.2",
3+
"version": "0.4.3",
44
"description": "A boilerplate generator to get started with OpenZeppelin Contracts",
55
"license": "MIT",
66
"repository": "github:OpenZeppelin/contracts-wizard",

packages/core/src/contract.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export interface ContractFunction extends BaseFunction {
4848
code: string[];
4949
mutability: FunctionMutability;
5050
final: boolean;
51+
comments: string[];
5152
}
5253

5354
export type FunctionKind = 'internal' | 'public';
@@ -154,6 +155,7 @@ export class ContractBuilder implements Contract {
154155
code: [],
155156
mutability: 'nonpayable',
156157
final: false,
158+
comments: [],
157159
...baseFn,
158160
};
159161
this.functionMap.set(signature, fn);
@@ -192,6 +194,14 @@ export class ContractBuilder implements Contract {
192194
}
193195
}
194196

197+
setFunctionComments(comments: string[], baseFn: BaseFunction) {
198+
const fn = this.addFunction(baseFn);
199+
if (fn.comments.length > 0) {
200+
throw new Error(`Function ${baseFn.name} already has comments`);
201+
}
202+
fn.comments = comments;
203+
}
204+
195205
/**
196206
* Note: The type in the variable is not currently transpiled, even if it refers to a contract
197207
*/

packages/core/src/erc20.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,14 @@ testERC20('erc20 votes', {
8080
votes: true,
8181
});
8282

83+
testERC20('erc20 votes + blocknumber', {
84+
votes: 'blocknumber',
85+
});
86+
87+
testERC20('erc20 votes + timestamp', {
88+
votes: 'timestamp',
89+
});
90+
8391
testERC20('erc20 flashmint', {
8492
flashmint: true,
8593
});

packages/core/src/erc20.test.ts.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,85 @@ Generated by [AVA](https://avajs.dev).
337337
}␊
338338
`
339339

340+
## erc20 votes + blocknumber
341+
342+
> Snapshot 1
343+
344+
`// SPDX-License-Identifier: MIT␊
345+
// Compatible with OpenZeppelin Contracts ^5.0.0␊
346+
pragma solidity ^0.8.20;␊
347+
348+
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊
349+
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";␊
350+
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";␊
351+
352+
contract MyToken is ERC20, ERC20Permit, ERC20Votes {␊
353+
constructor() ERC20("MyToken", "MTK") ERC20Permit("MyToken") {}␊
354+
355+
// The following functions are overrides required by Solidity.␊
356+
357+
function _update(address from, address to, uint256 value)␊
358+
internal␊
359+
override(ERC20, ERC20Votes)␊
360+
{␊
361+
super._update(from, to, value);␊
362+
}␊
363+
364+
function nonces(address owner)␊
365+
public␊
366+
view␊
367+
override(ERC20Permit, Nonces)␊
368+
returns (uint256)␊
369+
{␊
370+
return super.nonces(owner);␊
371+
}␊
372+
}␊
373+
`
374+
375+
## erc20 votes + timestamp
376+
377+
> Snapshot 1
378+
379+
`// SPDX-License-Identifier: MIT␊
380+
// Compatible with OpenZeppelin Contracts ^5.0.0␊
381+
pragma solidity ^0.8.20;␊
382+
383+
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊
384+
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";␊
385+
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";␊
386+
387+
contract MyToken is ERC20, ERC20Permit, ERC20Votes {␊
388+
constructor() ERC20("MyToken", "MTK") ERC20Permit("MyToken") {}␊
389+
390+
function clock() public view override returns (uint48) {␊
391+
return uint48(block.timestamp);␊
392+
}␊
393+
394+
// solhint-disable-next-line func-name-mixedcase␊
395+
function CLOCK_MODE() public pure override returns (string memory) {␊
396+
return "mode=timestamp";␊
397+
}␊
398+
399+
// The following functions are overrides required by Solidity.␊
400+
401+
function _update(address from, address to, uint256 value)␊
402+
internal␊
403+
override(ERC20, ERC20Votes)␊
404+
{␊
405+
super._update(from, to, value);␊
406+
}␊
407+
408+
function nonces(address owner)␊
409+
public␊
410+
view␊
411+
override(ERC20Permit, Nonces)␊
412+
returns (uint256)␊
413+
{␊
414+
return super.nonces(owner);␊
415+
}␊
416+
}␊
417+
`
418+
340419
## erc20 flashmint
341420

342421
> Snapshot 1

packages/core/src/erc20.test.ts.snap

158 Bytes
Binary file not shown.

packages/core/src/erc20.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { CommonOptions, withCommonDefaults, defaults as commonDefaults } from '.
66
import { setUpgradeable } from './set-upgradeable';
77
import { setInfo } from './set-info';
88
import { printContract } from './print';
9+
import { ClockMode, clockModeDefault, setClockMode } from './set-clock-mode';
910

1011
export interface ERC20Options extends CommonOptions {
1112
name: string;
@@ -15,7 +16,11 @@ export interface ERC20Options extends CommonOptions {
1516
premint?: string;
1617
mintable?: boolean;
1718
permit?: boolean;
18-
votes?: boolean;
19+
/**
20+
* Whether to keep track of historical balances for voting in on-chain governance, and optionally specify the clock mode.
21+
* Setting `true` is equivalent to 'blocknumber'. Setting a clock mode implies voting is enabled.
22+
*/
23+
votes?: boolean | ClockMode;
1924
flashmint?: boolean;
2025
}
2126

@@ -87,7 +92,8 @@ export function buildERC20(opts: ERC20Options): Contract {
8792
}
8893

8994
if (allOpts.votes) {
90-
addVotes(c);
95+
const clockMode = allOpts.votes === true ? clockModeDefault : allOpts.votes;
96+
addVotes(c, clockMode);
9197
}
9298

9399
if (allOpts.flashmint) {
@@ -166,7 +172,7 @@ function addPermit(c: ContractBuilder, name: string) {
166172

167173
}
168174

169-
function addVotes(c: ContractBuilder) {
175+
function addVotes(c: ContractBuilder, clockMode: ClockMode) {
170176
if (!c.parents.some(p => p.contract.name === 'ERC20Permit')) {
171177
throw new Error('Missing ERC20Permit requirement for ERC20Votes');
172178
}
@@ -180,6 +186,8 @@ function addVotes(c: ContractBuilder) {
180186
c.addOverride({
181187
name: 'Nonces',
182188
}, functions.nonces);
189+
190+
setClockMode(c, ERC20Votes, clockMode);
183191
}
184192

185193
function addFlashMint(c: ContractBuilder) {

packages/core/src/erc721.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,14 @@ testERC721('votes', {
8989
votes: true,
9090
});
9191

92+
testERC721('votes + blocknumber', {
93+
votes: 'blocknumber',
94+
});
95+
96+
testERC721('votes + timestamp', {
97+
votes: 'timestamp',
98+
});
99+
92100
testERC721('full upgradeable transparent', {
93101
mintable: true,
94102
enumerable: true,

packages/core/src/erc721.test.ts.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,83 @@ Generated by [AVA](https://avajs.dev).
445445
}␊
446446
`
447447

448+
## votes + blocknumber
449+
450+
> Snapshot 1
451+
452+
`// SPDX-License-Identifier: MIT␊
453+
// Compatible with OpenZeppelin Contracts ^5.0.0␊
454+
pragma solidity ^0.8.20;␊
455+
456+
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";␊
457+
import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";␊
458+
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Votes.sol";␊
459+
460+
contract MyToken is ERC721, EIP712, ERC721Votes {␊
461+
constructor() ERC721("MyToken", "MTK") EIP712("MyToken", "1") {}␊
462+
463+
// The following functions are overrides required by Solidity.␊
464+
465+
function _update(address to, uint256 tokenId, address auth)␊
466+
internal␊
467+
override(ERC721, ERC721Votes)␊
468+
returns (address)␊
469+
{␊
470+
return super._update(to, tokenId, auth);␊
471+
}␊
472+
473+
function _increaseBalance(address account, uint128 value)␊
474+
internal␊
475+
override(ERC721, ERC721Votes)␊
476+
{␊
477+
super._increaseBalance(account, value);␊
478+
}␊
479+
}␊
480+
`
481+
482+
## votes + timestamp
483+
484+
> Snapshot 1
485+
486+
`// SPDX-License-Identifier: MIT␊
487+
// Compatible with OpenZeppelin Contracts ^5.0.0␊
488+
pragma solidity ^0.8.20;␊
489+
490+
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";␊
491+
import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";␊
492+
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Votes.sol";␊
493+
494+
contract MyToken is ERC721, EIP712, ERC721Votes {␊
495+
constructor() ERC721("MyToken", "MTK") EIP712("MyToken", "1") {}␊
496+
497+
function clock() public view override returns (uint48) {␊
498+
return uint48(block.timestamp);␊
499+
}␊
500+
501+
// solhint-disable-next-line func-name-mixedcase␊
502+
function CLOCK_MODE() public pure override returns (string memory) {␊
503+
return "mode=timestamp";␊
504+
}␊
505+
506+
// The following functions are overrides required by Solidity.␊
507+
508+
function _update(address to, uint256 tokenId, address auth)␊
509+
internal␊
510+
override(ERC721, ERC721Votes)␊
511+
returns (address)␊
512+
{␊
513+
return super._update(to, tokenId, auth);␊
514+
}␊
515+
516+
function _increaseBalance(address account, uint128 value)␊
517+
internal␊
518+
override(ERC721, ERC721Votes)␊
519+
{␊
520+
super._increaseBalance(account, value);␊
521+
}␊
522+
}␊
523+
`
524+
448525
## full upgradeable transparent
449526

450527
> Snapshot 1

packages/core/src/erc721.test.ts.snap

145 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)