Restricción de transferencias de tokens

Limitar cómo las cuentas pueden realizar transacciones con restricciones de tokens.

Casos de uso

Supongamos que una empresa, CharlieChocolateFactory, desea hacerse pública tokenizando sus acciones y llevando a cabo una STO (Oferta de Valores Tokenizados). Crean un token llamado ccf.shares y lo configuran como restringible. Para cumplir con las regulaciones, la empresa quiere que solo los participantes que hayan pasado el proceso de KYC/AML puedan comprar y realizar transacciones con sus acciones.

Esta guía te muestra cómo utilizar la función de Bitxor Restricción de Tokens para definir reglas que determinen qué participantes pueden realizar transacciones con ccf.shares.

Prerrequisitos

  • Completa la guía creando un token.

  • Crea cuentas para CharlieChocolateFactory, Alice y Bob.

  • Carga la cuenta de CharlieChocolateFactory con suficiente bitxor para pagar las tarifas de transacción y la creación de tokens.

Creación de un token restringible

Antes de comenzar a trabajar con las Restricciones de Tokens, debemos haber creado un token restringible. Solo los tokens con la propiedad restrictable Propiedades del Token establecida en verdadero en el momento de su creación pueden aceptar restricciones de tokens.

  1. Comienza a crear el nuevo token restringible ccf.shares.

bitxor-cli transaction token --profile ccfactory --sync

¿Deseas que el token no expire? [s/n]: s
Ingresa la divisibilidad del token: 0
¿Deseas que el suministro del token sea mutable? [s/n]: s
¿Deseas que el token sea transferible? [s/n]: s
¿Deseas que el token sea restringible? [s/n]: s
Ingresa la cantidad de tokens: 1000
Transacción confirmada

El nuevo ID de token es: 634a8ac3fc2b65b3
  1. Luego, copia y guarda el identificador del token. Lo necesitaremos más adelante para definir restricciones.

Estableciendo una Restricción Global de Token

La empresa desea agregar una restricción para permitir únicamente que las cuentas con estados elevados interactúen con el activo. Para lograr esto, la empresa agregará una restricción global de token como {ccf.shares, KYC, EQ = 1}, lo que se puede interpretar como “solo permitir que las cuentas realicen transacciones con el token ccf.shares si su clave de restricción KYC tiene un valor igual a 1�?

../../_images/token-restriction-sto.png

Diagrama de caso de uso

  1. Abre un archivo nuevo y coloca el valor del identificador del token que obtuviste al crear el token en una variable llamada tokenId. Además, debes representar la clave KYC con un valor numérico codificado como UInt64.

// replace with token id
const tokenIdHex = '634a8ac3fc2b65b3';
const tokenId = new TokenId(tokenIdHex);
const key = KeyGenerator.generateUInt64Key('KYC'.toLowerCase());
// replace with token id
const tokenIdHex = '634a8ac3fc2b65b3';
const tokenId = new bitxor_sdk_1.TokenId(tokenIdHex);
const key = bitxor_sdk_1.KeyGenerator.generateUInt64Key('KYC'.toLowerCase());
  1. Luego, define una nueva Transacción de Restricción Global de Token. Pasa el identificador del token y las claves que definiste en el paso anterior como argumentos.

La SDK también solicitará el valor y tipo de restricción de token anterior para esta clave y token. Como es la primera restricción global que estamos anunciando, establece previousRestrictionValue en 0 y tokenRestrictionType en None.

// replace with network type
const networkType = NetworkType.TEST_NET;

const transaction = TokenGlobalRestrictionTransaction.create(
  Deadline.create(epochAdjustment),
  tokenId, // tokenId
  key, // restrictionKey
  UInt64.fromUint(0), // previousRestrictionValue
  TokenRestrictionType.NONE, // previousRestrictionType
  UInt64.fromUint(1), // newRestrictionValue
  TokenRestrictionType.EQ, // newRestrictionType
  networkType,
  undefined,
  UInt64.fromUint(2000000),
);
// replace with network type
const networkType = bitxor_sdk_1.NetworkType.TEST_NET;
const transaction = bitxor_sdk_1.TokenGlobalRestrictionTransaction.create(
  bitxor_sdk_1.Deadline.create(epochAdjustment),
  tokenId, // tokenId
  key, // restrictionKey
  bitxor_sdk_1.UInt64.fromUint(0), // previousRestrictionValue
  bitxor_sdk_1.TokenRestrictionType.NONE, // previousRestrictionType
  bitxor_sdk_1.UInt64.fromUint(1), // newRestrictionValue
  bitxor_sdk_1.TokenRestrictionType.EQ, // newRestrictionType
  networkType,
  undefined,
  bitxor_sdk_1.UInt64.fromUint(2000000),
);
  1. Después de definir la restricción global, firma la transacción con la cuenta del creador del token, CharlieChocolateFactory, y anúnciala a la red.

// 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 = bitxor_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 bitxor_sdk_1.RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();
transactionHttp.announce(signedTransaction).subscribe(
  (x) => console.log(x),
  (err) => console.error(err),
);

Asignando Restricciones de Direcciones de Token

Cuando los inversionistas completen el proceso de KYC/AML, CharlieChocolateFactory modificará sus cuentas con una Transacción de Restricción de Dirección de Token con los parámetros ccf.shares, KYC, 1, lo que permitirá que los inversionistas certificados participen en la STO. Otros que no hayan proporcionado la información necesaria no podrán recibir ni comerciar el activo.

Alice, una posible inversionista, pasa el proceso de KYC. Una vez que Alice ha sido verificada, la empresa etiqueta la cuenta de Alice con la restricción de dirección de token {ccf.shares, Alice, KYC, 1}. Por otro lado, Bob, otro inversionista interesado, no pasa el proceso de KYC. La cuenta de Bob no es elegible para recibir ccf.shares ya que no cumple con los requisitos de restricción global de token. Sin embargo, CharlieChocolateFactory decide etiquetar la cuenta con la restricción de dirección de token {ccf.shares, Bob, KYC, 0}.

resources/images/examples/token-restriction-sto-address.png

Diagrama de caso de uso

  1. Abre un archivo nuevo y coloca el valor del identificador del token en una variable llamada tokenId.

  1. Luego, define una nueva Transacción de Restricción de Dirección de Token para la cuenta de Alice y Bob, y asigna las restricciones de dirección de token correspondientes.

  1. Después de definir las restricciones de dirección de token, firma la transacción con la cuenta del creador del token, CharlieChocolateFactory, y anúnciala a la red.

Asignación de restricciones de dirección de token

Cuando los inversores completan el proceso KYC/AML, CharlieChocolateFactory modifica sus cuentas con una TokenAddressRestrictionTransaction con los parámetros ccf.shares, KYC, 1, lo que permite a los inversores certificados participar en la STO. Los demás que no hayan proporcionado la información necesaria no podrán recibir ni comercializar el activo.

Alice, una inversora potencial, pasa el proceso KYC. Una vez que se ha verificado a Alice, la empresa marca la cuenta de Alice con la restricción de dirección de token {ccf.shares, Alice, KYC, 1}. Por otro lado, Bob, otro inversor interesado, no pasa el proceso KYC. La cuenta de Bob no cumple con los requisitos de restricción global del token y, por lo tanto, no es elegible para recibir ccf.shares. Sin embargo, CharlieCholocalteFatory decide marcar la cuenta con la restricción de dirección de token {ccf.shares, Bob, KYC, 0}. De esta manera, saben que Bob ha intentado y ha fallado el proceso KYC.

  1. Define las transacciones TokenAddressRestrictionTransaction para las cuentas de Alice y Bob de la siguiente manera:

  • Alice: {ccf.shares, Alice, KYC, 1}

  • Bob: {ccf.shares, Bob, KYC, 0}

// replace with network type
const networkType = NetworkType.TEST_NET;
// replace with token id
const tokenIdHex = '634a8ac3fc2b65b3';
const tokenId = new TokenId(tokenIdHex);
// replace with address
const aliceRawAddress = 'BXRBDE-NCLKEB-ILBPWP-3JPB2X-NY64OE-7PYHHE-32I';
const aliceAddress = Address.createFromRawAddress(aliceRawAddress);
// replace with address
const bobRawAddress = 'BXRQ5E-YACWBP-CXKGIL-I6XWCH-DRFLTB-KUK34I-YJQ';
const bobAddress = Address.createFromRawAddress(bobRawAddress);

const key = KeyGenerator.generateUInt64Key('KYC'.toLowerCase());

const aliceTokenAddressRestrictionTransaction = TokenAddressRestrictionTransaction.create(
  Deadline.create(epochAdjustment),
  tokenId, // tokenId
  key, // restrictionKey
  aliceAddress, // address
  UInt64.fromUint(1), // newRestrictionValue
  networkType,
);

const bobTokenAddressRestrictionTransaction = TokenAddressRestrictionTransaction.create(
  Deadline.create(epochAdjustment),
  tokenId, // tokenId
  key, // restictionKey
  bobAddress, // address
  UInt64.fromUint(0), // newRestrictionValue
  networkType,
);
// replace with network type
const networkType = bitxor_sdk_1.NetworkType.TEST_NET;
// replace with token id
const tokenIdHex = '634a8ac3fc2b65b3';
const tokenId = new bitxor_sdk_1.TokenId(tokenIdHex);
// replace with address
const aliceRawAddress = 'BXRBDE-NCLKEB-ILBPWP-3JPB2X-NY64OE-7PYHHE-32I';
const aliceAddress = bitxor_sdk_1.Address.createFromRawAddress(aliceRawAddress);
// replace with address
const bobRawAddress = 'BXRQ5E-YACWBP-CXKGIL-I6XWCH-DRFLTB-KUK34I-YJQ';
const bobAddress = bitxor_sdk_1.Address.createFromRawAddress(bobRawAddress);
const key = bitxor_sdk_1.KeyGenerator.generateUInt64Key('KYC'.toLowerCase());
const aliceTokenAddressRestrictionTransaction = bitxor_sdk_1.TokenAddressRestrictionTransaction.create(
    bitxor_sdk_1.Deadline.create(epochAdjustment),
    tokenId, // tokenId
    key, // restrictionKey
    aliceAddress, // address
    bitxor_sdk_1.UInt64.fromUint(1), // newRestrictionValue
    networkType,
);
const bobTokenAddressRestrictionTransaction = bitxor_sdk_1.TokenAddressRestrictionTransaction.create(
    bitxor_sdk_1.Deadline.create(epochAdjustment),
    tokenId, // tokenId
    key, // restictionKey
    bobAddress, // address
    bitxor_sdk_1.UInt64.fromUint(0), // newRestrictionValue
    networkType,
);
  1. Ahora puedes anunciar las transacciones a la red. Para hacerlo, intenta anunciar ambas transacciones juntas utilizando una transacción agregada. Recuerda que tendrás que anunciar las transacciones desde la cuenta creadora del token.

// replace with company private key
const privateKey =
  'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
const account = Account.createFromPrivateKey(privateKey, networkType);

const aggregateTransaction = AggregateTransaction.createComplete(
  Deadline.create(epochAdjustment),
  [
    aliceTokenAddressRestrictionTransaction.toAggregate(account.publicAccount),
    bobTokenAddressRestrictionTransaction.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 = bitxor_sdk_1.Account.createFromPrivateKey(
    privateKey,
    networkType,
);
const aggregateTransaction = bitxor_sdk_1.AggregateTransaction.createComplete(
    bitxor_sdk_1.Deadline.create(epochAdjustment), [
        aliceTokenAddressRestrictionTransaction.toAggregate(account.publicAccount),
        bobTokenAddressRestrictionTransaction.toAggregate(account.publicAccount),
    ],
    networkType, [],
    bitxor_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 bitxor_sdk_1.RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();
transactionHttp.announce(signedTransaction).subscribe(
    (x) => console.log(x),
    (err) => console.error(err),
);
  1. Una vez que la transacción se haya confirmado, intenta enviar tokens a las cuentas de Alice y Bob.

Ahora deberías poder enviar ccf.shares a Alice sin problemas. Además, Alice podrá transferir tokens con otras cuentas que tengan restricciones establecidas en {ccf.shares, KYC, 1}.

bitxor-cli transaction transfer --recipient-address BXRBDE-NCLKEB-ILBPWP-3JPB2X-NY64OE-7PYHHE-32I --tokens 634a8ac3fc2b65b3::1 --sync

Sin embargo, cuando envíes el mismo token a la cuenta de Bob, deberías recibir el error Failure_RestrictionToken_Account_Unauthorized a través del canal de error de estado porque no se le permite realizar transacciones con ccf.shares.