type SeniorPool @entity { id: ID! # Hardcoded to "1", the Senior Pool is a singleton address: Bytes! sharePrice: BigInt! totalShares: BigInt! assets: BigInt! totalLoansOutstanding: BigInt! tranchedPools: [TranchedPool!]! withdrawalCancellationFee: BigDecimal! estimatedTotalInterest: BigDecimal! estimatedApy: BigDecimal! estimatedApyFromGfiRaw: BigDecimal! totalInvested: BigInt! totalWrittenDown: BigInt! defaultRate: BigDecimal! } enum StakedPositionType { Fidu CurveLP } type SeniorPoolStakedPosition @entity { id: ID! user: User! initialAmount: BigInt! amount: BigInt! positionType: StakedPositionType! """ endTime has to be queried via RPC on client due to a complication: calling positions() on the StakingRewards contract is difficult due to its signature changing over time. """ startTime: BigInt! totalRewardsClaimed: BigInt! vaultedAsset: VaultedStakedPosition } type BorrowerContract @entity { id: ID! createdAt: BigInt! user: User! } type User @entity { id: ID! seniorPoolStakedPositions: [SeniorPoolStakedPosition!]! @derivedFrom(field: "user") vaultedStakedPositions: [VaultedStakedPosition!]! @derivedFrom(field: "user") poolTokens: [PoolToken!]! vaultedPoolTokens: [VaultedPoolToken!]! @derivedFrom(field: "user") zaps: [Zap!]! @derivedFrom(field: "user") uidType: UidType isGoListed: Boolean! transactions: [Transaction!]! @derivedFrom(field: "user") borrowerContracts: [BorrowerContract!]! @derivedFrom(field: "user") communityRewardsTokens: [CommunityRewardsToken!]! @derivedFrom(field: "user") } interface TrancheInfo { id: ID! trancheId: BigInt! loan: Loan! lockedUntil: BigInt! principalDeposited: BigInt! principalSharePrice: BigInt! interestSharePrice: BigInt! } type SeniorTrancheInfo implements TrancheInfo @entity { id: ID! trancheId: BigInt! loan: Loan! tranchedPool: TranchedPool! lockedUntil: BigInt! principalDeposited: BigInt! principalSharePrice: BigInt! interestSharePrice: BigInt! } type JuniorTrancheInfo implements TrancheInfo @entity { id: ID! trancheId: BigInt! loan: Loan! tranchedPool: TranchedPool! lockedUntil: BigInt! principalDeposited: BigInt! principalSharePrice: BigInt! interestSharePrice: BigInt! } type CreditLine @entity { id: ID! borrowerContract: BorrowerContract! tranchedPool: TranchedPool! @derivedFrom(field: "creditLine") limit: BigInt! maxLimit: BigInt! interestApr: BigInt! balance: BigInt! interestAccruedAsOf: BigInt! nextDueTime: BigInt! termEndTime: BigInt! termStartTime: BigInt! lastFullPaymentTime: BigInt! version: TranchedPoolCreditLineVersion! lateFeeApr: BigDecimal! interestAprDecimal: BigDecimal! isEligibleForRewards: Boolean! } enum TranchedPoolCreditLineVersion { BEFORE_V2_2 V2_2 } enum UidType { NON_US_INDIVIDUAL US_ACCREDITED_INDIVIDUAL US_NON_ACCREDITED_INDIVIDUAL US_ENTITY NON_US_ENTITY } enum RepaymentFrequency { DAILY WEEKLY BIWEEKLY MONTHLY QUARTERLY HALFLY ANNUALLY } interface Loan { id: ID! address: Bytes! fundingLimit: BigInt! principalAmount: BigInt! """ The amount of interest determined to be owed to this pool, calculated at the very beginning """ initialInterestOwed: BigInt! usdcApy: BigDecimal! rawGfiApy: BigDecimal! totalDeposited: BigInt! remainingCapacity: BigInt! fundableAt: Int! createdAt: Int! allowedUidTypes: [UidType!]! backers: [User!]! numBackers: Int! isPaused: Boolean! drawdownsPaused: Boolean! tokens: [PoolToken!]! repaymentSchedule: [ScheduledRepayment!]! numRepayments: Int! principalAmountRepaid: BigInt! interestAmountRepaid: BigInt! # "Credit line" fields balance: BigInt! termEndTime: BigInt! termStartTime: BigInt! termInSeconds: Int! interestRate: BigDecimal! interestRateBigInt: BigInt! # For use in calculations that have to be done with integers (to mimic on-chain math) lateFeeRate: BigDecimal! borrowerContract: BorrowerContract! creditLineAddress: Bytes! repaymentFrequency: RepaymentFrequency! } type TranchedPool implements Loan @entity { id: ID! address: Bytes! principalAmount: BigInt! """ The amount of interest determined to be owed to this pool, calculated at the very beginning """ initialInterestOwed: BigInt! fundingLimit: BigInt! usdcApy: BigDecimal! rawGfiApy: BigDecimal! totalDeposited: BigInt! remainingCapacity: BigInt! fundableAt: Int! createdAt: Int! allowedUidTypes: [UidType!]! backers: [User!]! numBackers: Int! tokens: [PoolToken!]! repaymentSchedule: [ScheduledRepayment!]! numRepayments: Int! principalAmountRepaid: BigInt! interestAmountRepaid: BigInt! """ A null value indicates there have been no senior pool contributions to the pool yet """ actualSeniorPoolInvestment: BigInt balance: BigInt! nextDueTime: BigInt! termEndTime: BigInt! termStartTime: BigInt! termInSeconds: Int! interestRate: BigDecimal! interestRateBigInt: BigInt! lateFeeRate: BigDecimal! interestAccruedAsOf: BigInt! borrowerContract: BorrowerContract! repaymentFrequency: RepaymentFrequency! estimatedTotalAssets: BigInt! estimatedLeverageRatio: BigDecimal creditLine: CreditLine! creditLineAddress: Bytes! isPaused: Boolean! drawdownsPaused: Boolean! """ Set to true for tranched pools that were created before the advent of junior/senior tranches. These pools have junior investment only, and they are considered legacy """ isV1StyleDeal: Boolean! juniorFeePercent: BigInt! reserveFeePercent: BigInt! juniorDeposited: BigInt! """ This number is only becomes non-zero after the senior pool invests in a pool, which happens after the junior portion is locked """ estimatedSeniorPoolContribution: BigInt! seniorTranches: [SeniorTrancheInfo!]! @derivedFrom(field: "tranchedPool") juniorTranches: [JuniorTrancheInfo!]! @derivedFrom(field: "tranchedPool") version: TranchedPoolCreditLineVersion! } """ Captures protocol-wide data """ type Protocol @entity { id: ID! # Hardcoded to "1" because this is a singleton totalWritedowns: BigInt! totalDrawdowns: BigInt! defaultRate: BigDecimal! totalPrincipalCollected: BigInt! totalInterestCollected: BigInt! totalReserveCollected: BigInt! loans: [Loan!]! numLoans: Int! } type PoolToken @entity { id: ID! # Set to tokenId mintedAt: BigInt! user: User! loan: Loan! tranche: BigInt! principalAmount: BigInt! principalRedeemed: BigInt! interestRedeemed: BigInt! interestRedeemable: BigInt! # You may be wondering why principalRedeemable isn't also here. It's because when an early principal repayment happens on Callable Loans, that principal doesn't become withdrawable until the end of the principal period, and this is sadly not event driven behaviour. rewardsClaimable: BigInt! rewardsClaimed: BigInt! """ Refers to the rewards that are received from LP rewards matching """ stakingRewardsClaimable: BigInt! """ Refers to the rewards that are received from LP rewards matching """ stakingRewardsClaimed: BigInt! vaultedAsset: VaultedPoolToken """ Only applicable to Pool Tokens that belong to a Callable Loan. This will be always `false` for other loan types. """ isCapitalCalled: Boolean! """ Only applicable to Pool Tokens that belong to a Callable Loan. This will be always `null` for other loan types. """ calledAt: Int """ Only applicable to Pool Tokens that belong to a Callable Loan. This will be always `null` for other loan types. """ callDueAt: Int } type StakingRewardsData @entity { id: ID! # Set to "1" because this is a singleton """ The earn rate is a measure per second """ currentEarnRatePerToken: BigInt! } type BackerRewardsData @entity { id: ID! # Set to "1" because this is a singleton contractAddress: String! totalRewards: BigInt! totalRewardPercentOfTotalGFI: BigDecimal! """ Note that this value is measured in GFI, not dollars """ maxInterestDollarsEligible: BigInt! } enum TransactionCategory { SENIOR_POOL_DEPOSIT SENIOR_POOL_DEPOSIT_AND_STAKE SENIOR_POOL_STAKE SENIOR_POOL_WITHDRAWAL SENIOR_POOL_UNSTAKE_AND_WITHDRAWAL SENIOR_POOL_WITHDRAWAL_REQUEST SENIOR_POOL_ADD_TO_WITHDRAWAL_REQUEST SENIOR_POOL_CANCEL_WITHDRAWAL_REQUEST # NOTE: We do not need to define a `SENIOR_POOL_UNSTAKE_AND_WITHDRAWAL_MULTIPLE` transaction # category, because UnstakedAndWithdrew and UnstakedAndWithdrewMultiple events are emitted by # the StakingRewards contract mutually exclusively, so for indexing them in the subgraph, we # can combine both of them into the same category. SENIOR_POOL_UNSTAKE # NOTE: We do not define a `SENIOR_POOL_UNSTAKE_MULTIPLE` transaction category for two reasons: # (1) `Unstaked` and `UnstakedMultiple` events are not emitted mutually exclusively by the StakingRewards # contract; on the contrary, for every `UnstakedMultiple` event, a corresponding set of `Unstaked` # events are emitted whose params collectively comprise the same data as the `UnstakedMultiple` event. # So we don't need to index `UnstakedMultiple` events for the sake of obtaining any unique data. And # doing so would only create a need for the consumer of the subgraph to de-duplicate between an # `UnstakedMultiple` event and its corresponding set of `Unstaked` events. # (2) The positions unstaked in an `UnstakedMultiple` event can be of different types (e.g. FIDU # and Curve LP), so their respective amounts are not necessarily commensurable, so summing # them to a total amount would be inappropriate. Our `Transaction` schema currently supports # only a single `amount` value for the transaction, so it "wants" us to do such summing. But we # cannot. SENIOR_POOL_REDEMPTION SENIOR_POOL_DISTRIBUTION TRANCHED_POOL_DEPOSIT TRANCHED_POOL_WITHDRAWAL TRANCHED_POOL_REPAYMENT TRANCHED_POOL_DRAWDOWN UID_MINTED CURVE_FIDU_BUY CURVE_FIDU_SELL STAKING_REWARDS_CLAIMED BACKER_REWARDS_CLAIMED COMMUNITY_REWARDS_CLAIMED MEMBERSHIP_REWARDS_CLAIMED MEMBERSHIP_GFI_DEPOSIT MEMBERSHIP_GFI_WITHDRAWAL MEMBERSHIP_CAPITAL_DEPOSIT MEMBERSHIP_CAPITAL_WITHDRAWAL CALL_REQUEST_SUBMITTED } enum SupportedCrypto { USDC GFI FIDU CURVE_LP } enum SupportedNft { STAKING_TOKEN POOL_TOKEN UID } type Transaction @entity(immutable: true) { """ This is a concatenation of transaction hash with log index. One transaction can result in more than one of this entity. """ id: ID! # It's tempting to type this as Bytes, but there's a bug in graph-node that makes it impossible to query the `loan` property if you do that. https://github.com/graphprotocol/graph-node/issues/3566 (it says fixed but it is not fixed) transactionHash: Bytes! category: TransactionCategory! user: User! timestamp: Int! blockNumber: Int! sentAmount: BigInt sentToken: SupportedCrypto sentNftId: String sentNftType: SupportedNft receivedAmount: BigInt receivedToken: SupportedCrypto receivedNftId: String receivedNftType: SupportedNft loan: Loan fiduPrice: BigInt } type Zap @entity { """ Note that the ID of a Zap is the same as the ID of the (tranched) pool token it's tied to. Makes it easy to find a Zap corresponding to a pool token. """ id: ID! amount: BigInt! user: User! seniorPoolStakedPosition: SeniorPoolStakedPosition! poolToken: PoolToken! tranchedPool: TranchedPool! } enum CommunityRewardsTokenSource { MERKLE_DISTRIBUTOR BACKER_MERKLE_DISTRIBUTOR } type CommunityRewardsToken @entity { id: ID! source: CommunityRewardsTokenSource! index: Int! user: User! totalGranted: BigInt! totalClaimed: BigInt! cliffLength: BigInt! vestingLength: BigInt! vestingInterval: BigInt! grantedAt: BigInt! revokedAt: BigInt! startTime: BigInt! endTime: BigInt! } type MembershipEpoch @entity(immutable: true) { id: ID! epoch: BigInt! # The epoch number totalRewards: BigInt! # Measured in FIDU finalizedAt: Int! } type VaultedGfi @entity { id: ID! user: User! amount: BigInt! vaultedAt: Int! } type VaultedStakedPosition @entity { id: ID! user: User! usdcEquivalent: BigInt! vaultedAt: Int! seniorPoolStakedPosition: SeniorPoolStakedPosition! } type VaultedPoolToken @entity { id: ID! user: User! usdcEquivalent: BigInt! vaultedAt: Int! poolToken: PoolToken! loan: Loan! } type Membership @entity { id: ID! user: User! eligibleScore: BigInt! nextEpochScore: BigInt! } type MembershipRoster @entity { id: ID! # hard-coded to 1. This is a singleton. members: [Membership!]! eligibleScoreTotal: BigInt! nextEpochScoreTotal: BigInt! lastCheckpointedInEpoch: BigInt! } type MembershipRewardDisbursement @entity(immutable: true) { id: ID! user: User! epoch: BigInt! # The epoch number allocatedAt: Int! rewards: BigInt! } type SeniorPoolWithdrawalRequest @entity { id: ID! # corresponds to the user address, since each user can only have one withdrawal request active at a time tokenId: BigInt! # corresponds to the WithdrawalRequest token user: User! usdcWithdrawable: BigInt! fiduRequested: BigInt! requestedAt: Int! increasedAt: Int canceledAt: Int } type SeniorPoolWithdrawalEpoch @entity(immutable: true) { id: ID! epoch: BigInt! # the epoch number endsAt: Int! fiduRequested: BigInt! fiduLiquidated: BigInt! usdcAllocated: BigInt! } # Singleton that keeps track of all outstanding withdrawal requests type SeniorPoolWithdrawalRequestRoster @entity { id: ID! # hard-coded to 1. This is a singleton. requests: [SeniorPoolWithdrawalRequest!]! } type SeniorPoolWithdrawalDisbursement @entity(immutable: true) { id: ID! user: User! """ If the user has had multiple withdrawal requests submitted and fulfilled over time, it might be useful to know what tokenId this distribution was associated with. """ tokenId: BigInt! epoch: BigInt! # The epoch number. Not a reference. allocatedAt: Int! usdcAllocated: BigInt! fiduLiquidated: BigInt! } """ This entity is created when there is no money to allocate at the end of a withdrawal epoch """ type SeniorPoolWithdrawalDisbursementPostponement @entity(immutable: true) { id: ID! user: User! tokenId: BigInt! extendedEpoch: BigInt! # The epoch that was extended oldEndsAt: Int! newEndsAt: Int! } type CallableLoan implements Loan @entity { id: ID! address: Bytes! fundingLimit: BigInt! principalAmount: BigInt! initialInterestOwed: BigInt! usdcApy: BigDecimal! rawGfiApy: BigDecimal! totalDeposited: BigInt! remainingCapacity: BigInt! fundableAt: Int! createdAt: Int! allowedUidTypes: [UidType!]! backers: [User!]! numBackers: Int! isPaused: Boolean! drawdownsPaused: Boolean! tokens: [PoolToken!]! balance: BigInt! termEndTime: BigInt! termStartTime: BigInt! termInSeconds: Int! interestRate: BigDecimal! interestRateBigInt: BigInt! # For use in calculations that have to be done with integers (to mimic on-chain math) lateFeeRate: BigDecimal! lastFullPaymentTime: Int! availableForDrawdown: BigInt! borrowerContract: BorrowerContract! creditLineAddress: Bytes! repaymentFrequency: RepaymentFrequency! repaymentSchedule: [ScheduledRepayment!]! """ Please note that numRepayments never updates, even if the underlying repaymentSchedule is changed. It always reflects the initial number of expected repayments. """ numRepayments: Int! principalAmountRepaid: BigInt! interestAmountRepaid: BigInt! } type ScheduledRepayment @entity { id: ID! loan: Loan! paymentPeriod: Int! estimatedPaymentDate: Int! principal: BigInt! interest: BigInt! }