Skip to content

Nimiq Wallet Demo #263

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 34 commits into
base: master
Choose a base branch
from
Open

Nimiq Wallet Demo #263

wants to merge 34 commits into from

Conversation

onmax
Copy link
Member

@onmax onmax commented Mar 12, 2025

Demo Mode for the Nimiq Wallet

This PR provides a complete demo environment for the Nimiq Wallet, allowing users to try out wallet features without connecting to real blockchain networks.

How to activate it

Run yarn serve:demo or yarn build:demo.

When active, demo mode:

  • Creates fake accounts with demo balances
  • Generates fake transaction history
  • Simulates blockchain interactions like sending, receiving, and swapping
  • Obfuscates addresses and disables certain features that wouldn't work in demo mode
  • Redirects certain actions to explainer modals

This is intended for demonstration, educational, and testing purposes only.

This will be useful for the coming new nimiq.com/wallet page. As soon this feature goes live, soon after the new page will be updated.

The user sees and interacts with the wallet as any other user would with a real account. The difference is that the state, information and logic have been faked. Together with some visual cues, we invite the user to use and experiment with the Wallet, making sure that we set boundaries between what is real and what is not.

A picture is worth a thousand words, and a video is made of thousand1 pictures, so here are some videos:

Buy Fake NIM

A new UI has been developed for the modal

Screen.Recording.2025-03-12.090426.mp4

Stake Fake NIM

Screen.Recording.2025-03-12.093021.mp4

Swap NIM/BTC

Screen.Recording.2025-03-12.090720.mp4

TX details

If you want to see the transactions details being used:

  1. Go to Demo.ts
  2. Search for: defineNimFakeTransactions, defineBtcFakeTransactions, defineUsdcFakeTransactions or defineUsdtFakeTransactions

Try it yourself. Live preview.

  1. Go to the branch selector and select demo branch.
  2. Open the Nimiq Wallet Demo Testnet

Technical details.

Development

git fetch origin
git checkout demo
yarn install
yarn serve # open http://localhost:8081?demo

Tip

One good thing about this new feature is that we no longer have to run Keyguard and the Hub in parallel. Very useful if you are working on UI stuff.

Enabling the Demo.

Make sure to enable demo.enabled:

// src/config.local.ts

export default {
   demo: {
      enabled: true | false | 'subdomain.nimiq.com'
   }
}

New build

When running yarn build:demo or yarn serve with the demo initialized, we will use ``

i18n strings are extracted
BitcoinJS is built if not already present
The build is triggered with the build=demo environment variable

This makes Vue CLI use src/main-demo.ts as the entry point instead of src/main.ts
The config file src/config/config.demo.ts will be used
A special release tag is created in the format demo-YYYY-MM-DD to identify the demo build

All assets are compiled and optimized for production use

Enabling USDC/USDT on Polygon.

Make sure to enable the fastspot

// src/config.local.ts

export default {
   // Rest of your config
   polygon: {
      enabled: true,
   },
   fastspot: {
      enabled: true, // By default, set to false to hide the swap feature.
      // other fields are ok
   }
}

How this was implemented?

Visual summary

image

I've tried to touch as little code from the current application as possible, as we don't want to introduce any problems for current users. All demo related code can be found in lib/Demo.ts. I decided not to create multiple files on purpose, even though the code is a bit spaghetti in my opinion.

main.ts

We don't want to write anything to the user's storage, so when the application is initialised, the first thing it does is check whether the ?demo parameter exists or not.

If we are in the demo environment the stores will not be persistent.

We also "inject" the ?demo parameter after each route change.

We do use ?demo as query param and not path param since it is easier to propagate and not mess with the router

if (!checkIfDemoIsActive()) {
    // Normal app initialization
    await initStorage();
    initTrials();
    await initHubApi();
    syncFromHub();
} else {
    // Demo mode initialization
    dangerouslyInitializeDemo(router); // we do have the prefix `dangerously` to all exported function in demo
}

Hub API

We don't want to launch the Hub API when the demo is active. So a new "mock" implementation of the Hub API has been introduced:

const hubApi = checkIfDemoIsActive() ? DemoHubApi.create() : new HubApi(Config.hubEndpoint);

This demo, will block any attempt to open the hub popup and instead handle the opening of the new "DemoModalFallback" which will let the user know that the demo functionality is limited and they have reached the end of the demo.

Sometimes the HubAPI will not display this modal and will instead fake the operation. For example, if you are exchanging two currencies, the Hub API will create a fake exchange and you will see the whole process.

You can see the DemoHubApi in the ./lib/Demo.ts and the "Faked Flows" section in this PR.

Fake accounts & transactions

To add some realism, we insert fake transactions in NIM, BTC, USDC and USDT. The transactions in NIM have customised messages using cultural references from around the world. Fun fact: the NIM amount is 140418, which is a date: 14th of april, 2018.

We are prioritizing realism, so instead of using a Math.random for the values of each transaction we do use a ratio of the total amount that the user has now. This allows us to confidently know that the balance shown in the overview matches the transaction history.

Faked flows

One of the reasons of the Nimiq Wallet Demo is to let the user play without an account. Therefore the most notorious and important flows have been faked.

These are the faked flows:

  • Buy NIM: see replaceBuyNimFlow function
  • Stake NIM: see replaceStakingFlow function
  • Swap cryptos: We rely mainly on the SwapModal logic, but we intercept the fetch requests to the Fastspot API like the /limits for the swaps. For each of the key points in the dataflow, we return a specific set of values. See interceptFetchRequest.

The rest of data flows are
The next flows from the Wallet are out-of-scope:

  • Adjust stake
  • Unstake
  • Sell crypto
  • Swaps involving USDC/USDT
  • Buy BTC & USDC
  • Probably I am missing much more other flows, but I think you get the idea ;)

Communication with the host

Since this feature will be used as an iframe, we would like let the host and the app talk to each other. Therefore, we have the function attachIframeListeners.

Basically, the user has the option to click on "buy", "stake" or "swap" on the website. The selector will have one of the options highlighted when the user is in that flow.

Therefore, this communication needs to be two-way, meaning that if the user changes the flow from the application, the application needs to communicate this to the host, or if the user changes the flow from the selector, the host needs a request to initialise to that flow.

This has yet to be tested

Custom CSS + Visual cues

When the demo is enabled we add also some visual cues to the user to let them know where to click. This cues are added from Demo.ts@setupVisualCues function.

We also inject some CSS to make some minor adjustments.

Comment about Swaps

The demo only allows you to swap between NIM and BTC. Adding more cryptos add too much complexity and it is outside of the scope. We just need a minimal swap demo for users to enjoy.

Therefore the following things have been removed from the UI:

  • The account grid swap triggers & tooltip.
  • The custom background clip for the dots
  • The currency selector in the swap modal.

The removal of this items is dynamically and happens in Demo.ts.

Fallback Modal design

image

Using DOM observation

In order to minimize writing code in the core app logic, we have decided to use MutationObserver to watch for UI changes:

  • To setup the visual cues. This is useful if user navigates to settings and then come back to the main page.
  • To fake the NIM buy process.
  • To fake the Staking process
  • Enable sell modal, overwriting default behaviour
  • Remove addresses in UI. Avoid writing addresses in clipboard. See more info in the "Obfuscate addresses" section

Demo Mode notice

In the top left a new banner has been included to warn about the demo mode

image

Obfuscate addresses

In order to avoid loss of funds, all the NIM addresses have been replaced with XXXX as shown in the picture. The content that we write to the clipboard will be This is a demo address - not for actual use.

NIM BTC USDC
image image image

UX improvements

Translations

Should we translate the transactions messages and labels?


QA

The demo does not require any Hub nor Keyguard, making it easy to be used this mode for QA tasks. Like selecting address, interacting with the swap modal, navigate the address...

TODO

Fill NIM first

When we do a swap NIM -> BTC, I am not sure how modify the code in listenForSwapChanges in order to set the value so the animation first fills the NIM and then the BTC.

Footnotes

  1. We both know that this is not exactly true, but if I try to be correct, the sentence loses its wit.

@onmax onmax mentioned this pull request Mar 12, 2025
@julianbauer
Copy link
Member

Hey Maxi, thanks for putting this together, this will be such a helfpul resource!
I'll comment on your suggested UX improvements, and a bit beyond that on the videos you shared:

@julianbauer
Copy link
Member

julianbauer commented Mar 20, 2025

Buy Fake NIM

Completely agree with you, an interface where users can enter a number and that amount gets added to the balance is perfect. This is not too far from how the original interface for buying via OASIS looked like:
Setup – Select crypto   amount

I think it would be cool to bring it as close as possible to this, as even though users right now will have to go through moonpay et al, this would be the most native way to buy within the wallet. Can we mock a bank and identicon (we do have a generic bank icon too)? Can we show the fiat equivalent or would this be too much work? Here's the link to the Figma designs

Commented by Maxi: ✅ Done

Instead of the DEMO banner in the modal, I'd add a note to the whole interface (e.g. where we have the "POS Testnet" note), additionally if we think that's not enough, maybe a little note below the button saying that this is not real money and just for demo purposes.

Commented by Maxi: ✅ Done

Also in the TX list, would be cool to have a [fake bank icon instead of the identicon].(https://www.figma.com/design/D07VHQmmYSaOipkiivRjwv/Wallet-Fiat-Swaps?node-id=756-0&t=Ruacw6pwydVVUxsh-11)

Commented by Maxi: ✅ Done

@onmax
Copy link
Member Author

onmax commented Mar 20, 2025

Thanks @julianbauer. You can also add yourself as reviewer and add a review with request changes option ;)

Instead of the DEMO banner in the modal, I'd add a note to the whole interface (e.g. where we have the "POS Testnet" note), additionally if we think that's not enough, maybe a little note below the button saying that this is not real money and just for demo purposes.

Really nice solution. I will update the designs and propose a wording that we like :).

Also in the TX list, would be cool to have a [fake bank icon instead of the identicon].(figma.com/design/D07VHQmmYSaOipkiivRjwv/Wallet-Fiat-Swaps?node-id=756-0&t=Ruacw6pwydVVUxsh-11)

Yep I think we can do that. For that I believe we need to fake the swap as if the user has used supersimpleswap. I didn't want to add a sss swap since the service is offline for more than a year. Should we really add it?

I believe this swap is only in EUR, so a person from other continent don't care about this feature.

@julianbauer
Copy link
Member

julianbauer commented Mar 20, 2025

Yep I think we can do that. For that I believe we need to fake the swap as if the user has used supersimpleswap. I didn't want to add a sss swap since the service is online for more than a year. Should we really add it?

As we're faking it all anyway, could we just... fake it? Like, we probably have a fixed address for this transaction, couldn't we just replace the identicon with the bank icon? Or am I missing sth?

Commented by Maxi: ✅ Done

@sisou
Copy link
Member

sisou commented Mar 20, 2025

I'm against adding a EUR swap, as we are not currently offering that and don't know if we'll ever do again.

@julianbauer
Copy link
Member

julianbauer commented Mar 20, 2025

Stake NIM

Looks good! Probably obvious but 2 things:

  • We need some more fake content for the staking pool profile
    Commented by Maxi: ❌ Should be in the staking PR
  • The staking interface in the address overview is still the old one, would be great to work with the new, iterated designs here (Figma):
    Commented by Maxi: ❌ Should be in the staking PR

Default

@julianbauer
Copy link
Member

julianbauer commented Mar 20, 2025

@sisou

I'm against adding a EUR swap, as we are not currently offering that and don't know if we'll ever do again.

I'm with you, I wouldn't call it "the EUR swap" or link it to any of the services currently unavailable. It does provide an interface though that I think is nicer and closer to reality than just a single input, and by itself is quite neutral. Considering we have these anyway, why not use them?

Commented by Maxi: ✅ Done!

@julianbauer
Copy link
Member

julianbauer commented Mar 20, 2025

Swap NIM

  • Probably makes sense to speed this up a little bit – e.g. just stay for 1-2 seconds on each step
    Commented by Maxi: ✅ Done!
  • Before highlighting the BTC tile, first the NIM tile on the left should be filled (to show it's ready)
  • You're using the "clap hands" icon for a successful buy experience – for consistency I suggest to use that for the swap as well (or the other way around. I think in the wallet we're using the checkmarks, I do think the hands emoji is nicer though – should then ofc be updated in the wallet too)
    Commented by Maxi: ✅ Done!
  • The "Swap successful" toast stays indefinitely, and clicking on it opens the success step in the modal ;)

@julianbauer
Copy link
Member

julianbauer commented Mar 20, 2025

Reduce the balance to a certain amount. For example, $200 in NIM, $1300 in BTC and a few hundred in USD.
Dynamic balance depending on your country. We could use an API that gives an economic index based on a country code to set a balance that fits an average person in that country.

Great ideas, agreed!

Fallback modal

For the fallback modal, we'll come up with a custom design for this. Essentially we want to say "Hey this functionality doesn't exist because this is a demo, but if you're hooked, create a wallet and see the real thing"

Commented by Maxi:

✅ Done!

image

Fake addresses

Could we just use special characters in the middle of the addresses? Like NQ26 2Y2M XXXX XXXX XXXX XXXX XXXX MDIF 5SKD? That should generate identicons too, right? On top of that we should definitely deactivate all copy functionality (like clicking the address in Receive modal), and add a note to users to not send funds to these addresses because they don't exist

Commented by Maxi:

✅ Done!

NIM BTC USDC
image image image

@onmax
Copy link
Member Author

onmax commented Mar 21, 2025

The staking interface in the address overview is still the old one, would be great to work with the new, iterated designs here (Figma)

Agreed, but outside of the scope of this PR. The UI will be updated automatically when new designs get implemented.

I'm with you, I wouldn't call it "the EUR swap" or link it to any of the services currently unavailable. It does provide an interface though that I think is nicer and closer to reality than just a single input, and by itself is quite neutral. Considering we have these anyway, why not use them?

I will check how to do it, but I believe I might need to change some code in the application side, which is something I am trying to minimize. I will see how I can do it :)

Probably makes sense to speed this up a little bit – e.g. just stay for 1-2 seconds on each step

No problem

Before highlighting the BTC tile, first the NIM tile on the left should be filled (to show it's ready)

Ok, I think I missed a step in the faking process.

You're using the "clap hands" icon for a successful buy experience – for consistency I suggest to use that for the swap as well (or the other way around. I think in the wallet we're using the checkmarks, I do think the hands emoji is nicer though – should then ofc be updated in the wallet too)

I will update the buy flow with check mark then.

Dynamic balance depending on your country. We could use an API that gives an economic index based on a country code to set a balance that fits an average person in that country.

I will see the easiest way to do it without any impact.

For the fallback modal, we'll come up with a custom design for this. Essentially we want to say "Hey this functionality doesn't exist because this is a demo, but if you're hooked, create a wallet and see the real thing"

Ok, waiting for it.

Could we just use special characters in the middle of the addresses? Like NQ26 2Y2M XXXX XXXX XXXX XXXX XXXX MDIF 5SKD? That should generate identicons too, right? On top of that we should definitely deactivate all copy functionality (like clicking the address in Receive modal), and add a note to users to not send funds to these addresses because they don't exist

It will not generate identicons as I believe the code will say is a invalid address. but I can look for a work-around.

Thanks for you valuable feedback. I have updated the TODOs section in this PR to track progress

@julianbauer
Copy link
Member

It will not generate identicons as I believe the code will say is a invalid address. but I can look for a work-around.

Btw I'm basing my assumption on this demo here, where any text creates an identicon. Not sure how applicable this is of course: https://github.com/nimiq/identicons?tab=readme-ov-file

But then, as another workaround, you could use a real address for the identicon, but only show the masked version of it. There's no way to guess the address from the identicon so we should be safe!

@onmax
Copy link
Member Author

onmax commented Mar 21, 2025

Under the hood we are using ValidationUtils.isValidAddress(address) here.

But I think is going to change just the text content that we render and leave the identicon itself with a correct address as you mention

@onmax onmax requested a review from Copilot March 27, 2025 08:24
@onmax
Copy link
Member Author

onmax commented Apr 15, 2025

@julianbauer

Working on your feedback now.

I just realized that we didn't think about the receive modal. I am thinking of disabling the receive modal completely and always redirecting to the fallback modal. There is no need to show the receive modal at all in the demo. Let me know if this works for you.

Thanks a lot!

image

@julianbauer
Copy link
Member

julianbauer commented Apr 15, 2025

@julianbauer

Working on your feedback now.

I just realized that we didn't think about the receive modal. I am thinking of disabling the receive modal completely and always redirecting to the fallback modal. There is no need to show the receive modal at all in the demo. Let me know if this works for you.

Thanks a lot!

image

Ach, I think it's nice seeing how it looks like. If we mask the address properly like in your screenshot, I think there's value in showing it, just for context.

Commented by Maxi: ✅ Done

@onmax
Copy link
Member Author

onmax commented Apr 15, 2025

The issue now is the QR codes and request link. How should we handle those?

@julianbauer
Copy link
Member

I'd suggest to draw the line there: These can be left out :) – show the fallback modal when clicking on them

Copy link
Member Author

@onmax onmax left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor adjustments needed

@onmax onmax force-pushed the demo branch 3 times, most recently from 8614787 to ef9e296 Compare May 15, 2025 12:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants