From 7b961075ef6292ec6e173c9ab13114a91c12b26a Mon Sep 17 00:00:00 2001 From: Andrey Kopylov Date: Tue, 16 Jul 2024 20:26:23 +0500 Subject: [PATCH 01/44] [for-new-stake] add contract for genesis --- src/contracts/abis/GenesisVaultDiffAbi.json | 31 +++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/contracts/abis/GenesisVaultDiffAbi.json diff --git a/src/contracts/abis/GenesisVaultDiffAbi.json b/src/contracts/abis/GenesisVaultDiffAbi.json new file mode 100644 index 00000000..f1c990be --- /dev/null +++ b/src/contracts/abis/GenesisVaultDiffAbi.json @@ -0,0 +1,31 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "osTokenShares", + "type": "uint256" + }, + { + "internalType": "address", + "name": "referrer", + "type": "address" + } + ], + "name": "depositAndMintOsToken", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + } +] From aea23f98fda257bc248378b8feccb1300519a0c6 Mon Sep 17 00:00:00 2001 From: Andrey Kopylov Date: Wed, 17 Jul 2024 13:57:24 +0500 Subject: [PATCH 02/44] [for-new-stake] improve vaultMulticall --- src/utils/getVaultContract.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/utils/getVaultContract.ts diff --git a/src/utils/getVaultContract.ts b/src/utils/getVaultContract.ts new file mode 100644 index 00000000..fd36aca1 --- /dev/null +++ b/src/utils/getVaultContract.ts @@ -0,0 +1,23 @@ +import { Network } from './enums' + + +type Input = { + network: Network + vaultAddress: string + contracts: StakeWise.Contracts +} + +const getVaultContract = (values: Input) => { + const { vaultAddress, network, contracts } = values + + const isNativeToken = [ Network.Holesky, Network.Mainnet ].includes(network) + + if (isNativeToken) { + return contracts.helpers.createVault(vaultAddress) + } + + return contracts.helpers.createOtherTokenVault(vaultAddress) +} + + +export default getVaultContract From 6689ebdc209990fa90beb1e56aa7b4279817583e Mon Sep 17 00:00:00 2001 From: Andrey Kopylov Date: Mon, 22 Jul 2024 19:11:51 +0500 Subject: [PATCH 03/44] [for-new-stake] add stake calculator --- src/contracts/abis/GenesisVaultDiffAbi.json | 56 +++++++++++++++++++++ src/utils/configs/chiado.ts | 3 ++ src/utils/configs/gnosis.ts | 3 ++ src/utils/configs/holesky.ts | 3 ++ src/utils/configs/mainnet.ts | 3 ++ 5 files changed, 68 insertions(+) diff --git a/src/contracts/abis/GenesisVaultDiffAbi.json b/src/contracts/abis/GenesisVaultDiffAbi.json index f1c990be..505b73e9 100644 --- a/src/contracts/abis/GenesisVaultDiffAbi.json +++ b/src/contracts/abis/GenesisVaultDiffAbi.json @@ -27,5 +27,61 @@ ], "stateMutability": "payable", "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "osTokenShares", + "type": "uint256" + }, + { + "internalType": "address", + "name": "referrer", + "type": "address" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "rewardsRoot", + "type": "bytes32" + }, + { + "internalType": "int160", + "name": "reward", + "type": "int160" + }, + { + "internalType": "uint160", + "name": "unlockedMevReward", + "type": "uint160" + }, + { + "internalType": "bytes32[]", + "name": "proof", + "type": "bytes32[]" + } + ], + "internalType": "struct IKeeperRewards.HarvestParams", + "name": "harvestParams", + "type": "tuple" + } + ], + "name": "updateStateAndDepositAndMintOsToken", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" } ] diff --git a/src/utils/configs/chiado.ts b/src/utils/configs/chiado.ts index 14750f6c..e8d65ed6 100644 --- a/src/utils/configs/chiado.ts +++ b/src/utils/configs/chiado.ts @@ -46,6 +46,9 @@ export default { stakeCalculator: '0x35704E96851d4aDd48475757b8f9bbb2390D9e4E', leverageStrategy: ZeroAddress, }, + helpers: { + stakeCalculator: ZeroAddress, + }, }, tokens: { swise: constants.tokens.swise, diff --git a/src/utils/configs/gnosis.ts b/src/utils/configs/gnosis.ts index 23c9f14d..1ee0d134 100644 --- a/src/utils/configs/gnosis.ts +++ b/src/utils/configs/gnosis.ts @@ -46,6 +46,9 @@ export default { stakeCalculator: '0x2A415b65207049AC7481BF69ff9fc1B3Def97c9A', leverageStrategy: ZeroAddress, }, + helpers: { + stakeCalculator: ZeroAddress, + }, }, tokens: { swise: constants.tokens.swise, diff --git a/src/utils/configs/holesky.ts b/src/utils/configs/holesky.ts index da82a369..6fe334d5 100644 --- a/src/utils/configs/holesky.ts +++ b/src/utils/configs/holesky.ts @@ -46,6 +46,9 @@ export default { stakeCalculator: '0x8381012Dad419808125F009351732af36d4e1507', leverageStrategy: '0xdB38cfc6e98a34Cdc60c568f607417E646C75B34', }, + helpers: { + stakeCalculator: '0xAc640d35448F43e03229455EF1D799a8F77321A7', + }, }, tokens: { swise: constants.tokens.swise, diff --git a/src/utils/configs/mainnet.ts b/src/utils/configs/mainnet.ts index 5597340b..9743b9bd 100644 --- a/src/utils/configs/mainnet.ts +++ b/src/utils/configs/mainnet.ts @@ -51,6 +51,9 @@ export default { stakeCalculator: '0x75c57bd50A3EB7291Da3429956D3566E0153A38f', leverageStrategy: '0x48cD14FDB8e72A03C8D952af081DBB127D6281fc', }, + helpers: { + stakeCalculator: ZeroAddress, + }, }, tokens: { swise: constants.tokens.swise, From 0dacf5f593224a099a770c17c4c63bdeb4f6b6c2 Mon Sep 17 00:00:00 2001 From: Andrey Kopylov Date: Thu, 25 Jul 2024 17:48:59 +0500 Subject: [PATCH 04/44] [for-new-stake] improve stake calculator --- src/contracts/createContracts.ts | 3 +++ src/utils/configs/chiado.ts | 2 +- src/utils/configs/gnosis.ts | 2 +- src/utils/configs/holesky.ts | 4 ++-- src/utils/configs/mainnet.ts | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/contracts/createContracts.ts b/src/contracts/createContracts.ts index 43b88b3b..d851b67b 100644 --- a/src/contracts/createContracts.ts +++ b/src/contracts/createContracts.ts @@ -172,6 +172,9 @@ export const createContracts = (input: CreateContractsInput) => { stakeCalculator: getStakeCalculator(provider, config), leverageStrategy: getLeverageStrategy(provider, config), }, + special: { + stakeCalculator: getStakeCalculator(provider, config), + }, } } diff --git a/src/utils/configs/chiado.ts b/src/utils/configs/chiado.ts index e8d65ed6..6be1a8dd 100644 --- a/src/utils/configs/chiado.ts +++ b/src/utils/configs/chiado.ts @@ -46,7 +46,7 @@ export default { stakeCalculator: '0x35704E96851d4aDd48475757b8f9bbb2390D9e4E', leverageStrategy: ZeroAddress, }, - helpers: { + special: { stakeCalculator: ZeroAddress, }, }, diff --git a/src/utils/configs/gnosis.ts b/src/utils/configs/gnosis.ts index 1ee0d134..d79aba30 100644 --- a/src/utils/configs/gnosis.ts +++ b/src/utils/configs/gnosis.ts @@ -46,7 +46,7 @@ export default { stakeCalculator: '0x2A415b65207049AC7481BF69ff9fc1B3Def97c9A', leverageStrategy: ZeroAddress, }, - helpers: { + special: { stakeCalculator: ZeroAddress, }, }, diff --git a/src/utils/configs/holesky.ts b/src/utils/configs/holesky.ts index 6fe334d5..d4347f40 100644 --- a/src/utils/configs/holesky.ts +++ b/src/utils/configs/holesky.ts @@ -46,8 +46,8 @@ export default { stakeCalculator: '0x8381012Dad419808125F009351732af36d4e1507', leverageStrategy: '0xdB38cfc6e98a34Cdc60c568f607417E646C75B34', }, - helpers: { - stakeCalculator: '0xAc640d35448F43e03229455EF1D799a8F77321A7', + special: { + stakeCalculator: '0x7151c611d7f76AFF8F53E87B3846Dc38C444dE0A', }, }, tokens: { diff --git a/src/utils/configs/mainnet.ts b/src/utils/configs/mainnet.ts index 9743b9bd..c3d0bb1c 100644 --- a/src/utils/configs/mainnet.ts +++ b/src/utils/configs/mainnet.ts @@ -51,7 +51,7 @@ export default { stakeCalculator: '0x75c57bd50A3EB7291Da3429956D3566E0153A38f', leverageStrategy: '0x48cD14FDB8e72A03C8D952af081DBB127D6281fc', }, - helpers: { + special: { stakeCalculator: ZeroAddress, }, }, From 64f9e9ecced3e605ed71c77158117e67d881f091 Mon Sep 17 00:00:00 2001 From: Andrey Kopylov Date: Fri, 26 Jul 2024 18:47:22 +0500 Subject: [PATCH 05/44] [for-new-stake] improve abis --- src/utils/configs/holesky.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/configs/holesky.ts b/src/utils/configs/holesky.ts index d4347f40..fdae004b 100644 --- a/src/utils/configs/holesky.ts +++ b/src/utils/configs/holesky.ts @@ -47,7 +47,7 @@ export default { leverageStrategy: '0xdB38cfc6e98a34Cdc60c568f607417E646C75B34', }, special: { - stakeCalculator: '0x7151c611d7f76AFF8F53E87B3846Dc38C444dE0A', + stakeCalculator: '0x6228CD90A4aB2949eb27763205dA288E23dC09d1', }, }, tokens: { From 94fcded5b1317e7e99f30106f108356182b8bbb8 Mon Sep 17 00:00:00 2001 From: Andrey Kopylov Date: Mon, 29 Jul 2024 16:05:33 +0500 Subject: [PATCH 06/44] [for-new-stake] new stakecalculator --- src/utils/configs/holesky.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/configs/holesky.ts b/src/utils/configs/holesky.ts index fdae004b..01370e1e 100644 --- a/src/utils/configs/holesky.ts +++ b/src/utils/configs/holesky.ts @@ -47,7 +47,7 @@ export default { leverageStrategy: '0xdB38cfc6e98a34Cdc60c568f607417E646C75B34', }, special: { - stakeCalculator: '0x6228CD90A4aB2949eb27763205dA288E23dC09d1', + stakeCalculator: '0x63De511Ff504E70109Bb8312d1329f2C88c14f77', }, }, tokens: { From 7bd638522f1f1ee58b0f26e6d3b4672d555e87e3 Mon Sep 17 00:00:00 2001 From: Andrey Kopylov Date: Tue, 6 Aug 2024 19:43:40 +0500 Subject: [PATCH 07/44] [for-new-stake] change address --- src/utils/configs/gnosis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/configs/gnosis.ts b/src/utils/configs/gnosis.ts index d79aba30..7fab8cdc 100644 --- a/src/utils/configs/gnosis.ts +++ b/src/utils/configs/gnosis.ts @@ -47,7 +47,7 @@ export default { leverageStrategy: ZeroAddress, }, special: { - stakeCalculator: ZeroAddress, + stakeCalculator: '0x3c5634a5437A394353F49fe04FE5db11961c5c2D', }, }, tokens: { From 2f27efb0b8fef25a359c37ddcecf7ff8d1298f8e Mon Sep 17 00:00:00 2001 From: CAst Date: Fri, 20 Sep 2024 17:29:26 +0500 Subject: [PATCH 08/44] New createVault method (#169) * [improve-abi-diff] new createVault method * [improve-abi-diff] gitignor * [improve-abi-diff] fix --- .../{ => vault}/abis/GenesisVaultDiffAbi.json | 0 src/utils/getVaultContract.ts | 23 ------------------- 2 files changed, 23 deletions(-) rename src/contracts/{ => vault}/abis/GenesisVaultDiffAbi.json (100%) delete mode 100644 src/utils/getVaultContract.ts diff --git a/src/contracts/abis/GenesisVaultDiffAbi.json b/src/contracts/vault/abis/GenesisVaultDiffAbi.json similarity index 100% rename from src/contracts/abis/GenesisVaultDiffAbi.json rename to src/contracts/vault/abis/GenesisVaultDiffAbi.json diff --git a/src/utils/getVaultContract.ts b/src/utils/getVaultContract.ts deleted file mode 100644 index fd36aca1..00000000 --- a/src/utils/getVaultContract.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Network } from './enums' - - -type Input = { - network: Network - vaultAddress: string - contracts: StakeWise.Contracts -} - -const getVaultContract = (values: Input) => { - const { vaultAddress, network, contracts } = values - - const isNativeToken = [ Network.Holesky, Network.Mainnet ].includes(network) - - if (isNativeToken) { - return contracts.helpers.createVault(vaultAddress) - } - - return contracts.helpers.createOtherTokenVault(vaultAddress) -} - - -export default getVaultContract From 1df6f87a631ea73b88755b9075677773757f01dd Mon Sep 17 00:00:00 2001 From: Mike Diamond Date: Tue, 24 Sep 2024 10:44:58 +0300 Subject: [PATCH 09/44] New subgraph (#171) * [new subgraph] update schema * [codegen ci] add fallback subgraph codegen check (#168) * [codegen ci] add fallback subgraph codegen check (#168) * [new subgraph] Update codegen * [new subgraph] Update codegen * [new subgraph] Update types * [new subgraph] Update graphql --- .../osToken/requests/getOsTokenPosition.ts | 51 +++++++++++++++++++ .../requests/{getPosition => }/getPosition.md | 0 .../{getPosition/index.ts => getPosition.ts} | 0 3 files changed, 51 insertions(+) create mode 100644 src/methods/osToken/requests/getOsTokenPosition.ts rename src/methods/osToken/requests/{getPosition => }/getPosition.md (100%) rename src/methods/osToken/requests/{getPosition/index.ts => getPosition.ts} (100%) diff --git a/src/methods/osToken/requests/getOsTokenPosition.ts b/src/methods/osToken/requests/getOsTokenPosition.ts new file mode 100644 index 00000000..64a0ce44 --- /dev/null +++ b/src/methods/osToken/requests/getOsTokenPosition.ts @@ -0,0 +1,51 @@ +import { validateArgs } from '../../../utils' +import getHealthFactor from '../helpers/getHealthFactor' +import { wrapAbortPromise } from '../../../modules/gql-module' + + +type Output = { + minted: { + assets: bigint + shares: bigint + }, + healthFactor: ReturnType + protocolFeePercent: bigint +} + +type GetOsTokenPositionInput = { + userAddress: string + vaultAddress: string + stakedAssets: bigint + thresholdPercent: bigint + contracts: StakeWise.Contracts +} + +const getOsTokenPosition = async (values: GetOsTokenPositionInput) => { + const { contracts, vaultAddress, userAddress, stakedAssets, thresholdPercent } = values + + validateArgs.address({ vaultAddress, userAddress }) + validateArgs.bigint({ stakedAssets, thresholdPercent }) + + const vaultContract = contracts.helpers.createVault({ vaultAddress }) + const mintedShares = await vaultContract.osTokenPositions(userAddress) + + const [ mintedAssets, feePercent ] = await Promise.all([ + contracts.base.mintTokenController.convertToAssets(mintedShares), + contracts.base.mintTokenController.feePercent(), + ]) + + const protocolFeePercent = feePercent / 100n + const healthFactor = getHealthFactor({ mintedAssets, stakedAssets, thresholdPercent }) + + return { + minted: { + assets: mintedAssets, + shares: mintedShares, + }, + healthFactor, + protocolFeePercent, + } +} + + +export default wrapAbortPromise(getOsTokenPosition) diff --git a/src/methods/osToken/requests/getPosition/getPosition.md b/src/methods/osToken/requests/getPosition.md similarity index 100% rename from src/methods/osToken/requests/getPosition/getPosition.md rename to src/methods/osToken/requests/getPosition.md diff --git a/src/methods/osToken/requests/getPosition/index.ts b/src/methods/osToken/requests/getPosition.ts similarity index 100% rename from src/methods/osToken/requests/getPosition/index.ts rename to src/methods/osToken/requests/getPosition.ts From 159b1b518c9b7e2e70e59c374f1f3b2822e3fee9 Mon Sep 17 00:00:00 2001 From: CAst Date: Tue, 24 Sep 2024 17:15:19 +0500 Subject: [PATCH 10/44] Subgraph tvl apy (#172) * [subgraph-tvl-ap] add getStakewiseStats * [subgraph-tvl-ap] change urls * [subgraph-tvl-apy] rename validatorsRoot and keysManager --- src/methods/utils/getStakewiseStats.ts | 23 ++++++++++++++++++ .../util/params/getDepositDataRootParams.ts | 24 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 src/methods/utils/getStakewiseStats.ts create mode 100644 src/methods/vault/transactions/util/params/getDepositDataRootParams.ts diff --git a/src/methods/utils/getStakewiseStats.ts b/src/methods/utils/getStakewiseStats.ts new file mode 100644 index 00000000..a471c7af --- /dev/null +++ b/src/methods/utils/getStakewiseStats.ts @@ -0,0 +1,23 @@ +import graphql from '../../graphql' +import { apiUrls } from '../../utils' + + +type GetStakewiseStatsInput = { + options: StakeWise.Options +} + +const getStakewiseStats = (values: GetStakewiseStatsInput) => { + const { options } = values + + return graphql.subgraph.stats.fetchStatsQuery({ + url: apiUrls.getSubgraphqlUrl(options), + modifyResult: (data) => ({ + usersCount: data.networks[0].usersCount, + totalAssets: data.networks[0].totalAssets, + totalEarnedAssets: data.networks[0].totalEarnedAssets, + }), + }) +} + + +export default getStakewiseStats diff --git a/src/methods/vault/transactions/util/params/getDepositDataRootParams.ts b/src/methods/vault/transactions/util/params/getDepositDataRootParams.ts new file mode 100644 index 00000000..3c670749 --- /dev/null +++ b/src/methods/vault/transactions/util/params/getDepositDataRootParams.ts @@ -0,0 +1,24 @@ +import { validateArgs } from '../../../../../utils' +import { vaultMulticall } from '../../../../../contracts' + + +export type SetDepositDataRootParams = { + depositDataRoot: string +} + +const getDepositDataRootParams = (values: SetDepositDataRootParams) => { + const { depositDataRoot } = values + + validateArgs.string({ depositDataRoot }) + + const params: Parameters[0]['request']['params'] = [ + { + method: 'setValidatorsRoot', args: [ depositDataRoot ], + }, + ] + + return params +} + + +export default getDepositDataRootParams From e1af47258cd8da103a6d48149c778a4d54c42a2a Mon Sep 17 00:00:00 2001 From: Mike Diamond Date: Fri, 27 Sep 2024 13:18:12 +0300 Subject: [PATCH 11/44] [get config] update get config (#177) * [get config] update get config * [get config] update get config * [get config] update get config * [get config] update get config * [get config] update get config --- src/methods/osToken/requests/getConfig.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/methods/osToken/requests/getConfig.ts diff --git a/src/methods/osToken/requests/getConfig.ts b/src/methods/osToken/requests/getConfig.ts new file mode 100644 index 00000000..48a486cb --- /dev/null +++ b/src/methods/osToken/requests/getConfig.ts @@ -0,0 +1,20 @@ +import getVault from '../../vault/requests/getVault' + + +type GetConfigInput = { + options: StakeWise.Options + vaultAddress: string +} + +const getConfig = (input: GetConfigInput) => { + const { options, vaultAddress } = input + + return getVault({ + options, + vaultAddress, + }) + .then((vault) => vault.osTokenConfig) +} + + +export default getConfig From 12f0b6db3e032668f0d9bf378782b3092e7e6218 Mon Sep 17 00:00:00 2001 From: Kadyr Dzhemaledinov Date: Fri, 27 Sep 2024 16:59:50 +0300 Subject: [PATCH 12/44] Add new methods getUserStats & getVaultStats (#176) * Improve chart view logic in V3, add getUserStats, getVaultStats. * add deprecated to README * update docs * fixed import * improves, rename methods --- .../subgraph/vault/userStatsQuery.graphql | 29 ++++ .../getUserStats/modifyUserStats.spec.ts | 132 ++++++++++++++++++ .../requests/getUserStats/modifyUserStats.ts | 70 ++++++++++ .../vault/requests/getUserStats/types.ts | 17 +++ 4 files changed, 248 insertions(+) create mode 100644 src/graphql/subgraph/vault/userStatsQuery.graphql create mode 100644 src/methods/vault/requests/getUserStats/modifyUserStats.spec.ts create mode 100644 src/methods/vault/requests/getUserStats/modifyUserStats.ts create mode 100644 src/methods/vault/requests/getUserStats/types.ts diff --git a/src/graphql/subgraph/vault/userStatsQuery.graphql b/src/graphql/subgraph/vault/userStatsQuery.graphql new file mode 100644 index 00000000..f37c0459 --- /dev/null +++ b/src/graphql/subgraph/vault/userStatsQuery.graphql @@ -0,0 +1,29 @@ +query UserStats($user: Bytes! $vaultAddress: String! $daysCount: Int!) { + allocator: allocatorStats_collection( + interval: day + where: { allocator_: { address: $user, vault: $vaultAddress } } + first: $daysCount + ) { + timestamp + earnedAssets + totalAssets + } + exitRequest: exitRequestStats_collection( + interval: day + where: { exitRequest_: { receiver: $user, vault: $vaultAddress } } + first: $daysCount + ) { + timestamp + earnedAssets + totalAssets + } + rewardSplitter: rewardSplitterShareHolderStats_collection( + interval: day + where: { rewardSpliterShareHolder_: { address: $user, vault: $vaultAddress }} + first: $daysCount + ) { + timestamp + earnedAssets + totalAssets + } +} diff --git a/src/methods/vault/requests/getUserStats/modifyUserStats.spec.ts b/src/methods/vault/requests/getUserStats/modifyUserStats.spec.ts new file mode 100644 index 00000000..ffd349fd --- /dev/null +++ b/src/methods/vault/requests/getUserStats/modifyUserStats.spec.ts @@ -0,0 +1,132 @@ +import { formatEther } from 'ethers' + +import type { ModifiedUserStats } from './types' +import modifyUserStats from './modifyUserStats' +import type { UserStatsQueryPayload } from '../../../../graphql/subgraph/vault' + + +describe('modifyUserStats function', () => { + it('should correctly modify User Stats collection', () => { + const sampleInput: UserStatsQueryPayload = { + allocator: [ + { + timestamp: '1727136000000000', + earnedAssets: '27734011365427', + totalAssets: '715748104014280751', + }, + { + timestamp: '1727049600000000', + earnedAssets: '31538735933515', + totalAssets: '715720370002915324', + }, + { + timestamp: '1726963200000000', + earnedAssets: '28263735668759', + totalAssets: '715688831266981809', + }, + ], + exitRequest: [ + { + timestamp: '1727136000000000', + earnedAssets: '27734011365427', + totalAssets: '715748104014280751', + }, + { + timestamp: '1726876800000000', + earnedAssets: '36906487302928', + totalAssets: '715660567531313050', + }, + ], + rewardSplitter: [ + { + timestamp: '1727049600000000', + earnedAssets: '31538735933515', + totalAssets: '715720370002915324', + }, + { + timestamp: '1726790400000000', + earnedAssets: '31763194717568', + totalAssets: '715623661044010122', + }, + ], + } + + const expectedResult: ModifiedUserStats = { + balance: [ + { + time: 1727136000, + value: Number(formatEther('1431496208028561600')), + }, + { + time: 1727049600, + value: Number(formatEther('1431440740005830700')), + }, + { + time: 1726963200, + value: Number(formatEther('715688831266981809')), + }, + { + time: 1726876800, + value: Number(formatEther('715660567531313050')), + }, + { + time: 1726790400, + value: Number(formatEther('715623661044010122')), + }, + ], + rewards: [ + { + time: 1727136000, + value: Number(formatEther('55468022730854')), + }, + { + time: 1727049600, + value: Number(formatEther('63077471867030')), + }, + { + time: 1726963200, + value: Number(formatEther('28263735668759')), + }, + { + time: 1726876800, + value: Number(formatEther('36906487302928')), + }, + { + time: 1726790400, + value: Number(formatEther('31763194717568')), + }, + ], + apy: [ + { + time: 1727136000, + value: ( + Number(formatEther('27734011365427')) * 365 * 100) + / (Number(formatEther('715748104014280751')) - Number(formatEther('27734011365427')) + ), + }, + { + time: 1727049600, + value: ( + Number(formatEther('31538735933515')) * 365 * 100) + / (Number(formatEther('715720370002915324')) - Number(formatEther('31538735933515')) + ), + }, + { + time: 1726963200, + value: ( + Number(formatEther('28263735668759')) * 365 * 100) + / (Number(formatEther('715688831266981809')) - Number(formatEther('28263735668759')) + ), + }, + ], + } + + const result = modifyUserStats(sampleInput) + + expect(result).toEqual({ + apy: expectedResult.apy.sort((a, b) => a.time - b.time), + balance: expectedResult.balance.sort((a, b) => a.time - b.time), + rewards: expectedResult.rewards.sort((a, b) => a.time - b.time), + }) + }) +}) diff --git a/src/methods/vault/requests/getUserStats/modifyUserStats.ts b/src/methods/vault/requests/getUserStats/modifyUserStats.ts new file mode 100644 index 00000000..50e757f1 --- /dev/null +++ b/src/methods/vault/requests/getUserStats/modifyUserStats.ts @@ -0,0 +1,70 @@ +import { formatEther } from 'ethers' + +import type { ModifiedUserStats, UserStatsMap } from './types' +import type { UserStatsQueryPayload } from '../../../../graphql/subgraph/vault' + + +const updateUserStatsMap = ( + userStatsMap: UserStatsMap, + stat: { totalAssets: string, earnedAssets: string, timestamp: string }, + includeApy = false +) => { + const timeInSeconds = Number(stat.timestamp) / 1000000 + const balance = Number(formatEther(stat.totalAssets || '0')) + const rewards = Number(formatEther(stat.earnedAssets || '0')) + const totalApy = includeApy ? (rewards * 365 * 100) / (balance - rewards) : 0 + + if (!userStatsMap.balance[stat.timestamp]) { + userStatsMap.balance[stat.timestamp] = { value: 0, time: timeInSeconds } + } + + if (!userStatsMap.rewards[stat.timestamp]) { + userStatsMap.rewards[stat.timestamp] = { value: 0, time: timeInSeconds } + } + + if (includeApy && !userStatsMap.apy[stat.timestamp]) { + userStatsMap.apy[stat.timestamp] = { value: 0, time: timeInSeconds } + } + + userStatsMap.balance[stat.timestamp].value += balance + userStatsMap.rewards[stat.timestamp].value += rewards + + if (includeApy) { + userStatsMap.apy[stat.timestamp].value += totalApy + } +} + +const modifyUserStats = (data: UserStatsQueryPayload): ModifiedUserStats => { + const allocatorStats = data?.allocator || [] + const exitRequestStats = data?.exitRequest || [] + const rewardSplitterStats = data?.rewardSplitter || [] + + const userStatsMap: UserStatsMap = { + apy: {}, + balance: {}, + rewards: {}, + } + + allocatorStats.forEach((stat) => { + updateUserStatsMap(userStatsMap, stat, true) + }) + + exitRequestStats.forEach((stat) => { + updateUserStatsMap(userStatsMap, stat) + }) + + rewardSplitterStats.forEach((stat) => { + updateUserStatsMap(userStatsMap, stat) + }) + + const result = { + apy: Object.values(userStatsMap.apy).sort((a, b) => a.time - b.time), + balance: Object.values(userStatsMap.balance).sort((a, b) => a.time - b.time), + rewards: Object.values(userStatsMap.rewards).sort((a, b) => a.time - b.time), + } + + return result +} + + +export default modifyUserStats diff --git a/src/methods/vault/requests/getUserStats/types.ts b/src/methods/vault/requests/getUserStats/types.ts new file mode 100644 index 00000000..37e80456 --- /dev/null +++ b/src/methods/vault/requests/getUserStats/types.ts @@ -0,0 +1,17 @@ +type ChartStat = { + value: number + time: number +} + +export type UserStatsMap = { + apy: Record + balance: Record + rewards: Record +} + +export type ModifiedUserStats = { + apy: ChartStat[] + balance: ChartStat[] + rewards: ChartStat[] +} + From 903aabd9ed0f39932666780034e40d69a1d52a5c Mon Sep 17 00:00:00 2001 From: Andrey Kopylov Date: Fri, 27 Sep 2024 19:06:40 +0500 Subject: [PATCH 13/44] new addresses --- src/utils/configs/holesky.ts | 2 +- src/utils/configs/mainnet.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/configs/holesky.ts b/src/utils/configs/holesky.ts index 01370e1e..5457974c 100644 --- a/src/utils/configs/holesky.ts +++ b/src/utils/configs/holesky.ts @@ -47,7 +47,7 @@ export default { leverageStrategy: '0xdB38cfc6e98a34Cdc60c568f607417E646C75B34', }, special: { - stakeCalculator: '0x63De511Ff504E70109Bb8312d1329f2C88c14f77', + stakeCalculator: '0x7455d03c7137b6597ebaccb57c13ca62b39a75a4', }, }, tokens: { diff --git a/src/utils/configs/mainnet.ts b/src/utils/configs/mainnet.ts index c3d0bb1c..464f7301 100644 --- a/src/utils/configs/mainnet.ts +++ b/src/utils/configs/mainnet.ts @@ -52,7 +52,7 @@ export default { leverageStrategy: '0x48cD14FDB8e72A03C8D952af081DBB127D6281fc', }, special: { - stakeCalculator: ZeroAddress, + stakeCalculator: '0x29c708d94521af2c88402858049bd33e4606a3a2', }, }, tokens: { From 493a36776de97ebdd0acd36d1ca53b2b2cbbcbed Mon Sep 17 00:00:00 2001 From: CAst Date: Mon, 30 Sep 2024 17:35:03 +0500 Subject: [PATCH 14/44] [new-stake-calculator] set new addresses (#178) --- src/utils/configs/holesky.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/configs/holesky.ts b/src/utils/configs/holesky.ts index 5457974c..75e0618c 100644 --- a/src/utils/configs/holesky.ts +++ b/src/utils/configs/holesky.ts @@ -47,7 +47,7 @@ export default { leverageStrategy: '0xdB38cfc6e98a34Cdc60c568f607417E646C75B34', }, special: { - stakeCalculator: '0x7455d03c7137b6597ebaccb57c13ca62b39a75a4', + stakeCalculator: '0x90b82e4b3aa385b4a02b7ebc1892a4bed6b5c465', }, }, tokens: { From ce0ed21e69f9d6e87446d25b56452f2997bc04aa Mon Sep 17 00:00:00 2001 From: Kadyr Dzhemaledinov Date: Thu, 3 Oct 2024 11:54:43 +0300 Subject: [PATCH 15/44] Add new methods getFiatRates & getUserExchangeRewards (#179) * add new methods getFiatRates & getUserExchangeRewards * remove unused * update text in docs * remove usdToDaiRate * remove unused methods, update docs * update docs * improve date in milliseconds --- src/methods/utils/getFiatRates.ts | 42 +++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/methods/utils/getFiatRates.ts diff --git a/src/methods/utils/getFiatRates.ts b/src/methods/utils/getFiatRates.ts new file mode 100644 index 00000000..84913305 --- /dev/null +++ b/src/methods/utils/getFiatRates.ts @@ -0,0 +1,42 @@ +import graphql from '../../graphql' +import { apiUrls, configs, Network } from '../../utils' + + +type GetFiatRatesInput = { + options: StakeWise.Options +} + +const getMainnetGbpRate = () => { + return graphql.subgraph.stats.fetchFiatRatesQuery({ + url: configs[Network.Mainnet].api.subgraph, + modifyResult: (data): number => { + const usdInGbp = Number(data.networks[0].usdToGbpRate) + + return usdInGbp + }, + }) +} + +const getFiatRates = (values: GetFiatRatesInput) => { + const { options } = values + + return graphql.subgraph.stats.fetchFiatRatesQuery({ + url: apiUrls.getSubgraphqlUrl(options), + modifyResult: async (data) => { + const usd = Number(data.networks[0].assetsUsdRate) + const usdInEur = Number(data.networks[0].usdToEurRate) + + const isGnosis = [ Network.Gnosis, Network.Chiado ].includes(options.network) + const usdInGbp = isGnosis ? await getMainnetGbpRate() : Number(data.networks[0].usdToGbpRate) + + return { + assetsUsdRate: usd, + usdToEurRate: usdInEur, + usdToGbpRate: usdInGbp, + } + }, + }) +} + + +export default getFiatRates From aaec048797614f66355d1115abbd9e47864453a5 Mon Sep 17 00:00:00 2001 From: Kadyr Dzhemaledinov Date: Mon, 7 Oct 2024 11:25:10 +0300 Subject: [PATCH 16/44] Replace depositDataRoot & depositDataManager (#182) * replace (depositDataRoot & depositDataManager) V1 logic from vault.operate, to (vault.setDepositDataRoot & vault.setDepositDataManager) instead * add version * rename to input * PR improves --- .../util/params/getDepositDataRootParams.ts | 24 ------------------- 1 file changed, 24 deletions(-) delete mode 100644 src/methods/vault/transactions/util/params/getDepositDataRootParams.ts diff --git a/src/methods/vault/transactions/util/params/getDepositDataRootParams.ts b/src/methods/vault/transactions/util/params/getDepositDataRootParams.ts deleted file mode 100644 index 3c670749..00000000 --- a/src/methods/vault/transactions/util/params/getDepositDataRootParams.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { validateArgs } from '../../../../../utils' -import { vaultMulticall } from '../../../../../contracts' - - -export type SetDepositDataRootParams = { - depositDataRoot: string -} - -const getDepositDataRootParams = (values: SetDepositDataRootParams) => { - const { depositDataRoot } = values - - validateArgs.string({ depositDataRoot }) - - const params: Parameters[0]['request']['params'] = [ - { - method: 'setValidatorsRoot', args: [ depositDataRoot ], - }, - ] - - return params -} - - -export default getDepositDataRootParams From a7058cc4b080803e22e533b67fb6b1c23a186a2c Mon Sep 17 00:00:00 2001 From: CAst Date: Thu, 17 Oct 2024 19:29:01 +0500 Subject: [PATCH 17/44] Boost apy (#187) * [boost-apy] set apy logic * [boost-apy] change codegen * [boost-apy] change user stats * [boost-apy] improve ltv percent logic * [boost-apy] fix config * [boost-apy] fix readme * [boost-apy] remove import --- .../osToken/boostTokenSharesQuery.graphql | 10 ++++++ src/graphql/subgraph/osToken/index.ts | 3 ++ .../subgraph/vault/userStatsQuery.graphql | 9 +++++ src/graphql/subgraph/vault/vaultQuery.graphql | 1 + .../modifyExitRequests.ts | 2 +- .../requests/getUserStats/modifyUserStats.ts | 34 +++++++++++++------ .../vault/requests/getVault/modifyVault.ts | 2 ++ src/methods/vault/requests/getVault/types.ts | 2 ++ 8 files changed, 51 insertions(+), 12 deletions(-) create mode 100644 src/graphql/subgraph/osToken/boostTokenSharesQuery.graphql diff --git a/src/graphql/subgraph/osToken/boostTokenSharesQuery.graphql b/src/graphql/subgraph/osToken/boostTokenSharesQuery.graphql new file mode 100644 index 00000000..5e02ac68 --- /dev/null +++ b/src/graphql/subgraph/osToken/boostTokenSharesQuery.graphql @@ -0,0 +1,10 @@ +query BoostTokenShares( + $vaultAddress: String! + $userAddress: Bytes! +) { + leverageStrategyPositions( + where: { vault: $vaultAddress, user: $userAddress } + ) { + osTokenShares + } +} diff --git a/src/graphql/subgraph/osToken/index.ts b/src/graphql/subgraph/osToken/index.ts index 65dd2864..834901a6 100644 --- a/src/graphql/subgraph/osToken/index.ts +++ b/src/graphql/subgraph/osToken/index.ts @@ -1,2 +1,5 @@ export { fetchOsTokenApyQuery } from './osTokenApyQuery.graphql' export type { OsTokenApyQueryPayload, OsTokenApyQueryVariables } from './osTokenApyQuery.graphql' + +export { fetchBoostTokenSharesQuery } from './boostTokenSharesQuery.graphql' +export type { BoostTokenSharesQueryPayload, BoostTokenSharesQueryVariables } from './boostTokenSharesQuery.graphql' diff --git a/src/graphql/subgraph/vault/userStatsQuery.graphql b/src/graphql/subgraph/vault/userStatsQuery.graphql index f37c0459..f701da8e 100644 --- a/src/graphql/subgraph/vault/userStatsQuery.graphql +++ b/src/graphql/subgraph/vault/userStatsQuery.graphql @@ -26,4 +26,13 @@ query UserStats($user: Bytes! $vaultAddress: String! $daysCount: Int!) { earnedAssets totalAssets } + boost: leverageStrategyPositionStats_collection( + interval: day + where: {position_: {user: $user, vault: $vaultAddress}} + first: $daysCount + ) { + timestamp + earnedAssets: earnedBoostAssets + totalAssets: totalEarnedBoostAssets + } } diff --git a/src/graphql/subgraph/vault/vaultQuery.graphql b/src/graphql/subgraph/vault/vaultQuery.graphql index 09b45a5e..32efcc04 100644 --- a/src/graphql/subgraph/vault/vaultQuery.graphql +++ b/src/graphql/subgraph/vault/vaultQuery.graphql @@ -19,6 +19,7 @@ query Vault($address: ID!) { tokenName isGenesis feePercent + maxBoostApy totalAssets isBlocklist displayName diff --git a/src/methods/vault/requests/getExitQueuePositions/modifyExitRequests.ts b/src/methods/vault/requests/getExitQueuePositions/modifyExitRequests.ts index 12d1b33d..ad845872 100644 --- a/src/methods/vault/requests/getExitQueuePositions/modifyExitRequests.ts +++ b/src/methods/vault/requests/getExitQueuePositions/modifyExitRequests.ts @@ -50,7 +50,7 @@ const modifyExitRequests = async (values: ParseExitRequestsInput): Promise { + exitRequests.forEach((exitRequest, index) => { const { timestamp, totalAssets, diff --git a/src/methods/vault/requests/getUserStats/modifyUserStats.ts b/src/methods/vault/requests/getUserStats/modifyUserStats.ts index 50e757f1..670f79ca 100644 --- a/src/methods/vault/requests/getUserStats/modifyUserStats.ts +++ b/src/methods/vault/requests/getUserStats/modifyUserStats.ts @@ -6,13 +6,20 @@ import type { UserStatsQueryPayload } from '../../../../graphql/subgraph/vault' const updateUserStatsMap = ( userStatsMap: UserStatsMap, - stat: { totalAssets: string, earnedAssets: string, timestamp: string }, + stat: { + earnedAssets: string + totalAssets: string + timestamp: string + }, includeApy = false ) => { - const timeInSeconds = Number(stat.timestamp) / 1000000 - const balance = Number(formatEther(stat.totalAssets || '0')) - const rewards = Number(formatEther(stat.earnedAssets || '0')) - const totalApy = includeApy ? (rewards * 365 * 100) / (balance - rewards) : 0 + const timeInSeconds = Number(stat.timestamp) / 1_000_000 + const balance = Number(formatEther(stat.totalAssets || 0n)) + const rewards = Number(formatEther(stat.earnedAssets || 0n)) + + const totalApy = includeApy + ? (rewards * 365 * 100) / (balance - rewards) + : 0 if (!userStatsMap.balance[stat.timestamp]) { userStatsMap.balance[stat.timestamp] = { value: 0, time: timeInSeconds } @@ -35,6 +42,7 @@ const updateUserStatsMap = ( } const modifyUserStats = (data: UserStatsQueryPayload): ModifiedUserStats => { + const boostStats = data?.boost || [] const allocatorStats = data?.allocator || [] const exitRequestStats = data?.exitRequest || [] const rewardSplitterStats = data?.rewardSplitter || [] @@ -45,16 +53,20 @@ const modifyUserStats = (data: UserStatsQueryPayload): ModifiedUserStats => { rewards: {}, } - allocatorStats.forEach((stat) => { - updateUserStatsMap(userStatsMap, stat, true) + boostStats.forEach((stats) => { + updateUserStatsMap(userStatsMap, stats, true) + }) + + allocatorStats.forEach((stats) => { + updateUserStatsMap(userStatsMap, stats, true) }) - exitRequestStats.forEach((stat) => { - updateUserStatsMap(userStatsMap, stat) + rewardSplitterStats.forEach((stats) => { + updateUserStatsMap(userStatsMap, stats, true) }) - rewardSplitterStats.forEach((stat) => { - updateUserStatsMap(userStatsMap, stat) + exitRequestStats.forEach((stats) => { + updateUserStatsMap(userStatsMap, stats) }) const result = { diff --git a/src/methods/vault/requests/getVault/modifyVault.ts b/src/methods/vault/requests/getVault/modifyVault.ts index c385af3c..347feabb 100644 --- a/src/methods/vault/requests/getVault/modifyVault.ts +++ b/src/methods/vault/requests/getVault/modifyVault.ts @@ -26,6 +26,7 @@ const modifyVault = (input: ModifyVaultInput): ModifiedVault => { mevEscrow, createdAt, feePercent, + maxBoostApy, performance, totalAssets, whitelister, @@ -47,6 +48,7 @@ const modifyVault = (input: ModifyVaultInput): ModifiedVault => { isSmoothingPool: !mevEscrow, feePercent: feePercent / 100, vaultAdmin: getAddress(admin), + maxBoostApy: Number(maxBoostApy), performance: Number(performance), vaultAddress: getAddress(address), createdAt: Number(createdAt) * 1000, diff --git a/src/methods/vault/requests/getVault/types.ts b/src/methods/vault/requests/getVault/types.ts index bcfa34d3..bc4d7034 100644 --- a/src/methods/vault/requests/getVault/types.ts +++ b/src/methods/vault/requests/getVault/types.ts @@ -9,6 +9,7 @@ export type ModifiedVault = Omit< | 'version' | 'createdAt' | 'mevEscrow' + | 'maxBoostApy' | 'performance' | 'whitelister' | 'osTokenConfig' @@ -21,6 +22,7 @@ export type ModifiedVault = Omit< version: number createdAt: number vaultAdmin: string + maxBoostApy: number performance: number vaultAddress: string mevRecipient: string From f6be2a07047d429896cbc0f0f0f20345dc06764c Mon Sep 17 00:00:00 2001 From: Mike Diamond Date: Wed, 23 Oct 2024 16:37:56 +0300 Subject: [PATCH 18/44] Boost methods (#192) * remove duplicates (#186) * refactor checkRestakeOperatorsManagerAccess * update subgraphUrl logic * [boost ui] add boost multicall to sdk * [boost ui] add boost to sdk * [boost ui] add boost to sdk * [boost ui] update types * [boost ui] add boost methods * [boost ui] add boost methods * [boost ui] add boost methods * [boost ui] add boost methods * [boost ui] update type * [boost ui] update type * [boost ui] update permit signature --------- Co-authored-by: Kadyr Dzhemaledinov --- .../requests/getLeverageStrategyProxy.ts | 17 +++ .../osToken/transactions/boost/boostEncode.ts | 19 +++ .../osToken/transactions/boost/boostGas.ts | 24 ++++ .../osToken/transactions/boost/common.ts | 111 ++++++++++++++++++ .../osToken/transactions/boost/index.ts | 20 ++++ .../osToken/transactions/boost/types.d.ts | 29 +++++ src/methods/utils/getPermitSignature.ts | 74 ++++++++++++ src/utils/configs/holesky.ts | 1 + src/utils/configs/mainnet.ts | 1 + 9 files changed, 296 insertions(+) create mode 100644 src/methods/osToken/requests/getLeverageStrategyProxy.ts create mode 100644 src/methods/osToken/transactions/boost/boostEncode.ts create mode 100644 src/methods/osToken/transactions/boost/boostGas.ts create mode 100644 src/methods/osToken/transactions/boost/common.ts create mode 100644 src/methods/osToken/transactions/boost/index.ts create mode 100644 src/methods/osToken/transactions/boost/types.d.ts create mode 100644 src/methods/utils/getPermitSignature.ts diff --git a/src/methods/osToken/requests/getLeverageStrategyProxy.ts b/src/methods/osToken/requests/getLeverageStrategyProxy.ts new file mode 100644 index 00000000..101bc0da --- /dev/null +++ b/src/methods/osToken/requests/getLeverageStrategyProxy.ts @@ -0,0 +1,17 @@ +import { wrapAbortPromise } from '../../../modules/gql-module' + + +type GetLeverageStrategyProxyInput = { + contracts: StakeWise.Contracts + userAddress: string + vaultAddress: string +} + +const getLeverageStrategyProxy = (values: GetLeverageStrategyProxyInput) => { + const { contracts, userAddress, vaultAddress } = values + + return contracts.special.leverageStrategy.getStrategyProxy(vaultAddress, userAddress) +} + + +export default wrapAbortPromise(getLeverageStrategyProxy) diff --git a/src/methods/osToken/transactions/boost/boostEncode.ts b/src/methods/osToken/transactions/boost/boostEncode.ts new file mode 100644 index 00000000..0e2a9eef --- /dev/null +++ b/src/methods/osToken/transactions/boost/boostEncode.ts @@ -0,0 +1,19 @@ +import { BoostInput } from './types' +import { commonLogic } from './common' +import { boostMulticall } from '../../../../contracts' + + +const boostEncode = async (values: BoostInput): Promise => { + const multicallArgs = await commonLogic(values) + + return boostMulticall<{ data: string, to: string }>({ + ...multicallArgs, + request: { + ...multicallArgs.request, + transactionData: true, + }, + }) +} + + +export default boostEncode diff --git a/src/methods/osToken/transactions/boost/boostGas.ts b/src/methods/osToken/transactions/boost/boostGas.ts new file mode 100644 index 00000000..5848775c --- /dev/null +++ b/src/methods/osToken/transactions/boost/boostGas.ts @@ -0,0 +1,24 @@ +import type { BoostInput } from './types' +import { getGas } from '../../../../utils' +import { commonLogic } from './common' +import { boostMulticall } from '../../../../contracts' + + +const boostGas = async (values: BoostInput) => { + const { provider } = values + + const multicallArgs = await commonLogic({ ...values, mockPermitSignature: true }) + + const estimatedGas = await boostMulticall({ + ...multicallArgs, + request: { + ...multicallArgs.request, + estimateGas: true, + }, + }) + + return getGas({ estimatedGas, provider }) +} + + +export default boostGas diff --git a/src/methods/osToken/transactions/boost/common.ts b/src/methods/osToken/transactions/boost/common.ts new file mode 100644 index 00000000..30a1cb3a --- /dev/null +++ b/src/methods/osToken/transactions/boost/common.ts @@ -0,0 +1,111 @@ +import type { BoostInput } from './types' +import { validateArgs } from '../../../../utils' +import { boostMulticall } from '../../../../contracts' +import { getPermitSignature } from '../../../utils' +import getLeverageStrategyProxy from '../../requests/getLeverageStrategyProxy' + + +type CommonLogicInput = BoostInput & { + mockPermitSignature?: boolean +} + +export const commonLogic = async (values: CommonLogicInput) => { + const { + contracts, options, provider, amount, vaultAddress, userAddress, strategyProxy, + permitParams, mockPermitSignature, + } = values + + validateArgs.bigint({ amount }) + validateArgs.address({ vaultAddress, userAddress, strategyProxy }) + + if (permitParams) { + validateArgs.object({ permitParams }) + + const { vault, amount, deadline, v, r, s } = permitParams + + validateArgs.address({ 'permitParams.vault': vault }) + validateArgs.bigint({ 'permitParams.amount': amount }) + validateArgs.string({ 'permitParams.r': r, 'permitParams.s': s }) + validateArgs.number({ 'permitParams.v': v, 'permitParams.deadline': deadline }) + } + + const multicallArgs: Omit[0], 'request'> = { + leverageStrategyContract: contracts.special.leverageStrategy, + vaultAddress, + userAddress, + options, + } + + const params: Parameters[0]['request']['params'] = [] + + if (permitParams) { + const { vault, amount, deadline, v, r, s } = permitParams + + params.push({ + method: 'permit', + args: [ + vault, + amount, + deadline, + v, + r, + s, + ], + }) + } + else { + const strategyProxy = await getLeverageStrategyProxy({ + contracts, + userAddress, + vaultAddress, + }) + + const allowance = await contracts.tokens.mintToken.allowance(userAddress, strategyProxy) + const isPermitRequired = allowance < amount + + if (isPermitRequired) { + if (mockPermitSignature) { + params.push({ + method: 'permit', + args: [ + vaultAddress, + amount, + ], + }) + } + else { + const permitParams = await getPermitSignature({ + options, + provider, + contract: contracts.tokens.mintToken, + ownerAddress: userAddress, + spenderAddress: strategyProxy, + }) + + params.push({ + method: 'permit', + args: [ + vaultAddress, + permitParams.amount, + permitParams.deadline, + permitParams.v, + permitParams.r, + permitParams.s, + ], + }) + } + } + } + + params.push({ + method: 'deposit', + args: [ vaultAddress, amount ], + }) + + return { + ...multicallArgs, + request: { + params, + }, + } +} diff --git a/src/methods/osToken/transactions/boost/index.ts b/src/methods/osToken/transactions/boost/index.ts new file mode 100644 index 00000000..97dfb16c --- /dev/null +++ b/src/methods/osToken/transactions/boost/index.ts @@ -0,0 +1,20 @@ +import boostGas from './boostGas' +import boostEncode from './boostEncode' +import type { Boost } from './types' +import { commonLogic } from './common' +import { boostMulticall } from '../../../../contracts' + + +const boost: Boost = async (values) => { + const multicallArgs = await commonLogic(values) + + const result = await boostMulticall<{ hash: string }>(multicallArgs) + + return result.hash +} + +boost.encode = boostEncode +boost.estimateGas = boostGas + + +export default boost diff --git a/src/methods/osToken/transactions/boost/types.d.ts b/src/methods/osToken/transactions/boost/types.d.ts new file mode 100644 index 00000000..ca334cbc --- /dev/null +++ b/src/methods/osToken/transactions/boost/types.d.ts @@ -0,0 +1,29 @@ +import boostGas from './boostGas' +import burnEncode from './burnEncode' + + +type PermitParams = { + vault: string + amount: bigint + deadline: number + v: number + r: string + s: string +} + +export type BoostInput = { + amount: bigint + userAddress: string + vaultAddress: string + strategyProxy: string + permitParams?: PermitParams + options: StakeWise.Options + provider: StakeWise.Provider + contracts: StakeWise.Contracts +} + +export interface Boost { + (values: BoostInput): Promise + estimateGas: typeof boostGas + encode: typeof burnEncode +} diff --git a/src/methods/utils/getPermitSignature.ts b/src/methods/utils/getPermitSignature.ts new file mode 100644 index 00000000..cfa595a7 --- /dev/null +++ b/src/methods/utils/getPermitSignature.ts @@ -0,0 +1,74 @@ +import { Signature } from 'ethers' + +import { constants } from '../../utils' + + +const maxUint256 = constants.blockchain.maxUint256 + +type GetPermitSignatureInput = { + options: StakeWise.Options + provider: StakeWise.Provider + contract: StakeWise.ABI.Erc20Token + amount?: bigint + ownerAddress: string + spenderAddress: string +} + +const getPermitSignature = async (values: GetPermitSignatureInput) => { + const { options, provider, amount = maxUint256, contract, ownerAddress, spenderAddress } = values + + const currentTimestamp = Number((Date.now() / 1000).toFixed(0)) + const deadline = currentTimestamp + 3600 // + 1 hour + + const [ tokenName, mintTokenAddress, tokenNonce ] = await Promise.all([ + contract.name(), + contract.getAddress(), + contract.nonces(ownerAddress), + ]) + + const data = JSON.stringify({ + primaryType: 'Permit', + types: { + EIP712Domain: [ + { name: 'name', type: 'string' }, + { name: 'version', type: 'string' }, + { name: 'chainId', type: 'uint256' }, + { name: 'verifyingContract', type: 'address' }, + ], + Permit: [ + { name: 'owner', type: 'address' }, + { name: 'spender', type: 'address' }, + { name: 'value', type: 'uint256' }, + { name: 'nonce', type: 'uint256' }, + { name: 'deadline', type: 'uint256' }, + ], + }, + domain: { + name: tokenName, + version: '1', + chainId: options.network, + verifyingContract: mintTokenAddress, + }, + message: { + deadline, + owner: ownerAddress, + nonce: tokenNonce.toString(), + spender: spenderAddress, + value: amount, + }, + }) + + const signature = await provider.send('eth_signTypedData_v4', [ ownerAddress, data ]) + const { v, r, s } = await Signature.from(signature) + + return { + amount, + deadline, + v, + r, + s, + } +} + + +export default getPermitSignature diff --git a/src/utils/configs/holesky.ts b/src/utils/configs/holesky.ts index 75e0618c..5c69c9e2 100644 --- a/src/utils/configs/holesky.ts +++ b/src/utils/configs/holesky.ts @@ -48,6 +48,7 @@ export default { }, special: { stakeCalculator: '0x90b82e4b3aa385b4a02b7ebc1892a4bed6b5c465', + leverageStrategy: '0x3a0ba86467a58942e052370297c8008bb0e4be3e', }, }, tokens: { diff --git a/src/utils/configs/mainnet.ts b/src/utils/configs/mainnet.ts index 464f7301..980940b8 100644 --- a/src/utils/configs/mainnet.ts +++ b/src/utils/configs/mainnet.ts @@ -53,6 +53,7 @@ export default { }, special: { stakeCalculator: '0x29c708d94521af2c88402858049bd33e4606a3a2', + leverageStrategy: ZeroAddress, }, }, tokens: { From 57614d4e37f914724e8404cbf12c02de1e71395e Mon Sep 17 00:00:00 2001 From: CAst Date: Fri, 25 Oct 2024 14:01:26 +0500 Subject: [PATCH 19/44] Boost part 3 (#195) * Boost apy (#187) * [boost-apy] set apy logic * [boost-apy] change codegen * [boost-apy] change user stats * [boost-apy] improve ltv percent logic * [boost-apy] fix config * [boost-apy] fix readme * [boost-apy] remove import * [boost-part-2] remove boost percent (#189) * [boost ui] update boost data (#188) * remove duplicates (#186) (#191) * refactor checkRestakeOperatorsManagerAccess * update subgraphUrl logic * Boost methods (#192) * remove duplicates (#186) * refactor checkRestakeOperatorsManagerAccess * update subgraphUrl logic * [boost ui] add boost multicall to sdk * [boost ui] add boost to sdk * [boost ui] add boost to sdk * [boost ui] update types * [boost ui] add boost methods * [boost ui] add boost methods * [boost ui] add boost methods * [boost ui] add boost methods * [boost ui] update type * [boost ui] update type * [boost ui] update permit signature --------- Co-authored-by: Kadyr Dzhemaledinov * [boost-part-3] add boost method * [boost-part-3] fixes --------- Co-authored-by: Mike Diamond Co-authored-by: Kadyr Dzhemaledinov --- .../osToken/boostTokenSharesQuery.graphql | 24 +++++- src/methods/vault/requests/getBoost.ts | 74 +++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 src/methods/vault/requests/getBoost.ts diff --git a/src/graphql/subgraph/osToken/boostTokenSharesQuery.graphql b/src/graphql/subgraph/osToken/boostTokenSharesQuery.graphql index 5e02ac68..5ba8bd4b 100644 --- a/src/graphql/subgraph/osToken/boostTokenSharesQuery.graphql +++ b/src/graphql/subgraph/osToken/boostTokenSharesQuery.graphql @@ -3,8 +3,30 @@ query BoostTokenShares( $userAddress: Bytes! ) { leverageStrategyPositions( - where: { vault: $vaultAddress, user: $userAddress } + where: { + vault: $vaultAddress, + user: $userAddress + } ) { osTokenShares } + vaults( + where: { + addressString: $vaultAddress, + } + ) { + osTokenConfig { + ltvPercent + } + maxBoostApy + apy + } + allocators( + where: { + address: $userAddress, + vault: $vaultAddress + } + ) { + assets + } } diff --git a/src/methods/vault/requests/getBoost.ts b/src/methods/vault/requests/getBoost.ts new file mode 100644 index 00000000..5a2a68b0 --- /dev/null +++ b/src/methods/vault/requests/getBoost.ts @@ -0,0 +1,74 @@ +import graphql from '../../../graphql' +import { wrapAbortPromise } from '../../../modules/gql-module' +import { validateArgs, apiUrls, BigDecimal, Network, constants } from '../../../utils' + + +type GetBoostInput = { + userAddress: string + vaultAddress: string + options: StakeWise.Options + contracts: StakeWise.Contracts +} + +type Output = { + shares: bigint + percent: number + maxMintShares: bigint + isProfitable: boolean +} + +const getBoost = async (values: GetBoostInput) => { + const { contracts, options, vaultAddress, userAddress } = values + + validateArgs.address({ vaultAddress, userAddress }) + + const boost: Output = { + shares: 0n, + percent: 0, + maxMintShares: 0n, + isProfitable: false, + } + + if ([ Network.Mainnet, Network.Holesky ].includes(options.network)) { + const response = await graphql.subgraph.osToken.fetchBoostTokenSharesQuery({ + url: apiUrls.getSubgraphqlUrl(options), + variables: { + vaultAddress, + userAddress, + }, + }) + + const { leverageStrategyPositions, vaults, allocators } = response + + if (!vaults.length) { + return boost + } + + const { apy, maxBoostApy, osTokenConfig } = vaults[0] + + const stakedAssets = BigInt(allocators[0]?.assets || 0) + const ltvPercent = BigInt(osTokenConfig.ltvPercent || 0) + const boostShares = BigInt(leverageStrategyPositions[0]?.osTokenShares || 0) + + const maxMintAssets = stakedAssets * ltvPercent / constants.blockchain.amount1 + const maxMintShares = await contracts.base.mintTokenController.convertToShares(maxMintAssets) + + const boostPercent = maxMintShares ? ( + new BigDecimal(boostShares) + .multiply(100) + .divide(maxMintShares) + .decimals(2) + .toNumber() + ) : 0 + + boost.shares = boostShares + boost.percent = boostPercent + boost.maxMintShares = maxMintShares + boost.isProfitable = maxBoostApy > apy + } + + return boost +} + + +export default wrapAbortPromise(getBoost) From 32fc377dbe962ef8e96c31400dc1fcffec5bdea2 Mon Sep 17 00:00:00 2001 From: Mike Diamond Date: Mon, 28 Oct 2024 14:10:23 +0300 Subject: [PATCH 20/44] Unboost (#197) * [unboost ui] update addresses * [unboost ui] update boost action * [unboost ui] update boost action --- .../osToken/boostTokenSharesQuery.graphql | 2 ++ src/methods/vault/requests/getBoost.ts | 15 ++++++++++++--- src/utils/configs/holesky.ts | 2 +- src/utils/configs/mainnet.ts | 2 +- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/graphql/subgraph/osToken/boostTokenSharesQuery.graphql b/src/graphql/subgraph/osToken/boostTokenSharesQuery.graphql index 5ba8bd4b..428b047b 100644 --- a/src/graphql/subgraph/osToken/boostTokenSharesQuery.graphql +++ b/src/graphql/subgraph/osToken/boostTokenSharesQuery.graphql @@ -8,7 +8,9 @@ query BoostTokenShares( user: $userAddress } ) { + assets osTokenShares + exitingPercent } vaults( where: { diff --git a/src/methods/vault/requests/getBoost.ts b/src/methods/vault/requests/getBoost.ts index 5a2a68b0..67366447 100644 --- a/src/methods/vault/requests/getBoost.ts +++ b/src/methods/vault/requests/getBoost.ts @@ -12,7 +12,9 @@ type GetBoostInput = { type Output = { shares: bigint + assets: bigint percent: number + exitingPercent: number maxMintShares: bigint isProfitable: boolean } @@ -24,7 +26,9 @@ const getBoost = async (values: GetBoostInput) => { const boost: Output = { shares: 0n, + assets: 0n, percent: 0, + exitingPercent: 0, maxMintShares: 0n, isProfitable: false, } @@ -33,8 +37,8 @@ const getBoost = async (values: GetBoostInput) => { const response = await graphql.subgraph.osToken.fetchBoostTokenSharesQuery({ url: apiUrls.getSubgraphqlUrl(options), variables: { - vaultAddress, - userAddress, + userAddress: userAddress.toLowerCase(), + vaultAddress: vaultAddress.toLowerCase(), }, }) @@ -46,9 +50,12 @@ const getBoost = async (values: GetBoostInput) => { const { apy, maxBoostApy, osTokenConfig } = vaults[0] + const leverageStrategyPosition = leverageStrategyPositions[0] const stakedAssets = BigInt(allocators[0]?.assets || 0) const ltvPercent = BigInt(osTokenConfig.ltvPercent || 0) - const boostShares = BigInt(leverageStrategyPositions[0]?.osTokenShares || 0) + const boostShares = BigInt(leverageStrategyPosition?.osTokenShares || 0) + const boostAssets = BigInt(leverageStrategyPosition?.assets || 0) + const exitingPercent = Number(leverageStrategyPosition?.exitingPercent || 0) const maxMintAssets = stakedAssets * ltvPercent / constants.blockchain.amount1 const maxMintShares = await contracts.base.mintTokenController.convertToShares(maxMintAssets) @@ -62,7 +69,9 @@ const getBoost = async (values: GetBoostInput) => { ) : 0 boost.shares = boostShares + boost.assets = boostAssets boost.percent = boostPercent + boost.exitingPercent = exitingPercent boost.maxMintShares = maxMintShares boost.isProfitable = maxBoostApy > apy } diff --git a/src/utils/configs/holesky.ts b/src/utils/configs/holesky.ts index 5c69c9e2..1fc38d87 100644 --- a/src/utils/configs/holesky.ts +++ b/src/utils/configs/holesky.ts @@ -48,7 +48,7 @@ export default { }, special: { stakeCalculator: '0x90b82e4b3aa385b4a02b7ebc1892a4bed6b5c465', - leverageStrategy: '0x3a0ba86467a58942e052370297c8008bb0e4be3e', + leverageStrategy: '0xCC7657981B288f811f099235fc9E96A3b77Bca0C', }, }, tokens: { diff --git a/src/utils/configs/mainnet.ts b/src/utils/configs/mainnet.ts index 980940b8..deafb72c 100644 --- a/src/utils/configs/mainnet.ts +++ b/src/utils/configs/mainnet.ts @@ -53,7 +53,7 @@ export default { }, special: { stakeCalculator: '0x29c708d94521af2c88402858049bd33e4606a3a2', - leverageStrategy: ZeroAddress, + leverageStrategy: '0xc57E59764f398831C1444c14d296Af868d533196', }, }, tokens: { From 8e37be5d3caab42a198104f0e2599bfe5e246828 Mon Sep 17 00:00:00 2001 From: CAst Date: Mon, 28 Oct 2024 17:38:11 +0500 Subject: [PATCH 21/44] Boost updates (#198) * [boost-updates] set addresses * [boost-updates] update getBoost * [boost-updates] change readme --- src/graphql/subgraph/osToken/boostTokenSharesQuery.graphql | 2 +- src/methods/vault/requests/getBoost.ts | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/graphql/subgraph/osToken/boostTokenSharesQuery.graphql b/src/graphql/subgraph/osToken/boostTokenSharesQuery.graphql index 428b047b..289bb113 100644 --- a/src/graphql/subgraph/osToken/boostTokenSharesQuery.graphql +++ b/src/graphql/subgraph/osToken/boostTokenSharesQuery.graphql @@ -8,9 +8,9 @@ query BoostTokenShares( user: $userAddress } ) { - assets osTokenShares exitingPercent + boostRewardAssets: assets } vaults( where: { diff --git a/src/methods/vault/requests/getBoost.ts b/src/methods/vault/requests/getBoost.ts index 67366447..dd8ad4ff 100644 --- a/src/methods/vault/requests/getBoost.ts +++ b/src/methods/vault/requests/getBoost.ts @@ -14,6 +14,7 @@ type Output = { shares: bigint assets: bigint percent: number + rewardAssets: bigint exitingPercent: number maxMintShares: bigint isProfitable: boolean @@ -28,6 +29,7 @@ const getBoost = async (values: GetBoostInput) => { shares: 0n, assets: 0n, percent: 0, + rewardAssets: 0n, exitingPercent: 0, maxMintShares: 0n, isProfitable: false, @@ -51,11 +53,12 @@ const getBoost = async (values: GetBoostInput) => { const { apy, maxBoostApy, osTokenConfig } = vaults[0] const leverageStrategyPosition = leverageStrategyPositions[0] + const stakedAssets = BigInt(allocators[0]?.assets || 0) const ltvPercent = BigInt(osTokenConfig.ltvPercent || 0) const boostShares = BigInt(leverageStrategyPosition?.osTokenShares || 0) - const boostAssets = BigInt(leverageStrategyPosition?.assets || 0) const exitingPercent = Number(leverageStrategyPosition?.exitingPercent || 0) + const boostRewardAssets = BigInt(leverageStrategyPosition?.boostRewardAssets || 0) const maxMintAssets = stakedAssets * ltvPercent / constants.blockchain.amount1 const maxMintShares = await contracts.base.mintTokenController.convertToShares(maxMintAssets) @@ -69,11 +72,11 @@ const getBoost = async (values: GetBoostInput) => { ) : 0 boost.shares = boostShares - boost.assets = boostAssets boost.percent = boostPercent boost.exitingPercent = exitingPercent boost.maxMintShares = maxMintShares boost.isProfitable = maxBoostApy > apy + boost.rewardAssets = boostRewardAssets } return boost From b8627d9b3a9d884be51839c2e6452e4d365b5a2d Mon Sep 17 00:00:00 2001 From: Andrey Kopylov Date: Tue, 29 Oct 2024 14:47:59 +0500 Subject: [PATCH 22/44] fix getBoost --- src/methods/vault/requests/getBoost.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/methods/vault/requests/getBoost.ts b/src/methods/vault/requests/getBoost.ts index dd8ad4ff..cbe74d3f 100644 --- a/src/methods/vault/requests/getBoost.ts +++ b/src/methods/vault/requests/getBoost.ts @@ -12,7 +12,6 @@ type GetBoostInput = { type Output = { shares: bigint - assets: bigint percent: number rewardAssets: bigint exitingPercent: number @@ -27,7 +26,6 @@ const getBoost = async (values: GetBoostInput) => { const boost: Output = { shares: 0n, - assets: 0n, percent: 0, rewardAssets: 0n, exitingPercent: 0, From 1b07d3952c8034b4576a27089a71c2406424bbdf Mon Sep 17 00:00:00 2001 From: CAst Date: Thu, 31 Oct 2024 18:21:09 +0500 Subject: [PATCH 23/44] Fix user stats (#201) * [fix-user-stats] fixed * [fix-user-stats] improve --- .../requests/getUserStats/modifyUserStats.ts | 48 +++++++++---------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/src/methods/vault/requests/getUserStats/modifyUserStats.ts b/src/methods/vault/requests/getUserStats/modifyUserStats.ts index 670f79ca..42fb1ea7 100644 --- a/src/methods/vault/requests/getUserStats/modifyUserStats.ts +++ b/src/methods/vault/requests/getUserStats/modifyUserStats.ts @@ -6,38 +6,33 @@ import type { UserStatsQueryPayload } from '../../../../graphql/subgraph/vault' const updateUserStatsMap = ( userStatsMap: UserStatsMap, - stat: { + values: { earnedAssets: string totalAssets: string timestamp: string }, includeApy = false ) => { - const timeInSeconds = Number(stat.timestamp) / 1_000_000 - const balance = Number(formatEther(stat.totalAssets || 0n)) - const rewards = Number(formatEther(stat.earnedAssets || 0n)) + const { earnedAssets, totalAssets, timestamp } = values - const totalApy = includeApy - ? (rewards * 365 * 100) / (balance - rewards) - : 0 + const timeInSeconds = Number(timestamp) / 1_000_000 + const balance = Number(formatEther(totalAssets || 0n)) + const rewards = Number(formatEther(earnedAssets || 0n)) + const keys = (Object.keys(userStatsMap) as Array) - if (!userStatsMap.balance[stat.timestamp]) { - userStatsMap.balance[stat.timestamp] = { value: 0, time: timeInSeconds } - } - - if (!userStatsMap.rewards[stat.timestamp]) { - userStatsMap.rewards[stat.timestamp] = { value: 0, time: timeInSeconds } - } - - if (includeApy && !userStatsMap.apy[stat.timestamp]) { - userStatsMap.apy[stat.timestamp] = { value: 0, time: timeInSeconds } - } + keys.forEach((key) => { + if (!userStatsMap[key][timestamp]) { + userStatsMap[key][timestamp] = { value: 0, time: timeInSeconds } + } + }) - userStatsMap.balance[stat.timestamp].value += balance - userStatsMap.rewards[stat.timestamp].value += rewards + userStatsMap.balance[timestamp].value += balance + userStatsMap.rewards[timestamp].value += rewards if (includeApy) { - userStatsMap.apy[stat.timestamp].value += totalApy + const rewardsSum = userStatsMap.rewards[timestamp].value + + userStatsMap.apy[timestamp].value = (rewardsSum * 365 * 100) / (balance - rewards) } } @@ -53,18 +48,21 @@ const modifyUserStats = (data: UserStatsQueryPayload): ModifiedUserStats => { rewards: {}, } + // ATTN The order in which arrays are processed is important! + boostStats.forEach((stats) => { - updateUserStatsMap(userStatsMap, stats, true) + updateUserStatsMap(userStatsMap, stats) }) - allocatorStats.forEach((stats) => { - updateUserStatsMap(userStatsMap, stats, true) + rewardSplitterStats.forEach((stats) => { + updateUserStatsMap(userStatsMap, stats) }) - rewardSplitterStats.forEach((stats) => { + allocatorStats.forEach((stats) => { updateUserStatsMap(userStatsMap, stats, true) }) + // Rewards of this array do not participate in the APY calculation. exitRequestStats.forEach((stats) => { updateUserStatsMap(userStatsMap, stats) }) From fb5aac728d36ca1b95b272a9bddbd9236b471fb1 Mon Sep 17 00:00:00 2001 From: Mike Diamond Date: Mon, 4 Nov 2024 11:05:54 +0300 Subject: [PATCH 24/44] Unboost (#199) * [unboost ui] update apy calculation * [unboost ui] add unboost method * [unboost ui] update unboost ui * [unboost ui] update unboost ui * [unboost ui] update unboost ui * [unboost ui] update unboost ui * [unboost ui] update unboost ui * [unboost ui] update unboost ui * [unboost ui] fix boost --- .../osToken/transactions/boost/types.d.ts | 4 +- .../osToken/transactions/unboost/common.ts | 40 +++++++++++++++++++ .../osToken/transactions/unboost/index.ts | 20 ++++++++++ .../osToken/transactions/unboost/types.d.ts | 18 +++++++++ .../transactions/unboost/unboostEncode.ts | 19 +++++++++ .../transactions/unboost/unboostGas.ts | 24 +++++++++++ src/methods/utils/getBoostApy.ts | 26 ++++++++++++ src/methods/utils/index.ts | 1 + src/methods/vault/requests/getBoost.ts | 29 +++++--------- 9 files changed, 160 insertions(+), 21 deletions(-) create mode 100644 src/methods/osToken/transactions/unboost/common.ts create mode 100644 src/methods/osToken/transactions/unboost/index.ts create mode 100644 src/methods/osToken/transactions/unboost/types.d.ts create mode 100644 src/methods/osToken/transactions/unboost/unboostEncode.ts create mode 100644 src/methods/osToken/transactions/unboost/unboostGas.ts create mode 100644 src/methods/utils/getBoostApy.ts diff --git a/src/methods/osToken/transactions/boost/types.d.ts b/src/methods/osToken/transactions/boost/types.d.ts index ca334cbc..b839b00e 100644 --- a/src/methods/osToken/transactions/boost/types.d.ts +++ b/src/methods/osToken/transactions/boost/types.d.ts @@ -1,5 +1,5 @@ import boostGas from './boostGas' -import burnEncode from './burnEncode' +import boostEncode from './boostEncode' type PermitParams = { @@ -25,5 +25,5 @@ export type BoostInput = { export interface Boost { (values: BoostInput): Promise estimateGas: typeof boostGas - encode: typeof burnEncode + encode: typeof boostEncode } diff --git a/src/methods/osToken/transactions/unboost/common.ts b/src/methods/osToken/transactions/unboost/common.ts new file mode 100644 index 00000000..b6fd0c40 --- /dev/null +++ b/src/methods/osToken/transactions/unboost/common.ts @@ -0,0 +1,40 @@ +import { parseEther } from 'ethers' +import type { UnboostInput } from './types' +import { validateArgs } from '../../../../utils' +import { boostMulticall } from '../../../../contracts' + + +export const commonLogic = (values: UnboostInput) => { + const { contracts, options, percent, vaultAddress, userAddress } = values + + validateArgs.number({ percent }) + validateArgs.address({ vaultAddress, userAddress }) + + if (percent <= 0) { + throw new Error(`The "percent" argument must be greater than 0`) + } + if (percent > 100) { + throw new Error(`The "percent" argument must be less than or equal to 100`) + } + + const multicallArgs: Omit[0], 'request'> = { + leverageStrategyContract: contracts.special.leverageStrategy, + vaultAddress, + userAddress, + options, + } + + const params: Parameters[0]['request']['params'] = [] + + params.push({ + method: 'enterExitQueue', + args: [ vaultAddress, parseEther(String(percent / 100)) ], + }) + + return { + ...multicallArgs, + request: { + params, + }, + } +} diff --git a/src/methods/osToken/transactions/unboost/index.ts b/src/methods/osToken/transactions/unboost/index.ts new file mode 100644 index 00000000..0d00ec81 --- /dev/null +++ b/src/methods/osToken/transactions/unboost/index.ts @@ -0,0 +1,20 @@ +import unboostGas from './unboostGas' +import unboostEncode from './unboostEncode' +import type { Boost } from './types' +import { commonLogic } from './common' +import { boostMulticall } from '../../../../contracts' + + +const unboost: Boost = async (values) => { + const multicallArgs = commonLogic(values) + + const result = await boostMulticall<{ hash: string }>(multicallArgs) + + return result.hash +} + +unboost.encode = unboostEncode +unboost.estimateGas = unboostGas + + +export default unboost diff --git a/src/methods/osToken/transactions/unboost/types.d.ts b/src/methods/osToken/transactions/unboost/types.d.ts new file mode 100644 index 00000000..b5f0a2bc --- /dev/null +++ b/src/methods/osToken/transactions/unboost/types.d.ts @@ -0,0 +1,18 @@ +import unboostGas from './unboostGas' +import unboostEncode from './unboostEncode' + + +export type UnboostInput = { + percent: number + userAddress: string + vaultAddress: string + options: StakeWise.Options + provider: StakeWise.Provider + contracts: StakeWise.Contracts +} + +export interface Boost { + (values: UnboostInput): Promise + estimateGas: typeof unboostGas + encode: typeof unboostEncode +} diff --git a/src/methods/osToken/transactions/unboost/unboostEncode.ts b/src/methods/osToken/transactions/unboost/unboostEncode.ts new file mode 100644 index 00000000..1146a023 --- /dev/null +++ b/src/methods/osToken/transactions/unboost/unboostEncode.ts @@ -0,0 +1,19 @@ +import { UnboostInput } from './types' +import { commonLogic } from './common' +import { boostMulticall } from '../../../../contracts' + + +const unboostEncode = (values: UnboostInput): Promise => { + const multicallArgs = commonLogic(values) + + return boostMulticall<{ data: string, to: string }>({ + ...multicallArgs, + request: { + ...multicallArgs.request, + transactionData: true, + }, + }) +} + + +export default unboostEncode diff --git a/src/methods/osToken/transactions/unboost/unboostGas.ts b/src/methods/osToken/transactions/unboost/unboostGas.ts new file mode 100644 index 00000000..4e283788 --- /dev/null +++ b/src/methods/osToken/transactions/unboost/unboostGas.ts @@ -0,0 +1,24 @@ +import type { UnboostInput } from './types' +import { getGas } from '../../../../utils' +import { commonLogic } from './common' +import { boostMulticall } from '../../../../contracts' + + +const unboostGas = async (values: UnboostInput) => { + const { provider } = values + + const multicallArgs = commonLogic(values) + + const estimatedGas = await boostMulticall({ + ...multicallArgs, + request: { + ...multicallArgs.request, + estimateGas: true, + }, + }) + + return getGas({ estimatedGas, provider }) +} + + +export default unboostGas diff --git a/src/methods/utils/getBoostApy.ts b/src/methods/utils/getBoostApy.ts new file mode 100644 index 00000000..7f527fab --- /dev/null +++ b/src/methods/utils/getBoostApy.ts @@ -0,0 +1,26 @@ +import { BigDecimal } from '../../utils' + + +type Input = { + value: bigint + vaultAPY: number + maxBoostAPY: number + maxMintShares: bigint +} + +const getBoostApy = ({ value, vaultAPY, maxBoostAPY, maxMintShares }: Input) => { + const boostPercent = maxMintShares + ? new BigDecimal(value).multiply(100).divide(maxMintShares) + : 0n + + return new BigDecimal(maxBoostAPY) + .minus(vaultAPY) + .multiply(boostPercent) + .divide(100) + .plus(vaultAPY) + .decimals(2) + .toNumber() +} + + +export default getBoostApy diff --git a/src/methods/utils/index.ts b/src/methods/utils/index.ts index 0cf5063c..6fbaf523 100644 --- a/src/methods/utils/index.ts +++ b/src/methods/utils/index.ts @@ -1,4 +1,5 @@ export type { BaseInput } from './types' +export { default as getBoostApy } from './getBoostApy' export { default as getFiatRates } from './getFiatRates' export { default as getTransactions } from './getTransactions' export { default as getFiatRatesByDay } from './getFiatRatesByDay' diff --git a/src/methods/vault/requests/getBoost.ts b/src/methods/vault/requests/getBoost.ts index cbe74d3f..324441a2 100644 --- a/src/methods/vault/requests/getBoost.ts +++ b/src/methods/vault/requests/getBoost.ts @@ -1,6 +1,6 @@ import graphql from '../../../graphql' import { wrapAbortPromise } from '../../../modules/gql-module' -import { validateArgs, apiUrls, BigDecimal, Network, constants } from '../../../utils' +import { validateArgs, apiUrls, Network, constants } from '../../../utils' type GetBoostInput = { @@ -12,7 +12,6 @@ type GetBoostInput = { type Output = { shares: bigint - percent: number rewardAssets: bigint exitingPercent: number maxMintShares: bigint @@ -26,7 +25,6 @@ const getBoost = async (values: GetBoostInput) => { const boost: Output = { shares: 0n, - percent: 0, rewardAssets: 0n, exitingPercent: 0, maxMintShares: 0n, @@ -54,27 +52,20 @@ const getBoost = async (values: GetBoostInput) => { const stakedAssets = BigInt(allocators[0]?.assets || 0) const ltvPercent = BigInt(osTokenConfig.ltvPercent || 0) - const boostShares = BigInt(leverageStrategyPosition?.osTokenShares || 0) + const shares = BigInt(leverageStrategyPosition?.osTokenShares || 0) + const rewardAssets = BigInt(leverageStrategyPosition?.boostRewardAssets || 0) const exitingPercent = Number(leverageStrategyPosition?.exitingPercent || 0) - const boostRewardAssets = BigInt(leverageStrategyPosition?.boostRewardAssets || 0) const maxMintAssets = stakedAssets * ltvPercent / constants.blockchain.amount1 const maxMintShares = await contracts.base.mintTokenController.convertToShares(maxMintAssets) - const boostPercent = maxMintShares ? ( - new BigDecimal(boostShares) - .multiply(100) - .divide(maxMintShares) - .decimals(2) - .toNumber() - ) : 0 - - boost.shares = boostShares - boost.percent = boostPercent - boost.exitingPercent = exitingPercent - boost.maxMintShares = maxMintShares - boost.isProfitable = maxBoostApy > apy - boost.rewardAssets = boostRewardAssets + return { + shares, + rewardAssets, + maxMintShares, + exitingPercent, + isProfitable: Number(maxBoostApy) > Number(apy), + } } return boost From 3fba1303afc15b1d43e70a6f4e978591651bd330 Mon Sep 17 00:00:00 2001 From: MikeDiam Date: Tue, 5 Nov 2024 13:04:50 +0300 Subject: [PATCH 25/44] [unboost vault] remove getBoostApy --- src/methods/utils/getBoostApy.ts | 26 -------------------------- src/methods/utils/index.ts | 1 - 2 files changed, 27 deletions(-) delete mode 100644 src/methods/utils/getBoostApy.ts diff --git a/src/methods/utils/getBoostApy.ts b/src/methods/utils/getBoostApy.ts deleted file mode 100644 index 7f527fab..00000000 --- a/src/methods/utils/getBoostApy.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { BigDecimal } from '../../utils' - - -type Input = { - value: bigint - vaultAPY: number - maxBoostAPY: number - maxMintShares: bigint -} - -const getBoostApy = ({ value, vaultAPY, maxBoostAPY, maxMintShares }: Input) => { - const boostPercent = maxMintShares - ? new BigDecimal(value).multiply(100).divide(maxMintShares) - : 0n - - return new BigDecimal(maxBoostAPY) - .minus(vaultAPY) - .multiply(boostPercent) - .divide(100) - .plus(vaultAPY) - .decimals(2) - .toNumber() -} - - -export default getBoostApy diff --git a/src/methods/utils/index.ts b/src/methods/utils/index.ts index 6fbaf523..0cf5063c 100644 --- a/src/methods/utils/index.ts +++ b/src/methods/utils/index.ts @@ -1,5 +1,4 @@ export type { BaseInput } from './types' -export { default as getBoostApy } from './getBoostApy' export { default as getFiatRates } from './getFiatRates' export { default as getTransactions } from './getTransactions' export { default as getFiatRatesByDay } from './getFiatRatesByDay' From ecd491e1e80d9fd704069f6a07e7334817e8f495 Mon Sep 17 00:00:00 2001 From: CAst Date: Tue, 5 Nov 2024 19:24:44 +0500 Subject: [PATCH 26/44] Unboost queue (#204) * [unboost-queue] create getQueuePosition * [unboost-queue] create claimQueue * [unboost-queue] boost refactoring * [unboost-queue] improve --- .../osToken/boostTokenSharesQuery.graphql | 34 ------ src/graphql/subgraph/osToken/index.ts | 3 - .../getBoost.ts => boost/requests/getData.ts} | 2 +- .../requests/getLeverageStrategyProxy.ts | 0 .../osToken/transactions/boost/boostEncode.ts | 19 --- .../osToken/transactions/boost/boostGas.ts | 24 ---- .../osToken/transactions/boost/common.ts | 111 ------------------ .../osToken/transactions/boost/index.ts | 20 ---- .../osToken/transactions/boost/types.d.ts | 29 ----- .../osToken/transactions/unboost/common.ts | 40 ------- .../osToken/transactions/unboost/index.ts | 20 ---- .../osToken/transactions/unboost/types.d.ts | 18 --- .../transactions/unboost/unboostEncode.ts | 19 --- .../transactions/unboost/unboostGas.ts | 24 ---- 14 files changed, 1 insertion(+), 362 deletions(-) delete mode 100644 src/graphql/subgraph/osToken/boostTokenSharesQuery.graphql rename src/methods/{vault/requests/getBoost.ts => boost/requests/getData.ts} (96%) rename src/methods/{osToken => boost}/requests/getLeverageStrategyProxy.ts (100%) delete mode 100644 src/methods/osToken/transactions/boost/boostEncode.ts delete mode 100644 src/methods/osToken/transactions/boost/boostGas.ts delete mode 100644 src/methods/osToken/transactions/boost/common.ts delete mode 100644 src/methods/osToken/transactions/boost/index.ts delete mode 100644 src/methods/osToken/transactions/boost/types.d.ts delete mode 100644 src/methods/osToken/transactions/unboost/common.ts delete mode 100644 src/methods/osToken/transactions/unboost/index.ts delete mode 100644 src/methods/osToken/transactions/unboost/types.d.ts delete mode 100644 src/methods/osToken/transactions/unboost/unboostEncode.ts delete mode 100644 src/methods/osToken/transactions/unboost/unboostGas.ts diff --git a/src/graphql/subgraph/osToken/boostTokenSharesQuery.graphql b/src/graphql/subgraph/osToken/boostTokenSharesQuery.graphql deleted file mode 100644 index 289bb113..00000000 --- a/src/graphql/subgraph/osToken/boostTokenSharesQuery.graphql +++ /dev/null @@ -1,34 +0,0 @@ -query BoostTokenShares( - $vaultAddress: String! - $userAddress: Bytes! -) { - leverageStrategyPositions( - where: { - vault: $vaultAddress, - user: $userAddress - } - ) { - osTokenShares - exitingPercent - boostRewardAssets: assets - } - vaults( - where: { - addressString: $vaultAddress, - } - ) { - osTokenConfig { - ltvPercent - } - maxBoostApy - apy - } - allocators( - where: { - address: $userAddress, - vault: $vaultAddress - } - ) { - assets - } -} diff --git a/src/graphql/subgraph/osToken/index.ts b/src/graphql/subgraph/osToken/index.ts index 834901a6..65dd2864 100644 --- a/src/graphql/subgraph/osToken/index.ts +++ b/src/graphql/subgraph/osToken/index.ts @@ -1,5 +1,2 @@ export { fetchOsTokenApyQuery } from './osTokenApyQuery.graphql' export type { OsTokenApyQueryPayload, OsTokenApyQueryVariables } from './osTokenApyQuery.graphql' - -export { fetchBoostTokenSharesQuery } from './boostTokenSharesQuery.graphql' -export type { BoostTokenSharesQueryPayload, BoostTokenSharesQueryVariables } from './boostTokenSharesQuery.graphql' diff --git a/src/methods/vault/requests/getBoost.ts b/src/methods/boost/requests/getData.ts similarity index 96% rename from src/methods/vault/requests/getBoost.ts rename to src/methods/boost/requests/getData.ts index 324441a2..85bbdf5b 100644 --- a/src/methods/vault/requests/getBoost.ts +++ b/src/methods/boost/requests/getData.ts @@ -32,7 +32,7 @@ const getBoost = async (values: GetBoostInput) => { } if ([ Network.Mainnet, Network.Holesky ].includes(options.network)) { - const response = await graphql.subgraph.osToken.fetchBoostTokenSharesQuery({ + const response = await graphql.subgraph.boost.fetchBoostMainDataQuery({ url: apiUrls.getSubgraphqlUrl(options), variables: { userAddress: userAddress.toLowerCase(), diff --git a/src/methods/osToken/requests/getLeverageStrategyProxy.ts b/src/methods/boost/requests/getLeverageStrategyProxy.ts similarity index 100% rename from src/methods/osToken/requests/getLeverageStrategyProxy.ts rename to src/methods/boost/requests/getLeverageStrategyProxy.ts diff --git a/src/methods/osToken/transactions/boost/boostEncode.ts b/src/methods/osToken/transactions/boost/boostEncode.ts deleted file mode 100644 index 0e2a9eef..00000000 --- a/src/methods/osToken/transactions/boost/boostEncode.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { BoostInput } from './types' -import { commonLogic } from './common' -import { boostMulticall } from '../../../../contracts' - - -const boostEncode = async (values: BoostInput): Promise => { - const multicallArgs = await commonLogic(values) - - return boostMulticall<{ data: string, to: string }>({ - ...multicallArgs, - request: { - ...multicallArgs.request, - transactionData: true, - }, - }) -} - - -export default boostEncode diff --git a/src/methods/osToken/transactions/boost/boostGas.ts b/src/methods/osToken/transactions/boost/boostGas.ts deleted file mode 100644 index 5848775c..00000000 --- a/src/methods/osToken/transactions/boost/boostGas.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { BoostInput } from './types' -import { getGas } from '../../../../utils' -import { commonLogic } from './common' -import { boostMulticall } from '../../../../contracts' - - -const boostGas = async (values: BoostInput) => { - const { provider } = values - - const multicallArgs = await commonLogic({ ...values, mockPermitSignature: true }) - - const estimatedGas = await boostMulticall({ - ...multicallArgs, - request: { - ...multicallArgs.request, - estimateGas: true, - }, - }) - - return getGas({ estimatedGas, provider }) -} - - -export default boostGas diff --git a/src/methods/osToken/transactions/boost/common.ts b/src/methods/osToken/transactions/boost/common.ts deleted file mode 100644 index 30a1cb3a..00000000 --- a/src/methods/osToken/transactions/boost/common.ts +++ /dev/null @@ -1,111 +0,0 @@ -import type { BoostInput } from './types' -import { validateArgs } from '../../../../utils' -import { boostMulticall } from '../../../../contracts' -import { getPermitSignature } from '../../../utils' -import getLeverageStrategyProxy from '../../requests/getLeverageStrategyProxy' - - -type CommonLogicInput = BoostInput & { - mockPermitSignature?: boolean -} - -export const commonLogic = async (values: CommonLogicInput) => { - const { - contracts, options, provider, amount, vaultAddress, userAddress, strategyProxy, - permitParams, mockPermitSignature, - } = values - - validateArgs.bigint({ amount }) - validateArgs.address({ vaultAddress, userAddress, strategyProxy }) - - if (permitParams) { - validateArgs.object({ permitParams }) - - const { vault, amount, deadline, v, r, s } = permitParams - - validateArgs.address({ 'permitParams.vault': vault }) - validateArgs.bigint({ 'permitParams.amount': amount }) - validateArgs.string({ 'permitParams.r': r, 'permitParams.s': s }) - validateArgs.number({ 'permitParams.v': v, 'permitParams.deadline': deadline }) - } - - const multicallArgs: Omit[0], 'request'> = { - leverageStrategyContract: contracts.special.leverageStrategy, - vaultAddress, - userAddress, - options, - } - - const params: Parameters[0]['request']['params'] = [] - - if (permitParams) { - const { vault, amount, deadline, v, r, s } = permitParams - - params.push({ - method: 'permit', - args: [ - vault, - amount, - deadline, - v, - r, - s, - ], - }) - } - else { - const strategyProxy = await getLeverageStrategyProxy({ - contracts, - userAddress, - vaultAddress, - }) - - const allowance = await contracts.tokens.mintToken.allowance(userAddress, strategyProxy) - const isPermitRequired = allowance < amount - - if (isPermitRequired) { - if (mockPermitSignature) { - params.push({ - method: 'permit', - args: [ - vaultAddress, - amount, - ], - }) - } - else { - const permitParams = await getPermitSignature({ - options, - provider, - contract: contracts.tokens.mintToken, - ownerAddress: userAddress, - spenderAddress: strategyProxy, - }) - - params.push({ - method: 'permit', - args: [ - vaultAddress, - permitParams.amount, - permitParams.deadline, - permitParams.v, - permitParams.r, - permitParams.s, - ], - }) - } - } - } - - params.push({ - method: 'deposit', - args: [ vaultAddress, amount ], - }) - - return { - ...multicallArgs, - request: { - params, - }, - } -} diff --git a/src/methods/osToken/transactions/boost/index.ts b/src/methods/osToken/transactions/boost/index.ts deleted file mode 100644 index 97dfb16c..00000000 --- a/src/methods/osToken/transactions/boost/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import boostGas from './boostGas' -import boostEncode from './boostEncode' -import type { Boost } from './types' -import { commonLogic } from './common' -import { boostMulticall } from '../../../../contracts' - - -const boost: Boost = async (values) => { - const multicallArgs = await commonLogic(values) - - const result = await boostMulticall<{ hash: string }>(multicallArgs) - - return result.hash -} - -boost.encode = boostEncode -boost.estimateGas = boostGas - - -export default boost diff --git a/src/methods/osToken/transactions/boost/types.d.ts b/src/methods/osToken/transactions/boost/types.d.ts deleted file mode 100644 index b839b00e..00000000 --- a/src/methods/osToken/transactions/boost/types.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -import boostGas from './boostGas' -import boostEncode from './boostEncode' - - -type PermitParams = { - vault: string - amount: bigint - deadline: number - v: number - r: string - s: string -} - -export type BoostInput = { - amount: bigint - userAddress: string - vaultAddress: string - strategyProxy: string - permitParams?: PermitParams - options: StakeWise.Options - provider: StakeWise.Provider - contracts: StakeWise.Contracts -} - -export interface Boost { - (values: BoostInput): Promise - estimateGas: typeof boostGas - encode: typeof boostEncode -} diff --git a/src/methods/osToken/transactions/unboost/common.ts b/src/methods/osToken/transactions/unboost/common.ts deleted file mode 100644 index b6fd0c40..00000000 --- a/src/methods/osToken/transactions/unboost/common.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { parseEther } from 'ethers' -import type { UnboostInput } from './types' -import { validateArgs } from '../../../../utils' -import { boostMulticall } from '../../../../contracts' - - -export const commonLogic = (values: UnboostInput) => { - const { contracts, options, percent, vaultAddress, userAddress } = values - - validateArgs.number({ percent }) - validateArgs.address({ vaultAddress, userAddress }) - - if (percent <= 0) { - throw new Error(`The "percent" argument must be greater than 0`) - } - if (percent > 100) { - throw new Error(`The "percent" argument must be less than or equal to 100`) - } - - const multicallArgs: Omit[0], 'request'> = { - leverageStrategyContract: contracts.special.leverageStrategy, - vaultAddress, - userAddress, - options, - } - - const params: Parameters[0]['request']['params'] = [] - - params.push({ - method: 'enterExitQueue', - args: [ vaultAddress, parseEther(String(percent / 100)) ], - }) - - return { - ...multicallArgs, - request: { - params, - }, - } -} diff --git a/src/methods/osToken/transactions/unboost/index.ts b/src/methods/osToken/transactions/unboost/index.ts deleted file mode 100644 index 0d00ec81..00000000 --- a/src/methods/osToken/transactions/unboost/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import unboostGas from './unboostGas' -import unboostEncode from './unboostEncode' -import type { Boost } from './types' -import { commonLogic } from './common' -import { boostMulticall } from '../../../../contracts' - - -const unboost: Boost = async (values) => { - const multicallArgs = commonLogic(values) - - const result = await boostMulticall<{ hash: string }>(multicallArgs) - - return result.hash -} - -unboost.encode = unboostEncode -unboost.estimateGas = unboostGas - - -export default unboost diff --git a/src/methods/osToken/transactions/unboost/types.d.ts b/src/methods/osToken/transactions/unboost/types.d.ts deleted file mode 100644 index b5f0a2bc..00000000 --- a/src/methods/osToken/transactions/unboost/types.d.ts +++ /dev/null @@ -1,18 +0,0 @@ -import unboostGas from './unboostGas' -import unboostEncode from './unboostEncode' - - -export type UnboostInput = { - percent: number - userAddress: string - vaultAddress: string - options: StakeWise.Options - provider: StakeWise.Provider - contracts: StakeWise.Contracts -} - -export interface Boost { - (values: UnboostInput): Promise - estimateGas: typeof unboostGas - encode: typeof unboostEncode -} diff --git a/src/methods/osToken/transactions/unboost/unboostEncode.ts b/src/methods/osToken/transactions/unboost/unboostEncode.ts deleted file mode 100644 index 1146a023..00000000 --- a/src/methods/osToken/transactions/unboost/unboostEncode.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { UnboostInput } from './types' -import { commonLogic } from './common' -import { boostMulticall } from '../../../../contracts' - - -const unboostEncode = (values: UnboostInput): Promise => { - const multicallArgs = commonLogic(values) - - return boostMulticall<{ data: string, to: string }>({ - ...multicallArgs, - request: { - ...multicallArgs.request, - transactionData: true, - }, - }) -} - - -export default unboostEncode diff --git a/src/methods/osToken/transactions/unboost/unboostGas.ts b/src/methods/osToken/transactions/unboost/unboostGas.ts deleted file mode 100644 index 4e283788..00000000 --- a/src/methods/osToken/transactions/unboost/unboostGas.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { UnboostInput } from './types' -import { getGas } from '../../../../utils' -import { commonLogic } from './common' -import { boostMulticall } from '../../../../contracts' - - -const unboostGas = async (values: UnboostInput) => { - const { provider } = values - - const multicallArgs = commonLogic(values) - - const estimatedGas = await boostMulticall({ - ...multicallArgs, - request: { - ...multicallArgs.request, - estimateGas: true, - }, - }) - - return getGas({ estimatedGas, provider }) -} - - -export default unboostGas From 8e3763558adac090c15a7b43dc9435f0d3de8fb3 Mon Sep 17 00:00:00 2001 From: Kadyr Dzhemaledinov Date: Fri, 8 Nov 2024 13:37:58 +0300 Subject: [PATCH 27/44] Add isDepositWithMint option (#206) * add isDepositWithMint logic * add genesis * improve isDepositWithMint in vaultMulticall --- .../vault/abis/GenesisVaultDiffAbi.json | 87 ------------------- 1 file changed, 87 deletions(-) delete mode 100644 src/contracts/vault/abis/GenesisVaultDiffAbi.json diff --git a/src/contracts/vault/abis/GenesisVaultDiffAbi.json b/src/contracts/vault/abis/GenesisVaultDiffAbi.json deleted file mode 100644 index 505b73e9..00000000 --- a/src/contracts/vault/abis/GenesisVaultDiffAbi.json +++ /dev/null @@ -1,87 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "address", - "name": "receiver", - "type": "address" - }, - { - "internalType": "uint256", - "name": "osTokenShares", - "type": "uint256" - }, - { - "internalType": "address", - "name": "referrer", - "type": "address" - } - ], - "name": "depositAndMintOsToken", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "receiver", - "type": "address" - }, - { - "internalType": "uint256", - "name": "osTokenShares", - "type": "uint256" - }, - { - "internalType": "address", - "name": "referrer", - "type": "address" - }, - { - "components": [ - { - "internalType": "bytes32", - "name": "rewardsRoot", - "type": "bytes32" - }, - { - "internalType": "int160", - "name": "reward", - "type": "int160" - }, - { - "internalType": "uint160", - "name": "unlockedMevReward", - "type": "uint160" - }, - { - "internalType": "bytes32[]", - "name": "proof", - "type": "bytes32[]" - } - ], - "internalType": "struct IKeeperRewards.HarvestParams", - "name": "harvestParams", - "type": "tuple" - } - ], - "name": "updateStateAndDepositAndMintOsToken", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "payable", - "type": "function" - } -] From 4a749d2da166b1aa0d95e82274ba52addb77d27d Mon Sep 17 00:00:00 2001 From: CAst Date: Thu, 14 Nov 2024 16:12:08 +0500 Subject: [PATCH 28/44] Rewards data (#207) * [rewards-data] add merge rewards & fiat helper * [rewards-data] change reqests * [rewards-data] create calculateUserStats * [rewards-data] readme * [rewards-data] readme * [rewards-data] fix --- .../subgraph/vault/userStatsQuery.graphql | 28 +++- src/index.ts | 1 + src/methods/utils/getFiatRatesByDay.ts | 24 ++++ .../getUserRewards/getMainnetRates.ts | 24 ++++ .../{getUserStats => }/getUserStats.md | 0 .../index.ts => getUserStats.ts} | 0 .../getUserStats/modifyUserStats.spec.ts | 132 ------------------ .../requests/getUserStats/modifyUserStats.ts | 80 ----------- .../vault/requests/getUserStats/types.ts | 17 --- 9 files changed, 73 insertions(+), 233 deletions(-) create mode 100644 src/methods/utils/getFiatRatesByDay.ts create mode 100644 src/methods/vault/requests/getUserRewards/getMainnetRates.ts rename src/methods/vault/requests/{getUserStats => }/getUserStats.md (100%) rename src/methods/vault/requests/{getUserStats/index.ts => getUserStats.ts} (100%) delete mode 100644 src/methods/vault/requests/getUserStats/modifyUserStats.spec.ts delete mode 100644 src/methods/vault/requests/getUserStats/modifyUserStats.ts delete mode 100644 src/methods/vault/requests/getUserStats/types.ts diff --git a/src/graphql/subgraph/vault/userStatsQuery.graphql b/src/graphql/subgraph/vault/userStatsQuery.graphql index f701da8e..983dc5e3 100644 --- a/src/graphql/subgraph/vault/userStatsQuery.graphql +++ b/src/graphql/subgraph/vault/userStatsQuery.graphql @@ -1,7 +1,12 @@ query UserStats($user: Bytes! $vaultAddress: String! $daysCount: Int!) { allocator: allocatorStats_collection( interval: day - where: { allocator_: { address: $user, vault: $vaultAddress } } + where: { + allocator_: { + address: $user, + vault: $vaultAddress + } + } first: $daysCount ) { timestamp @@ -10,7 +15,12 @@ query UserStats($user: Bytes! $vaultAddress: String! $daysCount: Int!) { } exitRequest: exitRequestStats_collection( interval: day - where: { exitRequest_: { receiver: $user, vault: $vaultAddress } } + where: { + exitRequest_: { + receiver: $user, + vault: $vaultAddress + } + } first: $daysCount ) { timestamp @@ -19,7 +29,12 @@ query UserStats($user: Bytes! $vaultAddress: String! $daysCount: Int!) { } rewardSplitter: rewardSplitterShareHolderStats_collection( interval: day - where: { rewardSpliterShareHolder_: { address: $user, vault: $vaultAddress }} + where: { + rewardSpliterShareHolder_: { + address: $user, + vault: $vaultAddress + } + } first: $daysCount ) { timestamp @@ -28,7 +43,12 @@ query UserStats($user: Bytes! $vaultAddress: String! $daysCount: Int!) { } boost: leverageStrategyPositionStats_collection( interval: day - where: {position_: {user: $user, vault: $vaultAddress}} + where: { + position_: { + user: $user, + vault: $vaultAddress + } + } first: $daysCount ) { timestamp diff --git a/src/index.ts b/src/index.ts index 22ae44d1..68083df6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,7 @@ export * from './utils/enums' export { createContract } from './contracts' export { default as StakeWiseSDK } from './StakeWiseSDK' export { wrapAbortPromise, AbortPromise } from './modules/gql-module' +export { BigDecimal, configs, getGas, createProvider, mergeRewardsFiat, calculateUserStats } from './utils' export { configs, diff --git a/src/methods/utils/getFiatRatesByDay.ts b/src/methods/utils/getFiatRatesByDay.ts new file mode 100644 index 00000000..74187884 --- /dev/null +++ b/src/methods/utils/getFiatRatesByDay.ts @@ -0,0 +1,24 @@ +import { Network, configs } from '../../utils' +import graphql from '../../graphql' + + +type GetFiatRatesByDayInput = { + dateTo: number + dateFrom: number +} + +const getFiatRatesByDay = (input: Pick) => { + const { dateFrom, dateTo } = input + + return graphql.subgraph.stats.fetchFiatByDayQuery({ + url: configs[Network.Mainnet].api.subgraph, + variables: { + dateTo: String(dateTo * 1_000), + dateFrom: String(dateFrom * 1_000), + }, + modifyResult: (data) => data.exchangeRate, + }) +} + + +export default getFiatRatesByDay diff --git a/src/methods/vault/requests/getUserRewards/getMainnetRates.ts b/src/methods/vault/requests/getUserRewards/getMainnetRates.ts new file mode 100644 index 00000000..e8d53d26 --- /dev/null +++ b/src/methods/vault/requests/getUserRewards/getMainnetRates.ts @@ -0,0 +1,24 @@ +import { Network, configs } from '../../../../utils' +import graphql from '../../../../graphql' + + +type GetMainnetRatesInput = { + dateTo: number + dateFrom: number +} + +const getMainnetRates = (input: Pick) => { + const { dateFrom, dateTo } = input + + return graphql.subgraph.stats.fetchFiatByDayQuery({ + url: configs[Network.Mainnet].api.subgraph, + variables: { + dateTo: String(dateTo * 1_000), + dateFrom: String(dateFrom * 1_000), + }, + modifyResult: (data) => data.exchangeRate, + }) +} + + +export default getMainnetRates diff --git a/src/methods/vault/requests/getUserStats/getUserStats.md b/src/methods/vault/requests/getUserStats.md similarity index 100% rename from src/methods/vault/requests/getUserStats/getUserStats.md rename to src/methods/vault/requests/getUserStats.md diff --git a/src/methods/vault/requests/getUserStats/index.ts b/src/methods/vault/requests/getUserStats.ts similarity index 100% rename from src/methods/vault/requests/getUserStats/index.ts rename to src/methods/vault/requests/getUserStats.ts diff --git a/src/methods/vault/requests/getUserStats/modifyUserStats.spec.ts b/src/methods/vault/requests/getUserStats/modifyUserStats.spec.ts deleted file mode 100644 index ffd349fd..00000000 --- a/src/methods/vault/requests/getUserStats/modifyUserStats.spec.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { formatEther } from 'ethers' - -import type { ModifiedUserStats } from './types' -import modifyUserStats from './modifyUserStats' -import type { UserStatsQueryPayload } from '../../../../graphql/subgraph/vault' - - -describe('modifyUserStats function', () => { - it('should correctly modify User Stats collection', () => { - const sampleInput: UserStatsQueryPayload = { - allocator: [ - { - timestamp: '1727136000000000', - earnedAssets: '27734011365427', - totalAssets: '715748104014280751', - }, - { - timestamp: '1727049600000000', - earnedAssets: '31538735933515', - totalAssets: '715720370002915324', - }, - { - timestamp: '1726963200000000', - earnedAssets: '28263735668759', - totalAssets: '715688831266981809', - }, - ], - exitRequest: [ - { - timestamp: '1727136000000000', - earnedAssets: '27734011365427', - totalAssets: '715748104014280751', - }, - { - timestamp: '1726876800000000', - earnedAssets: '36906487302928', - totalAssets: '715660567531313050', - }, - ], - rewardSplitter: [ - { - timestamp: '1727049600000000', - earnedAssets: '31538735933515', - totalAssets: '715720370002915324', - }, - { - timestamp: '1726790400000000', - earnedAssets: '31763194717568', - totalAssets: '715623661044010122', - }, - ], - } - - const expectedResult: ModifiedUserStats = { - balance: [ - { - time: 1727136000, - value: Number(formatEther('1431496208028561600')), - }, - { - time: 1727049600, - value: Number(formatEther('1431440740005830700')), - }, - { - time: 1726963200, - value: Number(formatEther('715688831266981809')), - }, - { - time: 1726876800, - value: Number(formatEther('715660567531313050')), - }, - { - time: 1726790400, - value: Number(formatEther('715623661044010122')), - }, - ], - rewards: [ - { - time: 1727136000, - value: Number(formatEther('55468022730854')), - }, - { - time: 1727049600, - value: Number(formatEther('63077471867030')), - }, - { - time: 1726963200, - value: Number(formatEther('28263735668759')), - }, - { - time: 1726876800, - value: Number(formatEther('36906487302928')), - }, - { - time: 1726790400, - value: Number(formatEther('31763194717568')), - }, - ], - apy: [ - { - time: 1727136000, - value: ( - Number(formatEther('27734011365427')) * 365 * 100) - / (Number(formatEther('715748104014280751')) - Number(formatEther('27734011365427')) - ), - }, - { - time: 1727049600, - value: ( - Number(formatEther('31538735933515')) * 365 * 100) - / (Number(formatEther('715720370002915324')) - Number(formatEther('31538735933515')) - ), - }, - { - time: 1726963200, - value: ( - Number(formatEther('28263735668759')) * 365 * 100) - / (Number(formatEther('715688831266981809')) - Number(formatEther('28263735668759')) - ), - }, - ], - } - - const result = modifyUserStats(sampleInput) - - expect(result).toEqual({ - apy: expectedResult.apy.sort((a, b) => a.time - b.time), - balance: expectedResult.balance.sort((a, b) => a.time - b.time), - rewards: expectedResult.rewards.sort((a, b) => a.time - b.time), - }) - }) -}) diff --git a/src/methods/vault/requests/getUserStats/modifyUserStats.ts b/src/methods/vault/requests/getUserStats/modifyUserStats.ts deleted file mode 100644 index 42fb1ea7..00000000 --- a/src/methods/vault/requests/getUserStats/modifyUserStats.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { formatEther } from 'ethers' - -import type { ModifiedUserStats, UserStatsMap } from './types' -import type { UserStatsQueryPayload } from '../../../../graphql/subgraph/vault' - - -const updateUserStatsMap = ( - userStatsMap: UserStatsMap, - values: { - earnedAssets: string - totalAssets: string - timestamp: string - }, - includeApy = false -) => { - const { earnedAssets, totalAssets, timestamp } = values - - const timeInSeconds = Number(timestamp) / 1_000_000 - const balance = Number(formatEther(totalAssets || 0n)) - const rewards = Number(formatEther(earnedAssets || 0n)) - const keys = (Object.keys(userStatsMap) as Array) - - keys.forEach((key) => { - if (!userStatsMap[key][timestamp]) { - userStatsMap[key][timestamp] = { value: 0, time: timeInSeconds } - } - }) - - userStatsMap.balance[timestamp].value += balance - userStatsMap.rewards[timestamp].value += rewards - - if (includeApy) { - const rewardsSum = userStatsMap.rewards[timestamp].value - - userStatsMap.apy[timestamp].value = (rewardsSum * 365 * 100) / (balance - rewards) - } -} - -const modifyUserStats = (data: UserStatsQueryPayload): ModifiedUserStats => { - const boostStats = data?.boost || [] - const allocatorStats = data?.allocator || [] - const exitRequestStats = data?.exitRequest || [] - const rewardSplitterStats = data?.rewardSplitter || [] - - const userStatsMap: UserStatsMap = { - apy: {}, - balance: {}, - rewards: {}, - } - - // ATTN The order in which arrays are processed is important! - - boostStats.forEach((stats) => { - updateUserStatsMap(userStatsMap, stats) - }) - - rewardSplitterStats.forEach((stats) => { - updateUserStatsMap(userStatsMap, stats) - }) - - allocatorStats.forEach((stats) => { - updateUserStatsMap(userStatsMap, stats, true) - }) - - // Rewards of this array do not participate in the APY calculation. - exitRequestStats.forEach((stats) => { - updateUserStatsMap(userStatsMap, stats) - }) - - const result = { - apy: Object.values(userStatsMap.apy).sort((a, b) => a.time - b.time), - balance: Object.values(userStatsMap.balance).sort((a, b) => a.time - b.time), - rewards: Object.values(userStatsMap.rewards).sort((a, b) => a.time - b.time), - } - - return result -} - - -export default modifyUserStats diff --git a/src/methods/vault/requests/getUserStats/types.ts b/src/methods/vault/requests/getUserStats/types.ts deleted file mode 100644 index 37e80456..00000000 --- a/src/methods/vault/requests/getUserStats/types.ts +++ /dev/null @@ -1,17 +0,0 @@ -type ChartStat = { - value: number - time: number -} - -export type UserStatsMap = { - apy: Record - balance: Record - rewards: Record -} - -export type ModifiedUserStats = { - apy: ChartStat[] - balance: ChartStat[] - rewards: ChartStat[] -} - From c3bcc8ca4ccfb12144705850fb07a3e29c30dc12 Mon Sep 17 00:00:00 2001 From: Kadyr Dzhemaledinov Date: Mon, 25 Nov 2024 14:43:53 +0300 Subject: [PATCH 29/44] Add documentation website (#210) * update methods, add .md files * add new website * update dev command * app packages * update commands * test build * test build * update README * update README * update docs --- src/methods/boost/requests/getData.ts | 75 ------------------- .../requests/getLeverageStrategyProxy.ts | 17 ----- src/methods/osToken/requests/getConfig.ts | 20 ----- .../osToken/requests/getOsTokenPosition.ts | 51 ------------- .../requests/getPosition/getPosition.md | 50 +++++++++++++ .../{getPosition.ts => getPosition/index.ts} | 0 src/methods/utils/getFiatRates.ts | 42 ----------- src/methods/utils/getFiatRatesByDay.ts | 24 ------ src/methods/utils/getPermitSignature.ts | 74 ------------------ src/methods/utils/getStakewiseStats.ts | 23 ------ .../getSwiseUsdPrice/getSwiseUsdPrice.md | 41 ++++++++++ .../requests/getUserStats/getUserStats.md | 48 ++++++++++++ .../index.ts} | 0 13 files changed, 139 insertions(+), 326 deletions(-) delete mode 100644 src/methods/boost/requests/getData.ts delete mode 100644 src/methods/boost/requests/getLeverageStrategyProxy.ts delete mode 100644 src/methods/osToken/requests/getConfig.ts delete mode 100644 src/methods/osToken/requests/getOsTokenPosition.ts create mode 100644 src/methods/osToken/requests/getPosition/getPosition.md rename src/methods/osToken/requests/{getPosition.ts => getPosition/index.ts} (100%) delete mode 100644 src/methods/utils/getFiatRates.ts delete mode 100644 src/methods/utils/getFiatRatesByDay.ts delete mode 100644 src/methods/utils/getPermitSignature.ts delete mode 100644 src/methods/utils/getStakewiseStats.ts create mode 100644 src/methods/utils/getSwiseUsdPrice/getSwiseUsdPrice.md create mode 100644 src/methods/vault/requests/getUserStats/getUserStats.md rename src/methods/vault/requests/{getUserStats.ts => getUserStats/index.ts} (100%) diff --git a/src/methods/boost/requests/getData.ts b/src/methods/boost/requests/getData.ts deleted file mode 100644 index 85bbdf5b..00000000 --- a/src/methods/boost/requests/getData.ts +++ /dev/null @@ -1,75 +0,0 @@ -import graphql from '../../../graphql' -import { wrapAbortPromise } from '../../../modules/gql-module' -import { validateArgs, apiUrls, Network, constants } from '../../../utils' - - -type GetBoostInput = { - userAddress: string - vaultAddress: string - options: StakeWise.Options - contracts: StakeWise.Contracts -} - -type Output = { - shares: bigint - rewardAssets: bigint - exitingPercent: number - maxMintShares: bigint - isProfitable: boolean -} - -const getBoost = async (values: GetBoostInput) => { - const { contracts, options, vaultAddress, userAddress } = values - - validateArgs.address({ vaultAddress, userAddress }) - - const boost: Output = { - shares: 0n, - rewardAssets: 0n, - exitingPercent: 0, - maxMintShares: 0n, - isProfitable: false, - } - - if ([ Network.Mainnet, Network.Holesky ].includes(options.network)) { - const response = await graphql.subgraph.boost.fetchBoostMainDataQuery({ - url: apiUrls.getSubgraphqlUrl(options), - variables: { - userAddress: userAddress.toLowerCase(), - vaultAddress: vaultAddress.toLowerCase(), - }, - }) - - const { leverageStrategyPositions, vaults, allocators } = response - - if (!vaults.length) { - return boost - } - - const { apy, maxBoostApy, osTokenConfig } = vaults[0] - - const leverageStrategyPosition = leverageStrategyPositions[0] - - const stakedAssets = BigInt(allocators[0]?.assets || 0) - const ltvPercent = BigInt(osTokenConfig.ltvPercent || 0) - const shares = BigInt(leverageStrategyPosition?.osTokenShares || 0) - const rewardAssets = BigInt(leverageStrategyPosition?.boostRewardAssets || 0) - const exitingPercent = Number(leverageStrategyPosition?.exitingPercent || 0) - - const maxMintAssets = stakedAssets * ltvPercent / constants.blockchain.amount1 - const maxMintShares = await contracts.base.mintTokenController.convertToShares(maxMintAssets) - - return { - shares, - rewardAssets, - maxMintShares, - exitingPercent, - isProfitable: Number(maxBoostApy) > Number(apy), - } - } - - return boost -} - - -export default wrapAbortPromise(getBoost) diff --git a/src/methods/boost/requests/getLeverageStrategyProxy.ts b/src/methods/boost/requests/getLeverageStrategyProxy.ts deleted file mode 100644 index 101bc0da..00000000 --- a/src/methods/boost/requests/getLeverageStrategyProxy.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { wrapAbortPromise } from '../../../modules/gql-module' - - -type GetLeverageStrategyProxyInput = { - contracts: StakeWise.Contracts - userAddress: string - vaultAddress: string -} - -const getLeverageStrategyProxy = (values: GetLeverageStrategyProxyInput) => { - const { contracts, userAddress, vaultAddress } = values - - return contracts.special.leverageStrategy.getStrategyProxy(vaultAddress, userAddress) -} - - -export default wrapAbortPromise(getLeverageStrategyProxy) diff --git a/src/methods/osToken/requests/getConfig.ts b/src/methods/osToken/requests/getConfig.ts deleted file mode 100644 index 48a486cb..00000000 --- a/src/methods/osToken/requests/getConfig.ts +++ /dev/null @@ -1,20 +0,0 @@ -import getVault from '../../vault/requests/getVault' - - -type GetConfigInput = { - options: StakeWise.Options - vaultAddress: string -} - -const getConfig = (input: GetConfigInput) => { - const { options, vaultAddress } = input - - return getVault({ - options, - vaultAddress, - }) - .then((vault) => vault.osTokenConfig) -} - - -export default getConfig diff --git a/src/methods/osToken/requests/getOsTokenPosition.ts b/src/methods/osToken/requests/getOsTokenPosition.ts deleted file mode 100644 index 64a0ce44..00000000 --- a/src/methods/osToken/requests/getOsTokenPosition.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { validateArgs } from '../../../utils' -import getHealthFactor from '../helpers/getHealthFactor' -import { wrapAbortPromise } from '../../../modules/gql-module' - - -type Output = { - minted: { - assets: bigint - shares: bigint - }, - healthFactor: ReturnType - protocolFeePercent: bigint -} - -type GetOsTokenPositionInput = { - userAddress: string - vaultAddress: string - stakedAssets: bigint - thresholdPercent: bigint - contracts: StakeWise.Contracts -} - -const getOsTokenPosition = async (values: GetOsTokenPositionInput) => { - const { contracts, vaultAddress, userAddress, stakedAssets, thresholdPercent } = values - - validateArgs.address({ vaultAddress, userAddress }) - validateArgs.bigint({ stakedAssets, thresholdPercent }) - - const vaultContract = contracts.helpers.createVault({ vaultAddress }) - const mintedShares = await vaultContract.osTokenPositions(userAddress) - - const [ mintedAssets, feePercent ] = await Promise.all([ - contracts.base.mintTokenController.convertToAssets(mintedShares), - contracts.base.mintTokenController.feePercent(), - ]) - - const protocolFeePercent = feePercent / 100n - const healthFactor = getHealthFactor({ mintedAssets, stakedAssets, thresholdPercent }) - - return { - minted: { - assets: mintedAssets, - shares: mintedShares, - }, - healthFactor, - protocolFeePercent, - } -} - - -export default wrapAbortPromise(getOsTokenPosition) diff --git a/src/methods/osToken/requests/getPosition/getPosition.md b/src/methods/osToken/requests/getPosition/getPosition.md new file mode 100644 index 00000000..7a4aaa1d --- /dev/null +++ b/src/methods/osToken/requests/getPosition/getPosition.md @@ -0,0 +1,50 @@ +--- +id: getPosition +slug: /osToken/requests/getposition +--- + +#### Description: + +User position data + +#### Arguments: +| Name | Type | Required | Description | +|------------------|----------|----------|--------------------------------------------------------------| +| thresholdPercent | `bigint` | **Yes** | [sdk.vault.getVault](/vault/requests/getvault) | +| stakedAssets | `bigint` | **Yes** | [sdk.vault.getStakeBalance](/vault/requests/getstakebalance) | +| userAddress | `string` | **Yes** | The user address | +| vaultAddress | `string` | **Yes** | The address of the vault | + +#### Returns: + +```ts +type Output = { + minted: { + assets: bigint + shares: bigint + } + healthFactor: { + value: number + health: OsTokenPositionHealth + } + protocolFeePercent: bigint +} +``` + +| Name | Description | +|----------------------|-----------------------------------------------------------------| +| `minted.shares` | Balance | +| `minted.assets` | Balance in ETH | +| `healthFactor` | [sdk.osToken.getHealthFactor](/osToken/helpers/gethealthfactor) | +| `protocolFeePercent` | Usage fee percent | + +#### Example: + +```ts +await sdk.osToken.getPosition({ + stakedAssets: 0n, + userAddress: '0x...', + vaultAddress: '0x...', + thresholdPercent: 0n, +}) +``` diff --git a/src/methods/osToken/requests/getPosition.ts b/src/methods/osToken/requests/getPosition/index.ts similarity index 100% rename from src/methods/osToken/requests/getPosition.ts rename to src/methods/osToken/requests/getPosition/index.ts diff --git a/src/methods/utils/getFiatRates.ts b/src/methods/utils/getFiatRates.ts deleted file mode 100644 index 84913305..00000000 --- a/src/methods/utils/getFiatRates.ts +++ /dev/null @@ -1,42 +0,0 @@ -import graphql from '../../graphql' -import { apiUrls, configs, Network } from '../../utils' - - -type GetFiatRatesInput = { - options: StakeWise.Options -} - -const getMainnetGbpRate = () => { - return graphql.subgraph.stats.fetchFiatRatesQuery({ - url: configs[Network.Mainnet].api.subgraph, - modifyResult: (data): number => { - const usdInGbp = Number(data.networks[0].usdToGbpRate) - - return usdInGbp - }, - }) -} - -const getFiatRates = (values: GetFiatRatesInput) => { - const { options } = values - - return graphql.subgraph.stats.fetchFiatRatesQuery({ - url: apiUrls.getSubgraphqlUrl(options), - modifyResult: async (data) => { - const usd = Number(data.networks[0].assetsUsdRate) - const usdInEur = Number(data.networks[0].usdToEurRate) - - const isGnosis = [ Network.Gnosis, Network.Chiado ].includes(options.network) - const usdInGbp = isGnosis ? await getMainnetGbpRate() : Number(data.networks[0].usdToGbpRate) - - return { - assetsUsdRate: usd, - usdToEurRate: usdInEur, - usdToGbpRate: usdInGbp, - } - }, - }) -} - - -export default getFiatRates diff --git a/src/methods/utils/getFiatRatesByDay.ts b/src/methods/utils/getFiatRatesByDay.ts deleted file mode 100644 index 74187884..00000000 --- a/src/methods/utils/getFiatRatesByDay.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Network, configs } from '../../utils' -import graphql from '../../graphql' - - -type GetFiatRatesByDayInput = { - dateTo: number - dateFrom: number -} - -const getFiatRatesByDay = (input: Pick) => { - const { dateFrom, dateTo } = input - - return graphql.subgraph.stats.fetchFiatByDayQuery({ - url: configs[Network.Mainnet].api.subgraph, - variables: { - dateTo: String(dateTo * 1_000), - dateFrom: String(dateFrom * 1_000), - }, - modifyResult: (data) => data.exchangeRate, - }) -} - - -export default getFiatRatesByDay diff --git a/src/methods/utils/getPermitSignature.ts b/src/methods/utils/getPermitSignature.ts deleted file mode 100644 index cfa595a7..00000000 --- a/src/methods/utils/getPermitSignature.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { Signature } from 'ethers' - -import { constants } from '../../utils' - - -const maxUint256 = constants.blockchain.maxUint256 - -type GetPermitSignatureInput = { - options: StakeWise.Options - provider: StakeWise.Provider - contract: StakeWise.ABI.Erc20Token - amount?: bigint - ownerAddress: string - spenderAddress: string -} - -const getPermitSignature = async (values: GetPermitSignatureInput) => { - const { options, provider, amount = maxUint256, contract, ownerAddress, spenderAddress } = values - - const currentTimestamp = Number((Date.now() / 1000).toFixed(0)) - const deadline = currentTimestamp + 3600 // + 1 hour - - const [ tokenName, mintTokenAddress, tokenNonce ] = await Promise.all([ - contract.name(), - contract.getAddress(), - contract.nonces(ownerAddress), - ]) - - const data = JSON.stringify({ - primaryType: 'Permit', - types: { - EIP712Domain: [ - { name: 'name', type: 'string' }, - { name: 'version', type: 'string' }, - { name: 'chainId', type: 'uint256' }, - { name: 'verifyingContract', type: 'address' }, - ], - Permit: [ - { name: 'owner', type: 'address' }, - { name: 'spender', type: 'address' }, - { name: 'value', type: 'uint256' }, - { name: 'nonce', type: 'uint256' }, - { name: 'deadline', type: 'uint256' }, - ], - }, - domain: { - name: tokenName, - version: '1', - chainId: options.network, - verifyingContract: mintTokenAddress, - }, - message: { - deadline, - owner: ownerAddress, - nonce: tokenNonce.toString(), - spender: spenderAddress, - value: amount, - }, - }) - - const signature = await provider.send('eth_signTypedData_v4', [ ownerAddress, data ]) - const { v, r, s } = await Signature.from(signature) - - return { - amount, - deadline, - v, - r, - s, - } -} - - -export default getPermitSignature diff --git a/src/methods/utils/getStakewiseStats.ts b/src/methods/utils/getStakewiseStats.ts deleted file mode 100644 index a471c7af..00000000 --- a/src/methods/utils/getStakewiseStats.ts +++ /dev/null @@ -1,23 +0,0 @@ -import graphql from '../../graphql' -import { apiUrls } from '../../utils' - - -type GetStakewiseStatsInput = { - options: StakeWise.Options -} - -const getStakewiseStats = (values: GetStakewiseStatsInput) => { - const { options } = values - - return graphql.subgraph.stats.fetchStatsQuery({ - url: apiUrls.getSubgraphqlUrl(options), - modifyResult: (data) => ({ - usersCount: data.networks[0].usersCount, - totalAssets: data.networks[0].totalAssets, - totalEarnedAssets: data.networks[0].totalEarnedAssets, - }), - }) -} - - -export default getStakewiseStats diff --git a/src/methods/utils/getSwiseUsdPrice/getSwiseUsdPrice.md b/src/methods/utils/getSwiseUsdPrice/getSwiseUsdPrice.md new file mode 100644 index 00000000..4f4cc747 --- /dev/null +++ b/src/methods/utils/getSwiseUsdPrice/getSwiseUsdPrice.md @@ -0,0 +1,41 @@ +--- +id: getSwiseUsdPrice +--- + +#### Description: + +Current price of SWISE token to USD. + +#### Returns: + +```ts +type Output = string +``` + +#### Example: + +```ts +await sdk.utils.getSwiseUsdPrice() +``` +--- +### `getStakewiseStats` + +#### Description: + +TVL statistics, number of users, rewards earned + +#### Returns: + +```ts +type Output = { + usersCount: number + totalAssets: string + totalEarnedAssets: string +} +``` + +#### Example: + +```ts +await sdk.utils.getStakewiseStats() +``` diff --git a/src/methods/vault/requests/getUserStats/getUserStats.md b/src/methods/vault/requests/getUserStats/getUserStats.md new file mode 100644 index 00000000..90764cd6 --- /dev/null +++ b/src/methods/vault/requests/getUserStats/getUserStats.md @@ -0,0 +1,48 @@ +--- +id: getUserStats +--- + +#### Description: + +Getting the user stats collection for current vault. +With the help of this data it is possible to build a chart. + +#### Arguments: + +| Name | Type | Required | Description | +|--------------|----------|----------|--------------------------| +| daysCount | `number` | **Yes** | The limit in days | +| userAddress | `string` | **Yes** | The user address | +| vaultAddress | `string` | **Yes** | The address of the vault | + +#### Returns: + +```ts +type Stat = { + time: number + value: number +} + +type Output = { + apy: Stat[] + balance: Stat[] + rewards: Stat[] +} +``` + +| Name | Description | +|-----------|---------------------------------------------------------------------------------| +| `time` | Date and time for each data point | +| `apy` | Current APY based on time, rewards and balance. | +| `rewards` | Number of assets earned by the user in current vault during the interval in ETH | +| `balance` | Total assets by the user in current vault at the moment of time in ETH | + +#### Example: + +```ts +await sdk.vault.getUserStats({ + daysCount: 30, + userAddress: '0x...', + vaultAddress: '0x...', +}) +``` diff --git a/src/methods/vault/requests/getUserStats.ts b/src/methods/vault/requests/getUserStats/index.ts similarity index 100% rename from src/methods/vault/requests/getUserStats.ts rename to src/methods/vault/requests/getUserStats/index.ts From d389c4d066c1c19ebb0bf5dcce7029409ecd8201 Mon Sep 17 00:00:00 2001 From: Kadyr Dzhemaledinov Date: Tue, 26 Nov 2024 12:38:32 +0300 Subject: [PATCH 30/44] Migration and Search (#211) * add migration page * add search --- src/methods/vault/requests/getUserStats/getUserStats.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/methods/vault/requests/getUserStats/getUserStats.md b/src/methods/vault/requests/getUserStats/getUserStats.md index 90764cd6..479987d9 100644 --- a/src/methods/vault/requests/getUserStats/getUserStats.md +++ b/src/methods/vault/requests/getUserStats/getUserStats.md @@ -1,5 +1,6 @@ --- id: getUserStats +slug: /vault/requests/getuserstats --- #### Description: From 9827925b20e9bf63c7690a0e6139e0a2078079d3 Mon Sep 17 00:00:00 2001 From: CAst Date: Fri, 29 Nov 2024 18:44:45 +0500 Subject: [PATCH 31/44] [rewards-fix] fixed (#214) --- .../subgraph/vault/userStatsQuery.graphql | 18 +++++++++--------- src/index.ts | 11 ++++++++++- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/graphql/subgraph/vault/userStatsQuery.graphql b/src/graphql/subgraph/vault/userStatsQuery.graphql index 983dc5e3..3f45c546 100644 --- a/src/graphql/subgraph/vault/userStatsQuery.graphql +++ b/src/graphql/subgraph/vault/userStatsQuery.graphql @@ -1,13 +1,13 @@ -query UserStats($user: Bytes! $vaultAddress: String! $daysCount: Int!) { +query UserStats($userAddress: Bytes! $vaultAddress: String! $timestamp: Timestamp!) { allocator: allocatorStats_collection( interval: day where: { + timestamp_gte: $timestamp, allocator_: { - address: $user, + address: $userAddress, vault: $vaultAddress } } - first: $daysCount ) { timestamp earnedAssets @@ -16,12 +16,12 @@ query UserStats($user: Bytes! $vaultAddress: String! $daysCount: Int!) { exitRequest: exitRequestStats_collection( interval: day where: { + timestamp_gte: $timestamp, exitRequest_: { - receiver: $user, + receiver: $userAddress, vault: $vaultAddress } } - first: $daysCount ) { timestamp earnedAssets @@ -30,12 +30,12 @@ query UserStats($user: Bytes! $vaultAddress: String! $daysCount: Int!) { rewardSplitter: rewardSplitterShareHolderStats_collection( interval: day where: { + timestamp_gte: $timestamp, rewardSpliterShareHolder_: { - address: $user, + address: $userAddress, vault: $vaultAddress } } - first: $daysCount ) { timestamp earnedAssets @@ -44,12 +44,12 @@ query UserStats($user: Bytes! $vaultAddress: String! $daysCount: Int!) { boost: leverageStrategyPositionStats_collection( interval: day where: { + timestamp_gte: $timestamp, position_: { - user: $user, + user: $userAddress, vault: $vaultAddress } } - first: $daysCount ) { timestamp earnedAssets: earnedBoostAssets diff --git a/src/index.ts b/src/index.ts index 68083df6..53ac46a0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,7 +6,16 @@ export * from './utils/enums' export { createContract } from './contracts' export { default as StakeWiseSDK } from './StakeWiseSDK' export { wrapAbortPromise, AbortPromise } from './modules/gql-module' -export { BigDecimal, configs, getGas, createProvider, mergeRewardsFiat, calculateUserStats } from './utils' + +export { + configs, + BigDecimal, + getGas, + getTimestamp, + createProvider, + mergeRewardsFiat, + calculateUserStats, +} from './utils' export { configs, From 248c6abf140839c5a895e9c9f8007893c41e3550 Mon Sep 17 00:00:00 2001 From: CAst Date: Mon, 2 Dec 2024 18:02:58 +0500 Subject: [PATCH 32/44] [new-schema] changes (#215) --- src/graphql/subgraph/vault/userStatsQuery.graphql | 7 +++++-- src/graphql/subgraph/vault/vaultQuery.graphql | 1 - src/methods/vault/requests/getVault/modifyVault.ts | 2 -- src/methods/vault/requests/getVault/types.ts | 2 -- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/graphql/subgraph/vault/userStatsQuery.graphql b/src/graphql/subgraph/vault/userStatsQuery.graphql index 3f45c546..37a06b0e 100644 --- a/src/graphql/subgraph/vault/userStatsQuery.graphql +++ b/src/graphql/subgraph/vault/userStatsQuery.graphql @@ -9,6 +9,7 @@ query UserStats($userAddress: Bytes! $vaultAddress: String! $timestamp: Timestam } } ) { + apy timestamp earnedAssets totalAssets @@ -23,6 +24,7 @@ query UserStats($userAddress: Bytes! $vaultAddress: String! $timestamp: Timestam } } ) { + apy timestamp earnedAssets totalAssets @@ -52,7 +54,8 @@ query UserStats($userAddress: Bytes! $vaultAddress: String! $timestamp: Timestam } ) { timestamp - earnedAssets: earnedBoostAssets - totalAssets: totalEarnedBoostAssets + apy: strategyApy + earnedAssets: allocatorEarnedAssets + totalAssets: allocatorTotalEarnedAssets } } diff --git a/src/graphql/subgraph/vault/vaultQuery.graphql b/src/graphql/subgraph/vault/vaultQuery.graphql index 32efcc04..09b45a5e 100644 --- a/src/graphql/subgraph/vault/vaultQuery.graphql +++ b/src/graphql/subgraph/vault/vaultQuery.graphql @@ -19,7 +19,6 @@ query Vault($address: ID!) { tokenName isGenesis feePercent - maxBoostApy totalAssets isBlocklist displayName diff --git a/src/methods/vault/requests/getVault/modifyVault.ts b/src/methods/vault/requests/getVault/modifyVault.ts index 347feabb..c385af3c 100644 --- a/src/methods/vault/requests/getVault/modifyVault.ts +++ b/src/methods/vault/requests/getVault/modifyVault.ts @@ -26,7 +26,6 @@ const modifyVault = (input: ModifyVaultInput): ModifiedVault => { mevEscrow, createdAt, feePercent, - maxBoostApy, performance, totalAssets, whitelister, @@ -48,7 +47,6 @@ const modifyVault = (input: ModifyVaultInput): ModifiedVault => { isSmoothingPool: !mevEscrow, feePercent: feePercent / 100, vaultAdmin: getAddress(admin), - maxBoostApy: Number(maxBoostApy), performance: Number(performance), vaultAddress: getAddress(address), createdAt: Number(createdAt) * 1000, diff --git a/src/methods/vault/requests/getVault/types.ts b/src/methods/vault/requests/getVault/types.ts index bc4d7034..bcfa34d3 100644 --- a/src/methods/vault/requests/getVault/types.ts +++ b/src/methods/vault/requests/getVault/types.ts @@ -9,7 +9,6 @@ export type ModifiedVault = Omit< | 'version' | 'createdAt' | 'mevEscrow' - | 'maxBoostApy' | 'performance' | 'whitelister' | 'osTokenConfig' @@ -22,7 +21,6 @@ export type ModifiedVault = Omit< version: number createdAt: number vaultAdmin: string - maxBoostApy: number performance: number vaultAddress: string mevRecipient: string From 97d738beeb32e6c5d419eaadfc1bbdcd845d4a2d Mon Sep 17 00:00:00 2001 From: Andrey Kopylov Date: Mon, 2 Dec 2024 18:26:13 +0500 Subject: [PATCH 33/44] fix --- src/graphql/subgraph/vault/userStatsQuery.graphql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphql/subgraph/vault/userStatsQuery.graphql b/src/graphql/subgraph/vault/userStatsQuery.graphql index 37a06b0e..9d320866 100644 --- a/src/graphql/subgraph/vault/userStatsQuery.graphql +++ b/src/graphql/subgraph/vault/userStatsQuery.graphql @@ -54,7 +54,7 @@ query UserStats($userAddress: Bytes! $vaultAddress: String! $timestamp: Timestam } ) { timestamp - apy: strategyApy + apy earnedAssets: allocatorEarnedAssets totalAssets: allocatorTotalEarnedAssets } From 0c1629781ae62306332714cdc4ba8e4fa3a38864 Mon Sep 17 00:00:00 2001 From: Kadyr Dzhemaledinov Date: Fri, 6 Dec 2024 15:49:22 +0300 Subject: [PATCH 34/44] Update links, rename thresholdPercent to liqThresholdPercent (#219) * update jsdoc links * rename thresholdPercent to liqThresholdPercent --- src/methods/osToken/requests/getPosition/getPosition.md | 4 ++-- src/methods/utils/getSwiseUsdPrice/getSwiseUsdPrice.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/methods/osToken/requests/getPosition/getPosition.md b/src/methods/osToken/requests/getPosition/getPosition.md index 7a4aaa1d..2af949ee 100644 --- a/src/methods/osToken/requests/getPosition/getPosition.md +++ b/src/methods/osToken/requests/getPosition/getPosition.md @@ -10,7 +10,7 @@ User position data #### Arguments: | Name | Type | Required | Description | |------------------|----------|----------|--------------------------------------------------------------| -| thresholdPercent | `bigint` | **Yes** | [sdk.vault.getVault](/vault/requests/getvault) | +| liqThresholdPercent | `bigint` | **Yes** | [sdk.vault.getVault](/vault/requests/getvault) | | stakedAssets | `bigint` | **Yes** | [sdk.vault.getStakeBalance](/vault/requests/getstakebalance) | | userAddress | `string` | **Yes** | The user address | | vaultAddress | `string` | **Yes** | The address of the vault | @@ -45,6 +45,6 @@ await sdk.osToken.getPosition({ stakedAssets: 0n, userAddress: '0x...', vaultAddress: '0x...', - thresholdPercent: 0n, + liqThresholdPercent: 0n, }) ``` diff --git a/src/methods/utils/getSwiseUsdPrice/getSwiseUsdPrice.md b/src/methods/utils/getSwiseUsdPrice/getSwiseUsdPrice.md index 4f4cc747..b523a52b 100644 --- a/src/methods/utils/getSwiseUsdPrice/getSwiseUsdPrice.md +++ b/src/methods/utils/getSwiseUsdPrice/getSwiseUsdPrice.md @@ -1,5 +1,6 @@ --- id: getSwiseUsdPrice +slug: /utils/getswiseusdprice --- #### Description: From 2adb797f17d97482f5aa40f624ae6dcb44757bdb Mon Sep 17 00:00:00 2001 From: CAst Date: Mon, 9 Dec 2024 12:06:29 +0500 Subject: [PATCH 35/44] Fiat rates and APY (#220) * [fiat-rates] new logic * [fiat-rates] getUserApy method * [fiat-rates] change getOsTokenAPY --- .../getSwiseUsdPrice/getSwiseUsdPrice.md | 42 ------------------- 1 file changed, 42 deletions(-) delete mode 100644 src/methods/utils/getSwiseUsdPrice/getSwiseUsdPrice.md diff --git a/src/methods/utils/getSwiseUsdPrice/getSwiseUsdPrice.md b/src/methods/utils/getSwiseUsdPrice/getSwiseUsdPrice.md deleted file mode 100644 index b523a52b..00000000 --- a/src/methods/utils/getSwiseUsdPrice/getSwiseUsdPrice.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -id: getSwiseUsdPrice -slug: /utils/getswiseusdprice ---- - -#### Description: - -Current price of SWISE token to USD. - -#### Returns: - -```ts -type Output = string -``` - -#### Example: - -```ts -await sdk.utils.getSwiseUsdPrice() -``` ---- -### `getStakewiseStats` - -#### Description: - -TVL statistics, number of users, rewards earned - -#### Returns: - -```ts -type Output = { - usersCount: number - totalAssets: string - totalEarnedAssets: string -} -``` - -#### Example: - -```ts -await sdk.utils.getStakewiseStats() -``` From 1795d1a464aa1b61fc2c251cb0fa62972609f4a3 Mon Sep 17 00:00:00 2001 From: Kadyr Dzhemaledinov Date: Wed, 11 Dec 2024 15:42:17 +0300 Subject: [PATCH 36/44] remove apy in leverageStrategyPositionStats_collection (#223) --- src/graphql/subgraph/vault/userStatsQuery.graphql | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/graphql/subgraph/vault/userStatsQuery.graphql b/src/graphql/subgraph/vault/userStatsQuery.graphql index 9d320866..a8e4722e 100644 --- a/src/graphql/subgraph/vault/userStatsQuery.graphql +++ b/src/graphql/subgraph/vault/userStatsQuery.graphql @@ -24,7 +24,6 @@ query UserStats($userAddress: Bytes! $vaultAddress: String! $timestamp: Timestam } } ) { - apy timestamp earnedAssets totalAssets @@ -54,7 +53,6 @@ query UserStats($userAddress: Bytes! $vaultAddress: String! $timestamp: Timestam } ) { timestamp - apy earnedAssets: allocatorEarnedAssets totalAssets: allocatorTotalEarnedAssets } From 9409deb5b9d8b6852369e92e6ed30a51ce38b2f5 Mon Sep 17 00:00:00 2001 From: Mike Diamond Date: Thu, 19 Dec 2024 14:03:10 +0300 Subject: [PATCH 37/44] [update stats] update stats requests (#225) * [update stats] update stats requests * [update stats] ui fixes --- .../subgraph/vault/userStatsQuery.graphql | 42 ------------------- 1 file changed, 42 deletions(-) diff --git a/src/graphql/subgraph/vault/userStatsQuery.graphql b/src/graphql/subgraph/vault/userStatsQuery.graphql index a8e4722e..b17245a7 100644 --- a/src/graphql/subgraph/vault/userStatsQuery.graphql +++ b/src/graphql/subgraph/vault/userStatsQuery.graphql @@ -14,46 +14,4 @@ query UserStats($userAddress: Bytes! $vaultAddress: String! $timestamp: Timestam earnedAssets totalAssets } - exitRequest: exitRequestStats_collection( - interval: day - where: { - timestamp_gte: $timestamp, - exitRequest_: { - receiver: $userAddress, - vault: $vaultAddress - } - } - ) { - timestamp - earnedAssets - totalAssets - } - rewardSplitter: rewardSplitterShareHolderStats_collection( - interval: day - where: { - timestamp_gte: $timestamp, - rewardSpliterShareHolder_: { - address: $userAddress, - vault: $vaultAddress - } - } - ) { - timestamp - earnedAssets - totalAssets - } - boost: leverageStrategyPositionStats_collection( - interval: day - where: { - timestamp_gte: $timestamp, - position_: { - user: $userAddress, - vault: $vaultAddress - } - } - ) { - timestamp - earnedAssets: allocatorEarnedAssets - totalAssets: allocatorTotalEarnedAssets - } } From ca6e44ceb6241265fdd1b6c57d1929b135733d4c Mon Sep 17 00:00:00 2001 From: Mike Diamond Date: Tue, 24 Dec 2024 13:30:33 +0300 Subject: [PATCH 38/44] Currencies (#227) * [skeleton] add currencies * [currencies] update queries * [currencies] update graphql --- .../subgraph/stats/fiatByDayQuery._graphql | 21 ++++++++++ .../subgraph/stats/fiatRatesQuery._graphql | 12 ++++++ .../subgraph/vault/userRewardsQuery._graphql | 38 +++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 src/graphql/subgraph/stats/fiatByDayQuery._graphql create mode 100644 src/graphql/subgraph/stats/fiatRatesQuery._graphql create mode 100644 src/graphql/subgraph/vault/userRewardsQuery._graphql diff --git a/src/graphql/subgraph/stats/fiatByDayQuery._graphql b/src/graphql/subgraph/stats/fiatByDayQuery._graphql new file mode 100644 index 00000000..d2cf1fff --- /dev/null +++ b/src/graphql/subgraph/stats/fiatByDayQuery._graphql @@ -0,0 +1,21 @@ +query FiatByDay( + $dateTo: Timestamp! + $dateFrom: Timestamp! +) { + exchangeRate: exchangeRateStats_collection( + interval: day + where: { + timestamp_gte: $dateFrom, + timestamp_lte: $dateTo + } + ) { + timestamp + usdToEurRate + usdToGbpRate + usdToCnyRate + usdToJpyRate + usdToKrwRate + usdToAudRate + assetsUsdRate + } +} diff --git a/src/graphql/subgraph/stats/fiatRatesQuery._graphql b/src/graphql/subgraph/stats/fiatRatesQuery._graphql new file mode 100644 index 00000000..05ee8034 --- /dev/null +++ b/src/graphql/subgraph/stats/fiatRatesQuery._graphql @@ -0,0 +1,12 @@ +query FiatRatesNew { + networks { + assetsUsdRate + usdToEurRate + usdToGbpRate + usdToCnyRate + usdToJpyRate + usdToKrwRate + usdToAudRate + swiseUsdRate + } +} diff --git a/src/graphql/subgraph/vault/userRewardsQuery._graphql b/src/graphql/subgraph/vault/userRewardsQuery._graphql new file mode 100644 index 00000000..44b28f26 --- /dev/null +++ b/src/graphql/subgraph/vault/userRewardsQuery._graphql @@ -0,0 +1,38 @@ +query UserRewards( + $user: Bytes! + $dateTo: Timestamp! + $dateFrom: Timestamp! + $vaultAddress: String! + $includeExchangeRate: Boolean! +) { + exchangeRates: exchangeRateStats_collection ( + interval: day, + where: { + timestamp_gte: $dateFrom, + timestamp_lte: $dateTo + } + ) @include(if: $includeExchangeRate) { + timestamp + usdToEurRate + usdToGbpRate + usdToCnyRate + usdToJpyRate + usdToKrwRate + usdToAudRate + assetsUsdRate + } + allocator: allocatorStats_collection( + interval: day + where: { + timestamp_gte: $dateFrom, + timestamp_lte: $dateTo, + allocator_: { + address: $user, + vault: $vaultAddress + } + } + ) { + timestamp + earnedAssets + } +} From be58b6d9cd0c72e7142a6c7df288e92d347b9920 Mon Sep 17 00:00:00 2001 From: Mike Diamond Date: Mon, 6 Jan 2025 15:36:50 +0300 Subject: [PATCH 39/44] [requests improve] update requests (#229) * [requests improve] update requests * [requests improve] update requests * [requests improve] update requests * [requests improve] update gnosis rates * [requests improve] update gnosis rates --- .../subgraph/vault/userStatsQuery.graphql | 17 ------------- .../getUserRewards/getMainnetRates.ts | 24 ------------------- 2 files changed, 41 deletions(-) delete mode 100644 src/graphql/subgraph/vault/userStatsQuery.graphql delete mode 100644 src/methods/vault/requests/getUserRewards/getMainnetRates.ts diff --git a/src/graphql/subgraph/vault/userStatsQuery.graphql b/src/graphql/subgraph/vault/userStatsQuery.graphql deleted file mode 100644 index b17245a7..00000000 --- a/src/graphql/subgraph/vault/userStatsQuery.graphql +++ /dev/null @@ -1,17 +0,0 @@ -query UserStats($userAddress: Bytes! $vaultAddress: String! $timestamp: Timestamp!) { - allocator: allocatorStats_collection( - interval: day - where: { - timestamp_gte: $timestamp, - allocator_: { - address: $userAddress, - vault: $vaultAddress - } - } - ) { - apy - timestamp - earnedAssets - totalAssets - } -} diff --git a/src/methods/vault/requests/getUserRewards/getMainnetRates.ts b/src/methods/vault/requests/getUserRewards/getMainnetRates.ts deleted file mode 100644 index e8d53d26..00000000 --- a/src/methods/vault/requests/getUserRewards/getMainnetRates.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Network, configs } from '../../../../utils' -import graphql from '../../../../graphql' - - -type GetMainnetRatesInput = { - dateTo: number - dateFrom: number -} - -const getMainnetRates = (input: Pick) => { - const { dateFrom, dateTo } = input - - return graphql.subgraph.stats.fetchFiatByDayQuery({ - url: configs[Network.Mainnet].api.subgraph, - variables: { - dateTo: String(dateTo * 1_000), - dateFrom: String(dateFrom * 1_000), - }, - modifyResult: (data) => data.exchangeRate, - }) -} - - -export default getMainnetRates From 6cda626787aba7dc94c3af367805d23a59544f4d Mon Sep 17 00:00:00 2001 From: CAst Date: Tue, 14 Jan 2025 16:45:42 +0500 Subject: [PATCH 40/44] [updates-for-apy] update fields (#231) --- .../vault/requests/getExitQueuePositions/modifyExitRequests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/methods/vault/requests/getExitQueuePositions/modifyExitRequests.ts b/src/methods/vault/requests/getExitQueuePositions/modifyExitRequests.ts index ad845872..12d1b33d 100644 --- a/src/methods/vault/requests/getExitQueuePositions/modifyExitRequests.ts +++ b/src/methods/vault/requests/getExitQueuePositions/modifyExitRequests.ts @@ -50,7 +50,7 @@ const modifyExitRequests = async (values: ParseExitRequestsInput): Promise { + exitRequests.forEach((exitRequest) => { const { timestamp, totalAssets, From 2dbc16a39300009beb69797d7619acc4fe1fc64c Mon Sep 17 00:00:00 2001 From: MikeDiam Date: Wed, 15 Jan 2025 10:56:06 +0300 Subject: [PATCH 41/44] [remove duplicates] update white/block list requests --- src/methods/utils/getListVariables.ts | 66 +++++++++++++++++++ src/methods/utils/index.ts | 2 + .../vault/requests/getBlocklist/index.ts | 60 ++--------------- .../vault/requests/getWhitelist/index.ts | 60 ++--------------- 4 files changed, 80 insertions(+), 108 deletions(-) create mode 100644 src/methods/utils/getListVariables.ts diff --git a/src/methods/utils/getListVariables.ts b/src/methods/utils/getListVariables.ts new file mode 100644 index 00000000..30dcafc8 --- /dev/null +++ b/src/methods/utils/getListVariables.ts @@ -0,0 +1,66 @@ +import { isAddress } from 'ethers' +import { validateArgs } from '../../utils' +import { StakeWiseSubgraphGraph } from '../../types/graphql/subgraph' + + +export type GetListVariablesInput = { + vaultAddress: string + orderDirection?: StakeWiseSubgraphGraph.OrderDirection + search?: string + limit?: number + skip?: number + addressIn?: string[] +} + +const validateList = (addressIn: string[]) => { + const isValid = addressIn.every((address) => isAddress(address)) + + if (!isValid) { + throw new Error('The "addressIn" argument must be an array of valid addresses') + } +} + +const getListVariables = (input: GetListVariablesInput): T => { + const { vaultAddress, orderDirection, search, limit, skip, addressIn } = input + + validateArgs.address({ vaultAddress }) + + if (typeof skip !== 'undefined') { + validateArgs.number({ skip }) + } + + if (typeof limit !== 'undefined') { + validateArgs.number({ limit }) + } + + if (typeof search !== 'undefined') { + validateArgs.string({ search }) + } + + if (typeof orderDirection !== 'undefined') { + if (![ 'asc', 'desc' ].includes(orderDirection)) { + throw new Error(`The "orderDirection" argument must be "asc" or "desc"`) + } + } + + if (typeof addressIn !== 'undefined') { + validateArgs.array({ addressIn }) + validateList(addressIn as string[]) + } + + const vault = vaultAddress.toLowerCase() + + const where = search + ? { vault, address_in: addressIn, address_contains: search.toLowerCase() } + : { vault, address_in: addressIn } + + return { + where, + skip: skip || 0, + limit: limit || 100, + orderDirection: orderDirection || 'desc', + } as T +} + + +export default getListVariables diff --git a/src/methods/utils/index.ts b/src/methods/utils/index.ts index 0cf5063c..13873f11 100644 --- a/src/methods/utils/index.ts +++ b/src/methods/utils/index.ts @@ -1,6 +1,8 @@ export type { BaseInput } from './types' export { default as getFiatRates } from './getFiatRates' export { default as getTransactions } from './getTransactions' +export type { GetListVariablesInput } from './getListVariables' +export { default as getListVariables } from './getListVariables' export { default as getFiatRatesByDay } from './getFiatRatesByDay' export { default as getStakewiseStats } from './getStakewiseStats' export { default as getPermitSignature } from './getPermitSignature' diff --git a/src/methods/vault/requests/getBlocklist/index.ts b/src/methods/vault/requests/getBlocklist/index.ts index dca971b9..d6cb7ebf 100644 --- a/src/methods/vault/requests/getBlocklist/index.ts +++ b/src/methods/vault/requests/getBlocklist/index.ts @@ -1,71 +1,23 @@ -import { isAddress } from 'ethers' import type { BlocklistAccountsQueryVariables, BlocklistAccountsQueryPayload } from '../../../../graphql/subgraph/vault' -import { apiUrls, validateArgs } from '../../../../utils' +import { apiUrls } from '../../../../utils' import graphql from '../../../../graphql' import { ModifiedBlocklist } from './types' import modifyBlocklist from './modifyBlocklist' +import { getListVariables, GetListVariablesInput } from '../../../utils' -type GetBlocklistInput = { - vaultAddress: string - orderDirection?: BlocklistAccountsQueryVariables['orderDirection'] - search?: string - limit?: number - skip?: number - addressIn?: BlocklistAccountsQueryVariables['where']['address_in'] +type GetBlocklistInput = GetListVariablesInput & { options: StakeWise.Options } -const validateList = (addressIn: string[]) => { - const isValid = addressIn.every((address) => isAddress(address)) - - if (!isValid) { - throw new Error('The "addressIn" argument must be an array of valid addresses') - } -} - const getBlocklist = (input: GetBlocklistInput) => { - const { vaultAddress, orderDirection, search, limit, skip, addressIn, options } = input - - validateArgs.address({ vaultAddress }) - - if (typeof skip !== 'undefined') { - validateArgs.number({ skip }) - } - - if (typeof limit !== 'undefined') { - validateArgs.number({ limit }) - } - - if (typeof search !== 'undefined') { - validateArgs.string({ search }) - } - - if (typeof orderDirection !== 'undefined') { - if (![ 'asc', 'desc' ].includes(orderDirection)) { - throw new Error(`The "orderDirection" argument must be "asc" or "desc"`) - } - } - - if (typeof addressIn !== 'undefined') { - validateArgs.array({ addressIn }) - validateList(addressIn as string[]) - } - - const vault = vaultAddress.toLowerCase() + const { options, ...rest } = input - const where = search - ? { vault, address_in: addressIn, address_contains: search.toLowerCase() } as BlocklistAccountsQueryVariables['where'] - : { vault, address_in: addressIn } as BlocklistAccountsQueryVariables['where'] + const variables = getListVariables(rest) return graphql.subgraph.vault.fetchBlocklistAccountsQuery({ url: apiUrls.getSubgraphqlUrl(options), - variables: { - where, - skip: skip || 0, - limit: limit || 100, - orderDirection: orderDirection || 'desc', - }, + variables, modifyResult: (data: BlocklistAccountsQueryPayload) => modifyBlocklist({ data }), }) } diff --git a/src/methods/vault/requests/getWhitelist/index.ts b/src/methods/vault/requests/getWhitelist/index.ts index 4cb64721..a231e387 100644 --- a/src/methods/vault/requests/getWhitelist/index.ts +++ b/src/methods/vault/requests/getWhitelist/index.ts @@ -1,71 +1,23 @@ -import { isAddress } from 'ethers' import type { WhitelistAccountsQueryVariables, WhitelistAccountsQueryPayload } from '../../../../graphql/subgraph/vault' -import { apiUrls, validateArgs } from '../../../../utils' +import { apiUrls } from '../../../../utils' import graphql from '../../../../graphql' import { ModifiedWhitelist } from './types' import modifyWhitelist from './modifyWhitelist' +import { getListVariables, GetListVariablesInput } from '../../../utils' -type GetWhitelistInput = { - vaultAddress: string - orderDirection?: WhitelistAccountsQueryVariables['orderDirection'] - search?: string - limit?: number - skip?: number - addressIn?: WhitelistAccountsQueryVariables['where']['address_in'] +type GetWhitelistInput = GetListVariablesInput & { options: StakeWise.Options } -const validateList = (addressIn: string[]) => { - const isValid = addressIn.every((address) => isAddress(address)) - - if (!isValid) { - throw new Error('The "addressIn" argument must be an array of valid addresses') - } -} - const getWhitelist = (input: GetWhitelistInput) => { - const { vaultAddress, orderDirection, search, limit, skip, addressIn, options } = input - - validateArgs.address({ vaultAddress }) - - if (typeof skip !== 'undefined') { - validateArgs.number({ skip }) - } - - if (typeof limit !== 'undefined') { - validateArgs.number({ limit }) - } - - if (typeof search !== 'undefined') { - validateArgs.string({ search }) - } - - if (typeof orderDirection !== 'undefined') { - if (![ 'asc', 'desc' ].includes(orderDirection)) { - throw new Error(`The "orderDirection" argument must be "asc" or "desc"`) - } - } - - if (typeof addressIn !== 'undefined') { - validateArgs.array({ addressIn }) - validateList(addressIn as string[]) - } - - const vault = vaultAddress.toLowerCase() + const { options, ...rest } = input - const where = search - ? { vault, address_in: addressIn, address_contains: search.toLowerCase() } as WhitelistAccountsQueryVariables['where'] - : { vault, address_in: addressIn } as WhitelistAccountsQueryVariables['where'] + const variables = getListVariables(rest) return graphql.subgraph.vault.fetchWhitelistAccountsQuery({ url: apiUrls.getSubgraphqlUrl(options), - variables: { - where, - skip: skip || 0, - limit: limit || 100, - orderDirection: orderDirection || 'desc', - }, + variables, modifyResult: (data: WhitelistAccountsQueryPayload) => modifyWhitelist({ data }), }) } From 7389447b8919af1fea97c237151f645fe6a94e92 Mon Sep 17 00:00:00 2001 From: MikeDiam Date: Thu, 16 Jan 2025 16:43:05 +0300 Subject: [PATCH 42/44] [remove duplicates] update local storage --- src/index.ts | 1 + src/modules/gql-module/utils/getRequestUrl.ts | 2 +- .../utils/saveErrorUrlToSessionStorage.ts | 2 +- .../utils => }/local-storage/LocalStorage.ts | 0 .../local-storage/MemoryStorage.spec.ts | 66 +++++++++++++++++++ .../utils => }/local-storage/MemoryStorage.ts | 0 .../utils => }/local-storage/index.ts | 0 7 files changed, 69 insertions(+), 2 deletions(-) rename src/modules/{gql-module/utils => }/local-storage/LocalStorage.ts (100%) create mode 100644 src/modules/local-storage/MemoryStorage.spec.ts rename src/modules/{gql-module/utils => }/local-storage/MemoryStorage.ts (100%) rename src/modules/{gql-module/utils => }/local-storage/index.ts (100%) diff --git a/src/index.ts b/src/index.ts index 53ac46a0..27bd8dce 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,7 @@ import { constants } from './utils' export * from './utils/enums' export { createContract } from './contracts' export { default as StakeWiseSDK } from './StakeWiseSDK' +export { default as localStorage } from './modules/local-storage' export { wrapAbortPromise, AbortPromise } from './modules/gql-module' export { diff --git a/src/modules/gql-module/utils/getRequestUrl.ts b/src/modules/gql-module/utils/getRequestUrl.ts index 682bb6be..f0af5845 100644 --- a/src/modules/gql-module/utils/getRequestUrl.ts +++ b/src/modules/gql-module/utils/getRequestUrl.ts @@ -1,5 +1,5 @@ import { constants } from '../../../utils' -import localStorage from './local-storage' +import localStorage from '../../local-storage' const sessionErrorUrl = constants.sessionStorageNames.moduleErrorUrl diff --git a/src/modules/gql-module/utils/saveErrorUrlToSessionStorage.ts b/src/modules/gql-module/utils/saveErrorUrlToSessionStorage.ts index 3a1937ab..00a7736b 100644 --- a/src/modules/gql-module/utils/saveErrorUrlToSessionStorage.ts +++ b/src/modules/gql-module/utils/saveErrorUrlToSessionStorage.ts @@ -1,5 +1,5 @@ import { constants } from '../../../utils' -import localStorage from './local-storage' +import localStorage from '../../local-storage' const sessionErrorUrl = constants.sessionStorageNames.moduleErrorUrl diff --git a/src/modules/gql-module/utils/local-storage/LocalStorage.ts b/src/modules/local-storage/LocalStorage.ts similarity index 100% rename from src/modules/gql-module/utils/local-storage/LocalStorage.ts rename to src/modules/local-storage/LocalStorage.ts diff --git a/src/modules/local-storage/MemoryStorage.spec.ts b/src/modules/local-storage/MemoryStorage.spec.ts new file mode 100644 index 00000000..2b34b940 --- /dev/null +++ b/src/modules/local-storage/MemoryStorage.spec.ts @@ -0,0 +1,66 @@ +import MemoryStorage from './MemoryStorage' + + +describe('MemoryStorage', () => { + + it('should write and read values', () => { + const storage = new MemoryStorage() + + storage.setItem('key1', 'value1') + storage.setItem('key2', 'value2') + + expect(storage.getItem('key1')).toEqual('value1') + expect(storage.getItem('key2')).toEqual('value2') + }) + + it('should overwrite values', () => { + const storage = new MemoryStorage() + + storage.setItem('key', 'value1') + + expect(storage.getItem('key')).toEqual('value1') + + storage.setItem('key', 'value2') + + expect(storage.getItem('key')).toEqual('value2') + }) + + it('should remove and clear', () => { + const storage = new MemoryStorage() + + storage.setItem('key1', 'value1') + storage.setItem('key2', 'value2') + storage.removeItem('key2') + + expect(storage.getItem('key1')).toEqual('value1') + expect(storage.getItem('key2')).toBeNull() + + storage.clear() + + expect(storage.length).toEqual(0) + }) + + it('should return null for undefined values', () => { + const storage = new MemoryStorage() + + expect(storage.getItem('key')).toBeNull() + }) + + it('should return null for undefined keys', () => { + const storage = new MemoryStorage() + + expect(storage.key(1)).toBeNull() + }) + + it('should return correct length', () => { + const storage = new MemoryStorage() + + storage.setItem('key1', 'value1') + + expect(storage.length).toEqual(1) + + storage.setItem('key2', 'value2') + + expect(storage.length).toEqual(2) + }) +}) diff --git a/src/modules/gql-module/utils/local-storage/MemoryStorage.ts b/src/modules/local-storage/MemoryStorage.ts similarity index 100% rename from src/modules/gql-module/utils/local-storage/MemoryStorage.ts rename to src/modules/local-storage/MemoryStorage.ts diff --git a/src/modules/gql-module/utils/local-storage/index.ts b/src/modules/local-storage/index.ts similarity index 100% rename from src/modules/gql-module/utils/local-storage/index.ts rename to src/modules/local-storage/index.ts From 726659ea9c914bcb4f0842cbb58872fec167efd6 Mon Sep 17 00:00:00 2001 From: MikeDiam Date: Fri, 17 Jan 2025 10:27:40 +0300 Subject: [PATCH 43/44] [remove duplicates] move abortRequest to sdk --- jest.config.ts | 2 +- src/index.ts | 2 +- src/modules/gql-module/abortCallback.ts | 2 - src/modules/gql-module/abortPromise.spec.ts | 157 ++++++++++++++ src/modules/gql-module/abortRequest.spec.ts | 226 ++++++++++++++++++++ src/modules/gql-module/abortRequest.ts | 28 +-- src/modules/gql-module/index.ts | 1 + 7 files changed, 402 insertions(+), 16 deletions(-) create mode 100644 src/modules/gql-module/abortPromise.spec.ts create mode 100644 src/modules/gql-module/abortRequest.spec.ts diff --git a/jest.config.ts b/jest.config.ts index fc34ebc5..cd3e9527 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -6,7 +6,7 @@ const config: Config = { maxWorkers: 1, // Fixed https://github.com/jestjs/jest/issues/11617#issuecomment-1028651059 rootDir: './src', preset: 'ts-jest', - resetMocks: true, + resetMocks: false, testEnvironment: 'node', testMatch: [ '**/*.spec.ts' ], collectCoverageFrom: [ 'src/**/*.ts' ], diff --git a/src/index.ts b/src/index.ts index 27bd8dce..9ce4d47c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,7 +6,7 @@ export * from './utils/enums' export { createContract } from './contracts' export { default as StakeWiseSDK } from './StakeWiseSDK' export { default as localStorage } from './modules/local-storage' -export { wrapAbortPromise, AbortPromise } from './modules/gql-module' +export { wrapAbortPromise, AbortPromise, AbortRequest } from './modules/gql-module' export { configs, diff --git a/src/modules/gql-module/abortCallback.ts b/src/modules/gql-module/abortCallback.ts index b5040f5b..0b18280c 100644 --- a/src/modules/gql-module/abortCallback.ts +++ b/src/modules/gql-module/abortCallback.ts @@ -14,8 +14,6 @@ class AbortCallback { then(onSuccess: (data: any) => any, onError?: (error: any) => any) { if (this.isAborted) { - const dummyPromise = new Promise(() => {}) - return new AbortCallback(dummyPromise, this.onAbort) } diff --git a/src/modules/gql-module/abortPromise.spec.ts b/src/modules/gql-module/abortPromise.spec.ts new file mode 100644 index 00000000..5cf96a9f --- /dev/null +++ b/src/modules/gql-module/abortPromise.spec.ts @@ -0,0 +1,157 @@ +import AbortPromise from './abortPromise' +import AbortCallback from './abortCallback' + + +describe('AbortPromise', () => { + + it('resolves the promise', async () => { + const mock = 'response data' + const abortPromise = new AbortPromise((resolve) => { + resolve(mock) + }) + + const result = await abortPromise + + expect(result).toEqual(mock) + }) + + it('rejects the promise', async () => { + const mock = 'error' + const abortPromise = new AbortPromise((resolve, reject) => { + reject(mock) + }) + + await expect(abortPromise).rejects.toEqual(mock) + }) + + it('calls then on promise resolve', async () => { + const mockThenFn = jest.fn() + const mockCatchFn = jest.fn() + + const abortPromise = new AbortPromise((resolve, reject) => { + resolve(null) + }) + .then(mockThenFn, mockCatchFn) + + await abortPromise + + expect(mockThenFn).toBeCalledTimes(1) + expect(mockCatchFn).toBeCalledTimes(0) + }) + + it('calls catch on promise reject', async () => { + const mockThenFn = jest.fn() + const mockCatchFn = jest.fn() + + const abortPromise = new AbortPromise((resolve, reject) => { + reject(null) + }) + .then(mockThenFn, mockCatchFn) + + await abortPromise + + expect(mockThenFn).toBeCalledTimes(0) + expect(mockCatchFn).toBeCalledTimes(1) + }) + + it('doesn\'t resolve aborted promise', async () => { + const mockThenFn = jest.fn() + const mockCatchFn = jest.fn() + + const abortPromise = new AbortPromise((resolve, reject) => { + resolve(null) + }) + .then(mockThenFn, mockCatchFn) + + abortPromise.abort() + + expect(mockThenFn).toBeCalledTimes(0) + expect(mockCatchFn).toBeCalledTimes(0) + }) + + it('doesn\'t reject aborted promise', async () => { + const mockThenFn = jest.fn() + const mockCatchFn = jest.fn() + + const abortPromise = new AbortPromise((resolve, reject) => { + reject(null) + }) + .then(mockThenFn, mockCatchFn) + + abortPromise.abort() + + expect(mockThenFn).toBeCalledTimes(0) + expect(mockCatchFn).toBeCalledTimes(0) + }) + + it('resolves multiple promises in "all" method', async () => { + const mock1 = 'response data 1' + const mock2 = 'response data 2' + + const promise1 = new Promise((resolve) => resolve(mock1)) + const promise2 = new Promise((resolve) => resolve(mock2)) + + const result = await AbortPromise.all([ promise1, promise2 ]) + + expect(result).toEqual([ mock1, mock2 ]) + }) + + it('resolves promise in "race" method', async () => { + const mock = 'response data' + + const promise1 = new Promise((resolve) => resolve(mock)) + const promise2 = new Promise(() => {}) + + const result = await AbortPromise.race([ promise1, promise2 ]) + + expect(result).toEqual(mock) + }) + + it('resolves multiple abort promises in "all" method', async () => { + const mock1 = 'response data 1' + const mock2 = 'response data 2' + + const promise1 = new AbortPromise((resolve) => resolve(mock1)) + const promise2 = new AbortPromise((resolve) => resolve(mock2)) + + const result = await AbortPromise.all([ promise1, promise2 ]) + + expect(result).toEqual([ mock1, mock2 ]) + }) + + it('doesn\'t resolve multiple promises in "all" method on abort', async () => { + const abort1 = jest.fn() + const abort2 = jest.fn() + const mockThenFn = jest.fn() + const mockCatchFn = jest.fn() + + const dummyPromise = new Promise(() => {}) + + const promise1 = new AbortCallback(dummyPromise, abort1) + const promise2 = new AbortCallback(dummyPromise, abort2) + + // @ts-ignore + const promise = AbortPromise.all([ promise1, promise2 ]) + .then(mockThenFn, mockCatchFn) + + promise.abort() + + expect(abort1).toBeCalledTimes(1) + expect(abort2).toBeCalledTimes(1) + expect(mockThenFn).toBeCalledTimes(0) + expect(mockCatchFn).toBeCalledTimes(0) + }) + + it('doesn\'t reject multiple promises in "all" method on abort', async () => { + const mockThenFn = jest.fn() + const mockCatchFn = jest.fn() + + const promise = AbortPromise.all([ Promise.reject(), Promise.reject() ]) + .then(mockThenFn, mockCatchFn) + + promise.abort() + + expect(mockThenFn).toBeCalledTimes(0) + expect(mockCatchFn).toBeCalledTimes(0) + }) +}) diff --git a/src/modules/gql-module/abortRequest.spec.ts b/src/modules/gql-module/abortRequest.spec.ts new file mode 100644 index 00000000..b56844c4 --- /dev/null +++ b/src/modules/gql-module/abortRequest.spec.ts @@ -0,0 +1,226 @@ +import fetchMock from 'jest-fetch-mock' + +import AbortRequest from './abortRequest' + + +beforeEach(() => { + fetchMock.enableMocks() +}) + +afterEach(() => { + fetchMock.resetMocks() +}) + +describe('AbortRequest', () => { + it('resolves the request', async () => { + const mock = 'response data' + fetchMock.mockResponse(JSON.stringify({ data: mock })) + + const abortRequest = new AbortRequest('https://example.com', { + method: 'GET', + onSuccess: (data) => data, + }) + + const result = await abortRequest + + expect(result).toEqual(mock) + expect(fetchMock.mock.calls.length).toBe(1) + }) + + it('resolves modified data', async () => { + const mock = { test: 'response data' } + fetchMock.mockResponse(JSON.stringify({ data: mock })) + + const abortRequest = new AbortRequest<{ test: string }, string>('https://example.com', { + method: 'GET', + onSuccess: (data) => data.test, + }) + + const result = await abortRequest + + expect(result).toEqual(mock.test) + expect(fetchMock.mock.calls.length).toBe(1) + }) + + it('rejects the request', async () => { + fetchMock.mockReject(new Error('Request failed')) + + const abortRequest = new AbortRequest('https://example.com', { + method: 'GET', + onSuccess: (data) => data, + }) + + await expect(abortRequest).rejects.toThrow('Request failed') + expect(fetchMock.mock.calls.length).toBe(1) + }) + + it('aborts the request', async () => { + fetchMock.mockResponse(JSON.stringify({ data: 'response data' })) + + const abortRequest = new AbortRequest('https://example.com', { + method: 'GET', + onSuccess: (data) => data, + }) + + abortRequest.abort() + + const [ url, requestInit ] = fetchMock.mock.calls[0] + + expect(requestInit?.signal?.aborted).toEqual(true) + expect(fetchMock.mock.calls.length).toBe(1) + }) + + it('handles multiple requests for the same body', async () => { + const mock = 'response data' + + fetchMock.mockResponse(JSON.stringify({ data: mock })) + + const url = 'https://example.com' + const options = { + method: 'POST', + body: 'sameBody', + onSuccess: (data: string) => data, + } + + const abortRequest1 = new AbortRequest(url, options) + const abortRequest2 = new AbortRequest(url, options) + + expect(abortRequest1.request).toBe(abortRequest2.request) + + const [ result1, result2 ] = await Promise.all([ + abortRequest1, + abortRequest2, + ]) + + expect(result1).toEqual(mock) + expect(result2).toEqual(mock) + expect(fetchMock.mock.calls.length).toBe(1) + }) + + it('resolves the non-aborted request when multiple requests with the same body', async () => { + const mock = 'response data' + + fetchMock.mockResponse(JSON.stringify({ data: mock })) + + const url = 'https://example.com' + const options = { + method: 'POST', + body: 'sameBody', + onSuccess: (data: string) => data, + } + + const abortRequest1 = new AbortRequest(url, options) + const abortRequest2 = new AbortRequest(url, options) + + abortRequest1.abort() + + const result = await abortRequest2 + + expect(result).toEqual(mock) + expect(fetchMock.mock.calls.length).toBe(1) + }) + + it('has "abort" method after "then", "catch" and "finally" methods', async () => { + const mock = 'response data' + + fetchMock.mockResponse(JSON.stringify({ data: mock })) + + const catchCallback = jest.fn() + const finallyCallback = jest.fn() + const onSuccessCallback = jest.fn() + const thenErrorCallback = jest.fn() + const thenSuccessCallback = jest.fn() + + const initialRequest = new AbortRequest('https://example.com', { + method: 'POST', + body: 'sameBody', + onSuccess: onSuccessCallback, + }) + const abortRequest = initialRequest + .then(thenSuccessCallback, thenErrorCallback) + .catch(catchCallback) + .finally(finallyCallback) + + abortRequest.abort() + + const [ url, requestInit ] = fetchMock.mock.calls[0] + + expect(requestInit?.signal?.aborted).toEqual(true) + expect(fetchMock.mock.calls.length).toBe(1) + expect(thenSuccessCallback).not.toHaveBeenCalled() + expect(thenErrorCallback).not.toHaveBeenCalled() + expect(catchCallback).not.toHaveBeenCalled() + expect(finallyCallback).not.toHaveBeenCalled() + expect(onSuccessCallback).not.toHaveBeenCalled() + }) + + it('rejects the error in "catch" method', async () => { + const error = 'Request failed' + fetchMock.mockReject(new Error(error)) + + const abortRequest = new AbortRequest('https://example.com', { + method: 'GET', + onSuccess: (data) => data, + }) + .catch((error: any) => error.message) + + const data = await abortRequest + + await expect(data).toBe(error) + expect(fetchMock.mock.calls.length).toBe(1) + }) + + it('calls methods if promise is successful', async () => { + fetchMock.mockResponse(JSON.stringify({ data: 'response data' })) + + const catchCallback = jest.fn() + const finallyCallback = jest.fn() + const onSuccessCallback = jest.fn() + const thenErrorCallback = jest.fn() + const thenSuccessCallback = jest.fn() + + const abortRequest = new AbortRequest('https://example.com', { + method: 'GET', + onSuccess: onSuccessCallback, + }) + .then(thenSuccessCallback, thenErrorCallback) + .catch(catchCallback) + .finally(finallyCallback) + + await abortRequest + + expect(finallyCallback).toHaveBeenCalled() + expect(onSuccessCallback).toHaveBeenCalled() + expect(thenSuccessCallback).toHaveBeenCalled() + expect(thenErrorCallback).not.toHaveBeenCalled() + expect(catchCallback).not.toHaveBeenCalled() + }) + + it('calls methods if promise is not successful', async () => { + fetchMock.mockReject(new Error('Request failed')) + + const catchCallback = jest.fn() + const finallyCallback = jest.fn() + const onSuccessCallback = jest.fn() + const thenSuccessCallback = jest.fn() + const thenErrorCallback = jest.fn() + + thenErrorCallback.mockImplementation(() => Promise.reject()) + + const abortRequest = new AbortRequest('https://example.com', { + method: 'GET', + onSuccess: onSuccessCallback, + }) + .then(thenSuccessCallback, thenErrorCallback) + .catch(catchCallback) + .finally(finallyCallback) + + await abortRequest + + expect(finallyCallback).toHaveBeenCalled() + expect(onSuccessCallback).not.toHaveBeenCalled() + expect(thenSuccessCallback).not.toHaveBeenCalled() + expect(thenErrorCallback).toHaveBeenCalled() + expect(catchCallback).toHaveBeenCalled() + }) +}) diff --git a/src/modules/gql-module/abortRequest.ts b/src/modules/gql-module/abortRequest.ts index c3766f06..b261d856 100644 --- a/src/modules/gql-module/abortRequest.ts +++ b/src/modules/gql-module/abortRequest.ts @@ -7,11 +7,9 @@ type FirstCallback = (value: Data) => Data | any type EmptyCallback = () => void -type ErrorCallback = (error: Error | any) => Promise | AbortRequest - type AbortRequestInit = RequestInit & { - onSuccess: ModifyCallback - onError?: ErrorCallback + onSuccess?: ModifyCallback + onError?: (error: any) => Promise | AbortRequest } type PendingRequest = { @@ -21,9 +19,11 @@ type PendingRequest = { const requestsQueue: Record = {} +const dummyPromise = new Promise(() => {}) + // Returns fetch promise that can be aborted // If we create several promises, only one request will be executed -class AbortRequest { +class AbortRequest { private controller = new AbortController() request: Promise promise: AbortPromise @@ -36,8 +36,8 @@ class AbortRequest { this.body = init.body as string this.isAborted = false - this.requestId = `${url}_${this.body}` + this.requestId = `${url}_${this.body}` const pendingRequest = requestsQueue[this.requestId] if (pendingRequest) { @@ -68,16 +68,20 @@ class AbortRequest { throw new Error('Subgraph indexing error') } - return json?.data as Data + return (json?.data || json) as Data }) .catch((error) => { - requestsQueue[this.requestId] = undefined + if (!this.isAborted) { + requestsQueue[this.requestId] = undefined + + if (typeof onError === 'function') { + onError(error) + } - if (typeof onError === 'function') { - onError(error) + return Promise.reject(error) } - return Promise.reject(error) + return dummyPromise as Data }) requestsQueue[this.requestId] = { @@ -129,7 +133,7 @@ class AbortRequest { } else { requestsQueue[this.requestId] = undefined - this.controller.abort() + this.controller.abort('aborted') } } } diff --git a/src/modules/gql-module/index.ts b/src/modules/gql-module/index.ts index 352e0d23..19b947c9 100644 --- a/src/modules/gql-module/index.ts +++ b/src/modules/gql-module/index.ts @@ -1,4 +1,5 @@ export { default as graphqlFetch } from './graphqlFetch' export { default as AbortPromise } from './abortPromise' +export { default as AbortRequest } from './abortRequest' export type { FetchCodegenInput, FetchInput } from './types' export { default as wrapAbortPromise } from './wrapAbortPromise' From 9ba8220116564cd6a2752ae6fd070fc8eb7308e2 Mon Sep 17 00:00:00 2001 From: MikeDiam Date: Fri, 17 Jan 2025 10:47:17 +0300 Subject: [PATCH 44/44] [remove duplicates] rebase --- src/contracts/createContracts.ts | 3 -- .../subgraph/stats/fiatByDayQuery._graphql | 21 -------- .../subgraph/stats/fiatRatesQuery._graphql | 12 ----- .../subgraph/vault/userRewardsQuery._graphql | 38 -------------- src/index.ts | 10 ---- src/methods/osToken/requests/getPosition.md | 50 ------------------- src/methods/vault/requests/getUserStats.md | 49 ------------------ src/utils/configs/chiado.ts | 3 -- src/utils/configs/gnosis.ts | 3 -- src/utils/configs/holesky.ts | 4 -- src/utils/configs/mainnet.ts | 4 -- 11 files changed, 197 deletions(-) delete mode 100644 src/graphql/subgraph/stats/fiatByDayQuery._graphql delete mode 100644 src/graphql/subgraph/stats/fiatRatesQuery._graphql delete mode 100644 src/graphql/subgraph/vault/userRewardsQuery._graphql delete mode 100644 src/methods/osToken/requests/getPosition.md delete mode 100644 src/methods/vault/requests/getUserStats.md diff --git a/src/contracts/createContracts.ts b/src/contracts/createContracts.ts index d851b67b..43b88b3b 100644 --- a/src/contracts/createContracts.ts +++ b/src/contracts/createContracts.ts @@ -172,9 +172,6 @@ export const createContracts = (input: CreateContractsInput) => { stakeCalculator: getStakeCalculator(provider, config), leverageStrategy: getLeverageStrategy(provider, config), }, - special: { - stakeCalculator: getStakeCalculator(provider, config), - }, } } diff --git a/src/graphql/subgraph/stats/fiatByDayQuery._graphql b/src/graphql/subgraph/stats/fiatByDayQuery._graphql deleted file mode 100644 index d2cf1fff..00000000 --- a/src/graphql/subgraph/stats/fiatByDayQuery._graphql +++ /dev/null @@ -1,21 +0,0 @@ -query FiatByDay( - $dateTo: Timestamp! - $dateFrom: Timestamp! -) { - exchangeRate: exchangeRateStats_collection( - interval: day - where: { - timestamp_gte: $dateFrom, - timestamp_lte: $dateTo - } - ) { - timestamp - usdToEurRate - usdToGbpRate - usdToCnyRate - usdToJpyRate - usdToKrwRate - usdToAudRate - assetsUsdRate - } -} diff --git a/src/graphql/subgraph/stats/fiatRatesQuery._graphql b/src/graphql/subgraph/stats/fiatRatesQuery._graphql deleted file mode 100644 index 05ee8034..00000000 --- a/src/graphql/subgraph/stats/fiatRatesQuery._graphql +++ /dev/null @@ -1,12 +0,0 @@ -query FiatRatesNew { - networks { - assetsUsdRate - usdToEurRate - usdToGbpRate - usdToCnyRate - usdToJpyRate - usdToKrwRate - usdToAudRate - swiseUsdRate - } -} diff --git a/src/graphql/subgraph/vault/userRewardsQuery._graphql b/src/graphql/subgraph/vault/userRewardsQuery._graphql deleted file mode 100644 index 44b28f26..00000000 --- a/src/graphql/subgraph/vault/userRewardsQuery._graphql +++ /dev/null @@ -1,38 +0,0 @@ -query UserRewards( - $user: Bytes! - $dateTo: Timestamp! - $dateFrom: Timestamp! - $vaultAddress: String! - $includeExchangeRate: Boolean! -) { - exchangeRates: exchangeRateStats_collection ( - interval: day, - where: { - timestamp_gte: $dateFrom, - timestamp_lte: $dateTo - } - ) @include(if: $includeExchangeRate) { - timestamp - usdToEurRate - usdToGbpRate - usdToCnyRate - usdToJpyRate - usdToKrwRate - usdToAudRate - assetsUsdRate - } - allocator: allocatorStats_collection( - interval: day - where: { - timestamp_gte: $dateFrom, - timestamp_lte: $dateTo, - allocator_: { - address: $user, - vault: $vaultAddress - } - } - ) { - timestamp - earnedAssets - } -} diff --git a/src/index.ts b/src/index.ts index 9ce4d47c..301d978f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,14 +18,4 @@ export { calculateUserStats, } from './utils' -export { - configs, - BigDecimal, - getGas, - getTimestamp, - createProvider, - mergeRewardsFiat, - calculateUserStats, -} from './utils' - export const chains = constants.chains diff --git a/src/methods/osToken/requests/getPosition.md b/src/methods/osToken/requests/getPosition.md deleted file mode 100644 index 2af949ee..00000000 --- a/src/methods/osToken/requests/getPosition.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -id: getPosition -slug: /osToken/requests/getposition ---- - -#### Description: - -User position data - -#### Arguments: -| Name | Type | Required | Description | -|------------------|----------|----------|--------------------------------------------------------------| -| liqThresholdPercent | `bigint` | **Yes** | [sdk.vault.getVault](/vault/requests/getvault) | -| stakedAssets | `bigint` | **Yes** | [sdk.vault.getStakeBalance](/vault/requests/getstakebalance) | -| userAddress | `string` | **Yes** | The user address | -| vaultAddress | `string` | **Yes** | The address of the vault | - -#### Returns: - -```ts -type Output = { - minted: { - assets: bigint - shares: bigint - } - healthFactor: { - value: number - health: OsTokenPositionHealth - } - protocolFeePercent: bigint -} -``` - -| Name | Description | -|----------------------|-----------------------------------------------------------------| -| `minted.shares` | Balance | -| `minted.assets` | Balance in ETH | -| `healthFactor` | [sdk.osToken.getHealthFactor](/osToken/helpers/gethealthfactor) | -| `protocolFeePercent` | Usage fee percent | - -#### Example: - -```ts -await sdk.osToken.getPosition({ - stakedAssets: 0n, - userAddress: '0x...', - vaultAddress: '0x...', - liqThresholdPercent: 0n, -}) -``` diff --git a/src/methods/vault/requests/getUserStats.md b/src/methods/vault/requests/getUserStats.md deleted file mode 100644 index 479987d9..00000000 --- a/src/methods/vault/requests/getUserStats.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -id: getUserStats -slug: /vault/requests/getuserstats ---- - -#### Description: - -Getting the user stats collection for current vault. -With the help of this data it is possible to build a chart. - -#### Arguments: - -| Name | Type | Required | Description | -|--------------|----------|----------|--------------------------| -| daysCount | `number` | **Yes** | The limit in days | -| userAddress | `string` | **Yes** | The user address | -| vaultAddress | `string` | **Yes** | The address of the vault | - -#### Returns: - -```ts -type Stat = { - time: number - value: number -} - -type Output = { - apy: Stat[] - balance: Stat[] - rewards: Stat[] -} -``` - -| Name | Description | -|-----------|---------------------------------------------------------------------------------| -| `time` | Date and time for each data point | -| `apy` | Current APY based on time, rewards and balance. | -| `rewards` | Number of assets earned by the user in current vault during the interval in ETH | -| `balance` | Total assets by the user in current vault at the moment of time in ETH | - -#### Example: - -```ts -await sdk.vault.getUserStats({ - daysCount: 30, - userAddress: '0x...', - vaultAddress: '0x...', -}) -``` diff --git a/src/utils/configs/chiado.ts b/src/utils/configs/chiado.ts index 6be1a8dd..14750f6c 100644 --- a/src/utils/configs/chiado.ts +++ b/src/utils/configs/chiado.ts @@ -46,9 +46,6 @@ export default { stakeCalculator: '0x35704E96851d4aDd48475757b8f9bbb2390D9e4E', leverageStrategy: ZeroAddress, }, - special: { - stakeCalculator: ZeroAddress, - }, }, tokens: { swise: constants.tokens.swise, diff --git a/src/utils/configs/gnosis.ts b/src/utils/configs/gnosis.ts index 7fab8cdc..23c9f14d 100644 --- a/src/utils/configs/gnosis.ts +++ b/src/utils/configs/gnosis.ts @@ -46,9 +46,6 @@ export default { stakeCalculator: '0x2A415b65207049AC7481BF69ff9fc1B3Def97c9A', leverageStrategy: ZeroAddress, }, - special: { - stakeCalculator: '0x3c5634a5437A394353F49fe04FE5db11961c5c2D', - }, }, tokens: { swise: constants.tokens.swise, diff --git a/src/utils/configs/holesky.ts b/src/utils/configs/holesky.ts index 1fc38d87..da82a369 100644 --- a/src/utils/configs/holesky.ts +++ b/src/utils/configs/holesky.ts @@ -46,10 +46,6 @@ export default { stakeCalculator: '0x8381012Dad419808125F009351732af36d4e1507', leverageStrategy: '0xdB38cfc6e98a34Cdc60c568f607417E646C75B34', }, - special: { - stakeCalculator: '0x90b82e4b3aa385b4a02b7ebc1892a4bed6b5c465', - leverageStrategy: '0xCC7657981B288f811f099235fc9E96A3b77Bca0C', - }, }, tokens: { swise: constants.tokens.swise, diff --git a/src/utils/configs/mainnet.ts b/src/utils/configs/mainnet.ts index deafb72c..5597340b 100644 --- a/src/utils/configs/mainnet.ts +++ b/src/utils/configs/mainnet.ts @@ -51,10 +51,6 @@ export default { stakeCalculator: '0x75c57bd50A3EB7291Da3429956D3566E0153A38f', leverageStrategy: '0x48cD14FDB8e72A03C8D952af081DBB127D6281fc', }, - special: { - stakeCalculator: '0x29c708d94521af2c88402858049bd33e4606a3a2', - leverageStrategy: '0xc57E59764f398831C1444c14d296Af868d533196', - }, }, tokens: { swise: constants.tokens.swise,