Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion common/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -723,7 +723,6 @@ export interface IConfig {
warmupPeriod: BigNumber
reweightable: boolean
enableIssuancePremium: boolean
enablePermissionlessRefresh: boolean
tradingDelay: BigNumber
batchAuctionLength: BigNumber
dutchAuctionLength: BigNumber
Expand Down
16 changes: 16 additions & 0 deletions contracts/interfaces/IAssetRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ struct Registry {
* 3. The asset can be priced in the UoA, usually via an oracle
*/
interface IAssetRegistry is IComponent {
error IAssetRegistry__CannotRegisterRToken();
error IAssetRegistry__CannotSwapRToken();
error IAssetRegistry__NoERC20Collision();
error IAssetRegistry__CannotUnregisterRToken();
error IAssetRegistry__NoAssetToUnregister();
error IAssetRegistry__AssetNotFound();
error IAssetRegistry__ERC20Unregistered();
error IAssetRegistry__ERC20NotCollateral();
error IAssetRegistry__UnsupportedAsset();

/// Emitted when an asset is added to the registry
/// @param erc20 The ERC20 contract for the asset
/// @param asset The asset contract added to the registry
Expand All @@ -37,6 +47,12 @@ interface IAssetRegistry is IComponent {
/// @custom:refresher
function refresh() external;

/// Register a new JIT-deployed RTokenAsset instance
/// @param maxTradeVolume {UoA} The maximum trade volume for the RTokenAsset
/// @return swapped If the asset was swapped for a previously-registered asset
/// @custom:governance
function registerRTokenAsset(uint192 maxTradeVolume) external returns (bool swapped);

/// Register `asset`
/// If either the erc20 address or the asset was already registered, fail
/// @return true if the erc20 address was not already registered.
Expand Down
4 changes: 0 additions & 4 deletions contracts/interfaces/IDeployer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,5 @@ interface IDeployer is IVersioned {
Registries calldata registries
) external returns (address);

/// Deploys a new RTokenAsset instance. Not needed during normal deployment flow
/// @param maxTradeVolume {UoA} The maximum trade volume for the RTokenAsset
function deployRTokenAsset(IRToken rToken, uint192 maxTradeVolume) external returns (IAsset);

function implementations() external view returns (Implementations memory);
}
16 changes: 16 additions & 0 deletions contracts/p0/AssetRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "../plugins/assets/RTokenAsset.sol";
import "../interfaces/IMain.sol";
import "./mixins/Component.sol";

Expand Down Expand Up @@ -42,10 +43,23 @@
lastRefresh = uint48(block.timestamp);
}

/// Register a new JIT-deployed RTokenAsset instance
/// @param maxTradeVolume {UoA} The maximum trade volume for the RTokenAsset
/// @return swapped If the asset was swapped for a previously-registered asset
/// @custom:governance
function registerRTokenAsset(uint192 maxTradeVolume)
external
governance
returns (bool swapped)
{
return _registerIgnoringCollisions(new RTokenAsset(main.rToken(), maxTradeVolume));
}

/// Forbids registering a different asset for an ERC20 that is already registered
/// @return If the asset was moved from unregistered to registered
/// @custom:governance
function register(IAsset asset) external governance returns (bool) {
require(address(asset.erc20()) != address(main.rToken()), "cannot register RToken");
return _register(asset);
}

Expand All @@ -54,6 +68,7 @@
/// @return swapped If the asset was swapped for a previously-registered asset
/// @custom:governance
function swapRegistered(IAsset asset) external governance returns (bool swapped) {
require(address(asset.erc20()) != address(main.rToken()), "cannot swap RToken");
require(_erc20s.contains(address(asset.erc20())), "no ERC20 collision");
assert(assets[asset.erc20()] != IAsset(address(0)));

Expand All @@ -70,6 +85,7 @@
/// Unregister an asset, requiring that it is already registered
/// @custom:governance
function unregister(IAsset asset) external governance {
require(address(asset.erc20()) != address(main.rToken()), "cannot unregister RToken");
require(_erc20s.contains(address(asset.erc20())), "no asset to unregister");
require(assets[asset.erc20()] == asset, "asset not found");

Expand Down Expand Up @@ -125,7 +141,7 @@
assert(reg.erc20s.length == reg.assets.length);
}

function validateCurrentAssets() external view {}

Check warning on line 144 in contracts/p0/AssetRegistry.sol

View workflow job for this annotation

GitHub Actions / Lint Checks

Code contains empty blocks

/// @return The number of registered ERC20s
function size() external view returns (uint256) {
Expand Down
83 changes: 62 additions & 21 deletions contracts/p1/AssetRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity 0.8.19;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "../plugins/assets/RTokenAsset.sol";
import "../interfaces/IAssetRegistry.sol";
import "../interfaces/IMain.sol";
import "./mixins/Component.sol";
Expand Down Expand Up @@ -68,6 +69,18 @@ contract AssetRegistryP1 is ComponentP1, IAssetRegistry {
lastRefresh = uint48(block.timestamp); // safer to do this at end than start, actually
}

/// Register a new JIT-deployed RTokenAsset instance
/// @param maxTradeVolume {UoA} The maximum trade volume for the RTokenAsset
/// @return swapped If the asset was swapped for a previously-registered asset
/// @custom:governance
function registerRTokenAsset(uint192 maxTradeVolume)
external
governance
returns (bool swapped)
{
swapped = _registerIgnoringCollisions(new RTokenAsset(main.rToken(), maxTradeVolume));
}

/// Register `asset`
/// If either the erc20 address or the asset was already registered, fail
/// @return true if the erc20 address was not already registered.
Expand All @@ -76,6 +89,10 @@ contract AssetRegistryP1 is ComponentP1, IAssetRegistry {
// effects: assets' = assets.set(asset.erc20(), asset)
// returns: (asset.erc20 not in keys(assets))
function register(IAsset asset) external governance returns (bool) {
if (address(asset.erc20()) == address(main.rToken())) {
revert IAssetRegistry__CannotRegisterRToken();
}

return _register(asset);
}

Expand All @@ -88,7 +105,13 @@ contract AssetRegistryP1 is ComponentP1, IAssetRegistry {
// effects: assets' = assets + {asset.erc20(): asset}
// actions: if asset.erc20() is in basketHandler's basket then basketHandler.disableBasket()
function swapRegistered(IAsset asset) external governance returns (bool swapped) {
require(_erc20s.contains(address(asset.erc20())), "no ERC20 collision");
if (address(asset.erc20()) == address(main.rToken())) {
revert IAssetRegistry__CannotSwapRToken();
}

if (!_erc20s.contains(address(asset.erc20()))) {
revert IAssetRegistry__NoERC20Collision();
}

try basketHandler.quantity{ gas: _reserveGas() }(asset.erc20()) returns (uint192 quantity) {
if (quantity != 0) basketHandler.disableBasket(); // not an interaction
Expand All @@ -106,8 +129,17 @@ contract AssetRegistryP1 is ComponentP1, IAssetRegistry {
// checks: assets[asset.erc20()] == asset
// effects: assets' = assets - {asset.erc20():_} + {asset.erc20(), asset}
function unregister(IAsset asset) external governance {
require(_erc20s.contains(address(asset.erc20())), "no asset to unregister");
require(assets[asset.erc20()] == asset, "asset not found");
if (address(asset.erc20()) == address(main.rToken())) {
revert IAssetRegistry__CannotUnregisterRToken();
}

if (!_erc20s.contains(address(asset.erc20()))) {
revert IAssetRegistry__NoAssetToUnregister();
}

if (assets[asset.erc20()] != asset) {
revert IAssetRegistry__AssetNotFound();
}

try basketHandler.quantity{ gas: _reserveGas() }(asset.erc20()) returns (uint192 quantity) {
if (quantity != 0) basketHandler.disableBasket(); // not an interaction
Expand All @@ -124,16 +156,25 @@ contract AssetRegistryP1 is ComponentP1, IAssetRegistry {
// checks: erc20 in assets
// returns: assets[erc20]
function toAsset(IERC20 erc20) external view returns (IAsset) {
require(_erc20s.contains(address(erc20)), "erc20 unregistered");
if (!_erc20s.contains(address(erc20))) {
revert IAssetRegistry__ERC20Unregistered();
}

return assets[erc20];
}

/// Return the Collateral registered for erc20; revert if erc20 is not registered as Collateral
// checks: erc20 in assets, assets[erc20].isCollateral()
// returns: assets[erc20]
function toColl(IERC20 erc20) external view returns (ICollateral) {
require(_erc20s.contains(address(erc20)), "erc20 unregistered");
require(assets[erc20].isCollateral(), "erc20 is not collateral");
if (!_erc20s.contains(address(erc20))) {
revert IAssetRegistry__ERC20Unregistered();
}

if (!assets[erc20].isCollateral()) {
revert IAssetRegistry__ERC20NotCollateral();
}

return ICollateral(address(assets[erc20]));
}

Expand Down Expand Up @@ -175,14 +216,11 @@ contract AssetRegistryP1 is ComponentP1, IAssetRegistry {
uint256 assetLen = registry.assets.length;
for (uint256 i = 0; i < assetLen; ++i) {
IAsset asset = registry.assets[i];
bytes32 versionHash = keccak256(abi.encodePacked(this.version()));

require(
assetPluginRegistry.isValidAsset(
keccak256(abi.encodePacked(this.version())),
address(asset)
),
"unsupported asset"
);
if (!assetPluginRegistry.isValidAsset(versionHash, address(asset))) {
revert IAssetRegistry__UnsupportedAsset();
}
}
}
}
Expand All @@ -192,6 +230,8 @@ contract AssetRegistryP1 is ComponentP1, IAssetRegistry {
return _erc20s.length();
}

// === Internal ===

/// Register an asset
/// Forbids registering a different asset for an ERC20 that is already registered
/// @return registered If the asset was moved from unregistered to registered
Expand Down Expand Up @@ -219,14 +259,15 @@ contract AssetRegistryP1 is ComponentP1, IAssetRegistry {
}

AssetPluginRegistry assetPluginRegistry = main.assetPluginRegistry();
if (address(assetPluginRegistry) != address(0)) {
require(
main.assetPluginRegistry().isValidAsset(
keccak256(abi.encodePacked(this.version())),
address(asset)
),
"unsupported asset"
);
if (
address(assetPluginRegistry) != address(0) &&
address(asset.erc20()) != address(main.rToken())
) {
bytes32 versionHash = keccak256(abi.encodePacked(this.version()));

if (!assetPluginRegistry.isValidAsset(versionHash, address(asset))) {
revert IAssetRegistry__UnsupportedAsset();
}
}

IERC20Metadata erc20 = asset.erc20();
Expand Down
31 changes: 11 additions & 20 deletions contracts/p1/Deployer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import "../interfaces/IRToken.sol";
import "../interfaces/IStRSR.sol";
import "../mixins/Versioned.sol";
import "../plugins/assets/Asset.sol";
import "../plugins/assets/RTokenAsset.sol";
import "./Main.sol";
import "../libraries/String.sol";
import "../plugins/trading/GnosisTrade.sol";
Expand Down Expand Up @@ -251,14 +250,6 @@ contract DeployerP1 is IDeployer, Versioned {
params.redemptionThrottle
);

// Deploy RToken/RSR Assets
IAsset[] memory assets = new IAsset[](2);
assets[0] = new RTokenAsset(components.rToken, params.rTokenMaxTradeVolume);
assets[1] = rsrAsset;

// Init Asset Registry
components.assetRegistry.init(main, assets);

// Assign DAO Registries
if (address(registries.versionRegistry) != address(0)) {
main.setVersionRegistry(registries.versionRegistry);
Expand All @@ -270,6 +261,17 @@ contract DeployerP1 is IDeployer, Versioned {
main.setDAOFeeRegistry(registries.daoFeeRegistry);
}

// Register RSR Asset
IAsset[] memory assets = new IAsset[](1);
assets[0] = rsrAsset;
components.assetRegistry.init(main, assets);

// Register RToken Asset
require(
components.assetRegistry.registerRTokenAsset(params.rTokenMaxTradeVolume),
"RTokenAsset already registered"
);

// Transfer Ownership
main.grantRole(OWNER, owner);
main.renounceRole(OWNER, address(this));
Expand All @@ -278,15 +280,4 @@ contract DeployerP1 is IDeployer, Versioned {

return (address(components.rToken));
}

/// Deploys a new RTokenAsset instance. Not needed during normal deployment flow
/// @param maxTradeVolume {UoA} The maximum trade volume for the RTokenAsset
/// @return rTokenAsset The address of the newly deployed RTokenAsset
function deployRTokenAsset(IRToken rToken, uint192 maxTradeVolume)
external
returns (IAsset rTokenAsset)
{
rTokenAsset = new RTokenAsset(rToken, maxTradeVolume);
emit RTokenAssetCreated(rToken, rTokenAsset);
}
}
2 changes: 1 addition & 1 deletion contracts/registry/VersionRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { RoleRegistry } from "./RoleRegistry.sol";
/**
* @title VersionRegistry
* @notice A tiny contract for tracking deployment versions
* All versions registered are expected to include veRSR, so effectively 4.0.0+.
* All versions registered are expected to be >=4.0.0
*/
contract VersionRegistry {
mapping(bytes32 => IDeployer) public deployments;
Expand Down
9 changes: 8 additions & 1 deletion contracts/spells/3_4_0.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ interface ICachedComponent {
function cacheComponents() external;
}

// backwards compatibility: deployRTokenAsset was removed in 4.0.0
interface IDeployer3_4_0 is IDeployer {
function deployRTokenAsset(IRToken rToken, uint192 maxTradeVolume)
external
returns (IAsset rTokenAsset);
}

/**
* The upgrade contract for the 3.4.0 release. Each spell function can only be cast once per RToken.
*
Expand Down Expand Up @@ -415,7 +422,7 @@ contract Upgrade3_4_0 {

// RTokenAsset
proxy.assetRegistry.swapRegistered(
deployer.deployRTokenAsset(
IDeployer3_4_0(address(deployer)).deployRTokenAsset(
rToken,
proxy.assetRegistry.toAsset(IERC20(address(rToken))).maxTradeVolume()
)
Expand Down
Loading
Loading