Skip to content

Commit

Permalink
feat: add supply and borrower count to core pool
Browse files Browse the repository at this point in the history
  • Loading branch information
coreyar committed Nov 1, 2023
1 parent e926fb7 commit 34df4fb
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 52 deletions.
36 changes: 9 additions & 27 deletions subgraphs/venus/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ type Market @entity {
totalXvsDistributedMantissa: BigInt!
"vToken decimal length"
vTokenDecimals: Int!

"Number of accounts currently supplying to this market"
supplierCount: BigInt!

"Number of accounts with reasonable borrow positions in this market (excludes accounts with dust (potentially left over from liquidations))"
borrowerCountAdjusted: BigInt!

"Number of accounts currently borrowing from this market"
borrowerCount: BigInt!
}

"""
Expand All @@ -84,17 +93,6 @@ type Account @entity {
countLiquidator: Int!
"True if user has ever borrowed"
hasBorrowed: Boolean!

# The following values are added by the JS Wrapper, and must be calculated with the most up
# to date values based on the block delta for market.exchangeRate and market.borrowIndex
# They do not need to be in the schema, but they will show up in the explorer playground

# "If less than 1, the account can be liquidated
# health: BigDecimal!
# "Total assets supplied by user"
# totalBorrowValueInEth: BigDecimal!
# "Total assets borrowed from user"
# totalCollateralValueInEth: BigDecimal!
}

"""
Expand Down Expand Up @@ -131,22 +129,6 @@ type AccountVToken @entity {
totalUnderlyingRepaid: BigDecimal!
"Current borrow balance stored in contract (exclusive of interest since accrualBlockNumber)"
storedBorrowBalance: BigDecimal!

# The following values are added by the JS Wrapper, and must be calculated with the most up
# to date values based on the block delta for market.exchangeRate and market.borrowIndex
# They do not need to be in the schema, but they will show up in the explorer playground

# supplyBalanceUnderlying: BigDecimal!
# FORMULA: supplyBalanceUnderlying = vTokenBalance * market.exchangeRate

# lifetimeSupplyInterestAccrued: BigDecimal!
# FORMULA: lifetimeSupplyInterestAccrued = supplyBalanceUnderlying - totalUnderlyingSupplied + totalUnderlyingRedeemed

# borrowBalanceUnderlying: BigDecimal!
# FORMULA: borrowBalanceUnderlying = storedBorrowBalance * market.borrowIndex / accountBorrowIndex

# lifetimeBorrowInterestAccrued: BigDecimal!
# FORMULA: lifetimeBorrowInterestAccrued = borrowBalanceUnderlying - totalUnderlyingBorrowed + totalUnderlyingRepaid
}

"""
Expand Down
72 changes: 49 additions & 23 deletions subgraphs/venus/src/mappings/vToken.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/* eslint-disable prefer-const */
// to satisfy AS compiler
import { BigInt } from '@graphprotocol/graph-ts';

import {
Account,
BorrowEvent,
Expand All @@ -21,12 +23,14 @@ import {
RepayBorrow,
Transfer,
} from '../../generated/templates/VToken/VToken';
import { oneBigInt, zeroBigInt32 } from '../constants';
import { nullAddress } from '../constants/addresses';
import { createAccount } from '../operations/create';
import { createMarket } from '../operations/create';
import { updateCommonVTokenStats } from '../operations/update';
import { updateMarket } from '../operations/update';
import { exponentToBigDecimal } from '../utilities/exponentToBigDecimal';
import { getMarketId, getTransactionId } from '../utilities/ids';

/* Account supplies assets into market and receives vTokens in exchange
*
Expand All @@ -46,10 +50,7 @@ export const handleMint = (event: Mint): void => {
if (!market) {
market = createMarket(event.address.toHexString());
}
let mintID = event.transaction.hash
.toHexString()
.concat('-')
.concat(event.transactionLogIndex.toString());
let mintId = getTransactionId(event.transaction.hash, event.transactionLogIndex);

const vTokenDecimals = market.vTokenDecimals;

Expand All @@ -62,7 +63,7 @@ export const handleMint = (event: Mint): void => {
.div(exponentToBigDecimal(market.underlyingDecimals))
.truncate(market.underlyingDecimals);

let mint = new MintEvent(mintID);
let mint = new MintEvent(mintId);
mint.amount = vTokenAmount;
mint.to = event.params.minter;
mint.from = event.address;
Expand All @@ -71,6 +72,10 @@ export const handleMint = (event: Mint): void => {
mint.vTokenSymbol = market.symbol;
mint.underlyingAmount = underlyingAmount;
mint.save();

if (event.params.mintTokens.equals(event.params.totalSupply)) {
market.supplierCount = market.supplierCount.plus(oneBigInt);
}
};

/* Account supplies vTokens into market and receives underlying asset in exchange
Expand Down Expand Up @@ -114,6 +119,12 @@ export const handleRedeem = (event: Redeem): void => {
redeem.vTokenSymbol = market.symbol;
redeem.underlyingAmount = underlyingAmount;
redeem.save();

if (event.params.totalSupply.equals(zeroBigInt32)) {
// if the current balance is 0 then the user has withdrawn all their assets from this market
market.supplierCount = market.supplierCount.minus(oneBigInt);
market.save();
}
};

/* Borrow assets from the protocol. All values either BNB or BEP20
Expand Down Expand Up @@ -186,6 +197,13 @@ export const handleBorrow = (event: Borrow): void => {
borrow.blockTime = event.block.timestamp.toI32();
borrow.underlyingSymbol = market.underlyingSymbol;
borrow.save();

if (event.params.accountBorrows == event.params.borrowAmount) {
// if both the accountBorrows and the borrowAmount are the same, it means the account is a new borrower
market.borrowerCount = market.borrowerCount.plus(oneBigInt);
market.borrowerCountAdjusted = market.borrowerCountAdjusted.plus(oneBigInt);
market.save();
}
};

/* Repay some amount borrowed. Anyone can repay anyones balance
Expand Down Expand Up @@ -262,6 +280,17 @@ export const handleRepayBorrow = (event: RepayBorrow): void => {
repay.underlyingSymbol = market.underlyingSymbol;
repay.payer = event.params.payer;
repay.save();

if (event.params.accountBorrows.equals(zeroBigInt32)) {
market.borrowerCount = market.borrowerCount.minus(oneBigInt);
market.borrowerCountAdjusted = market.borrowerCountAdjusted.minus(oneBigInt);
market.save();
} else if (event.params.accountBorrows.le(new BigInt(10))) {
// Sometimes a liquidator will leave dust behind. If this happens we'll adjust count
// because the position only exists due to a technicality
market.borrowerCountAdjusted = market.borrowerCountAdjusted.minus(oneBigInt);
market.save();
}
};

/*
Expand Down Expand Up @@ -353,10 +382,10 @@ export const handleLiquidateBorrow = (event: LiquidateBorrow): void => {
export const handleTransfer = (event: Transfer): void => {
// We only updateMarket() if accrual block number is not up to date. This will only happen
// with normal transfers, since mint, redeem, and seize transfers will already run updateMarket()
let marketID = event.address.toHexString();
let market = Market.load(marketID);
let marketId = getMarketId(event.address);
let market = Market.load(marketId);
if (!market) {
market = createMarket(marketID);
market = createMarket(marketId);
}
if (market.accrualBlockNumber != event.block.number.toI32()) {
market = updateMarket(event.address, event.block.number.toI32(), event.block.timestamp.toI32());
Expand All @@ -366,19 +395,19 @@ export const handleTransfer = (event: Transfer): void => {

// Checking if the tx is FROM the vToken contract (i.e. this will not run when minting)
// If so, it is a mint, and we don't need to run these calculations
let accountFromID = event.params.from.toHex();
if (accountFromID != nullAddress.toHex()) {
let accountFrom = Account.load(accountFromID);
let accountFromId = event.params.from.toHex();
if (accountFromId != nullAddress.toHex()) {
let accountFrom = Account.load(accountFromId);
if (accountFrom == null) {
createAccount(accountFromID);
createAccount(accountFromId);
}

// Update vTokenStats common for all events, and return the stats to update unique
// values for each event
let vTokenStatsFrom = updateCommonVTokenStats(
market.id,
market.symbol,
accountFromID,
accountFromId,
event.transaction.hash,
event.block.timestamp,
event.block.number,
Expand All @@ -401,19 +430,19 @@ export const handleTransfer = (event: Transfer): void => {
// If so, we ignore it. this leaves an edge case, where someone who accidentally sends
// vTokens to a vToken contract, where it will not get recorded. Right now it would
// be messy to include, so we are leaving it out for now TODO fix this in future
let accountToID = event.params.to.toHex();
if (accountToID != marketID) {
let accountTo = Account.load(accountToID);
let accountToId = event.params.to.toHex();
if (accountToId != marketId) {
let accountTo = Account.load(accountToId);
if (accountTo == null) {
createAccount(accountToID);
createAccount(accountToId);
}

// Update vTokenStats common for all events, and return the stats to update unique
// values for each event
let vTokenStatsTo = updateCommonVTokenStats(
market.id,
market.symbol,
accountToID,
accountToId,
event.transaction.hash,
event.block.timestamp,
event.block.number,
Expand All @@ -432,12 +461,9 @@ export const handleTransfer = (event: Transfer): void => {
vTokenStatsTo.save();
}

let transferID = event.transaction.hash
.toHexString()
.concat('-')
.concat(event.transactionLogIndex.toString());
let transferId = getTransactionId(event.transaction.hash, event.transactionLogIndex);

let transfer = new TransferEvent(transferID);
let transfer = new TransferEvent(transferId);
transfer.amount = event.params.amount.toBigDecimal().div(exponentToBigDecimal(vTokenDecimals));
transfer.to = event.params.to;
transfer.from = event.params.from;
Expand Down
4 changes: 4 additions & 0 deletions subgraphs/venus/src/operations/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,9 @@ export function createMarket(marketAddress: string): Market {
market.reserveFactor = reserveFactor.reverted ? BigInt.fromI32(0) : reserveFactor.value;
market.totalXvsDistributedMantissa = zeroBigInt32;

market.supplierCount = zeroBigInt32;
market.borrowerCount = zeroBigInt32;
market.borrowerCountAdjusted = zeroBigInt32;

return market;
}
3 changes: 3 additions & 0 deletions subgraphs/venus/src/utilities/ids.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ export const getMarketId = (vTokenAddress: Address): string =>
export const getAccountVTokenId = (marketAddress: Address, accountAddress: Address): string =>
joinIds([marketAddress.toHexString(), accountAddress.toHexString()]);

export const getTransactionId = (transactionHash: Bytes, logIndex: BigInt): string =>
joinIds([transactionHash.toHexString(), logIndex.toString()]);

export const getAccountVTokenTransactionId = (
accountAddress: Address,
transactionHash: Bytes,
Expand Down
20 changes: 18 additions & 2 deletions subgraphs/venus/tests/VToken/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,8 @@ describe('VToken', () => {

handleMint(mintEvent);

assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'supplierCount', '2');

const supplier02 = user2Address;
mintEvent = createMintEvent(
aaaTokenAddress,
Expand All @@ -460,6 +462,7 @@ describe('VToken', () => {
createAccountVTokenBalanceOfMock(aaaTokenAddress, supplier02, mintTokens);

handleMint(mintEvent);
assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'supplierCount', '3');

let redeemEvent = createRedeemEvent(
aaaTokenAddress,
Expand All @@ -471,6 +474,7 @@ describe('VToken', () => {
createAccountVTokenBalanceOfMock(aaaTokenAddress, supplier02, zeroBigInt32);

handleRedeem(redeemEvent);
assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'supplierCount', '2');

redeemEvent = createRedeemEvent(
aaaTokenAddress,
Expand All @@ -482,6 +486,7 @@ describe('VToken', () => {
createAccountVTokenBalanceOfMock(aaaTokenAddress, supplier01, halfMintTokens);

handleRedeem(redeemEvent);
assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'supplierCount', '1');

redeemEvent = createRedeemEvent(
aaaTokenAddress,
Expand All @@ -493,6 +498,7 @@ describe('VToken', () => {
createAccountVTokenBalanceOfMock(aaaTokenAddress, supplier01, zeroBigInt32);

handleRedeem(redeemEvent);
assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'supplierCount', '0');
});

test('registers increase and decrease in the market borrower count', () => {
Expand All @@ -515,6 +521,8 @@ describe('VToken', () => {
);

handleBorrow(borrowEvent);
assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCount', '2');
assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCountAdjusted', '2');

const borrower02 = user2Address;
borrowEvent = createBorrowEvent(
Expand All @@ -526,6 +534,8 @@ describe('VToken', () => {
);

handleBorrow(borrowEvent);
assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCount', '3');
assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCountAdjusted', '3');

let repayEvent = createRepayBorrowEvent(
aaaTokenAddress,
Expand All @@ -537,6 +547,8 @@ describe('VToken', () => {
);

handleRepayBorrow(repayEvent);
assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCount', '2');
assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCountAdjusted', '2');

repayEvent = createRepayBorrowEvent(
aaaTokenAddress,
Expand All @@ -548,17 +560,21 @@ describe('VToken', () => {
);

handleRepayBorrow(repayEvent);
assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCount', '1');
assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCountAdjusted', '1');

repayEvent = createRepayBorrowEvent(
aaaTokenAddress,
borrower01,
borrower01,
halfBorrowAmountTokens,
zeroBigInt32,
zeroBigInt32,
oneBigInt,
oneBigInt,
);

handleRepayBorrow(repayEvent);
assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCount', '1');
assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCountAdjusted', '1');
});
});
});

0 comments on commit 34df4fb

Please sign in to comment.