[Symbol] Pythonでxymを送金する


概要

  • 秘密鍵を有するウォレットから任意のアドレスへモザイク(xym)の送金プログラムです
    • 注意 : 秘密鍵は絶対に公開しないでください
  • 全コードはGithubにて公開中
    • 本記事は「04_mosaicTransfer」
    • バグや次回の要望あればコメントください
  • 投げXYMは「NDLS6GYOIPHATATNAVVOUNJXBD6X4BXU6IRBHIY」まで
  • ノード運営しています。あなたの委任お待ちしております。
    • symbol-node.takagi-tech.com
    • ハーベスト報酬は技術検証や記事執筆、ノード維持費に使わせていただきます。

前回記事

事前準備

APIエンドポイント

公式ドキュメントはこちら。v1.0.0で動作確認しています。

Symbol SDK for Python

注意:Python3.7以上

pip install symbol-sdk-core-python

秘密鍵とウォレットの生成

(秘密鍵が既知であるウォレットを使う場合はこのステップは不要です)
また、メインネットの場合は、 SymFacade('public') となります。

from symbolchain.core.CryptoTypes import PrivateKey
from symbolchain.core.sym.KeyPair import KeyPair
from symbolchain.core.facade.SymFacade import SymFacade
from binascii import hexlify

facade = SymFacade('public_test')
new_privatekey = PrivateKey.random()
new_keypair = SymFacade.KeyPair(new_privatekey)
new_publickey = new_keypair.public_key
new_address = facade.network.public_key_to_address(new_publickey)

print("privateKey:" + str(new_privatekey))
print("address:" + str(new_address))

上記は、
1. 秘密鍵の生成
1. 公開鍵の生成
1. 公開鍵からウォレットアドレスの生成
を行っています。

privateKey:BE48D752961910610E3763D5F90538C0946CD95D6AC3DC8C7B1253879D******
address:TAJEWDXCCMQLWTKVWDAX2EW7KS3L2NT6BOOIDSQ

これで秘密鍵とウォレットアドレスの生成・確認ができました。
くれぐれも秘密鍵は他人に知られないように管理しましょう。

テストネットで任意のアドレスへのxymの入金

テストネットではウォレットを作成してもxymがありませんので、蛇口サイトから自分のアドレスに対して、使いたいxym数量を入金します。
(使い終わったら返しましょう)

各種設定の確認

今回は下記の条件で送金します。
注意点として、送金量は可分性が6(小数点以下6桁)なので、AMOUNTMAX_FEEに指定する値は1000000倍した数値を指定します。
また、手数料はノードの最低手数料以上でなければスパムとみなされ、トランザクションが受理されません。

# 送信元秘密鍵
FROM_PRIVATEKEY = "BE48D752961910610E3763D5F90538C0946CD95D6AC3DC8C7B1253879******"
# 送信先ウォレットアドレス
TO_ADDRESS = "TBMAGI3FR6ZFDXB5CHYTCE5EBKBSQP2CZPS7CGA"
# 送金量 (可分性が6なので1000000倍した数値を指定。この例だと2枚)
AMOUNT = 2000000
# 最大手数料 (この例だと0.1枚)
MAX_FEE = 100000
# メッセージ
MESSAGE = "I love symbol. こんにちは"
# Symbol誕生のUTC秒 (メインネットは1615853185)
BIRTHTIME = 1616694977
# トランザクションの有効期限(単位はhour。この例だと2時間)
EXP_TIME = 2
# XYMのモザイクID (メインネットは0x6BED913FA20223F8)
MOSAIC_ID = 0x091F837E059AE13C
# トランザクションの送信先ノード(この例はテストネットのノード)
NODEURL = "http://sym-test-01.opening-line.jp:3000"

トランザクションへの署名とノードへアナウンス

先ほど設定した条件で
1. トランザクションを生成
1. トランザクションへの署名
1. ノードへアナウンス
を行います。

import datetime
import urllib.request
import json
from binascii import unhexlify, hexlify
from symbolchain.core.CryptoTypes import PrivateKey
from symbolchain.core.sym.KeyPair import KeyPair
from symbolchain.core.facade.SymFacade import SymFacade

facade = SymFacade('public_test')
keypair = KeyPair(PrivateKey(unhexlify(FROM_PRIVATEKEY)))

# トランザクションの有効期限
deadline = (int((datetime.datetime.today() + datetime.timedelta(hours=EXP_TIME)).timestamp()) - BIRTHTIME) * 1000

# トランザクション内容
tx = facade.transaction_factory.create({
  'type': 'transfer',
  'signer_public_key': keypair.public_key,
  'fee': MAX_FEE,
  'deadline': deadline,
  'recipient_address': SymFacade.Address(TO_ADDRESS),
  'mosaics': [(MOSAIC_ID, AMOUNT)],
  'message': MESSAGE
})

# トランザクションに署名
signature = facade.sign_transaction(keypair, tx)
tx.signature = signature.bytes
tx_hash = facade.hash_transaction(tx)

# ノードへアナウンス
payload = {"payload": hexlify(tx.serialize()).decode('utf8').upper()}
req = urllib.request.Request(NODEURL + "/transactions",
                             json.dumps(payload).encode(),
                             {'Content-type': 'application/json'},
                             method='PUT')
with urllib.request.urlopen(req) as res:
    print("tx hash:" + str(tx_hash))
    print("status code:" + str(res.getcode()))

正しいトランザクションがノードに受理された場合は、下記のように 202 がAPIから返ってきます。

tx hash:33FF21C6C86D519387D3669AB242CE0ADD6506DF4E6F5FDDAB03AF084DBCB257
status code:202

トランザクションの承認状況の確認

先ほど生成したトランザクションのハッシュ値(ここでの例だと33FF21C6C86D519387D3669AB242CE0ADD6506DF4E6F5FDDAB03AF084DBCB257)を使い、トランザクションの承認状況を問い合わせます。

req = urllib.request.Request(NODEURL + "/transactionStatus/" + str(tx_hash))
with urllib.request.urlopen(req) as res:
    data = json.load(res)
    print(json.dumps(data, indent=2))
{
  "group": "confirmed",
  "code": "Success",
  "hash": "216C5629F9B10AB5C91673CBA221C61E22436731884FB1B2E3C13EC1C606DE43",
  "deadline": "9998763000",
  "height": "255648"
}

承認されるまでは"group": "unconfirmed"となります。
手数料の指定が小さいとトランザクションが混雑してきたときに承認されるまでの時間が長くなります。
無事に承認されると、"group": "confirmed"が返ってきます。これで送金が完了です。
エクスプローラーでも確認できます。

トランザクション内容の確認

前々回の記事を参考に、トランザクション詳細やメッセージの確認を行います。

# メッセージ確認
req = urllib.request.Request(NODEURL + '/accounts/' + TO_ADDRESS)
with urllib.request.urlopen(req) as res:
    accountInfo = json.load(res)
ADDRESS48 = accountInfo['account']['address']

url = NODEURL + '/transactions/confirmed'
params = {
    'address': TO_ADDRESS,
    'order': 'desc',
}

req = urllib.request.Request('{}?{}'.format(url, urllib.parse.urlencode(params)))
with urllib.request.urlopen(req) as res:
    data = json.load(res)

for d in data['data']:
    mosaic = None
    # モザイクの送受信があるか
    if "mosaics" in d['transaction']:
        for xym in d['transaction']['mosaics']:
            message = ""
            if "message" in d['transaction']:
                message = d['transaction']['message']

            if xym['id'] == '091F837E059AE13C':
                mosaic = xym['amount']
            else:
                continue
            # 受け取り人が自分であるか
            if d['transaction']['recipientAddress'] == ADDRESS48:
                isRecipient = True
            else:
                isRecipient = False
            print("height:{}, {}, {}xym, message[{}]".format(d['meta']['height'], '受信' if isRecipient else "送信", int(mosaic)/1000000, unhexlify(message).decode('utf-8')))
height:255648, 受信, 2.0xym, message[I love symbol. こんにちは]
height:255642, 受信, 1.0xym, message[I love symbol. こんにちは]
height:251422, 受信, 1.0xym, message[I love symbol.こんちゃす]
height:251381, 受信, 1.0xym, message[I love symbol.]
height:251373, 受信, 1.0xym, message[I love symbol.]
height:248840, 送信, 1.0xym, message[hello]
height:248824, 受信, 300.0xym, message[]

上記のように直近の送受信とメッセージが表示されれば取得成功です。

全コードはこちら

参考情報