var img = document.createElement('img'); img.src = "https://terradocs.matomo.cloud//piwik.php?idsite=2&rec=1&url=https://alliance.money" + location.pathname; img.style = "border:0"; img.alt = "tracker"; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(img,s);
Skip to main content

Integrate the Alliance module

Alliance can be integrated into both new and existing blockchains. It is an isolated module that doesnโ€™t modify any existing CosmosSDK code. New blockchains can simply import the module and existing chains can perform a software upgrade.

To connect with a member of the Alliance team for questions regarding integration, setting Alliance asset parameters, or other items, please complete this form.

caution

Follow all of the steps in this guide carefully.

Chains that want to create an Alliance must enable the following modules:

  • x/auth: retrieve information about internal blockchain accounts
  • x/bank: mint, burn or send coins
  • x/staking: bond, unbond, delegate tokens and hooks
  • x/distribution: withdraw rewards
  • x/gov: create Alliances through governance
Alliance-compatible assets

The Alliance module leverages the IBC module to bridge foreign tokens and the Token Factory module to mint native tokens. Alliance allows any native token recognized by the bank module to be staked as long as it has been whitelisted. CW-20 tokens sent via IBC from other chains can be used in the Alliance module if they are minted as native tokens.

Prerequisitesโ€‹

The Alliance Module requires:

Configuring and Adding Alliance to a Cosmos SDK Chainโ€‹

Implementation example

Refer to the example pull request for an implementation of the following steps.

  1. Add the Alliance package to the go.mod file with the latest released version and install it.
go.mod
Copy

_5
require (
_5
...
_5
github.com/terra-money/alliance v<LATEST-VERSION>
_5
...
_5
)

  1. Add the following modules to app.go
app.go
Copy

_4
alliancemodule "github.com/terra-money/alliance/x/alliance"
_4
alliancemoduleclient "github.com/terra-money/alliance/x/alliance/client"
_4
alliancemodulekeeper "github.com/terra-money/alliance/x/alliance/keeper"
_4
alliancemoduletypes "github.com/terra-money/alliance/x/alliance/types"

  1. Register the AllianceKeeper in App struct.
app.go
Copy

_5
type App struct {
_5
...
_5
AllianceKeeper alliancemodulekeeper.Keeper
_5
...
_5
}

  1. Add the Alliance module into the BasicManager instantiation.
app.go
Copy

_5
ModuleBasics = module.NewBasicManager(
_5
...
_5
alliancemodule.AppModuleBasic{},
_5
...
_5
)

  1. Add the Alliance module to the app. The Alliance module needs to be specified after the stakingKeeper is instantiated.
app.go
Copy

_21
...
_21
_21
stakingKeeper := stakingkeeper.NewKeeper(
_21
appCodec,
_21
keys[stakingtypes.StoreKey],
_21
app.AccountKeeper,
_21
app.BankKeeper,
_21
app.GetSubspace(stakingtypes.ModuleName),
_21
)
_21
_21
...
_21
_21
app.AllianceKeeper = alliancemodulekeeper.NewKeeper(
_21
appCodec,
_21
keys[alliancemoduletypes.StoreKey],
_21
app.GetSubspace(alliancemoduletypes.ModuleName),
_21
app.AccountKeeper,
_21
app.BankKeeper,
_21
&app.StakingKeeper,
_21
app.DistrKeeper,
_21
)

  1. Add the Alliance staking hooks to app.StakingKeeper.
app.go
Copy

_5
_5
app.StakingKeeper = *stakingKeeper.SetHooks(
_5
stakingtypes.NewMultiStakingHooks(app.DistrKeeper.Hooks(),
_5
app.SlashingKeeper.Hooks(), app.AllianceKeeper.StakingHooks()),
_5
)

  1. Add the requisite Alliance module types to the module account permissions.
app.go
Copy

_12
maccPerms = map[string][]string{
_12
authtypes.FeeCollectorName: nil,
_12
distrtypes.ModuleName: nil,
_12
icatypes.ModuleName: nil,
_12
minttypes.ModuleName: {authtypes.Minter},
_12
stakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking},
_12
stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking},
_12
govtypes.ModuleName: {authtypes.Burner},
_12
ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner},
_12
alliancemoduletypes.ModuleName: {authtypes.Minter, authtypes.Burner},
_12
alliancemoduletypes.RewardsPoolName: nil,
_12
}

  1. Add the alliance storekey to the KVStore.
app.go
Copy

_5
keys := sdk.NewKVStoreKeys(
_5
...
_5
alliancemoduletypes.StoreKey,
_5
...
_5
)

  1. Add the Alliance module to the app manager and simulation manager instantiations.
app.go
Copy

_13
app.mm = module.NewManager(
_13
...
_13
icaModule,
_13
alliancemodule.NewAppModule(appCodec, app.AllianceKeeper, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry),
_13
...
_13
)
_13
_13
app.sm = module.NewSimulationManager(
_13
...
_13
transferModule,
_13
alliancemodule.NewAppModule(appCodec, app.AllianceKeeper, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry),
_13
...
_13
)

  1. Add the module as the final element to the following:
  • SetOrderBeginBlockers
  • SetOrderEndBlockers
  • SetOrderInitGenesis
app.go
Copy

_14
app.mm.SetOrderBeginBlockers(
_14
...
_14
alliancemoduletypes.ModuleName,
_14
)
_14
_14
app.mm.SetOrderEndBlockers(
_14
...
_14
alliancemoduletypes.ModuleName,
_14
)
_14
_14
app.mm.SetOrderInitGenesis(
_14
...
_14
alliancemoduletypes.ModuleName,
_14
)

  1. Add the Alliance proposal handler route to the governance module.
app.go
Copy

_9
govRouter.
_9
_9
AddRoute(govtypes.RouterKey, govv1beta1.ProposalHandler).
_9
_9
...
_9
_9
AddRoute(ibcclienttypes.RouterKey, ibcclient.NewClientProposalHandler(app.IBCKeeper.ClientKeeper)).
_9
_9
AddRoute(alliancemoduletypes.RouterKey, alliancemodule.NewAllianceProposalHandler(app.AllianceKeeper))

  1. Add the governance proposal handlers.
app.go
Copy

_13
govProposalHandlers = append(govProposalHandlers,
_13
_13
...
_13
_13
ibcclientclient.UpgradeProposalHandler,
_13
_13
alliancemoduleclient.CreateAllianceProposalHandler,
_13
_13
alliancemoduleclient.UpdateAllianceProposalHandler,
_13
_13
alliancemoduleclient.DeleteAllianceProposalHandler,
_13
_13
)

  1. Block the module account address.
app.go
Copy

_9
func (app *App) BlockedModuleAccountAddrs() map[string]bool {
_9
_9
...
_9
_9
delete(modAccAddrs, authtypes.NewModuleAddress(alliancemoduletypes.ModuleName).String())
_9
_9
return modAccAddrs
_9
_9
}

  1. Add the init params keepers.
app.go
Copy

_9
func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino, key, tkey storetypes.StoreKey) paramskeeper.Keeper {
_9
_9
...
_9
_9
paramsKeeper.Subspace(alliancemoduletypes.ModuleName)
_9
_9
return paramsKeeper
_9
_9
}

Configuring the Bank Moduleโ€‹

caution

Make sure all three of the following steps are implemented.

Because the Alliance module mints and burns native staking tokens when rebalancing reward power, a chain's Total Supply API needs to be updated to return accurate results. This custom wrapper only affects the following APIs: /cosmos/bank/v1beta1/supply & /cosmos/bank/v1beta1/supply/by_denom. Follow these instructions to update your API for supply accuracy.

  1. Update the imports to use a custom wrapper over the Bank module.
app.go
Copy

_11
import (
_11
...
_11
// Delete or comment the native bank module imports
_11
// "github.com/cosmos/cosmos-sdk/x/bank"
_11
// bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
_11
_11
// Replace the imports from the Alliance module
_11
bank "github.com/terra-money/alliance/custom/bank"
_11
bankkeeper "github.com/terra-money/alliance/custom/bank/keeper"
_11
...
_11
)

  1. Add a line to register the keepers that the custom Bank module needs.
app.go
Copy

_12
app.AllianceKeeper = alliancemodulekeeper.NewKeeper(
_12
appCodec,
_12
keys[alliancemoduletypes.StoreKey],
_12
app.GetSubspace(alliancemoduletypes.ModuleName),
_12
app.AccountKeeper,
_12
app.BankKeeper,
_12
&stakingKeeper,
_12
app.DistrKeeper,
_12
)
_12
_12
// Add this after instantiating the `Alliance keeper`
_12
app.BankKeeper.RegisterKeepers(app.AllianceKeeper, &stakingKeeper)

  1. Add the following keeper to allow the Alliance module access to transfer tokens directly to accounts.
app.go
Copy

_17
app.BankKeeper = custombankkeeper.NewBaseKeeper(
_17
appCodec,
_17
keys[banktypes.StoreKey],
_17
app.AccountKeeper,
_17
app.GetSubspace(banktypes.ModuleName),
_17
app.BlockedModuleAccountAddrs(),
_17
)
_17
_17
//Then add the following function below the previous section
_17
_17
func (app *App) BlockedModuleAccountAddrs() map[string]bool {
_17
modAccAddrs := app.ModuleAccountAddrs()
_17
delete(modAccAddrs, authtypes.NewModuleAddress(govtypes.ModuleName).String())
_17
delete(modAccAddrs, authtypes.NewModuleAddress(alliancemoduletypes.ModuleName).String())
_17
_17
return modAccAddrs
_17
}

Specify the fee collector module accountโ€‹

The following steps allow you to specify a fee collector account, allowing for more flexibility in configuration. If you don't want to specify a custom fee collector, specify the default fee collector.

  1. Add authtypes.FeeCollectorName, to the AllianceKeeper in app.go.
app.go
Copy

_10
app.AllianceKeeper = alliancemodulekeeper.NewKeeper(
_10
appCodec,
_10
keys[alliancemoduletypes.StoreKey],
_10
app.GetSubspace(alliancemoduletypes.ModuleName),
_10
app.AccountKeeper,
_10
app.BankKeeper,
_10
app.StakingKeeper,
_10
app.DistrKeeper,
_10
authtypes.FeeCollectorName,
_10
)

  1. Add the following line to specify the fee collector. If you don't want to specify a custom fee collector, specify the default FeeCollector.
app.go
Copy

_5
type App struct {
_5
...
_5
feeCollectorName string // name of the FeeCollector ModuleAccount
_5
...
_5
}