Skip to content

Commit

Permalink
Merge pull request #298 from asymmetryfinance/sfrx-eth-adapt
Browse files Browse the repository at this point in the history
Redeploy + Update readme
  • Loading branch information
0xVertigo authored Feb 19, 2024
2 parents ae2ded9 + fe463f7 commit 35052cf
Show file tree
Hide file tree
Showing 12 changed files with 185 additions and 47 deletions.
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ technical details on internal works view ["AfEth"](./docs/AfEth.md).

|Description|Address + Link|
|-----------|--------------|
|The AfEth ERC1967 proxy, deployed using Solady's [ERC1967 Factory](https://etherscan.io/address/0x0000000000006396FF2a80c067f99B3d2Ab4Df24#readContract).| [`0x00000000fbAA96B36A2AcD4B7B36385c426B119D`](https://etherscan.io/address/0x00000000fbAA96B36A2AcD4B7B36385c426B119D)|
|The Votium ERC1967 proxy, also deployed using Solady's [ERC1967 Factory](https://etherscan.io/address/0x0000000000006396FF2a80c067f99B3d2Ab4Df24#readContract)|[`0x000000004c4bb6e0a169FE3A9228cd0F70873CdE`](https://etherscan.io/address/0x000000004c4bb6e0a169FE3A9228cd0F70873CdE)|
|Votium Implementation Contract|[`0xCf687792a1e65bB41793cc938Cf8e27E5D1B678b`](https://etherscan.io/address/0xCf687792a1e65bB41793cc938Cf8e27E5D1B678b)|
|AfEth Implementation Contract|[`0x1a0A62AA8A9471a6a726B5cDc24192Be3a3DBc7B`](https://etherscan.io/address/0x1a0A62AA8A9471a6a726B5cDc24192Be3a3DBc7B)|
|AfEthRelayer, also deployed using Solady's [ERC1967 Factory](https://etherscan.io/address/0x0000000000006396FF2a80c067f99B3d2Ab4Df24#readContract)|[`0x00000000b8791985c4bd2cbc4584cee89c4e95ef`](https://etherscan.io/address/0x00000000b8791985c4bd2cbc4584cee89c4e95ef)|
|The Main AfEth Contract (ERC1967 proxy)| [`0x0000000016E6Cb3038203c1129c8B4aEE7af7a11`](https://etherscan.io/address/0x0000000016e6cb3038203c1129c8b4aee7af7a11)|
|The Votium Strategy Contract (ERC1967 proxy)|[`0x00000069aBbB0B1Ad6975bcF753eEe15D318A0BF`](https://etherscan.io/address/0x00000069abbb0b1ad6975bcf753eee15d318a0bf)|
|Votium Implementation Contract|[`0x01e111D815261c8ad6A1271861970E804E4282E4`](https://etherscan.io/address/0x01e111d815261c8ad6a1271861970e804e4282e4)|
|AfEth Implementation Contract|[`0x0A36224486D4E49dEB27b489233c6B64e0241D6A`](https://etherscan.io/address/0x0a36224486d4e49deb27b489233c6b64e0241d6a)|
|AfEthRelayer|[`0x0000005aC28De2cbda005a8500A9578921FDB7da`](https://etherscan.io/address/0x0000005aC28De2cbda005a8500A9578921FDB7da)|
|AfEthRelayer Contract Implementation|[`0x47c212ddebcad886d0e7b8482bd13f8a27eb0b72`](https://etherscan.io/address/0x47c212ddebcad886d0e7b8482bd13f8a27eb0b72)|

31 changes: 18 additions & 13 deletions script/DeployMainnet.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,36 @@ pragma solidity 0.8.20;
import {Script} from "forge-std/Script.sol";
import {Test} from "forge-std/Test.sol";
import {console2 as console} from "forge-std/console2.sol";
import {ERC1967FactoryConstants} from "solady/src/utils/ERC1967FactoryConstants.sol";
import {ERC1967Factory} from "solady/src/utils/ERC1967Factory.sol";
import {SimpleProxyFactory} from "../src/utils/SimpleProxyFactory.sol";
import {VotiumStrategy} from "../src/strategies/VotiumStrategy.sol";
import {AfEth} from "../src/AfEth.sol";
import {console2 as console} from "forge-std/console2.sol";

/// @author philogy <https://github.com/philogy>
contract DeployMainnetScript is Test, Script {
bytes32 internal constant AF_ETH_VANITY_SALT = 0x67b80ff33e5937b58b2a46870a912cb11d231efbec654a8355b46947ec1a0010;
bytes32 internal constant VOTIUM_VANITY_SALT = 0x67b80ff33e5937b58b2a46870a912cb11d231efbf13066ae7fad3ff30a060010;
bytes32 internal constant AF_ETH_VANITY_SALT = 0x67b80ff33e5937b58b2a46870a912cb11d231efb29d74144d35ec0000fcd3cd6;
bytes32 internal constant VOTIUM_VANITY_SALT = 0x67b80ff33e5937b58b2a46870a912cb11d231efb29d74144d35e200008cc162e;

function run() public {
ERC1967Factory factory = ERC1967Factory(ERC1967FactoryConstants.ADDRESS);

address OWNER = 0x263b03BbA0BbbC320928B6026f5eAAFAD9F1ddeb;
address REWARDER = 0xa927c81CC214cc991613cB695751Bc932F042501;

// TODO: Not the recommended way of loading private key.
uint256 pk = vm.envUint("PRIV_KEY");
address me = vm.addr(pk);

vm.startBroadcast(pk);

(bool success, bytes memory addr) =
CREATE2_FACTORY.call(abi.encodePacked(bytes32(0), type(SimpleProxyFactory).creationCode));
require(success, "failed to deploy factory");

SimpleProxyFactory factory = SimpleProxyFactory(address(bytes20(addr)));

console.log("deployer: %s", me);
console.log("factory: %s", address(factory));
console.log("proxy inithash: %x", uint256(factory.PROXY_INIT_HASH()));

address votiumImplementation;
address afEthImplementation;
{
Expand All @@ -35,15 +43,12 @@ contract DeployMainnetScript is Test, Script {
votiumImplementation = address(new VotiumStrategy(afEthProxyAddr));
afEthImplementation = address(new AfEth(votiumProxyAddr));
}
address votium = factory.deployDeterministicAndCall(
votiumImplementation,
OWNER,
VOTIUM_VANITY_SALT,
abi.encodeCall(VotiumStrategy.initialize, (OWNER, REWARDER))
address votium = factory.deployDeterministic(
VOTIUM_VANITY_SALT, votiumImplementation, abi.encodeCall(VotiumStrategy.initialize, (OWNER, REWARDER))
);

address afEth = factory.deployDeterministicAndCall{value: 1 gwei}(
afEthImplementation, OWNER, AF_ETH_VANITY_SALT, abi.encodeCall(AfEth.initialize, (OWNER, REWARDER))
address afEth = factory.deployDeterministic{value: 1 gwei}(
AF_ETH_VANITY_SALT, afEthImplementation, abi.encodeCall(AfEth.initialize, (OWNER, REWARDER))
);

console.log("afEth successfuly deployed at %s", afEth);
Expand Down
18 changes: 5 additions & 13 deletions script/DeployRelay.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,16 @@ pragma solidity 0.8.20;

import {Script} from "forge-std/Script.sol";
import {Test} from "forge-std/Test.sol";
import {ERC1967FactoryConstants} from "solady/src/utils/ERC1967FactoryConstants.sol";
import {ERC1967Factory} from "solady/src/utils/ERC1967Factory.sol";
import {AfEthRelayer} from "../src/AfEthRelayer.sol";
import {SimpleProxyFactory} from "../src/utils/SimpleProxyFactory.sol";
import {console2 as console} from "forge-std/console2.sol";

/// @author philogy <https://github.com/philogy>
contract DeployRelayScript is Test, Script {
bytes32 internal constant AF_ETH_VANITY_SALT = 0x67b80ff33e5937b58b2a46870a912cb11d231efbec654a8355b46947ec1a0010;
bytes32 internal constant VOTIUM_VANITY_SALT = 0x67b80ff33e5937b58b2a46870a912cb11d231efbf13066ae7fad3ff30a060010;

bytes32 internal constant RELAY_VANITY_SALT = 0x67b80ff33e5937b58b2a46870a912cb11d231efbbab440b5457a1800089dc6c5;
bytes32 internal constant RELAY_VANITY_SALT = 0x67b80ff33e5937b58b2a46870a912cb11d231efb29d74144d35e2000083bf82a;

function run() public {
ERC1967Factory factory = ERC1967Factory(ERC1967FactoryConstants.ADDRESS);
SimpleProxyFactory factory = SimpleProxyFactory(0x51fBA11386fb26Ae017D539624435137a25d7CE9);

address OWNER = 0x263b03BbA0BbbC320928B6026f5eAAFAD9F1ddeb;

Expand All @@ -27,14 +23,10 @@ contract DeployRelayScript is Test, Script {

address relayImplementation = address(new AfEthRelayer());

address relay = factory.deployDeterministicAndCall(
relayImplementation,
OWNER,
RELAY_VANITY_SALT,
abi.encodeCall(AfEthRelayer.initialize, ())
address relay = factory.deployDeterministic(
RELAY_VANITY_SALT, relayImplementation, abi.encodeCall(AfEthRelayer.initialize, (OWNER))
);


console.log("relay successfuly deployed at %s", relay);

vm.stopBroadcast();
Expand Down
9 changes: 8 additions & 1 deletion src/AfEth.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity 0.8.20;

import {ERC20PermitUpgradeable} from
"@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {IAfEth} from "./interfaces/afeth/IAfEth.sol";
import {Ownable} from "solady/src/auth/Ownable.sol";
import {FixedPointMathLib} from "solady/src/utils/FixedPointMathLib.sol";
Expand All @@ -12,7 +13,7 @@ import {IVotiumStrategy} from "./interfaces/afeth/IVotiumStrategy.sol";
import {SfrxEthStrategy} from "./strategies/SfrxEthStrategy.sol";

/// @dev AfEth is the strategy manager for the sfrxETH and votium strategies
contract AfEth is IAfEth, Ownable, ERC20PermitUpgradeable {
contract AfEth is IAfEth, Ownable, ERC20PermitUpgradeable, UUPSUpgradeable {
using FixedPointMathLib for uint256;
using SafeTransferLib for address;
using SafeCastLib for uint256;
Expand Down Expand Up @@ -64,6 +65,7 @@ contract AfEth is IAfEth, Ownable, ERC20PermitUpgradeable {
string memory name_ = "Asymmetry Finance afETH";
__ERC20_init(name_, "afETH");
__ERC20Permit_init(name_);
__UUPSUpgradeable_init();
_initializeOwner(initialOwner);
emit SetRewarder(rewarder = initialRewarder);

Expand All @@ -87,6 +89,11 @@ contract AfEth is IAfEth, Ownable, ERC20PermitUpgradeable {
_mint(address(0xdead), recognizedValue);
}

/**
* @dev Allows the owner of the contract to upgrade to *any* new address.
*/
function _authorizeUpgrade(address /* newImplementation */ ) internal view override onlyOwner {}

modifier latestAt(uint256 deadline) {
if (block.timestamp > deadline) revert StaleAction();
_;
Expand Down
20 changes: 14 additions & 6 deletions src/AfEthRelayer.sol
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {Ownable} from "solady/src/auth/Ownable.sol";
import {SafeTransferLib} from "solady/src/utils/SafeTransferLib.sol";
import {IAfEth} from "./interfaces/afeth/IAfEth.sol";
import {ISafEth} from "./interfaces/safeth/ISafEth.sol";
import {IWETH, WETH} from "./interfaces/IWETH.sol";

// AfEth is the strategy manager for safEth and votium strategies
contract AfEthRelayer is Initializable {
contract AfEthRelayer is Ownable, UUPSUpgradeable {
using SafeTransferLib for address;

ISafEth public constant SAF_ETH = ISafEth(0x6732Efaf6f39926346BeF8b821a04B6361C4F3e5);
IAfEth public constant AF_ETH = IAfEth(0x00000000fbAA96B36A2AcD4B7B36385c426B119D);
IAfEth public constant AF_ETH = IAfEth(0x0000000016E6Cb3038203c1129c8B4aEE7af7a11);

address internal immutable THIS_ = address(this);

address internal constant ZERO_X_EXCHANGE = 0xDef1C0ded9bec7F1a1670819833240f027b25EfF;
address internal constant ZERO_X_ERC20_PROXY = 0x95E6F48254609A6ee006F7D493c8e5fB97094ceF;

struct SwapParams {
address sellToken;
Expand All @@ -41,7 +41,15 @@ contract AfEthRelayer is Initializable {
* @notice - Initialize values for the contracts
* @dev - This replaces the constructor for upgradeable contracts
*/
function initialize() external initializer {}
function initialize(address initialOwner) external initializer {
__UUPSUpgradeable_init();
_initializeOwner(initialOwner);
}

/**
* @dev Allows the owner of the contract to upgrade to *any* new address.
*/
function _authorizeUpgrade(address /* newImplementation */ ) internal view override onlyOwner {}

/**
* @notice Deposits into the SafEth contract and relay to owner address
Expand Down Expand Up @@ -82,7 +90,7 @@ contract AfEthRelayer is Initializable {
* allows for an optional nested call to an actual deposit transaction.
*/
function enableNewSellToken(address sellToken, bytes calldata innerCall) external payable {
sellToken.safeApproveWithRetry(ZERO_X_ERC20_PROXY, type(uint256).max);
sellToken.safeApproveWithRetry(ZERO_X_EXCHANGE, type(uint256).max);
if (innerCall.length > 0) {
// Delegate to `THIS_` (implementation address) to avoid proxy overhead, functionally
// equivalent to calling address(this).
Expand Down
7 changes: 7 additions & 0 deletions src/interfaces/IProxySource.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

/// @author philogy <https://github.com/philogy>
interface IProxySource {
function implementation() external view returns (address);
}
10 changes: 8 additions & 2 deletions src/strategies/VotiumStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity 0.8.20;

import {Ownable} from "solady/src/auth/Ownable.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {TrackedAllowances, Allowance} from "../utils/TrackedAllowances.sol";
import {FixedPointMathLib} from "solady/src/utils/FixedPointMathLib.sol";
import {SafeTransferLib} from "solady/src/utils/SafeTransferLib.sol";
Expand All @@ -22,7 +22,7 @@ import {IAfEth} from "../interfaces/afeth/IAfEth.sol";

/// @title Votium Strategy Token
/// @author Asymmetry Finance
contract VotiumStrategy is IVotiumStrategy, Ownable, TrackedAllowances, Initializable {
contract VotiumStrategy is IVotiumStrategy, Ownable, TrackedAllowances, UUPSUpgradeable {
using FixedPointMathLib for uint256;
using SafeTransferLib for address;
using SafeCastLib for uint256;
Expand Down Expand Up @@ -87,6 +87,7 @@ contract VotiumStrategy is IVotiumStrategy, Ownable, TrackedAllowances, Initiali
ISnapshotDelegationRegistry(SNAPSHOT_DELEGATE_REGISTRY).setDelegate(VOTE_DELEGATION_ID, VOTE_PROXY);
rewarder = initialRewarder;
_initializeOwner(initialOwner);
__UUPSUpgradeable_init();

// Approve once to save gas later by avoiding having to re-approve every time.
_grantAndTrackInfiniteAllowance(Allowance({spender: address(LOCKED_CVX), token: CVX}));
Expand All @@ -95,6 +96,11 @@ contract VotiumStrategy is IVotiumStrategy, Ownable, TrackedAllowances, Initiali
emit RewarderSet(initialRewarder);
}

/**
* @dev Allows the owner of the contract to upgrade to *any* new address.
*/
function _authorizeUpgrade(address /* newImplementation */ ) internal view override onlyOwner {}

/**
* @notice - Function to set the address of the rewarder account that periodically claims rewards
* @param newRewarder Address of the rewarder account
Expand Down
19 changes: 19 additions & 0 deletions src/utils/SimpleProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {Proxy} from "@openzeppelin/contracts/proxy/Proxy.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {IProxySource} from "../interfaces/IProxySource.sol";

/// @author philogy <https://github.com/philogy>
/// @dev ERC1967 Proxy that has no initcall or implementation factor in its deploy bytecode.
contract SimpleProxy is Proxy {
constructor() {
address implementation = IProxySource(msg.sender).implementation();
ERC1967Utils.upgradeToAndCall(implementation, new bytes(0));
}

function _implementation() internal view override returns (address) {
return ERC1967Utils.getImplementation();
}
}
39 changes: 39 additions & 0 deletions src/utils/SimpleProxyFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {IProxySource} from "../interfaces/IProxySource.sol";
import {SimpleProxy} from "./SimpleProxy.sol";

/// @author philogy <https://github.com/philogy>
contract SimpleProxyFactory is IProxySource {
address internal constant NO_IMPLEMENTATION = address(1);

bytes32 public immutable PROXY_INIT_HASH = keccak256(type(SimpleProxy).creationCode);

address public implementation = NO_IMPLEMENTATION;

error NotSaltOwner();
error InitCallFailed();

function deployDeterministic(bytes32 salt, address initialImplementation, bytes memory initCall)
external
payable
returns (address proxy)
{
address saltOwner = address(bytes20(salt));
if (saltOwner != address(0) && saltOwner != msg.sender) revert NotSaltOwner();
implementation = initialImplementation;
proxy = address(new SimpleProxy{salt: salt}());
implementation = NO_IMPLEMENTATION;

if (msg.value > 0 || initCall.length > 0) {
(bool success,) = proxy.call{value: msg.value}(initCall);
if (!success) revert InitCallFailed();
}
}

function predictDeterministicAddress(bytes32 salt) external view returns (address addr) {
return
address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, PROXY_INIT_HASH)))));
}
}
30 changes: 30 additions & 0 deletions test/AfEth.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,36 @@ contract AfEthTest is BaseTest {
assertApproxEqRel(ethOut, amount, 0.005e18, "unlocked ETH not equal to amount");
}

function testOwnerCanUpgrade() public {
address newImpl = makeAddr("new_impl");
vm.etch(
newImpl,
hex"7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc3d52593d3d3560e01c6352d1902d0361003557f35bfd"
);

vm.prank(owner);
afEth.upgradeToAndCall(newImpl, new bytes(0));

(bool success, bytes memory errorData) = address(afEth).call(hex"01020304");
assertFalse(success);
assertEq(abi.decode(errorData, (bytes32)), 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc);
}

function testOnlyOwnerCanUpgrade() public {
address newImpl = makeAddr("new_impl");
vm.etch(
newImpl,
hex"7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc3d52593d3d3560e01c6352d1902d0361003557f35bfd"
);

vm.prank(owner);
afEth.upgradeToAndCall(newImpl, new bytes(0));

(bool success, bytes memory errorData) = address(afEth).call(hex"01020304");
assertFalse(success);
assertEq(abi.decode(errorData, (bytes32)), 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc);
}

function _deposit(string memory label, uint256 amount) internal returns (uint256 amountOut) {
hoax(makeAddr(label), amount);
amountOut = afEth.deposit{value: amount}(0, block.timestamp);
Expand Down
20 changes: 20 additions & 0 deletions test/mocks/MockUpgrade.huff
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#define constant SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc

#define function proxiableUUID() view returns (bytes32)


#define macro _MAIN(z0) = takes(0) returns(0) {
[SLOT] <z0> mstore

msize <z0>

<z0> calldataload 0xe0 shr
__FUNC_SIG(proxiableUUID) sub error jumpi
return
error:
revert
}

#define macro MAIN() = takes(0) returns(0) {
_MAIN(returndatasize)
}
Loading

0 comments on commit 35052cf

Please sign in to comment.