Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
480 changes: 246 additions & 234 deletions .gas-snapshot

Large diffs are not rendered by default.

96 changes: 0 additions & 96 deletions script/Deploy.sol

This file was deleted.

70 changes: 17 additions & 53 deletions src/abstract/ReceiptVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import {
LibFixedPointDecimalArithmeticOpenZeppelin,
Math
} from "rain.math.fixedpoint/lib/LibFixedPointDecimalArithmeticOpenZeppelin.sol";
import {ICloneableFactoryV2} from "rain.factory/interface/ICloneableFactoryV2.sol";
//forge-lint: disable-next-line(unused-import)
// Export ICLONEABLE_V2_SUCCESS for concrete implementations.
// forge-lint: disable-next-line(unused-import)
import {ICloneableV2, ICLONEABLE_V2_SUCCESS} from "rain.factory/interface/ICloneableV2.sol";
import {Address} from "openzeppelin-contracts/contracts/utils/Address.sol";
import {
Expand Down Expand Up @@ -43,35 +43,17 @@ enum ShareAction {
Burn
}

/// Config for the _implementation_ of the `ReceiptVault` contract.
/// @param factory The factory that will be used to clone the receipt vault.
/// @param receiptImplementation The receipt implementation that will be cloned
/// by the factory.
struct ReceiptVaultConstructionConfigV2 {
ICloneableFactoryV2 factory;
IReceiptV3 receiptImplementation;
}

/// All config required to initialize `ReceiptVault` except the receipt address.
/// Included as a field on `ReceiptVaultConfig` which is the full initialization
/// config struct. This is used by the `ReceiptVaultFactory` which will create a
/// new receipt in the same transaction and build the full `ReceiptVaultConfig`.
/// All config required to initialize `ReceiptVault`.
/// @param receipt The `Receipt` e.g. built by `ReceiptVaultFactory` that is
/// owned by the `ReceiptVault` as an `IReceiptOwnerV1`.
/// @param asset As per ERC4626.
/// @param name As per ERC20.
/// @param symbol As per ERC20.
struct VaultConfig {
struct ReceiptVaultConfigV2 {
address asset;
string name;
string symbol;
}

/// All config required to initialize `ReceiptVault`.
/// @param receipt The `Receipt` e.g. built by `ReceiptVaultFactory` that is
/// owned by the `ReceiptVault` as an `IReceiptOwnerV1`.
/// @param vaultConfig all the vault configuration as `VaultConfig`.
struct ReceiptVaultConfig {
address receipt;
VaultConfig vaultConfig;
}

/// @title ReceiptVault
Expand Down Expand Up @@ -104,12 +86,13 @@ struct ReceiptVaultConfig {
/// with the shares' mint event DO NOT MOVE (whatever that means) until/unless
/// those shares are burned.
///
/// Each vault is deployed from a factory as a clone from a reference
/// implementation, allowing for the model to cheaply and freedomly scale
/// horizontally. This allows for some trust/permissioned concessions to be made
/// per-vault as new competing vaults can always be deployed and traded against
/// each other in parallel, allowing trust to be "policed" at the liquidity and
/// free market layer.
/// Each vault is designed to be an implementation for proxies, either clones,
/// beacons or transparent proxies, allowing for the model to cheaply and freely
/// scale horizontally. The deployer is responsible for correctly initializing
/// the proxies either way. This allows for some trust/permissioned concessions
/// to be made per-vault as new competing vaults can always be deployed and
/// traded against each other in parallel, allowing trust to be "policed" at the
/// liquidity and free market layer.
abstract contract ReceiptVault is
IReceiptManagerV2,
IReceiptVaultV3,
Expand All @@ -122,11 +105,6 @@ abstract contract ReceiptVault is
using LibFixedPointDecimalArithmeticOpenZeppelin for uint256;
using SafeERC20 for IERC20;

//slither-disable-next-line naming-convention
ICloneableFactoryV2 internal immutable I_FACTORY;
//slither-disable-next-line naming-convention
IReceiptV3 internal immutable I_RECEIPT_IMPLEMENTATION;

/// @param asset Underlying ERC4626 asset.
/// @param receipt ERC1155 Receipt owned by this receipt vault for the
/// purpose of tracking mints and enforcing integrity of subsequent burns.
Expand All @@ -143,14 +121,8 @@ abstract contract ReceiptVault is
}
}

/// `ReceiptVault` is intended to be cloned and initialized by a
/// `ReceiptVaultFactory` so is an implementation contract that can't itself
/// be initialized.
constructor(ReceiptVaultConstructionConfigV2 memory config) {
constructor() {
_disableInitializers();

I_FACTORY = config.factory;
I_RECEIPT_IMPLEMENTATION = config.receiptImplementation;
}

/// Deposits are payable so this allows refunds.
Expand All @@ -164,23 +136,15 @@ abstract contract ReceiptVault is
// solhint-disable-next-line func-name-mixedcase
// slither-disable-start naming-convention
// forge-lint: disable-next-line(mixed-case-function)
function __ReceiptVault_init(VaultConfig memory config) internal virtual {
function __ReceiptVault_init(ReceiptVaultConfigV2 memory config) internal virtual {
__Multicall_init();
__ERC20_init(config.name, config.symbol);

// Slither false positive here due to it being impossible to set the
// receipt before it has been deployed.
// slither-disable-next-line reentrancy-benign
IReceiptV3 managedReceipt =
IReceiptV3(I_FACTORY.clone(address(I_RECEIPT_IMPLEMENTATION), abi.encode(address(this))));

ReceiptVaultV17201Storage storage s = getStorageReceiptVault();
s.asset = IERC20(config.asset);
s.receipt = managedReceipt;
s.receipt = IReceiptV3(config.receipt);

// Sanity check here. Should always be true as we cloned the receipt
// from the factory ourselves just above.
address receiptManager = managedReceipt.manager();
address receiptManager = IReceiptV3(config.receipt).manager();
if (receiptManager != address(this)) {
revert WrongManager(address(this), receiptManager);
}
Expand Down
90 changes: 90 additions & 0 deletions src/concrete/deploy/ERC20PriceOracleReceiptVaultCloneDeployer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: LicenseRef-DCL-1.0
// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd
pragma solidity =0.8.25;

import {
ZeroReceiptImplementation,
ZeroVaultImplementation,
InitializeNonZeroReceipt,
InitializeReceiptFailed,
InitializeVaultFailed
} from "../../error/ErrDeployer.sol";
import {Clones} from "openzeppelin-contracts/contracts/proxy/Clones.sol";
import {Receipt, ICLONEABLE_V2_SUCCESS} from "../receipt/Receipt.sol";
import {
ERC20PriceOracleReceiptVault,
ERC20PriceOracleReceiptVaultConfigV2
} from "../vault/ERC20PriceOracleReceiptVault.sol";

/// Configuration for the ERC20PriceOracleReceiptVaultCloneDeployer construction.
/// @param receiptImplementation The address of the Receipt implementation
/// contract to clone from.
/// @param erc20PriceOracleReceiptVaultImplementation The address of the
/// ERC20PriceOracleReceiptVault implementation contract to clone from.
//forge-lint: disable-next-line(pascal-case-struct)
struct ERC20PriceOracleReceiptVaultCloneDeployerConfig {
address receiptImplementation;
address erc20PriceOracleReceiptVaultImplementation;
}

/// @title ERC20PriceOracleReceiptVaultCloneDeployer
/// Deploys ERC20PriceOracleReceiptVault contracts as minimal proxy contracts
/// and handles the necessary initialization atomically.
contract ERC20PriceOracleReceiptVaultCloneDeployer {
/// Emitted when a new deployment is successfully initialized.
/// @param sender The address that initiated the deployment.
/// @param erc20PriceOracleReceiptVault The address of the deployed
/// ERC20PriceOracleReceiptVault contract.
/// @param receipt The address of the deployed Receipt contract.
event ERC20PriceOracleReceiptVaultCloneDeployerDeployment(
address sender, address erc20PriceOracleReceiptVault, address receipt
);

/// The address of the Receipt implementation contract to clone from.
address public immutable I_RECEIPT_IMPLEMENTATION;

/// The address of the ERC20PriceOracleReceiptVault implementation contract
/// to clone from.
address public immutable I_ERC20_PRICE_ORACLE_RECEIPT_VAULT_IMPLEMENTATION;

/// @param config The configuration for the deployer.
constructor(ERC20PriceOracleReceiptVaultCloneDeployerConfig memory config) {
if (config.receiptImplementation == address(0)) revert ZeroReceiptImplementation();
if (config.erc20PriceOracleReceiptVaultImplementation == address(0)) revert ZeroVaultImplementation();
I_RECEIPT_IMPLEMENTATION = config.receiptImplementation;
I_ERC20_PRICE_ORACLE_RECEIPT_VAULT_IMPLEMENTATION = config.erc20PriceOracleReceiptVaultImplementation;
}

/// Deploys and initializes a new ERC20PriceOracleReceiptVault contract
/// along with its associated Receipt contract.
/// @param config The configuration for the ERC20PriceOracleReceiptVault.
/// @return The address of the newly deployed ERC20PriceOracleReceiptVault
/// contract.
function newERC20PriceOracleReceiptVault(ERC20PriceOracleReceiptVaultConfigV2 memory config)
external
returns (ERC20PriceOracleReceiptVault)
{
if (config.receiptVaultConfig.receipt != address(0)) {
revert InitializeNonZeroReceipt(config.receiptVaultConfig.receipt);
}

Receipt receipt = Receipt(Clones.clone(I_RECEIPT_IMPLEMENTATION));
ERC20PriceOracleReceiptVault erc20PriceOracleReceiptVault =
ERC20PriceOracleReceiptVault(payable(Clones.clone(I_ERC20_PRICE_ORACLE_RECEIPT_VAULT_IMPLEMENTATION)));

if (receipt.initialize(abi.encode(erc20PriceOracleReceiptVault)) != ICLONEABLE_V2_SUCCESS) {
revert InitializeReceiptFailed();
}

config.receiptVaultConfig.receipt = address(receipt);
if (erc20PriceOracleReceiptVault.initialize(abi.encode(config)) != ICLONEABLE_V2_SUCCESS) {
revert InitializeVaultFailed();
}

emit ERC20PriceOracleReceiptVaultCloneDeployerDeployment(
msg.sender, address(erc20PriceOracleReceiptVault), address(receipt)
);

return erc20PriceOracleReceiptVault;
}
}
Loading
Loading