|
19 | 19 | * along with confidential-token. If not, see <https://www.gnu.org/licenses/>. |
20 | 20 | */ |
21 | 21 |
|
22 | | -// cspell:words IERC20 |
23 | | - |
24 | 22 | pragma solidity ^0.8.27; |
25 | 23 |
|
26 | | -import { ERC20, IERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; |
27 | | -import { ERC20Wrapper } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Wrapper.sol"; |
28 | 24 | import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; |
29 | | -import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; |
30 | | -import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; |
31 | 25 |
|
32 | | -import { BITE, ConfidentialToken } from "./ConfidentialToken.sol"; |
33 | | -import { IConfidentialWrapper } from "./interfaces/IConfidentialWrapper.sol"; |
| 26 | +import { ConfidentialWrapperCore } from "./core/ConfidentialWrapperCore.sol"; |
34 | 27 |
|
35 | 28 |
|
36 | 29 | /// @title ConfidentialWrapper |
37 | 30 | /// @author Dmytro Stebaiev |
38 | 31 | /// @notice Confidential wrapper that adds confidentiality to an ERC20 token |
39 | | -contract ConfidentialWrapper is ConfidentialToken, ERC20Wrapper, IConfidentialWrapper { |
40 | | - using SafeERC20 for IERC20; |
41 | | - |
42 | | - uint8 private constant _WITHDRAW_TO_ACTION = 2; |
43 | | - |
44 | | - |
45 | | - /// @notice Amount of tokens requested to be wrapped |
46 | | - /// @dev Almost always equals to zero |
47 | | - /// @dev Has non-zero value only before the callback call is made |
48 | | - mapping (address holder => uint256 value) public requestedMints; |
49 | | - |
50 | | - error OutdatedMint(address to, uint256 value); |
51 | | - error ZeroValue(); |
52 | | - |
| 32 | +contract ConfidentialWrapper is ConfidentialWrapperCore { |
53 | 33 | constructor( |
54 | 34 | IERC20Metadata underlyingToken, |
55 | 35 | string memory version_, |
56 | 36 | address initialAuthority |
57 | 37 | ) |
58 | | - ConfidentialToken( |
59 | | - string.concat("Confidential ", underlyingToken.name()), |
60 | | - string.concat("cnf", underlyingToken.symbol()), |
61 | | - version_, |
62 | | - initialAuthority |
63 | | - ) |
64 | | - ERC20Wrapper(underlyingToken) |
65 | | - {} |
66 | | - |
67 | | - // External functions |
68 | | - |
69 | | - /// @inheritdoc IConfidentialWrapper |
70 | | - function releaseTo(address account, uint256 value) external override { |
71 | | - requestedMints[_msgSender()] -= value; |
72 | | - underlying().safeTransfer(account, value); |
73 | | - } |
74 | | - |
75 | | - // Public functions |
76 | | - |
77 | | - ///@inheritdoc ConfidentialToken |
78 | | - function transferFrom( |
79 | | - address from, |
80 | | - address to, |
81 | | - uint256 value |
82 | | - ) public virtual override(ConfidentialToken, ERC20) returns (bool result) { |
83 | | - return ConfidentialToken.transferFrom(from, to, value); |
84 | | - } |
85 | | - |
86 | | - /// @inheritdoc ERC20Wrapper |
87 | | - /// @dev Pending mint accounting is keyed by the recipient `account`, so only |
88 | | - /// `account` can later call `releaseTo` for this deposit. |
89 | | - function depositFor(address account, uint256 value) public override returns (bool success) { |
90 | | - requestedMints[account] += value; |
91 | | - return super.depositFor(account, value); |
92 | | - } |
93 | | - |
94 | | - /// @inheritdoc ERC20Wrapper |
95 | | - /// @dev This operation is asynchronous and finalizes in the callback. On |
96 | | - /// success, underlying tokens are sent to `account`. |
97 | | - function withdrawTo(address account, uint256 value) public override returns (bool success) { |
98 | | - _burnTo(_msgSender(), account, value); |
99 | | - return true; |
100 | | - } |
101 | | - |
102 | | - /// @inheritdoc ERC20Wrapper |
103 | | - function decimals() public view override(ERC20, ERC20Wrapper) returns (uint8 decimalsValue) { |
104 | | - return ERC20Wrapper.decimals(); |
105 | | - } |
106 | | - |
107 | | - /// @inheritdoc ConfidentialToken |
108 | | - function totalSupply() public view override(ConfidentialToken, ERC20) returns (uint256 supply) { |
109 | | - return ConfidentialToken.totalSupply(); |
110 | | - } |
111 | | - |
112 | | - /// @inheritdoc ConfidentialToken |
113 | | - function balanceOf(address account) public pure override(ConfidentialToken, ERC20) returns (uint256 balance) { |
114 | | - return ConfidentialToken.balanceOf(account); |
115 | | - } |
116 | | - |
117 | | - // Internal functions |
118 | | - |
119 | | - /// @notice Dispatches decrypted CTX actions for wrapper-specific flows. |
120 | | - /// @dev Handles `_WITHDRAW_TO_ACTION` locally and delegates all other actions to |
121 | | - /// the base ConfidentialToken logic. |
122 | | - /// @param action Action discriminator encoded in callback plaintext. |
123 | | - /// @param decryptedArguments Decrypted callback arguments from BITE. |
124 | | - /// @param plaintextArguments Plaintext callback arguments used for routing. |
125 | | - function _handleAction( |
126 | | - uint8 action, |
127 | | - bytes[] calldata decryptedArguments, |
128 | | - bytes[] calldata plaintextArguments |
129 | | - ) internal override { |
130 | | - if (action == _WITHDRAW_TO_ACTION) { |
131 | | - _handleWithdrawToRequest(decryptedArguments, plaintextArguments); |
132 | | - return; |
133 | | - } |
134 | | - super._handleAction(action, decryptedArguments, plaintextArguments); |
135 | | - } |
136 | | - |
137 | | - /// @notice Schedules an async burn that releases underlying to `to` on callback. |
138 | | - /// @dev Encodes `to` as extra plaintext callback data for |
139 | | - /// `_handleWithdrawToRequest`. |
140 | | - /// @param from Address whose confidential balance is debited. |
141 | | - /// @param to Recipient of the released underlying token. |
142 | | - /// @param value Amount to burn and unwrap. |
143 | | - function _burnTo(address from, address to, uint256 value) internal { |
144 | | - if (to == address(0) || to == address(this)) { |
145 | | - revert ERC20InvalidReceiver(to); |
146 | | - } |
147 | | - require(value != 0, ZeroValue()); |
148 | | - bytes memory encryptedValue = BITE.encryptTE(encryptTEAddress, abi.encodePacked(value)); |
149 | | - bytes[] memory extraArgs = new bytes[](1); |
150 | | - extraArgs[0] = abi.encodePacked(to); |
151 | | - _encryptedUpdateExtended({ |
152 | | - from: from, |
153 | | - to: address(0), |
154 | | - spender: address(0), |
155 | | - gasPayer: _msgSender(), |
156 | | - encryptedValue: encryptedValue, |
157 | | - action: _WITHDRAW_TO_ACTION, |
158 | | - extraPlaintextArguments: extraArgs |
159 | | - }); |
160 | | - } |
161 | | - |
162 | | - function _onUpdate(address from, address to, uint256 value) internal override { |
163 | | - ConfidentialToken._onUpdate(from, to, value); |
164 | | - if (from == address(0)) { |
165 | | - _onMint(to, value); |
166 | | - } |
167 | | - } |
168 | | - |
169 | | - function _update(address from, address to, uint256 value) internal override(ConfidentialToken, ERC20) { |
170 | | - ConfidentialToken._update(from, to, value); |
171 | | - } |
172 | | - |
173 | | - // Private functions |
174 | | - |
175 | | - function _onMint(address to, uint256 value) private { |
176 | | - bool previouslyRequested; |
177 | | - (previouslyRequested, requestedMints[to]) = Math.trySub( |
178 | | - requestedMints[to], |
179 | | - value |
180 | | - ); |
181 | | - require(previouslyRequested, OutdatedMint(to, value)); |
182 | | - } |
183 | | - |
184 | | - /// @notice Finalizes a queued wrapper burn/withdraw callback. |
185 | | - /// @dev Validates recipient plaintext argument, finalizes encrypted transfer |
186 | | - /// accounting via `_handleTransferRequest`, then releases underlying. |
187 | | - /// @param decryptedArguments Decrypted callback arguments from BITE. |
188 | | - /// @param plaintextArguments Plaintext callback arguments containing recipient. |
189 | | - function _handleWithdrawToRequest( |
190 | | - bytes[] calldata decryptedArguments, |
191 | | - bytes[] calldata plaintextArguments |
192 | | - ) |
193 | | - private |
| 38 | + initializer |
194 | 39 | { |
195 | | - require(plaintextArguments.length > 2, WrongPlaintextFormat()); |
196 | | - require(plaintextArguments[2].length == 20, WrongPlaintextFormat()); |
197 | | - |
198 | | - (bool finalized, uint256 value) = _handleTransferRequest(decryptedArguments, plaintextArguments); |
199 | | - if (!finalized) { |
200 | | - return; |
201 | | - } |
202 | | - |
203 | | - address recipient = address(bytes20(plaintextArguments[2])); |
204 | | - underlying().safeTransfer(recipient, value); |
| 40 | + __ConfidentialWrapper_init(underlyingToken, version_, initialAuthority); |
205 | 41 | } |
206 | 42 | } |
0 commit comments