From 6a834c20c929e893a611e6999b1caa6277008e06 Mon Sep 17 00:00:00 2001 From: matheus1lva Date: Wed, 7 Jan 2026 13:23:04 -0300 Subject: [PATCH 1/6] feat: replace APR oracle contracts with Kong GraphQL API for V3 vault forward APY - Update Kong GraphQL queries to fetch performance.oracle { apr, apy } fields - Add KongOracle and KongPerformance types to handle nullable float64 data - Update indexer to extract and store oracle data in TKongVaultSchema - Add GetKongOracleAPY() accessor function to storage layer - Replace on-chain oracle contract calls with Kong data fetching in forward.v3.go - Remove APR Oracle contract configs from chain files (keep struct field for strategy-level usage) - Maintain backward compatibility with v3:onchainOracle response type - Implement graceful degradation: return zero APY when Kong data unavailable --- common/env/chain.arbitrum.go | 4 -- common/env/chain.base.go | 4 -- common/env/chain.ethereum.go | 4 -- common/env/chain.gnosis.go | 1 - common/env/chain.katana.go | 4 -- common/env/chain.polygon.go | 4 -- common/env/chains.go | 2 +- internal/indexer/indexer.kong.go | 22 +++++++--- internal/kong/client.go | 22 ++++++++++ internal/models/vaults.go | 22 +++++++--- internal/storage/elem.vaults.go | 12 ++++++ processes/apr/forward.v3.go | 73 +++++++++++++++++--------------- 12 files changed, 105 insertions(+), 69 deletions(-) diff --git a/common/env/chain.arbitrum.go b/common/env/chain.arbitrum.go index e6669b234..0896e77be 100644 --- a/common/env/chain.arbitrum.go +++ b/common/env/chain.arbitrum.go @@ -30,10 +30,6 @@ var ARBITRUM = TChain{ Address: common.HexToAddress(`0x0e5b46E4b2a05fd53F5a4cD974eb98a9a613bcb7`), Block: 30385403, }, - APROracleContract: TContractData{ - Address: common.HexToAddress(`0x1981AD9F44F2EA9aDd2dC4AD7D075c102C70aF92`), - Block: 265347717, - }, StakingRewardRegistry: []TContractData{ { Address: common.HexToAddress(`0x26d8EA1d8759d0F9abBcf8181b1fD5D3635daD69`), diff --git a/common/env/chain.base.go b/common/env/chain.base.go index d0881c6bc..a3d666d6a 100644 --- a/common/env/chain.base.go +++ b/common/env/chain.base.go @@ -67,10 +67,6 @@ var BASE = TChain{ Label: `YEARN`, }, }, - APROracleContract: TContractData{ - Address: common.HexToAddress(`0x1981AD9F44F2EA9aDd2dC4AD7D075c102C70aF92`), - Block: 63167152, - }, ExtraVaults: []models.TVaultsFromRegistry{}, BlacklistedVaults: []common.Address{}, ExtraTokens: []common.Address{ diff --git a/common/env/chain.ethereum.go b/common/env/chain.ethereum.go index ad39d95ef..bb8ed431c 100644 --- a/common/env/chain.ethereum.go +++ b/common/env/chain.ethereum.go @@ -34,10 +34,6 @@ var ETHEREUM = TChain{ Address: common.HexToAddress(`0x8ee392a4787397126C163Cb9844d7c447da419D8`), Block: 14166636, }, - APROracleContract: TContractData{ - Address: common.HexToAddress(`0x1981AD9F44F2EA9aDd2dC4AD7D075c102C70aF92`), - Block: 19070394, - }, ExtraStakingContracts: []TExtraStakingContracts{ { VaultAddress: common.HexToAddress(`0xe24BA27551aBE96Ca401D39761cA2319Ea14e3CB`), diff --git a/common/env/chain.gnosis.go b/common/env/chain.gnosis.go index 341d51d2b..9306deeca 100644 --- a/common/env/chain.gnosis.go +++ b/common/env/chain.gnosis.go @@ -24,7 +24,6 @@ var GNOSIS = TChain{ Block: 821923, }, PartnerContract: TContractData{}, - APROracleContract: TContractData{}, Coin: models.TERC20Token{ Address: DEFAULT_COIN_ADDRESS, UnderlyingTokensAddresses: []common.Address{}, diff --git a/common/env/chain.katana.go b/common/env/chain.katana.go index 8fad2bb49..3c44473aa 100644 --- a/common/env/chain.katana.go +++ b/common/env/chain.katana.go @@ -63,10 +63,6 @@ var KATANA = TChain{ Label: `PUBLIC_ERC4626`, }, }, - APROracleContract: TContractData{ - Address: common.HexToAddress(`0x1981AD9F44F2EA9aDd2dC4AD7D075c102C70aF92`), - Block: 2237016, - }, ExtraVaults: []models.TVaultsFromRegistry{}, BlacklistedVaults: []common.Address{}, ExtraTokens: []common.Address{}, diff --git a/common/env/chain.polygon.go b/common/env/chain.polygon.go index 1b9d44fbf..751c42d04 100644 --- a/common/env/chain.polygon.go +++ b/common/env/chain.polygon.go @@ -23,10 +23,6 @@ var POLYGON = TChain{ Address: common.HexToAddress(`0xca11bde05977b3631167028862be2a173976ca11`), Block: 25770160, }, - APROracleContract: TContractData{ - Address: common.HexToAddress(`0x1981AD9F44F2EA9aDd2dC4AD7D075c102C70aF92`), - Block: 52516525, - }, Coin: models.TERC20Token{ Address: DEFAULT_COIN_ADDRESS, UnderlyingTokensAddresses: []common.Address{}, diff --git a/common/env/chains.go b/common/env/chains.go index 084c4dc4f..df399a7a8 100644 --- a/common/env/chains.go +++ b/common/env/chains.go @@ -74,7 +74,7 @@ type TChain struct { MulticallContract TContractData YBribeV3Contract TContractData PartnerContract TContractData - APROracleContract TContractData + APROracleContract TContractData // Deprecated for vault-level APY (uses Kong), still used for strategy-level APR Coin models.TERC20Token StakingRewardRegistry []TContractData Registries []TContractData diff --git a/internal/indexer/indexer.kong.go b/internal/indexer/indexer.kong.go index 0d30ffa16..6e6522c78 100644 --- a/internal/indexer/indexer.kong.go +++ b/internal/indexer/indexer.kong.go @@ -67,13 +67,23 @@ func IndexNewVaults(chainID uint64) map[common.Address]models.TVaultsFromRegistr }) } + // Extract performance data from Kong response (oracle APR/APY) + performance := models.TKongPerformance{} + if data.Vault.Performance != nil { + performance.Oracle = models.TKongOracle{ + Apr: data.Vault.Performance.Oracle.Apr, + Apy: data.Vault.Performance.Oracle.Apy, + } + } + kongSchema := models.TKongVaultSchema{ - ManagementFee: data.Vault.GetManagementFee(), - PerformanceFee: data.Vault.GetPerformanceFee(), - APY: data.APY, - Debts: debts, - TVL: data.Vault.GetTVL(), - TotalAssets: data.TotalAssets, + ManagementFee: data.Vault.GetManagementFee(), + PerformanceFee: data.Vault.GetPerformanceFee(), + APY: data.APY, + Performance: performance, + Debts: debts, + TVL: data.Vault.GetTVL(), + TotalAssets: data.TotalAssets, StrategyAddresses: data.Vault.GetStrategies(), } storage.StoreKongVaultData(chainID, vaultAddr, kongSchema) diff --git a/internal/kong/client.go b/internal/kong/client.go index 12a33a3ac..0702d3c70 100644 --- a/internal/kong/client.go +++ b/internal/kong/client.go @@ -72,6 +72,15 @@ type KongFees struct { PerformanceFee float64 `json:"performanceFee"` } +type KongOracle struct { + Apr *float64 `json:"apr"` // Float or null + Apy *float64 `json:"apy"` // Float or null +} + +type KongPerformance struct { + Oracle KongOracle `json:"oracle"` +} + type KongVault struct { Address string `json:"address"` ChainID int `json:"chainId"` @@ -84,6 +93,7 @@ type KongVault struct { Strategies []string `json:"strategies"` Debts []KongDebt `json:"debts"` TVL *KongTVL `json:"tvl"` + Performance *KongPerformance `json:"performance"` // Oracle APR/APY data APY models.KongAPY `json:"apy"` TotalAssets *bigNumber.Int `json:"totalAssets"` ManagementFee *string `json:"managementFee"` // BigInt as string (basis points) @@ -200,6 +210,12 @@ func (c *Client) FetchVaultsForChain(ctx context.Context, chainID uint64) ([]Kon managementFee performanceFee } + performance { + oracle { + apr + apy + } + } apy { pricePerShare weeklyNet @@ -281,6 +297,12 @@ func (c *Client) FetchAllVaults(ctx context.Context) (map[uint64][]KongVault, er managementFee performanceFee } + performance { + oracle { + apr + apy + } + } apy { pricePerShare weeklyNet diff --git a/internal/models/vaults.go b/internal/models/vaults.go index e5787842e..7b2e1d0a5 100644 --- a/internal/models/vaults.go +++ b/internal/models/vaults.go @@ -316,6 +316,15 @@ type KongAPY struct { Decimals uint64 `json:"decimals"` // Token decimals for PPS normalization } +type TKongOracle struct { + Apr *float64 `json:"apr"` // Float or null + Apy *float64 `json:"apy"` // Float or null +} + +type TKongPerformance struct { + Oracle TKongOracle `json:"oracle"` +} + type TKongVaultSchema struct { Hook struct { Fees struct { @@ -327,11 +336,12 @@ type TKongVaultSchema struct { ManagementFee CoercibleUint64 `json:"managementFee"` PerformanceFee CoercibleUint64 `json:"performanceFee"` } `json:"snapshot"` - TVL float64 `json:"tvl"` // TVL from Kong (tvl.close field) - Debts []TKongDebt `json:"debts"` // Debts array from Kong - TotalAssets *bigNumber.Int `json:"totalAssets"` // Total assets from Kong - APY KongAPY `json:"apy"` - ManagementFee uint64 `json:"managementFee"` // Basis points from Kong (direct field takes priority) - PerformanceFee uint64 `json:"performanceFee"` // Basis points from Kong (direct field takes priority) + TVL float64 `json:"tvl"` // TVL from Kong (tvl.close field) + Debts []TKongDebt `json:"debts"` // Debts array from Kong + TotalAssets *bigNumber.Int `json:"totalAssets"` // Total assets from Kong + Performance TKongPerformance `json:"performance"` // Oracle APR/APY data from Kong + APY KongAPY `json:"apy"` + ManagementFee uint64 `json:"managementFee"` // Basis points from Kong (direct field takes priority) + PerformanceFee uint64 `json:"performanceFee"` // Basis points from Kong (direct field takes priority) StrategyAddresses []common.Address `json:"strategyAddresses"` // Strategy addresses from Kong } diff --git a/internal/storage/elem.vaults.go b/internal/storage/elem.vaults.go index 233d7cd6e..b6b75864e 100644 --- a/internal/storage/elem.vaults.go +++ b/internal/storage/elem.vaults.go @@ -460,4 +460,16 @@ func GetKongTotalAssets(chainID uint64, vaultAddress common.Address) (*bigNumber return nil, false } return data.TotalAssets, true +} + +/************************************************************************************************** +** GetKongOracleAPY retrieves oracle APR/APY from Kong data for a vault +** Returns (apr, apy, exists) where apr and apy are *float64 (may be nil) +**************************************************************************************************/ +func GetKongOracleAPY(chainID uint64, vaultAddress common.Address) (*float64, *float64, bool) { + data, ok := GetKongVaultData(chainID, vaultAddress) + if !ok { + return nil, nil, false + } + return data.Performance.Oracle.Apr, data.Performance.Oracle.Apy, true } \ No newline at end of file diff --git a/processes/apr/forward.v3.go b/processes/apr/forward.v3.go index 4bd80ee21..031e71da0 100644 --- a/processes/apr/forward.v3.go +++ b/processes/apr/forward.v3.go @@ -1,17 +1,12 @@ package apr import ( - "math/big" "strings" - "github.com/ethereum/go-ethereum/common" "github.com/yearn/ydaemon/common/bigNumber" - "github.com/yearn/ydaemon/common/contracts" - "github.com/yearn/ydaemon/common/env" - "github.com/yearn/ydaemon/common/ethereum" - "github.com/yearn/ydaemon/common/helpers" "github.com/yearn/ydaemon/common/logs" "github.com/yearn/ydaemon/internal/models" + "github.com/yearn/ydaemon/internal/storage" ) func isV3Vault(vault models.TVault) bool { @@ -23,47 +18,55 @@ func computeVaultV3ForwardAPY( vault models.TVault, allStrategiesForVault map[string]models.TStrategy, ) TForwardAPY { - oracleAPR := bigNumber.NewFloat(0) - chain, ok := env.GetChain(vault.ChainID) - if !ok { - return TForwardAPY{} - } - oracleContract := chain.APROracleContract.Address - if oracleContract == common.HexToAddress(``) { - return TForwardAPY{} - } - oracle, err := contracts.NewYVaultsV3APROracleCaller(oracleContract, ethereum.GetRPC(vault.ChainID)) - if err != nil { - logs.Error(err) - return TForwardAPY{} - } + chainID := vault.ChainID /********************************************************************************************** - ** Use the oracle to get the APR of the vault. The oracle automatically handles: - ** - Single strategy vaults: Returns strategy APR - ** - Multi-strategy vaults: Returns weighted average with performance fees applied + ** Fetch Kong oracle APR/APY data (single source of truth) + ** Kong provides pre-calculated oracle APR and APY from daily timeseries hook **********************************************************************************************/ - expected, err := oracle.GetStrategyApr(nil, vault.Address, big.NewInt(0)) - if err != nil { - logs.Error(`GetStrategyApr failed for vault ` + vault.Address.Hex() + `: ` + err.Error()) - return TForwardAPY{} + oracleAPR, oracleAPY, ok := storage.GetKongOracleAPY(chainID, vault.Address) + + if !ok { + logs.Error("Kong oracle data missing for vault %s on chain %d - Kong source unavailable", vault.Address.Hex(), chainID) + return TForwardAPY{ + Type: `v3:onchainOracle`, + NetAPY: bigNumber.NewFloat(0), + Composite: TCompositeData{ + V3OracleCurrentAPR: bigNumber.NewFloat(0), + V3OracleStratRatioAPR: bigNumber.NewFloat(0), + }, + } + } + + // Handle nil values (Kong returns null for vaults without oracle data) + if oracleAPY == nil { + logs.Warning("Kong oracle APY is null for vault %s on chain %d - vault may not have oracle configured", vault.Address.Hex(), chainID) + return TForwardAPY{ + Type: `v3:onchainOracle`, + NetAPY: bigNumber.NewFloat(0), + Composite: TCompositeData{ + V3OracleCurrentAPR: bigNumber.NewFloat(0), + V3OracleStratRatioAPR: bigNumber.NewFloat(0), + }, + } } - oracleAPR = helpers.ToNormalizedAmount(bigNumber.SetInt(expected), 18) /********************************************************************************************** - ** Use the oracle APR as the primary APR (no manual calculation needed) + ** Use Kong's pre-calculated oracle APY + ** Kong already converts APR to APY using weekly compounding (52 periods/year) **********************************************************************************************/ - primaryAPR := oracleAPR - - primaryAPRFloat64, _ := primaryAPR.Float64() - primaryAPY := bigNumber.NewFloat(0).SetFloat64(convertFloatAPRToAPY(primaryAPRFloat64, 52)) + primaryAPY := bigNumber.NewFloat(*oracleAPY) + primaryAPR := bigNumber.NewFloat(0) + if oracleAPR != nil { + primaryAPR = bigNumber.NewFloat(*oracleAPR) + } return TForwardAPY{ Type: `v3:onchainOracle`, NetAPY: primaryAPY, Composite: TCompositeData{ - V3OracleCurrentAPR: primaryAPY, - V3OracleStratRatioAPR: bigNumber.NewFloat(0), + V3OracleCurrentAPR: primaryAPR, + V3OracleStratRatioAPR: bigNumber.NewFloat(0), // Not used with Kong oracle }, } } From 3dc7a3bb3a4f62eddb5fdc677beeec0b94376089 Mon Sep 17 00:00:00 2001 From: matheus1lva Date: Sun, 18 Jan 2026 11:19:58 -0300 Subject: [PATCH 2/6] add oracleApr from kong --- CLAUDE.md | 3 +- common/contracts/yVaultsV3.APROracle.go | 357 ------------------------ common/env/chains.go | 1 - internal/fetcher/strategies.go | 7 +- processes/apr/forward.strategy.go | 61 ---- 5 files changed, 4 insertions(+), 425 deletions(-) delete mode 100644 common/contracts/yVaultsV3.APROracle.go delete mode 100644 processes/apr/forward.strategy.go diff --git a/CLAUDE.md b/CLAUDE.md index e31e4489e..c8aa6b256 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -96,4 +96,5 @@ RISK_CDN_URL= # Risk score CDN URL (defaults to https://risk.yearn.fi/cdn/ - All addresses must be checksummed for consistency - Price data fetched from multiple sources with fallbacks - Risk scores calculated using 11-factor assessment system -- APY calculations support multiple strategies: Curve, Convex, Velodrome, Aerodrome, etc. \ No newline at end of file +- APY calculations support multiple strategies: Curve, Convex, Velodrome, Aerodrome, etc. +- V3 Vault APRs are sourced from Kong (single source of truth) bypassing direct on-chain Oracle calls. \ No newline at end of file diff --git a/common/contracts/yVaultsV3.APROracle.go b/common/contracts/yVaultsV3.APROracle.go deleted file mode 100644 index 9896a4f61..000000000 --- a/common/contracts/yVaultsV3.APROracle.go +++ /dev/null @@ -1,357 +0,0 @@ -// Code generated - DO NOT EDIT. -// This file is a generated binding and any manual changes will be lost. - -package contracts - -import ( - "errors" - "math/big" - "strings" - - ethereum "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/event" -) - -// Reference imports to suppress errors if they are not otherwise used. -var ( - _ = errors.New - _ = big.NewInt - _ = strings.NewReader - _ = ethereum.NotFound - _ = bind.Bind - _ = common.Big1 - _ = types.BloomLookup - _ = event.NewSubscription - _ = abi.ConvertType -) - -// YVaultsV3APROracleMetaData contains all meta data concerning the YVaultsV3APROracle contract. -var YVaultsV3APROracleMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_vault\",\"type\":\"address\"}],\"name\":\"getCurrentApr\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"apr\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_vault\",\"type\":\"address\"},{\"internalType\":\"int256\",\"name\":\"_delta\",\"type\":\"int256\"}],\"name\":\"getExpectedApr\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"apr\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_strategy\",\"type\":\"address\"},{\"internalType\":\"int256\",\"name\":\"_debtChange\",\"type\":\"int256\"}],\"name\":\"getStrategyApr\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"oracles\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_strategy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_oracle\",\"type\":\"address\"}],\"name\":\"setOracle\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_strategy\",\"type\":\"address\"}],\"name\":\"weightedApr\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", -} - -// YVaultsV3APROracleABI is the input ABI used to generate the binding from. -// Deprecated: Use YVaultsV3APROracleMetaData.ABI instead. -var YVaultsV3APROracleABI = YVaultsV3APROracleMetaData.ABI - -// YVaultsV3APROracle is an auto generated Go binding around an Ethereum contract. -type YVaultsV3APROracle struct { - YVaultsV3APROracleCaller // Read-only binding to the contract - YVaultsV3APROracleTransactor // Write-only binding to the contract - YVaultsV3APROracleFilterer // Log filterer for contract events -} - -// YVaultsV3APROracleCaller is an auto generated read-only Go binding around an Ethereum contract. -type YVaultsV3APROracleCaller struct { - contract *bind.BoundContract // Generic contract wrapper for the low level calls -} - -// YVaultsV3APROracleTransactor is an auto generated write-only Go binding around an Ethereum contract. -type YVaultsV3APROracleTransactor struct { - contract *bind.BoundContract // Generic contract wrapper for the low level calls -} - -// YVaultsV3APROracleFilterer is an auto generated log filtering Go binding around an Ethereum contract events. -type YVaultsV3APROracleFilterer struct { - contract *bind.BoundContract // Generic contract wrapper for the low level calls -} - -// YVaultsV3APROracleSession is an auto generated Go binding around an Ethereum contract, -// with pre-set call and transact options. -type YVaultsV3APROracleSession struct { - Contract *YVaultsV3APROracle // Generic contract binding to set the session for - CallOpts bind.CallOpts // Call options to use throughout this session - TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session -} - -// YVaultsV3APROracleCallerSession is an auto generated read-only Go binding around an Ethereum contract, -// with pre-set call options. -type YVaultsV3APROracleCallerSession struct { - Contract *YVaultsV3APROracleCaller // Generic contract caller binding to set the session for - CallOpts bind.CallOpts // Call options to use throughout this session -} - -// YVaultsV3APROracleTransactorSession is an auto generated write-only Go binding around an Ethereum contract, -// with pre-set transact options. -type YVaultsV3APROracleTransactorSession struct { - Contract *YVaultsV3APROracleTransactor // Generic contract transactor binding to set the session for - TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session -} - -// YVaultsV3APROracleRaw is an auto generated low-level Go binding around an Ethereum contract. -type YVaultsV3APROracleRaw struct { - Contract *YVaultsV3APROracle // Generic contract binding to access the raw methods on -} - -// YVaultsV3APROracleCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. -type YVaultsV3APROracleCallerRaw struct { - Contract *YVaultsV3APROracleCaller // Generic read-only contract binding to access the raw methods on -} - -// YVaultsV3APROracleTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. -type YVaultsV3APROracleTransactorRaw struct { - Contract *YVaultsV3APROracleTransactor // Generic write-only contract binding to access the raw methods on -} - -// NewYVaultsV3APROracle creates a new instance of YVaultsV3APROracle, bound to a specific deployed contract. -func NewYVaultsV3APROracle(address common.Address, backend bind.ContractBackend) (*YVaultsV3APROracle, error) { - contract, err := bindYVaultsV3APROracle(address, backend, backend, backend) - if err != nil { - return nil, err - } - return &YVaultsV3APROracle{YVaultsV3APROracleCaller: YVaultsV3APROracleCaller{contract: contract}, YVaultsV3APROracleTransactor: YVaultsV3APROracleTransactor{contract: contract}, YVaultsV3APROracleFilterer: YVaultsV3APROracleFilterer{contract: contract}}, nil -} - -// NewYVaultsV3APROracleCaller creates a new read-only instance of YVaultsV3APROracle, bound to a specific deployed contract. -func NewYVaultsV3APROracleCaller(address common.Address, caller bind.ContractCaller) (*YVaultsV3APROracleCaller, error) { - contract, err := bindYVaultsV3APROracle(address, caller, nil, nil) - if err != nil { - return nil, err - } - return &YVaultsV3APROracleCaller{contract: contract}, nil -} - -// NewYVaultsV3APROracleTransactor creates a new write-only instance of YVaultsV3APROracle, bound to a specific deployed contract. -func NewYVaultsV3APROracleTransactor(address common.Address, transactor bind.ContractTransactor) (*YVaultsV3APROracleTransactor, error) { - contract, err := bindYVaultsV3APROracle(address, nil, transactor, nil) - if err != nil { - return nil, err - } - return &YVaultsV3APROracleTransactor{contract: contract}, nil -} - -// NewYVaultsV3APROracleFilterer creates a new log filterer instance of YVaultsV3APROracle, bound to a specific deployed contract. -func NewYVaultsV3APROracleFilterer(address common.Address, filterer bind.ContractFilterer) (*YVaultsV3APROracleFilterer, error) { - contract, err := bindYVaultsV3APROracle(address, nil, nil, filterer) - if err != nil { - return nil, err - } - return &YVaultsV3APROracleFilterer{contract: contract}, nil -} - -// bindYVaultsV3APROracle binds a generic wrapper to an already deployed contract. -func bindYVaultsV3APROracle(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { - parsed, err := YVaultsV3APROracleMetaData.GetAbi() - if err != nil { - return nil, err - } - return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil -} - -// Call invokes the (constant) contract method with params as input values and -// sets the output to result. The result type might be a single field for simple -// returns, a slice of interfaces for anonymous returns and a struct for named -// returns. -func (_YVaultsV3APROracle *YVaultsV3APROracleRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { - return _YVaultsV3APROracle.Contract.YVaultsV3APROracleCaller.contract.Call(opts, result, method, params...) -} - -// Transfer initiates a plain transaction to move funds to the contract, calling -// its default method if one is available. -func (_YVaultsV3APROracle *YVaultsV3APROracleRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { - return _YVaultsV3APROracle.Contract.YVaultsV3APROracleTransactor.contract.Transfer(opts) -} - -// Transact invokes the (paid) contract method with params as input values. -func (_YVaultsV3APROracle *YVaultsV3APROracleRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { - return _YVaultsV3APROracle.Contract.YVaultsV3APROracleTransactor.contract.Transact(opts, method, params...) -} - -// Call invokes the (constant) contract method with params as input values and -// sets the output to result. The result type might be a single field for simple -// returns, a slice of interfaces for anonymous returns and a struct for named -// returns. -func (_YVaultsV3APROracle *YVaultsV3APROracleCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { - return _YVaultsV3APROracle.Contract.contract.Call(opts, result, method, params...) -} - -// Transfer initiates a plain transaction to move funds to the contract, calling -// its default method if one is available. -func (_YVaultsV3APROracle *YVaultsV3APROracleTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { - return _YVaultsV3APROracle.Contract.contract.Transfer(opts) -} - -// Transact invokes the (paid) contract method with params as input values. -func (_YVaultsV3APROracle *YVaultsV3APROracleTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { - return _YVaultsV3APROracle.Contract.contract.Transact(opts, method, params...) -} - -// GetCurrentApr is a free data retrieval call binding the contract method 0x59d8703d. -// -// Solidity: function getCurrentApr(address _vault) view returns(uint256 apr) -func (_YVaultsV3APROracle *YVaultsV3APROracleCaller) GetCurrentApr(opts *bind.CallOpts, _vault common.Address) (*big.Int, error) { - var out []interface{} - err := _YVaultsV3APROracle.contract.Call(opts, &out, "getCurrentApr", _vault) - - if err != nil { - return *new(*big.Int), err - } - - out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - - return out0, err - -} - -// GetCurrentApr is a free data retrieval call binding the contract method 0x59d8703d. -// -// Solidity: function getCurrentApr(address _vault) view returns(uint256 apr) -func (_YVaultsV3APROracle *YVaultsV3APROracleSession) GetCurrentApr(_vault common.Address) (*big.Int, error) { - return _YVaultsV3APROracle.Contract.GetCurrentApr(&_YVaultsV3APROracle.CallOpts, _vault) -} - -// GetCurrentApr is a free data retrieval call binding the contract method 0x59d8703d. -// -// Solidity: function getCurrentApr(address _vault) view returns(uint256 apr) -func (_YVaultsV3APROracle *YVaultsV3APROracleCallerSession) GetCurrentApr(_vault common.Address) (*big.Int, error) { - return _YVaultsV3APROracle.Contract.GetCurrentApr(&_YVaultsV3APROracle.CallOpts, _vault) -} - -// GetExpectedApr is a free data retrieval call binding the contract method 0x8c236964. -// -// Solidity: function getExpectedApr(address _vault, int256 _delta) view returns(uint256 apr) -func (_YVaultsV3APROracle *YVaultsV3APROracleCaller) GetExpectedApr(opts *bind.CallOpts, _vault common.Address, _delta *big.Int) (*big.Int, error) { - var out []interface{} - err := _YVaultsV3APROracle.contract.Call(opts, &out, "getExpectedApr", _vault, _delta) - - if err != nil { - return *new(*big.Int), err - } - - out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - - return out0, err - -} - -// GetExpectedApr is a free data retrieval call binding the contract method 0x8c236964. -// -// Solidity: function getExpectedApr(address _vault, int256 _delta) view returns(uint256 apr) -func (_YVaultsV3APROracle *YVaultsV3APROracleSession) GetExpectedApr(_vault common.Address, _delta *big.Int) (*big.Int, error) { - return _YVaultsV3APROracle.Contract.GetExpectedApr(&_YVaultsV3APROracle.CallOpts, _vault, _delta) -} - -// GetExpectedApr is a free data retrieval call binding the contract method 0x8c236964. -// -// Solidity: function getExpectedApr(address _vault, int256 _delta) view returns(uint256 apr) -func (_YVaultsV3APROracle *YVaultsV3APROracleCallerSession) GetExpectedApr(_vault common.Address, _delta *big.Int) (*big.Int, error) { - return _YVaultsV3APROracle.Contract.GetExpectedApr(&_YVaultsV3APROracle.CallOpts, _vault, _delta) -} - -// GetStrategyApr is a free data retrieval call binding the contract method 0x4d060e36. -// -// Solidity: function getStrategyApr(address _strategy, int256 _debtChange) view returns(uint256) -func (_YVaultsV3APROracle *YVaultsV3APROracleCaller) GetStrategyApr(opts *bind.CallOpts, _strategy common.Address, _debtChange *big.Int) (*big.Int, error) { - var out []interface{} - err := _YVaultsV3APROracle.contract.Call(opts, &out, "getStrategyApr", _strategy, _debtChange) - - if err != nil { - return *new(*big.Int), err - } - - out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - - return out0, err - -} - -// GetStrategyApr is a free data retrieval call binding the contract method 0x4d060e36. -// -// Solidity: function getStrategyApr(address _strategy, int256 _debtChange) view returns(uint256) -func (_YVaultsV3APROracle *YVaultsV3APROracleSession) GetStrategyApr(_strategy common.Address, _debtChange *big.Int) (*big.Int, error) { - return _YVaultsV3APROracle.Contract.GetStrategyApr(&_YVaultsV3APROracle.CallOpts, _strategy, _debtChange) -} - -// GetStrategyApr is a free data retrieval call binding the contract method 0x4d060e36. -// -// Solidity: function getStrategyApr(address _strategy, int256 _debtChange) view returns(uint256) -func (_YVaultsV3APROracle *YVaultsV3APROracleCallerSession) GetStrategyApr(_strategy common.Address, _debtChange *big.Int) (*big.Int, error) { - return _YVaultsV3APROracle.Contract.GetStrategyApr(&_YVaultsV3APROracle.CallOpts, _strategy, _debtChange) -} - -// Oracles is a free data retrieval call binding the contract method 0xaddd5099. -// -// Solidity: function oracles(address ) view returns(address) -func (_YVaultsV3APROracle *YVaultsV3APROracleCaller) Oracles(opts *bind.CallOpts, arg0 common.Address) (common.Address, error) { - var out []interface{} - err := _YVaultsV3APROracle.contract.Call(opts, &out, "oracles", arg0) - - if err != nil { - return *new(common.Address), err - } - - out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) - - return out0, err - -} - -// Oracles is a free data retrieval call binding the contract method 0xaddd5099. -// -// Solidity: function oracles(address ) view returns(address) -func (_YVaultsV3APROracle *YVaultsV3APROracleSession) Oracles(arg0 common.Address) (common.Address, error) { - return _YVaultsV3APROracle.Contract.Oracles(&_YVaultsV3APROracle.CallOpts, arg0) -} - -// Oracles is a free data retrieval call binding the contract method 0xaddd5099. -// -// Solidity: function oracles(address ) view returns(address) -func (_YVaultsV3APROracle *YVaultsV3APROracleCallerSession) Oracles(arg0 common.Address) (common.Address, error) { - return _YVaultsV3APROracle.Contract.Oracles(&_YVaultsV3APROracle.CallOpts, arg0) -} - -// WeightedApr is a free data retrieval call binding the contract method 0xf753dd5c. -// -// Solidity: function weightedApr(address _strategy) view returns(uint256) -func (_YVaultsV3APROracle *YVaultsV3APROracleCaller) WeightedApr(opts *bind.CallOpts, _strategy common.Address) (*big.Int, error) { - var out []interface{} - err := _YVaultsV3APROracle.contract.Call(opts, &out, "weightedApr", _strategy) - - if err != nil { - return *new(*big.Int), err - } - - out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - - return out0, err - -} - -// WeightedApr is a free data retrieval call binding the contract method 0xf753dd5c. -// -// Solidity: function weightedApr(address _strategy) view returns(uint256) -func (_YVaultsV3APROracle *YVaultsV3APROracleSession) WeightedApr(_strategy common.Address) (*big.Int, error) { - return _YVaultsV3APROracle.Contract.WeightedApr(&_YVaultsV3APROracle.CallOpts, _strategy) -} - -// WeightedApr is a free data retrieval call binding the contract method 0xf753dd5c. -// -// Solidity: function weightedApr(address _strategy) view returns(uint256) -func (_YVaultsV3APROracle *YVaultsV3APROracleCallerSession) WeightedApr(_strategy common.Address) (*big.Int, error) { - return _YVaultsV3APROracle.Contract.WeightedApr(&_YVaultsV3APROracle.CallOpts, _strategy) -} - -// SetOracle is a paid mutator transaction binding the contract method 0x5c38eb3a. -// -// Solidity: function setOracle(address _strategy, address _oracle) returns() -func (_YVaultsV3APROracle *YVaultsV3APROracleTransactor) SetOracle(opts *bind.TransactOpts, _strategy common.Address, _oracle common.Address) (*types.Transaction, error) { - return _YVaultsV3APROracle.contract.Transact(opts, "setOracle", _strategy, _oracle) -} - -// SetOracle is a paid mutator transaction binding the contract method 0x5c38eb3a. -// -// Solidity: function setOracle(address _strategy, address _oracle) returns() -func (_YVaultsV3APROracle *YVaultsV3APROracleSession) SetOracle(_strategy common.Address, _oracle common.Address) (*types.Transaction, error) { - return _YVaultsV3APROracle.Contract.SetOracle(&_YVaultsV3APROracle.TransactOpts, _strategy, _oracle) -} - -// SetOracle is a paid mutator transaction binding the contract method 0x5c38eb3a. -// -// Solidity: function setOracle(address _strategy, address _oracle) returns() -func (_YVaultsV3APROracle *YVaultsV3APROracleTransactorSession) SetOracle(_strategy common.Address, _oracle common.Address) (*types.Transaction, error) { - return _YVaultsV3APROracle.Contract.SetOracle(&_YVaultsV3APROracle.TransactOpts, _strategy, _oracle) -} diff --git a/common/env/chains.go b/common/env/chains.go index df399a7a8..8cdf4b5a2 100644 --- a/common/env/chains.go +++ b/common/env/chains.go @@ -74,7 +74,6 @@ type TChain struct { MulticallContract TContractData YBribeV3Contract TContractData PartnerContract TContractData - APROracleContract TContractData // Deprecated for vault-level APY (uses Kong), still used for strategy-level APR Coin models.TERC20Token StakingRewardRegistry []TContractData Registries []TContractData diff --git a/internal/fetcher/strategies.go b/internal/fetcher/strategies.go index 4b33ba771..15ed09767 100755 --- a/internal/fetcher/strategies.go +++ b/internal/fetcher/strategies.go @@ -100,14 +100,11 @@ func getStrategyAPR(chainID uint64, versionMajor string, strategy models.TStrate ** For earlier versions: Only try to get current APR ******************************************************************************************/ if versionMajor == `3` { - if forwardAPR, err := apr.ComputeForwardStrategyAPR(strategy); err == nil { - netAPR = forwardAPR - aprType = models.APRTypeForward - } else if currentAPR, err := apr.GetCurrentStrategyAPR(chainID, strategy.Address.Hex()); err == nil { + if currentAPR, err := apr.GetCurrentStrategyAPR(chainID, strategy.Address.Hex()); err == nil { netAPR = currentAPR aprType = models.APRTypeCurrent } else { - return netAPR, aprType, errors.New(`Error while computing forward APR for ` + strategy.Address.Hex() + ` | ` + strategy.VaultAddress.Hex() + `: ` + err.Error()) + return netAPR, aprType, errors.New(`Error while computing current APR for ` + strategy.Address.Hex() + ` | ` + strategy.VaultAddress.Hex() + `: ` + err.Error()) } } else { if currentAPR, err := apr.GetCurrentStrategyAPR(chainID, strategy.Address.Hex()); err == nil { diff --git a/processes/apr/forward.strategy.go b/processes/apr/forward.strategy.go deleted file mode 100644 index a68556e05..000000000 --- a/processes/apr/forward.strategy.go +++ /dev/null @@ -1,61 +0,0 @@ -package apr - -import ( - "errors" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/yearn/ydaemon/common/bigNumber" - "github.com/yearn/ydaemon/common/contracts" - "github.com/yearn/ydaemon/common/env" - "github.com/yearn/ydaemon/common/ethereum" - "github.com/yearn/ydaemon/common/helpers" - "github.com/yearn/ydaemon/internal/models" -) - -func ComputeForwardStrategyAPR(strategy models.TStrategy) (*bigNumber.Float, error) { - oracleAPR := bigNumber.NewFloat(0) - chain, ok := env.GetChain(strategy.ChainID) - if !ok { - return nil, errors.New(`chain not found`) - } - oracleContract := chain.APROracleContract.Address - if oracleContract == common.HexToAddress(``) { - return nil, errors.New(`oracle not found`) - } - oracle, err := contracts.NewYVaultsV3APROracleCaller(oracleContract, ethereum.GetRPC(strategy.ChainID)) - if err != nil { - return nil, err - } - - /********************************************************************************************** - ** If the vault is a single strategy vault, we can use the oracle directly to get the APR of - ** the vault as expected APR - **********************************************************************************************/ - var hasError error - expected, err := oracle.GetStrategyApr(nil, strategy.Address, big.NewInt(0)) - if err == nil { - oracleAPR = helpers.ToNormalizedAmount(bigNumber.SetInt(expected), 18) - } else { - hasError = err - } - - if hasError != nil || oracleAPR.IsZero() { - expected, newErr := oracle.GetCurrentApr(nil, strategy.VaultAddress) - err = newErr - if newErr == nil { - oracleAPR = helpers.ToNormalizedAmount(bigNumber.SetInt(expected), 18) - } else { - return nil, err - } - } - - /********************************************************************************************** - ** Define which APR we want to use as "Net APR". - **********************************************************************************************/ - primaryAPR := oracleAPR - primaryAPRFloat64, _ := primaryAPR.Float64() - primaryAPY := bigNumber.NewFloat(0).SetFloat64(convertFloatAPRToAPY(primaryAPRFloat64, 52)) - - return primaryAPY, nil -} From 6f3c7c4d7b7128e7299cafa7719ecce2628a6be9 Mon Sep 17 00:00:00 2001 From: matheus1lva Date: Sun, 18 Jan 2026 11:27:12 -0300 Subject: [PATCH 3/6] update docs --- docs/apr-oracle-integration.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 docs/apr-oracle-integration.md diff --git a/docs/apr-oracle-integration.md b/docs/apr-oracle-integration.md new file mode 100644 index 000000000..c0b96ffd0 --- /dev/null +++ b/docs/apr-oracle-integration.md @@ -0,0 +1,34 @@ +# APR Oracle Integration (Kong) + +As of version `yDaemon v3.0.0+`, the integration with the on-chain APR Oracle contract for V3 vaults has been replaced with a direct integration with Kong's GraphQL API. + +## Overview + +Previously, `yDaemon` calculated forward-looking APRs for V3 vaults by making RPC calls to a specific Oracle smart contract. This dependency has been removed in favor of fetching pre-calculated oracle data directly from Yearn's data aggregator, **Kong**. + +## Data Source + +The `performance.oracle` field from Kong's GraphQL API is now the single source of truth for: +- **Oracle APR**: The annualized rate based on recent performance. +- **Oracle APY**: The annualized yield (compounded). + +## Implementation Details + +- **Internal Logic**: The `forward.v3.go` process queries the internal storage (populated by the Kong indexer) instead of computing values on-chain. +- **Strategies**: Strategy-level APRs (`NetAPR`) for V3 also utilize this data source via `GetCurrentStrategyAPR` or the vault-level fallback, ensuring consistency. +- **Fallback**: If Kong data is unavailable or null, the system gracefully defaults to zero values, preventing API crashes. + +## API Response + +The external API response structure for vaults remains unchanged to ensure backward compatibility. The `apr.forwardAPR` object is populated with the data retrieved from Kong. + +```json +"forwardAPR": { + "type": "v3:onchainOracle", + "netAPR": 0.045, // Sourced from Kong + "composite": { + "v3OracleCurrentAPR": 0.045, + "v3OracleStratRatioAPR": 0.045 + } +} +``` From b10cf72d5b5b1007539cbad009ab90efaafc1803 Mon Sep 17 00:00:00 2001 From: matheus1lva Date: Wed, 21 Jan 2026 11:55:54 -0300 Subject: [PATCH 4/6] feat: remove legacy APR oracle fallback for strategies Removes the else block that called apr.GetCurrentStrategyAPR() which was part of the old APR oracle implementation. This code path is no longer needed after the Kong GraphQL API integration for V3 vault APY calculations. --- internal/fetcher/strategies.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/internal/fetcher/strategies.go b/internal/fetcher/strategies.go index 15ed09767..7d846f5d9 100755 --- a/internal/fetcher/strategies.go +++ b/internal/fetcher/strategies.go @@ -106,13 +106,6 @@ func getStrategyAPR(chainID uint64, versionMajor string, strategy models.TStrate } else { return netAPR, aprType, errors.New(`Error while computing current APR for ` + strategy.Address.Hex() + ` | ` + strategy.VaultAddress.Hex() + `: ` + err.Error()) } - } else { - if currentAPR, err := apr.GetCurrentStrategyAPR(chainID, strategy.Address.Hex()); err == nil { - netAPR = currentAPR - aprType = models.APRTypeCurrent - } else { - return netAPR, aprType, errors.New(`Error while computing current APR for ` + strategy.Address.Hex() + ` | ` + strategy.VaultAddress.Hex() + `: ` + err.Error()) - } } return netAPR, aprType, nil } From ab414a0d7cf3c4fe850dd9ed76d1519835bae021 Mon Sep 17 00:00:00 2001 From: matheus1lva Date: Thu, 22 Jan 2026 10:08:24 -0300 Subject: [PATCH 5/6] fix vars --- processes/apr/forward.v3.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/processes/apr/forward.v3.go b/processes/apr/forward.v3.go index 031e71da0..8ec4837f7 100644 --- a/processes/apr/forward.v3.go +++ b/processes/apr/forward.v3.go @@ -65,7 +65,7 @@ func computeVaultV3ForwardAPY( Type: `v3:onchainOracle`, NetAPY: primaryAPY, Composite: TCompositeData{ - V3OracleCurrentAPR: primaryAPR, + V3OracleCurrentAPR: primaryAPY, V3OracleStratRatioAPR: bigNumber.NewFloat(0), // Not used with Kong oracle }, } From 95ab6e0684a617bfd4a043a14a3c0106f7bf554f Mon Sep 17 00:00:00 2001 From: matheus1lva Date: Thu, 22 Jan 2026 16:36:38 -0300 Subject: [PATCH 6/6] remove unused --- processes/apr/forward.v3.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/processes/apr/forward.v3.go b/processes/apr/forward.v3.go index 8ec4837f7..68919b408 100644 --- a/processes/apr/forward.v3.go +++ b/processes/apr/forward.v3.go @@ -24,7 +24,7 @@ func computeVaultV3ForwardAPY( ** Fetch Kong oracle APR/APY data (single source of truth) ** Kong provides pre-calculated oracle APR and APY from daily timeseries hook **********************************************************************************************/ - oracleAPR, oracleAPY, ok := storage.GetKongOracleAPY(chainID, vault.Address) + _, oracleAPY, ok := storage.GetKongOracleAPY(chainID, vault.Address) if !ok { logs.Error("Kong oracle data missing for vault %s on chain %d - Kong source unavailable", vault.Address.Hex(), chainID) @@ -56,10 +56,6 @@ func computeVaultV3ForwardAPY( ** Kong already converts APR to APY using weekly compounding (52 periods/year) **********************************************************************************************/ primaryAPY := bigNumber.NewFloat(*oracleAPY) - primaryAPR := bigNumber.NewFloat(0) - if oracleAPR != nil { - primaryAPR = bigNumber.NewFloat(*oracleAPR) - } return TForwardAPY{ Type: `v3:onchainOracle`,