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

Commit

Permalink
merge + conflict fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrey authored and Andrey committed Aug 15, 2023
2 parents 6ae7c75 + da78372 commit 1dacaf0
Show file tree
Hide file tree
Showing 34 changed files with 2,513 additions and 2,131 deletions.
8 changes: 4 additions & 4 deletions .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ GANACHE_RPC_URL="http://127.0.0.1:8545"
GANACHE_PRIVATE_KEY="put your key here plz"

## Account private keys
DEPLOYER_KEY="0x036803cfb0810e2d3e9a8773179e47a7747155d17974a619f6cd130be0590556" # Dummy wallet for CI purposes replace with own
DEPLOYER_ADDRESS="0xd14f192084c1cdd017caacb1d9109f0507fc1c46" # dummy wallet for ci purposes replace with own
PROXY_ADMIN_KEY="0x036803cfb0810e2d3e9a8773179e47a7747155d17974a619f6cd130be0590556" # Dummy wallet for CI purposes replace with own
PROXY_ADMIN_ADDRESS="0xd14f192084c1cdd017caacb1d9109f0507fc1c46" # dummy wallet for ci purposes replace with own
DEPLOYER_KEY="0x13fd300b4664e8329b02d9ce1e1b779879c3dc99854bf596b9bac22872786b24" # Dummy wallet for CI purposes replace with own
DEPLOYER_ADDRESS="0xc9c192A1ef0BbEd747883F17B834CFD5F505a920" # dummy wallet for ci purposes replace with own
PROXY_ADMIN_KEY="0x6b2e3df50e192eacea6f44884555d5b1000ddec6d8124d2e9df2fd81468d26ce" # Dummy wallet for CI purposes replace with own
PROXY_ADMIN_ADDRESS="0x3584aC1A3353B400F4791788Fa52325054203DC3" # dummy wallet for ci purposes replace with own
AP_TEAM_1_KEY="0x036803cfb0810e2d3e9a8773179e47a7747155d17974a619f6cd130be0590556" # Dummy wallet for CI purposes replace with own
AP_TEAM_1_ADDRESS="0xd14f192084c1cdd017caacb1d9109f0507fc1c46" # dummy wallet for ci purposes replace with own
AP_TEAM_2_KEY="0x02490597040bcc85ebe1a32fb86947a7e8438336546dc2b40d0073155928afc0" # Dummy wallet for CI purposes replace with own
Expand Down
122 changes: 58 additions & 64 deletions contracts/core/accounts/facets/AccountsStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ contract AccountsStrategy is
{
using SafeERC20 for IERC20;

uint256 constant FIFTY_PERCENT_BIG_NUMBA_RATE =
(50 * LibAccounts.BIG_NUMBA_BASIS) / LibAccounts.PERCENT_BASIS;

/**
* @notice This function that allows users to deposit into a yield strategy using tokens from their locked or liquid account in an endowment.
* @dev Allows the owner of an endowment to invest tokens into specified yield vaults.
Expand All @@ -47,6 +50,10 @@ contract AccountsStrategy is
AccountStorage.State storage state = LibAccounts.diamondStorage();
AccountStorage.Endowment storage tempEndowment = state.ENDOWMENTS[id];

if (investRequest.lockAmt == 0 && investRequest.liquidAmt == 0) {
revert ZeroAmount();
}

// check if the msg sender is either the owner or their delegate address and
// that they have the power to manage the investments for an account balance
if (investRequest.lockAmt > 0) {
Expand Down Expand Up @@ -91,8 +98,7 @@ contract AccountsStrategy is
"Insufficient Balance"
);
require(
IterableMapping.get(state.STATES[id].balances.liquid, tokenAddress) >=
investRequest.liquidAmt,
IterableMapping.get(state.STATES[id].balances.liquid, tokenAddress) >= investRequest.liquidAmt,
"Insufficient Balance"
);

Expand All @@ -101,9 +107,16 @@ contract AccountsStrategy is
"Token not approved"
);

uint256 investAmt = investRequest.lockAmt + investRequest.liquidAmt;

uint32[] memory accts = new uint32[](1);
accts[0] = id;

IterableMapping.decr(state.STATES[id].balances.locked, tokenAddress, investRequest.lockAmt);
IterableMapping.decr(state.STATES[id].balances.liquid, tokenAddress, investRequest.liquidAmt);
state.STATES[id].activeStrategies[investRequest.strategy] = true;
emit EndowmentInvested(id);

// Strategy exists on the local network
if (Validator.compareStrings(state.config.networkName, stratParams.network)) {
IVault.VaultActionData memory payload = IVault.VaultActionData({
Expand All @@ -118,28 +131,16 @@ contract AccountsStrategy is
});
bytes memory packedPayload = RouterLib.packCallData(payload);

IERC20(tokenAddress).safeTransfer(
thisNetwork.router,
(investRequest.lockAmt + investRequest.liquidAmt)
);
IERC20(tokenAddress).safeTransfer(thisNetwork.router, investAmt);
IVault.VaultActionData memory response = IRouter(thisNetwork.router).executeWithTokenLocal(
state.config.networkName,
AddressToString.toString(address(this)),
packedPayload,
investRequest.token,
(investRequest.lockAmt + investRequest.liquidAmt)
investAmt
);

if (response.status == IVault.VaultActionStatus.SUCCESS) {
IterableMapping.decr(state.STATES[id].balances.locked, tokenAddress, investRequest.lockAmt);
IterableMapping.decr(
state.STATES[id].balances.liquid,
tokenAddress,
investRequest.liquidAmt
);
state.STATES[id].activeStrategies[investRequest.strategy] = true;
emit EndowmentInvested(response.status);
} else {
if (response.status != IVault.VaultActionStatus.SUCCESS) {
revert InvestFailed(response.status);
}
}
Expand All @@ -166,8 +167,7 @@ contract AccountsStrategy is
_payForGasWithAccountBalance(
id,
tokenAddress,
investRequest.lockAmt,
investRequest.liquidAmt,
(investRequest.liquidAmt * LibAccounts.BIG_NUMBA_BASIS) / investAmt,
(investRequest.gasFee - gasFwdGas)
);
}
Expand All @@ -178,25 +178,19 @@ contract AccountsStrategy is
AddressToString.toString(network.router),
packedPayload,
investRequest.token,
(investRequest.lockAmt + investRequest.liquidAmt),
investAmt,
tokenAddress,
investRequest.gasFee,
state.ENDOWMENTS[id].gasFwd
);
IERC20(tokenAddress).safeApprove(
thisNetwork.axelarGateway,
(investRequest.lockAmt + investRequest.liquidAmt)
);
IERC20(tokenAddress).safeApprove(thisNetwork.axelarGateway, investAmt);
IAxelarGateway(thisNetwork.axelarGateway).callContractWithToken(
stratParams.network,
AddressToString.toString(network.router),
packedPayload,
investRequest.token,
(investRequest.lockAmt + investRequest.liquidAmt)
investAmt
);
IterableMapping.decr(state.STATES[id].balances.locked, tokenAddress, investRequest.lockAmt);
IterableMapping.decr(state.STATES[id].balances.liquid, tokenAddress, investRequest.liquidAmt);
state.STATES[id].activeStrategies[investRequest.strategy] = true;
}
}

Expand All @@ -211,6 +205,10 @@ contract AccountsStrategy is
AccountStorage.State storage state = LibAccounts.diamondStorage();
AccountStorage.Endowment storage tempEndowment = state.ENDOWMENTS[id];

if (redeemRequest.lockAmt == 0 && redeemRequest.liquidAmt == 0) {
revert ZeroAmount();
}

// check if the msg sender is either the owner or their delegate address and
// that they have the power to manage the investments for an account balance
if (redeemRequest.lockAmt > 0) {
Expand Down Expand Up @@ -271,10 +269,12 @@ contract AccountsStrategy is
if (response.status == IVault.VaultActionStatus.SUCCESS) {
IterableMapping.incr(state.STATES[id].balances.locked, tokenAddress, response.lockAmt);
IterableMapping.incr(state.STATES[id].balances.liquid, tokenAddress, response.liqAmt);
emit EndowmentRedeemed(id, response.status);
} else if (response.status == IVault.VaultActionStatus.POSITION_EXITED) {
IterableMapping.incr(state.STATES[id].balances.locked, tokenAddress, response.lockAmt);
IterableMapping.incr(state.STATES[id].balances.liquid, tokenAddress, response.liqAmt);
state.STATES[id].activeStrategies[redeemRequest.strategy] = false;
emit EndowmentRedeemed(id, response.status);
} else {
revert RedeemFailed(response.status);
}
Expand All @@ -299,11 +299,12 @@ contract AccountsStrategy is
redeemRequest.gasFee
);
if (gasFwdGas < redeemRequest.gasFee) {
uint256 gasRateFromLiq_withPrecision = (redeemRequest.liquidAmt *
LibAccounts.BIG_NUMBA_BASIS) / (redeemRequest.liquidAmt + redeemRequest.lockAmt);
_payForGasWithAccountBalance(
id,
tokenAddress,
redeemRequest.lockAmt,
redeemRequest.liquidAmt,
gasRateFromLiq_withPrecision,
(redeemRequest.gasFee - gasFwdGas)
);
}
Expand Down Expand Up @@ -336,10 +337,10 @@ contract AccountsStrategy is
AccountStorage.State storage state = LibAccounts.diamondStorage();
AccountStorage.Endowment storage tempEndowment = state.ENDOWMENTS[id];

require(
redeemAllRequest.redeemLiquid || redeemAllRequest.redeemLocked,
"Must redeem at least one of Locked/Liquid"
);
if (!redeemAllRequest.redeemLiquid && !redeemAllRequest.redeemLocked) {
revert ZeroAmount();
}

if (redeemAllRequest.redeemLocked) {
require(
Validator.canChange(
Expand Down Expand Up @@ -402,7 +403,7 @@ contract AccountsStrategy is
IterableMapping.incr(state.STATES[id].balances.locked, tokenAddress, response.lockAmt);
IterableMapping.incr(state.STATES[id].balances.liquid, tokenAddress, response.liqAmt);
state.STATES[id].activeStrategies[redeemAllRequest.strategy] = false;
emit EndowmentRedeemed(response.status);
emit EndowmentRedeemed(id, response.status);
} else {
revert RedeemAllFailed(response.status);
}
Expand Down Expand Up @@ -430,8 +431,7 @@ contract AccountsStrategy is
_payForGasWithAccountBalance(
id,
tokenAddress,
1, // Split evenly
1,
FIFTY_PERCENT_BIG_NUMBA_RATE,
(redeemAllRequest.gasFee - gasFwdGas)
);
}
Expand Down Expand Up @@ -468,6 +468,7 @@ contract AccountsStrategy is
) {
IterableMapping.incr(state.STATES[id].balances.locked, response.token, response.lockAmt);
IterableMapping.incr(state.STATES[id].balances.liquid, response.token, response.liqAmt);
emit EndowmentRedeemed(id, response.status);
return true;
}
// Redeem/RedeemAll Cases
Expand All @@ -481,11 +482,13 @@ contract AccountsStrategy is
if (response.status == IVault.VaultActionStatus.SUCCESS) {
IterableMapping.incr(state.STATES[id].balances.locked, response.token, response.lockAmt);
IterableMapping.incr(state.STATES[id].balances.liquid, response.token, response.liqAmt);
emit EndowmentRedeemed(id, response.status);
return true;
} else if (response.status == IVault.VaultActionStatus.POSITION_EXITED) {
IterableMapping.incr(state.STATES[id].balances.locked, response.token, response.lockAmt);
IterableMapping.incr(state.STATES[id].balances.liquid, response.token, response.liqAmt);
state.STATES[id].activeStrategies[response.strategyId] = false;
emit EndowmentRedeemed(id, response.status);
return true;
}
} else {
Expand Down Expand Up @@ -570,50 +573,41 @@ contract AccountsStrategy is
* We split the gas payment proprotionally between locked and liquid if possible and
* use liquid funds for locked gas needs, but not the other way around in the case of a shortage.
* Revert if the combined balances of the account cannot cover both the investment request and the gas payment.
* @param id Endowment ID
* @param token Token address
* @param gasRateFromLiq_withPrecision Percentage of gas to pay from liquid portion
* @param gasRemaining Amount of gas to be payed from locked & liquid balances
*/
function _payForGasWithAccountBalance(
uint32 id,
address token,
uint256 lockAmt,
uint256 liqAmt,
uint256 gasRateFromLiq_withPrecision,
uint256 gasRemaining
) internal {
AccountStorage.State storage state = LibAccounts.diamondStorage();

uint256 lockBal = IterableMapping.get(state.STATES[id].balances.locked, token);
uint256 liqBal = IterableMapping.get(state.STATES[id].balances.liquid, token);
uint256 sendAmt = lockAmt + liqAmt;

// Split gas proportionally between liquid and lock amts
uint256 liqGas = (gasRemaining * ((liqAmt * LibAccounts.BIG_NUMBA_BASIS) / sendAmt)) /
LibAccounts.BIG_NUMBA_BASIS;
uint256 liqGas = (gasRemaining * gasRateFromLiq_withPrecision) / LibAccounts.BIG_NUMBA_BASIS;
uint256 lockGas = gasRemaining - liqGas;

uint256 lockNeed = lockGas + lockAmt;
uint256 liqNeed = liqGas + liqAmt;

// Cases:
// 1) lockBal and liqBal each cover the respective needs
if ((lockNeed <= lockBal) && (liqNeed <= liqBal)) {
if ((lockGas <= lockBal) && (liqGas <= liqBal)) {
IterableMapping.decr(state.STATES[id].balances.locked, token, lockGas);
IterableMapping.decr(state.STATES[id].balances.locked, token, liqGas);
} else if ((lockNeed > lockBal) && (liqNeed <= liqBal)) {
// 2) lockBal does not cover lockNeeds, liqBal can cover deficit in addition to liqNeeds
if ((lockNeed - lockBal) <= (liqBal - liqNeed)) {
IterableMapping.set(state.STATES[id].balances.locked, token, 0);
IterableMapping.decr(
state.STATES[id].balances.liquid,
token,
(liqGas + (lockNeed - lockBal))
);
}
// 3) lockBal does not cover lockNeeds and liqBal cannot cover -> revert
else {
IterableMapping.decr(state.STATES[id].balances.liquid, token, liqGas);
} else if ((lockGas > lockBal) && (liqGas <= liqBal)) {
// 2) lockBal does not cover lockGas, check if liqBal can cover deficit in addition to liqGas
uint256 lockNeedDeficit = lockGas - lockBal;
if (lockNeedDeficit <= (liqBal - liqGas)) {
IterableMapping.decr(state.STATES[id].balances.locked, token, (lockGas - lockNeedDeficit));
IterableMapping.decr(state.STATES[id].balances.liquid, token, (liqGas + lockNeedDeficit));
} else {
// 3) lockBal does not cover lockGas and liqBal cannot cover -> revert
revert InsufficientFundsForGas(id);
}
}
// 4) lockBal covers lockNeeds, liqBal does not cover liqNeeds -> revert
else {
} else {
// 4) lockBal covers lockGas, liqBal does not cover liqGas -> revert
revert InsufficientFundsForGas(id);
}
}
Expand Down
1 change: 0 additions & 1 deletion contracts/core/accounts/facets/AccountsSwapRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import "hardhat/console.sol";
import {IterableMapping} from "../../../lib/IterableMappingAddr.sol";

uint256 constant ACCEPTABLE_PRICE_DELAY = 300; // 5 minutes, in seconds
Expand Down
4 changes: 2 additions & 2 deletions contracts/core/accounts/interfaces/IAccountsEvents.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ interface IAccountsEvents {
address[] add,
address[] remove
);
event EndowmentInvested(IVault.VaultActionStatus);
event EndowmentRedeemed(IVault.VaultActionStatus);
event EndowmentInvested(uint256 endowId);
event EndowmentRedeemed(uint256 endowId, IVault.VaultActionStatus);
event RefundNeeded(IVault.VaultActionData);
event UnexpectedTokens(IVault.VaultActionData);
}
3 changes: 2 additions & 1 deletion contracts/core/accounts/interfaces/IAccountsStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ import {AccountMessages} from "../message.sol";
* @title AccountsStrategy
*/
interface IAccountsStrategy {
error InsufficientFundsForGas(uint32);
error InsufficientFundsForGas(uint32 endowId);
error InvestFailed(IVault.VaultActionStatus);
error RedeemFailed(IVault.VaultActionStatus);
error RedeemAllFailed(IVault.VaultActionStatus);
error UnexpectedResponse(IVault.VaultActionData);
error UnexpectedCaller(IVault.VaultActionData, string, string);
error ZeroAmount();

struct NetworkInfo {
uint256 chainId;
Expand Down
8 changes: 7 additions & 1 deletion contracts/core/index-fund/IIndexFund.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,15 @@ interface IIndexFund {
event ActiveFundUpdated(uint256 fundId);
event StateUpdated();

/*////////////////////////////////////////////////
ERRORS
*/ ////////////////////////////////////////////////
error InvalidAddress(string param);
error InvalidToken();

/*////////////////////////////////////////////////
ENDPOINTS
*/ ////////////////////////////////////////////////
*/ ////////////////////////////////////////////////

struct StateResponse {
uint256 activeFund; // index ID of the Active IndexFund
Expand Down
Loading

0 comments on commit 1dacaf0

Please sign in to comment.