Complete Guide to Working with Image Assets on the Bitfinity EVM

Complete Guide to Working with Image Assets on the Bitfinity EVM

In the digital age, the creation and dissemination of content on the World Wide Web have become fundamental components of our online experiences. To enable the seamless functioning of websites and applications, the efficient management of assets like HTML, CSS, images, and JavaScript and json files is paramount. Working with assets constitutes a foundational aspect of development, directly impacting the speed, performance, and overall user experience of online platforms. In this exploration, we will embark on a journey through the intricacies of handling and optimizing these essential assets, emerging techniques, and the vital role they play in shaping the modern internet landscape.

You can upload assets to the IC and get their url by using the asset Canister. The asset canister provides you with a mechanism to store and retrieve static assets from a canister deployed on the Internet Computer. It is generally used to serve HTML, JS, and CSS assets for single page applications, i.e. your application's frontend. Let’s create a folder called NFTproject and have create a dfx.json file.

In order to continue with this, you must first have dfx installed locally. You can read on how to install dfx here How do I install dfx? . It’s a very quick to read article on how to get this done.

In your root directory of your frontend code, create a dfx.json file. The source in the case is the project/assets and projects/src folder. Let's use the NNS Dapp to create our canister. We can access that here NNS Dapp . You wil need to create a canister and fund that canister with some cycles. Dfinity already has a detailed article on this here : How to Deploy Your First Canister Smart Contract Using the NNS Dapp. The UI has been updated but the procedure is the same.

  "canisters": {
    "www": {
      "frontend": {
        "entrypoint": "project/src/index.html"
      "source": [
      "type": "assets"
  "defaults": {
    "build": {
      "args": "",
      "packtool": ""
  "version": 1

Create a file on in the source of your project called canister_ids.json and update with the code below.

    "www": {
        "ic": "created_canister_id_goes_here"

From the article on how to use nns to create a canister, we learn that we associate a principal id to the canister id as a controller so only the device with that principal id can deploy or update the canister.
Now run dfx deploy --network ic --no-wallet. After the deployment is complete, you would be able to access your website on example https://{your_canister_id} Example is Now you have your project running directly on the Internet Computer’s blockchain and can access your assets like

Other Configurations,
You have the option to customize the way the asset canister handles requests for specific assets by specifying your preferred configuration within a file titled .ic-assets.json.

Within .ic-assets.json, each entry permits the specification of a glob pattern, along with the headers to be included in the response for any file that corresponds to the pattern. You also have the ability to determine whether redirects should be executed from an uncertified endpoint to a certified endpoint for a given filename pattern. You can read on the Asset Canister here : Asset canister | Internet Computer

    "match": "**/*",
    "headers": {
      // Security: The Content Security Policy (CSP) given below aims at working with many apps rather than providing maximal security.
      // We recommend tightening the CSP for your specific application. Some recommendations are as follows:
      // - Use the CSP Evaluator ( to validate the CSP you define.
      // - Follow the “Strict CSP” recommendations ( However, note that in the context of the IC,
      //   nonces cannot be used because the response bodies must be static to work well with HTTP asset certification.
      //   Thus, we recommend to include script hashes (in combination with strict-dynamic) in the CSP as described
      //   in in section “What if my site is static and I can't add nonces to scripts?”.
      //   See for example the II CSP (
      // - It is recommended to tighten the connect-src directive. With the current CSP configuration the browser can
      //   make requests to https://*, hence being able to call any canister via{canister-ID}.
      //   This could potentially be used in combination with another vulnerability (e.g. XSS) to exfiltrate private data.
      //   The developer can configure this policy to only allow requests to their specific canisters,
      //   e.g: connect-src 'self'{my-canister-ID}, where {my-canister-ID} has the following format: aaaaa-aaaaa-aaaaa-aaaaa-aaa
      // - It is recommended to configure style-src, style-src-elem and font-src directives with the resources your canister is going to use
      //   instead of using the wild card (*) option. Normally this will include 'self' but also other third party styles or fonts resources (e.g: or other CDNs)

      // Notes about the CSP below:
      // - script-src 'unsafe-eval' is currently required because agent-js uses a WebAssembly module for the validation of bls signatures.
      //   There is currently no other way to allow execution of WebAssembly modules with CSP.
      //   See:
      // - We added img-src data: because data: images are used often.
      // - frame-ancestors: none mitigates clickjacking attacks. See
      "Content-Security-Policy": "default-src 'self';script-src 'self' 'unsafe-eval';connect-src 'self' https://*;img-src 'self' data:;style-src * 'unsafe-inline';style-src-elem * 'unsafe-inline';font-src *;object-src 'none';base-uri 'self';frame-ancestors 'none';form-action 'self';upgrade-insecure-requests;",
      // Security: The permissions policy disables all features for security reasons. If your site needs such permissions, activate them.
      // To configure permissions go here
      "Permissions-Policy": "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(), clipboard-write=(), gamepad=(), speaker-selection=(), conversion-measurement=(), focus-without-user-activation=(), hid=(), idle-detection=(), interest-cohort=(), serial=(), sync-script=(), trust-token-redemption=(), window-placement=(), vertical-scroll=()",
      // Security: Mitigates clickjacking attacks.
      // See:
      "X-Frame-Options": "DENY",
      // Security: Avoids forwarding referrer information to other origins.
      // See:
      "Referrer-Policy": "same-origin",
      // Security: Tells the user’s browser that it must always use HTTPS with your site.
      // See:
      "Strict-Transport-Security": "max-age=31536000; includeSubDomains",
      // Security: Prevents the browser from interpreting files as a different MIME type to what is specified in the Content-Type header.
      // See:
      "X-Content-Type-Options": "nosniff",
      // Security: Enables browser features to mitigate some of the XSS attacks. Note that it has to be in mode=block.
      // See:
      "X-XSS-Protection": "1; mode=block"
    // redirect all requests from to (this redirection is the default)
    "allow_raw_access": false

Example Solidity code for mapping image URLs associated with user’s address

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract ImageURLStorage {
    // Mapping to store image URLs associated with a user's address
    mapping(address => string) public imageUrls;

    // Function to set the image URL for a user
    function setImageUrl(string memory url) public {
        imageUrls[msg.sender] = url;

    // Function to get the image URL for a user
    function getImageUrl() public view returns (string memory) {
        return imageUrls[msg.sender];

This is not always the case though, in some cases, you would want to save image hashes instead for somethings like NFTs. With that, you would need to use IPFS which works fine on the Bitfinity EVM. You can check out this article on NFTs here How to deploy a Solidity contract to ICP using the Bitfinity EVM

Blockchain technology provides a decentralized, secure, and transparent way to handle assets, offering solutions to challenges related to trust, ownership, and authenticity. It has the potential to reshape the way we think about asset management, making it more efficient and secure by eliminating the need for intermediaries.

Connect with Bitfinity Network

Bitfinity Wallet | Bitfinity Network | Twitter | Telegram | Discord | Github

*Important Disclaimer: While every effort is made on this website to provide accurate information, any opinions expressed or information disseminated do not necessarily reflect the views of Bitfinity itself. The information provided here is for general informational purposes only and should not be considered as financial advice.