Skip to content

Commit

Permalink
Merge branch 'master' into runtime-fv
Browse files Browse the repository at this point in the history
  • Loading branch information
aazhou1 authored Nov 7, 2024
2 parents 892901b + a537936 commit 341b395
Show file tree
Hide file tree
Showing 6 changed files with 315 additions and 73 deletions.
32 changes: 32 additions & 0 deletions .github/workflows/actions-sepolia-strategy-management.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: "[sepolia-deploy] manage strategy"
on:
workflow_dispatch:
inputs:
strategy:
description: 'strategy address'
required: true
default: '0x'


jobs:
acceptManagement:
runs-on: ubuntu-latest
environment:
name: sepolia
url: https://term-finance.github.io/yearn-v3-term-vault/
steps:
- uses: actions/checkout@master
with:
fetch-depth: 0
submodules: recursive
- uses: foundry-rs/foundry-toolchain@v1
- run: forge install
- run: forge build
- run: forge tree
- run: forge script script/StrategyManagement.s.sol:StrategyManagement --rpc-url $RPC_URL --broadcast --gas-price 500000000000 --verify --verbosity 4
env:
RPC_URL: ${{ secrets.RPC_URL }}
STRATEGY_MANAGEMENT_ADDRESS: ${{ secrets.STRATEGY_MANAGEMENT_ADDRESS }}
ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY }}
STRATEGY: ${{ github.event.inputs.strategy }}

16 changes: 15 additions & 1 deletion .github/workflows/deploy-sepolia-strategy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ on:
description: 'Yearn strategy name'
required: true
default: '0x'
governorRoleAddress:
description: 'Governor role address'
required: true
default: '0x'
strategyManagementAddress:
description: 'Strategy management address'
required: false
default: '0x'
discountRateMarkup:
description: 'Discount rate markup'
required: false
Expand All @@ -30,6 +38,10 @@ on:
description: 'Repo token concentration limit'
required: false
default: '0.01'
requiredReserveRatio:
description: 'Required reserve ratio'
required: false
default: '0.01'

jobs:
deploy:
Expand Down Expand Up @@ -64,5 +76,7 @@ jobs:
REPOTOKEN_CONCENTRATION_LIMIT: ${{ github.event.inputs.repoTokenConcentrationLimit }}
ADMIN_ADDRESS: ${{ vars.ADMIN_ADDRESS }}
DEVOPS_ADDRESS: ${{ vars.DEVOPS_ADDRESS }}
GOVERNOR_ROLE_ADDRESS: ${{ vars.GOVERNOR_ROLE_ADDRESS }}
GOVERNOR_ROLE_ADDRESS: ${{ github.event.inputs.governorRoleAddress }}
STRATEGY_MANAGEMENT_ADDRESS: ${{ github.event.inputs.strategyManagementAddress }}
NEW_REQUIRED_RESERVE_RATIO: ${{ github.event.inputs.requiredReserveRatio }}

206 changes: 166 additions & 40 deletions script/Strategy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,130 @@
pragma solidity ^0.8.18;

import "forge-std/Script.sol";
import "@tokenized-strategy/interfaces/ITokenizedStrategy.sol";
import "../src/Strategy.sol";
import "../src/TermVaultEventEmitter.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

contract DeployStrategy is Script {
/**
* @dev Converts a comma-separated string of addresses to an array of addresses.
* @param _input A string containing comma-separated addresses.
* @return addressArray An array of addresses parsed from the input string.
*/
function stringToAddressArray(string memory _input) public pure returns (address[] memory) {
// Step 1: Split the input string by commas
string[] memory parts = splitString(_input, ",");

// Step 2: Convert each part to an address
address[] memory addressArray = new address[](parts.length);
for (uint256 i = 0; i < parts.length; i++) {
addressArray[i] = parseAddress(parts[i]);
}

return addressArray;
}

/**
* @dev Converts a comma-separated string of integers to a uint256 array.
* @param _input A string containing comma-separated integers.
* @return uintArray An array of uint256 parsed from the input string.
*/
function stringToUintArray(string memory _input) public pure returns (uint256[] memory) {
// Step 1: Split the input string by commas
string[] memory parts = splitString(_input, ",");

// Step 2: Convert each part to a uint256
uint256[] memory uintArray = new uint256[](parts.length);
for (uint256 i = 0; i < parts.length; i++) {
uintArray[i] = parseUint(parts[i]);
}

return uintArray;
}

/**
* @dev Helper function to split a string by a delimiter
* @param _str The input string
* @param _delimiter The delimiter to split by
* @return An array of substrings
*/
function splitString(string memory _str, string memory _delimiter) internal pure returns (string[] memory) {
bytes memory strBytes = bytes(_str);
bytes memory delimiterBytes = bytes(_delimiter);
uint256 partsCount = 1;

// Count the parts to split the string
for (uint256 i = 0; i < strBytes.length - 1; i++) {
if (strBytes[i] == delimiterBytes[0]) {
partsCount++;
}
}

string[] memory parts = new string[](partsCount);
uint256 partIndex = 0;
bytes memory part;

for (uint256 i = 0; i < strBytes.length; i++) {
if (strBytes[i] == delimiterBytes[0]) {
parts[partIndex] = string(part);
part = "";
partIndex++;
} else {
part = abi.encodePacked(part, strBytes[i]);
}
}

// Add the last part
parts[partIndex] = string(part);

return parts;
}

/**
* @dev Helper function to parse a string and convert it to an address
* @param _str The string representation of an address
* @return The address parsed from the input string
*/
function parseAddress(string memory _str) internal pure returns (address) {
bytes memory tmp = bytes(_str);
require(tmp.length == 42, "Invalid address length"); // Must be 42 characters long (0x + 40 hex chars)

uint160 addr = 0;
for (uint256 i = 2; i < 42; i++) {
uint160 b = uint160(uint8(tmp[i]));

if (b >= 48 && b <= 57) { // 0-9
addr = addr * 16 + (b - 48);
} else if (b >= 65 && b <= 70) { // A-F
addr = addr * 16 + (b - 55);
} else if (b >= 97 && b <= 102) { // a-f
addr = addr * 16 + (b - 87);
} else {
revert("Invalid address character");
}
}

return address(addr);
}
/**
* @dev Helper function to parse a string and convert it to uint256
* @param _str The string representation of a number
* @return The uint256 parsed from the input string
*/
function parseUint(string memory _str) internal pure returns (uint256) {
bytes memory strBytes = bytes(_str);
uint256 result = 0;

for (uint256 i = 0; i < strBytes.length; i++) {
uint8 digit = uint8(strBytes[i]) - 48;
require(digit >= 0 && digit <= 9, "Invalid character in string");
result = result * 10 + digit;
}

return result;
}

function run() external {
uint256 deployerPK = vm.envUint("PRIVATE_KEY");

Expand All @@ -16,64 +135,71 @@ contract DeployStrategy is Script {
vm.startBroadcast(deployerPK);

// Retrieve environment variables
address asset = vm.envAddress("ASSET_ADDRESS");
string memory name = vm.envString("STRATEGY_NAME");
address yearnVaultAddress = vm.envAddress("YEARN_VAULT_ADDRESS");
address discountRateAdapterAddress = vm.envAddress("DISCOUNT_RATE_ADAPTER_ADDRESS");
address admin = vm.envAddress("ADMIN_ADDRESS");
address devops = vm.envAddress("DEVOPS_ADDRESS");
address governorRoleAddress = vm.envAddress("GOVERNOR_ROLE_ADDRESS");
address termController = vm.envOr("TERM_CONTROLLER_ADDRESS", address(0));
uint256 discountRateMarkup = vm.envOr("DISCOUNT_RATE_MARKUP", uint256(0));
address collateralTokenAddr = vm.envOr("COLLATERAL_TOKEN_ADDR", address(0));
uint256 minCollateralRatio = vm.envOr("MIN_COLLATERAL_RATIO", uint256(0));
uint256 timeToMaturityThreshold = vm.envOr("TIME_TO_MATURITY_THRESHOLD", uint256(0));
uint256 repoTokenConcentrationLimit = vm.envOr("REPOTOKEN_CONCENTRATION_LIMIT", uint256(0));
address strategyManagement = vm.envAddress("STRATEGY_MANAGEMENT_ADDRESS");
bool isTest = vm.envBool("IS_TEST");

TermVaultEventEmitter eventEmitter = _deployEventEmitter(admin, devops);

Strategy strategy = new Strategy(asset,
TermVaultEventEmitter eventEmitter = _deployEventEmitter();

Strategy.StrategyParams memory params = buildStrategyParams(address(eventEmitter));

Strategy strategy = new Strategy(
name,
yearnVaultAddress,
discountRateAdapterAddress,
address(eventEmitter),
governorRoleAddress
params
);

console.log("deployed strateghy contract to");
console.log("deployed strategy contract to");
console.log(address(strategy));

ITokenizedStrategy(address(strategy)).setPendingManagement(strategyManagement);
console.log("set pending management");
console.log(strategyManagement);


if (isTest) {
eventEmitter.pairVaultContract(address(strategy));
console.log("paired strategy contract with event emitter");
}

vm.stopBroadcast();
}

strategy.setTermController(termController);
console.log("set term controller");
console.log(termController);

strategy.setDiscountRateMarkup(discountRateMarkup);
console.log("set discount rate markup");
console.log(discountRateMarkup);
function buildStrategyParams(address eventEmitter) internal returns(Strategy.StrategyParams memory) {
address asset = vm.envAddress("ASSET_ADDRESS");
address yearnVaultAddress = vm.envAddress("YEARN_VAULT_ADDRESS");
address discountRateAdapterAddress = vm.envAddress("DISCOUNT_RATE_ADAPTER_ADDRESS");
address governorRoleAddress = vm.envAddress("GOVERNOR_ROLE_ADDRESS");
address termController = vm.envAddress("TERM_CONTROLLER_ADDRESS");
uint256 discountRateMarkup = vm.envUint("DISCOUNT_RATE_MARKUP");
address[] memory collateralTokenAddr = stringToAddressArray(vm.envString("COLLATERAL_TOKEN_ADDR"));
uint256[] memory minCollateralRatio = stringToUintArray(vm.envString("MIN_COLLATERAL_RATIO"));
uint256 timeToMaturityThreshold = vm.envUint("TIME_TO_MATURITY_THRESHOLD");
uint256 repoTokenConcentrationLimit = vm.envUint("REPOTOKEN_CONCENTRATION_LIMIT");
uint256 newRequiredReserveRatio = vm.envUint("NEW_REQUIRED_RESERVE_RATIO");

strategy.setCollateralTokenParams(collateralTokenAddr, minCollateralRatio);
console.log("set collateral token params");
console.log(collateralTokenAddr);
console.log(minCollateralRatio);
Strategy.StrategyParams memory params = Strategy.StrategyParams(
asset,
yearnVaultAddress,
discountRateAdapterAddress,
address(eventEmitter),
governorRoleAddress,
collateralTokenAddr,
minCollateralRatio,
termController,
repoTokenConcentrationLimit,
timeToMaturityThreshold,
newRequiredReserveRatio,
discountRateMarkup
);

strategy.setTimeToMaturityThreshold(timeToMaturityThreshold);
console.log("set time to maturity threshold");
console.log(timeToMaturityThreshold);
return params;

strategy.setRepoTokenConcentrationLimit(repoTokenConcentrationLimit);
console.log("set repo token concentration limit");
console.log(repoTokenConcentrationLimit);
}

vm.stopBroadcast();
}

function _deployEventEmitter(address admin, address devops) internal returns(TermVaultEventEmitter eventEmitter) {
function _deployEventEmitter() internal returns(TermVaultEventEmitter eventEmitter) {
address admin = vm.envAddress("ADMIN_ADDRESS");
address devops = vm.envAddress("DEVOPS_ADDRESS");
TermVaultEventEmitter eventEmitterImpl = new TermVaultEventEmitter();
console.log("deployed event emitter impl contract to");
console.log(address(eventEmitterImpl));
Expand Down
22 changes: 22 additions & 0 deletions script/StrategyManagement.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "forge-std/Script.sol";
import "@tokenized-strategy/interfaces/ITokenizedStrategy.sol";

contract StrategyManagement is Script {
function run() external {
uint256 strategyManagement = vm.envUint("STRATEGY_MANAGEMENT_ADDRESS");
address strategy = vm.envAddress("STRATEGY");

// Set up the RPC URL (optional if you're using the default foundry config)
string memory rpcUrl = vm.envString("RPC_URL");

vm.startBroadcast(strategyManagement);
ITokenizedStrategy(strategy).acceptManagement();
console.log("accepted management");
vm.stopBroadcast();


}
}
Loading

0 comments on commit 341b395

Please sign in to comment.