mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-11-04 06:37:26 +00:00 
			
		
		
		
	* add get set methods for swap reward indexes * add get set methods for swap accrual time * tidy up location of multi periods * add swap reward periods to params * add initial legacy types for incentive * minor refactor of migration code * add incentive migration for swap params * minor incentive test refactors * add math methods to RewardIndexes * add keeper method to increment global indexes * add swap keeper to incentive keeper * indicate if pool shares were found or not * add accumulator to compute new rewards each block * accumulate swap rewards globally * remove unecessary keeper method * expand doc comments on accumulator methods * test precision not lost in accumulation * minor fixes from merge * rename storeGlobalDelegatorFactor to match others * fix migration from merge * fix bug in app setup * fix accumulation bug when starting with no state * rename swap files to match others * add swap accumulation times to genesis * remove old migration refactor * minor updates to spec * add high level description of how rewards work
		
			
				
	
	
		
			353 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			353 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package v0_15
 | 
						|
 | 
						|
import (
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/cosmos/cosmos-sdk/codec"
 | 
						|
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
						|
	"github.com/cosmos/cosmos-sdk/x/genutil"
 | 
						|
	cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
 | 
						|
	tmtypes "github.com/tendermint/tendermint/types"
 | 
						|
 | 
						|
	"github.com/kava-labs/kava/app"
 | 
						|
	v0_14committee "github.com/kava-labs/kava/x/committee/legacy/v0_14"
 | 
						|
	v0_15committee "github.com/kava-labs/kava/x/committee/types"
 | 
						|
	v0_14incentive "github.com/kava-labs/kava/x/incentive/legacy/v0_14"
 | 
						|
	v0_15incentive "github.com/kava-labs/kava/x/incentive/types"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	// TODO: update GenesisTime and chain-id for kava-8 launch
 | 
						|
	GenesisTime = time.Date(2021, 4, 8, 15, 0, 0, 0, time.UTC)
 | 
						|
	ChainID     = "kava-8"
 | 
						|
	// TODO: update SWP reward per second amount before production
 | 
						|
	SwpRewardsPerSecond = sdk.NewCoin("swp", sdk.OneInt())
 | 
						|
)
 | 
						|
 | 
						|
// Migrate translates a genesis file from kava v0.14 format to kava v0.15 format
 | 
						|
func Migrate(genDoc tmtypes.GenesisDoc) tmtypes.GenesisDoc {
 | 
						|
	// migrate app state
 | 
						|
	var appStateMap genutil.AppMap
 | 
						|
	cdc := codec.New()
 | 
						|
	cryptoAmino.RegisterAmino(cdc)
 | 
						|
	tmtypes.RegisterEvidences(cdc)
 | 
						|
 | 
						|
	// Old codec does not need all old modules registered on it to correctly decode at this stage
 | 
						|
	// as it only decodes the app state into a map of module names to json encoded bytes.
 | 
						|
	if err := cdc.UnmarshalJSON(genDoc.AppState, &appStateMap); err != nil {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
 | 
						|
	MigrateAppState(appStateMap)
 | 
						|
 | 
						|
	v0_15Codec := app.MakeCodec()
 | 
						|
	marshaledNewAppState, err := v0_15Codec.MarshalJSON(appStateMap)
 | 
						|
	if err != nil {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
	genDoc.AppState = marshaledNewAppState
 | 
						|
	genDoc.GenesisTime = GenesisTime
 | 
						|
	genDoc.ChainID = ChainID
 | 
						|
	return genDoc
 | 
						|
}
 | 
						|
 | 
						|
// MigrateAppState migrates application state from v0.14 format to a kava v0.15 format
 | 
						|
// It modifies the provided genesis state in place.
 | 
						|
func MigrateAppState(v0_14AppState genutil.AppMap) {
 | 
						|
	v0_14Codec := makeV014Codec()
 | 
						|
	v0_15Codec := app.MakeCodec()
 | 
						|
 | 
						|
	// Migrate incentive app state
 | 
						|
	if v0_14AppState[v0_14incentive.ModuleName] != nil {
 | 
						|
		var incentiveGenState v0_14incentive.GenesisState
 | 
						|
		v0_14Codec.MustUnmarshalJSON(v0_14AppState[v0_14incentive.ModuleName], &incentiveGenState)
 | 
						|
		delete(v0_14AppState, v0_14incentive.ModuleName)
 | 
						|
		v0_14AppState[v0_15incentive.ModuleName] = v0_15Codec.MustMarshalJSON(Incentive(incentiveGenState))
 | 
						|
	}
 | 
						|
 | 
						|
	// Migrate commmittee app state
 | 
						|
	if v0_14AppState[v0_14committee.ModuleName] != nil {
 | 
						|
		// Unmarshal v14 committee genesis state and delete it
 | 
						|
		var committeeGS v0_14committee.GenesisState
 | 
						|
		v0_14Codec.MustUnmarshalJSON(v0_14AppState[v0_14committee.ModuleName], &committeeGS)
 | 
						|
		delete(v0_14AppState, v0_14committee.ModuleName)
 | 
						|
		// Marshal v15 committee genesis state
 | 
						|
		v0_14AppState[v0_15committee.ModuleName] = v0_15Codec.MustMarshalJSON(Committee(committeeGS))
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func makeV014Codec() *codec.Codec {
 | 
						|
	cdc := codec.New()
 | 
						|
	sdk.RegisterCodec(cdc)
 | 
						|
	v0_14committee.RegisterCodec(cdc)
 | 
						|
	v0_14incentive.RegisterCodec(cdc)
 | 
						|
	return cdc
 | 
						|
}
 | 
						|
 | 
						|
// Committee migrates from a v0.14 committee genesis state to a v0.15 committee genesis state
 | 
						|
func Committee(genesisState v0_14committee.GenesisState) v0_15committee.GenesisState {
 | 
						|
 | 
						|
	committees := []v0_15committee.Committee{}
 | 
						|
	votes := []v0_15committee.Vote{}
 | 
						|
	proposals := []v0_15committee.Proposal{}
 | 
						|
 | 
						|
	for _, com := range genesisState.Committees {
 | 
						|
		if com.ID == 1 {
 | 
						|
			// Initialize member committee without permissions
 | 
						|
			stabilityCom := v0_15committee.NewMemberCommittee(com.ID, com.Description, com.Members,
 | 
						|
				[]v0_15committee.Permission{}, com.VoteThreshold, com.ProposalDuration,
 | 
						|
				v0_15committee.FirstPastThePost)
 | 
						|
 | 
						|
			// Build stability committee permissions
 | 
						|
			var newStabilityCommitteePermissions []v0_15committee.Permission
 | 
						|
			var newStabilitySubParamPermissions v0_15committee.SubParamChangePermission
 | 
						|
			for _, perm := range com.Permissions {
 | 
						|
				subPerm, ok := perm.(v0_14committee.SubParamChangePermission)
 | 
						|
				if ok {
 | 
						|
					// update AllowedParams
 | 
						|
					var newAllowedParams v0_15committee.AllowedParams
 | 
						|
					for _, ap := range subPerm.AllowedParams {
 | 
						|
						newAP := v0_15committee.AllowedParam(ap)
 | 
						|
						newAllowedParams = append(newAllowedParams, newAP)
 | 
						|
					}
 | 
						|
					newStabilitySubParamPermissions.AllowedParams = newAllowedParams
 | 
						|
 | 
						|
					// update AllowedCollateralParams
 | 
						|
					var newCollateralParams v0_15committee.AllowedCollateralParams
 | 
						|
					collateralTypes := []string{"bnb-a", "busd-a", "busd-b", "btcb-a", "xrpb-a", "ukava-a", "hard-a", "hbtc-a"}
 | 
						|
					for _, cp := range subPerm.AllowedCollateralParams {
 | 
						|
						newCP := v0_15committee.NewAllowedCollateralParam(
 | 
						|
							cp.Type,
 | 
						|
							cp.Denom,
 | 
						|
							cp.LiquidationRatio,
 | 
						|
							cp.DebtLimit,
 | 
						|
							cp.StabilityFee,
 | 
						|
							cp.AuctionSize,
 | 
						|
							cp.LiquidationPenalty,
 | 
						|
							cp.Prefix,
 | 
						|
							cp.SpotMarketID,
 | 
						|
							cp.LiquidationMarketID,
 | 
						|
							cp.ConversionFactor,
 | 
						|
							true,
 | 
						|
							true,
 | 
						|
						)
 | 
						|
						newCollateralParams = append(newCollateralParams, newCP)
 | 
						|
					}
 | 
						|
					for _, cType := range collateralTypes {
 | 
						|
						var foundCtype bool
 | 
						|
						for _, cp := range newCollateralParams {
 | 
						|
							if cType == cp.Type {
 | 
						|
								foundCtype = true
 | 
						|
							}
 | 
						|
						}
 | 
						|
						if !foundCtype {
 | 
						|
							newCP := v0_15committee.NewAllowedCollateralParam(cType, false, false, true, true, true, false, false, false, false, false, true, true)
 | 
						|
							newCollateralParams = append(newCollateralParams, newCP)
 | 
						|
						}
 | 
						|
					}
 | 
						|
					newStabilitySubParamPermissions.AllowedCollateralParams = newCollateralParams
 | 
						|
 | 
						|
					// update AllowedDebtParam
 | 
						|
					newDP := v0_15committee.AllowedDebtParam{
 | 
						|
						Denom:            subPerm.AllowedDebtParam.Denom,
 | 
						|
						ReferenceAsset:   subPerm.AllowedDebtParam.ReferenceAsset,
 | 
						|
						ConversionFactor: subPerm.AllowedDebtParam.ConversionFactor,
 | 
						|
						DebtFloor:        subPerm.AllowedDebtParam.DebtFloor,
 | 
						|
					}
 | 
						|
					newStabilitySubParamPermissions.AllowedDebtParam = newDP
 | 
						|
 | 
						|
					// update AllowedAssetParams
 | 
						|
					var newAssetParams v0_15committee.AllowedAssetParams
 | 
						|
					for _, ap := range subPerm.AllowedAssetParams {
 | 
						|
						newAP := v0_15committee.AllowedAssetParam(ap)
 | 
						|
						newAssetParams = append(newAssetParams, newAP)
 | 
						|
					}
 | 
						|
					newStabilitySubParamPermissions.AllowedAssetParams = newAssetParams
 | 
						|
 | 
						|
					// Update Allowed Markets
 | 
						|
					var newMarketParams v0_15committee.AllowedMarkets
 | 
						|
					for _, mp := range subPerm.AllowedMarkets {
 | 
						|
						newMP := v0_15committee.AllowedMarket(mp)
 | 
						|
						newMarketParams = append(newMarketParams, newMP)
 | 
						|
					}
 | 
						|
					newStabilitySubParamPermissions.AllowedMarkets = newMarketParams
 | 
						|
 | 
						|
					// Add hard money market committee permissions
 | 
						|
					var newMoneyMarketParams v0_15committee.AllowedMoneyMarkets
 | 
						|
					hardMMDenoms := []string{"bnb", "busd", "btcb", "xrpb", "usdx", "ukava", "hard"}
 | 
						|
					for _, mmDenom := range hardMMDenoms {
 | 
						|
						newMoneyMarketParam := v0_15committee.NewAllowedMoneyMarket(mmDenom, true, false, false, true, true, true)
 | 
						|
						newMoneyMarketParams = append(newMoneyMarketParams, newMoneyMarketParam)
 | 
						|
					}
 | 
						|
					newStabilitySubParamPermissions.AllowedMoneyMarkets = newMoneyMarketParams
 | 
						|
					newStabilityCommitteePermissions = append(newStabilityCommitteePermissions, newStabilitySubParamPermissions)
 | 
						|
				}
 | 
						|
			}
 | 
						|
			newStabilityCommitteePermissions = append(newStabilityCommitteePermissions, v0_15committee.TextPermission{})
 | 
						|
 | 
						|
			// Set stability committee permissions
 | 
						|
			baseStabilityCom := stabilityCom.SetPermissions(newStabilityCommitteePermissions)
 | 
						|
			newStabilityCom := v0_15committee.MemberCommittee{BaseCommittee: baseStabilityCom}
 | 
						|
			committees = append(committees, newStabilityCom)
 | 
						|
		} else {
 | 
						|
			safetyCom := v0_15committee.NewMemberCommittee(com.ID, com.Description, com.Members,
 | 
						|
				[]v0_15committee.Permission{v0_15committee.SoftwareUpgradePermission{}},
 | 
						|
				com.VoteThreshold, com.ProposalDuration, v0_15committee.FirstPastThePost)
 | 
						|
			committees = append(committees, safetyCom)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	for _, v := range genesisState.Votes {
 | 
						|
		newVote := v0_15committee.NewVote(v.ProposalID, v.Voter, v0_15committee.Yes)
 | 
						|
		votes = append(votes, v0_15committee.Vote(newVote))
 | 
						|
	}
 | 
						|
 | 
						|
	for _, p := range genesisState.Proposals {
 | 
						|
		newPubProp := v0_15committee.PubProposal(p.PubProposal)
 | 
						|
		newProp := v0_15committee.NewProposal(newPubProp, p.ID, p.CommitteeID, p.Deadline)
 | 
						|
		proposals = append(proposals, newProp)
 | 
						|
	}
 | 
						|
	return v0_15committee.NewGenesisState(
 | 
						|
		genesisState.NextProposalID, committees, proposals, votes)
 | 
						|
}
 | 
						|
 | 
						|
// Incentive migrates from a v0.14 incentive genesis state to a v0.15 incentive genesis state
 | 
						|
func Incentive(incentiveGS v0_14incentive.GenesisState) v0_15incentive.GenesisState {
 | 
						|
	// Migrate params
 | 
						|
	var claimMultipliers v0_15incentive.Multipliers
 | 
						|
	for _, m := range incentiveGS.Params.ClaimMultipliers {
 | 
						|
		newMultiplier := v0_15incentive.NewMultiplier(v0_15incentive.MultiplierName(m.Name), m.MonthsLockup, m.Factor)
 | 
						|
		claimMultipliers = append(claimMultipliers, newMultiplier)
 | 
						|
	}
 | 
						|
 | 
						|
	var usdxMintingRewardPeriods v0_15incentive.RewardPeriods
 | 
						|
	for _, rp := range incentiveGS.Params.USDXMintingRewardPeriods {
 | 
						|
		usdxMintingRewardPeriod := v0_15incentive.NewRewardPeriod(rp.Active,
 | 
						|
			rp.CollateralType, rp.Start, rp.End, rp.RewardsPerSecond)
 | 
						|
		usdxMintingRewardPeriods = append(usdxMintingRewardPeriods, usdxMintingRewardPeriod)
 | 
						|
	}
 | 
						|
 | 
						|
	var hardSupplyRewardPeriods v0_15incentive.MultiRewardPeriods
 | 
						|
	for _, rp := range incentiveGS.Params.HardSupplyRewardPeriods {
 | 
						|
		hardSupplyRewardPeriod := v0_15incentive.NewMultiRewardPeriod(rp.Active,
 | 
						|
			rp.CollateralType, rp.Start, rp.End, rp.RewardsPerSecond)
 | 
						|
		hardSupplyRewardPeriods = append(hardSupplyRewardPeriods, hardSupplyRewardPeriod)
 | 
						|
	}
 | 
						|
 | 
						|
	var hardBorrowRewardPeriods v0_15incentive.MultiRewardPeriods
 | 
						|
	for _, rp := range incentiveGS.Params.HardBorrowRewardPeriods {
 | 
						|
		hardBorrowRewardPeriod := v0_15incentive.NewMultiRewardPeriod(rp.Active,
 | 
						|
			rp.CollateralType, rp.Start, rp.End, rp.RewardsPerSecond)
 | 
						|
		hardBorrowRewardPeriods = append(hardBorrowRewardPeriods, hardBorrowRewardPeriod)
 | 
						|
	}
 | 
						|
 | 
						|
	var hardDelegatorRewardPeriods v0_15incentive.MultiRewardPeriods
 | 
						|
	for _, rp := range incentiveGS.Params.HardDelegatorRewardPeriods {
 | 
						|
		rewardsPerSecond := sdk.NewCoins(rp.RewardsPerSecond, SwpRewardsPerSecond)
 | 
						|
		hardDelegatorRewardPeriod := v0_15incentive.NewMultiRewardPeriod(rp.Active,
 | 
						|
			rp.CollateralType, rp.Start, rp.End, rewardsPerSecond)
 | 
						|
		hardDelegatorRewardPeriods = append(hardDelegatorRewardPeriods, hardDelegatorRewardPeriod)
 | 
						|
	}
 | 
						|
 | 
						|
	// Build new params from migrated values
 | 
						|
	params := v0_15incentive.NewParams(
 | 
						|
		usdxMintingRewardPeriods,
 | 
						|
		hardSupplyRewardPeriods,
 | 
						|
		hardBorrowRewardPeriods,
 | 
						|
		hardDelegatorRewardPeriods,
 | 
						|
		v0_15incentive.DefaultMultiRewardPeriods, // TODO add expected swap reward periods
 | 
						|
		claimMultipliers,
 | 
						|
		incentiveGS.Params.ClaimEnd,
 | 
						|
	)
 | 
						|
 | 
						|
	// Migrate accumulation times
 | 
						|
	var usdxAccumulationTimes v0_15incentive.GenesisAccumulationTimes
 | 
						|
	for _, t := range incentiveGS.USDXAccumulationTimes {
 | 
						|
		newAccumulationTime := v0_15incentive.NewGenesisAccumulationTime(t.CollateralType, t.PreviousAccumulationTime)
 | 
						|
		usdxAccumulationTimes = append(usdxAccumulationTimes, newAccumulationTime)
 | 
						|
	}
 | 
						|
 | 
						|
	var hardSupplyAccumulationTimes v0_15incentive.GenesisAccumulationTimes
 | 
						|
	for _, t := range incentiveGS.HardSupplyAccumulationTimes {
 | 
						|
		newAccumulationTime := v0_15incentive.NewGenesisAccumulationTime(t.CollateralType, t.PreviousAccumulationTime)
 | 
						|
		hardSupplyAccumulationTimes = append(hardSupplyAccumulationTimes, newAccumulationTime)
 | 
						|
	}
 | 
						|
 | 
						|
	var hardBorrowAccumulationTimes v0_15incentive.GenesisAccumulationTimes
 | 
						|
	for _, t := range incentiveGS.HardBorrowAccumulationTimes {
 | 
						|
		newAccumulationTime := v0_15incentive.NewGenesisAccumulationTime(t.CollateralType, t.PreviousAccumulationTime)
 | 
						|
		hardBorrowAccumulationTimes = append(hardBorrowAccumulationTimes, newAccumulationTime)
 | 
						|
	}
 | 
						|
 | 
						|
	var hardDelegatorAccumulationTimes v0_15incentive.GenesisAccumulationTimes
 | 
						|
	for _, t := range incentiveGS.HardDelegatorAccumulationTimes {
 | 
						|
		newAccumulationTime := v0_15incentive.NewGenesisAccumulationTime(t.CollateralType, t.PreviousAccumulationTime)
 | 
						|
		hardDelegatorAccumulationTimes = append(hardDelegatorAccumulationTimes, newAccumulationTime)
 | 
						|
	}
 | 
						|
 | 
						|
	// Migrate USDX minting claims
 | 
						|
	var usdxMintingClaims v0_15incentive.USDXMintingClaims
 | 
						|
	for _, claim := range incentiveGS.USDXMintingClaims {
 | 
						|
		var rewardIndexes v0_15incentive.RewardIndexes
 | 
						|
		for _, ri := range claim.RewardIndexes {
 | 
						|
			rewardIndex := v0_15incentive.NewRewardIndex(ri.CollateralType, ri.RewardFactor)
 | 
						|
			rewardIndexes = append(rewardIndexes, rewardIndex)
 | 
						|
		}
 | 
						|
		usdxMintingClaim := v0_15incentive.NewUSDXMintingClaim(claim.Owner, claim.Reward, rewardIndexes)
 | 
						|
		usdxMintingClaims = append(usdxMintingClaims, usdxMintingClaim)
 | 
						|
	}
 | 
						|
 | 
						|
	// Migrate Hard protocol claims (including delegation rewards)
 | 
						|
	var hardClaims v0_15incentive.HardLiquidityProviderClaims
 | 
						|
	for _, claim := range incentiveGS.HardLiquidityProviderClaims {
 | 
						|
		// Migrate supply multi reward indexes
 | 
						|
		var supplyMultiRewardIndexes v0_15incentive.MultiRewardIndexes
 | 
						|
		for _, sri := range claim.SupplyRewardIndexes {
 | 
						|
			var rewardIndexes v0_15incentive.RewardIndexes
 | 
						|
			for _, ri := range sri.RewardIndexes {
 | 
						|
				rewardIndex := v0_15incentive.NewRewardIndex(ri.CollateralType, ri.RewardFactor)
 | 
						|
				rewardIndexes = append(rewardIndexes, rewardIndex)
 | 
						|
			}
 | 
						|
			supplyMultiRewardIndex := v0_15incentive.NewMultiRewardIndex(sri.CollateralType, rewardIndexes)
 | 
						|
			supplyMultiRewardIndexes = append(supplyMultiRewardIndexes, supplyMultiRewardIndex)
 | 
						|
		}
 | 
						|
 | 
						|
		// Migrate borrow multi reward indexes
 | 
						|
		var borrowMultiRewardIndexes v0_15incentive.MultiRewardIndexes
 | 
						|
		for _, bri := range claim.BorrowRewardIndexes {
 | 
						|
			var rewardIndexes v0_15incentive.RewardIndexes
 | 
						|
			for _, ri := range bri.RewardIndexes {
 | 
						|
				rewardIndex := v0_15incentive.NewRewardIndex(ri.CollateralType, ri.RewardFactor)
 | 
						|
				rewardIndexes = append(rewardIndexes, rewardIndex)
 | 
						|
			}
 | 
						|
			borrowMultiRewardIndex := v0_15incentive.NewMultiRewardIndex(bri.CollateralType, rewardIndexes)
 | 
						|
			borrowMultiRewardIndexes = append(borrowMultiRewardIndexes, borrowMultiRewardIndex)
 | 
						|
		}
 | 
						|
 | 
						|
		// Migrate delegator reward indexes to multi reward indexes under BondDenom
 | 
						|
		var delegatorMultiRewardIndexes v0_15incentive.MultiRewardIndexes
 | 
						|
		var delegatorRewardIndexes v0_15incentive.RewardIndexes
 | 
						|
		for _, ri := range claim.DelegatorRewardIndexes {
 | 
						|
			delegatorRewardIndex := v0_15incentive.NewRewardIndex(ri.CollateralType, ri.RewardFactor)
 | 
						|
			delegatorRewardIndexes = append(delegatorRewardIndexes, delegatorRewardIndex)
 | 
						|
		}
 | 
						|
		delegatorMultiRewardIndex := v0_15incentive.NewMultiRewardIndex(v0_15incentive.BondDenom, delegatorRewardIndexes)
 | 
						|
		delegatorMultiRewardIndexes = append(delegatorMultiRewardIndexes, delegatorMultiRewardIndex)
 | 
						|
 | 
						|
		hardClaim := v0_15incentive.NewHardLiquidityProviderClaim(claim.Owner, claim.Reward,
 | 
						|
			supplyMultiRewardIndexes, borrowMultiRewardIndexes, delegatorMultiRewardIndexes)
 | 
						|
		hardClaims = append(hardClaims, hardClaim)
 | 
						|
	}
 | 
						|
 | 
						|
	return v0_15incentive.NewGenesisState(
 | 
						|
		params,
 | 
						|
		usdxAccumulationTimes,
 | 
						|
		hardSupplyAccumulationTimes,
 | 
						|
		hardBorrowAccumulationTimes,
 | 
						|
		hardDelegatorAccumulationTimes,
 | 
						|
		v0_15incentive.DefaultGenesisAccumulationTimes, // There is no previous swap rewards to accumulation starts at genesis time.
 | 
						|
		usdxMintingClaims,
 | 
						|
		hardClaims,
 | 
						|
	)
 | 
						|
}
 |