Help understanding link()

I’m confused about the code in the link() function:

static async link(account, options) {
    const web3 = await MoralisWeb3.enableWeb3(options);
    const data = options?.signingMessage || MoralisWeb3.getSigningData();
    const user = await ParseUser.currentAsync();
    const ethAddress = account.toLowerCase();
    const EthAddress = ParseObject.extend('_EthAddress');
    const query = new ParseQuery(EthAddress);
    const ethAddressRecord = await query.get(ethAddress).catch(() => null);
    if (!ethAddressRecord) {
      const signature = await web3.eth.personal.sign(data, account, '');
      const authData = { id: ethAddress, signature, data };
      await user.linkWith('moralisEth', { authData });
    }
    user.set('accounts', uniq([ethAddress].concat(user.get('accounts') ?? [])));
    user.set('ethAddress', ethAddress);
    await user.save(null, options);
    return user;
  }

Could you please shortly explain what the if block is about? if the address is not found on the server it is linked?, but if it is found there is also code being executed afterwards, how come?

1 Like

if the address is not found on the server then current eth address is linked by asking to generate a signature. If the address was already found on the server then it means that the user already authenticated before with this address or it was already linked before and it only tries to update the accounts and ethAddress fields for current user object.

1 Like

Thx. If I understand it correctly you forgot to handle the possible exception in the unlink() function:

link()
...
const ethAddressRecord = await query.get(ethAddress).catch(() => null);
...

unlink()
...
const ethAddressRecord = await query.get(accountsLower);
...

Btw. accountsLower is plural but it stores only 1 value.

I think that if will be an exception there in unlink then it means that there is no account to unlink.

Right, that could theoretically happen when you for example manually delete a row on the server and your local ParseUser is out of sync, then unlink could crash, so I think handling that in the function would be good, for example in the case of getting the ParseException removing the ethAddress instead of calling user.set(‘ethAddress’, nextAccounts[0]);

async function a() {
 console.log("a1");
 EthAddress = Moralis.Object.extend('_EthAddress');
 query = new Moralis.Query(EthAddress);
 const ethAddressRecord = await query.get("a");
 console.log("a2");
}

The line with console.log("a2"); will not execute in this particular case. The execution will stop at the line that will generate the exception.

So if my ParseUser Object is out of sync for whatever reasson I would get an uncaught exception in the console and this would also lead to nothing happening on the UI because the function will never return a value as expected, so for example you have an unlink button, you will press it and nothing will happen, right?

Or you could do something like this:

async function a() {
 console.log("a1");
 EthAddress = Moralis.Object.extend('_EthAddress');
 query = new Moralis.Query(EthAddress);
 const ethAddressRecord = await query.get("a");
 console.log("a2");
}

try{
    await a()
} catch (err) {
    console.log("there was an error", err);
}

I already handled it by resetting/saving the outdated details, but the point of my post was that I think you should fix that function, or at least document the possible throw.

yep, we could do better regarding that throw, thanks for reporting/noticing it