본문 바로가기
blockchain

[BitcoinWallet] wallet에서 send Bitcoin 기능 구현하기

by clolee 2022. 11. 2.

sendBitcoin 함수 구현하기

1. 지갑 생성

 

2. 생성한 주소에 비트코인 전송

 

3. sendBTC.js > sendBitcoin 함수 구현

 

3-1) API 사용해 Get Unspent Transactions 

참고 : https://sochain.com/api/#get-network-confidence

 

https://chain.so/api/v2/get_tx_unspent/${sochain_network}/${sourceAddress}

ex) https://chain.so/api/v2/get_tx_unspent/BTCTEST/mngfToQdYLdetGDoZDHmYCzECitg9yxbPt     

 

sochain_network : BTCTEST (bitcoin testnet)

sourceAddress : wallet address.  

 

결과 :

하나의 트랜잭션 반환.

txid : 트랜잭션 id

 

여러 개의 트랜잭션이 생성될 경우 txs 배열에 트랜잭션이 추가됨.

 

3-2) Get Unspent Transactions 코드

 

sourceAddress : 우리가 비트코인을 보내기 위해 비트코인 자금을 가지고 있는 주소.

response : sourceAddress 통해 전달된 트랜잭션들을 확인

sochain_network : BTC testnet

    const sochain_network = "BTCTEST";
    const privateKey =
      "5d264784f587621ffae7013405b2ffb862b9115c0839dc1844263243c800538d";
    const sourceAddress = "mngfToQdYLdetGDoZDHmYCzECitg9yxbPt";
    
    const response = await axios.get(
      `https://chain.so/api/v2/get_tx_unspent/${sochain_network}/${sourceAddress}`
    );

 

3-3) transaction fee

 

참고 :

https://bitcoinfees.earn.com/

 

각기 다른 Fee와 Time을 갖고 있음

 

API 통해 사람들이 사용하는 수수료에 대한 요약/평균 정보 얻을 수 있음

https://bitcoinfees.earn.com/api/v1/fees/recommended

    const recommendedFee = await axios.get(
      "https://bitcoinfees.earn.com/api/v1/fees/recommended"
    );

 

3-4) 여러 개의 트랜잭션에 대해 input 추가하기

 

let inputCount = 0

- 초기값. 이후 주소에 해당하는 모든 input 트랜잭션 수를 계산해 업데이트.

 

let outputCount = 2

- output은 항상 최소 기본 2개가 됨. 수신자의 주소로 보내는 출력 하나, 나에게 변경사항을 반영한 잔액이 돌아오는 출력 하나.

    let inputCount = 0;
    let outputCount = 2;

 

bitcore.Transaction 사용하기

    const transaction = new bitcore.Transaction();

 

input 초기화

모든 unspent 트랜잭션 가져와 input으로 추가, 사용가능코인량 totalAmountAvailable 업데이트

 

response :

    let inputs = [];
    let utxos = response.data.data.txs;

    for (const element of utxos) {
      let utxo = {};
      utxo.satoshis = Math.floor(Number(element.value) * 100000000);
      utxo.script = element.script_hex;
      utxo.address = response.data.data.address;
      utxo.txId = element.txid;
      utxo.outputIndex = element.output_no;
      totalAmountAvailable += utxo.satoshis;
      inputCount += 1;
      inputs.push(utxo);
    }

 

 

3-5) 트랜잭션에 보내기

fee 의 경우 테스트 시 수수료를 적게 내고 싶으면 나눠서 크기를 줄임. 다른 이유는 없음.

Balance check. 

    const transactionSize =
      inputCount * 180 + outputCount * 34 + 10 - inputCount;

    fee = (transactionSize * recommededFee.data.hourFee) / 3;

    // Balance check
    if (totalAmountAvailable - satoshiToSend - fee < 0) {
      throw new Error("Balance is too low for this transaction");
    }

받는 주소, 보낼 금액 전달,

보낼 금액 전송 후 남은 금액 보낼 주소 (내주소 / 혹은 다른 주소로 보내도 됨),

개인키 전달해 서명... send Transaction 생성

    // Set transaction input
    transaction.from(inputs);

    // Set recieverAddress and amountToSend
    transaction.to(recieverAddress, satoshiToSend);

    // Update sender Address (receive the left over funds after transfer)
    transaction.change(sourceAddress);
    console.log(satoshiToSend, fee);

    // manually set transaction fees - 20 satoshis per byte
    transaction.fee(Math.round(fee));

    // Sign transaction with sender privateKey
    transaction.sign(privateKey);

 

트랜잭션 serialize,

send Transaction

POST /api/v2/send_tx/{NETWORK}

    // serialize transactions
    console.log(transaction.serialize());
    const serializedTransaction = transaction.serialize();

    // Send transaction
    const result = await axios({
      method: "POST",
      url: `https://chain.so/api/v2/send_tx/${sochain_network}`,
      data: {
        tx_hex: serializedTransaction,
      },
    });

 

성공 시 트랜잭션 반환, 실패 시 예외처리

  try {
	...
    
	return result.data.data;
  } catch (error) {
    return error;
  }

 

 

전체코드

src > api > sendBTC.js

const axios = require("axios");
const bitcore = require("bitcore-lib");

const sendBitcoin = async (recieverAddress, amountToSend) => {
  try {
    const sochain_network = "BTCTEST";
    const privateKey =
      "5d264784f587621ffae7013405b2ffb862b9115c0839dc1844263243c800538d";
    const sourceAddress = "mngfToQdYLdetGDoZDHmYCzECitg9yxbPt";
    const satoshiToSend = amountToSend * 100000000;
    let fee = 0;
    let inputCount = 0;
    let outputCount = 2;

    const response = await axios.get(
      `https://chain.so/api/v2/get_tx_unspent/${sochain_network}/${sourceAddress}`
    );

    const recommededFee = await axios.get(
      "https://bitcoinfees.earn.com/api/v1/fees/recommended"
    );

    const transaction = new bitcore.Transaction();
    let totalAmountAvailable = 0;

    let inputs = [];
    let utxos = response.data.data.txs;

    for (const element of utxos) {
      let utxo = {};
      utxo.satoshis = Math.floor(Number(element.value) * 100000000);
      utxo.script = element.script_hex;
      utxo.address = response.data.data.address;
      utxo.txId = element.txid;
      utxo.outputIndex = element.output_no;
      totalAmountAvailable += utxo.satoshis;
      inputCount += 1;
      inputs.push(utxo);
    }

    const transactionSize =
      inputCount * 180 + outputCount * 34 + 10 - inputCount;

    fee = (transactionSize * recommededFee.data.hourFee) / 3;

    // Balance check
    if (totalAmountAvailable - satoshiToSend - fee < 0) {
      throw new Error("Balance is too low for this transaction");
    }

    // Set transaction input
    transaction.from(inputs);

    // Set recieverAddress and amountToSend
    transaction.to(recieverAddress, satoshiToSend);

    // Update sender Address (receive the left over funds after transfer)
    transaction.change(sourceAddress);
    console.log(satoshiToSend, fee);

    // manually set transaction fees - 20 satoshis per byte
    transaction.fee(Math.round(fee));

    // Sign transaction with sender privateKey
    transaction.sign(privateKey);

    // serialize transactions
    console.log(transaction.serialize());
    const serializedTransaction = transaction.serialize();

    // Send transaction
    const result = await axios({
      method: "POST",
      url: `https://chain.so/api/v2/send_tx/${sochain_network}`,
      data: {
        tx_hex: serializedTransaction,
      },
    });

    return result.data.data;
  } catch (error) {
    return error;
  }
};

module.exports = {
  sendBitcoin: sendBitcoin,
};

메인넷 사용 ==> sochain_network = "BTC"

 

 

4. sendTransaction 실행해보기

src > app.js

const { sendBitcoin } = require("./api/sendBTC");

sendBitcoin("mhe5hkmx26CZkbMPprQQifLLwqfb5pZUKZ", 0.0001)
  .then((result) => {
    console.log(result);
  })
  .catch((error) => {
    console.log(error);
  });

 

테스트넷에서 확인하기

https://www.blockchain.com/btc-testnet/address/mhe5hkmx26CZkbMPprQQifLLwqfb5pZUKZ

 

mhe5hkmx26CZkbMPprQQifLLwqfb5pZUKZ 주소로 0.0001BTC 전송

수수료 제외한 나머지 금액은 내 주소로 전송

 

 

transaction hash 확인

https://www.blockchain.com/btc-testnet/tx/8c1d0041fca67bcbff5ea101f634e612bc71ffd494aaf255e44b0c0d4954e1d4

 

 

 

 

unspent transaction 에서 변화된 value확인

https://chain.so/api/v2/get_tx_unspent/BTCTEST/mngfToQdYLdetGDoZDHmYCzECitg9yxbPt

value 변화. 0.0005 -> 0.00032461

 

 

spent transaction 확인

https://chain.so/api/v2/get_tx_spent/BTCTEST/mngfToQdYLdetGDoZDHmYCzECitg9yxbPt

 

 

***

serialized transaction 을 push해서 트랜잭션 생성할 수 있음

https://live.blockcypher.com/btc/pushtx/

 

serialized transaction :

0200000001d4e154490d0c4be455f2aa94d4ff71bc12e634f601a15effcb7ba6fc41001d8c010000006a473044022061be827f864e6d10278fbf922c75cd5d2f6c7df026856e0dab420425f6d799b902201b9b5a7784b19ab8312bcd10bccf3803b6ad1818bb70260e39ae132141f3cb9c012102352cdb60484be3cf898dbebebd1a8641a3818d50822e6a66fa82ba5808032a72ffffffff0210270000000000001976a91417495904a810c3b4e42381dedb777ad14074b25b88ac4a3a0000000000001976a9144e9efd4fe5e4e7380982af853b886a69a57a01b088ac00000000

 

 

 

참고 :

https://youtu.be/ObRnmvIdecI

https://sochain.com/api/#get-network-confidence

https://bitcoinfaucet.uo1.net/

https://www.blockchain.com/explorer/assets/btc-testnet

https://live.blockcypher.com/

 

댓글