Skip to content
Merged
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
35 changes: 30 additions & 5 deletions abis/HorizonStaking.json
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,11 @@
"name": "HorizonStakingInvalidZeroTokens",
"type": "error"
},
{
"inputs": [],
"name": "HorizonStakingLegacySlashFailed",
"type": "error"
},
{
"inputs": [
{
Expand All @@ -280,6 +285,11 @@
"name": "HorizonStakingNothingThawing",
"type": "error"
},
{
"inputs": [],
"name": "HorizonStakingNothingToWithdraw",
"type": "error"
},
{
"inputs": [],
"name": "HorizonStakingProvisionAlreadyExists",
Expand Down Expand Up @@ -1159,6 +1169,12 @@
"internalType": "uint256",
"name": "tokens",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "shares",
"type": "uint256"
}
],
"name": "TokensUndelegated",
Expand Down Expand Up @@ -1517,7 +1533,7 @@
"outputs": [
{
"internalType": "uint256",
"name": "tokens",
"name": "",
"type": "uint256"
}
],
Expand Down Expand Up @@ -1682,6 +1698,19 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getStakingExtension",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
Expand Down Expand Up @@ -2423,9 +2452,5 @@
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
]
29 changes: 28 additions & 1 deletion schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,9 @@ type DataService @entity {
"Total GRT currently in allocations for this DataService"
totalTokensAllocated: BigInt!

"Ratio of max staked delegation tokens to indexers stake that earns rewards"
delegationRatio: Int

provisions: [Provision!]! @derivedFrom(field: "dataService")
}

Expand Down Expand Up @@ -867,6 +870,10 @@ type Provision @entity {

tokensAllocated: BigInt!

tokensSlashedServiceProvider: BigInt!

tokensSlashedDelegationPool: BigInt!

totalAllocationCount: BigInt!

allocationCount: Int!
Expand All @@ -893,12 +900,22 @@ type Provision @entity {
thawingPeriodPending: BigInt!

# Cuts for subgraph service. Percentage of rewards that the delegation pool receives
"Raw query fee cut. In Horizon this is amount of query fees that the delegation pool receives in PPM"
queryFeeCut: BigInt!

"Raw indexing fee cut. In Horizon this is amount of indexing fees that the delegation pool receives in PPM"
indexingFeeCut: BigInt!

"Raw indexing rewards cut. In Horizon this is amount of indexing rewards that the delegation pool receives in PPM"
indexingRewardsCut: BigInt!

"The percent of indexing rewards generated by the delegated stake that the Indexer keeps for itself"
indexingRewardEffectiveCut: BigDecimal!
"The percent of query fees generated by the delegated stake that the Indexer keeps for itself"
queryFeeEffectiveCut: BigDecimal!
"The percent of reward dilution delegators experience because of overdelegation. Overdelegated stake can't be used to generate rewards but still gets accounted while distributing the generated rewards. This causes dilution of the rewards for the rest of the pool."
overDelegationDilution: BigDecimal!

# Might want to add polymorphic handling of different fee cuts through a generic hashmap in the future

# Indexing rewards. Keeping the same naming from Indexer entity to make it easier to understand
Expand All @@ -924,6 +941,12 @@ type Provision @entity {
delegatorShares: BigInt!
"Exchange rate of of tokens received for each share"
delegationExchangeRate: BigDecimal!
"Ratio between the amount of the indexers own stake over the total usable stake (capped by the delegationRatio)."
ownStakeRatio: BigDecimal!
"Ratio between the amount of delegated stake over the total usable stake (capped by the delegationRatio)."
delegatedStakeRatio: BigDecimal!
"Percentage of indexers' own rewards received in relation to its own stake. 1 (100%) means that the indexer is receiving the exact amount that is generated by his own stake, the value can be over 100% or below depending on the amount of delegation and cuts set"
indexerRewardsOwnGenerationRatio: BigDecimal!

"Service registry URL for the indexer"
url: String
Expand Down Expand Up @@ -1111,8 +1134,12 @@ Delegator stake for a single Indexer
type DelegatedStake @entity {
"Concatenation of Delegator address and Indexer address"
id: ID!
"Index the stake is delegated to"
"Indexer the stake is delegated to"
indexer: Indexer!
"DataService the stake is delegated to. Only for Horizon delegations"
dataService: DataService
"Provision the stake is delegated to. Only for Horizon delegations"
provision: Provision
"Delegator"
delegator: Delegator!
"CUMULATIVE tokens delegated"
Expand Down
128 changes: 124 additions & 4 deletions src/mappings/helpers/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ export function createOrLoadProvision(indexerAddress: Bytes, verifierAddress: By
provision.dataService = verifierAddress.toHexString()
provision.tokensProvisioned = BigInt.fromI32(0)
provision.tokensAllocated = BigInt.fromI32(0)
provision.tokensSlashedServiceProvider = BigInt.fromI32(0)
provision.tokensSlashedDelegationPool = BigInt.fromI32(0)
provision.totalAllocationCount = BigInt.fromI32(0)
provision.allocationCount = 0
provision.tokensThawing = BigInt.fromI32(0)
Expand All @@ -216,6 +218,11 @@ export function createOrLoadProvision(indexerAddress: Bytes, verifierAddress: By
provision.delegatedTokens = BigInt.fromI32(0)
provision.delegatorShares = BigInt.fromI32(0)
provision.delegationExchangeRate = BigInt.fromI32(0).toBigDecimal()
provision.indexingRewardEffectiveCut = BigInt.fromI32(0).toBigDecimal()
provision.queryFeeEffectiveCut = BigInt.fromI32(0).toBigDecimal()
provision.ownStakeRatio = BigInt.fromI32(0).toBigDecimal()
provision.delegatedStakeRatio = BigInt.fromI32(0).toBigDecimal()
provision.indexerRewardsOwnGenerationRatio = BigInt.fromI32(0).toBigDecimal()
provision.save()
}

Expand Down Expand Up @@ -348,6 +355,46 @@ export function createOrLoadDelegatedStake(
}
return delegatedStake as DelegatedStake
}

export function createOrLoadDelegatedStakeForProvision(
delegator: string,
indexer: string,
dataService: string,
timestamp: i32,
): DelegatedStake {
let provisionId = joinID([indexer, dataService])
let id = joinID([delegator, provisionId])
let delegatedStake = DelegatedStake.load(id)
if (delegatedStake == null) {
delegatedStake = new DelegatedStake(id)
delegatedStake.indexer = indexer
delegatedStake.dataService = dataService
delegatedStake.provision = provisionId
delegatedStake.delegator = delegator
delegatedStake.stakedTokens = BigInt.fromI32(0)
delegatedStake.transferredToL2 = false
delegatedStake.stakedTokensTransferredToL2 = BigInt.fromI32(0)
delegatedStake.unstakedTokens = BigInt.fromI32(0)
delegatedStake.lockedTokens = BigInt.fromI32(0)
delegatedStake.lockedUntil = 0
delegatedStake.shareAmount = BigInt.fromI32(0)
delegatedStake.personalExchangeRate = BigDecimal.fromString('1')
delegatedStake.realizedRewards = BigDecimal.fromString('0')
delegatedStake.createdAt = timestamp

delegatedStake.save()

let delegatorEntity = Delegator.load(delegator)!
delegatorEntity.stakesCount = delegatorEntity.stakesCount + 1
delegatorEntity.save()

let graphNetwork = GraphNetwork.load('1')!
graphNetwork.delegationCount = graphNetwork.delegationCount + 1
graphNetwork.save()
}
return delegatedStake as DelegatedStake
}

export function createOrLoadCurator(curatorAddress: Bytes, timestamp: BigInt): Curator {
let id = curatorAddress.toHexString()
let curator = Curator.load(id)
Expand Down Expand Up @@ -916,6 +963,71 @@ export function updateAdvancedIndexerMetrics(indexer: Indexer): Indexer {
return indexer as Indexer
}

export function calculateOwnStakeRatioForProvision(provision: Provision): BigDecimal {
let totalTokens = provision.tokensProvisioned.plus(provision.delegatedTokens)
return totalTokens == BigInt.fromI32(0)
? BigDecimal.fromString('0')
: provision.tokensProvisioned.toBigDecimal().div(totalTokens.toBigDecimal())
}

export function calculateDelegatedStakeRatioForProvision(provision: Provision): BigDecimal {
let totalTokens = provision.tokensProvisioned.plus(provision.delegatedTokens)
return totalTokens == BigInt.fromI32(0)
? BigDecimal.fromString('0')
: provision.delegatedTokens.toBigDecimal().div(totalTokens.toBigDecimal())
}

export function calculateIndexingRewardEffectiveCutForProvision(provision: Provision): BigDecimal {
let delegatorCut =
provision.indexingRewardsCut.toBigDecimal() /
BigDecimal.fromString('1000000')
return provision.delegatedStakeRatio == BigDecimal.fromString('0')
? BigDecimal.fromString('0')
: BigDecimal.fromString('1') - delegatorCut / provision.delegatedStakeRatio
}

export function calculateQueryFeeEffectiveCutForProvision(provision: Provision): BigDecimal {
let delegatorCut =
provision.queryFeeCut.toBigDecimal() / BigDecimal.fromString('1000000')
return provision.delegatedStakeRatio == BigDecimal.fromString('0')
? BigDecimal.fromString('0')
: BigDecimal.fromString('1') - delegatorCut / provision.delegatedStakeRatio
}

export function calculateIndexerRewardOwnGenerationRatioForProvision(provision: Provision): BigDecimal {
let delegatorCut =
provision.indexingRewardsCut.toBigDecimal() / BigDecimal.fromString('1000000')
return provision.ownStakeRatio == BigDecimal.fromString('0')
? BigDecimal.fromString('0')
: (BigDecimal.fromString('1') - delegatorCut) / provision.ownStakeRatio
}

export function calculateOverdelegationDilutionForProvision(provision: Provision): BigDecimal {
let provisionedTokensBD = provision.tokensProvisioned.toBigDecimal()
let delegatedTokensBD = provision.delegatedTokens.toBigDecimal()
let dataService = DataService.load(provision.dataService)!
if (dataService.delegationRatio == null) {
return BigDecimal.fromString('0')
}
let delegationRatioBD = BigInt.fromI32(dataService.delegationRatio).toBigDecimal()
let maxDelegatedStake = provisionedTokensBD * delegationRatioBD
return provisionedTokensBD == BigDecimal.fromString('0')
? BigDecimal.fromString('0')
: BigDecimal.fromString('1') - maxDelegatedStake / max(maxDelegatedStake, delegatedTokensBD)
}

export function updateAdvancedProvisionMetrics(provision: Provision): Provision {
provision.ownStakeRatio = calculateOwnStakeRatioForProvision(provision as Provision)
provision.delegatedStakeRatio = calculateDelegatedStakeRatioForProvision(provision as Provision)
provision.indexingRewardEffectiveCut = calculateIndexingRewardEffectiveCutForProvision(provision as Provision)
provision.queryFeeEffectiveCut = calculateQueryFeeEffectiveCutForProvision(provision as Provision)
provision.indexerRewardsOwnGenerationRatio = calculateIndexerRewardOwnGenerationRatioForProvision(
provision as Provision,
)
provision.overDelegationDilution = calculateOverdelegationDilutionForProvision(provision as Provision)
return provision as Provision
}

export function updateDelegationExchangeRate(indexer: Indexer): Indexer {
indexer.delegationExchangeRate = indexer.delegatedTokens
.toBigDecimal()
Expand All @@ -924,6 +1036,14 @@ export function updateDelegationExchangeRate(indexer: Indexer): Indexer {
return indexer as Indexer
}

export function updateDelegationExchangeRateForProvision(provision: Provision): Provision {
provision.delegationExchangeRate = provision.delegatedTokens
.toBigDecimal()
.div(provision.delegatorShares.toBigDecimal())
.truncate(18)
return provision as Provision
}

// TODO - this is broken if we change the delegatio ratio
// Need to remove, or find a fix
export function calculateCapacities(indexer: Indexer): Indexer {
Expand Down Expand Up @@ -957,10 +1077,10 @@ export function calculatePricePerShare(deployment: SubgraphDeployment): BigDecim
deployment.signalAmount == BigInt.fromI32(0)
? BigDecimal.fromString('0')
: deployment.signalledTokens
.toBigDecimal()
.div(deployment.signalAmount.toBigDecimal())
.times(BigInt.fromI32(reserveRatioMultiplier).toBigDecimal())
.truncate(18)
.toBigDecimal()
.div(deployment.signalAmount.toBigDecimal())
.times(BigInt.fromI32(reserveRatioMultiplier).toBigDecimal())
.truncate(18)
return pricePerShare
}

Expand Down
Loading