-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathConfidentialWETH.sol
155 lines (130 loc) · 5.65 KB
/
ConfidentialWETH.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;
import "fhevm/lib/TFHE.sol";
import "fhevm/gateway/GatewayCaller.sol";
import { ReentrancyGuardTransient } from "@openzeppelin/contracts/utils/ReentrancyGuardTransient.sol";
import { ConfidentialERC20 } from "./ConfidentialERC20.sol";
import { IConfidentialERC20Wrapped } from "./IConfidentialERC20Wrapped.sol";
/**
* @title ConfidentialWETH.
* @notice This contract allows users to wrap/unwrap trustlessly
* ETH (or other native tokens) to ConfidentialERC20 tokens.
*/
abstract contract ConfidentialWETH is
ConfidentialERC20,
IConfidentialERC20Wrapped,
ReentrancyGuardTransient,
GatewayCaller
{
/// @notice Returned if ETH transfer fails.
error ETHTransferFail();
/// @notice Returned if the maximum decryption delay is higher than 1 day.
error MaxDecryptionDelayTooHigh();
/// @notice Tracks whether the account can move funds.
mapping(address account => bool isRestricted) public isAccountRestricted;
/// @notice Tracks the unwrap request to a unique request id.
mapping(uint256 requestId => UnwrapRequest unwrapRequest) public unwrapRequests;
/**
* @notice Deposit/withdraw ethers (or other native tokens).
* @dev The name/symbol are autogenerated.
* @param maxDecryptionDelay_ Maximum delay for the Gateway to decrypt.
* @dev Do not use a small value in production to avoid security issues if the response
* cannot be processed because the block time is higher than the delay.
* The current implementation expects the Gateway to always return a decrypted
* value within the delay specified, as long as it is sufficient enough.
*/
constructor(
uint256 maxDecryptionDelay_
) ConfidentialERC20(string(abi.encodePacked("Confidential Wrapped Ether")), string(abi.encodePacked("WETHc"))) {
/// @dev The maximum delay is set to 1 day.
if (maxDecryptionDelay_ > 1 days) {
revert MaxDecryptionDelayTooHigh();
}
}
/**
* @notice Receive function calls wrap().
*/
receive() external payable {
wrap();
}
/**
* @notice Unwrap ConfidentialERC20 tokens to ether (or other native tokens).
* @param amount Amount to unwrap.
*/
function unwrap(uint64 amount) public virtual {
_canTransferOrUnwrap(msg.sender);
/// @dev Once this function is called, it becomes impossible for the sender to move any token.
isAccountRestricted[msg.sender] = true;
ebool canUnwrap = TFHE.le(amount, _balances[msg.sender]);
uint256[] memory cts = new uint256[](1);
cts[0] = Gateway.toUint256(canUnwrap);
uint256 requestId = Gateway.requestDecryption(
cts,
this.callbackUnwrap.selector,
0,
block.timestamp + 100,
false
);
unwrapRequests[requestId] = UnwrapRequest({ account: msg.sender, amount: amount });
}
/**
* @notice Wrap ether (or other native token) to an encrypted format.
*/
function wrap() public payable virtual {
uint256 amountAdjusted = (msg.value) / (10 ** (18 - decimals()));
if (amountAdjusted > type(uint64).max) {
revert AmountTooHigh();
}
uint64 amountUint64 = uint64(amountAdjusted);
_unsafeMint(msg.sender, amountUint64);
_totalSupply += amountUint64;
emit Wrap(msg.sender, amountUint64);
}
/**
* @notice Callback function for the gateway.
* @param requestId Request id.
* @param canUnwrap Whether it can be unwrapped.
*/
function callbackUnwrap(uint256 requestId, bool canUnwrap) public virtual nonReentrant onlyGateway {
UnwrapRequest memory unwrapRequest = unwrapRequests[requestId];
if (canUnwrap) {
/// @dev It does a supply adjustment.
uint256 amountUint256 = unwrapRequest.amount * (10 ** (18 - decimals()));
/* solhint-disable avoid-call-value*/
/* solhint-disable avoid-low-level-calls*/
(bool callSuccess, ) = unwrapRequest.account.call{ value: amountUint256 }("");
if (callSuccess) {
_unsafeBurn(unwrapRequest.account, unwrapRequest.amount);
_totalSupply -= unwrapRequest.amount;
emit Unwrap(unwrapRequest.account, unwrapRequest.amount);
} else {
emit UnwrapFailTransferFail(unwrapRequest.account, unwrapRequest.amount);
}
} else {
emit UnwrapFailNotEnoughBalance(unwrapRequest.account, unwrapRequest.amount);
}
delete unwrapRequests[requestId];
delete isAccountRestricted[unwrapRequest.account];
}
function _canTransferOrUnwrap(address account) internal virtual {
if (isAccountRestricted[account]) {
revert CannotTransferOrUnwrap();
}
}
function _transferNoEvent(
address from,
address to,
euint64 amount,
ebool isTransferable
) internal virtual override {
_canTransferOrUnwrap(from);
super._transferNoEvent(from, to, amount, isTransferable);
}
function _unsafeBurn(address account, uint64 amount) internal {
euint64 newBalanceAccount = TFHE.sub(_balances[account], amount);
_balances[account] = newBalanceAccount;
TFHE.allowThis(newBalanceAccount);
TFHE.allow(newBalanceAccount, account);
emit Transfer(account, address(0), _PLACEHOLDER);
}
}