javascriptrustwebassemblycloudflare-workersrust-futures

Hanging promise canceled


I'm trying to set Cloudflare's workers to track the circulation of some ERC20 tokens as an exercise to learn web3 and wasm. Thought it could be simple enough, but about 90% of the time so far has been trying to solve this elusive error

A hanging Promise was canceled. This happens when the worker runtime is waiting for a Promise from JavaScript to resolve but has detected that the Promise cannot possibly ever resolve because all code and events related to the Promise's request context have already finished.

I look for additional information online, but it seems my error is from a different type(?).

Here's a simple snippet of code to reproduce.

mod erc20_abi;

use erc20_abi::ERC20_ABI;

use cfg_if::cfg_if;

use ethers::{
    contract::Contract,
    core::{abi::Abi, types::Address},
    prelude::{AbiError, U256},
    providers::{Http, Provider},
};
use num_format::{Locale, ToFormattedString};
use std::convert::TryFrom;
use wasm_bindgen::prelude::*;
cfg_if! {
    // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
    // allocator.
    if #[cfg(feature = "wee_alloc")] {
        extern crate wee_alloc;
        #[global_allocator]
        static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
    }
}

#[wasm_bindgen]
pub async fn handle() -> String {

    let web3_ethereum = Provider::<Http>::try_from(WEB3_URL_ETHEREUM).unwrap();

    let abi: Abi = serde_json::from_str(ERC20_ABI).unwrap();

    let token_contract_ethereum = Contract::new(parse_address(ADDRESS_ETH), 
    abi, web3_ethereum);

    let convert_wei_to_decimal = |bignumber: U256| -> String {
        (bignumber.as_u128() / u128::pow(10, 18)).to_formatted_string(&Locale::en)
    };

    // I believe this is the problem, since just returning a String works fine.
    let total_supply_ethereum = token_contract_ethereum
        .method::<_, U256>("totalSupply", ())
        .unwrap()
        .call()
        .await
        .unwrap();


    convert_wei_to_decimal(total_supply_ethereum)
}

fn parse_address(address: &str) -> Address {
    address.parse::<Address>().unwrap()
}


This is the worker/workers.js file

addEventListener('fetch', (event) => {
    event.respondWith(handleRequest(event.request))
})

const { handle } = wasm_bindgen;
const instance = wasm_bindgen(wasm);
/**
 * Fetch and log a request
 * @param {Request} request
 */
async function handleRequest(request) {
    await instance;
    const output = await handle();
    let res = new Response(output, { status: 200 });
    res.headers.set('Content-type', 'text/html');
    return res;
}

Cargo.toml

[package]
name = "circulating-supply"
version = "0.1.0"
license = "GPL-3.0-or-later"
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
crate-type = ["cdylib", "rlib"]

[profile.release]
opt-level = 's'  # Optimize for size.
lto = true
panic = "abort"
codegen-units = 1

[dependencies]
ethers = { git = "https://github.com/gakonst/ethers-rs" }
serde_json = "1.0.68"
num-format = "0.4.0"
cfg-if = "1.0.0"
wee_alloc = { version = "0.4.5", optional = true }
wasm-bindgen = "0.2.78"
wasm-bindgen-futures = "0.4.28"
js-sys = "0.3.55"

wrangler dev will compile it fine, but going to http://127.0.0.1:8787 will result in Error 1101


Solution

  • In my case a dependency used sth. not available in wasm runtime. I guess ethers cryptography dependencies also depend on sth. like getrandom.

    Adding this to Cargo.toml solved my issue.

    [target.wasm32-unknown-unknown.dependencies]
    getrandom = { version = "0.1", features = ["wasm-bindgen"] }
    

    This will force your dependencies based on getrandom use the wasm features of getrandom.

    If you need getrandom in version 0.2 use this:

    [target.wasm32-unknown-unknown.dependencies]
    getrandom = { version = "0.2", features = ["js"] }