Skip to content

Commit

Permalink
Merge pull request #58 from Tenderize/nv/local-testnet
Browse files Browse the repository at this point in the history
Local Testnet Deployment
  • Loading branch information
kyriediculous authored Jul 20, 2023
2 parents 3b1396e + 3af10ff commit d8130ea
Show file tree
Hide file tree
Showing 8 changed files with 274 additions and 7 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
lcov.info
yarn-debug.log*
yarn-error.log*
deployments.json

# broadcasts
!/broadcast
Expand Down
24 changes: 19 additions & 5 deletions script/DEPLOY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Tenderize Deployments

## Deploying To Anvil

Tenderizer uses `CREATE2` to deploy contracts. Forge expects this contract to be deterministically deployed at
`0x4e59b44847b379578588920ca78fbf26c0b4956c`.

Anvil by default doesn't have the `CREATE2` proxy deployed. Instead, `anvil_setCode` can be used as a workaround.

```sh
curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","id":67,"method":"anvil_setCode","params": ["0x4e59b44847b379578588920ca78fbf26c0b4956c","0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3"]}' 127.0.0.1:8545
```

As a sanity check you can run `cast code "0x4e59b44847b379578588920ca78fbf26c0b4956c" --rpc-url http://127.0.0.1:8545`
to see if this was succesful.

## Deploy Tenderizer

The Tenderizer Factory and all associated components need to be deployed only once per network by setting the vars in
Expand All @@ -14,12 +28,12 @@ This script will execute following calls:

1. Deploy Registry (without initialization)
- Deploy `Registry` implementation
- Deploy `ERC1967` Proxy
2. Deploy `Tenderizer` Implementation
3. Deploy `Unlocks`
- Deploy `ERC1967` UUPS Proxy
2. Deploy `Unlocks`
- Deploy `Renderer` Implementation
- Deploy `Renderer` ERC-1967 UUPS Proxy
- Deploy `Unlocks`
- Deploy `Renderer` `ERC1967` UUPS Proxy
- Deploy `Unlocks` contract
3. Deploy `Tenderizer` Implementation
4. Initialize `Registry` with `Tenderizer` implementation address and `Unlocks` address as arguments
5. Deploy `Factory` with `Registry` address as argument
- Set `FACTORY_ROLE` on `Registry` for `Factory`
Expand Down
11 changes: 9 additions & 2 deletions script/Tenderize_Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

pragma solidity 0.8.17;

import { Script } from "forge-std/Script.sol";
import { Script, console2 } from "forge-std/Script.sol";
import { ERC1967Proxy } from "openzeppelin-contracts/proxy/ERC1967/ERC1967Proxy.sol";

import { Tenderizer } from "core/tenderizer/Tenderizer.sol";
Expand Down Expand Up @@ -41,6 +41,8 @@ contract Tenderize_Deploy is Script {
// - Deploy Registry UUPS Proxy
address registryProxy = address(new ERC1967Proxy{salt: salt}(address(registry), ""));
vm.serializeAddress(json_output, "registry_proxy", registryProxy);
console2.log("Registry Implementation: ", address(registry));
console2.log("Registry Proxy: ", registryProxy);

// 2. Deploy Unlocks
// - Deploy Renderer Implementation
Expand All @@ -50,12 +52,16 @@ contract Tenderize_Deploy is Script {
ERC1967Proxy rendererProxy = new ERC1967Proxy{salt: salt}(address(renderer), abi.encodeCall(renderer.initialize, ()));
vm.serializeAddress(json_output, "renderer_proxy", address(rendererProxy));
// - Deploy Unlocks
Unlocks unlocks = new Unlocks{salt: salt}(address(rendererProxy), registryProxy);
Unlocks unlocks = new Unlocks{salt: salt}(address(registryProxy), address(rendererProxy));
vm.serializeAddress(json_output, "unlocks", address(unlocks));
console2.log("Renderer Implementation: ", address(renderer));
console2.log("Renderer Proxy: ", address(rendererProxy));
console2.log("Unlocks: ", address(unlocks));

// 3. Deploy Tenderizer Implementation
Tenderizer tenderizer = new Tenderizer{salt: salt}(registryProxy, address(unlocks));
vm.serializeAddress(json_output, "tenderizer_implementation", address(tenderizer));
console2.log("Tenderizer Implementation: ", address(tenderizer));

// 4. Initialize Registry
Registry(registryProxy).initialize(address(tenderizer), address(unlocks));
Expand All @@ -65,6 +71,7 @@ contract Tenderize_Deploy is Script {
vm.serializeAddress(json_output, "factory", address(factory));
// - Grant Factory role to Factory
Registry(registryProxy).grantRole(FACTORY_ROLE, address(factory));
console2.log("Factory: ", address(factory));

vm.stopBroadcast();

Expand Down
46 changes: 46 additions & 0 deletions script/XYZ_Data.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: MIT
//
// _____ _ _
// |_ _| | | (_)
// | | ___ _ __ __| | ___ _ __ _ _______
// | |/ _ \ '_ \ / _` |/ _ \ '__| |_ / _ \
// | | __/ | | | (_| | __/ | | |/ / __/
// \_/\___|_| |_|\__,_|\___|_| |_/___\___|
//
// Copyright (c) Tenderize Labs Ltd

pragma solidity 0.8.17;

import { Script, console2 } from "forge-std/Script.sol";
import { MockERC20 } from "solmate/test/utils/mocks/MockERC20.sol";
import { StakingXYZ } from "../test/helpers/StakingXYZ.sol";
import { XYZAdapter } from "../test/helpers/XYZAdapter.sol";
import { Registry } from "core/registry/Registry.sol";
import { Tenderizer } from "core/tenderizer/Tenderizer.sol";
import { Factory } from "core/factory/Factory.sol";

contract XYZ_Data is Script {
bytes32 private constant salt = 0x0;

function run() public {
MockERC20 XYZ = MockERC20(0xed9358918089a858d0af58AC63c93699a67B6b91);

uint256 privKey = vm.envUint("PRIVATE_KEY");
address me = vm.addr(privKey);
vm.startBroadcast(privKey);

XYZ.mint(me, 10_000_000_000 ether);
address tenderizer_1 = 0x6f674B27fE58740f14754a34ccd6C636646FA755;
address tenderizer_2 = 0x3186a94AA139f420228Bc73D68b448be52bC4106;
address tenderizer_3 = 0x2554110E3b2Ad09f0ba7D9392c9845592F55B1E8;
XYZ.approve(tenderizer_1, 10_000_000_000 ether);
XYZ.approve(tenderizer_2, 10_000_000_000 ether);
XYZ.approve(tenderizer_3, 10_000_000_000 ether);

Tenderizer(tenderizer_1).deposit(me, 35_983 ether);
Tenderizer(tenderizer_2).deposit(me, 12_821 ether);
Tenderizer(tenderizer_3).deposit(me, 5123 ether);

Tenderizer(tenderizer_1).unlock(1202 ether);
}
}
50 changes: 50 additions & 0 deletions script/XYZ_Deploy.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: MIT
//
// _____ _ _
// |_ _| | | (_)
// | | ___ _ __ __| | ___ _ __ _ _______
// | |/ _ \ '_ \ / _` |/ _ \ '__| |_ / _ \
// | | __/ | | | (_| | __/ | | |/ / __/
// \_/\___|_| |_|\__,_|\___|_| |_/___\___|
//
// Copyright (c) Tenderize Labs Ltd

pragma solidity 0.8.17;

import { Script, console2 } from "forge-std/Script.sol";
import { MockERC20 } from "solmate/test/utils/mocks/MockERC20.sol";
import { StakingXYZ } from "../test/helpers/StakingXYZ.sol";
import { XYZAdapter } from "../test/helpers/XYZAdapter.sol";
import { Registry } from "core/registry/Registry.sol";

import { Factory } from "core/factory/Factory.sol";

contract XYZ_Deploy is Script {
bytes32 private constant salt = 0x0;

function run() public {
address registry = vm.envAddress("REGISTRY");
address factory = vm.envAddress("FACTORY");

vm.startBroadcast(vm.envUint("PRIVATE_KEY"));

MockERC20 XYZ = new MockERC20{salt: salt}("XYZ", "XYZ", 18);
console2.log("XYZ Token: ", address(XYZ));
StakingXYZ stakingXYZ = new StakingXYZ{salt: salt}(address(XYZ));
console2.log("StakingXYZ: ", address(stakingXYZ));
XYZAdapter adapter = new XYZAdapter{salt: salt}(address(stakingXYZ), address(XYZ));
console2.log("XYZ Adapter: ", address(adapter));
// Register XYZ adapter
Registry(registry).registerAdapter(address(XYZ), address(adapter));

// Register some mock validators
address[] memory validators = new address[](3);
validators[0] = 0x597aD7F7A1C9F8d0121a9e949Cca7530F2B25ef6;
validators[1] = 0x6C06d3246FbB77C4Ad75480E03d2a0A8eaF68121;
validators[2] = 0xf909aC60C647a14DB3663dA5EcF5F8eCbE324395;

for (uint256 i = 0; i < validators.length; i++) {
Factory(factory).newTenderizer(address(XYZ), validators[i]);
}
}
}
19 changes: 19 additions & 0 deletions script/anvil_xyz.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash
set -x
nohup bash -c "anvil &" >/dev/null 2>&1 && sleep 5

forge build

curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","id":67,"method":"anvil_setCode","params": ["0x4e59b44847b379578588920ca78fbf26c0b4956c","0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3"]}' 127.0.0.1:8545

export PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
export REGISTRY=0x296dB3C224c7f3104f22Be3D4c1FBfFdE4A4431B
export FACTORY=0x4eF71bD00395C447A43dB077Abe05f0C7910B3A8

forge script script/Tenderize_Deploy.s.sol:Tenderize_Deploy --fork-url http://127.0.0.1:8545 --broadcast --private-key $PRIVATE_KEY -vvvv

forge script script/XYZ_Deploy.s.sol:XYZ_Deploy --fork-url http://127.0.0.1:8545 --broadcast --private-key $PRIVATE_KEY -vvvv

read -r -d '' _ </dev/tty
echo "Closing Down Anvil"
pkill -9 anvil
62 changes: 62 additions & 0 deletions test/helpers/StakingXYZ.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: MIT
//
// _____ _ _
// |_ _| | | (_)
// | | ___ _ __ __| | ___ _ __ _ _______
// | |/ _ \ '_ \ / _` |/ _ \ '__| |_ / _ \
// | | __/ | | | (_| | __/ | | |/ / __/
// \_/\___|_| |_|\__,_|\___|_| |_/___\___|
//
// Copyright (c) Tenderize Labs Ltd

import { ERC20 } from "solmate/tokens/ERC20.sol";

pragma solidity 0.8.17;

contract StakingXYZ {
mapping(address => uint256) public staked;

address immutable token;
uint256 public nextRewardTimeStamp;

uint256 constant rewardTime = 1 days;
uint256 constant unlockTime = 2 days;

struct Unlock {
uint256 amount;
uint256 maturity;
}

mapping(address => mapping(uint256 => Unlock)) public unlocks;
mapping(address => uint256) public unlockCount;

constructor(address _token) {
token = _token;
}

function stake(uint256 amount) external {
require(amount > 0, "amount must be greater than 0");
ERC20(token).transferFrom(msg.sender, address(this), amount);
staked[msg.sender] = staked[msg.sender] + amount;
}

function unstake(uint256 amount) external returns (uint256 unlockID) {
staked[msg.sender] -= amount;
unlockID = unlockCount[msg.sender]++;
unlocks[msg.sender][unlockID] = Unlock(amount, block.timestamp + unlockTime);
}

function withdraw(uint256 id) external returns (uint256) {
Unlock memory unlock = unlocks[msg.sender][id];
require(unlock.maturity < block.timestamp, "unlock time not reached");
delete unlocks[msg.sender][id];
ERC20(token).transfer(msg.sender, unlock.amount);
return unlock.amount;
}

function claimrewards() external {
if (block.timestamp < nextRewardTimeStamp) return;
staked[msg.sender] = staked[msg.sender] * 1.007e6 / 1e6;
nextRewardTimeStamp = block.timestamp;
}
}
68 changes: 68 additions & 0 deletions test/helpers/XYZAdapter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: MIT
//
// _____ _ _
// |_ _| | | (_)
// | | ___ _ __ __| | ___ _ __ _ _______
// | |/ _ \ '_ \ / _` |/ _ \ '__| |_ / _ \
// | | __/ | | | (_| | __/ | | |/ / __/
// \_/\___|_| |_|\__,_|\___|_| |_/___\___|
//
// Copyright (c) Tenderize Labs Ltd

import { ERC20 } from "solmate/tokens/ERC20.sol";

import { Adapter } from "core/adapters/Adapter.sol";
import { IERC165 } from "core/interfaces/IERC165.sol";

import { StakingXYZ } from "./StakingXYZ.sol";

pragma solidity 0.8.17;

contract XYZAdapter is Adapter {
address immutable STAKINGXYZ;
address immutable XYZ_TOKEN;

constructor(address _stakingXYZ, address _xyz) {
STAKINGXYZ = _stakingXYZ;
XYZ_TOKEN = _xyz;
}

function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
return interfaceId == type(Adapter).interfaceId || interfaceId == type(IERC165).interfaceId;
}

function previewDeposit(uint256 assets) external pure returns (uint256) {
return assets;
}

function previewWithdraw(uint256 unlockID) external view returns (uint256 amount) {
(amount,) = StakingXYZ(STAKINGXYZ).unlocks(address(this), unlockID);
}

function unlockMaturity(uint256 unlockID) external view returns (uint256 maturity) {
(, maturity) = StakingXYZ(STAKINGXYZ).unlocks(address(this), unlockID);
}

function stake(address, uint256 amount) external {
ERC20(XYZ_TOKEN).approve(STAKINGXYZ, amount);
StakingXYZ(STAKINGXYZ).stake(amount);
}

function unstake(address, uint256 amount) external returns (uint256 unlockID) {
unlockID = StakingXYZ(STAKINGXYZ).unstake(amount);
}

function withdraw(address, uint256 unlockID) external returns (uint256 amount) {
amount = StakingXYZ(STAKINGXYZ).withdraw(unlockID);
}

function rebase(address, uint256 currentStake) external returns (uint256 newStake) {
if (block.timestamp < StakingXYZ(STAKINGXYZ).nextRewardTimeStamp()) return currentStake;
StakingXYZ(STAKINGXYZ).claimrewards();
newStake = StakingXYZ(STAKINGXYZ).staked(address(this));
}

function isValidator(address) external pure returns (bool) {
return true;
}
}

0 comments on commit d8130ea

Please sign in to comment.