Last active
May 30, 2024 10:30
-
-
Save YasunoriMATSUOKA/cd268c9bb233f6ece5040d3aa755a175 to your computer and use it in GitHub Desktop.
Create or Update Metadata on Symbol blockchain with symbol-sdk@2 (node_modules/symbol-sdk/dist/src/service/MetadataTransactionService.jsの中のcreateAccountMetadataTransactionメソッドを参考に、それを使ったパターンと使わないパターンそれぞれのコードを例示)
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
import { | |
Account, | |
AggregateTransaction, | |
Deadline, | |
KeyGenerator, | |
MetadataTransactionService, | |
NetworkType, | |
RepositoryFactoryHttp, | |
UInt64, | |
} from "symbol-sdk"; | |
import { firstValueFrom } from "rxjs"; | |
(async () => { | |
const nodeUrl = "https://sym-test-03.opening-line.jp:3001"; | |
const repositoryFactory = new RepositoryFactoryHttp(nodeUrl); | |
const metadataRepository = repositoryFactory.createMetadataRepository(); | |
const txRepository = repositoryFactory.createTransactionRepository(); | |
const epochAdjustment = await firstValueFrom( | |
repositoryFactory.getEpochAdjustment(), | |
); | |
const generationHash = await firstValueFrom( | |
repositoryFactory.getGenerationHash(), | |
); | |
const aliceAccount = Account.createFromPrivateKey( | |
"_PUT_ALICE_PRIVATE_KEY_", | |
NetworkType.TEST_NET, | |
); | |
const bobAccount = Account.createFromPrivateKey( | |
"_PUT_BOB_PRIVATE_KEY_", | |
NetworkType.TEST_NET, | |
); | |
const feeMultiplier = 100; | |
const uint64Key = KeyGenerator.generateUInt64Key( | |
"_PUT_METADATA_KEY_", | |
); | |
const stringValue = "Hello, Bob!"; | |
const metadataTransactionService = new MetadataTransactionService(metadataRepository); | |
// Note: MetadataTransactionServiceの中の関数名がcreateだが中身の実装を見るとupdateも扱える実装となっている。したがって新規設定時も更新時もどちらも同じ実装でOK。 | |
const accountMetadataTx = await firstValueFrom( | |
metadataTransactionService.createAccountMetadataTransaction( | |
Deadline.create(epochAdjustment), | |
NetworkType.TEST_NET, | |
bobAccount.address, | |
uint64Key, | |
stringValue, | |
aliceAccount.address, | |
UInt64.fromUint(0), // Note: aggregateCompleteTxのsetMaxFeeForAggregateで指定するので本来不要だが、TSの型でUInt64が要求されているのでダミー値を入れておく必要がある。速習SymbolはTSではなくJS環境なので、ここに少し差が出る。 | |
), | |
); | |
const aggregateCompletedTx = AggregateTransaction.createComplete( | |
Deadline.create(epochAdjustment), | |
[accountMetadataTx.toAggregate(aliceAccount.publicAccount)], | |
NetworkType.TEST_NET, | |
[], | |
).setMaxFeeForAggregate(feeMultiplier, 1); | |
const signedTx = aliceAccount.signTransactionWithCosignatories( | |
aggregateCompletedTx, | |
[bobAccount], | |
generationHash, | |
); | |
console.dir( | |
{ | |
txHash: signedTx.hash, | |
txPayload: signedTx.payload, | |
}, | |
{ depth: null }, | |
); | |
const listener = repositoryFactory.createListener(); | |
await listener.open(); | |
listener.newBlock().subscribe((newBlock) => { | |
console.log(`newBlock: ${newBlock.height.toString()}`); | |
}); // Note: リスナーが意図せず切断されないよう毎ブロック30秒毎にメッセージが届くよう設定 | |
listener | |
.status(aliceAccount.address) | |
.subscribe((txStatusError) => console.error(txStatusError)); //Note: エラー検知 | |
listener.unconfirmedAdded(aliceAccount.address).subscribe((unconfirmedTx) => { | |
console.dir({ unconfirmedTx }, { depth: null }); | |
}); // Note: エラー無く、ブロックチェーンに情報が一旦届いたが、まだブロックに組み込まれていない状態の検知 | |
listener.confirmed(aliceAccount.address).subscribe((confirmedTx) => { | |
console.dir({ confirmedTx }, { depth: null }); | |
listener.close(); | |
}); // Note: ブロックに情報が組み込まれた状態。このタイミングでリスナーを切断してtxの状態遷移の監視を終了する | |
const txAnnounceResponse = await firstValueFrom( | |
txRepository.announce(signedTx), | |
); | |
console.dir({ txAnnounceResponse }, { depth: null }); | |
})(); |
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
import { | |
Account, | |
AccountMetadataTransaction, | |
AggregateTransaction, | |
Convert, | |
Deadline, | |
InnerTransaction, | |
KeyGenerator, | |
MetadataType, | |
NetworkType, | |
RepositoryFactoryHttp, | |
} from "symbol-sdk"; | |
import { firstValueFrom } from "rxjs"; | |
(async () => { | |
const nodeUrl = "https://sym-test-03.opening-line.jp:3001"; | |
const repositoryFactory = new RepositoryFactoryHttp(nodeUrl); | |
const metadataRepository = repositoryFactory.createMetadataRepository(); | |
const txRepository = repositoryFactory.createTransactionRepository(); | |
const epochAdjustment = await firstValueFrom( | |
repositoryFactory.getEpochAdjustment(), | |
); | |
const generationHash = await firstValueFrom( | |
repositoryFactory.getGenerationHash(), | |
); | |
const aliceAccount = Account.createFromPrivateKey( | |
"_PUT_ALICE_PRIVATE_KEY_", | |
NetworkType.TEST_NET, | |
); | |
const bobAccount = Account.createFromPrivateKey( | |
"_PUT_BOB_PRIVATE_KEY_", | |
NetworkType.TEST_NET, | |
); | |
const feeMultiplier = 100; | |
const uint64Key = KeyGenerator.generateUInt64Key( | |
"_PUT_METADATA_KEY_", | |
); | |
const stringValue = "Hello, Bob!"; | |
//Note: MetadataTransactionServiceを使わない場合は、自分自身で現状のMetadataを取得し、Metadataが既にあるか否かで処理を分ける必要がある | |
//Note: 現状のMetadataの取得 | |
const metadatas = await firstValueFrom( | |
metadataRepository.search({ | |
targetAddress: bobAccount.address, | |
scopedMetadataKey: uint64Key.toHex(), | |
sourceAddress: aliceAccount.address, | |
metadataType: MetadataType.Account, | |
}), | |
); | |
let accountMetadataTx: InnerTransaction; | |
if (metadatas.data.length > 0) { | |
// Note: 対象のメタデータが既に存在する場合=更新の場合 | |
const metadata = metadatas.data[0]; | |
const currentValueBytes = Convert.utf8ToUint8(metadata.metadataEntry.value); | |
const newValueBytes = Convert.utf8ToUint8(stringValue); | |
const valueSizeDelta = newValueBytes.length - currentValueBytes.length; | |
const valueBytes = Convert.hexToUint8( | |
Convert.xor(currentValueBytes, newValueBytes), | |
); | |
accountMetadataTx = AccountMetadataTransaction.create( | |
Deadline.create(epochAdjustment), | |
bobAccount.address, | |
uint64Key, | |
valueSizeDelta, | |
valueBytes, | |
NetworkType.TEST_NET, | |
).toAggregate(aliceAccount.publicAccount); | |
} else { | |
// Note: 対象のメタデータが存在しない場合=新規設定の場合 | |
const newValueBytes = Convert.utf8ToUint8(stringValue); | |
const valueSizeDelta = newValueBytes.length; | |
const valueBytes = newValueBytes; | |
accountMetadataTx = AccountMetadataTransaction.create( | |
Deadline.create(epochAdjustment), | |
bobAccount.address, | |
uint64Key, | |
valueSizeDelta, | |
valueBytes, | |
NetworkType.TEST_NET, | |
).toAggregate(aliceAccount.publicAccount); | |
} | |
const aggregateCompletedTx = AggregateTransaction.createComplete( | |
Deadline.create(epochAdjustment), | |
[accountMetadataTx], | |
NetworkType.TEST_NET, | |
[], | |
).setMaxFeeForAggregate(feeMultiplier, 1); | |
const signedTx = aliceAccount.signTransactionWithCosignatories( | |
aggregateCompletedTx, | |
[bobAccount], | |
generationHash, | |
); | |
console.dir( | |
{ | |
txHash: signedTx.hash, | |
txPayload: signedTx.payload, | |
}, | |
{ depth: null }, | |
); | |
const txAnnounceResponse = await firstValueFrom( | |
txRepository.announce(signedTx), | |
); | |
console.dir({ txAnnounceResponse }, { depth: null }); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
MetadataTransactionServiceの中のcreateAccountMetadataTransactionメソッドを使用してもよいと思います。(npmパッケージ内の実装を見るとcreateだけでなくupdateも扱える実装になっています。)
ハマりどころとしては、以下のような点がありうるでしょうか。
.toAggregate(alice.publicAccount)
を付け忘れたり、 (←すみません、最初このエラーが出るコードになってました。)