Skip to content

Commit a5de5bb

Browse files
authored
Merge pull request #2081 from valory-xyz/release/v1.8.6
Release/v1.8.6
2 parents c46741c + f02dec4 commit a5de5bb

23 files changed

Lines changed: 750 additions & 397 deletions

File tree

.github/workflows/snyk-security.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,12 @@ jobs:
5656
# Runs Snyk Code (SAST) analysis and uploads result into GitHub.
5757
# Use || true to not fail the pipeline
5858
- name: Snyk Code test
59-
run: snyk code test --sarif > snyk-code.sarif
59+
run: snyk code test --remote-repo-url=https://github.com/${{ github.repository }} --sarif > snyk-code.sarif
6060
continue-on-error: true
6161

6262
# Runs Snyk Open Source (SCA) analysis and uploads result to Snyk.
6363
- name: Snyk Open Source monitor
64-
run: snyk monitor --all-projects
64+
run: snyk monitor --remote-repo-url=https://github.com/${{ github.repository }} --all-projects
6565

6666
# Disabled because this repo does not use IaC or Docker yet
6767

.supply-chain/install-hooks.allowlist

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,3 @@ classic-level # node-gyp-build for the LevelDB C++ binding (hardhat>@nomicfound
1515
electron # postinstall downloads the Electron binary tarball for the current platform (direct devDep — required for `yarn dev` and electron-builder)
1616
keccak # node-gyp-build for the Keccak hash native binding (ethereum-cryptography>keccak + hardhat>keccak — used by ethers v5 for blockchain hashing; install falls back to JS via `|| exit 0` if compile fails)
1717
secp256k1 # node-gyp-build for the secp256k1 ECC signing binding (ethereum-cryptography>secp256k1 — used by ethers for transaction signing; install falls back to JS via `|| exit 0` if compile fails)
18-
sharp # libvips-backed image optimizer that Next.js 15 ships as a direct dep for next/image; install/check.js looks for a prebuilt binary for the current platform/libc and falls back to building from source if missing — no network beyond the prebuild fetch

docs/features/autorun.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,9 @@ All waits are guarded by `enabledRef.current`, `sleepAwareDelay()`, and hard tim
182182
| `started` | Agent deployed and running | Done |
183183
| `agent_blocked` | Deterministic blocker (low balance, evicted, etc.) | Skip with notification, advance queue |
184184
| `infra_failed` | Transient failure (RPC/network/timeout) | Mark `hasInfraFailed`, continue scan to try remaining candidates; schedule short rescan if all fail |
185-
| `aborted` | Auto-run disabled or sleep detected | Stop processing |
185+
| `aborted` | Auto-run disabled | Stop processing |
186+
187+
`aborted` is returned only when auto-run is actually disabled (`enabledRef.current` is false). Transient gates that fail while auto-run is still enabled — a balance-wait timeout, an interrupted retry backoff — return `infra_failed`, not `aborted`, so a single stalled agent does not pause the whole rotation.
186188

187189
The `infra_failed` handling prevents the scanner from permanently rotating the selected agent on a transient failure. Remaining candidates in the current scan cycle are still tried (they may have different staking contracts and sufficient balance), but the selected agent is not replaced as the preferred choice.
188190

docs/features/staking-and-rewards.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ The user picks which staking program to use. The view cycles through these state
7171

7272
- Default program = `selectedAgentConfig.defaultStakingProgramId` (from `frontend/config/agents.ts`)
7373
- "Change Configuration" button toggles to list view (`LIST_MANUAL`)
74-
- List is ordered by `useStakingContracts`: active program first, deprecated programs hidden, filtered by `agentsSupported`
74+
- List is ordered by `useStakingContracts`: current program first (shown even if deprecated), other deprecated programs hidden, filtered by `agentsSupported`. `currentStakingProgramId` here is the on-chain (subgraph) program falling back to the service-stored `staking_program_id` — it never falls back to the agent-config default, so the "Selected" badge can't land on a contract the user never joined (OPE-1841)
7575
- On selection, `SelectStakingButton` writes the chosen `staking_program_id` to the service config before first deploy
7676

7777
### Main page — Staking block
@@ -104,6 +104,8 @@ Two tabs:
104104
| Epoch countdown | `currentEpochLifetime` from `useStakingDetails` |
105105
| Stats card | Total rewards earned, current streak |
106106

107+
Contract name and stats are both keyed off `useStakingContracts().currentStakingProgramId` (single source — active/subgraph, then service-stored, never the config default), so the header can't name one contract while the stats show another (OPE-1841).
108+
107109
"Switch Staking Contract" button → opens SelectStakingPage in migrate mode (see Staking Migration below).
108110

109111
**Rewards History tab** (`RewardsHistory.tsx`)
@@ -275,11 +277,13 @@ Derives several status flags from `selectedStakingContractDetails`:
275277

276278
### Program ordering (useStakingContracts)
277279

280+
`currentStakingProgramId` = subgraph-derived `activeStakingProgramId`, falling back to the service-stored `user_params.staking_program_id`, else `null`. It deliberately does **not** fall back to `defaultStakingProgramId` — the default is not evidence of a stake, and surfacing it as "current" marked contracts the user never joined as "Selected" (OPE-1841).
281+
278282
`orderedStakingProgramIds` is built by filtering and sorting available programs:
279283

280-
1. Skip deprecated programs
281-
2. Skip programs that don't support the selected agent type
282-
3. Place the active staking program first
284+
1. Place the current staking program first — even if deprecated, so a user staked in a legacy contract can still see it marked as selected
285+
2. Skip remaining deprecated programs
286+
3. Skip programs that don't support the selected agent type
283287
4. Append remaining programs in original order
284288

285289
Returns empty array while `isActiveStakingProgramLoaded` is false.

frontend/.supply-chain/install-hooks.allowlist

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,4 @@
1111
# "package # ?" is a TODO, not justification.
1212

1313
@parcel/watcher # native fs-watcher prebuild fallback (sass>chokidar transitive); compiles a small N-API binding from source if no prebuilt is shipped for the platform
14-
sharp # libvips-backed image optimizer that Next.js 15 ships as a direct dep for next/image; install/check.js looks for a prebuilt binary for the current platform/libc and falls back to building from source if missing — no network beyond the prebuild fetch
1514
unrs-resolver # napi-postinstall stub that prints a platform-mismatch warning if the prebuilt binary's triple doesn't match the host (eslint-config-next>eslint-import-resolver-typescript>unrs-resolver — runs once at install, no network or file mutation beyond stdout)

frontend/components/AgentStaking/StakingContractDetails.tsx

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ import { TbLock, TbSparkles, TbSquareRoundedPercentage } from 'react-icons/tb';
44
import styled from 'styled-components';
55

66
import { CardFlex, Divider, InfoTooltip, Tooltip } from '@/components/ui';
7+
import { STAKING_PROGRAMS } from '@/config/stakingPrograms';
78
import { COLOR, NA, PAGES } from '@/constants';
89
import {
910
usePageState,
11+
useServices,
1012
useStakingContractDetails,
13+
useStakingContracts,
1114
useStakingDetails,
12-
useStakingProgram,
1315
} from '@/hooks';
1416
import { secondsToHours } from '@/utils';
1517

@@ -65,14 +67,15 @@ const ContractDetailsSection = ({
6567

6668
export const StakingContractDetails = () => {
6769
const { goto } = usePageState();
68-
const {
69-
activeStakingProgramId,
70-
defaultStakingProgramId,
71-
isActiveStakingProgramLoaded,
72-
selectedStakingProgramMeta,
73-
} = useStakingProgram();
74-
const currentStakingProgramId = isActiveStakingProgramLoaded
75-
? activeStakingProgramId || defaultStakingProgramId
70+
const { selectedAgentConfig } = useServices();
71+
// Single source for the contract shown here — name and stats must come from
72+
// the same program id, and it must never be the agent-config default
73+
// presented as if the user were staked in it (OPE-1841).
74+
const { currentStakingProgramId } = useStakingContracts();
75+
const currentStakingProgramMeta = currentStakingProgramId
76+
? STAKING_PROGRAMS[selectedAgentConfig.evmHomeChainId]?.[
77+
currentStakingProgramId
78+
]
7679
: null;
7780
const { stakingContractInfo } = useStakingContractDetails(
7881
currentStakingProgramId,
@@ -86,7 +89,7 @@ export const StakingContractDetails = () => {
8689
} = stakingContractInfo || {};
8790
const { currentEpochLifetime } = useStakingDetails();
8891

89-
if (!stakingContractInfo || !selectedStakingProgramMeta)
92+
if (!stakingContractInfo || !currentStakingProgramMeta)
9093
return (
9194
<Flex justify="center" align="center" className="mt-32 mb-32">
9295
<Text>Staking contract details are not available.</Text>
@@ -97,7 +100,7 @@ export const StakingContractDetails = () => {
97100
<Flex justify="space-between" align="center">
98101
<Flex align="center" gap={8}>
99102
<Title level={5} className="m-0">
100-
{selectedStakingProgramMeta?.name}
103+
{currentStakingProgramMeta?.name}
101104
</Title>
102105
<InfoTooltip iconColor={COLOR.BLACK} iconSize={18}>
103106
This is the staking contract your agent is currently joined to. The

frontend/components/MainPage/Home/Overview/AgentInfo/AgentDisabledAlert/index.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ReactNode, useMemo } from 'react';
22

33
import { AgentLowBalanceAlert } from '@/components/AgentLowBalanceAlert';
44
import { STEPS } from '@/components/AgentWallet/types';
5+
import { NoStakingRewardsAlert } from '@/components/NoStakingRewardsAlert';
56
import {
67
AgentSetupCompleteModal,
78
ContentTransition,
@@ -42,6 +43,7 @@ export const AgentDisabledAlert = () => {
4243
isEligibleForStaking,
4344
hasEnoughServiceSlots,
4445
isServiceStaked,
46+
selectedStakingContractDetails,
4547
} = useActiveStakingContractDetails();
4648
const { isInitialFunded } = useIsInitiallyFunded();
4749
const { isAnotherAgentRunning } = useAgentRunning();
@@ -122,11 +124,24 @@ export const AgentDisabledAlert = () => {
122124
return { key: 'evicted', content: <EvictedAlert /> };
123125
}
124126

127+
// Non-blocking: the reward pool of the selected contract is empty, so the
128+
// agent runs but earns nothing until it's refilled (OPE-1846). Rendered
129+
// alongside the low-balance alerts (which self-hide) so neither is masked.
130+
const hasNoStakingRewards =
131+
!isSelectedStakingContractDetailsLoading &&
132+
selectedStakingContractDetails?.availableRewards === 0;
133+
125134
// NOTE: Low-balance alerts, each component controls its own visibility.
126135
return {
127136
key: 'low-balance',
128137
content: (
129138
<>
139+
{hasNoStakingRewards && (
140+
<NoStakingRewardsAlert
141+
className="mt-16"
142+
onSwitch={() => goto(PAGES.SelectStaking)}
143+
/>
144+
)}
130145
<AgentLowBalanceAlert
131146
onFund={() =>
132147
goto(PAGES.AgentWallet, {
@@ -153,6 +168,7 @@ export const AgentDisabledAlert = () => {
153168
isSelectedStakingContractDetailsLoading,
154169
isServiceStaked,
155170
selectedAgentConfig,
171+
selectedStakingContractDetails,
156172
selectedStakingProgramMeta,
157173
]);
158174

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { Button, Typography } from 'antd';
2+
3+
import { Alert } from '@/components/ui';
4+
5+
const { Text } = Typography;
6+
7+
type NoStakingRewardsAlertProps = {
8+
/**
9+
* When provided, renders a "Switch Staking Contract" button that invokes it.
10+
* Omit on surfaces where the user is already choosing a contract.
11+
*/
12+
onSwitch?: () => void;
13+
className?: string;
14+
};
15+
16+
/**
17+
* Non-blocking warning shown when a staking contract's reward pool is empty
18+
* (`availableRewards === 0`). The agent can still run and be staked, but it
19+
* won't earn staking rewards until the contract is refilled (OPE-1846).
20+
*/
21+
export const NoStakingRewardsAlert = ({
22+
onSwitch,
23+
className,
24+
}: NoStakingRewardsAlertProps) => (
25+
<Alert
26+
showIcon
27+
type="warning"
28+
className={className}
29+
message={
30+
<>
31+
<Text className="text-sm font-weight-500">
32+
No staking rewards available
33+
</Text>
34+
<Text className={`text-sm flex mt-4 ${onSwitch ? 'mb-8' : ''}`}>
35+
This staking contract&apos;s reward pool is currently empty. Your
36+
agent can still run, but it won&apos;t earn staking rewards until the
37+
contract is refilled.
38+
</Text>
39+
{onSwitch && (
40+
<Button size="small" onClick={onSwitch}>
41+
Switch Staking Contract
42+
</Button>
43+
)}
44+
</>
45+
}
46+
/>
47+
);

frontend/components/SelectStakingPage/components/SelectActivityRewardsConfiguration.tsx

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,16 @@ import { Button as AntdButton, Flex, Typography } from 'antd';
33
import { ReactNode, useCallback, useEffect, useState } from 'react';
44
import styled from 'styled-components';
55

6+
import { NoStakingRewardsAlert } from '@/components/NoStakingRewardsAlert';
67
import { StakingContractCard } from '@/components/StakingContractCard';
78
import { MainContentContainer } from '@/components/ui';
89
import { PAGES, StakingProgramId } from '@/constants';
9-
import { usePageState, useStakingContracts, useStakingProgram } from '@/hooks';
10+
import {
11+
usePageState,
12+
useStakingContractDetails,
13+
useStakingContracts,
14+
useStakingProgram,
15+
} from '@/hooks';
1016
import { Nullable } from '@/types';
1117

1218
import { MigrateButtonText, useCanMigrate } from '../hooks/useCanMigrate';
@@ -84,6 +90,30 @@ const SwitchStakingButton = ({
8490
);
8591
};
8692

93+
/**
94+
* Non-blocking warning surfaced on a contract card whose reward pool is empty.
95+
* The contract stays selectable (`useCanMigrate` is unchanged) — this only
96+
* tells the user the agent won't earn rewards there until it's refilled.
97+
*/
98+
export const StakingRewardsWarning = ({
99+
stakingProgramId,
100+
}: {
101+
stakingProgramId: StakingProgramId;
102+
}) => {
103+
const { stakingContractInfo, isRewardsAvailable } =
104+
useStakingContractDetails(stakingProgramId);
105+
106+
// Only warn once details have loaded — `isRewardsAvailable` is false while
107+
// undefined, which would otherwise flash the warning during loading.
108+
if (!stakingContractInfo || isRewardsAvailable) return null;
109+
110+
return (
111+
<Flex className="px-24 mt-16">
112+
<NoStakingRewardsAlert className="w-full" />
113+
</Flex>
114+
);
115+
};
116+
87117
type SelectActivityRewardsConfigurationProps = {
88118
mode: SelectMode;
89119
backButton?: ReactNode;
@@ -145,6 +175,7 @@ export const SelectActivityRewardsConfiguration = ({
145175
stakingProgramId={stakingProgramId}
146176
renderAction={() => (
147177
<>
178+
<StakingRewardsWarning stakingProgramId={stakingProgramId} />
148179
{mode === 'onboard' && (
149180
<Flex className="px-24 pb-24 mt-40" gap={16}>
150181
<SelectStakingButton

frontend/context/AutoRunProvider/hooks/useAutoRunStartOperations.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,16 @@ export const useAutoRunStartOperations = ({
8080
// Lightweight global balance gate before the expensive start call.
8181
const balancesReady = await waitForBalancesReady();
8282
if (!balancesReady) {
83-
return { status: AUTO_RUN_START_STATUS.ABORTED };
83+
// Balances can be unready because auto-run was disabled, or just because
84+
// this agent's balances timed out. Only stop the rotation when disabled;
85+
// otherwise report a transient failure so the scanner tries other agents.
86+
if (!enabledRef.current) {
87+
return { status: AUTO_RUN_START_STATUS.ABORTED };
88+
}
89+
return {
90+
status: AUTO_RUN_START_STATUS.INFRA_FAILED,
91+
reason: 'balances not ready',
92+
};
8493
}
8594

8695
onAutoRunStartStateChange?.(true);

0 commit comments

Comments
 (0)