Account Abstraction
Currently, the Verifier contract uses Near AccountId
to identify users, which supports both Named and Implicit accounts.
Named Account: user.near
user.near
The only way to start using Named Account is to send a real tx intents.near::add_public_key(pk)
from user's Near wallet. Others can still deposit/transfer you money before you "claim" it.
Implicit Account
Such accounts already have a public key encoded in their names, so they can be used without any "claiming" from users. There is 1-to-1 relationship between its format and corresponding signing curve types:
EdDSA:
8c5cba35f5b4db9579c39175ad34a9275758eb29d4866f395ed1a5b5afcb9ffc
(i.e. "Implicit Near")ECDSA:
0x85d456B2DfF1fd8245387C0BfB64Dfb700e98Ef3
(i.e. "Implicit Eth")
This means that if a user logs in with Cosmos (ECDSA) wallet, then he will have an Implicit Eth address inside intents.near, while Solana/Ton (EdDSA) wallets will give you Implicit Near addresses.
It's not feasible to make these addresses different for each chain, since we only know the signature and public key. Even if we differentiate between them based on the used signing standard (NEP-413, EIP-712), then it still leads to ambiguity in case of importing same seed phrase into different wallets.
Account keys
Once an account is created, multiple additional keys could be added to it. Each of these keys has full control over the account and can add or remove other keys either directly via NEAR transactions or via signed intents.
Here is an example of adding public keys for Explicit Near Accounts via tx.
You can also do it manually with near-cli:
In addition to directly calling add_public_key
method it's also possible to submit a signed intent:
Authentication Flow example via Frontends
Users' wallets store their private keys and allow users to rotate them. In order to verify signatures inside of Verifier, Verifier should know which keys are associated with which "named" accounts. So, intents.near
contract should maintain a copy of mapping of account_ids to their public_keys (again, each account_id can have multiple public_keys registered). This copy should include a subset of valid public keys that were added as Full Access Keys to each NEAR account that wants to interact with Intents.
So, when a user user1
opens the Frontend for the first time and clicks "Connect Wallet", we ask his wallet to signMessage("Authenticate")
. As a result, we get a signature and, more importantly, the public_key as a counterpart of a Full Access Key that was used to sign this message and the account_id which the public_key corresponds to. We store this pair (account_id, public_key)
in browser's local storage.
Now, when a user wants to swap tokens, i.e. sign a state_change:
Firstly, check if this pair
(account_id, public_key)
is already registered in Intents contract on-chain by callingget_account_public_keys(account_id) -> Vec<Pubkey>
method onintents.near
contract.If there is no such user or if public_key was not registered for him, the user should be "registered" in Intents contract. For that we should ask user's wallet to
signMessage("user1 is an owner of public_key ed25519:abcd...")
Then we should send this signed message in the transaction (can be done by relay) to
intents.near
callingadd_public_key({"account_id": "user1", "public_key": "ed25519:abcd...", "signature": "xyz123..."})
method. This transaction would added25519:abcd...
to the list of public_keys that belongs touser1
Ask user's wallet to
signMessage({"account_id": "user1", "state_changes": [...] })
and this state_change along with the returned signature and public_key would be eventually sent by solver to theintents.near
.When
intents.near
receives transaction with such signature, it validates the signature for given public_key and atomically checks whether the public_key is registered foruser1
.
Unfortunately, this brings up a new challenge: whenever the user removes his Full Access Key, it should also be unregistered on intents.near
contract by calling unregister_key(public_key)
method sent from user's NEAR account. We can try to make it for him with a FunctionalKey added to user's account on NEAR and call by ourselves whenever we detect that our user has deleted a key from his NEAR account on-chain.
Last updated