送金成功の判定方法
iost、irc20、irc21 トークンの送金判定方法はほぼ同じで、receipt のトークンシンボルを区別すればよいだけです。
送金トランザクション成功の判定
あるトランザクションが送金を含み、その送金が成功したことを確認するには、次の 2 点を判定する必要があります。トランザクションが不可逆 (irreversible) 状態であること、そしてトランザクションが送金情報を含みかつ正常に実行されたこと。
トランザクションが不可逆かどうかの判定
トランザクションが不可逆かどうかを判定する方法は 3 つあります。
- getTxByHash API を呼び出し、戻り値の
statusフィールドがIRREVERSIBLEであればトランザクションは不可逆です。 - getTxReceiptByTxHash API を呼び出し(この API は不可逆トランザクションに対してのみ TxReceipt を返します)、戻り値が存在すればトランザクションは不可逆です。
- getBlockByNumber または getBlockByHash API を呼び出し、戻り値の
statusフィールドがIRREVERSIBLEであれば、そのブロックのすべてのトランザクションが不可逆です。
トランザクションが送金を含み、かつ正常に実行されたかの判定
トランザクションに送金が含まれているかを判定する唯一の方法は tx_receipt の receipts フィールドを確認することです。tx_receipt は getTxByHash API と getTxReceiptByTxHash API で取得できます。また、getBlockByNumber と getBlockByHash API が返すブロック構造にも、トランザクションの tx_receipt が含まれます。成功した送金トランザクションの receipts は次の形式です。
"tx_receipt": {
"tx_hash": "CHeKFLzzpcfZ2Fo9gHwRM7PFkZy5SoEKSda2ZrTYfHQf",
"gas_usage": 8750,
"ram_usage": {},
"status_code": "SUCCESS",
"message": "",
"returns": [
"[]"
],
"receipts": [
{
"func_name": "token.iost/transfer",
"content": "[\"iost\",\"fromacc\",\"toacc\",\"1.0000\",\"\"]"
}
]
},
receipts フィールドはリストです。ある項目の func_name フィールドが token.iost/transfer ならば、そのトランザクションは送金を含んでいます(iost であっても irc20 や irc21 であっても、func_name はいずれも token.iost/transfer です)。送金のトークンシンボル、アカウント、金額は該当項目の content フィールドからさらにパースしてください。content フィールドは 5 つの文字列を含む配列の JSON シリアライズ結果です。JSON デシリアライズすると、1 番目の要素がトークンシンボル(トークンシンボルはすべてのトークンの中で一意)、2 番目が送金元アカウント名、3 番目が送金先アカウント名、4 番目が送金額、5 番目が memo です。4 番目の要素である送金額を扱う際、[0-9.] 以外の文字を含む文字列が見つかった場合はその送金を無視してください。それ以外の場合は文字列を直接 float に変換して後続処理に使えます。
トランザクションが正常に実行されたことを確認するには、tx_receipt の status_code フィールドが SUCCESS と等しいかを確認します。
よくある誤り
actions で送金成功を判定する
actions はトランザクションの動作リスト、receipts はトランザクションの実行結果です。トランザクションの actions フィールドだけで送金の成功を判定するのは厳密ではありません。特殊なケース(例: 遅延トランザクション)では tx_receipt の status_code が SUCCESS であってもトランザクションが実際には成功していない場合があり、逆に送金トランザクションが実際に成功すれば必ず receipts が存在します。したがって送金成功は actions ではなく receipts フィールドで判定してください。
送金額の文字列をそのまま float に変換する
メインネットの token.iost コントラクトの送金は、トークンの decimal に合わせて送金額を切り詰めた後にその結果を float に変換します。したがって以下のような送金では
"receipts": [
{
"func_name": "token.iost/transfer",
"content": "[\"iost\",\"aaaaa\",\"bbbbb\",\"1.20294517598E7\",\"\"]"
}
]
実際に送金されたトークン量は 1.20294517 です。"1.20294517598E7" をそのまま float に変換すると期待通りにならない可能性があります。したがって送金額の文字列に [0-9.] 以外の文字が含まれていれば、その送金を無視することを推奨します。
トークンシンボルを判定しない
送金を判定する際は必ずトークン名、つまり content フィールドの 1 番目の要素を確認してください。以下のような送金でトークン名を確認せず iost トークン送金として処理してしまうと問題が起きます。
"receipts": [
{
"func_name": "token.iost/transfer",
"content": "[\"sometoken\",\"aaaaa\",\"bbbbb\",\"1.20294517\",\"\"]"
}
]
トランザクションの実行ステータスを確認しない
ram 不足のときは送金トランザクションが失敗しても receipts は生成されます。例えば次のようなケースです。
"tx_receipt": {
"tx_hash": "xxxxxxxxxxxxxxxxx",
"gas_usage": 186277,
"ram_usage": {},
"status_code": "BALANCE_NOT_ENOUGH",
"message": "balance not enough after executing actions: pay ram failed. id: irisye need 335, actual 115",
"returns": [
"[\"\"]"
],
"receipts": [
{
"func_name": "token.iost/transfer",
"content": "[\"iost\",\"aaaaaaa\",\"vote.iost\",\"10\",\"\"]"
}
]
},
status_code フィールドを確認しないと、送金が成功したと誤解してしまいます。
まとめ
送金の成功を確認するには、以下のすべてを判定する必要があります。
- トランザクションが不可逆であること。
tx_receipt.status_codeがSUCCESSであること。tx_receipt.receiptsの中にfunc_nameがtoken.iost/transferの項目があること。contentフィールドの 1 番目の要素(トークンシンボル)が期待値であること。contentフィールドの 4 番目の要素(金額)に不正な文字がないこと。