Firmar automáticamente transacciones agregadas enlazadas

Crea un bot para firmar automáticamente transacciones que requieren la firma de tu cuenta.

Requisitos previos

Desarrollando el bot

  1. Crea una función para firmar cualquier AggregateBondedTransaction.

const cosignAggregateBondedTransaction = (
  transaction: AggregateTransaction,
  account: Account,
): CosignatureSignedTransaction => {
  const cosignatureTransaction = CosignatureTransaction.create(transaction);
  return account.signCosignatureTransaction(cosignatureTransaction);
};
const cosignAggregateBondedTransaction = (transaction, account) => {
  const cosignatureTransaction = bitxor_sdk_1.CosignatureTransaction.create(
    transaction,
  );
  return account.signCosignatureTransaction(cosignatureTransaction);
};
            BiFunction<AggregateTransaction, Account, CosignatureSignedTransaction> cosignAggregateBondedTransaction = ((transaction, account) -> CosignatureTransaction
                .create(transaction).signWith(account));
  1. Crea un nuevo Listener para recibir notificaciones cada vez que una nueva AggregateBondedTransaction requiera la firma de tu cuenta.

  2. Abre la conexión. Solo necesitas abrir la conexión una vez y luego conectarte a todos los canales deseados.

  3. Comienza a escuchar nuevas transacciones, suscribiéndote al canal aggregateBondedAdded utilizando la dirección de tu cuenta.

Note

Para firmar automáticamente transacciones agregadas enlazadas que deben ser firmadas por cosignatarios de una cuenta multisig, utiliza la dirección multisig en su lugar. Consulta cómo obtener información de cuentas multisig en las que una cuenta es cosignataria.

5. Para cada transacción recibida, verifica si la has firmado. En este punto, es posible que desees realizar algunas verificaciones adicionales, como verificar el contenido de la transacción. Firma cada AggregateBondedTransaction pendiente utilizando la función creada anteriormente.

  1. Anuncia la CosignatureSignedTransaction a la red utilizando el repositorio TransactionHttp.

// replace with network type
const networkType = NetworkType.TEST_NET;
// replace with private key
const privateKey =
  '0000000000000000000000000000000000000000000000000000000000000000';
const account = Account.createFromPrivateKey(privateKey, networkType);
// replace with node endpoint
const nodeUrl = 'NODE_URL';
const repositoryFactory = new RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();
const listener = repositoryFactory.createListener();

listener.open().then(() => {
  listener
    .aggregateBondedAdded(account.address)
    .pipe(
      filter((_) => !_.signedByAccount(account.publicAccount)),
      map((transaction) =>
        cosignAggregateBondedTransaction(transaction, account),
      ),
      mergeMap((signedCosignatureTransaction) => {
        listener.close();
        return transactionHttp.announceAggregateBondedCosignature(
          signedCosignatureTransaction,
        );
      }),
    )
    .subscribe(
      (announcedTransaction) => {
        console.log(announcedTransaction);
        listener.close();
      },
      (err) => console.error(err),
    );
});
// replace with network type
const networkType = bitxor_sdk_1.NetworkType.TEST_NET;
// replace with private key
const privateKey =
  '0000000000000000000000000000000000000000000000000000000000000000';
const account = bitxor_sdk_1.Account.createFromPrivateKey(
  privateKey,
  networkType,
);
// replace with node endpoint
const nodeUrl = 'NODE_URL';
const repositoryFactory = new bitxor_sdk_1.RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();
const listener = repositoryFactory.createListener();
listener.open().then(() => {
  listener
    .aggregateBondedAdded(account.address)
    .pipe(
      operators_1.filter((_) => !_.signedByAccount(account.publicAccount)),
      operators_1.map((transaction) =>
        cosignAggregateBondedTransaction(transaction, account),
      ),
      operators_1.mergeMap((signedCosignatureTransaction) => {
        listener.close();
        return transactionHttp.announceAggregateBondedCosignature(
          signedCosignatureTransaction,
        );
      }),
    )
    .subscribe(
      (announcedTransaction) => {
        console.log(announcedTransaction);
        listener.close();
      },
      (err) => console.error(err),
    );
});
            NetworkType networkType = repositoryFactory.getNetworkType().toFuture().get();
            // replace with cosigner private key
            String privateKey = "";
            Account account = Account.createFromPrivateKey(privateKey, networkType);

            TransactionRepository transactionRepository = repositoryFactory.createTransactionRepository();

            try (Listener listener = repositoryFactory.createListener()) {
                listener.open().get();
                listener.aggregateBondedAdded(account.getAddress())
                    .filter(a -> a.signedByAccount(account.getPublicAccount()))
                    .map(a -> cosignAggregateBondedTransaction.apply(a, account))
                    .flatMap(transactionRepository::announceAggregateBondedCosignature).toFuture().get();
            }

¿Qué sigue?

Amplía la función anterior para firmar transacciones solo si cumplen con ciertas restricciones. Por ejemplo, adapta tu bot para firmar solo transacciones agregadas que cumplan con las siguientes condiciones:

  • La agregación tiene dos transacciones internas.

  • Las transacciones internas deben ser transferencias.

  • La transacción que envía fondos debe tener tu propia firma.

  • La transacción que envía fondos debe tener solo una moneda, siendo esta menor que 100 bitxor.

Aquí tienes una posible implementación:

const validTransaction = (
  transaction: Transaction,
  publicAccount: PublicAccount,
): boolean => {
  return (
    transaction instanceof TransferTransaction &&
    transaction.signer!.equals(publicAccount) &&
    transaction.tokens.length === 1 &&
    transaction.tokens[0].id.equals(
      new TokenId('5E62990DCAC5BE8A') ||
        transaction.tokens[0].id.equals(new NamespaceId('bitxor')),
    ) &&
    transaction.tokens[0].amount.compare(
      UInt64.fromUint(100 * Math.pow(10, 6)),
    ) < 0
  );
};

const cosignAggregateBondedTransaction = (
  transaction: AggregateTransaction,
  account: Account,
): CosignatureSignedTransaction => {
  const cosignatureTransaction = CosignatureTransaction.create(transaction);
  return account.signCosignatureTransaction(cosignatureTransaction);
};

// replace with network type
const networkType = NetworkType.TEST_NET;
// replace with private key
const privateKey =
  '0000000000000000000000000000000000000000000000000000000000000000';
const account = Account.createFromPrivateKey(privateKey, networkType);
// replace with node endpoint
const nodeUrl = 'NODE_URL';
const repositoryFactory = new RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();
const listener = repositoryFactory.createListener();

listener.open().then(() => {
  listener
    .aggregateBondedAdded(account.address)
    .pipe(
      filter((_) => _.innerTransactions.length === 2),
      filter((_) => !_.signedByAccount(account.publicAccount)),
      filter(
        (_) =>
          validTransaction(_.innerTransactions[0], account.publicAccount) ||
          validTransaction(_.innerTransactions[1], account.publicAccount),
      ),
      map((transaction) =>
        cosignAggregateBondedTransaction(transaction, account),
      ),
      mergeMap((signedCosignatureTransaction) =>
        transactionHttp.announceAggregateBondedCosignature(
          signedCosignatureTransaction,
        ),
      ),
    )
    .subscribe(
      (announcedTransaction) => {
        console.log(announcedTransaction);
        listener.close();
      },
      (err) => console.error(err),
    );
});
const validTransaction = (transaction, publicAccount) => {
    return (
        transaction instanceof bitxor_sdk_1.TransferTransaction &&
        transaction.signer.equals(publicAccount) &&
        transaction.tokens.length === 1 &&
        transaction.tokens[0].id.equals(
            new bitxor_sdk_1.TokenId('5E62990DCAC5BE8A') ||
            transaction.tokens[0].id.equals(
                new bitxor_sdk_1.NamespaceId('bitxor'),
            ),
        ) &&
        transaction.tokens[0].amount.compare(
            bitxor_sdk_1.UInt64.fromUint(100 * Math.pow(10, 6)),
        ) < 0
    );
};
const cosignAggregateBondedTransaction = (transaction, account) => {
    const cosignatureTransaction = bitxor_sdk_1.CosignatureTransaction.create(
        transaction,
    );
    return account.signCosignatureTransaction(cosignatureTransaction);
};
// replace with network type
const networkType = bitxor_sdk_1.NetworkType.TEST_NET;
// replace with private key
const privateKey =
    '0000000000000000000000000000000000000000000000000000000000000000';
const account = bitxor_sdk_1.Account.createFromPrivateKey(
    privateKey,
    networkType,
);
// replace with node endpoint
const nodeUrl = 'NODE_URL';
const repositoryFactory = new bitxor_sdk_1.RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();
const listener = repositoryFactory.createListener();
listener.open().then(() => {
    listener
        .aggregateBondedAdded(account.address)
        .pipe(
            operators_1.filter((_) => _.innerTransactions.length === 2),
            operators_1.filter((_) => !_.signedByAccount(account.publicAccount)),
            operators_1.filter(
                (_) =>
                validTransaction(_.innerTransactions[0], account.publicAccount) ||
                validTransaction(_.innerTransactions[1], account.publicAccount),
            ),
            operators_1.map((transaction) =>
                cosignAggregateBondedTransaction(transaction, account),
            ),
            operators_1.mergeMap((signedCosignatureTransaction) =>
                transactionHttp.announceAggregateBondedCosignature(
                    signedCosignatureTransaction,
                ),
            ),
        )
        .subscribe(
            (announcedTransaction) => {
                console.log(announcedTransaction);
                listener.close();
            },
            (err) => console.error(err),
        );
});
            NetworkCurrency networkCurrency = repositoryFactory.getNetworkCurrency().toFuture().get();

            BiFunction<Transaction, PublicAccount, Boolean> validTransaction = ((transaction, account) -> {
                if (transaction instanceof TransferTransaction) {
                    return false;
                }
                if (transaction.getSigner().map(s -> !s.equals(account)).orElse(true)) {
                    return false;
                }

                TransferTransaction transferTransaction = (TransferTransaction) transaction;
                return transferTransaction.getTokens().size() == 1 && transferTransaction.getTokens().stream()
                    .allMatch(m -> {
                        BigInteger maxBalance = networkCurrency.createAbsolute(BigInteger.valueOf(100)).getAmount();
                        if (m.getAmount().compareTo(maxBalance) > 0) {
                            return false;
                        }
                        return networkCurrency.getTokenId().map(tokenId -> tokenId.equals(m.getId())).orElse(false)
                            || networkCurrency.getNamespaceId().map(namespaceId -> namespaceId.equals(m.getId()))
                            .orElse(false);
                    });

            });

            BiFunction<AggregateTransaction, Account, CosignatureSignedTransaction> cosignAggregateBondedTransaction = ((transaction, account) -> CosignatureTransaction
                .create(transaction).signWith(account));

            NetworkType networkType = repositoryFactory.getNetworkType().toFuture().get();
            // replace with cosigner private key
            String privateKey = "";
            Account account = Account.createFromPrivateKey(privateKey, networkType);

            TransactionRepository transactionRepository = repositoryFactory.createTransactionRepository();

            try (Listener listener = repositoryFactory.createListener()) {
                listener.open().get();
                listener.aggregateBondedAdded(account.getAddress())
                    .filter(a -> a.signedByAccount(account.getPublicAccount())).filter(
                    a -> a.getInnerTransactions().stream()
                        .anyMatch(t -> validTransaction.apply(t, account.getPublicAccount())))
                    .map(a -> cosignAggregateBondedTransaction.apply(a, account))
                    .flatMap(transactionRepository::announceAggregateBondedCosignature).toFuture().get();
            }