How to return value of Struct inside Mapping

Hi, I want to return data from a mapping in the form of a struct. My contract code is:

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.4;

contract A {

    struct nftDataStruct {
        address ownerAddress;
        bool staked;
        uint64 timestamp;
        bool legacyStaking;
    }

    mapping(address => mapping(uint256 => nftDataStruct)) public nftData;

    function setNftData(address addy, uint256 id) public {
        nftData[addy][id] = nftDataStruct(addy, false, uint64(block.timestamp), false);
    }

}

So in Javascript I want to pass two arguments to get a certain value (an address + id), however they have no name so my code looks like this:

let options = {
    chain: eth,
    address: query_address,
    function_name: "nftData",
    abi: contractABI,
    params: {
        "" : _nft_address,
        "" : _token_id
    }
};
let nftDataResult = await Moralis.Web3API.native.runContractFunction(options);

But it seems like Moralis strips out the first argument, possibly because of having no name, as you can see when I console.log the options variable and look at the params:

params:
  "": "2"

And shows the error:
‘Uncaught (in promise) Error: invalid address (argument=“address”, value=“2”, code=INVALID_ARGUMENT, version=address/5.4.0)’

Any clues on how to get the data without writing my own getter function (seems like it should work in Solidity 8+)

In remix I can call the public variable and provide the arguments and Remix returns the struct. Looking at the transaction hash data it sends the input data as:

{
  "method": "nftData",
  "types": [
    "address",
    "uint256"
  ],
  "inputs": [
    "441a8506b1ae9b939eef42e2a8217a4872b23cdf",
    "a"
  ],
  "names": [
    "",
    ""
  ]
}

Not sure if I can add that in somewhere?

maybe use something low level like encodeFunctionCall, but the norm is if you want to fetch data from mapping you gotta have a getter function to do so, coz mappings are not directly translated to functions, even though it is public.

I’ve deployed and verified the contract on Rinkeby though and Etherscan can take the two arguments and return the Struct data? Isn’t it part of ABI encoding v2?

I’ll add in a getter function just so it’s there but thought I’d ask since Etherscan seems to be able to do it