@@ -18,19 +18,36 @@ package upgrade
1818
1919import (
2020 "context"
21+ "errors"
22+ "fmt"
23+ "time"
2124
25+ "cosmossdk.io/core/address"
2226 "cosmossdk.io/log"
27+ "cosmossdk.io/math"
2328 upgradetypes "cosmossdk.io/x/upgrade/types"
2429 sdk "github.com/cosmos/cosmos-sdk/types"
2530 "github.com/cosmos/cosmos-sdk/types/module"
26- clientkeeper "github.com/cosmos/ibc-go/v8/modules/core/02-client/keeper"
31+ authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
32+ bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
33+ swaptypes "swap.noble.xyz/types"
34+
35+ dollarkeeper "dollar.noble.xyz/v2/keeper"
36+ dollartypes "dollar.noble.xyz/v2/types"
37+ authoritykeeper "github.com/noble-assets/authority/keeper"
38+ swapkeeper "swap.noble.xyz/keeper"
39+ stableswaptypes "swap.noble.xyz/types/stableswap"
2740)
2841
2942func CreateUpgradeHandler (
3043 mm * module.Manager ,
3144 cfg module.Configurator ,
3245 logger log.Logger ,
33- clientKeeper clientkeeper.Keeper ,
46+ addressCodec address.Codec ,
47+ authorityKeeper * authoritykeeper.Keeper ,
48+ bankKeeper bankkeeper.Keeper ,
49+ dollarKeeper * dollarkeeper.Keeper ,
50+ swapKeeper * swapkeeper.Keeper ,
3451) upgradetypes.UpgradeHandler {
3552 return func (ctx context.Context , _ upgradetypes.Plan , vm module.VersionMap ) (module.VersionMap , error ) {
3653 vm , err := mm .RunMigrations (ctx , cfg , vm )
@@ -40,18 +57,164 @@ func CreateUpgradeHandler(
4057
4158 sdkCtx := sdk .UnwrapSDKContext (ctx )
4259
43- // In IBC-Go v8.7.0, the MsgRecoverClient message does not support the
44- // LegacyAminoJSON signing mode, preventing recovery via the Noble
45- // Maintenance Multisig. As a result, expired clients on mainnet must
46- // be manually recovered as part of a software upgrade.
4760 if sdkCtx .ChainID () == MainnetChainID {
48- // Substitute the IBC light client for the coreum-mainnet-1 chain.
49- err = clientKeeper .RecoverClient (sdkCtx , "07-tendermint-71" , "07-tendermint-226" )
50- if err != nil {
51- logger .Error ("failed to recover coreum-mainnet-1 client" , "error" , err )
61+ if err = claimSwapPoolsYield (
62+ ctx ,
63+ logger ,
64+ addressCodec ,
65+ authorityKeeper ,
66+ bankKeeper ,
67+ dollarKeeper ,
68+ swapKeeper ,
69+ ); err != nil {
70+ return vm , err
71+ }
72+
73+ if err = claimSwapPoolsProtocolFees (
74+ ctx ,
75+ logger ,
76+ swapKeeper ,
77+ "noble1c3chgrgr3xcktkpvezxz7g9kl7h64x8tdyd8ng" ,
78+ ); err != nil {
79+ return vm , err
80+ }
81+
82+ if err = closeSwapPools (ctx , logger , swapKeeper ); err != nil {
83+ return vm , err
5284 }
5385 }
5486
5587 return vm , nil
5688 }
5789}
90+
91+ // claimSwapPoolsYield claims the $USDN yield accrued inside the Noble Swap
92+ // pools and sends it to the authority address.
93+ func claimSwapPoolsYield (
94+ ctx context.Context ,
95+ logger log.Logger ,
96+ addressCodec address.Codec ,
97+ authorityKeeper * authoritykeeper.Keeper ,
98+ bankKeeper bankkeeper.Keeper ,
99+ dollarKeeper * dollarkeeper.Keeper ,
100+ swapKeeper * swapkeeper.Keeper ,
101+ ) error {
102+ authority , err := authorityKeeper .Owner .Get (ctx )
103+ if err != nil {
104+ return errors .New ("unable to get underlying authority address from state" )
105+ }
106+ authorityBz , err := addressCodec .StringToBytes (authority )
107+ if err != nil {
108+ return errors .New ("unable to decode underlying authority address" )
109+ }
110+
111+ dollarServer := dollarkeeper .NewMsgServer (dollarKeeper )
112+
113+ pools := swapKeeper .GetPools (ctx )
114+ for _ , pool := range pools {
115+ yield , address , err := dollarKeeper .GetYield (ctx , pool .Address )
116+ if err != nil {
117+ return fmt .Errorf ("unable to get yield for pool %d" , pool .Id )
118+ }
119+
120+ _ , err = dollarServer .ClaimYield (ctx , & dollartypes.MsgClaimYield {Signer : pool .Address })
121+ if err != nil {
122+ return fmt .Errorf ("unable to claim yield for pool %d" , pool .Id )
123+ }
124+
125+ err = bankKeeper .SendCoins (ctx , address , authorityBz , sdk .NewCoins (sdk .NewCoin (dollarKeeper .GetDenom (), yield )))
126+ if err != nil {
127+ return fmt .Errorf ("unable to transfer yield for pool %d" , pool .Id )
128+ }
129+
130+ logger .Info ("claimed swap pool yield" , "pool" , pool .Id , "yield" , yield )
131+ }
132+
133+ return nil
134+ }
135+
136+ // claimSwapPoolsProtocolFees claims the protocol fees accrued inside the Noble Swap
137+ // pools.
138+ func claimSwapPoolsProtocolFees (
139+ ctx context.Context ,
140+ logger log.Logger ,
141+ swapKeeper * swapkeeper.Keeper ,
142+ protocolFeesReceiver string ,
143+ ) error {
144+ swapServer := swapkeeper .NewMsgServer (swapKeeper )
145+
146+ _ , err := swapServer .WithdrawProtocolFees (ctx , & swaptypes.MsgWithdrawProtocolFees {
147+ Signer : authtypes .NewModuleAddressOrBech32Address ("authority" ).String (),
148+ To : protocolFeesReceiver ,
149+ })
150+ if err != nil {
151+ logger .Error (err .Error ())
152+ }
153+
154+ logger .Info ("claimed swap protocol fees" )
155+
156+ return nil
157+ }
158+
159+ func closeSwapPools (ctx context.Context , logger log.Logger , swapKeeper * swapkeeper.Keeper ) error {
160+ swapServer := swapkeeper .NewStableSwapMsgServer (swapKeeper )
161+
162+ // Iterate all the users that currently have bonded shares, which reflects all the liquidity providers.
163+ itr , err := swapKeeper .Stableswap .UsersTotalBondedShares .Iterate (ctx , nil )
164+ if err != nil {
165+ return err
166+ }
167+ for ; itr .Valid (); itr .Next () {
168+ key , _ := itr .Key ()
169+ poolId := key .K1 ()
170+ userAddress := key .K2 ()
171+
172+ amount , amountErr := itr .Value ()
173+ if amountErr != nil {
174+ return amountErr
175+ }
176+
177+ // Skip users that have already removed all liquidity.
178+ if ! amount .IsPositive () {
179+ continue
180+ }
181+
182+ if _ , err = swapServer .RemoveLiquidity (ctx , & stableswaptypes.MsgRemoveLiquidity {
183+ Signer : userAddress ,
184+ PoolId : poolId ,
185+ Percentage : math .LegacyNewDec (100 ), // 100%
186+ }); err != nil {
187+ return err
188+ }
189+
190+ // In order to trigger an immediate execution we need to update all the unbonding with an EndTime in the past.
191+ unbondEndTime := sdk .UnwrapSDKContext (ctx ).HeaderInfo ().Time .Add (- 24 * 3 * time .Hour )
192+ userTotalAmount := sdk .NewCoins ()
193+ for _ , position := range swapKeeper .Stableswap .GetUnbondingPositionsByProvider (ctx , userAddress ) {
194+ if err = swapKeeper .Stableswap .RemoveUnbondingPosition (ctx , position .Timestamp , position .Address , position .PoolId ); err != nil {
195+ return err
196+ }
197+ if err = swapKeeper .Stableswap .SetUnbondingPosition (ctx , unbondEndTime .Unix (), position .Address , position .PoolId , stableswaptypes.UnbondingPosition {
198+ Shares : position .UnbondingPosition .Shares ,
199+ Amount : position .UnbondingPosition .Amount ,
200+ EndTime : unbondEndTime ,
201+ }); err != nil {
202+ return err
203+ }
204+ userTotalAmount = userTotalAmount .Add (position .UnbondingPosition .Amount ... )
205+ }
206+ logger .Info ("removing liquidy" , "pool" , poolId , "address" , userAddress , "shares" , amount .String (), "amount" , userTotalAmount .String ())
207+ }
208+
209+ // Execute the BeginBlocker.
210+ if err = swapKeeper .BeginBlocker (ctx ); err != nil {
211+ return err
212+ }
213+
214+ // As last step, set the pool paused, no further interactions are allowed.
215+ if err = swapKeeper .SetPaused (ctx , 0 , true ); err != nil {
216+ return err
217+ }
218+
219+ return nil
220+ }
0 commit comments