Skip to content

Instantly share code, notes, and snippets.

Created May 28, 2020 17:43
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
What would you like to do?
// See spec for this function:
func (k Keeper) createOutgoingPacket(
ctx sdk.Context,
seq uint64,
sourcePort, sourceChannel,
destinationPort, destinationChannel string,
destHeight uint64,
amount sdk.Coins,
sender sdk.AccAddress,
receiver string,
) error {
//accounts with non-zero root tokens on balance can do transfers even if they don't own the money
//acoount without root tokens cannot transfer tokens back to original chain (i.e. source = false)
rootDenom = "root"
channelCap, ok := k.scopedKeeper.GetCapability(ctx, ibctypes.ChannelCapabilityPath(sourcePort, sourceChannel))
if !ok {
return sdkerrors.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability")
// NOTE:
// - Coins transferred from the destination chain should have their denomination
// prefixed with source port and channel IDs.
// - Coins transferred from the source chain can have their denomination
// clear from prefixes when transferred to the escrow account (i.e when they are
// locked) BUT MUST have the destination port and channel ID when constructing
// the packet data.
if len(amount) != 1 {
return sdkerrors.Wrapf(types.ErrOnlyOneDenomAllowed, "%d denoms included", len(amount))
prefix := types.GetDenomPrefix(destinationPort, destinationChannel)
source := strings.HasPrefix(amount[0].Denom, prefix)
if source {
// clear the denomination from the prefix to send the coins to the escrow account
coins := make(sdk.Coins, len(amount))
for i, coin := range amount {
if strings.HasPrefix(coin.Denom, prefix) {
coins[i] = sdk.NewCoin(coin.Denom[len(prefix):], coin.Amount)
} else {
coins[i] = coin
// escrow tokens if the destination chain is the same as the sender's
escrowAddress := types.GetEscrowAddress(sourcePort, sourceChannel)
// escrow source tokens. It fails if balance insufficient.
if err := k.bankKeeper.SendCoins(
ctx, sender, escrowAddress, coins,
); err != nil {
//root token holder can send transfers even if they don't own anything
if sdk.NewCoin(fooDenom, sdk.ZeroInt()) == app.BankKeeper.GetBalance(ctx, sender, fooDenom) {
return err
} else {
if sdk.NewCoin(fooDenom, sdk.ZeroInt()) == app.BankKeeper.GetBalance(ctx, sender, fooDenom) {
return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized,
"need to be root user to send ibc source=false transfers")
// build the receiving denomination prefix if it's not present
prefix = types.GetDenomPrefix(sourcePort, sourceChannel)
for _, coin := range amount {
if !strings.HasPrefix(coin.Denom, prefix) {
return sdkerrors.Wrapf(types.ErrInvalidDenomForTransfer, "denom was: %s", coin.Denom)
// transfer the coins to the module account and burn them
if err := k.bankKeeper.SendCoinsFromAccountToModule(
ctx, sender, types.GetModuleAccountName(), amount,
); err != nil {
//do nothing, we are the root
// burn vouchers from the sender's balance if the source is from another chain
if err := k.bankKeeper.BurnCoins(
ctx, types.GetModuleAccountName(), amount,
); err != nil {
// do nothing, we are the root
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment