Ejemplo n.º 1
0
    def create_solana_account(
            self,
            tx: solana.Transaction,
            commitment: Optional[Commitment] = Commitment.SINGLE):
        """Submit a request to Agora to create a Solana account.

        :param tx: The Solana transaction to create an account.
        :param commitment: The :class:`Commitment <agora.solana.commitment.Commitment>` to use.
        """
        def _submit_request():
            req = account_pb.CreateAccountRequest(
                transaction=model_pb.Transaction(value=tx.marshal()),
                commitment=commitment.to_proto(),
            )
            resp = self._account_stub_v4.CreateAccount(
                req, metadata=self._metadata, timeout=_GRPC_TIMEOUT_SECONDS)

            if resp.result == account_pb.CreateAccountResponse.Result.EXISTS:
                raise AccountExistsError()
            if resp.result == account_pb.CreateAccountResponse.Result.PAYER_REQUIRED:
                raise PayerRequiredError()
            if resp.result == account_pb.CreateAccountResponse.Result.BAD_NONCE:
                raise BadNonceError()
            if resp.result != account_pb.CreateAccountResponse.Result.OK:
                raise Error(f'unexpected result from agora: {resp.result}')

        retry(self._retry_strategies, _submit_request)
Ejemplo n.º 2
0
    def create_account(self, private_key: PrivateKey):
        if self._kin_version not in _SUPPORTED_VERSIONS:
            raise UnsupportedVersionError()

        retry(self.retry_strategies,
              self._create_stellar_account,
              private_key=private_key)
Ejemplo n.º 3
0
    def get_stellar_account_info(self, public_key: PublicKey) -> AccountInfo:
        """Get the info of a Stellar account from Agora.

        :param public_key: The :class:`PublicKey <agora.model.keys.PublicKey>` of the account to request the info for.
        :return: A :class:`AccountInfo <agora.model.account.AccountInfo>` object.
        """
        def _get_account():
            try:
                resp = self._account_stub_v3.GetAccountInfo(
                    account_pb_v3.GetAccountInfoRequest(
                        account_id=model_pb_v3.StellarAccountId(
                            value=public_key.stellar_address), ),
                    metadata=self._metadata,
                    timeout=_GRPC_TIMEOUT_SECONDS)
            except grpc.RpcError as e:
                raise BlockchainVersionError() if self._is_migration_error(
                    e) else e

            if resp.result == account_pb_v3.GetAccountInfoResponse.Result.NOT_FOUND:
                raise AccountNotFoundError

            return resp

        resp = retry(self._retry_strategies, _get_account)
        return AccountInfo.from_proto(resp.account_info)
Ejemplo n.º 4
0
    def _sign_and_submit_builder(
            self,
            signers: List[PrivateKey],
            builder: kin_base.Builder,
            invoice_list: Optional[InvoiceList] = None
    ) -> SubmitTransactionResult:
        source_info = self._internal_client.get_stellar_account_info(
            signers[0].public_key)
        offset = 1

        def _sign_and_submit():
            nonlocal offset

            # reset generated tx and te
            builder.tx = None
            builder.te = None

            builder.sequence = source_info.sequence_number + offset
            for signer in signers:
                builder.sign(signer.stellar_seed)

            result = self._internal_client.submit_stellar_transaction(
                base64.b64decode(builder.gen_xdr()), invoice_list)

            if result.tx_error and isinstance(result.tx_error.tx_error,
                                              BadNonceError):
                offset += 1
                raise result.tx_error.tx_error

            return result

        return retry(self._nonce_retry_strategies, _sign_and_submit)
Ejemplo n.º 5
0
    def get_transaction(
        self,
        tx_id: bytes,
        commitment: Optional[Commitment] = Commitment.SINGLE
    ) -> TransactionData:
        """Get a transaction from Agora.

        :param tx_id: The id of the transaction, in bytes.
        :param commitment: The :class:`Commitment <agora.solana.commitment.Commitment>` to use. Only applicable for
            Solana transactions.
        :return: A :class:`TransactionData <agora.model.transaction.TransactionData>` object.
        """
        def _get_transaction():
            req = tx_pb_v4.GetTransactionRequest(
                transaction_id=model_pb_v4.TransactionId(value=tx_id),
                commitment=commitment.to_proto(),
            )
            return self._transaction_stub_v4.GetTransaction(
                req, metadata=self._metadata, timeout=_GRPC_TIMEOUT_SECONDS)

        resp = retry(self._retry_strategies, _get_transaction)

        if resp.item.transaction_id.value:
            return TransactionData.from_proto(resp.item, resp.state)

        return TransactionData(tx_id,
                               TransactionState.from_proto_v4(resp.state))
Ejemplo n.º 6
0
    def get_solana_account_info(
            self,
            public_key: PublicKey,
            commitment: Optional[Commitment] = Commitment.SINGLE
    ) -> AccountInfo:
        """Get the info of a Solana account from Agora.

        :param public_key: The :class:`PublicKey <agora.model.keys.PublicKey>` of the account to request the info for.
        :param commitment: The :class:`Commitment <agora.solana.commitment.Commitment>` to use.
        :return: A :class:`AccountInfo <agora.model.account.AccountInfo>` object.
        """
        def _get():
            resp = self._account_stub_v4.GetAccountInfo(
                account_pb_v4.GetAccountInfoRequest(
                    account_id=model_pb_v4.SolanaAccountId(
                        value=public_key.raw),
                    commitment=commitment.to_proto(),
                ),
                metadata=self._metadata,
                timeout=_GRPC_TIMEOUT_SECONDS)
            if resp.result == account_pb_v4.GetAccountInfoResponse.Result.NOT_FOUND:
                raise AccountNotFoundError

            return AccountInfo.from_proto_v4(resp.account_info)

        return retry(self._retry_strategies, _get)
Ejemplo n.º 7
0
    def request_airdrop(
            self,
            public_key: PublicKey,
            quarks: int,
            commitment: Optional[Commitment] = Commitment.SINGLE) -> bytes:
        def _request_airdrop():
            resp = self._airdrop_stub_v4.RequestAirdrop(
                airdrop_pb_v4.RequestAirdropRequest(
                    account_id=model_pb_v4.SolanaAccountId(
                        value=public_key.raw),
                    quarks=quarks,
                    commitment=commitment.to_proto(),
                ),
                metadata=self._metadata,
                timeout=_GRPC_TIMEOUT_SECONDS)
            if resp.result == airdrop_pb_v4.RequestAirdropResponse.Result.OK:
                return resp.signature.value
            if resp.result == airdrop_pb_v4.RequestAirdropResponse.Result.NOT_FOUND:
                raise AccountNotFoundError()
            if resp.result == airdrop_pb_v4.RequestAirdropResponse.INSUFFICIENT_KIN:
                raise InsufficientBalanceError()

            raise Error(
                f'unexpected response from airdrop service: {resp.result}')

        return retry(self._retry_strategies, _request_airdrop)
Ejemplo n.º 8
0
    def _sign_and_submit_solana_tx(
        self, signers: List[PrivateKey], tx: solana.Transaction, commitment: Commitment,
        invoice_list: Optional[InvoiceList] = None, dedupe_id: Optional[bytes] = None,
    ) -> SubmitTransactionResult:
        def _get_blockhash_and_submit() -> SubmitTransactionResult:
            recent_blockhash = self._internal_client.get_recent_blockhash().blockhash.value
            tx.set_blockhash(recent_blockhash)
            tx.sign(signers)

            # If the transaction isn't signed by the subsidizer, request a signature.
            remote_signed = False
            if tx.signatures[0] == bytes(solana.SIGNATURE_LENGTH):
                sign_result = self._internal_client.sign_transaction(tx, invoice_list)
                if sign_result.invoice_errors:
                    return SubmitTransactionResult(sign_result.tx_id, sign_result.invoice_errors)

                if not sign_result.tx_id:
                    raise PayerRequiredError()

                remote_signed = True
                tx.signatures[0] = sign_result.tx_id

            result = self._internal_client.submit_solana_transaction(tx, invoice_list=invoice_list,
                                                                     commitment=commitment, dedupe_id=dedupe_id)
            if result.errors and isinstance(result.errors.tx_error, BadNonceError):
                if remote_signed:
                    tx.signatures[0] = bytes(solana.SIGNATURE_LENGTH)

                raise result.errors.tx_error

            return result

        return retry(self._nonce_retry_strategies, _get_blockhash_and_submit)
Ejemplo n.º 9
0
    def resolve_token_accounts(self, public_key: PublicKey) -> List[PublicKey]:
        """Resolve the provided public key to its token accounts.

        :param public_key: the :class:`PublicKey <agora.model.keys.PublicKey>` of the owner.
        :return: a list of :class:`PublicKey <agora.model.keys.PublicKey>` objects.
        """
        cached = self._get_from_cache(public_key)
        if cached:
            return cached

        def _call_resolve():
            response = self._account_stub.ResolveTokenAccounts(
                account_pb.ResolveTokenAccountsRequest(
                    account_id=model_pb.SolanaAccountId(value=public_key.raw)))
            if not response.token_accounts:
                raise NoTokenAccountsError()

            return response

        try:
            resp = retry(self._retry_strategies, _call_resolve)
            token_accounts = [
                PublicKey(account_id.value)
                for account_id in resp.token_accounts
            ]
        except NoTokenAccountsError:
            token_accounts = []

        if token_accounts:
            self._set_in_cache(public_key, token_accounts)

        return token_accounts
Ejemplo n.º 10
0
    def get_recent_blockhash(self) -> tx_pb_v4.GetRecentBlockhashResponse:
        def _get_recent_blockhash():
            return self._transaction_stub_v4.GetRecentBlockhash(
                tx_pb_v4.GetRecentBlockhashRequest(),
                metadata=self._metadata,
                timeout=_GRPC_TIMEOUT_SECONDS)

        return retry(self._retry_strategies, _get_recent_blockhash)
Ejemplo n.º 11
0
    def get_minimum_balance_for_rent_exception(self) -> int:
        def _submit_request():
            return self._transaction_stub_v4.GetMinimumBalanceForRentExemption(
                tx_pb.GetMinimumBalanceForRentExemptionRequest(
                    size=token.ACCOUNT_SIZE),
                metadata=self._metadata,
                timeout=_GRPC_TIMEOUT_SECONDS).lamports

        return retry(self._retry_strategies, _submit_request)
Ejemplo n.º 12
0
    def submit_solana_transaction(
            self,
            tx: solana.Transaction,
            invoice_list: Optional[InvoiceList] = None,
            commitment: Optional[Commitment] = Commitment.SINGLE,
            dedupe_id: Optional[bytes] = None) -> SubmitTransactionResult:
        """Submit a Solana transaction to Agora.

        :param tx: The Solana transaction.
        :param invoice_list: (optional) An :class:`InvoiceList <agora.model.invoice.InvoiceList>` to associate with the
            transaction
        :param commitment: The :class:`Commitment <agora.solana.commitment.Commitment>` to use.
        :param dedupe_id: The dedupe ID to use for the transaction submission
        :return: A :class:`SubmitTransactionResult <agora.client.internal.SubmitTransactionResult>` object.
        """

        attempt = 0
        tx_bytes = tx.marshal()

        def _submit_request():
            nonlocal attempt

            attempt += 1
            req = tx_pb.SubmitTransactionRequest(
                transaction=model_pb.Transaction(value=tx_bytes, ),
                invoice_list=invoice_list.to_proto() if invoice_list else None,
                commitment=commitment.to_proto(),
                dedupe_id=dedupe_id,
            )
            resp = self._transaction_stub_v4.SubmitTransaction(
                req, metadata=self._metadata, timeout=_GRPC_TIMEOUT_SECONDS)

            if resp.result == tx_pb.SubmitTransactionResponse.Result.REJECTED:
                raise TransactionRejectedError()
            if resp.result == tx_pb.SubmitTransactionResponse.Result.PAYER_REQUIRED:
                raise PayerRequiredError()

            result = SubmitTransactionResult(tx_id=resp.signature.value)
            if resp.result == tx_pb.SubmitTransactionResponse.Result.ALREADY_SUBMITTED:
                # If this occurs on the first attempt, it's likely due to the submission of two identical transactions
                # in quick succession and we should raise the error to the caller. Otherwise, it's likely that the
                # transaction completed successfully on a previous attempt that failed due to a transient error.
                if attempt == 1:
                    raise AlreadySubmittedError(tx_id=resp.signature.value)
            elif resp.result == tx_pb.SubmitTransactionResponse.Result.FAILED:
                result.errors = TransactionErrors.from_solana_tx(
                    tx, resp.transaction_error, resp.signature.value)
            elif resp.result == tx_pb.SubmitTransactionResponse.Result.INVOICE_ERROR:
                result.invoice_errors = resp.invoice_errors
            elif resp.result != tx_pb.SubmitTransactionResponse.Result.OK:
                raise TransactionError(
                    f'unexpected result from agora: {resp.result}',
                    tx_id=resp.signature.value)

            return result

        return retry(self._retry_strategies, _submit_request)
Ejemplo n.º 13
0
    def resolve_token_accounts(self, public_key: PublicKey) -> List[PublicKey]:
        def _resolve():
            return self._account_stub_v4.ResolveTokenAccounts(
                account_pb_v4.ResolveTokenAccountsRequest(
                    account_id=model_pb_v4.SolanaAccountId(
                        value=public_key.raw)))

        resp = retry(self._retry_strategies, _resolve)
        return [
            PublicKey(token_account.value)
            for token_account in resp.token_accounts
        ]
Ejemplo n.º 14
0
    def create_stellar_account(self, private_key: PrivateKey):
        """Submit a request to Agora to create a Stellar account.

        :param private_key: The :class:`PrivateKey <agora.model.keys.PrivateKey>` of the account to create
        """
        def _create():
            try:
                resp = self._account_stub_v3.CreateAccount(
                    account_pb_v3.CreateAccountRequest(
                        account_id=model_pb_v3.StellarAccountId(
                            value=private_key.public_key.stellar_address), ),
                    metadata=self._metadata,
                    timeout=_GRPC_TIMEOUT_SECONDS)
            except grpc.RpcError as e:
                raise BlockchainVersionError() if self._is_migration_error(
                    e) else e

            if resp.result == account_pb_v3.CreateAccountResponse.Result.EXISTS:
                raise AccountExistsError()

        retry(self._retry_strategies, _create)
Ejemplo n.º 15
0
    def get_blockchain_version(self) -> int:
        """Get the blockchain version to use.

        :return: the blockchain version
        """
        def _get_blockchain_version():
            return self._transaction_stub_v4.GetMinimumKinVersion(
                tx_pb_v4.GetMinimumKinVersionRequest(),
                metadata=self._metadata,
                timeout=_GRPC_TIMEOUT_SECONDS)

        resp = retry(self._retry_strategies, _get_blockchain_version)
        return resp.version
Ejemplo n.º 16
0
    def get_service_config(self) -> tx_pb_v4.GetServiceConfigResponse:
        resp_bytes = self._response_cache.get(_SERVICE_CONFIG_CACHE_KEY)
        if resp_bytes:
            resp = tx_pb_v4.GetServiceConfigResponse()
            resp.ParseFromString(resp_bytes)
            return resp

        def _get_config():
            return self._transaction_stub_v4.GetServiceConfig(
                tx_pb_v4.GetServiceConfigRequest(),
                metadata=self._metadata,
                timeout=_GRPC_TIMEOUT_SECONDS)

        resp = retry(self._retry_strategies, _get_config)
        self._response_cache.set(_SERVICE_CONFIG_CACHE_KEY,
                                 resp.SerializeToString(),
                                 1800)  # cache for 30 min
        return resp
Ejemplo n.º 17
0
    def _sign_and_submit_solana_tx(self,
                                   signers: List[PrivateKey],
                                   tx: solana.Transaction,
                                   commitment: Commitment,
                                   invoice_list: Optional[InvoiceList] = None):
        def _get_blockhash_and_submit():
            recent_blockhash = self._internal_client.get_recent_blockhash(
            ).blockhash.value
            tx.set_blockhash(recent_blockhash)
            tx.sign(signers)

            result = self._internal_client.submit_solana_transaction(
                tx.marshal(), invoice_list=invoice_list, commitment=commitment)
            if result.tx_error and isinstance(result.tx_error.tx_error,
                                              BadNonceError):
                raise result.tx_error.tx_error

            return result

        return retry(self._nonce_retry_strategies, _get_blockhash_and_submit)
Ejemplo n.º 18
0
    def submit_stellar_transaction(self,
                                   tx_bytes: bytes,
                                   invoice_list: Optional[InvoiceList] = None
                                   ) -> SubmitTransactionResult:
        """Submit a Stellar transaction to Agora.

        :param tx_bytes: The transaction envelope xdr, in bytes.
        :param invoice_list: (optional) An :class:`InvoiceList <agora.model.invoice.InvoiceList>` to associate with the
            transaction
        :return: A :class:`SubmitTransactionResult <agora.client.internal.SubmitTransactionResult>` object.
        """
        def _submit():
            req = tx_pb_v3.SubmitTransactionRequest(
                envelope_xdr=tx_bytes,
                invoice_list=invoice_list.to_proto() if invoice_list else None,
            )
            try:
                resp = self._transaction_stub_v3.SubmitTransaction(
                    req,
                    metadata=self._metadata,
                    timeout=_GRPC_TIMEOUT_SECONDS)
            except grpc.RpcError as e:
                raise BlockchainVersionError() if self._is_migration_error(
                    e) else e

            result = SubmitTransactionResult(tx_id=resp.hash.value)
            if resp.result == tx_pb_v3.SubmitTransactionResponse.Result.REJECTED:
                raise TransactionRejectedError()
            elif resp.result == tx_pb_v3.SubmitTransactionResponse.Result.INVOICE_ERROR:
                result.invoice_errors = resp.invoice_errors
            elif resp.result == tx_pb_v3.SubmitTransactionResponse.Result.FAILED:
                result.tx_error = TransactionErrors.from_result(
                    resp.result_xdr)
            elif resp.result != tx_pb_v3.SubmitTransactionResponse.Result.OK:
                raise Error(f'unexpected result from agora: {resp.result}')

            return result

        return retry(self._retry_strategies, _submit)
Ejemplo n.º 19
0
    def create_account(self,
                       private_key: PrivateKey,
                       commitment: Optional[Commitment] = None,
                       subsidizer: Optional[PrivateKey] = None):
        if self._kin_version not in _SUPPORTED_VERSIONS:
            raise UnsupportedVersionError()

        commitment = commitment if commitment else self._default_commitment
        if self._kin_version < 4:
            try:
                return self._internal_client.create_stellar_account(
                    private_key)
            except BlockchainVersionError:
                self._set_kin_version(4)

        def _submit_create_solana_account():
            self._internal_client.create_solana_account(private_key,
                                                        commitment=commitment,
                                                        subsidizer=subsidizer)

        return retry(self._nonce_retry_strategies,
                     _submit_create_solana_account)
Ejemplo n.º 20
0
    def sign_transaction(
            self,
            tx: solana.Transaction,
            invoice_list: Optional[InvoiceList] = None
    ) -> SignTransactionResult:
        """ Submits a transaction

        :param tx:
        :param invoice_list:
        :return: A :class:`SignTransactionResult <agora.client.internal.SignTransactionResult>` object.
        """
        tx_bytes = tx.marshal()

        result = SignTransactionResult()

        def _submit_request():
            req = tx_pb.SignTransactionRequest(
                transaction=model_pb.Transaction(value=tx_bytes, ),
                invoice_list=invoice_list.to_proto() if invoice_list else None,
            )
            resp = self._transaction_stub_v4.SignTransaction(
                req, metadata=self._metadata, timeout=_GRPC_TIMEOUT_SECONDS)

            if resp.signature and len(
                    resp.signature.value) == solana.SIGNATURE_LENGTH:
                result.tx_id = resp.signature.value

            if resp.result == tx_pb.SignTransactionResponse.Result.REJECTED:
                raise TransactionRejectedError()
            elif resp.result == tx_pb.SignTransactionResponse.Result.INVOICE_ERROR:
                result.invoice_errors = resp.invoice_errors
            elif resp.result != tx_pb.SignTransactionResponse.Result.OK:
                raise TransactionError(
                    f'unexpected result from agora: {resp.result}',
                    tx_id=resp.signature.value)

            return result

        return retry(self._retry_strategies, _submit_request)
Ejemplo n.º 21
0
    def _submit_stellar_transaction(
        self,
        tx_bytes: bytes,
        invoice_list: Optional[InvoiceList] = None
    ) -> SubmitStellarTransactionResult:
        """Submit a stellar transaction to Agora.
        :param tx_bytes: The transaction envelope xdr, in bytes
        :param invoice_list: (optional) An :class:`InvoiceList <agora.model.invoice.InvoiceList>` to associate with the
            transaction
        :raise: :exc:`TransactionRejectedError <agora.error.TransactionRejectedError>`: if the transaction was rejected
            by the configured app's webhook
        :raise: :exc:`InvoiceError <agora.error.InvoiceError>`: if the transaction failed for a invoice-related reason.
        :raise: :exc:`TransactionError <agora.error.TransactionError>`: if the transaction failed upon submission to the
            blockchain.
        :return: The transaction hash
        """
        def _submit():
            req = tx_pb.SubmitTransactionRequest(
                envelope_xdr=tx_bytes,
                invoice_list=invoice_list.to_proto() if invoice_list else None,
            )
            resp = self.transaction_stub.SubmitTransaction(
                req, timeout=_GRPC_TIMEOUT_SECONDS)

            result = SubmitStellarTransactionResult(tx_hash=resp.hash.value)
            if resp.result == tx_pb.SubmitTransactionResponse.Result.REJECTED:
                raise TransactionRejectedError()
            elif resp.result == tx_pb.SubmitTransactionResponse.Result.INVOICE_ERROR:
                result.invoice_errors = resp.invoice_errors
            elif resp.result == tx_pb.SubmitTransactionResponse.Result.FAILED:
                result.tx_error = TransactionErrors.from_result(
                    resp.result_xdr)
            elif resp.result != tx_pb.SubmitTransactionResponse.Result.OK:
                raise Error("unexpected result from agora: {}".format(
                    resp.result))

            return result

        return retry(self.retry_strategies, _submit)
Ejemplo n.º 22
0
    def _sign_and_submit_builder(
        self,
        signers: List[PrivateKey],
        builder: kin_base.Builder,
        invoice_list: Optional[InvoiceList] = None
    ) -> SubmitStellarTransactionResult:
        def _sign_and_submit():
            builder.te = None  # reset the envelope
            source_info = self._get_stellar_account_info(signers[0].public_key)
            builder.sequence = source_info.sequence_number + 1

            for signer in signers:
                builder.sign(signer.stellar_seed)

            result = self._submit_stellar_transaction(
                base64.b64decode(builder.gen_xdr()), invoice_list)
            if result.tx_error and isinstance(result.tx_error.tx_error,
                                              BadNonceError):
                raise result.tx_error.tx_error

            return result

        return retry(self.nonce_retry_strategies, _sign_and_submit)
Ejemplo n.º 23
0
    def resolve_token_accounts(
            self, public_key: PublicKey,
            include_account_info: bool) -> List[AccountInfo]:
        """Resolves token accounts using Agora.

        :param public_key: the public key of the account to resolve token accounts for.
        :param include_account_info: indicates whether to include token account info in the response
        :return: A list of :class:`AccountInfo <agora.model.account.AccountInfo>` objects each representing a token
            account. Information other than AccountInfo.account_id will only be populated if `include_account_info` is
            True.
        """
        def _resolve():
            return self._account_stub_v4.ResolveTokenAccounts(
                account_pb.ResolveTokenAccountsRequest(
                    account_id=model_pb.SolanaAccountId(value=public_key.raw),
                    include_account_info=include_account_info,
                ),
                metadata=self._metadata,
                timeout=_GRPC_TIMEOUT_SECONDS)

        resp = retry(self._retry_strategies, _resolve)

        # This is currently in place for backward compat with the server - `token_accounts` is deprecated
        if resp.token_accounts and len(resp.token_account_infos) != len(
                resp.token_accounts):
            # If we aren't requesting account info, we can interpolate the results ourselves.
            if not include_account_info:
                return [
                    AccountInfo(PublicKey(a.value))
                    for a in resp.token_accounts
                ]
            else:
                raise Error(
                    'server does not support resolving with account info')

        return [AccountInfo.from_proto(a) for a in resp.token_account_infos]
Ejemplo n.º 24
0
    def create_solana_account(
            self,
            private_key: PrivateKey,
            commitment: Optional[Commitment] = Commitment.SINGLE,
            subsidizer: Optional[PrivateKey] = None):
        """Submit a request to Agora to create a Solana account.

        :param private_key: The :class:`PrivateKey <agora.model.keys.PrivateKey>` of the account to create
        :param commitment: The :class:`Commitment <agora.solana.commitment.Commitment>` to use.
        :param subsidizer: The :class:`PrivateKey <agora.model.keys.PrivateKey>` of the account to use as the
            transaction payer.
        """
        def _create():
            nonlocal subsidizer

            service_config_resp = self.get_service_config()
            if not service_config_resp.subsidizer_account.value and not subsidizer:
                raise NoSubsidizerError()

            subsidizer_id = (subsidizer.public_key
                             if subsidizer else PublicKey(
                                 service_config_resp.subsidizer_account.value))

            recent_blockhash_future = self._transaction_stub_v4.GetRecentBlockhash.future(
                tx_pb_v4.GetRecentBlockhashRequest(),
                metadata=self._metadata,
                timeout=_GRPC_TIMEOUT_SECONDS)
            min_balance_future = self._transaction_stub_v4.GetMinimumBalanceForRentExemption.future(
                tx_pb_v4.GetMinimumBalanceForRentExemptionRequest(
                    size=token.ACCOUNT_SIZE),
                metadata=self._metadata,
                timeout=_GRPC_TIMEOUT_SECONDS)
            recent_blockhash_resp = recent_blockhash_future.result()
            min_balance_resp = min_balance_future.result()

            token_program = PublicKey(service_config_resp.token_program.value)
            transaction = Transaction.new(subsidizer_id, [
                system.create_account(
                    subsidizer_id,
                    private_key.public_key,
                    token_program,
                    min_balance_resp.lamports,
                    token.ACCOUNT_SIZE,
                ),
                token.initialize_account(
                    private_key.public_key,
                    PublicKey(service_config_resp.token.value),
                    private_key.public_key,
                    token_program,
                ),
                token.set_authority(
                    private_key.public_key,
                    private_key.public_key,
                    token.AuthorityType.CloseAccount,
                    token_program,
                    new_authority=subsidizer_id,
                )
            ])
            transaction.set_blockhash(recent_blockhash_resp.blockhash.value)
            transaction.sign([private_key])
            if subsidizer:
                transaction.sign([subsidizer])

            req = account_pb_v4.CreateAccountRequest(
                transaction=model_pb_v4.Transaction(
                    value=transaction.marshal()),
                commitment=commitment.to_proto(),
            )
            resp = self._account_stub_v4.CreateAccount(
                req, metadata=self._metadata, timeout=_GRPC_TIMEOUT_SECONDS)

            if resp.result == account_pb_v4.CreateAccountResponse.Result.EXISTS:
                raise AccountExistsError()
            if resp.result == account_pb_v4.CreateAccountResponse.Result.PAYER_REQUIRED:
                raise PayerRequiredError()
            if resp.result == account_pb_v4.CreateAccountResponse.Result.BAD_NONCE:
                raise BadNonceError()
            if resp.result != account_pb_v4.CreateAccountResponse.Result.OK:
                raise Error(f'unexpected result from agora: {resp.result}')

        retry(self._retry_strategies, _create)
Ejemplo n.º 25
0
 def create_account(self, private_key: PrivateKey, commitment: Optional[Commitment] = None,
                    subsidizer: Optional[PrivateKey] = None):
     commitment = commitment if commitment else self._default_commitment
     return retry(self._nonce_retry_strategies, self._create_solana_account, private_key, commitment,
                  subsidizer)