Last active
November 8, 2022 08:39
-
-
Save YasunoriMATSUOKA/432acf3eeca83897f55a549d2d99e34a to your computer and use it in GitHub Desktop.
Send tx with CDN of @nemtus/symbol-sdk-typescript and @nemtus/symbol-sdk-openapi-generator-typescript-axios
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<title>Document</title> | |
<script src="https://cdn.jsdelivr.net/npm/@nemtus/symbol-sdk-openapi-generator-typescript-axios@latest/index.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/@nemtus/symbol-sdk-typescript@latest/index.min.js"></script> | |
</head> | |
<body> | |
<script> | |
const restApiClient = window.symbolSdkOpenAPIGeneratorTypeScriptAxios; | |
const symbolSdk = window.symbolSdk.default; | |
const NODE_DOMAIN = "001-sai-dual.symboltest.net"; | |
(async () => { | |
// epochAdjustment, networkCurrencyMosaicIdの取得のためNetworkRoutesApi.getNetworkPropertiesを呼び出す | |
const configurationParameters = { | |
basePath: `https://${NODE_DOMAIN}:3001`, | |
}; | |
const configuration = new restApiClient.Configuration( | |
configurationParameters | |
); | |
const networkRoutesApi = new restApiClient.NetworkRoutesApi( | |
configuration | |
); | |
const networkPropertiesDTO = ( | |
await networkRoutesApi.getNetworkProperties() | |
).data; | |
// epochAdjustmentのレスポンス値は文字列でsが末尾に含まれるため除去してnumberに変換する | |
const epochAdjustmentOriginal = | |
networkPropertiesDTO.network.epochAdjustment; | |
if (!epochAdjustmentOriginal) { | |
throw Error("epochAdjustment is not found"); | |
} | |
const epochAdjustment = parseInt( | |
epochAdjustmentOriginal.replace(/s/g, "") | |
); | |
// networkCurrencyMosaicIdのレスポンス値はhex文字列で途中に'が含まれるため除去してBigIntに変換する | |
const networkCurrencyMosaicIdOriginal = | |
networkPropertiesDTO.chain.currencyMosaicId; | |
if (!networkCurrencyMosaicIdOriginal) { | |
throw Error("networkCurrencyMosaicId is not found"); | |
} | |
const networkCurrencyMosaicId = BigInt( | |
networkCurrencyMosaicIdOriginal.replace(/'/g, "") | |
); | |
// facadeの中に指定するtestnet等のネットワーク名を取得するためNetworkRoutesApi.getNetworkTypeを呼び出す | |
const networkTypeDTO = (await networkRoutesApi.getNetworkType()).data; | |
if (!networkTypeDTO) { | |
throw Error("networkType is not found"); | |
} | |
const networkName = networkTypeDTO.name; | |
// ネットワーク名を指定してSDKを初期化 | |
const facade = new symbolSdk.facade.SymbolFacade(networkName); | |
// トランザクションを送信するアカウント関連データを作成 | |
const privateKey = new symbolSdk.PrivateKey( | |
"PUT_YOUR_PRIVATE_KEY_HERE" | |
); | |
const keyPair = new symbolSdk.symbol.KeyPair(privateKey); | |
const signerPublicKeyString = keyPair.publicKey.toString(); | |
const signerAddressString = facade.network | |
.publicKeyToAddress(keyPair.publicKey) | |
.toString(); | |
// deadlineの計算(2時間で設定しているが変更可能、ただし遠すぎるとエラーになる。5~6時間くらいにテストネットでは閾値がある?) | |
const now = Date.now(); | |
const deadline = BigInt( | |
now - epochAdjustment * 1000 + 2 * 60 * 60 * 1000 | |
); | |
// 送信先アドレス ... Faucetのアドレスをここでは指定 | |
const recipientAddressString = | |
"TARDV42KTAIZEF64EQT4NXT7K55DHWBEFIXVJQY"; | |
// トランザクションのデータ生成 | |
const transaction = facade.transactionFactory.create({ | |
type: "transfer_transaction", | |
signerPublicKey: signerPublicKeyString, | |
deadline, | |
recipientAddress: recipientAddressString, | |
mosaics: [{ mosaicId: networkCurrencyMosaicId, amount: 1000000n }], | |
}); | |
// 手数料設定 ... 送信先ノードの設定によるが100なら基本的に足りないことはないと思う | |
const feeMultiplier = 100; | |
transaction.fee.value = BigInt(transaction.size * feeMultiplier); | |
// 署名 | |
const signature = facade.signTransaction(keyPair, transaction); | |
transaction.signature = new symbolSdk.symbol.Signature(signature.bytes); | |
// 各ネットワーク固有のgenerationHashSeedを設定 | |
transaction.network.generationHashSeed = facade.network; | |
// トランザクションのハッシュを計算 ... トランザクションの承認状態を後でWebSocketで確認する時などに必要 | |
const hash = facade.hashTransaction(transaction); | |
console.log(hash.toString()); | |
console.log( | |
`https://testnet.symbol.fyi/transactions/${hash.toString()}` | |
); | |
// トランザクション送信時にはこのデータを使う必要あり | |
const transactionPayload = | |
facade.transactionFactory.constructor.attachSignature( | |
transaction, | |
signature | |
); | |
// 1 confirmation以外の場合の設定 | |
const confirmationHeight = 6; // 6confで確認と見なす場合 | |
let transactionHeight = 0; | |
let blockHeight = 0; | |
let finalizedBlockHeight = 0; | |
// WebSocketでトランザクション送信時の各種イベントに応じた処理を事前定義しておく必要がある | |
const ws = new WebSocket(`wss://${NODE_DOMAIN}:3001/ws`); | |
ws.onopen = () => { | |
console.log("connection open"); | |
}; | |
ws.onclose = () => { | |
console.log("connection closed"); | |
}; | |
ws.onmessage = (msg) => { | |
const res = JSON.parse(msg.data); | |
if ("uid" in res) { | |
console.log(`uid : ${res.uid}`); | |
// ターゲットアドレスのトランザクションが未承認状態になったのを監視 | |
const unconfirmedBody = `{"uid": "${res.uid}", "subscribe": "unconfirmedAdded/${recipientAddressString}"}`; | |
console.log(unconfirmedBody); | |
ws.send(unconfirmedBody); | |
// ターゲットアドレスのトランザクションが承認されるの監視 | |
const confirmedBody = `{"uid": "${res.uid}", "subscribe": "confirmedAdded/${recipientAddressString}"}`; | |
console.log(confirmedBody); | |
ws.send(confirmedBody); | |
// ターゲットアドレスのトランザクションがエラーになったのを監視 | |
const statusBody = `{"uid": "${res.uid}", "subscribe": "status/${recipientAddressString}"}`; | |
console.log(statusBody); | |
ws.send(statusBody); | |
// 新しいブロックを監視 | |
const blockBody = `{"uid": "${res.uid}", "subscribe": "block"}`; | |
console.log(blockBody); | |
ws.send(blockBody); | |
// ファイナライズされたブロックを監視 | |
const finalizedBlockBody = `{"uid": "${res.uid}", "subscribe": "finalizedBlock"}`; | |
console.log(finalizedBlockBody); | |
ws.send(finalizedBlockBody); | |
} | |
// トランザクションが未承認になったときに発火 | |
if ( | |
res.topic === `unconfirmedAdded/${recipientAddressString}` && | |
res.data.meta.hash === hash.toString() | |
) { | |
console.log("transaction unconfirmed"); | |
} | |
// トランザクションが承認されたときに発火 | |
if ( | |
res.topic === `confirmedAdded/${recipientAddressString}` && | |
res.data.meta.hash === hash.toString() | |
) { | |
console.log("transaction confirmed"); | |
transactionHeight = parseInt(res.data.meta.height); | |
} | |
// ブロック生成時に発火 | |
if (res.topic === `block`) { | |
console.log("block"); | |
blockHeight = parseInt(res.data.block.height); | |
} | |
// ブロックのファイナライズ時に発火 | |
if (res.topic === `finalizedBlock`) { | |
console.log("finalizedBlock"); | |
console.log(res); | |
finalizedBlockHeight = parseInt(res.data.height); | |
} | |
// トランザクションがエラーになったときに発火 | |
if ( | |
res.topic === `status/${recipientAddressString}` && | |
res.data.hash === hash.toString() | |
) { | |
console.log(res.data.code); | |
ws.close(); | |
} else { | |
console.log(res); | |
} | |
// confirmationHeightブロック後に監視終了 | |
if ( | |
transactionHeight !== 0 && | |
transactionHeight + confirmationHeight - 1 <= blockHeight | |
) { | |
console.log( | |
`${confirmationHeight} blocks confirmed. transactionHeight is ${transactionHeight} blockHeight is ${blockHeight}.` | |
); | |
ws.close(); | |
} else { | |
console.log( | |
`wait for ${confirmationHeight} blocks. transactionHeight is ${transactionHeight} blockHeight is ${blockHeight}.` | |
); | |
} | |
// finalizedBlockHeightが対象ブロックを追い越した後に監視終了 | |
if ( | |
transactionHeight !== 0 && | |
transactionHeight <= finalizedBlockHeight | |
) { | |
console.log( | |
`${finalizedBlockHeight} block finalized. transactionHeight is ${transactionHeight} blockHeight is ${blockHeight}.` | |
); | |
ws.close(); | |
} else { | |
console.log( | |
`wait for finalized block. transactionHeight is ${transactionHeight} blockHeight is ${blockHeight}.` | |
); | |
} | |
}; | |
// トランザクションのアナウンス実行 | |
try { | |
const transactionRoutesApi = new restApiClient.TransactionRoutesApi( | |
configuration | |
); | |
console.log(transactionPayload); | |
const response = await transactionRoutesApi.announceTransaction({ | |
transactionPayload, | |
}); | |
console.log(response.data); | |
} catch (err) { | |
console.error(err); | |
} | |
})(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
フォーク元の変更に追従した結果、Revision 2のように若干記述方法が変わったことに注意。メインネットでのアップグレード及びテストネットのリセット後に対応済の場合はRevision 2の書き方をする必要がある。もし古いテストネットを使う場合は古いバージョンのSDKを使ってRevision 1の書き方をする必要があると思う。