Skip to content

Commit 0f028e1

Browse files
author
lukasz
committed
Make wrapper rely on underlying token shares balances
1 parent d59a149 commit 0f028e1

File tree

3 files changed

+682
-107
lines changed

3 files changed

+682
-107
lines changed

contracts/WrappedBackedTokenFactory.sol

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,16 @@ contract WrappedBackedTokenFactory is Ownable {
9898
);
9999

100100
bytes32 salt = keccak256(
101-
abi.encodePacked(configuration.name, configuration.symbol)
101+
abi.encodePacked(configuration.name, configuration.symbol, configuration.underlying)
102102
);
103103

104104
WrappedBackedTokenProxy newProxy = new WrappedBackedTokenProxy{salt: salt}(
105+
address(0),
106+
address(this),
107+
""
108+
);
109+
newProxy.upgradeToAndCall(
105110
address(wrappedTokenImplementation),
106-
address(proxyAdmin),
107111
abi.encodeWithSelector(
108112
bytes4(
109113
keccak256(
@@ -115,6 +119,7 @@ contract WrappedBackedTokenFactory is Ownable {
115119
configuration.underlying
116120
)
117121
);
122+
newProxy.changeAdmin(address(proxyAdmin));
118123

119124
WrappedBackedTokenImplementation newToken = WrappedBackedTokenImplementation(
120125
address(newProxy)

contracts/WrappedBackedTokenImplementation.sol

Lines changed: 76 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -39,22 +39,29 @@ pragma solidity 0.8.9;
3939
import "@openzeppelin/contracts-upgradeable-new/token/ERC20/extensions/ERC4626Upgradeable.sol";
4040
import "@openzeppelin/contracts-upgradeable-new/token/ERC20/extensions/ERC20PermitUpgradeable.sol";
4141
import "@openzeppelin/contracts-upgradeable-new/access/OwnableUpgradeable.sol";
42+
import "@openzeppelin/contracts-upgradeable-new/utils/math/MathUpgradeable.sol";
4243
import "./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

5762
contract 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

Comments
 (0)