set_option maxHeartbeats 300000 in
-- TODO: Generalize to `FunLike` classes,
/-- Lift a monoid with zero homomorphism `R[X] →*₀ G₀` to a `RatFunc R →*₀ G₀`
on the condition that `φ` maps non zero divisors to non zero divisors,
by mapping both the numerator and denominator and quotienting them. -/
def liftMonoidWithZeroHom (φ : R[X] →*₀ G₀) (hφ : R[X]⁰ ≤ G₀⁰.comap φ) : RatFunc R →*₀ G₀ where
  toFun f :=
    RatFunc.liftOn f (fun p q => φ p / φ q) fun {p q p' q'} hq hq' h => by
      cases subsingleton_or_nontrivial R
      · rw [Subsingleton.elim p q, Subsingleton.elim p' q, Subsingleton.elim q' q]
      rw [div_eq_div_iff, ← map_mul, mul_comm p, h, map_mul, mul_comm] <;>
        exact nonZeroDivisors.ne_zero (hφ ‹_›)
  map_one' := by
    dsimp only -- porting note: force the function to be applied
    rw [← ofFractionRing_one, ← Localization.mk_one, liftOn_ofFractionRing_mk]
    simp only [map_one, OneMemClass.coe_one, div_one]
  map_mul' x y := by
    cases' x with x
    cases' y with y
    induction' x using Localization.rec with p q
    induction' y using Localization.rec with p' q'
    · 

Proof state:
case ofFractionRing.ofFractionRing.f.f
K : Type u
inst✝⁴ : CommRing K
G₀ : Type u_1
L : Type u_2
R : Type u_3
S : Type u_4
F : Type u_5
inst✝³ : CommGroupWithZero G₀
inst✝² : Field L
inst✝¹ : CommRing R
inst✝ : CommRing S
φ : R[X] →*₀ G₀
hφ : R[X]⁰ ≤ Submonoid.comap φ G₀⁰
p : R[X]
q : ↥R[X]⁰
p' : R[X]
q' : ↥R[X]⁰
⊢ ZeroHom.toFun
      {
        toFun := fun f =>
          RatFunc.liftOn f (fun p q => φ p / φ q)
            (_ :
              ∀ {p q p' q' : R[X]},
                q ∈ R[X]⁰ → q' ∈ R[X]⁰ → q' * p = q * p' → (fun p q => φ p / φ q) p q = (fun p q => φ p / φ q) p' q'),
        map_zero' :=
          (_ :
            (fun f =>
                  RatFunc.liftOn f (fun p q => φ p / φ q)
                    (_ :
                      ∀ {p q p' q' : R[X]},
                        q ∈ R[X]⁰ →
                          q' ∈ R[X]⁰ → q' * p = q * p' → (fun p q => φ p / φ q) p q = (fun p q => φ p / φ q) p' q'))
                0 =
              0) }
      ({ toFractionRing := Localization.mk p q } * { toFractionRing := Localization.mk p' q' }) =
    ZeroHom.toFun
        {
          toFun := fun f =>
            RatFunc.liftOn f (fun p q => φ p / φ q)
              (_ :
                ∀ {p q p' q' : R[X]},
                  q ∈ R[X]⁰ → q' ∈ R[X]⁰ → q' * p = q * p' → (fun p q => φ p / φ q) p q = (fun p q => φ p / φ q) p' q'),
          map_zero' :=
            (_ :
              (fun f =>
                    RatFunc.liftOn f (fun p q => φ p / φ q)
                      (_ :
                        ∀ {p q p' q' : R[X]},
                          q ∈ R[X]⁰ →
                            q' ∈ R[X]⁰ → q' * p = q * p' → (fun p q => φ p / φ q) p q = (fun p q => φ p / φ q) p' q'))
                  0 =
                0) }
        { toFractionRing := Localization.mk p q } *
      ZeroHom.toFun
        {
          toFun := fun f =>
            RatFunc.liftOn f (fun p q => φ p / φ q)
              (_ :
                ∀ {p q p' q' : R[X]},
                  q ∈ R[X]⁰ → q' ∈ R[X]⁰ → q' * p = q * p' → (fun p q => φ p / φ q) p q = (fun p q => φ p / φ q) p' q'),
          map_zero' :=
            (_ :
              (fun f =>
                    RatFunc.liftOn f (fun p q => φ p / φ q)
                      (_ :
                        ∀ {p q p' q' : R[X]},
                          q ∈ R[X]⁰ →
                            q' ∈ R[X]⁰ → q' * p = q * p' → (fun p q => φ p / φ q) p q = (fun p q => φ p / φ q) p' q'))
                  0 =
                0) }
        { toFractionRing := Localization.mk p' q' }

Next tactic:
rw [← ofFractionRing_mul, Localization.mk_mul]