@@ -39,22 +39,29 @@ pragma solidity 0.8.9;
3939import "@openzeppelin/contracts-upgradeable-new/token/ERC20/extensions/ERC4626Upgradeable.sol " ;
4040import "@openzeppelin/contracts-upgradeable-new/token/ERC20/extensions/ERC20PermitUpgradeable.sol " ;
4141import "@openzeppelin/contracts-upgradeable-new/access/OwnableUpgradeable.sol " ;
42+ import "@openzeppelin/contracts-upgradeable-new/utils/math/MathUpgradeable.sol " ;
4243import "./SanctionsList.sol " ;
44+ import "./interfaces/IBackedAutoFeeToken.sol " ;
4345
4446/**
4547 * @dev
4648 *
4749 * This token contract is following the ERC20 standard.
4850 * It inherits ERC4626Upgradeable, which extends the basic ERC20 to be a representation of changing underlying token.
51+ * The underlying token is expected to be an IBackedAutoFeeToken, that is a token with an auto fee mechanism,
52+ * and a multiplier that changes over time to reflect the fees and corporate actions applied.
53+ * It translates the shares of the underlying IBackedAutoFeeToken to an amount of assets, using the current multiplier of the underlying token.
54+ * The shares of the underlying token are kept in the contract, and the corresponding amount of this token is minted to the user.
4955 * Enforces Sanctions List via the Chainalysis standard interface.
5056 * The contract contains one role:
5157 * - A pauser, that can pause or restore all transfers in the contract.
5258 * - An owner, that can set the above, and also the sanctionsList pointer.
53- * The owner can also set who can use the EIP-712 functionality, either specific accounts via a whitelist, or everyone.
5459 *
5560 */
5661
5762contract WrappedBackedTokenImplementation is OwnableUpgradeable , ERC4626Upgradeable , ERC20PermitUpgradeable {
63+ using MathUpgradeable for uint256 ;
64+
5865 string constant public VERSION = "1.0.0 " ;
5966
6067 // Calculating the Delegated Transfer typehash:
@@ -80,16 +87,9 @@ contract WrappedBackedTokenImplementation is OwnableUpgradeable, ERC4626Upgradea
8087 // Events:
8188 event NewPauser (address indexed newPauser );
8289 event NewSanctionsList (address indexed newSanctionsList );
83- event DelegateWhitelistChange (address indexed whitelistAddress , bool status );
84- event DelegateModeChange (bool delegateMode );
8590 event PauseModeChange (bool pauseMode );
8691 event NewTerms (string newTerms );
8792
88- modifier allowedDelegate {
89- require (delegateMode || delegateWhitelist[_msgSender ()], "WrappedBackedToken: Unauthorized delegate " );
90- _;
91- }
92-
9393
9494 // constructor, call initializer to lock the implementation instance.
9595 constructor () {
@@ -111,21 +111,6 @@ contract WrappedBackedTokenImplementation is OwnableUpgradeable, ERC4626Upgradea
111111 return ERC4626Upgradeable .decimals ();
112112 }
113113
114- /**
115- * @inheritdoc IERC20PermitUpgradeable
116- */
117- function permit (
118- address owner ,
119- address spender ,
120- uint256 value ,
121- uint256 deadline ,
122- uint8 v ,
123- bytes32 r ,
124- bytes32 s
125- ) public virtual override allowedDelegate {
126- super .permit (owner, spender, value, deadline, v, r, s);
127- }
128-
129114 /**
130115 * @dev Delegated Transfer, transfer via a sign message, using erc712.
131116 */
@@ -137,7 +122,7 @@ contract WrappedBackedTokenImplementation is OwnableUpgradeable, ERC4626Upgradea
137122 uint8 v ,
138123 bytes32 r ,
139124 bytes32 s
140- ) external virtual allowedDelegate {
125+ ) external virtual {
141126 require (block .timestamp <= deadline, "ERC20Permit: expired deadline " );
142127
143128 bytes32 structHash = keccak256 (abi.encode (DELEGATED_TRANSFER_TYPEHASH, owner, to, value, _useNonce (owner), deadline));
@@ -192,35 +177,6 @@ contract WrappedBackedTokenImplementation is OwnableUpgradeable, ERC4626Upgradea
192177 emit NewSanctionsList (newSanctionsList);
193178 }
194179
195-
196- /**
197- * @dev EIP-712 Function to change the delegate status of account.
198- * Allowed only for owner
199- *
200- * Emits a { DelegateWhitelistChange } event
201- *
202- * @param whitelistAddress The address for which to change the delegate status
203- * @param status The new delegate status
204- */
205- function setDelegateWhitelist (address whitelistAddress , bool status ) external onlyOwner {
206- delegateWhitelist[whitelistAddress] = status;
207- emit DelegateWhitelistChange (whitelistAddress, status);
208- }
209-
210- /**
211- * @dev EIP-712 Function to change the contract delegate mode. Allowed
212- * only for owner
213- *
214- * Emits a { DelegateModeChange } event
215- *
216- * @param _delegateMode The new delegate mode for the contract
217- */
218- function setDelegateMode (bool _delegateMode ) external onlyOwner {
219- delegateMode = _delegateMode;
220-
221- emit DelegateModeChange (_delegateMode);
222- }
223-
224180 /**
225181 * @dev Function to change the contract terms. Allowed only for owner
226182 *
@@ -268,4 +224,71 @@ contract WrappedBackedTokenImplementation is OwnableUpgradeable, ERC4626Upgradea
268224 super ._spendAllowance (owner, spender, amount);
269225 }
270226
227+ /** @dev See {IERC4626-previewMint}.
228+ *
229+ * Amounts are rounded down, in order to accomodate multiplier math done on underlying token
230+ */
231+ function previewMint (uint256 shares ) public view virtual override returns (uint256 ) {
232+ return _convertToAssets (shares, MathUpgradeable.Rounding.Down);
233+ }
234+
235+ /** @dev See {IERC4626-previewWithdraw}.
236+ *
237+ * Amounts are rounded down, in order to accomodate multiplier math done on underlying token
238+ */
239+ function previewWithdraw (uint256 assets ) public view virtual override returns (uint256 ) {
240+ return _convertToShares (assets, MathUpgradeable.Rounding.Down);
241+ }
242+
243+ /**
244+ * @dev Internal conversion function (from assets to shares) with support for rounding direction.
245+ */
246+ function _convertToShares (uint256 assets , MathUpgradeable.Rounding rounding ) internal view virtual override returns (uint256 ) {
247+ (uint256 currentMultiplier , ,) = IBackedAutoFeeToken (asset ()).getCurrentMultiplier ();
248+ return assets.mulDiv (1e18 , currentMultiplier, rounding);
249+ }
250+
251+ /**
252+ * @dev Internal conversion function (from shares to assets) with support for rounding direction.
253+ */
254+ function _convertToAssets (uint256 shares , MathUpgradeable.Rounding rounding ) internal view virtual override returns (uint256 ) {
255+ (uint256 currentMultiplier , ,) = IBackedAutoFeeToken (asset ()).getCurrentMultiplier ();
256+ return shares.mulDiv (currentMultiplier, 1e18 , rounding);
257+ }
258+
259+ /**
260+ * @dev Deposit/mint common workflow, adjusted to the fact, that wrapper token is keeping the shares of the underlying token and not the underlying tokens themselves,
261+ * so it needs to transfer the shares from the user, and not do erc20 transfers as in a normal ERC4626 implementation.
262+ */
263+ function _deposit (address caller , address receiver , uint256 assetsRequested , uint256 shares ) internal virtual override {
264+ IBackedAutoFeeToken assetToken = IBackedAutoFeeToken (asset ());
265+ assetToken.transferSharesFrom (caller, address (this ), shares);
266+ _mint (receiver, shares);
267+
268+ uint256 assets = convertToAssets (shares);
269+ emit Deposit (caller, receiver, assets, shares);
270+ }
271+
272+ /**
273+ * @dev Withdraw/redeem common workflow, adjusted to the fact, that wrapper token is keeping the shares of the underlying token and not the underlying tokens themselves,
274+ * so it needs to transfer the shares to the user, and not do erc20 transfers as in a normal ERC4626 implementation.
275+ */
276+ function _withdraw (
277+ address caller ,
278+ address receiver ,
279+ address owner ,
280+ uint256 assetsRequested ,
281+ uint256 shares
282+ ) internal virtual override {
283+ if (caller != owner) {
284+ _spendAllowance (owner, caller, shares);
285+ }
286+
287+ _burn (owner, shares);
288+ IBackedAutoFeeToken assetToken = IBackedAutoFeeToken (asset ());
289+ assetToken.transferShares (receiver, shares);
290+
291+ uint256 assets = convertToAssets (shares);
292+ emit Withdraw (caller, receiver, owner, assets, shares);
293+ }
271294}
0 commit comments