- {label}
+
+ {label}
{children}
)
diff --git a/apps/lido/app/src/components/shared/ListItemAddress.js b/apps/lido/app/src/components/shared/ListItemAddress.js
new file mode 100644
index 000000000..9169852e7
--- /dev/null
+++ b/apps/lido/app/src/components/shared/ListItemAddress.js
@@ -0,0 +1,13 @@
+import { IdentityBadge } from '@aragon/ui'
+import React from 'react'
+import { ListItem, LoadableElement } from '../shared'
+
+export const ListItemAddress = ({ label, value }) => {
+ return (
+
+
+
+
+
+ )
+}
diff --git a/apps/lido/app/src/components/shared/ListItemBasisPoints.js b/apps/lido/app/src/components/shared/ListItemBasisPoints.js
new file mode 100644
index 000000000..e33e4aea3
--- /dev/null
+++ b/apps/lido/app/src/components/shared/ListItemBasisPoints.js
@@ -0,0 +1,14 @@
+import React from 'react'
+import { BasisPoints } from './BasisPoints'
+import { ListItem } from './ListItem'
+import { LoadableElement } from './LoadableElement'
+
+export const ListItemBasisPoints = ({ label, value, ...rest }) => {
+ return (
+
+
+
+
+
+ )
+}
diff --git a/apps/lido/app/src/components/shared/ListItemBoolean.js b/apps/lido/app/src/components/shared/ListItemBoolean.js
new file mode 100644
index 000000000..be4803efe
--- /dev/null
+++ b/apps/lido/app/src/components/shared/ListItemBoolean.js
@@ -0,0 +1,11 @@
+import React from 'react'
+import { ListItem } from './ListItem'
+import { LoadableElement } from './LoadableElement'
+
+export const ListItemBoolean = ({ label, value, renderElements = ["Yes", "No"] }) => {
+ return (
+
+ {value ? renderElements[0] : renderElements[1]}
+
+ )
+}
diff --git a/apps/lido/app/src/components/shared/ListItemEther.js b/apps/lido/app/src/components/shared/ListItemEther.js
new file mode 100644
index 000000000..5d4108188
--- /dev/null
+++ b/apps/lido/app/src/components/shared/ListItemEther.js
@@ -0,0 +1,14 @@
+import React from 'react'
+import { ListItem } from './ListItem'
+import { Ether } from './Ether'
+import { LoadableElement } from './LoadableElement'
+
+export const ListItemEther = ({ label, value, symbol, symbolAfter }) => {
+ return (
+
+
+
+
+
+ )
+}
diff --git a/apps/lido/app/src/components/shared/ListItemUnformattedValue.js b/apps/lido/app/src/components/shared/ListItemUnformattedValue.js
new file mode 100644
index 000000000..9afaa736b
--- /dev/null
+++ b/apps/lido/app/src/components/shared/ListItemUnformattedValue.js
@@ -0,0 +1,11 @@
+import React from 'react'
+import { ListItem } from './ListItem'
+import { LoadableElement } from './LoadableElement'
+
+export const ListItemUnformattedValue = ({ label, value }) => {
+ return (
+
+ {value}
+
+ )
+}
diff --git a/apps/lido/app/src/components/shared/index.js b/apps/lido/app/src/components/shared/index.js
index ab21f1c2f..896d3b784 100644
--- a/apps/lido/app/src/components/shared/index.js
+++ b/apps/lido/app/src/components/shared/index.js
@@ -7,3 +7,9 @@ export { LoadableElement } from './LoadableElement'
export { Tooltip } from './Tooltip'
export { RestorationRate } from './RestorationRate'
export { Split } from './Split'
+export { ListItemEther } from './ListItemEther'
+export { ListItemUnformattedValue } from './ListItemUnformattedValue'
+export { ListItemBoolean } from './ListItemBoolean'
+export { ListItemAddress } from './ListItemAddress'
+export { ListItemBasisPoints } from './ListItemBasisPoints'
+export { Ether } from './Ether'
diff --git a/apps/lido/app/src/components/shared/styles.js b/apps/lido/app/src/components/shared/styles.js
index c94e07e57..54e4e9101 100644
--- a/apps/lido/app/src/components/shared/styles.js
+++ b/apps/lido/app/src/components/shared/styles.js
@@ -1,4 +1,4 @@
-import { GU, Info } from '@aragon/ui'
+import { Box, GU, Info } from '@aragon/ui'
import styled from 'styled-components'
export const InfoSpaced = styled(Info)`
@@ -18,3 +18,9 @@ export const Controls = styled.div`
margin-right: ${GU * 2}px;
}
`
+
+export const BoxUnpadded = styled(Box)`
+ & > div {
+ padding: 0;
+ }
+`
diff --git a/apps/lido/app/src/components/state/BufferedEther.js b/apps/lido/app/src/components/state/BufferedEther.js
deleted file mode 100644
index 0ca60fd3f..000000000
--- a/apps/lido/app/src/components/state/BufferedEther.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { useAppState } from '@aragon/api-react'
-import React from 'react'
-import { ListItem } from '../shared'
-import { Ether } from '../shared/Ether'
-
-export const BufferedEther = () => {
- const { bufferedEther } = useAppState()
-
- return (
-
-
-
- )
-}
diff --git a/apps/lido/app/src/components/state/DepositContract.js b/apps/lido/app/src/components/state/DepositContract.js
deleted file mode 100644
index 0e5056d78..000000000
--- a/apps/lido/app/src/components/state/DepositContract.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { useAppState } from '@aragon/api-react'
-import { IdentityBadge } from '@aragon/ui'
-import React from 'react'
-import { ListItem, LoadableElement } from '../shared'
-
-export const DepositContract = () => {
- const { depositContract } = useAppState()
-
- return (
-
-
-
-
-
- )
-}
diff --git a/apps/lido/app/src/components/state/ElRewardsVault.js b/apps/lido/app/src/components/state/ElRewardsVault.js
deleted file mode 100644
index fbfaf3b04..000000000
--- a/apps/lido/app/src/components/state/ElRewardsVault.js
+++ /dev/null
@@ -1,98 +0,0 @@
-import { useAppState, useAragonApi } from '@aragon/api-react'
-import { Button, IconEdit, IdentityBadge, SidePanel } from '@aragon/ui'
-import { Field, Form, Formik } from 'formik'
-import React, { useState } from 'react'
-import {
- Controls,
- IconButton,
- InfoSpaced,
- ListItem,
- LoadableElement,
- TextField,
-} from '../shared'
-import * as yup from 'yup'
-import { isAddress } from 'web3-utils'
-
-const fieldName = 'vault'
-
-const initialValues = {
- [fieldName]: '',
-}
-
-const validationSchema = yup.object().shape({
- vault: yup
- .string('Vault must be a string.')
- .test(fieldName, 'Vault must be a valid address.', (vault) => {
- return isAddress(vault)
- }),
-})
-
-export const ElRewardsVault = () => {
- const { api } = useAragonApi()
- const { elRewardsVault } = useAppState()
-
- const [sidePanelOpen, setSidePanelOpen] = useState(false)
- const openSidePanel = () => setSidePanelOpen(true)
- const closeSidePanel = () => setSidePanelOpen(false)
-
- const submit = ({ vault }) => {
- api
- .setELRewardsVault(vault)
- .toPromise()
- .catch(console.error)
- .finally(closeSidePanel)
- }
-
- return (
-
-
-
-
-
- } onClick={openSidePanel} />
-
-
-
- Set a new address for the execution layer rewards vault contract.
-
-
- {({ submitForm, isSubmitting, isValidating }) => {
- const handleSubmit = (event) => {
- event.preventDefault()
- submitForm()
- }
-
- return (
-
- )
- }}
-
-
-
- )
-}
diff --git a/apps/lido/app/src/components/state/ElRewardsWithdrawalLimit.js b/apps/lido/app/src/components/state/ElRewardsWithdrawalLimit.js
deleted file mode 100644
index 32fdd78b3..000000000
--- a/apps/lido/app/src/components/state/ElRewardsWithdrawalLimit.js
+++ /dev/null
@@ -1,100 +0,0 @@
-import { useAppState, useAragonApi } from '@aragon/api-react'
-import { Button, IconEdit, SidePanel } from '@aragon/ui'
-import { Field, Form, Formik } from 'formik'
-import React, { useState } from 'react'
-import * as yup from 'yup'
-
-import {
- Controls,
- IconButton,
- InfoSpaced,
- ListItem,
- TextField,
-} from '../shared'
-import { toBasisPoints } from '../../utils'
-import { BasisPoints } from '../shared/BasisPoints'
-
-export const ElRewardsWithdrawalLimit = () => {
- const { api } = useAragonApi()
- const { elRewardsWithdrawalLimit } = useAppState()
-
- const [sidePanelOpen, setSidePanelOpen] = useState(false)
- const openSidePanel = () => setSidePanelOpen(true)
- const closeSidePanel = () => setSidePanelOpen(false)
-
- const submit = ({ limit }) => {
- api
- .setELRewardsWithdrawalLimit(toBasisPoints(limit))
- .toPromise()
- .catch(console.error)
- .finally(closeSidePanel)
- }
-
- return (
-
-
-
- } onClick={openSidePanel} />
-
-
-
- Adjust the limit on withdrawable ether from execution layer rewards
- vault per oracle report.
-
- {
- const regex = /^\d{1,3}(\.\d{1,2})?$/
- return regex.test(value)
- }
- ),
- })}
- validateOnBlur={false}
- validateOnChange={false}
- onSubmit={submit}
- >
- {({ submitForm, isSubmitting, isValidating }) => {
- const handleSubmit = (event) => {
- event.preventDefault()
- submitForm()
- }
-
- return (
-
- )
- }}
-
-
-
- )
-}
diff --git a/apps/lido/app/src/components/state/Fee.js b/apps/lido/app/src/components/state/Fee.js
deleted file mode 100644
index 56d39abd7..000000000
--- a/apps/lido/app/src/components/state/Fee.js
+++ /dev/null
@@ -1,99 +0,0 @@
-import { useAppState, useAragonApi } from '@aragon/api-react'
-import { Button, IconEdit, SidePanel } from '@aragon/ui'
-import { Field, Form, Formik } from 'formik'
-import React, { useState } from 'react'
-import * as yup from 'yup'
-
-import {
- Controls,
- IconButton,
- InfoSpaced,
- ListItem,
- TextField,
-} from '../shared'
-import { toBasisPoints } from '../../utils'
-import { BasisPoints } from '../shared/BasisPoints'
-
-export const Fee = () => {
- const { api } = useAragonApi()
- const { fee } = useAppState()
-
- const [sidePanelOpen, setSidePanelOpen] = useState(false)
- const openSidePanel = () => setSidePanelOpen(true)
- const closeSidePanel = () => setSidePanelOpen(false)
-
- const submit = ({ fee }) => {
- api
- .setFee(toBasisPoints(fee))
- .toPromise()
- .catch(console.error)
- .finally(closeSidePanel)
- }
-
- return (
-
-
-
- } onClick={openSidePanel} />
-
-
-
- Adjust the fee applied on staking rewards.
-
- {
- const regex = /^\d{1,3}(\.\d{1,2})?$/
- return regex.test(value)
- }
- ),
- })}
- validateOnBlur={false}
- validateOnChange={false}
- onSubmit={submit}
- >
- {({ submitForm, isSubmitting, isValidating }) => {
- const handleSubmit = (event) => {
- event.preventDefault()
- submitForm()
- }
-
- return (
-
- )
- }}
-
-
-
- )
-}
diff --git a/apps/lido/app/src/components/state/FeeDistribution.js b/apps/lido/app/src/components/state/FeeDistribution.js
deleted file mode 100644
index f6cc8c8d3..000000000
--- a/apps/lido/app/src/components/state/FeeDistribution.js
+++ /dev/null
@@ -1,177 +0,0 @@
-import { useAppState, useAragonApi } from '@aragon/api-react'
-import { Button, IconEdit, IdentityBadge, SidePanel } from '@aragon/ui'
-import { Field, Form, Formik } from 'formik'
-import React, { useState } from 'react'
-import * as yup from 'yup'
-import BN from 'bn.js'
-import { IconButton, InfoSpaced, ListItem, TextField } from '../shared'
-import { capitalizeFirstLetter, sum, toBasisPoints } from '../../utils'
-import { BasisPoints } from '../shared/BasisPoints'
-
-const TREASURY = 'treasury'
-const INSURANCE = 'insurance'
-const OPERATORS = 'operators'
-
-const initialValues = {
- [TREASURY]: '',
- [INSURANCE]: '',
- [OPERATORS]: '',
-}
-
-const getFieldSchema = (fieldName) => {
- return yup
- .number(`${capitalizeFirstLetter(fieldName)} must be a number.`)
- .required(`${capitalizeFirstLetter(fieldName)} must be a valid number.`)
- .min(
- 0,
- `${capitalizeFirstLetter(fieldName)} must be greater than or equal zero.`
- )
- .max(
- 100,
- `${capitalizeFirstLetter(fieldName)} must be less than or equal 100. `
- )
- .test(
- fieldName,
- `${capitalizeFirstLetter(
- fieldName
- )} must a number with up to 2 optional decimal places.`,
- (value) => {
- const regex = /^\d{1,3}(\.\d{1,2})?$/
- return regex.test(value)
- }
- )
-}
-
-const validationSchema = yup
- .object()
- .shape({
- [TREASURY]: getFieldSchema(TREASURY),
- [INSURANCE]: getFieldSchema(INSURANCE),
- [OPERATORS]: getFieldSchema(OPERATORS),
- })
- .test({
- name: 'total',
- test: function ({ operators, insurance, treasury }) {
- const operatorsBps = toBasisPoints(operators)
- const insuranceBps = toBasisPoints(insurance)
- const treasuryBps = toBasisPoints(treasury)
-
- const total = sum(operatorsBps, insuranceBps, treasuryBps)
- const totalEquals10000 = new BN(total).eq(new BN(10000))
-
- if (totalEquals10000) return true
-
- return this.createError({
- path: 'total',
- message: 'All fields must add up to 100%',
- })
- },
- })
-
-export const FeeDistribution = () => {
- const { api } = useAragonApi()
- const { feeDistribution, treasury, operators, insuranceFund } = useAppState()
-
- const [sidePanelOpen, setSidePanelOpen] = useState(false)
- const openSidePanel = () => setSidePanelOpen(true)
- const closeSidePanel = () => setSidePanelOpen(false)
-
- const submit = ({ treasury, insurance, operators }) => {
- const insuranceBps = toBasisPoints(insurance)
- const treasuryBps = toBasisPoints(treasury)
- const operatorsBps = toBasisPoints(operators)
-
- api
- .setFeeDistribution(treasuryBps, insuranceBps, operatorsBps)
- .toPromise()
- .catch(console.error)
- .finally(closeSidePanel)
- }
-
- return (
- <>
-
- }
- />
-
- }
- nested
- >
-
-
- }
- nested
- >
-
-
- }
- nested
- >
-
-
-
-
- Reallocate the fee distribution between treasury, insurance and node
- operators. The fields must add up to 100%.
-
-
- {({ submitForm, isSubmitting, isValidating, errors }) => {
- const handleSubmit = (event) => {
- event.preventDefault()
- submitForm()
- }
-
- return (
-
- )
- }}
-
-
- >
- )
-}
diff --git a/apps/lido/app/src/components/state/NodeOperatorsRegistry.js b/apps/lido/app/src/components/state/NodeOperatorsRegistry.js
deleted file mode 100644
index ab1e73967..000000000
--- a/apps/lido/app/src/components/state/NodeOperatorsRegistry.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { useAppState } from '@aragon/api-react'
-import { IdentityBadge } from '@aragon/ui'
-import React from 'react'
-import { ListItem, LoadableElement } from '../shared'
-
-export const NodeOperatorsRegistry = () => {
- const { nodeOperatorsRegistry } = useAppState()
-
- return (
-
-
-
-
-
- )
-}
diff --git a/apps/lido/app/src/components/state/Oracle.js b/apps/lido/app/src/components/state/Oracle.js
deleted file mode 100644
index df1d604c3..000000000
--- a/apps/lido/app/src/components/state/Oracle.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { useAppState } from '@aragon/api-react'
-import { IdentityBadge } from '@aragon/ui'
-import React from 'react'
-import { ListItem, LoadableElement } from '../shared'
-
-export const Oracle = () => {
- const { oracle } = useAppState()
-
- return (
-
-
-
-
-
- )
-}
diff --git a/apps/lido/app/src/components/state/Status.js b/apps/lido/app/src/components/state/Status.js
deleted file mode 100644
index 298727552..000000000
--- a/apps/lido/app/src/components/state/Status.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import { useAppState, useAragonApi } from '@aragon/api-react'
-import { IconConnect, IconRemove } from '@aragon/ui'
-import React from 'react'
-import styled from 'styled-components'
-import { Controls, IconButton, ListItem, LoadableElement } from '../shared'
-
-const StoppedStyle = styled.span`
- color: ${(props) => props.theme.negative};
-`
-
-const LiveStyle = styled.span`
- color: ${(props) => props.theme.positive};
-`
-
-const ResumeIcon = styled(IconConnect)`
- color: ${(props) => props.theme.positive};
-`
-
-const StopIcon = styled(IconRemove)`
- color: ${(props) => props.theme.negative};
-`
-
-export const Status = () => {
- const { isStopped } = useAppState()
-
- const { api } = useAragonApi()
- const stop = () => {
- api.stop().toPromise()
- }
- const resume = () => {
- api.resume().toPromise()
- }
-
- return (
-
-
- {isStopped ? (
-
- Stopped
- } onClick={resume} />
-
- ) : (
-
- Live
- } onClick={stop} />
-
- )}
-
-
- )
-}
diff --git a/apps/lido/app/src/components/state/TotalPooledEther.js b/apps/lido/app/src/components/state/TotalPooledEther.js
deleted file mode 100644
index f61dfab17..000000000
--- a/apps/lido/app/src/components/state/TotalPooledEther.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { useAppState } from '@aragon/api-react'
-import React from 'react'
-import { ListItem } from '../shared'
-import { Ether } from '../shared/Ether'
-
-export const TotalPooledEther = () => {
- const { totalPooledEther } = useAppState()
-
- return (
-
-
-
- )
-}
diff --git a/apps/lido/app/src/components/state/WithdrawalCredentials.js b/apps/lido/app/src/components/state/WithdrawalCredentials.js
deleted file mode 100644
index 32c59ff13..000000000
--- a/apps/lido/app/src/components/state/WithdrawalCredentials.js
+++ /dev/null
@@ -1,104 +0,0 @@
-import { useAragonApi } from '@aragon/api-react'
-import { Button, IconEdit, SidePanel } from '@aragon/ui'
-import { Field, Form, Formik } from 'formik'
-import React, { useState } from 'react'
-import * as yup from 'yup'
-import {
- BytesBadge,
- Controls,
- IconButton,
- InfoSpaced,
- ListItem,
- LoadableElement,
- TextField,
-} from '../shared'
-
-const fieldName = 'credentials'
-
-const initialValues = {
- [fieldName]: '',
-}
-
-const validationSchema = yup.object().shape({
- credentials: yup
- .string('Credentials must be a string.')
- .test(
- fieldName,
- 'Credentials must be a 32-byte hexadecimal number with `0x` prefix.',
- (credentials) => {
- const regex = /^0x[a-fA-F0-9]{64}$/
- return regex.test(credentials)
- }
- ),
-})
-
-export const WithdrawalCredentials = () => {
- const { api, appState } = useAragonApi()
- const { withdrawalCredentials } = appState
-
- const [sidePanelOpen, setSidePanelOpen] = useState(false)
- const openSidePanel = () => setSidePanelOpen(true)
- const closeSidePanel = () => setSidePanelOpen(false)
-
- const submit = ({ credentials }) => {
- api
- .setWithdrawalCredentials(credentials)
- .toPromise()
- .catch(console.error)
- .finally(closeSidePanel)
- }
-
- return (
-
-
-
-
-
- } onClick={openSidePanel} />
-
-
-
- Set new withdrawal credentials. This action discards all unused
- signing keys as the signatures are invalidated.
-
-
- {({ submitForm, isSubmitting, isValidating }) => {
- const handleSubmit = (event) => {
- event.preventDefault()
- submitForm()
- }
-
- return (
-
- )
- }}
-
-
-
- )
-}
diff --git a/apps/lido/app/src/components/state/index.js b/apps/lido/app/src/components/state/index.js
deleted file mode 100644
index bbca4cb49..000000000
--- a/apps/lido/app/src/components/state/index.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import { Box } from '@aragon/ui'
-import React from 'react'
-import { BufferedEther } from './BufferedEther'
-import { DepositContract } from './DepositContract'
-import { ElRewardsVault } from './ElRewardsVault'
-import { ElRewardsWithdrawalLimit } from './ElRewardsWithdrawalLimit'
-import { Fee } from './Fee'
-import { FeeDistribution } from './FeeDistribution'
-import { NodeOperatorsRegistry } from './NodeOperatorsRegistry'
-import { Oracle } from './Oracle'
-import { Status } from './Status'
-import { TotalPooledEther } from './TotalPooledEther'
-import { WithdrawalCredentials } from './WithdrawalCredentials'
-
-export const State = () => {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
- )
-}
diff --git a/apps/lido/app/src/script.js b/apps/lido/app/src/script.js
index a118a58e3..5a7a259fe 100644
--- a/apps/lido/app/src/script.js
+++ b/apps/lido/app/src/script.js
@@ -1,9 +1,136 @@
import 'core-js/stable'
import 'regenerator-runtime/runtime'
import Aragon, { events } from '@aragon/api'
+import LocatorABI from "./LidoLocator.abi.json"
const app = new Aragon()
+const createFetcher = (functionName) => () => app.call(functionName).toPromise()
+
+const protocolVariables = [
+ {
+ stateKey: 'isStopped',
+ updateEvents: ['Resumed', 'Stopped'],
+ fetch: createFetcher('isStopped'),
+ },
+ {
+ stateKey: 'canDeposit',
+ updateEvents: [],
+ fetch: createFetcher('canDeposit'),
+ },
+ {
+ stateKey: 'bufferedEther',
+ updateEvents: ['Unbuffered'],
+ fetch: createFetcher('getBufferedEther'),
+ },
+ {
+ stateKey: 'depositableEther',
+ updateEvents: [],
+ fetch: createFetcher('getDepositableEther'),
+ },
+ {
+ stateKey: 'totalPooledEther',
+ updateEvents: [],
+ fetch: createFetcher('getTotalPooledEther'),
+ },
+ {
+ stateKey: 'totalELRewardsCollected',
+ updateEvents: ['ELRewardsReceived'],
+ fetch: createFetcher('getTotalELRewardsCollected'),
+ },
+ { stateKey: 'fee', updateEvents: [], fetch: createFetcher('getFee') },
+ {
+ stateKey: 'feeDistribution',
+ updateEvents: [],
+ fetch: createFetcher('getFeeDistribution'),
+ },
+ {
+ stateKey: 'withdrawalCredentials',
+ updateEvents: [],
+ fetch: createFetcher('getWithdrawalCredentials'),
+ },
+ {
+ stateKey: 'beaconStat',
+ updateEvents: ['CLValidatorsUpdated', 'DepositedValidatorsChanged'],
+ fetch: createFetcher('getBeaconStat'),
+ },
+ {
+ stateKey: 'treasury',
+ updateEvents: [],
+ fetch: createFetcher('getTreasury'),
+ },
+ {
+ stateKey: 'legacyOracle',
+ updateEvents: [],
+ fetch: createFetcher('getOracle'),
+ },
+ {
+ stateKey: 'recoveryVault',
+ updateEvents: [],
+ fetch: createFetcher('getRecoveryVault'),
+ },
+ {
+ stateKey: 'lidoLocator',
+ updateEvents: ['LidoLocatorSet'],
+ fetch: createFetcher('getLidoLocator'),
+ },
+ {
+ stateKey: 'symbol',
+ updateEvents: [],
+ fetch: createFetcher('symbol'),
+ },
+ {
+ stateKey: 'decimals',
+ updateEvents: [],
+ fetch: createFetcher('decimals'),
+ },
+ {
+ stateKey: 'totalSupply',
+ updateEvents: [],
+ fetch: createFetcher('totalSupply'),
+ },
+ {
+ stateKey: 'stakeLimitFullInfo',
+ updateEvents: [
+ 'StakingPaused',
+ 'StakingResumed',
+ 'StakingLimitSet',
+ 'StakingLimitRemoved',
+ ],
+ fetch: createFetcher('getStakeLimitFullInfo'),
+ },
+ {
+ stateKey: 'contractVersion',
+ updateEvents: ['ContractVersionSet'],
+ fetch: createFetcher('getContractVersion'),
+ },
+ {
+ stateKey: 'hasInitialized',
+ updateEvents: [],
+ fetch: createFetcher('hasInitialized'),
+ },
+ {
+ stateKey: 'initializationBlock',
+ updateEvents: [],
+ fetch: createFetcher('getInitializationBlock'),
+ },
+]
+
+const locatorVariables = [
+ "accountingOracle",
+ "burner",
+ "depositSecurityModule",
+ "elRewardsVault",
+ "lido",
+ "oracleDaemonConfig",
+ "oracleReportSanityChecker",
+ "postTokenRebaseReceiver",
+ "stakingRouter",
+ "validatorsExitBusOracle",
+ "withdrawalQueue",
+ "withdrawalVault"
+]
+
app.store(
async (state, { event }) => {
const nextState = {
@@ -11,47 +138,26 @@ app.store(
}
try {
- switch (event) {
- case 'Stopped':
- return { ...nextState, isStopped: await getIsStopped() }
- case 'Resumed':
- return { ...nextState, isStopped: await getIsStopped() }
- case 'FeeSet':
- return { ...nextState, fee: await getFee() }
- case 'FeeDistributionSet':
- return { ...nextState, feeDistribution: await getFeeDistribution() }
- case 'WithdrawalCredentialsSet':
- return {
- ...nextState,
- withdrawalCredentials: await getWithdrawalCredentials(),
- }
- case 'ELRewardsWithdrawalLimitSet':
- return {
- ...nextState,
- elRewardsWithdrawalLimit: await getElRewardsWithdrawalLimit(),
- }
- case 'ELRewardsVaultSet':
- return {
- ...nextState,
- elRewardsVault: await getElRewardsVault(),
- }
- case 'Unbuffered':
- return { ...nextState, bufferedEther: await getBufferedEther() }
- case 'StakingPaused':
- return { ...nextState, stakingLimitInfo: await getStakingLimitInfo() }
- case 'StakingResumed':
- return { ...nextState, stakingLimitInfo: await getStakingLimitInfo() }
- case 'StakingLimitSet':
- return { ...nextState, stakingLimitInfo: await getStakingLimitInfo() }
- case 'StakingLimitRemoved':
- return { ...nextState, stakingLimitInfo: await getStakingLimitInfo() }
- case events.SYNC_STATUS_SYNCING:
- return { ...nextState, isSyncing: true }
- case events.SYNC_STATUS_SYNCED:
- return { ...nextState, isSyncing: false }
- default:
- return state
+ if (event === events.SYNC_STATUS_SYNCING) {
+ return { ...nextState, isSyncing: true }
+ }
+
+ if (event === events.SYNC_STATUS_SYNCED) {
+ return { ...nextState, isSyncing: false }
+ }
+
+ const variable = protocolVariables.find(({ updateEvents }) =>
+ updateEvents.includes(event)
+ )
+
+ if (variable) {
+ return {
+ ...nextState,
+ [variable.stateKey]: await variable.fetch(),
+ }
}
+
+ return nextState
} catch (err) {
console.log(err)
}
@@ -69,128 +175,34 @@ app.store(
function initializeState() {
return async (cachedState) => {
- // fetch in parallel
- const allSettled = await Promise.allSettled([
- getIsStopped(),
- getFee(),
- getFeeDistribution(),
- getWithdrawalCredentials(),
- getElRewardsWithdrawalLimit(),
- getElRewardsVault(),
- getBufferedEther(),
- getTotalPooledEther(),
- getNodeOperatorsRegistry(),
- getDepositContract(),
- getOracle(),
- getOperators(),
- getTreasury(),
- getInsuranceFund(),
- getBeaconStat(),
- getStakingLimitInfo(),
- ])
-
- const [
- isStopped,
- fee,
- feeDistribution,
- withdrawalCredentials,
- elRewardsWithdrawalLimit,
- elRewardsVault,
- bufferedEther,
- totalPooledEther,
- nodeOperatorsRegistry,
- depositContract,
- oracle,
- operators,
- treasury,
- insuranceFund,
- beaconStat,
- stakingLimitInfo,
- ] = allSettled.map((settled) => settled.value)
-
- return {
- ...cachedState,
- isStopped,
- fee,
- feeDistribution,
- withdrawalCredentials,
- elRewardsWithdrawalLimit,
- elRewardsVault,
- bufferedEther,
- totalPooledEther,
- nodeOperatorsRegistry,
- depositContract,
- oracle,
- operators,
- treasury,
- insuranceFund,
- beaconStat,
- stakingLimitInfo,
- }
- }
-}
-
-// API
-function getIsStopped() {
- return app.call('isStopped').toPromise()
-}
-
-function getFee() {
- return app.call('getFee').toPromise()
-}
-
-function getFeeDistribution() {
- return app.call('getFeeDistribution').toPromise()
-}
+ const promises = protocolVariables.map((v) => v.fetch())
-function getWithdrawalCredentials() {
- return app.call('getWithdrawalCredentials').toPromise()
-}
-
-function getElRewardsWithdrawalLimit() {
- return app.call('getELRewardsWithdrawalLimit').toPromise()
-}
-
-function getElRewardsVault() {
- return app.call('getELRewardsVault').toPromise()
-}
+ const settledPromises = await Promise.allSettled(promises)
-function getBufferedEther() {
- return app.call('getBufferedEther').toPromise()
-}
-
-function getTotalPooledEther() {
- return app.call('getTotalPooledEther').toPromise()
-}
-
-function getNodeOperatorsRegistry() {
- return app.call('getOperators').toPromise()
-}
+ const updatedState = settledPromises.reduce((stateObject, cur, index) => {
+ stateObject[protocolVariables[index].stateKey] = cur.value
+ return stateObject
+ }, {})
-function getDepositContract() {
- return app.call('getDepositContract').toPromise()
-}
+ // fetch addresses from LidoLocator
-function getOracle() {
- return app.call('getOracle').toPromise()
-}
+ const { lidoLocator } = updatedState
-function getOperators() {
- return app.call('getOperators').toPromise()
-}
+ const locator = app.external(lidoLocator, LocatorABI)
+ const locatorPromises = locatorVariables.map(f => locator[f]().toPromise())
-function getTreasury() {
- return app.call('getTreasury').toPromise()
-}
+ const settledLocatorPromises = await Promise.allSettled(locatorPromises)
-function getInsuranceFund() {
- return app.call('getInsuranceFund').toPromise()
-}
+ const locations = settledLocatorPromises.reduce((locationsObject, cur, index) => {
+ locationsObject[locatorVariables[index]] = cur.value
+ return locationsObject
+ }, {})
-function getBeaconStat() {
- return app.call('getBeaconStat').toPromise()
-}
-function getStakingLimitInfo() {
- return app.call('getStakeLimitFullInfo').toPromise()
+ return {
+ ...cachedState,
+ ...updatedState,
+ ...locations
+ }
+ }
}
diff --git a/apps/lido/arapp.json b/apps/lido/arapp.json
index e50874b40..0b3211976 100644
--- a/apps/lido/arapp.json
+++ b/apps/lido/arapp.json
@@ -21,38 +21,8 @@
"params": []
},
{
- "name": "Set fee",
- "id": "MANAGE_FEE",
- "params": []
- },
- {
- "name": "Manage withdrawal credentials",
- "id": "MANAGE_WITHDRAWAL_KEY",
- "params": []
- },
- {
- "name": "Manage protocol contracts role",
- "id": "MANAGE_PROTOCOL_CONTRACTS_ROLE",
- "params": []
- },
- {
- "name": "Burn tokens",
- "id": "BURN_ROLE",
- "params": ["Account which tokens are to be burnt", "Token amount"]
- },
- {
- "name": "Deposit role",
- "id": "DEPOSIT_ROLE",
- "params": []
- },
- {
- "name": "Set EL rewards vault role",
- "id": "SET_EL_REWARDS_VAULT_ROLE",
- "params": []
- },
- {
- "name": "Set EL rewards withdrawal limit role",
- "id": "SET_EL_REWARDS_WITHDRAWAL_LIMIT_ROLE",
+ "name": "Unsafely change deposited validators",
+ "id": "UNSAFE_CHANGE_DEPOSITED_VALIDATORS_ROLE",
"params": []
}
],
@@ -81,4 +51,4 @@
},
"appName": "lido.lidopm.eth",
"path": "../../contracts/0.4.24/Lido.sol"
-}
+}
\ No newline at end of file
diff --git a/apps/lido/hardhat.config.js b/apps/lido/hardhat.config.js
index 213cb0417..63482a402 100644
--- a/apps/lido/hardhat.config.js
+++ b/apps/lido/hardhat.config.js
@@ -7,7 +7,7 @@ module.exports = {
...baseConfig,
paths: {
...baseConfig.paths,
- root: '../..'
+ root: '../..',
},
defaultNetwork: process.env.NETWORK_NAME || 'localhost',
// Aragon plugin configuration
@@ -18,6 +18,6 @@ module.exports = {
appSrcPath: 'apps/lido/app/',
appBuildOutputPath: 'apps/lido/dist/',
appName: 'lido',
- hooks // Path to script hooks
- }
+ hooks, // Path to script hooks
+ },
}
diff --git a/apps/lido/scripts/buidler-hooks.js b/apps/lido/scripts/buidler-hooks.js
index 817ab2c8b..d0eb47a22 100644
--- a/apps/lido/scripts/buidler-hooks.js
+++ b/apps/lido/scripts/buidler-hooks.js
@@ -53,5 +53,5 @@ module.exports = {
},
// Called after the app's proxy is updated with a new implementation.
- postUpdate: async ({ proxy, log }, { web3, artifacts }) => {}
+ postUpdate: async ({ proxy, log }, { web3, artifacts }) => {},
}
diff --git a/apps/lidooracle/app/package.json b/apps/lidooracle/app/package.json
index f2c83046b..ed6d566c3 100644
--- a/apps/lidooracle/app/package.json
+++ b/apps/lidooracle/app/package.json
@@ -1,5 +1,5 @@
{
- "name": "lidooracle-frontend",
+ "name": "legacyoracle-frontend",
"version": "1.0.0",
"main": "src/index.js",
"dependencies": {
diff --git a/apps/lidooracle/app/src/App.js b/apps/lidooracle/app/src/App.js
index 335ff31f9..7399dbe3a 100644
--- a/apps/lidooracle/app/src/App.js
+++ b/apps/lidooracle/app/src/App.js
@@ -1,454 +1,26 @@
-import React, { useCallback, useState } from 'react'
import { useAragonApi } from '@aragon/api-react'
-import {
- Button,
- ContextMenu,
- DataView,
- GU,
- Header,
- IconTrash,
- IdentityBadge,
- Main,
- Split,
- SyncIndicator,
- textStyle,
- useTheme,
-} from '@aragon/ui'
-import AddMemberSidePanel from './components/AddMemberSidePanel'
-import MenuItem from './components/MenuItem'
-import InfoBox from './components/InfoBox'
-import ChangeQuorumSidePanel from './components/ChangeQuorumSidePanel'
-import ChangeBeaconReportReceiverSidePanel from './components/ChangeBeaconReportReceiverSidePanel'
-import IconEdit from '@aragon/ui/dist/IconEdit'
-import ChangeIncreaseSidePanel from './components/ChangeIncreaseSidePanel'
-import ChangeDecreaseSidePanel from './components/ChangeDecreaseSidePanel'
-import { constants, ethers } from 'ethers'
+import { Header, Main, SyncIndicator, useTheme } from '@aragon/ui'
+import React from 'react'
+import { ThemeProvider } from 'styled-components'
+import { Primary } from './components/Primary'
+import { Secondary } from './components/Secondary'
+import { Split } from './components/shared'
+
export default function App() {
- const { api, appState, currentApp, guiStyle } = useAragonApi()
- const theme = useTheme()
- const {
- isSyncing,
- oracleMembers,
- quorum,
- currentFrame,
- expectedEpochId,
- currentOraclesReportStatus,
- allowedBeaconBalanceAnnualRelativeIncrease,
- allowedBeaconBalanceRelativeDecrease,
- beaconReportReceiver,
- currentReportVariants,
- lastCompletedReportDelta,
- version,
- } = appState
- const { appearance } = guiStyle
+ const { appState, currentApp, guiStyle } = useAragonApi()
const appName = (currentApp && currentApp.name) || 'app'
-
- // MEMBERS
-
- const [addMemberSidePanelOpen, setAddMemberSidePanelOpen] = useState(false)
-
- const openAddMemberSidePanel = useCallback(
- () => setAddMemberSidePanelOpen(true),
- []
- )
-
- const closeAddMemberSidePanel = useCallback(
- () => setAddMemberSidePanelOpen(false),
- []
- )
-
- const addOracleMember = useCallback(
- (address) => {
- return api.addOracleMember(address).toPromise()
- },
- [api]
- )
-
- const removeOracleMember = useCallback(
- (address) => {
- return api.removeOracleMember(address).toPromise()
- },
- [api]
- )
-
- // INCREASE
- const [increaseSidePanelOpen, setIncreaseSidePanelOpen] = useState(false)
-
- const openIncreaseSidePanel = useCallback(
- () => setIncreaseSidePanelOpen(true),
- []
- )
-
- const closeIncreaseSidePanel = useCallback(
- () => setIncreaseSidePanelOpen(false),
- []
- )
-
- const changeIncrease = useCallback(
- (value) => {
- return api
- .setAllowedBeaconBalanceAnnualRelativeIncrease(value)
- .toPromise()
- },
- [api]
- )
-
- // DECREASE
- const [decreaseSidePanelOpen, setDecreaseSidePanelOpen] = useState(false)
-
- const openDecreaseSidePanel = useCallback(
- () => setDecreaseSidePanelOpen(true),
- []
- )
-
- const closeDecreaseSidePanel = useCallback(
- () => setDecreaseSidePanelOpen(false),
- []
- )
-
- const changeDecrease = useCallback(
- (value) => {
- return api.setAllowedBeaconBalanceRelativeDecrease(value).toPromise()
- },
- [api]
- )
-
- // QUORUM
- const [changeQuorumSidePanelOpen, setChangeQuorumSidePanelOpen] = useState(
- false
- )
-
- const openChangeQuorumSidePanel = useCallback(
- () => setChangeQuorumSidePanelOpen(true),
- []
- )
-
- const closeChangeQuorumSidePanel = useCallback(
- () => setChangeQuorumSidePanelOpen(false),
- []
- )
-
- const setQuorum = useCallback(
- (quorum) => {
- return api.setQuorum(quorum).toPromise()
- },
- [api]
- )
-
- // BEACON REPORT RECEIVER
- const [
- reportReceiverSidePanelOpen,
- setReportReceiverSidePanelOpen,
- ] = useState(false)
-
- const openReportReceiverSidePanel = useCallback(
- () => setReportReceiverSidePanelOpen(true),
- []
- )
- const closeReportReceiverSidePanel = useCallback(
- () => setReportReceiverSidePanelOpen(false),
- []
- )
-
- const setBeaconReceiver = useCallback(
- (address) => {
- return api.setBeaconReportReceiver(address).toPromise()
- },
- [api]
- )
-
- // RENDER ELEMENTS
-
- const currentFrameEl = renderCurrentFrame(currentFrame)
- const lastCompletedReportDeltaEl = renderLastCompletedReportDelta(
- lastCompletedReportDelta
- )
-
- const renderSettings = useCallback(
- (value, i) => {
- switch (i) {
- case 0:
- return [
- 'Max Allowed APR',
-
- {value ? `${value / 100}%` : 'Unavailable'}
- }
- label="Change increase"
- display="icon"
- onClick={openIncreaseSidePanel}
- style={{ marginLeft: 10 }}
- />
-
,
- ]
- case 1:
- return [
- 'Drop Limit Between Frames',
-
- {value ? `${value / 100}%` : 'Unavailable'}
- }
- label="Change decrease"
- display="icon"
- onClick={openDecreaseSidePanel}
- style={{ marginLeft: 10 }}
- />
-
,
- ]
- case 2:
- return [
- 'Beacon Report Receiver',
-
-
- }
- label="Change receiver"
- display="icon"
- onClick={openReportReceiverSidePanel}
- style={{ marginLeft: 10 }}
- />
-
,
- ]
- default:
- return null
- }
- },
- [openDecreaseSidePanel, openIncreaseSidePanel, openReportReceiverSidePanel]
- )
+ const { appearance } = guiStyle
+ const { isSyncing } = appState
+ const theme = useTheme()
return (
- {isSyncing && }
-
- }
- />
-
- [
- ,
-
- {(2 ** i) & currentOraclesReportStatus ? 'Submitted' : ''}
- ,
- ]}
- renderEntryActions={(memberAddress) => (
-
-
- )}
- />
-
- Current Report Variants
-
- [
- {i},
- {beaconBalance} gwei,
- {beaconValidators},
- {count},
- ]}
- />
-
- Settings
-
-
- >
- }
- secondary={
- <>
-
-
- {currentFrameEl && (
- api.emitTrigger('UI:UpdateFrame')}
- />
- )}
- {lastCompletedReportDeltaEl && (
-
- )}
- {version && (
-
- Lido Oracle v{+version + 1}
-
- )}
- >
- }
- />
-
-
-
-
-
+
+ {isSyncing && }
+
+ } secondary={} />
+
)
}
-
-function renderCurrentFrame(frame) {
- if (!frame) {
- return null
- }
- return (
- <>
-
-
-
- >
- )
-}
-
-function renderLastCompletedReportDelta(lastCompletedReportDelta) {
- if (!lastCompletedReportDelta) return null
-
- return (
- <>
-
-
-
- >
- )
-}
-
-function LabelValue({ label, value }) {
- return (
-
-
- {label}
-
-
- {value}
-
-
- )
-}
-
-function formatUnixTime(unixTime) {
- return new Date(1000 * unixTime).toISOString().replace(/[.]\d+Z$/, 'Z')
-}
diff --git a/apps/lidooracle/app/src/abi/AccountingOracle.abi.json b/apps/lidooracle/app/src/abi/AccountingOracle.abi.json
new file mode 100644
index 000000000..cf50e6f36
--- /dev/null
+++ b/apps/lidooracle/app/src/abi/AccountingOracle.abi.json
@@ -0,0 +1,246 @@
+[
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "EXTRA_DATA_FORMAT_EMPTY",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "EXTRA_DATA_FORMAT_LIST",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "EXTRA_DATA_TYPE_EXITED_VALIDATORS",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "EXTRA_DATA_TYPE_STUCK_VALIDATORS",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "GENESIS_TIME",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "LEGACY_ORACLE",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "SECONDS_PER_SLOT",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getConsensusContract",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getConsensusReport",
+ "outputs": [
+ {
+ "internalType": "bytes32",
+ "name": "hash",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "uint256",
+ "name": "refSlot",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "processingDeadlineTime",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bool",
+ "name": "processingStarted",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getConsensusVersion",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getContractVersion",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getLastProcessingRefSlot",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getProcessingState",
+ "outputs": [
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "currentFrameRefSlot",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "processingDeadlineTime",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "mainDataHash",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "bool",
+ "name": "mainDataSubmitted",
+ "type": "bool"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "extraDataHash",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "uint256",
+ "name": "extraDataFormat",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bool",
+ "name": "extraDataSubmitted",
+ "type": "bool"
+ },
+ {
+ "internalType": "uint256",
+ "name": "extraDataItemsCount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "extraDataItemsSubmitted",
+ "type": "uint256"
+ }
+ ],
+ "internalType": "struct AccountingOracle.ProcessingState",
+ "name": "result",
+ "type": "tuple"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ }
+]
\ No newline at end of file
diff --git a/apps/lidooracle/app/src/abi/HashConsensus.abi.json b/apps/lidooracle/app/src/abi/HashConsensus.abi.json
new file mode 100644
index 000000000..520d0a6b8
--- /dev/null
+++ b/apps/lidooracle/app/src/abi/HashConsensus.abi.json
@@ -0,0 +1,249 @@
+[
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getChainConfig",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "slotsPerEpoch",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "secondsPerSlot",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "genesisTime",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getConsensusState",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "refSlot",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "consensusReport",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "bool",
+ "name": "isReportProcessing",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "addr",
+ "type": "address"
+ }
+ ],
+ "name": "getConsensusStateForMember",
+ "outputs": [
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "currentFrameRefSlot",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "currentFrameConsensusReport",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "bool",
+ "name": "isMember",
+ "type": "bool"
+ },
+ {
+ "internalType": "bool",
+ "name": "isFastLane",
+ "type": "bool"
+ },
+ {
+ "internalType": "bool",
+ "name": "canReport",
+ "type": "bool"
+ },
+ {
+ "internalType": "uint256",
+ "name": "lastMemberReportRefSlot",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "currentFrameMemberReport",
+ "type": "bytes32"
+ }
+ ],
+ "internalType": "struct HashConsensus.MemberConsensusState",
+ "name": "result",
+ "type": "tuple"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getCurrentFrame",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "refSlot",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "reportProcessingDeadlineSlot",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getFastLaneMembers",
+ "outputs": [
+ {
+ "internalType": "address[]",
+ "name": "addresses",
+ "type": "address[]"
+ },
+ {
+ "internalType": "uint256[]",
+ "name": "lastReportedRefSlots",
+ "type": "uint256[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getFrameConfig",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "initialEpoch",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "epochsPerFrame",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "fastLaneLengthSlots",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getInitialRefSlot",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getMembers",
+ "outputs": [
+ {
+ "internalType": "address[]",
+ "name": "addresses",
+ "type": "address[]"
+ },
+ {
+ "internalType": "uint256[]",
+ "name": "lastReportedRefSlots",
+ "type": "uint256[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getQuorum",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getReportProcessor",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getReportVariants",
+ "outputs": [
+ {
+ "internalType": "bytes32[]",
+ "name": "variants",
+ "type": "bytes32[]"
+ },
+ {
+ "internalType": "uint256[]",
+ "name": "support",
+ "type": "uint256[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ }
+]
\ No newline at end of file
diff --git a/apps/lidooracle/app/src/abi/Lido.abi.json b/apps/lidooracle/app/src/abi/Lido.abi.json
new file mode 100644
index 000000000..db53a4491
--- /dev/null
+++ b/apps/lidooracle/app/src/abi/Lido.abi.json
@@ -0,0 +1,16 @@
+[
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getLidoLocator",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ }
+]
\ No newline at end of file
diff --git a/apps/lidooracle/app/src/abi/LidoLocator.abi.json b/apps/lidooracle/app/src/abi/LidoLocator.abi.json
new file mode 100644
index 000000000..a2adbf3e4
--- /dev/null
+++ b/apps/lidooracle/app/src/abi/LidoLocator.abi.json
@@ -0,0 +1,16 @@
+[
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "accountingOracle",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ }
+]
\ No newline at end of file
diff --git a/apps/lidooracle/app/src/components/AddMemberSidePanel.js b/apps/lidooracle/app/src/components/AddMemberSidePanel.js
deleted file mode 100644
index cb21e90b1..000000000
--- a/apps/lidooracle/app/src/components/AddMemberSidePanel.js
+++ /dev/null
@@ -1,77 +0,0 @@
-import { Button, GU, SidePanel } from '@aragon/ui'
-import React, { useCallback } from 'react'
-import { Formik, Field } from 'formik'
-import * as yup from 'yup'
-import TextField from './TextField'
-import Info from '@aragon/ui/dist/Info'
-
-const initialValues = {
- address: '',
-}
-
-const validationSchema = yup.object().shape({
- address: yup.string().required().min(1),
-})
-
-function PanelContent({ api, onClose }) {
- const onSubmit = useCallback(
- ({ address }) => {
- api(address).then(() => {
- onClose()
- })
- },
- [api, onClose]
- )
-
- return (
-
- {({ submitForm, isSubmitting }) => {
- return (
-
- )
- }}
-
- )
-}
-
-export default (props) => (
-
-
-
-)
diff --git a/apps/lidooracle/app/src/components/ChangeBeaconReportReceiverSidePanel.js b/apps/lidooracle/app/src/components/ChangeBeaconReportReceiverSidePanel.js
deleted file mode 100644
index d908160a5..000000000
--- a/apps/lidooracle/app/src/components/ChangeBeaconReportReceiverSidePanel.js
+++ /dev/null
@@ -1,77 +0,0 @@
-import { Button, GU, SidePanel } from '@aragon/ui'
-import React, { useCallback } from 'react'
-import { Formik, Field } from 'formik'
-import * as yup from 'yup'
-import TextField from './TextField'
-import Info from '@aragon/ui/dist/Info'
-
-const initialValues = {
- address: '',
-}
-
-const validationSchema = yup.object().shape({
- address: yup.string().required().min(1),
-})
-
-function PanelContent({ api, onClose }) {
- const onSubmit = useCallback(
- ({ address }) => {
- api(address).finally(() => {
- onClose()
- })
- },
- [api, onClose]
- )
-
- return (
-
- {({ submitForm, isSubmitting }) => {
- return (
-
- )
- }}
-
- )
-}
-
-export default (props) => (
-
-
-
-)
diff --git a/apps/lidooracle/app/src/components/ChangeDecreaseSidePanel.js b/apps/lidooracle/app/src/components/ChangeDecreaseSidePanel.js
deleted file mode 100644
index 6974d5554..000000000
--- a/apps/lidooracle/app/src/components/ChangeDecreaseSidePanel.js
+++ /dev/null
@@ -1,102 +0,0 @@
-import { Button, GU, SidePanel } from '@aragon/ui'
-import React, { useCallback } from 'react'
-import { Formik, Field } from 'formik'
-import * as yup from 'yup'
-import TextField from './TextField'
-import Info from '@aragon/ui/dist/Info'
-
-const initialValues = {
- value: '',
-}
-
-const validationSchema = yup.object().shape({
- value: yup
- .number()
- .positive()
- .required()
- .min(0)
- .max(100)
- .test('Value', `Value can have up to 2 decimal places.`, (value) => {
- const regex = /^\d{1,3}(\.\d{1,2})?$/
- return regex.test(value)
- }),
-})
-
-function PanelContent({ api, onClose }) {
- const onSubmit = useCallback(
- ({ value }) => {
- api(value * 100).finally(() => {
- onClose()
- })
- },
- [api, onClose]
- )
-
- return (
-
- {({ submitForm, isSubmitting }) => {
- return (
-
- )
- }}
-
- )
-}
-
-export default (props) => (
-
-
-
-)
diff --git a/apps/lidooracle/app/src/components/ChangeIncreaseSidePanel.js b/apps/lidooracle/app/src/components/ChangeIncreaseSidePanel.js
deleted file mode 100644
index 626dfabec..000000000
--- a/apps/lidooracle/app/src/components/ChangeIncreaseSidePanel.js
+++ /dev/null
@@ -1,102 +0,0 @@
-import { Button, GU, SidePanel } from '@aragon/ui'
-import React, { useCallback } from 'react'
-import { Formik, Field } from 'formik'
-import * as yup from 'yup'
-import TextField from './TextField'
-import Info from '@aragon/ui/dist/Info'
-
-const initialValues = {
- value: '',
-}
-
-const validationSchema = yup.object().shape({
- value: yup
- .number()
- .positive()
- .required()
- .min(0)
- .max(100)
- .test('Value', `Value can have up to 2 decimal places.`, (value) => {
- const regex = /^\d{1,3}(\.\d{1,2})?$/
- return regex.test(value)
- }),
-})
-
-function PanelContent({ api, onClose }) {
- const onSubmit = useCallback(
- ({ value }) => {
- api(value * 100).finally(() => {
- onClose()
- })
- },
- [api, onClose]
- )
-
- return (
-
- {({ submitForm, isSubmitting }) => {
- return (
-
- )
- }}
-
- )
-}
-
-export default (props) => (
-
-
-
-)
diff --git a/apps/lidooracle/app/src/components/ChangeQuorumSidePanel.js b/apps/lidooracle/app/src/components/ChangeQuorumSidePanel.js
deleted file mode 100644
index 8c149d8d9..000000000
--- a/apps/lidooracle/app/src/components/ChangeQuorumSidePanel.js
+++ /dev/null
@@ -1,79 +0,0 @@
-import { Button, GU, SidePanel } from '@aragon/ui'
-import React, { useCallback } from 'react'
-import { Formik, Field } from 'formik'
-import * as yup from 'yup'
-import TextField from './TextField'
-import Info from '@aragon/ui/dist/Info'
-
-const initialValues = {
- quorum: '',
-}
-
-const validationSchema = yup.object().shape({
- quorum: yup.number().integer().required().min(0),
-})
-
-function PanelContent({ api, onClose }) {
- const onSubmit = useCallback(
- ({ quorum }) => {
- api(quorum).finally(() => {
- onClose()
- })
- },
- [api, onClose]
- )
-
- return (
-
- {({ submitForm, isSubmitting }) => {
- return (
-
- )
- }}
-
- )
-}
-
-export default (props) => (
-
-
-
-)
diff --git a/apps/lidooracle/app/src/components/InfoBox.js b/apps/lidooracle/app/src/components/InfoBox.js
deleted file mode 100644
index 453080669..000000000
--- a/apps/lidooracle/app/src/components/InfoBox.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import { Box, Button } from '@aragon/ui'
-import React from 'react'
-
-export default function InfoBox({
- heading,
- value,
- onClick,
- label,
- largeText = true,
-}) {
- return (
-
-
- {value}
-
- {onClick && }
-
- )
-}
diff --git a/apps/lidooracle/app/src/components/MenuItem.js b/apps/lidooracle/app/src/components/MenuItem.js
deleted file mode 100644
index f91d83e1f..000000000
--- a/apps/lidooracle/app/src/components/MenuItem.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import { ContextMenuItem, GU, useTheme } from '@aragon/ui'
-import React from 'react'
-
-export default function MenuItem({ onClick, label, icon, iconColor }) {
- const theme = useTheme()
-
- return (
-
-
- {icon}
-
-
- {label}
-
-
- )
-}
diff --git a/apps/lidooracle/app/src/components/Primary.js b/apps/lidooracle/app/src/components/Primary.js
new file mode 100644
index 000000000..438ef7cca
--- /dev/null
+++ b/apps/lidooracle/app/src/components/Primary.js
@@ -0,0 +1,59 @@
+import { useAppState } from '@aragon/api-react'
+import { DataView, IconConnect, IdentityBadge } from '@aragon/ui'
+import React from 'react'
+import { css } from 'styled-components'
+import { BoxUnpadded, BytesBadge, Dot, ListItem, ListItemAddress, ListItemBoolean, ListItemTimestamp, ListItemUnformattedValue, LoadableElement } from './shared'
+
+export const Primary = () => {
+ const {
+ reportVariants,
+ consensusReport,
+ reportProcessor,
+ memberDetails,
+ quorum,
+ initialRefSlot,
+ consensusVersion,
+ consensusContract,
+ lido
+ } = useAppState()
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+ [
+ •,
+ <> props.theme.warning}`} />>,
+ memberInfo?.currentFrameRefSlot,
+
+ ]}
+ />
+
+
+
+
+
+
+
+
+
+ >
+ )
+}
diff --git a/apps/lidooracle/app/src/components/Secondary.js b/apps/lidooracle/app/src/components/Secondary.js
new file mode 100644
index 000000000..3f803b9a5
--- /dev/null
+++ b/apps/lidooracle/app/src/components/Secondary.js
@@ -0,0 +1,25 @@
+import { useAppState } from '@aragon/api-react'
+import React from 'react'
+import { BoxUnpadded, BytesBadge, ListItem, ListItemBoolean, ListItemTimestamp, ListItemUnformattedValue, LoadableElement } from './shared'
+
+export const Secondary = () => {
+ const {
+ chainConfig,
+ frameConfig
+ } = useAppState()
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ >
+ )
+}
diff --git a/apps/lidooracle/app/src/components/TextField.js b/apps/lidooracle/app/src/components/TextField.js
deleted file mode 100644
index 7211d3697..000000000
--- a/apps/lidooracle/app/src/components/TextField.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Field, Info, TextInput } from '@aragon/ui'
-import React from 'react'
-
-const TextField = React.forwardRef(({ label, field, form, ...props }, ref) => {
- return (
-
-
- {form.errors[field.name] && (
-
- {form.errors[field.name]}
-
- )}
-
- )
-})
-
-export default TextField
diff --git a/apps/lidooracle/app/src/components/shared/BytesBadge.js b/apps/lidooracle/app/src/components/shared/BytesBadge.js
new file mode 100644
index 000000000..5a9b50b0f
--- /dev/null
+++ b/apps/lidooracle/app/src/components/shared/BytesBadge.js
@@ -0,0 +1,44 @@
+import { GU, textStyle } from '@aragon/ui'
+import BadgeBase from '@aragon/ui/dist/BadgeBase'
+import IconCheck from '@aragon/ui/dist/IconCheck'
+import React, { useCallback, useEffect, useState } from 'react'
+import { CopyToClipboard } from 'react-copy-to-clipboard'
+import styled from 'styled-components'
+
+const BadgeText = styled.span`
+ margin-left: ${0.5 * GU}px;
+`
+
+export const BytesBadge = ({ bytes }) => {
+ const shortened =
+ typeof bytes === 'string'
+ ? `${bytes.substring(0, 6)}…${bytes.substring(60)}`
+ : ''
+
+ const [copied, setCopied] = useState(false)
+ const handleCopy = useCallback(() => setCopied(true), [])
+
+ useEffect(() => {
+ let interval
+ if (copied) {
+ interval = setInterval(() => {
+ setCopied(false)
+ }, 3000)
+ }
+
+ return () => clearInterval(interval)
+ }, [copied])
+
+ return (
+
+
+ {copied && }
+ {shortened}
+ >
+ }
+ />
+
+ )
+}
diff --git a/apps/lidooracle/app/src/components/shared/ListItem.js b/apps/lidooracle/app/src/components/shared/ListItem.js
new file mode 100644
index 000000000..facd2f490
--- /dev/null
+++ b/apps/lidooracle/app/src/components/shared/ListItem.js
@@ -0,0 +1,41 @@
+import { GU, useTheme } from '@aragon/ui'
+import React from 'react'
+import styled from 'styled-components'
+
+const ListItemStyle = styled.li`
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: ${GU}px ${GU * 3}px ${GU}px
+ ${(props) => (props.nested ? GU * 6 : GU * 3)}px;
+ line-height: 40px;
+ border-top: 1px solid
+ ${(props) => (props.isDark ? '#2C3A58' : props.theme.border)};
+
+ & :first-of-type {
+ margin-top: 0;
+ border-top: none;
+ }
+`
+
+const ListItemLabel = styled.span`
+ color: ${(props) =>
+ props.isDark ? '#7C99D6' : props.theme.surfaceContentSecondary};
+`
+
+const ListItemValue = styled.strong`
+ text-align: right;
+`
+
+export const ListItem = ({ label, children, nested }) => {
+ const theme = useTheme()
+
+ const themeDark = theme?._name === 'dark'
+
+ return (
+
+ {label}
+ {children}
+
+ )
+}
diff --git a/apps/lidooracle/app/src/components/shared/ListItemAddress.js b/apps/lidooracle/app/src/components/shared/ListItemAddress.js
new file mode 100644
index 000000000..dd951d519
--- /dev/null
+++ b/apps/lidooracle/app/src/components/shared/ListItemAddress.js
@@ -0,0 +1,13 @@
+import { IdentityBadge } from '@aragon/ui'
+import React from 'react'
+import { ListItem, LoadableElement } from '.'
+
+export const ListItemAddress = ({ label, value }) => {
+ return (
+
+
+
+
+
+ )
+}
diff --git a/apps/lidooracle/app/src/components/shared/ListItemBoolean.js b/apps/lidooracle/app/src/components/shared/ListItemBoolean.js
new file mode 100644
index 000000000..be4803efe
--- /dev/null
+++ b/apps/lidooracle/app/src/components/shared/ListItemBoolean.js
@@ -0,0 +1,11 @@
+import React from 'react'
+import { ListItem } from './ListItem'
+import { LoadableElement } from './LoadableElement'
+
+export const ListItemBoolean = ({ label, value, renderElements = ["Yes", "No"] }) => {
+ return (
+
+ {value ? renderElements[0] : renderElements[1]}
+
+ )
+}
diff --git a/apps/lidooracle/app/src/components/shared/ListItemTimestamp.js b/apps/lidooracle/app/src/components/shared/ListItemTimestamp.js
new file mode 100644
index 000000000..f8cd18a2c
--- /dev/null
+++ b/apps/lidooracle/app/src/components/shared/ListItemTimestamp.js
@@ -0,0 +1,33 @@
+import React from 'react'
+import { ListItem } from './ListItem'
+import { LoadableElement } from './LoadableElement'
+
+export const ListItemTimestamp = ({ label, value }) => {
+ return (
+
+ {toDateTime(value)}
+
+ )
+}
+
+
+const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
+
+function toDateTime(unixTimestamp) {
+ const date = new Date(unixTimestamp * 1000);
+
+ const [hours, minutes, seconds, day, month, year] = [
+ zeroPad(date.getHours()),
+ zeroPad(date.getMinutes()),
+ zeroPad(date.getSeconds()),
+ date.getDate(),
+ months[date.getMonth()],
+ date.getFullYear()
+ ]
+
+ return `${hours}:${minutes}:${seconds} ${month} ${day}, ${year}`
+}
+
+function zeroPad(number) {
+ return String(number).padStart(2, "0")
+}
\ No newline at end of file
diff --git a/apps/lidooracle/app/src/components/shared/ListItemUnformattedValue.js b/apps/lidooracle/app/src/components/shared/ListItemUnformattedValue.js
new file mode 100644
index 000000000..9afaa736b
--- /dev/null
+++ b/apps/lidooracle/app/src/components/shared/ListItemUnformattedValue.js
@@ -0,0 +1,11 @@
+import React from 'react'
+import { ListItem } from './ListItem'
+import { LoadableElement } from './LoadableElement'
+
+export const ListItemUnformattedValue = ({ label, value }) => {
+ return (
+
+ {value}
+
+ )
+}
diff --git a/apps/lidooracle/app/src/components/shared/LoadableElement.js b/apps/lidooracle/app/src/components/shared/LoadableElement.js
new file mode 100644
index 000000000..a61ccfd18
--- /dev/null
+++ b/apps/lidooracle/app/src/components/shared/LoadableElement.js
@@ -0,0 +1,10 @@
+import React from 'react'
+import { LoadingRing } from '@aragon/ui'
+
+export const LoadableElement = ({ value, children }) => {
+ if (typeof value === 'undefined') {
+ return
+ }
+
+ return {children}
+}
diff --git a/apps/lidooracle/app/src/components/shared/Split.js b/apps/lidooracle/app/src/components/shared/Split.js
new file mode 100644
index 000000000..e5f69f60e
--- /dev/null
+++ b/apps/lidooracle/app/src/components/shared/Split.js
@@ -0,0 +1,67 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import { GU, Inside, useLayout } from '@aragon/ui'
+
+export const Split = ({ primary, secondary, invert }) => {
+ const { name: layout } = useLayout()
+ const oneColumn = layout === 'small' || layout === 'medium'
+
+ const inverted =
+ (!oneColumn && invert === 'horizontal') ||
+ (oneColumn && invert === 'vertical')
+
+ const primaryContent = (
+
+
+ {primary}
+
+
+ )
+
+ const secondaryContent = (
+
+
+ {secondary}
+
+
+ )
+
+ return (
+
+
+ {inverted ? secondaryContent : primaryContent}
+ {inverted ? primaryContent : secondaryContent}
+
+
+ )
+}
+
+Split.propTypes = {
+ invert: PropTypes.oneOf(['none', 'horizontal', 'vertical']),
+ primary: PropTypes.node,
+ secondary: PropTypes.node,
+}
+
+Split.defaultProps = {
+ invert: 'none',
+}
diff --git a/apps/lidooracle/app/src/components/shared/index.js b/apps/lidooracle/app/src/components/shared/index.js
new file mode 100644
index 000000000..b152690b0
--- /dev/null
+++ b/apps/lidooracle/app/src/components/shared/index.js
@@ -0,0 +1,9 @@
+export { Split } from "./Split"
+export { ListItem } from "./ListItem"
+export { LoadableElement } from "./LoadableElement"
+export { ListItemAddress } from "./ListItemAddress"
+export { ListItemUnformattedValue } from "./ListItemUnformattedValue"
+export { BytesBadge } from "./BytesBadge"
+export { ListItemBoolean } from "./ListItemBoolean"
+export { ListItemTimestamp } from "./ListItemTimestamp"
+export * from './styles'
\ No newline at end of file
diff --git a/apps/lidooracle/app/src/components/shared/styles.js b/apps/lidooracle/app/src/components/shared/styles.js
new file mode 100644
index 000000000..9bd9b22fc
--- /dev/null
+++ b/apps/lidooracle/app/src/components/shared/styles.js
@@ -0,0 +1,42 @@
+import { Box, GU, Info } from '@aragon/ui'
+import styled, { keyframes } from 'styled-components'
+
+export const InfoSpaced = styled(Info)`
+ margin: ${GU * 3}px 0;
+`
+
+export const FormSpaced = styled.form`
+ margin-top: ${3 * GU}px;
+`
+
+export const Controls = styled.div`
+ display: inline-flex;
+ justify-content: flex-start;
+ align-items: center;
+
+ & > :first-child {
+ margin-right: ${GU * 2}px;
+ }
+`
+
+export const BoxUnpadded = styled(Box)`
+ & > div {
+ padding: 0;
+ }
+`
+
+const blink = keyframes`
+ from {
+ opacity: 1;
+ }
+ to {
+ opacity: 0;
+ }
+`
+
+export const Dot = styled.span`
+ margin-right: ${GU}px;
+ color: ${(props) =>
+ props.active ? props.theme.positive : props.theme.negative};
+ animation: ${blink} 0.5s infinite alternate;
+`
\ No newline at end of file
diff --git a/apps/lidooracle/app/src/index.js b/apps/lidooracle/app/src/index.js
index 7b7d5e606..240d5d1be 100644
--- a/apps/lidooracle/app/src/index.js
+++ b/apps/lidooracle/app/src/index.js
@@ -6,22 +6,13 @@ import ReactDOM from 'react-dom'
import { AragonApi } from '@aragon/api-react'
import App from './App'
+const defaultState = {
+ isSyncing: true,
+}
+
const reducer = (state) => {
if (state === null) {
- return {
- oracleMembers: [],
- quorum: null,
- currentFrame: null,
- expectedEpochId: null,
- currentOraclesReportStatus: null,
- allowedBeaconBalanceAnnualRelativeIncrease: null,
- allowedBeaconBalanceRelativeDecrease: null,
- beaconReportReceiver: null,
- currentReportVariants: [],
- lastCompletedReportDelta: null,
- version: null,
- isSyncing: true,
- }
+ return defaultState
}
return state
}
diff --git a/apps/lidooracle/app/src/script.js b/apps/lidooracle/app/src/script.js
index a6a9e3aaa..7e1f0c12b 100644
--- a/apps/lidooracle/app/src/script.js
+++ b/apps/lidooracle/app/src/script.js
@@ -1,6 +1,10 @@
import 'core-js/stable'
import 'regenerator-runtime/runtime'
import Aragon, { events } from '@aragon/api'
+import LidoABI from "./abi/Lido.abi.json"
+import LidoLocatorABI from "./abi/LidoLocator.abi.json"
+import AccountingOracleABI from "./abi/AccountingOracle.abi.json"
+import HashConsensusABI from "./abi/HashConsensus.abi.json"
const app = new Aragon()
@@ -11,50 +15,15 @@ app.store(
}
try {
- switch (event) {
- case 'MemberAdded':
- return { ...nextState, oracleMembers: await getOracleMembers() }
- case 'MemberRemoved':
- return { ...nextState, oracleMembers: await getOracleMembers() }
- case 'QuorumChanged':
- return { ...nextState, quorum: await getQuorum() }
- case 'ExpectedEpochIdUpdated':
- return { ...nextState, expectedEpochId: await getExpectedEpochId() }
- case 'AllowedBeaconBalanceAnnualRelativeIncreaseSet':
- return {
- ...nextState,
- allowedBeaconBalanceAnnualRelativeIncrease: await getAllowedBeaconBalanceAnnualRelativeIncrease(),
- }
- case 'AllowedBeaconBalanceRelativeDecreaseSet':
- return {
- ...nextState,
- allowedBeaconBalanceRelativeDecrease: await getAllowedBeaconBalanceRelativeDecrease(),
- }
- case 'UI:UpdateFrame':
- return { ...nextState, currentFrame: await getCurrentFrame() }
- case 'BeaconReportReceiverSet':
- return {
- ...nextState,
- beaconReportReceiver: await getBeaconReportReceiver(),
- }
- case 'BeaconReported':
- return {
- ...nextState,
- currentReportVariants: await getCurrentReportVariants(),
- currentOraclesReportStatus: await getCurrentOraclesReportStatus(),
- }
- case 'ContractVersionSet':
- return {
- ...nextState,
- version: await getVersion(),
- }
- case events.SYNC_STATUS_SYNCING:
- return { ...nextState, isSyncing: true }
- case events.SYNC_STATUS_SYNCED:
- return { ...nextState, isSyncing: false }
- default:
- return state
+ if (event === events.SYNC_STATUS_SYNCING) {
+ return { ...nextState, isSyncing: true }
}
+
+ if (event === events.SYNC_STATUS_SYNCED) {
+ return { ...nextState, isSyncing: false }
+ }
+
+ return nextState
} catch (err) {
console.log(err)
}
@@ -70,112 +39,160 @@ app.store(
* *
***********************/
-function initializeState() {
- return async (cachedState) => {
- const [
- oracleMembers,
- quorum,
- currentFrame,
- expectedEpochId,
- currentOraclesReportStatus,
- allowedBeaconBalanceAnnualRelativeIncrease,
- allowedBeaconBalanceRelativeDecrease,
- beaconReportReceiver,
- currentReportVariants,
- lastCompletedReportDelta,
- version,
- ] = await Promise.all([
- getOracleMembers(),
- getQuorum(),
- getCurrentFrame(),
- getExpectedEpochId(),
- getCurrentOraclesReportStatus(),
- getAllowedBeaconBalanceAnnualRelativeIncrease(),
- getAllowedBeaconBalanceRelativeDecrease(),
- getBeaconReportReceiver(),
- getCurrentReportVariants(),
- getLastCompletedReportDelta(),
- getVersion(),
- ])
-
- return {
- ...cachedState,
- oracleMembers,
- quorum,
- currentFrame,
- expectedEpochId,
- currentOraclesReportStatus,
- allowedBeaconBalanceAnnualRelativeIncrease,
- allowedBeaconBalanceRelativeDecrease,
- beaconReportReceiver,
- currentReportVariants,
- lastCompletedReportDelta,
- version,
- }
- }
-}
-
-function getOracleMembers() {
- return app.call('getOracleMembers').toPromise()
-}
+const accountingOracleState = [
+ {
+ appStateKey: "extraDataFormatEmpty",
+ contractFunction: "EXTRA_DATA_FORMAT_EMPTY"
+ },
+ {
+ appStateKey: "extraDataFormatList",
+ contractFunction: "EXTRA_DATA_FORMAT_LIST"
+ },
+ {
+ appStateKey: "extraDataTypeExitedValidators",
+ contractFunction: "EXTRA_DATA_TYPE_EXITED_VALIDATORS"
+ },
+ {
+ appStateKey: "extraDataTypeStuckValidators",
+ contractFunction: "EXTRA_DATA_TYPE_STUCK_VALIDATORS"
+ },
+ {
+ appStateKey: "genesisTime",
+ contractFunction: "GENESIS_TIME"
+ },
+ {
+ appStateKey: "legacyOracle",
+ contractFunction: "LEGACY_ORACLE"
+ },
+ {
+ appStateKey: "secondsPerSlot",
+ contractFunction: "SECONDS_PER_SLOT"
+ },
+ {
+ appStateKey: "consensusContract",
+ contractFunction: "getConsensusContract"
+ },
+ {
+ appStateKey: "consensusReport",
+ contractFunction: "getConsensusReport"
+ },
+ {
+ appStateKey: "consensusVersion",
+ contractFunction: "getConsensusVersion"
+ },
+ {
+ appStateKey: "contractVersion",
+ contractFunction: "getContractVersion"
+ },
+ {
+ appStateKey: "lastProcessingRefSlot",
+ contractFunction: "getLastProcessingRefSlot"
+ },
+ {
+ appStateKey: "processingState",
+ contractFunction: "getProcessingState"
+ },
+ {
+ appStateKey: "contractVersion",
+ contractFunction: "getContractVersion"
+ },
+ {
+ appStateKey: "contractVersion",
+ contractFunction: "getContractVersion"
+ },
+]
-function getQuorum() {
- return app.call('getQuorum').toPromise()
-}
-async function getCurrentFrame() {
- const frame = await app.call('getCurrentFrame').toPromise()
- return {
- frameEpochId: String(frame.frameEpochId),
- frameStartTime: +frame.frameStartTime,
- frameEndTime: +frame.frameEndTime,
+const hashConsensusState = [
+ {
+ appStateKey: "chainConfig",
+ contractFunction: "getChainConfig"
+ },
+ {
+ appStateKey: "consensusState",
+ contractFunction: "getConsensusState"
+ },
+ {
+ appStateKey: "currentFrame",
+ contractFunction: "getCurrentFrame"
+ },
+ {
+ appStateKey: "fastLaneMembers",
+ contractFunction: "getFastLaneMembers"
+ },
+ {
+ appStateKey: "frameConfig",
+ contractFunction: "getFrameConfig"
+ },
+ {
+ appStateKey: "initialRefSlot",
+ contractFunction: "getInitialRefSlot"
+ },
+ {
+ appStateKey: "members",
+ contractFunction: "getMembers"
+ },
+ {
+ appStateKey: "quorum",
+ contractFunction: "getQuorum"
+ },
+ {
+ appStateKey: "reportProcessor",
+ contractFunction: "getReportProcessor"
+ },
+ {
+ appStateKey: "reportVariants",
+ contractFunction: "getReportVariants"
}
-}
+]
-function getExpectedEpochId() {
- return app.call('getExpectedEpochId').toPromise()
-}
+function initializeState() {
+ return async (cachedState) => {
-function getCurrentOraclesReportStatus() {
- return app.call('getCurrentOraclesReportStatus').toPromise()
-}
+ const lido = await app.call("getLido").toPromise()
+ const lidoContract = app.external(lido, LidoABI)
-function getCurrentReportVariantsSize() {
- return app.call('getCurrentReportVariantsSize').toPromise()
-}
-async function getCurrentReportVariant(index) {
- return app.call('getCurrentReportVariant', index).toPromise()
-}
+ const locator = await lidoContract.getLidoLocator().toPromise()
+ const locatorContract = app.external(locator, LidoLocatorABI)
-async function getCurrentReportVariants() {
- const size = await getCurrentReportVariantsSize()
- const variants = []
- for (let i = 0; i < size; i++) {
- const variant = await getCurrentReportVariant(i)
- variants.push(variant)
- }
+ const accountingOracle = await locatorContract.accountingOracle().toPromise()
+ const accountingOracleContract = app.external(accountingOracle, AccountingOracleABI)
+ const aoPromises = accountingOracleState.map(({ contractFunction }) => accountingOracleContract[contractFunction]().toPromise())
+ const settledAoPromises = await Promise.allSettled(aoPromises)
+ const aoState = settledAoPromises.reduce((stateObject, cur, index) => {
+ stateObject[accountingOracleState[index].appStateKey] = cur.value
+ return stateObject
+ }, {})
- return variants
-}
-function getLastCompletedReportDelta() {
- return app.call('getLastCompletedReportDelta').toPromise()
-}
+ const { consensusContract } = aoState
+ const hashConsensusContract = app.external(consensusContract, HashConsensusABI)
+ const hcPromises = hashConsensusState.map(({ contractFunction }) => hashConsensusContract[contractFunction]().toPromise())
+ const settledHcPromises = await Promise.allSettled(hcPromises)
+ const hcState = settledHcPromises.reduce((stateObject, cur, index) => {
+ stateObject[hashConsensusState[index].appStateKey] = cur.value
+ return stateObject
+ }, {})
-function getAllowedBeaconBalanceAnnualRelativeIncrease() {
- return app.call('getAllowedBeaconBalanceAnnualRelativeIncrease').toPromise()
-}
+ const memberPromises = hcState.members.addresses.map((memberAddress) => hashConsensusContract.getConsensusStateForMember(memberAddress).toPromise())
+ const settledMemberPromises = await Promise.allSettled(memberPromises)
-function getAllowedBeaconBalanceRelativeDecrease() {
- return app.call('getAllowedBeaconBalanceRelativeDecrease').toPromise()
-}
-function getBeaconReportReceiver() {
- return app.call('getBeaconReportReceiver').toPromise()
-}
+ hcState.memberDetails = settledMemberPromises.reduce((stateArray, cur, index) => {
+ stateArray.push({
+ ...cur.value,
+ address: hcState.members.addresses[index]
+ })
+ return stateArray
+ }, [])
-async function getVersion() {
- return app.call('getVersion').toPromise()
+ return {
+ ...cachedState,
+ lido,
+ ...aoState,
+ ...hcState
+ }
+ }
}
diff --git a/apps/lidooracle/arapp.json b/apps/lidooracle/arapp.json
index de6638c61..6529fc37d 100644
--- a/apps/lidooracle/arapp.json
+++ b/apps/lidooracle/arapp.json
@@ -1,31 +1,4 @@
{
- "roles": [
- {
- "name": "Add or remove oracle committee members",
- "id": "MANAGE_MEMBERS",
- "params": []
- },
- {
- "name": "Set the number of oracle members required to form a data point",
- "id": "MANAGE_QUORUM",
- "params": []
- },
- {
- "name": "Set the Beacon chain spec",
- "id": "SET_BEACON_SPEC",
- "params": []
- },
- {
- "name": "Set the sanity check bounds",
- "id": "SET_REPORT_BOUNDARIES",
- "params": []
- },
- {
- "name": "Set the beacon receiver contract address",
- "id": "SET_BEACON_REPORT_RECEIVER",
- "params": []
- }
- ],
"environments": {
"default": {
"appName": "oracle.lidopm.eth",
@@ -50,5 +23,5 @@
}
},
"appName": "oracle.lidopm.eth",
- "path": "../../contracts/0.4.24/oracle/LidoOracle.sol"
+ "path": "../../contracts/0.4.24/oracle/LegacyOracle.sol"
}
diff --git a/apps/lidooracle/hardhat.config.js b/apps/lidooracle/hardhat.config.js
index e2e150c56..a5240a862 100644
--- a/apps/lidooracle/hardhat.config.js
+++ b/apps/lidooracle/hardhat.config.js
@@ -1,13 +1,13 @@
require('@aragon/buidler-aragon')
const baseConfig = require('../../hardhat.config.js')
-const hooks = require('./scripts/buidler-hooks')
+const hooks = require('./scripts/buidler-hooks.js')
module.exports = {
...baseConfig,
paths: {
...baseConfig.paths,
- root: '../..'
+ root: '../..',
},
defaultNetwork: process.env.NETWORK_NAME || 'localhost',
// Aragon plugin configuration
@@ -15,9 +15,9 @@ module.exports = {
...baseConfig.aragon,
appServePort: 3011,
clientServePort: 3000,
- appSrcPath: 'apps/lidooracle/app/',
- appBuildOutputPath: 'apps/lidooracle/dist/',
- appName: 'lidooracle',
- hooks // Path to script hooks
- }
+ appSrcPath: 'apps/legacyoracle/app/',
+ appBuildOutputPath: 'apps/legacyoracle/dist/',
+ appName: 'legacyoracle',
+ hooks, // Path to script hooks
+ },
}
diff --git a/apps/lidooracle/manifest.json b/apps/lidooracle/manifest.json
index 4f976935f..6c10f3dff 100644
--- a/apps/lidooracle/manifest.json
+++ b/apps/lidooracle/manifest.json
@@ -1,16 +1,20 @@
{
"name": "Oracle",
"author": "Lido",
- "description": "An application for Aragon",
+ "description": "Aragon frontend for Lido V2 Oracle",
"details_url": "/meta/details.md",
- "source_url": "https://",
+ "source_url": "https://github.com/lidofinance/lido-dao",
"icons": [
{
"src": "/meta/icon.svg",
"sizes": "56x56"
}
],
- "screenshots": [{ "src": "/meta/screenshot-1.png" }],
+ "screenshots": [
+ {
+ "src": "/meta/screenshot-1.png"
+ }
+ ],
"start_url": "/index.html",
"script": "/script.js"
-}
+}
\ No newline at end of file
diff --git a/apps/lidooracle/scripts/buidler-hooks.js b/apps/lidooracle/scripts/buidler-hooks.js
index 20d8b2e1a..2f3f5caf2 100644
--- a/apps/lidooracle/scripts/buidler-hooks.js
+++ b/apps/lidooracle/scripts/buidler-hooks.js
@@ -31,5 +31,5 @@ module.exports = {
},
// Called after the app's proxy is updated with a new implementation.
- postUpdate: async ({ proxy, log }, { web3, artifacts }) => {}
+ postUpdate: async ({ proxy, log }, { web3, artifacts }) => {},
}
diff --git a/apps/node-operators-registry/app/package.json b/apps/node-operators-registry/app/package.json
index 9ad4e2335..1b626b0a6 100644
--- a/apps/node-operators-registry/app/package.json
+++ b/apps/node-operators-registry/app/package.json
@@ -15,25 +15,25 @@
"yup": "^0.29.3"
},
"devDependencies": {
- "@babel/core": "^7.11.6",
+ "@babel/core": "^7.21.0",
"@babel/preset-env": "^7.11.5",
"@babel/preset-react": "^7.10.1",
"babel-eslint": "^10.1.0",
"babel-plugin-styled-components": "^1.11.1",
"copyfiles": "^2.3.0",
- "eslint": "^7.9.0",
- "eslint-config-prettier": "^6.11.0",
- "eslint-config-standard": "^14.1.1",
+ "eslint": "^8.34.0",
+ "eslint-config-prettier": "^8.6.0",
+ "eslint-config-standard": "^17.0.0",
"eslint-config-standard-react": "^9.2.0",
- "eslint-plugin-import": "^2.22.0",
+ "eslint-plugin-import": "^2.27.5",
"eslint-plugin-node": "^11.1.0",
- "eslint-plugin-prettier": "^3.1.4",
- "eslint-plugin-promise": "^4.2.1",
+ "eslint-plugin-prettier": "^4.2.1",
+ "eslint-plugin-promise": "^6.1.1",
"eslint-plugin-react": "^7.20.6",
"eslint-plugin-react-hooks": "^4.1.2",
- "eslint-plugin-standard": "^4.0.1",
+ "eslint-plugin-standard": "^5.0.0",
"parcel-bundler": "^1.12.4",
- "prettier": "^2.1.2"
+ "prettier": "^2.8.4"
},
"scripts": {
"build": "yarn sync-assets && yarn build:app && yarn build:script",
diff --git a/apps/node-operators-registry/app/src/App.js b/apps/node-operators-registry/app/src/App.js
index 4ddb78a00..ca70537b2 100644
--- a/apps/node-operators-registry/app/src/App.js
+++ b/apps/node-operators-registry/app/src/App.js
@@ -1,293 +1,39 @@
-import React, { useCallback, useMemo, useState } from 'react'
+import React from 'react'
import { useAragonApi, useGuiStyle } from '@aragon/api-react'
import {
Button,
- ContextMenu,
- DataView,
Header,
- IconCheck,
- IconClose,
- IconWrite,
- IdentityBadge,
Main,
Split,
SyncIndicator,
- Toast,
useTheme,
} from '@aragon/ui'
-import AddNodeOperatorSidePanel from './components/AddNodeOperatorSidePanel'
-import AddSigningKeysSidePanel from './components/AddSigningKeysSidePanel'
-import MenuItem from './components/MenuItem'
-import IconQuestion from '@aragon/ui/dist/IconQuestion'
-import InfoBox from './components/InfoBox'
-import { getEndingBasedOnNumber } from './utils/helpers'
-import ChangeLimitPanel from './components/ChangeLimitPanel'
-import IconGroup from '@aragon/ui/dist/IconGroup'
+import { ThemeProvider } from 'styled-components'
+import { StakingRouterPrimary } from './components/stakingRouter'
+import { CuratedPrimary, CuratedSecondary } from './components/curated'
-function App() {
- const { api, appState, connectedAccount } = useAragonApi()
+const App = () => {
+ const { appState } = useAragonApi()
const { appearance } = useGuiStyle()
- const {
- nodeOperatorsCount,
- activeNodeOperatorsCount,
- nodeOperators,
- isSyncing,
- } = appState
+ const { isSyncing } = appState
- const theme = useTheme()
-
- // ADD NODE OPERATOR
- const [
- AddNodeOperatorSidePanelOpen,
- setAddNodeOperatorSidePanelOpen,
- ] = useState(false)
- const openAddNodeOperatorSidePanel = useCallback(
- () => setAddNodeOperatorSidePanelOpen(true),
- []
- )
- const closeAddNodeOperatorSidePanel = useCallback(
- () => setAddNodeOperatorSidePanelOpen(false),
- []
- )
- const addNodeOperatorApi = useCallback(
- (name, address) => api.addNodeOperator(name, address).toPromise(),
- [api]
- )
-
- // ENABLE / DISABLE
- const enableOperator = useCallback(
- (id) => {
- api.setNodeOperatorActive(id, true).toPromise()
- },
- [api]
- )
-
- const disableOperator = useCallback(
- (id) => {
- api.setNodeOperatorActive(id, false).toPromise()
- },
- [api]
- )
+ console.log(appState)
- // ADD SIGNING KEYS FOR MANAGER
- const [addSigningKeysToOperatorId, setAddSigningKeysToOperatorId] = useState(
- null
- )
- const openAddSKSidePanel = useCallback(
- (id) => setAddSigningKeysToOperatorId(id),
- []
- )
- const closeAddSKSidePanel = useCallback(
- () => setAddSigningKeysToOperatorId(null),
- []
- )
- const addSKManagerApi = useCallback(
- (quantity, pubkeys, signatures) =>
- api
- .addSigningKeys(
- addSigningKeysToOperatorId,
- quantity,
- pubkeys,
- signatures
- )
- .toPromise(),
- [api, addSigningKeysToOperatorId]
- )
-
- // ADD SIGNING KEYS FOR NodeOperator
- const currentUserOperatorId = useMemo(() => {
- const currentUserAmongOperators = nodeOperators.find(
- ({ rewardAddress }) => rewardAddress === connectedAccount
- )
- if (!currentUserAmongOperators) return -1
- return currentUserAmongOperators.id
- }, [connectedAccount, nodeOperators])
-
- // CHANGING STAKING LIMIT
- const [changeLimitOperatorId, setChangeLimitOperatorId] = useState(null)
- const openChangeLimitPanel = useCallback(
- (id) => setChangeLimitOperatorId(id),
- []
- )
- const closeChangeLimitPanel = useCallback(
- () => setChangeLimitOperatorId(null),
- []
- )
- const changeLimitApi = useCallback(
- (limit) =>
- api.setNodeOperatorStakingLimit(changeLimitOperatorId, limit).toPromise(),
- [api, changeLimitOperatorId]
- )
-
- // GET SIGNING KEYS
- const getSigningKeys = useCallback(
- (id) => {
- return api.call('getTotalSigningKeyCount', id).toPromise()
- },
- [api]
- )
-
- const getUnusedSigningKeyCount = useCallback(
- (id) => {
- return api.call('getUnusedSigningKeyCount', id).toPromise()
- },
- [api]
- )
+ const theme = useTheme()
return (
-
-
- }
- />
- [
- // eslint-disable-next-line react/jsx-key
- {id} ,
- // eslint-disable-next-line react/jsx-key
-
- {name} {currentUserOperatorId === id && '(you)'}
- ,
- // eslint-disable-next-line react/jsx-key
- ,
- // eslint-disable-next-line react/jsx-key
- {stakingLimit},
- // eslint-disable-next-line react/jsx-key
-
- {totalSigningKeys} / {usedSigningKeys} / {stoppedValidators}
- ,
- active ? (
-
- ACTIVE
-
- ) : (
-
- INACTIVE
-
- ),
- ]}
- renderEntryActions={({ name, active, id }) => (
-
- {active ? (
-
- )}
- />
- }
- secondary={
- <>
-
-
- >
- }
- />
-
-
-
+
+
+
+ } />
+
+ } secondary={} />
+
)
}
diff --git a/apps/node-operators-registry/app/src/abi/LidoLocator.abi.json b/apps/node-operators-registry/app/src/abi/LidoLocator.abi.json
new file mode 100644
index 000000000..be01706ab
--- /dev/null
+++ b/apps/node-operators-registry/app/src/abi/LidoLocator.abi.json
@@ -0,0 +1,16 @@
+[
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "stakingRouter",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ }
+]
\ No newline at end of file
diff --git a/apps/node-operators-registry/app/src/abi/StakingRouter.abi.json b/apps/node-operators-registry/app/src/abi/StakingRouter.abi.json
new file mode 100644
index 000000000..701d7b1df
--- /dev/null
+++ b/apps/node-operators-registry/app/src/abi/StakingRouter.abi.json
@@ -0,0 +1,1119 @@
+[
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "DEPOSIT_CONTRACT",
+ "outputs": [
+ {
+ "internalType": "contract IDepositContract",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "FEE_PRECISION_POINTS",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "MAX_STAKING_MODULES_COUNT",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "TOTAL_BASIS_POINTS",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "_stakingModuleId",
+ "type": "uint256"
+ }
+ ],
+ "name": "getAllNodeOperatorDigests",
+ "outputs": [
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "id",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bool",
+ "name": "isActive",
+ "type": "bool"
+ },
+ {
+ "components": [
+ {
+ "internalType": "bool",
+ "name": "isTargetLimitActive",
+ "type": "bool"
+ },
+ {
+ "internalType": "uint256",
+ "name": "targetValidatorsCount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "stuckValidatorsCount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "refundedValidatorsCount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "stuckPenaltyEndTimestamp",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "totalExitedValidators",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "totalDepositedValidators",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "depositableValidatorsCount",
+ "type": "uint256"
+ }
+ ],
+ "internalType": "struct StakingRouter.NodeOperatorSummary",
+ "name": "summary",
+ "type": "tuple"
+ }
+ ],
+ "internalType": "struct StakingRouter.NodeOperatorDigest[]",
+ "name": "",
+ "type": "tuple[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getAllStakingModuleDigests",
+ "outputs": [
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "nodeOperatorsCount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "activeNodeOperatorsCount",
+ "type": "uint256"
+ },
+ {
+ "components": [
+ {
+ "internalType": "uint24",
+ "name": "id",
+ "type": "uint24"
+ },
+ {
+ "internalType": "address",
+ "name": "stakingModuleAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "uint16",
+ "name": "stakingModuleFee",
+ "type": "uint16"
+ },
+ {
+ "internalType": "uint16",
+ "name": "treasuryFee",
+ "type": "uint16"
+ },
+ {
+ "internalType": "uint16",
+ "name": "targetShare",
+ "type": "uint16"
+ },
+ {
+ "internalType": "uint8",
+ "name": "status",
+ "type": "uint8"
+ },
+ {
+ "internalType": "string",
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "internalType": "uint64",
+ "name": "lastDepositAt",
+ "type": "uint64"
+ },
+ {
+ "internalType": "uint256",
+ "name": "lastDepositBlock",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "exitedValidatorsCount",
+ "type": "uint256"
+ }
+ ],
+ "internalType": "struct StakingRouter.StakingModule",
+ "name": "state",
+ "type": "tuple"
+ },
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "totalExitedValidators",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "totalDepositedValidators",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "depositableValidatorsCount",
+ "type": "uint256"
+ }
+ ],
+ "internalType": "struct StakingRouter.StakingModuleSummary",
+ "name": "summary",
+ "type": "tuple"
+ }
+ ],
+ "internalType": "struct StakingRouter.StakingModuleDigest[]",
+ "name": "",
+ "type": "tuple[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getContractVersion",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "_depositsCount",
+ "type": "uint256"
+ }
+ ],
+ "name": "getDepositsAllocation",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "allocated",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256[]",
+ "name": "allocations",
+ "type": "uint256[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getLido",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "_stakingModuleId",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256[]",
+ "name": "_nodeOperatorIds",
+ "type": "uint256[]"
+ }
+ ],
+ "name": "getNodeOperatorDigests",
+ "outputs": [
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "id",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bool",
+ "name": "isActive",
+ "type": "bool"
+ },
+ {
+ "components": [
+ {
+ "internalType": "bool",
+ "name": "isTargetLimitActive",
+ "type": "bool"
+ },
+ {
+ "internalType": "uint256",
+ "name": "targetValidatorsCount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "stuckValidatorsCount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "refundedValidatorsCount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "stuckPenaltyEndTimestamp",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "totalExitedValidators",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "totalDepositedValidators",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "depositableValidatorsCount",
+ "type": "uint256"
+ }
+ ],
+ "internalType": "struct StakingRouter.NodeOperatorSummary",
+ "name": "summary",
+ "type": "tuple"
+ }
+ ],
+ "internalType": "struct StakingRouter.NodeOperatorDigest[]",
+ "name": "digests",
+ "type": "tuple[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "_stakingModuleId",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "_offset",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "_limit",
+ "type": "uint256"
+ }
+ ],
+ "name": "getNodeOperatorDigests",
+ "outputs": [
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "id",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bool",
+ "name": "isActive",
+ "type": "bool"
+ },
+ {
+ "components": [
+ {
+ "internalType": "bool",
+ "name": "isTargetLimitActive",
+ "type": "bool"
+ },
+ {
+ "internalType": "uint256",
+ "name": "targetValidatorsCount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "stuckValidatorsCount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "refundedValidatorsCount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "stuckPenaltyEndTimestamp",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "totalExitedValidators",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "totalDepositedValidators",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "depositableValidatorsCount",
+ "type": "uint256"
+ }
+ ],
+ "internalType": "struct StakingRouter.NodeOperatorSummary",
+ "name": "summary",
+ "type": "tuple"
+ }
+ ],
+ "internalType": "struct StakingRouter.NodeOperatorDigest[]",
+ "name": "",
+ "type": "tuple[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "_stakingModuleId",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "_nodeOperatorId",
+ "type": "uint256"
+ }
+ ],
+ "name": "getNodeOperatorSummary",
+ "outputs": [
+ {
+ "components": [
+ {
+ "internalType": "bool",
+ "name": "isTargetLimitActive",
+ "type": "bool"
+ },
+ {
+ "internalType": "uint256",
+ "name": "targetValidatorsCount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "stuckValidatorsCount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "refundedValidatorsCount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "stuckPenaltyEndTimestamp",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "totalExitedValidators",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "totalDepositedValidators",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "depositableValidatorsCount",
+ "type": "uint256"
+ }
+ ],
+ "internalType": "struct StakingRouter.NodeOperatorSummary",
+ "name": "summary",
+ "type": "tuple"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getStakingFeeAggregateDistribution",
+ "outputs": [
+ {
+ "internalType": "uint96",
+ "name": "modulesFee",
+ "type": "uint96"
+ },
+ {
+ "internalType": "uint96",
+ "name": "treasuryFee",
+ "type": "uint96"
+ },
+ {
+ "internalType": "uint256",
+ "name": "basePrecision",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getStakingFeeAggregateDistributionE4Precision",
+ "outputs": [
+ {
+ "internalType": "uint16",
+ "name": "modulesFee",
+ "type": "uint16"
+ },
+ {
+ "internalType": "uint16",
+ "name": "treasuryFee",
+ "type": "uint16"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "_stakingModuleId",
+ "type": "uint256"
+ }
+ ],
+ "name": "getStakingModule",
+ "outputs": [
+ {
+ "components": [
+ {
+ "internalType": "uint24",
+ "name": "id",
+ "type": "uint24"
+ },
+ {
+ "internalType": "address",
+ "name": "stakingModuleAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "uint16",
+ "name": "stakingModuleFee",
+ "type": "uint16"
+ },
+ {
+ "internalType": "uint16",
+ "name": "treasuryFee",
+ "type": "uint16"
+ },
+ {
+ "internalType": "uint16",
+ "name": "targetShare",
+ "type": "uint16"
+ },
+ {
+ "internalType": "uint8",
+ "name": "status",
+ "type": "uint8"
+ },
+ {
+ "internalType": "string",
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "internalType": "uint64",
+ "name": "lastDepositAt",
+ "type": "uint64"
+ },
+ {
+ "internalType": "uint256",
+ "name": "lastDepositBlock",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "exitedValidatorsCount",
+ "type": "uint256"
+ }
+ ],
+ "internalType": "struct StakingRouter.StakingModule",
+ "name": "",
+ "type": "tuple"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "_stakingModuleId",
+ "type": "uint256"
+ }
+ ],
+ "name": "getStakingModuleActiveValidatorsCount",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "activeValidatorsCount",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256[]",
+ "name": "_stakingModuleIds",
+ "type": "uint256[]"
+ }
+ ],
+ "name": "getStakingModuleDigests",
+ "outputs": [
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "nodeOperatorsCount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "activeNodeOperatorsCount",
+ "type": "uint256"
+ },
+ {
+ "components": [
+ {
+ "internalType": "uint24",
+ "name": "id",
+ "type": "uint24"
+ },
+ {
+ "internalType": "address",
+ "name": "stakingModuleAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "uint16",
+ "name": "stakingModuleFee",
+ "type": "uint16"
+ },
+ {
+ "internalType": "uint16",
+ "name": "treasuryFee",
+ "type": "uint16"
+ },
+ {
+ "internalType": "uint16",
+ "name": "targetShare",
+ "type": "uint16"
+ },
+ {
+ "internalType": "uint8",
+ "name": "status",
+ "type": "uint8"
+ },
+ {
+ "internalType": "string",
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "internalType": "uint64",
+ "name": "lastDepositAt",
+ "type": "uint64"
+ },
+ {
+ "internalType": "uint256",
+ "name": "lastDepositBlock",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "exitedValidatorsCount",
+ "type": "uint256"
+ }
+ ],
+ "internalType": "struct StakingRouter.StakingModule",
+ "name": "state",
+ "type": "tuple"
+ },
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "totalExitedValidators",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "totalDepositedValidators",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "depositableValidatorsCount",
+ "type": "uint256"
+ }
+ ],
+ "internalType": "struct StakingRouter.StakingModuleSummary",
+ "name": "summary",
+ "type": "tuple"
+ }
+ ],
+ "internalType": "struct StakingRouter.StakingModuleDigest[]",
+ "name": "digests",
+ "type": "tuple[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getStakingModuleIds",
+ "outputs": [
+ {
+ "internalType": "uint256[]",
+ "name": "stakingModuleIds",
+ "type": "uint256[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "_stakingModuleId",
+ "type": "uint256"
+ }
+ ],
+ "name": "getStakingModuleIsActive",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "_stakingModuleId",
+ "type": "uint256"
+ }
+ ],
+ "name": "getStakingModuleIsDepositsPaused",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "_stakingModuleId",
+ "type": "uint256"
+ }
+ ],
+ "name": "getStakingModuleIsStopped",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "_stakingModuleId",
+ "type": "uint256"
+ }
+ ],
+ "name": "getStakingModuleLastDepositBlock",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "_stakingModuleId",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "_maxDepositsValue",
+ "type": "uint256"
+ }
+ ],
+ "name": "getStakingModuleMaxDepositsCount",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "_stakingModuleId",
+ "type": "uint256"
+ }
+ ],
+ "name": "getStakingModuleNonce",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "_stakingModuleId",
+ "type": "uint256"
+ }
+ ],
+ "name": "getStakingModuleStatus",
+ "outputs": [
+ {
+ "internalType": "enum StakingRouter.StakingModuleStatus",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "_stakingModuleId",
+ "type": "uint256"
+ }
+ ],
+ "name": "getStakingModuleSummary",
+ "outputs": [
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "totalExitedValidators",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "totalDepositedValidators",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "depositableValidatorsCount",
+ "type": "uint256"
+ }
+ ],
+ "internalType": "struct StakingRouter.StakingModuleSummary",
+ "name": "summary",
+ "type": "tuple"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getStakingModules",
+ "outputs": [
+ {
+ "components": [
+ {
+ "internalType": "uint24",
+ "name": "id",
+ "type": "uint24"
+ },
+ {
+ "internalType": "address",
+ "name": "stakingModuleAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "uint16",
+ "name": "stakingModuleFee",
+ "type": "uint16"
+ },
+ {
+ "internalType": "uint16",
+ "name": "treasuryFee",
+ "type": "uint16"
+ },
+ {
+ "internalType": "uint16",
+ "name": "targetShare",
+ "type": "uint16"
+ },
+ {
+ "internalType": "uint8",
+ "name": "status",
+ "type": "uint8"
+ },
+ {
+ "internalType": "string",
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "internalType": "uint64",
+ "name": "lastDepositAt",
+ "type": "uint64"
+ },
+ {
+ "internalType": "uint256",
+ "name": "lastDepositBlock",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "exitedValidatorsCount",
+ "type": "uint256"
+ }
+ ],
+ "internalType": "struct StakingRouter.StakingModule[]",
+ "name": "res",
+ "type": "tuple[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getStakingModulesCount",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getStakingRewardsDistribution",
+ "outputs": [
+ {
+ "internalType": "address[]",
+ "name": "recipients",
+ "type": "address[]"
+ },
+ {
+ "internalType": "uint256[]",
+ "name": "stakingModuleIds",
+ "type": "uint256[]"
+ },
+ {
+ "internalType": "uint96[]",
+ "name": "stakingModuleFees",
+ "type": "uint96[]"
+ },
+ {
+ "internalType": "uint96",
+ "name": "totalFee",
+ "type": "uint96"
+ },
+ {
+ "internalType": "uint256",
+ "name": "precisionPoints",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getTotalFeeE4Precision",
+ "outputs": [
+ {
+ "internalType": "uint16",
+ "name": "totalFee",
+ "type": "uint16"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getWithdrawalCredentials",
+ "outputs": [
+ {
+ "internalType": "bytes32",
+ "name": "",
+ "type": "bytes32"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ }
+]
\ No newline at end of file
diff --git a/apps/node-operators-registry/app/src/components/AddNodeOperatorSidePanel.js b/apps/node-operators-registry/app/src/components/AddNodeOperatorSidePanel.js
index 0e176627d..f705fba8b 100644
--- a/apps/node-operators-registry/app/src/components/AddNodeOperatorSidePanel.js
+++ b/apps/node-operators-registry/app/src/components/AddNodeOperatorSidePanel.js
@@ -1,5 +1,5 @@
import { Button, GU, SidePanel } from '@aragon/ui'
-import React, { useCallback } from 'react'
+import React from 'react'
import { Formik, Field } from 'formik'
import * as yup from 'yup'
import TextField from './TextField'
@@ -7,26 +7,21 @@ import TextField from './TextField'
const initialValues = {
name: '',
address: '',
- limit: '',
}
const validationSchema = yup.object().shape({
name: yup.string().required().min(1),
address: yup.string().required().min(1),
- limit: yup.number().integer().min(0).required(),
})
function PanelContent({ addNodeOperatorApi, onClose }) {
- const onSubmit = useCallback(
- ({ name, address }) => {
- addNodeOperatorApi(name, address)
- .catch(console.error)
- .finally(() => {
- onClose()
- })
- },
- [addNodeOperatorApi, onClose]
- )
+ const onSubmit = ({ name, address }) => {
+ addNodeOperatorApi(name, address)
+ .catch(console.error)
+ .finally(() => {
+ onClose()
+ })
+ }
return (
- {({ submitForm, isSubmitting }) => {
+ {({ submitForm, isSubmitting, errors, values }) => {
return (