Consideraciones sobre el diseño de activos
Cuentas de emisión y distribución
Es práctica recomendada en la red Stellar crear dos cuentas al emitir un activo: 1) la cuenta de emisión y 2) la cuenta de distribución.
La cuenta de emisión crea (o emite) el activo en la red ejecutando una operación de pago. La cuenta de emisión siempre estará vinculada a la identidad del activo. Cualquier cuenta que quiera mantener el activo debe primero establecer una línea de confianza con la cuenta de emisión. Lee sobre las líneas de confianza en nuestra sección de líneas de confianza.
La cuenta de distribución es el primer destinatario del activo emitido y maneja todas las demás transacciones.
Ten en cuenta que también puedes emitir un activo creando una oferta o un depósito de fondos de liquidez con la cuenta de emisión.
Es práctica recomendada emitir un activo enviándolo desde la cuenta de emisión a una cuenta de distribución por dos razones principales: seguridad y auditoría.
Seguridad
La cuenta de distribución será una cuenta activa, lo que significa que algún servicio web tiene acceso directo para firmar sus transacciones.
Por ejemplo, si la cuenta desde la que distribuyes también es la cuenta de emisión y es comprometida por un actor malicioso, el actor puede ahora emitir tanto del activo como desee. Si el actor malicioso canjea los tokens recién emitidos con un servicio anchor, es posible que el anchor no tenga la liquidez para admitir las retiradas de fondos de los clientes. El riesgo es menor si usas una cuenta de distribución: si la cuenta de distribución se ve comprometida, puedes congelar el saldo de activos de la cuenta y comenzar con una nueva cuenta de distribución sin cambiar la cuenta de emisión.
Auditoría
Usar una cuenta de distribución es mejor para la auditoría porque una cuenta de emisión no puede mantener un saldo de su propio activo. Enviar un activo de vuelta a su cuenta de emisión quema (elimina) el activo. Si tienes un inventario de los activos emitidos en una cuenta separada, es más fácil de rastrear y puede ayudar con la contabilidad.
Nombrar un activo
Una de las cosas que debes decidir al emitir un activo es cómo llamarlo. Un código de activo es el código identificador del activo. Hay tres formatos posibles: Alfanumérico 4, Alfanumérico 12 y participaciones de fondos de liquidez.
Conoce sobre las participaciones de fondos de liquidez en la Entrada de Enciclopedia de Fondos de Liquidez.
- Máximo de 4 caracteres alfanuméricos: Se permiten cualquier carácter del conjunto a-z, A-Z, 0-9. El código puede ser más corto que 4 caracteres, pero los caracteres finales deben estar vacíos.
- Máximo de 12 caracteres alfanuméricos: Se permiten cualquier carácter del conjunto a-z, A-Z, 0-9. El código puede tener cualquier cantidad de caracteres de 5 a 12, pero los caracteres finales deben estar vacíos.
- El activo de participación de fondo se define por el identificador del fondo de liquidez (PoolID), que a su vez se define por los dos activos de los que se componen sus reservas.
Siempre que se encuentre en alguna de estas categorías, puedes elegir cualquier código de activo que desees. Dicho esto, si estás emitiendo una moneda, deberías usar el código ISO 4217 apropiado, y si estás emitiendo una acción o un bono, el número ISIN apropiado. Hacer esto facilita que las interfaces de Stellar muestren y ordenen correctamente tu token en sus listados y permite a los potenciales titulares del token entender qué representa tu token.
Controlar el acceso a un activo con flags
Cuando emites un activo en Stellar, cualquiera puede mantenerlo por defecto. En general, eso es algo bueno: el acceso fácil significa mejor alcance y mejor liquidez. Sin embargo, si necesitas controlar el acceso a un activo para cumplir con regulaciones (o por cualquier otra razón), puedes hacerlo fácilmente habilitando flags en tu cuenta de emisión.
Los flags se crean a nivel de cuenta utilizando una operación set_options
. Se pueden establecer en cualquier momento del ciclo de vida de un activo, no solo cuando lo emites.
Tipos de flags
El (0xn) junto a cada tipo de flag denota la configuración de bits para cada flag.
Autorización Requerida (0x1)
Cuando AUTH_REQUIRED_FLAG
está habilitado, un emisor debe aprobar una cuenta antes de que esa cuenta pueda mantener su activo. Esta configuración permite a los emisores evaluar a los potenciales titulares de tokens y aprobar líneas de confianza.
Para permitir el acceso, el usuario crea una línea de confianza, y el emisor la aprueba cambiando el flag AUTHORIZE
con la operación Set_Trust_Line_Flag
.
Hay dos niveles de autorización que un emisor de activos puede otorgar utilizando la operación Set_Trust_Line_Flag
:
AUTHORIZED_FLAG
: significa autorización completa que permite a una cuenta transaccionar libremente con el activo para hacer y recibir pagos, realizar pedidos y depositar en un fondo de liquidez.AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG
: denota autorización limitada que permite a una cuenta mantener órdenes actuales, retirar de un fondo de liquidez o cancelar órdenes actuales - pero no transaccionar de otra manera con el activo.
Autorización Revocable (0x2)
Cuando AUTH_REVOCABLE_FLAG
está habilitado, un emisor puede revocar la autorización de una línea de confianza existente, congelando así el activo mantenido por una cuenta. Hacer esto evita que esa cuenta transfiera o comercie el activo y cancela las órdenes abiertas de la cuenta para el activo.
AUTH_REVOCABLE_FLAG
también permite al emisor reducir la autorización de completa a limitada, lo que impide que la cuenta transfiera o comercie el activo, pero no cancela las órdenes abiertas de la cuenta para el activo. Esta configuración es útil para emisores de activos regulados que necesitan autorizar transacciones caso por caso para asegurar que cada una cumpla con ciertos requisitos.
Todos los cambios en la autorización de activos se realizan con la operación Set Trustline Flags.
Hay tres niveles de autorización que un emisor de activos puede eliminar utilizando la operación Set_Trust_Line_Flag
:
AUTHORIZED_FLAG
: significa autorización completa que permite a una cuenta transaccionar libremente con el activo para hacer y recibir pagos y realizar pedidos.AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG
: denota autorización limitada que permite a una cuenta mantener órdenes actuales, pero no transaccionar de otra manera con el activo.CLAWBACK_ENABLED
: habilita a la cuenta de emisión para recuperar (quemar) todos los activos. Consulta nuestra sección sobre Clawbacks para más información.
Clawback Habilitado (0x8)
Con la flag AUTH_CLAWBACK_ENABLED_FLAG
establecida, cualquier línea de confianza subsecuente establecida con esta cuenta tendrá habilitados los clawbacks. Puedes leer más sobre clawbacks (y controlarlos de manera selectiva por cada línea de confianza) aquí.
Ten en cuenta que esta flag requiere que la revocación también esté establecida.
Autorización Inmutable (0x4)
Con esta configuración, ninguno de los otros flags de autorización (AUTH_REQUIRED_FLAG
, AUTH_REVOCABLE_FLAG
) puede ser establecido, y la cuenta de emisión no puede ser fusionada. Estableces esta flag para indicar a los potenciales titulares de tokens que tu cuenta de emisión y sus activos persistirán en el ledger en un estado abierto y accesible.
Operación Set Trustline Flag
La cuenta de emisión puede configurar varios flags de autorización y líneas de confianza para las líneas de confianza individuales a un activo. El parámetro de activo es del tipo TrustLineAsset. Si estás modificando una línea de confianza a un activo regular (es decir, uno en formato Code:Issuer), esto es equivalente al tipo de activo. Si estás modificando una línea de confianza a una participación de fondo, esta es la identificación única del fondo de liquidez.
Flujo de ejemplo
Veamos cómo un emisor de un activo regulado podría usar la flag AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG
.
Si el emisor desea aprobar transacciones caso por caso mientras permite que las cuentas mantengan ofertas, puede dejar una cuenta en el estado AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG
. Esa cuenta puede tener ofertas pero no puede hacer nada más con el activo.
Para iniciar una nueva operación, la cuenta mantenedora solicita que el emisor apruebe y firme una transacción. Una vez que el emisor inspecciona la operación y decide aprobarla, la coloca entre un conjunto de operaciones, primero otorgando autorización, luego reduciéndola.
Aquí hay un pago de A a B colocado entre operaciones set_trust_line_flags
:
- Operación 1: El emisor utiliza
SetTrustLineFlags
para autorizar completamente la cuenta A, activo X - Operación 2: El emisor utiliza
SetTrustLineFlags
para autorizar completamente la cuenta B, activo X - Operación 3: Pago de A a B
- Operación 4: El emisor utiliza
SetTrustLineFlags
para establecer la cuenta B, activo X en estadoAUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG
- Operación 5: El emisor utiliza
SetTrustLineFlags
para establecer la cuenta A, activo X en estadoAUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG
El sándwich de autorización permite al emisor inspeccionar el pago específico y otorgar autorización solo para ello. Dado que las operaciones agrupadas en una transacción son simultáneas, A y B solo están autorizados para la operación de pago específica y preaprobada. La autorización completa no se extiende más allá de la transacción específica.
Código de muestra
En los siguientes ejemplos de código, se omite la verificación de errores adecuada. Sin embargo, siempre debes validar tus resultados, ya que hay muchas formas en que las solicitudes pueden fallar. Consulta la página de Manejo de Errores para consejos sobre estrategias de gestión de errores.
El siguiente ejemplo establece que la autorización sea tanto requerida como revocable:
- JavaScript
- Java
- Python
var StellarSdk = require("stellar-sdk");
var server = new StellarSdk.Horizon.Server(
"https://horizon-testnet.stellar.org",
);
// Keys for issuing account
var issuingKeys = StellarSdk.Keypair.fromSecret(
"SCZANGBA5YHTNYVVV4C3U252E2B6P6F5T3U6MM63WBSBZATAQI3EBTQ4",
);
server
.loadAccount(issuingKeys.publicKey())
.then(function (issuer) {
var transaction = new StellarSdk.TransactionBuilder(issuer, {
fee: 100,
networkPassphrase: StellarSdk.Networks.TESTNET,
})
.addOperation(
StellarSdk.Operation.setOptions({
setFlags: StellarSdk.AuthRevocableFlag | StellarSdk.AuthRequiredFlag,
}),
)
// setTimeout is required for a transaction
.setTimeout(100)
.build();
transaction.sign(issuingKeys);
return server.submitTransaction(transaction);
})
.then(console.log)
.catch(function (error) {
console.error("Error!", error);
});
import org.stellar.sdk.*;
import org.stellar.sdk.Network;
import org.stellar.sdk.responses.AccountResponse;
Server server = new Server("https://horizon-testnet.stellar.org");
// Keys for issuing account
KeyPair issuingKeys = KeyPair
.fromSecretSeed("SCZANGBA5YHTNYVVV4C3U252E2B6P6F5T3U6MM63WBSBZATAQI3EBTQ4");
AccountResponse sourceAccount = server.accounts().account(issuingKeys.getAccountId());
Transaction setAuthorization = new Transaction.Builder(sourceAccount, Network.TESTNET)
.addOperation(new SetOptionsOperation.Builder()
.setSetFlags(
AccountFlag.AUTH_REQUIRED_FLAG.getValue() |
AccountFlag.AUTH_REVOCABLE_FLAG.getValue())
.build())
.build();
setAuthorization.sign(issuingKeys);
server.submitTransaction(setAuthorization);
from stellar_sdk import Keypair, Network, Server, TransactionBuilder, AuthorizationFlag
from stellar_sdk.exceptions import BaseHorizonError
# Configure Stellar SDK to talk to the horizon instance hosted by Stellar.org
# To use the live network, set the hostname to horizon_url for mainnet
server = Server(horizon_url="https://horizon-testnet.stellar.org")
# Use the test network, if you want to use the live network, please set it to `Network.PUBLIC_NETWORK_PASSPHRASE`
network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE
# Keys for accounts to issue and receive the new asset
issuing_keypair = Keypair.from_secret(
"SCZANGBA5YHTNYVVV4C3U252E2B6P6F5T3U6MM63WBSBZATAQI3EBTQ4"
)
issuing_public = issuing_keypair.public_key
# Transactions require a valid sequence number that is specific to this account.
# We can fetch the current sequence number for the source account from Horizon.
issuing_account = server.load_account(issuing_public)
transaction = (
TransactionBuilder(
source_account=issuing_account,
network_passphrase=network_passphrase,
base_fee=100,
)
.append_set_options_op(
set_flags=AuthorizationFlag.AUTH_REVOCABLE_FLAG | AuthorizationFlag.AUTHORIZATION_REQUIRED
)
.build()
)
transaction.sign(issuing_keypair)
try:
transaction_resp = server.submit_transaction(transaction)
print(f"Transaction Resp:\n{transaction_resp}")
except BaseHorizonError as e:
print(f"Error: {e}")
Limitar el suministro de un activo
Esta sección detalla cómo bloquear tu cuenta con el propósito de limitar el suministro de tu activo emitido. Sin embargo, bloquear tu cuenta significa que nunca podrás hacer nada con ella de nuevo: ya sea ajustar firmantes, cambiar el dominio principal, reclamar cualquier XLM mantenido, o cualquier otra operación. Tu cuenta estará completamente congelada.
Teniendo en cuenta esa advertencia, es posible bloquear la cuenta de emisión de un activo para que el suministro del activo no pueda aumentar. Para hacer esto, primero establece el peso maestro de la cuenta de emisión a 0 utilizando la operación Set Options. Esto evita que la cuenta de emisión pueda firmar transacciones y, por lo tanto, incapacita al emisor para emitir más activos. Asegúrate de hacer esto solo después de haber emitido todos los activos deseados a la cuenta de distribución. Si el activo tiene un Contrato de Activos Stellar, también asegúrate de que el administrador del contrato no haya sido actualizado desde el valor predeterminado (que es el emisor) utilizando la llamada de contrato set_admin
. Si el administrador no era el emisor, entonces el administrador podría emitir el activo incluso con la cuenta de emisión bloqueada.
Conoce más sobre los pesos de firmas en la Entrada de Enciclopedia sobre Firmas y Multisig.
Consulta cómo hacerlo en los pasos opcionales del Tutorial sobre Emisión de un Activo.