min stake from backend (#78)
resolves #74 Co-authored-by: johba <johba@harb.eth> Reviewed-on: https://codeberg.org/johba/harb/pulls/78
This commit is contained in:
parent
0b3545091f
commit
bd475c2271
11 changed files with 1176 additions and 1977 deletions
|
|
@ -30,6 +30,7 @@ type stats {
|
||||||
stakeTotalSupply: BigInt!
|
stakeTotalSupply: BigInt!
|
||||||
outstandingStake: BigInt!
|
outstandingStake: BigInt!
|
||||||
positionsUpdatedAt: BigInt!
|
positionsUpdatedAt: BigInt!
|
||||||
|
minStake: BigInt!
|
||||||
totalMinted: BigInt!
|
totalMinted: BigInt!
|
||||||
totalBurned: BigInt!
|
totalBurned: BigInt!
|
||||||
totalTaxPaid: BigInt!
|
totalTaxPaid: BigInt!
|
||||||
|
|
@ -102,6 +103,14 @@ input statsFilter {
|
||||||
positionsUpdatedAt_lt: BigInt
|
positionsUpdatedAt_lt: BigInt
|
||||||
positionsUpdatedAt_gte: BigInt
|
positionsUpdatedAt_gte: BigInt
|
||||||
positionsUpdatedAt_lte: BigInt
|
positionsUpdatedAt_lte: BigInt
|
||||||
|
minStake: BigInt
|
||||||
|
minStake_not: BigInt
|
||||||
|
minStake_in: [BigInt]
|
||||||
|
minStake_not_in: [BigInt]
|
||||||
|
minStake_gt: BigInt
|
||||||
|
minStake_lt: BigInt
|
||||||
|
minStake_gte: BigInt
|
||||||
|
minStake_lte: BigInt
|
||||||
totalMinted: BigInt
|
totalMinted: BigInt
|
||||||
totalMinted_not: BigInt
|
totalMinted_not: BigInt
|
||||||
totalMinted_in: [BigInt]
|
totalMinted_in: [BigInt]
|
||||||
|
|
@ -419,4 +428,4 @@ input positionsFilter {
|
||||||
payout_lt: BigInt
|
payout_lt: BigInt
|
||||||
payout_gte: BigInt
|
payout_gte: BigInt
|
||||||
payout_lte: BigInt
|
payout_lte: BigInt
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,10 @@ export const stats = onchainTable('stats', t => ({
|
||||||
.bigint()
|
.bigint()
|
||||||
.notNull()
|
.notNull()
|
||||||
.$default(() => 0n),
|
.$default(() => 0n),
|
||||||
|
minStake: t
|
||||||
|
.bigint()
|
||||||
|
.notNull()
|
||||||
|
.$default(() => 0n),
|
||||||
|
|
||||||
// Totals
|
// Totals
|
||||||
totalMinted: t
|
totalMinted: t
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,7 @@ export async function ensureStatsExists(context: StatsContext, timestamp?: bigin
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const [kraikenTotalSupply, stakeTotalSupply, outstandingStake] = await Promise.all([
|
const [kraikenTotalSupply, stakeTotalSupply, outstandingStake, minStake] = await Promise.all([
|
||||||
readWithFallback(
|
readWithFallback(
|
||||||
() =>
|
() =>
|
||||||
client.readContract({
|
client.readContract({
|
||||||
|
|
@ -163,6 +163,16 @@ export async function ensureStatsExists(context: StatsContext, timestamp?: bigin
|
||||||
0n,
|
0n,
|
||||||
'Stake.outstandingStake'
|
'Stake.outstandingStake'
|
||||||
),
|
),
|
||||||
|
readWithFallback(
|
||||||
|
() =>
|
||||||
|
client.readContract({
|
||||||
|
abi: contracts.Kraiken.abi,
|
||||||
|
address: contracts.Kraiken.address,
|
||||||
|
functionName: 'minStake',
|
||||||
|
}),
|
||||||
|
0n,
|
||||||
|
'Kraiken.minStake'
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
cachedStakeTotalSupply = stakeTotalSupply;
|
cachedStakeTotalSupply = stakeTotalSupply;
|
||||||
|
|
@ -178,6 +188,7 @@ export async function ensureStatsExists(context: StatsContext, timestamp?: bigin
|
||||||
ringBufferPointer: 0,
|
ringBufferPointer: 0,
|
||||||
lastHourlyUpdateTimestamp: currentHour,
|
lastHourlyUpdateTimestamp: currentHour,
|
||||||
ringBuffer: serializeRingBuffer(makeEmptyRingBuffer()),
|
ringBuffer: serializeRingBuffer(makeEmptyRingBuffer()),
|
||||||
|
minStake,
|
||||||
});
|
});
|
||||||
|
|
||||||
statsData = await context.db.find(stats, { id: STATS_ID });
|
statsData = await context.db.find(stats, { id: STATS_ID });
|
||||||
|
|
@ -295,3 +306,33 @@ export async function refreshOutstandingStake(context: StatsContext) {
|
||||||
outstandingStake,
|
outstandingStake,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function refreshMinStake(context: StatsContext, statsData?: Awaited<ReturnType<typeof ensureStatsExists>>) {
|
||||||
|
let currentStats = statsData;
|
||||||
|
if (!currentStats) {
|
||||||
|
currentStats = await context.db.find(stats, { id: STATS_ID });
|
||||||
|
}
|
||||||
|
if (!currentStats) {
|
||||||
|
currentStats = await ensureStatsExists(context);
|
||||||
|
}
|
||||||
|
if (!currentStats) return;
|
||||||
|
|
||||||
|
let minStake: bigint;
|
||||||
|
try {
|
||||||
|
minStake = await context.client.readContract({
|
||||||
|
abi: context.contracts.Kraiken.abi,
|
||||||
|
address: context.contracts.Kraiken.address,
|
||||||
|
functionName: 'minStake',
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
const logger = context.logger || console;
|
||||||
|
logger.warn('[stats.refreshMinStake] Failed to read Kraiken.minStake', error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((currentStats.minStake ?? 0n) === minStake) return;
|
||||||
|
|
||||||
|
await context.db.update(stats, { id: STATS_ID }).set({
|
||||||
|
minStake,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,13 @@ import type { Context } from 'ponder:registry';
|
||||||
/**
|
/**
|
||||||
* Validates that the deployed Kraiken contract version is compatible
|
* Validates that the deployed Kraiken contract version is compatible
|
||||||
* with this indexer's kraiken-lib version.
|
* with this indexer's kraiken-lib version.
|
||||||
*
|
*
|
||||||
* MUST be called at Ponder startup before processing any events.
|
* MUST be called at Ponder startup before processing any events.
|
||||||
* Fails hard (process.exit) on mismatch to prevent indexing wrong data.
|
* Fails hard (process.exit) on mismatch to prevent indexing wrong data.
|
||||||
*/
|
*/
|
||||||
export async function validateContractVersion(context: Context): Promise<void> {
|
export async function validateContractVersion(context: Context): Promise<void> {
|
||||||
|
const logger = context.logger || console;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const contractVersion = await context.client.readContract({
|
const contractVersion = await context.client.readContract({
|
||||||
address: context.contracts.Kraiken.address,
|
address: context.contracts.Kraiken.address,
|
||||||
|
|
@ -19,14 +21,14 @@ export async function validateContractVersion(context: Context): Promise<void> {
|
||||||
const versionNumber = Number(contractVersion);
|
const versionNumber = Number(contractVersion);
|
||||||
|
|
||||||
if (!isCompatibleVersion(versionNumber)) {
|
if (!isCompatibleVersion(versionNumber)) {
|
||||||
console.error(getVersionMismatchError(versionNumber, 'ponder'));
|
logger.error(getVersionMismatchError(versionNumber, 'ponder'));
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`✓ Contract version validated: v${versionNumber} (kraiken-lib v${KRAIKEN_LIB_VERSION})`);
|
logger.info(`✓ Contract version validated: v${versionNumber} (kraiken-lib v${KRAIKEN_LIB_VERSION})`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to read contract VERSION:', error);
|
logger.error('Failed to read contract VERSION:', error);
|
||||||
console.error('Ensure Kraiken contract has VERSION constant and is deployed correctly');
|
logger.error('Ensure Kraiken contract has VERSION constant and is deployed correctly');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import {
|
||||||
updateHourlyData,
|
updateHourlyData,
|
||||||
checkBlockHistorySufficient,
|
checkBlockHistorySufficient,
|
||||||
RING_BUFFER_SEGMENTS,
|
RING_BUFFER_SEGMENTS,
|
||||||
|
refreshMinStake,
|
||||||
} from './helpers/stats';
|
} from './helpers/stats';
|
||||||
import { validateContractVersion } from './helpers/version';
|
import { validateContractVersion } from './helpers/version';
|
||||||
|
|
||||||
|
|
@ -80,7 +81,9 @@ ponder.on('Kraiken:Transfer', async ({ event, context }) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
ponder.on('StatsBlock:block', async ({ event, context }) => {
|
ponder.on('StatsBlock:block', async ({ event, context }) => {
|
||||||
await ensureStatsExists(context, event.block.timestamp);
|
const statsData = await ensureStatsExists(context, event.block.timestamp);
|
||||||
|
|
||||||
|
await refreshMinStake(context, statsData);
|
||||||
|
|
||||||
// Only update hourly data if we have sufficient block history
|
// Only update hourly data if we have sufficient block history
|
||||||
if (checkBlockHistorySufficient(context, event)) {
|
if (checkBlockHistorySufficient(context, event)) {
|
||||||
|
|
|
||||||
3017
web-app/package-lock.json
generated
3017
web-app/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -155,7 +155,6 @@ import { useClaim } from '@/composables/useClaim';
|
||||||
import { useAdjustTaxRate } from '@/composables/useAdjustTaxRates';
|
import { useAdjustTaxRate } from '@/composables/useAdjustTaxRates';
|
||||||
import { useSnatchSelection } from '@/composables/useSnatchSelection';
|
import { useSnatchSelection } from '@/composables/useSnatchSelection';
|
||||||
import { assetsToShares } from '@/contracts/stake';
|
import { assetsToShares } from '@/contracts/stake';
|
||||||
import { getMinStake } from '@/contracts/harb';
|
|
||||||
import { useWallet } from '@/composables/useWallet';
|
import { useWallet } from '@/composables/useWallet';
|
||||||
import { ref, onMounted, watch, computed, watchEffect, getCurrentInstance } from 'vue';
|
import { ref, onMounted, watch, computed, watchEffect, getCurrentInstance } from 'vue';
|
||||||
import { useStatCollection, loadStats } from '@/composables/useStatCollection';
|
import { useStatCollection, loadStats } from '@/composables/useStatCollection';
|
||||||
|
|
@ -187,7 +186,7 @@ const snatchLabelId = `stake-snatch-${uid}`;
|
||||||
const stakeSummaryId = `stake-summary-${uid}`;
|
const stakeSummaryId = `stake-summary-${uid}`;
|
||||||
const formStatusId = `stake-status-${uid}`;
|
const formStatusId = `stake-status-${uid}`;
|
||||||
|
|
||||||
const minStake = ref<bigint>(0n);
|
const minStake = computed(() => statCollection.minStake ?? 0n);
|
||||||
const stakeSlots = ref<string>('0.00');
|
const stakeSlots = ref<string>('0.00');
|
||||||
const supplyFreeze = ref<number>(0);
|
const supplyFreeze = ref<number>(0);
|
||||||
let debounceTimer: ReturnType<typeof setTimeout>;
|
let debounceTimer: ReturnType<typeof setTimeout>;
|
||||||
|
|
@ -203,7 +202,6 @@ watchEffect(() => {
|
||||||
stake.stakingAmountShares = await assetsToShares(stake.stakingAmount);
|
stake.stakingAmountShares = await assetsToShares(stake.stakingAmount);
|
||||||
const stakingAmountSharesNumber = bigInt2Number(stake.stakingAmountShares, 18);
|
const stakingAmountSharesNumber = bigInt2Number(stake.stakingAmountShares, 18);
|
||||||
const stakeableSupplyNumber = bigInt2Number(statCollection.stakeableSupply, 18);
|
const stakeableSupplyNumber = bigInt2Number(statCollection.stakeableSupply, 18);
|
||||||
minStake.value = await getMinStake();
|
|
||||||
|
|
||||||
if (stakeableSupplyNumber === 0) {
|
if (stakeableSupplyNumber === 0) {
|
||||||
supplyFreeze.value = 0;
|
supplyFreeze.value = 0;
|
||||||
|
|
@ -502,7 +500,6 @@ async function handleSubmit() {
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
minStake.value = await getMinStake();
|
|
||||||
stake.stakingAmountNumber = minStakeAmount.value;
|
stake.stakingAmountNumber = minStakeAmount.value;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -17,20 +17,15 @@ const { darkTheme } = useDark();
|
||||||
const wallet = useWallet();
|
const wallet = useWallet();
|
||||||
const initialChainId = wallet.account.chainId ?? DEFAULT_CHAIN_ID;
|
const initialChainId = wallet.account.chainId ?? DEFAULT_CHAIN_ID;
|
||||||
const { activePositions } = usePositions(initialChainId);
|
const { activePositions } = usePositions(initialChainId);
|
||||||
const ignoreOwner = ref(false);
|
const stake = useStake();
|
||||||
|
const statCollection = useStatCollection(initialChainId);
|
||||||
const taxRate = ref<number>(1.0);
|
const minStake = computed(() => statCollection.minStake ?? 0n);
|
||||||
|
|
||||||
const minStakeAmount = computed(() => {
|
const minStakeAmount = computed(() => {
|
||||||
return formatBigIntDivision(minStake.value, 10n ** 18n);
|
return formatBigIntDivision(minStake.value, 10n ** 18n);
|
||||||
});
|
});
|
||||||
|
const ignoreOwner = ref(false);
|
||||||
|
|
||||||
const stakeAbleHarbAmount = computed(() => statCollection.kraikenTotalSupply / 5n);
|
const taxRate = ref<number>(1.0);
|
||||||
|
|
||||||
const minStake = computed(() => stakeAbleHarbAmount.value / 600n);
|
|
||||||
|
|
||||||
const stake = useStake();
|
|
||||||
const statCollection = useStatCollection(initialChainId);
|
|
||||||
const snatchPositions = computed(() => {
|
const snatchPositions = computed(() => {
|
||||||
if (
|
if (
|
||||||
bigInt2Number(statCollection.outstandingStake, 18) + stake.stakingAmountNumber <=
|
bigInt2Number(statCollection.outstandingStake, 18) + stake.stakingAmountNumber <=
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { config } from '@/wagmi';
|
||||||
import logger from '@/utils/logger';
|
import logger from '@/utils/logger';
|
||||||
import type { WatchBlocksReturnType } from 'viem';
|
import type { WatchBlocksReturnType } from 'viem';
|
||||||
import { bigInt2Number } from '@/utils/helper';
|
import { bigInt2Number } from '@/utils/helper';
|
||||||
|
import { minStake as stakeMinStake } from '@/contracts/stake';
|
||||||
import { DEFAULT_CHAIN_ID } from '@/config';
|
import { DEFAULT_CHAIN_ID } from '@/config';
|
||||||
import { createRetryManager, formatGraphqlError, resolveGraphqlEndpoint } from '@/utils/graphqlRetry';
|
import { createRetryManager, formatGraphqlError, resolveGraphqlEndpoint } from '@/utils/graphqlRetry';
|
||||||
const demo = sessionStorage.getItem('demo') === 'true';
|
const demo = sessionStorage.getItem('demo') === 'true';
|
||||||
|
|
@ -17,6 +18,7 @@ interface StatsRecord {
|
||||||
burnedLastDay: string;
|
burnedLastDay: string;
|
||||||
burnedLastWeek: string;
|
burnedLastWeek: string;
|
||||||
id: string;
|
id: string;
|
||||||
|
minStake: string;
|
||||||
mintNextHourProjected: string;
|
mintNextHourProjected: string;
|
||||||
mintedLastDay: string;
|
mintedLastDay: string;
|
||||||
mintedLastWeek: string;
|
mintedLastWeek: string;
|
||||||
|
|
@ -44,6 +46,7 @@ export async function loadStatsCollection(chainId: number, endpointOverride?: st
|
||||||
{
|
{
|
||||||
query: `query StatsQuery {
|
query: `query StatsQuery {
|
||||||
stats(id: "0x01") {
|
stats(id: "0x01") {
|
||||||
|
minStake
|
||||||
burnNextHourProjected
|
burnNextHourProjected
|
||||||
burnedLastDay
|
burnedLastDay
|
||||||
burnedLastWeek
|
burnedLastWeek
|
||||||
|
|
@ -134,6 +137,14 @@ const stakeTotalSupply = computed(() => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const minStake = computed(() => {
|
||||||
|
if (rawStatsCollections.value?.length > 0) {
|
||||||
|
return BigInt(rawStatsCollections.value[0].minStake);
|
||||||
|
} else {
|
||||||
|
return 0n;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
//Total Supply Change / 7d=mintedLastWeek−burnedLastWeek
|
//Total Supply Change / 7d=mintedLastWeek−burnedLastWeek
|
||||||
const totalSupplyChange7d = computed(() => {
|
const totalSupplyChange7d = computed(() => {
|
||||||
if (rawStatsCollections.value?.length > 0) {
|
if (rawStatsCollections.value?.length > 0) {
|
||||||
|
|
@ -207,9 +218,11 @@ export async function loadStats(chainId?: number) {
|
||||||
statsError.value = null;
|
statsError.value = null;
|
||||||
retryManager.reset();
|
retryManager.reset();
|
||||||
retryManager.clear();
|
retryManager.clear();
|
||||||
|
stakeMinStake.value = minStake.value;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
rawStatsCollections.value = [];
|
rawStatsCollections.value = [];
|
||||||
statsError.value = formatGraphqlError(error);
|
statsError.value = formatGraphqlError(error);
|
||||||
|
stakeMinStake.value = 0n;
|
||||||
retryManager.schedule();
|
retryManager.schedule();
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
|
@ -269,5 +282,6 @@ export function useStatCollection(chainId: number = DEFAULT_CHAIN_ID) {
|
||||||
claimedSlots,
|
claimedSlots,
|
||||||
statsError,
|
statsError,
|
||||||
loading,
|
loading,
|
||||||
|
minStake,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,31 +61,6 @@ export async function getAllowance() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getMinStake() {
|
|
||||||
logger.contract('getMinStake');
|
|
||||||
|
|
||||||
const publicClient = getWalletPublicClient();
|
|
||||||
if (publicClient) {
|
|
||||||
const result = (await publicClient.readContract({
|
|
||||||
abi: HarbContract.abi,
|
|
||||||
address: HarbContract.contractAddress,
|
|
||||||
functionName: 'minStake',
|
|
||||||
args: [],
|
|
||||||
})) as bigint;
|
|
||||||
allowance.value = result;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result: bigint = (await readContract(config as Config, {
|
|
||||||
abi: HarbContract.abi,
|
|
||||||
address: HarbContract.contractAddress,
|
|
||||||
functionName: 'minStake',
|
|
||||||
args: [],
|
|
||||||
})) as bigint;
|
|
||||||
allowance.value = result;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getNonce() {
|
export async function getNonce() {
|
||||||
logger.contract('getNonce');
|
logger.contract('getNonce');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ interface Contract {
|
||||||
contractAddress: Address;
|
contractAddress: Address;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const minStake = ref();
|
export const minStake = ref<bigint>(0n);
|
||||||
export const totalSupply = ref(0n);
|
export const totalSupply = ref(0n);
|
||||||
export const outstandingSupply = ref(0n);
|
export const outstandingSupply = ref(0n);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue