-1

Let's say I'm Alice and I have a task of transferring N ERC20 tokens via smart-contract deployed, say, on Avalache subnet from my address to my client Bob. And I want to execute my smart-contract transfer function with Rust program. Say I use ethers-rs and have the following code:

                let tx = self.contract.transfer(addr_to, amount).tx;
                let _res: TypedTransaction = tx;
                if let Some(receipt) = self
                    .contract
                    .transfer(addr_to, amount)
                    .send()
                    .await
                    .map_err(|e| EthersTransferError(e.to_string()))?
                    .await
                    .map_err(|e| EthersTransferError(e.to_string()))?
                {
                    return Ok(receipt);
                }

It takes about 5 seconds for this code to finish transaction and return a receipt with transaction hash, status and other important info. But the problem is: what if my program crashes during that 5 seconds, right before I receive a receipt. So my transaction might have succeeded or not, and I'm not able to check that since I don't have the receipt and any other information except from address, to address, amount and smart-contract address. But I NEED to know if my transaction succeeded, I should transfer tokens exactly once. So what can I do in this case? My thought was to grab events from the last blocks and find my transaction within them, so I did this:

    async fn find_tx(&self, to: Address, amount: U256, from_block: BlockNumber) -> Result<()> {
        let filter = Filter::new()
            .address(self.contract_addr)
            .event("Transfer(address,address,uint256)")
            .topic2(H256::from(to))
            .from_block(from_block);

        let mut interval = time::interval(time::Duration::from_secs(600));
        loop {
            interval.tick().await;
            let logs = self
                .client
                .get_logs(&filter)
                .await
                .map_err(|e| AvalancheGetLogsError(e.to_string()))?;

            for log in logs.iter() {
                if self.client.address() == Address::from(log.topics[1])
                    && to == Address::from(log.topics[2])
                    && amount == U256::from(log.data.as_bytes_ref())
                {
                    return Ok(());
                }
            }
        }
    }

The problem with this solution is that a) some chains set maximum amount of blocks to filter (like Fuji testnet - 2048 blocks), and my transaction can be far beyond that limit and b) it can check if transaction failed, code will stack in an infinite loop and I will have to wait days to make sure transaction didn't arrive.

So, I somehow need to find the status of my transaction with limited info. What do you think is the best programmatic solution for that? I'm sure I'm not the first one to encounter this issue. Do I have to rely on the API of the service that hosts my subnet? Some indexer, explorer? Somehow extend smart-contract functionality for additional checks? Does Geth have enough functionality to solve the issue? I'm lost a bit here. Thanks in advance

akolechko
  • 75
  • 5

1 Answers1

1
Mikko Ohtamaa
  • 82,057
  • 50
  • 264
  • 435