@openzeppelin/confidential-contracts 0.2.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -0
- package/build/contracts/Checkpoints.json +16 -0
- package/build/contracts/CheckpointsConfidential.json +16 -0
- package/build/contracts/ConfidentialFungibleToken.json +614 -0
- package/build/contracts/ConfidentialFungibleTokenERC20Wrapper.json +793 -0
- package/build/contracts/ConfidentialFungibleTokenUtils.json +10 -0
- package/build/contracts/ConfidentialFungibleTokenVotes.json +1002 -0
- package/build/contracts/ERC7821WithExecutor.json +145 -0
- package/build/contracts/IConfidentialFungibleToken.json +458 -0
- package/build/contracts/IConfidentialFungibleTokenReceiver.json +45 -0
- package/build/contracts/TFHESafeMath.json +10 -0
- package/build/contracts/VestingWalletCliffConfidential.json +275 -0
- package/build/contracts/VestingWalletCliffExecutorConfidential.json +424 -0
- package/build/contracts/VestingWalletCliffExecutorConfidentialFactory.json +290 -0
- package/build/contracts/VestingWalletConfidential.json +246 -0
- package/build/contracts/VotesConfidential.json +412 -0
- package/finance/ERC7821WithExecutor.sol +46 -0
- package/finance/VestingWalletCliffConfidential.sol +62 -0
- package/finance/VestingWalletCliffExecutorConfidentialFactory.sol +203 -0
- package/finance/VestingWalletConfidential.sol +130 -0
- package/governance/utils/VotesConfidential.sol +202 -0
- package/interfaces/IConfidentialFungibleToken.sol +135 -0
- package/interfaces/IConfidentialFungibleTokenReceiver.sol +19 -0
- package/package.json +39 -0
- package/token/ConfidentialFungibleToken.sol +314 -0
- package/token/extensions/ConfidentialFungibleTokenERC20Wrapper.sol +175 -0
- package/token/extensions/ConfidentialFungibleTokenVotes.sol +29 -0
- package/token/utils/ConfidentialFungibleTokenUtils.sol +46 -0
- package/utils/TFHESafeMath.sol +37 -0
- package/utils/structs/CheckpointsConfidential.sol +193 -0
- package/utils/structs/temporary-Checkpoints.sol +835 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// OpenZeppelin Confidential Contracts (last updated v0.2.0-rc.1) (finance/VestingWalletCliffExecutorConfidentialFactory.sol)
|
|
3
|
+
pragma solidity ^0.8.27;
|
|
4
|
+
|
|
5
|
+
import {FHE, euint64, externalEuint64, euint128} from "@fhevm/solidity/lib/FHE.sol";
|
|
6
|
+
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
|
|
7
|
+
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
|
|
8
|
+
import {IConfidentialFungibleToken} from "./../interfaces/IConfidentialFungibleToken.sol";
|
|
9
|
+
import {ERC7821WithExecutor} from "./ERC7821WithExecutor.sol";
|
|
10
|
+
import {VestingWalletCliffConfidential} from "./VestingWalletCliffConfidential.sol";
|
|
11
|
+
import {VestingWalletConfidential} from "./VestingWalletConfidential.sol";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @dev This factory enables creating {VestingWalletCliffExecutorConfidential} in batch.
|
|
15
|
+
*
|
|
16
|
+
* Confidential vesting wallets created inherit both {VestingWalletCliffConfidential} for vesting cliffs
|
|
17
|
+
* and {ERC7821WithExecutor} to allow for arbitrary calls to be executed from the vesting wallet.
|
|
18
|
+
*/
|
|
19
|
+
contract VestingWalletCliffExecutorConfidentialFactory {
|
|
20
|
+
struct VestingPlan {
|
|
21
|
+
address beneficiary;
|
|
22
|
+
externalEuint64 encryptedAmount;
|
|
23
|
+
uint48 startTimestamp;
|
|
24
|
+
uint48 durationSeconds;
|
|
25
|
+
uint48 cliffSeconds;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
address private immutable _vestingImplementation = address(new VestingWalletCliffExecutorConfidential());
|
|
29
|
+
|
|
30
|
+
event VestingWalletConfidentialFunded(
|
|
31
|
+
address indexed vestingWalletConfidential,
|
|
32
|
+
address indexed beneficiary,
|
|
33
|
+
address indexed confidentialFungibleToken,
|
|
34
|
+
euint64 encryptedAmount,
|
|
35
|
+
uint48 startTimestamp,
|
|
36
|
+
uint48 durationSeconds,
|
|
37
|
+
uint48 cliffSeconds,
|
|
38
|
+
address executor
|
|
39
|
+
);
|
|
40
|
+
event VestingWalletConfidentialCreated(
|
|
41
|
+
address indexed vestingWalletConfidential,
|
|
42
|
+
address indexed beneficiary,
|
|
43
|
+
uint48 startTimestamp,
|
|
44
|
+
uint48 durationSeconds,
|
|
45
|
+
uint48 cliffSeconds,
|
|
46
|
+
address indexed executor
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @dev Batches the funding of multiple confidential vesting wallets.
|
|
51
|
+
*
|
|
52
|
+
* Funds are sent to deterministic wallet addresses. Wallets can be created either
|
|
53
|
+
* before or after this operation.
|
|
54
|
+
*
|
|
55
|
+
* Emits a {VestingWalletConfidentialFunded} event for each funded vesting plan.
|
|
56
|
+
*/
|
|
57
|
+
function batchFundVestingWalletConfidential(
|
|
58
|
+
address confidentialFungibleToken,
|
|
59
|
+
VestingPlan[] calldata vestingPlans,
|
|
60
|
+
address executor,
|
|
61
|
+
bytes calldata inputProof
|
|
62
|
+
) public virtual {
|
|
63
|
+
uint256 vestingPlansLength = vestingPlans.length;
|
|
64
|
+
for (uint256 i = 0; i < vestingPlansLength; i++) {
|
|
65
|
+
VestingPlan memory vestingPlan = vestingPlans[i];
|
|
66
|
+
require(
|
|
67
|
+
vestingPlan.cliffSeconds <= vestingPlan.durationSeconds,
|
|
68
|
+
VestingWalletCliffConfidential.VestingWalletCliffConfidentialInvalidCliffDuration(
|
|
69
|
+
vestingPlan.cliffSeconds,
|
|
70
|
+
vestingPlan.durationSeconds
|
|
71
|
+
)
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
require(vestingPlan.beneficiary != address(0), OwnableUpgradeable.OwnableInvalidOwner(address(0)));
|
|
75
|
+
address vestingWalletAddress = predictVestingWalletConfidential(
|
|
76
|
+
vestingPlan.beneficiary,
|
|
77
|
+
vestingPlan.startTimestamp,
|
|
78
|
+
vestingPlan.durationSeconds,
|
|
79
|
+
vestingPlan.cliffSeconds,
|
|
80
|
+
executor
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
euint64 transferredAmount;
|
|
84
|
+
{
|
|
85
|
+
// avoiding stack too deep with scope
|
|
86
|
+
euint64 encryptedAmount = FHE.fromExternal(vestingPlan.encryptedAmount, inputProof);
|
|
87
|
+
FHE.allowTransient(encryptedAmount, confidentialFungibleToken);
|
|
88
|
+
transferredAmount = IConfidentialFungibleToken(confidentialFungibleToken).confidentialTransferFrom(
|
|
89
|
+
msg.sender,
|
|
90
|
+
vestingWalletAddress,
|
|
91
|
+
encryptedAmount
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
emit VestingWalletConfidentialFunded(
|
|
96
|
+
vestingWalletAddress,
|
|
97
|
+
vestingPlan.beneficiary,
|
|
98
|
+
confidentialFungibleToken,
|
|
99
|
+
transferredAmount,
|
|
100
|
+
vestingPlan.startTimestamp,
|
|
101
|
+
vestingPlan.durationSeconds,
|
|
102
|
+
vestingPlan.cliffSeconds,
|
|
103
|
+
executor
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @dev Creates a confidential vesting wallet.
|
|
110
|
+
*
|
|
111
|
+
* Emits a {VestingWalletConfidentialCreated}.
|
|
112
|
+
*/
|
|
113
|
+
function createVestingWalletConfidential(
|
|
114
|
+
address beneficiary,
|
|
115
|
+
uint48 startTimestamp,
|
|
116
|
+
uint48 durationSeconds,
|
|
117
|
+
uint48 cliffSeconds,
|
|
118
|
+
address executor
|
|
119
|
+
) public virtual returns (address) {
|
|
120
|
+
// Will revert if clone already created
|
|
121
|
+
address vestingWalletConfidentialAddress = Clones.cloneDeterministic(
|
|
122
|
+
_vestingImplementation,
|
|
123
|
+
_getCreate2VestingWalletConfidentialSalt(
|
|
124
|
+
beneficiary,
|
|
125
|
+
startTimestamp,
|
|
126
|
+
durationSeconds,
|
|
127
|
+
cliffSeconds,
|
|
128
|
+
executor
|
|
129
|
+
)
|
|
130
|
+
);
|
|
131
|
+
VestingWalletCliffExecutorConfidential(vestingWalletConfidentialAddress).initialize(
|
|
132
|
+
beneficiary,
|
|
133
|
+
startTimestamp,
|
|
134
|
+
durationSeconds,
|
|
135
|
+
cliffSeconds,
|
|
136
|
+
executor
|
|
137
|
+
);
|
|
138
|
+
emit VestingWalletConfidentialCreated(
|
|
139
|
+
beneficiary,
|
|
140
|
+
vestingWalletConfidentialAddress,
|
|
141
|
+
startTimestamp,
|
|
142
|
+
durationSeconds,
|
|
143
|
+
cliffSeconds,
|
|
144
|
+
executor
|
|
145
|
+
);
|
|
146
|
+
return vestingWalletConfidentialAddress;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @dev Predicts deterministic address for a confidential vesting wallet.
|
|
151
|
+
*/
|
|
152
|
+
function predictVestingWalletConfidential(
|
|
153
|
+
address beneficiary,
|
|
154
|
+
uint48 startTimestamp,
|
|
155
|
+
uint48 durationSeconds,
|
|
156
|
+
uint48 cliffSeconds,
|
|
157
|
+
address executor
|
|
158
|
+
) public view virtual returns (address) {
|
|
159
|
+
return
|
|
160
|
+
Clones.predictDeterministicAddress(
|
|
161
|
+
_vestingImplementation,
|
|
162
|
+
_getCreate2VestingWalletConfidentialSalt(
|
|
163
|
+
beneficiary,
|
|
164
|
+
startTimestamp,
|
|
165
|
+
durationSeconds,
|
|
166
|
+
cliffSeconds,
|
|
167
|
+
executor
|
|
168
|
+
)
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* @dev Gets create2 salt for a confidential vesting wallet.
|
|
174
|
+
*/
|
|
175
|
+
function _getCreate2VestingWalletConfidentialSalt(
|
|
176
|
+
address beneficiary,
|
|
177
|
+
uint48 startTimestamp,
|
|
178
|
+
uint48 durationSeconds,
|
|
179
|
+
uint48 cliffSeconds,
|
|
180
|
+
address executor
|
|
181
|
+
) internal pure virtual returns (bytes32) {
|
|
182
|
+
return keccak256(abi.encodePacked(beneficiary, startTimestamp, durationSeconds, cliffSeconds, executor));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// slither-disable-next-line locked-ether
|
|
187
|
+
contract VestingWalletCliffExecutorConfidential is VestingWalletCliffConfidential, ERC7821WithExecutor {
|
|
188
|
+
constructor() {
|
|
189
|
+
_disableInitializers();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function initialize(
|
|
193
|
+
address beneficiary,
|
|
194
|
+
uint48 startTimestamp,
|
|
195
|
+
uint48 durationSeconds,
|
|
196
|
+
uint48 cliffSeconds,
|
|
197
|
+
address executor
|
|
198
|
+
) public initializer {
|
|
199
|
+
__VestingWalletConfidential_init(beneficiary, startTimestamp, durationSeconds);
|
|
200
|
+
__VestingWalletCliffConfidential_init(cliffSeconds);
|
|
201
|
+
__ERC7821WithExecutor_init(executor);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// OpenZeppelin Confidential Contracts (last updated v0.2.0-rc.1) (finance/VestingWalletConfidential.sol)
|
|
3
|
+
pragma solidity ^0.8.24;
|
|
4
|
+
|
|
5
|
+
import {FHE, ebool, euint64, euint128} from "@fhevm/solidity/lib/FHE.sol";
|
|
6
|
+
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
|
|
7
|
+
import {ReentrancyGuardTransient} from "@openzeppelin/contracts/utils/ReentrancyGuardTransient.sol";
|
|
8
|
+
import {IConfidentialFungibleToken} from "./../interfaces/IConfidentialFungibleToken.sol";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @dev A vesting wallet is an ownable contract that can receive ConfidentialFungibleTokens, and release these
|
|
12
|
+
* assets to the wallet owner, also referred to as "beneficiary", according to a vesting schedule.
|
|
13
|
+
*
|
|
14
|
+
* Any assets transferred to this contract will follow the vesting schedule as if they were locked from the beginning.
|
|
15
|
+
* Consequently, if the vesting has already started, any amount of tokens sent to this contract will (at least partly)
|
|
16
|
+
* be immediately releasable.
|
|
17
|
+
*
|
|
18
|
+
* By setting the duration to 0, one can configure this contract to behave like an asset timelock that holds tokens for
|
|
19
|
+
* a beneficiary until a specified time.
|
|
20
|
+
*
|
|
21
|
+
* NOTE: Since the wallet is `Ownable`, and ownership can be transferred, it is possible to sell unvested tokens.
|
|
22
|
+
*
|
|
23
|
+
* NOTE: When using this contract with any token whose balance is adjusted automatically (i.e. a rebase token), make
|
|
24
|
+
* sure to account the supply/balance adjustment in the vesting schedule to ensure the vested amount is as intended.
|
|
25
|
+
*/
|
|
26
|
+
abstract contract VestingWalletConfidential is OwnableUpgradeable, ReentrancyGuardTransient {
|
|
27
|
+
/// @custom:storage-location erc7201:openzeppelin.storage.VestingWalletConfidential
|
|
28
|
+
struct VestingWalletStorage {
|
|
29
|
+
mapping(address token => euint64) _tokenReleased;
|
|
30
|
+
uint64 _start;
|
|
31
|
+
uint64 _duration;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.VestingWalletConfidential")) - 1)) & ~bytes32(uint256(0xff))
|
|
35
|
+
// solhint-disable-next-line const-name-snakecase
|
|
36
|
+
bytes32 private constant VestingWalletStorageLocation =
|
|
37
|
+
0x78ce9ee9eb65fa0cf5bf10e861c3a95cb7c3c713c96ab1e5323a21e846796800;
|
|
38
|
+
|
|
39
|
+
event VestingWalletConfidentialTokenReleased(address indexed token, euint64 amount);
|
|
40
|
+
|
|
41
|
+
/// @dev Timestamp at which the vesting starts.
|
|
42
|
+
function start() public view virtual returns (uint64) {
|
|
43
|
+
return _getVestingWalletStorage()._start;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// @dev Duration of the vesting in seconds.
|
|
47
|
+
function duration() public view virtual returns (uint64) {
|
|
48
|
+
return _getVestingWalletStorage()._duration;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/// @dev Timestamp at which the vesting ends.
|
|
52
|
+
function end() public view virtual returns (uint64) {
|
|
53
|
+
return start() + duration();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/// @dev Amount of token already released
|
|
57
|
+
function released(address token) public view virtual returns (euint64) {
|
|
58
|
+
return _getVestingWalletStorage()._tokenReleased[token];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @dev Getter for the amount of releasable `token` tokens. `token` should be the address of an
|
|
63
|
+
* {IConfidentialFungibleToken} contract.
|
|
64
|
+
*/
|
|
65
|
+
function releasable(address token) public virtual returns (euint64) {
|
|
66
|
+
return FHE.asEuint64(FHE.sub(vestedAmount(token, uint64(block.timestamp)), released(token)));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @dev Release the tokens that have already vested.
|
|
71
|
+
*
|
|
72
|
+
* Emits a {VestingWalletConfidentialTokenReleased} event.
|
|
73
|
+
*/
|
|
74
|
+
function release(address token) public virtual nonReentrant {
|
|
75
|
+
euint64 amount = releasable(token);
|
|
76
|
+
FHE.allowTransient(amount, token);
|
|
77
|
+
euint64 amountSent = IConfidentialFungibleToken(token).confidentialTransfer(owner(), amount);
|
|
78
|
+
|
|
79
|
+
euint64 newReleasedAmount = FHE.add(released(token), amountSent);
|
|
80
|
+
FHE.allow(newReleasedAmount, owner());
|
|
81
|
+
FHE.allowThis(newReleasedAmount);
|
|
82
|
+
_getVestingWalletStorage()._tokenReleased[token] = newReleasedAmount;
|
|
83
|
+
emit VestingWalletConfidentialTokenReleased(token, amountSent);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/// @dev Calculates the amount of tokens that has already vested. Default implementation is a linear vesting curve.
|
|
87
|
+
function vestedAmount(address token, uint64 timestamp) public virtual returns (euint128) {
|
|
88
|
+
return
|
|
89
|
+
_vestingSchedule(
|
|
90
|
+
FHE.add(
|
|
91
|
+
FHE.asEuint128(IConfidentialFungibleToken(token).confidentialBalanceOf(address(this))),
|
|
92
|
+
released(token)
|
|
93
|
+
),
|
|
94
|
+
timestamp
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @dev Initializes the vesting wallet for a given `beneficiary` with a start time of `startTimestamp`
|
|
100
|
+
* and an end time of `startTimestamp + durationSeconds`.
|
|
101
|
+
*/
|
|
102
|
+
// solhint-disable-next-line func-name-mixedcase
|
|
103
|
+
function __VestingWalletConfidential_init(
|
|
104
|
+
address beneficiary,
|
|
105
|
+
uint48 startTimestamp,
|
|
106
|
+
uint48 durationSeconds
|
|
107
|
+
) internal onlyInitializing {
|
|
108
|
+
__Ownable_init(beneficiary);
|
|
109
|
+
VestingWalletStorage storage $ = _getVestingWalletStorage();
|
|
110
|
+
$._start = startTimestamp;
|
|
111
|
+
$._duration = durationSeconds;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/// @dev This returns the amount vested, as a function of time, for an asset given its total historical allocation.
|
|
115
|
+
function _vestingSchedule(euint128 totalAllocation, uint64 timestamp) internal virtual returns (euint128) {
|
|
116
|
+
if (timestamp < start()) {
|
|
117
|
+
return euint128.wrap(0);
|
|
118
|
+
} else if (timestamp >= end()) {
|
|
119
|
+
return totalAllocation;
|
|
120
|
+
} else {
|
|
121
|
+
return FHE.div(FHE.mul(totalAllocation, (timestamp - start())), duration());
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function _getVestingWalletStorage() private pure returns (VestingWalletStorage storage $) {
|
|
126
|
+
assembly {
|
|
127
|
+
$.slot := VestingWalletStorageLocation
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// OpenZeppelin Confidential Contracts (last updated v0.2.0-rc.1) (governance/utils/VotesConfidential.sol)
|
|
3
|
+
pragma solidity ^0.8.24;
|
|
4
|
+
|
|
5
|
+
import {FHE, ebool, euint64} from "@fhevm/solidity/lib/FHE.sol";
|
|
6
|
+
import {IERC6372} from "@openzeppelin/contracts/interfaces/IERC6372.sol";
|
|
7
|
+
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
|
8
|
+
import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
|
|
9
|
+
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
|
|
10
|
+
import {Nonces} from "@openzeppelin/contracts/utils/Nonces.sol";
|
|
11
|
+
import {Time} from "@openzeppelin/contracts/utils/types/Time.sol";
|
|
12
|
+
import {CheckpointsConfidential} from "./../../utils/structs/CheckpointsConfidential.sol";
|
|
13
|
+
|
|
14
|
+
abstract contract VotesConfidential is Nonces, EIP712, IERC6372 {
|
|
15
|
+
using FHE for *;
|
|
16
|
+
using CheckpointsConfidential for CheckpointsConfidential.TraceEuint64;
|
|
17
|
+
|
|
18
|
+
bytes32 private constant DELEGATION_TYPEHASH =
|
|
19
|
+
keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
|
|
20
|
+
|
|
21
|
+
mapping(address account => address) private _delegatee;
|
|
22
|
+
|
|
23
|
+
mapping(address delegatee => CheckpointsConfidential.TraceEuint64) private _delegateCheckpoints;
|
|
24
|
+
|
|
25
|
+
CheckpointsConfidential.TraceEuint64 private _totalCheckpoints;
|
|
26
|
+
|
|
27
|
+
/// @dev The signature used has expired.
|
|
28
|
+
error VotesExpiredSignature(uint256 expiry);
|
|
29
|
+
|
|
30
|
+
/// @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of voting units.
|
|
31
|
+
event DelegateVotesChanged(address indexed delegate, euint64 previousVotes, euint64 newVotes);
|
|
32
|
+
|
|
33
|
+
/// @dev Emitted when an account changes their delegate.
|
|
34
|
+
event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
|
|
35
|
+
|
|
36
|
+
/// @dev The clock was incorrectly modified.
|
|
37
|
+
error ERC6372InconsistentClock();
|
|
38
|
+
|
|
39
|
+
/// @dev Lookup to future votes is not available.
|
|
40
|
+
error ERC5805FutureLookup(uint256 timepoint, uint48 clock);
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @dev Clock used for flagging checkpoints. Can be overridden to implement timestamp based
|
|
44
|
+
* checkpoints (and voting), in which case {CLOCK_MODE} should be overridden as well to match.
|
|
45
|
+
*/
|
|
46
|
+
function clock() public view virtual returns (uint48) {
|
|
47
|
+
return Time.blockNumber();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @dev Machine-readable description of the clock as specified in ERC-6372.
|
|
52
|
+
*/
|
|
53
|
+
// solhint-disable-next-line func-name-mixedcase
|
|
54
|
+
function CLOCK_MODE() public view virtual returns (string memory) {
|
|
55
|
+
// Check that the clock was not modified
|
|
56
|
+
if (clock() != Time.blockNumber()) {
|
|
57
|
+
revert ERC6372InconsistentClock();
|
|
58
|
+
}
|
|
59
|
+
return "mode=blocknumber&from=default";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/// @dev Returns the current amount of votes that `account` has.
|
|
63
|
+
function getVotes(address account) public view virtual returns (euint64) {
|
|
64
|
+
return _delegateCheckpoints[account].latest();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @dev Returns the amount of votes that `account` had at a specific moment in the past. If the {clock} is
|
|
69
|
+
* configured to use block numbers, this will return the value at the end of the corresponding block.
|
|
70
|
+
*
|
|
71
|
+
* Requirements:
|
|
72
|
+
*
|
|
73
|
+
* - `timepoint` must be in the past. If operating using block numbers, the block must be already mined.
|
|
74
|
+
*/
|
|
75
|
+
function getPastVotes(address account, uint256 timepoint) public view virtual returns (euint64) {
|
|
76
|
+
return _delegateCheckpoints[account].upperLookupRecent(_validateTimepoint(timepoint));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @dev Returns the total supply of votes available at a specific moment in the past. If the {clock} is
|
|
81
|
+
* configured to use block numbers, this will return the value at the end of the corresponding block.
|
|
82
|
+
*
|
|
83
|
+
* NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.
|
|
84
|
+
* Votes that have not been delegated are still part of total supply, even though they would not participate in a
|
|
85
|
+
* vote.
|
|
86
|
+
*
|
|
87
|
+
* Requirements:
|
|
88
|
+
*
|
|
89
|
+
* - `timepoint` must be in the past. If operating using block numbers, the block must be already mined.
|
|
90
|
+
*/
|
|
91
|
+
function getPastTotalSupply(uint256 timepoint) public view virtual returns (euint64) {
|
|
92
|
+
return _totalCheckpoints.upperLookupRecent(_validateTimepoint(timepoint));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @dev Returns the current total supply of votes as an encrypted uint64 (euint64). Must be implemented
|
|
97
|
+
* by the derived contract.
|
|
98
|
+
*/
|
|
99
|
+
function confidentialTotalSupply() public view virtual returns (euint64);
|
|
100
|
+
|
|
101
|
+
/// @dev Returns the delegate that `account` has chosen.
|
|
102
|
+
function delegates(address account) public view virtual returns (address) {
|
|
103
|
+
return _delegatee[account];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/// @dev Delegates votes from the sender to `delegatee`.
|
|
107
|
+
function delegate(address delegatee) public virtual {
|
|
108
|
+
_delegate(msg.sender, delegatee);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/// @dev Delegates votes from an EOA to `delegatee` via an ECDSA signature.
|
|
112
|
+
function delegateBySig(
|
|
113
|
+
address delegatee,
|
|
114
|
+
uint256 nonce,
|
|
115
|
+
uint256 expiry,
|
|
116
|
+
uint8 v,
|
|
117
|
+
bytes32 r,
|
|
118
|
+
bytes32 s
|
|
119
|
+
) public virtual {
|
|
120
|
+
if (block.timestamp > expiry) {
|
|
121
|
+
revert VotesExpiredSignature(expiry);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
address signer = ECDSA.recover(
|
|
125
|
+
_hashTypedDataV4(keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry))),
|
|
126
|
+
v,
|
|
127
|
+
r,
|
|
128
|
+
s
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
_useCheckedNonce(signer, nonce);
|
|
132
|
+
_delegate(signer, delegatee);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @dev Delegate all of `account`'s voting units to `delegatee`.
|
|
137
|
+
*
|
|
138
|
+
* Emits events {IVotes-DelegateChanged} and {IVotes-DelegateVotesChanged}.
|
|
139
|
+
*/
|
|
140
|
+
function _delegate(address account, address delegatee) internal virtual {
|
|
141
|
+
address oldDelegate = delegates(account);
|
|
142
|
+
_delegatee[account] = delegatee;
|
|
143
|
+
|
|
144
|
+
emit DelegateChanged(account, oldDelegate, delegatee);
|
|
145
|
+
_moveDelegateVotes(oldDelegate, delegatee, _getVotingUnits(account));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @dev Transfers, mints, or burns voting units. To register a mint, `from` should be zero. To register a burn, `to`
|
|
150
|
+
* should be zero. Total supply of voting units will be adjusted with mints and burns.
|
|
151
|
+
*
|
|
152
|
+
* WARNING: Must be called after {confidentialTotalSupply} is updated.
|
|
153
|
+
*/
|
|
154
|
+
function _transferVotingUnits(address from, address to, euint64 amount) internal virtual {
|
|
155
|
+
if (from == address(0) || to == address(0)) {
|
|
156
|
+
_push(_totalCheckpoints, confidentialTotalSupply());
|
|
157
|
+
}
|
|
158
|
+
_moveDelegateVotes(delegates(from), delegates(to), amount);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* @dev Moves delegated votes from one delegate to another.
|
|
163
|
+
*/
|
|
164
|
+
function _moveDelegateVotes(address from, address to, euint64 amount) internal virtual {
|
|
165
|
+
CheckpointsConfidential.TraceEuint64 storage store;
|
|
166
|
+
if (from != to && FHE.isInitialized(amount)) {
|
|
167
|
+
if (from != address(0)) {
|
|
168
|
+
store = _delegateCheckpoints[from];
|
|
169
|
+
euint64 newValue = store.latest().sub(amount);
|
|
170
|
+
newValue.allowThis();
|
|
171
|
+
newValue.allow(from);
|
|
172
|
+
euint64 oldValue = _push(store, newValue);
|
|
173
|
+
emit DelegateVotesChanged(from, oldValue, newValue);
|
|
174
|
+
}
|
|
175
|
+
if (to != address(0)) {
|
|
176
|
+
store = _delegateCheckpoints[to];
|
|
177
|
+
euint64 newValue = store.latest().add(amount);
|
|
178
|
+
newValue.allowThis();
|
|
179
|
+
newValue.allow(to);
|
|
180
|
+
euint64 oldValue = _push(store, newValue);
|
|
181
|
+
emit DelegateVotesChanged(to, oldValue, newValue);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/// @dev Validate that a timepoint is in the past, and return it as a uint48.
|
|
187
|
+
function _validateTimepoint(uint256 timepoint) internal view returns (uint48) {
|
|
188
|
+
uint48 currentTimepoint = clock();
|
|
189
|
+
if (timepoint >= currentTimepoint) revert ERC5805FutureLookup(timepoint, currentTimepoint);
|
|
190
|
+
return SafeCast.toUint48(timepoint);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* @dev Must return the voting units held by an account.
|
|
195
|
+
*/
|
|
196
|
+
function _getVotingUnits(address) internal view virtual returns (euint64);
|
|
197
|
+
|
|
198
|
+
function _push(CheckpointsConfidential.TraceEuint64 storage store, euint64 value) private returns (euint64) {
|
|
199
|
+
(euint64 oldValue, ) = store.push(clock(), value);
|
|
200
|
+
return oldValue;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// OpenZeppelin Confidential Contracts (last updated v0.2.0-rc.1) (interfaces/IConfidentialFungibleToken.sol)
|
|
3
|
+
pragma solidity ^0.8.24;
|
|
4
|
+
|
|
5
|
+
import {euint64, externalEuint64} from "@fhevm/solidity/lib/FHE.sol";
|
|
6
|
+
|
|
7
|
+
/// @dev Draft interface for a confidential fungible token standard utilizing the Zama TFHE library.
|
|
8
|
+
interface IConfidentialFungibleToken {
|
|
9
|
+
/**
|
|
10
|
+
* @dev Emitted when the expiration timestamp for an operator `operator` is updated for a given `holder`.
|
|
11
|
+
* The operator may move any amount of tokens on behalf of the holder until the timestamp `until`.
|
|
12
|
+
*/
|
|
13
|
+
event OperatorSet(address indexed holder, address indexed operator, uint48 until);
|
|
14
|
+
|
|
15
|
+
/// @dev Emitted when a confidential transfer is made from `from` to `to` of encrypted amount `amount`.
|
|
16
|
+
event ConfidentialTransfer(address indexed from, address indexed to, euint64 indexed amount);
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @dev Emitted when an encrypted amount is disclosed.
|
|
20
|
+
*
|
|
21
|
+
* Accounts with access to the encrypted amount `encryptedAmount` that is also accessible to this contract
|
|
22
|
+
* should be able to disclose the amount. This functionality is implementation specific.
|
|
23
|
+
*/
|
|
24
|
+
event AmountDisclosed(euint64 indexed encryptedAmount, uint64 amount);
|
|
25
|
+
|
|
26
|
+
/// @dev Returns the name of the token.
|
|
27
|
+
function name() external view returns (string memory);
|
|
28
|
+
|
|
29
|
+
/// @dev Returns the symbol of the token.
|
|
30
|
+
function symbol() external view returns (string memory);
|
|
31
|
+
|
|
32
|
+
/// @dev Returns the number of decimals of the token. Recommended to be 6.
|
|
33
|
+
function decimals() external view returns (uint8);
|
|
34
|
+
|
|
35
|
+
/// @dev Returns the token URI.
|
|
36
|
+
function tokenURI() external view returns (string memory);
|
|
37
|
+
|
|
38
|
+
/// @dev Returns the confidential total supply of the token.
|
|
39
|
+
function confidentialTotalSupply() external view returns (euint64);
|
|
40
|
+
|
|
41
|
+
/// @dev Returns the confidential balance of the account `account`.
|
|
42
|
+
function confidentialBalanceOf(address account) external view returns (euint64);
|
|
43
|
+
|
|
44
|
+
/// @dev Returns true if `spender` is currently an operator for `holder`.
|
|
45
|
+
function isOperator(address holder, address spender) external view returns (bool);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @dev Sets `operator` as an operator for `holder` until the timestamp `until`.
|
|
49
|
+
*
|
|
50
|
+
* NOTE: An operator may transfer any amount of tokens on behalf of a holder while approved.
|
|
51
|
+
*/
|
|
52
|
+
function setOperator(address operator, uint48 until) external;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @dev Transfers the encrypted amount `encryptedAmount` to `to` with the given input proof `inputProof`.
|
|
56
|
+
*
|
|
57
|
+
* Returns the encrypted amount that was actually transferred.
|
|
58
|
+
*/
|
|
59
|
+
function confidentialTransfer(
|
|
60
|
+
address to,
|
|
61
|
+
externalEuint64 encryptedAmount,
|
|
62
|
+
bytes calldata inputProof
|
|
63
|
+
) external returns (euint64);
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @dev Similar to {confidentialTransfer-address-externalEuint64-bytes} but without an input proof. The caller
|
|
67
|
+
* *must* already be allowed by ACL for the given `amount`.
|
|
68
|
+
*/
|
|
69
|
+
function confidentialTransfer(address to, euint64 amount) external returns (euint64 transferred);
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @dev Transfers the encrypted amount `encryptedAmount` from `from` to `to` with the given input proof
|
|
73
|
+
* `inputProof`. `msg.sender` must be either `from` or an operator for `from`.
|
|
74
|
+
*
|
|
75
|
+
* Returns the encrypted amount that was actually transferred.
|
|
76
|
+
*/
|
|
77
|
+
function confidentialTransferFrom(
|
|
78
|
+
address from,
|
|
79
|
+
address to,
|
|
80
|
+
externalEuint64 encryptedAmount,
|
|
81
|
+
bytes calldata inputProof
|
|
82
|
+
) external returns (euint64);
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @dev Similar to {confidentialTransferFrom-address-address-externalEuint64-bytes} but without an input proof.
|
|
86
|
+
* The caller *must* be already allowed by ACL for the given `amount`.
|
|
87
|
+
*/
|
|
88
|
+
function confidentialTransferFrom(address from, address to, euint64 amount) external returns (euint64 transferred);
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @dev Similar to {confidentialTransfer-address-externalEuint64-bytes} but with a callback to `to` after
|
|
92
|
+
* the transfer.
|
|
93
|
+
*
|
|
94
|
+
* The callback is made to the {IConfidentialFungibleTokenReceiver-onConfidentialTransferReceived} function on the
|
|
95
|
+
* to address with the actual transferred amount (may differ from the given `encryptedAmount`) and the given
|
|
96
|
+
* data `data`.
|
|
97
|
+
*/
|
|
98
|
+
function confidentialTransferAndCall(
|
|
99
|
+
address to,
|
|
100
|
+
externalEuint64 encryptedAmount,
|
|
101
|
+
bytes calldata inputProof,
|
|
102
|
+
bytes calldata data
|
|
103
|
+
) external returns (euint64 transferred);
|
|
104
|
+
|
|
105
|
+
/// @dev Similar to {confidentialTransfer-address-euint64} but with a callback to `to` after the transfer.
|
|
106
|
+
function confidentialTransferAndCall(
|
|
107
|
+
address to,
|
|
108
|
+
euint64 amount,
|
|
109
|
+
bytes calldata data
|
|
110
|
+
) external returns (euint64 transferred);
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @dev Similar to {confidentialTransferFrom-address-address-externalEuint64-bytes} but with a callback to `to`
|
|
114
|
+
* after the transfer.
|
|
115
|
+
*/
|
|
116
|
+
function confidentialTransferFromAndCall(
|
|
117
|
+
address from,
|
|
118
|
+
address to,
|
|
119
|
+
externalEuint64 encryptedAmount,
|
|
120
|
+
bytes calldata inputProof,
|
|
121
|
+
bytes calldata data
|
|
122
|
+
) external returns (euint64 transferred);
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @dev Similar to {confidentialTransferFrom-address-address-euint64} but with a callback to `to`
|
|
126
|
+
* after the transfer.
|
|
127
|
+
*
|
|
128
|
+
*/
|
|
129
|
+
function confidentialTransferFromAndCall(
|
|
130
|
+
address from,
|
|
131
|
+
address to,
|
|
132
|
+
euint64 amount,
|
|
133
|
+
bytes calldata data
|
|
134
|
+
) external returns (euint64 transferred);
|
|
135
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// OpenZeppelin Confidential Contracts (last updated v0.2.0-rc.1) (interfaces/IConfidentialFungibleTokenReceiver.sol)
|
|
3
|
+
pragma solidity ^0.8.24;
|
|
4
|
+
|
|
5
|
+
import {ebool, euint64} from "@fhevm/solidity/lib/FHE.sol";
|
|
6
|
+
|
|
7
|
+
/// @dev Interface for contracts that can receive confidential token transfers with a callback.
|
|
8
|
+
interface IConfidentialFungibleTokenReceiver {
|
|
9
|
+
/**
|
|
10
|
+
* @dev Called upon receiving a confidential token transfer. Returns an encrypted boolean indicating success
|
|
11
|
+
* of the callback. If false is returned, the transfer must be reversed.
|
|
12
|
+
*/
|
|
13
|
+
function onConfidentialTransferReceived(
|
|
14
|
+
address operator,
|
|
15
|
+
address from,
|
|
16
|
+
euint64 amount,
|
|
17
|
+
bytes calldata data
|
|
18
|
+
) external returns (ebool);
|
|
19
|
+
}
|