Skip to content

Commit 77f4a2e

Browse files
committed
feat: added horizon delegation events support
1 parent 437c6ab commit 77f4a2e

File tree

5 files changed

+331
-22
lines changed

5 files changed

+331
-22
lines changed

abis/HorizonStaking.json

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,11 @@
254254
"name": "HorizonStakingInvalidZeroTokens",
255255
"type": "error"
256256
},
257+
{
258+
"inputs": [],
259+
"name": "HorizonStakingLegacySlashFailed",
260+
"type": "error"
261+
},
257262
{
258263
"inputs": [
259264
{
@@ -280,6 +285,11 @@
280285
"name": "HorizonStakingNothingThawing",
281286
"type": "error"
282287
},
288+
{
289+
"inputs": [],
290+
"name": "HorizonStakingNothingToWithdraw",
291+
"type": "error"
292+
},
283293
{
284294
"inputs": [],
285295
"name": "HorizonStakingProvisionAlreadyExists",
@@ -1159,6 +1169,12 @@
11591169
"internalType": "uint256",
11601170
"name": "tokens",
11611171
"type": "uint256"
1172+
},
1173+
{
1174+
"indexed": false,
1175+
"internalType": "uint256",
1176+
"name": "shares",
1177+
"type": "uint256"
11621178
}
11631179
],
11641180
"name": "TokensUndelegated",
@@ -1517,7 +1533,7 @@
15171533
"outputs": [
15181534
{
15191535
"internalType": "uint256",
1520-
"name": "tokens",
1536+
"name": "",
15211537
"type": "uint256"
15221538
}
15231539
],
@@ -1682,6 +1698,19 @@
16821698
"stateMutability": "view",
16831699
"type": "function"
16841700
},
1701+
{
1702+
"inputs": [],
1703+
"name": "getStakingExtension",
1704+
"outputs": [
1705+
{
1706+
"internalType": "address",
1707+
"name": "",
1708+
"type": "address"
1709+
}
1710+
],
1711+
"stateMutability": "view",
1712+
"type": "function"
1713+
},
16851714
{
16861715
"inputs": [
16871716
{
@@ -2423,9 +2452,5 @@
24232452
],
24242453
"stateMutability": "nonpayable",
24252454
"type": "function"
2426-
},
2427-
{
2428-
"stateMutability": "payable",
2429-
"type": "receive"
24302455
}
24312456
]

schema.graphql

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -893,12 +893,22 @@ type Provision @entity {
893893
thawingPeriodPending: BigInt!
894894

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

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

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

905+
"The percent of indexing rewards generated by the delegated stake that the Indexer keeps for itself"
906+
indexingRewardEffectiveCut: BigDecimal!
907+
"The percent of query rebate rewards generated by the delegated stake that the Indexer keeps for itself"
908+
queryFeeEffectiveCut: BigDecimal!
909+
"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."
910+
overDelegationDilution: BigDecimal!
911+
902912
# Might want to add polymorphic handling of different fee cuts through a generic hashmap in the future
903913

904914
# Indexing rewards. Keeping the same naming from Indexer entity to make it easier to understand
@@ -924,6 +934,10 @@ type Provision @entity {
924934
delegatorShares: BigInt!
925935
"Exchange rate of of tokens received for each share"
926936
delegationExchangeRate: BigDecimal!
937+
"Ratio between the amount of the indexers own stake over the total usable stake."
938+
ownStakeRatio: BigDecimal!
939+
"Ratio between the amount of delegated stake over the total usable stake."
940+
delegatedStakeRatio: BigDecimal!
927941

928942
"Service registry URL for the indexer"
929943
url: String
@@ -1111,8 +1125,12 @@ Delegator stake for a single Indexer
11111125
type DelegatedStake @entity {
11121126
"Concatenation of Delegator address and Indexer address"
11131127
id: ID!
1114-
"Index the stake is delegated to"
1128+
"Indexer the stake is delegated to"
11151129
indexer: Indexer!
1130+
"DataService the stake is delegated to. Only for Horizon delegations"
1131+
dataService: DataService
1132+
"Provision the stake is delegated to. Only for Horizon delegations"
1133+
provision: Provision
11161134
"Delegator"
11171135
delegator: Delegator!
11181136
"CUMULATIVE tokens delegated"

src/mappings/helpers/helpers.ts

Lines changed: 114 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,46 @@ export function createOrLoadDelegatedStake(
348348
}
349349
return delegatedStake as DelegatedStake
350350
}
351+
352+
export function createOrLoadDelegatedStakeForProvision(
353+
delegator: string,
354+
indexer: string,
355+
dataService: string,
356+
timestamp: i32,
357+
): DelegatedStake {
358+
let provisionId = joinID([indexer, dataService])
359+
let id = joinID([delegator, provisionId])
360+
let delegatedStake = DelegatedStake.load(id)
361+
if (delegatedStake == null) {
362+
delegatedStake = new DelegatedStake(id)
363+
delegatedStake.indexer = indexer
364+
delegatedStake.dataService = dataService
365+
delegatedStake.provision = provisionId
366+
delegatedStake.delegator = delegator
367+
delegatedStake.stakedTokens = BigInt.fromI32(0)
368+
delegatedStake.transferredToL2 = false
369+
delegatedStake.stakedTokensTransferredToL2 = BigInt.fromI32(0)
370+
delegatedStake.unstakedTokens = BigInt.fromI32(0)
371+
delegatedStake.lockedTokens = BigInt.fromI32(0)
372+
delegatedStake.lockedUntil = 0
373+
delegatedStake.shareAmount = BigInt.fromI32(0)
374+
delegatedStake.personalExchangeRate = BigDecimal.fromString('1')
375+
delegatedStake.realizedRewards = BigDecimal.fromString('0')
376+
delegatedStake.createdAt = timestamp
377+
378+
delegatedStake.save()
379+
380+
let delegatorEntity = Delegator.load(delegator)!
381+
delegatorEntity.stakesCount = delegatorEntity.stakesCount + 1
382+
delegatorEntity.save()
383+
384+
let graphNetwork = GraphNetwork.load('1')!
385+
graphNetwork.delegationCount = graphNetwork.delegationCount + 1
386+
graphNetwork.save()
387+
}
388+
return delegatedStake as DelegatedStake
389+
}
390+
351391
export function createOrLoadCurator(curatorAddress: Bytes, timestamp: BigInt): Curator {
352392
let id = curatorAddress.toHexString()
353393
let curator = Curator.load(id)
@@ -916,6 +956,68 @@ export function updateAdvancedIndexerMetrics(indexer: Indexer): Indexer {
916956
return indexer as Indexer
917957
}
918958

959+
export function calculateOwnStakeRatioForProvision(provision: Provision): BigDecimal {
960+
let totalTokens = provision.tokensProvisioned.plus(provision.delegatedTokens)
961+
return totalTokens == BigInt.fromI32(0)
962+
? BigDecimal.fromString('0')
963+
: provision.tokensProvisioned.toBigDecimal().div(totalTokens.toBigDecimal())
964+
}
965+
966+
export function calculateDelegatedStakeRatioForProvision(provision: Provision): BigDecimal {
967+
let totalTokens = provision.tokensProvisioned.plus(provision.delegatedTokens)
968+
return totalTokens == BigInt.fromI32(0)
969+
? BigDecimal.fromString('0')
970+
: provision.delegatedTokens.toBigDecimal().div(totalTokens.toBigDecimal())
971+
}
972+
973+
export function calculateIndexingRewardEffectiveCutForProvision(provision: Provision): BigDecimal {
974+
let delegatorCut =
975+
provision.indexingRewardsCut.toBigDecimal() /
976+
BigDecimal.fromString('1000000')
977+
return provision.delegatedStakeRatio == BigDecimal.fromString('0')
978+
? BigDecimal.fromString('0')
979+
: BigDecimal.fromString('1') - delegatorCut / provision.delegatedStakeRatio
980+
}
981+
982+
export function calculateQueryFeeEffectiveCutForProvision(provision: Provision): BigDecimal {
983+
let delegatorCut =
984+
provision.queryFeeCut.toBigDecimal() / BigDecimal.fromString('1000000')
985+
return provision.delegatedStakeRatio == BigDecimal.fromString('0')
986+
? BigDecimal.fromString('0')
987+
: BigDecimal.fromString('1') - delegatorCut / provision.delegatedStakeRatio
988+
}
989+
990+
export function calculateIndexerRewardOwnGenerationRatioForProvision(provision: Provision): BigDecimal {
991+
let rewardCut =
992+
provision.indexingRewardsCut.toBigDecimal() / BigDecimal.fromString('1000000')
993+
return provision.ownStakeRatio == BigDecimal.fromString('0')
994+
? BigDecimal.fromString('0')
995+
: rewardCut / provision.ownStakeRatio
996+
}
997+
998+
export function calculateOverdelegationDilutionForProvision(provision: Provision): BigDecimal {
999+
let provisionedTokensBD = provision.tokensProvisioned.toBigDecimal()
1000+
let delegatedTokensBD = provision.delegatedTokens.toBigDecimal()
1001+
let graphNetwork = GraphNetwork.load('1')!
1002+
let delegationRatioBD = BigInt.fromI32(graphNetwork.delegationRatio).toBigDecimal()
1003+
let maxDelegatedStake = provisionedTokensBD * delegationRatioBD
1004+
return provisionedTokensBD == BigDecimal.fromString('0')
1005+
? BigDecimal.fromString('0')
1006+
: BigDecimal.fromString('1') - maxDelegatedStake / max(maxDelegatedStake, delegatedTokensBD)
1007+
}
1008+
1009+
export function updateAdvancedProvisionMetrics(provision: Provision): Provision {
1010+
provision.ownStakeRatio = calculateOwnStakeRatioForProvision(provision as Provision)
1011+
provision.delegatedStakeRatio = calculateDelegatedStakeRatioForProvision(provision as Provision)
1012+
provision.indexingRewardEffectiveCut = calculateIndexingRewardEffectiveCutForProvision(provision as Provision)
1013+
provision.queryFeeEffectiveCut = calculateQueryFeeEffectiveCutForProvision(provision as Provision)
1014+
provision.indexerRewardsOwnGenerationRatio = calculateIndexerRewardOwnGenerationRatioForProvision(
1015+
provision as Provision,
1016+
)
1017+
provision.overDelegationDilution = calculateOverdelegationDilutionForProvision(provision as Provision)
1018+
return provision as Provision
1019+
}
1020+
9191021
export function updateDelegationExchangeRate(indexer: Indexer): Indexer {
9201022
indexer.delegationExchangeRate = indexer.delegatedTokens
9211023
.toBigDecimal()
@@ -924,6 +1026,14 @@ export function updateDelegationExchangeRate(indexer: Indexer): Indexer {
9241026
return indexer as Indexer
9251027
}
9261028

1029+
export function updateDelegationExchangeRateForProvision(provision: Provision): Provision {
1030+
provision.delegationExchangeRate = provision.delegatedTokens
1031+
.toBigDecimal()
1032+
.div(provision.delegatorShares.toBigDecimal())
1033+
.truncate(18)
1034+
return provision as Provision
1035+
}
1036+
9271037
// TODO - this is broken if we change the delegatio ratio
9281038
// Need to remove, or find a fix
9291039
export function calculateCapacities(indexer: Indexer): Indexer {
@@ -957,10 +1067,10 @@ export function calculatePricePerShare(deployment: SubgraphDeployment): BigDecim
9571067
deployment.signalAmount == BigInt.fromI32(0)
9581068
? BigDecimal.fromString('0')
9591069
: deployment.signalledTokens
960-
.toBigDecimal()
961-
.div(deployment.signalAmount.toBigDecimal())
962-
.times(BigInt.fromI32(reserveRatioMultiplier).toBigDecimal())
963-
.truncate(18)
1070+
.toBigDecimal()
1071+
.div(deployment.signalAmount.toBigDecimal())
1072+
.times(BigInt.fromI32(reserveRatioMultiplier).toBigDecimal())
1073+
.truncate(18)
9641074
return pricePerShare
9651075
}
9661076

0 commit comments

Comments
 (0)