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
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 _
É 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)
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)
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)
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)
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()))
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
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)
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()
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.
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
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)
Latest comments (0)