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] [WIP] cheaper balance checks & more #2040

Draft
wants to merge 5 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/ethereum-contracts/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ All notable changes to the ethereum-contracts will be documented in this file.

This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [UNRELEASED]

### Added

- `SuperTokenV1Library`
- overloaded `claimAll` for the msg.sender to claim for themselves
- added `flowWithCtx` and `flowFromWithCtx`

## [v1.12.0]

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1348,8 +1348,7 @@ contract ConstantFlowAgreementV1 is
// do not enforce balance checks during callbacks for the appCreditToken
if (currentContext.callType != ContextDefinitions.CALL_INFO_CALL_TYPE_APP_CALLBACK ||
currentContext.appCreditToken != token) {
(int256 availableBalance,,) = token.realtimeBalanceOf(flowSender, currentContext.timestamp);
if (availableBalance < 0) {
if (! token.hasRealtimeBalanceOfAtLeast(flowSender, currentContext.timestamp, 0)) {
revert CFA_INSUFFICIENT_BALANCE();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,38 @@ contract GeneralDistributionAgreementV1 is AgreementBase, TokenMonad, IGeneralDi
owedBuffer = 0;
}

function hasRealtimeBalanceOfAtLeast(ISuperfluidToken token, address account, uint256 time, int256 minBalance)
public view override returns (bool)
{
UniversalIndexData memory universalIndexData = _getUIndexData(abi.encode(token), account);

int256 balance;
if (_isPool(token, account)) {
balance = ISuperfluidPool(account).getDisconnectedBalance(uint32(time));
} else {
// this is outflow and can only be negative or 0
balance = Value.unwrap(_getBasicParticleFromUIndex(universalIndexData).rtb(Time.wrap(uint32(time))));
}
// subtract buffer
balance -= universalIndexData.totalBuffer.toInt256();

{
if (balance >= minBalance) {
return true; // it can only increase from here, so we can stop calculating
}
(uint32[] memory slotIds, bytes32[] memory pidList) = _listPoolConnectionIds(token, account);
for (uint256 i = 0; i < slotIds.length; ++i) {
address pool = address(uint160(uint256(pidList[i])));
(bool exist, PoolMemberData memory poolMemberData) =
_getPoolMemberData(token, account, ISuperfluidPool(pool));
assert(exist);
assert(poolMemberData.pool == pool);
balance += ISuperfluidPool(pool).getClaimable(account, uint32(time));
}
}
return balance >= minBalance;
}

/// @dev ISuperAgreement.realtimeBalanceOf implementation
function realtimeBalanceOfNow(ISuperfluidToken token, address account)
external
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ library SuperTokenV1Library {
} // else no change, do nothing
return true;
} else {
// can't set negative flowrate
revert IConstantFlowAgreementV1.CFA_INVALID_FLOW_RATE();
}
}
Expand Down Expand Up @@ -430,8 +431,8 @@ library SuperTokenV1Library {
* @param token The token used in flow
* @param flowOperator The address given flow permissions
* @param allowCreate creation permissions
* @param allowCreate update permissions
* @param allowCreate deletion permissions
* @param allowUpdate update permissions
* @param allowDelete deletion permissions
* @param flowRateAllowance The allowance provided to flowOperator
*/
function setFlowPermissions(
Expand Down Expand Up @@ -802,7 +803,78 @@ library SuperTokenV1Library {
/** CFA With CTX FUNCTIONS ************************************* */

/**
* @dev Create flow with context and userData
* @dev Set CFA flowrate with context
* @param token Super token address
* @param receiver The receiver of the flow
* @param flowRate The wanted flowrate in wad/second. Only positive values are valid here.
* @param ctx Context bytes (see ISuperfluid.sol for Context struct)
* @return newCtx The updated context after the execution of the agreement function
*/
function flowWithCtx(
ISuperToken token,
address receiver,
int96 flowRate,
bytes memory ctx
) internal returns (bytes memory newCtx) {
// note: from the lib's perspective, the caller is "this", NOT "msg.sender"
address sender = address(this);
int96 prevFlowRate = getCFAFlowRate(token, sender, receiver);

if (flowRate > 0) {
if (prevFlowRate == 0) {
return createFlowWithCtx(token, receiver, flowRate, ctx);
} else if (prevFlowRate != flowRate) {
return updateFlowWithCtx(token, receiver, flowRate, ctx);
} // else no change, do nothing
return ctx;
} else if (flowRate == 0) {
if (prevFlowRate > 0) {
return deleteFlowWithCtx(token, sender, receiver, ctx);
} // else no change, do nothing
return ctx;
} else {
// can't set negative flowrate
revert IConstantFlowAgreementV1.CFA_INVALID_FLOW_RATE();
}
}

/**
* @notice Like `flowFrom`, with context
* @param token Super token address
* @param sender The sender of the flow
* @param receiver The receiver of the flow
* @param flowRate The wanted flowRate in wad/second. Only positive values are valid here.
* @param ctx Context bytes (see ISuperfluid.sol for Context struct)
* @return newCtx The updated context after the execution of the agreement function
*/
function flowFromWithCtx(
ISuperToken token,
address sender,
address receiver,
int96 flowRate,
bytes memory ctx
) internal returns (bytes memory newCtx) {
int96 prevFlowRate = getCFAFlowRate(token, sender, receiver);

if (flowRate > 0) {
if (prevFlowRate == 0) {
return createFlowFromWithCtx(token, sender, receiver, flowRate, ctx);
} else if (prevFlowRate != flowRate) {
return updateFlowFromWithCtx(token, sender, receiver, flowRate, ctx);
} // else no change, do nothing
return ctx;
} else if (flowRate == 0) {
if (prevFlowRate > 0) {
return deleteFlowFromWithCtx(token, sender, receiver, ctx);
} // else no change, do nothing
return ctx;
} else {
revert IConstantFlowAgreementV1.CFA_INVALID_FLOW_RATE();
}
}

/**
* @dev Create flow with context
* @param token The token to flow
* @param receiver The receiver of the flow
* @param flowRate The desired flowRate
Expand Down Expand Up @@ -1221,6 +1293,16 @@ library SuperTokenV1Library {
return createPool(token, address(this));
}

/**
* @dev Claims all tokens from the pool for the msg.sender
* @param token The Super Token address.
* @param pool The Superfluid Pool to claim from.
* @return A boolean value indicating whether the claim was successful.
*/
function claimAll(ISuperToken token, ISuperfluidPool pool) internal returns (bool) {
return claimAll(token, pool, address(this), new bytes(0));
}

/**
* @dev Claims all tokens from the pool.
* @param token The Super Token address.
Expand Down Expand Up @@ -1368,6 +1450,8 @@ library SuperTokenV1Library {

/**
* @dev Tries to distribute flow at `requestedFlowRate` of `token` from `from` to `pool`.
* Note: the "actual" flowrate set can also be less than `requestedFlowRate, depending on the
* current total pool units. In order to know beforehand, use `estimateDistributionActualAmount`.
* NOTE: The ability to set the `from` argument is needed only when liquidating a GDA flow.
* The GDA currently doesn't have ACL support.
* @param token The Super Token address.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,4 +277,17 @@ abstract contract IGeneralDistributionAgreementV1 is ISuperAgreement {
view
virtual
returns (bool);


function hasRealtimeBalanceOfAtLeast(
ISuperfluidToken token,
address account,
uint256 time,
int256 minAmount
)
external
view
virtual
returns (bool);

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,4 @@ interface ISuperAgreement {
uint256 deposit,
uint256 owedDeposit
);

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ interface ISuperfluidToken {
/**
* @dev Encoded liquidation type data mainly used for handling stack to deep errors
*
* @custom:note
* @custom:note
* - version: 1
* - liquidationType key:
* - 0 = reward account receives reward (PIC period)
Expand Down Expand Up @@ -83,6 +83,10 @@ interface ISuperfluidToken {
uint256 owedDeposit,
uint256 timestamp);

function hasRealtimeBalanceOfAtLeast(address account, uint256 timestamp, int256 minBalance)
external view
returns (bool);

/**
* @notice Check if account is critical
* @dev A critical account is when availableBalance < 0
Expand Down Expand Up @@ -231,7 +235,7 @@ interface ISuperfluidToken {
* @dev Update agreement state slot
* @param account Account to be updated
*
* @custom:note
* @custom:note
* - To clear the storage out, provide zero-ed array of intended length
*/
function updateAgreementStateSlot(
Expand Down Expand Up @@ -274,7 +278,7 @@ interface ISuperfluidToken {
* @param account Account to query.
* @param delta Amount of balance delta to be settled
*
* @custom:modifiers
* @custom:modifiers
* - onlyAgreement
*/
function settleBalance(
Expand All @@ -293,7 +297,7 @@ interface ISuperfluidToken {
* @param rewardAmount The amount the rewarded account will receive
* @param targetAccountBalanceDelta The delta amount the target account balance should change by
*
* @custom:note
* @custom:note
* - If a bailout is required (bailoutAmount > 0)
* - the actual reward (single deposit) goes to the executor,
* - while the reward account becomes the bailout account
Expand All @@ -303,7 +307,7 @@ interface ISuperfluidToken {
* - the targetAccount will pay the rewardAmount
* - the liquidator (reward account in PIC period) will receive the rewardAmount
*
* @custom:modifiers
* @custom:modifiers
* - onlyAgreement
*/
function makeLiquidationPayoutsV2
Expand All @@ -327,7 +331,7 @@ interface ISuperfluidToken {
* @param targetAccountBalanceDelta The amount the sender account balance should change by
* @param liquidationTypeData The encoded liquidation type data including the version (how to decode)
*
* @custom:note
* @custom:note
* Reward account rule:
* - if the agreement is liquidated during the PIC period
* - the rewardAmountReceiver will get the rewardAmount (remaining deposit), regardless of the liquidatorAccount
Expand Down Expand Up @@ -412,7 +416,7 @@ interface ISuperfluidToken {
*
* @custom:deprecated Use AgreementLiquidatedV2 instead
*
* @custom:note
* @custom:note
* Reward account rule:
* - if bailout is equal to 0, then
* - the bondAccount will get the rewardAmount,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
ISuperfluid,
ISuperAgreement,
ISuperfluidGovernance,
ISuperfluidToken
ISuperfluidToken,
IGeneralDistributionAgreementV1
} from "../interfaces/superfluid/ISuperfluid.sol";
import { FixedSizeData } from "../libs/FixedSizeData.sol";

Expand Down Expand Up @@ -105,6 +106,49 @@ abstract contract SuperfluidToken is ISuperfluidToken
}
}

function hasRealtimeBalanceOfAtLeast(
address account,
uint256 timestamp,
int256 minBalance
)
public view virtual override
returns (bool)
{
int256 availableBalance = _sharedSettledBalances[account];
ISuperAgreement[] memory activeAgreements = getAccountActiveAgreements(account);
IGeneralDistributionAgreementV1 gda;
for (uint256 i = 0; i < activeAgreements.length; ++i) {
if (activeAgreements[i].agreementType() ==
keccak256("org.superfluid-finance.agreements.GeneralDistributionAgreement.v1"))
{
gda = IGeneralDistributionAgreementV1(address(activeAgreements[i]));
continue;
} else {
(
int256 agreementDynamicBalance,
uint256 agreementDeposit,
uint256 agreementOwedDeposit) = activeAgreements[i]
.realtimeBalanceOf(
this,
account,
timestamp
);

availableBalance = availableBalance
+ agreementDynamicBalance
- (
agreementDeposit > agreementOwedDeposit ?
(agreementDeposit - agreementOwedDeposit) : 0
).toInt256();
}
}
if (address(gda) != address(0)) {
return gda.hasRealtimeBalanceOfAtLeast(this, account, timestamp, minBalance - availableBalance);
}

return availableBalance >= minBalance;
}

/// @dev ISuperfluidToken.realtimeBalanceOfNow implementation
function realtimeBalanceOfNow(
address account
Expand Down Expand Up @@ -211,8 +255,7 @@ abstract contract SuperfluidToken is ISuperfluidToken
)
internal
{
(int256 availableBalance,,) = realtimeBalanceOf(from, _host.getNow());
if (availableBalance < amount) {
if (! hasRealtimeBalanceOfAtLeast(from, _host.getNow(), amount)) {
revert SF_TOKEN_MOVE_INSUFFICIENT_BALANCE();
}
_sharedSettledBalances[from] = _sharedSettledBalances[from] - amount;
Expand Down
Loading
Loading