mirror of
https://github.com/logos-storage/logos-storage-contracts-eth.git
synced 2026-01-04 22:33:11 +00:00
111 lines
4.1 KiB
Solidity
111 lines
4.1 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity 0.8.28;
|
|
|
|
import "../Tokens.sol";
|
|
import "../Timestamps.sol";
|
|
|
|
/// Used to identify an account. The first 20 bytes consist of the address of
|
|
/// the account holder, and the last 12 bytes consist of a discriminator value.
|
|
type AccountId is bytes32;
|
|
|
|
/// Records the token balance and the incoming and outgoing token flows
|
|
struct Account {
|
|
Balance balance;
|
|
Flow flow;
|
|
}
|
|
|
|
/// The account balance. Fits in 32 bytes to minimize storage costs.
|
|
/// A uint128 is used to record the amount of tokens, which should be more than
|
|
/// enough. Given a standard 18 decimal places for the ERC20 token, this still
|
|
/// allows for 10^20 whole coins.
|
|
struct Balance {
|
|
/// Available tokens can be transfered
|
|
uint128 available;
|
|
/// Designated tokens can no longer be transfered
|
|
uint128 designated;
|
|
}
|
|
|
|
/// The incoming and outgoing flows of an account. Fits in 32 bytes to minimize
|
|
/// storage costs.
|
|
struct Flow {
|
|
/// Rate of outgoing tokens
|
|
TokensPerSecond outgoing;
|
|
/// Rate of incoming tokens
|
|
TokensPerSecond incoming;
|
|
/// Last time that the flow was updated
|
|
Timestamp updated;
|
|
}
|
|
|
|
library Accounts {
|
|
using Accounts for Account;
|
|
using Tokens for TokensPerSecond;
|
|
using Timestamps for Timestamp;
|
|
|
|
/// Creates an account id from the account holder address and a discriminator.
|
|
/// The discriminiator can be used to create different accounts that belong to
|
|
/// the same account holder.
|
|
function encodeId(
|
|
address holder,
|
|
bytes12 discriminator
|
|
) internal pure returns (AccountId) {
|
|
bytes32 left = bytes32(bytes20(holder));
|
|
bytes32 right = bytes32(uint256(uint96(discriminator)));
|
|
return AccountId.wrap(left | right);
|
|
}
|
|
|
|
/// Extracts the account holder and the discriminator from the the account id
|
|
function decodeId(AccountId id) internal pure returns (address, bytes12) {
|
|
bytes32 unwrapped = AccountId.unwrap(id);
|
|
address holder = address(bytes20(unwrapped));
|
|
bytes12 discriminator = bytes12(uint96(uint256(unwrapped)));
|
|
return (holder, discriminator);
|
|
}
|
|
|
|
/// Calculates whether the available balance is sufficient to sustain the
|
|
/// outgoing flow of tokens until the specified timestamp
|
|
function isSolventAt(
|
|
Account memory account,
|
|
Timestamp timestamp
|
|
) internal pure returns (bool) {
|
|
Duration duration = account.flow.updated.until(timestamp);
|
|
uint128 outgoing = account.flow.outgoing.accumulate(duration);
|
|
return outgoing <= account.balance.available;
|
|
}
|
|
|
|
/// Updates the available and designated balances by accumulating the
|
|
/// outgoing and incoming flows up until the specified timestamp. Outgoing
|
|
/// tokens are deducted from the available balance. Incoming tokens are added
|
|
/// to the designated tokens.
|
|
function accumulateFlows(
|
|
Account memory account,
|
|
Timestamp timestamp
|
|
) internal pure {
|
|
Duration duration = account.flow.updated.until(timestamp);
|
|
account.balance.available -= account.flow.outgoing.accumulate(duration);
|
|
account.balance.designated += account.flow.incoming.accumulate(duration);
|
|
account.flow.updated = timestamp;
|
|
}
|
|
|
|
/// Starts an incoming flow of tokens at the specified rate. If there already
|
|
/// is a flow of incoming tokens, then its rate is increased accordingly.
|
|
function flowIn(Account memory account, TokensPerSecond rate) internal view {
|
|
account.accumulateFlows(Timestamps.currentTime());
|
|
account.flow.incoming = account.flow.incoming + rate;
|
|
}
|
|
|
|
/// Starts an outgoing flow of tokens at the specified rate. If there is
|
|
/// already a flow of incoming tokens, then these are used to pay for the
|
|
/// outgoing flow. If there are insuffient incoming tokens, then the outgoing
|
|
/// rate is increased.
|
|
function flowOut(Account memory account, TokensPerSecond rate) internal view {
|
|
account.accumulateFlows(Timestamps.currentTime());
|
|
if (rate <= account.flow.incoming) {
|
|
account.flow.incoming = account.flow.incoming - rate;
|
|
} else {
|
|
account.flow.outgoing = account.flow.outgoing + rate;
|
|
account.flow.outgoing = account.flow.outgoing - account.flow.incoming;
|
|
account.flow.incoming = TokensPerSecond.wrap(0);
|
|
}
|
|
}
|
|
}
|