Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ETHEREUM-CONTRACTS] GDA View Function Fixes #1885

Merged
merged 16 commits into from
Mar 19, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
FlowRate
} from "@superfluid-finance/solidity-semantic-money/src/SemanticMoney.sol";
import { TokenMonad } from "@superfluid-finance/solidity-semantic-money/src/TokenMonad.sol";
import { SuperfluidPool } from "./SuperfluidPool.sol";
import { poolIndexDataToPDPoolIndex, SuperfluidPool } from "./SuperfluidPool.sol";
import { SuperfluidPoolDeployerLibrary } from "./SuperfluidPoolDeployerLibrary.sol";
import {
IGeneralDistributionAgreementV1,
Expand All @@ -32,6 +32,7 @@ import { SafeGasLibrary } from "../../libs/SafeGasLibrary.sol";
import { AgreementBase } from "../AgreementBase.sol";
import { AgreementLibrary } from "../AgreementLibrary.sol";


/**
* @title General Distribution Agreement
* @author Superfluid
Expand Down Expand Up @@ -80,6 +81,25 @@ contract GeneralDistributionAgreementV1 is AgreementBase, TokenMonad, IGeneralDi
using SafeCast for int256;
using SemanticMoney for BasicParticle;

struct UniversalIndexData {
int96 flowRate;
uint32 settledAt;
uint256 totalBuffer;
bool isPool;
int256 settledValue;
}

struct PoolMemberData {
address pool;
uint32 poolID; // the slot id in the pool's subs bitmap
}

struct FlowDistributionData {
uint32 lastUpdated;
int96 flowRate;
uint256 buffer; // stored as uint96
}

address public constant SLOTS_BITMAP_LIBRARY_ADDRESS = address(SlotsBitmapLibrary);

address public constant SUPERFLUID_POOL_DEPLOYER_ADDRESS = address(SuperfluidPoolDeployerLibrary);
Expand Down Expand Up @@ -170,6 +190,32 @@ contract GeneralDistributionAgreementV1 is AgreementBase, TokenMonad, IGeneralDi
return data.flowRate;
}

/// @inheritdoc IGeneralDistributionAgreementV1
function getFlow(ISuperfluidToken token, address from, ISuperfluidPool to)
external
view
override
returns (uint256 lastUpdated, int96 flowRate, uint256 deposit)
{
(, FlowDistributionData memory data) = _getFlowDistributionData(token, _getFlowDistributionHash(from, to));
lastUpdated = data.lastUpdated;
flowRate = data.flowRate;
deposit = data.buffer;
}

/// @inheritdoc IGeneralDistributionAgreementV1
function getAccountFlowInfo(ISuperfluidToken token, address account)
external
view
override
returns (uint256 timestamp, int96 flowRate, uint256 deposit)
{
UniversalIndexData memory universalIndexData = _getUIndexData(abi.encode(token), account);
timestamp = universalIndexData.settledAt;
flowRate = universalIndexData.flowRate;
deposit = universalIndexData.totalBuffer;
}

/// @inheritdoc IGeneralDistributionAgreementV1
function estimateFlowDistributionActualFlowRate(
ISuperfluidToken token,
Expand Down Expand Up @@ -428,6 +474,16 @@ contract GeneralDistributionAgreementV1 is AgreementBase, TokenMonad, IGeneralDi
FlowRate oldFlowRate;
}

// solhint-disable-next-line contract-name-camelcase
struct _StackVars_Liquidation {
ISuperfluidToken token;
int256 availableBalance;
address sender;
bytes32 distributionFlowHash;
int256 signedTotalGDADeposit;
address liquidator;
}

/// @inheritdoc IGeneralDistributionAgreementV1
function distributeFlow(
ISuperfluidToken token,
Expand Down Expand Up @@ -482,7 +538,7 @@ contract GeneralDistributionAgreementV1 is AgreementBase, TokenMonad, IGeneralDi
// liquidation case, requestedFlowRate == 0
(int256 availableBalance,,) = token.realtimeBalanceOf(from, flowVars.currentContext.timestamp);
// StackVarsLiquidation used to handle good ol' stack too deep
StackVarsLiquidation memory liquidationData;
_StackVars_Liquidation memory liquidationData;
{
liquidationData.token = token;
liquidationData.sender = from;
Expand Down Expand Up @@ -612,7 +668,7 @@ contract GeneralDistributionAgreementV1 is AgreementBase, TokenMonad, IGeneralDi
}
}

function _makeLiquidationPayouts(StackVarsLiquidation memory data) internal {
function _makeLiquidationPayouts(_StackVars_Liquidation memory data) internal {
(, FlowDistributionData memory flowDistributionData) =
_getFlowDistributionData(ISuperfluidToken(data.token), data.distributionFlowHash);
int256 signedSingleDeposit = flowDistributionData.buffer.toInt256();
Expand Down Expand Up @@ -871,7 +927,7 @@ contract GeneralDistributionAgreementV1 is AgreementBase, TokenMonad, IGeneralDi
address pool
) internal view override returns (PDPoolIndex memory) {
ISuperfluidPool.PoolIndexData memory data = SuperfluidPool(pool).getIndex();
return SuperfluidPool(pool).poolIndexDataToPDPoolIndex(data);
return poolIndexDataToPDPoolIndex(data);
}

function _setPDPIndex(bytes memory eff, address pool, PDPoolIndex memory p)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,36 @@ import { BeaconProxiable } from "../../upgradability/BeaconProxiable.sol";
import { IPoolMemberNFT } from "../../interfaces/agreements/gdav1/IPoolMemberNFT.sol";
import { SafeGasLibrary } from "../../libs/SafeGasLibrary.sol";

using SafeCast for uint256;
using SafeCast for int256;

function toSemanticMoneyUnit(uint128 units) pure returns (Unit) {
// @note safe upcasting from uint128 to uint256
// and use of safecast library for downcasting from uint256 to int128
return Unit.wrap(uint256(units).toInt256().toInt128());
}

function poolIndexDataToWrappedParticle(ISuperfluidPool.PoolIndexData memory data)
pure
returns (BasicParticle memory wrappedParticle)
{
wrappedParticle = BasicParticle({
_settled_at: Time.wrap(data.wrappedSettledAt),
_flow_rate: FlowRate.wrap(int128(data.wrappedFlowRate)), // upcast from int96 is safe
_settled_value: Value.wrap(data.wrappedSettledValue)
});
}

function poolIndexDataToPDPoolIndex(ISuperfluidPool.PoolIndexData memory data)
pure
returns (PDPoolIndex memory pdPoolIndex)
{
pdPoolIndex = PDPoolIndex({
total_units: toSemanticMoneyUnit(data.totalUnits),
_wrapped_particle: poolIndexDataToWrappedParticle(data)
});
}

/**
* @title SuperfluidPool
* @author Superfluid
Expand All @@ -34,8 +64,6 @@ import { SafeGasLibrary } from "../../libs/SafeGasLibrary.sol";
*/
contract SuperfluidPool is ISuperfluidPool, BeaconProxiable {
using SemanticMoney for BasicParticle;
using SafeCast for uint256;
using SafeCast for int256;

GeneralDistributionAgreementV1 public immutable GDA;

Expand Down Expand Up @@ -214,6 +242,26 @@ contract SuperfluidPool is ISuperfluidPool, BeaconProxiable {
return Value.unwrap(PDPoolMemberMU(pdPoolIndex, pdPoolMember).rtb(Time.wrap(time)));
}

/// @inheritdoc ISuperfluidPool
function getMemberData(address memberAddr) external view override returns (MemberData memory memberData) {
return _membersData[memberAddr];
}

/// @inheritdoc ISuperfluidPool
function getTotalAmountReceivedByMember(address memberAddr) external view override returns (uint256) {
0xdavinchee marked this conversation as resolved.
Show resolved Hide resolved
MemberData memory memberData = _membersData[memberAddr];

// max timestamp is uint32.max
return uint256(
Value.unwrap(
// PDPoolMemberMU(poolIndex, memberData)
PDPoolMemberMU(poolIndexDataToPDPoolIndex(_index), _memberDataToPDPoolMember(memberData)).settle(
Time.wrap(uint32(block.timestamp))
).m._settled_value
)
);
}

/// @inheritdoc ISuperfluidPool
function getMemberFlowRate(address memberAddr) external view override returns (int96) {
uint128 units = _getUnits(memberAddr);
Expand All @@ -222,29 +270,6 @@ contract SuperfluidPool is ISuperfluidPool, BeaconProxiable {
else return (_index.wrappedFlowRate * uint256(units).toInt256()).toInt96();
}

function _poolIndexDataToWrappedParticle(PoolIndexData memory data)
internal
pure
returns (BasicParticle memory wrappedParticle)
{
wrappedParticle = BasicParticle({
_settled_at: Time.wrap(data.wrappedSettledAt),
_flow_rate: FlowRate.wrap(int128(data.wrappedFlowRate)), // upcast from int96 is safe
_settled_value: Value.wrap(data.wrappedSettledValue)
});
}

function poolIndexDataToPDPoolIndex(PoolIndexData memory data)
public
pure
returns (PDPoolIndex memory pdPoolIndex)
{
pdPoolIndex = PDPoolIndex({
total_units: _toSemanticMoneyUnit(data.totalUnits),
_wrapped_particle: _poolIndexDataToWrappedParticle(data)
});
}

function _pdPoolIndexToPoolIndexData(PDPoolIndex memory pdPoolIndex)
internal
pure
Expand All @@ -264,7 +289,7 @@ contract SuperfluidPool is ISuperfluidPool, BeaconProxiable {
returns (PDPoolMember memory pdPoolMember)
{
pdPoolMember = PDPoolMember({
owned_units: _toSemanticMoneyUnit(memberData.ownedUnits),
owned_units: toSemanticMoneyUnit(memberData.ownedUnits),
_synced_particle: BasicParticle({
_settled_at: Time.wrap(memberData.syncedSettledAt),
_flow_rate: FlowRate.wrap(int128(memberData.syncedFlowRate)), // upcast from int96 is safe
Expand All @@ -274,12 +299,6 @@ contract SuperfluidPool is ISuperfluidPool, BeaconProxiable {
});
}

function _toSemanticMoneyUnit(uint128 units) internal pure returns (Unit) {
// @note safe upcasting from uint128 to uint256
// and use of safecast library for downcasting from uint256 to int128
return Unit.wrap(uint256(units).toInt256().toInt128());
}

function _pdPoolMemberToMemberData(PDPoolMember memory pdPoolMember, int256 claimedValue)
internal
pure
Expand Down Expand Up @@ -391,7 +410,7 @@ contract SuperfluidPool is ISuperfluidPool, BeaconProxiable {

uint32 time = uint32(ISuperfluid(superToken.getHost()).getNow());
Time t = Time.wrap(time);
Unit wrappedUnits = _toSemanticMoneyUnit(newUnits);
Unit wrappedUnits = toSemanticMoneyUnit(newUnits);

PDPoolIndex memory pdPoolIndex = poolIndexDataToPDPoolIndex(_index);
MemberData memory memberData = _membersData[memberAddr];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,27 @@ library SuperTokenV1Library {
(lastUpdated, flowRate, deposit, owedDeposit) = cfa.getFlow(token, sender, receiver);
}

/**
* @dev get flow info of a distributor to a pool for given token
* @param token The token used in flow
* @param distributor The sitributor of the flow
* @param pool The GDA pool
* @return lastUpdated Timestamp of flow creation or last flowrate change
* @return flowRate The flow rate
* @return deposit The amount of deposit the flow
*/
function getGDAFlowInfo(ISuperToken token, address distributor, ISuperfluidPool pool)
internal view
returns(uint256 lastUpdated, int96 flowRate, uint256 deposit)
{
(, IGeneralDistributionAgreementV1 gda) = _getHostAndGDA(token);
return gda.getFlow(token, distributor, pool);
}

/* function getGDAFlowInfo(ISuperToken token, address distributor, ISuperfluidPool pool) */
/* { */
/* } */

/**
* @dev get net flow rate for given account for given token (CFA + GDA)
* @param token Super token address
Expand Down Expand Up @@ -893,7 +914,72 @@ library SuperTokenV1Library {
}

/**
* @dev calculate buffer for a flow rate
* @dev get the aggregated flow info of the account (CFA + GDA)
* @param token Super token address
* @param account Account to query
* @return cfaLastUpdated Timestamp of the last change of the CFA net flow
* @return gdaLastUpdated Timestamp of the last change of the GDA net flow
* @return flowRate The net flow rate of token for account
* @return deposit The sum of all deposits for account's flows
* @return owedDeposit The sum of all owed deposits for account's flows
*/
function getAccountFlowInfo(ISuperToken token, address account)
internal
view
returns (uint256 cfaLastUpdated, uint256 gdaLastUpdated, int96 flowRate, uint256 deposit, uint256 owedDeposit)
{
(, IConstantFlowAgreementV1 cfa) = _getHostAndCFA(token);
(, IGeneralDistributionAgreementV1 gda) = _getHostAndGDA(token);

(uint256 lastUpdatedCFA, int96 cfaNetFlowRate, uint256 cfaDeposit, uint256 cfaOwedDeposit) =
cfa.getAccountFlowInfo(token, account);
(uint256 lastUpdatedGDA, int96 gdaNetFlowRate, uint256 gdaDeposit) = gda.getAccountFlowInfo(token, account);
int96 accountNetFlowRate = cfaNetFlowRate + gdaNetFlowRate;
uint256 accountDeposit = cfaDeposit + gdaDeposit;

return (lastUpdatedCFA, lastUpdatedGDA, accountNetFlowRate, accountDeposit, cfaOwedDeposit);
}

/**
* @dev get the aggregated CFA flow info of the account
* @param token Super token address
* @param account Account to query
* @return lastUpdated Timestamp of the last change of the net flow
* @return flowRate The net flow rate of token for account
* @return deposit The sum of all deposits for account's flows
* @return owedDeposit The sum of all owed deposits for account's flows
*/
function getCFAAccountFlowInfo(ISuperToken token, address account)
internal
view
returns (uint256 lastUpdated, int96 flowRate, uint256 deposit, uint256 owedDeposit)
{
(, IConstantFlowAgreementV1 cfa) = _getHostAndCFA(token);
return cfa.getAccountFlowInfo(token, account);
}

/**
* @dev get the aggregated GDA flow info of the account
* @param token Super token address
* @param account Account to query
* @return lastUpdated Timestamp of the last change of the net flow
* @return flowRate The net flow rate of token for account
* @return deposit The sum of all deposits for account's flows
* @return owedDeposit The sum of all owed deposits for account's flows
*/
function getGDAAccountFlowInfo(ISuperToken token, address account)
internal
view
returns (uint256 lastUpdated, int96 flowRate, uint256 deposit, uint256 owedDeposit)
{
(, IGeneralDistributionAgreementV1 gda) = _getHostAndGDA(token);
(lastUpdated, flowRate, deposit) = gda.getAccountFlowInfo(token, account);
}

/**
* @notice calculate buffer for a CFA/GDA flow rate
* @dev Even though we are using the CFA, the logic for calculating buffer is the same in the GDA
* and a change in the buffer logic in either means it is a BREAKING change
* @param token The token used in flow
* @param flowRate The flowrate to calculate the needed buffer for
* @return bufferAmount The buffer amount based on flowRate, liquidationPeriod and minimum deposit
Expand Down
Loading
Loading