Limitar cómo las cuentas pueden realizar transacciones con restricciones de tokens.
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
.
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.
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.
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
Luego, copia y guarda el identificador del token. Lo necesitaremos más adelante para definir restricciones.
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�?
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());
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),
);
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),
);
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}
.
Abre un archivo nuevo y coloca el valor del identificador del token en una variable llamada tokenId
.
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.
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.
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.
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,
);
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),
);
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
.