#SymbolでのNFTの発行について
Symbolでも他の仮想通貨同様にNFTのサービスがあり,主にモザイクを利用したNFT発行サービスが展開されています.モザイクを利用したNFT発行の場合,モザイク発行手数料(現行では50XYM)がかかります.このため,発行したNFTを50XYM以下で販売すると発行者が手数料負けします.
そこで今回はマルチシグを使って現在の仕組みでモザイク発行手数料が不要な,つまり今までよりも50XYM安く発行できるマルチシグアドレス型のNFTを実現することを目標にしてみました.
#マルチシグでNFTを再現する
###NFTの取引では何がやりとりされているのか
モザイク型のNFTを知るため,SymbolのNemberArtのNFTを調べてみます.
こちらは私がNemberArtで購入させて頂いたNFTです.
作品が展示されているのが分かります.ブロックチェーン上でどのように記録されているかを調べるために'Check NFT on Explorer'を押してみます
アクセスできませんね...
エクスプローラーの変更によってアクセスできなくなっているのが原因のようです.symbol.fyiに変えて試してみます
アクセスできました.これがNFT(モザイク)値の部分がモザイクに紐づけられている情報です.
私たちがNemberArtで売り買いしているのはこのようなモザイク(トークン)の情報です.この情報と作品を紐付け,取引できればNFTとして成立します.
取引がどのように行われるかを調べるため,トランザクションを見てみます.
1はNemberArtが手数料を負担するためのトランザクション,2がモザイク(NFT)の送付,3が購入金額の送付,4が手数料の送付となっています.モザイクを送ることで(2番のトランザクション)でNFTとして取引が成立したことになっています.
マルチシグのNFTでは,マルチシグアドレス自体の所有権を移転させることでこの機能を代替します.
画像の情報にアクセスしてみます.NemberArtでは画像はIFPSに保存されているので'watch artwork on IFPS'を押してアクセスします.
私の環境では表示に5分ほどかかったのですがなんとかアクセスできました.view on IFPS Gatewayを押すと画像が表示されます.
表示できました.保存先はIFPSなので,世界のどこかのサーバーがデータのピン止めを続けていない限りデータは消失します.頻繁にアクセスされると情報としてキャッシュされやすくなり,表示も早いのですがアクセスが無ければいつか消えてしまうのでパソコンやスマホに保存しておきましょう.
###NFTとなる条件を整理してみる
これらの結果をまとめてNFTとなる条件を整理してみます.ざっとまとめると次のようになりそうです.
・自由に取引することができる
・タイトルや作者の情報が含まれている
・データの中身,もしくはデータにアクセスする方法が記録されている
これらの条件をマルチシグで再現すると
・自由に取引することができる
=>連署者を変更することで,自由に取引,譲渡することができる
・タイトルや作者の情報が含まれている
・データの中身,もしくはデータにアクセスする方法が記録されている
=>これらの情報はアドレスにトランザクション,メタデータとして書き込こめる
実際にやってみます.
#実際にやってみる
##作成してみる
先程の条件を満たすトランザクションを作ってみます.まず,IFPSの仕組みによって画像が消えてしまわないよう,画像の情報自体をオンチェーンに乗せるトランザクションを作成します.詳しい実装についてはこちらをご覧ください.なお,書き込みの際には新しいアドレスの秘密鍵を用意し,そのアドレスに入金してから行ってください.
こちらの方法では手数料率100の場合100KBあたり約10XYM強程度で記録できます(手数料率10の場合は1XYMでいけます)トランザクションが送信失敗した際の対処はまたあまりできていないため大容量ファイル送信の際はお気をつけください(もしよければ改良してくれると嬉しいです)
書き込んだアドレスにプラットフォーム,作者からのNFTに関するメタデータを付与し,NFTにします.
async function createAddressNFT(account,nftAccount){
const platform = 'SampletPlatform';
const discription = 'NFT';
const title = 'Title: SampleTitle';
const creator = 'Creater: SampleCreater';
const infoHash = 'InfoHash: D900B6EE4283836B9138A4926302B990BDA3C4F99447BB6059F71850FCABDC43';
const platformKey = xym.KeyGenerator.generateUInt64Key('Platform');
const discriptionKey = xym.KeyGenerator.generateUInt64Key('Discription');
const titleKey = xym.KeyGenerator.generateUInt64Key('Title');
const creatorKey = xym.KeyGenerator.generateUInt64Key('Creater');
const infoHashKey = xym.KeyGenerator.generateUInt64Key('InfoHash')
//プラットフォームからのメタ情報
const platformMeta = xym.AccountMetadataTransaction.create(
xym.Deadline.create(epochAdjustment),
nftAccount.address,
platformKey,
platform.length,
platform,
networkType,
);
//説明のメタ情報
const discriptionMeta = xym.AccountMetadataTransaction.create(
xym.Deadline.create(epochAdjustment),
nftAccount.address,
discriptionKey,
discription.length,
discription,
networkType,
);
//作者名のメタ情報
const creatorMeta = xym.AccountMetadataTransaction.create(
xym.Deadline.create(epochAdjustment),
nftAccount.address,
creatorKey,
creator.length,
creator,
networkType,
);
//作品名のメタ情報
const titleMeta = xym.AccountMetadataTransaction.create(
xym.Deadline.create(epochAdjustment),
nftAccount.address,
titleKey,
title.length,
title,
networkType,
);
//情報トランザクションのハッシュのメタ情報
const infoHashMeta = xym.AccountMetadataTransaction.create(
xym.Deadline.create(epochAdjustment),
nftAccount.address,
infoHashKey,
infoHash.length,
infoHash,
networkType,
);
//スパムからの落書きを防止する
const accountRestriction = xym.AccountAddressRestrictionTransaction.create(
xym.Deadline.create(epochAdjustment),
xym.AddressRestrictionFlag.AllowIncomingAddress,
[account.address],
[],
networkType,
);
//マルチシグ化
const multisig = xym.MultisigAccountModificationTransaction.create(
xym.Deadline.create(epochAdjustment),
1,
1,
[account.address],
[],
networkType,
);
//アグリゲート作成
const aggregateTx = xym.AggregateTransaction.createComplete(
xym.Deadline.create(epochAdjustment),//有効期限
[
titleMeta.toAggregate(account.publicAccount),
creatorMeta.toAggregate(account.publicAccount),
platformMeta.toAggregate(platformAccount.publicAccount),
discriptionMeta.toAggregate(account.publicAccount),
infoHashMeta.toAggregate(account.publicAccount),
accountRestriction.toAggregate(nftAccount.publicAccount),
multisig.toAggregate(nftAccount.publicAccount),
],
networkType,
[],
).setMaxFeeForAggregate(100,2);
const signedTx = account.sign(aggregateTx,networkGenerationHash);//payloadを作成する
const nftAccountCosign = xym.CosignatureTransaction.signTransactionPayload(//作成したpayloadに連署する
nftAccount,
signedTx.payload,
networkGenerationHash
);
//プラットフォームが承認する(実際にはアグリゲートボンデッドを使う方が良いかもしれません)
const platformCosign = xym.CosignatureTransaction.signTransactionPayload(//作成したpayloadに連署する
platformAccount,
signedTx.payload,
networkGenerationHash
);
//完成させる
const complete = account.signTransactionGivenSignatures(
aggregateTx,
[nftAccountCosign,platformCosign],
networkGenerationHash
);
return complete;
}
これをアナウンスすると次のようなメタデータが書き込まれたNFTが完成し,a_accountが所有します.
メタデータの順番を変えられないかやってみたのですが,アグリゲートトランザクションのinnertransactionの並び替えでは上手くいきませんでした.メタデータの登録を複数回に分けて行えば綺麗な並びになると思います.
メタ情報付与の一連のソースはこちらです
const xym = require('symbol-sdk');
var networkGenerationHash ='';
var epochAdjustment = '';
var repo;
var transactionHttp;
var receiptRepo;
var node;
var networkType = xym.NetworkType.TEST_NET;
var feemultiplier;
const platformAccount = xym.Account.createFromPrivateKey(
'***',
networkType
)
//送信元アカウント
const a_account = xym.Account.createFromPrivateKey(
'***',
networkType,
);
(async()=>{
node = 'https://sym-test-09.opening-line.jp:3001';
repo = new xym.RepositoryFactoryHttp(node);
transactionHttp = repo.createTransactionRepository();
receiptRepo = repo.createReceiptRepository();
transactionStatusRepo = repo.createTransactionStatusRepository();
transactionService = new xym.TransactionService(transactionHttp,receiptRepo);
//websocketConnection();
await getInfo();
const nftAccount= xym.Account.createFromPrivateKey(
'***',//作成したNFTの秘密鍵
networkType
)
const aggTx = await createAddressNFT(a_account, nftAccount);
await anounceTX(aggTx);
})();
async function anounceTX(signed){
console.log("送信トランザクションハッシュ: "+signed.hash);
console.log(node+'/transactionStatus/'+signed.hash);
transactionHttp.announce(signed).subscribe(
(x) => console.log(x),
(err) => console.error(err),
);
}
async function createAddressNFT(account,nftAccount){
const platform = 'SampletPlatform';
const discription = 'NFT';
const title = 'Title: SampleTitle';
const creator = 'Creater: SampleCreater';
const infoHash = 'InfoHash: D900B6EE4283836B9138A4926302B990BDA3C4F99447BB6059F71850FCABDC43';
const platformKey = xym.KeyGenerator.generateUInt64Key('Platform');
const discriptionKey = xym.KeyGenerator.generateUInt64Key('Discription');
const titleKey = xym.KeyGenerator.generateUInt64Key('Title');
const creatorKey = xym.KeyGenerator.generateUInt64Key('Creater');
const infoHashKey = xym.KeyGenerator.generateUInt64Key('InfoHash')
//説明
const platformMeta = xym.AccountMetadataTransaction.create(
xym.Deadline.create(epochAdjustment),
nftAccount.address,
platformKey,
platform.length,
platform,
networkType,
);
//説明
const discriptionMeta = xym.AccountMetadataTransaction.create(
xym.Deadline.create(epochAdjustment),
nftAccount.address,
discriptionKey,
discription.length,
discription,
networkType,
);
const creatorMeta = xym.AccountMetadataTransaction.create(
xym.Deadline.create(epochAdjustment),
nftAccount.address,
creatorKey,
creator.length,
creator,
networkType,
);
const titleMeta = xym.AccountMetadataTransaction.create(
xym.Deadline.create(epochAdjustment),
nftAccount.address,
titleKey,
title.length,
title,
networkType,
);
const infoHashMeta = xym.AccountMetadataTransaction.create(
xym.Deadline.create(epochAdjustment),
nftAccount.address,
infoHashKey,
infoHash.length,
infoHash,
networkType,
);
const accountRestriction = xym.AccountAddressRestrictionTransaction.create(
xym.Deadline.create(epochAdjustment),
xym.AddressRestrictionFlag.AllowIncomingAddress,
[account.address],
[],
networkType,
);
const multisig = xym.MultisigAccountModificationTransaction.create(
xym.Deadline.create(epochAdjustment),
1,
1,
[account.address],
[],
networkType,
);
const aggregateTx = xym.AggregateTransaction.createComplete(
xym.Deadline.create(epochAdjustment),//有効期限
[
titleMeta.toAggregate(account.publicAccount),
creatorMeta.toAggregate(account.publicAccount),
platformMeta.toAggregate(platformAccount.publicAccount),
discriptionMeta.toAggregate(account.publicAccount),
infoHashMeta.toAggregate(account.publicAccount),
accountRestriction.toAggregate(nftAccount.publicAccount),
multisig.toAggregate(nftAccount.publicAccount),
],
networkType,
[],
).setMaxFeeForAggregate(100,2);
const signedTx = account.sign(aggregateTx,networkGenerationHash);//payloadを作成する
const nftAccountCosign = xym.CosignatureTransaction.signTransactionPayload(//作成したpayloadに連署する
nftAccount,
signedTx.payload,
networkGenerationHash
);
const platformCosign = xym.CosignatureTransaction.signTransactionPayload(//作成したpayloadに連署する
platformAccount,
signedTx.payload,
networkGenerationHash
);
const complete = account.signTransactionGivenSignatures(
aggregateTx,
[nftAccountCosign,platformCosign],
networkGenerationHash
);
return complete;
}
async function getInfo(){
feemultiplier = 100;
epochAdjustment = await repo.getEpochAdjustment().toPromise();
networkGenerationHash = await repo.getGenerationHash().toPromise();
currencyMosaicId = new xym.MosaicId((await repo.createNetworkRepository().getNetworkProperties().toPromise()).chain.currencyMosaicId.replace(/0x/,"").replace(/'/g,""));
}
このNFTを取引してみます.
##譲渡してみる
譲渡はSymbolDesktopWalletで簡単に行えます.
まず,譲渡したいNFTアドレスを選択します.
トランザクションをアナウンスすると譲渡先にマルチシグ変更のアグリゲートボンデッドが届くので,内容を確認して署名します.
##取引してみる
販売する際はお互いの持ち逃げを防ぐために,マルチシグの受け渡しと代金の引き渡しを同時に行う必要があります.このトランザクションはdesktopWalletでは作れないのでトランザクションを作ります.モザイク型のNFTであればシークレットロックを使った方法もありますが,アドレス型のNFTではアグリゲートトランザクションのみに限定されます.
今回はAがBに100XYMでNFTを手渡すとします
const xymMosaic = new xym.Mosaic(
currencyMosaicId,
xym.UInt64.fromUint(100 * 1000000)//送金量*10^(可分性)(xymは6)
);
//代金支払い
const btoaTX = xym.TransferTransaction.create(
xym.Deadline.create(epochAdjustment),
a_publicAccount.address,//受取人アドレス
[xymMosaic],//モザイク
xym.PlainMessage.create('BtoA'),//メッセージ
networkType,//ネットワークタイプ
);
//連署者変更
const multisigChange = xym.MultisigAccountModificationTransaction.create(
xym.Deadline.create(epochAdjustment),
0,
0,
[b_publicAccount.address],//取引先のアドレスを新たな連署者に
[a_account.address],//元の所有者を連署者から削除する
networkType,
);
const aggregateTx = xym.AggregateTransaction.createBonded(
xym.Deadline.create(epochAdjustment,48),//有効期限,アグリゲートボンデッドは2日後まで受け入れられる
[
btoaTX.toAggregate(b_publicAccount),//代金支払い
multisigChange.toAggregate(nft_publicAccount)//nftのパブリックアカウント(公開鍵から作ったアカウント)
],
networkType,
[],
).setMaxFeeForAggregate(100,1);
アナウンスし,連署されるとAに100XYMが,BにNFTが移動します
これでNFTアドレスの取引が完了しました.アグリゲートボンデッドは通常2日しか利用は不可能ですが,以下の記事の最後の方にある有効期限の延長方法によって延命をすることが可能だと思います(未検証).ハッシュロックも実装方法によっては節約可能なため,この方法によりさらに使い勝手の良いNFT取引ができると思います.
#複数の枚数発行する
複数枚数を作成する場合はデータの書き込みも複数回行わなければならないように思えます.しかし,大量の枚数をエアドロップしたい場合にはこの方法は非効率的となります.
書き込みデータは一つでも問題ないため,メタデータに記載する情報トランザクションハッシュをNFTアドレスに向けることで代用します.また,メタデータにシリアルナンバーを付与してもよいと思います.
#疑似的にlevyを再現する
法的に可能かは置いておき,仕組みとして実現できるか考えます.
nemにおいてlevy機能はモザイクを移転させたときに手数料としてXEMが徴収されるといった仕組みでした.Symbolでは(まだ)実装されておりませんが,NFTを移転させる,つまりアドレスを他者に譲渡するタイミングで作者にXYMが支払われれば良いと私は考えました.
そこで,マルチシグを利用することで作者に手数料を支払われなければ移転ができないように制限し,levyを再現してみました.
##levy用の連署者を含めたマルチシグ
Symbolのマルチシグでは複数人の連署をもってマルチシグアドレスのトランザクションを実行するといった仕組みを行うことができます.この仕組を利用し,発行するNFTに作者を連署者として残します.(つまり,NFTを作者と共同で所有する仕組みをとります)
const multisigChange = xym.MultisigAccountModificationTransaction.create(
xym.Deadline.create(epochAdjustment),
1,//承認数1増加
1,//削除数1増加
[buyerAddress],//購入者のアドレスを新たな連署者に追加
[],
networkType,
);
作者は二次売買のための連署を求められた際,作者にロイヤリティが支払われるトランザクションが含まれているか確認を行います.含まれていないは連署を拒否,含まれている場合は連署するといった形をとることでNFT所有者が二次販売する際に作者への手数料支払いを強制させることができます.作者の判断で手数料を免除することも可能です.
const xymMosaic = new xym.Mosaic(
currencyMosaicId,
xym.UInt64.fromUint(100 * 1000000)//送金量*10^(可分性)(xymは6)
);
const levyMosaic = new xym.Mosaic(
currencyMosaicId,
xym.UInt64.fromUint(10 * 1000000)//送金量*10^(可分性)(xymは6)
);
//代金支払い
const newBuyertobTX = xym.TransferTransaction.create(
xym.Deadline.create(epochAdjustment),
buyerAddress,//受取人アドレス
[xymMosaic],//モザイク
xym.PlainMessage.create('BtoA'),//メッセージ
networkType,//ネットワークタイプ
);
//levy
const levyTx = xym.TransferTransaction.create(
xym.Deadline.create(epochAdjustment),
a_address,//作者のアドレス
[levyMosaic],//モザイク
xym.PlainMessage.create('levy'),//メッセージ
networkType,//ネットワークタイプ
);
//連署者変更
const multisigChange = xym.MultisigAccountModificationTransaction.create(
xym.Deadline.create(epochAdjustment),
0,
0,
[newBuyer_publicAccount.address],//取引先のアドレスを新たな連署者に
[buyer_publicAccount.address],//元の所有者を連署者から削除する
networkType,
);
const aggregateTx = xym.AggregateTransaction.createBonded(
xym.Deadline.create(epochAdjustment,48),//有効期限,アグリゲートボンデッドは2日後まで受け入れられる
[
newBuyertobTX.toAggregate(newBuyer_publicAccount),//代金支払い
levyTx.toAggregate(newBuyer_PublicAccount),
multisigChange.toAggregate(nft_publicAccount)//nftのパブリックアカウント(公開鍵から作ったアカウント)
],
networkType,
[],
).setMaxFeeForAggregate(100,2);
作者が活動を辞める場合などは作者を連署者から外してlevyを取りやめたりすることも可能です.また,モザイク型NFTをマルチシグに入れることでモザイク型NFTにlevy機能を追加することも可能だと思います.(ただし,既に流通しているモザイク型NFTの場合作者以外でもlevyできる問題があります)
##自動連署bot
自動で連署するbotを用意することで作者が承認する手間を省けます.ただ,悪意のあるアグリゲートボンデッドの連署を防ぐため,トランザクションの精査をしっかり行う必要があります.こちらにも記事があります.
async function signPartialTransaction(account){//partial状態であれば全て署名するので注意
const transactions = await transactionHttp.search({ address: account.address, group: xym.TransactionGroup.Partial }).toPromise();
for (var data of transactions.data) {
const cosignatureTx = xym.CosignatureTransaction.create(data);
const signedTx = account.signCosignatureTransaction(cosignatureTx);
transactionHttp.announceAggregateBondedCosignature(signedTx).subscribe(
(x) => console.log(x),
(er) => console.error(er),
);
}
}
#良い点
##マルチシグの良い点
###1.発行手数料が非常に安い
モザイク発行手数料がかからないのでほぼ0XYMから発行することができます.今のところ,オンチェーンにしなければプラットフォームが全額肩代わりできるレベルで安いです.
発行手数料が完全に無料であれば参入したいクリエイターもいいと思います.50XYMの支払いが不要になることで,今まで以上に作者が多くの収益を得られるようになると思います.
###2.スパムのNFTにできない
NFTでは業者などから宣伝として勝手に広告やスパムのNFTを送りつけられることがあります.しかし,マルチシグ型では送りつけられる際に受け取り承認を必要とするため,スパムNFTの受け取り拒否が可能になります.
##levyの良い点
###1.XYMで確実にlevyできる
levyを確実にXYMで受け取ることができます
###2.levyの無効化が自由に可能
作者がlevyを一時的に外したり,ずっと無効化したりすることが可能です.さらに,作者が売買のコントロールをできるので不適切と判断する購入者をブロックすることが可能です.
#問題点
###マルチシグの問題
###1.バーンする際の問題
マルチシグの特性上,マルチシグアカウントを譲渡する際は譲渡先のアドレスの署名が必要になります.つまり,バーンする際は相手方の署名が必要となるため秘密鍵を生成する必要があります.
モザイク型NFTの場合は公開鍵が000000...のような秘密鍵が分からないと考えられるアドレスに送ることで,誰でもNFTをバーン(発行量を減らしたり,捨てたりする)ことができますが,マルチシグ型NFTは秘密鍵がなければバーンすることができません.秘密鍵を誰かが知っている限りは移転することが可能なため,この場合はバーンを行った人が秘密鍵を捨てているかを証明する必要がありますが,個人が本当に秘密鍵を捨てているか証明することは難しくなります.バーンを代行するサービスが必要になると思います.
###2.連署数の上限問題
マルチシグに連署できる数は1アカウントにつき25アドレスまでになっています.このため,モザイクNFTは無制限に所有できるのに対して,アドレス型のNFTは1アドレスが所有できるNFTの数に限度が発生します.
この問題はマルチシグのマルチシグを作ることによって拡張が可能です.Symbolのメインネットでは3階層のマルチシグを組むことができるのでマルチシグを3重に重ねがけ出来ます.
(メイン)ー(枠拡張用アドレス)ー(枠拡張用アドレス)ー(NFTアドレス)
のように組むことで最大 25(枠拡張用アドレス)*25(枠拡張用アドレス)*25(メイン)= 15625個まで増やすことが可能と思います.15625個以上のNFTを所有する方は少数派とは思うので実用上は十分に使えます.ただし,2階層のマルチシグNFTを組んだNFTの場合は1個で625個,1階層のマルチシグの場合は25個の枠を取ります.
また,枠を最大限活用するためにアドレス型NFTの所有には普段ハーベストや取引所への送金に使っているアドレスではなく,NFT用のアドレスを用意してあげたほうが良いと思います.
###levyの問題
###1.法律的な問題
プラットフォーム側が連署者として秘密鍵を持つ場合などは特に,カストディ規制にかからないようにサービスを作る必要があります.
###2.移動させる時の問題
作者の秘密鍵が完全に喪失された場合や突然連絡が取れなくなった場合,NFTは移転できなくなります.NFTのプラットフォームが連署者として加わればある程度は解決できると思うのですが,カストディの規制と付き合う必要があります.
###3.作者が承認を行わない問題
levy手数料を支払う場合でも,作者が許可しなければ転売できません.levyの手数料を上げるとある日言い出しても何もできなくなります.NFTのプラットフォームが連署者として加わることで抑制が可能とは思います.
#強制levyを行わない場合
現在,マーケットプレイスとして有名なopenSeaでは転売した際にクリエイターに還元される機能がありますがトークン自体にlevy機能がついているわけではなくopensea上で転売された時にクリエイターに還元される仕組みをとっているようです.
const xymMosaic = new xym.Mosaic(
currencyMosaicId,
xym.UInt64.fromUint(100 * 1000000)//送金量*10^(可分性)(xymは6)
);
const levyMosaic = new xym.Mosaic(
currencyMosaicId,
xym.UInt64.fromUint(10 * 1000000)//送金量*10^(可分性)(xymは6)
);
//代金支払い
const newBuyertobTX = xym.TransferTransaction.create(
xym.Deadline.create(epochAdjustment),
b_publicAccount.address,//受取人アドレス
[xymMosaic],//モザイク
xym.PlainMessage.create('BtoA'),//メッセージ
networkType,//ネットワークタイプ
);
//levy
const levyTx = xym.TransferTransaction.create(
xym.Deadline.create(epochAdjustment),
creator_address,//作者へ
[levyMosaic],//モザイク
xym.PlainMessage.create('levy'),//メッセージ
networkType,//ネットワークタイプ
);
//連署者変更
const multisigChange = xym.MultisigAccountModificationTransaction.create(
xym.Deadline.create(epochAdjustment),
0,
0,
[newBuyer_publicAccount.address],//取引先のアドレスを新たな連署者に
[b_publicAccount.address],//元の所有者を連署者から削除する
networkType,
);
const aggregateTx = xym.AggregateTransaction.createBonded(
xym.Deadline.create(epochAdjustment,48),//有効期限,アグリゲートボンデッドは2日後まで受け入れられる
[
newBuyertobTX.toAggregate(newbuyer_publicAccount),//代金支払い
multisigChange.toAggregate(nft_publicAccount),//nftのパブリックアカウント(公開鍵から作ったアカウント)
levyTx.toAggregate(newbuyer_publicAccount),//levy
],
networkType,
[],
).setMaxFeeForAggregate(100,1);
マルチシグの連署者になることでlevyを強制するのではなく,プラットフォームで販売する際の規約としてNFT転売の際にクリエイターにlevyとなる手数料を支払うトランザクションを混ぜることでもlevyは可能と思います.モザイクのNFTでも同様に可能です.
#おわりに
今回はマルチシグで,現在のSymbolの仕組みでもモザイク型NFTとほぼ同等のNFTがほぼ0XYMから発行できることを検証してみました.0XYMから発行できるのは魅力的だと思うのでぜひSymbolでNFTサービスを展開する際の発行手段の一つとして検討ください.個人的に時間があれば改良を行った上でまとめてライブラリとしてまとめてみたいです.