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

[RFR] create account token snapshot log for each account token snapshot updates #830

86 changes: 86 additions & 0 deletions packages/subgraph/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,12 @@ type AccountTokenSnapshot @entity {
updatedAtTimestamp: BigInt!
updatedAtBlockNumber: BigInt!
# ---------------------------------- state ----------------------------------
"""
isLiquidationEstimateOptimistic, If `totalSubscriptionsWithUnits > 0`, it is true.
Optimistic means that it is the earliest time the user will be liquidated as they may receive ongoing distributions which aren't tracked by the subgraph
"""
isLiquidationEstimateOptimistic: Boolean!

"""
Optimistic liquidation estimation property.
"""
Expand Down Expand Up @@ -1260,6 +1266,86 @@ type AccountTokenSnapshot @entity {
account: Account!
token: Token!
flowOperators: [FlowOperator!]! @derivedFrom(field: "accountTokenSnapshot")
accountTokenSnapshotLogs: [AccountTokenSnapshotLog!]! @derivedFrom(field: "accountTokenSnapshot")
}

"""
AccountTokenSnapshotLog: Historical entries of AccountTokenSnapshot updates.
"""
type AccountTokenSnapshotLog @entity {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be marked immutable somewhere? @0xdavinchee

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not super necessary tbh

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But it's supposed to be an immutable entity ;)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's add this to an issue and give the HOL entities an interface to implement and do this once the graph fixes the issue w/ mixing immutable/non-immutable

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently it got fixed: graphprotocol/graph-node#3553 ;) Maybe hasn't shown up in hosted service yet.

id: ID!
timestamp: BigInt!
blockNumber: BigInt!

transactionHash: Bytes!
logIndex: BigInt!
order: BigInt!
triggeredByEventName: String!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@0xdavinchee Could we use triggeredByEvent: Event! here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, but it may be useful to have both properties, one for filtering and the other for the information

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess... although in this case, the event ID actually contains all the same information, except the order, but of course it's not as explicit.

By the way, it seems that the Graph team is quite close to implementing filtering by child entity: graphprotocol/graph-node#3184

Once that gets merged, we can have a more normalized schema, i.e. less duplicated fields just for filtering.


# ---------------------------------- state ----------------------------------
"""
Optimistic liquidation estimation property.
"""
maybeCriticalAtTimestamp: BigInt!
"""
The current (as of timestamp) number of currently open streams.
"""
totalNumberOfActiveStreams: Int!

"""
The current (as of timestamp) number of all-time closed streams.
"""
totalNumberOfClosedStreams: Int!

"""
The current (as of timestamp) number of subscriptions with units allocated to them tied to this `account`.
"""
totalSubscriptionsWithUnits: Int!

"""
Counts all currently (as of timestamp) approved subscriptions whether or not they have units.
"""
totalApprovedSubscriptions: Int!

"""
Balance of `account` as of `timestamp`/`block`.
"""
balance: BigInt!

"""
The total (as of timestamp) deposit this account has held by the CFA agreement for `account` active streams.
"""
totalDeposit: BigInt!

"""
The total (as of timestamp) net flow rate of the `account` as of `timestamp`/`block`.
"""
totalNetFlowRate: BigInt!

"""
The total (as of timestamp) inflow rate (receive flowRate per second) of the `account`.
"""
totalInflowRate: BigInt!

"""
The total (as of timestamp) outflow rate (send flowrate per second) of the `account`.
"""
totalOutflowRate: BigInt!

"""
The total (as of timestamp) amount of `token` streamed to this `account` until
the `timestamp`/`block`.
"""
totalAmountStreamed: BigInt!

"""
The total (as of timestamp) amount of `token` this `account` has transferred.
"""
totalAmountTransferred: BigInt!
# ---------------------------------- links ----------------------------------
account: Account!
token: Token!
accountTokenSnapshot: AccountTokenSnapshot!
}

"""
Expand Down
43 changes: 43 additions & 0 deletions packages/subgraph/src/mappingHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {ISuperfluid as Superfluid} from "../generated/Host/ISuperfluid";
import {
Account,
AccountTokenSnapshot,
AccountTokenSnapshotLog,
FlowOperator,
Index,
IndexSubscription,
Expand All @@ -13,12 +14,14 @@ import {
} from "../generated/schema";
import {
BIG_INT_ZERO,
createLogID,
calculateMaybeCriticalAtTimestamp,
getAccountTokenSnapshotID,
getAmountStreamedSinceLastUpdatedAt,
getFlowOperatorID,
getIndexID,
getIsListedToken,
getOrder,
getStreamID,
getStreamRevisionID,
getSubscriptionID,
Expand Down Expand Up @@ -386,6 +389,7 @@ export function getOrInitAccountTokenSnapshot(
accountTokenSnapshot.totalNumberOfActiveStreams = 0;
accountTokenSnapshot.totalNumberOfClosedStreams = 0;
accountTokenSnapshot.totalSubscriptionsWithUnits = 0;
accountTokenSnapshot.isLiquidationEstimateOptimistic = false;
accountTokenSnapshot.totalApprovedSubscriptions = 0;
accountTokenSnapshot.balanceUntilUpdatedAt = BIG_INT_ZERO;
accountTokenSnapshot.totalNetFlowRate = BIG_INT_ZERO;
Expand Down Expand Up @@ -489,6 +493,7 @@ export function updateAggregateIDASubscriptionsData(
accountTokenSnapshot.totalSubscriptionsWithUnits =
accountTokenSnapshot.totalSubscriptionsWithUnits +
totalSubscriptionWithUnitsDelta;
accountTokenSnapshot.isLiquidationEstimateOptimistic = accountTokenSnapshot.totalSubscriptionsWithUnits > 0;
accountTokenSnapshot.totalApprovedSubscriptions =
accountTokenSnapshot.totalApprovedSubscriptions +
totalApprovedSubscriptionsDelta;
Expand All @@ -501,6 +506,7 @@ export function updateAggregateIDASubscriptionsData(
tokenStatistic.totalSubscriptionsWithUnits =
tokenStatistic.totalSubscriptionsWithUnits +
totalSubscriptionWithUnitsDelta;
accountTokenSnapshot.isLiquidationEstimateOptimistic = accountTokenSnapshot.totalSubscriptionsWithUnits > 0;
tokenStatistic.totalApprovedSubscriptions =
tokenStatistic.totalApprovedSubscriptions +
totalApprovedSubscriptionsDelta;
Expand Down Expand Up @@ -740,3 +746,40 @@ export function updateAggregateEntitiesTransferData(
tokenStatistic.totalAmountTransferredUntilUpdatedAt.plus(value);
tokenStatistic.save();
}

export function createAccountTokenSnapshotLogEntity(
event: ethereum.Event,
accountAddress: Address,
tokenAddress: Address,
eventName: string,
): void {
if (accountAddress.equals(ZERO_ADDRESS)) {
return;
}
let accountTokenSnapshot = getOrInitAccountTokenSnapshot(accountAddress, tokenAddress, event.block);
// Transaction
let atsLog = new AccountTokenSnapshotLog(createLogID("ATSLog", accountTokenSnapshot.id, event));
atsLog.transactionHash = event.transaction.hash;
atsLog.timestamp = event.block.timestamp;
atsLog.order = getOrder(event.block.number, event.logIndex);
atsLog.blockNumber = event.block.number;
atsLog.logIndex = event.logIndex;
atsLog.triggeredByEventName = eventName;
// Account token snapshot state
atsLog.totalNumberOfActiveStreams = accountTokenSnapshot.totalNumberOfActiveStreams;
atsLog.totalNumberOfClosedStreams = accountTokenSnapshot.totalNumberOfClosedStreams;
atsLog.totalSubscriptionsWithUnits = accountTokenSnapshot.totalSubscriptionsWithUnits;
atsLog.totalApprovedSubscriptions = accountTokenSnapshot.totalApprovedSubscriptions;
atsLog.balance = accountTokenSnapshot.balanceUntilUpdatedAt;
atsLog.totalNetFlowRate = accountTokenSnapshot.totalNetFlowRate;
atsLog.totalInflowRate = accountTokenSnapshot.totalInflowRate;
atsLog.totalOutflowRate = accountTokenSnapshot.totalOutflowRate;
atsLog.totalAmountStreamed = accountTokenSnapshot.totalAmountStreamedUntilUpdatedAt;
atsLog.totalAmountTransferred = accountTokenSnapshot.totalAmountTransferredUntilUpdatedAt;
atsLog.totalDeposit = accountTokenSnapshot.totalDeposit;
atsLog.maybeCriticalAtTimestamp = accountTokenSnapshot.maybeCriticalAtTimestamp;
atsLog.account = accountTokenSnapshot.account;
atsLog.token = accountTokenSnapshot.token;
atsLog.accountTokenSnapshot = accountTokenSnapshot.id;
atsLog.save();
}
3 changes: 3 additions & 0 deletions packages/subgraph/src/mappings/cfav1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
ZERO_ADDRESS,
} from "../utils";
import {
createAccountTokenSnapshotLogEntity,
getOrInitFlowOperator,
getOrInitStream,
getOrInitStreamRevision,
Expand Down Expand Up @@ -327,6 +328,8 @@ export function handleStreamUpdated(event: FlowUpdated): void {
isDelete,
event.block
);
createAccountTokenSnapshotLogEntity(event, senderAddress, tokenAddress, "FlowUpdated");
createAccountTokenSnapshotLogEntity(event, receiverAddress, tokenAddress, "FlowUpdated");
}

// NOTE: This handler is run right after handleStreamUpdated as the FlowUpdatedExtension
Expand Down
15 changes: 13 additions & 2 deletions packages/subgraph/src/mappings/idav1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@ import {
import {
BIG_INT_ZERO,
createEventID,
getIndexID, getOrder,
getIndexID,
getOrder,
subscriptionExists as subscriptionWithUnitsExists,
tokenHasValidHost,
} from "../utils";
import {
createAccountTokenSnapshotLogEntity,
getOrInitIndex,
getOrInitSubscription,
getOrInitTokenStatistic,
Expand Down Expand Up @@ -77,6 +79,7 @@ export function handleIndexCreated(event: IndexCreated): void {
event.block
);

createAccountTokenSnapshotLogEntity(event, event.params.publisher, event.params.token, "IndexCreated");
createIndexCreatedEntity(event, index.id);
}

Expand Down Expand Up @@ -149,7 +152,7 @@ export function handleIndexUpdated(event: IndexUpdated): void {
event.params.token,
event.block
);

createAccountTokenSnapshotLogEntity(event, event.params.publisher, event.params.token, "IndexUpdated");
createIndexUpdatedEntity(event, index.id);
}

Expand Down Expand Up @@ -242,6 +245,7 @@ export function handleSubscriptionApproved(event: SubscriptionApproved): void {
event.params.token,
event.block
);
createAccountTokenSnapshotLogEntity(event, event.params.publisher, event.params.token, "SubscriptionApproved");
}

subscription.save();
Expand All @@ -263,6 +267,7 @@ export function handleSubscriptionApproved(event: SubscriptionApproved): void {
index.save();

createSubscriptionApprovedEntity(event, subscription.id);
createAccountTokenSnapshotLogEntity(event, event.params.subscriber, event.params.token, "SubscriptionApproved")
}

export function handleSubscriptionDistributionClaimed(
Expand Down Expand Up @@ -307,6 +312,8 @@ export function handleSubscriptionDistributionClaimed(
event.params.token,
event.block
);
createAccountTokenSnapshotLogEntity(event, event.params.publisher, event.params.token, "SubscriptionDistributionClaimed");
createAccountTokenSnapshotLogEntity(event, event.params.subscriber, event.params.token, "SubscriptionDistributionClaimed");
}

/**
Expand Down Expand Up @@ -396,6 +403,8 @@ export function handleSubscriptionRevoked(event: SubscriptionRevoked): void {
subscription.save();

createSubscriptionRevokedEntity(event, subscription.id);
createAccountTokenSnapshotLogEntity(event, event.params.subscriber, event.params.token, "SubscriptionRevoked")
createAccountTokenSnapshotLogEntity(event, event.params.publisher, event.params.token, "SubscriptionRevoked");
}

/**
Expand Down Expand Up @@ -520,6 +529,8 @@ export function handleSubscriptionUnitsUpdated(
subscription.save();

createSubscriptionUnitsUpdatedEntity(event, subscription.id, oldUnits);
createAccountTokenSnapshotLogEntity(event, event.params.publisher, event.params.token, "SubscriptionUnitsUpdated");
createAccountTokenSnapshotLogEntity(event, event.params.subscriber, event.params.token, "SubscriptionUnitsUpdated");
msoni89 marked this conversation as resolved.
Show resolved Hide resolved
}

/**************************************************************************
Expand Down
40 changes: 27 additions & 13 deletions packages/subgraph/src/mappings/superToken.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,42 @@
import {
TokenUpgraded,
TokenDowngraded,
Transfer,
AgreementLiquidatedBy,
AgreementLiquidatedV2,
Burned,
Minted,
Sent,
AgreementLiquidatedV2,
TokenDowngraded,
TokenUpgraded,
Transfer,
} from "../../generated/templates/SuperToken/ISuperToken";
import {
AgreementLiquidatedByEvent,
AgreementLiquidatedV2Event,
BurnedEvent,
MintedEvent,
TokenUpgradedEvent,
SentEvent,
TokenDowngradedEvent,
TokenUpgradedEvent,
TransferEvent,
SentEvent,
AgreementLiquidatedV2Event,
} from "../../generated/schema";
import {createEventID, getOrder, tokenHasValidHost} from "../utils";
import {createEventID, getOrder, tokenHasValidHost, ZERO_ADDRESS} from "../utils";
import {
createAccountTokenSnapshotLogEntity,
getOrInitAccount,
getOrInitSuperToken,
getOrInitTokenStatistic,
updateAggregateEntitiesTransferData,
updateATSStreamedAndBalanceUntilUpdatedAt,
updateTokenStatsStreamedUntilUpdatedAt,
} from "../mappingHelpers";
import { getHostAddress } from "../addresses";
import { Address, BigInt, ethereum, log } from "@graphprotocol/graph-ts";
import {getHostAddress} from "../addresses";
import {Address, BigInt, ethereum, log} from "@graphprotocol/graph-ts";

function updateHOLEntitiesForLiquidation(
event: ethereum.Event,
liquidatorAccount: Address,
targetAccount: Address,
bondAccount: Address
bondAccount: Address,
eventName: String,
): void {
getOrInitSuperToken(event.address, event.block);

Expand All @@ -53,6 +55,9 @@ function updateHOLEntitiesForLiquidation(
event.address,
event.block
);
createAccountTokenSnapshotLogEntity(event, targetAccount, event.address, eventName);
createAccountTokenSnapshotLogEntity(event, liquidatorAccount, event.address, eventName);
createAccountTokenSnapshotLogEntity(event, bondAccount, event.address, eventName);
}

export function handleAgreementLiquidatedBy(
Expand All @@ -70,7 +75,8 @@ export function handleAgreementLiquidatedBy(
event,
event.params.liquidatorAccount,
event.params.penaltyAccount,
event.params.bondAccount
event.params.bondAccount,
"AgreementLiquidatedBy"
);
}

Expand All @@ -89,7 +95,8 @@ export function handleAgreementLiquidatedV2(
event,
event.params.liquidatorAccount,
event.params.targetAccount,
event.params.rewardAccount
event.params.rewardAccount,
"AgreementLiquidatedV2"
);
}

Expand All @@ -111,6 +118,7 @@ export function handleTokenUpgraded(event: TokenUpgraded): void {
event.address,
event.block
);
createAccountTokenSnapshotLogEntity(event, event.params.account, event.address, "TokenUpgraded");
}

export function handleTokenDowngraded(event: TokenDowngraded): void {
Expand All @@ -131,6 +139,7 @@ export function handleTokenDowngraded(event: TokenDowngraded): void {
event.address,
event.block
);
createAccountTokenSnapshotLogEntity(event, event.params.account, event.address, "TokenDowngraded");
}

export function handleTransfer(event: Transfer): void {
Expand Down Expand Up @@ -164,6 +173,11 @@ export function handleTransfer(event: Transfer): void {
event.params.value,
event.block
);

if (event.params.to.equals(ZERO_ADDRESS)) return;
if (event.params.from.equals(ZERO_ADDRESS)) return; // Ignoring downgrade and upgrade transfer event logs.
createAccountTokenSnapshotLogEntity(event, event.params.to, event.address, "Transfer");
createAccountTokenSnapshotLogEntity(event, event.params.from, event.address, "Transfer");
}

export function handleSent(event: Sent): void {
Expand Down
Loading