diff --git a/container/config.yml b/container/config.yml
index 735f9de..ccde0be 100644
--- a/container/config.yml
+++ b/container/config.yml
@@ -2,6 +2,7 @@ name: "Container"
 safemethods:
   - "count"
   - "containersOf"
+  - "deletionInfo"
   - "eACL"
   - "get"
   - "getContainerSize"
diff --git a/container/container_contract.go b/container/container_contract.go
index 5cde579..68d5b8f 100644
--- a/container/container_contract.go
+++ b/container/container_contract.go
@@ -64,6 +64,7 @@ const (
 	estimateKeyPrefix    = "cnr"
 	containerKeyPrefix   = 'x'
 	ownerKeyPrefix       = 'o'
+	graveKeyPrefix       = 'g'
 	estimatePostfixSize  = 10
 	// CleanupDelta contains the number of the last epochs for which container estimations are present.
 	CleanupDelta = 3
@@ -337,6 +338,25 @@ func Delete(containerID []byte, signature interop.Signature, publicKey interop.P
 	runtime.Notify("DeleteSuccess", containerID)
 }
 
+type DelInfo struct {
+	Owner interop.Hash160
+	Epoch int
+}
+
+// DeletionInfo method returns container deletion info.
+// If the container had never existed, NotFoundError is throwed.
+// It can be used to check whether non-existing container was indeed deleted
+// or does not yet exist at some height.
+func DeletionInfo(containerID []byte) DelInfo {
+	ctx := storage.GetReadOnlyContext()
+	graveKey := append([]byte{graveKeyPrefix}, containerID...)
+	data := storage.Get(ctx, graveKey).([]byte)
+	if data == nil {
+		panic(NotFoundError)
+	}
+	return std.Deserialize(data).(DelInfo)
+}
+
 // Get method returns a structure that contains a stable marshaled Container structure,
 // the signature, the public key of the container creator and a stable marshaled SessionToken
 // structure if it was provided.
@@ -596,6 +616,9 @@ func addContainer(ctx storage.Context, id, owner []byte, container Container) {
 
 	idKey := append([]byte{containerKeyPrefix}, id...)
 	common.SetSerialized(ctx, idKey, container)
+
+	graveKey := append([]byte{graveKeyPrefix}, id...)
+	storage.Delete(ctx, graveKey)
 }
 
 func removeContainer(ctx storage.Context, id []byte, owner []byte) {
@@ -604,6 +627,14 @@ func removeContainer(ctx storage.Context, id []byte, owner []byte) {
 	storage.Delete(ctx, containerListKey)
 
 	storage.Delete(ctx, append([]byte{containerKeyPrefix}, id...))
+
+	graveKey := append([]byte{graveKeyPrefix}, id...)
+	netmapContractAddr := storage.Get(ctx, netmapContractKey).(interop.Hash160)
+	epoch := contract.Call(netmapContractAddr, "epoch", contract.ReadOnly).(int)
+	common.SetSerialized(ctx, graveKey, DelInfo{
+		Owner: owner,
+		Epoch: epoch,
+	})
 }
 
 func getAllContainers(ctx storage.Context) [][]byte {
diff --git a/tests/container_test.go b/tests/container_test.go
index 70c6bdc..aa98f13 100644
--- a/tests/container_test.go
+++ b/tests/container_test.go
@@ -243,19 +243,43 @@ func addContainer(t *testing.T, c, cBal *neotest.ContractInvoker) (neotest.Signe
 }
 
 func TestContainerDelete(t *testing.T) {
-	c, cBal, _ := newContainerInvoker(t)
+	c, cBal, cNm := newContainerInvoker(t)
 
 	acc, cnt := addContainer(t, c, cBal)
 	cAcc := c.WithSigners(acc)
 	cAcc.InvokeFail(t, common.ErrAlphabetWitnessFailed, "delete",
 		cnt.id[:], cnt.sig, cnt.pub, cnt.token)
 
+	newDelInfo := func(acc neotest.Signer, epoch int64) *stackitem.Struct {
+		return stackitem.NewStruct([]stackitem.Item{
+			stackitem.NewBuffer([]byte(signerToOwner(acc))),
+			stackitem.NewBigInteger(big.NewInt(epoch)),
+		})
+	}
+
+	c.InvokeFail(t, container.NotFoundError, "deletionInfo", cnt.id[:])
 	c.Invoke(t, stackitem.Null{}, "delete", cnt.id[:], cnt.sig, cnt.pub, cnt.token)
+	c.Invoke(t, newDelInfo(acc, 0), "deletionInfo", cnt.id[:])
+
+	t.Run("multi-epoch", func(t *testing.T) {
+		cNm.Invoke(t, stackitem.Null{}, "newEpoch", 1)
+
+		t.Run("epoch tick does not change deletion info", func(t *testing.T) {
+			c.Invoke(t, stackitem.Null{}, "delete", cnt.id[:], cnt.sig, cnt.pub, cnt.token)
+			c.Invoke(t, newDelInfo(acc, 0), "deletionInfo", cnt.id[:])
+		})
+
+		acc1, cnt1 := addContainer(t, c, cBal)
+		c.Invoke(t, stackitem.Null{}, "delete", cnt1.id[:], cnt1.sig, cnt1.pub, cnt1.token)
+		c.Invoke(t, newDelInfo(acc, 0), "deletionInfo", cnt.id[:])
+		c.Invoke(t, newDelInfo(acc1, 1), "deletionInfo", cnt1.id[:])
+	})
 
 	t.Run("missing container", func(t *testing.T) {
 		id := cnt.id
 		id[0] ^= 0xFF
 		c.Invoke(t, stackitem.Null{}, "delete", cnt.id[:], cnt.sig, cnt.pub, cnt.token)
+		c.InvokeFail(t, container.NotFoundError, "deletionInfo", id[:])
 	})
 
 	c.InvokeFail(t, container.NotFoundError, "get", cnt.id[:])