WEB3DEV

Cover image for Enviando recompensas aos investidores da ICO utilizando transações em lote em Python - Algorand
Dimitris Carvalho Calixto
Dimitris Carvalho Calixto

Posted on • Atualizado em

Enviando recompensas aos investidores da ICO utilizando transações em lote em Python - Algorand

Esse artigo foi escrito por: Marc Fresquet Rius e traduzido por Dimitris Calixto, artigo original disponível aqui

Imagine que você é o proprietário de uma nova ASA (Algorand Standard Asset), e está procurando investidores para o seu projeto através de uma Oferta Inicial de Moeda.

Esses investidores enviaram um número de Algos para sua carteira em troca de uma quantia proporcional do seu próprio token (seu ASA). Note que os investidores têm que optar pela sua ASA para conseguir receber.

Para conseguir isso, você terá que realizar os seguintes passos:

Encontre todas as transações de pagamento efetuadas na sua carteira e agrupá-las por endereço do remetente.

Calcule o montante do seu token que você dará a cada endereço proporcionalmente ao seu investimento.

Envie uma transação para cada endereço com o montante calculado do seu token.

Todas as análises e exemplos serão feitos na TestNet, mas a mesma lógica se aplicaria também na MainNet.

Para simular um cenário real, vamos criar 4 carteiras diferentes:

Carteira 1: Investidor #1

Carteira 2: Investidor #2

Carteira 3: Investidor #3

Carteira 4: Nossa carteira

Note que a criação das carteiras é opcional, uma vez que se pode seguir todos os passos apenas com os exemplos fornecidos. Além disso, este tutorial não cobre contratos inteligentes ou assinaturas inteligentes.

Requisitos

Python >= 3.6

Uma conta PureStake: Veja o tutorial

Contas de TestNet com múltiplos fundos: Veja o tutorial

Base

Não é obrigatório, mas é útil ler os seguintes tutoriais:

Criando uma transação Python com PureStake API

Indexador para análise de Blockchain

Passos

1. Configuração do Ambiente

Instale todas as dependências no seu ambiente:

pip install requests

pip install numpy

pip install pandas

pip install py-algorand-sdk
Enter fullscreen mode Exit fullscreen mode

2. Definir variáveis

Neste exemplo, vamos analisar transações da nossa carteira feitas de 2022-02-07 a 2022-02-14. Assim que tivermos os endereços dos investidores, iremos recompensá-los com nosso token (65875461), correspondente a uma TESTCOIN na TestNet. O montante total do nosso token para ser recompensado será de 20000 unidades. Este montante deve ser definido em micros.

my_wallet = "6HNCL2A5RZJN5LLTBB76NUCRM5TRFGMTSPIXT4JTP3A2WXZZQZKYNLP4HI"

my_token_id = "65875461"  _# ASA identificador_

total_tokens_to_pay = 20_000_000_000  _# no micros_

txn_start = "2022-02-07"

txn_end = "2022-02-14"

my_wallet_pk = ""  _# Necessária para assinar transações_

purestake_token = ""  _# Sua chave da API PureStake _
Enter fullscreen mode Exit fullscreen mode

É aconselhável guardar as suas credenciais como variáveis de ambiente. Nunca compartilhe com ninguém.

3. Obter transações

Instanciaremos o indexador e definiremos uma função para obter todas as transações em um determinado período de tempo, dado um endereço. Recolheremos essas transações como um DataFrame pandas.

import pandas as pd

from algosdk.v2client import indexer

headers = {

   "X-API-Key": purestake_token,

}

myindexer = indexer.IndexerClient(

    indexer_token="",

    indexer_address="https://testnet-algorand.api.purestake.io/idx2",  

    headers=headers

)

def get_txn(address, start_time, end_time):

    response = myindexer.search_transactions_by_address(

        address=address,

        start_time=start_time,

        end_time=end_time

    )

    transactions = response["transactions"]

    txn_df = pd.DataFrame(transactions)

    return txn_df

_# Get txn feito no endereço_

txn_df = get_txn(my_wallet, txn_start, txn_end)
Enter fullscreen mode Exit fullscreen mode

As transações DataFrame fornecem múltiplas informações, mas só estamos interessados na quantidade de Algos que cada remetente pagou. Assim, todas as colunas não utilizadas serão removidas dos dados. Note que o montante é definido dentro de outra coluna, então devemos extraí-lo de lá.

def prepare_txn(txn_df):

    _# Filtrar para pagamentos_

    txn_filtered = txn_df[txn_df["tx-type"] == "pay"]

    _# Recuperar colunas úteis_

    txn_df_reduced = txn_filtered[["payment-transaction", "sender"]]

    _# Obter informações sobre o montante da transação (o montante é dado em microAlgos)_

    txn_df_reduced["amount"] = txn_df_reduced["payment-transaction"].apply(pd.Series)["amount"] / 1_000_000

    txn_df_reduced.drop(["payment-transaction"], axis=1, inplace=True)

    return txn_df_reduced

_# Limpar df e recuperar informações necessárias_

txn_cleaned = prepare_txn(txn_df)
Enter fullscreen mode Exit fullscreen mode

Um investidor pode fazer múltiplas transações na nossa carteira, então precisamos agrupar todas as transações por endereço do remetente e somar o seu montante. Uma vez que vamos recompensar proporcionalmente, calcularemos o montante do nosso token para ser enviado para cada endereço.

def group_txn_by_user(txn, total_tokens_to_pay):

    _# Agrupar txn por endereço_

    txn_grouped = txn.groupby(["sender"]).sum()

    _# Calcular a % de montante por endereço_

    txn_grouped["amount_per"] = txn_grouped["amount"] / txn_grouped["amount"].sum()

    _# Calcular os tokens que serão pagos para cada endereço_                  txn_grouped["tokens_to_pay"] = (txn_grouped["amount_per"] * total_tokens_to_pay)

txn_grouped.sort_values(by=["tokens_to_pay"], ascending=**False**, inplace=True)

    return txn_grouped

_# Grupo pagou montante por endereço e definiu recompensas ganhas_

txn_grouped = group_txn_by_user(txn_cleaned, total_tokens_to_pay)
Enter fullscreen mode Exit fullscreen mode

4. Recompensas de pagamento

Vamos instanciar um Cliente AlgodClient na TestNet usando as credenciais PureStake.

from algosdk.v2client import algod

algod_address = "https://testnet-algorand.api.purestake.io/ps2"

headers = {"X-Api-key": purestake_token}

algod_client = algod.AlgodClient(algod_token=purestake_token, algod_address=algod_address, headers=headers)
Enter fullscreen mode Exit fullscreen mode

E iremos definir uma função para enviar transações da nossa carteira para outra. Será necessário definir o identificador do token (a moeda que pretendemos enviar), e o montante. A chave privada da sua carteira também será necessária para assinar as transações. Lembre-se: não compartilhe com ninguém.


import json

import base64

from algosdk.future.transaction import AssetTransferTxn

def do_transaction(my_wallet, my_wallet_pk, dest_adress, amount, my_token_id):

    _# construir transação com os parâmetros sugeridos_

    params = algod_client.suggested_params()

    note = "Rewards".encode()  _# A nota será vista pelo destinatário no txn_

    _# construir txn_

    unsigned_txn = AssetTransferTxn(

        sender=my_wallet,

        sp=params,

        receiver=dest_adress,

        index=my_token_id,

        amt=amount,

        note=note

    )

    _# transação de sinal_

    signed_txn = unsigned_txn.sign(my_wallet_pk)

    _# enviar transação_

    txid = algod_client.send_transaction(signed_txn)

    print("Signed transaction with txID: **{}**".format(txid))

    _# esperar para confirmação_

    try:

        confirmed_txn = wait_for_confirmation(algod_client, txid)

    except Exception as err:

        print(err)

        return

    print("Transaction information: {}".format(

        json.dumps(confirmed_txn, indent=4)))

    print("Decoded note: {}".format(base64.b64decode(

        confirmed_txn["txn"]["txn"]["note"]).decode()))
Enter fullscreen mode Exit fullscreen mode

A função wait_for_confirmation é um serviço da Algorand Inc que verifica a transação para ser incluída na blockchain.

_# Função da Algorand Inc. - útil para aguardar a confirmação de uma transação_

def wait_for_confirmation(client, txid):

    last_round = client.status().get('last-round')

    txinfo = client.pending_transaction_info(txid)

    while not (txinfo.get('confirmed-round') **and** txinfo.get('confirmed-round') > 0):

        print('Waiting for confirmation')

        last_round += 1

        client.status_after_block(last_round)

        txinfo = client.pending_transaction_info(txid)

    print('Transaction confirmed in round', txinfo.get('confirmed-round'))

    return txinfo
Enter fullscreen mode Exit fullscreen mode

E, finalmente, criaremos uma função para iterar sobre a DataFrame dos investidores que criamos e realizaremos uma transação para cada um deles para enviar as suas recompensas.

def pay_battery(df_txs):

    for index, row in df_txs.iterrows():

        _# token value to pay must be int; otherwise, txn will fail_

        tokens_to_pay = row["tokens_to_pay"].astype(int).item()

        print("Starting transaction of {} to {}".format(tokens_to_pay, index))

        do_transaction(my_wallet, my_wallet_pk, index, tokens_to_pay, my_token_id)

_# Pagar recompensas_

pay_battery(txn_grouped)
Enter fullscreen mode Exit fullscreen mode

5. Executar tudo

import json

import base64

from algosdk.v2client import algod, indexer

from *algosdk.future.transaction import AssetTransferTxn

import pandas as pd

_# Definir variáveis _

my_wallet = "6HNCL2A5RZJN5LLTBB76NUCRM5TRFGMTSPIXT4JTP3A2WXZZQZKYNLP4HI"

my_token_id = "65875461"  _# ASA identificador_

total_tokens_to_pay = 20_000_000_000  _# no micros_

txn_start = "2022-02-07"

txn_end = "2022-02-14"

my_wallet_pk = ""  _# necessária para assinar transações_

purestake_token = ""  _# Sua chave da API PureStake_

_# Instância clientes_

headers = {

   "X-API-Key": purestake_token,

}

myindexer = indexer.IndexerClient(

    indexer_token="",

    indexer_address="https://testnet-algorand.api.purestake.io/idx2",

    headers=headers

)

algod_address = "https://testnet-algorand.api.purestake.io/ps2"

headers = {"X-Api-key": purestake_token}

algod_client = algod.AlgodClient(algod_token=purestake_token, algod_address=algod_address, headers=headers)

_# Função para obter transações_

def get_txn(address, start_time, end_time):

    response = myindexer.search_transactions_by_address(

        address=address,

        start_time=start_time,

        end_time=end_time

    )

    transactions = response["transactions"]

    txn_df = pd.DataFrame(transactions)

    return txn_df

_# Função para limpar transações e recuperar a informação necessária_

def prepare_txn(txn_df):

    _# Filtro para pagamentos_

    txn_filtered = txn_df[txn_df["tx-type"] == "pay"]

    _# Recuperar colunas úteis_

    txn_df_reduced = txn_filtered[["payment-transaction", "sender"]]

    _# Obter informações sobre o montante da transação (o montante é dado em microAlgos)_

    txn_df_reduced["amount"] = txn_df_reduced["payment-transaction"].apply(pd.Series)["amount"] / 1_000_000

    txn_df_reduced.drop(["payment-transaction"], axis=1, inplace=True)

    return txn_df_reduced

_# Função de agrupar transações por remetente e calcular tokens  para pagar a eles_

def group_txn_by_user(txn, total_tokens_to_pay):

    _# Grupo txn por endereço_

    txn_grouped = txn.groupby(["sender"]).sum()

    _# Calcular a % de montante por endereço_

    txn_grouped["amount_per"] = txn_grouped["amount"] / txn_grouped["amount"].sum()

    _# Calcular os tokens a serem pagos para cada endereço_

    txn_grouped["tokens_to_pay"] = (txn_grouped["amount_per"] * total_tokens_to_pay)

    txn_grouped.sort_values(by=["tokens_to_pay"], ascending=False, inplace=True)

    return txn_grouped

_# Funções para realizar as transações_

def do_transaction(my_wallet, my_wallet_pk, dest_adress, amount, my_token_id):

    _# construir transação com os parâmetros sugeridos_

    params = algod_client.suggested_params()

    note = "Rewards".encode()  _# a nota será vista pelo receptor no txn_

    _# construir txn_

    unsigned_txn = AssetTransferTxn(

        sender=my_wallet,

        sp=params,

        receiver=dest_adress,

        index=my_token_id,

        amt=amount,

        note=note

    )

    _# assinar a transação_

    signed_txn = unsigned_txn.sign(my_wallet_pk)

    _# enviar a transação_

    txid = algod_client.send_transaction(signed_txn)

    print("Signed transaction with txID: {}".format(txid))

    _# esperar pela confirmação_

    try:

        confirmed_txn = wait_for_confirmation(algod_client, txid)

    except Exception as err:

        print(err)

        return

    print("Transaction information: {}".format(

        json.dumps(confirmed_txn, indent=4)))

    print("Decoded note: {}".format(base64.b64decode(

        confirmed_txn["txn"]["txn"]["note"]).decode()))

def wait_for_confirmation(client, txid):

    last_round = client.status().get('last-round')

    txinfo = client.pending_transaction_info(txid)

    while not (txinfo.get('confirmed-round') and txinfo.get('confirmed-round') > 0):

        print('Waiting for confirmation')

        last_round += 1

        client.status_after_block(last_round)

        txinfo = client.pending_transaction_info(txid)

    print('Transaction confirmed in round', txinfo.get('confirmed-round'))

    return txinfo

**def** pay_battery(df_txs):

    **for** index, row **in** df_txs.iterrows():

        _# o valor do token pago deve ser int; caso contrário, txn falhará_

        tokens_to_pay = row["tokens_to_pay"].astype(int).item()

        print("Starting transaction of {} to {}".format(tokens_to_pay, index))

        do_transaction(my_wallet, my_wallet_pk, index, tokens_to_pay, my_token_id)

_# Executar todo a Pipeline_

def pay_rewards():

    _# Fazer txn no endereço_

    txn_df = get_txn(my_wallet, txn_start, txn_end)

    _# Limpar df e recuperar a informação necessárias_

    txn_cleaned = prepare_txn(txn_df)

    _# Grupo de montante pago por endereço e fixar recompensas ganhas_

    txn_grouped = group_txn_by_user(txn_cleaned, total_tokens_to_pay)

    _# Pagar recompensas_

    pay_battery(txn_grouped)

if __name__ == '__main__':

    pay_rewards()
Enter fullscreen mode Exit fullscreen mode

6. Revisar transações

Como dissemos, criamos 4 carteiras para reproduzir um cenário real:

Carteira 1: Investidor #1

Endereço: 5D2OWR6X5GQOGYR4YA5JYIL5DCDTBMYS2BSNDIKXLH4UHSEQVDE2EVVR6I

2 transações (2A + 4A)

Carteira 2: Investidor #2

Endereço: D5VFPNJTNMJIF2JDMWOEHPEZMFP2TNXXD63WNGK7HFS7X3AFQNVKNOIN4A

1 transação (11A)

Carteira 3: Investidor #3

Endereço: 3CMEBJQPHDLA7ZS3D3VYXOQ3WHCSSOEV6JTZNCUJI25SL3WJAFLLOPLPII

2 transações (1A + 2A)

Carteira 4: Nossa carteira

Endereço: 6HNCL2A5RZJN5LLTBB76NUCRM5TRFGMTSPIXT4JTP3A2WXZZQZKYNLP4HI

As transações podem ser encontradas na AlgoExplorer procurando pelo endereço.

Internet Transactions

Assim, uma vez terminado o ICO, é tempo de recompensar os investidores de acordo com sua contribuição. Lembre-se de que, para receberem as recompensas,eles devem optar primeiro pelo seu ASA nas suas carteiras. Fixamos 20000 unidades do nosso token para serem distribuídas entre eles. A quantia que devem receber seria a seguinte:

Carteira 1: 6000 TESTCOIN

Carteira 2: 11000 TESTCOIN

Carteira 3: 3000 TESTCOIN

Top to bottom transactions

Como ordenamos as contribuições recebidas de cima para baixo, o remetente com a maior contribuição será o primeiro a receber o TESTCOIN. Assim, já conseguimos recompensar os nossos investidores.

7.Experimente você mesmo

A fim de simular diferentes cenários, é aconselhável criar diferentes contas na TestNet e brincar com elas. Uma delas representará a sua própria carteira com seus tokens; as outras representarão os investidores que enviam transações para você.

Você pode encontrar mais informações sobre como criar múltiplas carteiras e financiá-las com Algos no tutorial Crie uma Conta na TestNet com Python.

Após a criação, é possível enviar transações de uma carteira para outra utilizando a carteira Algorand.

Para simular seu próprio token, você pode trocar alguns dos seus Algos na TestNet por qualquer outra moeda. Existem várias moedas criadas para fins de teste. A moeda de teste (ASA ID 65875461) que escolhemos é uma delas. Você pode utilizar o Tinyman DEX para a troca.

E para concluir, lembre-se que o código e os recursos fornecidos são destinados para operar na TestNet, mas a mesma lógica se aplicaria para operar tudo também na MainNet.

8. Repositório do código

git clone [https://github.com/marcfresquet/algo-utilities.git](https://github.com/marcfresquet/algo-utilities.git)

Enter fullscreen mode Exit fullscreen mode

Latest comments (0)