diff --git a/docs/developer-docs/defi/tokens/token-standards.mdx b/docs/developer-docs/defi/tokens/token-standards.mdx index 7dbbe456a2..e907b3fd83 100644 --- a/docs/developer-docs/defi/tokens/token-standards.mdx +++ b/docs/developer-docs/defi/tokens/token-standards.mdx @@ -40,7 +40,7 @@ The ICP token is the network's native token used for various utility and governa - Rewarding neurons for staking ICP into the neuron and voting on governance proposals. -View an example dapp for [ICP transfers](https://github.com/dfinity/examples/tree/master/motoko/icp_transfer). +View an example dapp for [ICP transfers](https://github.com/dfinity/examples/tree/master/motoko/token_transfer). ## ICRC-1 diff --git a/docs/developer-docs/getting-started/explore-examples.mdx b/docs/developer-docs/getting-started/explore-examples.mdx index 7bb6f0b9e8..49ec10405b 100644 --- a/docs/developer-docs/getting-started/explore-examples.mdx +++ b/docs/developer-docs/getting-started/explore-examples.mdx @@ -74,7 +74,7 @@ https://2drrs-wqaaa-aaaab-qblbq-cai.icp1.io The **backend canister** URL will open the [Candid UI](/docs/current/developer-docs/smart-contracts/candid/candid-concepts) which can interact directly with the backend canister's methods. A **method** is a function exposed by the canister that can be called by a user, another canister, or the application's frontend. -The **frontend** URL will open the application's frontend user interface. To create this frontend, the project's React files (shown in the ICP Ninja code editor within the `frontend/` folder) have been compiled and deployed as an [asset canister](/docs/current/developer-docs/web-apps/application-frontends/default-frontend). +The **frontend** URL will open the application's frontend user interface. To create this frontend, the project's React files (shown in the ICP Ninja code editor within the `frontend/` folder) have been compiled and deployed as an [asset canister](/docs/current/developer-docs/web-apps/application-frontends/overview). :::info This application will be live for 20 minutes. After 20 minutes, it will expire, and the URLs will become invalid. The project code will still be accessible, and you can redeploy the application at any time to view it for another 20 minutes. diff --git a/docs/developer-docs/smart-contracts/overview/introduction.mdx b/docs/developer-docs/smart-contracts/overview/introduction.mdx index 724264107c..09f42fdae2 100644 --- a/docs/developer-docs/smart-contracts/overview/introduction.mdx +++ b/docs/developer-docs/smart-contracts/overview/introduction.mdx @@ -6,7 +6,7 @@ import { MarkdownChipRow } from "/src/components/Chip/MarkdownChipRow"; import { GlossaryTooltip } from "/src/components/Tooltip/GlossaryTooltip"; -# Introduction +# What are canisters? diff --git a/docs/developer-docs/web-apps/application-frontends/default-frontend.mdx b/docs/developer-docs/web-apps/application-frontends/default-frontend.mdx deleted file mode 100644 index 811319ce9f..0000000000 --- a/docs/developer-docs/web-apps/application-frontends/default-frontend.mdx +++ /dev/null @@ -1,97 +0,0 @@ ---- -keywords: [beginner, tutorial, frontend, frontend canister, asset canister, assets, default] ---- - -import { MarkdownChipRow } from "/src/components/Chip/MarkdownChipRow"; -import { GlossaryTooltip } from "/src/components/Tooltip/GlossaryTooltip"; - -# Hosting application frontends using an asset canister - - - -When a dapp is deployed to ICP, it has the ability to host web assets natively. An ICP project's frontend is commonly referred to as the **frontend canister**, as it is a canister that hosts the frontend of the application. - -Dapps may have complex frontends that serve dynamic, interactive interfaces, or they can be simple, static webpages using HTML and CSS. Web assets can come in many forms, such as CSS, JavaScript, React, HTML, images, videos, or streaming content. Most web apps contain a combination of several assets and frameworks together. - -## What is an asset canister? - -When a project has a frontend canister configured in its `dfx.json` file, `dfx` will compile the project's web assets into an **asset canister**. An asset canister is a special type of custom canister that uses Rust to compile the project's asset files into a Wasm module that can be installed and deployed to ICP. This is because canisters deployed on ICP must have a Wasm module. - -## Application URLs - -Frontend canisters serve the application's web assets via a URL that contains the canister's ID. Local deployments use local URLs such as `http://127.0.0.1:4943/?canisterId=`, while applications deployed to the mainnet use public URLs containing the canister's ID followed by `.ic0.app`, `.icp0.io` or `raw.icp0.io`. - -### Raw HTTP interfaces - -The `raw.icp0.io` domain provides a way to access the raw HTTP interface of that canister. For local deployments that want to simulate the behavior of the `raw.icp0.io` domain, you must implement a method in your canister that consumes an HTTP request and outputs an HTTP response. Here is an example written in Motoko: - -```motoko no-repl -public shared(msg) func http_request(req: HttpRequest) : async HttpResponse { - { - status = { code = 200; reason = "OK" }; - headers = [( "Content-Type", "text/plain" )]; - body = Text.encodeUtf8("Hello, World!"); - } -}; -``` - -## Default `dfx` project configuration - -By default, each project created with `dfx new` will include a frontend canister unless the `--no-frontend` flag is used. `dfx` supports default templates for the SvelteKit, React, Vue, and Vanilla JS frameworks, or you can select "No JS template" if you'd like no frontend template to be used. - -Frontend canisters will be defined in your project's `dfx.json` file as `PROJECT_NAME_frontend`. - -```json -{ -  "canisters": { -    "PROJECT_NAME_backend": { -      "main": "src/PROJECT_NAME_backend/main.mo", -      "type": "motoko" -    }, -    "PROJECT_NAME_frontend": { -      "dependencies": [ -        "PROJECT_NAME_backend" -      ], -      "source": [ -        "src/PROJECT_NAME_frontend/dist" -      ], -      "type": "assets", -      "workspace": "PROJECT_NAME_frontend" -    } -  }, -  "defaults": { -    "build": { -      "args": "", -      "packtool": "" -    } -  }, -  "output_env_file": ".env", -  "version": 1 -} -``` - -Frontend canisters have the following default configuration settings: - -- Frontend assets for your project are compiled into their own canister. In this case, a canister named `PROJECT_NAME_frontend`. - -- The `PROJECT_NAME_frontend` canister has a default dependency on the `PROJECT_NAME_backend` canister for the project. - -- The `source` setting specifies the paths to the `src` directory that are used for the web assets that will be compiled into your `PROJECT_NAME_frontend` canister when you build the project. - -- The `type` setting specifies that the `PROJECT_NAME_frontend` should be deployed using the [asset canister](https://github.com/dfinity/sdk/tree/master/src/canisters/frontend/ic-frontend-canister), which compiles the web assets listed in `source` into a Wasm module. - -### Default frontend canister files - -The default files included in the frontend canister will depend on the framework you chose when you created your project, or you can use an [existing application frontend](existing-frontend.mdx). - -## Example frontend canisters - -A few example frontend canisters you can reference include: - -- [ICP Ninja sample projects](https://icp.ninja). - -- [Minimal counter dapp](https://github.com/dfinity/examples/tree/master/motoko/minimal-counter-dapp/src/minimal_dapp_frontend). - -- [Random maze](https://github.com/dfinity/examples/tree/master/motoko/random_maze/src/random_maze_assets). - -- [IC Point of sale](https://github.com/dfinity/examples/tree/master/motoko/ic-pos/src/icpos_frontend).q diff --git a/docs/developer-docs/web-apps/application-frontends/existing-frontend.mdx b/docs/developer-docs/web-apps/application-frontends/existing-frontend.mdx index 71d1154de4..37ed4bad54 100644 --- a/docs/developer-docs/web-apps/application-frontends/existing-frontend.mdx +++ b/docs/developer-docs/web-apps/application-frontends/existing-frontend.mdx @@ -9,11 +9,11 @@ import Tabs from "@theme/Tabs"; -# Using an existing frontend application +# Using an existing application frontend -While numerous starter projects and examples exist for those who prefer to start from scratch, deploying an existing frontend application as a frontend canister is also a viable option for building an application on ICP. +While numerous starter projects and examples exist for those who prefer to start from scratch, deploying an existing application frontend as a frontend canister is also a viable option for building an application on ICP. :::caution Server methods such as `getServerSideProps` are not supported, since it will be deployed as a client-only application. diff --git a/docs/developer-docs/web-apps/application-frontends/overview.mdx b/docs/developer-docs/web-apps/application-frontends/overview.mdx index c64548f258..d2da2ca0bb 100644 --- a/docs/developer-docs/web-apps/application-frontends/overview.mdx +++ b/docs/developer-docs/web-apps/application-frontends/overview.mdx @@ -5,16 +5,16 @@ keywords: [beginner, concept, frontend, asset canister, assets] import { MarkdownChipRow } from "/src/components/Chip/MarkdownChipRow"; import { GlossaryTooltip } from "/src/components/Tooltip/GlossaryTooltip"; -# What is an asset canister? +# Application frontends -Dapps often utilize a frontend interface to facilitate user interaction with the application through elements such as a dashboard, account page, media player, or other assets that are displayed in the web browser. These assets may be in the form of HTML, JavaScript, CSS, or other frontend frameworks like React. +Dapps often utilize a frontend interface to facilitate user interaction with the application through elements such as a dashboard, account page, media player, or other assets that are displayed in the web browser. These assets may be in the form of HTML, JavaScript, CSS, or use frontend frameworks like React. -Application frontends are often referred to as **asset canisters** on ICP, since they are canisters defined as `"type": "asset"` in the `dfx.json` file for a project. Projects created by the `dfx new` command have the option to include a default asset canister named using the project's name and the suffix `_frontend`, such as `hello_world_frontend`. +Application frontends are hosted on ICP using **asset canisters**. Asset canisters compile frontend assets like CSS and JavaScript into a Wasm module that can be deployed on ICP as a canister. :::info -The terms 'asset canister' and 'frontend canister' are often used interchangeably. +The terms 'asset canister' and 'frontend canister' are sometimes used interchangeably. **Asset** canister is typically used to describe the Rust code `dfx` uses in the background to compile a project's frontend assets into Wasm. **Frontend** canister is typically used as a general term to describe a project's frontend. ::: Application frontends exist in the following forms: @@ -33,7 +33,7 @@ When a canister is configured in a project's `dfx.json` file as `"type": "asset" ## Default settings -By default, all projects created with `dfx new` have the option to include a frontend canister in the project. This default frontend canister is written in Rust and uses one of the following frontend frameworks: SvelteKit, React, Vue, or Vanilla JS. The user can choose the framework through the `dfx new` interactive prompt in `dfx` versions 0.17.0 and newer. +By default, all projects created with `dfx new` have the option to include a frontend canister in the project. This default frontend canister is written in Rust and may use one of the following frontend frameworks: SvelteKit, React, Vue, or Vanilla JS. The user can choose the framework through the `dfx new` interactive prompt in `dfx` versions 0.17.0 and newer. For asset canisters created by `dfx new`, the default configuration will include: @@ -107,7 +107,25 @@ The [Express HTTP server](https://demergent-labs.github.io/azle/) package via Az The frontend canister can host roughly 1GiB in static files. It is recommended that you distribute your files across multiple canisters if the total size of all your assets begins to exceed this amount. Once you exceed this figure, your canister may fail to upgrade. -## Dynamic URLs +## Application URLs + +Frontend canisters serve the application's web assets via a URL that contains the canister's ID. Local deployments use local URLs such as `http://127.0.0.1:4943/?canisterId=`, while applications deployed to the mainnet use public URLs containing the canister's ID followed by `.ic0.app`, `.icp0.io` or `raw.icp0.io`. + +### Raw HTTP interfaces + +The `raw.icp0.io` domain provides a way to access the raw HTTP interface of that canister. For local deployments that want to simulate the behavior of the `raw.icp0.io` domain, you must implement a method in your canister that consumes an HTTP request and outputs an HTTP response. Here is an example written in Motoko: + +```motoko no-repl +public shared(msg) func http_request(req: HttpRequest) : async HttpResponse { + { + status = { code = 200; reason = "OK" }; + headers = [( "Content-Type", "text/plain" )]; + body = Text.encodeUtf8("Hello, World!"); + } +}; +``` + +### Dynamic URLs Dynamic URLs are currently not supported by the default frontend canister. @@ -138,7 +156,3 @@ let path = HttpCertificationPath::wildcard("/js"); - [Vite + SvelteKit + Motoko](https://github.com/letmejustputthishere/vite-sveltekit-motoko-ii/tree/main) template example. - Deploying an [existing Next.js application](./existing-frontend.mdx) as a frontend canister. - -## Next steps - -[Learn how to use and customize the default frontend canister](default-frontend.mdx). \ No newline at end of file diff --git a/docs/tutorials/developer-journey/level-1/1.3-first-dapp.mdx b/docs/tutorials/developer-journey/level-1/1.3-first-dapp.mdx index 447f680c6a..3884d1f3bb 100644 --- a/docs/tutorials/developer-journey/level-1/1.3-first-dapp.mdx +++ b/docs/tutorials/developer-journey/level-1/1.3-first-dapp.mdx @@ -623,7 +623,7 @@ Start by creating the file `src/poll_frontend/src/index.html` that contains the **What does this code do?** The HTML code above is a simple form that provides the end user with options to select using radio buttons. There is nothing 'Web3' or specific to ICP about this code. -Then, the `` tag is used to include some basic CSS for the page's styling. To learn more about adding a stylesheet, see: [add a stylesheet](/docs/current/developer-docs/web-apps/application-frontends/default-frontend). +Then, the `` tag is used to include some basic CSS for the page's styling. To learn more about adding a stylesheet, see: [add a stylesheet](/docs/current/developer-docs/web-apps/application-frontends/overview). ::: Next, you will need a `webpack.config.js` file that defines several parameters to facilitate your local development web server. diff --git a/plugins/utils/redirects.js b/plugins/utils/redirects.js index 99851dfc7d..d775251652 100644 --- a/plugins/utils/redirects.js +++ b/plugins/utils/redirects.js @@ -54,7 +54,7 @@ const redirects = ` /docs/rust-guide/rust-intro /docs/current/developer-docs/backend/rust/ /docs/languages/languages-overview /docs/current/developer-docs/smart-contracts/write/overview /docs/current/developer-docs/smart-contracts/write/choosing-language /docs/current/developer-docs/smart-contracts/write/overview - /docs/current/developer-docs/frontend/my-contacts /docs/current/developer-docs/web-apps/application-frontends/default-frontend + /docs/current/developer-docs/frontend/my-contacts /docs/current/developer-docs/web-apps/application-frontends/overview /docs/ic-interface-spec /docs/current/references/ic-interface-spec /docs/interface-spec /docs/current/references/ic-interface-spec /docs/current/developer-docs/updates/computation-and-storage-costs /docs/current/developer-docs/gas-cost @@ -77,7 +77,7 @@ const redirects = ` /docs/current/developer-docs/build/using-an-agent /docs/current/developer-docs/smart-contracts/write/overview /docs/current/developer-docs/build/backend/reproducible-builds /docs/current/developer-docs/smart-contracts/best-practices/reproducible-builds /docs/current/developer-docs/build/cdks/ /docs/current/motoko/main/getting-started/motoko-introduction - /docs/current/developer-docs/build/frontend/default-frontend /docs/current/developer-docs/web-apps/application-frontends/default-frontend + /docs/current/developer-docs/build/frontend/default-frontend /docs/current/developer-docs/web-apps/application-frontends/overview /docs/current/developer-docs/build/frontend/webpack-config /docs/current/developer-docs/web-apps/application-frontends/overview#modifying-the-webpack-configuration /docs/current/developer-docs/build/install-upgrade-remove /docs/current/developer-docs/getting-started/install /docs/current/developer-docs/build/languages/rust/* /docs/current/developer-docs/backend/rust/ @@ -109,8 +109,8 @@ const redirects = ` /docs/developers-guide/sdk-guide /docs/current/developer-docs/getting-started/install /docs/developers-guide/troubleshooting /docs/current/developer-docs/getting-started/troubleshooting /docs/developers-guide/tutorials-intro /docs/current/motoko/main/getting-started/motoko-introduction - /docs/developers-guide/tutorials/default-frontend /docs/current/developer-docs/web-apps/application-frontends/default-frontend - /docs/developers-guide/tutorials/my-contacts /docs/current/developer-docs/web-apps/application-frontends/default-frontend + /docs/developers-guide/tutorials/default-frontend /docs/current/developer-docs/web-apps/application-frontends/overview + /docs/developers-guide/tutorials/my-contacts /docs/current/developer-docs/web-apps/application-frontends/overview /docs/developers-guide/webpack-config /docs/current/developer-docs/web-apps/application-frontends/overview /docs/developers-guide/work-with-languages /docs/current/developer-docs/smart-contracts/write/overview /docs/developers-guide/working-with-canisters /docs/current/developer-docs/smart-contracts/maintain/settings @@ -339,8 +339,8 @@ const redirects = ` /docs/current/developer-docs/setup/best-practices/storage /docs/current/developer-docs/smart-contracts/best-practices/storage /docs/current/developer-docs/setup/best-practices/troubleshooting /docs/current/developer-docs/smart-contracts/best-practices/troubleshooting /docs/current/developer-docs/frontend/ /docs/current/developer-docs/web-apps/application-frontends/overview - /docs/current/developer-docs/frontend/default-frontend /docs/current/developer-docs/web-apps/application-frontends/default-frontend - /docs/current/developer-docs/frontend/add-stylesheet /docs/current/developer-docs/web-apps/application-frontends/default-frontend + /docs/current/developer-docs/frontend/default-frontend /docs/current/developer-docs/web-apps/application-frontends/overview + /docs/current/developer-docs/frontend/add-stylesheet /docs/current/developer-docs/web-apps/application-frontends/overview /docs/current/developer-docs/frontend/boilerplate-frontend /docs/current/developer-docs/web-apps/application-frontends/overview /docs/current/developer-docs/frontend/existing-frontend /docs/current/developer-docs/web-apps/application-frontends/existing-frontend /docs/current/developer-docs/production/custom-domain/ /docs/current/developer-docs/web-apps/custom-domains/using-custom-domains @@ -597,8 +597,8 @@ const redirects = ` /docs/current/references/t-ecdsa-how-it-works /docs/current/references/t-sigs-how-it-works /docs/current/developer-docs/web-apps/application-frontends/bundlers /docs/current/developer-docs/web-apps/application-frontends/webpack /docs/current/developer-docs/web-apps/application-frontends/webpack-dev-server /docs/current/developer-docs/web-apps/application-frontends/webpack - /docs/current/developer-docs/web-apps/application-frontends/serving-static-assets /docs/current/developer-docs/web-apps/application-frontends/default-frontend - /docs/current/developer-docs/web-apps/application-frontends/custom-frontend /docs/current/developer-docs/web-apps/application-frontends/default-frontend + /docs/current/developer-docs/web-apps/application-frontends/serving-static-assets /docs/current/developer-docs/web-apps/application-frontends/overview + /docs/current/developer-docs/web-apps/application-frontends/custom-frontend /docs/current/developer-docs/web-apps/application-frontends/overview /docs/current/developer-docs/security/rust-canister-development-security-best-practices /docs/current/developer-docs/security/security-best-practices/inter-canister-calls /docs/developers-guide/computation-and-storage-costs.html /docs/current/developer-docs/gas-cost /docs/current/developer-docs/getting-started/ /docs/current/developer-docs/getting-started/network-overview @@ -657,8 +657,10 @@ const redirects = ` /docs/current/developer-docs/defi/tokens/ledger/usage/overview /docs/current/developer-docs/defi/overview /docs/current/developer-docs/web-apps/obtain-verify-ic-pubkey /docs/current/developer-docs/developer-tools/cli-tools/cli-reference/dfx-ping /docs/current/developer-docs/web-apps/design-dapps /docs/current/developer-docs/smart-contracts/overview/introduction - /docs/current/developer-docs/web-apps/application-frontends/add-stylesheet /docs/current/developer-docs/web-apps/application-frontends/default-frontend - + /docs/current/developer-docs/smart-contracts/candid /docs/current/developer-docs/smart-contracts/candid/candid-concepts + /docs/current/developer-docs/build/cdks/motoko-dfinity/* /docs/current/motoko/main/getting-started/motoko-introduction + /docs/current/developer-docs/web-apps/application-frontends/add-stylesheet /docs/current/developer-docs/web-apps/application-frontends/overview + /docs/current/developer-docs/web-apps/application-frontends/default-frontend /docs/current/developer-docs/web-apps/application-frontends/overview ` .split(/[\r\n]+/) .map((line) => line.trim().replace(/^#.*$/, "").trim()) diff --git a/sidebars.js b/sidebars.js index 58489500ff..e82fc05607 100644 --- a/sidebars.js +++ b/sidebars.js @@ -66,9 +66,12 @@ const sidebars = { items: [ { type: "category", - label: "What are canister smart contracts?", + label: "What are canisters?", + link: { + type: "doc", + id: "developer-docs/smart-contracts/overview/introduction", + }, items: [ - "developer-docs/smart-contracts/overview/introduction", "developer-docs/smart-contracts/overview/inside-canisters", "developer-docs/smart-contracts/development-workflow", "developer-docs/smart-contracts/overview/trust-in-canisters", @@ -519,7 +522,6 @@ const sidebars = { id: "developer-docs/web-apps/application-frontends/overview", }, items: [ - "developer-docs/web-apps/application-frontends/default-frontend", "developer-docs/web-apps/application-frontends/existing-frontend", "developer-docs/web-apps/application-frontends/asset-security", "developer-docs/web-apps/application-frontends/webpack", diff --git a/src/css/custom.scss b/src/css/custom.scss index 2e4a88e8cf..30e2ce5fa0 100644 --- a/src/css/custom.scss +++ b/src/css/custom.scss @@ -446,6 +446,17 @@ div[class^="announcementBarContent"] { cursor: pointer; } +.markdown { + & > h1, + & > h2, + & > h3, + & > h4, + & > h5, + & > h6 { + scroll-margin-top: 10rem; + } +} + .gradient-text { background-image: linear-gradient(108.55deg, #3b00b9 0%, #18d0b5 149.76%); } @@ -774,4 +785,4 @@ div[class^="announcementBarContent"] { @tailwind base; @tailwind components; -@tailwind utilities; \ No newline at end of file +@tailwind utilities; diff --git a/src/pages/icp-tokens.tsx b/src/pages/icp-tokens.tsx index ac6e575ddf..46d8e98e91 100644 --- a/src/pages/icp-tokens.tsx +++ b/src/pages/icp-tokens.tsx @@ -59,7 +59,7 @@ const WalletCard: React.FC<{
@@ -504,9 +504,10 @@ function TokenHolders(): JSX.Element { - Learn more about custody options + See all wallets + @@ -523,47 +524,26 @@ function TokenHolders(): JSX.Element { transfers.

+ - - - -
- - {/* Column 2 */} - -

Mobile app wallets

+

Mobile app wallets

Mobile apps offer easy access to crypto assets for people who use them frequently. @@ -576,25 +556,25 @@ function TokenHolders(): JSX.Element { icon="/img/showcase/plug_logo.webp" />

- -

- Browser extension wallets -

+ + {/* Column 2 */} + +

Browser extension wallets

Great for users already familiar with crypto wallets from - other chains. + other chains.

@@ -605,64 +585,75 @@ function TokenHolders(): JSX.Element { icon="/img/showcase/plug_logo.webp" />
-
- {/* Column 3 */} - -

Hardware wallets

+

Hardware wallets

Maximum security. Hardware wallets hold private keys in airgapped machines or ledger devices.

-

- Institutional custody -

+
+ {/* Column 3 */} + +

Institutional custody

For anyone managing large amounts of crypto assets. Institutional custodians offer reliability and customer support.

+ + + - + diff --git a/static/img/showcase/logo_bity.png b/static/img/showcase/logo_bity.png new file mode 100644 index 0000000000..4e2ca88f94 Binary files /dev/null and b/static/img/showcase/logo_bity.png differ diff --git a/static/img/showcase/logo_cooper.png b/static/img/showcase/logo_cooper.png new file mode 100644 index 0000000000..884f3dd256 Binary files /dev/null and b/static/img/showcase/logo_cooper.png differ diff --git a/static/img/showcase/logo_primevault.png b/static/img/showcase/logo_primevault.png new file mode 100644 index 0000000000..73158fe030 Binary files /dev/null and b/static/img/showcase/logo_primevault.png differ diff --git a/static/img/showcase/logo_tangem.png b/static/img/showcase/logo_tangem.png new file mode 100644 index 0000000000..c2f6c4cf90 Binary files /dev/null and b/static/img/showcase/logo_tangem.png differ diff --git a/static/img/showcase/logo_trust.png b/static/img/showcase/logo_trust.png new file mode 100644 index 0000000000..d47f821766 Binary files /dev/null and b/static/img/showcase/logo_trust.png differ