{"id":13525706,"url":"https://github.com/eigerco/ethereum-canister","last_synced_at":"2026-01-17T19:19:47.774Z","repository":{"id":184452570,"uuid":"669077948","full_name":"eigerco/ethereum-canister","owner":"eigerco","description":null,"archived":false,"fork":false,"pushed_at":"2024-08-12T07:03:36.000Z","size":121,"stargazers_count":24,"open_issues_count":1,"forks_count":6,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-30T05:32:31.539Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eigerco.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-07-21T09:36:23.000Z","updated_at":"2025-01-20T09:51:52.000Z","dependencies_parsed_at":"2024-01-13T22:53:31.008Z","dependency_job_id":"bf6c4475-852e-438f-b41d-d5a8b30fac29","html_url":"https://github.com/eigerco/ethereum-canister","commit_stats":null,"previous_names":["eigerco/ethereum-canister"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/eigerco/ethereum-canister","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eigerco%2Fethereum-canister","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eigerco%2Fethereum-canister/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eigerco%2Fethereum-canister/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eigerco%2Fethereum-canister/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eigerco","download_url":"https://codeload.github.com/eigerco/ethereum-canister/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eigerco%2Fethereum-canister/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28516743,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T18:55:29.170Z","status":"ssl_error","status_checked_at":"2026-01-17T18:55:03.375Z","response_time":85,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T06:01:21.296Z","updated_at":"2026-01-17T19:19:47.743Z","avatar_url":"https://github.com/eigerco.png","language":"Rust","funding_links":[],"categories":["Chain Fusion"],"sub_categories":["Ethereum"],"readme":"# Ethereum Canister\n\nThe Ethereum canister offers a secure and trustless way to access Ethereum blockchain data within the [Internet Computer](https://internetcomputer.org/) ecosystem.\nBehind the scenes, it leverages our [fork](https://github.com/eigerco/helios) of the [`helios`](https://github.com/a16z/helios) light Ethereum client.\nThis client is equipped with the capability to validate the authenticity of fetched data.\n\nRead more about this project [on our blog](https://www.eiger.co/blog/icp-light-client)\n\n## Running and using the Ethereum canister\n\n```bash\ndfx start --clean --background --artificial-delay 100\n# deploy\ndfx deploy\n# start it\ndfx canister call ethereum_canister setup 'record {\n    network = variant { Mainnet };\n    execution_rpc_url = \"https://ethereum.publicnode.com\";\n    consensus_rpc_url = \"https://www.lightclientdata.org\";\n}'\n# utilize it\ndfx canister call ethereum_canister erc20_balance_of 'record {\n    contract = \"0xdAC17F958D2ee523a2206206994597C13D831ec7\";\n    account = \"0xF977814e90dA44bFA03b6295A0616a897441aceC\";\n}'\n(2_100_000_000_000_000 : nat) # canister's output\n```\n\n## Running end-to-end canister tests\n\n```bash\ndfx start --clean --background --artificial-delay 100\n\ncargo test --target x86_64-unknown-linux-gnu\n# or using nextest\ncargo nextest run --target x86_64-unknown-linux-gnu\n```\n\n## Design\n\nEthereum canister is a thin wrapper around the `helios` light client.\nThe client operates as global state and conducts periodic synchronization with the Ethereum consensus node in the background.\nCanister exposes a trustless RPC API of the execution node from the `helios` for other canisters.\n\n### Canister API\n\nYou can find the API definition for the Ethereum canister in the [`candid.did` file](src/ethereum_canister/candid.did).\nAdditionally all the api types for Rust canisters are in the [`interface`](src/interface/src/lib.rs) crate\n(will probably use a rename in the future) that doesn't depend on `helios` directly but on a lighter `ethers-core` for that matter.\n\nThe canister mostly just exposes functions from the underlying helios client. Most of the users familiar with\nEthereum will likely be familiar with them too so there is no point in trying to be innovative there. Those usually just\nhave the same name, take the same arguments, and return the same types (or their `Candid` counterparts).\n\nFunctions are categorized as update or query based on whether they involve making an RPC call to the execution node or not.\nExamples of queries can be `get_block_number` or `get_gas_price`, which just take information from already synchronized blocks.\nExamples of updates are calling the function of a contract or estimating the gas for a transaction as it requires fetching\nthe addresses and other data.\n\nFor the smart contract standards like the ERC20 or ERC721 the exposed API's are in form `${standard_name}_${function_name}`\neg. `erc20_balance_of`. The parameters to those functions are Candid's equivalents for the parameters from the contract's standard ABI.\nIt is on the ethereum canister to properly encode them before making a `call`.\n\nPresently, the setup function is the only exception to the aforementioned categorization. It is responsible for configuring and\ninitiating the helios client. It is required to be called before any other function, otherwise, the called function will return an error.\nIt takes urls to the consensus node and execution node the client will connect to, as well as the type of \nnetwork it should operate on and an optional weak subjectivity checkpoint, a trusted hash of a block that nodes agree on. If not\nprovided, the last checkpoint from provided consensus node will be taken. Please note that providing a checkpoint that is too old\ncan result in much more https outcalls and computations needed to reach the synchronization,\nin some cases exceeding the limits of an update call.\n\n### Synchronization\n\nThe background loop utilizes `ic_cdk_timers::set_timer_interval` and operates at 12-second intervals, mirroring the Ethereum slot time.\nThis is the only place where the running helios client is ever mutated and the time of locking for the updates was reduced to the\nrequired minimum which should be unnoticeable.\n\n### Upgrades\n\nThe canister stores its configuration and the latest checkpoint it has reached in stable memory. When upgrading the canister\nit should restart the helios client itself with the previous configuration and the checkpoint that was already trusted. The last\n64 blocks will be re-fetched.\n\n### Error handling\n\nExploring error handling strategies for inter-canister calls reveals two options: returning Results or triggering panics upon errors.\nThe panicking way was chosen, as the `Result`s seemed less ergonomic for potential developers and they feel a bit inconsistent\nas `CallResult` already has `CanisterError` and `CanisterReject` variants. Also going with the panics and having the state reverted\nfelt more familiar with the smart contracts on other blockchains.\n\n## Implementation notes\n\n### Helios\n\nThe foundation of this canister. The ethereum canister depends on the `client`, `consensus`, `execution`, and `common` helios crates.\nTo be able to use a helios on the ICP it had [to be forked](https://github.com/eigerco/helios). The fork introduced many\nchanges to the internals of helios thus making itself not-upstreamable in its current form. Probably it is possible to upstream\nsome of those changes and maybe even make it compatible with the ICP ecosystem but this is considered to be not a trivial task\nwith many possible ways of achieving the goals that should be considered and brainstormed.\nThe changes were made in a manner that helios still works perfectly fine for native, but when targeting wasm it only supports\nthe ICP and no longer the browsers.\n\nThe changes include:\n- updating the `helios` and making it compatible with the rust `stable` toolchain\n- getting rid of any occurrences of `wasm-bindgen`, `glue-timers`, and other browser-related wasm dependencies\n- getting rid of `tokio`-related stuff that is incompatible with wasm\n- using `ethers-core` where possible and removing `ethers-providers` entirely\n- replace `reqwest` with https outcalls when targeting wasm\n- update the `revm` to v3 and introduce a way to fetch missing slots outside in an async manner\n- change the updating logic to only lock for a short period and add proper shutdown and cleanup\n\nFor the complete list of changes see this [comparison](https://github.com/a16z/helios/compare/master...eigerco:helios:master).\n\n### Ethers\n\nThe `ethers` crate is the building block of helios and the home for most of the types from the Ethereum ecosystem. It also\nprovides the utilities to generate types and encoding logic from the smart contract's ABI. It would be really useful to have this\ncrate working for the ICP ecosystem completely nonetheless this is considered a nontrivial task. The main problem is that it\nhas already good support for browser-side wasm and some of its subcrates utilize not supported crates heavily. The main pain\npoint we've encountered was the `ethers-providers` crate. The best way to approach this would be to support ICP in crates\nlike [`instant`](https://github.com/sebcrozet/instant) and [`futures-timer`](https://github.com/async-rs/futures-timer) or even\n[`gloo-timers`](https://github.com/rustwasm/gloo), guard the http implementation behind the feature flag and add a way for a user\nto add a custom provider.\n\nThere was one change made to the ethers, allowing the use of `ethers-contract` without the need to depend on `ethers-providers`\nand it was [already upstreamed](https://github.com/gakonst/ethers-rs/pull/2536).\n\n## Cost analysis\n\nThe measurements presented below were acquired on a local dfx network setup. These measurements encompass supplementary\nlogs and logic integrated into the canister's code and its dependencies. They are not precise and shouldn't be taken as truth,\nrather just to shed some light on the potential costs.\n\n|                             | call cost [cycles] | payments [cycles] |  instructions | https outcalls |\n|:----------------------------|-------------------:|------------------:|--------------:|---------------:|\n| setup                       |    122,579,275,431 |   122,553,254,989 | 1,149,129,243 |             74 |\n| advance                     |     14,126,362,419 |    12,794,806,467 |   839,856,523 |              8 |\n| get_gas_price               |            105,594 |                 0 |         5,605 |              0 |\n| get_block_number            |            101,230 |                 0 |         2,336 |              0 |\n| erc20_balance_of **[SHIB]** |     14,438,775,194 |    14,438,169,162 |    10,996,872 |              9 |\n| erc20_balance_of **[BNB]**  |     14,439,107,417 |    14,438,470,723 |    12,031,945 |              9 |\n| erc20_balance_of **[USDT]** |     17,655,037,455 |    17,654,021,703 |    24,259,653 |             11 |\n| erc721_owner_of **[ENS]**   |     17,654,474,853 |    17,653,498,044 |    23,011,079 |             11 |\n| erc721_owner_of **[DREAD]** |     17,656,442,523 |    17,655,209,030 |    31,469,631 |             11 |\n| erc721_owner_of **[MIL]**   |     20,866,407,305 |    20,865,374,497 |    25,107,423 |             13 |\n\nAll the functions were measured with the advancing (sync loop) disabled except the advance itself.\n\nAll the functions were measured with 5 repetitions and the maximum acquired value was presented in the table.\n\nThe `setup` function was measured with fetching the latest checkpoint from the consensus node.\n\nThe 'call cost' was measured using the `dfx canister status` command before and after executing the case.\n\nThe 'payments' was measured using the difference between `ic_cdk::api::canister_balance` invoked at the beginning\nand the end of the function. It should reflect the amount spent for https outcalls.\n\nThe 'instructions' were measured using the difference between `ic_cdk::api::performance_counter` invoked at the\nbeginning and the end of the function. It should reflect the number of wasm instructions executed during the call.\n\nThe 'https outcalls' was measured by counting the calls to the `http::get` and `http::post` functions.\n\n## Next steps\n\n### Optimization ideas\n\nHttps outcalls are the biggest factor of the costs and it should be the primary focus when optimizing.\n\nAmong the methods, the setup call incurs the highest cost due to its requirement to initialize everything for the first time\nand retrieve the last 64 blocks. Unfortunately, it uses mostly the beacon API which is a restful http api, not a json-rpc like\nthe execution API, so requests cannot be batched easily. And even then, a single block can be bigger than 1MB so fetching two\nat the time could exceed the limit [of 2MB](https://internetcomputer.org/docs/current/developer-docs/integrations/https-outcalls/https-outcalls-how-it-works)\nfor the https outcall.\n\nHowever, it seems to be possible to heavily optimize all the execution RPC calls. Here is a sample log from the smart contract call.\n\n\u003cdetails\u003e\n\n\u003csummary\u003eLog - erc721_owner_of \u003cb\u003e[MIL]\u003c/b\u003e\u003c/summary\u003e\n\n```\ncommon::http::icp] POST https://ethereum.publicnode.com {\"id\":0,\"jsonrpc\":\"2.0\",\"method\":\"eth_createAccessList\",\"params\":[{\"type\":\"0x02\",\"to\":\"0x5af0d9827e0c53e4799bb226655a1de152a425a5\",\"gas\":\"0x5f5e100\",\"data\":\"0x6352211e0000000000000000000000000000000000000000000000000000000000001e5d\",\"accessList\":[],\"maxPriorityFeePerGas\":\"0x0\",\"maxFeePerGas\":\"0x0\"},\"0x111d0e5\"]}\ncommon::http::icp] POST https://ethereum.publicnode.com {\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"eth_getProof\",\"params\":[\"0x5af0d9827e0c53e4799bb226655a1de152a425a5\",[\"0x54a8fc6a2a9e7e91706be865e93ec51a73727c47639b740f76ecd87ef67aaffa\",\"0x0000000000000000000000000000000000000000000000000000000000000002\",\"0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb9789\"],\"0x111d0e5\"]}\ncommon::http::icp] POST https://ethereum.publicnode.com {\"id\":2,\"jsonrpc\":\"2.0\",\"method\":\"eth_getProof\",\"params\":[\"0x0000000000000000000000000000000000000000\",[],\"0x111d0e5\"]}\ncommon::http::icp] POST https://ethereum.publicnode.com {\"id\":3,\"jsonrpc\":\"2.0\",\"method\":\"eth_getProof\",\"params\":[\"0x5af0d9827e0c53e4799bb226655a1de152a425a5\",[],\"0x111d0e5\"]}\ncommon::http::icp] POST https://ethereum.publicnode.com {\"id\":4,\"jsonrpc\":\"2.0\",\"method\":\"eth_getProof\",\"params\":[\"0x388c818ca8b9251b393131c08a736a67ccb19297\",[],\"0x111d0e5\"]}\ncommon::http::icp] POST https://ethereum.publicnode.com {\"id\":5,\"jsonrpc\":\"2.0\",\"method\":\"eth_getCode\",\"params\":[\"0x388c818ca8b9251b393131c08a736a67ccb19297\",\"0x111d0e5\"]}\ncommon::http::icp] POST https://ethereum.publicnode.com {\"id\":6,\"jsonrpc\":\"2.0\",\"method\":\"eth_getCode\",\"params\":[\"0x5af0d9827e0c53e4799bb226655a1de152a425a5\",\"0x111d0e5\"]}\ncommon::http::icp] POST https://ethereum.publicnode.com {\"id\":7,\"jsonrpc\":\"2.0\",\"method\":\"eth_getCode\",\"params\":[\"0x5af0d9827e0c53e4799bb226655a1de152a425a5\",\"0x111d0e5\"]}\n\nexecution::evm] fetch basic evm state for address=0x0000000000000000000000000000000000000000\nexecution::evm] fetch basic evm state for address=0x388c818ca8b9251b393131c08a736a67ccb19297\nexecution::evm] fetch basic evm state for address=0x5af0d9827e0c53e4799bb226655a1de152a425a5\nexecution::evm] fetch evm state for address=0x5af0d9827e0c53e4799bb226655a1de152a425a5, slot=38292851690199200197994960496712248457597506228771887340217700269531888005114\n\nexecution::evm] Missing slots: MissingSlots { address: 0x5af0d9827e0c53e4799bb226655a1de152a425a5, slots: [0x54a8fc6a2a9e7e91706be865e93ec51a73727c47639b740f76ecd87ef67aaffa] }\ncommon::http::icp] POST https://ethereum.publicnode.com {\"id\":8,\"jsonrpc\":\"2.0\",\"method\":\"eth_getProof\",\"params\":[\"0x5af0d9827e0c53e4799bb226655a1de152a425a5\",[\"0x54a8fc6a2a9e7e91706be865e93ec51a73727c47639b740f76ecd87ef67aaffa\"],\"0x111d0e5\"]}\ncommon::http::icp] POST https://ethereum.publicnode.com {\"id\":9,\"jsonrpc\":\"2.0\",\"method\":\"eth_getCode\",\"params\":[\"0x5af0d9827e0c53e4799bb226655a1de152a425a5\",\"0x111d0e5\"]}\n\nexecution::evm] fetch basic evm state for address=0x0000000000000000000000000000000000000000\nexecution::evm] fetch basic evm state for address=0x388c818ca8b9251b393131c08a736a67ccb19297\nexecution::evm] fetch basic evm state for address=0x5af0d9827e0c53e4799bb226655a1de152a425a5\nexecution::evm] fetch evm state for address=0x5af0d9827e0c53e4799bb226655a1de152a425a5, slot=38292851690199200197994960496712248457597506228771887340217700269531888005114\nexecution::evm] fetch evm state for address=0x5af0d9827e0c53e4799bb226655a1de152a425a5, slot=2\n\nexecution::evm] Missing slots: MissingSlots { address: 0x5af0d9827e0c53e4799bb226655a1de152a425a5, slots: [0x0000000000000000000000000000000000000000000000000000000000000002] }\ncommon::http::icp] POST https://ethereum.publicnode.com {\"id\":10,\"jsonrpc\":\"2.0\",\"method\":\"eth_getProof\",\"params\":[\"0x5af0d9827e0c53e4799bb226655a1de152a425a5\",[\"0x0000000000000000000000000000000000000000000000000000000000000002\",\"0x54a8fc6a2a9e7e91706be865e93ec51a73727c47639b740f76ecd87ef67aaffa\"],\"0x111d0e5\"]}\ncommon::http::icp] POST https://ethereum.publicnode.com {\"id\":11,\"jsonrpc\":\"2.0\",\"method\":\"eth_getCode\",\"params\":[\"0x5af0d9827e0c53e4799bb226655a1de152a425a5\",\"0x111d0e5\"]}\n\nexecution::evm] fetch basic evm state for address=0x0000000000000000000000000000000000000000\nexecution::evm] fetch basic evm state for address=0x388c818ca8b9251b393131c08a736a67ccb19297\nexecution::evm] fetch basic evm state for address=0x5af0d9827e0c53e4799bb226655a1de152a425a5\nexecution::evm] fetch evm state for address=0x5af0d9827e0c53e4799bb226655a1de152a425a5, slot=38292851690199200197994960496712248457597506228771887340217700269531888005114\nexecution::evm] fetch evm state for address=0x5af0d9827e0c53e4799bb226655a1de152a425a5, slot=2\nexecution::evm] fetch evm state for address=0x5af0d9827e0c53e4799bb226655a1de152a425a5, slot=29102676481673041902632991033461445430619272659676223336789171408008386418569\n\nexecution::evm] Missing slots: MissingSlots { address: 0x5af0d9827e0c53e4799bb226655a1de152a425a5, slots: [0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb9789] }\ncommon::http::icp] POST https://ethereum.publicnode.com {\"id\":12,\"jsonrpc\":\"2.0\",\"method\":\"eth_getProof\",\"params\":[\"0x5af0d9827e0c53e4799bb226655a1de152a425a5\",[\"0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb9789\",\"0x54a8fc6a2a9e7e91706be865e93ec51a73727c47639b740f76ecd87ef67aaffa\",\"0x0000000000000000000000000000000000000000000000000000000000000002\"],\"0x111d0e5\"]}\ncommon::http::icp] POST https://ethereum.publicnode.com {\"id\":13,\"jsonrpc\":\"2.0\",\"method\":\"eth_getCode\",\"params\":[\"0x5af0d9827e0c53e4799bb226655a1de152a425a5\",\"0x111d0e5\"]}\n\nexecution::evm] fetch basic evm state for address=0x0000000000000000000000000000000000000000\nexecution::evm] fetch basic evm state for address=0x388c818ca8b9251b393131c08a736a67ccb19297\nexecution::evm] fetch basic evm state for address=0x5af0d9827e0c53e4799bb226655a1de152a425a5\nexecution::evm] fetch evm state for address=0x5af0d9827e0c53e4799bb226655a1de152a425a5, slot=38292851690199200197994960496712248457597506228771887340217700269531888005114\nexecution::evm] fetch evm state for address=0x5af0d9827e0c53e4799bb226655a1de152a425a5, slot=2\nexecution::evm] fetch evm state for address=0x5af0d9827e0c53e4799bb226655a1de152a425a5, slot=29102676481673041902632991033461445430619272659676223336789171408008386418569\n```\n\n\u003c/details\u003e\n\nFirstly, optimizing `Evm::batch_fetch_accounts` could involve consolidating requests to fetch codes and proofs into a single operation after establishing the access list.\nAlso `ExecutionClient::fetch_account` could fetch the code and proof in one go. That would reduce the amount of https outcalls used in this specific case from 13 to 5.\nAnother avenue to explore is determining the additional required slots for EVM execution and pre-fetching them. Alternatively, seeking a method to acquire a\ncomprehensive list of missing addresses from revm in a single instance—without resimulation—could be pursued, albeit this might entail modifications in revm.\nDepending on the outcomes, this might lead to a potential reduction in the number of calls from 5 down to 3 or possibly even 2.\n\nIf any of the urls provided as a configuration for the canister leads to the http redirections (eg. \"https://www.lightclientdata.org\" results in redirects), then all\nthe https outcalls will be repeated for the original url and then for the redirected on. Supporting redirections can be useful when self hosting nodes for this project,\nso it would be good to optimize it in a way it first checks for the redirections and then storing the actual final url for the requests. The original url can then be kept\nas a fallback url in case the redirected one is no longer valid.\n\n### Canister \n\nThe initial development was mainly focused on getting the `helios` and `ethers` to the point where they operate correctly in the ICP environment.\nThe next step would be to apply all the best practices for canisters implementation including things like controllers, metrics collection,\nAPI design, handling payments, and guarding some methods from public usage.\n\nAlso exposing the full helios API and seeking community input sounds like a neat idea.\n\n## Project \u0026 Roadmap\n\n[Milestones](https://github.com/eigerco/ethereum-canister/milestones)\n\n[Roadmap](https://github.com/orgs/eigerco/projects/11/views/2)\n\nThe milestones in the roadmap have been completed. This project was completed with support from Dfinity and the Internet Computer community.\n\n## About [Eiger](https://www.eiger.co)\n\nWe are engineers. We contribute to various ecosystems by building low level implementations and core components. We built this integration because we belive in Ethereum, Internet Computer and wanted to build verifyable cross-chain communication between the two.\n\nContact us at hello@eiger.co\nFollow us on [X/Twitter](https://x.com/eiger_co)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feigerco%2Fethereum-canister","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feigerco%2Fethereum-canister","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feigerco%2Fethereum-canister/lists"}