Repository: terra-money/core
File: custom/bank/keeper/keeper.go (Go)

Code before:
	err := k.BlockBeforeSend(ctx, moduleAccAddr, delegatorAddr, amt)
	if err != nil {
		return err
	}
	k.TrackBeforeSend(ctx, moduleAccAddr, delegatorAddr, amt)

	return k.Keeper.UndelegateCoins(ctx, moduleAccAddr, delegatorAddr, amt)
}

// DelegateCoins performs delegation by deducting amt coins from an account with
// address addr. For vesting accounts, delegations amounts are tracked for both
// vesting and vested coins. The coins are then transferred from the delegator
// address to a ModuleAccount address. If any of the delegation amounts are negative,
// an error is returned.
func (k Keeper) DelegateCoins(ctx sdk.Context, delegatorAddr, moduleAccAddr sdk.AccAddress, amt sdk.Coins) error {
	err := k.BlockBeforeSend(ctx, delegatorAddr, moduleAccAddr, amt)
	if err != nil {
		return err
	}
	k.TrackBeforeSend(ctx, delegatorAddr, moduleAccAddr, amt)

	return k.Keeper.DelegateCoins(ctx, delegatorAddr, moduleAccAddr, amt)
}

// InputOutputCoins performs multi-send functionality. It accepts a series of
// inputs that correspond to a series of outputs. It returns an error if the
// inputs and outputs don't line up or if any single transfer of tokens fails.
func (k Keeper) InputOutputCoins(ctx sdk.Context, inputs []banktypes.Input, outputs []banktypes.Output) error {
	// ValidateInputsOutputs is executed in the k.Keeper.InputOutputCoins
	// but we execute it here too just to avoid unnecessary computation
	// if the inputs and outputs are invalid, that way we can assume things like
	// the addreeses being valid and the inputs and outputs match balances
	if err := banktypes.ValidateInputsOutputs(inputs, outputs); err != nil {
		return err
	}

	// Create a slice to hold OutputCoin structs
	// to be sorted so the output is deterministic
	var outputCoinSlice customterratypes.OutputCoinSlice

	// Populate the slice with outputs and corresponding
	// coins to decrease the algorithm complexity.
	for _, output := range outputs {
		for _, coin := range output.Coins {
			outputCoinSlice = append(outputCoinSlice, customterratypes.OutputCoin{Output: output, Coin: coin})
		}
	}

	// Sort the slice based on denomination
	sort.Sort(outputCoinSlice)


Review comment (suggestion):
This is tricky, since multi-send does not actually map nicely who is the sender and who is the recipient.
```
Sender 1 ----|         |-> Recipient 1 
Sender 2 ----|---------|-> Recipient 2
Sender 3 ----|
```

Code after:
		return err
	}
	k.TrackBeforeSend(ctx, moduleAccAddr, delegatorAddr, amt)

	return k.Keeper.UndelegateCoins(ctx, moduleAccAddr, delegatorAddr, amt)
}

// DelegateCoins performs delegation by deducting amt coins from an account with
// address addr. For vesting accounts, delegations amounts are tracked for both
// vesting and vested coins. The coins are then transferred from the delegator
// address to a ModuleAccount address. If any of the delegation amounts are negative,
// an error is returned.
func (k Keeper) DelegateCoins(ctx sdk.Context, delegatorAddr, moduleAccAddr sdk.AccAddress, amt sdk.Coins) error {
	err := k.BlockBeforeSend(ctx, delegatorAddr, moduleAccAddr, amt)
	if err != nil {
		return err
	}
	k.TrackBeforeSend(ctx, delegatorAddr, moduleAccAddr, amt)

	return k.Keeper.DelegateCoins(ctx, delegatorAddr, moduleAccAddr, amt)
}

// InputOutputCoins performs multi-send functionality. It accepts a series of
// inputs that correspond to a series of outputs. It returns an error if the
// inputs and outputs don't line up or if any single transfer of tokens fails.
func (k Keeper) InputOutputCoins(ctx sdk.Context, inputs []banktypes.Input, outputs []banktypes.Output) error {
	// Only 1 input is allowed for all outputs check the following url:
	// https://github.com/terra-money/cosmos-sdk/blob/release/v0.47.x/x/bank/types/msgs.go#L87-L89
	//
	// This if statement is added here too so we know
	// when multiple inputs are allowed in the future
	// because ErrMultipleSenders will fail to import
	// because will be removed from the code.
	if len(inputs) != 1 {
		return banktypes.ErrMultipleSenders
	}
	input := inputs[0]
	inputaddress := sdk.MustAccAddressFromBech32(input.Address)

	for _, output := range outputs {
		outputaddress := sdk.MustAccAddressFromBech32(output.Address)

		err := k.BlockBeforeSend(ctx, inputaddress, outputaddress, output.Coins)
		if err != nil {
			return err
		}
		k.TrackBeforeSend(ctx, inputaddress, outputaddress, output.Coins)
	}

	return k.Keeper.InputOutputCoins(ctx, inputs, outputs)
}