Skip to content

Commit

Permalink
feat: properly handle lock period for delegations
Browse files Browse the repository at this point in the history
  • Loading branch information
belopash committed Oct 24, 2024
1 parent 84badd3 commit 200e0b0
Show file tree
Hide file tree
Showing 10 changed files with 298 additions and 168 deletions.
21 changes: 21 additions & 0 deletions src/api/contracts/subsquid.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,19 @@ export const softCapAbi = [
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

export const stakingAbi = [
{
type: 'function',
inputs: [
{ name: 'staker', internalType: 'address', type: 'address' },
{ name: 'worker', internalType: 'uint256', type: 'uint256' },
],
name: 'getDeposit',
outputs: [
{ name: 'depositAmount', internalType: 'uint256', type: 'uint256' },
{ name: 'withdrawAllowed', internalType: 'uint256', type: 'uint256' },
],
stateMutability: 'view',
},
{
type: 'function',
inputs: [
Expand Down Expand Up @@ -1144,6 +1157,14 @@ export const useReadStaking = /*#__PURE__*/ createUseReadContract({
abi: stakingAbi,
})

/**
* Wraps __{@link useReadContract}__ with `abi` set to __{@link stakingAbi}__ and `functionName` set to `"getDeposit"`
*/
export const useReadStakingGetDeposit = /*#__PURE__*/ createUseReadContract({
abi: stakingAbi,
functionName: 'getDeposit',
})

/**
* Wraps __{@link useReadContract}__ with `abi` set to __{@link stakingAbi}__ and `functionName` set to `"claimable"`
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { useMemo } from 'react';

import { getBlockTime } from '@lib/network';
import { Simplify } from 'type-fest';
import { useBlock, useReadContracts } from 'wagmi';

import { Worker, WorkerStatus } from '@api/subsquid-network-squid';
import { useContracts } from '@network/useContracts';

import {
stakingAbi,
useReadRouterStaking,
useReadRouterWorkerRegistration,
useReadWorkerRegistryLockPeriod,
workerRegistryAbi,
Expand Down Expand Up @@ -83,7 +86,7 @@ export function useFixWorkers<T extends Pick<Worker, 'id' | 'status'>>({
locked: true,
unlockedAt: new Date(
timestamp + getBlockTime(unlockBlock - lastL1Block.number),
).toString(),
).toISOString(),
}
: { locked: false };

Expand All @@ -106,3 +109,68 @@ export function useFixWorkers<T extends Pick<Worker, 'id' | 'status'>>({
data,
};
}

export function useFixDelegations<
T extends { id: string; delegations: { owner: { id: string } }[] },
>({ workers }: { workers?: T[] }) {
const { ROUTER, CHAIN_ID_L1 } = useContracts();

const { data: stakingAddress, isLoading: isStakingAddressLoading } = useReadRouterStaking({
address: ROUTER,
query: { enabled: !!ROUTER },
});

const { data: lastL1Block, isLoading: isLastL1BlockLoading } = useBlock({
chainId: CHAIN_ID_L1,
includeTransactions: false,
});

const { data: delegationsInfo, isLoading: isDelegationsInfoLoading } = useReadContracts({
contracts: workers?.flatMap(worker =>
worker.delegations.map(
delegation =>
({
abi: stakingAbi,
address: stakingAddress || '0x',
functionName: 'getDeposit',
args: [delegation.owner.id as `0x${string}`, worker.id],
}) as const,
),
),
allowFailure: false,
query: { enabled: !!workers && !!stakingAddress },
});

type R = Simplify<
Omit<T, 'delegations'> & {
delegations: Simplify<T['delegations'][number] & { unlockedAt?: string }>[];
}
>;

const data = useMemo(() => {
if (!delegationsInfo || !lastL1Block || !workers) return workers;

let index = 0;
return workers.map(
worker =>
({
...worker,
delegations: worker.delegations.map(delegation => {
const [, unlockBlock] = delegationsInfo[index++];
const timestamp = Number(lastL1Block.timestamp) * 1000;
const locked = lastL1Block.number < unlockBlock;
const unlockedAt = locked
? new Date(timestamp + getBlockTime(unlockBlock - lastL1Block.number)).toISOString()
: undefined;

return { ...delegation, locked, unlockedAt };
}),
}) as R,
);
}, [delegationsInfo, lastL1Block, workers]);

return {
data,
isLoading: isDelegationsInfoLoading || isLastL1BlockLoading || isStakingAddressLoading,
};
}
34 changes: 21 additions & 13 deletions src/api/subsquid-network-squid/workers-graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { PartialDeep, SimplifyDeep } from 'type-fest';
import { useAccount } from '@network/useAccount.ts';

import { useSquid } from './datasource';
import { useFixDelegations, useFixWorkers } from './fixes';
import {
AccountType,
Delegation,
Expand All @@ -21,7 +22,6 @@ import {
Worker,
} from './graphql';
import { useNetworkSettings } from './settings-graphql';
import { useFixWorkers } from './useFixWorkers';

// inherit API interface for internal class
// export interface BlockchainApiWorker extends Omit<WorkerFragmentFragment, 'createdAt'> {
Expand Down Expand Up @@ -381,10 +381,14 @@ export function useMyDelegations({ sortBy, sortDir }: { sortBy: WorkerSortBy; so
{ address: address || '0x' },
);

const { data: fixedDelegations, isLoading: isFixedDelegationsLoading } = useFixWorkers({
const { data: fixedWorkers, isLoading: isFixedWorkersLoading } = useFixWorkers({
workers: delegationsQuery?.workers,
});

const { data: fixedDelegations, isLoading: isFixedDelegationsLoading } = useFixDelegations({
workers: fixedWorkers,
});

const data = useMemo(() => {
type W = SimplifyDeep<
Pick<
Expand All @@ -402,6 +406,7 @@ export function useMyDelegations({ sortBy, sortDir }: { sortBy: WorkerSortBy; so
Pick<WorkerExtended, 'delegationCapacity' | 'myDelegation' | 'myTotalDelegationReward'> & {
delegations: (Pick<Delegation, 'deposit' | 'locked'> & {
owner: { id: string; type: AccountType };
unlockedAt?: string;
})[];
}
>;
Expand Down Expand Up @@ -434,12 +439,7 @@ export function useMyDelegations({ sortBy, sortDir }: { sortBy: WorkerSortBy; so
.toFixed();

worker.delegations.push({
deposit: d.deposit,
locked: d.locked,
owner: {
id: d.owner.id,
type: d.owner.type,
},
...d,
});
});

Expand All @@ -450,7 +450,11 @@ export function useMyDelegations({ sortBy, sortDir }: { sortBy: WorkerSortBy; so
}, [fixedDelegations, sortBy, sortDir]);

return {
isLoading: isSettingsLoading || isDelegationsQueryLoading || isFixedDelegationsLoading,
isLoading:
isSettingsLoading ||
isDelegationsQueryLoading ||
isFixedWorkersLoading ||
isFixedDelegationsLoading,
data,
};
}
Expand Down Expand Up @@ -536,22 +540,26 @@ export function useMyWorkerDelegations({
}) {
const { address } = useAccount();
const datasource = useSquid();
const { data, isLoading } = useMyDelegationsQuery(
const { data: delegations, isLoading: isDelegationsLoading } = useMyDelegationsQuery(
datasource,
{
workerId: peerId || '',
address: address || '',
},
{
select: res => {
return res.workers[0]?.delegations || [];
return res.workers;
},
enabled,
},
);

const { data: fixedDelegations, isLoading: isFixedDelegationsLoading } = useFixDelegations({
workers: delegations,
});

return {
isLoading: isLoading,
data,
isLoading: isDelegationsLoading || isFixedDelegationsLoading,
data: fixedDelegations?.[0]?.delegations,
};
}
1 change: 1 addition & 0 deletions src/pages/DelegationsPage/DelegationsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ export function MyDelegations() {
type: d.owner.type,
balance: d.deposit,
locked: d.locked || false,
unlockedAt: d.unlockedAt || '',
}))}
disabled={!worker.delegations.some(d => !d.locked)}
/>
Expand Down
9 changes: 8 additions & 1 deletion src/pages/WorkersPage/Worker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ export const Worker = ({ backPath }: { backPath: string }) => {
type: d.owner.type,
balance: d.deposit,
locked: d.locked || false,
// FIXME: some issue with types
unlockedAt: (d as any).unlockedAt,
}))}
disabled={isLoading || !delegations?.some(d => !d.locked)}
/>
Expand Down Expand Up @@ -272,7 +274,12 @@ export const Worker = ({ backPath }: { backPath: string }) => {
worker.status === WorkerStatus.Deregistering ? (
<WorkerWithdrawButton
worker={worker}
source={worker.owner}
source={{
...worker.owner,
// FIXME: some types issue
locked: (worker as any).locked,
unlockedAt: (worker as any).unlockedAt,
}}
disabled={worker.status !== WorkerStatus.Deregistered}
/>
) : (
Expand Down
Loading

0 comments on commit 200e0b0

Please sign in to comment.