--- a/net/minecraft/world/entity/animal/frog/Tadpole.java
+++ b/net/minecraft/world/entity/animal/frog/Tadpole.java
@@ -46,7 +_,7 @@
     public static int ticksToBeFrog = Math.abs(-24000);
     public static final float HITBOX_WIDTH = 0.4F;
     public static final float HITBOX_HEIGHT = 0.3F;
-    private int age = 0;
+    public int age = 0;
     protected static final ImmutableList<SensorType<? extends Sensor<? super Tadpole>>> SENSOR_TYPES = ImmutableList.of(
         SensorType.NEAREST_LIVING_ENTITIES, SensorType.NEAREST_PLAYERS, SensorType.HURT_BY, SensorType.FROG_TEMPTATIONS
     );
@@ -63,6 +_,7 @@
         MemoryModuleType.BREED_TARGET,
         MemoryModuleType.IS_PANICKING
     );
+    public boolean ageLocked; // Paper
 
     public Tadpole(EntityType<? extends AbstractFish> entityType, Level level) {
         super(entityType, level);
@@ -114,7 +_,7 @@
     @Override
     public void aiStep() {
         super.aiStep();
-        if (!this.level().isClientSide) {
+        if (!this.level().isClientSide && !this.ageLocked) { // Paper
             this.setAge(this.age + 1);
         }
     }
@@ -123,29 +_,31 @@
     public void addAdditionalSaveData(CompoundTag compound) {
         super.addAdditionalSaveData(compound);
         compound.putInt("Age", this.age);
+        compound.putBoolean("AgeLocked", this.ageLocked); // Paper
     }
 
     @Override
     public void readAdditionalSaveData(CompoundTag compound) {
         super.readAdditionalSaveData(compound);
         this.setAge(compound.getIntOr("Age", 0));
+        this.ageLocked = compound.getBooleanOr("AgeLocked", false); // Paper
     }
 
     @Nullable
     @Override
-    protected SoundEvent getAmbientSound() {
+    public SoundEvent getAmbientSound() {
         return null;
     }
 
     @Nullable
     @Override
-    protected SoundEvent getHurtSound(DamageSource damageSource) {
+    public SoundEvent getHurtSound(DamageSource damageSource) {
         return SoundEvents.TADPOLE_HURT;
     }
 
     @Nullable
     @Override
-    protected SoundEvent getDeathSound() {
+    public SoundEvent getDeathSound() {
         return SoundEvents.TADPOLE_DEATH;
     }
 
@@ -178,13 +_,19 @@
     @Override
     public void saveToBucketTag(ItemStack stack) {
         Bucketable.saveDefaultDataToBucketTag(this, stack);
-        CustomData.update(DataComponents.BUCKET_ENTITY_DATA, stack, compoundTag -> compoundTag.putInt("Age", this.getAge()));
+        // Paper start - Save tadpole age
+        CustomData.update(DataComponents.BUCKET_ENTITY_DATA, stack, compoundTag -> {
+            compoundTag.putInt("Age", this.getAge());
+            compoundTag.putBoolean("AgeLocked", this.ageLocked);
+        });
+        // Paper end - Save tadpole age
     }
 
     @Override
     public void loadFromBucketTag(CompoundTag tag) {
         Bucketable.loadDefaultDataFromBucketTag(this, tag);
         tag.getInt("Age").ifPresent(this::setAge);
+        this.ageLocked = tag.getBooleanOr("AgeLocked", false); // Paper
     }
 
     @Override
@@ -216,6 +_,7 @@
     }
 
     private void ageUp(int offset) {
+        if (this.ageLocked) return; // Paper
         this.setAge(this.age + offset * 20);
     }
 
@@ -228,12 +_,17 @@
 
     private void ageUp() {
         if (this.level() instanceof ServerLevel serverLevel) {
-            this.convertTo(EntityType.FROG, ConversionParams.single(this, false, false), mob -> {
+            Frog converted = this.convertTo(EntityType.FROG, ConversionParams.single(this, false, false), mob -> { // CraftBukkit
                 mob.finalizeSpawn(serverLevel, this.level().getCurrentDifficultyAt(mob.blockPosition()), EntitySpawnReason.CONVERSION, null);
                 mob.setPersistenceRequired();
                 mob.fudgePositionAfterSizeChange(this.getDimensions(this.getPose()));
                 this.playSound(SoundEvents.TADPOLE_GROW_UP, 0.15F, 1.0F);
-            });
+            // CraftBukkit start
+            }, org.bukkit.event.entity.EntityTransformEvent.TransformReason.METAMORPHOSIS, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.METAMORPHOSIS);
+            if (converted == null) {
+                this.setAge(0); // Sets the age to 0 for avoid a loop if the event is canceled
+            }
+            // CraftBukkit end
         }
     }
 
