Skip to content
This repository has been archived by the owner on Oct 6, 2023. It is now read-only.

Commit

Permalink
Changes to router and vault to remove array accountIds and switch to …
Browse files Browse the repository at this point in the history
…single accountId, added new external endpoint for harvesting to router
  • Loading branch information
stevieraykatz committed Sep 13, 2023
1 parent 8936571 commit 0da2d24
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 94 deletions.
17 changes: 15 additions & 2 deletions contracts/core/router/IRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,24 @@ interface IRouter is IAxelarExecutable {
event Refund(IVault.VaultActionData action, uint256 amount);
event Deposit(IVault.VaultActionData action);
event Redeem(IVault.VaultActionData action, uint256 amount);
event RewardsHarvested(IVault.VaultActionData action);
event ErrorLogged(IVault.VaultActionData action, string message);
event ErrorBytesLogged(IVault.VaultActionData action, bytes data);

/*////////////////////////////////////////////////
CUSTOM TYPES
*/ ////////////////////////////////////////////////
*/////////////////////////////////////////////////

/// @notice Harvest request
/// @param strategyId The 4 byte truncated keccak256 hash of the strategy name, i.e. bytes4(keccak256("Goldfinch"))
/// @param accountIds The endowment uids
struct HarvestRequest {
bytes4 strategyId;
uint32[] accountIds;
}

/*////////////////////////////////////////////////
METHODS
*/////////////////////////////////////////////////

function executeLocal(
string calldata sourceChain,
Expand All @@ -36,5 +47,7 @@ interface IRouter is IAxelarExecutable {
uint256 amount
) external returns (IVault.VaultActionData memory);

function harvest(HarvestRequest memory _action) external;

function sendTax(address token, uint256 amount, address payee) external;
}
167 changes: 88 additions & 79 deletions contracts/core/router/Router.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,39 @@ contract Router is IRouter, Initializable, AxelarExecutable {
MODIFIERS
*/ ///////////////////////////////////////////////

modifier onlyOneAccount(IVault.VaultActionData memory _action) {
require(_action.accountIds.length == 1, "Only one account allowed");
modifier onlySelf() {
require(msg.sender == address(this));
_;
}

modifier onlySelf() {
require(msg.sender == address(this));
modifier onlyLocalAccountsContract() {
string memory accountAddress = registrar.getAccountsContractAddressByChain(
registrar.thisChain()
);
require(StringToAddress.toAddress(accountAddress) == msg.sender, "Unauthorized local call");
_;
}

modifier onlyAccountsContract(string calldata _sourceChain, string calldata _sourceAddress) {
string memory accountsContractAddress = registrar.getAccountsContractAddressByChain(
_sourceChain
);
require(
keccak256(bytes(_sourceAddress)) == keccak256(bytes(accountsContractAddress)),
"Unauthorized Call"
);
_;
}

modifier notZeroAddress(string calldata _sourceAddress) {
require(StringToAddress.toAddress(_sourceAddress) != address(0), "Unauthorized Call");
_;
}

modifier operatorOnly {
require(
registrar.getVaultOperatorApproved(msg.sender)
);
_;
}

Expand All @@ -50,8 +76,6 @@ contract Router is IRouter, Initializable, AxelarExecutable {
string calldata tokenSymbol,
uint256 amount
) {
// Only one account accepted for deposit calls
require(action.accountIds.length == 1, "Only one account allowed");
// deposit only
require(action.selector == IVault.deposit.selector, "Only deposit accepts tokens");
// amt fwd equal expected amt
Expand Down Expand Up @@ -97,10 +121,6 @@ contract Router is IRouter, Initializable, AxelarExecutable {
else if (_action.selector == IVault.redeemAll.selector) {
return _redeemAll(_params, _action);
}
// HARVEST
else if (_action.selector == IVault.harvest.selector) {
return _harvest(_params, _action);
}
// INVALID SELCTOR
else {
revert("Invalid function selector provided");
Expand All @@ -122,76 +142,82 @@ contract Router is IRouter, Initializable, AxelarExecutable {
if (action.lockAmt > 0) {
// Send tokens to locked vault and call deposit
IERC20Metadata(action.token).safeTransfer(params.lockedVaultAddr, action.lockAmt);
IVault(params.lockedVaultAddr).deposit(action.accountIds[0], action.token, action.lockAmt);
IVault(params.lockedVaultAddr).deposit(action.accountId, action.token, action.lockAmt);
}

if (action.liqAmt > 0) {
// Send tokens to liquid vault and call deposit
IERC20Metadata(action.token).safeTransfer(params.liquidVaultAddr, action.liqAmt);
IVault(params.liquidVaultAddr).deposit(action.accountIds[0], action.token, action.liqAmt);
IVault(params.liquidVaultAddr).deposit(action.accountId, action.token, action.liqAmt);
}
}

// Vault action::Redeem
function _redeem(
LocalRegistrarLib.StrategyParams memory _params,
IVault.VaultActionData memory _action
) internal onlyOneAccount(_action) returns (IVault.VaultActionData memory) {
) internal returns (IVault.VaultActionData memory) {
IVault lockedVault = IVault(_params.lockedVaultAddr);
IVault liquidVault = IVault(_params.liquidVaultAddr);

// Redeem tokens from vaults which sends them from the vault to this contract
IVault.RedemptionResponse memory _redemptionLock = lockedVault.redeem(
_action.accountIds[0],
// Redeem tokens from vaults and then txfer to this contract
IVault.RedemptionResponse memory lockResponse = lockedVault.redeem(
_action.accountId,
_action.lockAmt
);
IERC20Metadata(_redemptionLock.token).safeTransferFrom(
_params.lockedVaultAddr,
address(this),
_redemptionLock.amount
);
if (lockResponse.amount > 0) {
IERC20Metadata(lockResponse.token).safeTransferFrom(
_params.lockedVaultAddr,
address(this),
lockResponse.amount
);
}
_action.lockAmt = lockResponse.amount;

IVault.RedemptionResponse memory _redemptionLiquid = liquidVault.redeem(
_action.accountIds[0],
IVault.RedemptionResponse memory liqResponse = liquidVault.redeem(
_action.accountId,
_action.liqAmt
);
IERC20Metadata(_redemptionLiquid.token).safeTransferFrom(
_params.liquidVaultAddr,
address(this),
_redemptionLiquid.amount
);
if (liqResponse.amount > 0) {
IERC20Metadata(liqResponse.token).safeTransferFrom(
_params.liquidVaultAddr,
address(this),
liqResponse.amount
);
}
_action.liqAmt = liqResponse.amount;

// Update _action with this chain's token address.
// Liquid token should ALWAYS == Locked token
_action.token = _redemptionLiquid.token;
_action.token = liqResponse.token;

// Pack and send the tokens back to Accounts contract
uint256 _redeemedAmt = _redemptionLock.amount + _redemptionLiquid.amount;
_action.lockAmt = _redemptionLock.amount;
_action.liqAmt = _redemptionLiquid.amount;
uint256 redeemedAmt = lockResponse.amount + liqResponse.amount;

if (
(_redemptionLock.status == IVault.VaultActionStatus.POSITION_EXITED) &&
(_redemptionLiquid.status == IVault.VaultActionStatus.POSITION_EXITED)
(lockResponse.status == IVault.VaultActionStatus.POSITION_EXITED) &&
(liqResponse.status == IVault.VaultActionStatus.POSITION_EXITED)
) {
_action.status = IVault.VaultActionStatus.POSITION_EXITED;
} else {
_action.status = IVault.VaultActionStatus.SUCCESS;
}
_action = _prepareToSendTokens(_action, _redeemedAmt);
emit Redeem(_action, _redeemedAmt);

_action = _prepareToSendTokens(_action, redeemedAmt);
emit Redeem(_action, redeemedAmt);
return _action;
}

// Vault action::RedeemAll
function _redeemAll(
LocalRegistrarLib.StrategyParams memory _params,
IVault.VaultActionData memory _action
) internal onlyOneAccount(_action) returns (IVault.VaultActionData memory) {
) internal returns (IVault.VaultActionData memory) {
IVault lockedVault = IVault(_params.lockedVaultAddr);
IVault liquidVault = IVault(_params.liquidVaultAddr);

// Redeem tokens from vaults and txfer them to the Router
IVault.RedemptionResponse memory lockResponse = lockedVault.redeemAll(_action.accountIds[0]);
// Redeem tokens from vaults and then txfer to this contract
IVault.RedemptionResponse memory lockResponse = lockedVault.redeemAll(_action.accountId);
if (lockResponse.amount > 0) {
IERC20Metadata(_action.token).safeTransferFrom(
_params.lockedVaultAddr,
Expand All @@ -201,7 +227,7 @@ contract Router is IRouter, Initializable, AxelarExecutable {
}
_action.lockAmt = lockResponse.amount;

IVault.RedemptionResponse memory liqResponse = liquidVault.redeemAll(_action.accountIds[0]);
IVault.RedemptionResponse memory liqResponse = liquidVault.redeemAll(_action.accountId);
if (liqResponse.amount > 0) {
IERC20Metadata(_action.token).safeTransferFrom(
_params.liquidVaultAddr,
Expand All @@ -211,6 +237,10 @@ contract Router is IRouter, Initializable, AxelarExecutable {
}
_action.liqAmt = liqResponse.amount;

// Update _action with this chain's token address.
// Liquid token should ALWAYS == Locked token
_action.token = liqResponse.token;

// Pack and send the tokens back
uint256 _redeemedAmt = lockResponse.amount + liqResponse.amount;
_action.status = IVault.VaultActionStatus.POSITION_EXITED;
Expand All @@ -220,24 +250,24 @@ contract Router is IRouter, Initializable, AxelarExecutable {
}

// Vault action::Harvest
function _harvest(
LocalRegistrarLib.StrategyParams memory _params,
IVault.VaultActionData memory _action
) internal returns (IVault.VaultActionData memory) {
IVault liquidVault = IVault(_params.liquidVaultAddr);
IVault lockedVault = IVault(_params.lockedVaultAddr);
function harvest(
HarvestRequest memory _action
) external operatorOnly {
LocalRegistrarLib.StrategyParams memory params = registrar.getStrategyParamsById(
_action.strategyId
);
IVault liquidVault = IVault(params.liquidVaultAddr);
IVault lockedVault = IVault(params.lockedVaultAddr);

// Harvest token, transfer to self and update action data
_action.liqAmt = liquidVault.harvest(_action.accountIds);
IERC20Metadata(_action.token).safeTransfer(address(this), _action.liqAmt);
_action.lockAmt = lockedVault.harvest(_action.accountIds);
IERC20Metadata(_action.token).safeTransfer(address(this), _action.lockAmt);
emit RewardsHarvested(_action);
_action.status = IVault.VaultActionStatus.SUCCESS;
IVault.RedemptionResponse memory _harvestLiq = liquidVault.harvest(_action.accountIds);
IERC20Metadata(_harvestLiq.token).safeTransfer(address(this), _harvestLiq.liqAmt);
IVault.RedemptionResponse memory _harvestLock = lockedVault.harvest(_action.accountIds);
IERC20Metadata(_harvestLock.token).safeTransfer(address(this), _harvestLock.lockAmt);

// Send tokens to receiver
uint256 totalAmt = _action.liqAmt + _action.lockAmt;
if (totalAmt == 0) return _action;
uint256 totalAmt = _harvestLiq.amount + _harvestLock.amount;
if (totalAmt == 0) return;

LibAccounts.FeeSetting memory feeSetting = registrar.getFeeSettingsByFeeType(
LibAccounts.FeeTypes.Harvest
Expand All @@ -256,7 +286,6 @@ contract Router is IRouter, Initializable, AxelarExecutable {
totalAmt
);
}
return _action;
}

/*////////////////////////////////////////////////
Expand All @@ -281,30 +310,6 @@ contract Router is IRouter, Initializable, AxelarExecutable {
return _executeWithToken(sourceChain, sourceAddress, payload, tokenSymbol, amount);
}

modifier onlyLocalAccountsContract() {
string memory accountAddress = registrar.getAccountsContractAddressByChain(
registrar.thisChain()
);
require(StringToAddress.toAddress(accountAddress) == msg.sender, "Unauthorized local call");
_;
}

modifier onlyAccountsContract(string calldata _sourceChain, string calldata _sourceAddress) {
string memory accountsContractAddress = registrar.getAccountsContractAddressByChain(
_sourceChain
);
require(
keccak256(bytes(_sourceAddress)) == keccak256(bytes(accountsContractAddress)),
"Unauthorized Call"
);
_;
}

modifier notZeroAddress(string calldata _sourceAddress) {
require(StringToAddress.toAddress(_sourceAddress) != address(0), "Unauthorized Call");
_;
}

function _prepareToSendTokens(
IVault.VaultActionData memory _action,
uint256 _sendAmt
Expand Down Expand Up @@ -514,6 +519,10 @@ contract Router is IRouter, Initializable, AxelarExecutable {
return IAxelarGasService(network.gasReceiver);
}

/*////////////////////////////////////////////////
HELPERS
*/ ////////////////////////////////////////////////

function _stringCompare(string memory s1, string memory s2) internal pure returns (bool result) {
result = (keccak256(abi.encodePacked(s1)) == keccak256(abi.encodePacked(s2)));
}
Expand Down
8 changes: 4 additions & 4 deletions contracts/core/router/RouterLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,22 @@ library RouterLib {
string memory destinationChain,
bytes4 strategyId,
bytes4 selector,
uint32[] memory accountIds,
uint32 accountId,
address token,
uint256 lockAmt,
uint256 liqAmt,
IVault.VaultActionStatus status
) = abi.decode(
_calldata,
(string, bytes4, bytes4, uint32[], address, uint256, uint256, IVault.VaultActionStatus)
(string, bytes4, bytes4, uint32, address, uint256, uint256, IVault.VaultActionStatus)
);

return
IVault.VaultActionData(
destinationChain,
strategyId,
selector,
accountIds,
accountId,
token,
lockAmt,
liqAmt,
Expand All @@ -48,7 +48,7 @@ library RouterLib {
_calldata.destinationChain,
_calldata.strategyId,
_calldata.selector,
_calldata.accountIds,
_calldata.accountId,
_calldata.token,
_calldata.lockAmt,
_calldata.liqAmt,
Expand Down
11 changes: 6 additions & 5 deletions contracts/core/vault/APVault_V1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ contract APVault_V1 is IVault, ERC4626AP, Ownable {

function harvest(
uint32[] calldata accountIds
) public virtual override notPaused onlyApproved returns (uint256 amt) {
) public virtual override notPaused onlyApproved returns (RedemptionResponse memory response) {
response.token = vaultConfig.baseToken; // always harvest the base token
for (uint32 i; i < accountIds.length; i++) {
uint32 accountId = accountIds[i];
uint256 baseTokenValue = IStrategy(vaultConfig.strategy).previewWithdraw(
Expand Down Expand Up @@ -213,18 +214,18 @@ contract APVault_V1 is IVault, ERC4626AP, Ownable {

// Call appropriate harvest method
if (vaultConfig.vaultType == VaultType.LIQUID) {
amt += _harvestLiquid(accountId, yieldBaseTokens, currentExRate_withPrecision, feeSetting);
response.amount += _harvestLiquid(accountId, yieldBaseTokens, currentExRate_withPrecision, feeSetting);
} else {
amt += _harvestLocked(accountId, yieldBaseTokens, currentExRate_withPrecision, feeSetting);
response.amount += _harvestLocked(accountId, yieldBaseTokens, currentExRate_withPrecision, feeSetting);
}
}

// Approve router for the harvest amt
if (amt > 0) {
if (response.amount > 0) {
string memory thisChain = IRegistrar(vaultConfig.registrar).thisChain();
LocalRegistrarLib.NetworkInfo memory network = IRegistrar(vaultConfig.registrar)
.queryNetworkConnection(thisChain);
IERC20Metadata(vaultConfig.baseToken).safeApprove(network.router, amt);
IERC20Metadata(vaultConfig.baseToken).safeApprove(network.router, response.amount);
}
}

Expand Down
Loading

0 comments on commit 0da2d24

Please sign in to comment.