feat(solana): add additional info with token account

vacuum/feat/solana
gabrielkerekes 6 months ago
parent e3eb913b58
commit 79b04d72a3

@ -42,6 +42,23 @@ message SolanaAddress {
required string address = 1; // Solana address as Base58 encoded string
}
/**
* @embed
*/
message SolanaTxTokenAccountInfo {
required string base_address = 1;
required string token_program = 2;
required string token_mint = 3;
required string token_account = 4;
}
/**
* @embed
*/
message SolanaTxAdditionalInfo {
repeated SolanaTxTokenAccountInfo token_accounts_infos = 1;
}
/**
* Request: Ask device to sign a Solana transaction
* @start
@ -51,6 +68,7 @@ message SolanaAddress {
message SolanaSignTx {
repeated uint32 address_n = 1; // BIP-32 path to derive the key to sign with
required bytes serialized_tx = 2; // serialized tx to be signed
optional SolanaTxAdditionalInfo additional_info = 3;
}
/**

@ -693,8 +693,12 @@ if not utils.BITCOIN_ONLY:
import apps.solana.get_public_key
apps.solana.layout
import apps.solana.layout
apps.solana.predefined_transaction
import apps.solana.predefined_transaction
apps.solana.sign_tx
import apps.solana.sign_tx
apps.solana.token_account
import apps.solana.token_account
apps.solana.transaction
import apps.solana.transaction
apps.solana.transaction.instruction

@ -15,14 +15,7 @@ from apps.common.paths import address_n_to_str
from .types import AddressType
if TYPE_CHECKING:
from trezor.ui.layouts import PropertyType
from .transaction.instructions import (
AssociatedTokenAccountProgramCreateInstruction,
Instruction,
SystemProgramTransferInstruction,
TokenProgramTransferCheckedInstruction,
)
from .transaction.instructions import Instruction, SystemProgramTransferInstruction
from .types import AddressReference
@ -285,28 +278,30 @@ async def confirm_system_transfer(
async def confirm_token_transfer(
create_token_account_instruction: AssociatedTokenAccountProgramCreateInstruction
| None,
transfer_token_instruction: TokenProgramTransferCheckedInstruction,
destination_account: bytes,
token_account: bytes,
token_mint: bytes,
amount: int,
decimals: int,
fee: int,
signer_path: list[int],
blockhash: bytes,
):
recipient_props: list[PropertyType] = [
("", base58.encode(transfer_token_instruction.destination_account[0]))
]
if create_token_account_instruction is not None:
recipient_props.append(("(account will be created)", ""))
await confirm_properties(
"confirm_recipient",
"Recipient",
recipient_props,
await confirm_value(
title="Recipient",
value=base58.encode(destination_account),
description="",
br_type="confirm_recipient",
br_code=ButtonRequestType.ConfirmOutput,
verb="CONTINUE",
info_items=(("Associated token account:", base58.encode(token_account)),)
if token_account != destination_account
else None,
)
await confirm_value(
title="Token address",
value=base58.encode(transfer_token_instruction.token_mint[0]),
value=base58.encode(token_mint),
description="",
br_type="confirm_token_address",
br_code=ButtonRequestType.ConfirmOutput,
@ -314,8 +309,8 @@ async def confirm_token_transfer(
)
await confirm_custom_transaction(
transfer_token_instruction.amount,
transfer_token_instruction.decimals,
amount,
decimals,
"[TOKEN]",
fee,
signer_path,

@ -0,0 +1,191 @@
from typing import TYPE_CHECKING
from trezor.crypto import base58
from .transaction import Transaction
from .transaction.instructions import (
AssociatedTokenAccountProgramCreateInstruction,
Instruction,
Token2022ProgramTransferCheckedInstruction,
TokenProgramTransferCheckedInstruction,
)
if TYPE_CHECKING:
from trezor.messages import SolanaTxAdditionalInfo
TransferTokenInstruction = (
TokenProgramTransferCheckedInstruction
| Token2022ProgramTransferCheckedInstruction
)
def get_token_transfer_instructions(
instructions: list[Instruction],
) -> list[TransferTokenInstruction]:
return [
instruction
for instruction in instructions
if TokenProgramTransferCheckedInstruction.is_type_of(instruction)
or Token2022ProgramTransferCheckedInstruction.is_type_of(instruction)
]
def get_create_associated_token_account_instructions(
instructions: list[Instruction],
) -> list[AssociatedTokenAccountProgramCreateInstruction]:
return [
instruction
for instruction in instructions
if AssociatedTokenAccountProgramCreateInstruction.is_type_of(instruction)
]
def is_predefined_token_transfer(
instructions: list[Instruction],
):
"""
Checks that the transaction consists of one or zero create token account instructions
and one or more transfer token instructions. Also checks that the token program, token mint
and destination in the instructions are the same. I.e. valid instructions can be:
[transfer]
[transfer, *transfer]
[create account, transfer]
[create account, transfer, *transfer]
"""
create_token_account_instructions = (
get_create_associated_token_account_instructions(instructions)
)
transfer_token_instructions = get_token_transfer_instructions(instructions)
if len(create_token_account_instructions) + len(transfer_token_instructions) != len(
instructions
):
# there are also other instructions
return False
if len(create_token_account_instructions) > 1:
# there is more than one create token account instruction
return False
if (
len(create_token_account_instructions) == 1
and instructions[0] != create_token_account_instructions[0]
):
# create account instruction has to be the first instruction
return False
if len(transfer_token_instructions) == 0:
# there are no transfer token instructions
return False
token_program = transfer_token_instructions[0].program_id
token_mint = transfer_token_instructions[0].token_mint[0]
token_account = transfer_token_instructions[0].destination_account[0]
owner = transfer_token_instructions[0].owner[0]
for transfer_token_instruction in transfer_token_instructions:
if (
transfer_token_instruction.program_id != token_program
or transfer_token_instruction.token_mint[0] != token_mint
or transfer_token_instruction.destination_account[0] != token_account
or transfer_token_instruction.owner[0] != owner
):
# there are different token accounts, don't handle as predefined
return False
# at this point there can only be zero or one create token account instructions
create_token_account_instruction = (
create_token_account_instructions[0]
if len(create_token_account_instructions) == 1
else None
)
if create_token_account_instruction is not None and (
create_token_account_instruction.spl_token[0] != base58.decode(token_program)
or create_token_account_instruction.token_mint[0] != token_mint
or create_token_account_instruction.associated_token_account[0] != token_account
):
# there are different token accounts, don't handle as predefined
return False
return True
async def try_confirm_token_transfer_transaction(
transaction: Transaction,
fee: int,
signer_path: list[int],
blockhash: bytes,
additional_info: SolanaTxAdditionalInfo | None = None,
) -> bool:
from .layout import confirm_token_transfer
from .token_account import try_get_token_account_base_address
if not is_predefined_token_transfer(
transaction.instructions,
):
return False
transfer_token_instructions = get_token_transfer_instructions(
transaction.instructions
)
# in is_predefined_token_transfer we made sure that these values are the same
# for all the transfer token instructions
token_program = base58.decode(transfer_token_instructions[0].program_id)
token_mint = transfer_token_instructions[0].token_mint[0]
token_account = transfer_token_instructions[0].destination_account[0]
base_address = (
try_get_token_account_base_address(
token_account,
token_program,
token_mint,
additional_info.token_accounts_infos,
)
if additional_info is not None
else None
)
total_token_amount = sum(
[
transfer_token_instruction.amount
for transfer_token_instruction in transfer_token_instructions
]
)
await confirm_token_transfer(
token_account if base_address is None else base_address,
token_account,
token_mint,
total_token_amount,
transfer_token_instructions[0].decimals,
fee,
signer_path,
blockhash,
)
return True
async def try_confirm_predefined_transaction(
transaction: Transaction,
fee: int,
signer_path: list[int],
blockhash: bytes,
additional_info: SolanaTxAdditionalInfo | None = None,
) -> bool:
from .layout import confirm_system_transfer
from .transaction.instructions import SystemProgramTransferInstruction
instructions = transaction.instructions
instructions_count = len(instructions)
if instructions_count == 1:
if SystemProgramTransferInstruction.is_type_of(instructions[0]):
await confirm_system_transfer(instructions[0], fee, signer_path, blockhash)
return True
return await try_confirm_token_transfer_transaction(
transaction, fee, signer_path, blockhash, additional_info
)

@ -26,6 +26,7 @@ async def sign_tx(
from apps.common import seed
from .layout import confirm_transaction
from .predefined_transaction import try_confirm_predefined_transaction
address_n = msg.address_n # local_cache_attribute
serialized_tx = msg.serialized_tx # local_cache_attribute
@ -54,7 +55,7 @@ async def sign_tx(
fee = calculate_fee(transaction)
if not await try_confirm_predefined_transaction(
transaction, fee, address_n, transaction.blockhash
transaction, fee, address_n, transaction.blockhash, msg.additional_info
):
await confirm_instructions(address_n, signer_public_key, transaction)
await confirm_transaction(
@ -68,56 +69,6 @@ async def sign_tx(
return SolanaTxSignature(signature=signature)
async def try_confirm_predefined_transaction(
transaction: Transaction, fee: int, signer_path: list[int], blockhash: bytes
) -> bool:
from .layout import confirm_system_transfer, confirm_token_transfer
from .transaction.instructions import (
AssociatedTokenAccountProgramCreateInstruction,
SystemProgramTransferInstruction,
TokenProgramTransferCheckedInstruction,
)
instructions = transaction.instructions
instructions_count = len(instructions)
if instructions_count == 1:
if SystemProgramTransferInstruction.is_type_of(instructions[0]):
await confirm_system_transfer(instructions[0], fee, signer_path, blockhash)
return True
if TokenProgramTransferCheckedInstruction.is_type_of(instructions[0]):
await confirm_token_transfer(
None, instructions[0], fee, signer_path, blockhash
)
return True
elif instructions_count == 2:
if AssociatedTokenAccountProgramCreateInstruction.is_type_of(
instructions[0]
) and TokenProgramTransferCheckedInstruction.is_type_of(instructions[1]):
create_token_account_instruction = instructions[0]
transfer_token_instruction = instructions[1]
# If the account being created is different from the recipient account we need
# to display all the instruction information.
if (
create_token_account_instruction.associated_token_account[0]
!= transfer_token_instruction.destination_account[0]
):
return False
await confirm_token_transfer(
instructions[0],
instructions[1],
fee,
signer_path,
blockhash,
)
return True
return False
async def confirm_instructions(
signer_path: list[int], signer_public_key: bytes, transaction: Transaction
) -> None:

@ -0,0 +1,65 @@
from typing import TYPE_CHECKING
from trezor.crypto import base58
if TYPE_CHECKING:
from trezor.messages import SolanaTxTokenAccountInfo
ASSOCIATED_TOKEN_ACCOUNT_PROGRAM = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
SEED_CONSTANT = "ProgramDerivedAddress"
def assert_is_associated_token_account(
base_address: bytes,
token_account_address: bytes,
token_program: bytes,
token_mint: bytes,
) -> None:
from trezor.crypto.hashlib import sha256
# based on the following sources:
# https://spl.solana.com/associated-token-account#finding-the-associated-token-account-address
# https://github.com/solana-labs/solana/blob/8fbe033eaca693ed8c3e90b19bc3f61b32885e5e/sdk/program/src/pubkey.rs#L495
for seed_bump in range(255, 0, -1):
seed = (
base_address
+ token_program
+ token_mint
+ bytes([seed_bump])
+ base58.decode(ASSOCIATED_TOKEN_ACCOUNT_PROGRAM)
+ SEED_CONSTANT.encode("utf-8")
)
account = sha256(seed).digest()
if account == token_account_address:
return
raise ValueError
def try_get_token_account_base_address(
token_account_address: bytes,
token_program: bytes,
token_mint: bytes,
token_accounts_infos: list[SolanaTxTokenAccountInfo],
) -> bytes | None:
for token_account_info in token_accounts_infos:
if (
base58.decode(token_account_info.token_account) == token_account_address
and base58.decode(token_account_info.token_program) == token_program
and base58.decode(token_account_info.token_mint) == token_mint
):
base_address = base58.decode(token_account_info.base_address)
assert_is_associated_token_account(
base_address,
token_account_address,
token_program,
token_mint,
)
return base_address
return None

@ -5257,15 +5257,51 @@ if TYPE_CHECKING:
def is_type_of(cls, msg: Any) -> TypeGuard["SolanaAddress"]:
return isinstance(msg, cls)
class SolanaTxTokenAccountInfo(protobuf.MessageType):
base_address: "str"
token_program: "str"
token_mint: "str"
token_account: "str"
def __init__(
self,
*,
base_address: "str",
token_program: "str",
token_mint: "str",
token_account: "str",
) -> None:
pass
@classmethod
def is_type_of(cls, msg: Any) -> TypeGuard["SolanaTxTokenAccountInfo"]:
return isinstance(msg, cls)
class SolanaTxAdditionalInfo(protobuf.MessageType):
token_accounts_infos: "list[SolanaTxTokenAccountInfo]"
def __init__(
self,
*,
token_accounts_infos: "list[SolanaTxTokenAccountInfo] | None" = None,
) -> None:
pass
@classmethod
def is_type_of(cls, msg: Any) -> TypeGuard["SolanaTxAdditionalInfo"]:
return isinstance(msg, cls)
class SolanaSignTx(protobuf.MessageType):
address_n: "list[int]"
serialized_tx: "bytes"
additional_info: "SolanaTxAdditionalInfo | None"
def __init__(
self,
*,
serialized_tx: "bytes",
address_n: "list[int] | None" = None,
additional_info: "SolanaTxAdditionalInfo | None" = None,
) -> None:
pass

@ -0,0 +1,206 @@
from common import *
from trezor.crypto import base58
from apps.solana.predefined_transaction import is_predefined_token_transfer
from apps.solana.transaction.instruction import Instruction
from apps.solana.transaction.instructions import (
ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID,
ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID_INS_CREATE,
SYSTEM_PROGRAM_ID,
SYSTEM_PROGRAM_ID_INS_TRANSFER,
TOKEN_2022_PROGRAM_ID,
TOKEN_2022_PROGRAM_ID_INS_TRANSFER_CHECKED,
TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID_INS_TRANSFER_CHECKED,
)
def create_mock_instruction(
program_id: str, instruction_id: int, parsed_data: dict[str, Any]
):
instruction = Instruction(
instruction_data=b"",
program_id=program_id,
accounts=[],
instruction_id=instruction_id,
property_templates=[],
accounts_template=[],
ui_properties=[],
ui_name="",
is_program_supported=True,
is_instruction_supported=True,
supports_multisig=False,
is_deprecated_warning=None,
)
instruction.parsed_data = parsed_data
return instruction
def create_transfer_token_instruction(
program_id=TOKEN_PROGRAM_ID,
instruction_id=TOKEN_PROGRAM_ID_INS_TRANSFER_CHECKED,
token_mint="GHArwcWCuk9WkUG4XKUbt935rKfmBmywbEWyFxdH3mou",
destination_account="92YgwqTtTWB7qY92JT6mbL2WCmhAs7LPZL4jLcizNfwx",
owner="14CCvQzQzHCVgZM3j9soPnXuJXh1RmCfwLVUcdfbZVBS",
):
return create_mock_instruction(
program_id,
instruction_id,
{
"token_mint": (base58.decode(token_mint),),
"destination_account": (base58.decode(destination_account),),
"owner": (base58.decode(owner),),
},
)
def create_create_token_account_instruction(
token_mint="GHArwcWCuk9WkUG4XKUbt935rKfmBmywbEWyFxdH3mou",
associated_token_account="92YgwqTtTWB7qY92JT6mbL2WCmhAs7LPZL4jLcizNfwx",
spl_token=TOKEN_PROGRAM_ID,
):
return create_mock_instruction(
ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID,
ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID_INS_CREATE,
{
"token_mint": (base58.decode(token_mint),),
"associated_token_account": (base58.decode(associated_token_account),),
"spl_token": (base58.decode(spl_token),),
},
)
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
class TestSolanaPredefinedTransactions(unittest.TestCase):
def test_is_predefined_token_transfer(self):
# note: if there are multiple transfer instructions they are the same
# in the tests because that's the info the test cares about. In reality
# the instructions can differ in the destination account and amount.
valid_test_cases = [
[create_transfer_token_instruction()],
[create_transfer_token_instruction(), create_transfer_token_instruction()],
[
create_create_token_account_instruction(),
create_transfer_token_instruction(),
],
[
create_create_token_account_instruction(),
create_transfer_token_instruction(),
create_transfer_token_instruction(),
],
[
create_create_token_account_instruction(
spl_token=TOKEN_2022_PROGRAM_ID
),
create_transfer_token_instruction(
program_id=TOKEN_2022_PROGRAM_ID,
instruction_id=TOKEN_2022_PROGRAM_ID_INS_TRANSFER_CHECKED,
),
create_transfer_token_instruction(
program_id=TOKEN_2022_PROGRAM_ID,
instruction_id=TOKEN_2022_PROGRAM_ID_INS_TRANSFER_CHECKED,
),
],
]
invalid_test_cases = [
# only create account
[
create_create_token_account_instruction(),
],
# there are other instructions
[
create_transfer_token_instruction(),
create_mock_instruction(
SYSTEM_PROGRAM_ID, SYSTEM_PROGRAM_ID_INS_TRANSFER, {}
),
],
# multiple create account instructions
[
create_create_token_account_instruction(),
create_create_token_account_instruction(),
create_transfer_token_instruction(),
],
# transfer instructions program_id mismatch
[
create_transfer_token_instruction(
program_id=TOKEN_PROGRAM_ID,
instruction_id=TOKEN_PROGRAM_ID_INS_TRANSFER_CHECKED,
),
create_transfer_token_instruction(
program_id=TOKEN_2022_PROGRAM_ID,
instruction_id=TOKEN_2022_PROGRAM_ID_INS_TRANSFER_CHECKED,
),
],
# transfer instructions token_mint mismatch
[
create_transfer_token_instruction(
token_mint="GHArwcWCuk9WkUG4XKUbt935rKfmBmywbEWyFxdH3mou"
),
create_transfer_token_instruction(
token_mint="GZDphoFQJ9m7uRU7TdS8cVDGFvsiQbcaY3n5mdoQHmDj"
),
],
# transfer instructions destination mismatch
[
create_transfer_token_instruction(
destination_account="92YgwqTtTWB7qY92JT6mbL2WCmhAs7LPZL4jLcizNfwx"
),
create_transfer_token_instruction(
destination_account="74pZnim7gywyschy4MGkW6eZURv1DBXqwHTCqLRk63wz"
),
],
# transfer instructions owner mismatch
[
create_transfer_token_instruction(
owner="14CCvQzQzHCVgZM3j9soPnXuJXh1RmCfwLVUcdfbZVBS"
),
create_transfer_token_instruction(
owner="BVRFH6vt5bNXub6WnnFRgaHFTcbkjBrf7x1troU1izGg"
),
],
# token program mismatch
[
create_create_token_account_instruction(
spl_token=TOKEN_2022_PROGRAM_ID
),
create_transfer_token_instruction(
program_id=TOKEN_PROGRAM_ID,
),
],
# create account token_mint mismatch
[
create_create_token_account_instruction(
token_mint="GZDphoFQJ9m7uRU7TdS8cVDGFvsiQbcaY3n5mdoQHmDj"
),
create_transfer_token_instruction(
token_mint="GHArwcWCuk9WkUG4XKUbt935rKfmBmywbEWyFxdH3mou",
),
],
# create account associated_token_account mismatch
[
create_create_token_account_instruction(
associated_token_account="74pZnim7gywyschy4MGkW6eZURv1DBXqwHTCqLRk63wz"
),
create_transfer_token_instruction(
destination_account="92YgwqTtTWB7qY92JT6mbL2WCmhAs7LPZL4jLcizNfwx",
),
],
# create account is not first
[
create_transfer_token_instruction(),
create_create_token_account_instruction(),
],
]
for instructions in valid_test_cases:
self.assertTrue(is_predefined_token_transfer(instructions))
for instructions in invalid_test_cases:
self.assertFalse(is_predefined_token_transfer(instructions))
if __name__ == "__main__":
unittest.main()

@ -1,4 +1,5 @@
from typing import TYPE_CHECKING
import json
from typing import TYPE_CHECKING, Optional, TextIO
import click
@ -50,12 +51,35 @@ def get_address(
@cli.command()
@click.argument("serialized_tx", type=str)
@click.option("-n", "--address", default=DEFAULT_PATH, help=PATH_HELP)
@click.option("-a", "--additional-information-file", type=click.File("r"))
@with_client
def sign_tx(
client: "TrezorClient",
address: str,
serialized_tx: str,
additional_information_file: Optional[TextIO],
) -> messages.SolanaTxSignature:
"""Sign Solana transaction."""
address_n = tools.parse_path(address)
return solana.sign_tx(client, address_n, bytes.fromhex(serialized_tx))
additional_information = None
if additional_information_file:
raw_additional_information = json.load(additional_information_file)
additional_information = messages.SolanaTxAdditionalInfo(
token_accounts_infos=[
messages.SolanaTxTokenAccountInfo(
base_address=token_account["base_address"],
token_program=token_account["token_program"],
token_mint=token_account["token_mint"],
token_account=token_account["token_account"],
)
for token_account in raw_additional_information["token_accounts_infos"]
]
)
return solana.sign_tx(
client,
address_n,
bytes.fromhex(serialized_tx),
additional_information,
)

@ -6716,11 +6716,49 @@ class SolanaAddress(protobuf.MessageType):
self.address = address
class SolanaTxTokenAccountInfo(protobuf.MessageType):
MESSAGE_WIRE_TYPE = None
FIELDS = {
1: protobuf.Field("base_address", "string", repeated=False, required=True),
2: protobuf.Field("token_program", "string", repeated=False, required=True),
3: protobuf.Field("token_mint", "string", repeated=False, required=True),
4: protobuf.Field("token_account", "string", repeated=False, required=True),
}
def __init__(
self,
*,
base_address: "str",
token_program: "str",
token_mint: "str",
token_account: "str",
) -> None:
self.base_address = base_address
self.token_program = token_program
self.token_mint = token_mint
self.token_account = token_account
class SolanaTxAdditionalInfo(protobuf.MessageType):
MESSAGE_WIRE_TYPE = None
FIELDS = {
1: protobuf.Field("token_accounts_infos", "SolanaTxTokenAccountInfo", repeated=True, required=False, default=None),
}
def __init__(
self,
*,
token_accounts_infos: Optional[Sequence["SolanaTxTokenAccountInfo"]] = None,
) -> None:
self.token_accounts_infos: Sequence["SolanaTxTokenAccountInfo"] = token_accounts_infos if token_accounts_infos is not None else []
class SolanaSignTx(protobuf.MessageType):
MESSAGE_WIRE_TYPE = 904
FIELDS = {
1: protobuf.Field("address_n", "uint32", repeated=True, required=False, default=None),
2: protobuf.Field("serialized_tx", "bytes", repeated=False, required=True),
3: protobuf.Field("additional_info", "SolanaTxAdditionalInfo", repeated=False, required=False, default=None),
}
def __init__(
@ -6728,9 +6766,11 @@ class SolanaSignTx(protobuf.MessageType):
*,
serialized_tx: "bytes",
address_n: Optional[Sequence["int"]] = None,
additional_info: Optional["SolanaTxAdditionalInfo"] = None,
) -> None:
self.address_n: Sequence["int"] = address_n if address_n is not None else []
self.serialized_tx = serialized_tx
self.additional_info = additional_info
class SolanaTxSignature(protobuf.MessageType):

@ -1,4 +1,4 @@
from typing import TYPE_CHECKING, List
from typing import TYPE_CHECKING, List, Optional
from . import messages
from .tools import expect
@ -40,10 +40,12 @@ def sign_tx(
client: "TrezorClient",
address_n: List[int],
serialized_tx: bytes,
additional_info: Optional[messages.SolanaTxAdditionalInfo],
) -> "MessageType":
return client.call(
messages.SolanaSignTx(
address_n=address_n,
serialized_tx=serialized_tx,
additional_info=additional_info,
)
)

@ -708,6 +708,466 @@ impl ::protobuf::reflect::ProtobufValue for SolanaAddress {
type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
}
// @@protoc_insertion_point(message:hw.trezor.messages.solana.SolanaTxTokenAccountInfo)
#[derive(PartialEq,Clone,Default,Debug)]
pub struct SolanaTxTokenAccountInfo {
// message fields
// @@protoc_insertion_point(field:hw.trezor.messages.solana.SolanaTxTokenAccountInfo.base_address)
pub base_address: ::std::option::Option<::std::string::String>,
// @@protoc_insertion_point(field:hw.trezor.messages.solana.SolanaTxTokenAccountInfo.token_program)
pub token_program: ::std::option::Option<::std::string::String>,
// @@protoc_insertion_point(field:hw.trezor.messages.solana.SolanaTxTokenAccountInfo.token_mint)
pub token_mint: ::std::option::Option<::std::string::String>,
// @@protoc_insertion_point(field:hw.trezor.messages.solana.SolanaTxTokenAccountInfo.token_account)
pub token_account: ::std::option::Option<::std::string::String>,
// special fields
// @@protoc_insertion_point(special_field:hw.trezor.messages.solana.SolanaTxTokenAccountInfo.special_fields)
pub special_fields: ::protobuf::SpecialFields,
}
impl<'a> ::std::default::Default for &'a SolanaTxTokenAccountInfo {
fn default() -> &'a SolanaTxTokenAccountInfo {
<SolanaTxTokenAccountInfo as ::protobuf::Message>::default_instance()
}
}
impl SolanaTxTokenAccountInfo {
pub fn new() -> SolanaTxTokenAccountInfo {
::std::default::Default::default()
}
// required string base_address = 1;
pub fn base_address(&self) -> &str {
match self.base_address.as_ref() {
Some(v) => v,
None => "",
}
}
pub fn clear_base_address(&mut self) {
self.base_address = ::std::option::Option::None;
}
pub fn has_base_address(&self) -> bool {
self.base_address.is_some()
}
// Param is passed by value, moved
pub fn set_base_address(&mut self, v: ::std::string::String) {
self.base_address = ::std::option::Option::Some(v);
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_base_address(&mut self) -> &mut ::std::string::String {
if self.base_address.is_none() {
self.base_address = ::std::option::Option::Some(::std::string::String::new());
}
self.base_address.as_mut().unwrap()
}
// Take field
pub fn take_base_address(&mut self) -> ::std::string::String {
self.base_address.take().unwrap_or_else(|| ::std::string::String::new())
}
// required string token_program = 2;
pub fn token_program(&self) -> &str {
match self.token_program.as_ref() {
Some(v) => v,
None => "",
}
}
pub fn clear_token_program(&mut self) {
self.token_program = ::std::option::Option::None;
}
pub fn has_token_program(&self) -> bool {
self.token_program.is_some()
}
// Param is passed by value, moved
pub fn set_token_program(&mut self, v: ::std::string::String) {
self.token_program = ::std::option::Option::Some(v);
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_token_program(&mut self) -> &mut ::std::string::String {
if self.token_program.is_none() {
self.token_program = ::std::option::Option::Some(::std::string::String::new());
}
self.token_program.as_mut().unwrap()
}
// Take field
pub fn take_token_program(&mut self) -> ::std::string::String {
self.token_program.take().unwrap_or_else(|| ::std::string::String::new())
}
// required string token_mint = 3;
pub fn token_mint(&self) -> &str {
match self.token_mint.as_ref() {
Some(v) => v,
None => "",
}
}
pub fn clear_token_mint(&mut self) {
self.token_mint = ::std::option::Option::None;
}
pub fn has_token_mint(&self) -> bool {
self.token_mint.is_some()
}
// Param is passed by value, moved
pub fn set_token_mint(&mut self, v: ::std::string::String) {
self.token_mint = ::std::option::Option::Some(v);
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_token_mint(&mut self) -> &mut ::std::string::String {
if self.token_mint.is_none() {
self.token_mint = ::std::option::Option::Some(::std::string::String::new());
}
self.token_mint.as_mut().unwrap()
}
// Take field
pub fn take_token_mint(&mut self) -> ::std::string::String {
self.token_mint.take().unwrap_or_else(|| ::std::string::String::new())
}
// required string token_account = 4;
pub fn token_account(&self) -> &str {
match self.token_account.as_ref() {
Some(v) => v,
None => "",
}
}
pub fn clear_token_account(&mut self) {
self.token_account = ::std::option::Option::None;
}
pub fn has_token_account(&self) -> bool {
self.token_account.is_some()
}
// Param is passed by value, moved
pub fn set_token_account(&mut self, v: ::std::string::String) {
self.token_account = ::std::option::Option::Some(v);
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_token_account(&mut self) -> &mut ::std::string::String {
if self.token_account.is_none() {
self.token_account = ::std::option::Option::Some(::std::string::String::new());
}
self.token_account.as_mut().unwrap()
}
// Take field
pub fn take_token_account(&mut self) -> ::std::string::String {
self.token_account.take().unwrap_or_else(|| ::std::string::String::new())
}
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
let mut fields = ::std::vec::Vec::with_capacity(4);
let mut oneofs = ::std::vec::Vec::with_capacity(0);
fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
"base_address",
|m: &SolanaTxTokenAccountInfo| { &m.base_address },
|m: &mut SolanaTxTokenAccountInfo| { &mut m.base_address },
));
fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
"token_program",
|m: &SolanaTxTokenAccountInfo| { &m.token_program },
|m: &mut SolanaTxTokenAccountInfo| { &mut m.token_program },
));
fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
"token_mint",
|m: &SolanaTxTokenAccountInfo| { &m.token_mint },
|m: &mut SolanaTxTokenAccountInfo| { &mut m.token_mint },
));
fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
"token_account",
|m: &SolanaTxTokenAccountInfo| { &m.token_account },
|m: &mut SolanaTxTokenAccountInfo| { &mut m.token_account },
));
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<SolanaTxTokenAccountInfo>(
"SolanaTxTokenAccountInfo",
fields,
oneofs,
)
}
}
impl ::protobuf::Message for SolanaTxTokenAccountInfo {
const NAME: &'static str = "SolanaTxTokenAccountInfo";
fn is_initialized(&self) -> bool {
if self.base_address.is_none() {
return false;
}
if self.token_program.is_none() {
return false;
}
if self.token_mint.is_none() {
return false;
}
if self.token_account.is_none() {
return false;
}
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
while let Some(tag) = is.read_raw_tag_or_eof()? {
match tag {
10 => {
self.base_address = ::std::option::Option::Some(is.read_string()?);
},
18 => {
self.token_program = ::std::option::Option::Some(is.read_string()?);
},
26 => {
self.token_mint = ::std::option::Option::Some(is.read_string()?);
},
34 => {
self.token_account = ::std::option::Option::Some(is.read_string()?);
},
tag => {
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u64 {
let mut my_size = 0;
if let Some(v) = self.base_address.as_ref() {
my_size += ::protobuf::rt::string_size(1, &v);
}
if let Some(v) = self.token_program.as_ref() {
my_size += ::protobuf::rt::string_size(2, &v);
}
if let Some(v) = self.token_mint.as_ref() {
my_size += ::protobuf::rt::string_size(3, &v);
}
if let Some(v) = self.token_account.as_ref() {
my_size += ::protobuf::rt::string_size(4, &v);
}
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
self.special_fields.cached_size().set(my_size as u32);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
if let Some(v) = self.base_address.as_ref() {
os.write_string(1, v)?;
}
if let Some(v) = self.token_program.as_ref() {
os.write_string(2, v)?;
}
if let Some(v) = self.token_mint.as_ref() {
os.write_string(3, v)?;
}
if let Some(v) = self.token_account.as_ref() {
os.write_string(4, v)?;
}
os.write_unknown_fields(self.special_fields.unknown_fields())?;
::std::result::Result::Ok(())
}
fn special_fields(&self) -> &::protobuf::SpecialFields {
&self.special_fields
}
fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
&mut self.special_fields
}
fn new() -> SolanaTxTokenAccountInfo {
SolanaTxTokenAccountInfo::new()
}
fn clear(&mut self) {
self.base_address = ::std::option::Option::None;
self.token_program = ::std::option::Option::None;
self.token_mint = ::std::option::Option::None;
self.token_account = ::std::option::Option::None;
self.special_fields.clear();
}
fn default_instance() -> &'static SolanaTxTokenAccountInfo {
static instance: SolanaTxTokenAccountInfo = SolanaTxTokenAccountInfo {
base_address: ::std::option::Option::None,
token_program: ::std::option::Option::None,
token_mint: ::std::option::Option::None,
token_account: ::std::option::Option::None,
special_fields: ::protobuf::SpecialFields::new(),
};
&instance
}
}
impl ::protobuf::MessageFull for SolanaTxTokenAccountInfo {
fn descriptor() -> ::protobuf::reflect::MessageDescriptor {
static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new();
descriptor.get(|| file_descriptor().message_by_package_relative_name("SolanaTxTokenAccountInfo").unwrap()).clone()
}
}
impl ::std::fmt::Display for SolanaTxTokenAccountInfo {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for SolanaTxTokenAccountInfo {
type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
}
// @@protoc_insertion_point(message:hw.trezor.messages.solana.SolanaTxAdditionalInfo)
#[derive(PartialEq,Clone,Default,Debug)]
pub struct SolanaTxAdditionalInfo {
// message fields
// @@protoc_insertion_point(field:hw.trezor.messages.solana.SolanaTxAdditionalInfo.token_accounts_infos)
pub token_accounts_infos: ::std::vec::Vec<SolanaTxTokenAccountInfo>,
// special fields
// @@protoc_insertion_point(special_field:hw.trezor.messages.solana.SolanaTxAdditionalInfo.special_fields)
pub special_fields: ::protobuf::SpecialFields,
}
impl<'a> ::std::default::Default for &'a SolanaTxAdditionalInfo {
fn default() -> &'a SolanaTxAdditionalInfo {
<SolanaTxAdditionalInfo as ::protobuf::Message>::default_instance()
}
}
impl SolanaTxAdditionalInfo {
pub fn new() -> SolanaTxAdditionalInfo {
::std::default::Default::default()
}
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
let mut fields = ::std::vec::Vec::with_capacity(1);
let mut oneofs = ::std::vec::Vec::with_capacity(0);
fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>(
"token_accounts_infos",
|m: &SolanaTxAdditionalInfo| { &m.token_accounts_infos },
|m: &mut SolanaTxAdditionalInfo| { &mut m.token_accounts_infos },
));
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<SolanaTxAdditionalInfo>(
"SolanaTxAdditionalInfo",
fields,
oneofs,
)
}
}
impl ::protobuf::Message for SolanaTxAdditionalInfo {
const NAME: &'static str = "SolanaTxAdditionalInfo";
fn is_initialized(&self) -> bool {
for v in &self.token_accounts_infos {
if !v.is_initialized() {
return false;
}
};
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
while let Some(tag) = is.read_raw_tag_or_eof()? {
match tag {
10 => {
self.token_accounts_infos.push(is.read_message()?);
},
tag => {
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u64 {
let mut my_size = 0;
for value in &self.token_accounts_infos {
let len = value.compute_size();
my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len;
};
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
self.special_fields.cached_size().set(my_size as u32);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
for v in &self.token_accounts_infos {
::protobuf::rt::write_message_field_with_cached_size(1, v, os)?;
};
os.write_unknown_fields(self.special_fields.unknown_fields())?;
::std::result::Result::Ok(())
}
fn special_fields(&self) -> &::protobuf::SpecialFields {
&self.special_fields
}
fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
&mut self.special_fields
}
fn new() -> SolanaTxAdditionalInfo {
SolanaTxAdditionalInfo::new()
}
fn clear(&mut self) {
self.token_accounts_infos.clear();
self.special_fields.clear();
}
fn default_instance() -> &'static SolanaTxAdditionalInfo {
static instance: SolanaTxAdditionalInfo = SolanaTxAdditionalInfo {
token_accounts_infos: ::std::vec::Vec::new(),
special_fields: ::protobuf::SpecialFields::new(),
};
&instance
}
}
impl ::protobuf::MessageFull for SolanaTxAdditionalInfo {
fn descriptor() -> ::protobuf::reflect::MessageDescriptor {
static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new();
descriptor.get(|| file_descriptor().message_by_package_relative_name("SolanaTxAdditionalInfo").unwrap()).clone()
}
}
impl ::std::fmt::Display for SolanaTxAdditionalInfo {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for SolanaTxAdditionalInfo {
type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
}
// @@protoc_insertion_point(message:hw.trezor.messages.solana.SolanaSignTx)
#[derive(PartialEq,Clone,Default,Debug)]
pub struct SolanaSignTx {
@ -716,6 +1176,8 @@ pub struct SolanaSignTx {
pub address_n: ::std::vec::Vec<u32>,
// @@protoc_insertion_point(field:hw.trezor.messages.solana.SolanaSignTx.serialized_tx)
pub serialized_tx: ::std::option::Option<::std::vec::Vec<u8>>,
// @@protoc_insertion_point(field:hw.trezor.messages.solana.SolanaSignTx.additional_info)
pub additional_info: ::protobuf::MessageField<SolanaTxAdditionalInfo>,
// special fields
// @@protoc_insertion_point(special_field:hw.trezor.messages.solana.SolanaSignTx.special_fields)
pub special_fields: ::protobuf::SpecialFields,
@ -769,7 +1231,7 @@ impl SolanaSignTx {
}
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
let mut fields = ::std::vec::Vec::with_capacity(2);
let mut fields = ::std::vec::Vec::with_capacity(3);
let mut oneofs = ::std::vec::Vec::with_capacity(0);
fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>(
"address_n",
@ -781,6 +1243,11 @@ impl SolanaSignTx {
|m: &SolanaSignTx| { &m.serialized_tx },
|m: &mut SolanaSignTx| { &mut m.serialized_tx },
));
fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, SolanaTxAdditionalInfo>(
"additional_info",
|m: &SolanaSignTx| { &m.additional_info },
|m: &mut SolanaSignTx| { &mut m.additional_info },
));
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<SolanaSignTx>(
"SolanaSignTx",
fields,
@ -796,6 +1263,11 @@ impl ::protobuf::Message for SolanaSignTx {
if self.serialized_tx.is_none() {
return false;
}
for v in &self.additional_info {
if !v.is_initialized() {
return false;
}
};
true
}
@ -811,6 +1283,9 @@ impl ::protobuf::Message for SolanaSignTx {
18 => {
self.serialized_tx = ::std::option::Option::Some(is.read_bytes()?);
},
26 => {
::protobuf::rt::read_singular_message_into_field(is, &mut self.additional_info)?;
},
tag => {
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
},
@ -829,6 +1304,10 @@ impl ::protobuf::Message for SolanaSignTx {
if let Some(v) = self.serialized_tx.as_ref() {
my_size += ::protobuf::rt::bytes_size(2, &v);
}
if let Some(v) = self.additional_info.as_ref() {
let len = v.compute_size();
my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len;
}
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
self.special_fields.cached_size().set(my_size as u32);
my_size
@ -841,6 +1320,9 @@ impl ::protobuf::Message for SolanaSignTx {
if let Some(v) = self.serialized_tx.as_ref() {
os.write_bytes(2, v)?;
}
if let Some(v) = self.additional_info.as_ref() {
::protobuf::rt::write_message_field_with_cached_size(3, v, os)?;
}
os.write_unknown_fields(self.special_fields.unknown_fields())?;
::std::result::Result::Ok(())
}
@ -860,6 +1342,7 @@ impl ::protobuf::Message for SolanaSignTx {
fn clear(&mut self) {
self.address_n.clear();
self.serialized_tx = ::std::option::Option::None;
self.additional_info.clear();
self.special_fields.clear();
}
@ -867,6 +1350,7 @@ impl ::protobuf::Message for SolanaSignTx {
static instance: SolanaSignTx = SolanaSignTx {
address_n: ::std::vec::Vec::new(),
serialized_tx: ::std::option::Option::None,
additional_info: ::protobuf::MessageField::none(),
special_fields: ::protobuf::SpecialFields::new(),
};
&instance
@ -1060,10 +1544,17 @@ static file_descriptor_proto_data: &'static [u8] = b"\
\x18\x01\x20\x03(\rR\x08addressN\x12!\n\x0cshow_display\x18\x02\x20\x01(\
\x08R\x0bshowDisplay\x12\x1a\n\x08chunkify\x18\x03\x20\x01(\x08R\x08chun\
kify\")\n\rSolanaAddress\x12\x18\n\x07address\x18\x01\x20\x02(\tR\x07add\
ress\"P\n\x0cSolanaSignTx\x12\x1b\n\taddress_n\x18\x01\x20\x03(\rR\x08ad\
dressN\x12#\n\rserialized_tx\x18\x02\x20\x02(\x0cR\x0cserializedTx\"1\n\
\x11SolanaTxSignature\x12\x1c\n\tsignature\x18\x01\x20\x02(\x0cR\tsignat\
ure\
ress\"\xa6\x01\n\x18SolanaTxTokenAccountInfo\x12!\n\x0cbase_address\x18\
\x01\x20\x02(\tR\x0bbaseAddress\x12#\n\rtoken_program\x18\x02\x20\x02(\t\
R\x0ctokenProgram\x12\x1d\n\ntoken_mint\x18\x03\x20\x02(\tR\ttokenMint\
\x12#\n\rtoken_account\x18\x04\x20\x02(\tR\x0ctokenAccount\"\x7f\n\x16So\
lanaTxAdditionalInfo\x12e\n\x14token_accounts_infos\x18\x01\x20\x03(\x0b\
23.hw.trezor.messages.solana.SolanaTxTokenAccountInfoR\x12tokenAccountsI\
nfos\"\xac\x01\n\x0cSolanaSignTx\x12\x1b\n\taddress_n\x18\x01\x20\x03(\r\
R\x08addressN\x12#\n\rserialized_tx\x18\x02\x20\x02(\x0cR\x0cserializedT\
x\x12Z\n\x0fadditional_info\x18\x03\x20\x01(\x0b21.hw.trezor.messages.so\
lana.SolanaTxAdditionalInfoR\x0eadditionalInfo\"1\n\x11SolanaTxSignature\
\x12\x1c\n\tsignature\x18\x01\x20\x02(\x0cR\tsignature\
";
/// `FileDescriptorProto` object which was a source for this generated file
@ -1082,11 +1573,13 @@ pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor {
let generated_file_descriptor = generated_file_descriptor_lazy.get(|| {
let mut deps = ::std::vec::Vec::with_capacity(1);
deps.push(super::messages_common::file_descriptor().clone());
let mut messages = ::std::vec::Vec::with_capacity(6);
let mut messages = ::std::vec::Vec::with_capacity(8);
messages.push(SolanaGetPublicKey::generated_message_descriptor_data());
messages.push(SolanaPublicKey::generated_message_descriptor_data());
messages.push(SolanaGetAddress::generated_message_descriptor_data());
messages.push(SolanaAddress::generated_message_descriptor_data());
messages.push(SolanaTxTokenAccountInfo::generated_message_descriptor_data());
messages.push(SolanaTxAdditionalInfo::generated_message_descriptor_data());
messages.push(SolanaSignTx::generated_message_descriptor_data());
messages.push(SolanaTxSignature::generated_message_descriptor_data());
let mut enums = ::std::vec::Vec::with_capacity(0);

Loading…
Cancel
Save