diff --git a/src/components/tx/XeAddressModal.test.js b/src/components/tx/XeAddressModal.test.js
new file mode 100644
index 0000000..e7620e1
--- /dev/null
+++ b/src/components/tx/XeAddressModal.test.js
@@ -0,0 +1,110 @@
+import { afterEach, describe, expect, it, vi } from 'vitest'
+import { createApp, h, nextTick, reactive } from 'vue'
+import XeAddressModal from './XeAddressModal.vue'
+import { xeAccountFromPhrase } from '../../utils/xe-account'
+
+let app
+let host
+
+afterEach(() => {
+ app?.unmount()
+ host?.remove()
+})
+
+async function mount() {
+ host = document.createElement('div')
+ document.body.append(host)
+ const props = reactive({ visible: false })
+ const created = vi.fn()
+ app = createApp({ render: () => h(XeAddressModal, { ...props, close: () => { props.visible = false }, onCreated: created }) })
+ app.mount(host)
+ props.visible = true
+ await nextTick()
+ return { props, created }
+}
+
+const button = text => [...host.querySelectorAll('button')].find(el => el.textContent.includes(text))
+const words = () => [...host.querySelectorAll('.xe-words li')].map(el => el.lastChild.textContent)
+
+async function fill(input, value) {
+ input.value = value
+ input.dispatchEvent(new Event('input', { bubbles: true }))
+ await nextTick()
+}
+
+describe('XE address modal v2', () => {
+ it('requires three correct words before emitting the derived address', async () => {
+ const { created } = await mount()
+ const phraseWords = words()
+ expect(phraseWords).toHaveLength(24)
+ const expected = xeAccountFromPhrase(phraseWords.join(' ')).address
+ expect(host.textContent).toContain(expected)
+ button('I have written them down').click()
+ await nextTick()
+ expect(button('Use this address').disabled).toBe(true)
+ const inputs = [...host.querySelectorAll('input')]
+ expect(inputs).toHaveLength(3)
+ await fill(inputs[0], 'wrong')
+ expect(button('Use this address').disabled).toBe(true)
+ for (const input of inputs) {
+ const index = Number(input.id.replace('xe-word-', '')) - 1
+ await fill(input, phraseWords[index])
+ }
+ button('Use this address').click()
+ expect(created).toHaveBeenCalledExactlyOnceWith(expected)
+ })
+ it('requires identical passphrase entries and clears both after applying', async () => {
+ await mount()
+ const phrase = words().join(' ')
+ const originalAddress = xeAccountFromPhrase(phrase).address
+ const input = host.querySelector('#xe-bip39-passphrase')
+ await fill(input, 'TREZOR')
+ expect(button('Apply passphrase').disabled).toBe(true)
+ const confirm = host.querySelector('#xe-bip39-passphrase-confirm')
+ await fill(confirm, 'TREZOR ')
+ expect(button('Apply passphrase').disabled).toBe(true)
+ expect(button('I have written them down').disabled).toBe(true)
+ // A dispatched event bypasses the disabled button, so test the method too.
+ button('Apply passphrase').dispatchEvent(new Event('click'))
+ await nextTick()
+ expect(host.textContent).toContain(originalAddress)
+ expect(input.value).toBe('TREZOR')
+ await fill(confirm, 'TREZOR')
+ expect(button('Apply passphrase').disabled).toBe(false)
+ button('Apply passphrase').click()
+ await nextTick()
+ expect(input.value).toBe('')
+ expect(confirm.value).toBe('')
+ expect(host.textContent).toContain(xeAccountFromPhrase(phrase, { passphrase: 'TREZOR' }).address)
+ expect(button('I have written them down').disabled).toBe(false)
+ button('I have written them down').click()
+ await nextTick()
+ expect(button('Use this address').disabled).toBe(true)
+ })
+ it('uses a fresh phrase on New address and clears both passphrase inputs on reset', async () => {
+ const { props } = await mount()
+ const firstPhrase = words().join(' ')
+ const input = host.querySelector('#xe-bip39-passphrase')
+ const confirm = host.querySelector('#xe-bip39-passphrase-confirm')
+ await fill(input, 'TREZOR')
+ await fill(confirm, 'TREZOR')
+ expect(button('I have written them down').disabled).toBe(true)
+ button('Apply passphrase').click()
+ await nextTick()
+ expect(input.value).toBe('')
+ expect(confirm.value).toBe('')
+ expect(host.textContent).toContain(xeAccountFromPhrase(firstPhrase, { passphrase: 'TREZOR' }).address)
+ await fill(confirm, 'unfinished')
+ expect(button('I have written them down').disabled).toBe(true)
+ button('New address').click()
+ await nextTick()
+ expect(input.value).toBe('')
+ expect(confirm.value).toBe('')
+ const nextPhrase = words().join(' ')
+ expect(nextPhrase).not.toBe(firstPhrase)
+ expect(host.textContent).toContain(xeAccountFromPhrase(nextPhrase).address)
+ props.visible = false
+ await nextTick()
+ expect(host.querySelectorAll('.xe-words li')).toHaveLength(0)
+ })
+})
diff --git a/src/components/tx/XeAddressModal.vue b/src/components/tx/XeAddressModal.vue
index 59ac9b7..1b185a1 100644
--- a/src/components/tx/XeAddressModal.vue
+++ b/src/components/tx/XeAddressModal.vue
@@ -20,20 +20,33 @@
{{ i + 1 }}{{ word }}
+
+ Advanced: BIP39 passphrase
+
+
+
+
+
+
Keep it separately. It is never stored. A wrong passphrase opens a different, empty account. Avoid a weak passphrase.
+
+
This address needs your phrase and BIP39 passphrase. The passphrase is not kept.
Anyone with these words controls your $XE. If you lose them, nobody can recover your $XE.
This wallet does not keep them.
+ The phrase is your only backup; keep any BIP39 passphrase separately.
+
Standard BIP39 + SLIP-0010 ed25519, m/44'/1'/0'. Coin type 1 is provisional.
+ Wallets made before v2 restore from their hex seed.
-
+
@@ -85,7 +98,7 @@
import Modal from '../Modal.vue'
import { h } from 'vue'
import { RefreshIcon, ShieldExclamationIcon } from '@heroicons/vue/outline'
-import { newXeAccount, pickCheckPositions } from '../../utils/xe-account'
+import { newXeAccount, xeAccountFromPhrase, pickCheckPositions } from '../../utils/xe-account'
const CHECK_WORDS = 3
@@ -115,12 +128,18 @@ export default {
step: 1,
words: [],
address: '',
+ bip39Passphrase: '',
+ bip39PassphraseConfirm: '',
+ usesPassphrase: false,
positions: [],
answers: [],
touched: []
}
},
computed: {
+ canApplyPassphrase() {
+ return this.bip39Passphrase === this.bip39PassphraseConfirm
+ },
matches() {
return this.positions.map((position, i) => this.answers[i].trim().toLowerCase() === this.words[position])
},
@@ -136,6 +155,9 @@ export default {
},
methods: {
generate() {
+ this.bip39Passphrase = ''
+ this.bip39PassphraseConfirm = ''
+ this.usesPassphrase = false
const { address, words } = newXeAccount()
this.words = words
this.address = address
@@ -144,10 +166,24 @@ export default {
this.touched = this.positions.map(() => false)
this.step = 1
},
+ applyPassphrase() {
+ if (!this.canApplyPassphrase) return
+ const { address } = xeAccountFromPhrase(this.words.join(' '), { passphrase: this.bip39Passphrase })
+ this.address = address
+ this.usesPassphrase = this.bip39Passphrase !== ''
+ this.bip39Passphrase = ''
+ this.bip39PassphraseConfirm = ''
+ this.positions = pickCheckPositions(CHECK_WORDS, this.words.length)
+ this.answers = this.positions.map(() => '')
+ this.touched = this.positions.map(() => false)
+ },
reset() {
this.step = 1
this.words = []
this.address = ''
+ this.bip39Passphrase = ''
+ this.bip39PassphraseConfirm = ''
+ this.usesPassphrase = false
this.positions = []
this.answers = []
this.touched = []
diff --git a/src/utils/xe-account.js b/src/utils/xe-account.js
index d8518b5..1451283 100644
--- a/src/utils/xe-account.js
+++ b/src/utils/xe-account.js
@@ -2,39 +2,86 @@
// Use of this source code is governed by a GNU GPL-style license
// that can be found in the LICENSE.md file. All rights reserved.
-// New XE address for Claim XE. XE is the new chain (xeprotocol/core), not the
-// Edge chain: its addresses are 64 lowercase hex with no prefix.
-//
-// The XE recovery phrase (core #1005) is the 32-byte ed25519 seed written as
-// 24 BIP39 English words: BIP39's entropy-to-mnemonic encoding applied to the
-// seed itself. No PBKDF2, passphrase or derivation path: the words are the
-// seed. The address is sha256("xe/account/v1" || ed25519 public key).
-// xe-recovery-phrase-vectors.json is a copy of the core vectors.
-
+// XE recovery phrase v2: BIP39 + SLIP-0010 ed25519, m/44'/C'/account'.
+// The shared core vectors pin every step; the leaf key is the signing seed.
import { ed25519 } from '@noble/curves/ed25519.js'
-import { entropyToMnemonic } from '@scure/bip39'
-import { sha256 } from '@noble/hashes/sha2.js'
+import { generateMnemonic, mnemonicToSeedSync, validateMnemonic } from '@scure/bip39'
+import { hmac } from '@noble/hashes/hmac.js'
+import { sha256, sha512 } from '@noble/hashes/sha2.js'
import { wordlist } from '@scure/bip39/wordlists/english.js'
-import { bytesToHex, concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils.js'
+import { bytesToHex, concatBytes, utf8ToBytes } from '@noble/hashes/utils.js'
const ACCOUNT_DOMAIN = utf8ToBytes('xe/account/v1')
+// Provisional until XE registers a SLIP-0044 coin type.
+export const RECOVERY_PHRASE_COIN_TYPE = 1
+const englishWords = new Set(wordlist)
-export const phraseFromSeed = seed => entropyToMnemonic(seed, wordlist)
+export class PhraseError extends Error {
+ constructor(code, message, position) {
+ super(message)
+ this.name = 'PhraseError'
+ this.code = code
+ this.position = position
+ }
+}
-export const publicKeyFromSeed = seed => ed25519.getPublicKey(seed)
+export const normalizePhrase = phrase => phrase.toLowerCase().split(/\p{White_Space}+/u).filter(Boolean).join(' ').normalize('NFKD')
+
+export function validatePhrase(phrase) {
+ const normalized = normalizePhrase(phrase)
+ const words = normalized ? normalized.split(' ') : []
+ if (![12, 15, 18, 21, 24].includes(words.length)) {
+ throw new PhraseError('word_count', 'recovery phrase must have 12, 15, 18, 21 or 24 words')
+ }
+ const unknown = words.findIndex(word => !englishWords.has(word))
+ if (unknown !== -1) {
+ throw new PhraseError('unknown_word', `unknown recovery phrase word at position ${unknown + 1}`, unknown + 1)
+ }
+ if (!validateMnemonic(normalized, wordlist)) {
+ throw new PhraseError('checksum', 'recovery phrase checksum does not match')
+ }
+ return normalized
+}
+function validateAccount(account) {
+ if (!Number.isInteger(account) || account < 0 || account >= 0x80000000) {
+ throw new PhraseError('account', 'recovery phrase account must be between 0 and 2147483647')
+ }
+}
+
+export function slip10Ed25519(seed, path) {
+ let node = hmac(sha512, utf8ToBytes('ed25519 seed'), seed)
+ for (const index of path) {
+ validateAccount(index)
+ const data = new Uint8Array(37)
+ data.set(node.subarray(0, 32), 1)
+ new DataView(data.buffer).setUint32(33, index + 0x80000000, false)
+ node = hmac(sha512, node.subarray(32), data)
+ }
+ return { key: node.slice(0, 32), chainCode: node.slice(32) }
+}
+
+export const publicKeyFromSeed = seed => ed25519.getPublicKey(seed)
export const addressFromPublicKey = publicKey => bytesToHex(sha256(concatBytes(ACCOUNT_DOMAIN, publicKey)))
-// A new XE account from a random seed. The seed is not kept: the words are the
-// only copy.
-export function newXeAccount() {
- const seed = randomBytes(32)
+export function xeAccountFromPhrase(phrase, { passphrase = '', account = 0 } = {}) {
+ validateAccount(account)
+ phrase = validatePhrase(phrase)
+ const seed = slip10Ed25519(mnemonicToSeedSync(phrase, passphrase), [44, RECOVERY_PHRASE_COIN_TYPE, account]).key
return {
- words: phraseFromSeed(seed).split(' '),
+ phrase,
+ words: phrase.split(' '),
+ account,
+ seed,
address: addressFromPublicKey(publicKeyFromSeed(seed))
}
}
+// New address always means a new 24-word phrase at account 0.
+export function newXeAccount({ passphrase = '' } = {}) {
+ return xeAccountFromPhrase(generateMnemonic(wordlist, 256), { passphrase })
+}
+
// Distinct word positions (0-based, in order) to ask back after the user has
// written the phrase down.
export function pickCheckPositions(count, total) {
diff --git a/src/utils/xe-account.test.js b/src/utils/xe-account.test.js
index a05ceee..af468a8 100644
--- a/src/utils/xe-account.test.js
+++ b/src/utils/xe-account.test.js
@@ -2,55 +2,75 @@
// Use of this source code is governed by a GNU GPL-style license
// that can be found in the LICENSE.md file. All rights reserved.
-import { mnemonicToEntropy } from '@scure/bip39'
+import { entropyToMnemonic, mnemonicToSeedSync } from '@scure/bip39'
import { sha256 } from '@noble/hashes/sha2.js'
import vectors from './xe-recovery-phrase-vectors.json'
import { wordlist } from '@scure/bip39/wordlists/english.js'
import {
- addressFromPublicKey, newXeAccount, phraseFromSeed, pickCheckPositions, publicKeyFromSeed
+ addressFromPublicKey, newXeAccount, xeAccountFromPhrase, pickCheckPositions,
+ publicKeyFromSeed, validatePhrase, slip10Ed25519, PhraseError, RECOVERY_PHRASE_COIN_TYPE
} from './xe-account'
import { bytesToHex, hexToBytes, utf8ToBytes } from '@noble/hashes/utils.js'
import { describe, expect, it } from 'vitest'
-// The vectors file is a copy of xeprotocol/core core/testdata/recovery-phrase-vectors.json.
-describe('XE recovery phrase vectors', () => {
- it('uses the BIP39 English wordlist that core pins', () => {
+// Byte-for-byte copy of core/core/testdata/recovery-phrase-vectors.json.
+describe('XE recovery phrase v2', () => {
+ it('pins the shared vectors and wordlist', () => {
+ expect(vectors.version).toBe(2)
+ expect(vectors.coin_type).toBe(RECOVERY_PHRASE_COIN_TYPE)
+ expect(vectors.coin_type_provisional).toBe(true)
+ expect(vectors.bip39).toHaveLength(24)
+ expect(vectors.slip10_ed25519).toHaveLength(12)
+ expect(vectors.xe).toHaveLength(10)
+ expect(vectors.invalid).toHaveLength(4)
expect(bytesToHex(sha256(utf8ToBytes(`${wordlist.join('\n')}\n`)))).toBe(vectors.wordlist_sha256)
})
-
- it('encodes each BIP39 32-byte entropy vector', () => {
- for (const v of vectors.bip39) {
- expect(phraseFromSeed(hexToBytes(v.entropy))).toBe(v.mnemonic)
- }
+ it.each(vectors.bip39)('official BIP39 entropy $entropy', v => {
+ expect(entropyToMnemonic(hexToBytes(v.entropy), wordlist)).toBe(v.mnemonic)
+ expect(validatePhrase(v.mnemonic)).toBe(v.mnemonic)
+ expect(bytesToHex(mnemonicToSeedSync(v.mnemonic, v.passphrase))).toBe(v.seed)
})
-
- it('derives the phrase, public key and address of each XE vector', () => {
- for (const v of vectors.xe) {
- const seed = hexToBytes(v.seed)
- const publicKey = publicKeyFromSeed(seed)
- expect(phraseFromSeed(seed)).toBe(v.phrase)
- expect(bytesToHex(publicKey)).toBe(v.public_key)
- expect(addressFromPublicKey(publicKey)).toBe(v.address)
+ it.each(vectors.slip10_ed25519)('official SLIP-0010 $path', v => {
+ const node = slip10Ed25519(hexToBytes(v.seed), v.path.split('/').slice(1).map(part => Number(part.slice(0, -1))))
+ expect(bytesToHex(node.key)).toBe(v.private_key)
+ expect(bytesToHex(node.chainCode)).toBe(v.chain_code)
+ expect('00' + bytesToHex(publicKeyFromSeed(node.key))).toBe(v.public_key)
+ })
+ it.each(vectors.xe)('XE account $account at $address', v => {
+ const wallet = xeAccountFromPhrase(v.phrase, v)
+ expect(bytesToHex(wallet.seed)).toBe(v.ed25519_seed)
+ expect(bytesToHex(publicKeyFromSeed(wallet.seed))).toBe(v.public_key)
+ expect(wallet.address).toBe(v.address)
+ })
+ it.each(vectors.invalid)('typed $error error', v => {
+ expect(() => validatePhrase(v.phrase)).toThrow(PhraseError)
+ try { validatePhrase(v.phrase) } catch (error) {
+ expect(error.code).toBe(v.error)
+ if (v.position) expect(error.position).toBe(v.position)
+ expect(String(error)).not.toContain('notaword')
}
})
-})
-
-describe('newXeAccount', () => {
- it('returns 24 words whose seed derives the returned address', () => {
- const { address, words } = newXeAccount()
- expect(words).toHaveLength(24)
- expect(address).toMatch(/^[0-9a-f]{64}$/)
- const seed = mnemonicToEntropy(words.join(' '), wordlist)
- expect(addressFromPublicKey(publicKeyFromSeed(seed))).toBe(address)
+ it.each([-1, 0.5, 2147483648, NaN, Infinity])('rejects account %s', account => {
+ expect(() => xeAccountFromPhrase(vectors.xe[0].phrase, { account })).toThrow(PhraseError)
})
-
- it('returns a new account each time', () => {
- expect(newXeAccount().address).not.toBe(newXeAccount().address)
+ it('normalizes Unicode whitespace and NFKD passphrases', () => {
+ const phrase = vectors.xe[0].phrase
+ expect(xeAccountFromPhrase('\u0085' + phrase.toUpperCase().replaceAll(' ', '\u2003\t')).address).toBe(vectors.xe[0].address)
+ expect(xeAccountFromPhrase(phrase, { passphrase: 'café' }).address).toBe(xeAccountFromPhrase(phrase, { passphrase: 'cafe\u0301' }).address)
+ expect(xeAccountFromPhrase(phrase, { passphrase: 'café ' }).address).not.toBe(xeAccountFromPhrase(phrase, { passphrase: 'café' }).address)
})
-})
-
-describe('pickCheckPositions', () => {
- it('picks distinct positions in order, inside the phrase', () => {
+ it('generates a new phrase for every account 0 address', () => {
+ const first = newXeAccount()
+ const second = newXeAccount()
+ expect(first.account).toBe(0)
+ expect(first.words).toHaveLength(24)
+ expect(first.phrase).toBe(first.words.join(' '))
+ expect(xeAccountFromPhrase(first.phrase).address).toBe(first.address)
+ expect(addressFromPublicKey(publicKeyFromSeed(first.seed))).toBe(first.address)
+ expect(second.phrase).not.toBe(first.phrase)
+ expect(second.address).not.toBe(first.address)
+ })
+ it('picks three distinct ordered word positions', () => {
for (let i = 0; i < 50; i++) {
const positions = pickCheckPositions(3, 24)
expect(positions).toHaveLength(3)
diff --git a/src/utils/xe-recovery-phrase-vectors.json b/src/utils/xe-recovery-phrase-vectors.json
index c5fa223..d2180db 100644
--- a/src/utils/xe-recovery-phrase-vectors.json
+++ b/src/utils/xe-recovery-phrase-vectors.json
@@ -1,70 +1,351 @@
{
- "description": "XE recovery phrase test vectors (xeprotocol/core#1005). An XE recovery phrase is the BIP39 entropy-to-mnemonic encoding of the 32-byte ed25519 seed with the BIP39 English wordlist: seed || first 8 bits of SHA-256(seed) gives 264 bits, read most significant bit first as 24 word indexes of 11 bits. Exactly 24 words. No PBKDF2, no passphrase, no derivation path: the words are the seed. bip39: the official BIP39 English vectors with 32-byte entropy, from trezor/python-mnemonic vectors.json, entropy and mnemonic only (the PBKDF2 seed and xprv columns do not apply). xe: seed (hex), phrase, public_key (hex ed25519 public key from the seed, RFC 8032) and address (hex SHA-256 of the UTF-8 bytes 'xe/account/v1' followed by the 32-byte public key).",
- "wordlist_sha256": "2f5eed53a4727b4bf8880d8f3f199efc90e58503646d9ff8eff3a2ed3b24dbda",
"bip39": [
+ {
+ "entropy": "00000000000000000000000000000000",
+ "mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
+ "passphrase": "TREZOR",
+ "seed": "c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04"
+ },
+ {
+ "entropy": "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
+ "mnemonic": "legal winner thank year wave sausage worth useful legal winner thank yellow",
+ "passphrase": "TREZOR",
+ "seed": "2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6fa457fe1296106559a3c80937a1c1069be3a3a5bd381ee6260e8d9739fce1f607"
+ },
+ {
+ "entropy": "80808080808080808080808080808080",
+ "mnemonic": "letter advice cage absurd amount doctor acoustic avoid letter advice cage above",
+ "passphrase": "TREZOR",
+ "seed": "d71de856f81a8acc65e6fc851a38d4d7ec216fd0796d0a6827a3ad6ed5511a30fa280f12eb2e47ed2ac03b5c462a0358d18d69fe4f985ec81778c1b370b652a8"
+ },
+ {
+ "entropy": "ffffffffffffffffffffffffffffffff",
+ "mnemonic": "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong",
+ "passphrase": "TREZOR",
+ "seed": "ac27495480225222079d7be181583751e86f571027b0497b5b5d11218e0a8a13332572917f0f8e5a589620c6f15b11c61dee327651a14c34e18231052e48c069"
+ },
+ {
+ "entropy": "000000000000000000000000000000000000000000000000",
+ "mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent",
+ "passphrase": "TREZOR",
+ "seed": "035895f2f481b1b0f01fcf8c289c794660b289981a78f8106447707fdd9666ca06da5a9a565181599b79f53b844d8a71dd9f439c52a3d7b3e8a79c906ac845fa"
+ },
+ {
+ "entropy": "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
+ "mnemonic": "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will",
+ "passphrase": "TREZOR",
+ "seed": "f2b94508732bcbacbcc020faefecfc89feafa6649a5491b8c952cede496c214a0c7b3c392d168748f2d4a612bada0753b52a1c7ac53c1e93abd5c6320b9e95dd"
+ },
+ {
+ "entropy": "808080808080808080808080808080808080808080808080",
+ "mnemonic": "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always",
+ "passphrase": "TREZOR",
+ "seed": "107d7c02a5aa6f38c58083ff74f04c607c2d2c0ecc55501dadd72d025b751bc27fe913ffb796f841c49b1d33b610cf0e91d3aa239027f5e99fe4ce9e5088cd65"
+ },
+ {
+ "entropy": "ffffffffffffffffffffffffffffffffffffffffffffffff",
+ "mnemonic": "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when",
+ "passphrase": "TREZOR",
+ "seed": "0cd6e5d827bb62eb8fc1e262254223817fd068a74b5b449cc2f667c3f1f985a76379b43348d952e2265b4cd129090758b3e3c2c49103b5051aac2eaeb890a528"
+ },
{
"entropy": "0000000000000000000000000000000000000000000000000000000000000000",
- "mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"
+ "mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
+ "passphrase": "TREZOR",
+ "seed": "bda85446c68413707090a52022edd26a1c9462295029f2e60cd7c4f2bbd3097170af7a4d73245cafa9c3cca8d561a7c3de6f5d4a10be8ed2a5e608d68f92fcc8"
},
{
"entropy": "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
- "mnemonic": "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title"
+ "mnemonic": "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title",
+ "passphrase": "TREZOR",
+ "seed": "bc09fca1804f7e69da93c2f2028eb238c227f2e9dda30cd63699232578480a4021b146ad717fbb7e451ce9eb835f43620bf5c514db0f8add49f5d121449d3e87"
},
{
"entropy": "8080808080808080808080808080808080808080808080808080808080808080",
- "mnemonic": "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless"
+ "mnemonic": "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless",
+ "passphrase": "TREZOR",
+ "seed": "c0c519bd0e91a2ed54357d9d1ebef6f5af218a153624cf4f2da911a0ed8f7a09e2ef61af0aca007096df430022f7a2b6fb91661a9589097069720d015e4e982f"
},
{
"entropy": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
- "mnemonic": "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote"
+ "mnemonic": "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote",
+ "passphrase": "TREZOR",
+ "seed": "dd48c104698c30cfe2b6142103248622fb7bb0ff692eebb00089b32d22484e1613912f0a5b694407be899ffd31ed3992c456cdf60f5d4564b8ba3f05a69890ad"
+ },
+ {
+ "entropy": "9e885d952ad362caeb4efe34a8e91bd2",
+ "mnemonic": "ozone drill grab fiber curtain grace pudding thank cruise elder eight picnic",
+ "passphrase": "TREZOR",
+ "seed": "274ddc525802f7c828d8ef7ddbcdc5304e87ac3535913611fbbfa986d0c9e5476c91689f9c8a54fd55bd38606aa6a8595ad213d4c9c9f9aca3fb217069a41028"
+ },
+ {
+ "entropy": "6610b25967cdcca9d59875f5cb50b0ea75433311869e930b",
+ "mnemonic": "gravity machine north sort system female filter attitude volume fold club stay feature office ecology stable narrow fog",
+ "passphrase": "TREZOR",
+ "seed": "628c3827a8823298ee685db84f55caa34b5cc195a778e52d45f59bcf75aba68e4d7590e101dc414bc1bbd5737666fbbef35d1f1903953b66624f910feef245ac"
},
{
"entropy": "68a79eaca2324873eacc50cb9c6eca8cc68ea5d936f98787c60c7ebc74e6ce7c",
- "mnemonic": "hamster diagram private dutch cause delay private meat slide toddler razor book happy fancy gospel tennis maple dilemma loan word shrug inflict delay length"
+ "mnemonic": "hamster diagram private dutch cause delay private meat slide toddler razor book happy fancy gospel tennis maple dilemma loan word shrug inflict delay length",
+ "passphrase": "TREZOR",
+ "seed": "64c87cde7e12ecf6704ab95bb1408bef047c22db4cc7491c4271d170a1b213d20b385bc1588d9c7b38f1b39d415665b8a9030c9ec653d75e65f847d8fc1fc440"
+ },
+ {
+ "entropy": "c0ba5a8e914111210f2bd131f3d5e08d",
+ "mnemonic": "scheme spot photo card baby mountain device kick cradle pact join borrow",
+ "passphrase": "TREZOR",
+ "seed": "ea725895aaae8d4c1cf682c1bfd2d358d52ed9f0f0591131b559e2724bb234fca05aa9c02c57407e04ee9dc3b454aa63fbff483a8b11de949624b9f1831a9612"
+ },
+ {
+ "entropy": "6d9be1ee6ebd27a258115aad99b7317b9c8d28b6d76431c3",
+ "mnemonic": "horn tenant knee talent sponsor spell gate clip pulse soap slush warm silver nephew swap uncle crack brave",
+ "passphrase": "TREZOR",
+ "seed": "fd579828af3da1d32544ce4db5c73d53fc8acc4ddb1e3b251a31179cdb71e853c56d2fcb11aed39898ce6c34b10b5382772db8796e52837b54468aeb312cfc3d"
},
{
"entropy": "9f6a2878b2520799a44ef18bc7df394e7061a224d2c33cd015b157d746869863",
- "mnemonic": "panda eyebrow bullet gorilla call smoke muffin taste mesh discover soft ostrich alcohol speed nation flash devote level hobby quick inner drive ghost inside"
+ "mnemonic": "panda eyebrow bullet gorilla call smoke muffin taste mesh discover soft ostrich alcohol speed nation flash devote level hobby quick inner drive ghost inside",
+ "passphrase": "TREZOR",
+ "seed": "72be8e052fc4919d2adf28d5306b5474b0069df35b02303de8c1729c9538dbb6fc2d731d5f832193cd9fb6aeecbc469594a70e3dd50811b5067f3b88b28c3e8d"
+ },
+ {
+ "entropy": "23db8160a31d3e0dca3688ed941adbf3",
+ "mnemonic": "cat swing flag economy stadium alone churn speed unique patch report train",
+ "passphrase": "TREZOR",
+ "seed": "deb5f45449e615feff5640f2e49f933ff51895de3b4381832b3139941c57b59205a42480c52175b6efcffaa58a2503887c1e8b363a707256bdd2b587b46541f5"
+ },
+ {
+ "entropy": "8197a4a47f0425faeaa69deebc05ca29c0a5b5cc76ceacc0",
+ "mnemonic": "light rule cinnamon wrap drastic word pride squirrel upgrade then income fatal apart sustain crack supply proud access",
+ "passphrase": "TREZOR",
+ "seed": "4cbdff1ca2db800fd61cae72a57475fdc6bab03e441fd63f96dabd1f183ef5b782925f00105f318309a7e9c3ea6967c7801e46c8a58082674c860a37b93eda02"
},
{
"entropy": "066dca1a2bb7e8a1db2832148ce9933eea0f3ac9548d793112d9a95c9407efad",
- "mnemonic": "all hour make first leader extend hole alien behind guard gospel lava path output census museum junior mass reopen famous sing advance salt reform"
+ "mnemonic": "all hour make first leader extend hole alien behind guard gospel lava path output census museum junior mass reopen famous sing advance salt reform",
+ "passphrase": "TREZOR",
+ "seed": "26e975ec644423f4a4c4f4215ef09b4bd7ef924e85d1d17c4cf3f136c2863cf6df0a475045652c57eb5fb41513ca2a2d67722b77e954b4b3fc11f7590449191d"
+ },
+ {
+ "entropy": "f30f8c1da665478f49b001d94c5fc452",
+ "mnemonic": "vessel ladder alter error federal sibling chat ability sun glass valve picture",
+ "passphrase": "TREZOR",
+ "seed": "2aaa9242daafcee6aa9d7269f17d4efe271e1b9a529178d7dc139cd18747090bf9d60295d0ce74309a78852a9caadf0af48aae1c6253839624076224374bc63f"
+ },
+ {
+ "entropy": "c10ec20dc3cd9f652c7fac2f1230f7a3c828389a14392f05",
+ "mnemonic": "scissors invite lock maple supreme raw rapid void congress muscle digital elegant little brisk hair mango congress clump",
+ "passphrase": "TREZOR",
+ "seed": "7b4a10be9d98e6cba265566db7f136718e1398c71cb581e1b2f464cac1ceedf4f3e274dc270003c670ad8d02c4558b2f8e39edea2775c9e232c7cb798b069e88"
},
{
"entropy": "f585c11aec520db57dd353c69554b21a89b20fb0650966fa0a9d6f74fd989d8f",
- "mnemonic": "void come effort suffer camp survey warrior heavy shoot primary clutch crush open amazing screen patrol group space point ten exist slush involve unfold"
+ "mnemonic": "void come effort suffer camp survey warrior heavy shoot primary clutch crush open amazing screen patrol group space point ten exist slush involve unfold",
+ "passphrase": "TREZOR",
+ "seed": "01f5bced59dec48e362f2c45b5de68b9fd6c92c6634f44d6d40aab69056506f0e35524a518034ddc1192e1dacd32c1ed3eaa3c3b131c88ed8e7e54c49a5d0998"
}
],
+ "coin_type": 1,
+ "coin_type_provisional": true,
+ "description": "BIP39 English + SLIP-0010 ed25519. Official Trezor English vectors and both SLIP-0010 ed25519 vector chains, verified by an independent Go reference before production implementation. SLIP public keys include the official 00 prefix; XE public keys are raw 32 bytes.",
+ "invalid": [
+ {
+ "error": "word_count",
+ "phrase": ""
+ },
+ {
+ "error": "word_count",
+ "phrase": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon"
+ },
+ {
+ "error": "checksum",
+ "phrase": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon"
+ },
+ {
+ "error": "unknown_word",
+ "phrase": "abandon abandon notaword abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
+ "position": 3
+ }
+ ],
+ "path_template": "m/44'/{coin_type}'/{account}'",
+ "slip10_ed25519": [
+ {
+ "chain_code": "90046a93de5380a72b5e45010748567d5ea02bbf6522f979e05c0d8d8ca9fffb",
+ "path": "m",
+ "private_key": "2b4be7f19ee27bbf30c667b642d5f4aa69fd169872f8fc3059c08ebae2eb19e7",
+ "public_key": "00a4b2856bfec510abab89753fac1ac0e1112364e7d250545963f135f2a33188ed",
+ "seed": "000102030405060708090a0b0c0d0e0f"
+ },
+ {
+ "chain_code": "8b59aa11380b624e81507a27fedda59fea6d0b779a778918a2fd3590e16e9c69",
+ "path": "m/0'",
+ "private_key": "68e0fe46dfb67e368c75379acec591dad19df3cde26e63b93a8e704f1dade7a3",
+ "public_key": "008c8a13df77a28f3445213a0f432fde644acaa215fc72dcdf300d5efaa85d350c",
+ "seed": "000102030405060708090a0b0c0d0e0f"
+ },
+ {
+ "chain_code": "a320425f77d1b5c2505a6b1b27382b37368ee640e3557c315416801243552f14",
+ "path": "m/0'/1'",
+ "private_key": "b1d0bad404bf35da785a64ca1ac54b2617211d2777696fbffaf208f746ae84f2",
+ "public_key": "001932a5270f335bed617d5b935c80aedb1a35bd9fc1e31acafd5372c30f5c1187",
+ "seed": "000102030405060708090a0b0c0d0e0f"
+ },
+ {
+ "chain_code": "2e69929e00b5ab250f49c3fb1c12f252de4fed2c1db88387094a0f8c4c9ccd6c",
+ "path": "m/0'/1'/2'",
+ "private_key": "92a5b23c0b8a99e37d07df3fb9966917f5d06e02ddbd909c7e184371463e9fc9",
+ "public_key": "00ae98736566d30ed0e9d2f4486a64bc95740d89c7db33f52121f8ea8f76ff0fc1",
+ "seed": "000102030405060708090a0b0c0d0e0f"
+ },
+ {
+ "chain_code": "8f6d87f93d750e0efccda017d662a1b31a266e4a6f5993b15f5c1f07f74dd5cc",
+ "path": "m/0'/1'/2'/2'",
+ "private_key": "30d1dc7e5fc04c31219ab25a27ae00b50f6fd66622f6e9c913253d6511d1e662",
+ "public_key": "008abae2d66361c879b900d204ad2cc4984fa2aa344dd7ddc46007329ac76c429c",
+ "seed": "000102030405060708090a0b0c0d0e0f"
+ },
+ {
+ "chain_code": "68789923a0cac2cd5a29172a475fe9e0fb14cd6adb5ad98a3fa70333e7afa230",
+ "path": "m/0'/1'/2'/2'/1000000000'",
+ "private_key": "8f94d394a8e8fd6b1bc2f3f49f5c47e385281d5c17e65324b0f62483e37e8793",
+ "public_key": "003c24da049451555d51a7014a37337aa4e12d41e485abccfa46b47dfb2af54b7a",
+ "seed": "000102030405060708090a0b0c0d0e0f"
+ },
+ {
+ "chain_code": "ef70a74db9c3a5af931b5fe73ed8e1a53464133654fd55e7a66f8570b8e33c3b",
+ "path": "m",
+ "private_key": "171cb88b1b3c1db25add599712e36245d75bc65a1a5c9e18d76f9f2b1eab4012",
+ "public_key": "008fe9693f8fa62a4305a140b9764c5ee01e455963744fe18204b4fb948249308a",
+ "seed": "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"
+ },
+ {
+ "chain_code": "0b78a3226f915c082bf118f83618a618ab6dec793752624cbeb622acb562862d",
+ "path": "m/0'",
+ "private_key": "1559eb2bbec5790b0c65d8693e4d0875b1747f4970ae8b650486ed7470845635",
+ "public_key": "0086fab68dcb57aa196c77c5f264f215a112c22a912c10d123b0d03c3c28ef1037",
+ "seed": "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"
+ },
+ {
+ "chain_code": "138f0b2551bcafeca6ff2aa88ba8ed0ed8de070841f0c4ef0165df8181eaad7f",
+ "path": "m/0'/2147483647'",
+ "private_key": "ea4f5bfe8694d8bb74b7b59404632fd5968b774ed545e810de9c32a4fb4192f4",
+ "public_key": "005ba3b9ac6e90e83effcd25ac4e58a1365a9e35a3d3ae5eb07b9e4d90bcf7506d",
+ "seed": "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"
+ },
+ {
+ "chain_code": "73bd9fff1cfbde33a1b846c27085f711c0fe2d66fd32e139d3ebc28e5a4a6b90",
+ "path": "m/0'/2147483647'/1'",
+ "private_key": "3757c7577170179c7868353ada796c839135b3d30554bbb74a4b1e4a5a58505c",
+ "public_key": "002e66aa57069c86cc18249aecf5cb5a9cebbfd6fadeab056254763874a9352b45",
+ "seed": "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"
+ },
+ {
+ "chain_code": "0902fe8a29f9140480a00ef244bd183e8a13288e4412d8389d140aac1794825a",
+ "path": "m/0'/2147483647'/1'/2147483646'",
+ "private_key": "5837736c89570de861ebc173b1086da4f505d4adb387c6a1b1342d5e4ac9ec72",
+ "public_key": "00e33c0f7d81d843c572275f287498e8d408654fdf0d1e065b84e2e6f157aab09b",
+ "seed": "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"
+ },
+ {
+ "chain_code": "5d70af781f3a37b829f0d060924d5e960bdc02e85423494afc0b1a41bbe196d4",
+ "path": "m/0'/2147483647'/1'/2147483646'/2'",
+ "private_key": "551d333177df541ad876a60ea71f00447931c0a9da16f227c11ea080d7391b8d",
+ "public_key": "0047150c75db263559a70d5778bf36abbab30fb061ad69f69ece61a72b0cfa4fc0",
+ "seed": "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"
+ }
+ ],
+ "version": 2,
+ "wordlist_sha256": "2f5eed53a4727b4bf8880d8f3f199efc90e58503646d9ff8eff3a2ed3b24dbda",
"xe": [
{
- "seed": "0000000000000000000000000000000000000000000000000000000000000000",
+ "account": 0,
+ "address": "294bfbdce14da4c715440065f1e94cbeb8053c63c228d6522b3ec9ab8d82d696",
+ "ed25519_seed": "7d203575d41abdaa7d0159b6a8e485d3093096a5a5aeb9739c626b5717e78624",
+ "passphrase": "",
+ "path": "m/44'/1'/0'",
+ "phrase": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
+ "public_key": "9b626614bec79527c8d0176d049ce72ccea9cbb44a80b4b3784edca18dd9b31b"
+ },
+ {
+ "account": 1,
+ "address": "8332fe88470d3fc5280539527154a8b8a1ec0ef89a92972a27daace126a0dc73",
+ "ed25519_seed": "72d309ea3765aed4384ff5bf67bb23b5f91886529ea67195b6333357cc5e8a71",
+ "passphrase": "",
+ "path": "m/44'/1'/1'",
"phrase": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
- "public_key": "3b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29",
- "address": "4ac0d6d4f0bd50354b93324c950096f6e0de5f91e42dac66583756afd50852de"
+ "public_key": "aa625ed91ccc7a74669b0f1d2d8ec2b02f4fa49ad37f97a64d4c12b4f29f19b9"
},
{
- "seed": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
- "phrase": "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote",
- "public_key": "76a1592044a6e4f511265bca73a604d90b0529d1df602be30a19a9257660d1f5",
- "address": "b25fe45d4afa568e72559a9e0bcf727c44dbdde8ab591df1f53fdd908cd73dbb"
+ "account": 0,
+ "address": "697ab39a01adee1ae662a330a94c08bc894f2c275ed08c01b0abb60be00fc280",
+ "ed25519_seed": "ae60c329be0040aff4398bdc5fa778b84943285f3db253db798dbc965a603f26",
+ "passphrase": "TREZOR",
+ "path": "m/44'/1'/0'",
+ "phrase": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
+ "public_key": "d38f0c46069036a0b6b9d0c71d525363074f47287637e4a9e1380b572ed3e6d7"
+ },
+ {
+ "account": 0,
+ "address": "81cbacf91f0905fb0362b0485e8edc7683b4698d3979460c4dc6ffeb5049b758",
+ "ed25519_seed": "aee46d61b83de269c446a04b2beb5ea7ee1ce72207b5e7b8a8a10e642405b680",
+ "passphrase": "",
+ "path": "m/44'/1'/0'",
+ "phrase": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
+ "public_key": "a90eb13b090b4d4c31ae87d864c0cd8db25216c90c1ce6fbfd04ddcdc1aa110c"
+ },
+ {
+ "account": 0,
+ "address": "b7f23581a788c20bf2915c20c3963035cb0bf35ad14d4b8be29e4e356b134e25",
+ "ed25519_seed": "422d3d2244d826fefe6e216316184631339cd209424f72de273aed3339cfa630",
+ "passphrase": "",
+ "path": "m/44'/1'/0'",
+ "phrase": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon address",
+ "public_key": "bb38e7d4fb05fd29d7cedfeaa048b0c734ad2938f93065da0f984a4a0335be9e"
},
{
- "seed": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
- "phrase": "abandon amount liar amount expire adjust cage candy arch gather drum bullet absurd math era live bid rhythm alien crouch range attend journey unaware",
- "public_key": "03a107bff3ce10be1d70dd18e74bc09967e4d6309ba50d5f1ddc8664125531b8",
- "address": "338c76e03f0a8dd21192808ba1a11879b2202591bf53f4338070119f4f54a0e1"
+ "account": 0,
+ "address": "347f9ea90d348ecbb433e04698653def09f952ae38c7dde59beb90ab8aade48b",
+ "ed25519_seed": "c03e5d6a86d247e914cc6026e2a497ca9ac5bdc079526b90eb9503aa0fa24bbf",
+ "passphrase": "",
+ "path": "m/44'/1'/0'",
+ "phrase": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon admit",
+ "public_key": "5af965d221d9402f42f0db502f506722afa90582906c350386bf6e108b324568"
},
{
- "seed": "7a11413e0111030869fb5d1e434ef96110be6dbb0fb160064b3fa7a36a5f35bf",
- "phrase": "key meat exhibit acquire awake loud pony hill bundle bounce salad season armor cycle umbrella wage gas crane soup exercise cute convince street trophy",
- "public_key": "42aa7e1f7b83cf10b730a4ac7e11c8c4dda9321b60207ee8a9649932cada7319",
- "address": "1cc821b6fedffceeac924054c97a3185ab835c498da225cbbd7bc89f1945a769"
+ "account": 0,
+ "address": "b64267a13412514eb502f7e7ff2c7eeeb67ed72e7fce48323a0caba4f4617f08",
+ "ed25519_seed": "6edd4467bfbce22d7d75dfa6f27be77c0b93054afdaefe6aac8a2667ac665fff",
+ "passphrase": "",
+ "path": "m/44'/1'/0'",
+ "phrase": "current atom you one bridge donkey joy ghost misery museum control roast fresh session wage route error rocket hello random happy broom pact prize",
+ "public_key": "baa9f48534fe96d4d96de832ac08bf4cc8809f1a2dc2ff54716ccb3fe296a1df"
},
{
- "seed": "1c9813e44d6cfe05cd8d55a442897c56091e03aa6744c82cc16e33383d3a054e",
- "phrase": "broken scene weekend onion soup actual curtain primary picture begin gallery project music achieve praise trigger sight gravity fortune grid long other benefit special",
- "public_key": "a0ccc1be08b3b369c3b5b2bc8e47a61a1cb2cc6fa5662a5882ba09065cf1558c",
- "address": "cadb731c0c153a76bd503702f7c2f51026623d2e3b31b51981cbe6873f0bb26b"
+ "account": 0,
+ "address": "7a3ca304f157dca1f5d00ae5aef57c07e0883bd86b713f5f12cd939bac321296",
+ "ed25519_seed": "a7f939308aba0135c7e90a1197a3114b2b8b866942668d76fe517401720a0ad2",
+ "passphrase": "",
+ "path": "m/44'/1'/0'",
+ "phrase": "quit possible silent pole same apple ready senior barely hire fruit alone vault sight pink retire glimpse illness you frame long text churn size",
+ "public_key": "c0e3810bfe461dba64fd787f9f606ca650773ec5ed4a6268d3e3020e528e41ce"
+ },
+ {
+ "account": 0,
+ "address": "cdc1ba866ddd2a5752c1239178e665bdacab567672d0463fd625fde357045ae4",
+ "ed25519_seed": "18bd53b00e8369b9d71eac8cb036a9aee2b909286dc48ac49c2e09224b3deb8a",
+ "passphrase": "",
+ "path": "m/44'/1'/0'",
+ "phrase": "addict stock task sea indoor submit hedgehog asthma wool garlic divert idle easy valve shoe toddler credit gaze rival faculty volcano spread negative crack",
+ "public_key": "06830dfabeaf5780b8b8978d8fa1c17d512e1ee493a844761faf94a6a28f92ef"
+ },
+ {
+ "account": 2147483647,
+ "address": "8a55e340aefad5ccb7c7294ac3e7fa48d25cdffc3f610a5de2138ad9c18bdd51",
+ "ed25519_seed": "737347d695dfdc418b613daf73c39df18de6968ab34d37b4514c2cbebcdd8c33",
+ "passphrase": "café㍍ガバヴァぱばぐゞちぢ十人十色",
+ "path": "m/44'/1'/2147483647'",
+ "phrase": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
+ "public_key": "58becdcf2c42de4f917ef2307eb15ddb744ad5916347a69c222d2991f81a55af"
}
]
}
diff --git a/vitest.config.js b/vitest.config.js
index 06b05e1..1338b8d 100644
--- a/vitest.config.js
+++ b/vitest.config.js
@@ -1,6 +1,8 @@
import { defineConfig } from 'vitest/config'
+import vue from '@vitejs/plugin-vue'
export default defineConfig({
+ plugins: [vue()],
test: {
environment: 'jsdom',
setupFiles: ['./src/utils/test-setup.js'],