🚪

仮想通貨Stacksに入門する ー 送金体験

2023/11/03に公開

Stacksという銘柄

Stacksは、スマートコントラクトと分散型アプリケーション(DApps)をBitcoin(BTC)に導入するために設計されたレイヤー1ブロックチェーンソリューションです。これらのスマートコントラクトは、セキュリティや安定性など、Bitcoinを強力にしている特徴を一切変えることなく、Bitcoinを支えています。

これらのDAppsは、オープンでモジュール化されているため、開発者はお互いのアプリの上に構築することができ、通常のアプリでは不可能な機能を作り出すことができます。StacksはベースレイヤーとしてBitcoinを使用しているため、ネットワーク上で起こるすべてのことは、最も広く使用されていて、最も安全なブロックチェーンーBitcoinで決済されます。

このプラットフォームには、Stacksトークン(STX)が使用されており、トークンはスマートコントラクトの実行、取引の処理、Stacks 2.0ブロックチェーンへの新しいデジタルアセットの登録に使用されています。

このプラットフォームは以前はBlockstackとして知られていましたが、エコシステムとオープンソースプロジェクトをBlockstack PBC(オリジナルプロトコルを構築した会社)から「切り離す」ために、2020年第4四半期にStacksにブランド名を変更しました

Stacks 2.0のメインネットは、2021年1月にローンチしました。

https://coinmarketcap.com/ja/currencies/stacks/

typescriptで送金体験をする

新しいStacks2.0アカウントを生成する

stacksのcliを使用することで、めっちゃ簡単にアカウントを作成することができます。

# stacksのcliをインストールする
npm install --global @stacks/cli

# 新しいアカウントを作成し、詳細を新しいファイルに保存
# '-t' オプションは、これをテストネットのアカウントとするもの
stx make_keychain -t > cli_keychain.json

cli_keychain.jsonの中身は以下のような感じになっている。新しい秘密鍵ごとに新しいアカウントが自動的に存在する。手動でStacks2.0ブロックチェーンのアカウントをインスタンス化する必要がなくなる。

cli_keychain.json
{
  // ニーモニック: アカウントにアクセスするために使用される24語のシード
  "mnemonic": "aaa bbb ccc ddd ...",
  "keyInfo": {
    // keyInfo.privateKey: アカウント秘密鍵で、トークン転送に必要ではsenderKeyと呼ばれることがある
    "privateKey": "5a3f1f15245bb3fb...",
    // keyInfo.address: アカウントのスタックアドレス
    "address": "STJRM2AMVF90ER6G3RW1QTF85E3HZH37006D5ER1",
    // keyInfo.btcAddress: 口座に対応するBTCアドレス
    "btcAddress": "biwSd6KTEvJcyX2R8oyfgj5REuLzczMYC1",
    // keyInfo.wif: btcAddressの秘密鍵を圧縮したもの
    "wif": "L4HXn7PLmzoNW...",
    // keyInfo.index: 0から始まるアカウントのナンス
    "index": 0
  }
}

秘密鍵・公開鍵・ウォレットアドレスの生成

ウォレットの生成1
import {
    makeRandomPrivKey,
    privateKeyToString,
    getAddressFromPrivateKey,
    TransactionVersion,
    getPublicKey,
    StacksPrivateKey,
    StacksPublicKey,
} from "@stacks/transactions";

// 秘密鍵の作成
const privateKey: StacksPrivateKey = makeRandomPrivKey();

// 秘密鍵から公開鍵の作成
const publicKey: StacksPublicKey = getPublicKey(privateKey);

const networkType: TransactionVersion = TransactionVersion.Testnet

// テストネットで秘密鍵からウォレットアドレスを作成
const stacksAddress: string = getAddressFromPrivateKey(
    privateKeyToString(privateKey),
    networkType     // メインネットで作成する場合は、ここは記述しなくて良い
);

console.log(stacksAddress);

プログラムではなく、パッケージでこれらの作成をすることもできる

ウォレットの生成2
$ npx -q stacks-gen sk --testnet

ファーセットを受け取る

上記で作成したアドレスに対して、ファーセットを受け取る。ニーモニックを入れてコネクションすれば、以下の画面で受け取ることができる。

https://explorer.hiro.so/sandbox/faucet?chain=testnet

この方法もあるが、curlで叩いて受け取ることもできる

ファーセットを受け取るためのコマンド
curl -L 'https://api.testnet.hiro.so/extended/v1/faucets/stx' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
  "address": "<自分のウォレットアドレスをここに>",
  "stacking": false
}'

トランザクションが成功すると以下のように表示される(このtxIdなどはそれっぽいですが、ただの乱数なので、存在しません。)

出力結果
{
  "success": true,
  "txId": "0x9ec281e9550213c79929b8b7d90dc32979d0562766f115f71591969458215d9d",
  "txRaw": "80800000000400c2963cf9cf3f60311c34087ce3cb55e51a2f20de92e3ecaf3c4ac277de6471217d23f019daa6234c6009a17ff33442aa9f3e80503020000000000051a1a01e0acdf4d4291b0de9ff9cdb579c895a635f9c7e518f5397d29bd8b3b45c8abdbc413000000000001368c00000000000000b4000033312aa92093bcd8c893b59f434adee97000000001dcd650046617563657400000000000000000000000000000000000000000000000000000000"
}

ウォレットアドレスで検索してみると以下のようになっています。受け取ることができましたね!

残高の確認

curlコマンドで残高を確認する

上記のようにexplorerを使用して確認することもできますが、curlコマンドを叩くことで、残高を確認することもできます。

残高取得するためのコマンド
$ curl 'https://stacks-node-api.testnet.stacks.co/extended/v1/address/<ウォレットアドレス>/balances'
出力結果
{
  "stx": {
    "balance": "500000000",
    "total_sent": "0",
    "total_received": "500000000",
    "total_fees_sent": "0",
    "total_miner_rewards_received": "0",
    "lock_tx_id": "",
    "locked": "0",
    "lock_height": 0,
    "burnchain_lock_height": 0,
    "burnchain_unlock_height": 0
  },
  "fungible_tokens": {},
  "non_fungible_tokens": {}
}%

"balance": "500000000",とあるように、残高が確認できます。

APIを使って残高を確認する

axiosを使用して、以下のようにAPIを叩いて、残高を取得することができます。

残高取得
import axios from 'axios';

// testnetのAPI(メインネットの場合は、testnetをmainnetにしてください。)
const API_URL = 'https://stacks-node-api.testnet.stacks.co';
const walletAddress = '<自分のウォレットアドレスをここに。>'; 

// レスポンスがないとエラーになってしまうので、同期処理にする
async function getStacksWalletBalance(address: string): Promise<number> {
    try {
        const response: AxiosResponse = await axios.get(`${API_URL}/extended/v1/address/${address}/balances`);
        const balanceMicrostacks: number = response.data.stx.balance; 
        // 過分性が6桁なので、補完する
        const balanceSTX: number = balanceMicrostacks / 1_000_000;
        return balanceSTX;

    } catch (error) {
        console.error('Error getting wallet balance:', error);
        throw error;
    }
}

// 残高を表示する
getStacksWalletBalance(walletAddress)
    .then(balance => console.log(`ウォレットの残高: ${balance}STX`))
    .catch(error => console.error(error));
出力結果
ウォレットの残高: 500STX

トランザクションの作成とブロードキャスト

Stacksのようなブロックチェーン上でトランザクションを作成し、ブロードキャストするには、通常いくつかのステップが必要となります!

  1. トランザクションの作成
    送信者のアドレス、受信者のアドレス、送信金額、スマートコントラクトに必要な追加データ(該当する場合があれば...)など、必要な情報をすべて含むトランザクションオブジェクトを作成する必要があります。

  2. トランザクションへの署名
    取引は送信者の秘密鍵で署名されないといけません。このプロセスはトランザクションを保護し、それが本当に送信者によって作成されたものであることを検証します。

  3. トランザクションのブロードキャスト
    署名されると、トランザクションはネットワークにブロードキャストされ、そこでマイナーによって次のブロックに含まれるように伝えられます。

実装

import { StacksTestnet } from '@stacks/network';
import {
    makeSTXTokenTransfer,
    broadcastTransaction,
    AnchorMode,
} from '@stacks/transactions';
import { BigNumber } from 'bignumber.js';

const API_URL: string = 'https://stacks-node-api.testnet.stacks.co';
const senderKey: string = '<送信者の秘密鍵をここに。>';
const recipient: string = '<受信者のウォレットアドレスをここに>';
const amountToSend: BigNumber = new BigNumber(1000);   // 送金額をここに。ただし、1 STX = 1,000,000 microstacksなので、マイクロスタックの単位で考える。
const network = new StacksTestnet();


async function createAndBroadcastTransaction() {
    try {
    // 1. トランザクションの作成
    const txOptions = {
        recipient,
        amount: amountToSend.toString(),
        senderKey,
        network,
        anchorMode: AnchorMode.Any,
        memo: 'Test transaction',
    };

    // 2. トランザクションへの署名
    // トランザクションは送信者の公開鍵と共に作成された時に自動で署名されるようになっている。
    const transaction = await makeSTXTokenTransfer(txOptions);

    // 3. トランザクションのブロードキャスト
    const broadcastResponse = await broadcastTransaction(transaction, network);

    console.log('ブロードキャストトランザクションのレスポンス:', broadcastResponse);
    if (broadcastResponse.hasOwnProperty('error')) {
        throw new Error(`エラー!!: ${broadcastResponse.error}`);
    }
    
    return broadcastResponse;
    } catch (error) {
    console.error('失敗!!!!:', error);
    throw error;
    }
}

// Use the function
createAndBroadcastTransaction()
    .then(response => console.log('ブロードキャスト成功!!!!:', response))
    .catch(error => console.error('エラー!!:', error));


実行結果
ブロードキャストトランザクションのレスポンス: {
  txid: '6f419be2c1d354f6edd5e83a0a945263c9eb4d3217ff559c9f473a9940f3f0ac'
}
ブロードキャスト成功!!!!: {
  txid: '6f419be2c1d354f6edd5e83a0a945263c9eb4d3217ff559c9f473a9940f3f0ac'
}

Explorerから確認する

txid: transaction idを検索してみると結果が以下のように出てきます。
ちゃんと送金されていますね!!

残高確認してみる

前述した残高確認で残高を確認してみましょう。

残高取得
ウォレット残高: 499.984135STX

参考資料

Stacks Docs | Stacks Docs

https://docs.stacks.co/docs/intro

Stacks Explorer by Hiro [Testnet mode]

https://explorer.hiro.so/?chain=testnet

hirosystems/stacks.js: JavaScript libraries for identity, auth, storage and transactions on the Stacks blockchain.

https://github.com/hirosystems/stacks.js

Stacks API

https://www.hiro.so/stacks-api

stacks-archive/stacks-transactions-js: The JavaScript library for generating Stacks 2.0 transactions

https://github.com/stacks-archive/stacks-transactions-js

Discussion