From 38e205e3c15119e06c4a04c4bdfcc2684d275b15 Mon Sep 17 00:00:00 2001 From: Jycssu Date: Sat, 9 Nov 2024 13:50:38 +0100 Subject: [PATCH] feat: include reg tokens locked inside the incentive vault --- src/hooks/useREG.ts | 12 +- src/utils/blockchain/abi/RegVaultABI.ts | 670 +++++++++++++++++++++ src/utils/blockchain/consts/otherTokens.ts | 2 + src/utils/blockchain/regVault.ts | 72 +++ 4 files changed, 755 insertions(+), 1 deletion(-) create mode 100644 src/utils/blockchain/abi/RegVaultABI.ts create mode 100644 src/utils/blockchain/regVault.ts diff --git a/src/hooks/useREG.ts b/src/hooks/useREG.ts index 1eeb9df..1e3473b 100644 --- a/src/hooks/useREG.ts +++ b/src/hooks/useREG.ts @@ -11,10 +11,12 @@ import { } from 'src/store/features/settings/settingsSelector' import { REGRealtoken } from 'src/store/features/wallets/walletsSelector' import { ERC20ABI } from 'src/utils/blockchain/abi/ERC20ABI' +import { RegVaultABI } from 'src/utils/blockchain/abi/RegVaultABI' import { DEFAULT_REG_PRICE, HoneySwapFactory_Address, REG_ContractAddress, + REG_VaultContractAddress, REG_asset_ID, REGtokenDecimals, USDConXdai_ContractAddress, @@ -27,6 +29,7 @@ import { averageValues, getUniV2AssetPrice, } from 'src/utils/blockchain/poolPrice' +import { getAddressesLockedBalances } from 'src/utils/blockchain/regVault' const getREG = async ( addressList: string[], @@ -43,11 +46,18 @@ const getREG = async ( ERC20ABI, GnosisRpcProvider, ) - const totalAmount = await getAddressesBalances( + const availableBalance = await getAddressesBalances( REG_ContractAddress, addressList, providers, ) + const lockedBalance = await getAddressesLockedBalances( + REG_VaultContractAddress, + addressList, + providers, + ) + + const totalAmount = availableBalance + lockedBalance const contractRegTotalSupply = await RegContract_Gnosis.totalSupply() const totalTokens = Number(contractRegTotalSupply) / 10 ** REGtokenDecimals const amount = totalAmount / 10 ** REGtokenDecimals diff --git a/src/utils/blockchain/abi/RegVaultABI.ts b/src/utils/blockchain/abi/RegVaultABI.ts new file mode 100644 index 0000000..710f0be --- /dev/null +++ b/src/utils/blockchain/abi/RegVaultABI.ts @@ -0,0 +1,670 @@ +export const RegVaultABI = [ + { inputs: [], stateMutability: 'nonpayable', type: 'constructor' }, + { inputs: [], name: 'AccessControlBadConfirmation', type: 'error' }, + { + inputs: [ + { internalType: 'address', name: 'account', type: 'address' }, + { internalType: 'bytes32', name: 'neededRole', type: 'bytes32' }, + ], + name: 'AccessControlUnauthorizedAccount', + type: 'error', + }, + { + inputs: [{ internalType: 'address', name: 'target', type: 'address' }], + name: 'AddressEmptyCode', + type: 'error', + }, + { + inputs: [{ internalType: 'address', name: 'account', type: 'address' }], + name: 'AddressInsufficientBalance', + type: 'error', + }, + { + inputs: [ + { internalType: 'address', name: 'implementation', type: 'address' }, + ], + name: 'ERC1967InvalidImplementation', + type: 'error', + }, + { inputs: [], name: 'ERC1967NonPayable', type: 'error' }, + { inputs: [], name: 'EnforcedPause', type: 'error' }, + { inputs: [], name: 'ExpectedPause', type: 'error' }, + { inputs: [], name: 'FailedInnerCall', type: 'error' }, + { inputs: [], name: 'InvalidInitialization', type: 'error' }, + { inputs: [], name: 'InvalidTimestampForEpoch', type: 'error' }, + { inputs: [], name: 'LockPeriodNotEnded', type: 'error' }, + { inputs: [], name: 'NotInitializing', type: 'error' }, + { inputs: [], name: 'OnlyRegGovernorAllowed', type: 'error' }, + { + inputs: [{ internalType: 'address', name: 'token', type: 'address' }], + name: 'SafeERC20FailedOperation', + type: 'error', + }, + { inputs: [], name: 'SubscriptionPeriodEnded', type: 'error' }, + { inputs: [], name: 'SubscriptionPeriodNotStarted', type: 'error' }, + { inputs: [], name: 'UUPSUnauthorizedCallContext', type: 'error' }, + { + inputs: [{ internalType: 'bytes32', name: 'slot', type: 'bytes32' }], + name: 'UUPSUnsupportedProxiableUUID', + type: 'error', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'user', type: 'address' }, + { + indexed: true, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + indexed: true, + internalType: 'uint256', + name: 'epoch', + type: 'uint256', + }, + ], + name: 'ClaimBonus', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'user', type: 'address' }, + { + indexed: true, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + indexed: true, + internalType: 'uint256', + name: 'epoch', + type: 'uint256', + }, + ], + name: 'Deposit', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint64', + name: 'version', + type: 'uint64', + }, + ], + name: 'Initialized', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'Paused', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'user', type: 'address' }, + { + indexed: true, + internalType: 'uint256', + name: 'proposalId', + type: 'uint256', + }, + { + indexed: true, + internalType: 'uint256', + name: 'epoch', + type: 'uint256', + }, + ], + name: 'RecordVote', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'user', type: 'address' }, + { + indexed: true, + internalType: 'uint256', + name: 'proposalId', + type: 'uint256', + }, + { + indexed: true, + internalType: 'uint256', + name: 'epoch', + type: 'uint256', + }, + ], + name: 'RecordVoteNotActive', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'bytes32', name: 'role', type: 'bytes32' }, + { + indexed: true, + internalType: 'bytes32', + name: 'previousAdminRole', + type: 'bytes32', + }, + { + indexed: true, + internalType: 'bytes32', + name: 'newAdminRole', + type: 'bytes32', + }, + ], + name: 'RoleAdminChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'bytes32', name: 'role', type: 'bytes32' }, + { + indexed: true, + internalType: 'address', + name: 'account', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'sender', + type: 'address', + }, + ], + name: 'RoleGranted', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'bytes32', name: 'role', type: 'bytes32' }, + { + indexed: true, + internalType: 'address', + name: 'account', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'sender', + type: 'address', + }, + ], + name: 'RoleRevoked', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint256', + name: 'subscriptionStart', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'subscriptionEnd', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'lockPeriodEnd', + type: 'uint256', + }, + { + indexed: false, + internalType: 'address', + name: 'bonusToken', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'totalBonus', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'epoch', + type: 'uint256', + }, + ], + name: 'SetNewEpoch', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'regGovernor', + type: 'address', + }, + ], + name: 'SetRegGovernor', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'regToken', + type: 'address', + }, + ], + name: 'SetRegToken', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'Unpaused', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'implementation', + type: 'address', + }, + ], + name: 'Upgraded', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'user', type: 'address' }, + { + indexed: true, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + indexed: true, + internalType: 'uint256', + name: 'epoch', + type: 'uint256', + }, + ], + name: 'Withdraw', + type: 'event', + }, + { + inputs: [], + name: 'DEFAULT_ADMIN_ROLE', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'PAUSER_ROLE', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'UPGRADER_ROLE', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'UPGRADE_INTERFACE_VERSION', + outputs: [{ internalType: 'string', name: '', type: 'string' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'user', type: 'address' }], + name: 'calculateBonus', + outputs: [ + { internalType: 'address[]', name: '', type: 'address[]' }, + { internalType: 'uint256[]', name: '', type: 'uint256[]' }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'claimBonus', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'uint256', name: 'amount', type: 'uint256' }], + name: 'deposit', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint8', name: 'v', type: 'uint8' }, + { internalType: 'bytes32', name: 'r', type: 'bytes32' }, + { internalType: 'bytes32', name: 's', type: 'bytes32' }, + ], + name: 'depositWithPermit', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'getCurrentEpoch', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getCurrentEpochState', + outputs: [ + { + components: [ + { + internalType: 'uint256', + name: 'subscriptionStart', + type: 'uint256', + }, + { internalType: 'uint256', name: 'subscriptionEnd', type: 'uint256' }, + { internalType: 'uint256', name: 'lockPeriodEnd', type: 'uint256' }, + { internalType: 'address', name: 'bonusToken', type: 'address' }, + { internalType: 'uint256', name: 'totalBonus', type: 'uint256' }, + { internalType: 'uint256', name: 'totalVotes', type: 'uint256' }, + { internalType: 'uint256', name: 'totalWeights', type: 'uint256' }, + ], + internalType: 'struct IREGIncentiveVault.EpochState', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getCurrentTotalDeposit', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'uint256', name: 'epoch', type: 'uint256' }], + name: 'getEpochState', + outputs: [ + { + components: [ + { + internalType: 'uint256', + name: 'subscriptionStart', + type: 'uint256', + }, + { internalType: 'uint256', name: 'subscriptionEnd', type: 'uint256' }, + { internalType: 'uint256', name: 'lockPeriodEnd', type: 'uint256' }, + { internalType: 'address', name: 'bonusToken', type: 'address' }, + { internalType: 'uint256', name: 'totalBonus', type: 'uint256' }, + { internalType: 'uint256', name: 'totalVotes', type: 'uint256' }, + { internalType: 'uint256', name: 'totalWeights', type: 'uint256' }, + ], + internalType: 'struct IREGIncentiveVault.EpochState', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getRegGovernor', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getRegToken', + outputs: [{ internalType: 'contract IERC20', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'bytes32', name: 'role', type: 'bytes32' }], + name: 'getRoleAdmin', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'user', type: 'address' }, + { internalType: 'uint256', name: 'epoch', type: 'uint256' }, + ], + name: 'getUserEpochState', + outputs: [ + { + components: [ + { internalType: 'uint256', name: 'depositAmount', type: 'uint256' }, + { internalType: 'uint256', name: 'voteAmount', type: 'uint256' }, + { internalType: 'bool', name: 'claimed', type: 'bool' }, + ], + internalType: 'struct IREGIncentiveVault.UserEpochState', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'user', type: 'address' }], + name: 'getUserGlobalState', + outputs: [ + { + components: [ + { internalType: 'uint256', name: 'currentDeposit', type: 'uint256' }, + { + internalType: 'uint256', + name: 'lastClaimedEpoch', + type: 'uint256', + }, + ], + internalType: 'struct IREGIncentiveVault.UserGlobalState', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'bytes32', name: 'role', type: 'bytes32' }, + { internalType: 'address', name: 'account', type: 'address' }, + ], + name: 'grantRole', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'bytes32', name: 'role', type: 'bytes32' }, + { internalType: 'address', name: 'account', type: 'address' }, + ], + name: 'hasRole', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'regGovernor', type: 'address' }, + { internalType: 'address', name: 'regToken', type: 'address' }, + { internalType: 'address', name: 'defaultAdmin', type: 'address' }, + { internalType: 'address', name: 'pauser', type: 'address' }, + { internalType: 'address', name: 'upgrader', type: 'address' }, + ], + name: 'initialize', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'pause', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'paused', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'proxiableUUID', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'user', type: 'address' }, + { internalType: 'uint256', name: 'proposalId', type: 'uint256' }, + ], + name: 'recordVote', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'bytes32', name: 'role', type: 'bytes32' }, + { internalType: 'address', name: 'callerConfirmation', type: 'address' }, + ], + name: 'renounceRole', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'bytes32', name: 'role', type: 'bytes32' }, + { internalType: 'address', name: 'account', type: 'address' }, + ], + name: 'revokeRole', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256', name: 'subscriptionStart', type: 'uint256' }, + { internalType: 'uint256', name: 'subscriptionEnd', type: 'uint256' }, + { internalType: 'uint256', name: 'lockPeriodEnd', type: 'uint256' }, + { internalType: 'address', name: 'bonusToken', type: 'address' }, + { internalType: 'uint256', name: 'totalBonus', type: 'uint256' }, + ], + name: 'setNewEpoch', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'regGovernor', type: 'address' }], + name: 'setRegGovernor', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'contract IERC20', name: 'regToken', type: 'address' }, + ], + name: 'setRegToken', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'bytes4', name: 'interfaceId', type: 'bytes4' }], + name: 'supportsInterface', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'unpause', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'newImplementation', type: 'address' }, + { internalType: 'bytes', name: 'data', type: 'bytes' }, + ], + name: 'upgradeToAndCall', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [{ internalType: 'uint256', name: 'amount', type: 'uint256' }], + name: 'withdraw', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] diff --git a/src/utils/blockchain/consts/otherTokens.ts b/src/utils/blockchain/consts/otherTokens.ts index 101e7f1..53b7f5f 100644 --- a/src/utils/blockchain/consts/otherTokens.ts +++ b/src/utils/blockchain/consts/otherTokens.ts @@ -6,6 +6,7 @@ const REGVotingPower_asset_ID = 2 const RWA_ContractAddress = '0x0675e8F4A52eA6c845CB6427Af03616a2af42170' // Gnosis/xDai, Ethereum const REG_ContractAddress = '0x0AA1e96D2a46Ec6beB2923dE1E61Addf5F5f1dce' +const REG_VaultContractAddress = '0xe1877d33471e37fe0f62d20e60c469eff83fb4a0' // Reg Voting Power only deployed on Gnosis/xDai const RegVotingPower_ContractAddress = '0x6382856a731Af535CA6aea8D364FCE67457da438' @@ -29,6 +30,7 @@ const HoneySwapFactory_Address = '0xA818b4F111Ccac7AA31D0BCc0806d64F2E0737D7' export { RWA_ContractAddress, REG_ContractAddress, + REG_VaultContractAddress, RegVotingPower_ContractAddress, WXDAI_ContractAddress, USDConXdai_ContractAddress, diff --git a/src/utils/blockchain/regVault.ts b/src/utils/blockchain/regVault.ts new file mode 100644 index 0000000..35047e3 --- /dev/null +++ b/src/utils/blockchain/regVault.ts @@ -0,0 +1,72 @@ +import { Contract, JsonRpcProvider } from 'ethers' + +import { RegVaultABI } from './abi/RegVaultABI' +import { batchCallOneContractOneFunctionMultipleParams } from './contract' + +export const getRegVaultAbiGetUserGlobalStateOnly = (): object[] | null => { + const RegVaultAbiGetUserGlobalStateOnly = RegVaultABI.find( + (abi) => abi.name === 'getUserGlobalState', + ) + if (!RegVaultAbiGetUserGlobalStateOnly) { + throw new Error('getUserGlobalState not found in RegVault ABI') + } + return [RegVaultAbiGetUserGlobalStateOnly] +} + +const getAddressesLockedBalances = async ( + contractAddress: string, + addressList: string[], + providers: JsonRpcProvider[], + consoleWarnOnError = false, +) => { + let totalAmount = 0 + try { + if (!contractAddress) { + consoleWarnOnError && console.error('Invalid contract address') + return totalAmount + } + if (!addressList?.length) { + consoleWarnOnError && console.error('Invalid address list') + return totalAmount + } + if (!providers?.length) { + consoleWarnOnError && console.error('Invalid providers') + return totalAmount + } + const regVaultAbiGetUserGlobalStateOnly = + getRegVaultAbiGetUserGlobalStateOnly() + if (!regVaultAbiGetUserGlobalStateOnly) { + throw new Error('getUserGlobalState ABI not found') + } + const statesPromises = providers.map((provider: JsonRpcProvider) => { + const RegVaultGetUserGlobalStateContract = new Contract( + contractAddress, + regVaultAbiGetUserGlobalStateOnly, + provider, + ) + const state = batchCallOneContractOneFunctionMultipleParams( + RegVaultGetUserGlobalStateContract, + 'getUserGlobalState', + addressList.map((address: string) => [address as unknown as object]), + ) + return state + }) + + const statesArray = await Promise.all(statesPromises.flat()) + const states = statesArray.flat() + // Sum all valid balances + states.forEach((state: object | null | undefined) => { + try { + if (state) { + totalAmount += Number((state as { 0: string; 1: string })['0']) + } + } catch (error) {} + }) + return totalAmount + } catch (error) { + console.warn('Failed to get balances', error) + } + return totalAmount +} + +export { getAddressesLockedBalances }