Ejemplo n.º 1
0
    def _create_account_args(
        self,
        owner: PublicKey,
        skip_confirmation: bool,
        balance_needed: int,
    ) -> Tuple[PublicKey, Transaction, Account, Account, TxOpts]:
        new_account = Account()
        # Allocate memory for the account

        # Construct transaction
        txn = Transaction()
        txn.add(
            sp.create_account(
                sp.CreateAccountParams(
                    from_pubkey=self.payer.public_key(),
                    new_account_pubkey=new_account.public_key(),
                    lamports=balance_needed,
                    space=ACCOUNT_LAYOUT.sizeof(),
                    program_id=self.program_id,
                )))
        txn.add(
            spl_token.initialize_account(
                spl_token.InitializeAccountParams(
                    account=new_account.public_key(),
                    mint=self.pubkey,
                    owner=owner,
                    program_id=self.program_id)))
        return (
            new_account.public_key(),
            txn,
            self.payer,
            new_account,
            TxOpts(skip_preflight=True, skip_confirmation=skip_confirmation),
        )
Ejemplo n.º 2
0
 def topup(self, api_endpoint, to, amount=None, skip_confirmation=True):
     """
     Send a small amount of native currency to the specified wallet to handle gas fees. Return a status flag of success or fail and the native transaction data.
     """
     msg = ""
     try:
         # Connect to the api_endpoint
         client = Client(api_endpoint)
         msg += "Initialized client"
         # List accounts
         sender_account = Account(self.private_key)
         dest_account = PublicKey(to)
         msg += " | Gathered accounts"
         # List signers
         signers = [sender_account]
         # Start transaction
         tx = Transaction()
         # Determine the amount to send
         try:
             if amount is None:
                 min_rent_reseponse = client.get_minimum_balance_for_rent_exemption(
                     ACCOUNT_LAYOUT.sizeof())
                 lamports = min_rent_reseponse["result"]
             else:
                 lamports = int(amount)
             msg += f" | Fetched lamports: {lamports * 1e-9} SOL"
         except Exception as e:
             msg += " | ERROR: couldn't process lamports"
             raise (e)
         # Generate transaction
         transfer_ix = transfer(
             TransferParams(from_pubkey=sender_account.public_key(),
                            to_pubkey=dest_account,
                            lamports=lamports))
         tx = tx.add(transfer_ix)
         msg += f" | Transferring funds"
         # Send request
         try:
             response = client.send_transaction(
                 tx,
                 *signers,
                 opts=types.TxOpts(skip_confirmation=skip_confirmation))
             return json.dumps({
                 'status':
                 HTTPStatus.OK,
                 'msg':
                 f"Successfully sent {lamports * 1e-9} SOL to {to}",
                 'tx':
                 response.get('result') if skip_confirmation else
                 response['result']['transaction']['signatures'],
             })
         except Exception as e:
             msg += f" | ERROR: Encountered exception while attempting to send transaction: {e}"
             raise (e)
     except Exception as e:
         return json.dumps({
             'status': HTTPStatus.BAD_REQUEST,
             'msg': msg,
         })
Ejemplo n.º 3
0
    def get_min_balance_rent_for_exempt_for_account(conn: Client) -> int:
        """Get the minimum balance for the account to be rent exempt.

        :param conn: RPC connection to a solana cluster.
        :return: Number of lamports required.
        """
        resp = conn.get_minimum_balance_for_rent_exemption(ACCOUNT_LAYOUT.sizeof())
        return resp["result"]
Ejemplo n.º 4
0
    def _create_account_info(self, info: RPCResponse) -> AccountInfo:
        if not info:
            raise ValueError("Invalid account owner")

        if info["result"]["value"]["owner"] != str(self.program_id):
            raise AttributeError("Invalid account owner")

        bytes_data = decode_byte_string(info["result"]["value"]["data"][0])
        if len(bytes_data) != ACCOUNT_LAYOUT.sizeof():
            raise ValueError("Invalid account size")

        decoded_data = ACCOUNT_LAYOUT.parse(bytes_data)

        mint = PublicKey(decoded_data.mint)
        owner = PublicKey(decoded_data.owner)
        amount = decoded_data.amount

        if decoded_data.delegate_option == 0:
            delegate = None
            delegated_amount = 0
        else:
            delegate = PublicKey(decoded_data.delegate)
            delegated_amount = decoded_data.delegated_amount

        is_initialized = decoded_data.state != 0
        is_frozen = decoded_data.state == 2

        if decoded_data.is_native_option == 1:
            rent_exempt_reserve = decoded_data.is_native
            is_native = True
        else:
            rent_exempt_reserve = None
            is_native = False

        if decoded_data.close_authority_option == 0:
            close_authority = None
        else:
            close_authority = PublicKey(decoded_data.owner)

        if mint != self.pubkey:
            raise AttributeError(f"Invalid account mint: {decoded_data.mint} != {self.pubkey}")

        return AccountInfo(
            mint,
            owner,
            amount,
            delegate,
            delegated_amount,
            is_initialized,
            is_frozen,
            is_native,
            rent_exempt_reserve,
            close_authority,
        )
Ejemplo n.º 5
0
    def create_account(
        self,
        owner: PublicKey,
        skip_confirmation: bool = False,
    ) -> PublicKey:
        """Create and initialize a new account.

        This account may then be used as a `transfer()` or `approve()` destination.

        :param owner: User account that will own the new account.
        :param skip_confirmation: (optional) Option to skip transaction confirmation.
        :return: Public key of the new empty account.

        If skip confirmation is set to `False`, this method will block for at most 30 seconds
        or until the transaction is confirmed.
        """
        new_account = Account()
        # Allocate memory for the account
        balance_needed = Token.get_min_balance_rent_for_exempt_for_account(
            self._conn)
        # Construct transaction
        txn = Transaction()
        txn.add(
            sp.create_account(
                sp.CreateAccountParams(
                    from_pubkey=self.payer.public_key(),
                    new_account_pubkey=new_account.public_key(),
                    lamports=balance_needed,
                    space=ACCOUNT_LAYOUT.sizeof(),
                    program_id=self.program_id,
                )))
        txn.add(
            spl_token.initialize_account(
                spl_token.InitializeAccountParams(
                    account=new_account.public_key(),
                    mint=self.pubkey,
                    owner=owner,
                    program_id=self.program_id)))
        # Send the two instructions
        self._conn.send_transaction(txn,
                                    self.payer,
                                    new_account,
                                    opts=TxOpts(
                                        skip_preflight=True,
                                        skip_confirmation=skip_confirmation))
        return new_account.public_key()
Ejemplo n.º 6
0
    def _create_wrapped_native_account_args(
        program_id: PublicKey,
        owner: PublicKey,
        payer: Keypair,
        amount: int,
        skip_confirmation: bool,
        balance_needed: int,
    ) -> Tuple[PublicKey, Transaction, Keypair, Keypair, TxOpts]:
        new_keypair = Keypair()
        # Allocate memory for the account
        # Construct transaction
        txn = Transaction()
        txn.add(
            sp.create_account(
                sp.CreateAccountParams(
                    from_pubkey=payer.public_key,
                    new_account_pubkey=new_keypair.public_key,
                    lamports=balance_needed,
                    space=ACCOUNT_LAYOUT.sizeof(),
                    program_id=program_id,
                )
            )
        )

        txn.add(
            sp.transfer(
                sp.TransferParams(from_pubkey=payer.public_key, to_pubkey=new_keypair.public_key, lamports=amount)
            )
        )

        txn.add(
            spl_token.initialize_account(
                spl_token.InitializeAccountParams(
                    account=new_keypair.public_key, mint=WRAPPED_SOL_MINT, owner=owner, program_id=program_id
                )
            )
        )

        return new_keypair.public_key, txn, payer, new_keypair, TxOpts(skip_confirmation=skip_confirmation)