hashgraph / hedera-smart-contracts

Contains Hedera Smart Contract Service supporting files
Apache License 2.0
46 stars 54 forks source link

Can't get HTS token address when creating the token using the interface IHederaTokenService #901

Open ed-marquez opened 3 months ago

ed-marquez commented 3 months ago

Description

Problem: When creating an HTS token using the interface IHederaTokenService, a developer can't get the token address using Ethers JS.

Context:

image

For devs working with Ethers/Hardhat, those tools only return a transaction hash (no tx ID). The mirror node REST API supports querying transactions with our 48-byte form of the tx hash, not the Ethereum 32-bytes one. So querying mirror nodes with the tx hashed obtained would not be seamless either.

Possible Solution:

Thanks to everyone who provided feedback and input!

Steps to reproduce

  1. Run this repo with GitPod via this link: https://gitpod.io/?autostart=true#https://github.com/ed-marquez/hedera-example-create-tokens-via-contracts/tree/5351753720d3db77cfab098834527fae1eafa299
  2. Enter npx hardhat test on the terminal
  3. See the results of the 3rd test case

Additional context

No response

Hedera network

mainnet, testnet

Version

v0.52

Operating system

macOS

konstantinabl commented 2 weeks ago

As you stated in your ticket there are indeed two possible solutions here. The first to emit an event, when a token is created. This however will result in us not being 100% equivalent with ERC20, since theres no such event there. The second option is to return the contractAddress field as part of the receipt, this will require some changes in the relay and might slightly slowdown the getReceipt endpoint, due to the fact that we will have to check the selector that was called with the transaction and if it is createFungible/createNonFungible etc. Two things to note here:

  1. Doing this won't be optimal for the cases where there's a nested call creating the token. Not sure if such cases are commont, though.
  2. It will require an extra step, where after getting the address from the receipt the user will have to do something like this to use the token as an ERC20 const tokenAddress = tokenCreateTx.contractAddress; const tokenAsErc20 = await ethers.getContractAt(ERC20ABI, tokenAddress); We'll discuss whats best

cc: @Nana-EC

Nana-EC commented 2 weeks ago

Thanks @ed-marquez these are fair points and @konstantinabl has highlighted some tradeoffs. In general we want to utilize the existing behaviour that EVM devs expect. Both logs and the address solution speak to that but they also fuse EVM and specific product considerations.

Ethers offers the contractFactory which handles both the deployment and getting the new contract address. It does this I believe by calculating the address based on EVM logic. In our case a new HTS token address is not predictable offchain and we need to get it from the network.

@konstantinabl expanding on the second suggestion of tokenCreateTx.contractAddress we should look at an example of this call. As you'd noted we'd have to confirm the function selector, then we'd have to make a call to MN to get the token details. What all steps might the relay have to take in this case and are there any edge cases where it wouldn't work? This will help us get a sense of feasibility and latency/perf concerns.

I'm biased towards a non CN change except we'd also have to consider how the MN web3 module would replicate this on direct calls to it's API outside of the relay

konstantinabl commented 2 weeks ago

@Nana-EC so regarding adding the tx.contractAddress I tested it and it currently returns the field, but it has the 0x167 address. So what we need to do is return the actual address of the token that was created. This can be done without extra calls, in the receipt we have the data and the function selector + the result of the function, which in this case is the responseCode + tokenAddress. The tradeoff here is if function selectors are changed in services we will have to manually change in our repo, but I guess this won't happen often. Also this will work only for direct calls, so wont return the tokenAddress of the created token if there is a nested call, but I guess this is the idea of the issue.

Nana-EC commented 2 weeks ago

@Nana-EC so regarding adding the tx.contractAddress I tested it and it currently returns the field, but it has the 0x167 address. So what we need to do is return the actual address of the token that was created. This can be done without extra calls, in the receipt we have the data and the function selector + the result of the function, which in this case is the responseCode + tokenAddress. The tradeoff here is if function selectors are changed in services we will have to manually change in our repo, but I guess this won't happen often. Also this will work only for direct calls, so wont return the tokenAddress of the created token if there is a nested call, but I guess this is the idea of the issue.

Thanks @konstantinabl good idea. Please go ahead and start on a design doc for this suggested feature so we can get a review and discussion going.