Skip to content

Commit cacadce

Browse files
committed
Fetch restaking events from Nimiq.Watch and display as gains
1 parent 2efb1ce commit cacadce

File tree

7 files changed

+66
-24
lines changed

7 files changed

+66
-24
lines changed

src/components/layouts/AddressOverview.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@
176176
/>
177177

178178
<template v-if="activeCurrency === 'nim'">
179-
<StakingPreview v-if="activeStake && windowWidth > 860" :show-gains="false" />
179+
<StakingPreview v-if="activeStake && windowWidth > 860" />
180180
<StakingButton v-else-if="showStakingButton" />
181181
</template>
182182

@@ -196,7 +196,7 @@
196196
</button>
197197
</div>
198198
</div>
199-
<StakingPreview v-if="activeStake && isMobile" class="staking-preview-mobile" :show-gains="false" />
199+
<StakingPreview v-if="activeStake && isMobile" class="staking-preview-mobile" />
200200
<div
201201
v-if="activeCurrency === 'usdc' && accountUsdcBridgedBalance >= 0.1e6"
202202
class="bridged-usdc-notice"

src/components/staking/StakingPreview.vue

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,14 @@
1313

1414
<div class="flex-grow"></div>
1515

16-
<div v-if="showGains" class="gain flex-row">
17-
<template v-if="gain">
18-
+<Amount :amount="gain" value-mask />
19-
</template>
20-
<template v-else-if="validator && 'annualReward' in validator">
21-
{{ (validator.annualReward * 100).toFixed(1) }}% {{ $t("p.a.") }}
22-
</template>
16+
<div v-if="restakingRewards !== null" class="gain flex-row">
17+
+<Amount :amount="restakingRewards" value-mask />
2318
</div>
2419
</button>
2520
</template>
2621

2722
<script lang="ts">
28-
import { computed, defineComponent, ref } from '@vue/composition-api';
23+
import { computed, defineComponent } from '@vue/composition-api';
2924
import Amount from '../Amount.vue';
3025
import { useStakingStore } from '../../stores/Staking';
3126
import OneLeafStakingIcon from '../icons/Staking/OneLeafStakingIcon.vue';
@@ -34,32 +29,26 @@ import ThreeLeafStakingIcon from '../icons/Staking/ThreeLeafStakingIcon.vue';
3429
import { useAddressStore } from '../../stores/Address';
3530
3631
export default defineComponent({
37-
props: {
38-
showGains: {
39-
type: Boolean,
40-
default: true,
41-
},
42-
},
4332
setup() {
44-
const { activeStake: stake, activeValidator: validator } = useStakingStore();
33+
const { activeStake: stake, restakingRewards } = useStakingStore();
4534
const { activeAddressInfo } = useAddressStore();
4635
47-
// TODO: Calculate gain
48-
const gain = ref(0);
49-
5036
const currentPercentage = computed(() => {
5137
const alreadyStakedAmount = stake.value?.activeBalance || 0;
52-
const availableAmount = (activeAddressInfo.value?.balance || 0) + alreadyStakedAmount;
38+
const deactivatedAmount = stake.value?.inactiveBalance || 0;
39+
const retiredAmount = stake.value?.retiredBalance || 0;
40+
const totalStakedAmount = alreadyStakedAmount + deactivatedAmount + retiredAmount;
41+
42+
const availableAmount = (activeAddressInfo.value?.balance || 0) + totalStakedAmount;
5343
const percent = (100 * alreadyStakedAmount) / availableAmount;
5444
5545
return percent;
5646
});
5747
5848
return {
5949
stake,
60-
validator,
61-
gain,
6250
currentPercentage,
51+
restakingRewards,
6352
};
6453
},
6554
components: {

src/config/config.local.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export default {
2525
prestakingEndBlock: 3_028_050,
2626
transitionBlock: 3_032_010,
2727
validatorsEndpoint: 'https://validators-api-testnet.nuxt.dev/api/v1/validators?only-known=false',
28+
stakeEventsEndpoint: 'https://v2.test.nimiqwatch.com/api/v2/staker/ADDRESS/events?filter=add-stake',
2829
genesis: {
2930
height: 3032010,
3031
date: new Date('2024-11-13T20:00:00Z'),

src/config/config.mainnet.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export default {
3535
prestakingEndBlock: 3_456_000, // ~2024-11-19T16:00:00Z
3636
transitionBlock: 3_456_000,
3737
validatorsEndpoint: 'https://validators-api-mainnet.nuxt.dev/api/v1/validators?only-known=false',
38+
stakeEventsEndpoint: 'https://v2.nimiqwatch.com/api/v2/staker/ADDRESS/events?filter=add-stake',
3839
genesis: {
3940
height: 3456000,
4041
date: new Date('2024-11-19T16:00:00Z'),

src/config/config.testnet.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export default {
2323
prestakingEndBlock: 3_028_050,
2424
transitionBlock: 3_032_010,
2525
validatorsEndpoint: 'https://validators-api-testnet.nuxt.dev/api/v1/validators?with-scores=true',
26+
stakeEventsEndpoint: 'https://v2.test.nimiqwatch.com/api/v2/staker/ADDRESS/events?filter=add-stake',
2627
genesis: {
2728
height: 3032010,
2829
date: new Date('2024-11-13T20:00:00Z'),

src/network.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { useTransactionsStore, TransactionState } from './stores/Transactions';
88
import { useNetworkStore } from './stores/Network';
99
import { useProxyStore } from './stores/Proxy';
1010
import { useConfig } from './composables/useConfig';
11-
import { useStakingStore, Validator } from './stores/Staking';
11+
import { AddStakeEvent, useStakingStore, Validator } from './stores/Staking';
1212
import { ENV_MAIN, STAKING_CONTRACT_ADDRESS } from './lib/Constants';
1313
import { calculateStakingReward } from './lib/AlbatrossMath';
1414
import { reportToSentry } from './lib/Sentry';
@@ -137,6 +137,22 @@ export async function launchNetwork() {
137137
});
138138
}
139139

140+
watch([addressStore.activeAddress], ([activeAddress]) => {
141+
if (!activeAddress) return;
142+
const { config } = useConfig();
143+
const endpoint = config.staking.stakeEventsEndpoint;
144+
const url = endpoint.replace('ADDRESS', activeAddress.replaceAll(' ', '+'));
145+
retry(
146+
() => fetch(url)
147+
.then((res) => res.json())
148+
.then((events: AddStakeEvent[]) => {
149+
useStakingStore().setStakingEvents(activeAddress, events);
150+
console.log('Got add-stake events for', activeAddress, events);
151+
}),
152+
{ maxRetries: 3 },
153+
).catch(reportFor('fetch(add-stake events)'));
154+
});
155+
140156
function forgetBalances(addresses: string[]) {
141157
for (const address of addresses) {
142158
balances.delete(address);

src/stores/Staking.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,18 @@ import { useAddressStore } from './Address';
55
export type StakingState = {
66
validators: Record<string, Validator>,
77
stakeByAddress: Record<string, Stake>,
8+
stakingEventsByAddress: Record<string, StakingEvent[]>,
89
}
910

11+
export type AddStakeEvent = {
12+
sender_address:string, // eslint-disable-line camelcase
13+
date: string,
14+
value: number,
15+
type: number,
16+
}
17+
18+
export type StakingEvent = AddStakeEvent;
19+
1020
export type Stake = {
1121
address: string,
1222
activeBalance: number, // activeBalance (does not include inactiveBalance)
@@ -58,6 +68,7 @@ export const useStakingStore = createStore({
5868
state: () => ({
5969
validators: {},
6070
stakeByAddress: {},
71+
stakingEventsByAddress: {},
6172
} as StakingState),
6273
getters: {
6374
validatorsList: (state): Readonly<Validator[]> => Object.values(state.validators),
@@ -156,6 +167,21 @@ export const useStakingStore = createStore({
156167
active: false,
157168
};
158169
},
170+
171+
stakingEventsByAddress: (state): Readonly<Record<string, StakingEvent[]>> => state.stakingEventsByAddress,
172+
stakingEvents: (state): Readonly<StakingEvent[] | null> => {
173+
const { activeAddress } = useAddressStore();
174+
if (!activeAddress.value) return null;
175+
176+
return state.stakingEventsByAddress[activeAddress.value] || null;
177+
},
178+
restakingRewards: (state, { stakingEvents }): Readonly<number | null> => {
179+
const events = stakingEvents.value as StakingEvent[] | null;
180+
if (!events) return null;
181+
182+
const addStakeEvents: AddStakeEvent[] = events.filter((event) => event.type === 6);
183+
return addStakeEvents.reduce((sum, event) => sum + event.value, 0);
184+
},
159185
},
160186
actions: {
161187
setStake(stake: Stake) {
@@ -205,5 +231,13 @@ export const useStakingStore = createStore({
205231

206232
this.state.validators = newValidators;
207233
},
234+
setStakingEvents(address: string, events: StakingEvent[]) {
235+
// Need to assign whole object for change detection of new addresses.
236+
// TODO: Simply set new stake in Vue 3.
237+
this.state.stakingEventsByAddress = {
238+
...this.state.stakingEventsByAddress,
239+
[address]: events,
240+
};
241+
},
208242
},
209243
});

0 commit comments

Comments
 (0)