Skip to content

Commit

Permalink
new global safe model
Browse files Browse the repository at this point in the history
  • Loading branch information
jahabeebs committed Jul 2, 2024
1 parent a3e6ebb commit 107e33b
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 15 deletions.
28 changes: 24 additions & 4 deletions src/hooks/useSafeData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,23 @@ export default function useSafeData() {
const geb = useGeb()
const tokensData = geb?.tokenList
const { account } = useActiveWeb3React()
const { safeModel: safeActions } = useStoreActions((state) => state)
const { safeModel: safeActions, globalSafeModel: globalSafeActions } = useStoreActions((state) => state)

const previousAccount = usePrevious(account)

const fetchGlobalSafes = useCallback(() => {
if (geb && tokensData) {
try {
globalSafeActions.fetchGlobalSafes({
geb,
tokensData,
})
} catch (error) {
console.debug('Failed to fetch user safes', error)
}
}
}, [geb, tokensData, globalSafeActions])

const fetchUserSafes = useCallback(() => {
if (account && geb && tokensData) {
try {
Expand All @@ -32,14 +46,20 @@ export default function useSafeData() {
// Fetch safes initially and on account or geb change
useEffect(() => {
fetchUserSafes()
fetchGlobalSafes()
const interval = setInterval(fetchUserSafes, 60000)
return () => clearInterval(interval)
}, [fetchUserSafes])
const globalInterval = setInterval(fetchGlobalSafes, 60000)
return () => {
clearInterval(interval)
clearInterval(globalInterval)
}
}, [fetchUserSafes, fetchGlobalSafes])

// Handles account changes
useEffect(() => {
if (account && previousAccount !== account) {
fetchUserSafes()
fetchGlobalSafes()
}
}, [account, previousAccount, fetchUserSafes])
}, [account, previousAccount, fetchUserSafes, fetchGlobalSafes])
}
39 changes: 39 additions & 0 deletions src/model/globalSafeModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { action, Action, thunk, Thunk } from 'easy-peasy'
import { StoreModel } from '~/model'
import { fetchGlobalSafes } from '~/services/safes'
import { timeout, ILiquidationData, ISafe, IFetchGlobalSafesPayload } from '~/utils'

export interface GlobalSafeModel {
list: Array<ISafe>
liquidationData: ILiquidationData | null
fetchGlobalSafes: Thunk<GlobalSafeModel, IFetchGlobalSafesPayload, any, StoreModel>
setList: Action<GlobalSafeModel, Array<ISafe>>
setLiquidationData: Action<GlobalSafeModel, ILiquidationData>
}

const globalSafeModel: GlobalSafeModel = {
list: [],
liquidationData: null,
fetchGlobalSafes: thunk(async (actions, payload) => {
let fetched
try {
fetched = await fetchGlobalSafes(payload)
} catch (e) {
console.debug('Failed to fetch global safes', e)
}
if (fetched) {
actions.setList(fetched.globalSafes)
actions.setLiquidationData(fetched.liquidationData)
await timeout(200)
return fetched
}
}),
setList: action((state, payload) => {
state.list = payload
}),
setLiquidationData: action((state, payload) => {
state.liquidationData = payload
}),
}

export default globalSafeModel
3 changes: 3 additions & 0 deletions src/model/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import settingsModel, { SettingsModel } from './settingsModel'
import popupsModel, { PopupsModel } from './popupsModel'
import connectWalletModel, { ConnectWalletModel } from './connectWalletModel'
import safeModel, { SafeModel } from './safeModel'
import globalSafeModel, { GlobalSafeModel } from './globalSafeModel'
import transactionsModel, { TransactionsModel } from './transactionsModel'
import multicallModel, { MulticallModel } from './multicallModel'
import auctionModel, { AuctionModel } from './auctionModel'
Expand All @@ -16,6 +17,7 @@ export interface StoreModel {
popupsModel: PopupsModel
connectWalletModel: ConnectWalletModel
safeModel: SafeModel
globalSafeModel: GlobalSafeModel
transactionsModel: TransactionsModel
multicallModel: MulticallModel
auctionModel: AuctionModel
Expand All @@ -31,6 +33,7 @@ const model: StoreModel = {
popupsModel,
connectWalletModel,
safeModel,
globalSafeModel,
transactionsModel,
multicallModel,
auctionModel,
Expand Down
25 changes: 24 additions & 1 deletion src/services/safes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { formatUserSafe, IFetchSafesPayload, IUserSafeList } from '~/utils'
import { formatUserSafe, IFetchGlobalSafesPayload, IFetchSafesPayload, IUserSafeList } from '~/utils'
import gebManager from '~/utils/gebManager'

export const fetchUserSafes = async (config: IFetchSafesPayload) => {
Expand Down Expand Up @@ -42,3 +42,26 @@ export const fetchUserSafesRaw = async (config: IFetchSafesPayload) => {

return response
}

export const fetchGlobalSafes = async (config: IFetchGlobalSafesPayload) => {
let ownerAddressesResponse = await gebManager.getGlobalSafesRpc()
if (!ownerAddressesResponse) return

let safesResponse = await gebManager.fetchSafesForOwners(config, ownerAddressesResponse.ownerAddresses)
if (!safesResponse) return

const liquidationData = {
collateralLiquidationData: safesResponse.collateralLiquidationData,
currentRedemptionPrice: safesResponse.systemState.currentRedemptionPrice.value,
currentRedemptionRate: safesResponse.systemState.currentRedemptionRate.annualizedRate,
globalDebt: safesResponse.systemState.globalDebt,
globalDebtCeiling: safesResponse.systemState.globalDebtCeiling,
perSafeDebtCeiling: safesResponse.systemState.perSafeDebtCeiling,
}

const globalSafes = formatUserSafe(safesResponse.safes, liquidationData, config.tokensData)
return {
globalSafes,
liquidationData,
}
}
39 changes: 34 additions & 5 deletions src/utils/gebManager/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import axios from 'axios'
import { BigNumber } from 'ethers'
import { Geb, utils } from '@opendollar/sdk'
import { ILiquidationResponse, IUserSafeList } from '../interfaces'

import { TokenLiquidationData, fetchLiquidationData } from '@opendollar/sdk/lib/virtual/virtualLiquidationData'
import { fetchLiquidationData, Geb, TokenLiquidationData, utils } from '@opendollar/sdk'
import { ILiquidationResponse, IUserSafeList, IOwnerAddressesResponse } from '../interfaces'
import { fetchUserSafes } from '@opendollar/sdk/lib/virtual/virtualUserSafes.js'
import { TokenData } from '@opendollar/sdk/lib/contracts/addreses'

Expand All @@ -14,6 +13,11 @@ interface UserListConfig {
safeId_not?: null
}

interface GlobalSafesConfig {
geb: Geb
tokensData: { [key: string]: TokenData }
}

// returns LiquidationData
const getLiquidationDataRpc = async (
geb: Geb,
Expand Down Expand Up @@ -98,6 +102,7 @@ const getUserSafesRpc = async (config: UserListConfig): Promise<IUserSafeList> =
safeHandler: safe.addy,
safeId: safe.id.toString(),
collateralType: safe.collateralType,
ownerAddress: config.address,
}))

return {
Expand All @@ -111,12 +116,36 @@ const getUserSafesRpc = async (config: UserListConfig): Promise<IUserSafeList> =
}
}

const getGlobalSafesRpc = async (): Promise<IOwnerAddressesResponse> => {
const response = await axios.get('https://bot.opendollar.com/api/vaults')
const ownerAddresses: string[] = Array.from(new Set(response.data.details.map((safe: any) => safe.owner)))
return { ownerAddresses }
}

const fetchSafesForOwners = async (config: GlobalSafesConfig, ownerAddresses: string[]): Promise<IUserSafeList> => {
const allSafes: any[] = []
const safePromises = ownerAddresses.map((address) => getUserSafesRpc({ ...config, address }))

const results = await Promise.all(safePromises)
results.forEach((result) => {
allSafes.push(...result.safes)
})

const liquidationData = await getLiquidationDataRpc(config.geb, config.tokensData)
return {
safes: allSafes,
erc20Balances: [],
...liquidationData,
}
}

const gebManager = {
getUserSafesRpc,
getGlobalSafesRpc,
fetchSafesForOwners,
getLiquidationDataRpc,
}

// Helper functions
export const parseWad = (val: BigNumber) => utils.wadToFixed(val).toString()
export const parseRay = (val: BigNumber) => utils.rayToFixed(val).toString()
export const parseRad = (val: BigNumber) => utils.radToFixed(val).toString()
Expand Down
20 changes: 15 additions & 5 deletions src/utils/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,13 @@ export const toFixedString = (value: string, type: keyof typeof floatsTypes = 'W
}
}

const getBytes32String = (collateralType: string, tokensData: { [key: string]: TokenData }): string | null => {
const token = Object.values(tokensData).find(
(token) => token.symbol === collateralType || token.bytes32String === collateralType
)
return token ? token.bytes32String : null
}

export const formatUserSafe = (
safes: Array<any>,
liquidationData: ILiquidationData,
Expand All @@ -141,18 +148,19 @@ export const formatUserSafe = (
const { currentRedemptionPrice, currentRedemptionRate, collateralLiquidationData } = liquidationData

return safes
.filter((s) => s.collateralType in collateralBytes32)
.map((s) => {
const token = collateralBytes32[s.collateralType]
const bytes32String = getBytes32String(s.collateralType, tokensData)
if (!bytes32String || !(bytes32String in collateralBytes32)) return null

const token = collateralBytes32[bytes32String]
const accumulatedRate = collateralLiquidationData[token]?.accumulatedRate
const currentPrice = collateralLiquidationData[token]?.currentPrice
const availableDebt = returnAvailableDebt(currentPrice?.safetyPrice, '0', s.collateral, s.debt)
const liquidationCRatio = collateralLiquidationData[token]?.liquidationCRatio
const safetyCRatio = collateralLiquidationData[token]?.safetyCRatio
const liquidationPenalty = collateralLiquidationData[token]?.liquidationPenalty
const totalAnnualizedStabilityFee = collateralLiquidationData[token]?.totalAnnualizedStabilityFee

const availableDebt = returnAvailableDebt(currentPrice?.safetyPrice, '0', s.collateral, s.debt)

const totalDebt = returnTotalValue(returnTotalDebt(s.debt, accumulatedRate) as string, '0').toString()

const liquidationPrice = getLiquidationPrice(
Expand All @@ -171,12 +179,13 @@ export const formatUserSafe = (

return {
id: s.safeId,
ownerAddress: s.ownerAddress,
safeHandler: s.safeHandler,
date: s.createdAt,
riskState: ratioChecker(Number(collateralRatio), Number(liquidationCRatio), Number(safetyCRatio)),
collateral: s.collateral,
collateralType: s.collateralType,
collateralName: collateralBytes32[s.collateralType],
collateralName: collateralBytes32[bytes32String],
debt: s.debt,
totalDebt,
availableDebt,
Expand All @@ -192,6 +201,7 @@ export const formatUserSafe = (
currentRedemptionRate: currentRedemptionRate || '0',
} as ISafe
})
.filter((s): s is ISafe => s !== null)
.sort((a, b) => Number(b.riskState) - Number(a.riskState) || Number(b.debt) - Number(a.debt))
}

Expand Down
9 changes: 9 additions & 0 deletions src/utils/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ export interface DynamicObject {
[key: string]: any
}

export interface IOwnerAddressesResponse {
ownerAddresses: string[]
}

interface IColors {
primary: string
secondary: string
Expand Down Expand Up @@ -366,6 +370,11 @@ export interface IFetchSafesPayload {
tokensData: { [key: string]: TokenData }
}

export interface IFetchGlobalSafesPayload {
geb: Geb
tokensData: { [key: string]: TokenData }
}

export interface IFetchSafeById extends IFetchSafesPayload {
safeId: string
}
Expand Down

0 comments on commit 107e33b

Please sign in to comment.