diff --git a/core/blockchain.go b/core/blockchain.go
index 6ecab669b5525bcd22ab505c7ca6e50bff733f05..4b0886f06f693850dafa4a39b9d57375d792cdb7 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -196,6 +196,8 @@ type BlockChain struct {
 	badBlocks       *lru.Cache                     // Bad block cache
 	shouldPreserve  func(*types.Block) bool        // Function used to determine whether should preserve the given block.
 	terminateInsert func(common.Hash, uint64) bool // Testing hook used to terminate ancient receipt chain insertion.
+
+	borReceiptsCache *lru.Cache // Cache for the most recent bor receipt receipts per block
 }
 
 // NewBlockChain returns a fully initialised block chain using information
@@ -219,6 +221,8 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
 	futureBlocks, _ := lru.New(maxFutureBlocks)
 	badBlocks, _ := lru.New(badBlockLimit)
 
+	borReceiptsCache, _ := lru.New(receiptsCacheLimit)
+
 	bc := &BlockChain{
 		chainConfig:    chainConfig,
 		cacheConfig:    cacheConfig,
@@ -236,6 +240,8 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
 		engine:         engine,
 		vmConfig:       vmConfig,
 		badBlocks:      badBlocks,
+
+		borReceiptsCache: borReceiptsCache,
 	}
 	bc.validator = NewBlockValidator(chainConfig, bc, engine)
 	bc.prefetcher = newStatePrefetcher(chainConfig, bc, engine)
@@ -1155,7 +1161,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
 				}
 				h := rawdb.ReadCanonicalHash(bc.db, frozen)
 				b := rawdb.ReadBlock(bc.db, h, frozen)
-				size += rawdb.WriteAncientBlock(bc.db, b, rawdb.ReadReceipts(bc.db, h, frozen, bc.chainConfig), rawdb.ReadTd(bc.db, h, frozen))
+				size += rawdb.WriteAncientBlock(bc.db, b, rawdb.ReadReceipts(bc.db, h, frozen, bc.chainConfig), rawdb.ReadTd(bc.db, h, frozen), rawdb.ReadBorReceipt(bc.db, h, frozen))
 				count += 1
 
 				// Always keep genesis block in active database.
@@ -1198,7 +1204,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
 				log.Info("Migrated ancient blocks", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
 			}
 			// Flush data into ancient database.
-			size += rawdb.WriteAncientBlock(bc.db, block, receiptChain[i], bc.GetTd(block.Hash(), block.NumberU64()))
+			size += rawdb.WriteAncientBlock(bc.db, block, receiptChain[i], bc.GetTd(block.Hash(), block.NumberU64()), bc.GetBorReceiptByHash(block.Hash()))
 
 			// Write tx indices if any condition is satisfied:
 			// * If user requires to reserve all tx indices(txlookuplimit=0)
@@ -1438,6 +1444,15 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
 	rawdb.WriteTd(blockBatch, block.Hash(), block.NumberU64(), externTd)
 	rawdb.WriteBlock(blockBatch, block)
 	rawdb.WriteReceipts(blockBatch, block.Hash(), block.NumberU64(), receipts)
+
+	// storing bor block receipt
+	blockLogs := state.Logs()
+	if len(blockLogs) > len(logs) {
+		rawdb.WriteBorReceipt(blockBatch, block.Hash(), block.NumberU64(), &types.BorReceiptForStorage{
+			Logs: blockLogs[len(logs):],
+		})
+	}
+
 	rawdb.WritePreimages(blockBatch, state.Preimages())
 	if err := blockBatch.Write(); err != nil {
 		log.Crit("Failed to write block into disk", "err", err)
diff --git a/core/bor_blockchain.go b/core/bor_blockchain.go
new file mode 100644
index 0000000000000000000000000000000000000000..21e5b73fbd19f546558eaba3f0d011d5b6498276
--- /dev/null
+++ b/core/bor_blockchain.go
@@ -0,0 +1,30 @@
+package core
+
+import (
+	"github.com/maticnetwork/bor/common"
+	"github.com/maticnetwork/bor/core/rawdb"
+	"github.com/maticnetwork/bor/core/types"
+)
+
+// GetBorReceiptByHash retrieves the bor block receipt in a given block.
+func (bc *BlockChain) GetBorReceiptByHash(hash common.Hash) *types.BorReceipt {
+	if receipt, ok := bc.borReceiptsCache.Get(hash); ok {
+		return receipt.(*types.BorReceipt)
+	}
+
+	// read header from hash
+	number := rawdb.ReadHeaderNumber(bc.db, hash)
+	if number == nil {
+		return nil
+	}
+
+	// read bor reciept by hash and number
+	receipt := rawdb.ReadBorReceipt(bc.db, hash, *number)
+	if receipt == nil {
+		return nil
+	}
+
+	// add into bor receipt cache
+	bc.borReceiptsCache.Add(hash, receipt)
+	return receipt
+}
diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go
index 1c6b213de298ecc663257cf0fe3e3ab3a3b14e6f..d863e2408814850354c685ac176dd2122036ffbd 100644
--- a/core/rawdb/accessors_chain.go
+++ b/core/rawdb/accessors_chain.go
@@ -596,7 +596,7 @@ func WriteBlock(db ethdb.KeyValueWriter, block *types.Block) {
 }
 
 // WriteAncientBlock writes entire block data into ancient store and returns the total written size.
-func WriteAncientBlock(db ethdb.AncientWriter, block *types.Block, receipts types.Receipts, td *big.Int) int {
+func WriteAncientBlock(db ethdb.AncientWriter, block *types.Block, receipts types.Receipts, td *big.Int, borReceipt *types.BorReceipt) int {
 	// Encode all block components to RLP format.
 	headerBlob, err := rlp.EncodeToBytes(block.Header())
 	if err != nil {
@@ -618,8 +618,17 @@ func WriteAncientBlock(db ethdb.AncientWriter, block *types.Block, receipts type
 	if err != nil {
 		log.Crit("Failed to RLP encode block total difficulty", "err", err)
 	}
+
+	borReceiptBlob := make([]byte, 0)
+	if borReceipt != nil {
+		borReceiptBlob, err = rlp.EncodeToBytes(borReceipt)
+		if err != nil {
+			log.Crit("Failed to RLP encode bor block receipt", "err", err)
+		}
+	}
+
 	// Write all blob to flatten files.
-	err = db.AppendAncient(block.NumberU64(), block.Hash().Bytes(), headerBlob, bodyBlob, receiptBlob, tdBlob)
+	err = db.AppendAncient(block.NumberU64(), block.Hash().Bytes(), headerBlob, bodyBlob, receiptBlob, tdBlob, borReceiptBlob)
 	if err != nil {
 		log.Crit("Failed to write block data to ancient store", "err", err)
 	}
@@ -632,6 +641,9 @@ func DeleteBlock(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
 	DeleteHeader(db, hash, number)
 	DeleteBody(db, hash, number)
 	DeleteTd(db, hash, number)
+
+	// delete bor receipt
+	DeleteBorReceipt(db, hash, number)
 }
 
 // DeleteBlockWithoutNumber removes all block data associated with a hash, except
@@ -641,6 +653,9 @@ func DeleteBlockWithoutNumber(db ethdb.KeyValueWriter, hash common.Hash, number
 	deleteHeaderWithoutNumber(db, hash, number)
 	DeleteBody(db, hash, number)
 	DeleteTd(db, hash, number)
+
+	// delete bor receipt
+	DeleteBorReceipt(db, hash, number)
 }
 
 // FindCommonAncestor returns the last common ancestor of two block headers
diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go
index 61c0925c63898cba7b6ab23a6aa03dbbe3a5036a..1b93aade8829d2f514f79dc94dbf851b33354c1f 100644
--- a/core/rawdb/accessors_chain_test.go
+++ b/core/rawdb/accessors_chain_test.go
@@ -396,7 +396,7 @@ func TestAncientStorage(t *testing.T) {
 		t.Fatalf("non existent td returned")
 	}
 	// Write and verify the header in the database
-	WriteAncientBlock(db, block, nil, big.NewInt(100))
+	WriteAncientBlock(db, block, nil, big.NewInt(100), nil)
 	if blob := ReadHeaderRLP(db, hash, number); len(blob) == 0 {
 		t.Fatalf("no header returned")
 	}
diff --git a/core/rawdb/bor_receipt.go b/core/rawdb/bor_receipt.go
new file mode 100644
index 0000000000000000000000000000000000000000..e34e0dc89b171fe2bcec7bdc8acc9dad8766abaf
--- /dev/null
+++ b/core/rawdb/bor_receipt.go
@@ -0,0 +1,133 @@
+package rawdb
+
+import (
+	"fmt"
+
+	"github.com/maticnetwork/bor/common"
+	"github.com/maticnetwork/bor/core/types"
+	"github.com/maticnetwork/bor/ethdb"
+	"github.com/maticnetwork/bor/log"
+	"github.com/maticnetwork/bor/rlp"
+)
+
+var (
+	borReceiptPrefix = []byte("bor-receipt-") // borReceiptPrefix + number + block hash -> bor block receipt
+
+	// freezerReceiptTable indicates the name of the freezer bor receipts table.
+	freezerBorReceiptTable = "bor-receipts"
+)
+
+// borReceiptKey = borReceiptPrefix + num (uint64 big endian) + hash
+func borReceiptKey(number uint64, hash common.Hash) []byte {
+	return append(append(borReceiptPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
+}
+
+// HasBorReceipt verifies the existence of all block receipt belonging
+// to a block.
+func HasBorReceipt(db ethdb.Reader, hash common.Hash, number uint64) bool {
+	if has, err := db.Ancient(freezerHashTable, number); err == nil && common.BytesToHash(has) == hash {
+		return true
+	}
+
+	if has, err := db.Has(borReceiptKey(number, hash)); !has || err != nil {
+		return false
+	}
+
+	return true
+}
+
+// ReadBorReceiptRLP retrieves all the transaction receipts belonging to a block in RLP encoding.
+func ReadBorReceiptRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
+	// First try to look up the data in ancient database. Extra hash
+	// comparison is necessary since ancient database only maintains
+	// the canonical data.
+	data, _ := db.Ancient(freezerBorReceiptTable, number)
+	if len(data) > 0 {
+		h, _ := db.Ancient(freezerHashTable, number)
+		if common.BytesToHash(h) == hash {
+			return data
+		}
+	}
+	// Then try to look up the data in leveldb.
+	data, _ = db.Get(borReceiptKey(number, hash))
+	if len(data) > 0 {
+		fmt.Println("==> RAWDB IN ReadBorReceiptRLP", common.Bytes2Hex(data))
+		return data
+	}
+	// In the background freezer is moving data from leveldb to flatten files.
+	// So during the first check for ancient db, the data is not yet in there,
+	// but when we reach into leveldb, the data was already moved. That would
+	// result in a not found error.
+	data, _ = db.Ancient(freezerReceiptTable, number)
+	if len(data) > 0 {
+		h, _ := db.Ancient(freezerHashTable, number)
+		if common.BytesToHash(h) == hash {
+			return data
+		}
+	}
+	return nil // Can't find the data anywhere.
+}
+
+// ReadRawBorReceipt retrieves the block receipt belonging to a block.
+// The receipt metadata fields are not guaranteed to be populated, so they
+// should not be used. Use ReadBorReceipt instead if the metadata is needed.
+func ReadRawBorReceipt(db ethdb.Reader, hash common.Hash, number uint64) *types.BorReceipt {
+	// Retrieve the flattened receipt slice
+	data := ReadBorReceiptRLP(db, hash, number)
+	if data == nil || len(data) == 0 {
+		return nil
+	}
+
+	// Convert the receipts from their storage form to their internal representation
+	var storageReceipt types.BorReceiptForStorage
+	if err := rlp.DecodeBytes(data, &storageReceipt); err != nil {
+		log.Error("Invalid receipt array RLP", "hash", hash, "err", err)
+		return nil
+	}
+
+	return (*types.BorReceipt)(&storageReceipt)
+}
+
+// ReadBorReceipt retrieves all the bor block receipts belonging to a block, including
+// its correspoinding metadata fields. If it is unable to populate these metadata
+// fields then nil is returned.
+func ReadBorReceipt(db ethdb.Reader, hash common.Hash, number uint64) *types.BorReceipt {
+	// We're deriving many fields from the block body, retrieve beside the receipt
+	borReceipt := ReadRawBorReceipt(db, hash, number)
+	if borReceipt == nil {
+		return nil
+	}
+
+	body := ReadBody(db, hash, number)
+	if body == nil {
+		log.Error("Missing body but have bor receipt", "hash", hash, "number", number)
+		return nil
+	}
+
+	if err := borReceipt.DeriveFields(hash, number); err != nil {
+		log.Error("Failed to derive bor receipt fields", "hash", hash, "number", number, "err", err)
+		return nil
+	}
+	return borReceipt
+}
+
+// WriteBorReceipt stores all the bor receipt belonging to a block.
+func WriteBorReceipt(db ethdb.KeyValueWriter, hash common.Hash, number uint64, borReceipt *types.BorReceiptForStorage) {
+	// Convert the bor receipt into their storage form and serialize them
+	bytes, err := rlp.EncodeToBytes(borReceipt)
+	if err != nil {
+		log.Crit("Failed to encode bor receipt", "err", err)
+	}
+
+	// Store the flattened receipt slice
+	if err := db.Put(borReceiptKey(number, hash), bytes); err != nil {
+		log.Crit("Failed to store bor receipt", "err", err)
+	}
+}
+
+// DeleteBorReceipt removes receipt data associated with a block hash.
+func DeleteBorReceipt(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
+	if err := db.Delete(borReceiptKey(number, hash)); err != nil {
+		log.Crit("Failed to delete bor receipt", "err", err)
+	}
+}
diff --git a/core/rawdb/database.go b/core/rawdb/database.go
index b9829d236e9fdbd3b8cf27ca56df0c250f06f63b..1375f5b349acf308aac0473eeb44b6ff18a24522 100644
--- a/core/rawdb/database.go
+++ b/core/rawdb/database.go
@@ -79,7 +79,7 @@ func (db *nofreezedb) AncientSize(kind string) (uint64, error) {
 }
 
 // AppendAncient returns an error as we don't have a backing chain freezer.
-func (db *nofreezedb) AppendAncient(number uint64, hash, header, body, receipts, td []byte) error {
+func (db *nofreezedb) AppendAncient(number uint64, hash, header, body, receipts, td, borBlockReceipt []byte) error {
 	return errNotSupported
 }
 
diff --git a/core/rawdb/freezer.go b/core/rawdb/freezer.go
index 42bb69d70d64131f94edcb670d7632cc14b8d528..3690dde9af56f9f70e9e06de5b38b8372d17bcd8 100644
--- a/core/rawdb/freezer.go
+++ b/core/rawdb/freezer.go
@@ -180,7 +180,7 @@ func (f *freezer) AncientSize(kind string) (uint64, error) {
 // Notably, this function is lock free but kind of thread-safe. All out-of-order
 // injection will be rejected. But if two injections with same number happen at
 // the same time, we can get into the trouble.
-func (f *freezer) AppendAncient(number uint64, hash, header, body, receipts, td []byte) (err error) {
+func (f *freezer) AppendAncient(number uint64, hash, header, body, receipts, td, borBlockReceipt []byte) (err error) {
 	// Ensure the binary blobs we are appending is continuous with freezer.
 	if atomic.LoadUint64(&f.frozen) != number {
 		return errOutOrderInsertion
@@ -217,6 +217,11 @@ func (f *freezer) AppendAncient(number uint64, hash, header, body, receipts, td
 		log.Error("Failed to append ancient difficulty", "number", f.frozen, "hash", hash, "err", err)
 		return err
 	}
+	if err := f.tables[freezerBorReceiptTable].Append(f.frozen, borBlockReceipt); err != nil {
+		log.Error("Failed to append bor block receipt", "number", f.frozen, "hash", hash, "err", err)
+		return err
+	}
+
 	atomic.AddUint64(&f.frozen, 1) // Only modify atomically
 	return nil
 }
@@ -340,9 +345,13 @@ func (f *freezer) freeze(db ethdb.KeyValueStore) {
 				log.Error("Total difficulty missing, can't freeze", "number", f.frozen, "hash", hash)
 				break
 			}
+
+			// bor block receipt
+			borBlockReceipt := ReadBorReceiptRLP(nfdb, hash, f.frozen)
+
 			log.Trace("Deep froze ancient block", "number", f.frozen, "hash", hash)
 			// Inject all the components into the relevant data tables
-			if err := f.AppendAncient(f.frozen, hash[:], header, body, receipts, td); err != nil {
+			if err := f.AppendAncient(f.frozen, hash[:], header, body, receipts, td, borBlockReceipt); err != nil {
 				break
 			}
 			ancients = append(ancients, hash)
diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go
index 9157c97a626b785bcbd7c85ec6358c53d64a4d29..4def3c77e87268dc25764b9bc5b892c3fc65bceb 100644
--- a/core/rawdb/schema.go
+++ b/core/rawdb/schema.go
@@ -101,6 +101,7 @@ var freezerNoSnappy = map[string]bool{
 	freezerHashTable:       true,
 	freezerBodiesTable:     false,
 	freezerReceiptTable:    false,
+	freezerBorReceiptTable: false,
 	freezerDifficultyTable: true,
 }
 
diff --git a/core/rawdb/table.go b/core/rawdb/table.go
index 428373c4705a2457f8e40316b2bfbc7eb9642e63..ca1689eb51ae57bcf1420c04df2a7ec93c4a4031 100644
--- a/core/rawdb/table.go
+++ b/core/rawdb/table.go
@@ -76,8 +76,8 @@ func (t *table) AncientSize(kind string) (uint64, error) {
 
 // AppendAncient is a noop passthrough that just forwards the request to the underlying
 // database.
-func (t *table) AppendAncient(number uint64, hash, header, body, receipts, td []byte) error {
-	return t.db.AppendAncient(number, hash, header, body, receipts, td)
+func (t *table) AppendAncient(number uint64, hash, header, body, receipts, td, borBlockReceipt []byte) error {
+	return t.db.AppendAncient(number, hash, header, body, receipts, td, borBlockReceipt)
 }
 
 // TruncateAncients is a noop passthrough that just forwards the request to the underlying
diff --git a/core/types/bor_receipt.go b/core/types/bor_receipt.go
new file mode 100644
index 0000000000000000000000000000000000000000..a62895e319d58bbb1236a333e4346e301ce270f7
--- /dev/null
+++ b/core/types/bor_receipt.go
@@ -0,0 +1,110 @@
+package types
+
+import (
+	"io"
+	"math/big"
+
+	"github.com/maticnetwork/bor/common"
+	"github.com/maticnetwork/bor/crypto"
+	"github.com/maticnetwork/bor/rlp"
+)
+
+// BorReceipt represents the results of a block state syncs
+type BorReceipt struct {
+	// Consensus fields
+	Bloom Bloom  `json:"logsBloom"         gencodec:"required"`
+	Logs  []*Log `json:"logs"              gencodec:"required"`
+
+	// Inclusion information: These fields provide information about the inclusion of the
+	// transaction corresponding to this receipt.
+	BlockHash   common.Hash `json:"blockHash,omitempty"`
+	BlockNumber *big.Int    `json:"blockNumber,omitempty"`
+}
+
+// borReceiptRLP is the consensus encoding of a block receipt.
+type borReceiptRLP struct {
+	Bloom Bloom
+	Logs  []*Log
+}
+
+// storedBorReceiptRLP is the storage encoding of a block receipt.
+type storedBorReceiptRLP struct {
+	Logs []*LogForStorage
+}
+
+// EncodeRLP implements rlp.Encoder, and flattens the consensus fields of a block receipt
+// into an RLP stream. If no post state is present, byzantium fork is assumed.
+func (r *BorReceipt) EncodeRLP(w io.Writer) error {
+	return rlp.Encode(w, &borReceiptRLP{r.Bloom, r.Logs})
+}
+
+// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a block receipt
+// from an RLP stream.
+func (r *BorReceipt) DecodeRLP(s *rlp.Stream) error {
+	var dec receiptRLP
+	if err := s.Decode(&dec); err != nil {
+		return err
+	}
+	r.Bloom, r.Logs = dec.Bloom, dec.Logs
+	return nil
+}
+
+// BorReceiptForStorage is a wrapper around a Bor Receipt that flattens and parses the
+// entire content of a receipt, as opposed to only the consensus fields originally.
+type BorReceiptForStorage BorReceipt
+
+// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt
+// into an RLP stream.
+func (r *BorReceiptForStorage) EncodeRLP(w io.Writer) error {
+	enc := &storedBorReceiptRLP{
+		Logs: make([]*LogForStorage, len(r.Logs)),
+	}
+
+	for i, log := range r.Logs {
+		enc.Logs[i] = (*LogForStorage)(log)
+	}
+	return rlp.Encode(w, enc)
+}
+
+// DecodeRLP implements rlp.Decoder, and loads both consensus and implementation
+// fields of a receipt from an RLP stream.
+func (r *BorReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
+	// Retrieve the entire receipt blob as we need to try multiple decoders
+	blob, err := s.Raw()
+	if err != nil {
+		return err
+	}
+
+	return decodeStoredBorReceiptRLP(r, blob)
+}
+
+func decodeStoredBorReceiptRLP(r *BorReceiptForStorage, blob []byte) error {
+	var stored storedBorReceiptRLP
+	if err := rlp.DecodeBytes(blob, &stored); err != nil {
+		return err
+	}
+
+	r.Logs = make([]*Log, len(stored.Logs))
+	for i, log := range stored.Logs {
+		r.Logs[i] = (*Log)(log)
+	}
+	r.Bloom = BytesToBloom(LogsBloom(r.Logs).Bytes())
+
+	return nil
+}
+
+// DeriveFields fills the receipts with their computed fields based on consensus
+// data and contextual infos like containing block and transactions.
+func (r *BorReceipt) DeriveFields(hash common.Hash, number uint64) error {
+	txHash := common.BytesToHash(crypto.Keccak256(append([]byte("matic-receipt-"), hash.Bytes()...)))
+
+	// The derived log fields can simply be set from the block and transaction
+	for j := 0; j < len(r.Logs); j++ {
+		r.Logs[j].BlockNumber = number
+		r.Logs[j].BlockHash = hash
+		r.Logs[j].TxHash = txHash
+		r.Logs[j].TxIndex = uint(0)
+		r.Logs[j].Index = uint(j)
+	}
+	return nil
+}
diff --git a/eth/api_backend.go b/eth/api_backend.go
index 6f50814ccdc02095ff56cc4044bba0a4bd4ca5a3..31e38ab78c625efd972467640c783cf247df61ba 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -23,7 +23,6 @@ import (
 
 	"github.com/maticnetwork/bor/accounts"
 	"github.com/maticnetwork/bor/common"
-	"github.com/maticnetwork/bor/consensus/bor"
 	"github.com/maticnetwork/bor/core"
 	"github.com/maticnetwork/bor/core/bloombits"
 	"github.com/maticnetwork/bor/core/rawdb"
@@ -312,17 +311,3 @@ func (b *EthAPIBackend) ServiceFilter(ctx context.Context, session *bloombits.Ma
 		go session.Multiplex(bloomRetrievalBatch, bloomRetrievalWait, b.eth.bloomRequests)
 	}
 }
-
-func (b *EthAPIBackend) GetRootHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64) (string, error) {
-	var api *bor.API
-	for _, _api := range b.eth.Engine().APIs(b.eth.BlockChain()) {
-		if _api.Namespace == "bor" {
-			api = _api.Service.(*bor.API)
-		}
-	}
-	root, err := api.GetRootHash(starBlockNr, endBlockNr)
-	if err != nil {
-		return "", err
-	}
-	return root, nil
-}
diff --git a/eth/bor_api_backend.go b/eth/bor_api_backend.go
new file mode 100644
index 0000000000000000000000000000000000000000..ac2c4ec7312e82517b7d2f8c1f9ebc72ad43c6db
--- /dev/null
+++ b/eth/bor_api_backend.go
@@ -0,0 +1,46 @@
+package eth
+
+import (
+	"context"
+
+	ethereum "github.com/maticnetwork/bor"
+	"github.com/maticnetwork/bor/common"
+	"github.com/maticnetwork/bor/consensus/bor"
+	"github.com/maticnetwork/bor/core/types"
+)
+
+func (b *EthAPIBackend) GetRootHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64) (string, error) {
+	var api *bor.API
+	for _, _api := range b.eth.Engine().APIs(b.eth.BlockChain()) {
+		if _api.Namespace == "bor" {
+			api = _api.Service.(*bor.API)
+		}
+	}
+
+	root, err := api.GetRootHash(starBlockNr, endBlockNr)
+	if err != nil {
+		return "", err
+	}
+	return root, nil
+}
+
+func (b *EthAPIBackend) GetBorBlockReceipt(ctx context.Context, hash common.Hash) (*types.BorReceipt, error) {
+	receipt := b.eth.blockchain.GetBorReceiptByHash(hash)
+	if receipt == nil {
+		return nil, ethereum.NotFound
+	}
+
+	return receipt, nil
+}
+
+// func (b *EthAPIBackend) GetBorBlockLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) {
+// 	receipts := b.eth.blockchain.GetReceiptsByHash(hash)
+// 	if receipts == nil {
+// 		return nil, nil
+// 	}
+// 	logs := make([][]*types.Log, len(receipts))
+// 	for i, receipt := range receipts {
+// 		logs[i] = receipt.Logs
+// 	}
+// 	return logs, nil
+// }
diff --git a/ethclient/bor_ethclient.go b/ethclient/bor_ethclient.go
new file mode 100644
index 0000000000000000000000000000000000000000..4b1a01a26a657e917d316b18096eac039451264b
--- /dev/null
+++ b/ethclient/bor_ethclient.go
@@ -0,0 +1,30 @@
+package ethclient
+
+import (
+	"context"
+
+	ethereum "github.com/maticnetwork/bor"
+	"github.com/maticnetwork/bor/common"
+	"github.com/maticnetwork/bor/core/types"
+)
+
+// GetRootHash returns the merkle root of the block headers
+func (ec *Client) GetRootHash(ctx context.Context, startBlockNumber uint64, endBlockNumber uint64) (string, error) {
+	var rootHash string
+	if err := ec.c.CallContext(ctx, &rootHash, "eth_getRootHash", startBlockNumber, endBlockNumber); err != nil {
+		return "", err
+	}
+	return rootHash, nil
+}
+
+// GetBorBlockReceipt returns bor block receipt
+func (ec *Client) GetBorBlockReceipt(ctx context.Context, hash common.Hash) (*types.BorReceipt, error) {
+	var r *types.BorReceipt
+	err := ec.c.CallContext(ctx, &r, "eth_getBorBlockReceipt", hash)
+	if err == nil {
+		if r == nil {
+			return nil, ethereum.NotFound
+		}
+	}
+	return r, err
+}
diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go
index 78b58e7a1515305d0ae36db5ea6c789a4c4b4c09..44fbd10de46f05839f588bb8f29db9b31b72644a 100644
--- a/ethclient/ethclient.go
+++ b/ethclient/ethclient.go
@@ -522,15 +522,6 @@ func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) er
 	return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", hexutil.Encode(data))
 }
 
-// GetRootHash returns the merkle root of the block headers
-func (ec *Client) GetRootHash(ctx context.Context, startBlockNumber uint64, endBlockNumber uint64) (string, error) {
-	var rootHash string
-	if err := ec.c.CallContext(ctx, &rootHash, "eth_getRootHash", startBlockNumber, endBlockNumber); err != nil {
-		return "", err
-	}
-	return rootHash, nil
-}
-
 func toCallArg(msg ethereum.CallMsg) interface{} {
 	arg := map[string]interface{}{
 		"from": msg.From,
diff --git a/ethdb/database.go b/ethdb/database.go
index 0dc14624b98e6ed7a43c2648064071bae66a5d6d..e90f023fbdfc06c92c05b425d7ba5e07f06608ad 100644
--- a/ethdb/database.go
+++ b/ethdb/database.go
@@ -87,7 +87,7 @@ type AncientReader interface {
 type AncientWriter interface {
 	// AppendAncient injects all binary blobs belong to block at the end of the
 	// append-only immutable table files.
-	AppendAncient(number uint64, hash, header, body, receipt, td []byte) error
+	AppendAncient(number uint64, hash, header, body, receipt, td, borBlockReceipt []byte) error
 
 	// TruncateAncients discards all but the first n ancient data from the ancient store.
 	TruncateAncients(n uint64) error
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index f329e2636b20d1d9fd92684f1dc688f952e7cc65..09a3e0c473ee2ff9e862bdfe8db3242fefb56efe 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -748,14 +748,6 @@ func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.A
 	return res[:], state.Error()
 }
 
-func (s *PublicBlockChainAPI) GetRootHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64) (string, error) {
-	root, err := s.b.GetRootHash(ctx, starBlockNr, endBlockNr)
-	if err != nil {
-		return "", err
-	}
-	return root, nil
-}
-
 // CallArgs represents the arguments for a call.
 type CallArgs struct {
 	From     *common.Address `json:"from"`
diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go
index 0a1a606ef8411598969c4c889927cec85b3cee8d..272aea858ec040de8b1f0978bb405e6f44beb309 100644
--- a/internal/ethapi/backend.go
+++ b/internal/ethapi/backend.go
@@ -65,7 +65,10 @@ type Backend interface {
 	SubscribeStateSyncEvent(ch chan<- core.StateSyncEvent) event.Subscription
 	SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
 	SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription
+
+	// Bor API
 	GetRootHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64) (string, error)
+	GetBorBlockReceipt(ctx context.Context, hash common.Hash) (*types.BorReceipt, error)
 
 	// Transaction pool API
 	SendTx(ctx context.Context, signedTx *types.Transaction) error
diff --git a/internal/ethapi/bor_api.go b/internal/ethapi/bor_api.go
new file mode 100644
index 0000000000000000000000000000000000000000..3cf1347a4839c013a83665aa8d14fe727851aa80
--- /dev/null
+++ b/internal/ethapi/bor_api.go
@@ -0,0 +1,20 @@
+package ethapi
+
+import (
+	"context"
+
+	"github.com/maticnetwork/bor/common"
+	"github.com/maticnetwork/bor/core/types"
+)
+
+func (s *PublicBlockChainAPI) GetRootHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64) (string, error) {
+	root, err := s.b.GetRootHash(ctx, starBlockNr, endBlockNr)
+	if err != nil {
+		return "", err
+	}
+	return root, nil
+}
+
+func (s *PublicBlockChainAPI) GetBorBlockReceipt(ctx context.Context, hash common.Hash) (*types.BorReceipt, error) {
+	return s.b.GetBorBlockReceipt(ctx, hash)
+}
diff --git a/les/api_backend.go b/les/api_backend.go
index 46c348eb500717028ca81174a616cc5d8ce1d4c0..86dc91ea205ae608efa37eb2323be9ea0e09cd01 100644
--- a/les/api_backend.go
+++ b/les/api_backend.go
@@ -287,3 +287,7 @@ func (b *LesApiBackend) ServiceFilter(ctx context.Context, session *bloombits.Ma
 func (b *LesApiBackend) GetRootHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64) (string, error) {
 	return "", errors.New("Not implemented")
 }
+
+func (b *LesApiBackend) GetBorBlockReceipt(ctx context.Context, hash common.Hash) (*types.BorReceipt, error) {
+	return nil, errors.New("Not implemented")
+}