diff --git a/db_scripts/delete_rewards_v3.sql b/db_scripts/delete_rewards_v3.sql new file mode 100644 index 00000000..442052bd --- /dev/null +++ b/db_scripts/delete_rewards_v3.sql @@ -0,0 +1,14 @@ +delete from farm_V3; +delete from user_lmdetails_v3 ; +delete from sync_adapters where type='LMRewardsv3'; + +WITH data as (select * from sync_adapters where type = 'AddressProvider') +insert into sync_adapters(address, type, last_sync, firstlog_at, version, discovered_at, disabled) values ( + '0x00000000000000000000000000000000000beef3', + 'LMRewardsv3', + (SELECT firstlog_at-1 from data), + (SELECT firstlog_at from data), + '300', + (SELECT firstlog_at from data), + 'f' + ); \ No newline at end of file diff --git a/db_scripts/reset_diesel_tables.sql b/db_scripts/reset_diesel_tables.sql index 25b22e44..929e3ae4 100644 --- a/db_scripts/reset_diesel_tables.sql +++ b/db_scripts/reset_diesel_tables.sql @@ -1,2 +1,2 @@ delete from diesel_balances; delete from diesel_transfers; delete from lm_rewards; -update sync_adapters set details='{}'::jsonb, last_sync=(SELECT firstlog_at-1 FROM sync_adapters WHERE type='AddressProvider') WHERE type='PoolLMRewards'; \ No newline at end of file +update sync_adapters set details='{}'::jsonb, last_sync=(SELECT firstlog_at-1 FROM sync_adapters WHERE type='AddressProvider') WHERE type='LMRewardsv2'; \ No newline at end of file diff --git a/debts/index.go b/debts/index.go index be189147..9f6292db 100644 --- a/debts/index.go +++ b/debts/index.go @@ -168,7 +168,7 @@ func (eng *DebtEngine) notifiedIfLiquidable(sessionId string, notified bool) { func (eng *DebtEngine) AreActiveAdapterSynchronized() bool { data := schemas.DebtSync{} query := `SELECT count(distinct last_sync) as last_calculated_at FROM sync_adapters - WHERE disabled=false AND type NOT IN ('RebaseToken','Treasury','PoolLMRewards','GearToken')` + WHERE disabled=false AND type NOT IN ('RebaseToken','Treasury','LMRewardv2','LMRewardv3','GearToken')` err := eng.db.Raw(query).Find(&data).Error if err != nil { log.Fatal(err) diff --git a/ds/adapter_kit.go b/ds/adapter_kit.go index 29051a9a..29d80aa1 100644 --- a/ds/adapter_kit.go +++ b/ds/adapter_kit.go @@ -18,7 +18,7 @@ func (kit *AdapterKit) init() { // another level created bcz of poolKeeper. kit.AddLevel([]string{PoolQuotaWrapper, ChainlinkPriceFeed}) // REVERT_CM_WRAPPER - kit.AddLevel([]string{CMWrapper, AggregatedQueryFeedWrapper, PoolLMRewards}) + kit.AddLevel([]string{CMWrapper, AggregatedQueryFeedWrapper, LMRewardsv2, LMRewardsv3}) // REVERT_CF_WRAPPER kit.AddLevel([]string{CFWrapper, CreditConfigurator, Treasury}) // - we are dropping the uni check, so the dependency is reversed. @@ -32,7 +32,7 @@ func (kit *AdapterKit) init() { // - Treasury dependent on pools, so it is last // - acl, PriceOracle and geartoken are independent // - creditconfigurator and core.CreditFilter are same dependent on creditmanager - // - pool -> dieseltokens -> PoolLMRewards + // - pool -> dieseltokens -> LMRewardsv2 and LMRewardsv3 } func (kit *AdapterKit) AddLevel(lvl []string) { diff --git a/ds/adapter_name.go b/ds/adapter_name.go index 6a01811b..367e1d0b 100644 --- a/ds/adapter_name.go +++ b/ds/adapter_name.go @@ -20,7 +20,7 @@ const ( Treasury = "Treasury" AccountManager = "AccountManager" CreditConfigurator = "CreditConfigurator" - PoolLMRewards = "PoolLMRewards" + LMRewardsv2 = "LMRewardsv2" // RebaseToken = "RebaseToken" // Wrapper @@ -32,8 +32,12 @@ const ( PoolQuotaWrapper = "PoolQuotaWrapper" // v3 PoolQuotaKeeper = "PoolKeeper" + LMRewardsv3 = "LMRewardsv3" ) +// beef2 -- v2LMRewards +// beef3 -- v3LMRewards + func IsWrapperAdapter(name string) bool { return strings.HasSuffix(name, "Wrapper") } diff --git a/ds/dc_wrapper/wrapper.go b/ds/dc_wrapper/wrapper.go index 2c17d1f0..1d4fefd9 100644 --- a/ds/dc_wrapper/wrapper.go +++ b/ds/dc_wrapper/wrapper.go @@ -178,6 +178,14 @@ func (dcw *DataCompressorWrapper) GetKeyAndAddress(version core.VersionType, blo key, discoveredAt := dcw.getDataCompressorIndex(blockNum) return key, dcw.getDCAddr(discoveredAt) } +func (dcw *DataCompressorWrapper) GetLatestv3DC() (common.Address, bool) { + version := core.NewVersion(300) + dc, ok := dcw.versionToAddress[version] + if !ok { + return core.NULL_ADDR, false + } + return dc.address, true +} func (dcw *DataCompressorWrapper) GetCreditAccountData(version core.VersionType, blockNum int64, creditManager common.Address, borrower common.Address, account common.Address) ( call multicall.Multicall2Call, @@ -316,6 +324,17 @@ func (dcw *DataCompressorWrapper) GetCreditManagerData(version core.VersionType, return } +func (dcw *DataCompressorWrapper) GetPoolListv3() ([]dcv3.PoolData, bool) { + dcAddr, found := dcw.GetLatestv3DC() + if !found { + return nil, false + } + con, err := dcv3.NewDataCompressorv3(dcAddr, dcw.client) + log.CheckFatal(err) + poolList, err := con.GetPoolsV3List(nil) + log.CheckFatal(err) + return poolList, true +} func (dcw *DataCompressorWrapper) GetPoolData(version core.VersionType, blockNum int64, _pool common.Address) ( call multicall.Multicall2Call, resultFn func([]byte) (dc.PoolCallData, error), diff --git a/engine/index.go b/engine/index.go index a13b3785..4d9511f7 100644 --- a/engine/index.go +++ b/engine/index.go @@ -13,7 +13,8 @@ import ( "github.com/Gearbox-protocol/third-eye/config" "github.com/Gearbox-protocol/third-eye/ds" "github.com/Gearbox-protocol/third-eye/models/address_provider" - "github.com/Gearbox-protocol/third-eye/models/pool_lmrewards" + lmrewardsv2 "github.com/Gearbox-protocol/third-eye/models/pool_lmrewards/v2" + lmrewardsv3 "github.com/Gearbox-protocol/third-eye/models/pool_lmrewards/v3" "github.com/Gearbox-protocol/third-eye/models/rebase_token" "github.com/Gearbox-protocol/third-eye/repository" "github.com/ethereum/go-ethereum/common" @@ -78,9 +79,12 @@ func (e *Engine) getLastSyncedTill() int64 { obj := address_provider.NewAddressProvider(e.Client, e.repo, e.config.AddressProviderAddrs) e.repo.AddSyncAdapter(obj) // pool LM rewards - poolLMRewardObj := pool_lmrewards.NewPoolLMRewards("0x000000000000000000000000000000000000beef", obj.FirstLogAt-1, e.Client, e.repo) + lmrewardsv2Obj := lmrewardsv2.NewLMRewardsv2("0x00000000000000000000000000000000000beef2", obj.FirstLogAt-1, e.Client, e.repo) + e.repo.AddSyncAdapter(lmrewardsv2Obj) + lmrewardsv3Obj := lmrewardsv3.NewLMRewardsv3("0x00000000000000000000000000000000000beef3", obj.FirstLogAt-1, e.Client, e.repo) + e.repo.AddSyncAdapter(lmrewardsv3Obj) + // e.addstETHToken() - e.repo.AddSyncAdapter(poolLMRewardObj) return obj.GetLastSync() } else { @@ -100,13 +104,13 @@ func (e *Engine) SyncHandler() { // only do batch sync if latestblock is far from currently synced block if lastSyncedTill+e.batchSizeForHistory <= latestBlockNum { syncedTill := e.syncLoop(lastSyncedTill, latestBlockNum) - log.Infof("Synced till %d sleeping for 5 mins", syncedTill) + log.Infof("Synced till %d", syncedTill) } for { latestBlockNum = e.GetLatestFinalizedBlock(4) e.SyncAndFlush(latestBlockNum) - log.Infof("Synced till %d sleeping for 5 mins", latestBlockNum) - time.Sleep(30 * time.Second) // on kovan 5 blocks in 1 min , sleep for 5 mins + log.Infof("Synced till %d sleeping for 2 mins", latestBlockNum) + time.Sleep(2 * time.Minute) // on kovan 5 blocks in 1 min , sleep for 5 mins } } diff --git a/go.mod b/go.mod index 70072a6b..69424878 100644 --- a/go.mod +++ b/go.mod @@ -73,4 +73,4 @@ require ( replace github.com/btcsuite/btcd => github.com/btcsuite/btcd v0.22.1 -// replace github.com/Gearbox-protocol/sdk-go v0.0.0-20231219113729-4d2522d6c5ee => ../sdk-go +// replace github.com/Gearbox-protocol/sdk-go v0.0.0-20240112091010-e6a3b573c3cb => ../sdk-go diff --git a/migrations/000037_inch_farming_rewards.up.sql b/migrations/000037_inch_farming_rewards.up.sql new file mode 100644 index 00000000..15480178 --- /dev/null +++ b/migrations/000037_inch_farming_rewards.up.sql @@ -0,0 +1,41 @@ +create table farm_v3 ( + pool varchar(42), + farm varchar(42), + -- + checkpoint integer, + farmed_per_token varchar(80), + -- + reward varchar(80), + period integer, + end_ts integer, + -- + total_supply varchar(80), + diesel_token varchar(42), + PRIMARY KEY (farm) +); + +create table user_lmdetails_v3 ( + balances_bi varchar(80), + balances double precision, + correction varchar(80), + account varchar(42), + farm varchar(42), + diesel_sym varchar(42), + PRIMARY KEY(farm, account) +); + +-- 1inch lm rewards are stored with gear. + +update sync_adapters set type='LMRewardsv2', address='0x00000000000000000000000000000000000beef2' where type = 'PoolLMRewards'; +-- +WITH data as (select * from sync_adapters where type = 'AddressProvider') +insert into sync_adapters(address, type, last_sync, firstlog_at, version, discovered_at, disabled) values ( + '0x00000000000000000000000000000000000beef3', + 'LMRewardsv3', + (SELECT firstlog_at-1 from data), + (SELECT firstlog_at from data), + '300', + (SELECT firstlog_at from data), + 'f' + ); + diff --git a/models/pool/pool_v3/farmed_fix_user.go b/models/pool/pool_v3/farmed_fix_user.go new file mode 100644 index 00000000..434b5e65 --- /dev/null +++ b/models/pool/pool_v3/farmed_fix_user.go @@ -0,0 +1,127 @@ +package pool_v3 + +import ( + "math/big" + + "github.com/Gearbox-protocol/sdk-go/core" + "github.com/Gearbox-protocol/sdk-go/core/schemas" + "github.com/Gearbox-protocol/sdk-go/log" + "github.com/Gearbox-protocol/sdk-go/utils" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "gorm.io/gorm" +) + +type UpdatePoolLedger struct { + Zapper string + Pool string + User string + BlockNum int64 + TxHash string + Shares *big.Int + Type string +} + +func (mdl *Poolv3) updateFarmedv3(txLog types.Log) { + from := common.BytesToAddress(txLog.Topics[1][:]).Hex() + to := common.BytesToAddress(txLog.Topics[2][:]).Hex() + shares := new(big.Int).SetBytes(txLog.Data[:]) + txHash := txLog.TxHash.Hex() + blockNum := int64(txLog.BlockNumber) + + if from == core.NULL_ADDR.Hex() || to == core.NULL_ADDR.Hex() { + return + } + + if from == mdl.getZapPoolv2() { + mdl.updatesForPoolv2 = append(mdl.updatesForPoolv2, UpdatePoolLedger{ + Zapper: from, + User: to, + Pool: mdl.getPoolv2(), + BlockNum: blockNum, + TxHash: txHash, + Type: "RemoveLiquidity", + }) + } + if from == mdl.getZapUnderlying() || from == mdl.getZapPoolv2() { // usdc-farmedUSDCv3, dUSDC-farmedUSDCv3 + if txHash != mdl.addLiquidityEvent.TxHash || + blockNum != mdl.addLiquidityEvent.BlockNumber || + shares.Cmp(mdl.addLiquidityEvent.SharesBI.Convert()) != 0 || + from != mdl.addLiquidityEvent.User { + log.Fatal(utils.ToJson(mdl.addLiquidityEvent), "addLiquidityEvent", "txHash", txHash, "blockNum", blockNum, "shares", shares) + } + mdl.addLiquidityEvent.User = to + mdl.Repo.AddPoolLedger(mdl.addLiquidityEvent) + mdl.addLiquidityEvent = nil + } + + if to == mdl.getZapUnderlying() || to == mdl.getZapPoolv2() { + mdl.removeLiqUpdate = &UpdatePoolLedger{ + Zapper: to, + User: from, + Pool: mdl.Address, + BlockNum: blockNum, + TxHash: txHash, + Shares: shares, + } + } + if to == mdl.getZapPoolv2() { + mdl.updatesForPoolv2 = append(mdl.updatesForPoolv2, UpdatePoolLedger{ + Zapper: to, + User: from, + Pool: mdl.getPoolv2(), + BlockNum: blockNum, + TxHash: txHash, + Type: "AddLiquidity", + }) + } +} + +func (mdl *Poolv3) changeAddressOnAddLiq(event *schemas.PoolLedger) { + if mdl.removeLiqUpdate != nil { + log.Fatal(utils.ToJson(mdl.removeLiqUpdate), "removeLiqUpdate is not nil on add Liq", utils.ToJson(event)) + } + if mdl.addLiquidityEvent != nil { + log.Fatal(utils.ToJson(mdl.addLiquidityEvent), "addLiquidityEvent is not nil", "pool", mdl.Address, "event", event) + } + mdl.addLiquidityEvent = event +} +func (mdl *Poolv3) changeAddressOnRemoveLiq(event *schemas.PoolLedger) { + if mdl.removeLiqUpdate == nil { + log.Fatal(utils.ToJson(event)) + } + txHash := mdl.removeLiqUpdate.TxHash + blockNum := mdl.removeLiqUpdate.BlockNum + shares := mdl.removeLiqUpdate.Shares + if txHash != event.TxHash || + blockNum != event.BlockNumber || + shares.Cmp(event.SharesBI.Convert()) != 0 || + mdl.removeLiqUpdate.Zapper != event.User { + log.Fatal(utils.ToJson(mdl.removeLiqUpdate), "removeLiqUpdate", "txHash", event.TxHash, "blockNum", event.BlockNumber, "shares", event.SharesBI, "user", event.User) + } + if mdl.addLiquidityEvent != nil { + log.Fatal(utils.ToJson(mdl.addLiquidityEvent), "addLiquidityEvent is not nil", "pool", mdl.Address, "event", event) + } + // + // + event.User = mdl.removeLiqUpdate.User + mdl.Repo.AddPoolLedger(event) + mdl.removeLiqUpdate = nil +} + +func (mdl *Poolv3) UpdatePoolv2Ledger(tx *gorm.DB) { + for _, update := range mdl.updatesForPoolv2 { + if update.Type == "" { + continue + } + x := tx.Exec(`UPDATE pool_ledger set user_address=? WHERE block_num=? AND event = ? AND tx_hash=? AND user_address in (?, ?)`, + update.User, update.BlockNum, update.Type, update.TxHash, update.User, update.Zapper) + log.CheckFatal(x.Error) + // + + if x.RowsAffected != 1 { + log.Infof("SEELCT * from pool_ledger WHERE block_num=%d AND event = '%s' AND tx_hash='%s' AND user_address in ('%s', '%s')", update.BlockNum, update.Type, update.TxHash, update.User, update.Zapper) + log.Fatal("Can't update for", utils.ToJson(update)) + } + } +} diff --git a/models/pool/pool_v3/get_details.go b/models/pool/pool_v3/get_details.go new file mode 100644 index 00000000..3fc3a83c --- /dev/null +++ b/models/pool/pool_v3/get_details.go @@ -0,0 +1,82 @@ +package pool_v3 + +import ( + dcv3 "github.com/Gearbox-protocol/sdk-go/artifacts/dataCompressorv3" + "github.com/Gearbox-protocol/sdk-go/log" +) + +func (mdl *Poolv3) setDetailsByKey(key, value string) { + if mdl.Details == nil { + mdl.Details = make(map[string]interface{}) + } + mdl.Details[key] = value +} +func (mdl *Poolv3) setPoolQuotaKeeper() { + if mdl.getPoolKeeper() != "" { + return + } + poolKeeper, err := mdl.contract.PoolQuotaKeeper(nil) + log.CheckFatal(err) + mdl.setDetailsByKey("PoolKeeper", poolKeeper.Hex()) +} + +func (mdl *Poolv3) getPoolKeeper() string { + return mdl.GetDetailsByKey("PoolKeeper") +} +func (mdl *Poolv3) getFarmedUSDCv3() string { + return mdl.GetDetailsByKey("farmedUSDCv3") +} +func (mdl *Poolv3) getdUSDC() string { + return mdl.GetDetailsByKey("dUSDC") +} +func (mdl *Poolv3) getPoolv2() string { + return mdl.GetDetailsByKey("poolv2") +} +func (mdl *Poolv3) getZapUnderlying() string { + return mdl.GetDetailsByKey("USDC-farmedUSDCv3") +} +func (mdl *Poolv3) getZapPoolv2() string { + return mdl.GetDetailsByKey("dUSDC-farmedUSDCv3") +} + +func (mdl *Poolv3) setZapper() { + if mdl.Details != nil && mdl.Details["farmedUSDCv3"] != nil { // if zapper already set + return + } + pools, found := mdl.Repo.GetDCWrapper().GetPoolListv3() + if !found { + return + } + var poolToCheck dcv3.PoolData + for _, pool := range pools { + if pool.Addr.Hex() == mdl.Address { + poolToCheck = pool + break + } + } + + // out = farmedUSDCv3, dUSDCv3 + for _, zapper := range poolToCheck.Zappers { + if zapper.TokenIn == poolToCheck.Underlying && zapper.TokenOut != poolToCheck.DieselToken { // tokenIn = USDC, tokenOut != dUSDCv3 + mdl.setDetailsByKey("USDC-farmedUSDCv3", zapper.Zapper.Hex()) + mdl.setDetailsByKey("farmedUSDCv3", zapper.TokenOut.Hex()) + } + } + for _, zapper := range poolToCheck.Zappers { + if zapper.TokenIn != poolToCheck.Underlying && + zapper.TokenOut.Hex() == mdl.GetDetailsByKey("farmedUSDCv3") { // tokenIn = dUSDC, tokenOut = farmedUSDCv3 + mdl.setDetailsByKey("dUSDC-farmedUSDCv3", zapper.Zapper.Hex()) + mdl.setDetailsByKey("dUSDC", zapper.TokenIn.Hex()) + } + } + + if mdl.GetDetailsByKey("dUSDC") == "" { + log.Fatal("Can't get dUSDC from zapper for ", mdl.Address) + } + dieselTokenToPool := mdl.Repo.GetDieselTokens() + pool, ok := dieselTokenToPool[mdl.GetDetailsByKey("dUSDC")] + if !ok { + log.Fatalf("Can't get poolv2(dieselToken: %s) from poolv3: %s ", mdl.GetDetailsByKey("dUSDC"), mdl.Address) + } + mdl.setDetailsByKey("poolv2", pool.Pool) +} diff --git a/models/pool/pool_v3/model.go b/models/pool/pool_v3/model.go index c169adb6..62bf47a6 100644 --- a/models/pool/pool_v3/model.go +++ b/models/pool/pool_v3/model.go @@ -19,6 +19,10 @@ type Poolv3 struct { State *schemas.PoolState gatewayHandler pool_common.GatewayHandler repayEvents []*schemas.PoolLedger + // + addLiquidityEvent *schemas.PoolLedger + updatesForPoolv2 []UpdatePoolLedger + removeLiqUpdate *UpdatePoolLedger } func (pool *Poolv3) GetRepayEvent() *schemas.PoolLedger { @@ -56,14 +60,10 @@ func NewPool(addr string, client core.ClientI, repo ds.RepositoryI, discoveredAt return name }(), }) + // create a pool stat snapshot at first log of the pool pool.onBlockChangeInternally(pool.DiscoveredAt) - // poolQuotaKeeper, err := core.CallFuncWithExtraBytes(client, "1ab7c7d7", common.HexToAddress(pool.Address), discoveredAt, nil) - // if err != nil { - // log.Fatalf("can't get pool quota keeper for %s: %s", pool.Address, err) - // } - // pool.setPoolQuotaKeeper(string(poolQuotaKeeper), discoveredAt) return pool } @@ -79,6 +79,9 @@ func NewPoolFromAdapter(adapter *ds.SyncAdapter) *Poolv3 { return contract }(), } + obj.setPoolQuotaKeeper() + obj.setZapper() + // return obj } @@ -95,9 +98,8 @@ func (mdl Poolv3) Topics() [][]common.Hash { core.Topic("SetPoolQuotaKeeper(address)"), core.Topic("AddCreditManager(address)"), core.Topic("SetWithdrawFee(uint256)"), - // for weth gateway - core.Topic("WithdrawETH(address,address)"), - // for wsteth gateway, this event is on stETH token + core.Topic("UpdateTokenQuotaRate(address,uint256)"), + // for farmedUSDCv3 core.Topic("Transfer(address,address,uint256)"), }, } @@ -105,9 +107,18 @@ func (mdl Poolv3) Topics() [][]common.Hash { func (mdl Poolv3) GetAllAddrsForLogs() (addrs []common.Address) { addrs = append(addrs, mdl.SyncAdapter.GetAllAddrsForLogs()...) + if mdl.getPoolKeeper() != "" { + addrs = append(addrs, common.HexToAddress(mdl.getPoolKeeper())) + } + if mdl.getFarmedUSDCv3() != "" { + addrs = append(addrs, common.HexToAddress(mdl.getFarmedUSDCv3())) + } if mdl.gatewayHandler.Gateway == core.NULL_ADDR { return } - addrs = append(addrs, mdl.gatewayHandler.Gateway, mdl.gatewayHandler.Token) + addrs = append(addrs, + mdl.gatewayHandler.Gateway, + mdl.gatewayHandler.Token, + ) return } diff --git a/models/pool/pool_v3/on_log.go b/models/pool/pool_v3/on_log.go index f6f88a1a..446525bb 100644 --- a/models/pool/pool_v3/on_log.go +++ b/models/pool/pool_v3/on_log.go @@ -25,6 +25,12 @@ func (mdl Poolv3) getDecimals() int8 { func (mdl *Poolv3) OnLog(txLog types.Log) { blockNum := int64(txLog.BlockNumber) switch txLog.Topics[0] { + case core.Topic("Transfer(address,address,uint256)"): + if txLog.Address.Hex() == mdl.getFarmedUSDCv3() { + mdl.updateFarmedv3(txLog) + } + case core.Topic("UpdateTokenQuotaRate(address,uint256)"): + mdl.updateBorrowRate(blockNum) case core.Topic("SetInterestRateModel(address)"): interestRateModel := common.BytesToAddress(txLog.Topics[1][:]) mdl.Repo.AddDAOOperation(&schemas.DAOOperation{ @@ -40,7 +46,7 @@ func (mdl *Poolv3) OnLog(txLog types.Log) { case core.Topic("Deposit(address,address,uint256,uint256)"): deposit, err := mdl.contract.ParseDeposit(txLog) log.CheckFatal(err) - mdl.Repo.AddPoolLedger(&schemas.PoolLedger{ + event := &schemas.PoolLedger{ LogId: txLog.Index, BlockNumber: blockNum, TxHash: txLog.TxHash.Hex(), @@ -53,14 +59,19 @@ func (mdl *Poolv3) OnLog(txLog types.Log) { Shares: utils.GetFloat64Decimal(deposit.Shares, mdl.getDecimals()), AmountBI: (*core.BigInt)(deposit.Assets), Amount: utils.GetFloat64Decimal(deposit.Shares, mdl.getDecimals()), - }) + } + if deposit.Sender.Hex() != mdl.getZapUnderlying() && deposit.Sender.Hex() != mdl.getZapPoolv2() { + mdl.Repo.AddPoolLedger(event) + } else { + mdl.changeAddressOnAddLiq(event) + } pool_common.CheckIfAmountMoreThan1Mil(mdl.Client, mdl.Repo, mdl.State, deposit.Assets, blockNum, txLog.TxHash.Hex(), "deposit") mdl.updateBorrowRate(blockNum) // while processing withdrawal event, add to receiver and sub from User case core.Topic("Withdraw(address,address,address,uint256,uint256)"): withdrawal, err := mdl.contract.ParseWithdraw(txLog) log.CheckFatal(err) - mdl.Repo.AddPoolLedger(&schemas.PoolLedger{ + event := &schemas.PoolLedger{ LogId: txLog.Index, BlockNumber: blockNum, TxHash: txLog.TxHash.Hex(), @@ -75,7 +86,13 @@ func (mdl *Poolv3) OnLog(txLog types.Log) { Shares: utils.GetFloat64Decimal(withdrawal.Assets, mdl.getDecimals()), AmountBI: (*core.BigInt)(withdrawal.Assets), Amount: utils.GetFloat64Decimal(withdrawal.Shares, mdl.getDecimals()), - }) + } + if withdrawal.Sender.Hex() != mdl.getZapUnderlying() && withdrawal.Sender.Hex() != mdl.getZapPoolv2() { + mdl.Repo.AddPoolLedger(event) + } else { + mdl.changeAddressOnRemoveLiq(event) + } + // pool_common.CheckIfAmountMoreThan1Mil(mdl.Client, mdl.Repo, mdl.State, withdrawal.Assets, blockNum, txLog.TxHash.Hex(), "withdraw") mdl.updateBorrowRate(blockNum) case core.Topic("Borrow(address,address,uint256)"): @@ -107,7 +124,7 @@ func (mdl *Poolv3) OnLog(txLog types.Log) { mdl.updateBorrowRate(blockNum) case core.Topic("SetPoolQuotaKeeper(address)"): poolQuotaKeeper := common.BytesToAddress(txLog.Topics[1][:]).Hex() - mdl.setPoolQuotaKeeper(poolQuotaKeeper, blockNum) + mdl.setPoolKeeperAdapter(poolQuotaKeeper, blockNum) case core.Topic("AddCreditManager(address)"): newCreditManager := common.BytesToAddress(txLog.Topics[1][:]) mdl.Repo.AddDAOOperation(&schemas.DAOOperation{ @@ -150,7 +167,8 @@ func (mdl *Poolv3) OnLog(txLog types.Log) { } } -func (mdl Poolv3) setPoolQuotaKeeper(poolQuotaKeeper string, blockNum int64) { +func (mdl Poolv3) setPoolKeeperAdapter(poolQuotaKeeper string, blockNum int64) { pqk := pool_quota_keeper.NewPoolQuotaKeeper(poolQuotaKeeper, mdl.Address, blockNum, mdl.Client, mdl.Repo) + mdl.setDetailsByKey("PoolKeeper", poolQuotaKeeper) mdl.Repo.AddSyncAdapter(pqk) } diff --git a/models/pool/pool_v3/state_snapshot_and_gateway_fix.go b/models/pool/pool_v3/state_snapshot_and_gateway_fix.go index 9ebebc4e..1b70cf7f 100644 --- a/models/pool/pool_v3/state_snapshot_and_gateway_fix.go +++ b/models/pool/pool_v3/state_snapshot_and_gateway_fix.go @@ -100,4 +100,5 @@ func (p *Poolv3) createSnapshot(blockNum int64, state dc.PoolCallData) { WithdrawFee: int(state.WithdrawFee.Convert().Int64()), CumulativeIndexRAY: (*core.BigInt)(state.CumulativeIndexRAY), }) + } diff --git a/models/pool_lmrewards/index.go b/models/pool_lmrewards/index.go new file mode 100644 index 00000000..733bffe4 --- /dev/null +++ b/models/pool_lmrewards/index.go @@ -0,0 +1,13 @@ +package pool_lmrewards + +import "github.com/Gearbox-protocol/sdk-go/core" + +type LMReward struct { + User string `gorm:"primaryKey;column:user_address"` + Pool string `gorm:"primaryKey;column:pool"` + Reward *core.BigInt `gorm:"column:reward"` +} + +func (LMReward) TableName() string { + return "lm_rewards" +} diff --git a/models/pool_lmrewards/load_save_to_db.go b/models/pool_lmrewards/v2/load_save_to_db.go similarity index 75% rename from models/pool_lmrewards/load_save_to_db.go rename to models/pool_lmrewards/v2/load_save_to_db.go index 7addb46c..3fcaa76d 100644 --- a/models/pool_lmrewards/load_save_to_db.go +++ b/models/pool_lmrewards/v2/load_save_to_db.go @@ -1,4 +1,4 @@ -package pool_lmrewards +package v2 import ( "math/big" @@ -6,6 +6,7 @@ import ( "github.com/Gearbox-protocol/sdk-go/core" "github.com/Gearbox-protocol/sdk-go/log" "github.com/Gearbox-protocol/sdk-go/utils" + "github.com/Gearbox-protocol/third-eye/models/pool_lmrewards" ) type DieselBalance struct { @@ -19,7 +20,7 @@ func (DieselBalance) TableName() string { return "diesel_balances" } -func (mdl PoolLMRewards) GetDieselBalances() (dieselBalances []DieselBalance) { +func (mdl LMRewardsv2) GetDieselBalances() (dieselBalances []DieselBalance) { if !mdl.hasDataToSave { return } @@ -37,7 +38,7 @@ func (mdl PoolLMRewards) GetDieselBalances() (dieselBalances []DieselBalance) { return dieselBalances } -func (mdl PoolLMRewards) LoadDieselBalances(dieselBalances []DieselBalance) { +func (mdl LMRewardsv2) LoadDieselBalances(dieselBalances []DieselBalance) { for _, dieselBalance := range dieselBalances { if _, ok := mdl.dieselBalances[dieselBalance.Diesel]; !ok { mdl.dieselBalances[dieselBalance.Diesel] = map[string]*big.Int{} @@ -46,23 +47,13 @@ func (mdl PoolLMRewards) LoadDieselBalances(dieselBalances []DieselBalance) { } } -type LMReward struct { - User string `gorm:"primaryKey;column:user_address"` - Pool string `gorm:"primaryKey;column:pool"` - Reward *core.BigInt `gorm:"column:reward"` -} - -func (LMReward) TableName() string { - return "lm_rewards" -} - -func (mdl PoolLMRewards) GetLMRewards() (rewards []LMReward) { +func (mdl LMRewardsv2) GetLMRewards() (rewards []pool_lmrewards.LMReward) { if !mdl.hasDataToSave { return } for pool, rewardForUsers := range mdl.rewards { for user, reward := range rewardForUsers { - rewards = append(rewards, LMReward{ + rewards = append(rewards, pool_lmrewards.LMReward{ User: user, Pool: pool, Reward: (*core.BigInt)(reward), @@ -72,11 +63,11 @@ func (mdl PoolLMRewards) GetLMRewards() (rewards []LMReward) { return rewards } -func (mdl *PoolLMRewards) SyncComplete() { +func (mdl *LMRewardsv2) SyncComplete() { mdl.hasDataToSave = false } -func (mdl PoolLMRewards) LoadLMRewards(rewards []LMReward) { +func (mdl LMRewardsv2) LoadLMRewards(rewards []pool_lmrewards.LMReward) { for _, reward := range rewards { if mdl.rewards[reward.Pool] == nil { mdl.rewards[reward.Pool] = map[string]*big.Int{} @@ -85,7 +76,7 @@ func (mdl PoolLMRewards) LoadLMRewards(rewards []LMReward) { } } -func (mdl *PoolLMRewards) totalSuppliesToDetails() { +func (mdl *LMRewardsv2) totalSuppliesToDetails() { supplies := core.Json{} for tokenSym, totalSupply := range mdl.totalSupplies { supplies[tokenSym] = (*core.BigInt)(totalSupply) @@ -106,7 +97,7 @@ func toBigInt(x interface{}) *big.Int { } return nil } -func (mdl *PoolLMRewards) detailsToTotalSupplies() { +func (mdl *LMRewardsv2) detailsToTotalSupplies() { supplies := map[string]*big.Int{} for tokenSym, totalSupply := range mdl.Details { supplies[tokenSym] = toBigInt(totalSupply) diff --git a/models/pool_lmrewards/log.go b/models/pool_lmrewards/v2/log.go similarity index 85% rename from models/pool_lmrewards/log.go rename to models/pool_lmrewards/v2/log.go index e1da271b..3ff719a6 100644 --- a/models/pool_lmrewards/log.go +++ b/models/pool_lmrewards/v2/log.go @@ -1,4 +1,4 @@ -package pool_lmrewards +package v2 import ( "math/big" @@ -12,7 +12,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" ) -func (mdl *PoolLMRewards) GetAddrsForLogs() (addrs []common.Address) { +func (mdl *LMRewardsv2) GetAddrsForLogs() (addrs []common.Address) { addrs = append(addrs, common.HexToAddress(mdl.Address)) return addrs } @@ -21,12 +21,12 @@ func (mdl *PoolLMRewards) GetAddrsForLogs() (addrs []common.Address) { // -func (mdl *PoolLMRewards) Topics() [][]common.Hash { +func (mdl *LMRewardsv2) Topics() [][]common.Hash { return [][]common.Hash{{core.Topic("Transfer(address,address,uint256)")}} } // onlog for transfer token events only, which is provided by Topics() func -func (mdl *PoolLMRewards) OnLog(txLog types.Log) { +func (mdl *LMRewardsv2) OnLog(txLog types.Log) { // conditions to return amount, ok := new(big.Int).SetString(common.BytesToHash(txLog.Data).Hex()[2:], 16) if !ok { @@ -75,11 +75,11 @@ func (mdl *PoolLMRewards) OnLog(txLog types.Log) { } } -func (mdl PoolLMRewards) addDieselTransfer(dt *schemas.DieselTransfer) { +func (mdl LMRewardsv2) addDieselTransfer(dt *schemas.DieselTransfer) { mdl.Repo.AddDieselTransfer(dt) } -func (mdl PoolLMRewards) addBalance(tokenSym, user string, amount *big.Int) { +func (mdl LMRewardsv2) addBalance(tokenSym, user string, amount *big.Int) { if mdl.dieselBalances[tokenSym] == nil { mdl.dieselBalances[tokenSym] = map[string]*big.Int{} } @@ -90,7 +90,7 @@ func (mdl PoolLMRewards) addBalance(tokenSym, user string, amount *big.Int) { } // inclusive of from and to -func (mdl PoolLMRewards) calculateRewards(from, to int64) { +func (mdl LMRewardsv2) calculateRewards(from, to int64) { snapshots := pkg.GetRewardPerToken(mdl.chainId, from, to) snapStart := from @@ -121,7 +121,7 @@ func (mdl PoolLMRewards) calculateRewards(from, to int64) { } } -func (mdl *PoolLMRewards) addUserReward(pool, user string, reward *big.Int) { +func (mdl *LMRewardsv2) addUserReward(pool, user string, reward *big.Int) { if mdl.rewards[pool] == nil { mdl.rewards[pool] = map[string]*big.Int{} } @@ -131,8 +131,8 @@ func (mdl *PoolLMRewards) addUserReward(pool, user string, reward *big.Int) { ) } -// PoolLMRewards has fake address so no need for adding .Address value to addrs -func (mdl *PoolLMRewards) GetAllAddrsForLogs() (addrs []common.Address) { +// LMRewardsv2 has fake address so no need for adding .Address value to addrs +func (mdl *LMRewardsv2) GetAllAddrsForLogs() (addrs []common.Address) { for addr, poolAndUToken := range mdl.Repo.GetDieselTokens() { if poolAndUToken.Version.MoreThanEq(core.NewVersion(300)) { continue diff --git a/models/pool_lmrewards/model.go b/models/pool_lmrewards/v2/model.go similarity index 82% rename from models/pool_lmrewards/model.go rename to models/pool_lmrewards/v2/model.go index 46d406fb..e54261e2 100644 --- a/models/pool_lmrewards/model.go +++ b/models/pool_lmrewards/v2/model.go @@ -1,4 +1,4 @@ -package pool_lmrewards +package v2 import ( "context" @@ -10,7 +10,7 @@ import ( "github.com/Gearbox-protocol/third-eye/ds" ) -type PoolLMRewards struct { +type LMRewardsv2 struct { *ds.SyncAdapter pendingCalcBlock int64 chainId int64 @@ -29,13 +29,13 @@ type _PoolAndDecimals struct { pool string } -func NewPoolLMRewards(addr string, syncedTill int64, client core.ClientI, repo ds.RepositoryI) *PoolLMRewards { - return NewPoolLMRewardsFromAdapter( +func NewLMRewardsv2(addr string, syncedTill int64, client core.ClientI, repo ds.RepositoryI) *LMRewardsv2 { + return NewLMRewardsv2FromAdapter( &ds.SyncAdapter{ SyncAdapterSchema: &schemas.SyncAdapterSchema{ LastSync: syncedTill, Contract: &schemas.Contract{ - ContractName: ds.PoolLMRewards, + ContractName: ds.LMRewardsv2, Address: addr, Client: client, }, @@ -46,10 +46,10 @@ func NewPoolLMRewards(addr string, syncedTill int64, client core.ClientI, repo d ) } -func NewPoolLMRewardsFromAdapter(adapter *ds.SyncAdapter) *PoolLMRewards { +func NewLMRewardsv2FromAdapter(adapter *ds.SyncAdapter) *LMRewardsv2 { chainId, err := adapter.Client.ChainID(context.Background()) log.CheckFatal(err) - obj := &PoolLMRewards{ + obj := &LMRewardsv2{ SyncAdapter: adapter, pendingCalcBlock: adapter.LastSync + 1, chainId: chainId.Int64(), @@ -63,7 +63,7 @@ func NewPoolLMRewardsFromAdapter(adapter *ds.SyncAdapter) *PoolLMRewards { return obj } -func (mdl *PoolLMRewards) AfterSyncHook(syncedTill int64) { +func (mdl *LMRewardsv2) AfterSyncHook(syncedTill int64) { mdl.calculateRewards(mdl.pendingCalcBlock, syncedTill) mdl.pendingCalcBlock = syncedTill + 1 // diff --git a/models/pool_lmrewards/reward_calc_test.go b/models/pool_lmrewards/v2/reward_calc_test.go similarity index 97% rename from models/pool_lmrewards/reward_calc_test.go rename to models/pool_lmrewards/v2/reward_calc_test.go index 01b0aa87..35c02dfd 100644 --- a/models/pool_lmrewards/reward_calc_test.go +++ b/models/pool_lmrewards/v2/reward_calc_test.go @@ -1,4 +1,4 @@ -package pool_lmrewards +package v2 import ( "testing" @@ -68,7 +68,7 @@ func TestRewardCalc(t *testing.T) { addrs = append(addrs, common.HexToAddress(addr)) } // - obj := NewPoolLMRewards(core.NULL_ADDR.Hex(), 13810899, client, repo) + obj := NewLMRewardsv2(core.NULL_ADDR.Hex(), 13810899, client, repo) obj.GetAllAddrsForLogs() // var till int64 = 16925064 diff --git a/models/pool_lmrewards/v3/calc.go b/models/pool_lmrewards/v3/calc.go new file mode 100644 index 00000000..783b7aaa --- /dev/null +++ b/models/pool_lmrewards/v3/calc.go @@ -0,0 +1,90 @@ +package v3 + +import ( + "math/big" + + "github.com/Gearbox-protocol/sdk-go/core" + "github.com/Gearbox-protocol/sdk-go/log" + "github.com/ethereum/go-ethereum/common" +) + +func (mdl *LMRewardsv3) updateFarmedPerToken(farmAddr string, currentTs uint64) { + // sol:updateFarmedPerToken + farm := mdl.farms[farmAddr] + farm.Checkpoint = currentTs + farm.Fpt = (*core.BigInt)(farm.calcFarmedPerToken(currentTs)) +} + +func (mdl *LMRewardsv3) performTransfer(farmAddr, from, to string, amount *big.Int) { + fromZero := from == core.NULL_ADDR.Hex() + toZero := to == core.NULL_ADDR.Hex() + // + if fromZero || toZero { + diff := amount + if toZero { + diff = new(big.Int).Neg(diff) + } + mdl.farms[farmAddr].TotalSupply = (*core.BigInt)(new(big.Int).Add( + mdl.farms[farmAddr].TotalSupply.Convert(), + diff, + )) + } + // + diesel := mdl.Repo.GetToken(mdl.farms[farmAddr].DieselToken) + if to == "0x2E67A94b39c1946D100D85Ba724c116a652515B9" { + log.Info("", diesel.Decimals) + } + + if mdl.users[common.HexToAddress(farmAddr)] == nil { + mdl.users[common.HexToAddress(farmAddr)] = map[string]*UserLMDetails{} + } + farmAndItsUsers := mdl.users[common.HexToAddress(farmAddr)] + if !fromZero { + if farmAndItsUsers[from] == nil { + farmAndItsUsers[from] = &UserLMDetails{ + Correction: (*core.BigInt)(new(big.Int)), + BalancesBI: (*core.BigInt)(new(big.Int)), + Account: from, + Farm: farmAddr, + DieselSym: diesel.Symbol, + } + } + farmAndItsUsers[from].SubBalances(amount, diesel.Decimals) + } + if !toZero { + if farmAndItsUsers[to] == nil { + farmAndItsUsers[to] = &UserLMDetails{ + Correction: (*core.BigInt)(new(big.Int)), + BalancesBI: (*core.BigInt)(new(big.Int)), + Account: to, + Farm: farmAddr, + DieselSym: diesel.Symbol, + } + } + farmAndItsUsers[to].AddBalances(amount, diesel.Decimals) + } +} +func (mdl *LMRewardsv3) updateBalances(farmAddr, from, to string, amount *big.Int, currentTs uint64) { + // + mdl.performTransfer(farmAddr, from, to, amount) + // + fromZero := from == core.NULL_ADDR.Hex() + toZero := to == core.NULL_ADDR.Hex() + + farm := mdl.farms[farmAddr] + farmAndItsUsers := mdl.users[common.HexToAddress(farmAddr)] + if amount.Sign() > 0 && from != to { + if fromZero || toZero { + mdl.updateFarmedPerToken(farmAddr, currentTs) + } + + // + diff := new(big.Int).Mul(amount, farm.calcFarmedPerToken(currentTs)) + if !fromZero { + farmAndItsUsers[from].SubCorrection(diff) + } + if !toZero { + farmAndItsUsers[to].AddCorrection(diff) + } + } +} diff --git a/models/pool_lmrewards/v3/farm.go b/models/pool_lmrewards/v3/farm.go new file mode 100644 index 00000000..24f371ce --- /dev/null +++ b/models/pool_lmrewards/v3/farm.go @@ -0,0 +1,76 @@ +package v3 + +import ( + "math/big" + + "github.com/Gearbox-protocol/sdk-go/core" + "github.com/Gearbox-protocol/sdk-go/utils" +) + +var _SCALE = utils.GetExpInt(18) + +type Farmv3 struct { + Pool string `gorm:"column:pool"` + Farm string `gorm:"column:farm;primaryKey"` + DieselToken string `gorm:"column:diesel_token"` + // + Checkpoint uint64 `gorm:"column:checkpoint"` + Fpt *core.BigInt `gorm:"column:farmed_per_token"` + // + Reward *core.BigInt `gorm:"column:reward"` + Period uint64 `gorm:"column:period"` + EndTs uint64 `gorm:"column:end_ts"` + // + TotalSupply *core.BigInt `gorm:"column:total_supply"` +} + +func (Farmv3) TableName() string { + return "farm_v3" +} + +// farmAccounting +func (farm *Farmv3) startFarming(reward *big.Int, newPeriod, currentTs uint64) { + lastEndTs := farm.EndTs + if lastEndTs > currentTs { + finishedRewards := new(big.Int).Quo(farm.farmedSinceCheckpointScaled(currentTs), _SCALE) + remainingFunds := new(big.Int).Sub(farm.Reward.Convert(), finishedRewards) + reward = new(big.Int).Add(reward, remainingFunds) + } + // + farm.EndTs = currentTs + newPeriod + farm.Period = newPeriod + farm.Reward = (*core.BigInt)(reward) +} +func (farm *Farmv3) stopFarming(reward *big.Int, currentTs uint64) { + farm.EndTs = currentTs + farm.Period = 0 + farm.Reward = (*core.BigInt)(new(big.Int)) +} + +func (farm *Farmv3) farmedSinceCheckpointScaled(currentTs uint64) *big.Int { + if farm.Period == 0 { + return big.NewInt(0) + } + elapsed := utils.Min(currentTs, farm.EndTs) - (farm.EndTs - farm.Period) + num := new(big.Int).Mul( + new(big.Int).Mul(big.NewInt(int64(elapsed)), farm.Reward.Convert()), + _SCALE, + ) + return new(big.Int).Quo( + num, + big.NewInt(int64(farm.Period)), + ) +} + +// userAccounting +func (farm *Farmv3) calcFarmedPerToken(currentTs uint64) *big.Int { + fpt := farm.Fpt.Convert() + if farm.TotalSupply.Convert().Sign() != 0 { + _fpt := new(big.Int).Quo( + farm.farmedSinceCheckpointScaled(currentTs), + farm.TotalSupply.Convert(), + ) + fpt = new(big.Int).Add(fpt, _fpt) + } + return fpt +} diff --git a/models/pool_lmrewards/v3/log.go b/models/pool_lmrewards/v3/log.go new file mode 100644 index 00000000..390463cb --- /dev/null +++ b/models/pool_lmrewards/v3/log.go @@ -0,0 +1,72 @@ +package v3 + +import ( + "math/big" + + "github.com/Gearbox-protocol/sdk-go/core" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// https://etherscan.io/address/0x9ef444a6d7f4a5adcd68fd5329aa5240c90e14d2#code farmingPool +func (mdl LMRewardsv3) OnLog(txLog types.Log) { + farmAddr := txLog.Address.Hex() + currentTs := mdl.Repo.SetAndGetBlock(int64(txLog.BlockNumber)).Timestamp + switch txLog.Topics[0] { + case core.Topic("Transfer(address,address,uint256)"): + from := common.BytesToAddress(txLog.Topics[1][:]).Hex() + to := common.BytesToAddress(txLog.Topics[2][:]).Hex() + amount := new(big.Int).SetBytes(txLog.Data) + mdl.updateBalances(farmAddr, from, to, amount, currentTs) + case core.Topic("RewardUpdated(uint256,uint256)"): + // sol:updateFarmedPerToken + mdl.updateFarmedPerToken(farmAddr, currentTs) + // farmInfo.startFarming + farm := mdl.farms[farmAddr] + reward := new(big.Int).SetBytes(txLog.Data[:32]) + period := new(big.Int).SetBytes(txLog.Data[32:]) + if period.Int64() == 0 { // startFarming can't have 0 period as it reverts in sol + farm.stopFarming(reward, currentTs) + } else { + farm.startFarming(reward, period.Uint64(), currentTs) + } + } +} + +func (mdl *LMRewardsv3) getFarmsv3() { + if len(mdl.farms) != 0 { // already set + return + } + pools, found := mdl.Repo.GetDCWrapper().GetPoolListv3() + if found && len(mdl.farms) == 0 { + addrToSym := core.GetAddrToSymbolByChainId(core.GetChainId(mdl.Client)) + poolAndFarms := []*Farmv3{} + for _, pool := range pools { + for _, zapper := range pool.Zappers { + if _, ok := addrToSym[zapper.TokenOut]; !ok && zapper.TokenIn == pool.Underlying { + poolAndFarms = append(poolAndFarms, &Farmv3{ + Farm: zapper.TokenOut.Hex(), + Pool: pool.Addr.Hex(), + DieselToken: pool.DieselToken.Hex(), + // initial + Fpt: (*core.BigInt)(new(big.Int)), + TotalSupply: (*core.BigInt)(new(big.Int)), + Reward: (*core.BigInt)(new(big.Int)), + }) + } + } + } + mdl.SetUnderlyingState(poolAndFarms) + } +} + +// LMRewardsv2 has fake address so no need for adding .Address value to addrs +func (mdl *LMRewardsv3) GetAllAddrsForLogs() (addrs []common.Address) { + mdl.getFarmsv3() + // + for addr := range mdl.farms { + addrs = append(addrs, common.HexToAddress(addr)) + } + addrs = append(addrs, common.HexToAddress(mdl.Address)) + return addrs +} diff --git a/models/pool_lmrewards/v3/model.go b/models/pool_lmrewards/v3/model.go new file mode 100644 index 00000000..c54181ca --- /dev/null +++ b/models/pool_lmrewards/v3/model.go @@ -0,0 +1,92 @@ +package v3 + +import ( + "math/big" + + "github.com/Gearbox-protocol/sdk-go/core" + "github.com/Gearbox-protocol/sdk-go/core/schemas" + "github.com/Gearbox-protocol/sdk-go/utils" + "github.com/Gearbox-protocol/third-eye/ds" + "github.com/ethereum/go-ethereum/common" +) + +// farmingPool https://etherscan.io/address/0x9ef444a6d7f4a5adcd68fd5329aa5240c90e14d2#code +// farmAccounting +// userAccounting +// farmingLib +type UserLMDetails struct { + Correction *core.BigInt `gorm:"column:correction"` + BalancesBI *core.BigInt `gorm:"column:balances_bi"` + Balances float64 `gorm:"column:balances"` + Account string `gorm:"column:account;primaryKey"` + Farm string `gorm:"column:farm;primaryKey"` + DieselSym string `gorm:"column:diesel_sym"` + updated bool `gorm:"-"` +} + +func (UserLMDetails) TableName() string { + return "user_lmdetails_v3" +} + +func (user UserLMDetails) GetPoints(farm *Farmv3, currentTs uint64) *big.Int { + fpt := farm.calcFarmedPerToken(currentTs) + num := new(big.Int).Mul(user.BalancesBI.Convert(), fpt) + + // + return new(big.Int).Quo(new(big.Int).Sub(num, user.Correction.Convert()), _SCALE) +} + +func (details *UserLMDetails) AddBalances(amount *big.Int, decimals int8) { + details.updated = true + details.BalancesBI = (*core.BigInt)(new(big.Int).Add(details.BalancesBI.Convert(), amount)) + details.Balances = utils.GetFloat64Decimal(details.BalancesBI.Convert(), decimals) +} +func (details *UserLMDetails) SubBalances(amount *big.Int, decimals int8) { + details.updated = true + details.AddBalances(new(big.Int).Neg(amount), decimals) +} +func (details *UserLMDetails) AddCorrection(amount *big.Int) { + details.updated = true + details.Correction = (*core.BigInt)(new(big.Int).Add(details.Correction.Convert(), amount)) +} +func (details *UserLMDetails) SubCorrection(amount *big.Int) { + details.updated = true + details.AddCorrection(new(big.Int).Neg(amount)) +} + +type LMRewardsv3 struct { + *ds.SyncAdapter + farms map[string]*Farmv3 + // farmv3 to user to balance + users map[common.Address]map[string]*UserLMDetails +} + +func NewLMRewardsv3(addr string, syncedTill int64, client core.ClientI, repo ds.RepositoryI) *LMRewardsv3 { + return NewLMRewardsv3FromAdapter( + &ds.SyncAdapter{ + SyncAdapterSchema: &schemas.SyncAdapterSchema{ + LastSync: syncedTill, + Contract: &schemas.Contract{ + ContractName: ds.LMRewardsv3, + Address: addr, + Client: client, + }, + V: core.NewVersion(300), + }, + Repo: repo, + }, + ) +} + +func NewLMRewardsv3FromAdapter(adapter *ds.SyncAdapter) *LMRewardsv3 { + // chainId, err := adapter.Client.ChainID(context.Background()) + // log.CheckFatal(err) + obj := &LMRewardsv3{ + SyncAdapter: adapter, + } + return obj +} + +func (mdl *LMRewardsv3) AfterSyncHook(syncedTill int64) { + mdl.SyncAdapter.AfterSyncHook(syncedTill) +} diff --git a/models/pool_lmrewards/v3/state.go b/models/pool_lmrewards/v3/state.go new file mode 100644 index 00000000..e30b1564 --- /dev/null +++ b/models/pool_lmrewards/v3/state.go @@ -0,0 +1,71 @@ +package v3 + +import ( + "github.com/Gearbox-protocol/sdk-go/core" + "github.com/Gearbox-protocol/sdk-go/log" + "github.com/ethereum/go-ethereum/common" + + "github.com/Gearbox-protocol/third-eye/models/pool_lmrewards" + "gorm.io/gorm" + "gorm.io/gorm/clause" +) + +func (mdl *LMRewardsv3) SetUnderlyingState(obj interface{}) { + switch ans := obj.(type) { + case []*Farmv3: + farms := map[string]*Farmv3{} + for _, farm := range ans { + farms[farm.Farm] = farm + } + mdl.farms = farms + case []*UserLMDetails: + users := map[common.Address]map[string]*UserLMDetails{} + for _, user := range ans { + if users[common.HexToAddress(user.Farm)] == nil { + users[common.HexToAddress(user.Farm)] = map[string]*UserLMDetails{} + } + users[common.HexToAddress(user.Farm)][user.Account] = user + } + mdl.users = users + default: + log.Fatalf("Not able to parse underlying state for %T", obj) + } +} + +func (mdl *LMRewardsv3) Save(tx *gorm.DB, currentTs uint64) { + farms := []*Farmv3{} + for _, entry := range mdl.farms { + farms = append(farms, entry) + } + err := tx.Clauses(clause.OnConflict{UpdateAll: true}).CreateInBatches(farms, 500).Error + log.CheckFatal(err) + + // + users := []*UserLMDetails{} + for _, farmAndItsUsers := range mdl.users { + for _, entry := range farmAndItsUsers { + if entry.updated { + users = append(users, entry) + entry.updated = false + } + } + } + err = tx.Clauses(clause.OnConflict{UpdateAll: true}).CreateInBatches(users, 500).Error + log.CheckFatal(err) + + // + rewards := []*pool_lmrewards.LMReward{} + for _, farmAndItsUsers := range mdl.users { + for _, user := range farmAndItsUsers { + farm := mdl.farms[user.Farm] + reward := user.GetPoints(farm, currentTs) + rewards = append(rewards, &pool_lmrewards.LMReward{ + User: user.Account, + Pool: user.Farm, + Reward: (*core.BigInt)(reward), + }) + } + } + err = tx.Clauses(clause.OnConflict{UpdateAll: true}).CreateInBatches(rewards, 500).Error + log.CheckFatal(err) +} diff --git a/models/wrappers/pool_wrapper/index.go b/models/wrappers/pool_wrapper/index.go index c2e9bdb8..ccc127a9 100644 --- a/models/wrappers/pool_wrapper/index.go +++ b/models/wrappers/pool_wrapper/index.go @@ -3,7 +3,9 @@ package pool_wrapper import ( "github.com/Gearbox-protocol/sdk-go/core" "github.com/Gearbox-protocol/third-eye/ds" + "github.com/Gearbox-protocol/third-eye/models/pool/pool_v3" "github.com/Gearbox-protocol/third-eye/models/wrappers" + "gorm.io/gorm" ) type PoolWrapper struct { @@ -17,3 +19,11 @@ func NewPoolWrapper(client core.ClientI) *PoolWrapper { w.ViaDataProcess = ds.ViaMultipleLogs return w } + +func (w *PoolWrapper) UpdatePoolv2Ledger(tx *gorm.DB) { + for _, adapter := range w.Adapters.GetAll() { + if adapter.GetVersion().MoreThanEq(core.NewVersion(300)) { + adapter.(*pool_v3.Poolv3).UpdatePoolv2Ledger(tx) + } + } +} diff --git a/repository/debt_db.go b/repository/debt_db.go index 40375aa7..cca5e273 100644 --- a/repository/debt_db.go +++ b/repository/debt_db.go @@ -8,13 +8,12 @@ import ( func (repo *Repository) LoadLastDebtSync() int64 { data := schemas.DebtSync{} query := `SELECT max(b) as last_calculated_at from - (select min(firstlog_at) as b from sync_Adapters where type!='PoolLMRewards' + (select min(firstlog_at) as b from sync_Adapters + WHERE type NOT IN ('RebaseToken','Treasury','LMRewardsv2','LMRewardsv3','GearToken') union select max(last_calculated_at) as b FROM debt_sync) tmp` err := repo.db.Raw(query).Find(&data).Error - if err != nil { - log.Fatal(err) - } + log.CheckFatal(err) // last debt sync starts from the discover at of address provider to the last debt block stored in debt_sync table if data.LastCalculatedAt != 0 { return data.LastCalculatedAt diff --git a/repository/handlers/kit_handler.go b/repository/handlers/kit_handler.go index b86dc6b1..40d3caa3 100644 --- a/repository/handlers/kit_handler.go +++ b/repository/handlers/kit_handler.go @@ -49,7 +49,7 @@ func NewAdpterKitHandler(client core.ClientI, repo ds.RepositoryI, cfg *config.C // injected in the app itself // are aggregatedBlockFeed, cfWrapper // -// whereas adapter with fake address are AccountManager, and PoolLMRewards and CompositeChainlinkPF +// whereas adapter with fake address are AccountManager, and LMRewardsv2 and CompositeChainlinkPF func (handler *AdapterKitHandler) addSyncAdapter(adapterI ds.SyncAdapterI) { switch adapterI.GetName() { case ds.QueryPriceFeed: diff --git a/repository/handlers/sync_adapters.go b/repository/handlers/sync_adapters.go index 1255b47d..661e076f 100644 --- a/repository/handlers/sync_adapters.go +++ b/repository/handlers/sync_adapters.go @@ -22,11 +22,13 @@ import ( "github.com/Gearbox-protocol/third-eye/models/credit_manager" "github.com/Gearbox-protocol/third-eye/models/gear_token" "github.com/Gearbox-protocol/third-eye/models/pool" - "github.com/Gearbox-protocol/third-eye/models/pool_lmrewards" + lmrewardsv2 "github.com/Gearbox-protocol/third-eye/models/pool_lmrewards/v2" + lmrewardsv3 "github.com/Gearbox-protocol/third-eye/models/pool_lmrewards/v3" "github.com/Gearbox-protocol/third-eye/models/pool_quota_keeper" "github.com/Gearbox-protocol/third-eye/models/price_oracle" "github.com/Gearbox-protocol/third-eye/models/rebase_token" "github.com/Gearbox-protocol/third-eye/models/treasury" + "github.com/Gearbox-protocol/third-eye/models/wrappers/pool_wrapper" "gorm.io/gorm" "gorm.io/gorm/clause" ) @@ -120,8 +122,10 @@ func (repo *SyncAdaptersRepo) PrepareSyncAdapter(adapter *ds.SyncAdapter) ds.Syn repo.extras.GetDCWrapper().LoadMultipleDC(ap.Details["dc"]) } return ap - case ds.PoolLMRewards: - return pool_lmrewards.NewPoolLMRewardsFromAdapter(adapter) + case ds.LMRewardsv2: + return lmrewardsv2.NewLMRewardsv2FromAdapter(adapter) + case ds.LMRewardsv3: + return lmrewardsv3.NewLMRewardsv3FromAdapter(adapter) case ds.AccountFactory: return account_factory.NewAccountFactoryFromAdapter(adapter) case ds.Pool: @@ -186,6 +190,9 @@ func (repo *SyncAdaptersRepo) AddSyncAdapter(newAdapterI ds.SyncAdapterI) { func (repo *SyncAdaptersRepo) GetKit() *ds.AdapterKit { return repo.kit } +func (repo *SyncAdaptersRepo) GetPoolWrapper() *pool_wrapper.PoolWrapper { + return repo.poolWrapper +} //////////////////// // for price oracle diff --git a/repository/index.go b/repository/index.go index 5d8e33f0..972f33d7 100644 --- a/repository/index.go +++ b/repository/index.go @@ -88,7 +88,8 @@ func (repo *Repository) Init() { // syncadapter state for cm and pool is set after loading of pool/credit manager table data from db repo.SyncAdaptersRepo.LoadSyncAdapters(repo.db) // load poolLMrewards - repo.loadLMRewardDetails() + repo.loadLMRewardDetailsv2() + repo.loadLMRewardDetailsv3() repo.loadLastRebaseDetails() // // for disabling previous token oracle if new oracle is set diff --git a/repository/reward.go b/repository/reward_v2.go similarity index 61% rename from repository/reward.go rename to repository/reward_v2.go index c24288d0..02070b45 100644 --- a/repository/reward.go +++ b/repository/reward_v2.go @@ -5,28 +5,29 @@ import ( "github.com/Gearbox-protocol/sdk-go/utils" "github.com/Gearbox-protocol/third-eye/ds" "github.com/Gearbox-protocol/third-eye/models/pool_lmrewards" + lmrewardsv2 "github.com/Gearbox-protocol/third-eye/models/pool_lmrewards/v2" "gorm.io/gorm" "gorm.io/gorm/clause" ) -func (repo *Repository) loadLMRewardDetails() { - defer utils.Elapsed("loadLMRewardDetails")() - adapterAddrs := repo.GetAdapterAddressByName(ds.PoolLMRewards) +func (repo *Repository) loadLMRewardDetailsv2() { + defer utils.Elapsed("loadLMRewardDetailsv2")() + adapterAddrs := repo.GetAdapterAddressByName(ds.LMRewardsv2) if len(adapterAddrs) == 0 { return } // load poolLMRewardadapter adapterAddr := adapterAddrs[0] - adapter := repo.GetAdapter(adapterAddr).(*pool_lmrewards.PoolLMRewards) + adapter := repo.GetAdapter(adapterAddr).(*lmrewardsv2.LMRewardsv2) // lm rewards rewardData := []pool_lmrewards.LMReward{} - err := repo.db.Raw(`SELECT * FROM lm_rewards`).Find(&rewardData).Error + err := repo.db.Raw(`SELECT * FROM lm_rewards where pool in (SELECT address FROM pools where _version!=300)`).Find(&rewardData).Error if err != nil { log.Fatal(err) } adapter.LoadLMRewards(rewardData) // - dBalanceData := []pool_lmrewards.DieselBalance{} + dBalanceData := []lmrewardsv2.DieselBalance{} err = repo.db.Raw(`SELECT * FROM diesel_balances`).Find(&dBalanceData).Error if err != nil { log.Fatal(err) @@ -34,9 +35,9 @@ func (repo *Repository) loadLMRewardDetails() { adapter.LoadDieselBalances(dBalanceData) } -func (repo Repository) saveLMRewardDetails(tx *gorm.DB) { - adapterAddr := repo.GetAdapterAddressByName(ds.PoolLMRewards)[0] - adapter := repo.GetAdapter(adapterAddr).(*pool_lmrewards.PoolLMRewards) +func (repo Repository) saveLMRewardDetailsv2(tx *gorm.DB) { + adapterAddr := repo.GetAdapterAddressByName(ds.LMRewardsv2)[0] + adapter := repo.GetAdapter(adapterAddr).(*lmrewardsv2.LMRewardsv2) // if rewards := adapter.GetLMRewards(); len(rewards) != 0 { diff --git a/repository/reward_v3.go b/repository/reward_v3.go new file mode 100644 index 00000000..29d0bb87 --- /dev/null +++ b/repository/reward_v3.go @@ -0,0 +1,39 @@ +package repository + +import ( + "github.com/Gearbox-protocol/sdk-go/log" + "github.com/Gearbox-protocol/sdk-go/utils" + "github.com/Gearbox-protocol/third-eye/ds" + lmrewardsv3 "github.com/Gearbox-protocol/third-eye/models/pool_lmrewards/v3" + "gorm.io/gorm" +) + +func (repo *Repository) loadLMRewardDetailsv3() { + defer utils.Elapsed("loadLMRewardDetailsv3")() + // load poolLMRewardadapter + adapterAddrs := repo.GetAdapterAddressByName(ds.LMRewardsv3) + if len(adapterAddrs) == 0 { + return + } + adapterAddr := adapterAddrs[0] + adapter := repo.GetAdapter(adapterAddr).(*lmrewardsv3.LMRewardsv3) + // + farms := []*lmrewardsv3.Farmv3{} + err := repo.db.Raw(`SELECT * FROM farm_v3`).Find(&farms).Error + log.CheckFatal(err) + adapter.SetUnderlyingState(farms) + // + details := []*lmrewardsv3.UserLMDetails{} + err = repo.db.Raw(`SELECT * FROM user_lmdetails_v3`).Find(&details).Error + log.CheckFatal(err) + adapter.SetUnderlyingState(details) +} + +func (repo Repository) saveLMRewardDetailsv3(tx *gorm.DB, syncTill int64) { + adapterAddr := repo.GetAdapterAddressByName(ds.LMRewardsv3)[0] + adapter := repo.GetAdapter(adapterAddr).(*lmrewardsv3.LMRewardsv3) + // + currentTs := repo.SetAndGetBlock(syncTill).Timestamp + adapter.Save(tx, currentTs) + +} diff --git a/repository/save.go b/repository/save.go index 28b40359..38805c03 100644 --- a/repository/save.go +++ b/repository/save.go @@ -24,7 +24,8 @@ func (repo *Repository) Flush(syncTill int64) error { repo.TokensRepo.Save(tx) repo.SyncAdaptersRepo.Save(tx) - repo.saveLMRewardDetails(tx) // save LM reward and diesel token balances of users + repo.saveLMRewardDetailsv2(tx) // save LM reward and diesel token balances of users + repo.saveLMRewardDetailsv3(tx, syncTill) repo.SessionRepo.Save(tx) @@ -34,6 +35,10 @@ func (repo *Repository) Flush(syncTill int64) error { // save current treasury snapshot repo.TreasuryRepo.Save(tx) + + // fix pool v2 ledger + repo.GetPoolWrapper().UpdatePoolv2Ledger(tx) + // info := tx.Commit() log.CheckFatal(info.Error) return nil diff --git a/scripts/fix_pool_ledger_v3/main.go b/scripts/fix_pool_ledger_v3/main.go new file mode 100644 index 00000000..5b1b083c --- /dev/null +++ b/scripts/fix_pool_ledger_v3/main.go @@ -0,0 +1,119 @@ +package main + +import ( + "github.com/Gearbox-protocol/sdk-go/core" + "github.com/Gearbox-protocol/sdk-go/core/schemas" + "github.com/Gearbox-protocol/sdk-go/log" + "github.com/Gearbox-protocol/sdk-go/pkg" + "github.com/Gearbox-protocol/sdk-go/pkg/priceFetcher" + "github.com/Gearbox-protocol/third-eye/config" + "github.com/Gearbox-protocol/third-eye/ds" + "github.com/Gearbox-protocol/third-eye/ds/dc_wrapper" + "github.com/Gearbox-protocol/third-eye/ethclient" + "github.com/Gearbox-protocol/third-eye/models/pool/pool_v3" + "github.com/Gearbox-protocol/third-eye/repository" + "github.com/ethereum/go-ethereum/common" + "gorm.io/gorm/clause" +) + +// dUSDC - farmedUSDCv3 https://etherscan.io/tx/0x737fb7e55268d6ef95806c60e074948515fa19e1add8499f20f18ad3f62cf250 +type Repo struct { + ds.DummyRepo + events []*schemas.PoolLedger + dc *dc_wrapper.DataCompressorWrapper + dieselTokens map[string]*schemas.UTokenAndPool + tStore *priceFetcher.TokensStore +} + +func (r *Repo) AddPoolLedger(event *schemas.PoolLedger) { + r.events = append(r.events, event) +} +func (r *Repo) GetToken(token string) *schemas.Token { + return r.tStore.GetToken(token) +} +func (r *Repo) getPoolLedger() []*schemas.PoolLedger { + ans := r.events + r.events = nil + return ans +} + +func (r *Repo) AddDieselToken(dieselToken, underlyingToken, pool string, version core.VersionType) { + r.dieselTokens[dieselToken] = &schemas.UTokenAndPool{ + UToken: underlyingToken, + Pool: pool, + Version: version, + } +} + +func (r *Repo) GetDieselTokens() map[string]*schemas.UTokenAndPool { + return r.dieselTokens +} + +func (r *Repo) GetDCWrapper() *dc_wrapper.DataCompressorWrapper { + return r.dc +} + +func NewRepo(client core.ClientI) *Repo { + r := Repo{dieselTokens: map[string]*schemas.UTokenAndPool{}, tStore: priceFetcher.NewTokensStore(client)} + r.dc = dc_wrapper.NewDataCompressorWrapper(client) + r.dc.AddDataCompressorByVersion(core.NewVersion(300), "0xc0101abAFce0BD3de10aa1F3dd827672B150436E", 18798875) + return &r +} +func main() { + cfg := config.NewConfig() + db := repository.NewDBClient(cfg) + client := ethclient.NewEthClient(cfg) + r := NewRepo(client) + + var adapters []*ds.SyncAdapter + err := db.Raw(`SELECT * from sync_adapters where type='Pool' and version=300`).Find(&adapters).Error + log.CheckFatal(err) + + states := []*schemas.PoolState{} + err = db.Raw(`SELECT * from pools where _version=300`).Find(&states).Error + log.CheckFatal(err) + { + states := []*schemas.PoolState{} + err = db.Raw(`SELECT * from pools`).Find(&states).Error + log.CheckFatal(err) + for _, state := range states { + r.AddDieselToken(state.DieselToken, state.UnderlyingToken, state.Address, state.Version) + } + } + + pools := map[string]*pool_v3.Poolv3{} + for _, adapter := range adapters { + adapter.Client = client + adapter.Repo = r + pool := pool_v3.NewPoolFromAdapter(adapter) + pools[pool.GetAddress()] = pool + } + + for _, state := range states { + pool := pools[state.Address] + pool.SetUnderlyingState(state) + } + + for _, pool := range pools { + txLogs, err := pkg.Node{Client: client}.GetLogs(pool.FirstLogAt, pool.LastSync, + pool.GetAllAddrsForLogs(), + [][]common.Hash{{ + core.Topic("Transfer(address,address,uint256)"), + core.Topic("Deposit(address,address,uint256,uint256)"), + core.Topic("Withdraw(address,address,address,uint256,uint256)"), + }}) + log.CheckFatal(err) + for _, txLog := range txLogs { + log.Info(pool.GetAddress(), txLog.BlockNumber) + pool.OnLog(txLog) + } + err = db.Exec(`DELETE from pool_ledger where pool=? and event in ('AddLiquidity','RemoveLiquidity')`, pool.GetAddress()).Error + log.CheckFatal(err) + events := r.getPoolLedger() + err = db.CreateInBatches(events, 500).Error + log.CheckFatal(err) + pool.UpdatePoolv2Ledger(db) + err = db.Clauses(clause.OnConflict{UpdateAll: true}).Create(pool.SyncAdapter).Error + log.CheckFatal(err) + } +}