Skip to content

Commit

Permalink
Add upgrade changes
Browse files Browse the repository at this point in the history
  • Loading branch information
nickkatsios committed May 7, 2024
1 parent 07b2cd2 commit ca995d7
Show file tree
Hide file tree
Showing 6 changed files with 385 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
cache/
out/

.vscode

# Ignores development broadcast logs
/broadcast

Expand Down
14 changes: 14 additions & 0 deletions mocks/wells/MockWellUpgradeable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {WellUpgradeable} from "src/WellUpgradeable.sol";

// this needs to be here for upgrade checks
/// @custom:oz-upgrades-from WellUpgradeable
contract MockWellUpgradeable is WellUpgradeable {

function getVersion(uint256 i) external pure returns (uint256) {
return i;
}
}
27 changes: 27 additions & 0 deletions script/helpers/WellDeployer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {LibContractInfo} from "src/libraries/LibContractInfo.sol";
import {LibWellConstructor} from "src/libraries/LibWellConstructor.sol";
import {Well, Call, IERC20} from "src/Well.sol";
import {Aquifer} from "src/Aquifer.sol";
import {WellUpgradeable} from "src/WellUpgradeable.sol";
import {LibWellUpgradeableConstructor} from "src/libraries/LibWellUpgradeableConstructor.sol";

abstract contract WellDeployer {
/**
Expand All @@ -28,4 +30,29 @@ abstract contract WellDeployer {
LibWellConstructor.encodeWellDeploymentData(_aquifer, _tokens, _wellFunction, _pumps);
_well = Well(Aquifer(_aquifer).boreWell(_wellImplementation, immutableData, initData, _salt));
}


/**
* @notice Encode the Well's immutable data, and deploys the well. Modified for upgradeable wells.
* @param _aquifer The address of the Aquifer which will deploy this Well.
* @param _wellImplementation The address of the Well implementation.
* @param _tokens A list of ERC20 tokens supported by the Well.
* @param _wellFunction A single Call struct representing a call to the Well Function.
* @param _pumps An array of Call structs representings calls to Pumps.
* @param _salt The salt to deploy the Well with (`bytes32(0)` for none). See {LibClone}.
* @param owner The address of the owner of the Well.
*/
function encodeAndBoreWellUpgradeable(
address _aquifer,
address _wellImplementation,
IERC20[] memory _tokens,
Call memory _wellFunction,
Call[] memory _pumps,
bytes32 _salt,
address owner
) internal returns (WellUpgradeable _well) {
(bytes memory immutableData, bytes memory initData) =
LibWellUpgradeableConstructor.encodeWellDeploymentData(_aquifer, _tokens, _wellFunction, _pumps, owner);
_well = WellUpgradeable(Aquifer(_aquifer).boreWell(_wellImplementation, immutableData, initData, _salt));
}
}
69 changes: 69 additions & 0 deletions src/WellUpgradeable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {Well} from "src/Well.sol";
import {UUPSUpgradeable} from "ozu/proxy/utils/UUPSUpgradeable.sol";
import {OwnableUpgradeable} from "ozu/access/OwnableUpgradeable.sol";
import {IERC20, SafeERC20} from "oz/token/ERC20/utils/SafeERC20.sol";


/**
* @title Well
* @author Publius, Silo Chad, Brean, Deadmanwalking
* @dev A Well is a constant function AMM allowing the provisioning of liquidity
* into a single pooled on-chain liquidity position.
*
* Rebasing Tokens:
* - Positive rebasing tokens are supported by Wells, but any tokens recieved from a
* rebase will not be rewarded to LP holders and instead can be extracted by anyone
* using `skim`, `sync` or `shift`.
* - Negative rebasing tokens should not be used in Well as the effect of a negative
* rebase will be realized by users interacting with the Well, not LP token holders.
*
* Fee on Tranfer (FoT) Tokens:
* - When transferring fee on transfer tokens to a Well (swapping from or adding liquidity),
* use `swapFromFeeOnTrasfer` or `addLiquidityFeeOnTransfer`. `swapTo` does not support
* fee on transfer tokens (See {swapTo}).
* - When recieving fee on transfer tokens from a Well (swapping to and removing liquidity),
* INCLUDE the fee that is taken on transfer when calculating amount out values.
*/
contract WellUpgradeable is Well, UUPSUpgradeable, OwnableUpgradeable {

/**
* Perform an upgrade of an ERC1967Proxy, when this contract.
* is set as the implementation behind such a proxy.
* The _authorizeUpgrade function must be overridden.
* to include access restriction to the upgrade mechanism.
*/
function _authorizeUpgrade(address) internal override onlyOwner {}

function init(string memory _name, string memory _symbol, address owner) external initializer {
// owner of well param as the aquifier address will be the owner initially
// ownable init transfers ownership to msg.sender
__ERC20Permit_init(_name);
__ERC20_init(_name, _symbol);
__ReentrancyGuard_init();
__UUPSUpgradeable_init();
// first time this is called the owner will be the msg.sender
// which is the aquifer that bore the well
__Ownable_init();
// then ownership can be transfered to the wanted address
// note: to init owner with __Ownable_init(ownerAddress); we would need to adjust the lib code
transferOwnership(owner);

IERC20[] memory _tokens = tokens();
uint256 tokensLength = _tokens.length;
for (uint256 i; i < tokensLength - 1; ++i) {
for (uint256 j = i + 1; j < tokensLength; ++j) {
if (_tokens[i] == _tokens[j]) {
revert DuplicateTokens(_tokens[i]);
}
}
}
}

function getVersion() external virtual pure returns (uint256) {
return 1;
}
}
88 changes: 88 additions & 0 deletions src/libraries/LibWellUpgradeableConstructor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// SPDX-License-Identifier: MIT
// forgefmt: disable-start

pragma solidity ^0.8.20;

import {LibContractInfo} from "src/libraries/LibContractInfo.sol";
import {Call, IERC20} from "src/Well.sol";

library LibWellUpgradeableConstructor {

/**
* @notice Encode the Well's immutable data and init data with an owner.
*/
function encodeWellDeploymentData(
address _aquifer,
IERC20[] memory _tokens,
Call memory _wellFunction,
Call[] memory _pumps,
address owner
) internal view returns (bytes memory immutableData, bytes memory initData) {
immutableData = encodeWellImmutableData(_aquifer, _tokens, _wellFunction, _pumps);
initData = encodeWellInitFunctionCall(_tokens, _wellFunction, owner);
}

/**
* @notice Encode the Well's immutable data.
* @param _aquifer The address of the Aquifer which will deploy this Well.
* @param _tokens A list of ERC20 tokens supported by the Well.
* @param _wellFunction A single Call struct representing a call to the Well Function.
* @param _pumps An array of Call structs representings calls to Pumps.
* @dev `immutableData` is tightly packed, however since `_tokens` itself is
* an array, each address in the array will be padded up to 32 bytes.
*
* Arbitrary-length bytes are applied to the end of the encoded bytes array
* for easy reading of statically-sized data.
*
*/
function encodeWellImmutableData(
address _aquifer,
IERC20[] memory _tokens,
Call memory _wellFunction,
Call[] memory _pumps
) internal pure returns (bytes memory immutableData) {

immutableData = abi.encodePacked(
_aquifer, // aquifer address
_tokens.length, // number of tokens
_wellFunction.target, // well function address
_wellFunction.data.length, // well function data length
_pumps.length, // number of pumps
_tokens, // tokens array
_wellFunction.data // well function data (bytes)
);
for (uint256 i; i < _pumps.length; ++i) {
immutableData = abi.encodePacked(
immutableData, // previously packed pumps
_pumps[i].target, // pump address
_pumps[i].data.length, // pump data length
_pumps[i].data // pump data (bytes)
);
}
}

function encodeWellInitFunctionCall(
IERC20[] memory _tokens,
Call memory _wellFunction,
address owner
) public view returns (bytes memory initFunctionCall) {
string memory name = LibContractInfo.getSymbol(address(_tokens[0]));
string memory symbol = name;
for (uint256 i = 1; i < _tokens.length; ++i) {
name = string.concat(name, ":", LibContractInfo.getSymbol(address(_tokens[i])));
symbol = string.concat(symbol, LibContractInfo.getSymbol(address(_tokens[i])));
}
name = string.concat(name, " ", LibContractInfo.getName(_wellFunction.target), " Well");
symbol = string.concat(symbol, LibContractInfo.getSymbol(_wellFunction.target), "w");

// See {Well.init}.
initFunctionCall = abi.encodeWithSignature("init(string,string,address)", name, symbol, owner);
}

/**
* @notice Encode a Call struct representing an arbitrary call to `target` with additional data `data`.
*/
function encodeCall(address target, bytes memory data) public pure returns (Call memory) {
return Call(target, data);
}
}
Loading

0 comments on commit ca995d7

Please sign in to comment.