diff --git a/CRM/Core/BAO/MessageTemplate.php b/CRM/Core/BAO/MessageTemplate.php
index 2b217a834bcc..44267824ccee 100644
--- a/CRM/Core/BAO/MessageTemplate.php
+++ b/CRM/Core/BAO/MessageTemplate.php
@@ -9,6 +9,8 @@
  +--------------------------------------------------------------------+
  */
 
+use Civi\Token\TokenProcessor;
+
 /**
  *
  * @package CRM
@@ -425,7 +427,7 @@ public static function sendTemplate($params) {
       $mailContent['subject'] = $params['subject'];
     }
 
-    $mailContent = self::renderMessageTemplate($mailContent, $params['disableSmarty'], $params['contactId'] ?? NULL, $params['tplParams']);
+    $mailContent = self::renderMessageTemplate($mailContent, (bool) $params['disableSmarty'], $params['contactId'] ?? NULL, $params['tplParams']);
 
     // send the template, honouring the target user’s preferences (if any)
     $sent = FALSE;
@@ -692,35 +694,26 @@ protected static function parseThroughSmarty(array $mailContent, $tplParams): ar
    *
    * @param array $mailContent
    * @param bool $disableSmarty
-   * @param int $contactID
+   * @param int|NULL $contactID
    * @param array $smartyAssigns
    *
    * @return array
-   * @throws \CRM_Core_Exception
    */
-  public static function renderMessageTemplate(array $mailContent, $disableSmarty, $contactID, $smartyAssigns): array {
-    $tokens = self::getTokensToResolve($mailContent);
-
-    // When using Smarty we need to pass the $escapeSmarty parameter.
-    $escapeSmarty = !$disableSmarty;
-
-    $mailContent = self::resolveDomainTokens($mailContent, $tokens, $escapeSmarty);
-
-    if ($contactID) {
-      $mailContent = self::resolveContactTokens($contactID, $tokens, $mailContent, $escapeSmarty);
-    }
-
-    // Normally Smarty is run, but it can be disabled using the disableSmarty
-    // parameter, which may be useful for non-core uses of MessageTemplate.send
-    // In particular it helps with the mosaicomsgtpl extension.
-    if (!$disableSmarty) {
-      $mailContent = self::parseThroughSmarty($mailContent, $smartyAssigns);
-    }
-    else {
-      // Since we're not relying on Smarty for this function, we DIY.
-      // strip whitespace from ends and turn into a single line
-      $mailContent['subject'] = trim(preg_replace('/[\r\n]+/', ' ', $mailContent['subject']));
+  public static function renderMessageTemplate(array $mailContent, bool $disableSmarty, $contactID, array $smartyAssigns): array {
+    CRM_Core_Smarty::singleton()->pushScope($smartyAssigns);
+    $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), ['smarty' => !$disableSmarty]);
+    $tokenProcessor->addMessage('html', $mailContent['html'], 'text/html');
+    $tokenProcessor->addMessage('text', $mailContent['text'], 'text/plain');
+    $tokenProcessor->addMessage('subject', $mailContent['subject'], 'text/plain');
+    $tokenProcessor->addRow($contactID ? ['contactId' => $contactID] : []);
+    $tokenProcessor->evaluate();
+    foreach ($tokenProcessor->getRows() as $row) {
+      $mailContent['html'] = $row->render('html');
+      $mailContent['text'] = $row->render('text');
+      $mailContent['subject'] = $row->render('subject');
     }
+    CRM_Core_Smarty::singleton()->popScope();
+    $mailContent['subject'] = trim(preg_replace('/[\r\n]+/', ' ', $mailContent['subject']));
     return $mailContent;
   }
 
diff --git a/CRM/Utils/Token.php b/CRM/Utils/Token.php
index 038c191543b3..c47c96e7d3dd 100644
--- a/CRM/Utils/Token.php
+++ b/CRM/Utils/Token.php
@@ -267,11 +267,12 @@ public static function getDomainTokenReplacement($token, $domain, $html = FALSE,
       $value = "{domain.$token}";
     }
     elseif ($token === 'address') {
-      static $addressCache = [];
+      $cacheKey = __CLASS__ . 'address_token_cache' . CRM_Core_Config::domainID();
+      $addressCache = Civi::cache()->has($cacheKey) ? Civi::cache()->get($cacheKey) : [];
 
-      $cache_key = $html ? 'address-html' : 'address-text';
-      if (array_key_exists($cache_key, $addressCache)) {
-        return $addressCache[$cache_key];
+      $fieldKey = $html ? 'address-html' : 'address-text';
+      if (array_key_exists($fieldKey, $addressCache)) {
+        return $addressCache[$fieldKey];
       }
 
       $value = NULL;
@@ -285,7 +286,7 @@ public static function getDomainTokenReplacement($token, $domain, $html = FALSE,
         else {
           $value = $loc[$token][1]['display_text'];
         }
-        $addressCache[$cache_key] = $value;
+        Civi::cache()->set($cacheKey, $value);
       }
     }
     elseif ($token === 'name' || $token === 'id' || $token === 'description') {
diff --git a/tests/phpunit/CRM/Event/Form/Registration/ConfirmTest.php b/tests/phpunit/CRM/Event/Form/Registration/ConfirmTest.php
index 7278eacafc1d..c8df7cf9eea2 100644
--- a/tests/phpunit/CRM/Event/Form/Registration/ConfirmTest.php
+++ b/tests/phpunit/CRM/Event/Form/Registration/ConfirmTest.php
@@ -20,7 +20,7 @@ public function setUp() {
    *
    * @throws \Exception
    */
-  public function testSubmit() {
+  public function testSubmit(): void {
     $event = $this->eventCreate();
     $mut = new CiviMailUtils($this, TRUE);
     CRM_Event_Form_Registration_Confirm::testSubmit([
@@ -77,15 +77,11 @@ public function testSubmit() {
       ],
     ]);
 
-    $participant = $this->callAPISuccessGetSingle('Participant', []);
     $mut->checkMailLog([
       'Dear Logged In,  Thank you for your registration.  This is a confirmation that your registration has been received and your status has been updated to Registered.',
     ]);
     $mut->stop();
     $mut->clearMessages();
-    $tplVars = CRM_Core_Smarty::singleton()->get_template_vars();
-    $this->assertEquals($participant['id'], $tplVars['participantID']);
-
   }
 
   /**
diff --git a/tests/phpunit/api/v3/ContributionTest.php b/tests/phpunit/api/v3/ContributionTest.php
index e48fd5950339..4664f7e83ba9 100644
--- a/tests/phpunit/api/v3/ContributionTest.php
+++ b/tests/phpunit/api/v3/ContributionTest.php
@@ -3662,9 +3662,8 @@ public function setUpPendingContribution($priceFieldValueID, $contriParams = [])
    * Test sending a mail via the API.
    *
    * @throws \CRM_Core_Exception
-   * @throws \CiviCRM_API3_Exception
    */
-  public function testSendMail() {
+  public function testSendMail(): void {
     $mut = new CiviMailUtils($this, TRUE);
     $orderParams = $this->_params;
     $orderParams['contribution_status_id'] = 'Pending';
@@ -3694,7 +3693,6 @@ public function testSendMail() {
     $mut->stop();
     $tplVars = CRM_Core_Smarty::singleton()->get_template_vars();
     $this->assertEquals('bob', $tplVars['billingName']);
-    $this->assertEquals("bob\nblah\n", $tplVars['address']);
   }
 
   /**