Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
c57ac32
fix: correctness fixes and rename yoroiProvider to cardanoProvider
Nebyt Jul 8, 2026
cb75f1f
refactor: memoize cardanoProvider and tidy index.js
Nebyt Jul 8, 2026
5c50a61
refactor: add debug-gated logger, replace console.* calls
Nebyt Jul 8, 2026
7e16a8a
feat: runtime on/off switch for logger via browser console
Nebyt Jul 8, 2026
28f59e4
test: add smoke + util tests and CI workflow
Nebyt Jul 8, 2026
1a33f28
refactor: extract runApiCall helper, collapse card handlers
Nebyt Jul 8, 2026
9de376a
refactor: route card input fields through InputWithLabel
Nebyt Jul 8, 2026
5cf31ce
refactor: extract useResponseState hook for subtabs
Nebyt Jul 8, 2026
8dbb81a
refactor: extract buildCert helper for gov panels
Nebyt Jul 8, 2026
2021b84
refactor: add hexArrayToBech32Addresses/hexArrayToUtxos helpers
Nebyt Jul 8, 2026
650e1b2
refactor: extract parseCredential, ChainStatusMessage, AccessButtonShell
Nebyt Jul 8, 2026
f6cdcf7
feat: user-facing error toasts for wallet connect failures
Nebyt Jul 8, 2026
8b9a38e
style: apply prettier to src
Nebyt Jul 8, 2026
a57c800
chore: tooling and docs — lint/format scripts, README, .env
Nebyt Jul 8, 2026
fd4b20f
refactor: shared connection-state machine + documented chain contract
Nebyt Jul 8, 2026
fd8513a
fix: network toggle unclickable — stop inactive TabPanels overlaying …
Nebyt Jul 8, 2026
c5640e4
refactor: share chunkMessageTo64Bytes; drop NFTTab char-based slicer
Nebyt Jul 8, 2026
795e919
chore: bump version to 2.4.0
Nebyt Jul 8, 2026
56a23dc
Potential fix for pull request finding 'CodeQL / Workflow does not co…
Nebyt Jul 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copy to `.env` to override defaults. Only variables prefixed with REACT_APP_
# are exposed to the app (Create React App convention).

# Enable verbose debug/log output in production builds.
# - During local development (`npm start`) logs are ON by default regardless.
# - In production builds logs are OFF unless this is set to "true".
# - You can also toggle logs at runtime from the browser console, without
# rebuilding: dappLogs.on() / dappLogs.off() / dappLogs.toggle().
REACT_APP_DEBUG=false
4 changes: 4 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ jobs:
run: |
npm i

- name: Create production .env
run: |
echo "REACT_APP_DEBUG=false" > .env

- name: Build
run: |
npm run build
Expand Down
37 changes: 37 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Test

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Read .nvmrc
id: nvm
run: echo "version=$(cat .nvmrc)" >> $GITHUB_OUTPUT

- name: Setup node
uses: actions/setup-node@v4
with:
node-version: '${{ steps.nvm.outputs.version }}'
cache: 'npm'

- name: Install dependencies
run: npm i

- name: Check formatting
run: npm run format:check

- name: Lint
run: npm run lint

- name: Run tests
run: npm run test:ci
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
5 changes: 1 addition & 4 deletions .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,5 @@
"semi": false,
"singleQuote": true,
"bracketSpacing": false,
"arrowParens": "always",
"importOrder": ["^@(.*)$", "^[./]" ],
"importOrderSeparation": true,
"importOrderSortSpecifiers": true
"arrowParens": "always"
}
89 changes: 86 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,90 @@
# dapp-example

The project is created with purpose to show how to work with yoroi dApp-connector.
A simple, multi-chain example dApp that shows how to interact with wallet
connectors — built for clarity, learning, testing, and extension.

It may not look good but it works and can be used as an example to other projects how to interact with the dApp-connector.
Live demo: https://dapp-example.yoroiwallet.com/

You can find the dApp on this link https://dapp-example.yoroiwallet.com/
> It may not look fancy, but it works and is meant as a reference for other
> projects on how to talk to wallet dApp-connectors.

## Supported chains

- **Cardano** (CIP-30) — live, via browser wallet extensions (e.g. Yoroi)
- **Ethereum** (EIP-1193) — live, via `window.ethereum`
- **Bitcoin** — planned (provider is currently a stub)

## Tech stack

- React 18 (functional components + hooks), JavaScript only (no TypeScript)
- Tailwind CSS + Material Tailwind
- Create React App + craco
- Cardano serialization via `@emurgo/cardano-serialization-lib-browser`

## Getting started

Prerequisites: Node.js (see [`.nvmrc`](./.nvmrc)) and npm.

```bash
npm install # install dependencies
npm start # run the dev server at http://localhost:3000
npm run build # production build into ./build
```

A Cardano and/or Ethereum browser wallet extension is required to exercise the
connect flows.

## Available scripts

| Script | Description |
| --- | --- |
| `npm start` | Start the development server |
| `npm run build` | Production build |
| `npm test` | Run tests in watch mode |
| `npm run test:ci` | Run tests once (CI mode) |
| `npm run lint` | Lint `src` with ESLint |
| `npm run lint:fix` | Lint and auto-fix |
| `npm run format` | Format `src` with Prettier |
| `npm run format:check` | Check formatting without writing |

## Project structure

```
src/
hooks/ # Providers (one per chain) + network toggle + shared hooks
components/ # UI: access buttons, tabs, cards, shared inputs
utils/ # Helpers and blockchain utilities (cslTools, ethereumUtils, ...)
```

Each chain follows the same flow: **Provider → Access Button → Main Tab →
Subtabs → Cards**. See [`CLAUDE.md`](./CLAUDE.md) for the full architecture and
conventions.

## Logging

The app uses a small debug-gated logger (`src/utils/logger.js`):

- `debug` / `log` / `info` print only in development, or in a production build
started with `REACT_APP_DEBUG=true` (see [`.env.example`](./.env.example)).
- `warn` / `error` always print.

You can toggle logs live from the browser console — no rebuild needed:

```js
dappLogs.on() // enable and remember across reloads
dappLogs.off() // disable and remember across reloads
dappLogs.toggle() // flip current state
dappLogs.status() // -> true | false
dappLogs.reset() // forget the override, fall back to the build default
```

## Testing

Tests use Jest + React Testing Library (via CRA). Run `npm run test:ci` for a
single pass, or `npm test` to watch. Tests and linting run in CI on pushes to
`main` and on pull requests.

## Configuration

Copy [`.env.example`](./.env.example) to `.env` to override defaults. Only
variables prefixed with `REACT_APP_` are exposed to the app.
21 changes: 19 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dapp-example",
"version": "2.3.0",
"version": "2.4.0",
"private": true,
"dependencies": {
"@emurgo/cardano-serialization-lib-browser": "14.1.2",
Expand Down Expand Up @@ -30,12 +30,18 @@
"@rollup/plugin-terser": "^0.4.4",
"autoprefixer": "^10.4.8",
"postcss": "^8.4.16",
"prettier": "^3.9.4",
"tailwindcss": "^3.1.8"
},
"scripts": {
"start": "craco start",
"build": "CI=false && craco build",
"test": "craco test",
"test:ci": "CI=true craco test",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"format": "prettier --write \"src/**/*.js\"",
"format:check": "prettier --check \"src/**/*.js\"",
"eject": "craco eject"
},
"eslintConfig": {
Expand Down
11 changes: 6 additions & 5 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import logger from './utils/logger'
import React, {useEffect} from 'react'
import AccessButton from './components/accessButton'
import MainTab from './components/tabs/mainTab'
import TabsComponent from './components/tabs/tabsComponent'
import useYoroi from './hooks/yoroiProvider'
import useCardano from './hooks/cardanoProvider'
import useNetwork, {NETWORK_CARDANO, NETWORK_ETHEREUM} from './hooks/networkProvider'
import BitcoinAccessButton from './components/bitcoinAccessButton'
import BitcoinMainTab from './components/tabs/bitcoinMainTab'
Expand All @@ -24,7 +25,7 @@ import EthTransactionsTab from './components/tabs/subtabs/ethTransactionsTab'
import Erc20Tab from './components/tabs/subtabs/erc20Tab'

const App = () => {
const {connectionState, selectedWallet, setConnectionState, setConnectionStateFalse} = useYoroi()
const {connectionState, selectedWallet, setConnectionState, setConnectionStateFalse} = useCardano()
const {activeNetwork} = useNetwork()
const isWalletConnected = connectionState === CONNECTED
const isNoProvider = connectionState === NO_PROVIDER
Expand All @@ -42,7 +43,7 @@ const App = () => {

useEffect(() => {
const getConnectionState = async () => {
console.debug(`[dApp][App] Checking connection works`)
logger.debug(`[dApp][App] Checking connection works`)
try {
const walletObject = window.cardano[selectedWallet]
const conState = await walletStateWithTimeout(walletObject, 10000)
Expand All @@ -54,14 +55,14 @@ const App = () => {
}
} catch (error) {
setConnectionStateFalse()
console.error(error)
logger.error(error)
}
}

if (isWalletConnected) {
const connectionTimer = setInterval(getConnectionState, 10000)
return () => {
console.debug(`[dApp][App] Checking connection is stopped`)
logger.debug(`[dApp][App] Checking connection is stopped`)
clearInterval(connectionTimer)
}
}
Expand Down
46 changes: 23 additions & 23 deletions src/components/accessButton.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import logger from '../utils/logger'
import React from 'react'
import useYoroi from '../hooks/yoroiProvider'
import useCardano from '../hooks/cardanoProvider'
import {IN_PROGRESS} from '../utils/connectionStates'
import WalletsModal from './walletsModal'
import AccessButtonShell from './accessButtonShell'

const AccessButton = () => {
const {api, connectionState, availableWallets, selectedWallet} = useYoroi()
console.log(`[dApp][AccessButton] available wallets: ${availableWallets.length}`)
const {api, connectionState, availableWallets, selectedWallet} = useCardano()
logger.log(`[dApp][AccessButton] available wallets: ${availableWallets.length}`)

const getWalletIcon = () => {
return window.cardano[selectedWallet].icon
Expand All @@ -19,27 +21,25 @@ const AccessButton = () => {
}

return (
<div className="mx-auto bg-gray-900">
<div className="grid justify-items-center py-3">
{api ? (
<div className="flex items-center justify-center gap-3 py-5">
<img src={getWalletIcon()} alt="wallet icon" className="w-10 sm:w-14 lg:w-16" />
<div className="text-base sm:text-xl font-bold tracking-tight text-white text-center">
<div>Connected To {getWalletName()}</div>
<div className="py-1 text-sm sm:text-base">anonymous wallet</div>
</div>
<AccessButtonShell>
{api ? (
<div className="flex items-center justify-center gap-3 py-5">
<img src={getWalletIcon()} alt="wallet icon" className="w-10 sm:w-14 lg:w-16" />
<div className="text-base sm:text-xl font-bold tracking-tight text-white text-center">
<div>Connected To {getWalletName()}</div>
<div className="py-1 text-sm sm:text-base">anonymous wallet</div>
</div>
) : connectionState === IN_PROGRESS ? (
<div className="pt-5 pb-5 text-m font-bold tracking-tight text-green-500">
<label>Wallet connecting is in progress ...</label>
</div>
) : (
<div>
<WalletsModal />
</div>
)}
</div>
</div>
</div>
) : connectionState === IN_PROGRESS ? (
<div className="pt-5 pb-5 text-m font-bold tracking-tight text-green-500">
<label>Wallet connecting is in progress ...</label>
</div>
) : (
<div>
<WalletsModal />
</div>
)}
</AccessButtonShell>
)
}

Expand Down
10 changes: 10 additions & 0 deletions src/components/accessButtonShell.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from 'react'

// Shared chrome for each chain's access button: dark background + centered grid.
const AccessButtonShell = ({children, innerClassName = 'grid justify-items-center py-3'}) => (
<div className="mx-auto bg-gray-900">
<div className={innerClassName}>{children}</div>
</div>
)

export default AccessButtonShell
26 changes: 11 additions & 15 deletions src/components/bitcoinAccessButton.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
import React from 'react'
import AccessButtonShell from './accessButtonShell'

const BitcoinAccessButton = () => {
return (
<div className="mx-auto bg-gray-900">
<div className="grid justify-items-center py-3">
<button
className="rounded-md bg-orange-600 py-5 px-5 disabled:opacity-50 text-white font-semibold cursor-not-allowed"
disabled
>
Bitcoin (Coming Soon)
</button>
</div>
</div>
)
}
const BitcoinAccessButton = () => (
<AccessButtonShell>
<button
className="rounded-md bg-orange-600 py-5 px-5 disabled:opacity-50 text-white font-semibold cursor-not-allowed"
disabled
>
Bitcoin (Coming Soon)
</button>
</AccessButtonShell>
)

export default BitcoinAccessButton
Loading
Loading