near / near-wallet

Web wallet for NEAR Protocol which stores keys in browser's localStorage
https://wallet.near.org
MIT License
220 stars 176 forks source link

Better NEP141 token detection if promises are used #1864

Open vans163 opened 3 years ago

vans163 commented 3 years ago

If a call on a non-NEP141 contract includes promises that call ft_* or storage functions on a NEP141 contract, the wallet will not pickup/register the token.

Currently an extra step is required to get the tokens to appear on wallet.near.org, which is to call one of the required functions on the NEP141 contact to get the tokens to appear.

If the wallet could pick up promises, it makes DeFi and things like the mint contract we use easier as there is 1 less step in the loop.

Maybe example:

zod.near
mint.zod.near

call mint.zod.near mint {amount: 100}
-> if no_storage ? promise(zod.near, storage_deposit)
-> promise(zod.near, ft_transfer, mint.zod.near, predecessor)
(token does not appear in wallet)

call zod.near storage_deposit {account: "self.near"}
call mint.zod.near mint {amount: 100}
-> promise(zod.near, ft_transfer, mint.zod.near, predecessor)
(token appears in wallet)
stefanopepe commented 3 years ago

We are discussing the implementation of fungible token transfers on issue #1714. The only limitation is how to avoid spam or a cluttered interface: in a situation with thousands of fungible tokens and hundreds of airdrops, we wanted to limit the possibility to "spam" airdropped coins to wallets - as we've seen in the past on Ethereum.

Right now the solution is the one above (call a method), we may introduce the manual tracking or a "refresh" button to add this type of tokens.

Do you have any hints or examples on how this problem was solved elsewhere?

vans163 commented 3 years ago

I see, in this case I showed above, the spam airdrops will not appear, since the receiver does not call anything.

But if getting all tokens to appear (example you get sent 10,000 tokens but your wallet seems empty) to me something like sushiswap search makes sense. So you receive ZOD token but your balance is not showing it, you click Add Token and search for it. This Add Token button could make a very cheap call to the token contract that fails, the names could be stored on-chain in a token registry. I am not sure what supports wildcard search thought, example so typing ZO would return ZOD, ZOO, ZOK, (etc)?

A "Hide [<1$,<100$] balance toggle" for priced tokens. A 0.0001 amount of a token could be worth $100 while 100000.12 of another could be worth 0.0001$. So if the wallet is able to resolve a price for a token (defi, cefi) and the price is <$1/100 hide it, If no price hide it. If price > show it.

A "sort by highest value" like etherscan does, no price sort default, if price sort by value.

Just some things that come to mind off the top.

stefanopepe commented 3 years ago

@Patrick1904 I remember we had a similar conversation before, and we were looking for a scalable solution to manage fungible tokens list in the wallet. Is this going to change with the new fungible tokens transfer function?

vans163 commented 3 years ago

I think just having the wallet code dig 1 nest deeper into the transaction when it parses it is solving 85% of it. It solves a lot of usecases like claiming airdrops (active airdrops where a user needs to make a tx to get the airdrop) and DeFI.

The other 15% where you get sent tokens from another wallet (without calling anything onchain), so you got a passive airdrop or a friend sent you tokens could be tackled later.

Example:

Here is the execution plan for our mint function. https://explorer.mainnet.near.org/transactions/GmanUywyEePAC1uEfRH8MC2WjLeNGKe3fvmAQvZedAJc image

The contract uses a promise to call the zod.near contract to do the ft_transfer. The predecessor is the minting contract but the sender is the user who needs the token to appear on their wallet.

if (execution_plan contains_transaction_with receiver_that_is_a_NEP141_token)
  show token inside sender wallet

If the wallet can pick this up as it scans, and make the token appear would fix 85% of it.

vgrichina commented 3 years ago

@vans163 I think https://explorer.mainnet.near.org/transactions/GmanUywyEePAC1uEfRH8MC2WjLeNGKe3fvmAQvZedAJc is not picked up because it doesn't have ft_transfer, it has ft_transfer_preregister.

While query in contract helper only has ft_transfer and ft_transfer_call. Promises or no promises generally shouldn't have anything to do with it. With a caveat that there is another query which checks whether you called ft_ method on a contract directly https://github.com/near/near-contract-helper/blob/c55e036700e7b5e866f785313c1924674b48e0b7/middleware/indexer.js#L142 Most likely it picks up ft_transfer_preregister when you called contract directly.

I think the easiest way to resolve this is to add ft_transfer_preregister in a list of methods in query. However looks like this method isn't part of the standard https://github.com/near/NEPs/blob/master/specs/Standards/FungibleToken/Core.md? Where does it come from?

vans163 commented 3 years ago

Ah I see this is the difference in the behaviour then, that caveat you described. I thought it would be the same for promises and well as the toplevel calls.

That is a method I made up which reserves storage for the receiver [if it does not exist (and paid by caller)] + does the transfer in 1 go.

It seems the solution here then would be to write a 3 promise chain in the mint contract which I was trying to avoid

promise1 = check_receiver_storage()
promise2 = if (no_storage) buy storage
promise3 = ft_transfer as before