mirror of
https://github.com/Instadapp/fluid-contracts-public.git
synced 2024-07-29 21:57:37 +00:00
d7a58e88ff
ARB: deploy protocols
288 lines
13 KiB
Solidity
288 lines
13 KiB
Solidity
// SPDX-License-Identifier: BUSL-1.1
|
|
pragma solidity 0.8.21;
|
|
|
|
import { SSTORE2 } from "solmate/src/utils/SSTORE2.sol";
|
|
|
|
import { ErrorTypes } from "../../errorTypes.sol";
|
|
import { Error } from "../../error.sol";
|
|
import { IFluidVaultFactory } from "../../interfaces/iVaultFactory.sol";
|
|
|
|
import { LiquiditySlotsLink } from "../../../../libraries/liquiditySlotsLink.sol";
|
|
|
|
import { IFluidVaultT1 } from "../../interfaces/iVaultT1.sol";
|
|
import { FluidVaultT1 } from "../../vaultT1/coreModule/main.sol";
|
|
|
|
interface IERC20 {
|
|
function decimals() external view returns (uint8);
|
|
}
|
|
|
|
contract FluidVaultT1DeploymentLogic is Error {
|
|
address internal constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
|
|
|
/// @dev SSTORE2 pointer for the VaultT1 creation code. Stored externally to reduce factory bytecode (in 2 parts)
|
|
address internal immutable VAULT_T1_CREATIONCODE_ADDRESS_1;
|
|
address internal immutable VAULT_T1_CREATIONCODE_ADDRESS_2;
|
|
|
|
/// @notice address of liquidity contract
|
|
address public immutable LIQUIDITY;
|
|
|
|
/// @notice address of Admin implementation
|
|
address public immutable ADMIN_IMPLEMENTATION;
|
|
|
|
/// @notice address of Secondary implementation
|
|
address public immutable SECONDARY_IMPLEMENTATION;
|
|
|
|
/// @notice address of this contract
|
|
address public immutable ADDRESS_THIS;
|
|
|
|
/// @notice Emitted when a new vaultT1 is deployed.
|
|
/// @param vault The address of the newly deployed vault.
|
|
/// @param vaultId The id of the newly deployed vault.
|
|
/// @param supplyToken The address of the supply token.
|
|
/// @param borrowToken The address of the borrow token.
|
|
event VaultT1Deployed(
|
|
address indexed vault,
|
|
uint256 vaultId,
|
|
address indexed supplyToken,
|
|
address indexed borrowToken
|
|
);
|
|
|
|
constructor(address liquidity_, address vaultAdminImplementation_, address vaultSecondaryImplementation_) {
|
|
LIQUIDITY = liquidity_;
|
|
ADMIN_IMPLEMENTATION = vaultAdminImplementation_;
|
|
SECONDARY_IMPLEMENTATION = vaultSecondaryImplementation_;
|
|
|
|
// split storing creation code into two SSTORE2 pointers, because:
|
|
// due to contract code limits 24576 bytes is the maximum amount of data that can be written in a single pointer / key.
|
|
// Attempting to write more will result in failure.
|
|
// So by splitting in two parts we can make sure that the contract bytecode size can use up the full limit of 24576 bytes.
|
|
uint256 creationCodeLength_ = type(FluidVaultT1).creationCode.length;
|
|
VAULT_T1_CREATIONCODE_ADDRESS_1 = SSTORE2.write(
|
|
_bytesSlice(type(FluidVaultT1).creationCode, 0, creationCodeLength_ / 2)
|
|
);
|
|
// slice lengths:
|
|
// when even length, e.g. 250:
|
|
// part 1 = 0 -> 250 / 2, so 0 until 125 length, so 0 -> 125
|
|
// part 2 = 250 / 2 -> 250 - 250 / 2, so 125 until 125 length, so 125 -> 250
|
|
// when odd length: e.g. 251:
|
|
// part 1 = 0 -> 251 / 2, so 0 until 125 length, so 0 -> 125
|
|
// part 2 = 251 / 2 -> 251 - 251 / 2, so 125 until 126 length, so 125 -> 251
|
|
VAULT_T1_CREATIONCODE_ADDRESS_2 = SSTORE2.write(
|
|
_bytesSlice(
|
|
type(FluidVaultT1).creationCode,
|
|
creationCodeLength_ / 2,
|
|
creationCodeLength_ - creationCodeLength_ / 2
|
|
)
|
|
);
|
|
|
|
ADDRESS_THIS = address(this);
|
|
}
|
|
|
|
/// @notice Computes vaultT1 bytecode for the given supply token (`supplyToken_`) and borrow token (`borrowToken_`).
|
|
/// This will be called by the VaultFactory via .delegateCall
|
|
/// @param supplyToken_ The address of the supply token.
|
|
/// @param borrowToken_ The address of the borrow token.
|
|
/// @return vaultCreationBytecode_ Returns the bytecode of the new vault to deploy.
|
|
function vaultT1(
|
|
address supplyToken_,
|
|
address borrowToken_
|
|
) external returns (bytes memory vaultCreationBytecode_) {
|
|
if (address(this) == ADDRESS_THIS) revert FluidVaultError(ErrorTypes.VaultFactory__OnlyDelegateCallAllowed);
|
|
|
|
if (supplyToken_ == borrowToken_) revert FluidVaultError(ErrorTypes.VaultFactory__SameTokenNotAllowed);
|
|
|
|
IFluidVaultT1.ConstantViews memory constants_;
|
|
constants_.liquidity = LIQUIDITY;
|
|
constants_.factory = address(this);
|
|
constants_.adminImplementation = ADMIN_IMPLEMENTATION;
|
|
constants_.secondaryImplementation = SECONDARY_IMPLEMENTATION;
|
|
constants_.supplyToken = supplyToken_;
|
|
constants_.supplyDecimals = supplyToken_ != NATIVE_TOKEN ? IERC20(supplyToken_).decimals() : 18;
|
|
constants_.borrowToken = borrowToken_;
|
|
constants_.borrowDecimals = borrowToken_ != NATIVE_TOKEN ? IERC20(borrowToken_).decimals() : 18;
|
|
constants_.vaultId = IFluidVaultFactory(address(this)).totalVaults();
|
|
|
|
address vault_ = IFluidVaultFactory(address(this)).getVaultAddress(constants_.vaultId);
|
|
|
|
constants_ = _calculateLiquidityVaultSlots(constants_, vault_);
|
|
|
|
vaultCreationBytecode_ = abi.encodePacked(vaultT1CreationBytecode(), abi.encode(constants_));
|
|
|
|
emit VaultT1Deployed(vault_, constants_.vaultId, supplyToken_, borrowToken_);
|
|
|
|
return vaultCreationBytecode_;
|
|
}
|
|
|
|
/// @notice returns the stored VaultT1 creation bytecode
|
|
function vaultT1CreationBytecode() public view returns (bytes memory) {
|
|
return
|
|
_bytesConcat(SSTORE2.read(VAULT_T1_CREATIONCODE_ADDRESS_1), SSTORE2.read(VAULT_T1_CREATIONCODE_ADDRESS_2));
|
|
}
|
|
|
|
/// @dev Calculates the liquidity vault slots for the given supply token, borrow token, and vault (`vault_`).
|
|
/// @param constants_ Constants struct as used in Vault T1
|
|
/// @param vault_ The address of the vault.
|
|
/// @return liquidityVaultSlots_ Returns the calculated liquidity vault slots set in the `IFluidVaultT1.ConstantViews` struct.
|
|
function _calculateLiquidityVaultSlots(
|
|
IFluidVaultT1.ConstantViews memory constants_,
|
|
address vault_
|
|
) private pure returns (IFluidVaultT1.ConstantViews memory) {
|
|
constants_.liquiditySupplyExchangePriceSlot = LiquiditySlotsLink.calculateMappingStorageSlot(
|
|
LiquiditySlotsLink.LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT,
|
|
constants_.supplyToken
|
|
);
|
|
constants_.liquidityBorrowExchangePriceSlot = LiquiditySlotsLink.calculateMappingStorageSlot(
|
|
LiquiditySlotsLink.LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT,
|
|
constants_.borrowToken
|
|
);
|
|
constants_.liquidityUserSupplySlot = LiquiditySlotsLink.calculateDoubleMappingStorageSlot(
|
|
LiquiditySlotsLink.LIQUIDITY_USER_SUPPLY_DOUBLE_MAPPING_SLOT,
|
|
vault_,
|
|
constants_.supplyToken
|
|
);
|
|
constants_.liquidityUserBorrowSlot = LiquiditySlotsLink.calculateDoubleMappingStorageSlot(
|
|
LiquiditySlotsLink.LIQUIDITY_USER_BORROW_DOUBLE_MAPPING_SLOT,
|
|
vault_,
|
|
constants_.borrowToken
|
|
);
|
|
return constants_;
|
|
}
|
|
|
|
// @dev taken from https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol
|
|
function _bytesConcat(bytes memory _preBytes, bytes memory _postBytes) private pure returns (bytes memory) {
|
|
bytes memory tempBytes;
|
|
|
|
assembly {
|
|
// Get a location of some free memory and store it in tempBytes as
|
|
// Solidity does for memory variables.
|
|
tempBytes := mload(0x40)
|
|
|
|
// Store the length of the first bytes array at the beginning of
|
|
// the memory for tempBytes.
|
|
let length := mload(_preBytes)
|
|
mstore(tempBytes, length)
|
|
|
|
// Maintain a memory counter for the current write location in the
|
|
// temp bytes array by adding the 32 bytes for the array length to
|
|
// the starting location.
|
|
let mc := add(tempBytes, 0x20)
|
|
// Stop copying when the memory counter reaches the length of the
|
|
// first bytes array.
|
|
let end := add(mc, length)
|
|
|
|
for {
|
|
// Initialize a copy counter to the start of the _preBytes data,
|
|
// 32 bytes into its memory.
|
|
let cc := add(_preBytes, 0x20)
|
|
} lt(mc, end) {
|
|
// Increase both counters by 32 bytes each iteration.
|
|
mc := add(mc, 0x20)
|
|
cc := add(cc, 0x20)
|
|
} {
|
|
// Write the _preBytes data into the tempBytes memory 32 bytes
|
|
// at a time.
|
|
mstore(mc, mload(cc))
|
|
}
|
|
|
|
// Add the length of _postBytes to the current length of tempBytes
|
|
// and store it as the new length in the first 32 bytes of the
|
|
// tempBytes memory.
|
|
length := mload(_postBytes)
|
|
mstore(tempBytes, add(length, mload(tempBytes)))
|
|
|
|
// Move the memory counter back from a multiple of 0x20 to the
|
|
// actual end of the _preBytes data.
|
|
mc := end
|
|
// Stop copying when the memory counter reaches the new combined
|
|
// length of the arrays.
|
|
end := add(mc, length)
|
|
|
|
for {
|
|
let cc := add(_postBytes, 0x20)
|
|
} lt(mc, end) {
|
|
mc := add(mc, 0x20)
|
|
cc := add(cc, 0x20)
|
|
} {
|
|
mstore(mc, mload(cc))
|
|
}
|
|
|
|
// Update the free-memory pointer by padding our last write location
|
|
// to 32 bytes: add 31 bytes to the end of tempBytes to move to the
|
|
// next 32 byte block, then round down to the nearest multiple of
|
|
// 32. If the sum of the length of the two arrays is zero then add
|
|
// one before rounding down to leave a blank 32 bytes (the length block with 0).
|
|
mstore(
|
|
0x40,
|
|
and(
|
|
add(add(end, iszero(add(length, mload(_preBytes)))), 31),
|
|
not(31) // Round down to the nearest 32 bytes.
|
|
)
|
|
)
|
|
}
|
|
|
|
return tempBytes;
|
|
}
|
|
|
|
// @dev taken from https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol
|
|
function _bytesSlice(bytes memory _bytes, uint256 _start, uint256 _length) private pure returns (bytes memory) {
|
|
require(_length + 31 >= _length, "slice_overflow");
|
|
require(_bytes.length >= _start + _length, "slice_outOfBounds");
|
|
|
|
bytes memory tempBytes;
|
|
|
|
assembly {
|
|
switch iszero(_length)
|
|
case 0 {
|
|
// Get a location of some free memory and store it in tempBytes as
|
|
// Solidity does for memory variables.
|
|
tempBytes := mload(0x40)
|
|
|
|
// The first word of the slice result is potentially a partial
|
|
// word read from the original array. To read it, we calculate
|
|
// the length of that partial word and start copying that many
|
|
// bytes into the array. The first word we copy will start with
|
|
// data we don't care about, but the last `lengthmod` bytes will
|
|
// land at the beginning of the contents of the new array. When
|
|
// we're done copying, we overwrite the full first word with
|
|
// the actual length of the slice.
|
|
let lengthmod := and(_length, 31)
|
|
|
|
// The multiplication in the next line is necessary
|
|
// because when slicing multiples of 32 bytes (lengthmod == 0)
|
|
// the following copy loop was copying the origin's length
|
|
// and then ending prematurely not copying everything it should.
|
|
let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
|
|
let end := add(mc, _length)
|
|
|
|
for {
|
|
// The multiplication in the next line has the same exact purpose
|
|
// as the one above.
|
|
let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
|
|
} lt(mc, end) {
|
|
mc := add(mc, 0x20)
|
|
cc := add(cc, 0x20)
|
|
} {
|
|
mstore(mc, mload(cc))
|
|
}
|
|
|
|
mstore(tempBytes, _length)
|
|
|
|
//update free-memory pointer
|
|
//allocating the array padded to 32 bytes like the compiler does now
|
|
mstore(0x40, and(add(mc, 31), not(31)))
|
|
}
|
|
//if we want a zero-length slice let's just return a zero-length array
|
|
default {
|
|
tempBytes := mload(0x40)
|
|
//zero out the 32 bytes slice we are about to return
|
|
//we need to do it because Solidity does not garbage collect
|
|
mstore(tempBytes, 0)
|
|
|
|
mstore(0x40, add(tempBytes, 0x20))
|
|
}
|
|
}
|
|
|
|
return tempBytes;
|
|
}
|
|
}
|