Skip to content

Commit

Permalink
feat: forwarding accounts (backport #302) (#305)
Browse files Browse the repository at this point in the history
Co-authored-by: John Letey <john@nobleassets.xyz>
  • Loading branch information
mergify[bot] and johnletey authored Feb 23, 2024
1 parent 4d053bf commit 9c1e703
Show file tree
Hide file tree
Showing 33 changed files with 5,501 additions and 641 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ endif
### Protobuf ###
###############################################################################

BUF_VERSION=1.28.1
BUF_VERSION=1.29.0

proto-all: proto-format proto-lint proto-gen

Expand Down
4 changes: 4 additions & 0 deletions app/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
fiattokenfactory "github.com/circlefin/noble-fiattokenfactory/x/fiattokenfactory/keeper"
fiattokenfactorytypes "github.com/circlefin/noble-fiattokenfactory/x/fiattokenfactory/types"
"github.com/cosmos/cosmos-sdk/types/bech32"
"github.com/noble-assets/noble/v4/x/forwarding"
forwardingkeeper "github.com/noble-assets/noble/v4/x/forwarding/keeper"
tokenfactory "github.com/noble-assets/noble/v4/x/tokenfactory/keeper"
tokenfactorytypes "github.com/noble-assets/noble/v4/x/tokenfactory/types"

Expand All @@ -26,6 +28,7 @@ type HandlerOptions struct {
IBCKeeper *ibckeeper.Keeper
GlobalFeeSubspace paramtypes.Subspace
StakingSubspace paramtypes.Subspace
ForwardingKeeper *forwardingkeeper.Keeper
}

type IsPausedDecorator struct {
Expand Down Expand Up @@ -228,6 +231,7 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
ante.NewRejectExtensionOptionsDecorator(),
NewIsBlacklistedDecorator(options.tokenFactoryKeeper, options.fiatTokenFactoryKeeper),
NewIsPausedDecorator(options.tokenFactoryKeeper, options.fiatTokenFactoryKeeper),
forwarding.NewAnteDecorator(options.ForwardingKeeper, options.AccountKeeper),
ante.NewMempoolFeeDecorator(),
ante.NewValidateBasicDecorator(),
ante.NewTxTimeoutHeightDecorator(),
Expand Down
30 changes: 28 additions & 2 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ import (
cctp "github.com/circlefin/noble-cctp/x/cctp"
cctpkeeper "github.com/circlefin/noble-cctp/x/cctp/keeper"
cctptypes "github.com/circlefin/noble-cctp/x/cctp/types"

"github.com/noble-assets/noble/v4/x/forwarding"
forwardingkeeper "github.com/noble-assets/noble/v4/x/forwarding/keeper"
forwardingtypes "github.com/noble-assets/noble/v4/x/forwarding/types"
)

const (
Expand Down Expand Up @@ -150,6 +154,7 @@ var (
tariff.AppModuleBasic{},
cctp.AppModuleBasic{},
paramauthorityibc.AppModuleBasic{},
forwarding.AppModuleBasic{},
)

// module account permissions
Expand Down Expand Up @@ -226,6 +231,7 @@ type App struct {
FiatTokenFactoryKeeper *fiattokenfactorymodulekeeper.Keeper
TariffKeeper tariffkeeper.Keeper
CCTPKeeper *cctpkeeper.Keeper
ForwardingKeeper *forwardingkeeper.Keeper

// this line is used by starport scaffolding # stargate/app/keeperDeclaration

Expand Down Expand Up @@ -264,9 +270,12 @@ func New(
paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey, feegrant.StoreKey, evidencetypes.StoreKey,
ibctransfertypes.StoreKey, icahosttypes.StoreKey, capabilitytypes.StoreKey,
tokenfactorymoduletypes.StoreKey, fiattokenfactorymoduletypes.StoreKey, packetforwardtypes.StoreKey, stakingtypes.StoreKey,
cctptypes.StoreKey,
cctptypes.StoreKey, forwardingtypes.StoreKey,
)
tkeys := sdk.NewTransientStoreKeys(
paramstypes.TStoreKey,
forwardingtypes.TransientStoreKey,
)
tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey)
memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey)

app := &App{
Expand Down Expand Up @@ -473,8 +482,19 @@ func New(
app.FiatTokenFactoryKeeper,
)

app.ForwardingKeeper = forwardingkeeper.NewKeeper(
appCodec,
keys[forwardingtypes.StoreKey],
tkeys[forwardingtypes.TransientStoreKey],
app.AccountKeeper,
app.BankKeeper,
app.IBCKeeper.ChannelKeeper,
app.TransferKeeper,
)

var transferStack ibcporttypes.IBCModule
transferStack = transfer.NewIBCModule(app.TransferKeeper)
transferStack = forwarding.NewMiddleware(transferStack, app.AccountKeeper, app.ForwardingKeeper)
transferStack = packetforward.NewIBCMiddleware(
transferStack,
app.PacketForwardKeeper,
Expand Down Expand Up @@ -528,6 +548,7 @@ func New(
globalfee.NewAppModule(app.GetSubspace(globalfee.ModuleName)),
tariff.NewAppModule(appCodec, app.TariffKeeper, app.AccountKeeper, app.BankKeeper),
cctp.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.CCTPKeeper),
forwarding.NewAppModule(app.ForwardingKeeper),
)

// During begin block slashing happens after distr.BeginBlocker so that
Expand Down Expand Up @@ -559,6 +580,7 @@ func New(
fiattokenfactorymoduletypes.ModuleName,
globalfee.ModuleName,
cctptypes.ModuleName,
forwardingtypes.ModuleName,
)

app.mm.SetOrderEndBlockers(
Expand All @@ -585,6 +607,7 @@ func New(
globalfee.ModuleName,
tarifftypes.ModuleName,
cctptypes.ModuleName,
forwardingtypes.ModuleName,
)

// NOTE: The genutils module must occur after staking so that pools are
Expand Down Expand Up @@ -616,6 +639,7 @@ func New(
fiattokenfactorymoduletypes.ModuleName,
globalfee.ModuleName,
cctptypes.ModuleName,
forwardingtypes.ModuleName,

// this line is used by starport scaffolding # stargate/app/initGenesis
)
Expand Down Expand Up @@ -663,6 +687,8 @@ func New(
IBCKeeper: app.IBCKeeper,
GlobalFeeSubspace: app.GetSubspace(globalfee.ModuleName),
StakingSubspace: app.GetSubspace(stakingtypes.ModuleName),

ForwardingKeeper: app.ForwardingKeeper,
},
)
if err != nil {
Expand Down
243 changes: 243 additions & 0 deletions interchaintest/forwarding_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
package interchaintest_test

import (
"context"
"encoding/json"
"fmt"
transfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"
"github.com/icza/dyno"
forwardingtypes "github.com/noble-assets/noble/v4/x/forwarding/types"
"github.com/strangelove-ventures/interchaintest/v4"
"github.com/strangelove-ventures/interchaintest/v4/chain/cosmos"
"github.com/strangelove-ventures/interchaintest/v4/ibc"
"github.com/strangelove-ventures/interchaintest/v4/relayer/hermes"
"github.com/strangelove-ventures/interchaintest/v4/testreporter"
"github.com/strangelove-ventures/interchaintest/v4/testutil"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
"testing"
)

func TestForwarding_RegisterOnNoble(t *testing.T) {
t.Parallel()

ctx, wrapper, gaia, sender, receiver := ForwardingSuite(t)
validator := wrapper.chain.Validators[0]

address, exists := ForwardingAccount(t, ctx, validator, receiver)
require.False(t, exists)

_, err := validator.ExecTx(ctx, sender.KeyName(), "forwarding", "register-account", "channel-0", receiver.FormattedAddress())
require.NoError(t, err)

_, exists = ForwardingAccount(t, ctx, validator, receiver)
require.True(t, exists)

require.NoError(t, validator.SendFunds(ctx, sender.KeyName(), ibc.WalletAmount{
Address: address,
Denom: "uusdc",
Amount: 1_000_000,
}))
require.NoError(t, testutil.WaitForBlocks(ctx, 10, wrapper.chain, gaia))

senderBalance, err := wrapper.chain.AllBalances(ctx, sender.FormattedAddress())
require.NoError(t, err)
require.True(t, senderBalance.IsZero())

balance, err := wrapper.chain.AllBalances(ctx, address)
require.NoError(t, err)
require.True(t, balance.IsZero())

receiverBalance, err := gaia.GetBalance(ctx, receiver.FormattedAddress(), transfertypes.DenomTrace{
Path: "transfer/channel-0",
BaseDenom: "uusdc",
}.IBCDenom())
require.NoError(t, err)
require.Equal(t, int64(1_000_000), receiverBalance)
}

func TestForwarding_RegisterViaTransfer(t *testing.T) {
t.Parallel()

ctx, wrapper, gaia, _, receiver := ForwardingSuite(t)
validator := wrapper.chain.Validators[0]

address, exists := ForwardingAccount(t, ctx, validator, receiver)
require.False(t, exists)

_, err := gaia.SendIBCTransfer(ctx, "channel-0", receiver.KeyName(), ibc.WalletAmount{
Address: address,
Denom: "uatom",
Amount: 100000,
}, ibc.TransferOptions{
Memo: fmt.Sprintf("{\"noble\":{\"forwarding\":{\"recipient\":\"%s\"}}}", receiver.FormattedAddress()),
})
require.NoError(t, err)

require.NoError(t, testutil.WaitForBlocks(ctx, 10, wrapper.chain, gaia))

_, exists = ForwardingAccount(t, ctx, validator, receiver)
require.True(t, exists)

balance, err := wrapper.chain.AllBalances(ctx, address)
require.NoError(t, err)
require.True(t, balance.IsZero())

receiverBalance, err := gaia.GetBalance(ctx, receiver.FormattedAddress(), "uatom")
require.NoError(t, err)
require.Equal(t, int64(998000), receiverBalance)
}

func TestForwarding_FrontRunAccount(t *testing.T) {
t.Parallel()

ctx, wrapper, gaia, sender, receiver := ForwardingSuite(t)
validator := wrapper.chain.Validators[0]

address, exists := ForwardingAccount(t, ctx, validator, receiver)
require.False(t, exists)

require.NoError(t, validator.SendFunds(ctx, sender.KeyName(), ibc.WalletAmount{
Address: address,
Denom: "uusdc",
Amount: 1_000_000,
}))

_, exists = ForwardingAccount(t, ctx, validator, receiver)
require.False(t, exists)

_, err := validator.ExecTx(ctx, sender.KeyName(), "forwarding", "register-account", "channel-0", receiver.FormattedAddress())
require.NoError(t, err)

_, exists = ForwardingAccount(t, ctx, validator, receiver)
require.True(t, exists)

require.NoError(t, testutil.WaitForBlocks(ctx, 10, wrapper.chain, gaia))

senderBalance, err := wrapper.chain.AllBalances(ctx, sender.FormattedAddress())
require.NoError(t, err)
require.True(t, senderBalance.IsZero())

balance, err := wrapper.chain.AllBalances(ctx, address)
require.NoError(t, err)
require.True(t, balance.IsZero())

receiverBalance, err := gaia.GetBalance(ctx, receiver.FormattedAddress(), transfertypes.DenomTrace{
Path: "transfer/channel-0",
BaseDenom: "uusdc",
}.IBCDenom())
require.NoError(t, err)
require.Equal(t, int64(1_000_000), receiverBalance)
}

func TestForwarding_RegisterViaPacket(t *testing.T) {
t.Skip()
}

//

func ForwardingAccount(t *testing.T, ctx context.Context, validator *cosmos.ChainNode, receiver ibc.Wallet) (address string, exists bool) {
raw, _, err := validator.ExecQuery(ctx, "forwarding", "address", "channel-0", receiver.FormattedAddress())
require.NoError(t, err)

var res forwardingtypes.QueryAddressResponse
require.NoError(t, json.Unmarshal(raw, &res))

return res.Address, res.Exists
}

func ForwardingSuite(t *testing.T) (ctx context.Context, wrapper genesisWrapper, gaia *cosmos.CosmosChain, sender ibc.Wallet, receiver ibc.Wallet) {
ctx = context.Background()
logger := zaptest.NewLogger(t)
reporter := testreporter.NewNopReporter()
execReporter := reporter.RelayerExecReporter(t)
client, network := interchaintest.DockerSetup(t)

var numValidators, numFullNodes = 1, 0

spec := nobleChainSpec(ctx, &wrapper, "noble-1", numValidators, numFullNodes, false, false, false, false)
spec.ModifyGenesis = func(cfg ibc.ChainConfig, bz []byte) ([]byte, error) {
bz, err := modifyGenesisAll(&wrapper, false, false)(cfg, bz)
if err != nil {
return nil, err
}

genesis := make(map[string]interface{})
if err := json.Unmarshal(bz, &genesis); err != nil {
return nil, err
}

if err := dyno.Set(genesis, "0", "app_state", "tariff", "params", "transfer_fee_bps"); err != nil {
return nil, err
}
if err := dyno.Set(genesis, "0", "app_state", "tariff", "params", "transfer_fee_max"); err != nil {
return nil, err
}

return json.Marshal(&genesis)
}

factory := interchaintest.NewBuiltinChainFactory(logger, []*interchaintest.ChainSpec{
spec,
{
Name: "gaia",
Version: "v14.1.0",
NumValidators: &numValidators,
NumFullNodes: &numFullNodes,
ChainConfig: ibc.ChainConfig{
ChainID: "cosmoshub-4",
},
},
})

chains, err := factory.Chains(t.Name())
require.NoError(t, err)

noble := chains[0].(*cosmos.CosmosChain)
gaia = chains[1].(*cosmos.CosmosChain)
wrapper.chain = noble

rly := interchaintest.NewBuiltinRelayerFactory(
ibc.Hermes,
logger,
).Build(t, client, network).(*hermes.Relayer)

interchain := interchaintest.NewInterchain().
AddChain(noble).
AddChain(gaia).
AddRelayer(rly, "rly").
AddLink(interchaintest.InterchainLink{
Chain1: noble,
Chain2: gaia,
Relayer: rly,
Path: "transfer",
})

require.NoError(t, interchain.Build(ctx, execReporter, interchaintest.InterchainBuildOptions{
TestName: t.Name(),
Client: client,
NetworkID: network,
}))

t.Cleanup(func() {
_ = interchain.Close()
})

require.NoError(t, rly.StartRelayer(ctx, execReporter))

roles := wrapper.fiatTfRoles
sender = wrapper.extraWallets.User
validator := noble.Validators[0]

_, err = validator.ExecTx(ctx, roles.MasterMinter.KeyName(), "fiat-tokenfactory", "configure-minter-controller", roles.MinterController.FormattedAddress(), roles.Minter.FormattedAddress(), "-b", "block")
require.NoError(t, err)
_, err = validator.ExecTx(ctx, roles.MinterController.KeyName(), "fiat-tokenfactory", "configure-minter", roles.Minter.FormattedAddress(), "1000000uusdc", "-b", "block")
require.NoError(t, err)
_, err = validator.ExecTx(ctx, roles.Minter.KeyName(), "fiat-tokenfactory", "mint", sender.FormattedAddress(), "1000000uusdc", "-b", "block")
require.NoError(t, err)

receivers := interchaintest.GetAndFundTestUsers(t, ctx, "receiver", 1_000_000, gaia)
receiver = receivers[0]

return
}
Loading

0 comments on commit 9c1e703

Please sign in to comment.