Solana SPL token transfer with python - python

So after having read a couple of articles I'm yet to understand how to create a Transaction and send custom SPL tokens across the Solana blockchain.
I've attached my code below.
I truly don't understand what each part of the transaction is supposed to be.
So I figured that owner is the account/wallet that is sending and paying for the transaction. And I'm assuming that dest is where I wish to send the tokens to.
This is the token (on devnet) that I wish to send, But I don't seem to be able.
from spl.token.constants import TOKEN_PROGRAM_ID
from spl.token.instructions import transfer_checked, TransferCheckedParams
from solana.rpc.commitment import Confirmed
from solana.rpc.api import Client
from solana.rpc.types import TxOpts
from solana.keypair import Keypair
from solana.publickey import PublicKey
from solana.transaction import Transaction
import os
from dotenv import load_dotenv
class TransferService:
def __init__(self, client: Client, service: SolanaService, token) -> None:
self.client = client
self.service = service
self.keypair = self.service.get_keypair(token)
def make_transaction(self, source, mint, dest, owner, amount=1, decimals=0) -> Transaction:
transaction = Transaction()
transaction.add(transfer_checked(
TransferCheckedParams(
program_id=TOKEN_PROGRAM_ID,
mint=PublicKey(mint),
source=PublicKey(source),
dest=PublicKey(dest),
owner=owner,
amount=amount,
decimals=decimals,
signers=[]
)))
return transaction
def send_transaction(self, transaction) -> None:
self.client.send_transaction(
transaction,
self.keypair,
opts=TxOpts(skip_confirmation=False, preflight_commitment=Confirmed)
)
load_dotenv()
if __name__ == "__main__":
token = os.getenv('TOKEN')
client = Client('https://api.devnet.solana.com')
service = SolanaService(client)
token = os.getenv('KEYPAIR')
transfer = TransferService(client, service, token)
a = client.get_account_info(transfer.keypair.public_key)
transaction = transfer.make_transaction(
source='CtURxXpzn9aredXse2KNtyDMeVW627tL3p7DCucdv8bc',
mint='DCzbhHu3YGnc8Vhez4YEMznQ38ad6WYGVYqeB4Wn3mie',
dest='sPkypr2LBtF5Go87zYSn5fBtWxCDEcobWeQQxXHpxJR',
owner=transfer.keypair.public_key,
amount=1,
decimals=9
)
transfer.send_transaction(transaction)

The destination sPkypr2LBtF5Go87zYSn5fBtWxCDEcobWeQQxXHpxJR is incorrect.
When you send SPL tokens, the source and dest must be addresses of token accounts, and in this case, sPkypr2LBtF5Go87zYSn5fBtWxCDEcobWeQQxXHpxJR is a wallet, so you'll need to create a recipient account for this wallet.
The preferred standard is to use an associated token account, created using something like create_associated_token_account from the SPL part of solana-py client: https://github.com/michaelhly/solana-py/blob/2c45353cb510bfeb7259fa19dacbaefe6b9ae3d1/src/spl/token/client.py#L173
For reference, the most important part is creating the instruction to create the associated token account:
def create_associated_token_account(payer: PublicKey, owner: PublicKey, mint: PublicKey) -> TransactionInstruction:
associated_token_address = PublicKey.find_program_address(
seeds=[bytes(owner), bytes(TOKEN_PROGRAM_ID), bytes(mint)], program_id=ASSOCIATED_TOKEN_PROGRAM_ID
)
return TransactionInstruction(
keys=[
AccountMeta(pubkey=payer, is_signer=True, is_writable=True),
AccountMeta(pubkey=associated_token_address, is_signer=False, is_writable=True),
AccountMeta(pubkey=owner, is_signer=False, is_writable=False),
AccountMeta(pubkey=mint, is_signer=False, is_writable=False),
AccountMeta(pubkey=SYS_PROGRAM_ID, is_signer=False, is_writable=False),
AccountMeta(pubkey=TOKEN_PROGRAM_ID, is_signer=False, is_writable=False),
AccountMeta(pubkey=SYSVAR_RENT_PUBKEY, is_signer=False, is_writable=False),
],
program_id=ASSOCIATED_TOKEN_PROGRAM_ID,
)
More background information about associated token accounts at https://spl.solana.com/associated-token-account

Related

Transfer a solana token from my wallet to another in python

So I am working on a script that transfers tokens (custom-made tokens not owned by me) from my wallet to another in python using solana.py. How do I go about doing that?
(By tokens I mean, tokens like Dust, Luv, or any other solana token)
where do I get the info needed regarding the tokens like token mint, token account address, etc. from?
And what does that info mean?
I have found this code snippet for transferring tokens from one wallet to another, but I am not sure about what things are here and where do I get them from, like token account, owner, etc.
I think this code snippet is for token owners, but I not the token owner, i have to token in my wallet I just want to transfer it to another wallet
from spl.token.constants import TOKEN_PROGRAM_ID
from spl.token.instructions import transfer_checked, TransferCheckedParams
from solana.rpc.commitment import Confirmed
from solana.rpc.api import Client
from solana.rpc.types import TxOpts
from solana.keypair import Keypair
from solana.publickey import PublicKey
from solana.transaction import Transaction
from_token_account = PublicKey("Brnvzh...bGPED")
to_token_account = PublicKey("6Uij3...Ahmtp")
from_wallet_address = PublicKey("rjPKeL...wedQp")
mint_public_id = PublicKey("4qYnL....Pt4taGk")
SECRET_KEY = bytes([43,124,...,3,226,229,189]) #from the account you are sending from. AKA owner account. You will find this in id.json
transaction = Transaction()
transaction.add(
transfer_checked(
TransferCheckedParams(
TOKEN_PROGRAM_ID, #DON'T WORRY ABOUT THIS! DON'T TOUCH IT!
from_token_account, #Its not your wallet address! Its the token account address!
mint_public_id, # token address
to_token_account, # to the receiving token account.
from_wallet_address, # wallet address connected to the from_token_account. needs to have SOL
1, #amount of tokens to send.
9, #default decimal places. Don't touch in it most cases
[] #default. Don't touch it in most cases
)
)
)
client = Client(endpoint="https://api.devnet.solana.com", commitment=Confirmed) #devnet you can change it to the main net if you want
owner = Keypair.from_secret_key(SECRET_KEY) # <-- need the keypair for the token owner here! [20,103,349, ... 230,239,239]
client.send_transaction(
transaction, owner, opts=TxOpts(skip_confirmation=False, preflight_commitment=Confirmed)) #don't touch it in most cases.

Deploy resource in Azure with python

I am working on deploy resources in Azure using python based on provided templates. As a starting point I am working with https://github.com/Azure-Samples/Hybrid-Resource-Manager-Python-Template-Deployment
Using as it is, I am having an issue at the beginning of the deployment (deployer.py deploy function)
def deploy(self, template, parameters):
"""Deploy the template to a resource group."""
self.client.resource_groups.create_or_update(
self.resourceGroup,
{
'location': os.environ['AZURE_RESOURCE_LOCATION']
}
)
The error message is
Message='ServicePrincipalCredentials' object has no attribute
'get_token'
The statement is correct, ServicePrincipalCredentials get_token attribute doesn't exist, however token is, may be an error due to an outdated version?
Based on the constructor information, the error may be on credentials creation or client creation
def __init__(self, subscription_id, resource_group, pub_ssh_key_path='~/id_rsa.pub'):
mystack_cloud = get_cloud_from_metadata_endpoint(
os.environ['ARM_ENDPOINT'])
subscription_id = os.environ['AZURE_SUBSCRIPTION_ID'] //This may be an error as subscription_id is already provided as a parameter
credentials = ServicePrincipalCredentials(
client_id=os.environ['AZURE_CLIENT_ID'],
secret=os.environ['AZURE_CLIENT_SECRET'],
tenant=os.environ['AZURE_TENANT_ID'],
cloud_environment=mystack_cloud
) --> here
self.subscription_id = subscription_id
self.resource_group = resource_group
self.dns_label_prefix = self.name_generator.haikunate()
pub_ssh_key_path = os.path.expanduser(pub_ssh_key_path)
# Will raise if file not exists or not enough permission
with open(pub_ssh_key_path, 'r') as pub_ssh_file_fd:
self.pub_ssh_key = pub_ssh_file_fd.read()
self.credentials = credentials
self.client = ResourceManagementClient(self.credentials, self.subscription_id,
base_url=mystack_cloud.endpoints.resource_manager) --> here
Do you know how I can fix this?
After struggling a little, I could find a solution. Just replace
credentials = ServicePrincipalCredentials(
client_id=os.environ['AZURE_CLIENT_ID'],
secret=os.environ['AZURE_CLIENT_SECRET'],
tenant=os.environ['AZURE_TENANT_ID'],
cloud_environment=mystack_cloud
)
for
self.credentials = DefaultAzureCredential()
The final code looks like:
from azure.identity import DefaultAzureCredential
def __init__(self, subscriptionId, resourceGroup):
endpoints = get_cloud_from_metadata_endpoint(os.environ.get("ARM_ENDPOINT"))
self.subscriptionId = subscriptionId
self.resourceGroup = resourceGroup
self.credentials = DefaultAzureCredential()
self.client = ResourceManagementClient(self.credentials, self.subscriptionId,
base_url=endpoints.endpoints.resource_manager)

getting a "this program may not be used for excuting instructions" error when making a simple spl transfer

i'm trying to send an spl-token transaction but it's not working, it's a transacion on the devnet with a token that i newly created, my code is
from spl.token.instructions import transfer, TransferParams
from spl.token.client import Client
from solana.publickey import PublicKey
from solana.transaction import Transaction
from solana.keypair import Keypair
address = "address sending from"
my_token = "3hALJzSz2bx8gxgrHg7EQQtdiHxG7d7LNswxVMXrUApw" #token addredd on devnet
private_key= "64 bit key"
def send_payouts_spl(dest,amount):
source = address
transfer_params= TransferParams(
amount=amount,
dest=PublicKey(dest),
owner=PublicKey(source),
program_id=PublicKey(my_token),
source=PublicKey(source)
)
txn = Transaction()
txn.add(transfer(transfer_params))
solana_client = Client("https://api.devnet.solana.com")
owner = Keypair.from_secret_key(private_key)
tx_id = solana_client.send_transaction(txn, owner)
return tx_id
and also the error that i'm getting
solana.rpc.core.RPCException: {'code': -32002, 'message': 'Transaction simulation failed: This program may not be used for executing instructions', 'data': {'accounts': None, 'err': 'InvalidProgramForExecution', 'logs': [], 'unitsConsumed': 0}}
also if it helps, my devnet token address and my devnet address are
3hALJzSz2bx8gxgrHg7EQQtdiHxG7d7LNswxVMXrUApw, EckcvMCmpkKwF4hDhWxq8cm4qy8JBkb2vBVQDu4WvxmM respectively
In Solana, there's typically just one token program that is shared for all token types (mints).
When you provide the program_id, you shouldn't provide the address for your mint, but rather the id for the SPL Token Program, which is TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA. Also, the owner should be the key that owns the token account.
So instead, try:
owner = Keypair.from_secret_key(private_key)
transfer_params= TransferParams(
amount=amount,
dest=PublicKey(dest),
owner=PublicKey(owner.public_key),
program_id=PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'),
source=PublicKey(source)
)

Bad credentials from Astra DB connect

Currently I am learning about Astra DB from this youtube link
https://www.youtube.com/watch?v=NyDT3KkscSk&t=2439s
I manage to download the connection.zip file from Astra DB and generated admin token keys. But when I try to do connection such as:
from app.crud import create_entry
I will get this error:
raise NoHostAvailable("Unable to connect to any servers", errors)
cassandra.cluster.NoHostAvailable: ('Unable to connect to any servers', {'98db9cb2-907a-4f9d-a935-69b69fb2157f-asia-south1.db.astra.datastax.com:29042:611a8370-f129-4099-84e2-c3b2f426ebdc': AuthenticationFailed('Failed to authenticate to 98db9cb2-907a-4f9d-a935-69b69fb2157f-asia-south1.db.astra.datastax.com:29042:611a8370-f129-4099-84e2-c3b2f426ebdc: Error from server: code=0100 [Bad credentials] message="We recently improved your database security. To find out more and reconnect, see https://docs.datastax.com/en/astra/docs/manage-application-tokens.html"'), '98db9cb2-907a-4f9d-a935-69b69fb2157f-asia-south1.db.astra.datastax.com:29042:040ab116-8c77-4eb4-a357-c9bdcbb637d4': AuthenticationFailed('Failed to authenticate to 98db9cb2-907a-4f9d-a935-69b69fb2157f-asia-south1.db.astra.datastax.com:29042:040ab116-8c77-4eb4-a357-c9bdcbb637d4: Error from server: code=0100 [Bad credentials] message="We recently improved your database security. To find out more and reconnect, see https://docs.datastax.com/en/astra/docs/manage-application-tokens.html"'), '98db9cb2-907a-4f9d-a935-69b69fb2157f-asia-south1.db.astra.datastax.com:29042:536e6e99-ef4e-47d0-9308-b0c6cdf4aa37': AuthenticationFailed('Failed to authenticate to 98db9cb2-907a-4f9d-a935-69b69fb2157f-asia-south1.db.astra.datastax.com:29042:536e6e99-ef4e-47d0-9308-b0c6cdf4aa37: Error from server: code=0100 [Bad credentials] message="We recently improved your database security. To find out more and reconnect, see https://docs.datastax.com/en/astra/docs/manage-application-tokens.html"')})
Here is my db.py:
import os
import pathlib
from dotenv import load_dotenv
from cassandra.cluster import Cluster
from cassandra.auth import PlainTextAuthProvider
from cassandra.cqlengine.connection import register_connection, set_default_connection
BASE_DIR = pathlib.Path(__file__).parent
CLUSTER_BUNDLE = BASE_DIR / 'ignored'/ 'connect.zip'
load_dotenv()
astra_db_client_id = os.environ.get('ASTRA_DB_CLIENT_ID')
astra_db_client_secret = os.environ.get('ASTRA_DB_CLIENT_SECRET')
def get_cluster():
cloud_config= {
'secure_connect_bundle': CLUSTER_BUNDLE
}
auth_provider = PlainTextAuthProvider(astra_db_client_id, astra_db_client_secret)
cluster = Cluster(cloud=cloud_config, auth_provider=auth_provider, control_connection_timeout=30,
connect_timeout=30)
return cluster
def get_session():
cluster = get_cluster()
session = cluster.connect()
register_connection(str(session), session=session)
set_default_connection(str(session))
return session
# session = get_session()
# row = session.execute("select release_version from system.local").one()
# if row:
# print(row[0])
# else:
# print("An error occurred.")
I tried to recreate the token key many times and re download the drivers as well but I still have no luck in passing the bad credential errors here:
my crud.py
from .db import get_session
from .models import Product
from cassandra.cqlengine.management import sync_table
session = get_session()
sync_table(Product)
def create_entry(data:dict):
return Product.create(**data)
models.py
from cassandra.cqlengine import columns
from cassandra.cqlengine.models import Model
class Product(Model): # -> table
__keyspace__ = "testing" #
asin = columns.Text(primary_key=True, required=True)
title = columns.Text()
You might want to take a look at this
https://docs.datastax.com/en/astra/docs/docs/using-the-datastax-python-driver-to-connect-to-your-database.html
Specifically the section here:
from cassandra.cluster import Cluster
from cassandra.auth import PlainTextAuthProvider
cloud_config= {
'secure_connect_bundle': '/path/to/secure-connect-database_name.zip'
}
auth_provider = PlainTextAuthProvider('username', 'password')
cluster = Cluster(cloud=cloud_config, auth_provider=auth_provider)
session = cluster.connect()
When creating the connection you’ll want to pass the secure connect bundle zip. You’ll then provide the clientId and clientSecret as the username and password from the connections file you downloaded.

How to create a SECRET_HASH for AWS Cognito using boto3?

I want to create/calculate a SECRET_HASH for AWS Cognito using boto3 and python. This will be incorporated in to my fork of warrant.
I configured my cognito app client to use an app client secret. However, this broke the following code.
def renew_access_token(self):
"""
Sets a new access token on the User using the refresh token.
NOTE:
Does not work if "App client secret" is enabled. 'SECRET_HASH' is needed in AuthParameters.
'SECRET_HASH' requires HMAC calculations.
Does not work if "Device Tracking" is turned on.
https://stackoverflow.com/a/40875783/1783439
'DEVICE_KEY' is needed in AuthParameters. See AuthParameters section.
https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_InitiateAuth.html
"""
refresh_response = self.client.initiate_auth(
ClientId=self.client_id,
AuthFlow='REFRESH_TOKEN',
AuthParameters={
'REFRESH_TOKEN': self.refresh_token
# 'SECRET_HASH': How to generate this?
},
)
self._set_attributes(
refresh_response,
{
'access_token': refresh_response['AuthenticationResult']['AccessToken'],
'id_token': refresh_response['AuthenticationResult']['IdToken'],
'token_type': refresh_response['AuthenticationResult']['TokenType']
}
)
When I run this I receive the following exception:
botocore.errorfactory.NotAuthorizedException:
An error occurred (NotAuthorizedException) when calling the InitiateAuth operation:
Unable to verify secret hash for client <client id echoed here>.
This answer informed me that a SECRET_HASH is required to use the cognito client secret.
The aws API reference docs AuthParameters section states the following:
For REFRESH_TOKEN_AUTH/REFRESH_TOKEN: USERNAME (required), SECRET_HASH
(required if the app client is configured with a client secret),
REFRESH_TOKEN (required), DEVICE_KEY
The boto3 docs state that a SECRET_HASH is
A keyed-hash message authentication code (HMAC) calculated using the
secret key of a user pool client and username plus the client ID in
the message.
The docs explain what is needed, but not how to achieve this.
The below get_secret_hash method is a solution that I wrote in Python for a Cognito User Pool implementation, with example usage:
import boto3
import botocore
import hmac
import hashlib
import base64
class Cognito:
client_id = app.config.get('AWS_CLIENT_ID')
user_pool_id = app.config.get('AWS_USER_POOL_ID')
identity_pool_id = app.config.get('AWS_IDENTITY_POOL_ID')
client_secret = app.config.get('AWS_APP_CLIENT_SECRET')
# Public Keys used to verify tokens returned by Cognito:
# http://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html#amazon-cognito-identity-user-pools-using-id-and-access-tokens-in-web-api
id_token_public_key = app.config.get('JWT_ID_TOKEN_PUB_KEY')
access_token_public_key = app.config.get('JWT_ACCESS_TOKEN_PUB_KEY')
def __get_client(self):
return boto3.client('cognito-idp')
def get_secret_hash(self, username):
# A keyed-hash message authentication code (HMAC) calculated using
# the secret key of a user pool client and username plus the client
# ID in the message.
message = username + self.client_id
dig = hmac.new(self.client_secret, msg=message.encode('UTF-8'),
digestmod=hashlib.sha256).digest()
return base64.b64encode(dig).decode()
# REQUIRES that `ADMIN_NO_SRP_AUTH` be enabled on Client App for User Pool
def login_user(self, username_or_alias, password):
try:
return self.__get_client().admin_initiate_auth(
UserPoolId=self.user_pool_id,
ClientId=self.client_id,
AuthFlow='ADMIN_NO_SRP_AUTH',
AuthParameters={
'USERNAME': username_or_alias,
'PASSWORD': password,
'SECRET_HASH': self.get_secret_hash(username_or_alias)
}
)
except botocore.exceptions.ClientError as e:
return e.response
I also got a TypeError when I tried the above solution. Here is the solution that worked for me:
import hmac
import hashlib
import base64
# Function used to calculate SecretHash value for a given client
def calculateSecretHash(client_id, client_secret, username):
key = bytes(client_secret, 'utf-8')
message = bytes(f'{username}{client_id}', 'utf-8')
return base64.b64encode(hmac.new(key, message, digestmod=hashlib.sha256).digest()).decode()
# Usage example
calculateSecretHash(client_id, client_secret, username)

Categories

Resources