Como faço para criar dapps multi-chain para usuários da KeepKey e promover meu produto ou serviço?
Consulte nosso guia do desenvolvedor para criar dApps de várias redes:
Neste tutorial, criaremos um aplicativo que envia e recebe a criptomoeda DASH. Este exercício demonstrará com que rapidez e facilidade aplicativos multi-chain podem ser desenvolvidos na KeepKey usando o KeepKey Desktop Application.
Direto para o código: https://github.com/BitHighlander/dash-dapp
Tecnologias usadas:
- Dapp-template: Veja um tutorial sobre como este modelo foi construído aqui.
- GitHub: GitHub é um sistema de controle de versão de código aberto para rastrear alterações no código-fonte. Ele é usado por milhões de desenvolvedores e empresas para armazenar e compartilhar seu código, gerenciar projetos e colaborar com outros desenvolvedores.
- Vercel.io: Vercel é uma plataforma de nuvem para hospedar aplicativos front-end. Ele fornece uma plataforma simples e intuitiva para implantar aplicativos React de forma rápida e fácil.
- KeepKey SDK: KeepKey SDK é um kit de desenvolvimento de software de código aberto para construir com a carteira de hardware KeepKey.
- Pioneer API/SDK: O Pioneer API/SDK é um kit de ferramentas para criar aplicativos e serviços de blockchain. Ele fornece ferramentas para os desenvolvedores criarem aplicativos blockchain de maneira rápida e fácil, como carteiras, trocas e dapps.
Resumo do que este guia cobre
- Criar um aplicativo React da Web básico
- Usar o KeepKey SDK para integrar com o KeepKey Desktop
- Usar o Pioneer-SDK para consultar a blockchain em busca de informações DASH UTXO
- Criar uma carteira básica com funcionalidade de envio e recebimento
- Publicar este aplicativo com Vercel.io e levá-lo para produção
- Envie um aplicativo de produção para a loja KeepKey Dapp via pioneer
Modelo KeepKey Dapp
Para usar este tutorial, começamos com o exemplo de repositório modelo.
git clone https://github.com/BitHighlander/dapp-template dash-dapp
- Clone o projeto:
git clone https://github.com/BitHighlander/dapp-template dash-dapp
- Remova o modelo de repositório do upstream:
git remote rm origin
- Envie para o nosso próprio github:
Se você estiver usando um IDE como webstorm ou VScode, simplesmente abra o novo diretório dash-dapp em seu IDE aqui.
Agora envie como um novo projeto para o github.
Webstorm:
Enviar nova ramificação como mestre
Comandos do git:
git remote add origin <our_repo_url>
git push origin master
Vamos começar construindo
Agora construa
yarn && yarn dev
Execute este aplicativo e conecte sua keepkey. Configure uma carteira, se ainda não o fez.
KeepKey sdk
Agora precisamos que o aplicativo carregue os saldos DASH dos usuários na inicialização. Para fazer isso, importaremos o KeepKey SDK. Usaremos a chamada de API 'getPubkeys' para gerar um Xpub para DASH.
Usamos o SDK para obter o publicKey para Dash
let path =
{
symbol: 'DASH',
address_n: [0x80000000 + 44, 0x80000000 + 5, 0x80000000 + 0],
coin: 'Bitcoin',
script_type: 'p2pkh',
showDisplay: false
}
let responsePubkey = await sdk.system.info.getPublicKey(path)
console.log("responsePubkey: ", responsePubkey)
console.log("responsePubkey: ", responsePubkey.xpub)
Observe que o path para DASH é 5, conforme encontrado em https://github.com/satoshilabs/slips/blob/master/slip-0044.md
E, finalmente, retornamos o xpub para dash.
(NOTA) Você pode verificar “checkpoint-1” para continuar o tutorial a partir deste ponto
Trabalho de UI, vamos preparar o aplicativo para saldo e endereço
estamos usando useState para definir valores de dentro de nossa função onStart para elementos na UI.
const [address, setAddress] = useState('')
const [balance, setBalance] = useState('0.000')
Em seguida, vamos calcular o saldo para DASH
Agora que as coisas estão divertidas, precisamos descobrir quanto DASH um usuário tem de saldo. Para fazer isso, vamos importar o pioneer-sdk.
yarn add @pioneer-platform/pioneer-client
primeiro importamos o Pioneer-sdk, e configuramos
const configPioneer = {
queryKey:'sdk:test-tutorial-medium',
username:"dash-dapp",
spec:"https://pioneers.dev/spec/swagger.json"
}
let pioneer = new Client(configPioneer.spec,configPioneer)
pioneer = await pioneer.init()
Estamos definindo a queryKey, nome de usuário e especificação
Os documentos da Pioneer API podem ser encontrados aqui
Em seguida, usamos o cliente API para consultar as entradas não gastas de um xpub.
Um xpub é uma chave pública usada em um sistema de carteira determinístico hierárquico, e mais informações podem ser encontradas nesta postagem do blog: https://www.swanbitcoin.com/whats-in-an-xpub/
//get balance DASH
let data = await pioneer.ListUnspent({network:'DASH',xpub:responsePubkey.xpub})
data = data.data
console.log("txData: ",data)
E, finalmente, iteramos sobre essas entradas para calcular um saldo
let balance = 0
for(let i = 0; i < data.length; i++){
balance += parseInt(data[i].value)
}
console.log("balance: ",balance)
let balanceNative = balance
setBalance(balanceNative) let balance = 0
for(let i = 0; i < data.length; i++){
balance += parseInt(data[i].value)
}
console.log("balance: ",balance)
let balanceNative = balance
setBalance(balanceNative)
(NOTA) Você pode verificar “checkpoint-2” para continuar o tutorial a partir deste ponto
Agora vamos ver tudo funcionando!
Vamos ativar nosso site
yarn start
E abra o KeepKey Desktop
E vemos nosso total!
Agora vamos gerar um novo endereço para o usuário depositar DASH.
//get new address
let newAddyIndex = await pioneer.GetChangeAddress({network:'DASH',xpub:responsePubkey.xpub})
newAddyIndex = newAddyIndex.data.changeIndex
newAddyIndex = newAddyIndex + 1
console.log("newAddyIndex: ",newAddyIndex)
A Pioneer usará o Xpub para escanear a blockchain e detectar quaisquer transações anteriores associadas à carteira. Em seguida, ele criará uma lista de todos os endereços usados e não usados associados à carteira e determinará o próximo índice de endereço não usado.
Os caminhos BIP44 são caminhos determinísticos hierárquicos usados para gerar chaves públicas e privadas para carteiras de criptomoedas. Esses caminhos são construídos usando uma estrutura de árvore de 4 níveis, composta pelos seguintes níveis: propósito (m), tipo de moeda (44'/0'/0'/0), conta (0) e troco (0). Os caminhos BIP44 são usados para garantir que a mesma carteira possa ser usada para várias moedas e contas, mantendo os fundos seguros e organizados.
e envie-o de volta para a API do KeepKey-desktop para gerar o endereço
let addressInfo = {
addressNList: [0x80000000 + 44, 0x80000000 + 5, 0x80000000 + 0, 0x80000000 + newAddyIndex],
coin: 'Dash',
scriptType: 'p2pkh',
showDisplay: false
}
let address = await sdk.address.uTXOGetAddress({
address_n: addressInfo.addressNList,
script_type:addressInfo.scriptType,
coin:addressInfo.coin
})
O 0x80000000 é uma notação hexadecimal que indica o início do caminho codificado para a rede Dash. Este caminho é usado para diferenciá-lo de outras redes ao enviar ou receber fundos. O 5 indica que este é um endereço de rede Dash. Isso é usado para identificar a moeda que está sendo usada e garantir que os fundos sejam enviados para a rede correta.
E agora temos um endereço fresquinho para o usuário depositar também!
Leia mais sobre por que não reutilizar endereços é importante aqui: https://en.bitcoin.it/wiki/Address_reuse
E agora temos um aplicativo para o qual um usuário pode enviar um Dash!
(NOTA) Você pode verificar “checkpoint-7” para continuar o tutorial a partir deste ponto.
Agora vamos enviar DASH!
Para enviar, primeiro vamos adicionar um modal de envio.
https://chakra-ui.com/docs/components/modal
https://github.com/BitHighlander/dash-dapp/commit/2b09208d16816284e68a238713e3eca9dcc375b9
Agora vamos adicionar campos para
Valor: quantidade de Dash que temos para enviar.
Endereço: o endereço Dash para o qual desejamos enviar.
Adicionamos esses valores ao nosso estado de reação
const [amount, setAmount] = useState('0.00000000')
const [toAddress, setToAddress] = useState('')
E adicionamos o seguinte em nosso próprio modal
<div>
amount: <input type="text" name="amount" value={amount}/>
</div>
<br/>
<div>
address: <input type="text" name="address" value={toAddress} placeholder="XwNbd46qdmbVWLdXievBhBMW7JYy8WiE7n"/>
</div>
E depois de recebermos os valores dos campos de entrada, chamamos uma função onSend()
<Button colorScheme='green' mr={3} onClick={onSend}>
Send
</Button>
e simulamos este onSend com apenas um log por enquanto
let onSend = async function(){
try{
//
console.log("Sending Dash!")
}catch(e){
console.error(e)
}
}
- Ok, não vamos realmente enviar algum DASH!
Para enviar o Dash, precisamos criar uma transação. Geralmente, essa é a parte mais complicada de trabalhar com cripto. É importante entender os perigos aqui. Ao trabalhar com uma transação UXTO estamos calculando uma taxa. Esta taxa é geralmente representada por
sum(inputs) - sum(outputs) = fee
Se você calcular mal uma taxa, por exemplo, esquecendo ou descartando a saída do endereço de alteração de uma transação, essa taxa vai para os mineradores DASH e é essencialmente perdida.
Nosso modal de envio terá 3 etapas:
- Gerar transação
- Assinar transação
- Transmitir transação
Então, primeiro precisamos das entradas que foram consultadas para o saldo. então vamos adicionar um parâmetro ao estado de reação que são nossas entradas.
const [inputs, setInputs] = useState([])
e configurá-los na inicialização
let data = await pioneer.ListUnspent({network:'DASH',xpub:responsePubkey.xpub})
data = data.data
setInputs(data)
Agora vamos criar nosso objeto de saída
//balance check
let amountOut = parseInt(amount * 100000000)
if(amountOut > balance) throw new Error("Insufficient funds!")
//prepare coinselect
let utxos = []
for(let i = 0; i < inputs.length; i++){
let input = inputs[i]
let utxo = {
txId:input.txid,
vout:input.vout,
value:parseInt(input.value),
nonWitnessUtxo: Buffer.from(input.hex, 'hex'),
hex: input.hex,
tx: input.tx,
path:input.path
}
utxos.push(utxo)
}
if (utxos.length === 0) throw Error("101 YOUR BROKE! no UTXO's found! ")
let outputs = [
{
address:toAddress,
value: amountOut
}
]
Aqui estamos preparando o formato UXTO para corresponder aos parâmetros necessários para coinselect. Um pacote npm que usamos para seleção de UXTO
https://www.npmjs.com/package/coinselect
A seleção de UTXO pode ser muito dinâmica. Ao usar algoritmos específicos, podemos minimizar o tamanho da transação e, portanto, o custo das taxas de transação. Neste exemplo, usaremos o Blackjack, o algoritmo padrão fornecido pela Coinselect.
No entanto, antes de fazer isso, precisamos coletar dados de taxas da Pioneer API. Precisamos saber o valor da taxa recomendada para adicionar à transação.
Agora, enviamos as entradas, saídas e taxa para Coinselect. Isso gerará a seleção ideal de UTXOs para nossa transação.
const coinSelect = require('coinselect')
...
let selectedResults = coinSelect(utxos, targets, feeRateInfo)
console.log("result coinselect algo: ",selectedResults)
O resultado que obtemos do coinselect é o seguinte
{
"inputs": [
{
"txId": "7bf822b0d24094b9eb95bc536fc83e2ea2250d76b3ee485efa173ba91a607eaf",
"vout": 0,
"value": 138791400,
"nonWitnessUtxo": {
"type": "Buffer",
"data": [
.... (omitted)
]
},
"hex": "0200000001bd45d819befe8eb4f63c7cab40f54310a88da6d6a434f6856085bb0a318f6315000000006b483045022100fffe72251016e22a9d62cb5e81880014471a8670df9972428e9384116c6f8ff102207ad2846253e6e107731d5a062998fff0b142a905552dbe37b59ede3ec2db65000121020e4325904d8f1473cc2a5841d53bfe0a2e7d01fdbd947adb76d99cb58bf6cec0fdffffff02e8c94508000000001976a914e2e74abdea3612eeb9def06e9c54bbb62b74daf688acff944e01000000001976a914ef8b1dac4586c7d94eb208f5a91365946ff72b3188ac00000000",
"tx": {
"txid": "7bf822b0d24094b9eb95bc536fc83e2ea2250d76b3ee485efa173ba91a607eaf",
"hash": "7bf822b0d24094b9eb95bc536fc83e2ea2250d76b3ee485efa173ba91a607eaf",
"version": 2,
"vin": [
{
"txid": "15638f310abb856085f634a4d6a68da81043f540ab7c3cf6b48efebe19d845bd",
"addr": "XdsAkkhiH66eCJx445hQ1s4Fdi5eNkwfd4",
"scriptSig": {
"hex": "0014459a4d8600bfdaa52708eaae5be1dcf959069efc"
},
"valueSat": 160723567,
"value": 1.60723567
}
],
"vout": [
{
"value": "138791400",
"scriptPubKey": {
"hex": "76a914e2e74abdea3612eeb9def06e9c54bbb62b74daf688ac"
}
},
{
"value": "21927167",
"scriptPubKey": {
"hex": "76a914ef8b1dac4586c7d94eb208f5a91365946ff72b3188ac"
}
}
],
"hex": "0200000001bd45d819befe8eb4f63c7cab40f54310a88da6d6a434f6856085bb0a318f6315000000006b483045022100fffe72251016e22a9d62cb5e81880014471a8670df9972428e9384116c6f8ff102207ad2846253e6e107731d5a062998fff0b142a905552dbe37b59ede3ec2db65000121020e4325904d8f1473cc2a5841d53bfe0a2e7d01fdbd947adb76d99cb58bf6cec0fdffffff02e8c94508000000001976a914e2e74abdea3612eeb9def06e9c54bbb62b74daf688acff944e01000000001976a914ef8b1dac4586c7d94eb208f5a91365946ff72b3188ac00000000"
},
"path": "m/44'/5'/0'/0/0"
}
],
"outputs": [
{
"address": "XwNbd46qdmbVWLdXievBhBMW7JYy8WiE7n",
"value": 100000000
},
{
"value": 38789140
}
],
"fee": 2260
}
Agora precisamos formatar esta transação para que a Keepkey assine.
for(let i = 0; i < selectedResults.inputs.length; i++){
//get input info
let inputInfo = selectedResults.inputs[i]
console.log("inputInfo: ",inputInfo)
let input = {
addressNList:bip32ToAddressNList(inputInfo.path),
scriptType:core.BTCInputScriptType.SpendWitness,
amount:String(inputInfo.value),
vout:inputInfo.vout,
txid:inputInfo.txId,
segwit:false,
hex:inputInfo.hex,
tx:inputInfo.tx
}
inputs.push(input)
}
let {
bip32ToAddressNList
} = require('@pioneer-platform/pioneer-coins')
Usamos algumas bibliotecas auxiliares e formatos fornecidos por HDwallet e Pioneer.
Agora precisamos calcular um endereço de mudança.
Primeiro, obtemos xpub na conta 1 para alteração.
//get xpub for change addresses
let path =
{
symbol: 'DASH',
address_n: [0x80000000 + 44, 0x80000000 + 5, 0x80000000 + 1],
coin: 'Bitcoin',
script_type: 'p2pkh',
showDisplay: false
}
let responsePubkeyChange = await sdk.system.info.getPublicKey(path)
console.log("responsePubkeyChange: ", responsePubkeyChange)
console.log("responsePubkeyChange: ", responsePubkeyChange.xpub)
E então pedimos ao pioneer o próximo índice de mudança disponível.
//get change address
let changeAddyIndex = await pioneer.GetChangeAddress({network:'DASH',xpub:responsePubkeyChange.xpub})
changeAddyIndex = changeAddyIndex.data.changeIndex
changeAddyIndex = changeAddyIndex + 1
console.log("changeAddyIndex: ",changeAddyIndex)
e, finalmente, geramos o endereço de alteração por meio do keepkey-sdk.
let addressInfo = {
addressNList: [0x80000000 + 44, 0x80000000 + 5, 0x80000000 + 1, 0x80000000 + changeAddyIndex],
coin: 'Dash',
scriptType: 'p2pkh',
showDisplay: false
}
let address = await sdk.address.uTXOGetAddress({
address_n: addressInfo.addressNList,
script_type:addressInfo.scriptType,
coin:addressInfo.coin
})
console.log("address: ",address)
Agora que temos o endereço de mudança, reunimos os resultados finais.
const outputsFinal = []
console.log("selectedResults.outputs: ",selectedResults.outputs)
console.log("outputsFinal: ",outputsFinal)
for(let i = 0; i < selectedResults.outputs.length; i++){
let outputInfo = selectedResults.outputs[i]
console.log("outputInfo: ",outputInfo)
if(outputInfo.address){
//not change
let output = {
address:toAddress,
addressType:"spend",
scriptType:core.BTCInputScriptType.SpendWitness,
amount:String(outputInfo.value),
isChange: false,
}
if(output.address)outputsFinal.push(output)
} else {
//change
let output = {
address:changeAddress,
addressType:"spend",
scriptType:core.BTCInputScriptType.SpendWitness,
amount:String(outputInfo.value),
isChange: true,
}
if(output.address)outputsFinal.push(output)
}
console.log(i,"outputsFinal: ",outputsFinal)
}
e depois assinamos
let hdwalletTxDescription = {
coin: 'Dash',
inputs:inputsSelected,
outputs:outputsFinal,
version: 1,
locktime: 0,
}
//signTx
let signedTxTransfer = await sdk.bitcoin.btcSignTransaction(hdwalletTxDescription)
signedTxTransfer = JSON.parse(signedTxTransfer)
setSignedTx(signedTxTransfer.serializedTx)
Agora precisamos construir a UI para transmissão.
<ModalFooter>
{!signedTx ? <div>
<Button colorScheme='green' mr={3} onClick={onSend}>
Send
</Button>
</div> : </div>}
{!txid ? <div>
{signedTx ? <div>
<Button colorScheme='green' mr={3} onClick={onBroadcast}>
broadcast
</Button>
</div> : </div>}
</div> : </div>}
<Button colorScheme='blue' mr={3} onClick={onClose}>
exit
</Button>
</ModalFooter>
Lógico, se houver um erro exibe um erro
Lógico, se não houver um Tx assinado, mostre o botão assinar
Se houver um txid, não mostre o botão de transmissão
Etapas de uma transação:
não assinado -> assinado -> transmitido -> confirmado
Usamos Pioneer API para transmissão
//broadcast TX
let broadcastBodyTransfer = {
network:"DASH",
serialized:signedTx,
txid:"unknown",
invocationId:"unknown"
}
let resultBroadcastTransfer = await pioneer.Broadcast(null,broadcastBodyTransfer)
resultBroadcastTransfer = resultBroadcastTransfer.data
console.log("resultBroadcastTransfer: ",resultBroadcastTransfer)
if(resultBroadcastTransfer.error){
setError(resultBroadcastTransfer.error)
}
if(resultBroadcastTransfer.txid){
setTxid(resultBroadcastTransfer.txid)
}
agora exibimos o txid para o usuário
com um link para um explorador de blocos
{txid ? <div>txid: <a href={'https://chainz.cryptoid.info/dash/tx.dws?'+txid+'.htm'}/> {txid}</div> : <div></div>}
Agora temos um Dapp completo!
Agora vamos implantar o aplicativo para produção com Vercel
Inscreva-se no Vercel
Depois de vincular sua conta do github ao vercel, podemos implantar o projeto
O Vercel detectará o formato de compilação do aplicativo conforme usamos create-react-add, e todas as configurações necessárias saem da caixa.
Após a implantação bem-sucedida, você pode coletar a URL do seu
https://dash-dapp.vercel.app/
Depois que obtivermos um URL para nosso dapp, vamos para o pioneer e enviar.
Inicie o Pioneers Dapp na Dapp Store
E navegamos para “Explore Dapps”.
Agora nos conectamos ao Dapp via Wallet-Connect
E enviamos o Dapp para a Dapp Store
Usei um link que encontrei do coincap.io para o ícone do aplicativo.
Em seguida, assinamos o envio para registrar nosso endereço KeepKey como desenvolvedor/proprietário
(Todos os Dapps enviados devem ser revisados antes de serem aprovados. Entre em nosso discord se quiser acelerar a aprovação)
Após a aprovação, seu aplicativo aparecerá qualificado para votação
Agora vamos usar o fox para votar em nosso dapp
e agora os usuários da plataforma Desktop podem usar seu App!
Você não concluiu um Dapp inteiro do zero, implantou-o em produção com o Vercel.io e enviou e obteve aprovação para entrada na loja KeepKeys Dapp! Tudo o que resta é promover seu aplicativo e pedir que mais Fox holders votem!
Os principais desenvolvedores dapp são elegíveis para futuras recompensas e airdrops de FOX.