Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Bump defichain dependencies #321

Merged
merged 14 commits into from
Jul 23, 2021
9 changes: 6 additions & 3 deletions app/api/wallet/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ it('should createWallet', async () => {
const data: WalletData = {
version: "v1",
type: WalletType.MNEMONIC_UNPROTECTED,
raw: "408b285c123836004f4b8842c89324c1f01382450c0d439af345ba7fc49acf705489c6fc77dbd4e3dc1dd8cc6bc9f043db8ada1e243c4a0eafb290d399480840"
raw: JSON.stringify([
'235b34cd7c9f6d7e4595ffe9ae4b1cb5606df8aca2b527d20a07c8f56b2342f4', // root priv key
'f40eaad21641ca7cb5ac00f9ce21cac9ba070bb673a237f7bce57acda54386a4' // chain code
])
}

const wallet = createWallet(data, options, provider)
const wallet = createWallet(data, network, provider)

expect(wallet.get(0).withTransactionBuilder().utxo).toBeDefined()
expect(wallet.get(0).withTransactionBuilder().dex).toBeDefined()
Expand All @@ -38,6 +41,6 @@ it('should fail as wallet type not available', async () => {
}

expect(() => {
createWallet(data, options, provider)
createWallet(data, network, provider)
}).toThrow('wallet undefined not available')
})
4 changes: 2 additions & 2 deletions app/api/wallet/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Network } from '@defichain/jellyfish-network'
import { JellyfishWallet, WalletHdNode } from '@defichain/jellyfish-wallet'
import { WhaleWalletAccount, WhaleWalletAccountProvider } from '@defichain/whale-api-wallet'
import { EnvironmentNetwork } from '../../environment'
import { Mnemonic } from './mnemonic'
import { WalletData, WalletType } from './persistence'

export type Wallet = JellyfishWallet<WhaleWalletAccount, WalletHdNode>

export function createWallet (data: WalletData, options: Network, accountProvider: WhaleWalletAccountProvider): Wallet {
export function createWallet (data: WalletData, options: EnvironmentNetwork, accountProvider: WhaleWalletAccountProvider): Wallet {
switch (data.type) {
case WalletType.MNEMONIC_UNPROTECTED:
return new JellyfishWallet(Mnemonic.createProvider(data, options), accountProvider)
Expand Down
22 changes: 15 additions & 7 deletions app/api/wallet/mnemonic.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { EnvironmentNetwork } from "../../environment";
import { Mnemonic } from "./mnemonic";
import { getJellyfishNetwork } from "./network";
import { WalletData, WalletType } from "./persistence";

beforeEach(() => {
Expand All @@ -12,9 +11,12 @@ describe('getMnemonicHdNodeProvider', () => {
const data: WalletData = {
version: "v1",
type: WalletType.MNEMONIC_UNPROTECTED,
raw: "408b285c123836004f4b8842c89324c1f01382450c0d439af345ba7fc49acf705489c6fc77dbd4e3dc1dd8cc6bc9f043db8ada1e243c4a0eafb290d399480840"
raw: JSON.stringify([
fuxingloh marked this conversation as resolved.
Show resolved Hide resolved
'235b34cd7c9f6d7e4595ffe9ae4b1cb5606df8aca2b527d20a07c8f56b2342f4', // root priv key
'f40eaad21641ca7cb5ac00f9ce21cac9ba070bb673a237f7bce57acda54386a4' // chain code
])
}
const options = getJellyfishNetwork(EnvironmentNetwork.LocalPlayground)
const options = EnvironmentNetwork.LocalPlayground

const provider = Mnemonic.createProvider(data, options)
expect(provider).toBeTruthy()
Expand All @@ -30,20 +32,26 @@ describe('getMnemonicHdNodeProvider', () => {

describe('addMnemonicHdNodeProvider', () => {
it('should set mnemonic (abandon x23)', async () => {
expect(Mnemonic.createWalletDataAbandon23()).toStrictEqual({
expect(Mnemonic.createWalletDataAbandon23(EnvironmentNetwork.LocalPlayground)).toStrictEqual({
version: "v1",
type: WalletType.MNEMONIC_UNPROTECTED,
raw: "408b285c123836004f4b8842c89324c1f01382450c0d439af345ba7fc49acf705489c6fc77dbd4e3dc1dd8cc6bc9f043db8ada1e243c4a0eafb290d399480840"
raw: JSON.stringify([
'235b34cd7c9f6d7e4595ffe9ae4b1cb5606df8aca2b527d20a07c8f56b2342f4',
'f40eaad21641ca7cb5ac00f9ce21cac9ba070bb673a237f7bce57acda54386a4'
])
})
})

it('should set mnemonic (void come effort ...)', async () => {
const 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'.split(' ')

expect(Mnemonic.createWalletData(mnemonic)).toStrictEqual({
expect(Mnemonic.createWalletData(mnemonic, EnvironmentNetwork.LocalPlayground)).toStrictEqual({
version: "v1",
type: WalletType.MNEMONIC_UNPROTECTED,
raw: 'b873212f885ccffbf4692afcb84bc2e55886de2dfa07d90f5c3c239abc31c0a6ce047e30fd8bf6a281e71389aa82d73df74c7bbfb3b06b4639a5cee775cccd3c'
raw: JSON.stringify([
'b21fcb414b4414e9bcf7ae647a79a4d29280f6b71cba204cb4dd3d6c6568d0fc',
'bbb5f26acee2e3713d43cf4e702f2b1ff8672afa9e0d5ac846196689e1d893d2'
])
})
})
})
43 changes: 27 additions & 16 deletions app/api/wallet/mnemonic.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,44 @@
import { Network } from '@defichain/jellyfish-network'
import { MnemonicHdNodeProvider, mnemonicToSeed } from '@defichain/jellyfish-wallet-mnemonic'
import { Bip32Options, MnemonicHdNodeProvider } from '@defichain/jellyfish-wallet-mnemonic'
import { EnvironmentNetwork } from '../../environment'
import { getJellyfishNetwork } from './network'
import { WalletData, WalletType } from './persistence'

function createProvider (data: WalletData, options: Network): MnemonicHdNodeProvider {
const seed = Buffer.from(data.raw, 'hex')

return MnemonicHdNodeProvider.fromSeed(seed, {
function getBip32Option (envNetwork: EnvironmentNetwork): Bip32Options {
const network = getJellyfishNetwork(envNetwork)
return {
bip32: {
public: options.bip32.publicPrefix,
private: options.bip32.privatePrefix
public: network.bip32.publicPrefix,
private: network.bip32.privatePrefix
},
wif: options.wifPrefix
})
wif: network.wifPrefix
}
}

function createProvider (data: WalletData, network: EnvironmentNetwork): MnemonicHdNodeProvider {
if (data.type !== WalletType.MNEMONIC_UNPROTECTED || data.version !== 'v1') {
throw new Error('Unexpected WalletData')
}
const [privKey, chainCode] = JSON.parse(data.raw)
return MnemonicHdNodeProvider.fromData({
words: [],
privKey,
chainCode
}, getBip32Option(network))
}

export function createWalletData (mnemonic: string[]): WalletData {
const seed = mnemonicToSeed(mnemonic)
const hex = seed.toString('hex')
export function createWalletData (mnemonic: string[], network: EnvironmentNetwork): WalletData {
fuxingloh marked this conversation as resolved.
Show resolved Hide resolved
const mnemonicData = MnemonicHdNodeProvider.wordsToData(mnemonic, getBip32Option(network))
return {
version: 'v1',
type: WalletType.MNEMONIC_UNPROTECTED,
raw: hex
raw: JSON.stringify([mnemonicData.privKey, mnemonicData.chainCode])
}
}

export function createWalletDataAbandon23 (): WalletData {
export function createWalletDataAbandon23 (network: EnvironmentNetwork): WalletData {
return createWalletData([
'abandon', 'abandon', 'abandon', 'abandon', 'abandon', 'abandon', 'abandon', 'abandon', 'abandon', 'abandon', 'abandon', 'abandon', 'abandon', 'abandon', 'abandon', 'abandon', 'abandon', 'abandon', 'abandon', 'abandon', 'abandon', 'abandon', 'abandon', 'art'
])
], network)
}

export const Mnemonic = {
Expand Down
2 changes: 1 addition & 1 deletion app/contexts/WalletManagementContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export function WalletManagementProvider (props: React.PropsWithChildren<any>):
const wallets = useMemo(() => {
const options = getJellyfishNetwork(network)
const provider = new WhaleWalletAccountProvider(client, options)
return dataList.map(data => createWallet(data, options, provider))
return dataList.map(data => createWallet(data, network, provider))
}, [dataList])

const management: WalletManagement = {
Expand Down
96 changes: 5 additions & 91 deletions app/screens/PlaygroundNavigator/sections/PlaygroundToken.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,16 @@
import { TokenInfo } from '@defichain/jellyfish-api-core/dist/category/token'
import { UTXO } from '@defichain/jellyfish-api-core/dist/category/wallet'
import { Bech32, Bs58, WIF } from '@defichain/jellyfish-crypto'
import { RegTest } from '@defichain/jellyfish-network'
import {
CTransactionSegWit,
DeFiTransactionConstants,
OP_CODES,
Script,
Transaction,
Vout
} from '@defichain/jellyfish-transaction'
import { TransactionSigner } from '@defichain/jellyfish-transaction-signature'
import { toOPCodes } from '@defichain/jellyfish-transaction/dist/script/_buffer'
import { PlaygroundRpcClient } from '@defichain/playground-api-client'
import { BigNumber } from 'bignumber.js'
import React, { useEffect, useState } from 'react'
import { SmartBuffer } from 'smart-buffer'
import { Text, View } from '../../../components'
import { usePlaygroundContext } from '../../../contexts/PlaygroundContext'
import { useWalletManagementContext } from '../../../contexts/WalletManagementContext'
import { tailwind } from '../../../tailwind'
import { PlaygroundAction } from '../components/PlaygroundAction'
import { PlaygroundStatus } from '../components/PlaygroundStatus'

/**
* Depend on defid setup in playground
* @see https://github.com/DeFiCh/playground/blob/main/src/module.playground/setup/setup.ts#L8-L9
*/
const PLAYGROUND_MN = {
address: 'mwsZw8nF7pKxWH8eoKL9tPxTpaFkz7QeLU',
privKey: 'cRiRQ9cHmy5evDqNDdEV8f6zfbK6epi9Fpz4CRZsmLEmkwy54dWz',
pubKeyHash: Bs58.toHash160('mwsZw8nF7pKxWH8eoKL9tPxTpaFkz7QeLU').buffer
}

export function PlaygroundToken (): JSX.Element | null {
const { wallets } = useWalletManagementContext()
const { rpc } = usePlaygroundContext()
const { rpc, api } = usePlaygroundContext()
const [status, setStatus] = useState<string>('loading')
const [tokens, setTokens] = useState<PlaygroundTokenInfo[]>([])

Expand Down Expand Up @@ -85,8 +60,10 @@ export function PlaygroundToken (): JSX.Element | null {
testID='playground_token_DFI'
title='Top up 10.0 DFI to Wallet'
onPress={async () => {
const script = await wallets[0].get(0).getScript()
await playgroundUtxoToUserAccount(rpc, script)
await api.wallet.sendTokenDfiToAddress({
amount: '10',
address: await wallets[0].get(0).getAddress()
})
}}
/>
{actions}
Expand All @@ -103,66 +80,3 @@ async function getTokens (rpcClient: PlaygroundRpcClient): Promise<PlaygroundTok
return { id, ...info }
}).sort(a => Number.parseInt(a.id))
}

/**
* this action requires 2 steps, convert utxos into token
* main user token balance require at least 6-8 seconds to be reflected
*/
async function playgroundUtxoToUserAccount (rpcClient: PlaygroundRpcClient, recipientLockScript: Script, amount: BigNumber = new BigNumber(10)): Promise<string> {
const pair = WIF.asEllipticPair(PLAYGROUND_MN.privKey)

// create a P2WPKH address to hold utxo
const tempMNAddress = Bech32.fromHash160(PLAYGROUND_MN.pubKeyHash, RegTest.bech32.hrp)

// fund utxo to playground new address
const txid = await rpcClient.call('sendtoaddress', [tempMNAddress, amount.plus(0.001).toNumber()], 'bignumber')
await (new Promise(resolve => { setTimeout(resolve, 3100) })) // wait for a block
const utxos = await rpcClient.call('listunspent', [1, 9999999, [tempMNAddress], false], 'bignumber')
const utxo: UTXO = (utxos as UTXO[]).find(utxo => utxo.txid === txid) as UTXO
const utxoLockScript = { stack: toOPCodes(SmartBuffer.fromBuffer(Buffer.from(utxo.scriptPubKey, 'hex'))) }

// send token to user (wallet account)
const a2a: Vout = {
value: amount,
script: {
stack: [OP_CODES.OP_RETURN, OP_CODES.OP_DEFI_TX_UTXOS_TO_ACCOUNT({
to: [{
script: recipientLockScript,
balances: [{
token: 0,
amount
}]
}]
})]
},
tokenId: 0x00
}

const transaction: Transaction = {
version: DeFiTransactionConstants.Version,
vin: [
{
index: utxo.vout,
script: { stack: [] },
sequence: 0xffffffff,
txid: utxo.txid
}
],
vout: [
a2a
// assume no change, 0.001 spared for fee
],
lockTime: 0x00000000
}

const signed = await TransactionSigner.sign(transaction, [{
prevout: {
value: new BigNumber(10.001),
script: utxoLockScript,
tokenId: 0x00
},
ellipticPair: pair
}])

return await rpcClient.rawtx.sendRawTransaction(new CTransactionSegWit(signed).toHex())
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function PlaygroundUTXO (): JSX.Element | null {
const [status, setStatus] = useState<string>('loading')

useEffect(() => {
api.playground.wallet().then(() => {
api.wallet.balances().then(() => {
fuxingloh marked this conversation as resolved.
Show resolved Hide resolved
setStatus('online')
}).catch(() => {
setStatus('error')
Expand Down
10 changes: 6 additions & 4 deletions app/screens/PlaygroundNavigator/sections/PlaygroundWallet.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { generateMnemonic } from '@defichain/jellyfish-wallet-mnemonic'
import { generateMnemonicWords } from '@defichain/jellyfish-wallet-mnemonic'
import * as Random from 'expo-random'
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { Mnemonic } from '../../../api/wallet/mnemonic'
import { Text, View } from '../../../components'
import { useNetworkContext } from '../../../contexts/NetworkContext'
import { useWalletManagementContext } from '../../../contexts/WalletManagementContext'
import { useWhaleApiClient } from '../../../contexts/WhaleContext'
import { fetchTokens } from '../../../hooks/wallet/TokensAPI'
Expand All @@ -14,6 +15,7 @@ import { PlaygroundStatus } from '../components/PlaygroundStatus'

export function PlaygroundWallet (): JSX.Element | null {
const { wallets, clearWallets, setWallet } = useWalletManagementContext()
const network = useNetworkContext()
const whaleApiClient = useWhaleApiClient()
const dispatch = useDispatch()
const address = useSelector((state: RootState) => state.wallet.address)
Expand All @@ -39,19 +41,19 @@ export function PlaygroundWallet (): JSX.Element | null {
<PlaygroundAction
testID='playground_wallet_abandon'
title='Setup wallet with abandon x23 + art as mnemonic seed'
onPress={async () => await setWallet(Mnemonic.createWalletDataAbandon23())}
onPress={async () => await setWallet(Mnemonic.createWalletDataAbandon23(network.network))}
/>

<PlaygroundAction
testID='playground_wallet_random'
title='Setup wallet with a randomly generated mnemonic seed'
onPress={async () => {
const words = generateMnemonic(24, numOfBytes => {
const words = generateMnemonicWords(24, (numOfBytes: number) => {
const bytes = Random.getRandomBytes(numOfBytes)
return Buffer.from(bytes)
})

await setWallet(Mnemonic.createWalletData(words))
await setWallet(Mnemonic.createWalletData(words, network.network))
}}
/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useState } from 'react'
import { KeyboardAvoidingView, ScrollView, TouchableOpacity } from 'react-native'
import { Mnemonic } from '../../../api/wallet/mnemonic'
import { Text, TextInput, View } from '../../../components'
import { useNetworkContext } from '../../../contexts/NetworkContext'
import { useWalletManagementContext } from '../../../contexts/WalletManagementContext'
import { tailwind } from '../../../tailwind'
import { WalletParamList } from '../WalletNavigator'
Expand All @@ -15,11 +16,12 @@ export function WalletMnemonicCreateVerify ({ route }: Props): JSX.Element {
const enteredWords: string[] = []

const [valid, setValid] = useState<boolean>(true)
const { network } = useNetworkContext()
const { setWallet } = useWalletManagementContext()

async function onVerify (): Promise<void> {
if (actualWords.join(' ') === enteredWords.join(' ')) {
await setWallet(Mnemonic.createWalletData(enteredWords))
await setWallet(Mnemonic.createWalletData(enteredWords, network))
} else {
setValid(false)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@ import { useState } from 'react'
import { KeyboardAvoidingView, ScrollView, TouchableOpacity } from 'react-native'
import { Mnemonic } from '../../../api/wallet/mnemonic'
import { Text, TextInput, View } from '../../../components'
import { useNetworkContext } from '../../../contexts/NetworkContext'
import { useWalletManagementContext } from '../../../contexts/WalletManagementContext'
import { tailwind } from '../../../tailwind'

export function WalletMnemonicRestore (): JSX.Element {
const { network } = useNetworkContext()
const { setWallet } = useWalletManagementContext()
const [phrase, setPhrase] = useState<string>('')
const [valid, setValid] = useState<boolean>(true)

async function onRestore (): Promise<void> {
const words = phrase.split(' ')
if (validateMnemonicSentence(words)) {
await setWallet(Mnemonic.createWalletData(words))
await setWallet(Mnemonic.createWalletData(words, network))
} else {
setValid(false)
}
Expand Down
Loading