mirror of
https://github.com/Permissionless-Software-Foundation/ipfs-bch-wallet-service.git
synced 2026-09-21 16:52:03 -07:00
Adding default startup wallet
This commit is contained in:
+11
-1
@@ -17,6 +17,7 @@ import Nodemailer from './nodemailer.js'
|
|||||||
import JSONFiles from './json-files.js'
|
import JSONFiles from './json-files.js'
|
||||||
import FullStackJWT from './fullstack-jwt.js'
|
import FullStackJWT from './fullstack-jwt.js'
|
||||||
import config from '../../config/index.js'
|
import config from '../../config/index.js'
|
||||||
|
import Wallet from './wallet.adapter.js'
|
||||||
|
|
||||||
class Adapters {
|
class Adapters {
|
||||||
constructor (localConfig = {}) {
|
constructor (localConfig = {}) {
|
||||||
@@ -29,6 +30,7 @@ class Adapters {
|
|||||||
this.jsonFiles = new JSONFiles()
|
this.jsonFiles = new JSONFiles()
|
||||||
this.bchjs = new BCHJS()
|
this.bchjs = new BCHJS()
|
||||||
this.config = config
|
this.config = config
|
||||||
|
this.wallet = new Wallet(localConfig)
|
||||||
|
|
||||||
// Get a valid JWT API key and instance bch-js.
|
// Get a valid JWT API key and instance bch-js.
|
||||||
this.fullStackJwt = new FullStackJWT(config)
|
this.fullStackJwt = new FullStackJWT(config)
|
||||||
@@ -36,14 +38,22 @@ class Adapters {
|
|||||||
|
|
||||||
async start () {
|
async start () {
|
||||||
try {
|
try {
|
||||||
|
let apiToken
|
||||||
if (this.config.getJwtAtStartup) {
|
if (this.config.getJwtAtStartup) {
|
||||||
// Get a JWT token and instantiate bch-js with it. Then pass that instance
|
// Get a JWT token and instantiate bch-js with it. Then pass that instance
|
||||||
// to all the rest of the apps controllers and adapters.
|
// to all the rest of the apps controllers and adapters.
|
||||||
await this.fullStackJwt.getJWT()
|
apiToken = await this.fullStackJwt.getJWT()
|
||||||
// Instantiate bch-js with the JWT token, and overwrite the placeholder for bch-js.
|
// Instantiate bch-js with the JWT token, and overwrite the placeholder for bch-js.
|
||||||
this.bchjs = await this.fullStackJwt.instanceBchjs()
|
this.bchjs = await this.fullStackJwt.instanceBchjs()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a default instance of minimal-slp-wallet without initializing it
|
||||||
|
// (without retrieving the wallets UTXOs). This instance will be overwritten
|
||||||
|
// if the operator has configured BCH payments.
|
||||||
|
console.log('\nCreating default startup wallet. This wallet may be overwritten.')
|
||||||
|
await this.wallet.instanceWalletWithoutInitialization({}, { apiToken })
|
||||||
|
this.bchjs = this.wallet.bchWallet.bchjs
|
||||||
|
|
||||||
// Start the IPFS node.
|
// Start the IPFS node.
|
||||||
// Do not start these adapters if this is an e2e test.
|
// Do not start these adapters if this is an e2e test.
|
||||||
if (this.config.env !== 'test') {
|
if (this.config.env !== 'test') {
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
Wallet adapter. This library instantiates minimal-slp-wallet based on different
|
||||||
|
configuration settings. That single instance of minimal-slp-wallet is then
|
||||||
|
easily passed around the rest of this application.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Global npm libraries
|
||||||
|
import BchWallet from 'minimal-slp-wallet'
|
||||||
|
|
||||||
|
// Local libraries
|
||||||
|
import config from '../../config/index.js'
|
||||||
|
import JsonFiles from './json-files.js'
|
||||||
|
|
||||||
|
class Wallet {
|
||||||
|
constructor (localConfig = {}) {
|
||||||
|
// Encapsulate dependencies
|
||||||
|
this.config = config
|
||||||
|
this.BchWallet = BchWallet
|
||||||
|
this.jsonFiles = new JsonFiles()
|
||||||
|
|
||||||
|
// Bind 'this' object to all subfunctions
|
||||||
|
this.instanceWalletWithoutInitialization = this.instanceWalletWithoutInitialization.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is used for initializing the wallet, without waiting to update the wallet
|
||||||
|
// UTXOs from the blockchain.
|
||||||
|
// This is useful when the wallet is simply needed to make calls to the blockchain,
|
||||||
|
// and there is no need to hydrate it with UTXO data.
|
||||||
|
async instanceWalletWithoutInitialization (walletData = {}, advancedConfig = {}) {
|
||||||
|
try {
|
||||||
|
// Use the apiToken from the config settings, if one is not passed-in at
|
||||||
|
// run-time.
|
||||||
|
if (!advancedConfig.apiToken) {
|
||||||
|
advancedConfig.apiToken = this.config.apiToken
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an authentication password is passed in the config, add it.
|
||||||
|
if (this.config.authPass) {
|
||||||
|
advancedConfig.authPass = this.config.authPass
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect and configure different blockchain infrastructure settings.
|
||||||
|
if (this.config.useFullStackCash) {
|
||||||
|
advancedConfig.interface = 'rest-api'
|
||||||
|
advancedConfig.restURL = this.config.apiServer
|
||||||
|
} else {
|
||||||
|
// By default use the web3 Cash Stack (https://cashstack.info)
|
||||||
|
advancedConfig.interface = 'consumer-api'
|
||||||
|
advancedConfig.restURL = this.config.consumerUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('advancedConfig setting when creating wallet: ', advancedConfig)
|
||||||
|
|
||||||
|
// Instantiate minimal-slp-wallet.
|
||||||
|
if (walletData.mnemonic) {
|
||||||
|
// Instance the wallet using the mnemonic passed in to this function.
|
||||||
|
console.log('Mnemonic provided to wallet library from calling function.')
|
||||||
|
this.bchWallet = await this._instanceWallet(walletData.mnemonic, advancedConfig)
|
||||||
|
} else if (this.config.mnemonic) {
|
||||||
|
// Otherwise use the mnemonic in the config setting or passed as an environment variable.
|
||||||
|
console.log('Mnemonic retrieved from config file or environment variable.')
|
||||||
|
this.bchWallet = await this._instanceWallet(this.config.mnemonic, advancedConfig)
|
||||||
|
} else {
|
||||||
|
// If no mnemonic is provided, then generate a new mnemonic to create the wallet.
|
||||||
|
console.log('New mnemonic generated.')
|
||||||
|
this.bchWallet = await this._instanceWallet(undefined, advancedConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for wallet to initialize.
|
||||||
|
await this.bchWallet.walletInfoPromise
|
||||||
|
console.log('BCH wallet initialized.')
|
||||||
|
console.log(`Wallet address: ${this.bchWallet.walletInfo.cashAddress}`)
|
||||||
|
console.log(`Wallet mnemonic: ${this.bchWallet.walletInfo.mnemonic}`)
|
||||||
|
|
||||||
|
return this.bchWallet
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error in instanceWalletWithoutInitialization()')
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is used for easier mocking of unit tests.
|
||||||
|
async _instanceWallet (mnemonic, config) {
|
||||||
|
const wallet = new this.BchWallet(mnemonic, config)
|
||||||
|
await wallet.walletInfoPromise
|
||||||
|
|
||||||
|
console.log('_instanceWallet() wallet.bchjs.restURL: ', wallet.bchjs.restURL)
|
||||||
|
|
||||||
|
return wallet
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the wallet file, or create one if the file doesn't exist.
|
||||||
|
// Does not instance the wallet. The output of this function is expected to
|
||||||
|
// be passed to instanceWallet().
|
||||||
|
async openWallet (advancedConfig = {}) {
|
||||||
|
try {
|
||||||
|
let walletData
|
||||||
|
|
||||||
|
// Try to open the wallet.json file.
|
||||||
|
try {
|
||||||
|
// console.log('this.WALLET_FILE: ', this.WALLET_FILE)
|
||||||
|
walletData = await this.jsonFiles.readJSON(this.WALLET_FILE)
|
||||||
|
} catch (err) {
|
||||||
|
// Create a new wallet file if one does not already exist.
|
||||||
|
console.log('Wallet file not found. Creating new wallet.json file.')
|
||||||
|
|
||||||
|
// Create a new wallet.
|
||||||
|
// No-Update flag creates wallet without making any network calls.
|
||||||
|
const walletInstance = await this.instanceWalletWithoutInitialization({}, advancedConfig)
|
||||||
|
|
||||||
|
walletData = walletInstance.walletInfo
|
||||||
|
|
||||||
|
// Add the nextAddress property
|
||||||
|
walletData.nextAddress = 1
|
||||||
|
|
||||||
|
// Write the wallet data to the JSON file.
|
||||||
|
await this.jsonFiles.writeJSON(walletData, this.WALLET_FILE)
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log('walletData: ', walletData)
|
||||||
|
return walletData
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error in openWallet()')
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Wallet
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"privateKey": "KwftZ1XUKMP9cLwTjXM3Axk5dnjy28nZ7b54PMushFEYs71tXQZu",
|
||||||
|
"publicKey": "038ac0965dc75b9b98a8e1fee01d8e4bcb23770ad00d8b41f76ef3c07519e76ded",
|
||||||
|
"mnemonic": "blue unfair eye isolate capital devote horse lumber thrive second text flame",
|
||||||
|
"cashAddress": "bitcoincash:qprt28ydgfgx8da26y35us57ur5pexwjns0uvyn3qj",
|
||||||
|
"address": "bitcoincash:qprt28ydgfgx8da26y35us57ur5pexwjns0uvyn3qj",
|
||||||
|
"legacyAddress": "17SsGH7mb4nQEVh7hwTPYbYbgmWTnyWf3y",
|
||||||
|
"hdPath": "m/44'/245'/0'/0/0",
|
||||||
|
"slpAddress": "simpleledger:qprt28ydgfgx8da26y35us57ur5pexwjnsr88lx37v",
|
||||||
|
"nextAddress": 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
/*
|
||||||
|
Unit tests for the Wallet Adapter library.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Global npm libraries
|
||||||
|
import { assert } from 'chai'
|
||||||
|
import sinon from 'sinon'
|
||||||
|
import BchWallet from 'minimal-slp-wallet'
|
||||||
|
import fs from 'fs'
|
||||||
|
|
||||||
|
// Local libraries
|
||||||
|
import WalletAdapter from '../../../src/adapters/wallet.adapter.js'
|
||||||
|
import { MockBchWallet } from '../mocks/adapters/wallet.js'
|
||||||
|
|
||||||
|
// Hack to get __dirname back.
|
||||||
|
// https://blog.logrocket.com/alternatives-dirname-node-js-es-modules/
|
||||||
|
import * as url from 'url'
|
||||||
|
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
|
||||||
|
|
||||||
|
// Global constants
|
||||||
|
const testWalletFile = `${__dirname.toString()}/test-wallet.json`
|
||||||
|
|
||||||
|
describe('#wallet', () => {
|
||||||
|
let uut
|
||||||
|
let sandbox
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
// Delete the test file if it exists.
|
||||||
|
try {
|
||||||
|
deleteFile(testWalletFile)
|
||||||
|
} catch (err) { }
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
uut = new WalletAdapter()
|
||||||
|
sandbox = sinon.createSandbox()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => sandbox.restore())
|
||||||
|
|
||||||
|
after(() => {
|
||||||
|
// Delete the test file if it exists.
|
||||||
|
try {
|
||||||
|
deleteFile(testWalletFile)
|
||||||
|
} catch (err) { }
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('#_instanceWallet', () => {
|
||||||
|
it('should create a wallet given a mnemonic', async () => {
|
||||||
|
const mnemonic = 'wagon tray learn flat erase laugh lonely rug check captain jacket morning'
|
||||||
|
const result = await uut._instanceWallet(mnemonic)
|
||||||
|
// console.log('result: ', result)
|
||||||
|
assert.equal(result.walletInfo.mnemonic, mnemonic)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('#openWallet', () => {
|
||||||
|
it('should create a new wallet when wallet file does not exist', async () => {
|
||||||
|
// Mock dependencies
|
||||||
|
uut.BchWallet = MockBchWallet
|
||||||
|
// Ensure we open the test file, not the production wallet file.
|
||||||
|
uut.WALLET_FILE = testWalletFile
|
||||||
|
const result = await uut.openWallet()
|
||||||
|
// console.log('result: ', result)
|
||||||
|
assert.property(result, 'mnemonic')
|
||||||
|
assert.property(result, 'privateKey')
|
||||||
|
assert.property(result, 'publicKey')
|
||||||
|
assert.property(result, 'cashAddress')
|
||||||
|
assert.property(result, 'address')
|
||||||
|
assert.property(result, 'slpAddress')
|
||||||
|
assert.property(result, 'legacyAddress')
|
||||||
|
assert.property(result, 'hdPath')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should open existing wallet file', async () => {
|
||||||
|
// This test case uses the file created in the previous test case.
|
||||||
|
// Ensure we open the test file, not the production wallet file.
|
||||||
|
uut.WALLET_FILE = testWalletFile
|
||||||
|
const result = await uut.openWallet()
|
||||||
|
// console.log('result: ', result)
|
||||||
|
assert.property(result, 'mnemonic')
|
||||||
|
assert.property(result, 'privateKey')
|
||||||
|
assert.property(result, 'publicKey')
|
||||||
|
assert.property(result, 'cashAddress')
|
||||||
|
assert.property(result, 'address')
|
||||||
|
assert.property(result, 'slpAddress')
|
||||||
|
assert.property(result, 'legacyAddress')
|
||||||
|
assert.property(result, 'hdPath')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should catch and throw an error', async () => {
|
||||||
|
try {
|
||||||
|
// Force an error
|
||||||
|
uut.WALLET_FILE = ''
|
||||||
|
uut.BchWallet = () => {
|
||||||
|
}
|
||||||
|
await uut.openWallet()
|
||||||
|
// console.log('result: ', result)
|
||||||
|
assert.fail('Unexpected code path')
|
||||||
|
} catch (err) {
|
||||||
|
// console.log('err: ', err)
|
||||||
|
assert.include(err.message, 'this.BchWallet is not a constructor')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('#instanceWalletWithoutInitialization', () => {
|
||||||
|
it('should create an instance of BchWallet', async () => {
|
||||||
|
// Create a mock wallet.
|
||||||
|
const mockWallet = new BchWallet()
|
||||||
|
await mockWallet.walletInfoPromise
|
||||||
|
sandbox.stub(mockWallet, 'initialize').resolves()
|
||||||
|
|
||||||
|
// Mock dependencies
|
||||||
|
sandbox.stub(uut, '_instanceWallet').resolves(mockWallet)
|
||||||
|
uut.config.authPass = 'fake-auth-pass'
|
||||||
|
|
||||||
|
// Ensure we open the test file, not the production wallet file.
|
||||||
|
uut.WALLET_FILE = testWalletFile
|
||||||
|
const walletData = await uut.openWallet()
|
||||||
|
|
||||||
|
// console.log('walletData: ', walletData)
|
||||||
|
const result = await uut.instanceWalletWithoutInitialization(walletData)
|
||||||
|
// console.log('result: ', result)
|
||||||
|
|
||||||
|
assert.property(result, 'walletInfoPromise')
|
||||||
|
assert.property(result, 'walletInfo')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should catch and throw an error', async () => {
|
||||||
|
try {
|
||||||
|
// Force an error
|
||||||
|
sandbox.stub(uut, '_instanceWallet').rejects(new Error('test error'))
|
||||||
|
|
||||||
|
await uut.instanceWalletWithoutInitialization()
|
||||||
|
|
||||||
|
assert.fail('Unexpected code path')
|
||||||
|
} catch (err) {
|
||||||
|
// console.log('err: ', err)
|
||||||
|
assert.include(err.message, 'test error')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create an instance of BchWallet using web2 infra', async () => {
|
||||||
|
// Create a mock wallet.
|
||||||
|
const mockWallet = new BchWallet()
|
||||||
|
await mockWallet.walletInfoPromise
|
||||||
|
sandbox.stub(mockWallet, 'initialize').resolves()
|
||||||
|
// Mock dependencies
|
||||||
|
sandbox.stub(uut, '_instanceWallet').resolves(mockWallet)
|
||||||
|
// Ensure we open the test file, not the production wallet file.
|
||||||
|
uut.WALLET_FILE = testWalletFile
|
||||||
|
const walletData = await uut.openWallet()
|
||||||
|
// console.log('walletData: ', walletData)
|
||||||
|
// Force desired code path
|
||||||
|
uut.config.useFullStackCash = true
|
||||||
|
const result = await uut.instanceWalletWithoutInitialization(walletData)
|
||||||
|
// console.log('result: ', result)
|
||||||
|
assert.property(result, 'walletInfoPromise')
|
||||||
|
assert.property(result, 'walletInfo')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should generate wallet from mnemonic in config', async () => {
|
||||||
|
// Create a mock wallet.
|
||||||
|
const mockWallet = new BchWallet()
|
||||||
|
await mockWallet.walletInfoPromise
|
||||||
|
sandbox.stub(mockWallet, 'initialize').resolves()
|
||||||
|
|
||||||
|
// Mock dependencies
|
||||||
|
sandbox.stub(uut, '_instanceWallet').resolves(mockWallet)
|
||||||
|
|
||||||
|
// Ensure we open the test file, not the production wallet file.
|
||||||
|
uut.WALLET_FILE = testWalletFile
|
||||||
|
const walletData = await uut.openWallet()
|
||||||
|
// console.log('walletData: ', walletData)
|
||||||
|
|
||||||
|
const originalConfig = uut.config.mnemonic
|
||||||
|
uut.config.mnemonic = walletData.mnemonic
|
||||||
|
|
||||||
|
const result = await uut.instanceWalletWithoutInitialization({})
|
||||||
|
// console.log('result: ', result)
|
||||||
|
|
||||||
|
uut.config.mnemonic = originalConfig
|
||||||
|
|
||||||
|
assert.property(result, 'walletInfoPromise')
|
||||||
|
assert.property(result, 'walletInfo')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const deleteFile = (filepath) => {
|
||||||
|
try {
|
||||||
|
// Delete state if exist
|
||||||
|
fs.unlinkSync(filepath)
|
||||||
|
} catch (err) {
|
||||||
|
// console.error('Error trying to delete file: ', err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
import BCHJS from "@psf/bch-js";
|
||||||
|
|
||||||
|
const mockWallet = {
|
||||||
|
mnemonic: 'course abstract aerobic deer try switch turtle diet fence affair butter top',
|
||||||
|
privateKey: 'L5D2UAam8tvo3uii5kpgaGyjvVMimdrXu8nWGQSQjuuAix6ji1YQ',
|
||||||
|
publicKey: '0379433ffc401483ade310469953c1cba77c71af904f07c15bde330d7198b4d6dc',
|
||||||
|
cashAddress: 'bitcoincash:qzl0d3gcqeypv4cy7gh8rgdszxa9vvm2acv7fqtd00',
|
||||||
|
address: 'bitcoincash:qzl0d3gcqeypv4cy7gh8rgdszxa9vvm2acv7fqtd00',
|
||||||
|
slpAddress: 'simpleledger:qzl0d3gcqeypv4cy7gh8rgdszxa9vvm2acq9zm7d33',
|
||||||
|
legacyAddress: '1JQj1KcQL7GPKzc1D2PvdUSgw3MbDtrHzi',
|
||||||
|
hdPath: "m/44'/245'/0'/0/0",
|
||||||
|
nextAddress: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
class MockBchWallet {
|
||||||
|
constructor() {
|
||||||
|
this.walletInfoPromise = true;
|
||||||
|
this.walletInfo = mockWallet;
|
||||||
|
this.initialize = async () => {}
|
||||||
|
this.bchjs = new BCHJS();
|
||||||
|
this.burnTokens = async () => {
|
||||||
|
return { success: true, txid: 'txid' };
|
||||||
|
};
|
||||||
|
this.sendTokens = async () => {
|
||||||
|
return 'fakeTxid';
|
||||||
|
};
|
||||||
|
this.getUtxos = async () => { };
|
||||||
|
this.getBalance = async () => { };
|
||||||
|
this.listTokens = async () => { };
|
||||||
|
this.getTxData = async () => {
|
||||||
|
return [{
|
||||||
|
tokenTicker: 'TROUT'
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
this.optimize = async () => { };
|
||||||
|
// Environment variable is used by wallet-balance.unit.js to force an error.
|
||||||
|
if (process.env.NO_UTXO) {
|
||||||
|
this.utxos = {};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.utxos = {
|
||||||
|
utxoStore: {
|
||||||
|
address: 'bitcoincash:qqetvdnlt0p8g27dr44cx7h057kpzly9xse9huc97z',
|
||||||
|
bchUtxos: [
|
||||||
|
{
|
||||||
|
height: 700685,
|
||||||
|
tx_hash: '1fc577caaff5626a8477162581e57bae1b19dc6aa6c10638013c2b1ba14dc654',
|
||||||
|
tx_pos: 0,
|
||||||
|
value: 1000,
|
||||||
|
txid: '1fc577caaff5626a8477162581e57bae1b19dc6aa6c10638013c2b1ba14dc654',
|
||||||
|
vout: 0,
|
||||||
|
isValid: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
height: 700685,
|
||||||
|
tx_hash: '1fc577caaff5626a8477162581e57bae1b19dc6aa6c10638013c2b1ba14dc654',
|
||||||
|
tx_pos: 2,
|
||||||
|
value: 19406,
|
||||||
|
txid: '1fc577caaff5626a8477162581e57bae1b19dc6aa6c10638013c2b1ba14dc654',
|
||||||
|
vout: 2,
|
||||||
|
isValid: false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
nullUtxos: [],
|
||||||
|
slpUtxos: {
|
||||||
|
type1: {
|
||||||
|
mintBatons: [],
|
||||||
|
tokens: [
|
||||||
|
{
|
||||||
|
'height': 717331,
|
||||||
|
'tx_hash': '74889580bb1a5f8c026aa2f55118ac9917df3332f7abae72a70343daa1c29621',
|
||||||
|
'tx_pos': 1,
|
||||||
|
'value': 546,
|
||||||
|
'txid': '74889580bb1a5f8c026aa2f55118ac9917df3332f7abae72a70343daa1c29621',
|
||||||
|
'vout': 1,
|
||||||
|
'isSlp': true,
|
||||||
|
'type': 'token',
|
||||||
|
'qty': '10',
|
||||||
|
'tokenId': '600ee24d0f208aebc2bdd2c4ee1b9acb6d57343561442e8676b5bbea311d5a0f',
|
||||||
|
'address': 'bitcoincash:qqraj35x6l2qyqhjm5l7qlt7z2245ez8l5z3dwkeq5',
|
||||||
|
'ticker': 'FLIPS',
|
||||||
|
'name': 'FLIPS',
|
||||||
|
'documentUri': '',
|
||||||
|
'documentHash': '',
|
||||||
|
'decimals': 1,
|
||||||
|
'qtyStr': '1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'height': 730597,
|
||||||
|
'tx_hash': '52520faddfafc46b8f8c9548b097f3a3b82a5bf363b5095047b9c5f83247fe36',
|
||||||
|
'tx_pos': 1,
|
||||||
|
'value': 546,
|
||||||
|
'txid': '52520faddfafc46b8f8c9548b097f3a3b82a5bf363b5095047b9c5f83247fe36',
|
||||||
|
'vout': 1,
|
||||||
|
'isSlp': true,
|
||||||
|
'type': 'token',
|
||||||
|
'qty': '34999991',
|
||||||
|
'tokenId': '38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0',
|
||||||
|
'address': 'bitcoincash:qqraj35x6l2qyqhjm5l7qlt7z2245ez8l5z3dwkeq5',
|
||||||
|
'ticker': 'PSF',
|
||||||
|
'name': 'Permissionless Software Foundation',
|
||||||
|
'documentUri': 'psfoundation.cash',
|
||||||
|
'documentHash': '',
|
||||||
|
'decimals': 8,
|
||||||
|
'qtyStr': '0.34999991'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'height': 730597,
|
||||||
|
'tx_hash': '5dc7e7c91382aed1666a51212dfb74050261e12c3c4f62b6b1e57f42d6c51ee1',
|
||||||
|
'tx_pos': 2,
|
||||||
|
'value': 546,
|
||||||
|
'txid': '5dc7e7c91382aed1666a51212dfb74050261e12c3c4f62b6b1e57f42d6c51ee1',
|
||||||
|
'vout': 2,
|
||||||
|
'isSlp': true,
|
||||||
|
'type': 'token',
|
||||||
|
'qty': '18898',
|
||||||
|
'tokenId': 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||||
|
'address': 'bitcoincash:qqraj35x6l2qyqhjm5l7qlt7z2245ez8l5z3dwkeq5',
|
||||||
|
'ticker': 'TROUT',
|
||||||
|
'name': "Trout's test token",
|
||||||
|
'documentUri': 'troutsblog.com',
|
||||||
|
'documentHash': '',
|
||||||
|
'decimals': 2,
|
||||||
|
'qtyStr': '188.98'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
nft: {
|
||||||
|
tokens: []
|
||||||
|
},
|
||||||
|
group: {
|
||||||
|
tokens: [],
|
||||||
|
mintBatons: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { MockBchWallet };
|
||||||
|
|
||||||
|
export { mockWallet };
|
||||||
|
|
||||||
|
export default {
|
||||||
|
MockBchWallet,
|
||||||
|
mockWallet
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user