diff --git a/.changeset/big-snakes-marry.md b/.changeset/big-snakes-marry.md
new file mode 100644
index 000000000000..d3d8e8b01bf0
--- /dev/null
+++ b/.changeset/big-snakes-marry.md
@@ -0,0 +1,5 @@
+---
+"ledger-live-desktop": patch
+---
+
+Always skip the analytics prompt in e2e tests
diff --git a/.changeset/calm-pets-shout.md b/.changeset/calm-pets-shout.md
new file mode 100644
index 000000000000..d99d754e65de
--- /dev/null
+++ b/.changeset/calm-pets-shout.md
@@ -0,0 +1,5 @@
+---
+"ledger-live-desktop": patch
+---
+
+bump storyly version
diff --git a/.changeset/clever-rocks-watch.md b/.changeset/clever-rocks-watch.md
new file mode 100644
index 000000000000..fcdf5af7b7ac
--- /dev/null
+++ b/.changeset/clever-rocks-watch.md
@@ -0,0 +1,8 @@
+---
+"live-mobile": patch
+---
+
+fix(wallet-api): only call success and fail handlers when closing the flow on LLM
+
+Allows to properly retry the sign message without sending an error through the wallet-api
+We also only send the success when we actually close the last success screen
diff --git a/.changeset/dry-otters-shop.md b/.changeset/dry-otters-shop.md
new file mode 100644
index 000000000000..187563b2688a
--- /dev/null
+++ b/.changeset/dry-otters-shop.md
@@ -0,0 +1,6 @@
+---
+"ledger-live-desktop": patch
+"@ledgerhq/live-nft": patch
+---
+
+Add detail drawers for inscriptions of ordinals protocol
diff --git a/.changeset/empty-clouds-glow.md b/.changeset/empty-clouds-glow.md
new file mode 100644
index 000000000000..373b48d68356
--- /dev/null
+++ b/.changeset/empty-clouds-glow.md
@@ -0,0 +1,5 @@
+---
+"ledger-live-desktop": patch
+---
+
+Add ui for empty rare sats and inscriptions. Add error message from Error when inscriptions / rare sats can't load. Add dumy drawer for inscriptions
diff --git a/.changeset/four-worms-end.md b/.changeset/four-worms-end.md
new file mode 100644
index 000000000000..a64eceb72b84
--- /dev/null
+++ b/.changeset/four-worms-end.md
@@ -0,0 +1,6 @@
+---
+"@ledgerhq/live-common": patch
+"@ledgerhq/coin-evm": patch
+---
+
+chore: update default explorer for local base & zkevm config, update evm bridge test
diff --git a/.changeset/fresh-cobras-argue.md b/.changeset/fresh-cobras-argue.md
new file mode 100644
index 000000000000..581e0dd9b2cd
--- /dev/null
+++ b/.changeset/fresh-cobras-argue.md
@@ -0,0 +1,5 @@
+---
+"ledger-live-desktop": patch
+---
+
+Fix unstable integration test
diff --git a/.changeset/green-starfishes-suffer.md b/.changeset/green-starfishes-suffer.md
new file mode 100644
index 000000000000..4aacaeeb1498
--- /dev/null
+++ b/.changeset/green-starfishes-suffer.md
@@ -0,0 +1,5 @@
+---
+"live-mobile": patch
+---
+
+add change address disclaimer for utxos accounts
diff --git a/.changeset/olive-drinks-complain.md b/.changeset/olive-drinks-complain.md
new file mode 100644
index 000000000000..8bd20d8f14ad
--- /dev/null
+++ b/.changeset/olive-drinks-complain.md
@@ -0,0 +1,6 @@
+---
+"ledger-live-desktop": patch
+"live-mobile": patch
+---
+
+Update turbo.json to accept envs
diff --git a/.changeset/rare-yaks-deny.md b/.changeset/rare-yaks-deny.md
new file mode 100644
index 000000000000..e5c8877cb941
--- /dev/null
+++ b/.changeset/rare-yaks-deny.md
@@ -0,0 +1,8 @@
+---
+"@ledgerhq/coin-near": patch
+"ledger-live-desktop": patch
+"live-mobile": patch
+"@ledgerhq/live-common": patch
+---
+
+remove rewards feature from near
diff --git a/.changeset/six-hairs-raise.md b/.changeset/six-hairs-raise.md
new file mode 100644
index 000000000000..4ad083d7a294
--- /dev/null
+++ b/.changeset/six-hairs-raise.md
@@ -0,0 +1,5 @@
+---
+"live-mobile": patch
+---
+
+Run tests files at `e2e/specs/*.spec.ts`
diff --git a/.changeset/slow-dingos-destroy.md b/.changeset/slow-dingos-destroy.md
new file mode 100644
index 000000000000..93f3e2a4871e
--- /dev/null
+++ b/.changeset/slow-dingos-destroy.md
@@ -0,0 +1,9 @@
+---
+"ledger-live-desktop": patch
+"live-mobile": patch
+"@ledgerhq/live-common": patch
+---
+
+fix(LLM): allow wallet-connect live-app to open a deep-link at any time
+
+Refactor by sharing a single constant for wallet-connect live-app manifest id
diff --git a/.changeset/sour-poets-play.md b/.changeset/sour-poets-play.md
new file mode 100644
index 000000000000..ae7b88b5aee8
--- /dev/null
+++ b/.changeset/sour-poets-play.md
@@ -0,0 +1,7 @@
+---
+"live-mobile": patch
+---
+
+fix(LLM): wallet-connect deep-link from native app for a request would reload the page
+
+Reloading the page would break the wallet-api flow shown when coming back to the app
diff --git a/.changeset/strong-goats-rush.md b/.changeset/strong-goats-rush.md
new file mode 100644
index 000000000000..319fbaa8634e
--- /dev/null
+++ b/.changeset/strong-goats-rush.md
@@ -0,0 +1,5 @@
+---
+"live-mobile": patch
+---
+
+Always skip the analytics prompt in e2e tests
diff --git a/.changeset/tough-terms-film.md b/.changeset/tough-terms-film.md
new file mode 100644
index 000000000000..ce11edd712fb
--- /dev/null
+++ b/.changeset/tough-terms-film.md
@@ -0,0 +1,7 @@
+---
+"live-mobile": patch
+---
+
+fix(wallet-api): open any deep-link from the webview on LLM
+
+Needed for wallet-connect-live-app deep-link back to mobile dApps
diff --git a/.changeset/witty-bugs-add.md b/.changeset/witty-bugs-add.md
new file mode 100644
index 000000000000..232f7175fa82
--- /dev/null
+++ b/.changeset/witty-bugs-add.md
@@ -0,0 +1,5 @@
+---
+"ledger-live-desktop": patch
+---
+
+storyly stories localization
diff --git a/.changeset/witty-cows-breathe.md b/.changeset/witty-cows-breathe.md
new file mode 100644
index 000000000000..16b2ab79e57d
--- /dev/null
+++ b/.changeset/witty-cows-breathe.md
@@ -0,0 +1,5 @@
+---
+"ledger-live-desktop": patch
+---
+
+Add protectBox to protect ordinals in discovery drawer
diff --git a/.github/workflows/build-and-test-external.yml b/.github/workflows/build-and-test-external.yml
index af5ed98ceefc..96769045a1fd 100644
--- a/.github/workflows/build-and-test-external.yml
+++ b/.github/workflows/build-and-test-external.yml
@@ -14,7 +14,7 @@ permissions:
jobs:
determine-affected:
name: "Turbo Affected"
- if: ${{contains(needs.determine-affected.outputs.paths, 'ledger-live-desktop') && github.event.pull_request.head.repo.full_name != github.repository }}
+ if: ${{github.event.pull_request.head.repo.full_name != github.repository }}
uses: LedgerHQ/ledger-live/.github/workflows/turbo-affected-reusable.yml@develop
with:
head_branch: ${{ github.event.pull_request.head.ref || github.event.merge_group.head_ref }}
@@ -26,18 +26,30 @@ jobs:
needs: determine-affected
if: ${{contains(needs.determine-affected.outputs.paths, 'ledger-live-desktop') && github.event.pull_request.head.repo.full_name != github.repository }}
uses: LedgerHQ/ledger-live/.github/workflows/build-desktop-external-reusable.yml@develop
+ with:
+ ref: ${{ github.event.pull_request.head.ref }}
+ repository: ${{ github.event.pull_request.head.repo.full_name }}
+ secrets: inherit
test-desktop-external:
name: "[External] Test Desktop"
needs: determine-affected
if: ${{contains(needs.determine-affected.outputs.paths, 'ledger-live-desktop') && github.event.pull_request.head.repo.full_name != github.repository }}
uses: LedgerHQ/ledger-live/.github/workflows/test-desktop-external-reusable.yml@develop
+ with:
+ ref: ${{ github.event.pull_request.head.ref }}
+ repository: ${{ github.event.pull_request.head.repo.full_name }}
+ secrets: inherit
build-mobile-external:
name: "[External] Build Mobile"
needs: determine-affected
if: ${{contains(needs.determine-affected.outputs.paths, 'ledger-live-mobile') && github.event.pull_request.head.repo.full_name != github.repository}}
uses: LedgerHQ/ledger-live/.github/workflows/build-mobile-external-reusable.yml@develop
+ with:
+ ref: ${{ github.event.pull_request.head.ref }}
+ repository: ${{ github.event.pull_request.head.repo.full_name }}
+ secrets: inherit
# Final Check required
ok:
diff --git a/.github/workflows/build-and-test-pr.yml b/.github/workflows/build-and-test-pr.yml
index cf2b38919b9e..37c55935c2e0 100644
--- a/.github/workflows/build-and-test-pr.yml
+++ b/.github/workflows/build-and-test-pr.yml
@@ -14,6 +14,7 @@ permissions:
jobs:
determine-affected:
name: "Turbo Affected"
+ if: ${{github.event.pull_request.head.repo.full_name == github.repository }}
uses: LedgerHQ/ledger-live/.github/workflows/turbo-affected-reusable.yml@develop
with:
head_branch: ${{ github.event.pull_request.head.ref || github.event.merge_group.head_ref }}
diff --git a/.github/workflows/build-desktop-external-reusable.yml b/.github/workflows/build-desktop-external-reusable.yml
index e22f9a170bca..67bc572856df 100644
--- a/.github/workflows/build-desktop-external-reusable.yml
+++ b/.github/workflows/build-desktop-external-reusable.yml
@@ -2,20 +2,18 @@ name: "@Desktop • Build App (external)"
on:
workflow_call:
- workflow_dispatch:
inputs:
ref:
+ type: string
description: |
If you run this manually, and want to run on a PR, the correct ref should be refs/pull/{PR_NUMBER}/merge to
have the "normal" scenario involving checking out a merge commit between your branch and the base branch.
If you want to run only on a branch or specific commit, you can use either the sha or the branch name instead (prefer the first verion for PRs).
- required: false
- login:
- description: The GitHub username that triggered the workflow
- required: false
- base_ref:
- description: The base branch to merge the head into when checking out the code
- required: false
+ required: true
+ repository:
+ description: The repository to checkout the code from
+ type: string
+ required: true
jobs:
build-desktop-app:
@@ -46,7 +44,9 @@ jobs:
}
- uses: actions/checkout@v4
with:
- ref: ${{ github.ref_name }}
+ ref: ${{ inputs.ref }}
+ repository: ${{ inputs.repository }}
+ token: ${{ secrets.GITHUB_TOKEN }}
persist-credentials: false
- name: Setup git user
uses: LedgerHQ/ledger-live/tools/actions/composites/setup-git-user@develop
diff --git a/.github/workflows/build-mobile-external-reusable.yml b/.github/workflows/build-mobile-external-reusable.yml
index 30b9dc52fd37..fd729a3cae3e 100644
--- a/.github/workflows/build-mobile-external-reusable.yml
+++ b/.github/workflows/build-mobile-external-reusable.yml
@@ -2,23 +2,18 @@ name: "@Mobile • Build App (external)"
on:
workflow_call:
- workflow_dispatch:
inputs:
ref:
+ type: string
description: |
If you run this manually, and want to run on a PR, the correct ref should be refs/pull/{PR_NUMBER}/merge to
have the "normal" scenario involving checking out a merge commit between your branch and the base branch.
If you want to run only on a branch or specific commit, you can use either the sha or the branch name instead (prefer the first verion for PRs).
- login:
- description: The GitHub username that triggered the workflow
- required: false
- base_ref:
- description: The base branch to merge the head into when checking out the code
- required: false
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref_name != 'develop' && github.ref || github.run_id }}
- cancel-in-progress: true
+ required: true
+ repository:
+ description: The repository to checkout the code from
+ type: string
+ required: true
permissions:
contents: read
@@ -26,13 +21,15 @@ permissions:
jobs:
build-mobile-app-android:
name: "Build Ledger Live Mobile (Android)"
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
env:
NODE_OPTIONS: "--max-old-space-size=7168"
steps:
- uses: actions/checkout@v4
with:
- ref: ${{ github.ref_name }}
+ ref: ${{ inputs.ref }}
+ repository: ${{ inputs.repository }}
+ token: ${{ secrets.GITHUB_TOKEN }}
persist-credentials: false
- name: Setup git user
uses: LedgerHQ/ledger-live/tools/actions/composites/setup-git-user@develop
@@ -92,7 +89,9 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
- ref: ${{ github.ref_name }}
+ ref: ${{ inputs.ref }}
+ repository: ${{ inputs.repository }}
+ token: ${{ secrets.GITHUB_TOKEN }}
persist-credentials: false
- name: Setup git user
uses: LedgerHQ/ledger-live/tools/actions/composites/setup-git-user@develop
@@ -105,7 +104,7 @@ jobs:
run: pnpm i --filter="live-mobile..." --filter="ledger-live" --no-frozen-lockfile --unsafe-perm
report:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
needs: [build-mobile-app-android, build-mobile-app-ios]
if: ${{ !cancelled() && github.event_name == 'workflow_dispatch' }}
steps:
diff --git a/.github/workflows/test-desktop-external-reusable.yml b/.github/workflows/test-desktop-external-reusable.yml
index 3069cf438fd1..d39b21a02ecb 100644
--- a/.github/workflows/test-desktop-external-reusable.yml
+++ b/.github/workflows/test-desktop-external-reusable.yml
@@ -2,20 +2,18 @@ name: "@Desktop • Test App (external)"
on:
workflow_call:
- workflow_dispatch:
inputs:
ref:
+ type: string
description: |
If you run this manually, and want to run on a PR, the correct ref should be refs/pull/{PR_NUMBER}/merge to
have the "normal" scenario involving checking out a merge commit between your branch and the base branch.
If you want to run only on a branch or specific commit, you can use either the sha or the branch name instead (prefer the first verion for PRs).
- required: false
- login:
- description: The GitHub username that triggered the workflow
- required: false
- base_ref:
- description: The base branch to merge the head into when checking out the code
- required: false
+ required: true
+ repository:
+ description: The repository to checkout the code from
+ type: string
+ required: true
permissions:
id-token: write
@@ -27,12 +25,14 @@ jobs:
env:
NODE_OPTIONS: "--max-old-space-size=7168"
FORCE_COLOR: 3
- CI_OS: ubuntu-latest
- runs-on: ubuntu-latest
+ CI_OS: ubuntu-22.04
+ runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
- ref: ${{ github.ref_name }}
+ ref: ${{ inputs.ref }}
+ repository: ${{ inputs.repository }}
+ token: ${{ secrets.GITHUB_TOKEN }}
persist-credentials: false
- name: Setup the toolchain
uses: LedgerHQ/ledger-live/tools/actions/composites/setup-toolchain@develop
@@ -63,12 +63,14 @@ jobs:
env:
NODE_OPTIONS: "--max-old-space-size=7168"
FORCE_COLOR: 3
- CI_OS: ubuntu-latest
- runs-on: ubuntu-latest
+ CI_OS: ubuntu-22.04
+ runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
- ref: ${{ github.ref_name }}
+ ref: ${{ inputs.ref }}
+ repository: ${{ inputs.repository }}
+ token: ${{ secrets.GITHUB_TOKEN }}
persist-credentials: false
- name: Setup the toolchain
uses: LedgerHQ/ledger-live/tools/actions/composites/setup-toolchain@develop
@@ -84,17 +86,19 @@ jobs:
env:
NODE_OPTIONS: "--max-old-space-size=7168"
FORCE_COLOR: 3
- CI_OS: ubuntu-latest
+ CI_OS: ubuntu-22.04
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
# DEBUG: "pw:browser*"
# DEBUG_LOGS: 1
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
outputs:
status: ${{ steps.tests.outcome }}
steps:
- uses: actions/checkout@v4
with:
- ref: ${{ github.ref_name }}
+ ref: ${{ inputs.ref }}
+ repository: ${{ inputs.repository }}
+ token: ${{ secrets.GITHUB_TOKEN }}
persist-credentials: false
- name: Setup the toolchain
uses: LedgerHQ/ledger-live/tools/actions/composites/setup-toolchain@develop
@@ -147,12 +151,14 @@ jobs:
report:
needs: [codechecks, unit-tests, e2e-tests-linux]
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
if: ${{ !cancelled() && github.event_name == 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v4
with:
- ref: ${{ github.ref_name }}
+ ref: ${{ inputs.ref }}
+ repository: ${{ inputs.repository }}
+ token: ${{ secrets.GITHUB_TOKEN }}
persist-credentials: false
- name: "download linter results"
uses: actions/download-artifact@v4
@@ -318,22 +324,3 @@ jobs:
with:
name: summary.json
path: ${{ github.workspace }}/summary.json
-
- allure-report:
- name: "Allure Reports Export on Server"
- needs: [e2e-tests-linux]
- runs-on: [ledger-live-medium]
- if: ${{ !cancelled() && github.ref_name == 'develop' }}
- steps:
- - name: checkout
- uses: actions/checkout@v4
- with:
- ref: ${{ github.ref_name }}
- - name: Send Results and Generate Allure Report - Linux
- uses: LedgerHQ/ledger-live/tools/actions/composites/upload-allure-report@develop
- if: ${{ !cancelled() }}
- with:
- platform: linux
- login: ${{ vars.ALLURE_USERNAME }}
- password: ${{ secrets.ALLURE_LEDGER_LIVE_PASSWORD }}
- path: allure-results-linux
diff --git a/.github/workflows/test-mobile-e2e-reusable.yml b/.github/workflows/test-mobile-e2e-reusable.yml
index cd50fb28eaea..da711859c946 100644
--- a/.github/workflows/test-mobile-e2e-reusable.yml
+++ b/.github/workflows/test-mobile-e2e-reusable.yml
@@ -32,6 +32,11 @@ on:
description: "[iOS] Test Execution ticket ID. Ex: 'B2CQA-2461'"
required: false
type: string
+ speculos_tests:
+ description: Run Speculos tests
+ required: false
+ type: boolean
+ default: false
# Uncomment to have log-level: trace on detox run and build
# (cf: apps/ledger-live-mobile/detox.config.js)
@@ -173,6 +178,8 @@ jobs:
AVD_CORES: 4
AVD_RAM_SIZE: 4096M
AVD_OPTIONS: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
+ SPECULOS_IMAGE_TAG: ghcr.io/ledgerhq/speculos:0.9.5
+ COINAPPS: ${{ github.workspace }}/coin-apps
outputs:
status: ${{ steps.detox.outcome }}
steps:
@@ -255,12 +262,22 @@ jobs:
disable-linux-hw-accel: false
emulator-options: ${{ env.AVD_OPTIONS }}
script: ./tools/scripts/wait_emulator_idle.sh
+ - name: Setup Speculos image and Coin Apps
+ if: ${{ inputs.speculos_tests }}
+ uses: LedgerHQ/ledger-live/tools/actions/composites/setup-speculos_image@develop
+ with:
+ coinapps_path: ${{ env.COINAPPS }}
+ speculos_tag: ${{ env.SPECULOS_IMAGE_TAG }}
+ bot_id: ${{ secrets.GH_BOT_APP_ID }}
+ bot_key: ${{ secrets.GH_BOT_PRIVATE_KEY }}
- name: Run Android Tests
id: detox
- run: pnpm mobile e2e:ci -p android -t
+ run: pnpm mobile e2e:ci -p android -t $([[ "$INPUT_SPECULOS" == "true" ]] && printf %s '--speculos')
timeout-minutes: 45
env:
DETOX_INSTALL_TIMEOUT: 120000
+ SEED: ${{ secrets.SEED_QAA_B2C }}
+ INPUT_SPECULOS: ${{ inputs.speculos_tests }}
- name: Generate single file Allure report
if: ${{ !cancelled() || steps.detox.outcome == 'cancelled' }}
run: pnpm dlx allure-commandline generate apps/ledger-live-mobile/artifacts --single-file
diff --git a/.github/workflows/test-mobile-e2e.yml b/.github/workflows/test-mobile-e2e.yml
index 5f28d86dfb9a..54369f71fd71 100644
--- a/.github/workflows/test-mobile-e2e.yml
+++ b/.github/workflows/test-mobile-e2e.yml
@@ -28,6 +28,11 @@ on:
description: "[iOS] Test Execution ticket ID. Ex: 'B2CQA-2461'"
required: false
type: string
+ speculos_tests:
+ description: Run Speculos tests
+ required: false
+ type: boolean
+ default: false
# Uncomment to have log-level: trace on detox run and build
# (cf: apps/ledger-live-mobile/detox.config.js)
diff --git a/.github/workflows/test-mobile.yml b/.github/workflows/test-mobile.yml
index 315586c03f30..f2faa42bb4cd 100644
--- a/.github/workflows/test-mobile.yml
+++ b/.github/workflows/test-mobile.yml
@@ -2,12 +2,6 @@ name: "@Mobile • Test App"
run-name: "@Mobile • Test App triggered by ${{ inputs.login || github.actor }} ${{ format('on ref {0}', github.ref_name) }}"
on:
- push:
- branches:
- - main
- - develop
- - release
- - hotfix
workflow_dispatch:
inputs:
ref:
diff --git a/.github/workflows/test-ui-e2e-only-desktop.yml b/.github/workflows/test-ui-e2e-only-desktop.yml
index 5ea2379d9460..8cce945c1b09 100644
--- a/.github/workflows/test-ui-e2e-only-desktop.yml
+++ b/.github/workflows/test-ui-e2e-only-desktop.yml
@@ -130,7 +130,7 @@ jobs:
export COINAPPS=$PWD/coin-apps
export MOCK=0
if [ "${{ inputs.invert_filter }}" = true ]; then invert_filter="-invert"; fi
- xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- pnpm desktop test:playwright:speculos ${INPUTS_TEST_FILTER:+--grep$invert_filter} "${{ inputs.test_filter }}" --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
+ xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- pnpm desktop test:playwright:speculos --max-failures 50 ${INPUTS_TEST_FILTER:+--grep$invert_filter} "${{ inputs.test_filter }}" --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
env:
INPUTS_TEST_FILTER: ${{ inputs.test_filter }}
SEED: ${{ secrets.SEED_QAA_B2C }}
diff --git a/apps/ledger-live-desktop/index-types.d.ts b/apps/ledger-live-desktop/index-types.d.ts
index 64587364fbb3..d1e41cc52d9f 100644
--- a/apps/ledger-live-desktop/index-types.d.ts
+++ b/apps/ledger-live-desktop/index-types.d.ts
@@ -63,6 +63,7 @@ interface Window {
getAllFeatureFlags: (appLanguage: string) => Partial<{ [key in FeatureId]: Feature }>;
getAllEnvs: () => { [key in EnvName]: unknown };
+ saveLogs: (path: string) => void;
// for mocking purposes apparently?
// eslint-disable-next-line
diff --git a/apps/ledger-live-desktop/package.json b/apps/ledger-live-desktop/package.json
index 0ab851b4b4f6..2c4905e1cd61 100644
--- a/apps/ledger-live-desktop/package.json
+++ b/apps/ledger-live-desktop/package.json
@@ -143,7 +143,7 @@
"rxjs": "^7.8.1",
"secp256k1": "^4.0.3",
"semver": "^7.1.3",
- "storyly-web": "^2.8.0",
+ "storyly-web": "^3.5.0",
"styled-components": "^5.3.3",
"styled-system": "^5.1.5",
"timemachine": "^0.3.2",
diff --git a/apps/ledger-live-desktop/src/newArch/features/AnalyticsOptInPrompt/screens/VariantA/Main/components/MainFooter/index.tsx b/apps/ledger-live-desktop/src/newArch/features/AnalyticsOptInPrompt/screens/VariantA/Main/components/MainFooter/index.tsx
index 0b29747d0e65..bb762e6033d9 100644
--- a/apps/ledger-live-desktop/src/newArch/features/AnalyticsOptInPrompt/screens/VariantA/Main/components/MainFooter/index.tsx
+++ b/apps/ledger-live-desktop/src/newArch/features/AnalyticsOptInPrompt/screens/VariantA/Main/components/MainFooter/index.tsx
@@ -38,7 +38,13 @@ const MainFooter = ({ setWantToManagePreferences, onShareAnalyticsChange }: Main
>
{t("analyticsOptInPrompt.variantA.refuse")}
-
{
+type Props = {
+ error: Error;
+};
+
+const Error: React.FC = ({ error }) => {
const { t } = useTranslation();
return (
-
+
{t("crash.title")}
+ {`(${error?.message})`}
);
};
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/Actions.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/Actions.tsx
new file mode 100644
index 000000000000..f81c2a28202b
--- /dev/null
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/Actions.tsx
@@ -0,0 +1,32 @@
+import React from "react";
+import { Flex, Icons, IconsLegacy, Text } from "@ledgerhq/react-ui";
+import Button from "~/renderer/components/Button";
+import { useTranslation } from "react-i18next";
+
+const Actions = () => {
+ const { t } = useTranslation();
+ return (
+
+ {
+ /* TODO */
+ }}
+ center
+ >
+
+
+
+ {t("ordinals.inscriptions.detailsDrawer.hide")}
+
+
+
+
+
+
+
+ );
+};
+export default Actions;
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/SubTitle.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/SubTitle.tsx
new file mode 100644
index 000000000000..21c43cae7fe2
--- /dev/null
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/SubTitle.tsx
@@ -0,0 +1,25 @@
+import React from "react";
+import { Flex, Text } from "@ledgerhq/react-ui";
+import IconContainer from "LLD/features/Collectibles/components/Collection/TableRow/IconContainer";
+import { IconProps } from "LLD/features/Collectibles/types/Collection";
+import { MappingKeys } from "LLD/features/Collectibles/types/Ordinals";
+import { useTranslation } from "react-i18next";
+
+type Props = {
+ icons: (({ size, color, style }: IconProps) => JSX.Element)[];
+ names: MappingKeys[];
+};
+
+const SubTitle: React.FC = ({ icons, names }) => {
+ const { t } = useTranslation();
+ return (
+
+
+
+ {t("ordinals.inscriptions.detailsDrawer.storedOnChain")}
+
+
+ );
+};
+
+export default SubTitle;
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/index.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/index.tsx
new file mode 100644
index 000000000000..abedd3a803ae
--- /dev/null
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/index.tsx
@@ -0,0 +1,66 @@
+import React from "react";
+import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";
+import { DetailDrawer } from "LLD/features/Collectibles/components";
+import { CollectibleTypeEnum } from "LLD/features/Collectibles/types/enum/Collectibles";
+import useInscriptionDetailDrawer from "./useInscriptionDetailDrawer";
+import Actions from "./Actions";
+import SubTitle from "./SubTitle";
+
+type ViewProps = ReturnType & {
+ onClose: () => void;
+};
+
+type Props = {
+ inscription: SimpleHashNft;
+ correspondingRareSat: SimpleHashNft | null | undefined;
+ isLoading: boolean;
+ onClose: () => void;
+};
+
+const View: React.FC = ({ data, rareSat, onClose }) => (
+
+ {rareSat?.icons && (
+
+
+
+ )}
+
+
+
+
+);
+
+const InscriptionDetailDrawer = ({
+ inscription,
+ isLoading,
+ correspondingRareSat,
+ onClose,
+}: Props) => {
+ return (
+
+ );
+};
+
+export default InscriptionDetailDrawer;
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/useInscriptionDetailDrawer.ts b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/useInscriptionDetailDrawer.ts
new file mode 100644
index 000000000000..c82cae275514
--- /dev/null
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DetailsDrawer/useInscriptionDetailDrawer.ts
@@ -0,0 +1,64 @@
+import { useMemo, useState } from "react";
+import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";
+import useCollectibles from "LLD/features/Collectibles/hooks/useCollectibles";
+import { createDetails } from "LLD/features/Collectibles/utils/createInscriptionDetailsArrays";
+import { useCalendarFormatted } from "~/renderer/hooks/useDateFormatter";
+import { Tag } from "LLD/features/Collectibles/types/DetailDrawer";
+import { processRareSat } from "../helpers";
+
+type Props = {
+ isLoading: boolean;
+ inscription: SimpleHashNft;
+ correspondingRareSat: SimpleHashNft | null | undefined;
+};
+
+const useInscriptionDetailDrawer = ({ isLoading, inscription, correspondingRareSat }: Props) => {
+ const [useFallback, setUseFallback] = useState(false);
+ const imageUri =
+ inscription.video_url || inscription.previews?.image_large_url || inscription.image_url;
+
+ const isVideo = !!inscription.video_url;
+
+ const contentType = isVideo ? "video" : imageUri ? "image" : "";
+
+ const { isPanAndZoomOpen, openCollectiblesPanAndZoom, closeCollectiblesPanAndZoom } =
+ useCollectibles();
+
+ const createdDateFromTimestamp = new Date(inscription.first_created?.timestamp || 0);
+ const formattedCreatedDate = useCalendarFormatted(createdDateFromTimestamp);
+ const createdDate = createdDateFromTimestamp === new Date(0) ? "" : formattedCreatedDate;
+ const details = createDetails(inscription, createdDate);
+
+ const tags: Tag[] =
+ inscription.extra_metadata?.attributes?.map(attr => ({
+ key: attr.trait_type,
+ value: attr.value,
+ })) || [];
+
+ const rareSat = useMemo(() => {
+ if (correspondingRareSat) return processRareSat(correspondingRareSat);
+ }, [correspondingRareSat]);
+
+ const data = {
+ areFieldsLoading: isLoading,
+ collectibleName: inscription.name || inscription.contract.name || "",
+ contentType,
+ collectionName: inscription.collection.name || "",
+ details: details,
+ previewUri: imageUri,
+ originalUri: imageUri,
+ isPanAndZoomOpen,
+ mediaType: inscription.video_properties?.mime_type || "image",
+ tags: tags,
+ useFallback: useFallback,
+ tokenId: inscription.nft_id,
+ isOpened: true,
+ closeCollectiblesPanAndZoom,
+ openCollectiblesPanAndZoom,
+ setUseFallback: setUseFallback,
+ };
+
+ return { inscription, data, rareSat };
+};
+
+export default useInscriptionDetailDrawer;
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DiscoveryDrawer/Header.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DiscoveryDrawer/Header.tsx
index b1a82554bdb0..281c62f68903 100644
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DiscoveryDrawer/Header.tsx
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DiscoveryDrawer/Header.tsx
@@ -5,6 +5,8 @@ const DiscoveryDrawerHeader: React.FC = () => (
(
top: 0,
left: "50%",
transform: "translateX(-50%) translateY(-50%)",
- filter: "blur(120px)",
+ filter: "blur(17px)",
+ opacity: 0.2,
}}
>
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DiscoveryDrawer/ProtectBox.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DiscoveryDrawer/ProtectBox.tsx
new file mode 100644
index 000000000000..8c428e9f8a18
--- /dev/null
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DiscoveryDrawer/ProtectBox.tsx
@@ -0,0 +1,35 @@
+import React, { useCallback } from "react";
+import { Flex, Text } from "@ledgerhq/react-ui";
+import Switch from "~/renderer/components/Switch";
+import { hasProtectedOrdinalsAssetsSelector } from "~/renderer/reducers/settings";
+import { setHasProtectedOrdinalsAssets } from "~/renderer/actions/settings";
+import { useDispatch, useSelector } from "react-redux";
+import { useTranslation } from "react-i18next";
+
+const ProtectBox: React.FC = () => {
+ const { t } = useTranslation();
+ const dispatch = useDispatch();
+ const hasProtectedOrdinals = useSelector(hasProtectedOrdinalsAssetsSelector);
+
+ const onSwitchChange = useCallback(() => {
+ dispatch(setHasProtectedOrdinalsAssets(!hasProtectedOrdinals));
+ }, [dispatch, hasProtectedOrdinals]);
+
+ return (
+
+
+ {t("ordinals.inscriptions.discoveryDrawer.protectTitle")}
+
+
+
+ {t("ordinals.inscriptions.discoveryDrawer.protectDescription")}
+
+
+
+
+
+
+ );
+};
+
+export default ProtectBox;
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DiscoveryDrawer/index.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DiscoveryDrawer/index.tsx
index 806c91d5ece9..b2c36f00ffdc 100644
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DiscoveryDrawer/index.tsx
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/DiscoveryDrawer/index.tsx
@@ -1,6 +1,7 @@
import React from "react";
import DiscoveryDrawerHeader from "./Header";
-import { Button, CryptoIcon, Flex, Link, Text } from "@ledgerhq/react-ui";
+import ProtectBox from "./ProtectBox";
+import { Button, CryptoIcon, Flex, Text } from "@ledgerhq/react-ui";
import { SideDrawer } from "~/renderer/components/SideDrawer";
import { Direction } from "@ledgerhq/react-ui/components/layout/Drawer/index";
import { space } from "@ledgerhq/react-ui/styles/theme";
@@ -17,7 +18,6 @@ const DiscoveryDrawer = ({ isOpen, onClose }: Props) => {
const { t } = useTranslation();
const learnMoreUrl = urls.whatAreOrdinals;
const onButtonClick = () => openURL(learnMoreUrl);
- const onLinkClick = () => onClose();
return (
{
>
{t("ordinals.inscriptions.discoveryDrawer.description")}
-
+
{t("ordinals.inscriptions.discoveryDrawer.learnMore")}
-
- {t("ordinals.inscriptions.discoveryDrawer.viewCoinControl")}
-
+
);
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/Item.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/Item.tsx
deleted file mode 100644
index dc3f2ab8ffd8..000000000000
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/Item.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import { InscriptionsItemProps } from "LLD/features/Collectibles/types/Inscriptions";
-import TableRow from "LLD/features/Collectibles/components/Collection/TableRow";
-import React from "react";
-
-type ItemProps = {
- isLoading: boolean;
-} & InscriptionsItemProps;
-
-const Item: React.FC = ({
- isLoading,
- tokenName,
- collectionName,
- tokenIcons,
- media,
- rareSatName,
- onClick,
-}) => (
-
-);
-
-export default Item;
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/Item/index.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/Item/index.tsx
new file mode 100644
index 000000000000..b9c404527346
--- /dev/null
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/Item/index.tsx
@@ -0,0 +1,40 @@
+import React, { useMemo } from "react";
+import { InscriptionsItemProps } from "LLD/features/Collectibles/types/Inscriptions";
+import TableRow from "LLD/features/Collectibles/components/Collection/TableRow";
+import { GroupedNftOrdinals } from "@ledgerhq/live-nft-react/index";
+import { findCorrespondingSat } from "LLD/features/Collectibles/utils/findCorrespondingSat";
+import { processRareSat } from "../helpers";
+
+type ItemProps = {
+ isLoading: boolean;
+ inscriptionsGroupedWithRareSats: GroupedNftOrdinals[];
+} & InscriptionsItemProps;
+
+const Item: React.FC = ({
+ isLoading,
+ tokenName,
+ collectionName,
+ media,
+ nftId,
+ inscriptionsGroupedWithRareSats,
+ onClick,
+}) => {
+ const correspondingRareSat = findCorrespondingSat(inscriptionsGroupedWithRareSats, nftId);
+ const rareSat = useMemo(() => {
+ if (correspondingRareSat) return processRareSat(correspondingRareSat.rareSat);
+ }, [correspondingRareSat]);
+
+ return (
+
+ );
+};
+
+export default Item;
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/helpers.ts b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/helpers.ts
index 0917bb99b130..695f0a214b1d 100644
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/helpers.ts
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/helpers.ts
@@ -1,37 +1,14 @@
import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";
-import { IconProps } from "LLD/features/Collectibles/types/Collection";
-import { mappingKeysWithIconAndName } from "../Icons";
-import { MappingKeys } from "LLD/features/Collectibles/types/Ordinals";
+import { createRareSatObject, matchCorrespondingIcon } from "../helpers";
-function matchCorrespondingIcon(
- rareSats: SimpleHashNft[],
-): Array JSX.Element> }> {
- return rareSats.map(rareSat => {
- const iconKeys: string[] = [];
- const rarity = rareSat.extra_metadata?.ordinal_details?.sat_rarity?.toLowerCase();
-
- if (rarity && rarity !== "common") {
- iconKeys.push(rarity.replace(" ", "_"));
- }
-
- const icons = iconKeys
- .map(
- iconKey =>
- mappingKeysWithIconAndName[iconKey as keyof typeof mappingKeysWithIconAndName]?.icon,
- )
- .filter(Boolean) as Array<({ size, color, style }: IconProps) => JSX.Element>;
-
- return { ...rareSat, icons };
- });
-}
-
-export function getInscriptionsData(inscriptions: SimpleHashNft[]) {
- const inscriptionsWithIcons = matchCorrespondingIcon(inscriptions);
- return inscriptionsWithIcons.map(item => ({
+export function getInscriptionsData(
+ inscriptions: SimpleHashNft[],
+ onInscriptionClick: (inscription: SimpleHashNft) => void,
+) {
+ return inscriptions.map(item => ({
tokenName: item.name || item.contract.name || "",
+ nftId: item.nft_id,
collectionName: item.collection.name,
- tokenIcons: item.icons,
- rareSatName: [item.extra_metadata?.ordinal_details?.sat_rarity] as MappingKeys[],
media: {
uri: item.image_url || item.previews?.image_small_url,
isLoading: false,
@@ -39,9 +16,12 @@ export function getInscriptionsData(inscriptions: SimpleHashNft[]) {
contentType: item.extra_metadata?.ordinal_details?.content_type,
mediaType: "image",
},
- onClick: () => {
- console.log(`you clicked on : \x1b[32m${item.name}\x1b[0m inscription`);
- },
- // it does nothing for now but it will be used for the next PR with the drawer
+ onClick: () => onInscriptionClick(item),
}));
}
+
+export function processRareSat(inscription: SimpleHashNft) {
+ const matchedRareSatsIcons = matchCorrespondingIcon(inscription);
+ const rareSatObject = createRareSatObject({ rareSat: matchedRareSatsIcons });
+ return rareSatObject.rareSat[0];
+}
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/index.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/index.tsx
index 489b9f88d905..e6269cb4034b 100644
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/index.tsx
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/index.tsx
@@ -1,5 +1,5 @@
import React from "react";
-import { Box, Flex } from "@ledgerhq/react-ui";
+import { Box, Flex, Icons } from "@ledgerhq/react-ui";
import { useInscriptionsModel } from "./useInscriptionsModel";
import TableContainer from "~/renderer/components/TableContainer";
import TableHeader from "LLD/features/Collectibles/components/Collection/TableHeader";
@@ -9,13 +9,28 @@ import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";
import Loader from "../Loader";
import Error from "../Error";
import Item from "./Item";
+import EmptyCollection from "LLD/features/Collectibles/components/Collection/EmptyCollection";
+import { CollectibleTypeEnum } from "LLD/features/Collectibles/types/enum/Collectibles";
+import Button from "~/renderer/components/Button";
+import { useTranslation } from "react-i18next";
+import { GroupedNftOrdinals } from "@ledgerhq/live-nft-react/index";
-type ViewProps = ReturnType & { isLoading: boolean; isError: boolean };
+type ViewProps = ReturnType & {
+ isLoading: boolean;
+ isError: boolean;
+ error: Error | null;
+ inscriptionsGroupedWithRareSats: GroupedNftOrdinals[];
+ onReceive: () => void;
+};
type Props = {
inscriptions: SimpleHashNft[];
isLoading: boolean;
isError: boolean;
+ error: Error | null;
+ inscriptionsGroupedWithRareSats: GroupedNftOrdinals[];
+ onReceive: () => void;
+ onInscriptionClick: (inscription: SimpleHashNft) => void;
};
const View: React.FC = ({
@@ -23,34 +38,40 @@ const View: React.FC = ({
isLoading,
isError,
inscriptions,
+ error,
+ inscriptionsGroupedWithRareSats,
onShowMore,
+ onReceive,
}) => {
+ const { t } = useTranslation();
const hasInscriptions = inscriptions.length > 0 && !isError;
const nothingToShow = !hasInscriptions && !isLoading && !isError;
+ const hasError = isError && error;
return (
{isLoading && }
- {isError && }
+ {hasError && }
{hasInscriptions &&
inscriptions.map((item, index) => (
))}
{nothingToShow && (
-
- {"NOTHING TO SHOW WAITING FOR DESIGN"}
-
+
+
+
+
+ {t("ordinals.inscriptions.receive")}
+
+
+
)}
{displayShowMore && !isError && }
@@ -58,8 +79,23 @@ const View: React.FC = ({
);
};
-const Inscriptions: React.FC = ({ inscriptions, isLoading, isError }) => (
-
+const Inscriptions: React.FC = ({
+ inscriptions,
+ isLoading,
+ isError,
+ error,
+ inscriptionsGroupedWithRareSats,
+ onReceive,
+ onInscriptionClick,
+}) => (
+
);
export default Inscriptions;
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/useInscriptionsModel.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/useInscriptionsModel.tsx
index c3893f978713..431e3234b68f 100644
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/useInscriptionsModel.tsx
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/Inscriptions/useInscriptionsModel.tsx
@@ -5,21 +5,28 @@ import { InscriptionsItemProps } from "LLD/features/Collectibles/types/Inscripti
type Props = {
inscriptions: SimpleHashNft[];
+ onInscriptionClick: (inscription: SimpleHashNft) => void;
};
-export const useInscriptionsModel = ({ inscriptions }: Props) => {
- const [displayShowMore, setDisplayShowMore] = useState(false);
- const [displayedObjects, setDisplayedObjects] = useState([]);
-
+export const useInscriptionsModel = ({ inscriptions, onInscriptionClick }: Props) => {
const items: InscriptionsItemProps[] = useMemo(
- () => getInscriptionsData(inscriptions),
- [inscriptions],
+ () => getInscriptionsData(inscriptions, onInscriptionClick),
+ [inscriptions, onInscriptionClick],
);
+ const initialDisplayedObjects = items.slice(0, 3);
+ const initialDisplayShowMore = items.length > 3;
+
+ const [displayShowMore, setDisplayShowMore] = useState(initialDisplayShowMore);
+ const [displayedObjects, setDisplayedObjects] =
+ useState(initialDisplayedObjects);
+
useEffect(() => {
- if (items.length > 3) setDisplayShowMore(true);
- setDisplayedObjects(items.slice(0, 3));
- }, [items]);
+ if (displayedObjects.length === 0) {
+ if (items.length > 3) setDisplayShowMore(true);
+ setDisplayedObjects(items.slice(0, 3));
+ }
+ }, [items, displayedObjects.length]);
const onShowMore = () => {
setDisplayedObjects(prevDisplayedObjects => {
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/RareSats/helpers.ts b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/RareSats/helpers.ts
deleted file mode 100644
index 9a7423bbe53b..000000000000
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/RareSats/helpers.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import { mappingKeysWithIconAndName } from "../Icons";
-import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";
-import {
- SimpleHashNftWithIcons,
- RareSat,
- MappingKeys,
-} from "LLD/features/Collectibles/types/Ordinals";
-
-export function matchCorrespondingIcon(rareSats: SimpleHashNft[]): SimpleHashNftWithIcons[] {
- return rareSats.map(rareSat => {
- const iconKeys: string[] = [];
- if (rareSat.name) {
- iconKeys.push(rareSat.name.toLowerCase().replace(" ", "_"));
- }
-
- if (rareSat.extra_metadata?.utxo_details?.satributes) {
- iconKeys.push(...Object.keys(rareSat.extra_metadata.utxo_details.satributes));
- }
-
- const icons = iconKeys
- .map(
- iconKey =>
- mappingKeysWithIconAndName[iconKey as keyof typeof mappingKeysWithIconAndName]?.icon,
- )
- .filter(Boolean) as RareSat["icons"];
-
- return { ...rareSat, icons };
- });
-}
-
-export function createRareSatObject(
- rareSats: Record,
-): Record {
- const result: Record = {};
-
- for (const [key, value] of Object.entries(rareSats)) {
- result[key] = value.map(rareSat => {
- const { icons, extra_metadata } = rareSat;
- const year = extra_metadata?.utxo_details?.sat_ranges?.[0]?.year || "";
- const displayed_names =
- Object.values(extra_metadata?.utxo_details?.satributes || {})
- .map(attr => attr.display_name)
- .join(" / ") || "";
- const names = rareSat.extra_metadata?.utxo_details?.satributes
- ? (Object.keys(rareSat.extra_metadata.utxo_details.satributes) as MappingKeys[])
- : [];
- const count = extra_metadata?.utxo_details?.value || 0;
- const isMultipleRow = value.length > 1;
-
- return {
- year,
- displayed_names,
- names,
- count: `${count} ${count > 1 ? "sats" : "sat"}`,
- isMultipleRow,
- icons,
- };
- });
- }
-
- return result;
-}
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/RareSats/index.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/RareSats/index.tsx
index fe8603eedfb6..e1e8dc8f9adb 100644
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/RareSats/index.tsx
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/RareSats/index.tsx
@@ -4,16 +4,22 @@ import TableContainer from "~/renderer/components/TableContainer";
import TableHeader from "LLD/features/Collectibles/components/Collection/TableHeader";
import Item from "./Item";
import { TableHeaderTitleKey } from "LLD/features/Collectibles/types/Collection";
-import { Box, Flex } from "@ledgerhq/react-ui";
+import { Box, Flex, Icons } from "@ledgerhq/react-ui";
import { TableHeader as TableHeaderContainer } from "./TableHeader";
import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";
import Loader from "../Loader";
import Error from "../Error";
+import EmptyCollection from "../../../components/Collection/EmptyCollection";
+import { CollectibleTypeEnum } from "../../../types/enum/Collectibles";
+import Button from "~/renderer/components/Button";
+import { useTranslation } from "react-i18next";
type ViewProps = ReturnType & {
isLoading: boolean;
isError: boolean;
isFetched: boolean;
+ error: Error | null;
+ onReceive: () => void;
};
type Props = {
@@ -21,19 +27,23 @@ type Props = {
isLoading: boolean;
isError: boolean;
isFetched: boolean;
+ error: Error | null;
+ onReceive: () => void;
};
-function View({ rareSats, isLoading, isError, isFetched }: ViewProps) {
- const isLoaded = isFetched;
+function View({ rareSats, isLoading, isError, isFetched, error, onReceive }: ViewProps) {
+ const { t } = useTranslation();
+ const isLoaded = isFetched && !isError && !isLoading;
const hasRareSats = Object.values(rareSats).length > 0;
const dataReady = isLoaded && hasRareSats;
+ const hasError = isError && error;
return (
{isLoading && }
- {isError && }
+ {hasError && }
{dataReady && }
{dataReady &&
@@ -45,9 +55,14 @@ function View({ rareSats, isLoading, isError, isFetched }: ViewProps) {
))}
{isLoaded && !hasRareSats && (
-
- {"NOTHING TO SHOW WAITING FOR DESIGN"}
-
+
+
+
+
+ {t("ordinals.rareSats.receive")}
+
+
+
)}
@@ -55,12 +70,14 @@ function View({ rareSats, isLoading, isError, isFetched }: ViewProps) {
);
}
-const RareSats = ({ rareSats, isLoading, isError, isFetched }: Props) => {
+const RareSats = ({ rareSats, isLoading, isError, isFetched, error, onReceive }: Props) => {
return (
);
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/RareSats/useRareSatsModel.ts b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/RareSats/useRareSatsModel.ts
index a59b1eafc054..e73078536530 100644
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/RareSats/useRareSatsModel.ts
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/RareSats/useRareSatsModel.ts
@@ -1,4 +1,4 @@
-import { matchCorrespondingIcon, createRareSatObject } from "./helpers";
+import { matchCorrespondingIcon, createRareSatObject } from "../helpers";
import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";
import { regroupRareSatsByContractAddress } from "@ledgerhq/live-nft-react";
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/helpers.ts b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/helpers.ts
new file mode 100644
index 000000000000..c323b37e8bd9
--- /dev/null
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/components/helpers.ts
@@ -0,0 +1,78 @@
+import { mappingKeysWithIconAndName } from "./Icons";
+import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";
+import {
+ SimpleHashNftWithIcons,
+ RareSat,
+ MappingKeys,
+} from "LLD/features/Collectibles/types/Ordinals";
+
+function processSingleRareSat(rareSat: SimpleHashNft): SimpleHashNftWithIcons {
+ const iconKeys: string[] = [];
+ if (rareSat.name) {
+ iconKeys.push(rareSat.name.toLowerCase().replace(" ", "_"));
+ }
+
+ if (rareSat.extra_metadata?.utxo_details?.satributes) {
+ iconKeys.push(...Object.keys(rareSat.extra_metadata.utxo_details.satributes));
+ }
+
+ const icons = iconKeys
+ .map(
+ iconKey =>
+ mappingKeysWithIconAndName[iconKey as keyof typeof mappingKeysWithIconAndName]?.icon,
+ )
+ .filter(Boolean) as RareSat["icons"];
+
+ return { ...rareSat, icons };
+}
+
+function mapToRareSat(rareSat: SimpleHashNftWithIcons, isMultipleRow: boolean): RareSat {
+ const { icons, extra_metadata } = rareSat;
+ const year = extra_metadata?.utxo_details?.sat_ranges?.[0]?.year || "";
+ const displayed_names =
+ Object.values(extra_metadata?.utxo_details?.satributes || {})
+ .map(attr => attr.display_name)
+ .join(" / ") || "";
+ const names = rareSat.extra_metadata?.utxo_details?.satributes
+ ? (Object.keys(rareSat.extra_metadata.utxo_details.satributes) as MappingKeys[])
+ : [];
+ const count = extra_metadata?.utxo_details?.value || 0;
+
+ return {
+ year,
+ displayed_names,
+ names,
+ count: `${count} ${count > 1 ? "sats" : "sat"}`,
+ isMultipleRow,
+ icons,
+ };
+}
+
+function processRareSatObject(
+ rareSats: Record,
+): Record {
+ const result: Record = {};
+
+ for (const [key, value] of Object.entries(rareSats)) {
+ result[key] = value.map(rareSat => mapToRareSat(rareSat, value.length > 1));
+ }
+
+ return result;
+}
+
+export function matchCorrespondingIcon(
+ rareSats: SimpleHashNft | SimpleHashNft[],
+): SimpleHashNftWithIcons[] {
+ const rareSatArray = Array.isArray(rareSats) ? rareSats : [rareSats];
+ return rareSatArray.map(processSingleRareSat);
+}
+
+export function createRareSatObject(
+ rareSats: Record | SimpleHashNftWithIcons[],
+): Record {
+ if (Array.isArray(rareSats)) {
+ return { default: rareSats.map(rareSat => mapToRareSat(rareSat, rareSats.length > 1)) };
+ } else {
+ return processRareSatObject(rareSats);
+ }
+}
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/screens/Account/index.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/screens/Account/index.tsx
index 7f8bb60fa721..cbec04e52909 100644
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/screens/Account/index.tsx
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/screens/Account/index.tsx
@@ -5,6 +5,7 @@ import RareSats from "../../components/RareSats";
import DiscoveryDrawer from "../../components/Inscriptions/DiscoveryDrawer";
import { BitcoinAccount } from "@ledgerhq/coin-bitcoin/lib/types";
import { useBitcoinAccountModel } from "./useBitcoinAccountModel";
+import InscriptionDetailsDrawer from "../../components/Inscriptions/DetailsDrawer";
type ViewProps = ReturnType;
@@ -17,12 +18,32 @@ const View: React.FC = ({
rest,
rareSats,
isDrawerOpen,
+ selectedInscription,
+ correspondingRareSat,
+ inscriptionsGroupedWithRareSats,
handleDrawerClose,
+ onReceive,
+ onInscriptionClick,
+ onDetailsDrawerClose,
}) => (
-
-
+
+
+ {selectedInscription && (
+
+ )}
);
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/screens/Account/useBitcoinAccountModel.ts b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/screens/Account/useBitcoinAccountModel.ts
index 0c7ab8993add..c1c8d0ad620b 100644
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/screens/Account/useBitcoinAccountModel.ts
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/Ordinals/screens/Account/useBitcoinAccountModel.ts
@@ -1,9 +1,12 @@
import { BitcoinAccount } from "@ledgerhq/coin-bitcoin/lib/types";
+import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";
import useFetchOrdinals from "LLD/features/Collectibles/hooks/useFetchOrdinals";
-import { useState, useEffect } from "react";
+import { useState, useEffect, useCallback } from "react";
import { useDispatch, useSelector } from "react-redux";
+import { openModal } from "~/renderer/actions/modals";
import { setHasSeenOrdinalsDiscoveryDrawer } from "~/renderer/actions/settings";
import { hasSeenOrdinalsDiscoveryDrawerSelector } from "~/renderer/reducers/settings";
+import { findCorrespondingSat } from "LLD/features/Collectibles/utils/findCorrespondingSat";
interface Props {
account: BitcoinAccount;
@@ -12,8 +15,14 @@ interface Props {
export const useBitcoinAccountModel = ({ account }: Props) => {
const dispatch = useDispatch();
const hasSeenDiscoveryDrawer = useSelector(hasSeenOrdinalsDiscoveryDrawerSelector);
+ const [selectedInscription, setSelectedInscription] = useState(null);
+ const [correspondingRareSat, setCorrespondingRareSat] = useState<
+ SimpleHashNft | null | undefined
+ >(null);
- const { rareSats, inscriptions, ...rest } = useFetchOrdinals({ account });
+ const { rareSats, inscriptions, inscriptionsGroupedWithRareSats, ...rest } = useFetchOrdinals({
+ account,
+ });
const [isDrawerOpen, setIsDrawerOpen] = useState(!hasSeenDiscoveryDrawer);
@@ -28,5 +37,34 @@ export const useBitcoinAccountModel = ({ account }: Props) => {
dispatch(setHasSeenOrdinalsDiscoveryDrawer(true));
};
- return { rareSats, inscriptions, rest, isDrawerOpen, handleDrawerClose };
+ const onReceive = useCallback(() => {
+ dispatch(
+ openModal("MODAL_RECEIVE", {
+ account,
+ receiveOrdinalMode: true,
+ }),
+ );
+ }, [dispatch, account]);
+
+ const onInscriptionClick = (inscription: SimpleHashNft) => {
+ const groupedNft = findCorrespondingSat(inscriptionsGroupedWithRareSats, inscription.nft_id);
+ setCorrespondingRareSat(groupedNft?.rareSat ?? null);
+ setSelectedInscription(inscription);
+ };
+
+ const onDetailsDrawerClose = () => setSelectedInscription(null);
+
+ return {
+ rareSats,
+ inscriptions,
+ rest,
+ isDrawerOpen,
+ selectedInscription,
+ correspondingRareSat,
+ inscriptionsGroupedWithRareSats,
+ onReceive,
+ handleDrawerClose,
+ onInscriptionClick,
+ onDetailsDrawerClose,
+ };
};
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/__integration__/bitcoinPage.test.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/__integration__/bitcoinPage.test.tsx
index e1046b592629..66797fa60c8e 100644
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/__integration__/bitcoinPage.test.tsx
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/__integration__/bitcoinPage.test.tsx
@@ -18,24 +18,24 @@ jest.mock("~/renderer/linking", () => ({
describe("displayBitcoinPage", () => {
it("should display Bitcoin page with rare sats and inscriptions", async () => {
- const { user } = render();
+ const { user } = render(, {
+ initialState: {
+ settings: {
+ hasSeenOrdinalsDiscoveryDrawer: true,
+ },
+ },
+ });
- await waitFor(() => expect(screen.getByText(/inscription #63691311/i)).toBeVisible());
- await waitFor(() => expect(screen.getByTestId(/raresaticon-palindrome-0/i)).toBeVisible());
- await user.hover(screen.getByTestId(/raresaticon-palindrome-0/i));
- await waitFor(() => expect(screen.getByText(/in a playful twist/i)).toBeVisible());
- await waitFor(() =>
- expect(
- screen.getByText(/block 9 \/ first transaction \/ nakamoto \/ vintage/i),
- ).toBeVisible(),
- );
+ await waitFor(() => expect(screen.getByText(/the great war #3695/i)).toBeVisible());
await waitFor(() => expect(screen.getByText(/see more inscriptions/i)).toBeVisible());
await user.click(screen.getByText(/see more inscriptions/i));
- await waitFor(() => expect(screen.getByText(/timechain #136/i)).toBeVisible());
- await waitFor(() => expect(screen.getByTestId(/raresaticon-jpeg-0/i)).toBeVisible());
- await user.hover(screen.getByTestId(/raresaticon-jpeg-0/i));
- await waitFor(() => expect(screen.getByText(/journey into the past with jpeg/i)).toBeVisible());
+ await user.click(screen.getByText(/see more inscriptions/i));
+ await waitFor(() => expect(screen.getByText(/bitcoin puppet #71/i)).toBeVisible());
+ await waitFor(() => expect(screen.queryAllByTestId(/raresaticon-pizza-0/i)).toHaveLength(2));
+ await user.hover(screen.queryAllByTestId(/raresaticon-pizza-0/i)[0]);
+ await waitFor(() => expect(screen.getByText(/papa john's pizza/i)).toBeVisible());
});
+
it("should open discovery drawer when it is the first time feature is activated", async () => {
const { user } = render();
@@ -43,4 +43,20 @@ describe("displayBitcoinPage", () => {
await user.click(screen.getByText(/learn more/i));
expect(openURL).toHaveBeenCalledWith("https://www.ledger.com/academy/bitcoin-ordinals");
});
+
+ it("should open inscription detail drawer", async () => {
+ const { user } = render(, {
+ initialState: {
+ settings: {
+ hasSeenOrdinalsDiscoveryDrawer: true,
+ },
+ },
+ });
+
+ await waitFor(() => expect(screen.getByText(/the great war #3695/i)).toBeVisible());
+ await user.click(screen.getByText(/the great war #3695/i));
+ await expect(screen.getByText(/hide/i)).toBeVisible();
+ // sat name
+ await expect(screen.getByText(/dlngbapxjdv/i)).toBeVisible();
+ });
});
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/__integration__/nftsCollection.test.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/__integration__/nftsCollection.test.tsx
index 5104ff9891f1..5cf226f23b07 100644
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/__integration__/nftsCollection.test.tsx
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/__integration__/nftsCollection.test.tsx
@@ -22,9 +22,9 @@ describe("displayNftCollection", () => {
});
await waitFor(() => expect(screen.getByText(/momentum/i)).toBeVisible());
- await expect(screen.getByText(/receive nft/i)).toBeVisible();
- await expect(screen.getByText(/see gallery/i)).toBeVisible();
- await expect(screen.getByText(/see more collections/i)).toBeVisible();
+ expect(screen.getByText(/receive nft/i)).toBeVisible();
+ expect(screen.getByText(/see gallery/i)).toBeVisible();
+ expect(screen.getByText(/see more collections/i)).toBeVisible();
});
it("should open the NFTs gallery", async () => {
@@ -36,10 +36,10 @@ describe("displayNftCollection", () => {
});
await waitFor(() => expect(screen.getByText(/momentum/i)).toBeVisible());
- await expect(screen.getByText(/receive nft/i)).toBeVisible();
- await expect(screen.getByText(/see gallery/i)).toBeVisible();
+ expect(screen.getByText(/receive nft/i)).toBeVisible();
+ expect(screen.getByText(/see gallery/i)).toBeVisible();
await user.click(screen.getByText(/see gallery/i));
- await expect(screen.getByText(/all nft/i)).toBeVisible();
+ expect(screen.getByText(/all nft/i)).toBeVisible();
});
it("should open the corresponding NFTs collection and the correct detail drawer", async () => {
@@ -65,10 +65,10 @@ describe("displayNftCollection", () => {
// Open external viewer
await user.click(screen.getByTestId("external-viewer-button"));
- await expect(screen.getByText(/open in opensea.io/i)).toBeVisible();
+ expect(screen.getByText(/open in opensea.io/i)).toBeVisible();
// Close drawer
- await waitFor(() => user.click(screen.getByTestId("drawer-close-button")));
+ await user.click(screen.getByTestId("drawer-close-button"));
await waitFor(() => expect(screen.queryByTestId("side-drawer-container")).toBeNull());
});
@@ -80,7 +80,7 @@ describe("displayNftCollection", () => {
initialRoute: `/`,
});
- await expect(screen.getByText(/receive nft/i)).toBeVisible();
- await expect(screen.getByText(/learn more/i)).toBeVisible();
+ expect(screen.getByText(/receive nft/i)).toBeVisible();
+ expect(screen.getByText(/learn more/i)).toBeVisible();
});
});
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/components/Collection/EmptyCollection.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/components/Collection/EmptyCollection.tsx
index 464912b45c42..9dde6dc79a93 100644
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/components/Collection/EmptyCollection.tsx
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/components/Collection/EmptyCollection.tsx
@@ -30,7 +30,7 @@ const Placeholder = styled.div`
type Props = {
collectionType: CollectibleType;
- currencyName: string;
+ currencyName?: string;
children?: ReactNode;
};
@@ -59,8 +59,22 @@ const EmptyCollection: React.FC = ({ collectionType, currencyName, childr
);
- case CollectibleTypeEnum.Ordinal:
- return null;
+ case CollectibleTypeEnum.Inscriptions:
+ return (
+
+
+ {t("ordinals.inscriptions.empty")}
+
+
+ );
+ case CollectibleTypeEnum.RareSat:
+ return (
+
+
+ {t("ordinals.rareSats.empty")}
+
+
+ );
default:
return t("collectibles.emptyState.default");
}
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/components/Collection/HeaderActions.tsx b/apps/ledger-live-desktop/src/newArch/features/Collectibles/components/Collection/HeaderActions.tsx
index 5a3074491d6d..8f993d1b3d24 100644
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/components/Collection/HeaderActions.tsx
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/components/Collection/HeaderActions.tsx
@@ -1,7 +1,7 @@
-import Button from "~/renderer/components/Button";
-import { t } from "i18next";
import React from "react";
+import Button from "~/renderer/components/Button";
import Box from "~/renderer/components/Box";
+import { useTranslation } from "react-i18next";
type Props = {
children?: JSX.Element;
@@ -9,6 +9,7 @@ type Props = {
};
const HeaderActions: React.FC = ({ children, textKey }) => {
+ const { t } = useTranslation();
return (
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/hooks/useFetchOrdinals.ts b/apps/ledger-live-desktop/src/newArch/features/Collectibles/hooks/useFetchOrdinals.ts
index 01da9b38977c..17587a67bff1 100644
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/hooks/useFetchOrdinals.ts
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/hooks/useFetchOrdinals.ts
@@ -7,12 +7,10 @@ type Props = {
const useFetchOrdinals = ({ account }: Props) => {
const utxosAddresses = account.bitcoinResources?.utxos?.map(utxo => utxo.address).join(",") || "";
- const { rareSats, inscriptions, isLoading, isError, isFetched } = fetchOrdinalsFromSimpleHash({
+ return fetchOrdinalsFromSimpleHash({
addresses: utxosAddresses,
threshold: 0,
});
-
- return { rareSats, inscriptions, isLoading, isError, isFetched };
};
export default useFetchOrdinals;
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/types/Inscriptions.ts b/apps/ledger-live-desktop/src/newArch/features/Collectibles/types/Inscriptions.ts
index ed2fd9f9088a..36057670e8c5 100644
--- a/apps/ledger-live-desktop/src/newArch/features/Collectibles/types/Inscriptions.ts
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/types/Inscriptions.ts
@@ -1,12 +1,9 @@
-import { OrdinalsRowProps } from "./Collection";
import { MediaProps } from "./Media";
-import { MappingKeys } from "./Ordinals";
export type InscriptionsItemProps = {
tokenName: string;
collectionName: string;
- tokenIcons: OrdinalsRowProps["tokenIcons"];
media: MediaProps;
- rareSatName?: MappingKeys[];
+ nftId: string;
onClick: () => void;
};
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/utils/createInscriptionDetailsArrays.ts b/apps/ledger-live-desktop/src/newArch/features/Collectibles/utils/createInscriptionDetailsArrays.ts
new file mode 100644
index 000000000000..e328c1c41122
--- /dev/null
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/utils/createInscriptionDetailsArrays.ts
@@ -0,0 +1,114 @@
+import { DetailsArray } from "LLD/features/Collectibles/types/DetailDrawer";
+import { SimpleHashNft } from "@ledgerhq/live-nft/api/types";
+
+const createDetail = ({
+ key,
+ title,
+ value,
+ isCopyable = false,
+ isHash = false,
+}: {
+ key: string;
+ title: string;
+ value?: string | number;
+ isCopyable?: boolean;
+ isHash?: boolean;
+}) => {
+ if (value !== undefined && value !== null) {
+ return {
+ key,
+ title,
+ value: String(value),
+ isCopyable,
+ isHash,
+ };
+ }
+ return null;
+};
+
+export function createDetails(inscription: SimpleHashNft, createdDate: string): DetailsArray {
+ return [
+ createDetail({
+ key: "Description",
+ title: "ordinals.inscriptions.detailsDrawer.description",
+ value: inscription.collection.description,
+ }),
+ createDetail({
+ key: "Collection Name",
+ title: "ordinals.inscriptions.detailsDrawer.collectionName",
+ value: inscription.collection.name,
+ }),
+ createDetail({
+ key: "Satribute",
+ title: "ordinals.inscriptions.detailsDrawer.satribute",
+ value: inscription.extra_metadata?.ordinal_details?.sat_rarity,
+ }),
+ createDetail({
+ key: "InscriptionID",
+ title: "ordinals.inscriptions.detailsDrawer.inscriptionId",
+ value: inscription.extra_metadata?.ordinal_details?.inscription_id,
+ isCopyable: true,
+ isHash: (inscription.extra_metadata?.ordinal_details?.inscription_id?.length ?? 0) >= 4,
+ }),
+ createDetail({
+ key: "InscriptionNumber",
+ title: "ordinals.inscriptions.detailsDrawer.inscriptionNumber",
+ value: inscription.extra_metadata?.ordinal_details?.inscription_number,
+ isCopyable: true,
+ }),
+ createDetail({
+ key: "MintedTo",
+ title: "ordinals.inscriptions.detailsDrawer.mintedTo",
+ value: inscription.first_created?.minted_to,
+ isCopyable: true,
+ isHash: (inscription.first_created?.minted_to?.length ?? 0) >= 4,
+ }),
+ createDetail({
+ key: "GenesisTx",
+ title: "ordinals.inscriptions.detailsDrawer.genesisTx",
+ value: inscription.first_created?.transaction,
+ isCopyable: true,
+ isHash: (inscription.first_created?.transaction?.length ?? 0) >= 4,
+ }),
+ createDetail({
+ key: "BlockNumber",
+ title: "ordinals.inscriptions.detailsDrawer.blockNumber",
+ value: inscription.first_created?.block_number,
+ isCopyable: true,
+ }),
+ createDetail({
+ key: "ContentType",
+ title: "ordinals.inscriptions.detailsDrawer.contentType",
+ value: inscription.extra_metadata?.ordinal_details?.content_type,
+ }),
+ createDetail({
+ key: "CreatedDate",
+ title: "ordinals.inscriptions.detailsDrawer.createdDate",
+ value: createdDate,
+ }),
+ createDetail({
+ key: "SatNumber",
+ title: "ordinals.inscriptions.detailsDrawer.satNumber",
+ value: inscription.extra_metadata?.ordinal_details?.sat_number,
+ isCopyable: true,
+ }),
+ createDetail({
+ key: "SatName",
+ title: "ordinals.inscriptions.detailsDrawer.satName",
+ value: inscription.extra_metadata?.ordinal_details?.sat_name,
+ isCopyable: true,
+ }),
+ createDetail({
+ key: "Location",
+ title: "ordinals.inscriptions.detailsDrawer.location",
+ value: inscription.extra_metadata?.ordinal_details?.location,
+ isCopyable: true,
+ isHash: (inscription.extra_metadata?.ordinal_details?.location?.length ?? 0) >= 4,
+ }),
+ createDetail({
+ key: "OutputValue",
+ title: "ordinals.inscriptions.detailsDrawer.outputValue",
+ value: inscription.extra_metadata?.ordinal_details?.output_value,
+ }),
+ ].filter(Boolean) as DetailsArray;
+}
diff --git a/apps/ledger-live-desktop/src/newArch/features/Collectibles/utils/findCorrespondingSat.ts b/apps/ledger-live-desktop/src/newArch/features/Collectibles/utils/findCorrespondingSat.ts
new file mode 100644
index 000000000000..24766e20e405
--- /dev/null
+++ b/apps/ledger-live-desktop/src/newArch/features/Collectibles/utils/findCorrespondingSat.ts
@@ -0,0 +1,9 @@
+import { GroupedNftOrdinals } from "@ledgerhq/live-nft-react/index";
+
+export const findCorrespondingSat = (
+ inscriptionsGroupedWithRareSats: GroupedNftOrdinals[],
+ nftId: string,
+) =>
+ inscriptionsGroupedWithRareSats.find(
+ ({ inscription: groupedInscription }) => groupedInscription.nft_id === nftId,
+ );
diff --git a/apps/ledger-live-desktop/src/renderer/actions/settings.ts b/apps/ledger-live-desktop/src/renderer/actions/settings.ts
index 0a93392dd663..ad3a94577590 100644
--- a/apps/ledger-live-desktop/src/renderer/actions/settings.ts
+++ b/apps/ledger-live-desktop/src/renderer/actions/settings.ts
@@ -393,3 +393,8 @@ export const setHasSeenOrdinalsDiscoveryDrawer = (payload: boolean) => ({
type: "SET_HAS_SEEN_ORDINALS_DISCOVERY_DRAWER",
payload,
});
+
+export const setHasProtectedOrdinalsAssets = (payload: boolean) => ({
+ type: "SET_HAS_PROTECTED_ORDINALS_ASSETS",
+ payload,
+});
diff --git a/apps/ledger-live-desktop/src/renderer/components/AccountsList/AccountRow.tsx b/apps/ledger-live-desktop/src/renderer/components/AccountsList/AccountRow.tsx
index 706565331c23..6bb5bec7d559 100644
--- a/apps/ledger-live-desktop/src/renderer/components/AccountsList/AccountRow.tsx
+++ b/apps/ledger-live-desktop/src/renderer/components/AccountsList/AccountRow.tsx
@@ -142,7 +142,13 @@ function AccountRow(props: Props) {
color="palette.text.shade60"
/>
) : null}
- {!isDisabled && !isReadonly && }
+ {!isDisabled && !isReadonly && (
+
+ )}
);
}
diff --git a/apps/ledger-live-desktop/src/renderer/components/debug/DebugMock.tsx b/apps/ledger-live-desktop/src/renderer/components/debug/DebugMock.tsx
index 2a67cc95d45a..d969436677e4 100644
--- a/apps/ledger-live-desktop/src/renderer/components/debug/DebugMock.tsx
+++ b/apps/ledger-live-desktop/src/renderer/components/debug/DebugMock.tsx
@@ -26,6 +26,8 @@ import { ListAppsResult } from "@ledgerhq/live-common/apps/types";
import { AnnouncementDeviceModelId } from "@ledgerhq/live-common/notifications/AnnouncementProvider/types";
import { getAllFeatureFlags } from "@ledgerhq/live-common/e2e/index";
import { getAllEnvs } from "@ledgerhq/live-env";
+import { ipcRenderer } from "electron";
+import { memoryLogger } from "~/renderer/logger";
const mockListAppsResult = (
appDesc: string,
@@ -281,6 +283,25 @@ interface RawEvents {
}
window.getAllFeatureFlags = getAllFeatureFlags;
window.getAllEnvs = getAllEnvs;
+window.saveLogs = async (path: string): Promise => {
+ const memoryLogs = memoryLogger.getMemoryLogs();
+
+ try {
+ // Serializes ourself with `stringify` to avoid "object could not be cloned" errors from the electron IPC serializer.
+ const memoryLogsStr = JSON.stringify(memoryLogs, null, 2);
+ // Requests the main process to save logs in a file
+ await ipcRenderer.invoke(
+ "save-logs",
+ {
+ canceled: false,
+ filePath: path,
+ },
+ memoryLogsStr,
+ );
+ } catch (error) {
+ console.error("Error while requesting to save logs from the renderer process", error);
+ }
+};
if (getEnv("MOCK")) {
window.mock = {
diff --git a/apps/ledger-live-desktop/src/renderer/families/near/Staking/Header.tsx b/apps/ledger-live-desktop/src/renderer/families/near/Staking/Header.tsx
index f06ff6a9bc2d..b9bbcfc57321 100644
--- a/apps/ledger-live-desktop/src/renderer/families/near/Staking/Header.tsx
+++ b/apps/ledger-live-desktop/src/renderer/families/near/Staking/Header.tsx
@@ -32,9 +32,6 @@ export const Header = () => (
-
-
-
diff --git a/apps/ledger-live-desktop/src/renderer/families/near/Staking/Row.tsx b/apps/ledger-live-desktop/src/renderer/families/near/Staking/Row.tsx
index 1db1848e31e8..6d097874497a 100644
--- a/apps/ledger-live-desktop/src/renderer/families/near/Staking/Row.tsx
+++ b/apps/ledger-live-desktop/src/renderer/families/near/Staking/Row.tsx
@@ -88,7 +88,6 @@ export function Row({
validatorId,
staked,
formattedAmount,
- formattedRewards,
formattedPending,
formattedAvailable,
validator,
@@ -151,11 +150,6 @@ export function Row({
{formattedAmount}
-
-
- {formattedRewards}
-
-
{formattedPending}
diff --git a/apps/ledger-live-desktop/src/renderer/hooks/useDeeplinking.ts b/apps/ledger-live-desktop/src/renderer/hooks/useDeeplinking.ts
index 0834195f8231..ba0631a9d6c5 100644
--- a/apps/ledger-live-desktop/src/renderer/hooks/useDeeplinking.ts
+++ b/apps/ledger-live-desktop/src/renderer/hooks/useDeeplinking.ts
@@ -19,6 +19,7 @@ import { useStorylyContext } from "~/storyly/StorylyProvider";
import { useNavigateToPostOnboardingHubCallback } from "~/renderer/components/PostOnboardingHub/logic/useNavigateToPostOnboardingHubCallback";
import { usePostOnboardingDeeplinkHandler } from "@ledgerhq/live-common/postOnboarding/hooks/index";
import { setDrawerVisibility as setLedgerSyncDrawerVisibility } from "../actions/walletSync";
+import { WC_ID } from "@ledgerhq/live-common/wallet-api/constants";
const getAccountsOrSubAccountsByCurrency = (
currency: CryptoOrTokenCurrency,
@@ -315,8 +316,9 @@ export function useDeepLinkHandler() {
}
break;
case "wc": {
+ const wcPathname = `/platform/${WC_ID}`;
// Only prevent requests if already on the wallet connect live-app
- if (location.pathname === "/platform/ledger-wallet-connect") {
+ if (location.pathname === wcPathname) {
try {
// Prevent a request from updating the live-app url and reloading it
if (!query.uri || new URL(query.uri).searchParams.get("requestId")) {
@@ -327,7 +329,7 @@ export function useDeepLinkHandler() {
}
}
setTrackingSource("deeplink");
- navigate("/platform/ledger-wallet-connect", query);
+ navigate(wcPathname, query);
break;
}
diff --git a/apps/ledger-live-desktop/src/renderer/modals/Receive/Body.tsx b/apps/ledger-live-desktop/src/renderer/modals/Receive/Body.tsx
index a78d8185ec59..821fc94f75a8 100644
--- a/apps/ledger-live-desktop/src/renderer/modals/Receive/Body.tsx
+++ b/apps/ledger-live-desktop/src/renderer/modals/Receive/Body.tsx
@@ -28,6 +28,7 @@ export type Data = {
startWithWarning?: boolean;
receiveTokenMode?: boolean;
receiveNFTMode?: boolean;
+ receiveOrdinalMode?: boolean;
eventType?: string;
isFromPostOnboardingEntryPoint?: boolean;
};
@@ -58,6 +59,7 @@ export type StepProps = {
token: TokenCurrency | undefined | null;
receiveTokenMode: boolean;
receiveNFTMode: boolean;
+ receiveOrdinalMode: boolean;
closeModal: () => void;
isAddressVerified: boolean | undefined | null;
verifyAddressError: Error | undefined | null;
@@ -208,6 +210,7 @@ const Body = ({
disabledSteps,
receiveTokenMode: !!params.receiveTokenMode,
receiveNFTMode: !!params.receiveNFTMode,
+ receiveOrdinalMode: !!params.receiveOrdinalMode,
hideBreadcrumb,
token,
isAddressVerified,
diff --git a/apps/ledger-live-desktop/src/renderer/modals/Receive/steps/StepReceiveFunds.tsx b/apps/ledger-live-desktop/src/renderer/modals/Receive/steps/StepReceiveFunds.tsx
index 20d0c2c9d7d7..fc9fed00f4d4 100644
--- a/apps/ledger-live-desktop/src/renderer/modals/Receive/steps/StepReceiveFunds.tsx
+++ b/apps/ledger-live-desktop/src/renderer/modals/Receive/steps/StepReceiveFunds.tsx
@@ -40,6 +40,7 @@ import { useCompleteActionCallback } from "~/renderer/components/PostOnboardingH
import { getDefaultAccountName } from "@ledgerhq/live-wallet/accountName";
import { useMaybeAccountName } from "~/renderer/reducers/wallet";
import { UTXOAddressAlert } from "~/renderer/components/UTXOAddressAlert";
+import { isUTXOCompliant } from "@ledgerhq/live-common/currencies/helpers";
const Separator = styled.div`
border-top: 1px solid #99999933;
@@ -56,8 +57,6 @@ const QRCodeWrapper = styled.div`
background: white;
`;
-const UTXOFamily = ["bitcoin", "bitcoin_cash", "dash", "dogecoin", "litecoin", "zcash", "cardano"];
-
const Receive1ShareAddress = ({
account,
name,
@@ -69,7 +68,7 @@ const Receive1ShareAddress = ({
address: string;
showQRCodeModal: () => void;
}) => {
- const isUTXOCompliant = UTXOFamily.includes(account.currency.id);
+ const isUTXOCompliantCurrency = isUTXOCompliant(account.currency.family);
return (
<>
@@ -99,7 +98,7 @@ const Receive1ShareAddress = ({
- {isUTXOCompliant && (
+ {isUTXOCompliantCurrency && (
diff --git a/apps/ledger-live-desktop/src/renderer/reducers/settings.ts b/apps/ledger-live-desktop/src/renderer/reducers/settings.ts
index 359bdcab40c1..24f033dc2cba 100644
--- a/apps/ledger-live-desktop/src/renderer/reducers/settings.ts
+++ b/apps/ledger-live-desktop/src/renderer/reducers/settings.ts
@@ -114,6 +114,7 @@ export type SettingsState = {
anonymousBrazeId: string | null;
starredMarketCoins: string[];
hasSeenOrdinalsDiscoveryDrawer: boolean;
+ hasProtectedOrdinalsAssets: boolean;
};
export const getInitialLanguageAndLocale = (): { language: Language; locale: Locale } => {
@@ -171,6 +172,7 @@ export const INITIAL_STATE: SettingsState = {
hasInstalledApps: true,
lastSeenDevice: null,
hasSeenOrdinalsDiscoveryDrawer: false,
+ hasProtectedOrdinalsAssets: false,
devicesModelList: [],
lastSeenCustomImage: {
size: 0,
@@ -264,6 +266,7 @@ type HandlersPayloads = {
MARKET_ADD_STARRED_COINS: string;
MARKET_REMOVE_STARRED_COINS: string;
SET_HAS_SEEN_ORDINALS_DISCOVERY_DRAWER: boolean;
+ SET_HAS_PROTECTED_ORDINALS_ASSETS: boolean;
};
type SettingsHandlers = Handlers;
@@ -464,6 +467,10 @@ const handlers: SettingsHandlers = {
...state,
hasSeenOrdinalsDiscoveryDrawer: payload,
}),
+ SET_HAS_PROTECTED_ORDINALS_ASSETS: (state: SettingsState, { payload }) => ({
+ ...state,
+ hasProtectedOrdinalsAssets: payload,
+ }),
};
export default handleActions(
@@ -806,3 +813,5 @@ export const currenciesSettingsSelector = (state: State) => state.settings.curre
export const starredMarketCoinsSelector = (state: State) => state.settings.starredMarketCoins;
export const hasSeenOrdinalsDiscoveryDrawerSelector = (state: State) =>
state.settings.hasSeenOrdinalsDiscoveryDrawer;
+export const hasProtectedOrdinalsAssetsSelector = (state: State) =>
+ state.settings.hasProtectedOrdinalsAssets;
diff --git a/apps/ledger-live-desktop/src/renderer/screens/account/AccountHeaderActions.tsx b/apps/ledger-live-desktop/src/renderer/screens/account/AccountHeaderActions.tsx
index d2e25a7714e0..4b90d1f8761b 100644
--- a/apps/ledger-live-desktop/src/renderer/screens/account/AccountHeaderActions.tsx
+++ b/apps/ledger-live-desktop/src/renderer/screens/account/AccountHeaderActions.tsx
@@ -39,6 +39,7 @@ import { ManageAction } from "~/renderer/families/types";
import { getAvailableProviders } from "@ledgerhq/live-common/exchange/swap/index";
import { useFetchCurrencyAll } from "@ledgerhq/live-common/exchange/swap/hooks/index";
import { isWalletConnectSupported } from "@ledgerhq/live-common/walletConnect/index";
+import { WC_ID } from "@ledgerhq/live-common/wallet-api/constants";
type RenderActionParams = {
label: React.ReactNode;
@@ -137,7 +138,7 @@ const AccountHeaderSettingsButtonComponent = ({ account, parentAccount, openModa
initialAccountId: mainAccount.id,
};
history.push({
- pathname: "/platform/ledger-wallet-connect",
+ pathname: `/platform/${WC_ID}`,
state: params,
});
}, [mainAccount.id, history]);
diff --git a/apps/ledger-live-desktop/src/storyly/StorylyProvider.tsx b/apps/ledger-live-desktop/src/storyly/StorylyProvider.tsx
index 067a8b513683..aa3172ea51fa 100644
--- a/apps/ledger-live-desktop/src/storyly/StorylyProvider.tsx
+++ b/apps/ledger-live-desktop/src/storyly/StorylyProvider.tsx
@@ -3,11 +3,12 @@ import useFeature from "@ledgerhq/live-common/featureFlags/useFeature";
import { closeAllModal } from "~/renderer/actions/modals";
import { context } from "~/renderer/drawers/Provider";
import { closeInformationCenter } from "~/renderer/actions/UI";
-import { useDispatch } from "react-redux";
+import { useDispatch, useSelector } from "react-redux";
import { openURL } from "~/renderer/linking";
import { Feature_Storyly, StorylyInstanceType } from "@ledgerhq/types-live";
-import { StorylyRef } from "storyly-web";
+import { StorylyRef, Story } from "storyly-web";
+import { languageSelector } from "~/renderer/reducers/settings";
interface StorylyProviderProps {
children: ReactNode;
}
@@ -37,6 +38,9 @@ const StorylyProvider: React.FC = ({ children }) => {
const storylyRef = useRef(null);
const { params } = useFeature("storyly") || {};
+
+ const language = useSelector(languageSelector);
+
const stories = params?.stories;
useEffect(() => {
@@ -44,22 +48,28 @@ const StorylyProvider: React.FC = ({ children }) => {
storylyRef.current?.init({
layout: "classic",
token: token,
- events: {
- closeStoryGroup: clear,
- actionClicked: story => {
- if (story?.media?.actionUrl) {
- openURL(story.media.actionUrl);
- storylyRef.current?.close?.();
- dispatch(closeAllModal());
- setDrawer();
- dispatch(closeInformationCenter());
- }
- },
- },
});
+
+ storylyRef.current?.on("actionClicked", (story: Story) => {
+ if (!story.actionUrl) return;
+ openURL(story.actionUrl as string);
+ storylyRef.current?.close?.();
+ dispatch(closeAllModal());
+ setDrawer();
+ dispatch(closeInformationCenter());
+ });
+
+ storylyRef.current?.on("closeStoryGroup", clear);
+
storylyRef.current?.openStory({ group: query?.g, story: query?.s, playMode: query?.play });
}, [params, token, query, dispatch, setDrawer]);
+ useEffect(() => {
+ if (language) {
+ storylyRef.current?.setLang({ language });
+ }
+ }, [language]);
+
useEffect(() => {
if (url) {
const storylyUrl = new URL(`ledgerlive://${url}`);
diff --git a/apps/ledger-live-desktop/src/storyly/useStoryly.tsx b/apps/ledger-live-desktop/src/storyly/useStoryly.tsx
index 2e84f519e696..1f182ff39b37 100644
--- a/apps/ledger-live-desktop/src/storyly/useStoryly.tsx
+++ b/apps/ledger-live-desktop/src/storyly/useStoryly.tsx
@@ -8,7 +8,7 @@ import { useDispatch } from "react-redux";
import { languageSelector } from "~/renderer/reducers/settings";
import { StorylyStyleProps, useStorylyDefaultStyleProps } from "./style";
import { openURL } from "~/renderer/linking";
-import { StorylyRef, StorylyData } from "storyly-web";
+import { StorylyRef, StorylyData, Story, StoryGroup } from "storyly-web";
/**
* Hook to use Storyly
@@ -26,7 +26,7 @@ export const useStoryly = (
const dispatch = useDispatch();
const { setDrawer } = useContext(context);
const ref = useRef();
- const dataRef = useRef();
+ const dataRef = useRef();
const props = useStorylyDefaultStyleProps();
const language = useSelector(languageSelector);
@@ -37,31 +37,28 @@ export const useStoryly = (
ref.current?.init({
layout: "classic",
token: storyly.params?.stories[instanceId].token || "",
- lang: language,
segments: [`lang_${language}`],
- events: {
- isReady: data => {
- dataRef.current = data;
- // Triggered when story is ready.
- },
- actionClicked: story => {
- if (story?.media?.actionUrl) {
- openURL(story.media.actionUrl);
- ref.current?.close?.();
- dispatch(closeAllModal());
- setDrawer();
- }
- },
- },
props: { ...props, ...options?.styleProps },
});
+
+ ref.current?.on("loaded", (data: StorylyData) => {
+ dataRef.current = data.groupList;
+ });
+
+ ref.current?.on("actionClicked", (story: Story) => {
+ if (!story.actionUrl) return;
+ openURL(story.actionUrl);
+ ref.current?.close?.();
+ dispatch(closeAllModal());
+ setDrawer();
+ });
});
/**
* Change `lang` and `segments` based on the app language
*/
useLayoutEffect(() => {
- ref.current?.setLang({ language: language });
+ ref.current?.setLang({ language });
ref.current?.setSegments([`lang_${language}`]);
}, [language]);
diff --git a/apps/ledger-live-desktop/src/types/storyly-web.d.ts b/apps/ledger-live-desktop/src/types/storyly-web.d.ts
index 26e98b1278b6..1acf9ef8e966 100644
--- a/apps/ledger-live-desktop/src/types/storyly-web.d.ts
+++ b/apps/ledger-live-desktop/src/types/storyly-web.d.ts
@@ -1,38 +1,53 @@
declare module "storyly-web" {
- type StorylyData = Array<{
- title: string;
- image: URL;
- pinned: boolean;
- id: number;
- }>;
-
type Story = {
group_id: number;
id: number;
index: number;
- media: {
- actionUrl?: string;
- };
+ actionUrl: string | undefined;
seen: boolean;
title: string;
};
+ type StoryGroup = {
+ iconUrl: string;
+ id: number;
+ nudge?: string;
+ pinned?: boolean;
+ stories: Story[];
+ title: string;
+ type: unknown;
+ };
+
+ type StorylyData = {
+ dataSource: string;
+ groupList: StoryGroup[];
+ };
+
/**
* Storyly Options
*/
interface StorylyOptions {
layout: "classic" | "modern";
token: string;
- events?: {
- closeStoryGroup?(): void;
- isReady?(data: StorylyData): void;
- actionClicked?(story: Story): void;
- };
- lang?: Language;
+ locale?: LanguagePrefixed;
segments?: string[];
props?: StorylyStyleProps;
}
+ /**
+ * Storyly user events
+ */
+ type StorylyUserEvents =
+ | "actionClicked"
+ | "loaded"
+ | "loadFailed"
+ | "openStoryGroup"
+ | "storyOpenFailed"
+ | "storyImpression"
+ | "storyCompleted"
+ | "groupCompleted"
+ | "closeStoryGroup";
+
/**
* Storyly Ref
*/
@@ -42,6 +57,7 @@ declare module "storyly-web" {
setLang: (options: { language: StorylyOptions["lang"] }) => void;
openStory: (props: openStoryParams) => void;
close: () => void;
+ on: (event: StorylyUserEvents, callback: (...args) => void) => void;
}
interface openStoryParams {
@@ -64,5 +80,13 @@ declare module "storyly-web" {
>;
export default StorylyWeb;
- export { StorylyRef, StorylyOptions, StorylyData, openStoryParams };
+ export {
+ StorylyRef,
+ StorylyOptions,
+ StorylyData,
+ openStoryParams,
+ StorylyUserEvents,
+ Story,
+ StoryGroup,
+ };
}
diff --git a/apps/ledger-live-desktop/static/i18n/en/app.json b/apps/ledger-live-desktop/static/i18n/en/app.json
index 23772be65a3c..52bdf5fe71c2 100644
--- a/apps/ledger-live-desktop/static/i18n/en/app.json
+++ b/apps/ledger-live-desktop/static/i18n/en/app.json
@@ -6595,19 +6595,41 @@
"ordinals": {
"inscriptions": {
"seeMore": "See more inscriptions",
+ "empty": "To add Inscriptions, simply send them to your Bitcoin address.",
+ "receive": "Receive Inscription",
"discoveryDrawer": {
"title": "Discover Ordinals",
"description": "Do you know that you may own valuable rare sats and inscriptions?",
"learnMore": "Learn more",
- "viewCoinControl": "View coin control"
+ "protectTitle": "Protect your ordinals assets",
+ "protectDescription": "Exclude your Ordinals assets from your transactions."
+ },
+ "detailsDrawer": {
+ "description": "Description",
+ "collectionName": "Collection Name",
+ "inscriptionId": "Inscription ID",
+ "inscriptionNumber": "Inscription number",
+ "mintedTo": "Minted to",
+ "genesisTx": "Genesis transaction",
+ "blockNumber": "Block Number",
+ "contentType": "Content type",
+ "createdDate": "Created date",
+ "satribute": "Sat Rarity",
+ "satNumber": "Sat number",
+ "satName": "Sat name",
+ "location": "Location",
+ "outputValue": "Output value",
+ "storedOnChain": "Stored On-Chain",
+ "hide": "Hide"
}
},
"rareSats": {
+ "empty": "To add Rare Sats, simply send them to your Bitcoin address.",
+ "receive": "Receive Rare Sat",
"title": "Rare Sats",
"table": {
"type": "Sat Type / Amount",
- "year": "Year",
- "utxo": "UTXO size"
+ "year": "Year"
},
"rareSat": {
"description": {
diff --git a/apps/ledger-live-desktop/tests/component/layout.component.ts b/apps/ledger-live-desktop/tests/component/layout.component.ts
index 39a9df55bed4..8c0fc4695173 100644
--- a/apps/ledger-live-desktop/tests/component/layout.component.ts
+++ b/apps/ledger-live-desktop/tests/component/layout.component.ts
@@ -36,7 +36,6 @@ export class Layout extends Component {
// general
readonly inputError = this.page.locator("id=input-error"); // no data-testid because css style is applied
readonly insufficientFundsWarning = this.page.getByTestId("insufficient-funds-warning");
- private loadingSpinner = this.page.getByTestId("loading-spinner");
readonly logo = this.page.getByTestId("logo");
// updater
@@ -104,10 +103,12 @@ export class Layout extends Component {
}
@step("Check warning message")
- async checkWarningMessage(expectedWarningMessage: RegExp) {
- await expect(this.insufficientFundsWarning).toBeVisible();
- const warningText = await this.insufficientFundsWarning.innerText();
- expect(warningText).toMatch(expectedWarningMessage);
+ async checkWarningMessage(expectedWarningMessage: RegExp | string | null) {
+ if (expectedWarningMessage !== null) {
+ await expect(this.insufficientFundsWarning).toBeVisible();
+ const warningText = await this.insufficientFundsWarning.innerText();
+ expect(warningText).toMatch(expectedWarningMessage);
+ }
}
@step("Check if the error message is the same as expected")
diff --git a/apps/ledger-live-desktop/tests/enum/Account.ts b/apps/ledger-live-desktop/tests/enum/Account.ts
index 72487e364cae..e42e2ce69cc2 100644
--- a/apps/ledger-live-desktop/tests/enum/Account.ts
+++ b/apps/ledger-live-desktop/tests/enum/Account.ts
@@ -7,13 +7,13 @@ export class Account {
public readonly address: string,
) {}
- static readonly BTC_1 = new Account(
+ static readonly BTC_NATIVE_SEGWIT_1 = new Account(
Currency.BTC,
"Bitcoin 1",
"bc1qx7f9plgr8msjatkv0dw2ne8gguwfjqr6xyjp50",
);
- static readonly BTC_2 = new Account(
+ static readonly BTC_NATIVE_SEGWIT_2 = new Account(
Currency.BTC,
"Bitcoin 2",
"bc1q7ezsfc44adw2gyzqjmwhuh2e83uk8u5hrw590r",
diff --git a/apps/ledger-live-desktop/tests/fixtures/common.ts b/apps/ledger-live-desktop/tests/fixtures/common.ts
index 82d6e8e497e3..d6ef37942414 100644
--- a/apps/ledger-live-desktop/tests/fixtures/common.ts
+++ b/apps/ledger-live-desktop/tests/fixtures/common.ts
@@ -1,5 +1,6 @@
import { test as base, Page, ElectronApplication, ChromiumBrowserContext } from "@playwright/test";
import fsPromises from "fs/promises";
+import merge from "lodash/merge";
import * as path from "path";
import { OptionalFeatureMap } from "@ledgerhq/types-live";
import { getEnv, setEnv } from "@ledgerhq/live-env";
@@ -16,9 +17,10 @@ type TestFixtures = {
lang: string;
theme: "light" | "dark" | "no-preference" | undefined;
speculosApp: AppInfos;
- userdata: string;
+ userdata?: string;
+ settings: Record;
userdataDestinationPath: string;
- userdataOriginalFile: string;
+ userdataOriginalFile?: string;
userdataFile: string;
env: Record;
electronApp: ElectronApplication;
@@ -40,6 +42,7 @@ export const test = base.extend({
lang: "en-US",
theme: "dark",
userdata: undefined,
+ settings: { shareAnalytics: true, hasSeenAnalyticsOptInPrompt: true },
featureFlags: undefined,
simulateCamera: undefined,
speculosApp: undefined,
@@ -53,7 +56,7 @@ export const test = base.extend({
await use(path.join(__dirname, "../artifacts/userdata", randomUUID()));
},
userdataOriginalFile: async ({ userdata }, use) => {
- await use(path.join(__dirname, "../userdata/", `${userdata}.json`));
+ await use(userdata && path.join(__dirname, "../userdata/", `${userdata}.json`));
},
userdataFile: async ({ userdataDestinationPath }, use) => {
const fullFilePath = path.join(userdataDestinationPath, "app.json");
@@ -63,9 +66,9 @@ export const test = base.extend({
{
lang,
theme,
- userdata,
userdataDestinationPath,
userdataOriginalFile,
+ settings,
env,
featureFlags,
simulateCamera,
@@ -77,9 +80,11 @@ export const test = base.extend({
// create userdata path
await fsPromises.mkdir(userdataDestinationPath, { recursive: true });
- if (userdata) {
- await fsPromises.copyFile(userdataOriginalFile, `${userdataDestinationPath}/app.json`);
- }
+ const fileUserData = userdataOriginalFile
+ ? await fsPromises.readFile(userdataOriginalFile, { encoding: "utf-8" }).then(JSON.parse)
+ : {};
+ const userData = merge({ data: { settings } }, fileUserData);
+ await fsPromises.writeFile(`${userdataDestinationPath}/app.json`, JSON.stringify(userData));
let device: any | undefined;
diff --git a/apps/ledger-live-desktop/tests/handlers/fixtures/ordinals/mockedOrdinals.json b/apps/ledger-live-desktop/tests/handlers/fixtures/ordinals/mockedOrdinals.json
index 09b50da03fe3..4b2172a88599 100644
--- a/apps/ledger-live-desktop/tests/handlers/fixtures/ordinals/mockedOrdinals.json
+++ b/apps/ledger-live-desktop/tests/handlers/fixtures/ordinals/mockedOrdinals.json
@@ -4,10 +4,10 @@
"previous": null,
"nfts": [
{
- "nft_id": "utxo.7893390614d9a5608e39ea637e5794f7564d3fcdd5ad46105f349c2cf45272fb.1",
+ "nft_id": "utxo.77777302c93304f4c506566f5e897c9dfdefb7436aa587dc6927b94222429580.782",
"chain": "utxo",
- "contract_address": "7893390614d9a5608e39ea637e5794f7564d3fcdd5ad46105f349c2cf45272fb",
- "token_id": "1",
+ "contract_address": "77777302c93304f4c506566f5e897c9dfdefb7436aa587dc6927b94222429580",
+ "token_id": "782",
"name": null,
"description": null,
"previews": {
@@ -30,17 +30,17 @@
"other_properties": null,
"background_color": null,
"external_url": null,
- "created_date": "2024-03-14T11:34:34",
+ "created_date": "2024-09-01T21:29:08",
"status": "minted",
"token_count": 1,
"owner_count": 1,
"owners": [
{
- "owerner_address": "weneverknow",
+ "owner_address": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "first_acquired_date": "2024-03-14T11:34:34",
- "last_acquired_date": "2024-03-14T11:34:34"
+ "first_acquired_date": "2024-09-01T21:29:08",
+ "last_acquired_date": "2024-09-01T21:29:08"
}
],
"contract": {
@@ -71,6 +71,7 @@
"metaplex_mint": null,
"metaplex_candy_machine": null,
"metaplex_first_verified_creator": null,
+ "mpl_core_collection_address": null,
"spam_score": null,
"floor_prices": [],
"top_bids": [],
@@ -84,12 +85,12 @@
"last_sale": null,
"primary_sale": null,
"first_created": {
- "minted_to": "bc1pgtat0n2kavrz4ufhngm2muzxzx6pcmvr4czp089v48u5sgvpd9vqjsuaql",
+ "minted_to": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "timestamp": "2024-03-14T11:34:34",
- "block_number": 834655,
- "transaction": "",
+ "timestamp": "2024-09-01T21:29:08",
+ "block_number": 859428,
+ "transaction": "77777302c93304f4c506566f5e897c9dfdefb7436aa587dc6927b94222429580",
"transaction_initiator": null
},
"rarity": {
@@ -104,7 +105,7 @@
"distinct_rare_sats": 0,
"satributes": {
"common": {
- "count": 8000,
+ "count": 330,
"display_name": "Common",
"description": "Any sat that is not the first sat of its block",
"icon": "https://cdn.simplehash.com/rare_sats/satribute_common.png"
@@ -112,28 +113,21 @@
},
"sat_ranges": [
{
- "starting_sat": 574534229491726,
- "value": 8000,
+ "starting_sat": 1467197131900234,
+ "value": 330,
"distinct_rare_sats": 0,
- "year": "2011",
+ "year": "2015",
"subranges": [
{
- "starting_sat": 574534229491726,
- "value": 8000,
+ "starting_sat": 1467197131900234,
+ "value": 330,
"sat_types": ["common"]
}
]
}
],
- "block_number": 834655,
- "value": 8000,
- "script_pub_key": {
- "asm": "",
- "desc": "",
- "hex": "",
- "address": "",
- "type": ""
- }
+ "block_number": 859428,
+ "value": 330
},
"image_original_url": null,
"animation_original_url": null,
@@ -141,22 +135,28 @@
}
},
{
- "nft_id": "utxo.7893390614d9a5608e39ea637e5794f7564d3fcdd5ad46105f349c2cf45272fb.0",
- "chain": "utxo",
- "contract_address": "7893390614d9a5608e39ea637e5794f7564d3fcdd5ad46105f349c2cf45272fb",
- "token_id": "0",
- "name": null,
+ "nft_id": "bitcoin.77777302c93304f4c506566f5e897c9dfdefb7436aa587dc6927b94222429580i782",
+ "chain": "bitcoin",
+ "contract_address": "77777302c93304f4c506566f5e897c9dfdefb7436aa587dc6927b94222429580i782",
+ "token_id": null,
+ "name": "The Great War #3695",
"description": null,
"previews": {
- "image_small_url": null,
- "image_medium_url": null,
- "image_large_url": null,
- "image_opengraph_url": null,
- "blurhash": null,
- "predominant_color": null
+ "image_small_url": "https://lh3.googleusercontent.com/in-9dWuTK6PXzTj9Oi0UsZhQKenWkWsLt8tOelWP6jitx_7Yk5Ut8_t9pM5pC7rDnSaaLJHu-4frN9vKMfSV0kd5XpGICCwhdG0=s250",
+ "image_medium_url": "https://lh3.googleusercontent.com/in-9dWuTK6PXzTj9Oi0UsZhQKenWkWsLt8tOelWP6jitx_7Yk5Ut8_t9pM5pC7rDnSaaLJHu-4frN9vKMfSV0kd5XpGICCwhdG0",
+ "image_large_url": "https://lh3.googleusercontent.com/in-9dWuTK6PXzTj9Oi0UsZhQKenWkWsLt8tOelWP6jitx_7Yk5Ut8_t9pM5pC7rDnSaaLJHu-4frN9vKMfSV0kd5XpGICCwhdG0=s1000",
+ "image_opengraph_url": "https://lh3.googleusercontent.com/in-9dWuTK6PXzTj9Oi0UsZhQKenWkWsLt8tOelWP6jitx_7Yk5Ut8_t9pM5pC7rDnSaaLJHu-4frN9vKMfSV0kd5XpGICCwhdG0=k-w1200-s2400-rj",
+ "blurhash": "UAEUlW^jOrS2?GNb-U%1ENIpniSO}?oeS4NH",
+ "predominant_color": "#795943"
+ },
+ "image_url": "https://cdn.simplehash.com/assets/ef326456195c1581207fba92a1d22443ee253f3c8cc5b73d52429c31dfa61f6e.png",
+ "image_properties": {
+ "width": 700,
+ "height": 401,
+ "size": 109996,
+ "mime_type": "image/png",
+ "exif_orientation": null
},
- "image_url": null,
- "image_properties": null,
"video_url": null,
"video_properties": null,
"audio_url": null,
@@ -167,22 +167,22 @@
"other_properties": null,
"background_color": null,
"external_url": null,
- "created_date": "2024-03-14T11:34:34",
+ "created_date": "2024-09-01T21:29:08",
"status": "minted",
"token_count": 1,
"owner_count": 1,
"owners": [
{
- "owerner_address": "weneverknow",
+ "owner_address": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "first_acquired_date": "2024-03-14T11:34:34",
- "last_acquired_date": "2024-03-14T11:34:34"
+ "first_acquired_date": "2024-09-01T21:29:08",
+ "last_acquired_date": "2024-09-01T21:29:08"
}
],
"contract": {
- "type": "UTXO",
- "name": null,
+ "type": "ORDINALS",
+ "name": "Inscription #75152040",
"symbol": null,
"deployed_by": null,
"deployed_via_contract": null,
@@ -190,43 +190,84 @@
"has_multiple_collections": false
},
"collection": {
- "collection_id": null,
- "name": null,
- "description": null,
- "image_url": null,
- "image_properties": null,
+ "collection_id": "2a8e8537a2b2305e9e53ee5b50535514",
+ "name": "The Great War",
+ "description": "Machines Together are Strong!",
+ "image_url": "https://lh3.googleusercontent.com/peiZ7Mq2ZJQ-pJxVJTcOSLjujmBqAuPM2WG6wnXx7Et2Gx-kxZaPE2p9UcgrxwzKXKj3eQToHLfNf7maQU3eqF4eFQKqzGJ3o6o",
+ "image_properties": {
+ "width": 512,
+ "height": 291,
+ "mime_type": "image/png"
+ },
"banner_image_url": null,
"category": null,
"is_nsfw": null,
- "external_url": null,
- "twitter_username": null,
- "discord_url": null,
+ "external_url": "https://btcmachine.gg/",
+ "twitter_username": "btcordinal",
+ "discord_url": "https://discord.gg/bWsUa599Ya",
"instagram_username": null,
"medium_username": null,
"telegram_url": null,
- "marketplace_pages": [],
+ "marketplace_pages": [
+ {
+ "marketplace_id": "magiceden",
+ "marketplace_name": "Magic Eden",
+ "marketplace_collection_id": "the-great-war",
+ "nft_url": "https://magiceden.io/ordinals/item-details/77777302c93304f4c506566f5e897c9dfdefb7436aa587dc6927b94222429580i782",
+ "collection_url": "https://magiceden.io/ordinals/marketplace/the-great-war",
+ "verified": null
+ }
+ ],
"metaplex_mint": null,
"metaplex_candy_machine": null,
"metaplex_first_verified_creator": null,
- "spam_score": null,
- "floor_prices": [],
+ "mpl_core_collection_address": null,
+ "spam_score": 0,
+ "floor_prices": [
+ {
+ "marketplace_id": "magiceden",
+ "marketplace_name": "Magic Eden",
+ "value": 9000,
+ "payment_token": {
+ "payment_token_id": "bitcoin.native",
+ "name": "Bitcoin",
+ "symbol": "BTC",
+ "address": null,
+ "decimals": 8
+ },
+ "value_usd_cents": 570
+ },
+ {
+ "marketplace_id": "okx",
+ "marketplace_name": "OKX",
+ "value": 100000,
+ "payment_token": {
+ "payment_token_id": "bitcoin.native",
+ "name": "Bitcoin",
+ "symbol": "BTC",
+ "address": null,
+ "decimals": 8
+ },
+ "value_usd_cents": 6329
+ }
+ ],
"top_bids": [],
- "distinct_owner_count": null,
- "distinct_nft_count": null,
- "total_quantity": null,
- "chains": [],
+ "distinct_owner_count": 7209,
+ "distinct_nft_count": 7279,
+ "total_quantity": 7279,
+ "chains": ["bitcoin"],
"top_contracts": [],
"collection_royalties": []
},
"last_sale": null,
"primary_sale": null,
"first_created": {
- "minted_to": "bc1pgtat0n2kavrz4ufhngm2muzxzx6pcmvr4czp089v48u5sgvpd9vqjsuaql",
+ "minted_to": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "timestamp": "2024-03-14T11:34:34",
- "block_number": 834655,
- "transaction": "",
+ "timestamp": "2024-09-01T21:29:08",
+ "block_number": 859428,
+ "transaction": "77777302c93304f4c506566f5e897c9dfdefb7436aa587dc6927b94222429580",
"transaction_initiator": null
},
"rarity": {
@@ -237,51 +278,31 @@
"royalty": [],
"extra_metadata": {
"attributes": [],
- "utxo_details": {
- "distinct_rare_sats": 0,
- "satributes": {
- "common": {
- "count": 9000,
- "display_name": "Common",
- "description": "Any sat that is not the first sat of its block",
- "icon": "https://cdn.simplehash.com/rare_sats/satribute_common.png"
- }
- },
- "sat_ranges": [
- {
- "starting_sat": 574534229482726,
- "value": 9000,
- "distinct_rare_sats": 0,
- "year": "2011",
- "subranges": [
- {
- "starting_sat": 574534229482726,
- "value": 9000,
- "sat_types": ["common"]
- }
- ]
- }
- ],
- "block_number": 834655,
- "value": 9000,
- "script_pub_key": {
- "asm": "",
- "desc": "",
- "hex": "",
- "address": "",
- "type": ""
- }
+ "ordinal_details": {
+ "inscription_id": "77777302c93304f4c506566f5e897c9dfdefb7436aa587dc6927b94222429580i782",
+ "inscription_number": 75152040,
+ "content_length": null,
+ "content_type": "image/png",
+ "sat_number": 1467197131900234,
+ "sat_name": "dlngbapxjdv",
+ "sat_rarity": "common",
+ "protocol_name": null,
+ "protocol_content": null,
+ "location": "77777302c93304f4c506566f5e897c9dfdefb7436aa587dc6927b94222429580:782:0",
+ "output_value": 330,
+ "parents": [],
+ "charms": ["vindicated"]
},
- "image_original_url": null,
+ "image_original_url": "https://ordinals.simplehash.com/content/77777302c93304f4c506566f5e897c9dfdefb7436aa587dc6927b94222429580i782",
"animation_original_url": null,
"metadata_original_url": null
}
},
{
- "nft_id": "utxo.51fb634f0fefa3441e1a60090d9e292ce1f0803258c2dae818410db4192c89f6.0",
+ "nft_id": "utxo.309cf2eaacb4fc1de643c8f5acb8aa6767947b0ad7d8c18e2da01546c403e87f.337",
"chain": "utxo",
- "contract_address": "51fb634f0fefa3441e1a60090d9e292ce1f0803258c2dae818410db4192c89f6",
- "token_id": "0",
+ "contract_address": "309cf2eaacb4fc1de643c8f5acb8aa6767947b0ad7d8c18e2da01546c403e87f",
+ "token_id": "337",
"name": null,
"description": null,
"previews": {
@@ -304,17 +325,17 @@
"other_properties": null,
"background_color": null,
"external_url": null,
- "created_date": "2024-03-08T10:37:28",
+ "created_date": "2024-08-30T15:25:25",
"status": "minted",
"token_count": 1,
"owner_count": 1,
"owners": [
{
- "owerner_address": "weneverknow",
+ "owner_address": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "first_acquired_date": "2024-03-08T10:37:28",
- "last_acquired_date": "2024-03-08T10:37:28"
+ "first_acquired_date": "2024-08-30T15:25:25",
+ "last_acquired_date": "2024-08-30T15:25:25"
}
],
"contract": {
@@ -345,6 +366,7 @@
"metaplex_mint": null,
"metaplex_candy_machine": null,
"metaplex_first_verified_creator": null,
+ "mpl_core_collection_address": null,
"spam_score": null,
"floor_prices": [],
"top_bids": [],
@@ -358,12 +380,12 @@
"last_sale": null,
"primary_sale": null,
"first_created": {
- "minted_to": "bc1pgtat0n2kavrz4ufhngm2muzxzx6pcmvr4czp089v48u5sgvpd9vqjsuaql",
+ "minted_to": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "timestamp": "2024-03-08T10:37:28",
- "block_number": 833719,
- "transaction": "",
+ "timestamp": "2024-08-30T15:25:25",
+ "block_number": 859100,
+ "transaction": "309cf2eaacb4fc1de643c8f5acb8aa6767947b0ad7d8c18e2da01546c403e87f",
"transaction_initiator": null
},
"rarity": {
@@ -378,7 +400,7 @@
"distinct_rare_sats": 0,
"satributes": {
"common": {
- "count": 600,
+ "count": 330,
"display_name": "Common",
"description": "Any sat that is not the first sat of its block",
"icon": "https://cdn.simplehash.com/rare_sats/satribute_common.png"
@@ -386,28 +408,21 @@
},
"sat_ranges": [
{
- "starting_sat": 1032093336971063,
- "value": 600,
+ "starting_sat": 454039669334920,
+ "value": 330,
"distinct_rare_sats": 0,
- "year": "2012",
+ "year": "2010",
"subranges": [
{
- "starting_sat": 1032093336971063,
- "value": 600,
+ "starting_sat": 454039669334920,
+ "value": 330,
"sat_types": ["common"]
}
]
}
],
- "block_number": 833719,
- "value": 600,
- "script_pub_key": {
- "asm": "",
- "desc": "",
- "hex": "",
- "address": "",
- "type": ""
- }
+ "block_number": 859100,
+ "value": 330
},
"image_original_url": null,
"animation_original_url": null,
@@ -415,19 +430,19 @@
}
},
{
- "nft_id": "bitcoin.51fb634f0fefa3441e1a60090d9e292ce1f0803258c2dae818410db4192c89f6i0",
+ "nft_id": "bitcoin.309cf2eaacb4fc1de643c8f5acb8aa6767947b0ad7d8c18e2da01546c403e87fi337",
"chain": "bitcoin",
- "contract_address": "51fb634f0fefa3441e1a60090d9e292ce1f0803258c2dae818410db4192c89f6i0",
+ "contract_address": "309cf2eaacb4fc1de643c8f5acb8aa6767947b0ad7d8c18e2da01546c403e87fi337",
"token_id": null,
"name": null,
"description": null,
"previews": {
- "image_small_url": "https://lh3.googleusercontent.com/3SvffDeKTOIYaByvQqk2pnIlWKsBUcwW_u5jAEdwLGNbRJgjJPWv544atw0tKuR78-xy8YbhDSbNh-zofN4C7AnBCEj6t9TqXA=s250",
- "image_medium_url": "https://lh3.googleusercontent.com/3SvffDeKTOIYaByvQqk2pnIlWKsBUcwW_u5jAEdwLGNbRJgjJPWv544atw0tKuR78-xy8YbhDSbNh-zofN4C7AnBCEj6t9TqXA",
- "image_large_url": "https://lh3.googleusercontent.com/3SvffDeKTOIYaByvQqk2pnIlWKsBUcwW_u5jAEdwLGNbRJgjJPWv544atw0tKuR78-xy8YbhDSbNh-zofN4C7AnBCEj6t9TqXA=s1000",
- "image_opengraph_url": "https://lh3.googleusercontent.com/3SvffDeKTOIYaByvQqk2pnIlWKsBUcwW_u5jAEdwLGNbRJgjJPWv544atw0tKuR78-xy8YbhDSbNh-zofN4C7AnBCEj6t9TqXA=k-w1200-s2400-rj",
- "blurhash": "UPFI}DWVl-bGSgoLWVo1l-Woq^WoW.n*WVj[",
- "predominant_color": "#84e7db"
+ "image_small_url": "https://lh3.googleusercontent.com/tiTaXpnYHh7JKPvuqBkmDKAmOMES0DSOWKQ1XrmEMgdqOivoQgiDlomhWDkY9mAn1ZHrwkxi0-MySgouowgIQxAWIadLzI2T-w=s250",
+ "image_medium_url": "https://lh3.googleusercontent.com/tiTaXpnYHh7JKPvuqBkmDKAmOMES0DSOWKQ1XrmEMgdqOivoQgiDlomhWDkY9mAn1ZHrwkxi0-MySgouowgIQxAWIadLzI2T-w",
+ "image_large_url": "https://lh3.googleusercontent.com/tiTaXpnYHh7JKPvuqBkmDKAmOMES0DSOWKQ1XrmEMgdqOivoQgiDlomhWDkY9mAn1ZHrwkxi0-MySgouowgIQxAWIadLzI2T-w=s1000",
+ "image_opengraph_url": "https://lh3.googleusercontent.com/tiTaXpnYHh7JKPvuqBkmDKAmOMES0DSOWKQ1XrmEMgdqOivoQgiDlomhWDkY9mAn1ZHrwkxi0-MySgouowgIQxAWIadLzI2T-w=k-w1200-s2400-rj",
+ "blurhash": "U22$HeayIUj[WBfQj[ay00j[%MayxuayWBj[",
+ "predominant_color": "#040404"
},
"image_url": null,
"image_properties": null,
@@ -437,28 +452,26 @@
"audio_properties": null,
"model_url": null,
"model_properties": null,
- "other_url": "https://ordinals.simplehash.com/content/51fb634f0fefa3441e1a60090d9e292ce1f0803258c2dae818410db4192c89f6i0",
- "other_properties": {
- "mime_type": "text/html"
- },
+ "other_url": null,
+ "other_properties": null,
"background_color": null,
"external_url": null,
- "created_date": "2024-03-08T10:37:28",
+ "created_date": "2024-08-30T15:25:25",
"status": "minted",
"token_count": 1,
"owner_count": 1,
"owners": [
{
- "owerner_address": "weneverknow",
+ "owner_address": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "first_acquired_date": "2024-03-08T10:37:28",
- "last_acquired_date": "2024-03-08T10:37:28"
+ "first_acquired_date": "2024-08-30T15:25:25",
+ "last_acquired_date": "2024-08-30T15:25:25"
}
],
"contract": {
"type": "ORDINALS",
- "name": "Inscription #63691311",
+ "name": "Inscription #75032420",
"symbol": null,
"deployed_by": null,
"deployed_via_contract": null,
@@ -484,6 +497,7 @@
"metaplex_mint": null,
"metaplex_candy_machine": null,
"metaplex_first_verified_creator": null,
+ "mpl_core_collection_address": null,
"spam_score": null,
"floor_prices": [],
"top_bids": [],
@@ -497,12 +511,12 @@
"last_sale": null,
"primary_sale": null,
"first_created": {
- "minted_to": "bc1pgtat0n2kavrz4ufhngm2muzxzx6pcmvr4czp089v48u5sgvpd9vqjsuaql",
+ "minted_to": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "timestamp": "2024-03-08T10:37:28",
- "block_number": 833719,
- "transaction": "",
+ "timestamp": "2024-08-30T15:25:25",
+ "block_number": 859100,
+ "transaction": "309cf2eaacb4fc1de643c8f5acb8aa6767947b0ad7d8c18e2da01546c403e87f",
"transaction_initiator": null
},
"rarity": {
@@ -514,30 +528,30 @@
"extra_metadata": {
"attributes": [],
"ordinal_details": {
- "inscription_id": "51fb634f0fefa3441e1a60090d9e292ce1f0803258c2dae818410db4192c89f6i0",
- "inscription_number": 63691311,
- "content_length": 188,
- "content_type": "text/html;charset=utf-8",
- "sat_number": 1032093336971063,
- "sat_name": "gnqupvpiwdm",
- "sat_rarity": "palindrome",
+ "inscription_id": "309cf2eaacb4fc1de643c8f5acb8aa6767947b0ad7d8c18e2da01546c403e87fi337",
+ "inscription_number": 75032420,
+ "content_length": 19,
+ "content_type": "text/plain;charset=utf-8",
+ "sat_number": 454039669334920,
+ "sat_name": "kqcxdspodgj",
+ "sat_rarity": "common",
"protocol_name": null,
"protocol_content": null,
- "location": "51fb634f0fefa3441e1a60090d9e292ce1f0803258c2dae818410db4192c89f6:0:0",
- "output_value": 600,
- "parents": null,
- "charms": null
+ "location": "309cf2eaacb4fc1de643c8f5acb8aa6767947b0ad7d8c18e2da01546c403e87f:337:0",
+ "output_value": 330,
+ "parents": [],
+ "charms": ["vindicated"]
},
- "image_original_url": "https://ordinals.simplehash.com/content/51fb634f0fefa3441e1a60090d9e292ce1f0803258c2dae818410db4192c89f6i0",
+ "image_original_url": "https://ordinals.simplehash.com/content/309cf2eaacb4fc1de643c8f5acb8aa6767947b0ad7d8c18e2da01546c403e87fi337",
"animation_original_url": null,
"metadata_original_url": null
}
},
{
- "nft_id": "utxo.b2011509f56ca83378d5e72e413ed5c939cfa4ff3d82680c1b12b5a6b406ac95.0",
+ "nft_id": "utxo.b23fde8d4aa533ea881a57a1db6cf9c1df7a679cdb3ff53a9459512e9632131c.1270",
"chain": "utxo",
- "contract_address": "b2011509f56ca83378d5e72e413ed5c939cfa4ff3d82680c1b12b5a6b406ac95",
- "token_id": "0",
+ "contract_address": "b23fde8d4aa533ea881a57a1db6cf9c1df7a679cdb3ff53a9459512e9632131c",
+ "token_id": "1270",
"name": null,
"description": null,
"previews": {
@@ -560,17 +574,17 @@
"other_properties": null,
"background_color": null,
"external_url": null,
- "created_date": "2024-03-08T10:33:01",
+ "created_date": "2024-08-04T05:57:28",
"status": "minted",
"token_count": 1,
"owner_count": 1,
"owners": [
{
- "owerner_address": "weneverknow",
+ "owner_address": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "first_acquired_date": "2024-03-08T10:33:01",
- "last_acquired_date": "2024-03-08T10:33:01"
+ "first_acquired_date": "2024-08-04T05:57:28",
+ "last_acquired_date": "2024-08-04T05:57:28"
}
],
"contract": {
@@ -601,6 +615,7 @@
"metaplex_mint": null,
"metaplex_candy_machine": null,
"metaplex_first_verified_creator": null,
+ "mpl_core_collection_address": null,
"spam_score": null,
"floor_prices": [],
"top_bids": [],
@@ -614,12 +629,12 @@
"last_sale": null,
"primary_sale": null,
"first_created": {
- "minted_to": "bc1pgtat0n2kavrz4ufhngm2muzxzx6pcmvr4czp089v48u5sgvpd9vqjsuaql",
+ "minted_to": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "timestamp": "2024-03-08T10:33:01",
- "block_number": 833718,
- "transaction": "",
+ "timestamp": "2024-08-04T05:57:28",
+ "block_number": 855306,
+ "transaction": "b23fde8d4aa533ea881a57a1db6cf9c1df7a679cdb3ff53a9459512e9632131c",
"transaction_initiator": null
},
"rarity": {
@@ -634,7 +649,7 @@
"distinct_rare_sats": 0,
"satributes": {
"common": {
- "count": 600,
+ "count": 546,
"display_name": "Common",
"description": "Any sat that is not the first sat of its block",
"icon": "https://cdn.simplehash.com/rare_sats/satribute_common.png"
@@ -642,28 +657,21 @@
},
"sat_ranges": [
{
- "starting_sat": 1032093336949888,
- "value": 600,
+ "starting_sat": 1503604759309658,
+ "value": 546,
"distinct_rare_sats": 0,
- "year": "2012",
+ "year": "2016",
"subranges": [
{
- "starting_sat": 1032093336949888,
- "value": 600,
+ "starting_sat": 1503604759309658,
+ "value": 546,
"sat_types": ["common"]
}
]
}
],
- "block_number": 833718,
- "value": 600,
- "script_pub_key": {
- "asm": "",
- "desc": "",
- "hex": "",
- "address": "",
- "type": ""
- }
+ "block_number": 855306,
+ "value": 546
},
"image_original_url": null,
"animation_original_url": null,
@@ -671,19 +679,19 @@
}
},
{
- "nft_id": "bitcoin.b2011509f56ca83378d5e72e413ed5c939cfa4ff3d82680c1b12b5a6b406ac95i0",
- "chain": "bitcoin",
- "contract_address": "b2011509f56ca83378d5e72e413ed5c939cfa4ff3d82680c1b12b5a6b406ac95i0",
- "token_id": null,
+ "nft_id": "utxo.b23fde8d4aa533ea881a57a1db6cf9c1df7a679cdb3ff53a9459512e9632131c.1235",
+ "chain": "utxo",
+ "contract_address": "b23fde8d4aa533ea881a57a1db6cf9c1df7a679cdb3ff53a9459512e9632131c",
+ "token_id": "1235",
"name": null,
"description": null,
"previews": {
- "image_small_url": "https://lh3.googleusercontent.com/xTE3sfYiK7gxq_cIVzNTdy3pWfOOOondhBMWCPNFt69EItUBBdj_EWA_xWw2tK0Z2Kr8JqjoHv-0YsBP-NoF44RN_hkxikyCkN4=s250",
- "image_medium_url": "https://lh3.googleusercontent.com/xTE3sfYiK7gxq_cIVzNTdy3pWfOOOondhBMWCPNFt69EItUBBdj_EWA_xWw2tK0Z2Kr8JqjoHv-0YsBP-NoF44RN_hkxikyCkN4",
- "image_large_url": "https://lh3.googleusercontent.com/xTE3sfYiK7gxq_cIVzNTdy3pWfOOOondhBMWCPNFt69EItUBBdj_EWA_xWw2tK0Z2Kr8JqjoHv-0YsBP-NoF44RN_hkxikyCkN4=s1000",
- "image_opengraph_url": "https://lh3.googleusercontent.com/xTE3sfYiK7gxq_cIVzNTdy3pWfOOOondhBMWCPNFt69EItUBBdj_EWA_xWw2tK0Z2Kr8JqjoHv-0YsBP-NoF44RN_hkxikyCkN4=k-w1200-s2400-rj",
- "blurhash": "UDA]pJRk0foxMzoLtPbH0ModkqR*%KWXM}oe",
- "predominant_color": "#e28c3b"
+ "image_small_url": null,
+ "image_medium_url": null,
+ "image_large_url": null,
+ "image_opengraph_url": null,
+ "blurhash": null,
+ "predominant_color": null
},
"image_url": null,
"image_properties": null,
@@ -693,28 +701,26 @@
"audio_properties": null,
"model_url": null,
"model_properties": null,
- "other_url": "https://ordinals.simplehash.com/content/b2011509f56ca83378d5e72e413ed5c939cfa4ff3d82680c1b12b5a6b406ac95i0",
- "other_properties": {
- "mime_type": "text/html"
- },
+ "other_url": null,
+ "other_properties": null,
"background_color": null,
"external_url": null,
- "created_date": "2024-03-08T10:33:01",
+ "created_date": "2024-08-04T05:57:28",
"status": "minted",
"token_count": 1,
"owner_count": 1,
"owners": [
{
- "owerner_address": "weneverknow",
+ "owner_address": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "first_acquired_date": "2024-03-08T10:33:01",
- "last_acquired_date": "2024-03-08T10:33:01"
+ "first_acquired_date": "2024-08-04T05:57:28",
+ "last_acquired_date": "2024-08-04T05:57:28"
}
],
"contract": {
- "type": "ORDINALS",
- "name": "Inscription #63690060",
+ "type": "UTXO",
+ "name": null,
"symbol": null,
"deployed_by": null,
"deployed_via_contract": null,
@@ -740,6 +746,7 @@
"metaplex_mint": null,
"metaplex_candy_machine": null,
"metaplex_first_verified_creator": null,
+ "mpl_core_collection_address": null,
"spam_score": null,
"floor_prices": [],
"top_bids": [],
@@ -753,12 +760,12 @@
"last_sale": null,
"primary_sale": null,
"first_created": {
- "minted_to": "bc1pgtat0n2kavrz4ufhngm2muzxzx6pcmvr4czp089v48u5sgvpd9vqjsuaql",
+ "minted_to": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "timestamp": "2024-03-08T10:33:01",
- "block_number": 833718,
- "transaction": "",
+ "timestamp": "2024-08-04T05:57:28",
+ "block_number": 855306,
+ "transaction": "b23fde8d4aa533ea881a57a1db6cf9c1df7a679cdb3ff53a9459512e9632131c",
"transaction_initiator": null
},
"rarity": {
@@ -769,31 +776,44 @@
"royalty": [],
"extra_metadata": {
"attributes": [],
- "ordinal_details": {
- "inscription_id": "b2011509f56ca83378d5e72e413ed5c939cfa4ff3d82680c1b12b5a6b406ac95i0",
- "inscription_number": 63690060,
- "content_length": 187,
- "content_type": "text/html;charset=utf-8",
- "sat_number": 1032093336949888,
- "sat_name": "gnqupvpkblx",
- "sat_rarity": "common",
- "protocol_name": null,
- "protocol_content": null,
- "location": "b2011509f56ca83378d5e72e413ed5c939cfa4ff3d82680c1b12b5a6b406ac95:0:0",
- "output_value": 600,
- "parents": null,
- "charms": null
+ "utxo_details": {
+ "distinct_rare_sats": 0,
+ "satributes": {
+ "common": {
+ "count": 546,
+ "display_name": "Common",
+ "description": "Any sat that is not the first sat of its block",
+ "icon": "https://cdn.simplehash.com/rare_sats/satribute_common.png"
+ }
+ },
+ "sat_ranges": [
+ {
+ "starting_sat": 1503604759290548,
+ "value": 546,
+ "distinct_rare_sats": 0,
+ "year": "2016",
+ "subranges": [
+ {
+ "starting_sat": 1503604759290548,
+ "value": 546,
+ "sat_types": ["common"]
+ }
+ ]
+ }
+ ],
+ "block_number": 855306,
+ "value": 546
},
- "image_original_url": "https://ordinals.simplehash.com/content/b2011509f56ca83378d5e72e413ed5c939cfa4ff3d82680c1b12b5a6b406ac95i0",
+ "image_original_url": null,
"animation_original_url": null,
"metadata_original_url": null
}
},
{
- "nft_id": "utxo.398901189471c069dd74068a2884e02829c831432e705b28a0c337696e681b97.0",
+ "nft_id": "utxo.b23fde8d4aa533ea881a57a1db6cf9c1df7a679cdb3ff53a9459512e9632131c.527",
"chain": "utxo",
- "contract_address": "398901189471c069dd74068a2884e02829c831432e705b28a0c337696e681b97",
- "token_id": "0",
+ "contract_address": "b23fde8d4aa533ea881a57a1db6cf9c1df7a679cdb3ff53a9459512e9632131c",
+ "token_id": "527",
"name": null,
"description": null,
"previews": {
@@ -816,17 +836,17 @@
"other_properties": null,
"background_color": null,
"external_url": null,
- "created_date": "2024-03-08T10:06:46",
+ "created_date": "2024-08-04T05:57:28",
"status": "minted",
"token_count": 1,
"owner_count": 1,
"owners": [
{
- "owerner_address": "weneverknow",
+ "owner_address": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "first_acquired_date": "2024-03-08T10:06:46",
- "last_acquired_date": "2024-03-08T10:06:46"
+ "first_acquired_date": "2024-08-04T05:57:28",
+ "last_acquired_date": "2024-08-04T05:57:28"
}
],
"contract": {
@@ -857,6 +877,7 @@
"metaplex_mint": null,
"metaplex_candy_machine": null,
"metaplex_first_verified_creator": null,
+ "mpl_core_collection_address": null,
"spam_score": null,
"floor_prices": [],
"top_bids": [],
@@ -870,12 +891,12 @@
"last_sale": null,
"primary_sale": null,
"first_created": {
- "minted_to": "bc1pgtat0n2kavrz4ufhngm2muzxzx6pcmvr4czp089v48u5sgvpd9vqjsuaql",
+ "minted_to": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "timestamp": "2024-03-08T10:06:46",
- "block_number": 833714,
- "transaction": "",
+ "timestamp": "2024-08-04T05:57:28",
+ "block_number": 855306,
+ "transaction": "b23fde8d4aa533ea881a57a1db6cf9c1df7a679cdb3ff53a9459512e9632131c",
"transaction_initiator": null
},
"rarity": {
@@ -890,7 +911,7 @@
"distinct_rare_sats": 0,
"satributes": {
"common": {
- "count": 600,
+ "count": 546,
"display_name": "Common",
"description": "Any sat that is not the first sat of its block",
"icon": "https://cdn.simplehash.com/rare_sats/satribute_common.png"
@@ -898,28 +919,21 @@
},
"sat_ranges": [
{
- "starting_sat": 1032093336943299,
- "value": 600,
+ "starting_sat": 1503604758646012,
+ "value": 546,
"distinct_rare_sats": 0,
- "year": "2012",
+ "year": "2016",
"subranges": [
{
- "starting_sat": 1032093336943299,
- "value": 600,
+ "starting_sat": 1503604758646012,
+ "value": 546,
"sat_types": ["common"]
}
]
}
],
- "block_number": 833714,
- "value": 600,
- "script_pub_key": {
- "asm": "",
- "desc": "",
- "hex": "",
- "address": "",
- "type": ""
- }
+ "block_number": 855306,
+ "value": 546
},
"image_original_url": null,
"animation_original_url": null,
@@ -927,50 +941,54 @@
}
},
{
- "nft_id": "bitcoin.398901189471c069dd74068a2884e02829c831432e705b28a0c337696e681b97i0",
+ "nft_id": "bitcoin.804861bb7c27f592916dbe0636ed0a53dcab04ca4266172979311631e715d2f1i27",
"chain": "bitcoin",
- "contract_address": "398901189471c069dd74068a2884e02829c831432e705b28a0c337696e681b97i0",
+ "contract_address": "804861bb7c27f592916dbe0636ed0a53dcab04ca4266172979311631e715d2f1i27",
"token_id": null,
- "name": null,
+ "name": "GhostXXX #6528",
"description": null,
"previews": {
- "image_small_url": null,
- "image_medium_url": null,
- "image_large_url": null,
- "image_opengraph_url": null,
- "blurhash": null,
- "predominant_color": null
+ "image_small_url": "https://lh3.googleusercontent.com/rRZzvp2AG4SLspFR-mZHc4fAsQRjqs-VAZqL4D3Qzm9KUTU97aHlsaIx6DIWmUS-V1BqKV48-r6YkEaKmfP6UBWpQ0Sww7XYYjY=s250",
+ "image_medium_url": "https://lh3.googleusercontent.com/rRZzvp2AG4SLspFR-mZHc4fAsQRjqs-VAZqL4D3Qzm9KUTU97aHlsaIx6DIWmUS-V1BqKV48-r6YkEaKmfP6UBWpQ0Sww7XYYjY",
+ "image_large_url": "https://lh3.googleusercontent.com/rRZzvp2AG4SLspFR-mZHc4fAsQRjqs-VAZqL4D3Qzm9KUTU97aHlsaIx6DIWmUS-V1BqKV48-r6YkEaKmfP6UBWpQ0Sww7XYYjY=s1000",
+ "image_opengraph_url": "https://lh3.googleusercontent.com/rRZzvp2AG4SLspFR-mZHc4fAsQRjqs-VAZqL4D3Qzm9KUTU97aHlsaIx6DIWmUS-V1BqKV48-r6YkEaKmfP6UBWpQ0Sww7XYYjY=k-w1200-s2400-rj",
+ "blurhash": "UxKd}FR*~qxtxbf6oeWVxvoLV@WBxuofWBof",
+ "predominant_color": "#e0ddda"
+ },
+ "image_url": "https://cdn.simplehash.com/assets/8ca17982ddbcbf401cd4f0eecc09a4c1dc3aef42ac5c6700eb207a94b8e3d17b.png",
+ "image_properties": {
+ "width": 44,
+ "height": 44,
+ "size": 315,
+ "mime_type": "image/png",
+ "exif_orientation": null
},
- "image_url": null,
- "image_properties": null,
"video_url": null,
"video_properties": null,
"audio_url": null,
"audio_properties": null,
"model_url": null,
"model_properties": null,
- "other_url": "https://ordinals.simplehash.com/content/398901189471c069dd74068a2884e02829c831432e705b28a0c337696e681b97i0",
- "other_properties": {
- "mime_type": "text/html"
- },
+ "other_url": null,
+ "other_properties": null,
"background_color": null,
"external_url": null,
- "created_date": "2024-03-08T10:06:46",
+ "created_date": "2024-08-03T13:24:17",
"status": "minted",
"token_count": 1,
"owner_count": 1,
"owners": [
{
- "owerner_address": "weneverknow",
+ "owner_address": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "first_acquired_date": "2024-03-08T10:06:46",
- "last_acquired_date": "2024-03-08T10:06:46"
+ "first_acquired_date": "2024-08-04T05:57:28",
+ "last_acquired_date": "2024-08-04T05:57:28"
}
],
"contract": {
"type": "ORDINALS",
- "name": "Inscription #63677178",
+ "name": "Inscription #73735901",
"symbol": null,
"deployed_by": null,
"deployed_via_contract": null,
@@ -978,21 +996,21 @@
"has_multiple_collections": false
},
"collection": {
- "collection_id": "30bda699616f2f7c4d8269825498578d",
- "name": "Luminescates",
- "description": "Introducing Luminescates!Explore an infinite variety of radiant beings that enchant, astonish, and enthrall.Immerse yourself in the vast expanse of boundless Luminescates—what wonders will you discover‽Highly interactive and endlessly entertaining, explore the mesmerising combinations and lose yourself in infinity!Controls:\"1\" create new Luminescate.\"double click\" change frequency\"click & drag\" change shape/size\"space bar\" swap Lemniscate typeLuminescence + Lemniscates = Luminescates.",
- "image_url": "https://lh3.googleusercontent.com/6XhTag87_zRPXjFlVmYfj1tUr_iye2wWse4wOX3IPyQec8Adzs3VOk_I4EZDathyzmwV_rj95lCSsqw7wpzKIViWJ51wAg1_lPc",
+ "collection_id": "3adf01c69aaf5056674ac99d5bea3266",
+ "name": "GhostXXX",
+ "description": "During the bear market, the Monkes accidentally froze to death and turned into Ghosts. All addresses holding Monkes will receive Ghosts before their Monkes die.",
+ "image_url": "https://lh3.googleusercontent.com/Sab013L4LqO5JQpXSiRRT7cVz78z2l8KFXYi9UNuaWU7Y5fz8QYfITfgnFcm-B1D4UnVmr6QGjIXb5p7cl66csl_T-SS-HBU2YU",
"image_properties": {
- "width": 502,
- "height": 512,
- "mime_type": "image/jpeg"
+ "width": 500,
+ "height": 500,
+ "mime_type": "image/png"
},
"banner_image_url": null,
"category": null,
"is_nsfw": null,
- "external_url": null,
- "twitter_username": "0xJANK",
- "discord_url": "https://discord.gg/4rQ9cv4JeF",
+ "external_url": "https://ghost.club",
+ "twitter_username": "GhostXXXNFT",
+ "discord_url": null,
"instagram_username": null,
"medium_username": null,
"telegram_url": null,
@@ -1000,21 +1018,35 @@
{
"marketplace_id": "magiceden",
"marketplace_name": "Magic Eden",
- "marketplace_collection_id": "luminescates",
- "nft_url": "https://magiceden.io/ordinals/item-details/398901189471c069dd74068a2884e02829c831432e705b28a0c337696e681b97i0",
- "collection_url": "https://magiceden.io/ordinals/marketplace/luminescates",
+ "marketplace_collection_id": "ghostxxx",
+ "nft_url": "https://magiceden.io/ordinals/item-details/804861bb7c27f592916dbe0636ed0a53dcab04ca4266172979311631e715d2f1i27",
+ "collection_url": "https://magiceden.io/ordinals/marketplace/ghostxxx",
"verified": null
}
],
"metaplex_mint": null,
"metaplex_candy_machine": null,
"metaplex_first_verified_creator": null,
+ "mpl_core_collection_address": null,
"spam_score": 0,
"floor_prices": [
{
"marketplace_id": "magiceden",
"marketplace_name": "Magic Eden",
- "value": 13690,
+ "value": 34000,
+ "payment_token": {
+ "payment_token_id": "bitcoin.native",
+ "name": "Bitcoin",
+ "symbol": "BTC",
+ "address": null,
+ "decimals": 8
+ },
+ "value_usd_cents": 2152
+ },
+ {
+ "marketplace_id": "okx",
+ "marketplace_name": "OKX",
+ "value": 59000,
"payment_token": {
"payment_token_id": "bitcoin.native",
"name": "Bitcoin",
@@ -1022,13 +1054,13 @@
"address": null,
"decimals": 8
},
- "value_usd_cents": 796
+ "value_usd_cents": 3734
}
],
"top_bids": [],
- "distinct_owner_count": 211,
- "distinct_nft_count": 332,
- "total_quantity": 332,
+ "distinct_owner_count": 5301,
+ "distinct_nft_count": 10000,
+ "total_quantity": 10000,
"chains": ["bitcoin"],
"top_contracts": [],
"collection_royalties": []
@@ -1036,59 +1068,91 @@
"last_sale": null,
"primary_sale": null,
"first_created": {
- "minted_to": "bc1pgtat0n2kavrz4ufhngm2muzxzx6pcmvr4czp089v48u5sgvpd9vqjsuaql",
+ "minted_to": "bc1pfp9w5366sgnsqek87jx5ksq4p5vjp4zusjkg8xjdknz93nlczktq22hjjl",
"quantity": 1,
"quantity_string": "1",
- "timestamp": "2024-03-08T10:06:46",
- "block_number": 833714,
- "transaction": "",
+ "timestamp": "2024-08-03T13:24:17",
+ "block_number": 855216,
+ "transaction": "804861bb7c27f592916dbe0636ed0a53dcab04ca4266172979311631e715d2f1",
"transaction_initiator": null
},
"rarity": {
- "rank": null,
- "score": null,
- "unique_attributes": null
+ "rank": 155,
+ "score": 1.562,
+ "unique_attributes": 0
},
"royalty": [],
"extra_metadata": {
- "attributes": [],
+ "attributes": [
+ {
+ "trait_type": "BODY",
+ "value": "ZOMBIE",
+ "display_type": null
+ },
+ {
+ "trait_type": "HEAD",
+ "value": "BOWLER",
+ "display_type": null
+ },
+ {
+ "trait_type": "EYES",
+ "value": "PATCH",
+ "display_type": null
+ },
+ {
+ "trait_type": "EARRING",
+ "value": "DIAMOND",
+ "display_type": null
+ },
+ {
+ "trait_type": "COUNT",
+ "value": "4",
+ "display_type": null
+ }
+ ],
"ordinal_details": {
- "inscription_id": "398901189471c069dd74068a2884e02829c831432e705b28a0c337696e681b97i0",
- "inscription_number": 63677178,
- "content_length": 188,
- "content_type": "text/html;charset=utf-8",
- "sat_number": 1032093336943299,
- "sat_name": "gnqupvpklfi",
+ "inscription_id": "804861bb7c27f592916dbe0636ed0a53dcab04ca4266172979311631e715d2f1i27",
+ "inscription_number": 73735901,
+ "content_length": 315,
+ "content_type": "image/png",
+ "sat_number": 1503604758646012,
+ "sat_name": "deuxcwmoqrt",
"sat_rarity": "common",
"protocol_name": null,
"protocol_content": null,
- "location": "398901189471c069dd74068a2884e02829c831432e705b28a0c337696e681b97:0:0",
- "output_value": 600,
- "parents": null,
- "charms": null
+ "location": "b23fde8d4aa533ea881a57a1db6cf9c1df7a679cdb3ff53a9459512e9632131c:527:0",
+ "output_value": 546,
+ "parents": ["b8b4768ffe2468c776a222886c4e5d2d6233e61ff10ceec540e74fa0ea2da828i0"],
+ "charms": ["vindicated"]
},
- "image_original_url": "https://ordinals.simplehash.com/content/398901189471c069dd74068a2884e02829c831432e705b28a0c337696e681b97i0",
+ "image_original_url": "https://ordinals.simplehash.com/content/804861bb7c27f592916dbe0636ed0a53dcab04ca4266172979311631e715d2f1i27",
"animation_original_url": null,
"metadata_original_url": null
}
},
{
- "nft_id": "utxo.68f11cb8770c6e761a5763bb47f00b368e03a4960046f441617c46edad2b0215.5",
- "chain": "utxo",
- "contract_address": "68f11cb8770c6e761a5763bb47f00b368e03a4960046f441617c46edad2b0215",
- "token_id": "5",
- "name": null,
+ "nft_id": "bitcoin.4a635b9a8f46d717cb0664d10615ef3b9eecee511774665af7e4c1ae2991e470i270",
+ "chain": "bitcoin",
+ "contract_address": "4a635b9a8f46d717cb0664d10615ef3b9eecee511774665af7e4c1ae2991e470i270",
+ "token_id": null,
+ "name": "GhostXXX #7271",
"description": null,
"previews": {
- "image_small_url": null,
- "image_medium_url": null,
- "image_large_url": null,
- "image_opengraph_url": null,
- "blurhash": null,
- "predominant_color": null
+ "image_small_url": "https://lh3.googleusercontent.com/tpC_TLjKgG7PIYw3L6m7ysWfsXSfE4X-rLuPI5hAlkSL1M2YXIKN5Oyp5kspiDV7R0UQOYwmwfTniGwveIQZwzGPlfTLjTaqm_Y=s250",
+ "image_medium_url": "https://lh3.googleusercontent.com/tpC_TLjKgG7PIYw3L6m7ysWfsXSfE4X-rLuPI5hAlkSL1M2YXIKN5Oyp5kspiDV7R0UQOYwmwfTniGwveIQZwzGPlfTLjTaqm_Y",
+ "image_large_url": "https://lh3.googleusercontent.com/tpC_TLjKgG7PIYw3L6m7ysWfsXSfE4X-rLuPI5hAlkSL1M2YXIKN5Oyp5kspiDV7R0UQOYwmwfTniGwveIQZwzGPlfTLjTaqm_Y=s1000",
+ "image_opengraph_url": "https://lh3.googleusercontent.com/tpC_TLjKgG7PIYw3L6m7ysWfsXSfE4X-rLuPI5hAlkSL1M2YXIKN5Oyp5kspiDV7R0UQOYwmwfTniGwveIQZwzGPlfTLjTaqm_Y=k-w1200-s2400-rj",
+ "blurhash": "UfL|=eRj_Nxu.SfkRPofx]WBRjRjV@t7WVWB",
+ "predominant_color": "#743b1b"
+ },
+ "image_url": "https://cdn.simplehash.com/assets/099120f89bc951cf43e691930a4e58238b81721d8bcf64a541b99b856cad920d.png",
+ "image_properties": {
+ "width": 44,
+ "height": 44,
+ "size": 255,
+ "mime_type": "image/png",
+ "exif_orientation": null
},
- "image_url": null,
- "image_properties": null,
"video_url": null,
"video_properties": null,
"audio_url": null,
@@ -1099,22 +1163,22 @@
"other_properties": null,
"background_color": null,
"external_url": null,
- "created_date": "2024-03-08T09:47:36",
+ "created_date": "2024-08-03T13:48:39",
"status": "minted",
"token_count": 1,
"owner_count": 1,
"owners": [
{
- "owerner_address": "weneverknow",
+ "owner_address": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "first_acquired_date": "2024-03-08T09:47:36",
- "last_acquired_date": "2024-03-08T09:47:36"
+ "first_acquired_date": "2024-08-04T05:57:28",
+ "last_acquired_date": "2024-08-04T05:57:28"
}
],
"contract": {
- "type": "UTXO",
- "name": null,
+ "type": "ORDINALS",
+ "name": "Inscription #73737253",
"symbol": null,
"deployed_by": null,
"deployed_via_contract": null,
@@ -1122,43 +1186,1132 @@
"has_multiple_collections": false
},
"collection": {
- "collection_id": "0123456789abcdeffedcba9876543210",
- "name": "Rare Sats",
- "description": "Rare Sats are attributes, or \"satributes,\" ascribed to different types of sats. Sats are the smallest unit of a Bitcoin, and satributes commemorate special moments like when a sat was mined or used in a transaction.",
- "image_url": null,
- "image_properties": null,
+ "collection_id": "3adf01c69aaf5056674ac99d5bea3266",
+ "name": "GhostXXX",
+ "description": "During the bear market, the Monkes accidentally froze to death and turned into Ghosts. All addresses holding Monkes will receive Ghosts before their Monkes die.",
+ "image_url": "https://lh3.googleusercontent.com/Sab013L4LqO5JQpXSiRRT7cVz78z2l8KFXYi9UNuaWU7Y5fz8QYfITfgnFcm-B1D4UnVmr6QGjIXb5p7cl66csl_T-SS-HBU2YU",
+ "image_properties": {
+ "width": 500,
+ "height": 500,
+ "mime_type": "image/png"
+ },
"banner_image_url": null,
"category": null,
"is_nsfw": null,
- "external_url": null,
- "twitter_username": null,
+ "external_url": "https://ghost.club",
+ "twitter_username": "GhostXXXNFT",
"discord_url": null,
"instagram_username": null,
"medium_username": null,
"telegram_url": null,
- "marketplace_pages": [],
+ "marketplace_pages": [
+ {
+ "marketplace_id": "magiceden",
+ "marketplace_name": "Magic Eden",
+ "marketplace_collection_id": "ghostxxx",
+ "nft_url": "https://magiceden.io/ordinals/item-details/4a635b9a8f46d717cb0664d10615ef3b9eecee511774665af7e4c1ae2991e470i270",
+ "collection_url": "https://magiceden.io/ordinals/marketplace/ghostxxx",
+ "verified": null
+ }
+ ],
"metaplex_mint": null,
"metaplex_candy_machine": null,
"metaplex_first_verified_creator": null,
- "spam_score": null,
- "floor_prices": [],
- "top_bids": [],
- "distinct_owner_count": 5346731,
- "distinct_nft_count": 7558913,
- "total_quantity": 7558900,
- "chains": ["utxo"],
- "top_contracts": [],
+ "mpl_core_collection_address": null,
+ "spam_score": 0,
+ "floor_prices": [
+ {
+ "marketplace_id": "magiceden",
+ "marketplace_name": "Magic Eden",
+ "value": 34000,
+ "payment_token": {
+ "payment_token_id": "bitcoin.native",
+ "name": "Bitcoin",
+ "symbol": "BTC",
+ "address": null,
+ "decimals": 8
+ },
+ "value_usd_cents": 2152
+ },
+ {
+ "marketplace_id": "okx",
+ "marketplace_name": "OKX",
+ "value": 59000,
+ "payment_token": {
+ "payment_token_id": "bitcoin.native",
+ "name": "Bitcoin",
+ "symbol": "BTC",
+ "address": null,
+ "decimals": 8
+ },
+ "value_usd_cents": 3734
+ }
+ ],
+ "top_bids": [],
+ "distinct_owner_count": 5301,
+ "distinct_nft_count": 10000,
+ "total_quantity": 10000,
+ "chains": ["bitcoin"],
+ "top_contracts": [],
+ "collection_royalties": []
+ },
+ "last_sale": null,
+ "primary_sale": null,
+ "first_created": {
+ "minted_to": "bc1pw532e9xwkejn7f5zx6fr3ehtm92lz3qspw98zndryxzncaaaptjs0n44y8",
+ "quantity": 1,
+ "quantity_string": "1",
+ "timestamp": "2024-08-03T13:48:39",
+ "block_number": 855218,
+ "transaction": "4a635b9a8f46d717cb0664d10615ef3b9eecee511774665af7e4c1ae2991e470",
+ "transaction_initiator": null
+ },
+ "rarity": {
+ "rank": 474,
+ "score": 1.466,
+ "unique_attributes": 0
+ },
+ "royalty": [],
+ "extra_metadata": {
+ "attributes": [
+ {
+ "trait_type": "BODY",
+ "value": "DARK",
+ "display_type": null
+ },
+ {
+ "trait_type": "HEAD",
+ "value": "COUNT",
+ "display_type": null
+ },
+ {
+ "trait_type": "EYES",
+ "value": "BORED",
+ "display_type": null
+ },
+ {
+ "trait_type": "EARRING",
+ "value": "SILVER",
+ "display_type": null
+ },
+ {
+ "trait_type": "COUNT",
+ "value": "4",
+ "display_type": null
+ }
+ ],
+ "ordinal_details": {
+ "inscription_id": "4a635b9a8f46d717cb0664d10615ef3b9eecee511774665af7e4c1ae2991e470i270",
+ "inscription_number": 73737253,
+ "content_length": 255,
+ "content_type": "image/png",
+ "sat_number": 1503604759309658,
+ "sat_name": "deuxcwlcwyx",
+ "sat_rarity": "common",
+ "protocol_name": null,
+ "protocol_content": null,
+ "location": "b23fde8d4aa533ea881a57a1db6cf9c1df7a679cdb3ff53a9459512e9632131c:1270:0",
+ "output_value": 546,
+ "parents": ["b8b4768ffe2468c776a222886c4e5d2d6233e61ff10ceec540e74fa0ea2da828i0"],
+ "charms": ["vindicated"]
+ },
+ "image_original_url": "https://ordinals.simplehash.com/content/4a635b9a8f46d717cb0664d10615ef3b9eecee511774665af7e4c1ae2991e470i270",
+ "animation_original_url": null,
+ "metadata_original_url": null
+ }
+ },
+ {
+ "nft_id": "bitcoin.4a635b9a8f46d717cb0664d10615ef3b9eecee511774665af7e4c1ae2991e470i235",
+ "chain": "bitcoin",
+ "contract_address": "4a635b9a8f46d717cb0664d10615ef3b9eecee511774665af7e4c1ae2991e470i235",
+ "token_id": null,
+ "name": "GhostXXX #7236",
+ "description": null,
+ "previews": {
+ "image_small_url": "https://lh3.googleusercontent.com/5RMAI1GyYoQDdoE-qjskONXbnprlJM2Pu1bxGqfxD5JyEPC7sJohjF1jlCwws33IXAYmmQOUFSGxnY_fcYE031JVXGOIrtGv5dU=s250",
+ "image_medium_url": "https://lh3.googleusercontent.com/5RMAI1GyYoQDdoE-qjskONXbnprlJM2Pu1bxGqfxD5JyEPC7sJohjF1jlCwws33IXAYmmQOUFSGxnY_fcYE031JVXGOIrtGv5dU",
+ "image_large_url": "https://lh3.googleusercontent.com/5RMAI1GyYoQDdoE-qjskONXbnprlJM2Pu1bxGqfxD5JyEPC7sJohjF1jlCwws33IXAYmmQOUFSGxnY_fcYE031JVXGOIrtGv5dU=s1000",
+ "image_opengraph_url": "https://lh3.googleusercontent.com/5RMAI1GyYoQDdoE-qjskONXbnprlJM2Pu1bxGqfxD5JyEPC7sJohjF1jlCwws33IXAYmmQOUFSGxnY_fcYE031JVXGOIrtGv5dU=k-w1200-s2400-rj",
+ "blurhash": "UYNAxDa$_4In%N%Mt6D%?IWUITafxoRkM|xu",
+ "predominant_color": "#e0ded9"
+ },
+ "image_url": "https://cdn.simplehash.com/assets/a664639605b8eb97d126c7a10ada152fd7bb8f5e8825c25a7676dfebc977228c.png",
+ "image_properties": {
+ "width": 44,
+ "height": 44,
+ "size": 249,
+ "mime_type": "image/png",
+ "exif_orientation": null
+ },
+ "video_url": null,
+ "video_properties": null,
+ "audio_url": null,
+ "audio_properties": null,
+ "model_url": null,
+ "model_properties": null,
+ "other_url": null,
+ "other_properties": null,
+ "background_color": null,
+ "external_url": null,
+ "created_date": "2024-08-03T13:48:39",
+ "status": "minted",
+ "token_count": 1,
+ "owner_count": 1,
+ "owners": [
+ {
+ "owner_address": "weNeverKnow",
+ "quantity": 1,
+ "quantity_string": "1",
+ "first_acquired_date": "2024-08-04T05:57:28",
+ "last_acquired_date": "2024-08-04T05:57:28"
+ }
+ ],
+ "contract": {
+ "type": "ORDINALS",
+ "name": "Inscription #73737218",
+ "symbol": null,
+ "deployed_by": null,
+ "deployed_via_contract": null,
+ "owned_by": null,
+ "has_multiple_collections": false
+ },
+ "collection": {
+ "collection_id": "3adf01c69aaf5056674ac99d5bea3266",
+ "name": "GhostXXX",
+ "description": "During the bear market, the Monkes accidentally froze to death and turned into Ghosts. All addresses holding Monkes will receive Ghosts before their Monkes die.",
+ "image_url": "https://lh3.googleusercontent.com/Sab013L4LqO5JQpXSiRRT7cVz78z2l8KFXYi9UNuaWU7Y5fz8QYfITfgnFcm-B1D4UnVmr6QGjIXb5p7cl66csl_T-SS-HBU2YU",
+ "image_properties": {
+ "width": 500,
+ "height": 500,
+ "mime_type": "image/png"
+ },
+ "banner_image_url": null,
+ "category": null,
+ "is_nsfw": null,
+ "external_url": "https://ghost.club",
+ "twitter_username": "GhostXXXNFT",
+ "discord_url": null,
+ "instagram_username": null,
+ "medium_username": null,
+ "telegram_url": null,
+ "marketplace_pages": [
+ {
+ "marketplace_id": "magiceden",
+ "marketplace_name": "Magic Eden",
+ "marketplace_collection_id": "ghostxxx",
+ "nft_url": "https://magiceden.io/ordinals/item-details/4a635b9a8f46d717cb0664d10615ef3b9eecee511774665af7e4c1ae2991e470i235",
+ "collection_url": "https://magiceden.io/ordinals/marketplace/ghostxxx",
+ "verified": null
+ }
+ ],
+ "metaplex_mint": null,
+ "metaplex_candy_machine": null,
+ "metaplex_first_verified_creator": null,
+ "mpl_core_collection_address": null,
+ "spam_score": 0,
+ "floor_prices": [
+ {
+ "marketplace_id": "magiceden",
+ "marketplace_name": "Magic Eden",
+ "value": 34000,
+ "payment_token": {
+ "payment_token_id": "bitcoin.native",
+ "name": "Bitcoin",
+ "symbol": "BTC",
+ "address": null,
+ "decimals": 8
+ },
+ "value_usd_cents": 2152
+ },
+ {
+ "marketplace_id": "okx",
+ "marketplace_name": "OKX",
+ "value": 59000,
+ "payment_token": {
+ "payment_token_id": "bitcoin.native",
+ "name": "Bitcoin",
+ "symbol": "BTC",
+ "address": null,
+ "decimals": 8
+ },
+ "value_usd_cents": 3734
+ }
+ ],
+ "top_bids": [],
+ "distinct_owner_count": 5301,
+ "distinct_nft_count": 10000,
+ "total_quantity": 10000,
+ "chains": ["bitcoin"],
+ "top_contracts": [],
+ "collection_royalties": []
+ },
+ "last_sale": null,
+ "primary_sale": null,
+ "first_created": {
+ "minted_to": "bc1pw532e9xwkejn7f5zx6fr3ehtm92lz3qspw98zndryxzncaaaptjs0n44y8",
+ "quantity": 1,
+ "quantity_string": "1",
+ "timestamp": "2024-08-03T13:48:39",
+ "block_number": 855218,
+ "transaction": "4a635b9a8f46d717cb0664d10615ef3b9eecee511774665af7e4c1ae2991e470",
+ "transaction_initiator": null
+ },
+ "rarity": {
+ "rank": 8823,
+ "score": 0.855,
+ "unique_attributes": 0
+ },
+ "royalty": [],
+ "extra_metadata": {
+ "attributes": [
+ {
+ "trait_type": "BODY",
+ "value": "BINARY",
+ "display_type": null
+ },
+ {
+ "trait_type": "HEAD",
+ "value": "RABBIT",
+ "display_type": null
+ },
+ {
+ "trait_type": "EYES",
+ "value": "PEPE",
+ "display_type": null
+ },
+ {
+ "trait_type": "EARRING",
+ "value": "NONE",
+ "display_type": null
+ },
+ {
+ "trait_type": "COUNT",
+ "value": "3",
+ "display_type": null
+ }
+ ],
+ "ordinal_details": {
+ "inscription_id": "4a635b9a8f46d717cb0664d10615ef3b9eecee511774665af7e4c1ae2991e470i235",
+ "inscription_number": 73737218,
+ "content_length": 249,
+ "content_type": "image/png",
+ "sat_number": 1503604759290548,
+ "sat_name": "deuxcwldzfx",
+ "sat_rarity": "common",
+ "protocol_name": null,
+ "protocol_content": null,
+ "location": "b23fde8d4aa533ea881a57a1db6cf9c1df7a679cdb3ff53a9459512e9632131c:1235:0",
+ "output_value": 546,
+ "parents": ["b8b4768ffe2468c776a222886c4e5d2d6233e61ff10ceec540e74fa0ea2da828i0"],
+ "charms": ["vindicated"]
+ },
+ "image_original_url": "https://ordinals.simplehash.com/content/4a635b9a8f46d717cb0664d10615ef3b9eecee511774665af7e4c1ae2991e470i235",
+ "animation_original_url": null,
+ "metadata_original_url": null
+ }
+ },
+ {
+ "nft_id": "utxo.18ace7a965abb7e8a32f0eb5b940e51dd184986982153dac01af4906febc2b6b.518",
+ "chain": "utxo",
+ "contract_address": "18ace7a965abb7e8a32f0eb5b940e51dd184986982153dac01af4906febc2b6b",
+ "token_id": "518",
+ "name": null,
+ "description": null,
+ "previews": {
+ "image_small_url": null,
+ "image_medium_url": null,
+ "image_large_url": null,
+ "image_opengraph_url": null,
+ "blurhash": null,
+ "predominant_color": null
+ },
+ "image_url": null,
+ "image_properties": null,
+ "video_url": null,
+ "video_properties": null,
+ "audio_url": null,
+ "audio_properties": null,
+ "model_url": null,
+ "model_properties": null,
+ "other_url": null,
+ "other_properties": null,
+ "background_color": null,
+ "external_url": null,
+ "created_date": "2024-08-04T05:46:53",
+ "status": "minted",
+ "token_count": 1,
+ "owner_count": 1,
+ "owners": [
+ {
+ "owner_address": "weNeverKnow",
+ "quantity": 1,
+ "quantity_string": "1",
+ "first_acquired_date": "2024-08-04T05:46:53",
+ "last_acquired_date": "2024-08-04T05:46:53"
+ }
+ ],
+ "contract": {
+ "type": "UTXO",
+ "name": null,
+ "symbol": null,
+ "deployed_by": null,
+ "deployed_via_contract": null,
+ "owned_by": null,
+ "has_multiple_collections": false
+ },
+ "collection": {
+ "collection_id": null,
+ "name": null,
+ "description": null,
+ "image_url": null,
+ "image_properties": null,
+ "banner_image_url": null,
+ "category": null,
+ "is_nsfw": null,
+ "external_url": null,
+ "twitter_username": null,
+ "discord_url": null,
+ "instagram_username": null,
+ "medium_username": null,
+ "telegram_url": null,
+ "marketplace_pages": [],
+ "metaplex_mint": null,
+ "metaplex_candy_machine": null,
+ "metaplex_first_verified_creator": null,
+ "mpl_core_collection_address": null,
+ "spam_score": null,
+ "floor_prices": [],
+ "top_bids": [],
+ "distinct_owner_count": null,
+ "distinct_nft_count": null,
+ "total_quantity": null,
+ "chains": [],
+ "top_contracts": [],
+ "collection_royalties": []
+ },
+ "last_sale": null,
+ "primary_sale": null,
+ "first_created": {
+ "minted_to": "weNeverKnow",
+ "quantity": 1,
+ "quantity_string": "1",
+ "timestamp": "2024-08-04T05:46:53",
+ "block_number": 855305,
+ "transaction": "18ace7a965abb7e8a32f0eb5b940e51dd184986982153dac01af4906febc2b6b",
+ "transaction_initiator": null
+ },
+ "rarity": {
+ "rank": null,
+ "score": null,
+ "unique_attributes": null
+ },
+ "royalty": [],
+ "extra_metadata": {
+ "attributes": [],
+ "utxo_details": {
+ "distinct_rare_sats": 0,
+ "satributes": {
+ "common": {
+ "count": 546,
+ "display_name": "Common",
+ "description": "Any sat that is not the first sat of its block",
+ "icon": "https://cdn.simplehash.com/rare_sats/satribute_common.png"
+ }
+ },
+ "sat_ranges": [
+ {
+ "starting_sat": 1429048782833982,
+ "value": 546,
+ "distinct_rare_sats": 0,
+ "year": "2015",
+ "subranges": [
+ {
+ "starting_sat": 1429048782833982,
+ "value": 546,
+ "sat_types": ["common"]
+ }
+ ]
+ }
+ ],
+ "block_number": 855305,
+ "value": 546
+ },
+ "image_original_url": null,
+ "animation_original_url": null,
+ "metadata_original_url": null
+ }
+ },
+ {
+ "nft_id": "bitcoin.d4b1f46fd5efe85b5bfa073924d0606713ad197bcdca541ffea8d3ca1106f27ei18",
+ "chain": "bitcoin",
+ "contract_address": "d4b1f46fd5efe85b5bfa073924d0606713ad197bcdca541ffea8d3ca1106f27ei18",
+ "token_id": null,
+ "name": "GhostXXX #519",
+ "description": null,
+ "previews": {
+ "image_small_url": "https://lh3.googleusercontent.com/K_a8wL6deY6UGeS-LqqptRDe9UZMvOjlkwuPdZXFA7xq7h1HQo2JgF-SRKZ7_NZCuIT2fKSC7O5IEEbhPQtjXqOYrL-IUzoG_w=s250",
+ "image_medium_url": "https://lh3.googleusercontent.com/K_a8wL6deY6UGeS-LqqptRDe9UZMvOjlkwuPdZXFA7xq7h1HQo2JgF-SRKZ7_NZCuIT2fKSC7O5IEEbhPQtjXqOYrL-IUzoG_w",
+ "image_large_url": "https://lh3.googleusercontent.com/K_a8wL6deY6UGeS-LqqptRDe9UZMvOjlkwuPdZXFA7xq7h1HQo2JgF-SRKZ7_NZCuIT2fKSC7O5IEEbhPQtjXqOYrL-IUzoG_w=s1000",
+ "image_opengraph_url": "https://lh3.googleusercontent.com/K_a8wL6deY6UGeS-LqqptRDe9UZMvOjlkwuPdZXFA7xq7h1HQo2JgF-SRKZ7_NZCuIT2fKSC7O5IEEbhPQtjXqOYrL-IUzoG_w=k-w1200-s2400-rj",
+ "blurhash": "UlN+^rWU.loM.SofRPayyWafR6a_a3kCR%j]",
+ "predominant_color": "#a60404"
+ },
+ "image_url": "https://cdn.simplehash.com/assets/1d7b677990ddc2da0c031331e4c5aed7395128461d7b54975d0f05edabeac3c9.png",
+ "image_properties": {
+ "width": 44,
+ "height": 44,
+ "size": 201,
+ "mime_type": "image/png",
+ "exif_orientation": null
+ },
+ "video_url": null,
+ "video_properties": null,
+ "audio_url": null,
+ "audio_properties": null,
+ "model_url": null,
+ "model_properties": null,
+ "other_url": null,
+ "other_properties": null,
+ "background_color": null,
+ "external_url": null,
+ "created_date": "2024-08-03T09:01:30",
+ "status": "minted",
+ "token_count": 1,
+ "owner_count": 1,
+ "owners": [
+ {
+ "owner_address": "weNeverKnow",
+ "quantity": 1,
+ "quantity_string": "1",
+ "first_acquired_date": "2024-08-04T05:46:53",
+ "last_acquired_date": "2024-08-04T05:46:53"
+ }
+ ],
+ "contract": {
+ "type": "ORDINALS",
+ "name": "Inscription #73724179",
+ "symbol": null,
+ "deployed_by": null,
+ "deployed_via_contract": null,
+ "owned_by": null,
+ "has_multiple_collections": false
+ },
+ "collection": {
+ "collection_id": "3adf01c69aaf5056674ac99d5bea3266",
+ "name": "GhostXXX",
+ "description": "During the bear market, the Monkes accidentally froze to death and turned into Ghosts. All addresses holding Monkes will receive Ghosts before their Monkes die.",
+ "image_url": "https://lh3.googleusercontent.com/Sab013L4LqO5JQpXSiRRT7cVz78z2l8KFXYi9UNuaWU7Y5fz8QYfITfgnFcm-B1D4UnVmr6QGjIXb5p7cl66csl_T-SS-HBU2YU",
+ "image_properties": {
+ "width": 500,
+ "height": 500,
+ "mime_type": "image/png"
+ },
+ "banner_image_url": null,
+ "category": null,
+ "is_nsfw": null,
+ "external_url": "https://ghost.club",
+ "twitter_username": "GhostXXXNFT",
+ "discord_url": null,
+ "instagram_username": null,
+ "medium_username": null,
+ "telegram_url": null,
+ "marketplace_pages": [
+ {
+ "marketplace_id": "magiceden",
+ "marketplace_name": "Magic Eden",
+ "marketplace_collection_id": "ghostxxx",
+ "nft_url": "https://magiceden.io/ordinals/item-details/d4b1f46fd5efe85b5bfa073924d0606713ad197bcdca541ffea8d3ca1106f27ei18",
+ "collection_url": "https://magiceden.io/ordinals/marketplace/ghostxxx",
+ "verified": null
+ }
+ ],
+ "metaplex_mint": null,
+ "metaplex_candy_machine": null,
+ "metaplex_first_verified_creator": null,
+ "mpl_core_collection_address": null,
+ "spam_score": 0,
+ "floor_prices": [
+ {
+ "marketplace_id": "magiceden",
+ "marketplace_name": "Magic Eden",
+ "value": 34000,
+ "payment_token": {
+ "payment_token_id": "bitcoin.native",
+ "name": "Bitcoin",
+ "symbol": "BTC",
+ "address": null,
+ "decimals": 8
+ },
+ "value_usd_cents": 2152
+ },
+ {
+ "marketplace_id": "okx",
+ "marketplace_name": "OKX",
+ "value": 59000,
+ "payment_token": {
+ "payment_token_id": "bitcoin.native",
+ "name": "Bitcoin",
+ "symbol": "BTC",
+ "address": null,
+ "decimals": 8
+ },
+ "value_usd_cents": 3734
+ }
+ ],
+ "top_bids": [],
+ "distinct_owner_count": 5301,
+ "distinct_nft_count": 10000,
+ "total_quantity": 10000,
+ "chains": ["bitcoin"],
+ "top_contracts": [],
+ "collection_royalties": []
+ },
+ "last_sale": null,
+ "primary_sale": null,
+ "first_created": {
+ "minted_to": "bc1ppx8zwtf4tw86h3295reqa0lpnk24ylv96vgvylksku4zy4vret7q8zp05x",
+ "quantity": 1,
+ "quantity_string": "1",
+ "timestamp": "2024-08-03T09:01:30",
+ "block_number": 855197,
+ "transaction": "d4b1f46fd5efe85b5bfa073924d0606713ad197bcdca541ffea8d3ca1106f27e",
+ "transaction_initiator": null
+ },
+ "rarity": {
+ "rank": 1770,
+ "score": 1.097,
+ "unique_attributes": 0
+ },
+ "royalty": [],
+ "extra_metadata": {
+ "attributes": [
+ {
+ "trait_type": "BODY",
+ "value": "RED",
+ "display_type": null
+ },
+ {
+ "trait_type": "HEAD",
+ "value": "NONE",
+ "display_type": null
+ },
+ {
+ "trait_type": "EYES",
+ "value": "RBM",
+ "display_type": null
+ },
+ {
+ "trait_type": "EARRING",
+ "value": "NONE",
+ "display_type": null
+ },
+ {
+ "trait_type": "COUNT",
+ "value": "2",
+ "display_type": null
+ }
+ ],
+ "ordinal_details": {
+ "inscription_id": "d4b1f46fd5efe85b5bfa073924d0606713ad197bcdca541ffea8d3ca1106f27ei18",
+ "inscription_number": 73724179,
+ "content_length": 201,
+ "content_type": "image/png",
+ "sat_number": 1429048782833982,
+ "sat_name": "dsnxsdfrobr",
+ "sat_rarity": "common",
+ "protocol_name": null,
+ "protocol_content": null,
+ "location": "18ace7a965abb7e8a32f0eb5b940e51dd184986982153dac01af4906febc2b6b:518:0",
+ "output_value": 546,
+ "parents": ["b8b4768ffe2468c776a222886c4e5d2d6233e61ff10ceec540e74fa0ea2da828i0"],
+ "charms": ["vindicated"]
+ },
+ "image_original_url": "https://ordinals.simplehash.com/content/d4b1f46fd5efe85b5bfa073924d0606713ad197bcdca541ffea8d3ca1106f27ei18",
+ "animation_original_url": null,
+ "metadata_original_url": null
+ }
+ },
+ {
+ "nft_id": "utxo.75c5b63e46f2b855b5237349307ecba4e9c0111ff5ac87c3f49fb3588468c6a5.92",
+ "chain": "utxo",
+ "contract_address": "75c5b63e46f2b855b5237349307ecba4e9c0111ff5ac87c3f49fb3588468c6a5",
+ "token_id": "92",
+ "name": null,
+ "description": null,
+ "previews": {
+ "image_small_url": null,
+ "image_medium_url": null,
+ "image_large_url": null,
+ "image_opengraph_url": null,
+ "blurhash": null,
+ "predominant_color": null
+ },
+ "image_url": null,
+ "image_properties": null,
+ "video_url": null,
+ "video_properties": null,
+ "audio_url": null,
+ "audio_properties": null,
+ "model_url": null,
+ "model_properties": null,
+ "other_url": null,
+ "other_properties": null,
+ "background_color": null,
+ "external_url": null,
+ "created_date": "2024-07-22T15:34:36",
+ "status": "minted",
+ "token_count": 1,
+ "owner_count": 1,
+ "owners": [
+ {
+ "owner_address": "weNeverKnow",
+ "quantity": 1,
+ "quantity_string": "1",
+ "first_acquired_date": "2024-07-22T15:34:36",
+ "last_acquired_date": "2024-07-22T15:34:36"
+ }
+ ],
+ "contract": {
+ "type": "UTXO",
+ "name": null,
+ "symbol": null,
+ "deployed_by": null,
+ "deployed_via_contract": null,
+ "owned_by": null,
+ "has_multiple_collections": false
+ },
+ "collection": {
+ "collection_id": null,
+ "name": null,
+ "description": null,
+ "image_url": null,
+ "image_properties": null,
+ "banner_image_url": null,
+ "category": null,
+ "is_nsfw": null,
+ "external_url": null,
+ "twitter_username": null,
+ "discord_url": null,
+ "instagram_username": null,
+ "medium_username": null,
+ "telegram_url": null,
+ "marketplace_pages": [],
+ "metaplex_mint": null,
+ "metaplex_candy_machine": null,
+ "metaplex_first_verified_creator": null,
+ "mpl_core_collection_address": null,
+ "spam_score": null,
+ "floor_prices": [],
+ "top_bids": [],
+ "distinct_owner_count": null,
+ "distinct_nft_count": null,
+ "total_quantity": null,
+ "chains": [],
+ "top_contracts": [],
+ "collection_royalties": []
+ },
+ "last_sale": null,
+ "primary_sale": null,
+ "first_created": {
+ "minted_to": "weNeverKnow",
+ "quantity": 1,
+ "quantity_string": "1",
+ "timestamp": "2024-07-22T15:34:36",
+ "block_number": 853394,
+ "transaction": "75c5b63e46f2b855b5237349307ecba4e9c0111ff5ac87c3f49fb3588468c6a5",
+ "transaction_initiator": null
+ },
+ "rarity": {
+ "rank": null,
+ "score": null,
+ "unique_attributes": null
+ },
+ "royalty": [],
+ "extra_metadata": {
+ "attributes": [],
+ "utxo_details": {
+ "distinct_rare_sats": 0,
+ "satributes": {
+ "common": {
+ "count": 546,
+ "display_name": "Common",
+ "description": "Any sat that is not the first sat of its block",
+ "icon": "https://cdn.simplehash.com/rare_sats/satribute_common.png"
+ }
+ },
+ "sat_ranges": [
+ {
+ "starting_sat": 726543639768428,
+ "value": 546,
+ "distinct_rare_sats": 0,
+ "year": "2011",
+ "subranges": [
+ {
+ "starting_sat": 726543639768428,
+ "value": 546,
+ "sat_types": ["common"]
+ }
+ ]
+ }
+ ],
+ "block_number": 853394,
+ "value": 546
+ },
+ "image_original_url": null,
+ "animation_original_url": null,
+ "metadata_original_url": null
+ }
+ },
+ {
+ "nft_id": "utxo.e75219cba8d15f60f0ac75a0e9a7890389018a0375d32a1d0c04d9292f88586f.420",
+ "chain": "utxo",
+ "contract_address": "e75219cba8d15f60f0ac75a0e9a7890389018a0375d32a1d0c04d9292f88586f",
+ "token_id": "420",
+ "name": null,
+ "description": null,
+ "previews": {
+ "image_small_url": null,
+ "image_medium_url": null,
+ "image_large_url": null,
+ "image_opengraph_url": null,
+ "blurhash": null,
+ "predominant_color": null
+ },
+ "image_url": null,
+ "image_properties": null,
+ "video_url": null,
+ "video_properties": null,
+ "audio_url": null,
+ "audio_properties": null,
+ "model_url": null,
+ "model_properties": null,
+ "other_url": null,
+ "other_properties": null,
+ "background_color": null,
+ "external_url": null,
+ "created_date": "2024-07-09T07:31:41",
+ "status": "minted",
+ "token_count": 1,
+ "owner_count": 1,
+ "owners": [
+ {
+ "owner_address": "weNeverKnow",
+ "quantity": 1,
+ "quantity_string": "1",
+ "first_acquired_date": "2024-07-09T07:31:41",
+ "last_acquired_date": "2024-07-09T07:31:41"
+ }
+ ],
+ "contract": {
+ "type": "UTXO",
+ "name": null,
+ "symbol": null,
+ "deployed_by": null,
+ "deployed_via_contract": null,
+ "owned_by": null,
+ "has_multiple_collections": false
+ },
+ "collection": {
+ "collection_id": null,
+ "name": null,
+ "description": null,
+ "image_url": null,
+ "image_properties": null,
+ "banner_image_url": null,
+ "category": null,
+ "is_nsfw": null,
+ "external_url": null,
+ "twitter_username": null,
+ "discord_url": null,
+ "instagram_username": null,
+ "medium_username": null,
+ "telegram_url": null,
+ "marketplace_pages": [],
+ "metaplex_mint": null,
+ "metaplex_candy_machine": null,
+ "metaplex_first_verified_creator": null,
+ "mpl_core_collection_address": null,
+ "spam_score": null,
+ "floor_prices": [],
+ "top_bids": [],
+ "distinct_owner_count": null,
+ "distinct_nft_count": null,
+ "total_quantity": null,
+ "chains": [],
+ "top_contracts": [],
+ "collection_royalties": []
+ },
+ "last_sale": null,
+ "primary_sale": null,
+ "first_created": {
+ "minted_to": "weNeverKnow",
+ "quantity": 1,
+ "quantity_string": "1",
+ "timestamp": "2024-07-09T07:31:41",
+ "block_number": 851367,
+ "transaction": "e75219cba8d15f60f0ac75a0e9a7890389018a0375d32a1d0c04d9292f88586f",
+ "transaction_initiator": null
+ },
+ "rarity": {
+ "rank": null,
+ "score": null,
+ "unique_attributes": null
+ },
+ "royalty": [],
+ "extra_metadata": {
+ "attributes": [],
+ "utxo_details": {
+ "distinct_rare_sats": 0,
+ "satributes": {
+ "common": {
+ "count": 546,
+ "display_name": "Common",
+ "description": "Any sat that is not the first sat of its block",
+ "icon": "https://cdn.simplehash.com/rare_sats/satribute_common.png"
+ }
+ },
+ "sat_ranges": [
+ {
+ "starting_sat": 1407852856843715,
+ "value": 546,
+ "distinct_rare_sats": 0,
+ "year": "2015",
+ "subranges": [
+ {
+ "starting_sat": 1407852856843715,
+ "value": 546,
+ "sat_types": ["common"]
+ }
+ ]
+ }
+ ],
+ "block_number": 851367,
+ "value": 546
+ },
+ "image_original_url": null,
+ "animation_original_url": null,
+ "metadata_original_url": null
+ }
+ },
+ {
+ "nft_id": "utxo.e75219cba8d15f60f0ac75a0e9a7890389018a0375d32a1d0c04d9292f88586f.419",
+ "chain": "utxo",
+ "contract_address": "e75219cba8d15f60f0ac75a0e9a7890389018a0375d32a1d0c04d9292f88586f",
+ "token_id": "419",
+ "name": null,
+ "description": null,
+ "previews": {
+ "image_small_url": null,
+ "image_medium_url": null,
+ "image_large_url": null,
+ "image_opengraph_url": null,
+ "blurhash": null,
+ "predominant_color": null
+ },
+ "image_url": null,
+ "image_properties": null,
+ "video_url": null,
+ "video_properties": null,
+ "audio_url": null,
+ "audio_properties": null,
+ "model_url": null,
+ "model_properties": null,
+ "other_url": null,
+ "other_properties": null,
+ "background_color": null,
+ "external_url": null,
+ "created_date": "2024-07-09T07:31:41",
+ "status": "minted",
+ "token_count": 1,
+ "owner_count": 1,
+ "owners": [
+ {
+ "owner_address": "weNeverKnow",
+ "quantity": 1,
+ "quantity_string": "1",
+ "first_acquired_date": "2024-07-09T07:31:41",
+ "last_acquired_date": "2024-07-09T07:31:41"
+ }
+ ],
+ "contract": {
+ "type": "UTXO",
+ "name": null,
+ "symbol": null,
+ "deployed_by": null,
+ "deployed_via_contract": null,
+ "owned_by": null,
+ "has_multiple_collections": false
+ },
+ "collection": {
+ "collection_id": null,
+ "name": null,
+ "description": null,
+ "image_url": null,
+ "image_properties": null,
+ "banner_image_url": null,
+ "category": null,
+ "is_nsfw": null,
+ "external_url": null,
+ "twitter_username": null,
+ "discord_url": null,
+ "instagram_username": null,
+ "medium_username": null,
+ "telegram_url": null,
+ "marketplace_pages": [],
+ "metaplex_mint": null,
+ "metaplex_candy_machine": null,
+ "metaplex_first_verified_creator": null,
+ "mpl_core_collection_address": null,
+ "spam_score": null,
+ "floor_prices": [],
+ "top_bids": [],
+ "distinct_owner_count": null,
+ "distinct_nft_count": null,
+ "total_quantity": null,
+ "chains": [],
+ "top_contracts": [],
+ "collection_royalties": []
+ },
+ "last_sale": null,
+ "primary_sale": null,
+ "first_created": {
+ "minted_to": "weNeverKnow",
+ "quantity": 1,
+ "quantity_string": "1",
+ "timestamp": "2024-07-09T07:31:41",
+ "block_number": 851367,
+ "transaction": "e75219cba8d15f60f0ac75a0e9a7890389018a0375d32a1d0c04d9292f88586f",
+ "transaction_initiator": null
+ },
+ "rarity": {
+ "rank": null,
+ "score": null,
+ "unique_attributes": null
+ },
+ "royalty": [],
+ "extra_metadata": {
+ "attributes": [],
+ "utxo_details": {
+ "distinct_rare_sats": 0,
+ "satributes": {
+ "common": {
+ "count": 546,
+ "display_name": "Common",
+ "description": "Any sat that is not the first sat of its block",
+ "icon": "https://cdn.simplehash.com/rare_sats/satribute_common.png"
+ }
+ },
+ "sat_ranges": [
+ {
+ "starting_sat": 1407852856843169,
+ "value": 546,
+ "distinct_rare_sats": 0,
+ "year": "2015",
+ "subranges": [
+ {
+ "starting_sat": 1407852856843169,
+ "value": 546,
+ "sat_types": ["common"]
+ }
+ ]
+ }
+ ],
+ "block_number": 851367,
+ "value": 546
+ },
+ "image_original_url": null,
+ "animation_original_url": null,
+ "metadata_original_url": null
+ }
+ },
+ {
+ "nft_id": "utxo.e75219cba8d15f60f0ac75a0e9a7890389018a0375d32a1d0c04d9292f88586f.418",
+ "chain": "utxo",
+ "contract_address": "e75219cba8d15f60f0ac75a0e9a7890389018a0375d32a1d0c04d9292f88586f",
+ "token_id": "418",
+ "name": null,
+ "description": null,
+ "previews": {
+ "image_small_url": null,
+ "image_medium_url": null,
+ "image_large_url": null,
+ "image_opengraph_url": null,
+ "blurhash": null,
+ "predominant_color": null
+ },
+ "image_url": null,
+ "image_properties": null,
+ "video_url": null,
+ "video_properties": null,
+ "audio_url": null,
+ "audio_properties": null,
+ "model_url": null,
+ "model_properties": null,
+ "other_url": null,
+ "other_properties": null,
+ "background_color": null,
+ "external_url": null,
+ "created_date": "2024-07-09T07:31:41",
+ "status": "minted",
+ "token_count": 1,
+ "owner_count": 1,
+ "owners": [
+ {
+ "owner_address": "weNeverKnow",
+ "quantity": 1,
+ "quantity_string": "1",
+ "first_acquired_date": "2024-07-09T07:31:41",
+ "last_acquired_date": "2024-07-09T07:31:41"
+ }
+ ],
+ "contract": {
+ "type": "UTXO",
+ "name": null,
+ "symbol": null,
+ "deployed_by": null,
+ "deployed_via_contract": null,
+ "owned_by": null,
+ "has_multiple_collections": false
+ },
+ "collection": {
+ "collection_id": null,
+ "name": null,
+ "description": null,
+ "image_url": null,
+ "image_properties": null,
+ "banner_image_url": null,
+ "category": null,
+ "is_nsfw": null,
+ "external_url": null,
+ "twitter_username": null,
+ "discord_url": null,
+ "instagram_username": null,
+ "medium_username": null,
+ "telegram_url": null,
+ "marketplace_pages": [],
+ "metaplex_mint": null,
+ "metaplex_candy_machine": null,
+ "metaplex_first_verified_creator": null,
+ "mpl_core_collection_address": null,
+ "spam_score": null,
+ "floor_prices": [],
+ "top_bids": [],
+ "distinct_owner_count": null,
+ "distinct_nft_count": null,
+ "total_quantity": null,
+ "chains": [],
+ "top_contracts": [],
"collection_royalties": []
},
"last_sale": null,
"primary_sale": null,
"first_created": {
- "minted_to": "bc1pgtat0n2kavrz4ufhngm2muzxzx6pcmvr4czp089v48u5sgvpd9vqjsuaql",
+ "minted_to": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "timestamp": "2024-03-08T09:47:36",
- "block_number": 833711,
- "transaction": "",
+ "timestamp": "2024-07-09T07:31:41",
+ "block_number": 851367,
+ "transaction": "e75219cba8d15f60f0ac75a0e9a7890389018a0375d32a1d0c04d9292f88586f",
"transaction_initiator": null
},
"rarity": {
@@ -1170,45 +2323,32 @@
"extra_metadata": {
"attributes": [],
"utxo_details": {
- "distinct_rare_sats": 330,
+ "distinct_rare_sats": 0,
"satributes": {
- "block_78": {
- "count": 330,
- "display_name": "Block 78",
- "description": "Sats mined by Hal Finney in block 78 which was the first block mined by someone other than Satoshi",
- "icon": "https://cdn.simplehash.com/rare_sats/satribute_block78.png"
- },
- "vintage": {
- "count": 330,
- "display_name": "Vintage",
- "description": "Sats mined in the first 1,000 blocks",
- "icon": "https://cdn.simplehash.com/rare_sats/satribute_vintage.png"
+ "common": {
+ "count": 546,
+ "display_name": "Common",
+ "description": "Any sat that is not the first sat of its block",
+ "icon": "https://cdn.simplehash.com/rare_sats/satribute_common.png"
}
},
"sat_ranges": [
{
- "starting_sat": 392582274563,
- "value": 330,
- "distinct_rare_sats": 330,
- "year": "2009",
+ "starting_sat": 1407852856842623,
+ "value": 546,
+ "distinct_rare_sats": 0,
+ "year": "2015",
"subranges": [
{
- "starting_sat": 392582274563,
- "value": 330,
- "sat_types": ["block_78", "vintage"]
+ "starting_sat": 1407852856842623,
+ "value": 546,
+ "sat_types": ["common"]
}
]
}
],
- "block_number": 833711,
- "value": 330,
- "script_pub_key": {
- "asm": "",
- "desc": "",
- "hex": "",
- "address": "",
- "type": ""
- }
+ "block_number": 851367,
+ "value": 546
},
"image_original_url": null,
"animation_original_url": null,
@@ -1216,10 +2356,10 @@
}
},
{
- "nft_id": "utxo.68f11cb8770c6e761a5763bb47f00b368e03a4960046f441617c46edad2b0215.4",
+ "nft_id": "utxo.e75219cba8d15f60f0ac75a0e9a7890389018a0375d32a1d0c04d9292f88586f.417",
"chain": "utxo",
- "contract_address": "68f11cb8770c6e761a5763bb47f00b368e03a4960046f441617c46edad2b0215",
- "token_id": "4",
+ "contract_address": "e75219cba8d15f60f0ac75a0e9a7890389018a0375d32a1d0c04d9292f88586f",
+ "token_id": "417",
"name": null,
"description": null,
"previews": {
@@ -1242,17 +2382,17 @@
"other_properties": null,
"background_color": null,
"external_url": null,
- "created_date": "2024-03-08T09:47:36",
+ "created_date": "2024-07-09T07:31:41",
"status": "minted",
"token_count": 1,
"owner_count": 1,
"owners": [
{
- "owerner_address": "weneverknow",
+ "owner_address": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "first_acquired_date": "2024-03-08T09:47:36",
- "last_acquired_date": "2024-03-08T09:47:36"
+ "first_acquired_date": "2024-07-09T07:31:41",
+ "last_acquired_date": "2024-07-09T07:31:41"
}
],
"contract": {
@@ -1265,9 +2405,9 @@
"has_multiple_collections": false
},
"collection": {
- "collection_id": "0123456789abcdeffedcba9876543210",
- "name": "Rare Sats",
- "description": "Rare Sats are attributes, or \"satributes,\" ascribed to different types of sats. Sats are the smallest unit of a Bitcoin, and satributes commemorate special moments like when a sat was mined or used in a transaction.",
+ "collection_id": null,
+ "name": null,
+ "description": null,
"image_url": null,
"image_properties": null,
"banner_image_url": null,
@@ -1283,25 +2423,26 @@
"metaplex_mint": null,
"metaplex_candy_machine": null,
"metaplex_first_verified_creator": null,
+ "mpl_core_collection_address": null,
"spam_score": null,
"floor_prices": [],
"top_bids": [],
- "distinct_owner_count": 5346731,
- "distinct_nft_count": 7558913,
- "total_quantity": 7558900,
- "chains": ["utxo"],
+ "distinct_owner_count": null,
+ "distinct_nft_count": null,
+ "total_quantity": null,
+ "chains": [],
"top_contracts": [],
"collection_royalties": []
},
"last_sale": null,
"primary_sale": null,
"first_created": {
- "minted_to": "bc1pgtat0n2kavrz4ufhngm2muzxzx6pcmvr4czp089v48u5sgvpd9vqjsuaql",
+ "minted_to": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "timestamp": "2024-03-08T09:47:36",
- "block_number": 833711,
- "transaction": "",
+ "timestamp": "2024-07-09T07:31:41",
+ "block_number": 851367,
+ "transaction": "e75219cba8d15f60f0ac75a0e9a7890389018a0375d32a1d0c04d9292f88586f",
"transaction_initiator": null
},
"rarity": {
@@ -1313,39 +2454,32 @@
"extra_metadata": {
"attributes": [],
"utxo_details": {
- "distinct_rare_sats": 330,
+ "distinct_rare_sats": 0,
"satributes": {
- "vintage": {
- "count": 330,
- "display_name": "Vintage",
- "description": "Sats mined in the first 1,000 blocks",
- "icon": "https://cdn.simplehash.com/rare_sats/satribute_vintage.png"
+ "common": {
+ "count": 546,
+ "display_name": "Common",
+ "description": "Any sat that is not the first sat of its block",
+ "icon": "https://cdn.simplehash.com/rare_sats/satribute_common.png"
}
},
"sat_ranges": [
{
- "starting_sat": 4834087454144,
- "value": 330,
- "distinct_rare_sats": 330,
- "year": "2009",
+ "starting_sat": 1407852856842077,
+ "value": 546,
+ "distinct_rare_sats": 0,
+ "year": "2015",
"subranges": [
{
- "starting_sat": 4834087454144,
- "value": 330,
- "sat_types": ["vintage"]
+ "starting_sat": 1407852856842077,
+ "value": 546,
+ "sat_types": ["common"]
}
]
}
],
- "block_number": 833711,
- "value": 330,
- "script_pub_key": {
- "asm": "",
- "desc": "",
- "hex": "",
- "address": "",
- "type": ""
- }
+ "block_number": 851367,
+ "value": 546
},
"image_original_url": null,
"animation_original_url": null,
@@ -1353,10 +2487,10 @@
}
},
{
- "nft_id": "utxo.68f11cb8770c6e761a5763bb47f00b368e03a4960046f441617c46edad2b0215.3",
+ "nft_id": "utxo.f0f6b021af782a55cbbe912f4cbb1de3b101a0b123b6896c57da77768b648e19.405",
"chain": "utxo",
- "contract_address": "68f11cb8770c6e761a5763bb47f00b368e03a4960046f441617c46edad2b0215",
- "token_id": "3",
+ "contract_address": "f0f6b021af782a55cbbe912f4cbb1de3b101a0b123b6896c57da77768b648e19",
+ "token_id": "405",
"name": null,
"description": null,
"previews": {
@@ -1379,17 +2513,17 @@
"other_properties": null,
"background_color": null,
"external_url": null,
- "created_date": "2024-03-08T09:47:36",
+ "created_date": "2024-07-09T07:20:28",
"status": "minted",
"token_count": 1,
"owner_count": 1,
"owners": [
{
- "owerner_address": "weneverknow",
+ "owner_address": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "first_acquired_date": "2024-03-08T09:47:36",
- "last_acquired_date": "2024-03-08T09:47:36"
+ "first_acquired_date": "2024-07-09T07:20:28",
+ "last_acquired_date": "2024-07-09T07:20:28"
}
],
"contract": {
@@ -1402,9 +2536,9 @@
"has_multiple_collections": false
},
"collection": {
- "collection_id": "0123456789abcdeffedcba9876543210",
- "name": "Rare Sats",
- "description": "Rare Sats are attributes, or \"satributes,\" ascribed to different types of sats. Sats are the smallest unit of a Bitcoin, and satributes commemorate special moments like when a sat was mined or used in a transaction.",
+ "collection_id": null,
+ "name": null,
+ "description": null,
"image_url": null,
"image_properties": null,
"banner_image_url": null,
@@ -1420,25 +2554,26 @@
"metaplex_mint": null,
"metaplex_candy_machine": null,
"metaplex_first_verified_creator": null,
+ "mpl_core_collection_address": null,
"spam_score": null,
"floor_prices": [],
"top_bids": [],
- "distinct_owner_count": 5346731,
- "distinct_nft_count": 7558913,
- "total_quantity": 7558900,
- "chains": ["utxo"],
+ "distinct_owner_count": null,
+ "distinct_nft_count": null,
+ "total_quantity": null,
+ "chains": [],
"top_contracts": [],
"collection_royalties": []
},
"last_sale": null,
"primary_sale": null,
"first_created": {
- "minted_to": "bc1pgtat0n2kavrz4ufhngm2muzxzx6pcmvr4czp089v48u5sgvpd9vqjsuaql",
+ "minted_to": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "timestamp": "2024-03-08T09:47:36",
- "block_number": 833711,
- "transaction": "",
+ "timestamp": "2024-07-09T07:20:28",
+ "block_number": 851365,
+ "transaction": "f0f6b021af782a55cbbe912f4cbb1de3b101a0b123b6896c57da77768b648e19",
"transaction_initiator": null
},
"rarity": {
@@ -1450,39 +2585,163 @@
"extra_metadata": {
"attributes": [],
"utxo_details": {
- "distinct_rare_sats": 330,
+ "distinct_rare_sats": 0,
"satributes": {
- "pizza": {
- "count": 330,
- "display_name": "Pizza",
- "description": "Sat from the 10,000 Bitcoins used to purchase two Papa John's pizzas on May 22, 2010",
- "icon": "https://cdn.simplehash.com/rare_sats/satribute_pizza.png"
+ "common": {
+ "count": 546,
+ "display_name": "Common",
+ "description": "Any sat that is not the first sat of its block",
+ "icon": "https://cdn.simplehash.com/rare_sats/satribute_common.png"
}
},
"sat_ranges": [
{
- "starting_sat": 265675444186667,
- "value": 330,
- "distinct_rare_sats": 330,
+ "starting_sat": 1407852856127909,
+ "value": 546,
+ "distinct_rare_sats": 0,
+ "year": "2015",
+ "subranges": [
+ {
+ "starting_sat": 1407852856127909,
+ "value": 546,
+ "sat_types": ["common"]
+ }
+ ]
+ }
+ ],
+ "block_number": 851365,
+ "value": 546
+ },
+ "image_original_url": null,
+ "animation_original_url": null,
+ "metadata_original_url": null
+ }
+ },
+ {
+ "nft_id": "utxo.7ad820b4632c736e1d425d0b2685c1325033dd229d0d419b794d7a5f3f74b15e.868",
+ "chain": "utxo",
+ "contract_address": "7ad820b4632c736e1d425d0b2685c1325033dd229d0d419b794d7a5f3f74b15e",
+ "token_id": "868",
+ "name": null,
+ "description": null,
+ "previews": {
+ "image_small_url": null,
+ "image_medium_url": null,
+ "image_large_url": null,
+ "image_opengraph_url": null,
+ "blurhash": null,
+ "predominant_color": null
+ },
+ "image_url": null,
+ "image_properties": null,
+ "video_url": null,
+ "video_properties": null,
+ "audio_url": null,
+ "audio_properties": null,
+ "model_url": null,
+ "model_properties": null,
+ "other_url": null,
+ "other_properties": null,
+ "background_color": null,
+ "external_url": null,
+ "created_date": "2024-07-08T09:04:28",
+ "status": "minted",
+ "token_count": 1,
+ "owner_count": 1,
+ "owners": [
+ {
+ "owner_address": "weNeverKnow",
+ "quantity": 1,
+ "quantity_string": "1",
+ "first_acquired_date": "2024-07-08T09:04:28",
+ "last_acquired_date": "2024-07-08T09:04:28"
+ }
+ ],
+ "contract": {
+ "type": "UTXO",
+ "name": null,
+ "symbol": null,
+ "deployed_by": null,
+ "deployed_via_contract": null,
+ "owned_by": null,
+ "has_multiple_collections": false
+ },
+ "collection": {
+ "collection_id": null,
+ "name": null,
+ "description": null,
+ "image_url": null,
+ "image_properties": null,
+ "banner_image_url": null,
+ "category": null,
+ "is_nsfw": null,
+ "external_url": null,
+ "twitter_username": null,
+ "discord_url": null,
+ "instagram_username": null,
+ "medium_username": null,
+ "telegram_url": null,
+ "marketplace_pages": [],
+ "metaplex_mint": null,
+ "metaplex_candy_machine": null,
+ "metaplex_first_verified_creator": null,
+ "mpl_core_collection_address": null,
+ "spam_score": null,
+ "floor_prices": [],
+ "top_bids": [],
+ "distinct_owner_count": null,
+ "distinct_nft_count": null,
+ "total_quantity": null,
+ "chains": [],
+ "top_contracts": [],
+ "collection_royalties": []
+ },
+ "last_sale": null,
+ "primary_sale": null,
+ "first_created": {
+ "minted_to": "weNeverKnow",
+ "quantity": 1,
+ "quantity_string": "1",
+ "timestamp": "2024-07-08T09:04:28",
+ "block_number": 851229,
+ "transaction": "7ad820b4632c736e1d425d0b2685c1325033dd229d0d419b794d7a5f3f74b15e",
+ "transaction_initiator": null
+ },
+ "rarity": {
+ "rank": null,
+ "score": null,
+ "unique_attributes": null
+ },
+ "royalty": [],
+ "extra_metadata": {
+ "attributes": [],
+ "utxo_details": {
+ "distinct_rare_sats": 0,
+ "satributes": {
+ "common": {
+ "count": 546,
+ "display_name": "Common",
+ "description": "Any sat that is not the first sat of its block",
+ "icon": "https://cdn.simplehash.com/rare_sats/satribute_common.png"
+ }
+ },
+ "sat_ranges": [
+ {
+ "starting_sat": 397222941709103,
+ "value": 546,
+ "distinct_rare_sats": 0,
"year": "2010",
"subranges": [
{
- "starting_sat": 265675444186667,
- "value": 330,
- "sat_types": ["pizza"]
+ "starting_sat": 397222941709103,
+ "value": 546,
+ "sat_types": ["common"]
}
]
}
],
- "block_number": 833711,
- "value": 330,
- "script_pub_key": {
- "asm": "",
- "desc": "",
- "hex": "",
- "address": "",
- "type": ""
- }
+ "block_number": 851229,
+ "value": 546
},
"image_original_url": null,
"animation_original_url": null,
@@ -1490,10 +2749,10 @@
}
},
{
- "nft_id": "utxo.68f11cb8770c6e761a5763bb47f00b368e03a4960046f441617c46edad2b0215.2",
+ "nft_id": "utxo.7db3045a891175c48c322412248e1a3c344535586a51da88ddc8a184fefb9e2c.348",
"chain": "utxo",
- "contract_address": "68f11cb8770c6e761a5763bb47f00b368e03a4960046f441617c46edad2b0215",
- "token_id": "2",
+ "contract_address": "7db3045a891175c48c322412248e1a3c344535586a51da88ddc8a184fefb9e2c",
+ "token_id": "348",
"name": null,
"description": null,
"previews": {
@@ -1516,17 +2775,17 @@
"other_properties": null,
"background_color": null,
"external_url": null,
- "created_date": "2024-03-08T09:47:36",
+ "created_date": "2024-06-19T01:41:47",
"status": "minted",
"token_count": 1,
"owner_count": 1,
"owners": [
{
- "owerner_address": "weneverknow",
+ "owner_address": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "first_acquired_date": "2024-03-08T09:47:36",
- "last_acquired_date": "2024-03-08T09:47:36"
+ "first_acquired_date": "2024-06-19T01:41:47",
+ "last_acquired_date": "2024-06-19T01:41:47"
}
],
"contract": {
@@ -1539,9 +2798,9 @@
"has_multiple_collections": false
},
"collection": {
- "collection_id": "0123456789abcdeffedcba9876543210",
- "name": "Rare Sats",
- "description": "Rare Sats are attributes, or \"satributes,\" ascribed to different types of sats. Sats are the smallest unit of a Bitcoin, and satributes commemorate special moments like when a sat was mined or used in a transaction.",
+ "collection_id": null,
+ "name": null,
+ "description": null,
"image_url": null,
"image_properties": null,
"banner_image_url": null,
@@ -1557,25 +2816,26 @@
"metaplex_mint": null,
"metaplex_candy_machine": null,
"metaplex_first_verified_creator": null,
+ "mpl_core_collection_address": null,
"spam_score": null,
"floor_prices": [],
"top_bids": [],
- "distinct_owner_count": 5346731,
- "distinct_nft_count": 7558913,
- "total_quantity": 7558900,
- "chains": ["utxo"],
+ "distinct_owner_count": null,
+ "distinct_nft_count": null,
+ "total_quantity": null,
+ "chains": [],
"top_contracts": [],
"collection_royalties": []
},
"last_sale": null,
"primary_sale": null,
"first_created": {
- "minted_to": "bc1pgtat0n2kavrz4ufhngm2muzxzx6pcmvr4czp089v48u5sgvpd9vqjsuaql",
+ "minted_to": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "timestamp": "2024-03-08T09:47:36",
- "block_number": 833711,
- "transaction": "",
+ "timestamp": "2024-06-19T01:41:47",
+ "block_number": 848555,
+ "transaction": "7db3045a891175c48c322412248e1a3c344535586a51da88ddc8a184fefb9e2c",
"transaction_initiator": null
},
"rarity": {
@@ -1587,39 +2847,32 @@
"extra_metadata": {
"attributes": [],
"utxo_details": {
- "distinct_rare_sats": 547,
+ "distinct_rare_sats": 0,
"satributes": {
- "jpeg": {
- "count": 547,
- "display_name": "JPEG",
- "description": "Sats involved in the possible first bitcoin trade for an image on February 24, 2010",
- "icon": "https://cdn.simplehash.com/rare_sats/satribute_jpeg.png"
+ "common": {
+ "count": 330,
+ "display_name": "Common",
+ "description": "Any sat that is not the first sat of its block",
+ "icon": "https://cdn.simplehash.com/rare_sats/satribute_common.png"
}
},
"sat_ranges": [
{
- "starting_sat": 152060443363905,
- "value": 547,
- "distinct_rare_sats": 547,
- "year": "2009",
+ "starting_sat": 1315430465973172,
+ "value": 330,
+ "distinct_rare_sats": 0,
+ "year": "2014",
"subranges": [
{
- "starting_sat": 152060443363905,
- "value": 547,
- "sat_types": ["jpeg"]
+ "starting_sat": 1315430465973172,
+ "value": 330,
+ "sat_types": ["common"]
}
]
}
],
- "block_number": 833711,
- "value": 547,
- "script_pub_key": {
- "asm": "",
- "desc": "",
- "hex": "",
- "address": "",
- "type": ""
- }
+ "block_number": 848555,
+ "value": 330
},
"image_original_url": null,
"animation_original_url": null,
@@ -1627,10 +2880,10 @@
}
},
{
- "nft_id": "utxo.68f11cb8770c6e761a5763bb47f00b368e03a4960046f441617c46edad2b0215.1",
+ "nft_id": "utxo.2fb06a7d9b5a4272eb069d5f4dc1d106b28c5c3e270058fdeb359078b35decb6.3",
"chain": "utxo",
- "contract_address": "68f11cb8770c6e761a5763bb47f00b368e03a4960046f441617c46edad2b0215",
- "token_id": "1",
+ "contract_address": "2fb06a7d9b5a4272eb069d5f4dc1d106b28c5c3e270058fdeb359078b35decb6",
+ "token_id": "3",
"name": null,
"description": null,
"previews": {
@@ -1653,17 +2906,17 @@
"other_properties": null,
"background_color": null,
"external_url": null,
- "created_date": "2024-03-08T09:47:36",
+ "created_date": "2024-05-01T00:46:24",
"status": "minted",
"token_count": 1,
"owner_count": 1,
"owners": [
{
- "owerner_address": "weneverknow",
+ "owner_address": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "first_acquired_date": "2024-03-08T09:47:36",
- "last_acquired_date": "2024-03-08T09:47:36"
+ "first_acquired_date": "2024-05-01T00:46:24",
+ "last_acquired_date": "2024-05-01T00:46:24"
}
],
"contract": {
@@ -1676,9 +2929,9 @@
"has_multiple_collections": false
},
"collection": {
- "collection_id": "0123456789abcdeffedcba9876543210",
- "name": "Rare Sats",
- "description": "Rare Sats are attributes, or \"satributes,\" ascribed to different types of sats. Sats are the smallest unit of a Bitcoin, and satributes commemorate special moments like when a sat was mined or used in a transaction.",
+ "collection_id": null,
+ "name": null,
+ "description": null,
"image_url": null,
"image_properties": null,
"banner_image_url": null,
@@ -1694,25 +2947,26 @@
"metaplex_mint": null,
"metaplex_candy_machine": null,
"metaplex_first_verified_creator": null,
+ "mpl_core_collection_address": null,
"spam_score": null,
"floor_prices": [],
"top_bids": [],
- "distinct_owner_count": 5346731,
- "distinct_nft_count": 7558913,
- "total_quantity": 7558900,
- "chains": ["utxo"],
+ "distinct_owner_count": null,
+ "distinct_nft_count": null,
+ "total_quantity": null,
+ "chains": [],
"top_contracts": [],
"collection_royalties": []
},
"last_sale": null,
"primary_sale": null,
"first_created": {
- "minted_to": "bc1pgtat0n2kavrz4ufhngm2muzxzx6pcmvr4czp089v48u5sgvpd9vqjsuaql",
+ "minted_to": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "timestamp": "2024-03-08T09:47:36",
- "block_number": 833711,
- "transaction": "",
+ "timestamp": "2024-05-01T00:46:24",
+ "block_number": 841575,
+ "transaction": "2fb06a7d9b5a4272eb069d5f4dc1d106b28c5c3e270058fdeb359078b35decb6",
"transaction_initiator": null
},
"rarity": {
@@ -1724,39 +2978,32 @@
"extra_metadata": {
"attributes": [],
"utxo_details": {
- "distinct_rare_sats": 555,
+ "distinct_rare_sats": 0,
"satributes": {
- "nakamoto": {
- "count": 555,
- "display_name": "Nakamoto",
- "description": "Sat mined by Satoshi Nakamoto",
- "icon": "https://cdn.simplehash.com/rare_sats/satribute_nakamoto.png"
+ "common": {
+ "count": 546,
+ "display_name": "Common",
+ "description": "Any sat that is not the first sat of its block",
+ "icon": "https://cdn.simplehash.com/rare_sats/satribute_common.png"
}
},
"sat_ranges": [
{
- "starting_sat": 95469010861290,
- "value": 555,
- "distinct_rare_sats": 555,
- "year": "2009",
+ "starting_sat": 1936011249986600,
+ "value": 546,
+ "distinct_rare_sats": 0,
+ "year": "2023",
"subranges": [
{
- "starting_sat": 95469010861290,
- "value": 555,
- "sat_types": ["nakamoto"]
+ "starting_sat": 1936011249986600,
+ "value": 546,
+ "sat_types": ["common"]
}
]
}
],
- "block_number": 833711,
- "value": 555,
- "script_pub_key": {
- "asm": "",
- "desc": "",
- "hex": "",
- "address": "",
- "type": ""
- }
+ "block_number": 841575,
+ "value": 546
},
"image_original_url": null,
"animation_original_url": null,
@@ -1764,10 +3011,10 @@
}
},
{
- "nft_id": "utxo.ce4b148f42e96e30110e666952aeeff6b66a491f7534066076197ff04030318c.1",
+ "nft_id": "utxo.9aac9d569b3020ab4943d8027a956acc541ce9fd7e49753c7934d42f5f7531c8.9",
"chain": "utxo",
- "contract_address": "ce4b148f42e96e30110e666952aeeff6b66a491f7534066076197ff04030318c",
- "token_id": "1",
+ "contract_address": "9aac9d569b3020ab4943d8027a956acc541ce9fd7e49753c7934d42f5f7531c8",
+ "token_id": "9",
"name": null,
"description": null,
"previews": {
@@ -1790,17 +3037,17 @@
"other_properties": null,
"background_color": null,
"external_url": null,
- "created_date": "2023-12-11T22:53:55",
+ "created_date": "2024-04-22T16:11:59",
"status": "minted",
"token_count": 1,
"owner_count": 1,
"owners": [
{
- "owerner_address": "weneverknow",
+ "owner_address": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "first_acquired_date": "2023-12-11T22:53:55",
- "last_acquired_date": "2023-12-11T22:53:55"
+ "first_acquired_date": "2024-04-22T16:11:59",
+ "last_acquired_date": "2024-04-22T16:11:59"
}
],
"contract": {
@@ -1813,9 +3060,9 @@
"has_multiple_collections": false
},
"collection": {
- "collection_id": "0123456789abcdeffedcba9876543210",
- "name": "Rare Sats",
- "description": "Rare Sats are attributes, or \"satributes,\" ascribed to different types of sats. Sats are the smallest unit of a Bitcoin, and satributes commemorate special moments like when a sat was mined or used in a transaction.",
+ "collection_id": null,
+ "name": null,
+ "description": null,
"image_url": null,
"image_properties": null,
"banner_image_url": null,
@@ -1831,25 +3078,26 @@
"metaplex_mint": null,
"metaplex_candy_machine": null,
"metaplex_first_verified_creator": null,
+ "mpl_core_collection_address": null,
"spam_score": null,
"floor_prices": [],
"top_bids": [],
- "distinct_owner_count": 5346731,
- "distinct_nft_count": 7558913,
- "total_quantity": 7558900,
- "chains": ["utxo"],
+ "distinct_owner_count": null,
+ "distinct_nft_count": null,
+ "total_quantity": null,
+ "chains": [],
"top_contracts": [],
"collection_royalties": []
},
"last_sale": null,
"primary_sale": null,
"first_created": {
- "minted_to": "bc1pgtat0n2kavrz4ufhngm2muzxzx6pcmvr4czp089v48u5sgvpd9vqjsuaql",
+ "minted_to": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "timestamp": "2023-12-11T22:53:55",
- "block_number": 820769,
- "transaction": "",
+ "timestamp": "2024-04-22T16:11:59",
+ "block_number": 840363,
+ "transaction": "9aac9d569b3020ab4943d8027a956acc541ce9fd7e49753c7934d42f5f7531c8",
"transaction_initiator": null
},
"rarity": {
@@ -1861,39 +3109,32 @@
"extra_metadata": {
"attributes": [],
"utxo_details": {
- "distinct_rare_sats": 835,
+ "distinct_rare_sats": 0,
"satributes": {
- "pizza": {
- "count": 835,
- "display_name": "Pizza",
- "description": "Sat from the 10,000 Bitcoins used to purchase two Papa John's pizzas on May 22, 2010",
- "icon": "https://cdn.simplehash.com/rare_sats/satribute_pizza.png"
+ "common": {
+ "count": 569,
+ "display_name": "Common",
+ "description": "Any sat that is not the first sat of its block",
+ "icon": "https://cdn.simplehash.com/rare_sats/satribute_common.png"
}
},
"sat_ranges": [
{
- "starting_sat": 157020560807939,
- "value": 835,
- "distinct_rare_sats": 835,
- "year": "2009",
+ "starting_sat": 862882403746026,
+ "value": 569,
+ "distinct_rare_sats": 0,
+ "year": "2012",
"subranges": [
{
- "starting_sat": 157020560807939,
- "value": 835,
- "sat_types": ["pizza"]
+ "starting_sat": 862882403746026,
+ "value": 569,
+ "sat_types": ["common"]
}
]
}
],
- "block_number": 820769,
- "value": 835,
- "script_pub_key": {
- "asm": "",
- "desc": "",
- "hex": "",
- "address": "",
- "type": ""
- }
+ "block_number": 840363,
+ "value": 569
},
"image_original_url": null,
"animation_original_url": null,
@@ -1901,10 +3142,10 @@
}
},
{
- "nft_id": "utxo.adca6501891f2cfce159802f6a2fd798460b004924c5aa5c3352cec4dafa30ba.1",
+ "nft_id": "utxo.baac1c3ae497b1589ee1794475439adae9cbec7acd2490ba8812e5d2a7313136.1067",
"chain": "utxo",
- "contract_address": "adca6501891f2cfce159802f6a2fd798460b004924c5aa5c3352cec4dafa30ba",
- "token_id": "1",
+ "contract_address": "baac1c3ae497b1589ee1794475439adae9cbec7acd2490ba8812e5d2a7313136",
+ "token_id": "1067",
"name": null,
"description": null,
"previews": {
@@ -1927,17 +3168,17 @@
"other_properties": null,
"background_color": null,
"external_url": null,
- "created_date": "2023-12-11T15:39:00",
+ "created_date": "2024-04-22T12:07:00",
"status": "minted",
"token_count": 1,
"owner_count": 1,
"owners": [
{
- "owerner_address": "weneverknow",
+ "owner_address": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "first_acquired_date": "2023-12-11T15:39:00",
- "last_acquired_date": "2023-12-11T15:39:00"
+ "first_acquired_date": "2024-04-22T12:07:00",
+ "last_acquired_date": "2024-04-22T12:07:00"
}
],
"contract": {
@@ -1950,9 +3191,9 @@
"has_multiple_collections": false
},
"collection": {
- "collection_id": "0123456789abcdeffedcba9876543210",
- "name": "Rare Sats",
- "description": "Rare Sats are attributes, or \"satributes,\" ascribed to different types of sats. Sats are the smallest unit of a Bitcoin, and satributes commemorate special moments like when a sat was mined or used in a transaction.",
+ "collection_id": null,
+ "name": null,
+ "description": null,
"image_url": null,
"image_properties": null,
"banner_image_url": null,
@@ -1968,25 +3209,26 @@
"metaplex_mint": null,
"metaplex_candy_machine": null,
"metaplex_first_verified_creator": null,
+ "mpl_core_collection_address": null,
"spam_score": null,
"floor_prices": [],
"top_bids": [],
- "distinct_owner_count": 5346731,
- "distinct_nft_count": 7558913,
- "total_quantity": 7558900,
- "chains": ["utxo"],
+ "distinct_owner_count": null,
+ "distinct_nft_count": null,
+ "total_quantity": null,
+ "chains": [],
"top_contracts": [],
"collection_royalties": []
},
"last_sale": null,
"primary_sale": null,
"first_created": {
- "minted_to": "bc1pgtat0n2kavrz4ufhngm2muzxzx6pcmvr4czp089v48u5sgvpd9vqjsuaql",
+ "minted_to": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "timestamp": "2023-12-11T15:39:00",
- "block_number": 820722,
- "transaction": "",
+ "timestamp": "2024-04-22T12:07:00",
+ "block_number": 840338,
+ "transaction": "baac1c3ae497b1589ee1794475439adae9cbec7acd2490ba8812e5d2a7313136",
"transaction_initiator": null
},
"rarity": {
@@ -1998,34 +3240,10 @@
"extra_metadata": {
"attributes": [],
"utxo_details": {
- "distinct_rare_sats": 1,
+ "distinct_rare_sats": 0,
"satributes": {
- "block_9": {
- "count": 1,
- "display_name": "Block 9",
- "description": "Sats mined in block 9 which are the oldest sats in circulation",
- "icon": "https://cdn.simplehash.com/rare_sats/satribute_block9.png"
- },
- "first_tx": {
- "count": 1,
- "display_name": "First Transaction",
- "description": "Sat from the 10 Bitcoins sent from Satashi Nakamoto to Hal Finney in the first Bitcoin transaction ever on January 12, 2009",
- "icon": "https://cdn.simplehash.com/rare_sats/satribute_first_transaction.png"
- },
- "nakamoto": {
- "count": 1,
- "display_name": "Nakamoto",
- "description": "Sat mined by Satoshi Nakamoto",
- "icon": "https://cdn.simplehash.com/rare_sats/satribute_nakamoto.png"
- },
- "vintage": {
- "count": 1,
- "display_name": "Vintage",
- "description": "Sats mined in the first 1,000 blocks",
- "icon": "https://cdn.simplehash.com/rare_sats/satribute_vintage.png"
- },
"common": {
- "count": 330,
+ "count": 888,
"display_name": "Common",
"description": "Any sat that is not the first sat of its block",
"icon": "https://cdn.simplehash.com/rare_sats/satribute_common.png"
@@ -2033,41 +3251,21 @@
},
"sat_ranges": [
{
- "starting_sat": 45558858287,
- "value": 1,
- "distinct_rare_sats": 1,
- "year": "2009",
- "subranges": [
- {
- "starting_sat": 45558858287,
- "value": 1,
- "sat_types": ["block_9", "first_tx", "nakamoto", "vintage"]
- }
- ]
- },
- {
- "starting_sat": 640648318319738,
- "value": 330,
+ "starting_sat": 1247871895113823,
+ "value": 888,
"distinct_rare_sats": 0,
- "year": "2011",
+ "year": "2014",
"subranges": [
{
- "starting_sat": 640648318319738,
- "value": 330,
+ "starting_sat": 1247871895113823,
+ "value": 888,
"sat_types": ["common"]
}
]
}
],
- "block_number": 820722,
- "value": 331,
- "script_pub_key": {
- "asm": "",
- "desc": "",
- "hex": "",
- "address": "",
- "type": ""
- }
+ "block_number": 840338,
+ "value": 888
},
"image_original_url": null,
"animation_original_url": null,
@@ -2075,10 +3273,10 @@
}
},
{
- "nft_id": "utxo.5e9b99b09f1f259bf2d0afda1ee819abca2de094f8c58c56dd5a5b754f766204.1",
+ "nft_id": "utxo.2c9d54d4c3c5851162aa9bd3efbb67701a72e2dc912790f6263791171d11d58e.0",
"chain": "utxo",
- "contract_address": "5e9b99b09f1f259bf2d0afda1ee819abca2de094f8c58c56dd5a5b754f766204",
- "token_id": "1",
+ "contract_address": "2c9d54d4c3c5851162aa9bd3efbb67701a72e2dc912790f6263791171d11d58e",
+ "token_id": "0",
"name": null,
"description": null,
"previews": {
@@ -2101,17 +3299,17 @@
"other_properties": null,
"background_color": null,
"external_url": null,
- "created_date": "2023-12-11T15:00:05",
+ "created_date": "2024-04-13T18:53:35",
"status": "minted",
"token_count": 1,
"owner_count": 1,
"owners": [
{
- "owerner_address": "weneverknow",
+ "owner_address": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "first_acquired_date": "2023-12-11T15:00:05",
- "last_acquired_date": "2023-12-11T15:00:05"
+ "first_acquired_date": "2024-04-13T18:53:35",
+ "last_acquired_date": "2024-04-13T18:53:35"
}
],
"contract": {
@@ -2124,9 +3322,9 @@
"has_multiple_collections": false
},
"collection": {
- "collection_id": null,
- "name": null,
- "description": null,
+ "collection_id": "0123456789abcdeffedcba9876543210",
+ "name": "Rare Sats",
+ "description": "Rare Sats are attributes, or \"satributes,\" ascribed to different types of sats. Sats are the smallest unit of a Bitcoin, and satributes commemorate special moments like when a sat was mined or used in a transaction.",
"image_url": null,
"image_properties": null,
"banner_image_url": null,
@@ -2142,25 +3340,26 @@
"metaplex_mint": null,
"metaplex_candy_machine": null,
"metaplex_first_verified_creator": null,
+ "mpl_core_collection_address": null,
"spam_score": null,
"floor_prices": [],
"top_bids": [],
- "distinct_owner_count": null,
- "distinct_nft_count": null,
- "total_quantity": null,
- "chains": [],
+ "distinct_owner_count": 5342069,
+ "distinct_nft_count": 7562897,
+ "total_quantity": 7562869,
+ "chains": ["utxo"],
"top_contracts": [],
"collection_royalties": []
},
"last_sale": null,
"primary_sale": null,
"first_created": {
- "minted_to": "bc1pgtat0n2kavrz4ufhngm2muzxzx6pcmvr4czp089v48u5sgvpd9vqjsuaql",
+ "minted_to": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "timestamp": "2023-12-11T15:00:05",
- "block_number": 820718,
- "transaction": "",
+ "timestamp": "2024-04-13T18:53:35",
+ "block_number": 839067,
+ "transaction": "2c9d54d4c3c5851162aa9bd3efbb67701a72e2dc912790f6263791171d11d58e",
"transaction_initiator": null
},
"rarity": {
@@ -2172,52 +3371,32 @@
"extra_metadata": {
"attributes": [],
"utxo_details": {
- "distinct_rare_sats": 0,
+ "distinct_rare_sats": 10000,
"satributes": {
- "common": {
+ "pizza": {
"count": 10000,
- "display_name": "Common",
- "description": "Any sat that is not the first sat of its block",
- "icon": "https://cdn.simplehash.com/rare_sats/satribute_common.png"
+ "display_name": "Pizza",
+ "description": "Sat from the 10,000 Bitcoins used to purchase two Papa John's pizzas on May 22, 2010",
+ "icon": "https://cdn.simplehash.com/rare_sats/satribute_pizza.png"
}
},
"sat_ranges": [
{
- "starting_sat": 926537729411206,
- "value": 546,
- "distinct_rare_sats": 0,
- "year": "2012",
- "subranges": [
- {
- "starting_sat": 926537729411206,
- "value": 546,
- "sat_types": ["common"]
- }
- ]
- },
- {
- "starting_sat": 1612575222953082,
- "value": 9454,
- "distinct_rare_sats": 0,
- "year": "2017",
+ "starting_sat": 280943728804176,
+ "value": 10000,
+ "distinct_rare_sats": 10000,
+ "year": "2010",
"subranges": [
{
- "starting_sat": 1612575222953082,
- "value": 9454,
- "sat_types": ["common"]
+ "starting_sat": 280943728804176,
+ "value": 10000,
+ "sat_types": ["pizza"]
}
]
}
],
- "block_number": 820718,
- "value": 10000,
- "script_pub_key": {
- "asm": "",
- "desc": "",
- "hex": "",
- "address": "",
- "type": ""
- }
+ "block_number": 839067,
+ "value": 10000
},
"image_original_url": null,
"animation_original_url": null,
@@ -2225,25 +3404,25 @@
}
},
{
- "nft_id": "bitcoin.e5757a74ba8121f7f81c0c568371e29d42235943a0f0c60ce7a59d9b104e7d67i0",
+ "nft_id": "bitcoin.44758bf08f91e23917596469969c5bb29d2778598468c135374d34583c273698i0",
"chain": "bitcoin",
- "contract_address": "e5757a74ba8121f7f81c0c568371e29d42235943a0f0c60ce7a59d9b104e7d67i0",
+ "contract_address": "44758bf08f91e23917596469969c5bb29d2778598468c135374d34583c273698i0",
"token_id": null,
- "name": "Bitcoin Flower",
+ "name": "Bitcoin Puppet #71",
"description": null,
"previews": {
- "image_small_url": "https://lh3.googleusercontent.com/l1i9mrMjxrIs57l2EfawBJde67CYdNQFojn8dC2jyJSIW73J0pR4uv7GP9CCbGvgalsLWyI-KD1xNgfaVPFIGiYX71oMxqq9kx8=s250",
- "image_medium_url": "https://lh3.googleusercontent.com/l1i9mrMjxrIs57l2EfawBJde67CYdNQFojn8dC2jyJSIW73J0pR4uv7GP9CCbGvgalsLWyI-KD1xNgfaVPFIGiYX71oMxqq9kx8",
- "image_large_url": "https://lh3.googleusercontent.com/l1i9mrMjxrIs57l2EfawBJde67CYdNQFojn8dC2jyJSIW73J0pR4uv7GP9CCbGvgalsLWyI-KD1xNgfaVPFIGiYX71oMxqq9kx8=s1000",
- "image_opengraph_url": "https://lh3.googleusercontent.com/l1i9mrMjxrIs57l2EfawBJde67CYdNQFojn8dC2jyJSIW73J0pR4uv7GP9CCbGvgalsLWyI-KD1xNgfaVPFIGiYX71oMxqq9kx8=k-w1200-s2400-rj",
- "blurhash": "U6JGg5WC}[$*#JWUO$sq-Tj?9[WUTBjunBj[",
- "predominant_color": "#ab8483"
- },
- "image_url": "https://cdn.simplehash.com/assets/c5def939b99b6eacd018b7bf25ffdd52dd228a7aef0770796306251c84f69460.webp",
+ "image_small_url": "https://lh3.googleusercontent.com/JqpTW8X2eNlaHdsdHixez51YDpb37Knh53pnlXiepluyMzjpikT36wosl4E99SuDQiWstYVE3VVd96G0_0Ii5BsObpZm84xqxA=s250",
+ "image_medium_url": "https://lh3.googleusercontent.com/JqpTW8X2eNlaHdsdHixez51YDpb37Knh53pnlXiepluyMzjpikT36wosl4E99SuDQiWstYVE3VVd96G0_0Ii5BsObpZm84xqxA",
+ "image_large_url": "https://lh3.googleusercontent.com/JqpTW8X2eNlaHdsdHixez51YDpb37Knh53pnlXiepluyMzjpikT36wosl4E99SuDQiWstYVE3VVd96G0_0Ii5BsObpZm84xqxA=s1000",
+ "image_opengraph_url": "https://lh3.googleusercontent.com/JqpTW8X2eNlaHdsdHixez51YDpb37Knh53pnlXiepluyMzjpikT36wosl4E99SuDQiWstYVE3VVd96G0_0Ii5BsObpZm84xqxA=k-w1200-s2400-rj",
+ "blurhash": "UNGad}$*9}bu=gsoNZS29bWo%1jG0|SK-Aso",
+ "predominant_color": "#675352"
+ },
+ "image_url": "https://cdn.simplehash.com/assets/fafa2597d6d850658e9fdf61ca5353ebf48062081ef50108bd2547861b75658c.webp",
"image_properties": {
- "width": 48,
- "height": 48,
- "size": 362,
+ "width": 256,
+ "height": 256,
+ "size": 2892,
"mime_type": "image/webp",
"exif_orientation": null
},
@@ -2257,22 +3436,22 @@
"other_properties": null,
"background_color": null,
"external_url": null,
- "created_date": "2023-06-11T00:40:10",
+ "created_date": "2024-01-04T08:42:53",
"status": "minted",
"token_count": 1,
"owner_count": 1,
"owners": [
{
- "owerner_address": "weneverknow",
+ "owner_address": "weNeverKnow",
"quantity": 1,
"quantity_string": "1",
- "first_acquired_date": "2023-12-11T15:00:05",
- "last_acquired_date": "2023-12-11T15:00:05"
+ "first_acquired_date": "2024-03-01T14:42:17",
+ "last_acquired_date": "2024-04-13T18:53:35"
}
],
"contract": {
"type": "ORDINALS",
- "name": "Inscription #11382871",
+ "name": "Inscription #53161134",
"symbol": null,
"deployed_by": null,
"deployed_via_contract": null,
@@ -2280,21 +3459,21 @@
"has_multiple_collections": false
},
"collection": {
- "collection_id": "a7a586797ba73659efcbd802e35a2cf3",
- "name": "Bitcoin Flowers",
- "description": "Bitcoin Flowers are an integrated part of the Bitcoin Bees ecosystem.Collect the flowers and make your own taproot garden, the Bees will love it..",
- "image_url": "https://lh3.googleusercontent.com/iNZ7hNXb9e5efu4kcTuTz2bjyo3xMflPJLbTW_q45HDBB3KJ2D__VuVMf0FMT1aFwb1VtaEloOpecw-QZqwbJmlgLSWG3liFuuk",
+ "collection_id": "043a8ba6ee41ccac8e8c34245416b1f0",
+ "name": "Bitcoin Puppets",
+ "description": "When you engage with the Puppets, leave behind the shackles of conventional thinking and surrender to the enchantment of the unconventional. Embrace the whimsy, relish in the absurd, and allow your imagination to roam freely. In this realm of handcrafted wonders, there are no guarantees, no prescribed paths to follow. It is a sanctuary for the offbeat and a testament to the limitless possibilities of artistic expression.",
+ "image_url": "https://lh3.googleusercontent.com/UzAORCwx179DqX_eWlxAfBncSfChn1njznY8iCZMAG1IjXPvLuLRLBst1hx_916vORYfKxl6HLXwo2dJ0RP6t1U3wfHVgM-nTTU",
"image_properties": {
- "width": 500,
- "height": 500,
+ "width": 512,
+ "height": 512,
"mime_type": "image/png"
},
"banner_image_url": null,
"category": null,
"is_nsfw": null,
- "external_url": null,
- "twitter_username": "OrdinalBees",
- "discord_url": "https://discord.gg/9aX49Kbc5A",
+ "external_url": "https://ordpuppetinuundoxxedmillionaires.com/",
+ "twitter_username": "lepuppeteerfou",
+ "discord_url": "https://discord.gg/F2BBWn2mzP",
"instagram_username": null,
"medium_username": null,
"telegram_url": null,
@@ -2302,21 +3481,22 @@
{
"marketplace_id": "magiceden",
"marketplace_name": "Magic Eden",
- "marketplace_collection_id": "bitcoin-flowers",
- "nft_url": "https://magiceden.io/ordinals/item-details/e5757a74ba8121f7f81c0c568371e29d42235943a0f0c60ce7a59d9b104e7d67i0",
- "collection_url": "https://magiceden.io/ordinals/marketplace/bitcoin-flowers",
+ "marketplace_collection_id": "bitcoin-puppets",
+ "nft_url": "https://magiceden.io/ordinals/item-details/44758bf08f91e23917596469969c5bb29d2778598468c135374d34583c273698i0",
+ "collection_url": "https://magiceden.io/ordinals/marketplace/bitcoin-puppets",
"verified": null
}
],
"metaplex_mint": null,
"metaplex_candy_machine": null,
"metaplex_first_verified_creator": null,
+ "mpl_core_collection_address": null,
"spam_score": 0,
"floor_prices": [
{
"marketplace_id": "magiceden",
"marketplace_name": "Magic Eden",
- "value": 8800,
+ "value": 11688000,
"payment_token": {
"payment_token_id": "bitcoin.native",
"name": "Bitcoin",
@@ -2324,25 +3504,12 @@
"address": null,
"decimals": 8
},
- "value_usd_cents": 512
+ "value_usd_cents": 739734
},
{
"marketplace_id": "okx",
"marketplace_name": "OKX",
- "value": 17000,
- "payment_token": {
- "payment_token_id": "bitcoin.native",
- "name": "Bitcoin",
- "symbol": "BTC",
- "address": null,
- "decimals": 8
- },
- "value_usd_cents": 989
- },
- {
- "marketplace_id": "unisat",
- "marketplace_name": "UniSat",
- "value": 50000,
+ "value": 13999000,
"payment_token": {
"payment_token_id": "bitcoin.native",
"name": "Bitcoin",
@@ -2350,24 +3517,24 @@
"address": null,
"decimals": 8
},
- "value_usd_cents": 2909
+ "value_usd_cents": 885997
}
],
"top_bids": [],
- "distinct_owner_count": 2922,
- "distinct_nft_count": 10000,
- "total_quantity": 10000,
+ "distinct_owner_count": 6616,
+ "distinct_nft_count": 10001,
+ "total_quantity": 10001,
"chains": ["bitcoin"],
"top_contracts": [],
"collection_royalties": []
},
"last_sale": {
- "from_address": "bc1p8dd094pp8gepf5kp82qgkgw4hq06yly29u7dr7g0xwn4k0muudgqljurum",
- "to_address": "bc1pgtat0n2kavrz4ufhngm2muzxzx6pcmvr4czp089v48u5sgvpd9vqjsuaql",
+ "from_address": "bc1pl4gzlx8w372nvymy9pqqtssl6tjglt9rx4566kae2tgpq2qu5wwqs9sgpx",
+ "to_address": "bc1pf4q9l8u6f9gd04l96ppw3v4wejne2nra76amwe035fsk4ul7vleqxerkpl",
"quantity": 1,
"quantity_string": "1",
- "timestamp": "2023-12-11T15:00:05",
- "transaction": "",
+ "timestamp": "2024-02-26T13:53:50",
+ "transaction": "9a93677fb0fdac40968dd6f02f38d09bf768deae71869dd93a26ed3ee94573be",
"marketplace_id": "magiceden",
"marketplace_name": "Magic Eden",
"is_bundle_sale": false,
@@ -2378,195 +3545,73 @@
"address": null,
"decimals": 8
},
- "unit_price": 8500,
- "total_price": 8500,
- "unit_price_usd_cents": 356
+ "unit_price": 36900000,
+ "total_price": 36900000,
+ "unit_price_usd_cents": 1889167
},
"primary_sale": null,
"first_created": {
- "minted_to": "bc1pwfvcyx75cxahelslz8jk5awc8hkptdgwlq2zu9g804q48h44h3pss2v5rn",
+ "minted_to": "bc1pyk5qytzl9afgxx0z3rpfmk3tsah82kqqu9a0aeuck28hyq2kwcus65nfj3",
"quantity": 1,
"quantity_string": "1",
- "timestamp": "2023-06-11T00:40:10",
- "block_number": 793795,
- "transaction": "",
+ "timestamp": "2024-01-04T08:42:53",
+ "block_number": 824282,
+ "transaction": "44758bf08f91e23917596469969c5bb29d2778598468c135374d34583c273698",
"transaction_initiator": null
},
"rarity": {
- "rank": null,
- "score": null,
- "unique_attributes": null
+ "rank": 2760,
+ "score": 1.033,
+ "unique_attributes": 0
},
"royalty": [],
"extra_metadata": {
- "attributes": [],
- "ordinal_details": {
- "inscription_id": "e5757a74ba8121f7f81c0c568371e29d42235943a0f0c60ce7a59d9b104e7d67i0",
- "inscription_number": 11382871,
- "content_length": 362,
- "content_type": "image/webp",
- "sat_number": 926537729411206,
- "sat_name": "hhcguwtfjlz",
- "sat_rarity": "common",
- "protocol_name": null,
- "protocol_content": null,
- "location": "5e9b99b09f1f259bf2d0afda1ee819abca2de094f8c58c56dd5a5b754f766204:1:0",
- "output_value": 10000,
- "parents": null,
- "charms": null
- },
- "image_original_url": "https://ordinals.simplehash.com/content/e5757a74ba8121f7f81c0c568371e29d42235943a0f0c60ce7a59d9b104e7d67i0",
- "animation_original_url": null,
- "metadata_original_url": null
- }
- },
- {
- "nft_id": "bitcoin.5ef46a44ee9777f77670940cadd760df3be6583b4724d50a923bde0d19317894i0",
- "chain": "bitcoin",
- "contract_address": "5ef46a44ee9777f77670940cadd760df3be6583b4724d50a923bde0d19317894i0",
- "token_id": null,
- "name": "Timechain #136",
- "description": null,
- "previews": {
- "image_small_url": "https://lh3.googleusercontent.com/bEJ7jNgQqze5h93mq_EpSVQKr6YLOpQDEjn013rXaH9bAiQvvHMV9FiLh4I6oo9_OydqImDDULqT0PExTGappBo2Mvr6oBDJZ3Me=s250",
- "image_medium_url": "https://lh3.googleusercontent.com/bEJ7jNgQqze5h93mq_EpSVQKr6YLOpQDEjn013rXaH9bAiQvvHMV9FiLh4I6oo9_OydqImDDULqT0PExTGappBo2Mvr6oBDJZ3Me",
- "image_large_url": "https://lh3.googleusercontent.com/bEJ7jNgQqze5h93mq_EpSVQKr6YLOpQDEjn013rXaH9bAiQvvHMV9FiLh4I6oo9_OydqImDDULqT0PExTGappBo2Mvr6oBDJZ3Me=s1000",
- "image_opengraph_url": "https://lh3.googleusercontent.com/bEJ7jNgQqze5h93mq_EpSVQKr6YLOpQDEjn013rXaH9bAiQvvHMV9FiLh4I6oo9_OydqImDDULqT0PExTGappBo2Mvr6oBDJZ3Me=k-w1200-s2400-rj",
- "blurhash": "U14Cw{j[0wfjD%a|-pay5OfQ=}j[=}jt55fj",
- "predominant_color": "#13352c"
- },
- "image_url": "https://cdn.simplehash.com/assets/ffc566633c3bd957c4f7325b48e16a05b9fa9be93f457966e150c04459f4784c.png",
- "image_properties": {
- "width": 960,
- "height": 960,
- "size": 2417953,
- "mime_type": "image/png",
- "exif_orientation": null
- },
- "video_url": null,
- "video_properties": null,
- "audio_url": null,
- "audio_properties": null,
- "model_url": null,
- "model_properties": null,
- "other_url": "https://ordinals.simplehash.com/content/5ef46a44ee9777f77670940cadd760df3be6583b4724d50a923bde0d19317894i0",
- "other_properties": {
- "mime_type": "text/html"
- },
- "background_color": null,
- "external_url": null,
- "created_date": "2023-02-13T21:12:28",
- "status": "minted",
- "token_count": 1,
- "owner_count": 1,
- "owners": [
- {
- "owner_address": "bc1puq6wydt80qtc9kejhn3qjd0nzkwfplqgfwkmctl55npuwrskuu3slrnnj3",
- "quantity": 1,
- "quantity_string": "1",
- "first_acquired_date": "2023-02-13T21:29:06",
- "last_acquired_date": "2023-02-13T21:29:06"
- }
- ],
- "contract": {
- "type": "ORDINALS",
- "name": "Inscription #76557",
- "symbol": null,
- "deployed_by": null,
- "deployed_via_contract": null,
- "owned_by": null,
- "has_multiple_collections": false
- },
- "collection": {
- "collection_id": "0b8bd940186a1d33bba650287fa0e0d7",
- "name": "Timechain",
- "description": "Timechain is the first long-form generative art collection on Bitcoin. It was created to honor Satoshi Nakamoto’s original vision for Bitcoin (“the timechain”) and celebrate the launch of Ordinals.Timechain is living artwork. An animated, ever-changing hourglass serves as the focal point. A kaleidoscope of colors that allows you to visualize the two opposite sides of time: continuous and fractured.Like water flowing down a river, time is continuous. Yet we measure time in fractions: seconds, minutes, and hours. The sand falling through the hourglass is fluid and continuous, but the shadow of the hourglass remains still.Complementing the hourglass in the background are sine waves that dance in an orderly tandem with the hourglass, creating a hypnotic visual experience. But at special times — like angel number moments — the artwork bursts into a polychromatic dance, with the background waves moving in chaotic directions, representing an intersection between the dimensions of time.",
- "image_url": "https://lh3.googleusercontent.com/3-Tx0wtd71-8PvpYhYoUzAz4ex6lBgclifDSAdzjeK_5W5GMmjYztDrS11-TZ0GhoNLFFVYt0iWTJhdBrP32L_HVFFrQ3AMdvQ",
- "image_properties": {
- "width": 512,
- "height": 460,
- "mime_type": "image/png"
- },
- "banner_image_url": null,
- "category": null,
- "is_nsfw": null,
- "external_url": "https://generative.xyz/generative/1000001",
- "twitter_username": "NewBitcoinCity",
- "discord_url": "https://discord.gg/yNbatuGMDG",
- "instagram_username": null,
- "medium_username": null,
- "telegram_url": null,
- "marketplace_pages": [
+ "attributes": [
{
- "marketplace_id": "magiceden",
- "marketplace_name": "Magic Eden",
- "marketplace_collection_id": "timechain",
- "nft_url": "https://magiceden.io/ordinals/item-details/5ef46a44ee9777f77670940cadd760df3be6583b4724d50a923bde0d19317894i0",
- "collection_url": "https://magiceden.io/ordinals/marketplace/timechain",
- "verified": null
- }
- ],
- "metaplex_mint": null,
- "metaplex_candy_machine": null,
- "metaplex_first_verified_creator": null,
- "spam_score": 0,
- "floor_prices": [
+ "trait_type": "Attributes",
+ "value": "Cigarette",
+ "display_type": null
+ },
{
- "marketplace_id": "magiceden",
- "marketplace_name": "Magic Eden",
- "value": 1100000,
- "payment_token": {
- "payment_token_id": "bitcoin.native",
- "name": "Bitcoin",
- "symbol": "BTC",
- "address": null,
- "decimals": 8
- },
- "value_usd_cents": 63233
+ "trait_type": "Background",
+ "value": "Violet",
+ "display_type": null
+ },
+ {
+ "trait_type": "Face",
+ "value": "Ord Glasses",
+ "display_type": null
+ },
+ {
+ "trait_type": "Hats",
+ "value": "Miner",
+ "display_type": null
+ },
+ {
+ "trait_type": "Item",
+ "value": "Staff",
+ "display_type": null
+ },
+ {
+ "trait_type": "Shirt",
+ "value": "Bitcoin Monk",
+ "display_type": null
}
],
- "top_bids": [],
- "distinct_owner_count": 105,
- "distinct_nft_count": 140,
- "total_quantity": 140,
- "chains": ["bitcoin"],
- "top_contracts": [],
- "collection_royalties": []
- },
- "last_sale": null,
- "primary_sale": null,
- "first_created": {
- "minted_to": "bc1ppz5f0u8k8zkjptx56flxx2ul9pa29wgr9aql2yk5rpwusfpkh5cq2yau2p",
- "quantity": 1,
- "quantity_string": "1",
- "timestamp": "2023-02-13T21:12:28",
- "block_number": 776398,
- "transaction": "",
- "transaction_initiator": null
- },
- "rarity": {
- "rank": null,
- "score": null,
- "unique_attributes": null
- },
- "royalty": [],
- "extra_metadata": {
- "attributes": [],
- "high_res_img_url": "https://cdn.generative.xyz/thumb/5ef46a44ee9777f77670940cadd760df3be6583b4724d50a923bde0d19317894i0-1677063161.png",
"ordinal_details": {
- "inscription_id": "5ef46a44ee9777f77670940cadd760df3be6583b4724d50a923bde0d19317894i0",
- "inscription_number": 76557,
- "content_length": 104383,
- "content_type": "text/html;charset=utf-8",
- "sat_number": 741545957501281,
- "sat_name": "ipedezionvs",
+ "inscription_id": "44758bf08f91e23917596469969c5bb29d2778598468c135374d34583c273698i0",
+ "inscription_number": 53161134,
+ "content_length": 2892,
+ "content_type": "image/webp",
+ "sat_number": 280943728804176,
+ "sat_name": "lvzulmofkpt",
"sat_rarity": "common",
"protocol_name": null,
"protocol_content": null,
- "location": "71f53e77b75ae4ae4f56ad147553e1586fb0eab5ae69ac676cfb644954e46606:0:0",
- "output_value": 8335
+ "location": "2c9d54d4c3c5851162aa9bd3efbb67701a72e2dc912790f6263791171d11d58e:0:0",
+ "output_value": 10000
},
- "image_original_url": "https://ordinals.simplehash.com/content/5ef46a44ee9777f77670940cadd760df3be6583b4724d50a923bde0d19317894i0",
+ "image_original_url": "https://ordinals.simplehash.com/content/44758bf08f91e23917596469969c5bb29d2778598468c135374d34583c273698i0",
"animation_original_url": null,
"metadata_original_url": null
}
diff --git a/apps/ledger-live-desktop/tests/handlers/index.ts b/apps/ledger-live-desktop/tests/handlers/index.ts
index 369c16330c8f..df84b0f3291b 100644
--- a/apps/ledger-live-desktop/tests/handlers/index.ts
+++ b/apps/ledger-live-desktop/tests/handlers/index.ts
@@ -1,3 +1,8 @@
import NFTsHandlers from "./nfts";
export default [...NFTsHandlers];
+
+export const ALLOWED_UNHANDLED_REQUESTS = [
+ "https://nft.api.live.ledger.com/v1/marketdata/ethereum/1/contract/0xe3BE0054Da2F8da5002E8bdD8AA4c7fDf851E86D/floor-price",
+ "https://nft.api.live.ledger.com/v1/ethereum/1/contracts/infos",
+];
diff --git a/apps/ledger-live-desktop/tests/jestSetup.js b/apps/ledger-live-desktop/tests/jestSetup.js
index 2957526bcb73..d5aef69b2f0c 100644
--- a/apps/ledger-live-desktop/tests/jestSetup.js
+++ b/apps/ledger-live-desktop/tests/jestSetup.js
@@ -1,17 +1,23 @@
import "@jest/globals";
import "@testing-library/jest-dom";
import { server } from "./server";
+import { ALLOWED_UNHANDLED_REQUESTS } from "./handlers";
global.setImmediate = global.setImmediate || ((fn, ...args) => global.setTimeout(fn, 0, ...args));
-beforeAll(() => server.listen());
+beforeAll(() =>
+ server.listen({
+ onUnhandledRequest(request, print) {
+ if (ALLOWED_UNHANDLED_REQUESTS.some(ignoredUrl => request.url.includes(ignoredUrl))) {
+ return;
+ }
+ print.warning();
+ },
+ }),
+);
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
-server.events.on("request:start", ({ request }) => {
- console.log("MSW intercepted:", request.method, request.url);
-});
-
jest.mock("src/sentry/install", () => ({
init: jest.fn(),
setUser: jest.fn(),
diff --git a/apps/ledger-live-desktop/tests/page/abstractClasses.ts b/apps/ledger-live-desktop/tests/page/abstractClasses.ts
index 776c85c8f37e..0d2041efedc9 100644
--- a/apps/ledger-live-desktop/tests/page/abstractClasses.ts
+++ b/apps/ledger-live-desktop/tests/page/abstractClasses.ts
@@ -16,6 +16,7 @@ export abstract class Component extends PageHolder {
`//*[contains(text(),"${text}")]/following::span[contains(text(),"${followingText}")]`,
)
.first();
+ protected loadingSpinner = this.page.getByTestId("loading-spinner");
async waitForPageDomContentLoadedState() {
return await this.page.waitForLoadState("domcontentloaded");
diff --git a/apps/ledger-live-desktop/tests/page/modal/add.account.modal.ts b/apps/ledger-live-desktop/tests/page/modal/add.account.modal.ts
index 4f97831e6f92..241c9bc619c8 100644
--- a/apps/ledger-live-desktop/tests/page/modal/add.account.modal.ts
+++ b/apps/ledger-live-desktop/tests/page/modal/add.account.modal.ts
@@ -8,6 +8,8 @@ export class AddAccountModal extends Modal {
private selectAccount = this.page.locator("text=Choose a crypto asset"); // FIXME: I need an id
readonly selectAccountInput = this.page.locator('[placeholder="Search"]'); // FIXME: I need an id
readonly addAccountsButton = this.page.getByTestId("add-accounts-import-add-button");
+ private deselectAllButton = this.page.getByText("Deselect all");
+ private checkbox = this.page.getByTestId("accountRow-checkbox").first();
private accountsList = this.page.getByTestId("add-accounts-step-import-accounts-list");
private stopButton = this.page.getByTestId("add-accounts-import-stop-button");
private doneButton = this.page.getByTestId("add-accounts-finish-close-button");
@@ -92,10 +94,21 @@ export class AddAccountModal extends Modal {
await expect(this.closeButton).toBeVisible();
await this.continue();
await this.waitForSync();
+ await this.loadingSpinner.waitFor({ state: "hidden" });
+ }
+
+ @step("click `Add Accounts` button - mocked tests")
+ async addAccountsMocked() {
+ await this.addAccountsButton.click();
+ await expect(this.successAddLabel).toBeVisible();
}
@step("Click `Add Accounts` button")
async addAccounts() {
+ if (await this.deselectAllButton.isVisible()) {
+ await this.deselectAllButton.click();
+ await this.checkbox.click({ force: true });
+ }
await this.addAccountsButton.click();
await expect(this.successAddLabel).toBeVisible();
}
diff --git a/apps/ledger-live-desktop/tests/page/onboarding.page.ts b/apps/ledger-live-desktop/tests/page/onboarding.page.ts
index 08cebe9974ee..48c64788178d 100644
--- a/apps/ledger-live-desktop/tests/page/onboarding.page.ts
+++ b/apps/ledger-live-desktop/tests/page/onboarding.page.ts
@@ -4,6 +4,7 @@ import { AppPage } from "tests/page/abstractClasses";
export class OnboardingPage extends AppPage {
deviceAction = new DeviceAction(this.page);
private getStartedButton = this.page.locator('button:has-text("Get Started")');
+ private acceptAnalyticsButton = this.page.getByTestId("accept-analytics-button");
private selectDeviceButton = (deviceId: string) => this.page.getByTestId(`v3-device-${deviceId}`);
private checkMyNanoButton = this.page.locator('button:has-text("Check my Nano")');
readonly continueButton = this.page.locator('button:has-text("Continue")');
@@ -36,6 +37,9 @@ export class OnboardingPage extends AppPage {
async getStarted() {
await this.getStartedButton.click();
+
+ // Click on accept analytics button if it exists
+ await this.acceptAnalyticsButton.click().catch(() => {});
}
async hoverDevice(device: "nanoS" | "nanoX" | "nanoSP" | "stax") {
diff --git a/apps/ledger-live-desktop/tests/specs/accounts/account.spec.ts b/apps/ledger-live-desktop/tests/specs/accounts/account.spec.ts
index 4bd5c17d10bf..b3e46876edc5 100644
--- a/apps/ledger-live-desktop/tests/specs/accounts/account.spec.ts
+++ b/apps/ledger-live-desktop/tests/specs/accounts/account.spec.ts
@@ -51,7 +51,7 @@ test.describe.parallel("Accounts @smoke", () => {
});
await test.step(`[${currency}] Scan and add accounts`, async () => {
- await addAccountModal.addAccounts();
+ await addAccountModal.addAccountsMocked();
await expect.soft(addAccountModal.container).toHaveScreenshot(`${currency}-success.png`);
});
diff --git a/apps/ledger-live-desktop/tests/specs/onboarding/connect-device.spec.ts b/apps/ledger-live-desktop/tests/specs/onboarding/connect-device.spec.ts
index b12c3bcae262..e2b0cb6ac040 100644
--- a/apps/ledger-live-desktop/tests/specs/onboarding/connect-device.spec.ts
+++ b/apps/ledger-live-desktop/tests/specs/onboarding/connect-device.spec.ts
@@ -2,6 +2,10 @@ import { expect } from "@playwright/test";
import test from "../../fixtures/common";
import { OnboardingPage } from "../../page/onboarding.page";
+test.use({
+ settings: { hasSeenAnalyticsOptInPrompt: false },
+});
+
enum Nano {
nanoX = "nanoX",
nanoS = "nanoS",
diff --git a/apps/ledger-live-desktop/tests/specs/onboarding/restore-device.spec.ts b/apps/ledger-live-desktop/tests/specs/onboarding/restore-device.spec.ts
index ebf8000482a0..ebda43e18f3f 100644
--- a/apps/ledger-live-desktop/tests/specs/onboarding/restore-device.spec.ts
+++ b/apps/ledger-live-desktop/tests/specs/onboarding/restore-device.spec.ts
@@ -2,6 +2,10 @@ import { expect } from "@playwright/test";
import test from "../../fixtures/common";
import { OnboardingPage } from "../../page/onboarding.page";
+test.use({
+ settings: { hasSeenAnalyticsOptInPrompt: false },
+});
+
enum Nano {
nanoX = "nanoX",
nanoS = "nanoS",
diff --git a/apps/ledger-live-desktop/tests/specs/onboarding/setup-device.spec.ts b/apps/ledger-live-desktop/tests/specs/onboarding/setup-device.spec.ts
index a7bee4e152c5..821ce6be982e 100644
--- a/apps/ledger-live-desktop/tests/specs/onboarding/setup-device.spec.ts
+++ b/apps/ledger-live-desktop/tests/specs/onboarding/setup-device.spec.ts
@@ -2,6 +2,10 @@ import test from "../../fixtures/common";
import { expect } from "@playwright/test";
import { OnboardingPage } from "../../page/onboarding.page";
+test.use({
+ settings: { hasSeenAnalyticsOptInPrompt: false },
+});
+
enum Nano {
nanoX = "nanoX",
nanoS = "nanoS",
diff --git a/apps/ledger-live-desktop/tests/specs/speculos/add.account.spec.ts b/apps/ledger-live-desktop/tests/specs/speculos/add.account.spec.ts
index 3078a47be449..eb4b9779097b 100644
--- a/apps/ledger-live-desktop/tests/specs/speculos/add.account.spec.ts
+++ b/apps/ledger-live-desktop/tests/specs/speculos/add.account.spec.ts
@@ -3,38 +3,40 @@ import { Currency } from "../../enum/Currency";
import { addTmsLink } from "tests/utils/allureUtils";
import { getDescription } from "../../utils/customJsonReporter";
-const currencies: Currency[] = [
- Currency.BTC,
- Currency.ETH,
- Currency.ETC,
- Currency.XRP,
- //todo: Reactivate after DOT API issue is resolved - TSD-3603
- //Currency.DOT,
- Currency.TRX,
- Currency.ADA,
- Currency.XLM,
- Currency.BCH,
- Currency.ALGO,
- Currency.ATOM,
- Currency.XTZ,
- Currency.SOL,
- Currency.TON,
+const currencies = [
+ {
+ currency: Currency.BTC,
+ xrayTicket: "B2CQA-2499, B2CQA-2644, B2CQA-2658, B2CQA-2672, B2CQA-929, B2CQA-786",
+ },
+ { currency: Currency.ETH, xrayTicket: "B2CQA-2503, B2CQA-2645, B2CQA-2659, B2CQA-2673" },
+ { currency: Currency.ETC, xrayTicket: "B2CQA-2502, B2CQA-2646, B2CQA-2660, B2CQA-2674" },
+ { currency: Currency.XRP, xrayTicket: "B2CQA-2505, B2CQA-2647, B2CQA-2661, B2CQA-2675" },
+ { currency: Currency.DOT, xrayTicket: "B2CQA-2504, B2CQA-2648, B2CQA-2662, B2CQA-2676" },
+ { currency: Currency.TRX, xrayTicket: "B2CQA-2508, B2CQA-2649, B2CQA-2663, B2CQA-2677" },
+ { currency: Currency.ADA, xrayTicket: "B2CQA-2500, B2CQA-2650, B2CQA-2664, B2CQA-2678" },
+ { currency: Currency.XLM, xrayTicket: "B2CQA-2506, B2CQA-2651, B2CQA-2665, B2CQA-2679" },
+ { currency: Currency.BCH, xrayTicket: "B2CQA-2498, B2CQA-2652, B2CQA-2666, B2CQA-2680" },
+ { currency: Currency.ALGO, xrayTicket: "B2CQA-2497, B2CQA-2653, B2CQA-2667, B2CQA-2681" },
+ { currency: Currency.ATOM, xrayTicket: "B2CQA-2501, B2CQA-2654, B2CQA-2668, B2CQA-2682" },
+ { currency: Currency.XTZ, xrayTicket: "B2CQA-2507, B2CQA-2655, B2CQA-2669, B2CQA-2683" },
+ { currency: Currency.SOL, xrayTicket: "B2CQA-2642, B2CQA-2656, B2CQA-2670, B2CQA-2684" },
+ { currency: Currency.TON, xrayTicket: "B2CQA-2643, B2CQA-2657, B2CQA-2671, B2CQA-2685" },
];
for (const currency of currencies) {
test.describe("Add Accounts", () => {
test.use({
userdata: "skip-onboarding",
- speculosApp: currency.speculosApp,
+ speculosApp: currency.currency.speculosApp,
});
let firstAccountName = "NO ACCOUNT NAME YET";
test(
- `[${currency.name}] Add account`,
+ `[${currency.currency.name}] Add account`,
{
annotation: {
type: "TMS",
- description: "B2CQA-101, B2CQA-102, B2CQA-314, B2CQA-330, B2CQA-929, B2CQA-786",
+ description: currency.xrayTicket,
},
},
async ({ app }) => {
@@ -42,13 +44,13 @@ for (const currency of currencies) {
await app.portfolio.openAddAccountModal();
await app.addAccount.expectModalVisiblity();
- await app.addAccount.selectCurrency(currency);
+ await app.addAccount.selectCurrency(currency.currency);
firstAccountName = await app.addAccount.getFirstAccountName();
await app.addAccount.addAccounts();
await app.addAccount.done();
// Todo: Remove 'if' when CounterValue is fixed for $TON - LIVE-13685
- if (currency.name !== Currency.TON.name) {
+ if (currency.currency.name !== Currency.TON.name) {
await app.layout.expectBalanceVisibility();
}
await app.layout.goToAccounts();
diff --git a/apps/ledger-live-desktop/tests/specs/speculos/delete.account.spec.ts b/apps/ledger-live-desktop/tests/specs/speculos/delete.account.spec.ts
index ffac42606dbe..a78f532861e6 100644
--- a/apps/ledger-live-desktop/tests/specs/speculos/delete.account.spec.ts
+++ b/apps/ledger-live-desktop/tests/specs/speculos/delete.account.spec.ts
@@ -3,45 +3,45 @@ import { Account } from "../../enum/Account";
import { addTmsLink } from "tests/utils/allureUtils";
import { getDescription } from "../../utils/customJsonReporter";
-const accounts: Account[] = [
- Account.BTC_1,
- Account.ETH_1,
- Account.SOL_1,
- Account.TRX_1,
- Account.DOT_1,
- Account.XRP_1,
- Account.ADA_1,
- Account.ALGO_1,
- Account.XLM_1,
- Account.BCH_1,
- Account.ATOM_1,
- Account.XTZ_1,
+const accounts = [
+ { account: Account.BTC_NATIVE_SEGWIT_1, xrayTicket: "B2CQA-2548" },
+ { account: Account.ETH_1, xrayTicket: "B2CQA-2551" },
+ { account: Account.SOL_1, xrayTicket: "B2CQA-2553" },
+ { account: Account.XRP_1, xrayTicket: "B2CQA-2557" },
+ { account: Account.ADA_1, xrayTicket: "B2CQA-2549" },
+ { account: Account.DOT_1, xrayTicket: "B2CQA-2552" },
+ { account: Account.TRX_1, xrayTicket: "B2CQA-2556" },
+ { account: Account.XLM_1, xrayTicket: "B2CQA-2554" },
+ { account: Account.BCH_1, xrayTicket: "B2CQA-2547" },
+ { account: Account.ALGO_1, xrayTicket: "B2CQA-2546" },
+ { account: Account.ATOM_1, xrayTicket: "B2CQA-2550" },
+ { account: Account.XTZ_1, xrayTicket: "B2CQA-2555" },
];
for (const account of accounts) {
test.describe("Delete Accounts", () => {
test.use({
userdata: "speculos-tests-app",
- speculosApp: account.currency.speculosApp,
+ speculosApp: account.account.currency.speculosApp,
});
test(
- `[${account.currency.name}] Delete Account`,
+ `[${account.account.currency.name}] Delete Account`,
{
annotation: {
type: "TMS",
- description: "B2CQA-320",
+ description: account.xrayTicket,
},
},
async ({ app }) => {
await addTmsLink(getDescription(test.info().annotations).split(", "));
await app.layout.goToAccounts();
- await app.accounts.navigateToAccountByName(account.accountName);
- await app.account.expectAccountVisibility(account.accountName);
+ await app.accounts.navigateToAccountByName(account.account.accountName);
+ await app.account.expectAccountVisibility(account.account.accountName);
await app.account.deleteAccount();
- await app.accounts.expectAccountAbsence(account.accountName);
+ await app.accounts.expectAccountAbsence(account.account.accountName);
},
);
});
diff --git a/apps/ledger-live-desktop/tests/specs/speculos/receive.address.spec.ts b/apps/ledger-live-desktop/tests/specs/speculos/receive.address.spec.ts
index 17373434ef47..ccc7afdfdbd9 100644
--- a/apps/ledger-live-desktop/tests/specs/speculos/receive.address.spec.ts
+++ b/apps/ledger-live-desktop/tests/specs/speculos/receive.address.spec.ts
@@ -3,18 +3,17 @@ import { Account } from "../../enum/Account";
import { addTmsLink } from "tests/utils/allureUtils";
import { getDescription } from "../../utils/customJsonReporter";
-const accounts: Account[] = [
- // Derivation path is updated when account receive money
- Account.BTC_1,
- Account.ETH_1,
- Account.SOL_1,
- Account.TRX_1,
- Account.DOT_1,
- Account.XRP_1,
- Account.BCH_1,
- Account.ATOM_1,
- Account.XTZ_1,
- Account.BSC_1,
+const accounts = [
+ { account: Account.BTC_NATIVE_SEGWIT_1, xrayTicket: "B2CQA-2559, B2CQA-2687" },
+ { account: Account.ETH_1, xrayTicket: "B2CQA-2561, B2CQA-2688, B2CQA-2697" },
+ { account: Account.SOL_1, xrayTicket: "B2CQA-2563, B2CQA-2689" },
+ { account: Account.TRX_1, xrayTicket: "B2CQA-2565, B2CQA-2690, B2CQA-2699" },
+ { account: Account.DOT_1, xrayTicket: "B2CQA-2562, B2CQA-2691" },
+ { account: Account.XRP_1, xrayTicket: "B2CQA-2566, B2CQA-2692" },
+ { account: Account.BCH_1, xrayTicket: "B2CQA-2558, B2CQA-2693" },
+ { account: Account.ATOM_1, xrayTicket: "B2CQA-2560, B2CQA-2694" },
+ { account: Account.XTZ_1, xrayTicket: "B2CQA-2564, B2CQA-2695" },
+ { account: Account.BSC_1, xrayTicket: "B2CQA-2686, B2CQA-2696, B2CQA-2698" },
];
//Warning 🚨: Test may fail due to the GetAppAndVersion issue - Jira: LIVE-12581
@@ -22,11 +21,11 @@ for (const account of accounts) {
test.describe("Receive", () => {
test.use({
userdata: "speculos-tests-app",
- speculosApp: account.currency.speculosApp,
+ speculosApp: account.account.currency.speculosApp,
});
test(
- `[${account.currency.name}] Receive`,
+ `[${account.account.currency.name}] Receive`,
{
annotation: {
type: "TMS",
@@ -37,24 +36,27 @@ for (const account of accounts) {
await addTmsLink(getDescription(test.info().annotations).split(", "));
await app.layout.goToAccounts();
- await app.accounts.navigateToAccountByName(account.accountName);
- await app.account.expectAccountVisibility(account.accountName);
+ await app.accounts.navigateToAccountByName(account.account.accountName);
+ await app.account.expectAccountVisibility(account.account.accountName);
await app.account.clickReceive();
- switch (account) {
+ switch (account.account) {
case Account.TRX_1:
- await app.receive.verifySendCurrencyTokensWarningMessage(account, "TRC10/TRC20");
+ await app.receive.verifySendCurrencyTokensWarningMessage(
+ account.account,
+ "TRC10/TRC20",
+ );
break;
case Account.ETH_1:
- await app.receive.verifySendCurrencyTokensWarningMessage(account, "Ethereum");
+ await app.receive.verifySendCurrencyTokensWarningMessage(account.account, "Ethereum");
break;
case Account.BSC_1:
- await app.receive.verifySendCurrencyTokensWarningMessage(account, "BEP20");
+ await app.receive.verifySendCurrencyTokensWarningMessage(account.account, "BEP20");
break;
}
await app.modal.continue();
- await app.receive.expectValidReceiveAddress(account.address);
+ await app.receive.expectValidReceiveAddress(account.account.address);
- await app.speculos.expectValidReceiveAddress(account);
+ await app.speculos.expectValidReceiveAddress(account.account);
await app.receive.expectApproveLabel();
},
);
diff --git a/apps/ledger-live-desktop/tests/specs/speculos/send.tx.spec.ts b/apps/ledger-live-desktop/tests/specs/speculos/send.tx.spec.ts
index 4a993700adc5..684827381abe 100644
--- a/apps/ledger-live-desktop/tests/specs/speculos/send.tx.spec.ts
+++ b/apps/ledger-live-desktop/tests/specs/speculos/send.tx.spec.ts
@@ -9,32 +9,44 @@ const transactionsInputsInvalid = [
{
transaction: new Transaction(Account.ETH_1, Account.ETH_2, "", Fee.MEDIUM),
expectedErrorMessage: null,
+ xrayTicket: "B2CQA-2568",
},
{
transaction: new Transaction(Account.ETH_1, Account.ETH_2, "0", Fee.MEDIUM),
expectedErrorMessage: null,
+ xrayTicket: "B2CQA-2569",
},
{
transaction: new Transaction(Account.XRP_1, Account.XRP_2, "1", Fee.MEDIUM),
expectedErrorMessage: "Recipient address is inactive. Send at least 10 XRP to activate it",
+ xrayTicket: "B2CQA-2571",
},
{
transaction: new Transaction(Account.DOT_1, Account.DOT_2, "1.2", Fee.MEDIUM),
expectedErrorMessage: "Balance cannot be below 1 DOT. Send max to empty account.",
+ xrayTicket: "B2CQA-2567",
},
{
transaction: new Transaction(Account.DOT_1, Account.DOT_3, "0.5", Fee.MEDIUM),
expectedErrorMessage: "Recipient address is inactive. Send at least 1 DOT to activate it",
+ xrayTicket: "B2CQA-2570",
},
{
transaction: new Transaction(Account.ETH_1, Account.ETH_2, "100", Fee.MEDIUM),
expectedErrorMessage: "Sorry, insufficient funds",
+ xrayTicket: "B2CQA-2572",
},
];
const transactionE2E = [
- new Transaction(Account.sep_ETH_1, Account.sep_ETH_2, "0.00001", Fee.SLOW),
- new Transaction(Account.DOGE_1, Account.DOGE_2, "0.01", Fee.SLOW),
+ {
+ transaction: new Transaction(Account.sep_ETH_1, Account.sep_ETH_2, "0.00001", Fee.SLOW),
+ xrayTicket: "B2CQA-2574",
+ },
+ {
+ transaction: new Transaction(Account.DOGE_1, Account.DOGE_2, "0.01", Fee.SLOW),
+ xrayTicket: "B2CQA-2573",
+ },
];
//Warning 🚨: Test may fail due to the GetAppAndVersion issue - Jira: LIVE-12581 or insufficient funds
@@ -43,39 +55,43 @@ for (const transaction of transactionE2E) {
test.describe("Send from 1 account to another", () => {
test.use({
userdata: "speculos-tests-app",
- speculosApp: transaction.accountToDebit.currency.speculosApp,
+ speculosApp: transaction.transaction.accountToDebit.currency.speculosApp,
});
test(
- `Send from ${transaction.accountToDebit.accountName} to ${transaction.accountToCredit.accountName}`,
+ `Send from ${transaction.transaction.accountToDebit.accountName} to ${transaction.transaction.accountToCredit.accountName}`,
{
annotation: {
type: "TMS",
- description: "B2CQA-473",
+ description: transaction.xrayTicket,
},
},
async ({ app }) => {
await addTmsLink(getDescription(test.info().annotations).split(", "));
await app.layout.goToAccounts();
- await app.accounts.navigateToAccountByName(transaction.accountToDebit.accountName);
+ await app.accounts.navigateToAccountByName(
+ transaction.transaction.accountToDebit.accountName,
+ );
await app.account.clickSend();
- await app.send.fillTxInfo(transaction);
- await app.send.expectTxInfoValidity(transaction);
+ await app.send.fillTxInfo(transaction.transaction);
+ await app.send.expectTxInfoValidity(transaction.transaction);
await app.send.clickContinueToDevice();
- await app.speculos.expectValidTxInfo(transaction);
+ await app.speculos.expectValidTxInfo(transaction.transaction);
await app.send.expectTxSent();
await app.account.navigateToViewDetails();
- await app.drawer.addressValueIsVisible(transaction.accountToCredit.address);
+ await app.drawer.addressValueIsVisible(transaction.transaction.accountToCredit.address);
await app.drawer.close();
await app.layout.goToAccounts();
- await app.accounts.navigateToAccountByName(transaction.accountToCredit.accountName);
+ await app.accounts.navigateToAccountByName(
+ transaction.transaction.accountToCredit.accountName,
+ );
await app.layout.syncAccounts();
await app.account.clickOnLastOperation();
- await app.drawer.expectReceiverInfos(transaction);
+ await app.drawer.expectReceiverInfos(transaction.transaction);
},
);
});
@@ -97,7 +113,7 @@ test.describe("Send token (subAccount) - invalid address input", () => {
{
annotation: {
type: "TMS",
- description: "B2CQA-479",
+ description: "B2CQA-2702",
},
},
async ({ app }) => {
@@ -123,12 +139,14 @@ test.describe("Send token (subAccount) - invalid amount input", () => {
expectedWarningMessage: new RegExp(
/You need \d+\.\d+ BNB in your account to pay for transaction fees on the Binance Smart Chain network\. .*/,
),
+ xrayTicket: "B2CQA-2700",
},
{
transaction: new Transaction(Account.ETH_USDT_2, Account.ETH_USDT_1, "1", Fee.MEDIUM),
expectedWarningMessage: new RegExp(
/You need \d+\.\d+ ETH in your account to pay for transaction fees on the Ethereum network\. .*/,
),
+ xrayTicket: "B2CQA-2701",
},
];
for (const transaction of tokenTransactionInvalid) {
@@ -141,7 +159,7 @@ test.describe("Send token (subAccount) - invalid amount input", () => {
{
annotation: {
type: "TMS",
- description: "B2CQA-475",
+ description: transaction.xrayTicket,
},
},
async ({ app }) => {
@@ -178,7 +196,7 @@ test.describe("Send token (subAccount) - valid address & amount input", () => {
{
annotation: {
type: "TMS",
- description: "B2CQA-479, B2CQA-475",
+ description: "B2CQA-2703, B2CQA-475",
},
},
async ({ app }) => {
@@ -199,7 +217,7 @@ test.describe("Send token (subAccount) - valid address & amount input", () => {
test.describe("Check invalid address input error", () => {
const transactionInvalidAddress = new Transaction(
Account.ETH_1,
- Account.BTC_1,
+ Account.BTC_NATIVE_SEGWIT_1,
"0.00001",
Fee.MEDIUM,
);
@@ -242,7 +260,7 @@ for (const transaction of transactionsInputsInvalid) {
{
annotation: {
type: "TMS",
- description: "B2CQA-473",
+ description: transaction.xrayTicket,
},
},
async ({ app }) => {
diff --git a/apps/ledger-live-desktop/tests/specs/speculos/subAccount.spec.ts b/apps/ledger-live-desktop/tests/specs/speculos/subAccount.spec.ts
index dff28423accc..5fdb6d5b680a 100644
--- a/apps/ledger-live-desktop/tests/specs/speculos/subAccount.spec.ts
+++ b/apps/ledger-live-desktop/tests/specs/speculos/subAccount.spec.ts
@@ -3,39 +3,38 @@ import { Account } from "../../enum/Account";
import { addTmsLink } from "tests/utils/allureUtils";
import { getDescription } from "../../utils/customJsonReporter";
-const subAccounts: Account[] = [
- Account.ETH_USDT_1,
- Account.XLM_USCD,
- Account.ALGO_USDT_1,
- Account.TRX_USDT,
- Account.BSC_BUSD_1,
- Account.POL_DAI_1,
+const subAccounts = [
+ { account: Account.ETH_USDT_1, xrayTicket1: "B2CQA-2577, B2CQA-1079", xrayTicket2: "B2CQA-2583" },
+ { account: Account.XLM_USCD, xrayTicket1: "B2CQA-2579", xrayTicket2: "B2CQA-2585" },
+ { account: Account.ALGO_USDT_1, xrayTicket1: "B2CQA-2575", xrayTicket2: "B2CQA-2581" },
+ { account: Account.TRX_USDT, xrayTicket1: "B2CQA-2580", xrayTicket2: "B2CQA-2586" },
+ { account: Account.BSC_BUSD_1, xrayTicket1: "B2CQA-2576", xrayTicket2: "B2CQA-2582" },
+ { account: Account.POL_DAI_1, xrayTicket1: "B2CQA-2578", xrayTicket2: "B2CQA-2584" },
];
-const subAccountReceive: Account[] = [
- Account.ETH_USDT_1,
- Account.ETH_LIDO,
- Account.TRX_USDT,
- Account.TRX_BTT,
- Account.BSC_BUSD_1,
- Account.BSC_SHIBA,
- Account.POL_DAI_1,
- Account.POL_UNI,
+const subAccountReceive = [
+ { account: Account.ETH_USDT_1, xrayTicket: "B2CQA-2492" },
+ { account: Account.ETH_LIDO, xrayTicket: "B2CQA-2491" },
+ { account: Account.TRX_USDT, xrayTicket: "B2CQA-2496" },
+ { account: Account.BSC_BUSD_1, xrayTicket: "B2CQA-2489" },
+ { account: Account.BSC_SHIBA, xrayTicket: "B2CQA-2490" },
+ { account: Account.POL_DAI_1, xrayTicket: "B2CQA-2493" },
+ { account: Account.POL_UNI, xrayTicket: "B2CQA-2494" },
];
for (const token of subAccounts) {
test.describe("Add subAccount without parent", () => {
test.use({
userdata: "skip-onboarding",
- speculosApp: token.currency.speculosApp,
+ speculosApp: token.account.currency.speculosApp,
});
test(
- `Add Sub Account without parent (${token.currency.speculosApp.name}) - ${token.currency.ticker}`,
+ `Add Sub Account without parent (${token.account.currency.speculosApp.name}) - ${token.account.currency.ticker}`,
{
annotation: {
type: "TMS",
- description: "B2CQA-2448, B2CQA-1079",
+ description: token.xrayTicket1,
},
},
async ({ app }) => {
@@ -44,15 +43,15 @@ for (const token of subAccounts) {
await app.portfolio.openAddAccountModal();
await app.addAccount.expectModalVisiblity();
- await app.addAccount.selectToken(token);
+ await app.addAccount.selectToken(token.account);
await app.addAccount.addAccounts();
await app.addAccount.done();
await app.layout.goToPortfolio();
- await app.portfolio.navigateToAsset(token.currency.name);
- await app.account.navigateToToken(token);
+ await app.portfolio.navigateToAsset(token.account.currency.name);
+ await app.account.navigateToToken(token.account);
await app.account.expectLastOperationsVisibility();
- await app.account.expectTokenAccount(token);
+ await app.account.expectTokenAccount(token.account);
},
);
});
@@ -63,31 +62,31 @@ for (const token of subAccountReceive) {
test.describe("Add subAccount when parent exists", () => {
test.use({
userdata: "speculos-subAccount",
- speculosApp: token.currency.speculosApp,
+ speculosApp: token.account.currency.speculosApp,
});
test(
- `[${token.currency.speculosApp.name}] Add subAccount when parent exists (${token.currency.ticker})`,
+ `[${token.account.currency.speculosApp.name}] Add subAccount when parent exists (${token.account.currency.ticker})`,
{
annotation: {
type: "TMS",
- description: "B2CQA-640",
+ description: token.xrayTicket,
},
},
async ({ app }) => {
await addTmsLink(getDescription(test.info().annotations).split(", "));
await app.layout.goToAccounts();
- await app.accounts.navigateToAccountByName(token.accountName);
- await app.account.expectAccountVisibility(token.accountName);
+ await app.accounts.navigateToAccountByName(token.account.accountName);
+ await app.account.expectAccountVisibility(token.account.accountName);
await app.account.clickAddToken();
- await app.receive.selectToken(token);
+ await app.receive.selectToken(token.account);
await app.modal.continue();
- await app.receive.expectValidReceiveAddress(token.address);
+ await app.receive.expectValidReceiveAddress(token.account.address);
- await app.speculos.expectValidReceiveAddress(token);
+ await app.speculos.expectValidReceiveAddress(token.account);
await app.receive.expectApproveLabel();
},
);
@@ -98,23 +97,23 @@ for (const token of subAccounts) {
test.describe("Token visible in parent account", () => {
test.use({
userdata: "speculos-subAccount",
- speculosApp: token.currency.speculosApp,
+ speculosApp: token.account.currency.speculosApp,
});
test(
- `Token visible in parent account (${token.currency.speculosApp.name}) - ${token.currency.ticker}`,
+ `Token visible in parent account (${token.account.currency.speculosApp.name}) - ${token.account.currency.ticker}`,
{
annotation: {
type: "TMS",
- description: "B2CQA-1425",
+ description: token.xrayTicket2,
},
},
async ({ app }) => {
await addTmsLink(getDescription(test.info().annotations).split(", "));
await app.layout.goToAccounts();
- await app.accounts.navigateToAccountByName(token.accountName);
- await app.account.expectTokenToBePresent(token);
+ await app.accounts.navigateToAccountByName(token.account.accountName);
+ await app.account.expectTokenToBePresent(token.account);
},
);
});
diff --git a/apps/ledger-live-desktop/tests/specs/speculos/swap.spec.ts b/apps/ledger-live-desktop/tests/specs/speculos/swap.spec.ts
index cdd1924b219a..6841f9d20102 100644
--- a/apps/ledger-live-desktop/tests/specs/speculos/swap.spec.ts
+++ b/apps/ledger-live-desktop/tests/specs/speculos/swap.spec.ts
@@ -8,7 +8,7 @@ import { Providers, Rates } from "tests/enum/Swap";
const swaps = [
{
- swap: new Swap(Account.ETH_1, Account.BTC_1, "0.015", Fee.MEDIUM),
+ swap: new Swap(Account.ETH_1, Account.BTC_NATIVE_SEGWIT_1, "0.015", Fee.MEDIUM),
},
];
diff --git a/apps/ledger-live-desktop/tests/specs/syncOnboarding/manual.spec.ts b/apps/ledger-live-desktop/tests/specs/syncOnboarding/manual.spec.ts
index 65096a066b47..6bc23aaef368 100644
--- a/apps/ledger-live-desktop/tests/specs/syncOnboarding/manual.spec.ts
+++ b/apps/ledger-live-desktop/tests/specs/syncOnboarding/manual.spec.ts
@@ -2,8 +2,10 @@ import { expect } from "@playwright/test";
import test from "../../fixtures/common";
import { OnboardingPage } from "../../page/onboarding.page";
import { DeviceModelId } from "@ledgerhq/devices";
+
test.use({
env: { MOCK_NO_BYPASS: "1" },
+ settings: { hasSeenAnalyticsOptInPrompt: false },
});
const modelIds = [
diff --git a/apps/ledger-live-desktop/tests/userdata/1AccountBTC1AccountETH-encrypted.json b/apps/ledger-live-desktop/tests/userdata/1AccountBTC1AccountETH-encrypted.json
index 10ecfd0fd9a1..d0e1e8e3c868 100644
--- a/apps/ledger-live-desktop/tests/userdata/1AccountBTC1AccountETH-encrypted.json
+++ b/apps/ledger-live-desktop/tests/userdata/1AccountBTC1AccountETH-encrypted.json
@@ -18,7 +18,7 @@
"loaded": true,
"shareAnalytics": true,
"sharePersonalizedRecommandations": false,
- "hasSeenAnalyticsOptInPrompt": false,
+ "hasSeenAnalyticsOptInPrompt": true,
"sentryLogs": true,
"lastUsedVersion": "99.99.99",
"dismissedBanners": [],
diff --git a/apps/ledger-live-desktop/tests/utils/allureUtils.ts b/apps/ledger-live-desktop/tests/utils/allureUtils.ts
index 49a90707c53f..291f1b754388 100644
--- a/apps/ledger-live-desktop/tests/utils/allureUtils.ts
+++ b/apps/ledger-live-desktop/tests/utils/allureUtils.ts
@@ -30,4 +30,15 @@ export async function captureArtifacts(page: Page, testInfo: TestInfo) {
await testInfo.attach("Test Video", { body: videoData, contentType: "video/webm" });
}
}
+
+ const filePath = `tests/artifacts/${testInfo.title.replace(/[^a-zA-Z0-9]/g, " ")}.json`;
+
+ await page.evaluate(filePath => {
+ window.saveLogs(filePath);
+ }, filePath);
+
+ await testInfo.attach("Test logs", {
+ path: filePath,
+ contentType: "application/json",
+ });
}
diff --git a/apps/ledger-live-mobile/.env.mock b/apps/ledger-live-mobile/.env.mock
index 49b3538d982f..920bb6c6f5d3 100644
--- a/apps/ledger-live-mobile/.env.mock
+++ b/apps/ledger-live-mobile/.env.mock
@@ -9,7 +9,7 @@ ADJUST_APP_TOKEN=cbxft2ch7wn4
BRAZE_ANDROID_API_KEY="be5e1bc8-43f1-4864-b097-076a3c693a43"
BRAZE_IOS_API_KEY="e0a7dfaf-fc30-48f6-b998-01dbebbb73a4"
BRAZE_CUSTOM_ENDPOINT="sdk.fra-02.braze.eu"
-FEATURE_FLAGS={"ratingsPrompt":{"enabled":false},"brazePushNotifications":{"enabled":false},"llmAnalyticsOptInPrompt":{"enabled":false}}
+FEATURE_FLAGS={"ratingsPrompt":{"enabled":false},"brazePushNotifications":{"enabled":false}}
# Fix random iOS app crash https://github.com/wix/Detox/pull/3135
SIMCTL_CHILD_NSZombieEnabled=YES
MOCK_REMOTE_LIVE_MANIFEST=[{"name":"Dummy Wallet API Live App","homepageUrl":"https://developers.ledger.com/","icon":"","platforms":["ios","android","desktop"],"apiVersion":"2.0.0","manifestVersion":"1","branch":"stable","categories":["tools"],"currencies":"*","content":{"shortDescription":{"en":"App to test the Wallet API"},"description":{"en":"App to test the Wallet API with Playwright"}},"permissions":["account.list","account.receive","account.request","currency.list","device.close","device.exchange","device.transport","message.sign","transaction.sign","transaction.signAndBroadcast","storage.set","storage.get","bitcoin.getXPub","wallet.capabilities","wallet.userId","wallet.info"],"domains":["http://*"],"visibility":"complete","id":"dummy-live-app","url":"http://localhost:52619"}]
\ No newline at end of file
diff --git a/apps/ledger-live-mobile/README.md b/apps/ledger-live-mobile/README.md
index 00a62dd8bd88..15da94b620ba 100644
--- a/apps/ledger-live-mobile/README.md
+++ b/apps/ledger-live-mobile/README.md
@@ -25,7 +25,7 @@ We also share core business logic with Ledger Live mobile through [@ledgerhq/liv
### iOS
-- XCode
+- XCode (our CI builds run 15.4, so 15.4 is recommended)
- Ruby 3.3.0 or above. The macOS built-in Ruby [does not work properly for installing dependencies of the iOS app](https://jeffreymorgan.io/articles/ruby-on-macos-with-rvm/), you have to install Ruby with for instance [Homebrew](https://brew.sh/) or [rvm](https://rvm.io/rvm/install) and make sure that `which ruby` points to that newly installed Ruby.
### Android
diff --git a/apps/ledger-live-mobile/e2e/bridge/proxy.ts b/apps/ledger-live-mobile/e2e/bridge/proxy.ts
index 284eb574e39f..90d03c498b84 100644
--- a/apps/ledger-live-mobile/e2e/bridge/proxy.ts
+++ b/apps/ledger-live-mobile/e2e/bridge/proxy.ts
@@ -29,7 +29,7 @@ interface ProxyOptions {
speculosApiPort: string;
}
-export async function startProxy(speculosApiPort?: string, proxyPort?: string): Promise {
+export async function startProxy(proxyPort?: string, speculosApiPort?: string): Promise {
if (!proxyPort) proxyPort = (await findFreePort()).toString();
if (!speculosApiPort) speculosApiPort = getEnv("SPECULOS_API_PORT").toString();
invariant(speculosApiPort, "E2E Proxy : speculosApiPort is not defined");
diff --git a/apps/ledger-live-mobile/e2e/bridge/server.ts b/apps/ledger-live-mobile/e2e/bridge/server.ts
index f8aa7291f47b..e70229d73c8c 100644
--- a/apps/ledger-live-mobile/e2e/bridge/server.ts
+++ b/apps/ledger-live-mobile/e2e/bridge/server.ts
@@ -2,6 +2,7 @@ import { Server, WebSocket } from "ws";
import path from "path";
import fs from "fs";
import net from "net";
+import merge from "lodash/merge";
import { toAccountRaw } from "@ledgerhq/live-common/account/index";
import { NavigatorName } from "../../src/const";
import { Subject } from "rxjs";
@@ -83,7 +84,9 @@ export async function loadConfig(fileName: string, agreed: true = true): Promise
const { data } = JSON.parse(f.toString());
- await postMessage({ type: "importSettings", id: uniqueId(), payload: data.settings });
+ const defaultSettings = { shareAnalytics: true, hasSeenAnalyticsOptInPrompt: true };
+ const settings = merge(defaultSettings, data.settings);
+ await postMessage({ type: "importSettings", id: uniqueId(), payload: settings });
navigate(NavigatorName.Base);
diff --git a/apps/ledger-live-mobile/e2e/helpers.ts b/apps/ledger-live-mobile/e2e/helpers.ts
index cb44df561000..c429dd585b94 100644
--- a/apps/ledger-live-mobile/e2e/helpers.ts
+++ b/apps/ledger-live-mobile/e2e/helpers.ts
@@ -183,7 +183,10 @@ export async function launchSpeculos(appName: string) {
invariant(speculosApiPort, "[E2E Setup] speculosApiPort not defined");
setEnv("SPECULOS_API_PORT", speculosApiPort);
- const proxyAddress = await startProxy();
+ const proxyPort = await findFreePort();
+ await device.reverseTcpPort(proxyPort);
+ await startProxy(proxyPort.toString());
+ const proxyAddress = `localhost:${proxyPort}`;
speculosDevices.push([proxyAddress, speculosDevice]);
console.warn(`Speculos started on ${proxyAddress}`);
return proxyAddress;
diff --git a/apps/ledger-live-mobile/e2e/jest.config.js b/apps/ledger-live-mobile/e2e/jest.config.js
index 329b857f4b6b..71f0fa4f95ab 100644
--- a/apps/ledger-live-mobile/e2e/jest.config.js
+++ b/apps/ledger-live-mobile/e2e/jest.config.js
@@ -35,7 +35,7 @@ module.exports = async () => ({
},
setupFilesAfterEnv: ["/e2e/setup.ts"],
testTimeout: 150000,
- testMatch: ["/e2e/specs/!(speculos)/**/*.spec.ts"],
+ testMatch: ["/e2e/specs/{*.spec.ts,!(speculos)/**/*.spec.ts}"],
reporters: ["detox/runners/jest/reporter", ["jest-allure2-reporter", jestAllure2ReporterOptions]],
globalSetup: "detox/runners/jest/globalSetup",
globalTeardown: "/e2e/jest.globalTeardown.ts",
diff --git a/apps/ledger-live-mobile/fastlane/.env.ios.nightly b/apps/ledger-live-mobile/fastlane/.env.ios.nightly
index 07af2ac6514e..bdafb9c14760 100644
--- a/apps/ledger-live-mobile/fastlane/.env.ios.nightly
+++ b/apps/ledger-live-mobile/fastlane/.env.ios.nightly
@@ -4,3 +4,4 @@ MY_APP_BUNDLE_ID="com.ledger.live"
APP_NAME="LL NIGHTLY"
SENTRY_ENVIRONMENT=nightly
SENTRY_PROJECT=llm-ios-nightly
+FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT=180
diff --git a/apps/ledger-live-mobile/package.json b/apps/ledger-live-mobile/package.json
index 654a27093157..04537efd996f 100644
--- a/apps/ledger-live-mobile/package.json
+++ b/apps/ledger-live-mobile/package.json
@@ -20,6 +20,7 @@
"e2e:build": "pnpm detox build",
"e2e:ci": "zx ./scripts/e2e-ci.mjs",
"e2e:test": "pnpm detox test",
+ "e2e:test:speculos": "pnpm detox test --testMatch $(pwd)/e2e/specs/speculos/**/*.spec.ts",
"prebeta": "bundle install",
"debug:detox": "pnpm detox test -c ios.manual currencies.spec.ts",
"ios:staging": "ENVFILE=.env.ios.staging react-native run-ios --mode Staging",
diff --git a/apps/ledger-live-mobile/scripts/e2e-ci.mjs b/apps/ledger-live-mobile/scripts/e2e-ci.mjs
index 734b2104afaa..85c6d35d2268 100755
--- a/apps/ledger-live-mobile/scripts/e2e-ci.mjs
+++ b/apps/ledger-live-mobile/scripts/e2e-ci.mjs
@@ -2,13 +2,14 @@
import { basename } from "path";
let platform, test, build, bundle;
+let speculos = "";
let cache = true;
const usage = (exitCode = 1) => {
console.log(
`Usage: ${basename(
__filename,
- )} -p --platform [-h --help] [-t --test] [-b --build] [--bundle] [--cache | --no-cache]`,
+ )} -p --platform [-h --help] [-t --test] [-b --build] [--bundle] [--cache | --no-cache] [--speculos]`,
);
process.exit(exitCode);
};
@@ -34,7 +35,7 @@ const bundle_ios_with_cache = async () => {
};
const test_ios = async () => {
- await $`pnpm mobile e2e:test \
+ await $`pnpm mobile e2e:test${speculos} \
-c ios.sim.release \
--loglevel error \
--record-logs all \
@@ -50,7 +51,7 @@ const build_android = async () => {
};
const test_android = async () => {
- await $`pnpm mobile e2e:test \\
+ await $`pnpm mobile e2e:test${speculos} \\
-c android.emu.release \\
--loglevel error \\
--record-logs all \\
@@ -104,6 +105,9 @@ for (const argName in argv) {
break;
case "_":
break;
+ case "speculos":
+ speculos = ":speculos";
+ break;
default:
usage(42);
break;
diff --git a/apps/ledger-live-mobile/src/components/RootNavigator/WalletConnectLiveAppNavigator.tsx b/apps/ledger-live-mobile/src/components/RootNavigator/WalletConnectLiveAppNavigator.tsx
index cd476c7839bc..4790d3b9e1f2 100644
--- a/apps/ledger-live-mobile/src/components/RootNavigator/WalletConnectLiveAppNavigator.tsx
+++ b/apps/ledger-live-mobile/src/components/RootNavigator/WalletConnectLiveAppNavigator.tsx
@@ -10,8 +10,7 @@ import { LiveApp } from "~/screens/Platform";
import { uriSelector } from "~/reducers/walletconnect";
import { setWallectConnectUri } from "~/actions/walletconnect";
import { WalletConnectLiveAppNavigatorParamList } from "./types/WalletConnectLiveAppNavigator";
-
-const PLATFORM = "ledger-wallet-connect";
+import { WC_ID } from "@ledgerhq/live-common/wallet-api/constants";
const options = {
headerBackImage: () => (
@@ -48,7 +47,7 @@ export default function WalletConnectLiveAppNavigator() {
key: _props.route.key,
name: ScreenName.PlatformApp,
params: {
- platform: PLATFORM,
+ platform: WC_ID,
uri: uri || _props.route.params?.uri,
requestId: _props.route.params?.requestId,
sessionTopic: _props.route.params?.sessionTopic,
diff --git a/apps/ledger-live-mobile/src/components/Web3AppWebview/PlatformAPIWebview.tsx b/apps/ledger-live-mobile/src/components/Web3AppWebview/PlatformAPIWebview.tsx
index 4888645316c4..e842ff25ddfa 100644
--- a/apps/ledger-live-mobile/src/components/Web3AppWebview/PlatformAPIWebview.tsx
+++ b/apps/ledger-live-mobile/src/components/Web3AppWebview/PlatformAPIWebview.tsx
@@ -507,13 +507,9 @@ export const PlatformAPIWebview = forwardRef(
const onOpenWindow = useCallback((event: WebViewOpenWindowEvent) => {
const { targetUrl } = event.nativeEvent;
- Linking.canOpenURL(targetUrl).then(supported => {
- if (supported) {
- Linking.openURL(targetUrl);
- } else {
- console.error(`Don't know how to open URI: ${targetUrl}`);
- }
- });
+ // Don't use canOpenURL as we cannot check unknown apps on the phone
+ // Without listing everything in plist and android manifest
+ Linking.openURL(targetUrl);
}, []);
useEffect(() => {
diff --git a/apps/ledger-live-mobile/src/components/Web3AppWebview/WalletAPIWebview.tsx b/apps/ledger-live-mobile/src/components/Web3AppWebview/WalletAPIWebview.tsx
index 136d825b3783..d5d69a1a0ee2 100644
--- a/apps/ledger-live-mobile/src/components/Web3AppWebview/WalletAPIWebview.tsx
+++ b/apps/ledger-live-mobile/src/components/Web3AppWebview/WalletAPIWebview.tsx
@@ -5,7 +5,7 @@ import Config from "react-native-config";
import { WebviewAPI, WebviewProps } from "./types";
import { useWebView } from "./helpers";
import { NetworkError } from "./NetworkError";
-import { INTERNAL_APP_IDS } from "@ledgerhq/live-common/wallet-api/constants";
+import { INTERNAL_APP_IDS, WC_ID } from "@ledgerhq/live-common/wallet-api/constants";
import { useInternalAppIds } from "@ledgerhq/live-common/hooks/useInternalAppIds";
import { INJECTED_JAVASCRIPT } from "./dappInject";
import { NoAccountScreen } from "./NoAccountScreen";
@@ -41,7 +41,8 @@ export const WalletAPIWebview = forwardRef(
const internalAppIds = useInternalAppIds() || INTERNAL_APP_IDS;
- const javaScriptCanOpenWindowsAutomatically = internalAppIds.includes(manifest.id);
+ const javaScriptCanOpenWindowsAutomatically =
+ internalAppIds.includes(manifest.id) || manifest.id === WC_ID;
if (!!manifest.dapp && noAccounts) {
return ;
diff --git a/apps/ledger-live-mobile/src/components/Web3AppWebview/helpers.ts b/apps/ledger-live-mobile/src/components/Web3AppWebview/helpers.ts
index 3b32d4aa8eab..2b87b8a7dd0f 100644
--- a/apps/ledger-live-mobile/src/components/Web3AppWebview/helpers.ts
+++ b/apps/ledger-live-mobile/src/components/Web3AppWebview/helpers.ts
@@ -163,13 +163,9 @@ export function useWebView(
const onOpenWindow = useCallback((event: WebViewOpenWindowEvent) => {
const { targetUrl } = event.nativeEvent;
- Linking.canOpenURL(targetUrl).then(supported => {
- if (supported) {
- Linking.openURL(targetUrl);
- } else {
- console.error(`Don't know how to open URI: ${targetUrl}`);
- }
- });
+ // Don't use canOpenURL as we cannot check unknown apps on the phone
+ // Without listing everything in plist and android manifest
+ Linking.openURL(targetUrl);
}, []);
return {
diff --git a/apps/ledger-live-mobile/src/families/near/Staking/index.tsx b/apps/ledger-live-mobile/src/families/near/Staking/index.tsx
index e9ad9038bebb..b575c8a15a6f 100644
--- a/apps/ledger-live-mobile/src/families/near/Staking/index.tsx
+++ b/apps/ledger-live-mobile/src/families/near/Staking/index.tsx
@@ -149,14 +149,6 @@ function StakingPositions({ account }: Props) {
),
},
- {
- label: t("near.staking.drawer.rewards"),
- Component: (
-
- {stakingPosition.formattedRewards ?? ""}
-
- ),
- },
{
label: t("near.staking.drawer.pending"),
Component: (
diff --git a/apps/ledger-live-mobile/src/locales/en/common.json b/apps/ledger-live-mobile/src/locales/en/common.json
index 38b2c3e0d8c4..cc635e420b64 100644
--- a/apps/ledger-live-mobile/src/locales/en/common.json
+++ b/apps/ledger-live-mobile/src/locales/en/common.json
@@ -3158,6 +3158,7 @@
}
},
"receiveConfirmation": {
+ "utxoWarning": "For privacy reasons, a new address is generated for every transaction. The previous ones do remain valid.",
"title": "Receive {{currencyTicker}}",
"onCurrencyName": "On {{currencyName}}",
"copyAdress": "Copy address",
@@ -5916,7 +5917,6 @@
"status": "Status",
"active": "Active",
"inactive": "Inactive",
- "rewards": "Rewards",
"pending": "Pending",
"available": "Withdrawable"
},
diff --git a/apps/ledger-live-mobile/src/navigation/DeeplinksProvider.tsx b/apps/ledger-live-mobile/src/navigation/DeeplinksProvider.tsx
index ebbd52345b89..1b2b3d634c1f 100644
--- a/apps/ledger-live-mobile/src/navigation/DeeplinksProvider.tsx
+++ b/apps/ledger-live-mobile/src/navigation/DeeplinksProvider.tsx
@@ -459,7 +459,7 @@ export const DeeplinksProvider = ({
) {
const uri = isWalletConnectUrl(url) ? url : new URL(url).searchParams.get("uri");
// Only update for a connection not a request
- if (uri && !new URL(uri).searchParams.get("requestId")) {
+ if (uri && uri !== "wc:" && !new URL(uri).searchParams.get("requestId")) {
// TODO use wallet-api to push event instead of reloading the webview
dispatch(setWallectConnectUri(uri));
}
diff --git a/apps/ledger-live-mobile/src/screens/Platform/v2/hooks.ts b/apps/ledger-live-mobile/src/screens/Platform/v2/hooks.ts
index 58d7b6e67e7b..0653d7aad098 100644
--- a/apps/ledger-live-mobile/src/screens/Platform/v2/hooks.ts
+++ b/apps/ledger-live-mobile/src/screens/Platform/v2/hooks.ts
@@ -14,6 +14,7 @@ import {
DAPP_DISCLAIMER_ID,
DISCOVER_STORE_KEY,
BROWSE_SEARCH_OPTIONS,
+ WC_ID,
} from "@ledgerhq/live-common/wallet-api/constants";
import { DiscoverDB, AppManifest } from "@ledgerhq/live-common/wallet-api/types";
import { useNavigation, useRoute } from "@react-navigation/native";
@@ -100,8 +101,6 @@ export type Disclaimer = DisclaimerRaw & {
openApp: (manifest: AppManifest) => void;
};
-const WALLET_CONNECT_LIVE_APP = "ledger-wallet-connect";
-
function useDisclaimer(appendRecentlyUsed: (manifest: AppManifest) => void): Disclaimer {
const isReadOnly = useSelector(readOnlyModeEnabledSelector);
const [isDismissed, dismiss] = useBanner(DAPP_DISCLAIMER_ID);
@@ -117,7 +116,7 @@ function useDisclaimer(appendRecentlyUsed: (manifest: AppManifest) => void): Dis
(manifest: AppManifest) => {
// Navigate to the WalletConnect navigator screen instead of the discover one
// In order to avoid issue with deeplinks opening wallet-connect multiple times
- if (manifest.id === WALLET_CONNECT_LIVE_APP) {
+ if (manifest.id === WC_ID) {
navigation.navigate(NavigatorName.WalletConnect, {
screen: ScreenName.WalletConnectConnect,
params: {},
diff --git a/apps/ledger-live-mobile/src/screens/ReceiveFunds/03-Confirmation.tsx b/apps/ledger-live-mobile/src/screens/ReceiveFunds/03-Confirmation.tsx
index a345ece5cd67..8fc419880fcd 100644
--- a/apps/ledger-live-mobile/src/screens/ReceiveFunds/03-Confirmation.tsx
+++ b/apps/ledger-live-mobile/src/screens/ReceiveFunds/03-Confirmation.tsx
@@ -1,5 +1,5 @@
import React, { useCallback, useEffect, useMemo, useState } from "react";
-import { Linking, Share, View } from "react-native";
+import { Dimensions, Linking, Share, View } from "react-native";
import { useDispatch, useSelector } from "react-redux";
import QRCode from "react-native-qrcode-svg";
import { useTranslation } from "react-i18next";
@@ -27,7 +27,6 @@ import { ScreenName } from "~/const";
import { track, TrackScreen } from "~/analytics";
import byFamily from "../../generated/Confirmation";
import byFamilyPostAlert from "../../generated/ReceiveConfirmationPostAlert";
-
import { ReceiveFundsStackParamList } from "~/components/RootNavigator/types/ReceiveFundsNavigator";
import { BaseComposite, StackNavigatorProps } from "~/components/RootNavigator/types/helpers";
import styled, { BaseStyledProps } from "@ledgerhq/native-ui/components/styled";
@@ -37,12 +36,16 @@ import { BankMedium } from "@ledgerhq/native-ui/assets/icons";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { hasClosedWithdrawBannerSelector } from "~/reducers/settings";
import { setCloseWithdrawBanner } from "~/actions/settings";
-import * as Animatable from "react-native-animatable";
import { useCompleteActionCallback } from "~/logic/postOnboarding/useCompleteAction";
import { urls } from "~/utils/urls";
import { useMaybeAccountName } from "~/reducers/wallet";
-
-const AnimatedView = Animatable.View;
+import Animated, {
+ useSharedValue,
+ useAnimatedStyle,
+ withTiming,
+ runOnJS,
+} from "react-native-reanimated";
+import { isUTXOCompliant } from "@ledgerhq/live-common/currencies/helpers";
type ScreenProps = BaseComposite<
StackNavigatorProps
@@ -83,7 +86,7 @@ function ReceiveConfirmationInner({ navigation, route, account, parentAccount }:
const insets = useSafeAreaInsets();
const hasClosedWithdrawBanner = useSelector(hasClosedWithdrawBannerSelector);
- const [displayBanner, setBanner] = useState(!hasClosedWithdrawBanner);
+ const [displayBanner, setDisplayBanner] = useState(!hasClosedWithdrawBanner);
const onRetry = useCallback(() => {
track("button_clicked", {
@@ -113,8 +116,9 @@ function ReceiveConfirmationInner({ navigation, route, account, parentAccount }:
button: "How to withdraw from exchange",
page: "Receive Account Qr Code",
});
+
dispatch(setCloseWithdrawBanner(true));
- setBanner(false);
+ setDisplayBanner(false);
}, [dispatch]);
const clickLearn = () => {
@@ -125,6 +129,7 @@ function ReceiveConfirmationInner({ navigation, route, account, parentAccount }:
});
Linking.openURL(urls.withdrawCrypto);
};
+
useEffect(() => {
if (route.params?.createTokenAccount && !hasAddedTokenAccount) {
const newMainAccount = { ...mainAccount };
@@ -225,9 +230,26 @@ function ReceiveConfirmationInner({ navigation, route, account, parentAccount }:
const mainAccountName = useMaybeAccountName(mainAccount);
+ const screenHeight = Dimensions.get("window").height;
+ const bannerHeight = useSharedValue(screenHeight * 0.23);
+ const bannerOpacity = useSharedValue(1);
+
+ const animatedBannerStyle = useAnimatedStyle(() => ({
+ height: withTiming(bannerHeight.value, { duration: 200 }, onFinish => {
+ if (onFinish && bannerHeight.value === 0) {
+ runOnJS(hideBanner)();
+ }
+ }),
+ opacity: withTiming(bannerOpacity.value, { duration: 200 }),
+ }));
+
+ const handleBannerClose = useCallback(() => {
+ bannerHeight.value = 0;
+ bannerOpacity.value = 0;
+ }, [bannerHeight, bannerOpacity]);
+
if (!account || !currency || !mainAccount) return null;
- // check for coin specific UI
if (currency.type === "CryptoCurrency" && Object.keys(byFamily).includes(currency.family)) {
const CustomConfirmation =
currency.type === "CryptoCurrency"
@@ -255,8 +277,11 @@ function ReceiveConfirmationInner({ navigation, route, account, parentAccount }:
: null;
}
+ const isAnAccount = account.type === "Account";
+ const isUTXOCompliantCurrency = isAnAccount && isUTXOCompliant(account.currency.family);
+
return (
-
+
-
-
+
+ {isUTXOCompliantCurrency && (
+
+ {t("transfer.receive.receiveConfirmation.utxoWarning")}
+
+ )}
+
{t("transfer.receive.receiveConfirmation.sendWarning", {
network: network || currency.name,
})}
@@ -382,21 +405,25 @@ function ReceiveConfirmationInner({ navigation, route, account, parentAccount }:
{CustomConfirmationAlert && }
-
-
+ {displayBanner && (
+
+
+
+ )}
+
+
{t("transfer.receive.receiveConfirmation.verifyAddress")}
-
- {displayBanner ? (
-
-
-
- ) : (
-
-
-
- )}
{verified ? null : isModalOpened ? (
@@ -413,16 +440,13 @@ type BannerProps = {
const WithdrawBanner = ({ onPress, hideBanner }: BannerProps) => {
const { t } = useTranslation();
- const insets = useSafeAreaInsets();
return (
-
- }
- onPressDismiss={hideBanner}
- onPress={onPress}
- />
-
+ }
+ onPressDismiss={hideBanner}
+ onPress={onPress}
+ />
);
};
diff --git a/apps/ledger-live-mobile/src/screens/SignMessage/04-ValidationError.tsx b/apps/ledger-live-mobile/src/screens/SignMessage/04-ValidationError.tsx
index 13facc028e45..b0aff88d5a3d 100644
--- a/apps/ledger-live-mobile/src/screens/SignMessage/04-ValidationError.tsx
+++ b/apps/ledger-live-mobile/src/screens/SignMessage/04-ValidationError.tsx
@@ -1,4 +1,4 @@
-import React, { useCallback, useEffect } from "react";
+import React, { useCallback } from "react";
import { StyleSheet } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { CompositeScreenProps, useTheme } from "@react-navigation/native";
@@ -22,14 +22,12 @@ export default function ValidationError({ navigation, route }: Navigation) {
const { colors } = useTheme();
const { error, onFailHandler } = route.params;
- useEffect(() => {
+ const onClose = useCallback(() => {
if (onFailHandler && error) {
onFailHandler(error);
}
- }, [onFailHandler, error]);
- const onClose = useCallback(() => {
navigation.getParent>().pop();
- }, [navigation]);
+ }, [error, navigation, onFailHandler]);
const retry = useCallback(() => {
navigation.goBack();
}, [navigation]);
diff --git a/apps/ledger-live-mobile/src/screens/SignMessage/04-ValidationSuccess.tsx b/apps/ledger-live-mobile/src/screens/SignMessage/04-ValidationSuccess.tsx
index 9e1e3daa100f..3480b7c4ee8d 100644
--- a/apps/ledger-live-mobile/src/screens/SignMessage/04-ValidationSuccess.tsx
+++ b/apps/ledger-live-mobile/src/screens/SignMessage/04-ValidationSuccess.tsx
@@ -1,4 +1,4 @@
-import React, { useCallback, useEffect } from "react";
+import React, { useCallback } from "react";
import { View, StyleSheet } from "react-native";
import { useTranslation } from "react-i18next";
import { CompositeScreenProps, useTheme } from "@react-navigation/native";
@@ -25,14 +25,13 @@ export default function ValidationSuccess({
const { t } = useTranslation();
const { signature, onConfirmationHandler } = route.params;
- useEffect(() => {
+ const onClose = useCallback(() => {
if (onConfirmationHandler && signature) {
onConfirmationHandler(signature);
}
- }, [onConfirmationHandler, signature]);
- const onClose = useCallback(() => {
navigation.getParent>().pop();
- }, [navigation]);
+ }, [navigation, onConfirmationHandler, signature]);
+
return (
= {
chainId: 1,
nonce: 0,
}),
- expectedStatus: (_account, transaction): Partial => {
- const estimatedFees = transaction.gasLimit.times(transaction.maxFeePerGas || 0);
+ expectedStatus: (_account, _, status): Partial => {
+ const estimatedFees = status.estimatedFees;
return {
amount: new BigNumber(1e19), // 10 ETH
estimatedFees, // fees are calculated during preparation and therefore cannot be guessed without mocks
totalSpent: new BigNumber(1e19).plus(estimatedFees), // fees are calculated during preparation and therefore cannot be guessed without mocks
errors: {
amount: new NotEnoughBalance(), // "The parent account balance is insufficient for network fees" since account is empty
- gasLimit: new FeeNotLoaded(), // "Could not load fee rates. Please set manual fees" because gas estimation failed as the account is empty
},
warnings: {},
};
diff --git a/libs/coin-modules/coin-near/src/account.ts b/libs/coin-modules/coin-near/src/account.ts
index a55f2b6441f1..23679cc95401 100644
--- a/libs/coin-modules/coin-near/src/account.ts
+++ b/libs/coin-modules/coin-near/src/account.ts
@@ -24,8 +24,8 @@ function formatAccountSpecifics(account: NearAccount): string {
str += `\n Staking Positions:\n`;
str += nearResources.stakingPositions
.map(
- ({ validatorId, staked, pending, available, rewards }) =>
- ` Validator ID: ${validatorId} | Staked: ${staked} | Pending Release: ${pending} | Available: ${available} | Rewards: ${rewards}`,
+ ({ validatorId, staked, pending, available }) =>
+ ` Validator ID: ${validatorId} | Staked: ${staked} | Pending Release: ${pending} | Available: ${available}`,
)
.join("\n");
}
diff --git a/libs/coin-modules/coin-near/src/api/node.ts b/libs/coin-modules/coin-near/src/api/node.ts
index 789e39a584be..cc8c35fe16c1 100644
--- a/libs/coin-modules/coin-near/src/api/node.ts
+++ b/libs/coin-modules/coin-near/src/api/node.ts
@@ -217,7 +217,7 @@ export const getStakingPositions = async (
const activeDelegatedStakeBalance = await account.getActiveDelegatedStakeBalance();
const stakingPositions = await Promise.all(
- activeDelegatedStakeBalance.stakedValidators.map(async ({ validatorId, amount: rawTotal }) => {
+ activeDelegatedStakeBalance.stakedValidators.map(async ({ validatorId }) => {
const contract = new nearAPI.Contract(account, validatorId, {
viewMethods: [
"get_account_staked_balance",
@@ -248,12 +248,10 @@ export const getStakingPositions = async (
available = unstaked;
pending = new BigNumber(0);
}
- let rewards = new BigNumber(0);
const staked = new BigNumber(rawStaked);
available = new BigNumber(available);
pending = new BigNumber(pending);
- rewards = new BigNumber(rawTotal || "0").minus(staked);
const stakingThreshold = getYoctoThreshold();
@@ -271,7 +269,6 @@ export const getStakingPositions = async (
staked,
available,
pending,
- rewards: rewards.gt(0) ? rewards : new BigNumber(0),
validatorId,
};
}),
diff --git a/libs/coin-modules/coin-near/src/api/sdk.types.ts b/libs/coin-modules/coin-near/src/api/sdk.types.ts
index ee87b16322df..2aac91f78d4a 100644
--- a/libs/coin-modules/coin-near/src/api/sdk.types.ts
+++ b/libs/coin-modules/coin-near/src/api/sdk.types.ts
@@ -69,7 +69,6 @@ export type NearStakingPosition = {
staked: BigNumber;
available: BigNumber;
pending: BigNumber;
- rewards: BigNumber;
validatorId: string;
};
diff --git a/libs/coin-modules/coin-near/src/logic.ts b/libs/coin-modules/coin-near/src/logic.ts
index 29c326b02ce5..ef204c186ccf 100644
--- a/libs/coin-modules/coin-near/src/logic.ts
+++ b/libs/coin-modules/coin-near/src/logic.ts
@@ -119,7 +119,6 @@ export const mapStakingPositions = (
return {
...sp,
formattedAmount: formatCurrencyUnit(unit, sp.staked, formatConfig),
- formattedRewards: formatCurrencyUnit(unit, sp.rewards, formatConfig),
formattedPending: formatCurrencyUnit(unit, sp.pending, formatConfig),
formattedAvailable: formatCurrencyUnit(unit, sp.available, formatConfig),
rank,
diff --git a/libs/coin-modules/coin-near/src/serialization.ts b/libs/coin-modules/coin-near/src/serialization.ts
index 715f71222d7c..e15332ef49e2 100644
--- a/libs/coin-modules/coin-near/src/serialization.ts
+++ b/libs/coin-modules/coin-near/src/serialization.ts
@@ -10,15 +10,12 @@ export function toNearResourcesRaw(r: NearResources): NearResourcesRaw {
pendingBalance: pendingBalance.toString(),
availableBalance: availableBalance.toString(),
storageUsageBalance: storageUsageBalance.toString(),
- stakingPositions: stakingPositions.map(
- ({ staked, validatorId, available, pending, rewards }) => ({
- staked: staked.toString(),
- available: available.toString(),
- pending: pending.toString(),
- rewards: rewards.toString(),
- validatorId,
- }),
- ),
+ stakingPositions: stakingPositions.map(({ staked, validatorId, available, pending }) => ({
+ staked: staked.toString(),
+ available: available.toString(),
+ pending: pending.toString(),
+ validatorId,
+ })),
};
}
@@ -35,15 +32,12 @@ export function fromNearResourcesRaw(r: NearResourcesRaw): NearResources {
pendingBalance: new BigNumber(pendingBalance),
availableBalance: new BigNumber(availableBalance),
storageUsageBalance: new BigNumber(storageUsageBalance),
- stakingPositions: stakingPositions.map(
- ({ staked, validatorId, available, pending, rewards }) => ({
- staked: new BigNumber(staked),
- available: new BigNumber(available),
- pending: new BigNumber(pending),
- rewards: new BigNumber(rewards),
- validatorId,
- }),
- ),
+ stakingPositions: stakingPositions.map(({ staked, validatorId, available, pending }) => ({
+ staked: new BigNumber(staked),
+ available: new BigNumber(available),
+ pending: new BigNumber(pending),
+ validatorId,
+ })),
};
}
diff --git a/libs/coin-modules/coin-near/src/types.ts b/libs/coin-modules/coin-near/src/types.ts
index 7b8affe9203b..bdbb2e5abb13 100644
--- a/libs/coin-modules/coin-near/src/types.ts
+++ b/libs/coin-modules/coin-near/src/types.ts
@@ -54,7 +54,6 @@ export type NearResourcesRaw = {
staked: string;
available: string;
pending: string;
- rewards: string;
validatorId: string;
}[];
};
@@ -73,7 +72,6 @@ export type NearValidatorItem = {
export type NearMappedStakingPosition = NearStakingPosition & {
formattedAmount: string;
- formattedRewards: string;
formattedPending: string;
formattedAvailable: string;
rank: number;
diff --git a/libs/ledger-live-common/src/currencies/helpers.ts b/libs/ledger-live-common/src/currencies/helpers.ts
index 0c50932a4fd7..70c839fa1e05 100644
--- a/libs/ledger-live-common/src/currencies/helpers.ts
+++ b/libs/ledger-live-common/src/currencies/helpers.ts
@@ -16,6 +16,10 @@ export function isTokenCurrency(currency: Currency): currency is TokenCurrency {
return currency.type === "TokenCurrency";
}
+export function isUTXOCompliant(currencyFamilly: string): boolean {
+ return currencyFamilly === "bitcoin" || currencyFamilly === "cardano";
+}
+
export function listCurrencies(includeTokens: boolean): CryptoOrTokenCurrency[] {
const currencies = listSupportedCurrencies();
diff --git a/libs/ledger-live-common/src/exchange/platform/startExchange.ts b/libs/ledger-live-common/src/exchange/platform/startExchange.ts
index 959292d301e4..1bfbce785695 100644
--- a/libs/ledger-live-common/src/exchange/platform/startExchange.ts
+++ b/libs/ledger-live-common/src/exchange/platform/startExchange.ts
@@ -30,7 +30,7 @@ const startExchange = (input: StartExchangeInput): Observable {
await withDevicePromise(deviceId, async transport => {
- const providerNameAndSignature = getProviderConfig(exchangeType, provider);
+ const providerNameAndSignature = await getProviderConfig(exchangeType, provider);
if (!providerNameAndSignature) throw new Error("Could not get provider infos");
diff --git a/libs/ledger-live-common/src/exchange/providers/getProvidersData.test.ts b/libs/ledger-live-common/src/exchange/providers/getProvidersData.test.ts
new file mode 100644
index 000000000000..1f1ceef705dc
--- /dev/null
+++ b/libs/ledger-live-common/src/exchange/providers/getProvidersData.test.ts
@@ -0,0 +1,106 @@
+import { transformData, getProvidersData, ProvidersDataResponse } from "./getProvidersData";
+import network from "@ledgerhq/live-network";
+
+type TransformedProviderData = {
+ name: string;
+ publicKey: {
+ curve: string;
+ data: Buffer;
+ };
+ signature: Buffer;
+};
+
+jest.mock("@ledgerhq/live-network");
+
+describe("transformData", () => {
+ it("should transform providers data correctly", () => {
+ const providersData: ProvidersDataResponse = [
+ {
+ name: "ProviderA",
+ signature: "a1b2c3",
+ public_key: "1234567890abcdef",
+ public_key_curve: "secp256k1",
+ },
+ {
+ name: "ProviderB",
+ signature: "d4e5f6",
+ public_key: "abcdef1234567890",
+ public_key_curve: "secp256r1",
+ },
+ ];
+
+ const expected: Record = {
+ providera: {
+ name: "ProviderA",
+ publicKey: {
+ curve: "secp256k1",
+ data: Buffer.from("1234567890abcdef", "hex"),
+ },
+ signature: Buffer.from("a1b2c3", "hex"),
+ },
+ providerb: {
+ name: "ProviderB",
+ publicKey: {
+ curve: "secp256r1",
+ data: Buffer.from("abcdef1234567890", "hex"),
+ },
+ signature: Buffer.from("d4e5f6", "hex"),
+ },
+ };
+
+ const result = transformData(providersData);
+ expect(result).toEqual(expected);
+ });
+
+ it("should handle empty providers data", () => {
+ const providersData: ProvidersDataResponse = [];
+
+ const expected: Record = {};
+
+ const result = transformData(providersData);
+ expect(result).toEqual(expected);
+ });
+});
+
+describe("getProvidersData", () => {
+ it("should fetch and transform providers data", async () => {
+ const mockProvidersData: ProvidersDataResponse = [
+ {
+ name: "ProviderA",
+ signature: "a1b2c3",
+ public_key: "1234567890abcdef",
+ public_key_curve: "secp256k1",
+ },
+ ];
+
+ (network as jest.Mock).mockResolvedValue({ data: mockProvidersData });
+
+ const result = await getProvidersData("someType");
+
+ expect(network).toHaveBeenCalledWith({
+ method: "GET",
+ url: "https://crypto-assets-service.api.ledger.com/v1/partners",
+ params: {
+ output: "name,signature,public_key,public_key_curve",
+ service_name: "someType",
+ },
+ });
+
+ expect(result).toEqual({
+ providera: {
+ name: "ProviderA",
+ publicKey: {
+ curve: "secp256k1",
+ data: Buffer.from("1234567890abcdef", "hex"),
+ },
+ signature: Buffer.from("a1b2c3", "hex"),
+ },
+ });
+ });
+
+ it("should handle errors when fetching data", async () => {
+ (network as jest.Mock).mockRejectedValue(new Error("Network error"));
+
+ await expect(getProvidersData("someType")).rejects.toThrow("Network error");
+ });
+});
diff --git a/libs/ledger-live-common/src/exchange/providers/getProvidersData.ts b/libs/ledger-live-common/src/exchange/providers/getProvidersData.ts
new file mode 100644
index 000000000000..ac00655ed859
--- /dev/null
+++ b/libs/ledger-live-common/src/exchange/providers/getProvidersData.ts
@@ -0,0 +1,42 @@
+import { ExchangeProviderNameAndSignature } from ".";
+import network from "@ledgerhq/live-network";
+
+export type ProvidersDataResponse = {
+ name: string;
+ signature: string;
+ public_key: string;
+ public_key_curve: string;
+}[];
+
+export function transformData(
+ providersData: ProvidersDataResponse,
+): Record {
+ const transformed = {};
+ providersData.forEach(provider => {
+ const key = provider.name.toLowerCase();
+ transformed[key] = {
+ name: provider.name,
+ publicKey: {
+ curve: provider.public_key_curve,
+ data: Buffer.from(provider.public_key, "hex"),
+ },
+ signature: Buffer.from(provider.signature, "hex"),
+ };
+ });
+ return transformed;
+}
+
+export const getProvidersData = async (
+ type,
+): Promise> => {
+ const { data: providersData } = await network({
+ method: "GET",
+ url: "https://crypto-assets-service.api.ledger.com/v1/partners",
+ params: {
+ output: "name,signature,public_key,public_key_curve",
+ service_name: type,
+ },
+ });
+
+ return transformData(providersData);
+};
diff --git a/libs/ledger-live-common/src/exchange/providers/index.ts b/libs/ledger-live-common/src/exchange/providers/index.ts
index da7bbef7e93e..e0ce5b22e165 100644
--- a/libs/ledger-live-common/src/exchange/providers/index.ts
+++ b/libs/ledger-live-common/src/exchange/providers/index.ts
@@ -26,10 +26,10 @@ export function convertToAppExchangePartnerKey(
};
}
-export const getProviderConfig = (
+export const getProviderConfig = async (
exchangeType: ExchangeTypes,
providerName: string,
-): ExchangeProviderNameAndSignature => {
+): Promise => {
if (getEnv("MOCK_EXCHANGE_TEST_CONFIG") && testProvider) {
return testProvider;
}
@@ -41,7 +41,7 @@ export const getProviderConfig = (
case ExchangeTypes.Sell:
case ExchangeTypes.SellNg:
- return getSellProvider(providerName.toLowerCase());
+ return await getSellProvider(providerName.toLowerCase());
default:
throw new Error(`Unknown partner ${providerName} type`);
diff --git a/libs/ledger-live-common/src/exchange/providers/sell.ts b/libs/ledger-live-common/src/exchange/providers/sell.ts
index d820242d6b22..07a8ae3924b4 100644
--- a/libs/ledger-live-common/src/exchange/providers/sell.ts
+++ b/libs/ledger-live-common/src/exchange/providers/sell.ts
@@ -1,5 +1,6 @@
import { getEnv } from "@ledgerhq/live-env";
import { ExchangeProviderNameAndSignature } from ".";
+import { getProvidersData } from "./getProvidersData";
const testSellProvider: ExchangeProviderNameAndSignature = {
name: "SELL_TEST",
@@ -17,33 +18,45 @@ const testSellProvider: ExchangeProviderNameAndSignature = {
version: 2,
};
-const sellProviders: Record = {
- coinify: {
- name: "Coinify",
- publicKey: {
- curve: "secp256r1",
- data: Buffer.from(
- "04CEA7DC8B189FA0D7A5A97530B50556CB0C14079C39CD44532D7037F2B96F0FA9C2DE588E1840B351B71114EE4021FC260F790A6F2D0CDF1C3E1899CCF97D3CCB",
- "hex",
- ),
- },
- signature: Buffer.from(
- "3043021f023ecbbb1dfd44f390944bd1f6c039942943009a51ca4f134589441476651a02200cbfdf2ebe32eb0b0a88be9b1fec343ed5b230a69e65a1d15b4e34ef4206a9dd",
- "hex",
- ),
- },
+let providerDataCache: Record | null = null;
+
+/**
+ * The result is cached after the first successful fetch to avoid redundant network calls.
+ * - "fund" providers currently include Mercuryo, which is stored as a fund provider.
+ * If this behavior changes, this logic should be revisited.
+ * Reference: https://github.dev/LedgerHQ/crypto-assets/blob/main/assets/partners/mercuryo/common.json
+ */
+export const fetchAndMergeProviderData = async () => {
+ if (providerDataCache) {
+ return providerDataCache;
+ }
+ try {
+ const [sellProvidersData, fundProviderData] = await Promise.all([
+ getProvidersData("sell"),
+ getProvidersData("fund"), // Mercuryo is currently treated as a fund provider
+ ]);
+ providerDataCache = { ...sellProvidersData, ...fundProviderData };
+ return providerDataCache;
+ } catch (error) {
+ console.error("Error fetching or processing provider data:", error);
+ }
};
-export const getSellProvider = (providerName: string): ExchangeProviderNameAndSignature => {
+export const getSellProvider = async (
+ providerName: string,
+): Promise => {
if (getEnv("MOCK_EXCHANGE_TEST_CONFIG")) {
return testSellProvider;
}
-
- const res = sellProviders[providerName.toLowerCase()];
+ const res = await fetchAndMergeProviderData();
if (!res) {
+ throw new Error("Failed to fetch provider data");
+ }
+ const provider = res[providerName.toLowerCase()];
+ if (!provider) {
throw new Error(`Unknown partner ${providerName}`);
}
- return res;
+ return provider;
};
diff --git a/libs/ledger-live-common/src/exchange/providers/swap.ts b/libs/ledger-live-common/src/exchange/providers/swap.ts
index 9338a69f9c1d..3a18b928e18f 100644
--- a/libs/ledger-live-common/src/exchange/providers/swap.ts
+++ b/libs/ledger-live-common/src/exchange/providers/swap.ts
@@ -1,5 +1,6 @@
import { ExchangeProviderNameAndSignature } from ".";
import { isIntegrationTestEnv } from "../swap/utils/isIntegrationTestEnv";
+import { getProvidersData } from "./getProvidersData";
import network from "@ledgerhq/live-network";
export type SwapProviderConfig = {
@@ -185,22 +186,6 @@ type CurrencyData = {
signature: string;
};
-type ProvidersDataResponse = {
- name: string;
- signature: string;
- public_key: string;
- public_key_curve: string;
-}[];
-
-type ProviderData = {
- name: string;
- publicKey: {
- curve: string;
- data: Buffer;
- };
- signature: Buffer;
-};
-
let providerDataCache: Record | null = null;
export const getSwapProvider = async (
@@ -215,35 +200,6 @@ export const getSwapProvider = async (
return res[providerName.toLowerCase()];
};
-function transformData(providersData: ProvidersDataResponse): Record {
- const transformed = {};
- providersData.forEach(provider => {
- const key = provider.name.toLowerCase();
- transformed[key] = {
- name: provider.name,
- publicKey: {
- curve: provider.public_key_curve,
- data: Buffer.from(provider.public_key, "hex"),
- },
- signature: Buffer.from(provider.signature, "hex"),
- };
- });
- return transformed;
-}
-
-export const getProvidersData = async (): Promise> => {
- const { data: providersData } = await network({
- method: "GET",
- url: "https://crypto-assets-service.api.ledger.com/v1/partners",
- params: {
- output: "name,signature,public_key,public_key_curve",
- service_name: "swap",
- },
- });
-
- return transformData(providersData);
-};
-
/**
* Retrieves the currency data for a given ID
* @param currencyId The unique identifier for the currency.
@@ -285,7 +241,7 @@ export const fetchAndMergeProviderData = async () => {
try {
const [providersData, providersExtraData] = await Promise.all([
- getProvidersData(),
+ getProvidersData("swap"),
getProvidersCDNData(),
]);
diff --git a/libs/ledger-live-common/src/families/evm/config.ts b/libs/ledger-live-common/src/families/evm/config.ts
index 25e97982bfbc..dd667bf20277 100644
--- a/libs/ledger-live-common/src/families/evm/config.ts
+++ b/libs/ledger-live-common/src/families/evm/config.ts
@@ -550,8 +550,8 @@ const evmConfig: CurrencyLiveConfigDefinition = {
uri: "https://zkevm-rpc.com",
},
explorer: {
- type: "etherscan",
- uri: "https://api-zkevm.polygonscan.com/api",
+ type: "blockscout",
+ uri: "https://zkevm.blockscout.com/api",
},
},
},
@@ -566,8 +566,8 @@ const evmConfig: CurrencyLiveConfigDefinition = {
uri: "https://developer-access-mainnet.base.org",
},
explorer: {
- type: "etherscan",
- uri: "https://api.basescan.org/api",
+ type: "blockscout",
+ uri: "https://base.blockscout.com/api",
},
},
},
diff --git a/libs/ledger-live-common/src/families/near/__snapshots__/bridge.integration.test.ts.snap b/libs/ledger-live-common/src/families/near/__snapshots__/bridge.integration.test.ts.snap
index e987b6ac5b45..966e6cc2e0a3 100644
--- a/libs/ledger-live-common/src/families/near/__snapshots__/bridge.integration.test.ts.snap
+++ b/libs/ledger-live-common/src/families/near/__snapshots__/bridge.integration.test.ts.snap
@@ -18,7 +18,6 @@ exports[`near currency bridge scanAccounts near seed 1 1`] = `
{
"available": "10000000000000000001",
"pending": "0",
- "rewards": "10000000000000000001",
"staked": "0",
"validatorId": "figment.poolv1.near",
},
diff --git a/libs/ledger-live-common/src/families/near/banner.test.ts b/libs/ledger-live-common/src/families/near/banner.test.ts
index 78852671f140..e6b54dc19a0f 100644
--- a/libs/ledger-live-common/src/families/near/banner.test.ts
+++ b/libs/ledger-live-common/src/families/near/banner.test.ts
@@ -125,7 +125,6 @@ describe("near/banner", () => {
staked: new BigNumber("1.29802125309300073830514e+23"),
available: new BigNumber("1"),
pending: new BigNumber("0"),
- rewards: new BigNumber("1.462125309300073830515e+21"),
validatorId: "vcap.poolv1.near",
};
jest.spyOn(preloadedData, "getCurrentNearPreloadData").mockReturnValue(validatorsMap);
diff --git a/libs/ledger-live-common/src/wallet-api/constants.ts b/libs/ledger-live-common/src/wallet-api/constants.ts
index dec4009fd284..d5cf195b1f53 100644
--- a/libs/ledger-live-common/src/wallet-api/constants.ts
+++ b/libs/ledger-live-common/src/wallet-api/constants.ts
@@ -49,4 +49,6 @@ export const BUY_SELL_UI_APP_ID = "buy-sell-ui";
export const CARD_APP_ID = "card-program";
+export const WC_ID = "ledger-wallet-connect";
+
export const INTERNAL_APP_IDS = [DEFAULT_MULTIBUY_APP_ID, BUY_SELL_UI_APP_ID, CARD_APP_ID];
diff --git a/libs/ledgerjs/packages/context-module/package.json b/libs/ledgerjs/packages/context-module/package.json
index a455812e585a..464e14501e65 100644
--- a/libs/ledgerjs/packages/context-module/package.json
+++ b/libs/ledgerjs/packages/context-module/package.json
@@ -2,6 +2,7 @@
"name": "@ledgerhq/context-module",
"version": "0.1.2",
"description": "Ledger context module",
+ "private": true,
"files": [
"./lib"
],
@@ -29,4 +30,4 @@
"axios": "1.7.7",
"ethers": "^5.7.2"
}
-}
\ No newline at end of file
+}
diff --git a/libs/ledgerjs/packages/keyring-eth/package.json b/libs/ledgerjs/packages/keyring-eth/package.json
index fa0e36fc0280..df8deaabfa34 100644
--- a/libs/ledgerjs/packages/keyring-eth/package.json
+++ b/libs/ledgerjs/packages/keyring-eth/package.json
@@ -2,6 +2,7 @@
"name": "@ledgerhq/keyring-eth",
"version": "0.2.4",
"description": "Ledger keyring eth module",
+ "private": true,
"files": [
"./lib"
],
diff --git a/libs/live-nft-react/src/hooks/useFetchOrdinals.ts b/libs/live-nft-react/src/hooks/useFetchOrdinals.ts
index 0464912acbba..dac2bd116ac0 100644
--- a/libs/live-nft-react/src/hooks/useFetchOrdinals.ts
+++ b/libs/live-nft-react/src/hooks/useFetchOrdinals.ts
@@ -6,9 +6,15 @@ import { FetchNftsProps, OrdinalsChainsEnum } from "./types";
import { NFTS_QUERY_KEY } from "../queryKeys";
import { processOrdinals } from "./helpers/ordinals";
-type Result = UseInfiniteQueryResult, Error> & {
+export interface GroupedNftOrdinals {
+ rareSat: SimpleHashNft;
+ inscription: SimpleHashNft;
+}
+
+export type Result = UseInfiniteQueryResult, Error> & {
rareSats: SimpleHashNft[];
inscriptions: SimpleHashNft[];
+ inscriptionsGroupedWithRareSats: GroupedNftOrdinals[];
};
export function useFetchOrdinals({ addresses, threshold }: FetchNftsProps): Result {
@@ -35,6 +41,18 @@ export function useFetchOrdinals({ addresses, threshold }: FetchNftsProps): Resu
const { rareSats, inscriptions } = useMemo(() => processOrdinals(nfts), [nfts]);
+ const inscriptionsGroupedWithRareSats: GroupedNftOrdinals[] = [];
+
+ rareSats.forEach(sat => {
+ const inscription = inscriptions.find(inscription => {
+ const location = inscription.extra_metadata?.ordinal_details?.location?.split(":")[0];
+ return location === sat.contract_address;
+ });
+ if (inscription) {
+ inscriptionsGroupedWithRareSats.push({ rareSat: sat, inscription });
+ }
+ });
+
useEffect(() => {
if (queryResult.hasNextPage && !queryResult.isFetchingNextPage) {
queryResult.fetchNextPage();
@@ -45,5 +63,6 @@ export function useFetchOrdinals({ addresses, threshold }: FetchNftsProps): Resu
...queryResult,
rareSats,
inscriptions,
+ inscriptionsGroupedWithRareSats,
};
}
diff --git a/libs/live-nft/src/api/types.ts b/libs/live-nft/src/api/types.ts
index 4491f38c75da..acddedd2dda2 100644
--- a/libs/live-nft/src/api/types.ts
+++ b/libs/live-nft/src/api/types.ts
@@ -68,6 +68,46 @@ export interface preview {
readonly predominate_color: string;
}
+export interface video_properties {
+ readonly audio_coding: string;
+ readonly duration: number;
+ readonly height: number;
+ readonly mime_type: string;
+ readonly size: number;
+ readonly video_coding: string;
+ readonly width: number;
+}
+
+export interface owner {
+ readonly first_acquired_date: string;
+ readonly last_acquired_date: string;
+ readonly owner_address: string;
+ readonly quantity: number;
+ readonly quantity_string: string;
+}
+
+export interface collection {
+ readonly name: string;
+ readonly spam_score: number;
+ readonly description?: string;
+}
+
+export interface firstCreated {
+ readonly block_number: number;
+ readonly minted_to: string;
+ readonly quantity: number;
+ readonly quantity_string: string;
+ readonly timestamp: string;
+ readonly transaction: string;
+ readonly transaction_initiator: string;
+}
+
+export interface attribute {
+ readonly trait_type: string;
+ readonly value: string;
+ readonly display_type: string;
+}
+
export interface SimpleHashNft {
readonly nft_id: string;
readonly chain: string;
@@ -79,10 +119,11 @@ export interface SimpleHashNft {
readonly token_count: number;
readonly previews?: preview;
readonly other_url?: string;
- readonly collection: {
- readonly name: string;
- readonly spam_score: number;
- };
+ readonly video_url?: string;
+ readonly video_properties?: video_properties;
+ readonly owners?: owner[];
+ readonly collection: collection;
+ readonly first_created?: firstCreated;
readonly contract: {
readonly type: string;
readonly name?: string;
@@ -91,6 +132,7 @@ export interface SimpleHashNft {
readonly ledger_metadata?: {
readonly ledger_stax_image: string;
};
+ readonly attributes?: attribute[];
readonly utxo_details?: UtxoDetails;
readonly ordinal_details?: ordinal_details;
readonly image_original_url: string;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 5943e68ee2e1..9107c46762e3 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -536,8 +536,8 @@ importers:
specifier: ^7.1.3
version: 7.5.4
storyly-web:
- specifier: ^2.8.0
- version: 2.16.5
+ specifier: ^3.5.0
+ version: 3.6.7
styled-components:
specifier: ^5.3.3
version: 5.3.11(react-dom@18.2.0(react@18.2.0))(react-is@17.0.2)(react@18.2.0)
@@ -12347,10 +12347,6 @@ packages:
resolution: {integrity: sha512-yqcRVnjf+qS+tC4NxOKLJOaSJ+csHmh/dHUzvCTkf5rLsplwXYRnny2r0tqGTQ4tuXMxwgSMKPYwicg81P+xuw==}
engines: {node: '>=12'}
- '@sentry-internal/feedback@7.109.0':
- resolution: {integrity: sha512-EL7N++poxvJP9rYvh6vSu24tsKkOveNCcCj4IM7+irWPjsuD2GLYYlhp/A/Mtt9l7iqO4plvtiQU5HGk7smcTQ==}
- engines: {node: '>=12'}
-
'@sentry-internal/feedback@8.16.0':
resolution: {integrity: sha512-BmRazZKl6iiVSg6eybUNOI1ve4eZqYpJYjkX48Jedn+7iZg7z12MNYl6IWPFBcN+sg+clf4wiKDr/SYS0yNemQ==}
engines: {node: '>=14.18'}
@@ -12359,10 +12355,6 @@ packages:
resolution: {integrity: sha512-TnqxqJGhbFhhYRhTG2WLFer+lVieV7mNGeIxFBiw1L4kuj8KGl+C0sknssKyZSRVJFSahhHIosHJGRMkkD//7g==}
engines: {node: '>=12'}
- '@sentry-internal/replay-canvas@7.109.0':
- resolution: {integrity: sha512-Lh/K60kmloR6lkPUcQP0iamw7B/MdEUEx/ImAx4tUSMrLj+IoUEcq/ECgnnVyQkJq59+8nPEKrVLt7x6PUPEjw==}
- engines: {node: '>=12'}
-
'@sentry-internal/replay-canvas@8.16.0':
resolution: {integrity: sha512-Bjh6pCDLZIPAPU2dNvJfI7BQV16rsRtYcylJgkGamjf8IcaBu7r/Whsvt1q34xO29xc0ISlp+0xG+YAdN1690Q==}
engines: {node: '>=14.18'}
@@ -12375,18 +12367,10 @@ packages:
resolution: {integrity: sha512-+u9RRf5eL3StiyiRyAHZmdkAR7GTSGx4Mt4Lmi5NEtCcWlTGZ1QgW2r8ZbhouVmTiJkjhQgYCyej3cojtazeJg==}
engines: {node: '>=8'}
- '@sentry-internal/tracing@7.109.0':
- resolution: {integrity: sha512-PzK/joC5tCuh2R/PRh+7dp+uuZl7pTsBIjPhVZHMTtb9+ls65WkdZJ1/uKXPouyz8NOo9Xok7aEvEo9seongyw==}
- engines: {node: '>=8'}
-
'@sentry/browser@7.100.1':
resolution: {integrity: sha512-IxHQ08ixf0bmaWpe4yt1J4UUsOpg02fxax9z3tOQYXw5MSzz5pDXn8M8DFUVJB3wWuyXhHXTub9yD3VIP9fnoA==}
engines: {node: '>=8'}
- '@sentry/browser@7.109.0':
- resolution: {integrity: sha512-yx+OFG+Ab9qUDDgV9ZDv8M9O9Mqr0fjKta/LMlWALYLjzkMvxsPlRPFj7oMBlHqOTVLDeg7lFYmsA8wyWQ8Z8g==}
- engines: {node: '>=8'}
-
'@sentry/browser@8.16.0':
resolution: {integrity: sha512-8Fxmk2aFWRixi2IKixiJR10Du34yb13HYr2iRw1haPKb5ZKa6CFA+XAnSzwpPZxO0RSHuPQR06YNkXaQ8fRAQQ==}
engines: {node: '>=14.18'}
@@ -12491,10 +12475,6 @@ packages:
resolution: {integrity: sha512-f+ItUge/o9AjlveQq0ZUbQauKlPH1FIJbC1TRaYLJ4KNfOdrsh8yZ29RmWv0cFJ/e+FGTr603gWpRPObF5rM8Q==}
engines: {node: '>=8'}
- '@sentry/core@7.109.0':
- resolution: {integrity: sha512-xwD4U0IlvvlE/x/g/W1I8b4Cfb16SsCMmiEuBf6XxvAa3OfWBxKoqLifb3GyrbxMC4LbIIZCN/SvLlnGJPgszA==}
- engines: {node: '>=8'}
-
'@sentry/core@8.16.0':
resolution: {integrity: sha512-l9mQgm5OqnykvZMh6PmJ/9ygW4qLyEFop+pQH/uM5zQCZQvEa7rvAd9QXKHdbVKq1CxJa/nJiByc8wPWxsftGQ==}
engines: {node: '>=14.18'}
@@ -12557,10 +12537,6 @@ packages:
resolution: {integrity: sha512-B1NFjzGEFaqejxBRdUyEzH8ChXc2kfiqlA/W/Lg0aoWIl2/7nuMk+l4ld9gW5F5bIAXDTVd5vYltb1lWEbpr7w==}
engines: {node: '>=12'}
- '@sentry/replay@7.109.0':
- resolution: {integrity: sha512-hCDjbTNO7ErW/XsaBXlyHFsUhneyBUdTec1Swf98TFEfVqNsTs6q338aUcaR8dGRLbLrJ9YU9D1qKq++v5h2CA==}
- engines: {node: '>=12'}
-
'@sentry/types@6.19.7':
resolution: {integrity: sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg==}
engines: {node: '>=6'}
@@ -12569,10 +12545,6 @@ packages:
resolution: {integrity: sha512-fLM+LedHuKzOd8IhXBqaQuym+AA519MGjeczBa5kGakes/BbAsUMwsNfjsKQedp7Kh44RgYF99jwoRPK2oDrXw==}
engines: {node: '>=8'}
- '@sentry/types@7.109.0':
- resolution: {integrity: sha512-egCBnDv3YpVFoNzRLdP0soVrxVLCQ+rovREKJ1sw3rA2/MFH9WJ+DZZexsX89yeAFzy1IFsCp7/dEqudusml6g==}
- engines: {node: '>=8'}
-
'@sentry/types@8.16.0':
resolution: {integrity: sha512-cIRsn7gWGVaWHgCniBWA0N8PNwzDYibhjyjPRTMxUjuZCT37i7zxByKKmd9u4TpRIJ64MyirNyM0O6T0A26fpg==}
engines: {node: '>=14.18'}
@@ -12585,10 +12557,6 @@ packages:
resolution: {integrity: sha512-Ve6dXr1o6xiBe3VCoJgiutmBKrugryI65EZAbYto5XI+t+PjiLLf9wXtEMF24ZrwImo4Lv3E9Uqza+fWkEbw6A==}
engines: {node: '>=8'}
- '@sentry/utils@7.109.0':
- resolution: {integrity: sha512-3RjxMOLMBwZ5VSiH84+o/3NY2An4Zldjz0EbfEQNRY9yffRiCPJSQiCJID8EoylCFOh/PAhPimBhqbtWJxX6iw==}
- engines: {node: '>=8'}
-
'@sentry/utils@8.16.0':
resolution: {integrity: sha512-tltCf2DVzz5TiYjxu/Rxbc9Qmm04893MFshV97jOTBcQeO2AAZBEl5rAoTCv1P08y7Yg+KiVwCx9Zj2x5U80/g==}
engines: {node: '>=14.18'}
@@ -17792,9 +17760,6 @@ packages:
resolution: {integrity: sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==}
engines: {node: '>= 0.8'}
- copy-to-clipboard@3.3.3:
- resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==}
-
copy-to@2.0.1:
resolution: {integrity: sha512-3DdaFaU/Zf1AnpLiFDeNCD4TOWe3Zl2RZaTzUvWiIk5ERzcCodOE20Vqq4fzCbNoHURFHT4/us/Lfq+S2zyY4w==}
@@ -18567,9 +18532,6 @@ packages:
des.js@1.1.0:
resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==}
- desandro-matches-selector@2.0.2:
- resolution: {integrity: sha512-+1q0nXhdzg1IpIJdMKalUwvvskeKnYyEe3shPRwedNcWtnhEKT3ZxvFjzywHDeGcKViIxTCAoOYQWP1qD7VNyg==}
-
destroy@1.2.0:
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
@@ -19487,9 +19449,6 @@ packages:
resolution: {integrity: sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==}
engines: {node: '>=6.5.0', npm: '>=3'}
- ev-emitter@1.1.1:
- resolution: {integrity: sha512-ipiDYhdQSCZ4hSbX4rMW+XzNKMD1prg/sTvoVmSLkuQ1MVlwjJQQA+sW8tMYR3BLUr9KjodFV4pvzunvRhd33Q==}
-
event-emitter@0.3.5:
resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==}
@@ -20324,9 +20283,6 @@ packages:
firebase@9.23.0:
resolution: {integrity: sha512-/4lUVY0lUvBDIaeY1q6dUYhS8Sd18Qb9CgWkPZICUo9IXpJNCEagfNZXBBFCkMTTN5L5gx2Hjr27y21a9NzUcA==}
- fizzy-ui-utils@2.0.7:
- resolution: {integrity: sha512-CZXDVXQ1If3/r8s0T+v+qVeMshhfcuq0rqIFgJnrtd+Bu8GmDmqMjntjUePypVtjHXKJ6V4sw9zeyox34n9aCg==}
-
flat-cache@3.2.0:
resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==}
engines: {node: ^10.12.0 || >=12.0.0}
@@ -20631,9 +20587,6 @@ packages:
resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==}
engines: {node: '>=8'}
- get-size@2.0.3:
- resolution: {integrity: sha512-lXNzT/h/dTjTxRbm9BXb+SGxxzkm97h/PCIKtlN/CBCxxmkkIVV21udumMS93MuVTDX583gqc94v3RjuHmI+2Q==}
-
get-stream@3.0.0:
resolution: {integrity: sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==}
engines: {node: '>=4'}
@@ -21866,12 +21819,6 @@ packages:
peerDependencies:
ws: '*'
- isotope-horizontal@2.0.1:
- resolution: {integrity: sha512-rTm8K3d3Xt/pQBOdZj06IJpNkAjL+my4SWQLxJhjnxWTdDEX+r5qBsdstOou9kIvJFkgLYaR+YOog+7en6kHXw==}
-
- isotope-layout@3.0.6:
- resolution: {integrity: sha512-z2ZKablhocXhoNyWwzJPFd7u7FWbYbVJA51Nvsqsod8jH2ExGc1SwDsSWKE54e3PhXzqf2yZPhFSq/c2MR1arw==}
-
isstream@0.1.2:
resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==}
@@ -23460,9 +23407,6 @@ packages:
marky@1.2.5:
resolution: {integrity: sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q==}
- masonry-layout@4.2.2:
- resolution: {integrity: sha512-iGtAlrpHNyxaR19CvKC3npnEcAwszXoyJiI8ARV2ePi7fmYhIud25MHK8Zx4P0LCC4d3TNO9+rFa1KoK1OEOaA==}
-
match-sorter@6.3.4:
resolution: {integrity: sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==}
@@ -24719,9 +24663,6 @@ packages:
outdent@0.5.0:
resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==}
- outlayer@2.1.1:
- resolution: {integrity: sha512-+GplXsCQ3VrbGujAeHEzP9SXsBmJxzn/YdDSQZL0xqBmAWBmortu2Y9Gwdp9J0bgDQ8/YNIPMoBM13nTwZfAhw==}
-
outvariant@1.4.2:
resolution: {integrity: sha512-Ou3dJ6bA/UJ5GVHxah4LnqDwZRwAmWxrG3wtrHrbGnP4RnLCtA64A4F+ae7Y8ww660JaddSoArUR5HjipWSHAQ==}
@@ -27756,8 +27697,8 @@ packages:
prop-types:
optional: true
- storyly-web@2.16.5:
- resolution: {integrity: sha512-5kb3krdsjNCiALAVWBFDfPtj6EVODujalCgppzD1eUgjByfdYC5+yTyqblUlk1AAd9evGzaxqhHut0W4Ph8LLA==}
+ storyly-web@3.6.7:
+ resolution: {integrity: sha512-2i7yOUF4xENFynQwDGZmGWLKHcXeTjhxJwtD8g4Vj163GI9Zw8WfLwsYgSZWgP1mMC+vq3iP+l8TgHuBd/r2rg==}
stream-browserify@3.0.0:
resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==}
@@ -28452,9 +28393,6 @@ packages:
tocbot@4.25.0:
resolution: {integrity: sha512-kE5wyCQJ40hqUaRVkyQ4z5+4juzYsv/eK+aqD97N62YH0TxFhzJvo22RUQQZdO3YnXAk42ZOfOpjVdy+Z0YokA==}
- toggle-selection@1.0.6:
- resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==}
-
toidentifier@1.0.0:
resolution: {integrity: sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==}
engines: {node: '>=0.6'}
@@ -33977,7 +33915,7 @@ snapshots:
'@emotion/core@10.3.1(react@18.2.0)':
dependencies:
- '@babel/runtime': 7.24.1
+ '@babel/runtime': 7.25.0
'@emotion/cache': 10.0.29
'@emotion/css': 10.0.27
'@emotion/serialize': 0.11.16
@@ -39659,12 +39597,6 @@ snapshots:
'@sentry/types': 7.100.1
'@sentry/utils': 7.100.1
- '@sentry-internal/feedback@7.109.0':
- dependencies:
- '@sentry/core': 7.109.0
- '@sentry/types': 7.109.0
- '@sentry/utils': 7.109.0
-
'@sentry-internal/feedback@8.16.0':
dependencies:
'@sentry/core': 8.16.0
@@ -39678,13 +39610,6 @@ snapshots:
'@sentry/types': 7.100.1
'@sentry/utils': 7.100.1
- '@sentry-internal/replay-canvas@7.109.0':
- dependencies:
- '@sentry/core': 7.109.0
- '@sentry/replay': 7.109.0
- '@sentry/types': 7.109.0
- '@sentry/utils': 7.109.0
-
'@sentry-internal/replay-canvas@8.16.0':
dependencies:
'@sentry-internal/replay': 8.16.0
@@ -39705,12 +39630,6 @@ snapshots:
'@sentry/types': 7.100.1
'@sentry/utils': 7.100.1
- '@sentry-internal/tracing@7.109.0':
- dependencies:
- '@sentry/core': 7.109.0
- '@sentry/types': 7.109.0
- '@sentry/utils': 7.109.0
-
'@sentry/browser@7.100.1':
dependencies:
'@sentry-internal/feedback': 7.100.1
@@ -39721,16 +39640,6 @@ snapshots:
'@sentry/types': 7.100.1
'@sentry/utils': 7.100.1
- '@sentry/browser@7.109.0':
- dependencies:
- '@sentry-internal/feedback': 7.109.0
- '@sentry-internal/replay-canvas': 7.109.0
- '@sentry-internal/tracing': 7.109.0
- '@sentry/core': 7.109.0
- '@sentry/replay': 7.109.0
- '@sentry/types': 7.109.0
- '@sentry/utils': 7.109.0
-
'@sentry/browser@8.16.0':
dependencies:
'@sentry-internal/browser-utils': 8.16.0
@@ -39834,11 +39743,6 @@ snapshots:
'@sentry/types': 7.100.1
'@sentry/utils': 7.100.1
- '@sentry/core@7.109.0':
- dependencies:
- '@sentry/types': 7.109.0
- '@sentry/utils': 7.109.0
-
'@sentry/core@8.16.0':
dependencies:
'@sentry/types': 8.16.0
@@ -39974,19 +39878,10 @@ snapshots:
'@sentry/types': 7.100.1
'@sentry/utils': 7.100.1
- '@sentry/replay@7.109.0':
- dependencies:
- '@sentry-internal/tracing': 7.109.0
- '@sentry/core': 7.109.0
- '@sentry/types': 7.109.0
- '@sentry/utils': 7.109.0
-
'@sentry/types@6.19.7': {}
'@sentry/types@7.100.1': {}
- '@sentry/types@7.109.0': {}
-
'@sentry/types@8.16.0': {}
'@sentry/utils@6.19.7':
@@ -39998,10 +39893,6 @@ snapshots:
dependencies:
'@sentry/types': 7.100.1
- '@sentry/utils@7.109.0':
- dependencies:
- '@sentry/types': 7.109.0
-
'@sentry/utils@8.16.0':
dependencies:
'@sentry/types': 8.16.0
@@ -46240,7 +46131,7 @@ snapshots:
babel-plugin-macros@2.8.0:
dependencies:
- '@babel/runtime': 7.24.1
+ '@babel/runtime': 7.25.0
cosmiconfig: 6.0.0
resolve: 1.22.8
@@ -47833,10 +47724,6 @@ snapshots:
depd: 2.0.0
keygrip: 1.1.0
- copy-to-clipboard@3.3.3:
- dependencies:
- toggle-selection: 1.0.6
-
copy-to@2.0.1: {}
copy-webpack-plugin@10.2.4(webpack@5.91.0(metro@0.80.8)):
@@ -48685,7 +48572,7 @@ snapshots:
date-fns@2.30.0:
dependencies:
- '@babel/runtime': 7.24.1
+ '@babel/runtime': 7.25.0
dateformat@4.6.3: {}
@@ -48904,8 +48791,6 @@ snapshots:
inherits: 2.0.4
minimalistic-assert: 1.0.1
- desandro-matches-selector@2.0.2: {}
-
destroy@1.2.0: {}
detect-indent@6.1.0: {}
@@ -49144,7 +49029,7 @@ snapshots:
dom-helpers@5.2.1:
dependencies:
- '@babel/runtime': 7.24.1
+ '@babel/runtime': 7.25.0
csstype: 3.1.3
dom-serializer@0.2.2:
@@ -49799,8 +49684,8 @@ snapshots:
'@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.2.2)
eslint: 8.57.0
eslint-import-resolver-node: 0.3.9
- eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
- eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
+ eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
+ eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
eslint-plugin-jsx-a11y: 6.8.0(eslint@8.57.0)
eslint-plugin-react: 7.34.1(eslint@8.57.0)
eslint-plugin-react-hooks: 4.6.0(eslint@8.57.0)
@@ -49857,23 +49742,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0):
- dependencies:
- debug: 4.3.4
- enhanced-resolve: 5.16.0
- eslint: 8.57.0
- eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0)
- eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
- fast-glob: 3.3.2
- get-tsconfig: 4.7.5
- is-core-module: 2.13.1
- is-glob: 4.0.3
- transitivePeerDependencies:
- - '@typescript-eslint/parser'
- - eslint-import-resolver-node
- - eslint-import-resolver-webpack
- - supports-color
-
eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0):
dependencies:
debug: 4.3.4
@@ -49918,17 +49786,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0):
- dependencies:
- debug: 3.2.7
- optionalDependencies:
- '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.2.2)
- eslint: 8.57.0
- eslint-import-resolver-node: 0.3.9
- eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
- transitivePeerDependencies:
- - supports-color
-
eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0):
dependencies:
debug: 3.2.7
@@ -50018,33 +49875,6 @@ snapshots:
- eslint-import-resolver-webpack
- supports-color
- eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
- dependencies:
- array-includes: 3.1.8
- array.prototype.findlastindex: 1.2.5
- array.prototype.flat: 1.3.2
- array.prototype.flatmap: 1.3.2
- debug: 3.2.7
- doctrine: 2.1.0
- eslint: 8.57.0
- eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0)
- hasown: 2.0.2
- is-core-module: 2.13.1
- is-glob: 4.0.3
- minimatch: 3.1.2
- object.fromentries: 2.0.8
- object.groupby: 1.0.3
- object.values: 1.2.0
- semver: 6.3.1
- tsconfig-paths: 3.15.0
- optionalDependencies:
- '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.2.2)
- transitivePeerDependencies:
- - eslint-import-resolver-typescript
- - eslint-import-resolver-webpack
- - supports-color
-
eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
dependencies:
array-includes: 3.1.8
@@ -50510,8 +50340,6 @@ snapshots:
is-hex-prefixed: 1.0.0
strip-hex-prefix: 1.0.0
- ev-emitter@1.1.1: {}
-
event-emitter@0.3.5:
dependencies:
d: 1.0.2
@@ -50827,12 +50655,12 @@ snapshots:
react: 18.2.0
react-native: 0.73.6(@babel/core@7.24.3)(react@18.2.0)
- expo-font@11.4.0(67hrxnryf4ycl5nhvln5laaykq):
+ expo-font@11.4.0(hxmq25pkdf6fvmayb5kzqynb3q):
dependencies:
expo: 49.0.23(@babel/core@7.24.3)(@expo/metro-config@0.10.7)(expo-modules-core@1.5.11)(glob@7.2.3)(metro-core@0.80.8)(metro@0.80.8)(minimatch@5.1.6)(react-native@0.73.6(@babel/core@7.24.3)(@babel/preset-env@7.24.3(@babel/core@7.24.3))(metro-resolver@0.80.8)(metro-transform-worker@0.80.8)(react@18.2.0))(react@18.2.0)
fontfaceobserver: 2.3.0
optionalDependencies:
- expo-asset: 8.10.1(tw2cl75ufjodqrrf2cewclqsqi)
+ expo-asset: 8.10.1(expo-constants@14.5.1(expo-modules-core@1.5.11)(expo@49.0.23)(react-native@0.73.6(@babel/core@7.24.3)(@babel/preset-env@7.24.3(@babel/core@7.24.3))(metro-resolver@0.80.8)(metro-transform-worker@0.80.8)(react@18.2.0))(react@18.2.0))(expo-modules-core@1.5.11(expo-constants@14.5.1)(react-native@0.73.6(@babel/core@7.24.3)(@babel/preset-env@7.24.3(@babel/core@7.24.3))(metro-resolver@0.80.8)(metro-transform-worker@0.80.8)(react@18.2.0))(react@18.2.0))(expo@49.0.23(@babel/core@7.24.3)(@expo/metro-config@0.10.7)(expo-modules-core@1.5.11)(glob@7.2.3)(metro-core@0.80.8)(metro@0.80.8)(minimatch@5.1.6)(react-native@0.73.6(@babel/core@7.24.3)(@babel/preset-env@7.24.3(@babel/core@7.24.3))(metro-resolver@0.80.8)(metro-transform-worker@0.80.8)(react@18.2.0))(react@18.2.0))(react-native@0.73.6(@babel/core@7.24.3)(@babel/preset-env@7.24.3(@babel/core@7.24.3))(metro-resolver@0.80.8)(metro-transform-worker@0.80.8)(react@18.2.0))(react@18.2.0)
expo-constants: 14.4.2(expo-modules-core@1.5.11(expo-constants@14.5.1)(react-native@0.73.6(@babel/core@7.24.3)(@babel/preset-env@7.24.3(@babel/core@7.24.3))(metro-resolver@0.80.8)(metro-transform-worker@0.80.8)(react@18.2.0))(react@18.2.0))(expo@49.0.23(@babel/core@7.24.3)(@expo/metro-config@0.10.7)(expo-modules-core@1.5.11)(glob@7.2.3)(metro-core@0.80.8)(metro@0.80.8)(minimatch@5.1.6)(react-native@0.73.6(@babel/core@7.24.3)(@babel/preset-env@7.24.3(@babel/core@7.24.3))(metro-resolver@0.80.8)(metro-transform-worker@0.80.8)(react@18.2.0))(react@18.2.0))(react-native@0.73.6(@babel/core@7.24.3)(@babel/preset-env@7.24.3(@babel/core@7.24.3))(metro-resolver@0.80.8)(metro-transform-worker@0.80.8)(react@18.2.0))(react@18.2.0)
expo-modules-core: 1.5.11(expo-constants@14.5.1)(react-native@0.73.6(@babel/core@7.24.3)(@babel/preset-env@7.24.3(@babel/core@7.24.3))(metro-resolver@0.80.8)(metro-transform-worker@0.80.8)(react@18.2.0))(react@18.2.0)
react: 18.2.0
@@ -50957,7 +50785,7 @@ snapshots:
expo-asset: 8.10.1(tw2cl75ufjodqrrf2cewclqsqi)
expo-constants: 14.4.2(expo-modules-core@1.5.11(expo-constants@14.5.1)(react-native@0.73.6(@babel/core@7.24.3)(@babel/preset-env@7.24.3(@babel/core@7.24.3))(metro-resolver@0.80.8)(metro-transform-worker@0.80.8)(react@18.2.0))(react@18.2.0))(expo@49.0.23(@babel/core@7.24.3)(@expo/metro-config@0.10.7)(expo-modules-core@1.5.11)(glob@7.2.3)(metro-core@0.80.8)(metro@0.80.8)(minimatch@5.1.6)(react-native@0.73.6(@babel/core@7.24.3)(@babel/preset-env@7.24.3(@babel/core@7.24.3))(metro-resolver@0.80.8)(metro-transform-worker@0.80.8)(react@18.2.0))(react@18.2.0))(react-native@0.73.6(@babel/core@7.24.3)(@babel/preset-env@7.24.3(@babel/core@7.24.3))(metro-resolver@0.80.8)(metro-transform-worker@0.80.8)(react@18.2.0))(react@18.2.0)
expo-file-system: 15.4.5(tw2cl75ufjodqrrf2cewclqsqi)
- expo-font: 11.4.0(67hrxnryf4ycl5nhvln5laaykq)
+ expo-font: 11.4.0(hxmq25pkdf6fvmayb5kzqynb3q)
expo-keep-awake: 12.3.0(tw2cl75ufjodqrrf2cewclqsqi)
expo-modules-autolinking: 1.5.1(55fu4l7dolnnxrys6pt2pnhfne)
fbemitter: 3.0.0
@@ -51385,10 +51213,6 @@ snapshots:
transitivePeerDependencies:
- encoding
- fizzy-ui-utils@2.0.7:
- dependencies:
- desandro-matches-selector: 2.0.2
-
flat-cache@3.2.0:
dependencies:
flatted: 3.3.1
@@ -51726,8 +51550,6 @@ snapshots:
get-port@5.1.1: {}
- get-size@2.0.3: {}
-
get-stream@3.0.0: {}
get-stream@4.1.0:
@@ -52314,7 +52136,7 @@ snapshots:
history@4.10.1:
dependencies:
- '@babel/runtime': 7.24.1
+ '@babel/runtime': 7.25.0
loose-envify: 1.4.0
resolve-pathname: 3.0.0
tiny-invariant: 1.3.3
@@ -53174,18 +52996,6 @@ snapshots:
dependencies:
ws: 8.17.1
- isotope-horizontal@2.0.1:
- dependencies:
- isotope-layout: 3.0.6
-
- isotope-layout@3.0.6:
- dependencies:
- desandro-matches-selector: 2.0.2
- fizzy-ui-utils: 2.0.7
- get-size: 2.0.3
- masonry-layout: 4.2.2
- outlayer: 2.1.1
-
isstream@0.1.2: {}
istanbul-lib-coverage@3.2.2: {}
@@ -56430,11 +56240,6 @@ snapshots:
marky@1.2.5: {}
- masonry-layout@4.2.2:
- dependencies:
- get-size: 2.0.3
- outlayer: 2.1.1
-
match-sorter@6.3.4:
dependencies:
'@babel/runtime': 7.24.1
@@ -58465,12 +58270,6 @@ snapshots:
outdent@0.5.0: {}
- outlayer@2.1.1:
- dependencies:
- ev-emitter: 1.1.1
- fizzy-ui-utils: 2.0.7
- get-size: 2.0.3
-
outvariant@1.4.2: {}
ow@0.17.0:
@@ -62528,14 +62327,10 @@ snapshots:
optionalDependencies:
prop-types: 15.8.1
- storyly-web@2.16.5:
+ storyly-web@3.6.7:
dependencies:
- '@babel/runtime': 7.24.1
- '@sentry/browser': 7.109.0
+ '@babel/runtime': 7.25.0
canvas-confetti: 1.9.2
- copy-to-clipboard: 3.3.3
- isotope-horizontal: 2.0.1
- isotope-layout: 3.0.6
platform: 1.3.6
swiper: 7.4.1
uuid: 8.3.2
@@ -63606,8 +63401,6 @@ snapshots:
tocbot@4.25.0: {}
- toggle-selection@1.0.6: {}
-
toidentifier@1.0.0: {}
toidentifier@1.0.1: {}
diff --git a/tools/actions/composites/setup-speculos_image/action.yml b/tools/actions/composites/setup-speculos_image/action.yml
new file mode 100644
index 000000000000..498c5517fe54
--- /dev/null
+++ b/tools/actions/composites/setup-speculos_image/action.yml
@@ -0,0 +1,34 @@
+name: "Setup Speculos Image and Coin Apps"
+description: "Uploading allure report to Allure Server"
+inputs:
+ coinapps_path:
+ required: true
+ description: "Path to the coin apps folder"
+ speculos_tag:
+ required: false
+ description: "Speculos docker image tag"
+ bot_id:
+ description: "GitHub Bot ID"
+ bot_key:
+ description: "GitHub Bot private key"
+
+runs:
+ using: composite
+
+ steps:
+ - name: Generate token
+ id: generate-token
+ uses: tibdex/github-app-token@v1
+ with:
+ app_id: ${{ inputs.bot_id }}
+ private_key: ${{ inputs.bot_key }}
+ - name: Retrieving coin apps
+ uses: actions/checkout@v4
+ with:
+ ref: master
+ repository: LedgerHQ/coin-apps
+ token: ${{ steps.generate-token.outputs.token }}
+ path: ${{ inputs.coinapps_path }}
+ - name: Pull docker image
+ run: docker pull ${{ inputs.speculos_tag }}
+ shell: bash
diff --git a/turbo.json b/turbo.json
index 47c9ae79538e..140d8db52b3d 100644
--- a/turbo.json
+++ b/turbo.json
@@ -68,7 +68,8 @@
},
"start": {
"cache": false,
- "dependsOn": ["^build"]
+ "dependsOn": ["^build"],
+ "persistent": true
},
"unimported": {
"cache": false