モザイク制限でアカウントが処理できる手段を制限します。
たとえば、CharlieChocolateFactory という会社が株式をトークン化して STO を実施して、公開したいとします。モザイク ccf.shares
を作成して 制限可能 に設定します。規制を順守するために、会社は KYC/AML プロセスに合格した参加者のみが株式を購入して、取引することを望んでいます。
このガイドでは、Symbol の モザイク制限 機能を使用して ccf.shares
を取引できる参加者を決定するルールを定義する方法を示します。
Mosaic Restrictions を使用する前に、制限可能なモザイクを作成する必要があります。モザイク制限を受け入れることができるのは、作成時に restrictable
プロパティ が true に設定されているモザイクのみです。
Symbol CLI によって CharlieChocolateFactory アカウント を使用して、新しい制限可能なモザイク ccf.shares
の作成を開始します
symbol-cli transaction mosaic --profile ccfactory --sync
Do you want a non-expiring mosaic? [y/n]: y
Enter mosaic divisibility: 0
Do you want mosaic to have supply mutable? [y/n]: y
Do you want mosaic to be transferable? [y/n]: y
Do you want mosaic to be restrictable? [y/n]: y
Enter amount of tokens: 1000
Transaction confirmed
The new mosaic id is: 634a8ac3fc2b65b3
次に、モザイク識別子をコピーして保存します。制限を定義するときに必要です。
会社はステータスを昇格したアカウントだけがアセットをやり取りできるように制限を追加したいと考えています。これを実現するためには、会社はモザイクのグローバル制限として {ccf.shares, KYC, EQ = 1}
を追加します。これは "KYC
の制限キーの値が 1 である場合に、アカウントへ ccf.share
のやり取りを許可する" と解釈します。
新しいファイルを作成し、モザイクの作成時に取得したモザイク識別子の値を mosaicId
という名前の変数に入れます。また UInt64 としてエンコードされた数値でキー `` KYC`` を表現する必要があります。
// replace with mosaic id
const mosaicIdHex = '634a8ac3fc2b65b3';
const mosaicId = new MosaicId(mosaicIdHex);
const key = KeyGenerator.generateUInt64Key('KYC'.toLowerCase());
// replace with mosaic id
const mosaicIdHex = '634a8ac3fc2b65b3';
const mosaicId = new symbol_sdk_1.MosaicId(mosaicIdHex);
const key = symbol_sdk_1.KeyGenerator.generateUInt64Key('KYC'.toLowerCase());
次に、新しい MosaicGlobalRestrictionTransaction を定義します。前の手順で定義した mosaic id とキーを引数として渡します。
SDK はこのキーとモザイクの以前のモザイク制限値とタイプも要求します。最初のグローバル制限のアナウンスなので previousRestrictionValue
を 0
に設定し mosaicRestrictionType
を None
に設定します。
// replace with network type
const networkType = NetworkType.TEST_NET;
const transaction = MosaicGlobalRestrictionTransaction.create(
Deadline.create(epochAdjustment),
mosaicId, // mosaicId
key, // restrictionKey
UInt64.fromUint(0), // previousRestrictionValue
MosaicRestrictionType.NONE, // previousRestrictionType
UInt64.fromUint(1), // newRestrictionValue
MosaicRestrictionType.EQ, // newRestrictionType
networkType,
undefined,
UInt64.fromUint(2000000),
);
// replace with network type
const networkType = symbol_sdk_1.NetworkType.TEST_NET;
const transaction = symbol_sdk_1.MosaicGlobalRestrictionTransaction.create(
symbol_sdk_1.Deadline.create(epochAdjustment),
mosaicId, // mosaicId
key, // restrictionKey
symbol_sdk_1.UInt64.fromUint(0), // previousRestrictionValue
symbol_sdk_1.MosaicRestrictionType.NONE, // previousRestrictionType
symbol_sdk_1.UInt64.fromUint(1), // newRestrictionValue
symbol_sdk_1.MosaicRestrictionType.EQ, // newRestrictionType
networkType,
undefined,
symbol_sdk_1.UInt64.fromUint(2000000),
);
グローバル制限を定義したら、モザイク作成者のアカウント—CharlieChocolateFactory—でトランザクションに署名してネットワークにアナウンスします。
// replace with company private key
const privateKey =
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
const account = Account.createFromPrivateKey(privateKey, networkType);
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash =
'1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransaction = account.sign(transaction, networkGenerationHash);
// replace with node endpoint
const nodeUrl = 'NODE_URL';
const repositoryFactory = new RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();
transactionHttp.announce(signedTransaction).subscribe(
(x) => console.log(x),
(err) => console.error(err),
);
// replace with company private key
const privateKey =
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
const account = symbol_sdk_1.Account.createFromPrivateKey(
privateKey,
networkType,
);
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash =
'1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransaction = account.sign(transaction, networkGenerationHash);
// replace with node endpoint
const nodeUrl = 'NODE_URL';
const repositoryFactory = new symbol_sdk_1.RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();
transactionHttp.announce(signedTransaction).subscribe(
(x) => console.log(x),
(err) => console.error(err),
);
投資家が KYC/AML 手続きを完了すると CharlieChocolateFactory は、パタメータ ccf.shares, KYC, 1
を持つ MosaicAddressRestrictionTransaction で彼らのアカウントを変更し、認定投資家として STO へ参加できるようにします。必要な情報を提供していない他の人はアセットを受け取ったり取引したりできません。
潜在的な投資家である Alice は KYC 手続きに合格しました。Alice が承認されると、同社は Alice のアカウントにモザイクアドレス制限 {ccf.shares, Alice, KYC, 1}
をタグ付けします。一方で、もう一人の興味を持っている投資家の Bob は KYC 手続きに合格しませんでした。Bob のアカウントはモザイクのグローバル制限による要件を満たしていないため ccf.shares
を受け取る資格がありません。それでも CharlieCholocalteFatory は モザイクアドレス制限 {ccf.shares, Bob, KYC, 0}
でアカウントにタグを付けることにしました。そうすることで Bob が KYC 手続きを試みて、失敗したことを知ることができます。
Alice と Bob のアカウントの両方の MosaicAddressRestrictionTransaction を次のように定義します:
Alice: {ccf.shares, Alice, KYC, 1}
Bob: {ccf.shares, Bob, KYC, 0}
// replace with network type
const networkType = NetworkType.TEST_NET;
// replace with mosaic id
const mosaicIdHex = '634a8ac3fc2b65b3';
const mosaicId = new MosaicId(mosaicIdHex);
// replace with address
const aliceRawAddress = 'TCHBDE-NCLKEB-ILBPWP-3JPB2X-NY64OE-7PYHHE-32I';
const aliceAddress = Address.createFromRawAddress(aliceRawAddress);
// replace with address
const bobRawAddress = 'TB6Q5E-YACWBP-CXKGIL-I6XWCH-DRFLTB-KUK34I-YJQ';
const bobAddress = Address.createFromRawAddress(bobRawAddress);
const key = KeyGenerator.generateUInt64Key('KYC'.toLowerCase());
const aliceMosaicAddressRestrictionTransaction = MosaicAddressRestrictionTransaction.create(
Deadline.create(epochAdjustment),
mosaicId, // mosaicId
key, // restrictionKey
aliceAddress, // address
UInt64.fromUint(1), // newRestrictionValue
networkType,
);
const bobMosaicAddressRestrictionTransaction = MosaicAddressRestrictionTransaction.create(
Deadline.create(epochAdjustment),
mosaicId, // mosaicId
key, // restictionKey
bobAddress, // address
UInt64.fromUint(0), // newRestrictionValue
networkType,
);
// replace with network type
const networkType = symbol_sdk_1.NetworkType.TEST_NET;
// replace with mosaic id
const mosaicIdHex = '634a8ac3fc2b65b3';
const mosaicId = new symbol_sdk_1.MosaicId(mosaicIdHex);
// replace with address
const aliceRawAddress = 'TCHBDE-NCLKEB-ILBPWP-3JPB2X-NY64OE-7PYHHE-32I';
const aliceAddress = symbol_sdk_1.Address.createFromRawAddress(aliceRawAddress);
// replace with address
const bobRawAddress = 'TB6Q5E-YACWBP-CXKGIL-I6XWCH-DRFLTB-KUK34I-YJQ';
const bobAddress = symbol_sdk_1.Address.createFromRawAddress(bobRawAddress);
const key = symbol_sdk_1.KeyGenerator.generateUInt64Key('KYC'.toLowerCase());
const aliceMosaicAddressRestrictionTransaction = symbol_sdk_1.MosaicAddressRestrictionTransaction.create(
symbol_sdk_1.Deadline.create(epochAdjustment),
mosaicId, // mosaicId
key, // restrictionKey
aliceAddress, // address
symbol_sdk_1.UInt64.fromUint(1), // newRestrictionValue
networkType,
);
const bobMosaicAddressRestrictionTransaction = symbol_sdk_1.MosaicAddressRestrictionTransaction.create(
symbol_sdk_1.Deadline.create(epochAdjustment),
mosaicId, // mosaicId
key, // restictionKey
bobAddress, // address
symbol_sdk_1.UInt64.fromUint(0), // newRestrictionValue
networkType,
);
これでトランザクションをネットワークにアナウンスできます。これを行うために アグリゲートトランザクション を使用して両方のトランザクションをまとめてアナウンスしてみてください。モザイクの作成者アカウントからトランザクションを発表する必要があることは気に留めておいてください。
// replace with company private key
const privateKey =
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
const account = Account.createFromPrivateKey(privateKey, networkType);
const aggregateTransaction = AggregateTransaction.createComplete(
Deadline.create(epochAdjustment),
[
aliceMosaicAddressRestrictionTransaction.toAggregate(account.publicAccount),
bobMosaicAddressRestrictionTransaction.toAggregate(account.publicAccount),
],
networkType,
[],
UInt64.fromUint(2000000),
);
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash =
'1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransaction = account.sign(
aggregateTransaction,
networkGenerationHash,
);
console.log(signedTransaction.hash);
// replace with node endpoint
const nodeUrl = 'NODE_URL';
const repositoryFactory = new RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();
transactionHttp.announce(signedTransaction).subscribe(
(x) => console.log(x),
(err) => console.error(err),
);
// replace with company private key
const privateKey =
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
const account = symbol_sdk_1.Account.createFromPrivateKey(
privateKey,
networkType,
);
const aggregateTransaction = symbol_sdk_1.AggregateTransaction.createComplete(
symbol_sdk_1.Deadline.create(epochAdjustment),
[
aliceMosaicAddressRestrictionTransaction.toAggregate(account.publicAccount),
bobMosaicAddressRestrictionTransaction.toAggregate(account.publicAccount),
],
networkType,
[],
symbol_sdk_1.UInt64.fromUint(2000000),
);
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash =
'1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransaction = account.sign(
aggregateTransaction,
networkGenerationHash,
);
console.log(signedTransaction.hash);
// replace with node endpoint
const nodeUrl = 'NODE_URL';
const repositoryFactory = new symbol_sdk_1.RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();
transactionHttp.announce(signedTransaction).subscribe(
(x) => console.log(x),
(err) => console.error(err),
);
トランザクションが承認されたら、Alice と Bob のアカウントにモザイクを送信してみてください。
何の問題もなく Alice に ccf.shares
を送ることができるはずです。さらに Alice は {ccf.shares, KYC, 1}
に設定された制限を持つ、他のアカウントとモザイクを転送できます。
symbol-cli transaction transfer --recipient-address TCHBDE-NCLKEB-ILBPWP-3JPB2X-NY64OE-7PYHHE-32I --mosaics 634a8ac3fc2b65b3::1 --sync
ただし Bob のアカウントに同じモザイクを送信する場合は ccf.shares
の取引が許可されていないため、ステータスエラーチャンネルを通じて Failure_RestrictionMosaic_Account_Unauthorized
エラーが表示されます。