Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ack callbacks for IBC transfers #3589

Merged
merged 26 commits into from
Dec 16, 2022
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
2f48bf0
added ack callbacks
nicolaslara Dec 1, 2022
a119e98
updated bad import in tests
nicolaslara Dec 1, 2022
a99bee2
gofumpt
nicolaslara Dec 1, 2022
eff6127
remove unused file
nicolaslara Dec 1, 2022
696bc72
removed memo on callback packets and add tests for acks
nicolaslara Dec 2, 2022
ce4ef41
only removing the memo if the only key is the callback
nicolaslara Dec 2, 2022
d44c9f7
renamed ibc_hooks to ibchooks
nicolaslara Dec 10, 2022
a03d6bb
renaming helper method
nicolaslara Dec 10, 2022
f289823
Merge branch 'main' into nicolas/transfer-ack-callback
nicolaslara Dec 10, 2022
5bdeb31
renamed callback key
nicolaslara Dec 13, 2022
981dad6
Merge branch 'main' into nicolas/transfer-ack-callback
nicolaslara Dec 14, 2022
4987812
added missing store key to upgrade
nicolaslara Dec 14, 2022
bccc9a4
added apline debug image so that sh is available
nicolaslara Dec 14, 2022
0bfa4a3
fixed callback param
nicolaslara Dec 14, 2022
37c80f4
fixed readme
nicolaslara Dec 15, 2022
05fe3ae
Update x/ibc-hooks/wasm_hook.go
nicolaslara Dec 15, 2022
7d3566c
added test for empty memo
nicolaslara Dec 15, 2022
f83a3e2
renamed ibc hooks keeper
nicolaslara Dec 15, 2022
ade5cdd
Merge branch 'main' into nicolas/transfer-ack-callback
nicolaslara Dec 15, 2022
aadb41f
removed unused import
nicolaslara Dec 15, 2022
ba8bf7b
fofumpt
nicolaslara Dec 15, 2022
0a9ac6b
deleting the key once it's no longer needed
nicolaslara Dec 15, 2022
6aeef7b
Update x/ibc-hooks/README.md
nicolaslara Dec 16, 2022
3857b43
Update x/ibc-hooks/README.md
nicolaslara Dec 16, 2022
7001f1e
Update x/ibc-hooks/README.md
nicolaslara Dec 16, 2022
ff8ea2f
renamed packet without memo
nicolaslara Dec 16, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,10 @@ docker-build-debug:
@DOCKER_BUILDKIT=1 docker build -t osmosis:${COMMIT} --build-arg BASE_IMG_TAG=debug -f Dockerfile .
@DOCKER_BUILDKIT=1 docker tag osmosis:${COMMIT} osmosis:debug

docker-build-debug-alpine:
@DOCKER_BUILDKIT=1 docker build -t osmosis:${COMMIT} --build-arg BASE_IMG_TAG=debug --build-arg RUNNER_IMAGE=$(RUNNER_BASE_IMAGE_ALPINE) -f Dockerfile .
@DOCKER_BUILDKIT=1 docker tag osmosis:${COMMIT} osmosis:debug

docker-build-e2e-init-chain:
@DOCKER_BUILDKIT=1 docker build -t osmosis-e2e-init-chain:debug --build-arg E2E_SCRIPT_NAME=chain --platform=linux/x86_64 -f tests/e2e/initialization/init.Dockerfile .

Expand Down
18 changes: 15 additions & 3 deletions app/keepers/keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ import (
downtimedetector "github.com/osmosis-labs/osmosis/v13/x/downtime-detector"
downtimetypes "github.com/osmosis-labs/osmosis/v13/x/downtime-detector/types"
ibchooks "github.com/osmosis-labs/osmosis/v13/x/ibc-hooks"
ibchookskeeper "github.com/osmosis-labs/osmosis/v13/x/ibc-hooks/keeper"
ibchookstypes "github.com/osmosis-labs/osmosis/v13/x/ibc-hooks/types"
ibcratelimit "github.com/osmosis-labs/osmosis/v13/x/ibc-rate-limit"
ibcratelimittypes "github.com/osmosis-labs/osmosis/v13/x/ibc-rate-limit/types"
"github.com/osmosis-labs/osmosis/v13/x/swaprouter"
Expand Down Expand Up @@ -110,6 +112,7 @@ type AppKeepers struct {
DowntimeKeeper *downtimedetector.Keeper
SlashingKeeper *slashingkeeper.Keeper
IBCKeeper *ibckeeper.Keeper
IBCHooksKeeper *ibchookskeeper.Keeper
ICAHostKeeper *icahostkeeper.Keeper
TransferKeeper *ibctransferkeeper.Keeper
EvidenceKeeper *evidencekeeper.Keeper
Expand Down Expand Up @@ -222,7 +225,13 @@ func (appKeepers *AppKeepers) InitNormalKeepers(
appKeepers.ScopedIBCKeeper,
)

appKeepers.WireICS20PreWasmKeeper(appCodec, bApp)
// Configure the hooks keeper
hooksKeeper := ibchookskeeper.NewKeeper(
appKeepers.keys[ibchookstypes.StoreKey],
)
appKeepers.IBCHooksKeeper = &hooksKeeper

appKeepers.WireICS20PreWasmKeeper(appCodec, bApp, appKeepers.IBCHooksKeeper)

icaHostKeeper := icahostkeeper.NewKeeper(
appCodec, appKeepers.keys[icahosttypes.StoreKey],
Expand Down Expand Up @@ -428,9 +437,11 @@ func (appKeepers *AppKeepers) InitNormalKeepers(
// appkeepers.WasmHooks AND appKeepers.RateLimitingICS4Wrapper
func (appKeepers *AppKeepers) WireICS20PreWasmKeeper(
appCodec codec.Codec,
bApp *baseapp.BaseApp) {
bApp *baseapp.BaseApp,
hooksKeeper *ibchookskeeper.Keeper,
) {
// Setup the ICS4Wrapper used by the hooks middleware
wasmHooks := ibchooks.NewWasmHooks(nil) // The contract keeper needs to be set later
wasmHooks := ibchooks.NewWasmHooks(hooksKeeper, nil) // The contract keeper needs to be set later
appKeepers.Ics20WasmHooks = &wasmHooks
appKeepers.HooksICS4Wrapper = ibchooks.NewICS4Middleware(
appKeepers.IBCKeeper.ChannelKeeper,
Expand Down Expand Up @@ -640,5 +651,6 @@ func KVStoreKeys() []string {
tokenfactorytypes.StoreKey,
valsetpreftypes.StoreKey,
protorevtypes.StoreKey,
ibchookstypes.StoreKey,
}
}
5 changes: 3 additions & 2 deletions app/modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
ibc "github.com/cosmos/ibc-go/v3/modules/core"
ibchost "github.com/cosmos/ibc-go/v3/modules/core/24-host"
ibckeeper "github.com/cosmos/ibc-go/v3/modules/core/keeper"
ibchookstypes "github.com/osmosis-labs/osmosis/v13/x/ibc-hooks/types"

ica "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts"
icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types"
Expand Down Expand Up @@ -85,7 +86,7 @@ import (
var moduleAccountPermissions = map[string][]string{
authtypes.FeeCollectorName: nil,
distrtypes.ModuleName: nil,
ibc_hooks.ModuleName: nil,
ibchookstypes.ModuleName: nil,
icatypes.ModuleName: nil,
minttypes.ModuleName: {authtypes.Minter, authtypes.Burner},
minttypes.DeveloperVestingModuleAcctName: nil,
Expand Down Expand Up @@ -242,7 +243,7 @@ func OrderInitGenesis(allModuleNames []string) []string {
// wasm after ibc transfer
wasm.ModuleName,
// ibc_hooks after auth keeper
ibc_hooks.ModuleName,
ibchookstypes.ModuleName,
}
}

Expand Down
7 changes: 4 additions & 3 deletions app/upgrades/v13/upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package v13_test

import (
"fmt"
ibchookstypes "github.com/osmosis-labs/osmosis/v13/x/ibc-hooks/types"
"testing"

ibcratelimittypes "github.com/osmosis-labs/osmosis/v13/x/ibc-rate-limit/types"
Expand Down Expand Up @@ -58,14 +59,14 @@ func (suite *UpgradeTestSuite) TestUpgrade() {
suite.App.AccountKeeper.RemoveAccount(suite.Ctx, acc)
// Because of SDK version map bug, we can't do the following, and instaed do a massive hack
// vm := suite.App.UpgradeKeeper.GetModuleVersionMap(suite.Ctx)
// delete(vm, ibc_hooks.ModuleName)
// delete(vm, ibchookstypes.ModuleName)
// OR
// vm[ibc_hooks.ModuleName] = 0
// vm[ibchookstypes.ModuleName] = 0
// suite.App.UpgradeKeeper.SetModuleVersionMap(suite.Ctx, vm)
upgradeStoreKey := suite.App.AppKeepers.GetKey(upgradetypes.StoreKey)
store := suite.Ctx.KVStore(upgradeStoreKey)
versionStore := prefix.NewStore(store, []byte{upgradetypes.VersionMapByte})
versionStore.Delete([]byte(ibc_hooks.ModuleName))
versionStore.Delete([]byte(ibchookstypes.ModuleName))

hasAcc := suite.App.AccountKeeper.HasAccount(suite.Ctx, ibc_hooks.WasmHookModuleAccountAddr)
suite.Require().False(hasAcc)
Expand Down
3 changes: 2 additions & 1 deletion app/upgrades/v14/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package v14

import (
store "github.com/cosmos/cosmos-sdk/store/types"
ibchookstypes "github.com/osmosis-labs/osmosis/v13/x/ibc-hooks/types"

"github.com/osmosis-labs/osmosis/v13/app/upgrades"
downtimetypes "github.com/osmosis-labs/osmosis/v13/x/downtime-detector/types"
Expand All @@ -17,7 +18,7 @@ var Upgrade = upgrades.Upgrade{
UpgradeName: UpgradeName,
CreateUpgradeHandler: CreateUpgradeHandler,
StoreUpgrades: store.StoreUpgrades{
Added: []string{valsetpreftypes.StoreKey, protorevtypes.StoreKey, swaproutertypes.StoreKey, downtimetypes.StoreKey},
Added: []string{valsetpreftypes.StoreKey, protorevtypes.StoreKey, swaproutertypes.StoreKey, downtimetypes.StoreKey, ibchookstypes.StoreKey},
Deleted: []string{},
},
}
36 changes: 35 additions & 1 deletion x/ibc-hooks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,40 @@ In wasm hooks, post packet execution:
* if wasm message has error, return ErrAck
* otherwise continue through middleware

## Ack callbacks

A contract that, through a wasm hook, sends an IBC transfer, may need to listen for the ACK from that packet. To allow
nicolaslara marked this conversation as resolved.
Show resolved Hide resolved
contracts to listen on the ack of specific packets, we provide Ack callbacks.

### Testing strategy
### Design

The sender of an IBC transfer packet may specify a callback for when the ack of that packet is received in the memo
field of the transfer packet.
nicolaslara marked this conversation as resolved.
Show resolved Hide resolved

### Use case

The crosschain swaps implementation sends an IBC transfer. If the transfer were to fail, we want to allow the sender
to be able to retrieve their funds (which would otherwise be stuck in the contract). To do this, we allow users to
retrieve the funds after the timeout has passed, but without the ack information, we cannot guarantee that the send
hasn't failed (i.e.: returned an error ack notifying that the receiving change didn't accept it)

### Implementation

#### Callback information in memo

The for the callback to be processed, the transfer packet's memo should look like this:
nicolaslara marked this conversation as resolved.
Show resolved Hide resolved

`{"ibc_callback": "osmo1contractAddr"}`

The wasm hooks will keep the mapping from the packet's channel and sequence to the contract in storage. When an ack is
received, it will notify the specified contract via a sudo message.
Comment on lines +142 to +145
Copy link
Member

@ValarDragon ValarDragon Dec 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just realized, contract authors have to handle someone setting a callback the contract wasn't expecting. (So they need to do some validation)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add a version field on ibc_callback or probably not needed? (e.g. ibc_callback_v1)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

someone setting a callback the contract wasn't expecting

Senders don't specify the callback format though. Just who will receive it. So as long as the format doesn't change on the chain, contracts should be able to trust the interface.

I like the idea of versioning when we need it. But since we don't have plans for v2 yet, I think the easiest thing here would be to leave it as is and then add ibc_callback_v2 if we make a change.


#### Interface for receiving the Ack

The contract that awaits the callback should implement the following interface for a sudo message:

* `ReceiveAck { channel: String, sequence: u64, ack: String, success: bool }`

# Testing strategy

See go tests.
Binary file modified x/ibc-hooks/bytecode/counter.wasm
Binary file not shown.
Binary file added x/ibc-hooks/bytecode/crosschain_swaps.wasm
Binary file not shown.
Binary file added x/ibc-hooks/bytecode/swaprouter.wasm
Binary file not shown.
4 changes: 3 additions & 1 deletion x/ibc-hooks/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/address"

"github.com/osmosis-labs/osmosis/v13/x/ibc-hooks/types"

"github.com/osmosis-labs/osmosis/v13/osmoutils"
)

var WasmHookModuleAccountAddr sdk.AccAddress = address.Module(ModuleName, []byte("wasm-hook intermediary account"))
var WasmHookModuleAccountAddr sdk.AccAddress = address.Module(types.ModuleName, []byte("wasm-hook intermediary account"))

func IbcHooksInitGenesis(ctx sdk.Context, ak osmoutils.AccountKeeper) {
err := osmoutils.CreateModuleAccount(ctx, ak, WasmHookModuleAccountAddr)
Expand Down
133 changes: 122 additions & 11 deletions x/ibc-hooks/ibc_middleware_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"fmt"
"testing"

ibc_hooks "github.com/osmosis-labs/osmosis/v13/x/ibc-hooks"
ibchooks "github.com/osmosis-labs/osmosis/v13/x/ibc-hooks"

"github.com/osmosis-labs/osmosis/v13/osmoutils"

Expand Down Expand Up @@ -201,7 +201,7 @@ func (suite *HooksTestSuite) receivePacketWithSequence(receiver, memo string, pr
func (suite *HooksTestSuite) TestRecvTransferWithMetadata() {
// Setup contract
suite.chainA.StoreContractCode(&suite.Suite, "./bytecode/echo.wasm")
addr := suite.chainA.InstantiateContract(&suite.Suite, "{}")
addr := suite.chainA.InstantiateContract(&suite.Suite, "{}", 1)

ackBytes := suite.receivePacket(addr.String(), fmt.Sprintf(`{"wasm": {"contract": "%s", "msg": {"echo": {"msg": "test"} } } }`, addr))
ackStr := string(ackBytes)
Expand All @@ -217,7 +217,7 @@ func (suite *HooksTestSuite) TestRecvTransferWithMetadata() {
func (suite *HooksTestSuite) TestFundsAreTransferredToTheContract() {
// Setup contract
suite.chainA.StoreContractCode(&suite.Suite, "./bytecode/echo.wasm")
addr := suite.chainA.InstantiateContract(&suite.Suite, "{}")
addr := suite.chainA.InstantiateContract(&suite.Suite, "{}", 1)

// Check that the contract has no funds
localDenom := osmoutils.MustExtractDenomFromPacketOnRecv(suite.makeMockPacket("", "", 0))
Expand All @@ -243,7 +243,7 @@ func (suite *HooksTestSuite) TestFundsAreTransferredToTheContract() {
func (suite *HooksTestSuite) TestFundsAreReturnedOnFailedContractExec() {
// Setup contract
suite.chainA.StoreContractCode(&suite.Suite, "./bytecode/echo.wasm")
addr := suite.chainA.InstantiateContract(&suite.Suite, "{}")
addr := suite.chainA.InstantiateContract(&suite.Suite, "{}", 1)

// Check that the contract has no funds
localDenom := osmoutils.MustExtractDenomFromPacketOnRecv(suite.makeMockPacket("", "", 0))
Expand Down Expand Up @@ -311,7 +311,7 @@ func (suite *HooksTestSuite) TestPacketsThatShouldBeSkipped() {
func (suite *HooksTestSuite) TestFundTracking() {
// Setup contract
suite.chainA.StoreContractCode(&suite.Suite, "./bytecode/counter.wasm")
addr := suite.chainA.InstantiateContract(&suite.Suite, `{"count": 0}`)
addr := suite.chainA.InstantiateContract(&suite.Suite, `{"count": 0}`, 1)

// Check that the contract has no funds
localDenom := osmoutils.MustExtractDenomFromPacketOnRecv(suite.makeMockPacket("", "", 0))
Expand All @@ -325,29 +325,140 @@ func (suite *HooksTestSuite) TestFundTracking() {

state := suite.chainA.QueryContract(
&suite.Suite, addr,
[]byte(fmt.Sprintf(`{"get_count": {"addr": "%s"}}`, ibc_hooks.WasmHookModuleAccountAddr)))
[]byte(fmt.Sprintf(`{"get_count": {"addr": "%s"}}`, ibchooks.WasmHookModuleAccountAddr)))
suite.Require().Equal(`{"count":0}`, state)

state = suite.chainA.QueryContract(
&suite.Suite, addr,
[]byte(fmt.Sprintf(`{"get_total_funds": {"addr": "%s"}}`, ibc_hooks.WasmHookModuleAccountAddr)))
suite.Require().Equal(`{"total_funds":[]}`, state)
[]byte(fmt.Sprintf(`{"get_total_funds": {"addr": "%s"}}`, ibchooks.WasmHookModuleAccountAddr)))
suite.Require().Equal(`{"total_funds":[{"denom":"ibc/C053D637CCA2A2BA030E2C5EE1B28A16F71CCB0E45E8BE52766DC1B241B77878","amount":"1"}]}`, state)
nicolaslara marked this conversation as resolved.
Show resolved Hide resolved

suite.receivePacketWithSequence(
addr.String(),
fmt.Sprintf(`{"wasm": {"contract": "%s", "msg": {"increment": {} } } }`, addr), 1)

state = suite.chainA.QueryContract(
&suite.Suite, addr,
[]byte(fmt.Sprintf(`{"get_count": {"addr": "%s"}}`, ibc_hooks.WasmHookModuleAccountAddr)))
[]byte(fmt.Sprintf(`{"get_count": {"addr": "%s"}}`, ibchooks.WasmHookModuleAccountAddr)))
suite.Require().Equal(`{"count":1}`, state)

state = suite.chainA.QueryContract(
&suite.Suite, addr,
[]byte(fmt.Sprintf(`{"get_total_funds": {"addr": "%s"}}`, ibc_hooks.WasmHookModuleAccountAddr)))
suite.Require().Equal(`{"total_funds":[{"denom":"ibc/C053D637CCA2A2BA030E2C5EE1B28A16F71CCB0E45E8BE52766DC1B241B77878","amount":"1"}]}`, state)
[]byte(fmt.Sprintf(`{"get_total_funds": {"addr": "%s"}}`, ibchooks.WasmHookModuleAccountAddr)))
suite.Require().Equal(`{"total_funds":[{"denom":"ibc/C053D637CCA2A2BA030E2C5EE1B28A16F71CCB0E45E8BE52766DC1B241B77878","amount":"2"}]}`, state)

// Check that the token has now been transferred to the contract
balance = suite.chainA.GetOsmosisApp().BankKeeper.GetBalance(suite.chainA.GetContext(), addr, localDenom)
suite.Require().Equal(sdk.NewInt(2), balance.Amount)
}

// custom MsgTransfer constructor that supports Memo
func NewMsgTransfer(
token sdk.Coin, sender, receiver string, memo string,
) *transfertypes.MsgTransfer {
return &transfertypes.MsgTransfer{
SourcePort: "transfer",
SourceChannel: "channel-0",
Token: token,
Sender: sender,
Receiver: receiver,
TimeoutHeight: clienttypes.NewHeight(0, 100),
TimeoutTimestamp: 0,
Memo: memo,
}
}
Comment on lines +355 to +369
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be at all valuable to include a MsgTransfer without a memo, to ensure packet is still properly relayed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added a test for this. I'm still using NewMsgTransfer but with "" as the memo.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sweet! Do you feel like using a message that has the field completely missing would potentially act differently than a blank string?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it will, as getMemo() would still return the default "" if the memo isn't there.


type Direction int64

const (
AtoB Direction = iota
BtoA
)

func (suite *HooksTestSuite) GetEndpoints(direction Direction) (sender *ibctesting.Endpoint, receiver *ibctesting.Endpoint) {
switch direction {
case AtoB:
sender = suite.path.EndpointA
receiver = suite.path.EndpointB
case BtoA:
sender = suite.path.EndpointB
receiver = suite.path.EndpointA
}
return sender, receiver
}

func (suite *HooksTestSuite) RelayPacket(packet channeltypes.Packet, direction Direction) (*sdk.Result, []byte) {
sender, receiver := suite.GetEndpoints(direction)

err := receiver.UpdateClient()
suite.Require().NoError(err)

// receiver Receives
receiveResult, err := receiver.RecvPacketWithResult(packet)
suite.Require().NoError(err)

ack, err := ibctesting.ParseAckFromEvents(receiveResult.GetEvents())
suite.Require().NoError(err)

// sender Acknowledges
err = sender.AcknowledgePacket(packet, ack)
suite.Require().NoError(err)

err = sender.UpdateClient()
suite.Require().NoError(err)
err = receiver.UpdateClient()
suite.Require().NoError(err)

return receiveResult, ack
}

func (suite *HooksTestSuite) FullSend(msg sdk.Msg, direction Direction) (*sdk.Result, *sdk.Result, string, error) {
var sender *osmosisibctesting.TestChain
switch direction {
case AtoB:
sender = suite.chainA
case BtoA:
sender = suite.chainB
}
sendResult, err := sender.SendMsgsNoCheck(msg)
suite.Require().NoError(err)

packet, err := ibctesting.ParsePacketFromEvents(sendResult.GetEvents())
suite.Require().NoError(err)

receiveResult, ack := suite.RelayPacket(packet, direction)

return sendResult, receiveResult, string(ack), err
}

func (suite *HooksTestSuite) TestAcks() {
suite.chainA.StoreContractCode(&suite.Suite, "./bytecode/counter.wasm")
addr := suite.chainA.InstantiateContract(&suite.Suite, `{"count": 0}`, 1)

// Generate swap instructions for the contract
callbackMemo := fmt.Sprintf(`{"ibc_callback":"%s"}`, addr)
// Send IBC transfer with the memo with crosschain-swap instructions
transferMsg := NewMsgTransfer(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1000)), suite.chainA.SenderAccount.GetAddress().String(), addr.String(), callbackMemo)
suite.FullSend(transferMsg, AtoB)

// The test contract will increment the counter for itself every time it receives an ack
state := suite.chainA.QueryContract(
&suite.Suite, addr,
[]byte(fmt.Sprintf(`{"get_count": {"addr": "%s"}}`, addr)))
suite.Require().Equal(`{"count":1}`, state)

suite.FullSend(transferMsg, AtoB)
state = suite.chainA.QueryContract(
&suite.Suite, addr,
[]byte(fmt.Sprintf(`{"get_count": {"addr": "%s"}}`, addr)))
suite.Require().Equal(`{"count":2}`, state)

}

func (suite *HooksTestSuite) TestSendWithoutMemo() {
// Sending a packet without memo to ensure that the ibc_callback middleware doesn't interfere with a regular send
transferMsg := NewMsgTransfer(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1000)), suite.chainA.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), "")
_, _, ack, err := suite.FullSend(transferMsg, AtoB)
suite.Require().NoError(err)
suite.Require().Contains(ack, "result")
}
Loading