This is only slightly shorter (1,031 characters vs. yours at 1,035), but removed lambda functions and used Named functions instead so every group of three digits could be processed recursively instead of sequentially. It also moves all the word lists to a single place into a calling shell routine to simplify the function call. Finally, the logic around dollars and cents was moved to a third, inline formula which fixes the problems you have with pluralizing dollars and cents.
Starting from the top down, the formula to print Dollars and Cents is:
Let(D,Int(A1),C,mod(round(A1*100),100),If(D>0,INTTOWORDS(D) & " Dollar"
& If(D>1,"s",""),"") & If(C>0,If(D>0," and ","") & INTTOWORDS(C) & " Cent"
& If(C>1,"s",""),""))
Here A1 is the currency amount to be turned into words. Notice it calls on a named function, INTTOWORDS, to actually print the numbers in the correct context of the words, 'Dollar'(s), ' and ', 'Cent'(s). The INTTOWORDS function is a simple calling shell whose named function definition is this:
Named Function: INTTOWORDS, parameter1: nbr
=NBRWORDS(nbr,{"";" thousand";" million";" billion";" trillion"},
{"";"";"twenty";"thirty";"forty";"fifty";"sixty";"seventy";"eighty";"ninety"},
{"";"one";"two";"three";"four";"five"; "six"; "seven"; "eight";"nine"; "ten";
"eleven"; "twelve"; "thirteen"; "fourteen"; "fifteen"; "sixteen"; "seventeen";
"eighteen"; "nineteen"})
Notice that this only calls another named function, NBRWORDS, which then includes all the words we need to construct each group of three digits. The first array, which I call the Suffix, is initialized in this call, but the top row gets popped off with each successive call as it processes the "thousands" group and then the "millions" group, etc. successively. The NBRWORDS logic basically processes a group of 3 digits, adding the correct suffix, and calls itself for the next group of 3 digits, until it runs out.
Named Function: NBRWORDS parameter1:nbr parameter2:Suffix(array) parameter3:tenswords(array) parameter4: oneswords(array)
=Let(hundreds,mod(Int(Nbr/100),10), tens,mod(Int(Nbr/10),10), ones,mod(Int(Nbr),10)+If(tens=1,10,0),
NextNbr,Int(Nbr/1000), If(NextNbr>0,NBRWORDS(NextNbr
,MakeArray(Rows(Suffix)-1,1,LAMBDA(Row,Col,Index(Suffix,Row+1,Col))), tenswords, oneswords),"")
& If(mod(Int(Nbr),1000)>0,
If(hundreds>0, If(NextNbr>0," ","") & Index(oneswords,hundreds+1) & " hundred","")
& If(tens>1,If(hundreds+NextNbr>0," ","") & index(tenswords,tens+1),"")
& If(ones>0,If(hundreds+NextNbr+tens>0," ","") & index(oneswords,ones+1),"")
& Index(Suffix,1),""))
The MakeArray is the function that 'pops' the first row off the Suffix array in the recursive call to NBRWORDS. There's still quite a bit of IF() conditional text concatenation especially around getting the right spaces between parts which keeps it pretty verbose.
Here are some of the actual results:

Notice B2 gets the pluralization right. B1 shows a large number with zeros in different places. Need to see other examples?
Edit
Hey @Harun24hr: Did some research into making recursive calls to Lambda functions and came up with the following:
You can use a Let() function to give a Lambda function a name, but you cannot refer to it directly in the body of the lambda because that name has not been has not been defined until the body of the Lambda is fully completed and you will get a #NAME? error.
INCORRECT--> =Let(RecFctrl, Lambda(N, N*If(N>1,RecFctrl(N-1),1)), RecFctrl(9))
What you can do instead is add one more parameter to the Lambda call and pass down the Lambda function itself inside the Lambda call. So the correct code to get factorials computed is:
CORRECT--> =Let(RecFctrl, Lambda(self, N, N*If(N>1,self(self,N-1),1)), RecFctrl(RecFctrl,9))
This added another arbitrarily-named parameter, self, and then used it inside the body of the Lambda itself as a function call and also passed it down to the next level call as self(self,N-1). Then, in the body of the Let() it was referred to by its defined name, RecFctrl, but once again the function was passed down as RecFctrl(RecFctrl,9).
Kind of a long-winded explanation, but this meant it was possible to put the recursive Lambda function into an inline formula, combining the two Named Functions above, with only a few minor changes:
=Let(D,Int(A1),C,mod(round(A1*100),100),
WdsFromN,Lambda(this,N, Suffix, tenswords, oneswords,
Let(hundreds,mod(Int(N/100),10), tens,mod(Int(N/10),10),
ones,mod(Int(N),10)+If(tens=1,10,0), NextN,Int(N/1000),
TextJoin(" ",TRUE, If(NextN>0, this(this,NextN,
MakeArray(Rows(Suffix)-1,1,LAMBDA(Row,Col,Index(Suffix,Row+1,Col))),
tenswords, oneswords),""),
If(hundreds>0,Index(oneswords,hundreds+1) & " hundred",""),
index(tenswords,tens+1), index(oneswords,ones+1),
If(mod(Int(N),1000)>0,Index(Suffix,1),"")))),
NToWds,LAMBDA(nbr,WdsFromN(WdsFromN,nbr,
"";"thousand";"million";"billion";"trillion"},
{"";"";"twenty";"thirty";"forty";"fifty";"sixty";"seventy";"eighty";"ninety"},
{"";"one";"two";"three";"four";"five"; "six"; "seven"; "eight";"nine"; "ten";
"eleven"; "twelve"; "thirteen"; "fourteen"; "fifteen"; "sixteen";
"seventeen"; "eighteen"; "nineteen"})),
If(D>0,NToWds(D) & " Dollar" & If(D>1,"s",""),"")
& If(C>0,If(D>0," and ","") & NToWds(C) & " Cent" & If(C>1,"s",""),""))
This is pretty much the same code as above but combined into one unwieldy formula, which is still shorter (993 characters) than yours (1035). You can shorten it still further by replacing the long MAKEARRAY() function with a DROP(SUFFIX,1) instead. I did not do that because everything in this formula also runs in Google Sheets, but the DROP function is not defined in Sheets (where I am testing this formula). Now, here are some tests of the combined formula:
