モザイク転送の制限

モザイク制限でアカウントが処理できる手段を制限します。

ユースケース

たとえば、CharlieChocolateFactory という会社が株式をトークン化して STO を実施して、公開したいとします。モザイク ccf.shares を作成して 制限可能 に設定します。規制を順守するために、会社は KYC/AML プロセスに合格した参加者のみが株式を購入して、取引することを望んでいます。

このガイドでは、Symbol の モザイク制限 機能を使用して ccf.shares を取引できる参加者を決定するルールを定義する方法を示します。

前提条件

  • モザイクの作成 ガイドを完了している
  • CharlieChocolateFactory と Alice と Bob を表現するための アカウント を作成します。
  • CharlieChocolateFactory のアカウントに手数料の支払いとモザイクの作成に十分な symbol.xym を入金してください。

モザイク制限の作成

Mosaic Restrictions を使用する前に、制限可能なモザイクを作成する必要があります。モザイク制限を受け入れることができるのは、作成時に restrictable プロパティ が true に設定されているモザイクのみです。

  1. 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
  1. 次に、モザイク識別子をコピーして保存します。制限を定義するときに必要です。

グローバルモザイク制限の設定

会社はステータスを昇格したアカウントだけがアセットをやり取りできるように制限を追加したいと考えています。これを実現するためには、会社はモザイクのグローバル制限として {ccf.shares, KYC, EQ = 1} を追加します。これは 「KYC の制限キーの値が 1 である場合に、アカウントへ ccf.share のやり取りを許可する」 と解釈します。

../../_images/mosaic-restriction-sto.png

ユースケース図

  1. 新しいファイルを作成し、モザイクの作成時に取得したモザイク識別子の値を 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());
  1. 次に、新しい MosaicGlobalRestrictionTransaction を定義します。前の手順で定義した mosaic id とキーを引数として渡します。

SDK はこのキーとモザイクの以前のモザイク制限値とタイプも要求します。最初のグローバル制限のアナウンスなので previousRestrictionValue0 に設定し mosaicRestrictionTypeNone に設定します。

// 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),
);
  1. グローバル制限を定義したら、モザイク作成者のアカウント—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 = 'http://api-01.us-east-1.testnet.symboldev.network:3000';
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 = 'http://api-01.us-east-1.testnet.symboldev.network:3000';
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 手続きを試みて、失敗したことを知ることができます。

  1. 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,
);
  1. これでトランザクションをネットワークにアナウンスできます。これを行うために アグリゲートトランザクション を使用して両方のトランザクションをまとめてアナウンスしてみてください。モザイクの作成者アカウントからトランザクションを発表する必要があることは気に留めておいてください。
// 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 = 'http://api-01.us-east-1.testnet.symboldev.network:3000';
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 = 'http://api-01.us-east-1.testnet.symboldev.network:3000';
const repositoryFactory = new symbol_sdk_1.RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();
transactionHttp.announce(signedTransaction).subscribe(
  (x) => console.log(x),
  (err) => console.error(err),
);
  1. トランザクションが承認されたら、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 エラーが表示されます。