Ejemplo n.º 1
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.º 2
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.º 3
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.º 4
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.º 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)
Ejemplo n.º 7
0
 def collect(self,
             api_endpoint,
             pool_account,
             collector,
             skip_confirmation=True):
     msg = ""
     client = Client(api_endpoint)
     msg += "Initialized client"
     signers = [Account(self.private_key)]
     pool = self.load_binary_option(api_endpoint, pool_account)
     pool_account = PublicKey(pool_account)
     collector_account = PublicKey(collector)
     escrow_account = PublicKey(pool["escrow"])
     escrow_mint_account = PublicKey(pool["escrow_mint"])
     long_token_mint_account = PublicKey(pool["long_mint"])
     short_token_mint_account = PublicKey(pool["short_mint"])
     token_account = PublicKey(TOKEN_PROGRAM_ID)
     escrow_authority_account = PublicKey.find_program_address(
         [
             bytes(long_token_mint_account),
             bytes(short_token_mint_account),
             bytes(token_account),
             bytes(PublicKey(BINARY_OPTION_PROGRAM_ID))
         ],
         PublicKey(BINARY_OPTION_PROGRAM_ID),
     )[0]
     # Transaction
     tx = Transaction()
     atas = []
     for mint_account in (long_token_mint_account, short_token_mint_account,
                          escrow_mint_account):
         token_pda_address = get_associated_token_address(
             collector_account, mint_account)
         associated_token_account_info = client.get_account_info(
             token_pda_address)
         account_info = associated_token_account_info['result']['value']
         if account_info is not None:
             account_state = ACCOUNT_LAYOUT.parse(
                 base64.b64decode(account_info['data'][0])).state
         else:
             account_state = 0
         if account_state == 0:
             msg += f" | Error Fetching PDA: {token_pda_address}"
             raise Exception()
         else:
             msg += f" | Fetched PDA: {token_pda_address}"
         atas.append(token_pda_address)
     collect_ix = collect_instruction(
         pool_account,
         collector_account,
         atas[0],
         atas[1],
         atas[2],
         long_token_mint_account,
         short_token_mint_account,
         escrow_account,
         escrow_authority_account,
         token_account,
     )
     tx = tx.add(collect_ix)
     try:
         response = client.send_transaction(
             tx,
             *signers,
             opts=types.TxOpts(skip_confirmation=skip_confirmation))
         return json.dumps({
             'status':
             HTTPStatus.OK,
             'msg':
             msg + f" | Collect successful",
             '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}"
         print(msg)
         raise (e)
Ejemplo n.º 8
0
 def trade(self,
           api_endpoint,
           pool_account,
           buyer_encrypted_private_key,
           seller_encrypted_private_key,
           size,
           buyer_price,
           seller_price,
           skip_confirmation=True):
     msg = ""
     client = Client(api_endpoint)
     msg += "Initialized client"
     # Create account objects
     buyer_private_key = list(
         self.cipher.decrypt(buyer_encrypted_private_key))
     seller_private_key = list(
         self.cipher.decrypt(seller_encrypted_private_key))
     assert (len(buyer_private_key) == 32)
     assert (len(seller_private_key) == 32)
     source_account = Account(self.private_key)
     buyer = Account(buyer_private_key)
     seller = Account(seller_private_key)
     # Signers
     signers = [buyer, seller, source_account]
     pool = self.load_binary_option(api_endpoint, pool_account)
     # List non-derived accounts
     pool_account = PublicKey(pool_account)
     escrow_account = PublicKey(pool["escrow"])
     escrow_mint_account = PublicKey(pool["escrow_mint"])
     long_token_mint_account = PublicKey(pool["long_mint"])
     short_token_mint_account = PublicKey(pool["short_mint"])
     buyer_account = buyer.public_key()
     seller_account = seller.public_key()
     token_account = PublicKey(TOKEN_PROGRAM_ID)
     escrow_owner_account = PublicKey.find_program_address(
         [
             bytes(long_token_mint_account),
             bytes(short_token_mint_account),
             bytes(token_account),
             bytes(PublicKey(BINARY_OPTION_PROGRAM_ID))
         ],
         PublicKey(BINARY_OPTION_PROGRAM_ID),
     )[0]
     # Transaction
     tx = Transaction()
     atas = []
     for acct in [buyer_account, seller_account]:
         acct_atas = []
         for mint_account in (long_token_mint_account,
                              short_token_mint_account,
                              escrow_mint_account):
             token_pda_address = get_associated_token_address(
                 acct, mint_account)
             associated_token_account_info = client.get_account_info(
                 token_pda_address)
             account_info = associated_token_account_info['result']['value']
             if account_info is not None:
                 account_state = ACCOUNT_LAYOUT.parse(
                     base64.b64decode(account_info['data'][0])).state
             else:
                 account_state = 0
             if account_state == 0:
                 msg += f" | Creating PDA: {token_pda_address}"
                 associated_token_account_ix = create_associated_token_account(
                     payer=source_account.public_key(),
                     owner=acct,
                     mint=mint_account,
                 )
                 tx = tx.add(associated_token_account_ix)
             else:
                 msg += f" | Fetched PDA: {token_pda_address}"
             acct_atas.append(token_pda_address)
         atas.append(acct_atas)
     trade_ix = trade_instruction(
         pool_account,
         escrow_account,
         long_token_mint_account,
         short_token_mint_account,
         buyer_account,
         seller_account,
         atas[0][2],
         atas[1][2],
         atas[0][0],
         atas[0][1],
         atas[1][0],
         atas[1][1],
         escrow_owner_account,
         token_account,
         int(size),
         int(buyer_price),
         int(seller_price),
     )
     tx = tx.add(trade_ix)
     # Send request
     try:
         response = client.send_transaction(
             tx,
             *signers,
             opts=types.TxOpts(skip_confirmation=skip_confirmation))
         return json.dumps({
             'status':
             HTTPStatus.OK,
             'msg':
             msg + f" | Trade successful",
             '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)