Skip to content

Commit

Permalink
added x/bridge module (#7684)
Browse files Browse the repository at this point in the history
* made x/tokenfactory mint and burn methods public

* added x/bridge module

* added x/bridge into the app

* deleted wiring in app

* review

* reverter osmoutils

* inbound and outbound transfers handlers

* code review

* reverted tokenfactory

* code review round 2

* Generated protofile changes

---------

Co-authored-by: github-actions <[email protected]>
  • Loading branch information
keruch and github-actions authored Mar 14, 2024
1 parent a4f3ece commit f5a5942
Show file tree
Hide file tree
Showing 27 changed files with 1,415 additions and 131 deletions.
7 changes: 3 additions & 4 deletions proto/osmosis/bridge/v1beta1/events.proto
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,8 @@ message EventUpdateParams {
}

message EventChangeAssetStatus {
// Sender is a sender's address
string sender = 1;
// NewAssetStatus is a pair of the asset and its new status
AssetWithStatus old_asset_status = 2 [ (gogoproto.nullable) = false ];
AssetWithStatus new_asset_status = 3 [ (gogoproto.nullable) = false ];
Asset asset = 2 [ (gogoproto.nullable) = false ];
AssetStatus old_asset_status = 3;
AssetStatus new_asset_status = 4;
}
11 changes: 6 additions & 5 deletions proto/osmosis/bridge/v1beta1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,13 @@ message MsgChangeAssetStatus {

// Sender is a sender's address
string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
// NewAssetStatus is a pair of the asset and its new status.
// Asset is an asset to update.
// The asset should be known; otherwise, the method will failed.
AssetWithStatus new_asset_status = 2 [
(gogoproto.moretags) = "yaml:\"new_asset_status\"",
(gogoproto.nullable) = false
];
Asset asset = 2
[ (gogoproto.moretags) = "yaml:\"asset\"", (gogoproto.nullable) = false ];
// NewAssetStatus is a new asset's status.
AssetStatus new_asset_status = 3
[ (gogoproto.moretags) = "yaml:\"new_asset_status\"" ];
}

message MsgChangeAssetStatusResponse {}
3 changes: 3 additions & 0 deletions x/bridge/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Bridge

The bridge module allows...
20 changes: 20 additions & 0 deletions x/bridge/client/cli/query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package cli

import (
"github.com/spf13/cobra"

"github.com/osmosis-labs/osmosis/osmoutils/osmocli"
"github.com/osmosis-labs/osmosis/v23/x/bridge/types"
)

// GetQueryCmd returns the cli query commands for this module
func GetQueryCmd() *cobra.Command {
cmd := osmocli.QueryIndexCmd(types.ModuleName)

cmd.AddCommand(osmocli.GetParams[*types.QueryParamsRequest](
types.ModuleName,
types.NewQueryClient,
))

return cmd
}
55 changes: 55 additions & 0 deletions x/bridge/client/cli/tx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package cli

import (
"github.com/spf13/cobra"

"github.com/osmosis-labs/osmosis/osmoutils/osmocli"
"github.com/osmosis-labs/osmosis/v23/x/bridge/types"
)

// GetTxCmd returns the transaction commands for this module
func GetTxCmd() *cobra.Command {
cmd := osmocli.TxIndexCmd(types.ModuleName)
cmd.AddCommand(
NewInboundTransferCmd(),
NewOutboundTransferCmd(),
NewUpdateParamsCmd(),
NewChangeAssetStatusCmd(),
)

return cmd
}

func NewInboundTransferCmd() *cobra.Command {
return osmocli.BuildTxCli[*types.MsgInboundTransfer](&osmocli.TxCliDesc{
Use: "inbound-transfer",
Short: "Make an inbound transfer from the external chain to osmosis.",
})
}

func NewOutboundTransferCmd() *cobra.Command {
return osmocli.BuildTxCli[*types.MsgOutboundTransfer](&osmocli.TxCliDesc{
Use: "outbound-transfer",
Short: "Make an outbound transfer from osmosis to the external chain.",
})
}

func NewUpdateParamsCmd() *cobra.Command {
return osmocli.BuildTxCli[*types.MsgUpdateParams](&osmocli.TxCliDesc{
Use: "update-params",
Short: "Update the x/bridge module params.",
})
}

func NewChangeAssetStatusCmd() *cobra.Command {
return osmocli.BuildTxCli[*types.MsgChangeAssetStatus](&osmocli.TxCliDesc{
Use: "change-asset-status",
Short: "Change the asset status to the one specified in the call.",
Long: `Change the asset status to the one specified in the call.
Available statuses:
ASSET_STATUS_OK
ASSET_STATUS_BLOCKED_INBOUND
ASSET_STATUS_BLOCKED_OUTBOUND
ASSET_STATUS_BLOCKED_BOTH`,
})
}
Binary file added x/bridge/images/MintBurn.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
45 changes: 45 additions & 0 deletions x/bridge/images/MintBurn.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
@startuml

participant "Alice BTC addr" as AliceBTC
participant "BTC vault" as BTCVault
participant "BTC Block Scanner/TSS" as ValidatorsSet
participant "x/bridge" as Bridge
participant "x/tokenfactory" as Tokenfactory
participant "Alice OSMO addr" as AliceOsmosis

== BTC to OSMO ==

AliceBTC --> BTCVault : Send tx with right memo\nincluding the osmo address
ValidatorsSet --> BTCVault : Scan the tx and validate it
ValidatorsSet --> Bridge : Send observed inbound tx
Bridge --> Tokenfactory : Mint **osmobtc** tokens\nto the osmo address\nfrom the memo
note over Tokenfactory
Admin of the denom
is the x/bridge module
end note
Tokenfactory --> AliceOsmosis : Update Alice balance
Tokenfactory --> Bridge : Response for the mint
alt failure
Bridge --> AliceBTC : TODO Refund
end

== OSMO to BTC ==

AliceOsmosis --> Bridge : Call **MsgTransfer** through **MsgServer**
Bridge --> Bridge : Check if Alice has sufficient balance
Bridge --> Tokenfactory : Burn **osmobtc** tokens\nfrom the osmo address\ngot in the tx
note over Tokenfactory
Admin of the denom
is the x/bridge module
end note
Tokenfactory --> AliceOsmosis : Update Alice balance
Tokenfactory --> Bridge : Response for the burn
alt success
Bridge --> ValidatorsSet : Send outbound tx
ValidatorsSet --> BTCVault : Release the AliceBTC for Alice
BTCVault --> AliceBTC : Send a tx with AliceBTC
else failure
Bridge --> AliceOsmosis : TODO Refund
end

@enduml
63 changes: 63 additions & 0 deletions x/bridge/keeper/assets.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package keeper

import (
"fmt"

errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/osmosis-labs/osmosis/v23/x/bridge/types"
)

type ChangeAssetStatusResult struct {
OldStatus types.AssetStatus
NewStatus types.AssetStatus
}

// ChangeAssetStatus changes the status of the provided asset to newStatus.
// Returns error if the provided asset is not found in the module params.
func (k Keeper) ChangeAssetStatus(
ctx sdk.Context,
asset types.Asset,
newStatus types.AssetStatus,
) (ChangeAssetStatusResult, error) {
// get current params
params := k.GetParams(ctx)

// check if the specified asset is known
const notFoundIdx = -1
var assetIdx = notFoundIdx
for i := range params.Assets {
if params.Assets[i].Asset == asset {
assetIdx = i
break
}
}
if assetIdx == notFoundIdx {
return ChangeAssetStatusResult{}, errorsmod.Wrapf(types.ErrInvalidAsset, "Asset not found")
}

// update assetIdx asset status
oldStatus := params.Assets[assetIdx].AssetStatus
params.Assets[assetIdx].AssetStatus = newStatus
k.SetParam(ctx, types.KeyAssets, params.Assets)

return ChangeAssetStatusResult{
OldStatus: oldStatus,
NewStatus: newStatus,
}, nil
}

// createAssets creates tokenfactory denoms for all provided assets
func (k Keeper) createAssets(ctx sdk.Context, assets []types.AssetWithStatus) error {
bridgeModuleAddr := k.accountKeeper.GetModuleAddress(types.ModuleName)

for _, asset := range assets {
_, err := k.tokenFactoryKeeper.CreateDenom(ctx, bridgeModuleAddr.String(), asset.Asset.Name())
if err != nil {
return fmt.Errorf("can't create a new denom %s: %s", asset.Asset.Name(), err)
}
}

return nil
}
29 changes: 29 additions & 0 deletions x/bridge/keeper/genesis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package keeper

import (
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/osmosis-labs/osmosis/v23/x/bridge/types"
)

// InitGenesis initializes the bridge module's state from a provided genesis state.
func (k Keeper) InitGenesis(ctx sdk.Context, genState types.GenesisState) {
// create denoms for all new assets
err := k.createAssets(ctx, genState.Params.Assets)
if err != nil {
panic(fmt.Errorf("can't create assets on x/bridge genesis: %w", err))
}

// don't need to specifically create the signers, just save them

k.SetParams(ctx, genState.Params)
}

// ExportGenesis returns the bridge module's exported genesis.
func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState {
return &types.GenesisState{
Params: k.GetParams(ctx),
}
}
27 changes: 27 additions & 0 deletions x/bridge/keeper/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package keeper

import (
sdk "github.com/cosmos/cosmos-sdk/types"
"golang.org/x/exp/slices"
)

// Difference returns the slice of elements that are elements of a but not elements of b.
// TODO: Placed here temporarily. Delete after releasing the new osmoutils version.
func Difference[T comparable](a, b []T) []T {
mb := make(map[T]struct{}, len(a))
for _, x := range b {
mb[x] = struct{}{}
}
diff := make([]T, 0)
for _, x := range a {
if _, found := mb[x]; !found {
diff = append(diff, x)
}
}
return diff
}

// validateSenderIsSigner ensures that the sender is a part of the signers set.
func (k Keeper) validateSenderIsSigner(ctx sdk.Context, sender string) bool {
return slices.Contains(k.GetParams(ctx).Signers, sender)
}
53 changes: 53 additions & 0 deletions x/bridge/keeper/keeper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package keeper

import (
"fmt"

"github.com/cometbft/cometbft/libs/log"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"

"github.com/osmosis-labs/osmosis/v23/x/bridge/types"
)

type Keeper struct {
storeKey storetypes.StoreKey
paramSpace paramtypes.Subspace

accountKeeper types.AccountKeeper
tokenFactoryKeeper types.TokenFactoryKeeper

govModuleAddr string
}

// NewKeeper returns a new instance of the x/bridge keeper.
func NewKeeper(
storeKey storetypes.StoreKey,
paramSpace paramtypes.Subspace,
accountKeeper types.AccountKeeper,
tokenFactoryKeeper types.TokenFactoryKeeper,
govModuleAddr string,
) Keeper {
// ensure bridge module account is set
if addr := accountKeeper.GetModuleAddress(types.ModuleName); addr == nil {
panic("the bridge module account has not been set")
}

if !paramSpace.HasKeyTable() {
paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable())
}

return Keeper{
storeKey: storeKey,
paramSpace: paramSpace,
accountKeeper: accountKeeper,
tokenFactoryKeeper: tokenFactoryKeeper,
govModuleAddr: govModuleAddr,
}
}

// Logger returns a logger for the x/bridge module.
func (k Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
}
Loading

0 comments on commit f5a5942

Please sign in to comment.