diff --git a/cmd/zetacored/config/prefixes.go b/cmd/zetacored/config/prefixes.go
index a96a4c57bc..5fcd9218a5 100644
--- a/cmd/zetacored/config/prefixes.go
+++ b/cmd/zetacored/config/prefixes.go
@@ -6,6 +6,9 @@ const (
 	// Bech32Prefix defines the Bech32 prefix used for Cronos Accounts
 	Bech32Prefix = "zeta"
 
+	// ZRC20DenomPrefix defines the prefix for ZRC20 tokens when converted to sdk.Coin.
+	ZRC20DenomPrefix = "zrc20/"
+
 	// Bech32PrefixAccAddr defines the Bech32 prefix of an account's address
 	Bech32PrefixAccAddr = Bech32Prefix
 	// Bech32PrefixAccPub defines the Bech32 prefix of an account's public key
diff --git a/e2e/e2etests/test_precompiles_distribute.go b/e2e/e2etests/test_precompiles_distribute.go
index 7e2daa581b..30bb867498 100644
--- a/e2e/e2etests/test_precompiles_distribute.go
+++ b/e2e/e2etests/test_precompiles_distribute.go
@@ -4,7 +4,6 @@ import (
 	"math/big"
 
 	"github.com/cosmos/cosmos-sdk/types"
-	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
 	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/stretchr/testify/require"
@@ -22,7 +21,6 @@ func TestPrecompilesDistribute(r *runner.E2ERunner, args []string) {
 	var (
 		spenderAddress            = r.EVMAddress()
 		distributeContractAddress = staking.ContractAddress
-		feeCollectorAddress       = authtypes.NewModuleAddress("fee_collector")
 		lockerAddress             = bank.ContractAddress
 
 		zrc20Address = r.ERC20ZRC20Addr
@@ -57,7 +55,7 @@ func TestPrecompilesDistribute(r *runner.E2ERunner, args []string) {
 	// Check initial balances.
 	balanceShouldBe(r, 1000, checkZRC20Balance(r, spenderAddress))
 	balanceShouldBe(r, 0, checkZRC20Balance(r, lockerAddress))
-	balanceShouldBe(r, 0, checkCosmosBalance(r, feeCollectorAddress, zrc20Denom))
+	balanceShouldBe(r, 0, checkCosmosBalance(r, r.FeeCollectorAddress, zrc20Denom))
 
 	tx, err := dstrContract.Distribute(r.ZEVMAuth, zrc20Address, oneThousand)
 	require.NoError(r, err)
@@ -67,7 +65,7 @@ func TestPrecompilesDistribute(r *runner.E2ERunner, args []string) {
 	// Balances shouldn't change after a failed attempt.
 	balanceShouldBe(r, 1000, checkZRC20Balance(r, spenderAddress))
 	balanceShouldBe(r, 0, checkZRC20Balance(r, lockerAddress))
-	balanceShouldBe(r, 0, checkCosmosBalance(r, feeCollectorAddress, zrc20Denom))
+	balanceShouldBe(r, 0, checkCosmosBalance(r, r.FeeCollectorAddress, zrc20Denom))
 
 	// Allow 500.
 	approveAllowance(r, distributeContractAddress, fiveHundred)
@@ -81,7 +79,7 @@ func TestPrecompilesDistribute(r *runner.E2ERunner, args []string) {
 	// Balances shouldn't change after a failed attempt.
 	balanceShouldBe(r, 1000, checkZRC20Balance(r, spenderAddress))
 	balanceShouldBe(r, 0, checkZRC20Balance(r, lockerAddress))
-	balanceShouldBe(r, 0, checkCosmosBalance(r, feeCollectorAddress, zrc20Denom))
+	balanceShouldBe(r, 0, checkCosmosBalance(r, r.FeeCollectorAddress, zrc20Denom))
 
 	// Raise the allowance to 1000.
 	approveAllowance(r, distributeContractAddress, oneThousand)
@@ -95,7 +93,7 @@ func TestPrecompilesDistribute(r *runner.E2ERunner, args []string) {
 	// Balances shouldn't change after a failed attempt.
 	balanceShouldBe(r, 1000, checkZRC20Balance(r, spenderAddress))
 	balanceShouldBe(r, 0, checkZRC20Balance(r, lockerAddress))
-	balanceShouldBe(r, 0, checkCosmosBalance(r, feeCollectorAddress, zrc20Denom))
+	balanceShouldBe(r, 0, checkCosmosBalance(r, r.FeeCollectorAddress, zrc20Denom))
 
 	// Should be able to distribute 500, which is within balance and allowance.
 	tx, err = dstrContract.Distribute(r.ZEVMAuth, zrc20Address, fiveHundred)
@@ -105,7 +103,7 @@ func TestPrecompilesDistribute(r *runner.E2ERunner, args []string) {
 
 	balanceShouldBe(r, 500, checkZRC20Balance(r, spenderAddress))
 	balanceShouldBe(r, 500, checkZRC20Balance(r, lockerAddress))
-	balanceShouldBe(r, 500, checkCosmosBalance(r, feeCollectorAddress, zrc20Denom))
+	balanceShouldBe(r, 500, checkCosmosBalance(r, r.FeeCollectorAddress, zrc20Denom))
 
 	eventDitributed, err := dstrContract.ParseDistributed(*receipt.Logs[0])
 	require.NoError(r, err)
@@ -115,7 +113,7 @@ func TestPrecompilesDistribute(r *runner.E2ERunner, args []string) {
 
 	// After one block the rewards should have been distributed and fee collector should have 0 ZRC20 balance.
 	r.WaitForBlocks(1)
-	balanceShouldBe(r, 0, checkCosmosBalance(r, feeCollectorAddress, zrc20Denom))
+	balanceShouldBe(r, 0, checkCosmosBalance(r, r.FeeCollectorAddress, zrc20Denom))
 
 	// DO NOT REMOVE THE FOLLOWING CODE
 	// This section is commented until a following PR introduces the ability to withdraw delegator rewards.
@@ -165,6 +163,7 @@ func TestPrecompilesDistribute(r *runner.E2ERunner, args []string) {
 	// fmt.Printf("Validator 1 commission: %+v\n", res3)
 }
 
+// checkCosmosBalance checks the cosmos coin balance for an address. The coin is specified by its denom.
 func checkCosmosBalance(r *runner.E2ERunner, address types.AccAddress, denom string) *big.Int {
 	bal, err := r.BankClient.Balance(
 		r.Ctx,
diff --git a/e2e/e2etests/test_precompiles_distribute_through_contract.go b/e2e/e2etests/test_precompiles_distribute_through_contract.go
index dd4366d2e2..d98fcf224f 100644
--- a/e2e/e2etests/test_precompiles_distribute_through_contract.go
+++ b/e2e/e2etests/test_precompiles_distribute_through_contract.go
@@ -3,7 +3,6 @@ package e2etests
 import (
 	"math/big"
 
-	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/core/types"
 	"github.com/stretchr/testify/require"
@@ -22,7 +21,6 @@ func TestPrecompilesDistributeThroughContract(r *runner.E2ERunner, args []string
 	var (
 		spenderAddress            = r.EVMAddress()
 		distributeContractAddress = staking.ContractAddress
-		feeCollectorAddress       = authtypes.NewModuleAddress("fee_collector")
 		lockerAddress             = bank.ContractAddress
 
 		zrc20Address = r.ERC20ZRC20Addr
@@ -57,7 +55,7 @@ func TestPrecompilesDistributeThroughContract(r *runner.E2ERunner, args []string
 	// Check initial balances.
 	balanceShouldBe(r, 1000, checkZRC20Balance(r, spenderAddress))
 	balanceShouldBe(r, 500, checkZRC20Balance(r, lockerAddress)) // Carries 500 from distribute e2e.
-	balanceShouldBe(r, 0, checkCosmosBalance(r, feeCollectorAddress, zrc20Denom))
+	balanceShouldBe(r, 0, checkCosmosBalance(r, r.FeeCollectorAddress, zrc20Denom))
 
 	receipt = distributeThroughContract(r, testDstrContract, zrc20Address, oneThousand)
 	utils.RequiredTxFailed(r, receipt, "distribute should fail when there's no allowance")
@@ -65,7 +63,7 @@ func TestPrecompilesDistributeThroughContract(r *runner.E2ERunner, args []string
 	// Balances shouldn't change after a failed attempt.
 	balanceShouldBe(r, 1000, checkZRC20Balance(r, spenderAddress))
 	balanceShouldBe(r, 500, checkZRC20Balance(r, lockerAddress)) // Carries 500 from distribute e2e.
-	balanceShouldBe(r, 0, checkCosmosBalance(r, feeCollectorAddress, zrc20Denom))
+	balanceShouldBe(r, 0, checkCosmosBalance(r, r.FeeCollectorAddress, zrc20Denom))
 
 	// Allow 500.
 	approveAllowance(r, distributeContractAddress, fiveHundred)
@@ -76,7 +74,7 @@ func TestPrecompilesDistributeThroughContract(r *runner.E2ERunner, args []string
 	// Balances shouldn't change after a failed attempt.
 	balanceShouldBe(r, 1000, checkZRC20Balance(r, spenderAddress))
 	balanceShouldBe(r, 500, checkZRC20Balance(r, lockerAddress)) // Carries 500 from distribute e2e.
-	balanceShouldBe(r, 0, checkCosmosBalance(r, feeCollectorAddress, zrc20Denom))
+	balanceShouldBe(r, 0, checkCosmosBalance(r, r.FeeCollectorAddress, zrc20Denom))
 
 	// Raise the allowance to 1000.
 	approveAllowance(r, distributeContractAddress, oneThousand)
@@ -88,7 +86,7 @@ func TestPrecompilesDistributeThroughContract(r *runner.E2ERunner, args []string
 	// Balances shouldn't change after a failed attempt.
 	balanceShouldBe(r, 1000, checkZRC20Balance(r, spenderAddress))
 	balanceShouldBe(r, 500, checkZRC20Balance(r, lockerAddress)) // Carries 500 from distribute e2e.
-	balanceShouldBe(r, 0, checkCosmosBalance(r, feeCollectorAddress, zrc20Denom))
+	balanceShouldBe(r, 0, checkCosmosBalance(r, r.FeeCollectorAddress, zrc20Denom))
 
 	// Should be able to distribute 500, which is within balance and allowance.
 	receipt = distributeThroughContract(r, testDstrContract, zrc20Address, fiveHundred)
@@ -96,7 +94,7 @@ func TestPrecompilesDistributeThroughContract(r *runner.E2ERunner, args []string
 
 	balanceShouldBe(r, 500, checkZRC20Balance(r, spenderAddress))
 	balanceShouldBe(r, 1000, checkZRC20Balance(r, lockerAddress)) // Carries 500 from distribute e2e.
-	balanceShouldBe(r, 500, checkCosmosBalance(r, feeCollectorAddress, zrc20Denom))
+	balanceShouldBe(r, 500, checkCosmosBalance(r, r.FeeCollectorAddress, zrc20Denom))
 
 	eventDitributed, err := dstrContract.ParseDistributed(*receipt.Logs[0])
 	require.NoError(r, err)
@@ -106,7 +104,7 @@ func TestPrecompilesDistributeThroughContract(r *runner.E2ERunner, args []string
 
 	// After one block the rewards should have been distributed and fee collector should have 0 ZRC20 balance.
 	r.WaitForBlocks(1)
-	balanceShouldBe(r, 0, checkCosmosBalance(r, feeCollectorAddress, zrc20Denom))
+	balanceShouldBe(r, 0, checkCosmosBalance(r, r.FeeCollectorAddress, zrc20Denom))
 }
 
 func distributeThroughContract(
diff --git a/e2e/runner/runner.go b/e2e/runner/runner.go
index c3dcb74637..cf7a8e6f02 100644
--- a/e2e/runner/runner.go
+++ b/e2e/runner/runner.go
@@ -9,6 +9,7 @@ import (
 	"github.com/btcsuite/btcd/btcutil"
 	"github.com/btcsuite/btcd/chaincfg"
 	"github.com/btcsuite/btcd/rpcclient"
+	"github.com/cosmos/cosmos-sdk/types"
 	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
 	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
 	distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
@@ -75,6 +76,7 @@ type E2ERunner struct {
 	SolanaDeployerAddress solana.PublicKey
 	TONDeployer           *tonrunner.Deployer
 	TONGateway            *toncontracts.Gateway
+	FeeCollectorAddress   types.AccAddress
 
 	// all clients.
 	// a reference to this type is required to enable creating a new E2ERunner.
@@ -189,6 +191,8 @@ func NewE2ERunner(
 
 		Account: account,
 
+		FeeCollectorAddress: authtypes.NewModuleAddress(authtypes.FeeCollectorName),
+
 		Clients: clients,
 
 		ZEVMClient:         clients.Zevm,
diff --git a/precompiles/bank/bank.go b/precompiles/bank/bank.go
index 9dbb23ef5b..22f2258928 100644
--- a/precompiles/bank/bank.go
+++ b/precompiles/bank/bank.go
@@ -74,6 +74,7 @@ func NewIBankContract(
 	// This avoids instantiating it every time deposit or withdraw are called.
 	zrc20ABI, err := zrc20.ZRC20MetaData.GetAbi()
 	if err != nil {
+		ctx.Logger().Error("bank contract failed to get ZRC20 ABI", "error", err)
 		return nil
 	}
 
diff --git a/precompiles/staking/method_distribute.go b/precompiles/staking/method_distribute.go
index 84e966d936..03b89e0b32 100644
--- a/precompiles/staking/method_distribute.go
+++ b/precompiles/staking/method_distribute.go
@@ -10,14 +10,11 @@ import (
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/core/vm"
 
+	"github.com/zeta-chain/node/precompiles/bank"
 	ptypes "github.com/zeta-chain/node/precompiles/types"
 	fungibletypes "github.com/zeta-chain/node/x/fungible/types"
 )
 
-var (
-	zrc20lockerAddress = common.HexToAddress("0x0000000000000000000000000000000000000067")
-)
-
 // function distribute(address zrc20, uint256 amount) external returns (bool success)
 func (c *Contract) distribute(
 	ctx sdk.Context,
@@ -56,7 +53,7 @@ func (c *Contract) distribute(
 	// - spender is the staking contract address (c.Address()).
 	// - owner is the caller address.
 	// - locker is the bank address. Assets are locked under this address to prevent liquidity fragmentation.
-	if err := c.fungibleKeeper.LockZRC20(ctx, c.zrc20ABI, zrc20Addr, c.Address(), caller, zrc20lockerAddress, amount); err != nil {
+	if err := c.fungibleKeeper.LockZRC20(ctx, c.zrc20ABI, zrc20Addr, c.Address(), caller, bank.ContractAddress, amount); err != nil {
 		return nil, &ptypes.ErrUnexpected{
 			When: "LockZRC20InBank",
 			Got:  err.Error(),
diff --git a/precompiles/staking/staking.go b/precompiles/staking/staking.go
index dbb1b6d482..16fc2f1f98 100644
--- a/precompiles/staking/staking.go
+++ b/precompiles/staking/staking.go
@@ -88,6 +88,7 @@ func NewIStakingContract(
 	// This avoids instantiating it every time deposit or withdraw are called.
 	zrc20ABI, err := zrc20.ZRC20MetaData.GetAbi()
 	if err != nil {
+		ctx.Logger().Error("staking contract failed to get ZRC20 ABI", "error", err)
 		return nil
 	}
 
diff --git a/precompiles/types/coin.go b/precompiles/types/coin.go
index 0c95eb108b..7c0bc25317 100644
--- a/precompiles/types/coin.go
+++ b/precompiles/types/coin.go
@@ -6,14 +6,14 @@ import (
 	"cosmossdk.io/math"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	"github.com/ethereum/go-ethereum/common"
-)
 
-const ZRC20DenomPrefix = "zrc20/"
+	"github.com/zeta-chain/node/cmd/zetacored/config"
+)
 
 // ZRC20ToCosmosDenom returns the cosmos coin address for a given ZRC20 address.
 // This is converted to "zrc20/{ZRC20Address}".
 func ZRC20ToCosmosDenom(ZRC20Address common.Address) string {
-	return ZRC20DenomPrefix + ZRC20Address.String()
+	return config.ZRC20DenomPrefix + ZRC20Address.String()
 }
 
 func CreateCoinSet(tokenDenom string, amount *big.Int) (sdk.Coins, error) {
diff --git a/x/fungible/keeper/zrc20_cosmos_coins_mapping.go b/x/fungible/keeper/zrc20_cosmos_coins_mapping.go
index 140dc52aab..7ac3d7ef22 100644
--- a/x/fungible/keeper/zrc20_cosmos_coins_mapping.go
+++ b/x/fungible/keeper/zrc20_cosmos_coins_mapping.go
@@ -24,6 +24,7 @@ func (k Keeper) LockZRC20(
 	amount *big.Int,
 ) error {
 	// owner is the EOA owner of the ZRC20 tokens.
+	// spender is the EOA allowed to spend ZRC20 on owner's behalf.
 	// locker is the address that will lock the ZRC20 tokens, i.e: bank precompile.
 	if err := k.CheckZRC20Allowance(ctx, zrc20ABI, owner, spender, zrc20Address, amount); err != nil {
 		return errors.Wrap(err, "failed allowance check")