Skip to content

Commit

Permalink
Pack validator report values
Browse files Browse the repository at this point in the history
  • Loading branch information
shanejearley committed May 4, 2023
1 parent 510d280 commit 46207bd
Show file tree
Hide file tree
Showing 44 changed files with 324 additions and 1,651 deletions.
37 changes: 17 additions & 20 deletions apps/web/src/composables/ssv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { Account, Pool, ProviderString } from '@casimir/types'
import { ReadyOrStakeString } from '../interfaces'

/** Manager contract */
let casimirManager: CasimirManager
let manager: CasimirManager

export default function useSSV() {
const { ethereumURL } = useEnvironment()
Expand All @@ -20,12 +20,12 @@ export default function useSSV() {
const { getEthersTrezorSigner } = useTrezor()
const { isWalletConnectSigner, getEthersWalletConnectSigner } = useWalletConnect()

if (!casimirManager) {
casimirManager = (() => {
const address = import.meta.env.PUBLIC_CASIMIR_MANAGER
if (!manager) {
manager = (() => {
const address = import.meta.env.PUBLIC_MANAGER_ADDRESS
if (!address) console.log(
`
The PUBLIC_CASIMIR_MANAGER environment variable is empty.\n
The PUBLIC_MANAGER_ADDRESS environment variable is empty.\n
If you are on mainnet or testnet, the contract does not exist yet.\n
If you are on the local network, check your terminal logs for a contract address or errors.
`
Expand All @@ -45,20 +45,19 @@ export default function useSSV() {
const signerCreator = signerCreators[signerType as keyof typeof signerCreators]
let signer = signerCreator(walletProvider)
if (isWalletConnectSigner(signer)) signer = await signer
const casimirManagerSigner = casimirManager.connect(signer as ethers.Signer)
const fees = await casimirManagerSigner.getFees()
const managerSigner = manager.connect(signer as ethers.Signer)
const fees = await managerSigner.getFees()
const { LINK, SSV } = fees
const feesTotalPercent = LINK + SSV
const depositAmount = parseFloat(amount) * ((100 + feesTotalPercent) / 100)
const value = ethers.utils.parseEther(depositAmount.toString())
const result = await casimirManagerSigner.deposit({ value, type: 0 })
const result = await managerSigner.depositStake({ value, type: 0 })
return await result.wait()
}

async function getDepositFees() {
const provider = new ethers.providers.JsonRpcProvider(ethereumURL)
const casimirManagerProvider = casimirManager.connect(provider)
const fees = await casimirManagerProvider.getFees()
const fees = await manager.connect(provider).getFees()
const { LINK, SSV } = fees
const feesTotalPercent = LINK + SSV
const feesRounded = Math.round(feesTotalPercent * 100) / 100
Expand All @@ -67,19 +66,17 @@ export default function useSSV() {

async function getPools(address: string, readyOrStake: ReadyOrStakeString): Promise<Pool[]> {
const { user } = useUsers()
const provider = new ethers.providers.JsonRpcProvider(ethereumURL)
const casimirManagerProvider = casimirManager.connect(provider)

const userStake = await casimirManagerProvider.getUserStake(address) // to get user's stake balance
const poolStake = await casimirManagerProvider.getStake() // to get total stake balance
const poolIds = readyOrStake === 'ready' ? await casimirManagerProvider.getReadyPoolIds() : await casimirManagerProvider.getStakedPoolIds() // to get ready (open) pool IDs OR to get staked (active) pool IDs
const provider = new ethers.providers.JsonRpcProvider(ethereumURL)
const userStake = await manager.connect(provider).getUserStake(address) // to get user's stake balance
const poolStake = await manager.connect(provider).getStake() // to get total stake balance
const poolIds = readyOrStake === 'ready' ? await manager.connect(provider).getReadyPoolIds() : await manager.connect(provider).getStakedPoolIds() // to get ready (open) pool IDs OR to get staked (active) pool IDs

console.log('userStake :>> ', ethers.utils.formatEther(userStake))
console.log('poolStake :>> ', ethers.utils.formatEther(poolStake))
console.log('poolIds :>> ', poolIds)

return await Promise.all(poolIds.map(async (poolId: number) => {
const { deposits, operatorIds, validatorPublicKey } = await casimirManagerProvider.getPool(poolId)
const { deposits, operatorIds, validatorPublicKey } = await manager.connect(provider).getPoolDetails(poolId)

// TODO: Decide when/how to get rewards/userRewards
let pool: Pool = {
Expand Down Expand Up @@ -147,11 +144,11 @@ export default function useSSV() {
const signerCreator = signerCreators[signerType as keyof typeof signerCreators]
let signer = signerCreator(walletProvider)
if (isWalletConnectSigner(signer)) signer = await signer
const casimirManagerSigner = casimirManager.connect(signer as ethers.Signer)
const managerSigner = manager.connect(signer as ethers.Signer)
const value = ethers.utils.parseEther(amount)
const result = await casimirManagerSigner.withdraw(value)
const result = await managerSigner.requestWithdrawal(value)
return await result.wait()
}

return { casimirManager, deposit, getDepositFees, getPools, withdraw }
return { manager, deposit, getDepositFees, getPools, withdraw }
}
8 changes: 4 additions & 4 deletions apps/web/src/composables/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const user = ref<UserWithAccounts>()
// pools: []
// }
// )
const { casimirManager, getPools } = useSSV()
const { manager, getPools } = useSSV()

export default function useUsers () {

Expand Down Expand Up @@ -176,13 +176,13 @@ export default function useUsers () {
const provider = new ethers.providers.JsonRpcProvider(ethereumURL)

const validatorInitFilter = {
address: casimirManager.address,
address: manager.address,
topics: [
// ethers.utils.id('ManagerDistribution(address,uint256,uint256,uint256)'), // TODO: Make sure to query for past events on page load (Fetch and then subscribe),
ethers.utils.id('PoolStaked(uint32)'),
ethers.utils.id('PoolInitiated(uint32)'),
]
}
casimirManager.connect(provider).on(validatorInitFilter, async () => {
manager.connect(provider).on(validatorInitFilter, async () => {
console.log('ValidatorInit event... updating pools')
user.value.balance = await getUserBalance()
user.value.pools = await getPools(user.value.id)
Expand Down
2 changes: 0 additions & 2 deletions common/hardhat/.gitignore

This file was deleted.

8 changes: 0 additions & 8 deletions common/hardhat/hardhat.config.ts

This file was deleted.

18 changes: 0 additions & 18 deletions common/hardhat/package.json

This file was deleted.

27 changes: 0 additions & 27 deletions common/hardhat/tsconfig.json

This file was deleted.

6 changes: 3 additions & 3 deletions contracts/ethereum/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ Core internal contracts and interfaces are located in the [src](./src) directory
| Contract | Description | Docs |
| --- | --- | --- |
| [CasimirManager](./src/CasimirManager.sol) | Manages stake distribution | [docs/index.md#casimirmanager](./docs/index.md#casimirmanager) |
| [CasimirAutomation](./src/CasimirAutomation.sol) | Automates event handling | [docs/index.md#casimirautomation](./docs/index.md#casimirautomation) |
| [CasimirUpkeep](./src/CasimirUpkeep.sol) | Automates event handling | [docs/index.md#CasimirUpkeep](./docs/index.md#CasimirUpkeep) |

**Vendor Contracts:**

Expand All @@ -116,11 +116,11 @@ Mock (development-only) contracts and interfaces are located in the [src/mock](.

### Distributed Key Generation

Casimir trustlessly distributes validator key shares to operators using the [rockx-dkg-cli](https://github.com/RockX-SG/rockx-dkg-cli). The DKG server is called via [Automated Chainlink Functions](https://docs.chain.link/chainlink-functions/tutorials/automate-functions/) to generate, reshare, and exit validators.
Casimir trustlessly distributes validator key shares to operators using the [rockx-dkg-cli](https://github.com/RockX-SG/rockx-dkg-cli).

### Oracles

The contract loosely depends on two decentralized oracles. The first oracle provides a PoR feed aggregating the total of all Casimir validator balances on the Beacon chain. The second oracle automates checking balance changes, responds with relevant validator actions, and updates the contract only when necessary conditions are met (see [Chainlink PoR](https://docs.chain.link/data-feeds/proof-of-reserve) and [Chainlink Automation](https://docs.chain.link/chainlink-automation/introduction)). External requests are made using trust-minimized compute infrastructure (see [Chainlink Functions](https://docs.chain.link/chainlink-functions)).
The contract loosely depends on two decentralized oracles. The first oracle automatically syncs validator configuration, statuses, and balances when necessary conditions are met (see [Chainlink Automation](https://docs.chain.link/chainlink-automation/introduction)) by performing external requests with trust-minimized compute infrastructure (see [Chainlink Functions](https://docs.chain.link/chainlink-functions)). The second oracle watches the manager contract events, automatically executes zero-coordination distributed key generation (DKG) operations: validator key creating, resharing, and exiting (see [Chainlink Keepers](https://docs.chain.link/chainlink-keepers/introduction)) off-chain, and submits ceremony verification proofs.

## 👥 Users

Expand Down
4 changes: 2 additions & 2 deletions contracts/ethereum/docs/index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Solidity API

## CasimirAutomation
## CasimirUpkeep

### oracleHeartbeat

Expand Down Expand Up @@ -755,7 +755,7 @@ receive() external payable
_Will be removed in production
Used for mocking sweeps from Beacon to the manager_

## ICasimirAutomation
## ICasimirUpkeep

### OracleReport

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ async function deployContract(name: string, proxy?: boolean, args?: Record<strin
}
}

export { deployContract }
export { deployContract }
59 changes: 59 additions & 0 deletions contracts/ethereum/helpers/upkeep.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { ethers } from 'hardhat'
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
import { CasimirManager, CasimirUpkeep, MockFunctionsOracle } from '../build/artifacts/types'

export async function runUpkeep({
upkeep, chainlink
}: {
upkeep: CasimirUpkeep, chainlink: SignerWithAddress
}) {
let ranUpkeep = false
const checkData = ethers.utils.toUtf8Bytes('')
const { ...check } = await upkeep.connect(chainlink).checkUpkeep(checkData)
const { upkeepNeeded, performData } = check
if (upkeepNeeded) {
const performUpkeep = await upkeep.connect(chainlink).performUpkeep(performData)
await performUpkeep.wait()
ranUpkeep = true
}
return ranUpkeep
}

export async function fulfillOracleAnswer({
upkeep, chainlink, nextActiveStakeAmount, nextSweptRewardsAmount, nextSweptExitsAmount, nextDepositedCount, nextExitedCount
}: {
upkeep: CasimirUpkeep,
chainlink: SignerWithAddress,
nextActiveStakeAmount: number,
nextSweptRewardsAmount: number,
nextSweptExitsAmount: number,
nextDepositedCount: number,
nextExitedCount: number
}) {
const activeStakeAmount = ethers.utils.parseUnits(nextActiveStakeAmount.toString(), 'gwei').toString()
const sweptRewardsAmount = ethers.utils.parseUnits(nextSweptRewardsAmount.toString(), 'gwei').toString()
const sweptExitsAmount = ethers.utils.parseUnits(nextSweptExitsAmount.toString(), 'gwei').toString()
const depositedCount = nextDepositedCount.toString()
const exitedCount = nextExitedCount.toString()
const requestId = ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(['uint256'], [1]))
const packedResponse = packResponse(
activeStakeAmount,
sweptRewardsAmount,
sweptExitsAmount,
depositedCount,
exitedCount
)
const responseBytes = ethers.utils.defaultAbiCoder.encode(['uint256'], [packedResponse.toString()])
const errorBytes = ethers.utils.toUtf8Bytes('')
const mockFulfillRequest = await upkeep.connect(chainlink).mockFulfillRequest(requestId, responseBytes, errorBytes)
await mockFulfillRequest.wait()
}

function packResponse(activeStakeAmount: string, sweptRewardsAmount: string, sweptExitsAmount: string, depositedCount: string, exitedCount: string) {
let packed = ethers.BigNumber.from(activeStakeAmount)
packed = packed.or(ethers.BigNumber.from(sweptRewardsAmount).shl(64))
packed = packed.or(ethers.BigNumber.from(sweptExitsAmount).shl(128))
packed = packed.or(ethers.BigNumber.from(depositedCount).shl(192))
packed = packed.or(ethers.BigNumber.from(exitedCount).shl(224))
return packed
}
2 changes: 1 addition & 1 deletion contracts/ethereum/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"prepare": "npm run build"
},
"dependencies": {
"@chainlink/contracts": "^0.5.1",
"@chainlink/contracts": "^0.6.1",
"@openzeppelin/contracts": "^4.8.0",
"@openzeppelin/contracts-upgradeable": "^4.8.0",
"@uniswap/v3-core": "1.0.1",
Expand Down
Loading

0 comments on commit 46207bd

Please sign in to comment.