mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-17 21:22:10 +00:00
feat(solana): add additional info with token account
This commit is contained in:
parent
6912bf6e7f
commit
6aa5ac869a
@ -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,
|
||||
|
191
core/src/apps/solana/predefined_transaction.py
Normal file
191
core/src/apps/solana/predefined_transaction.py
Normal file
@ -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:
|
||||
|
65
core/src/apps/solana/token_account.py
Normal file
65
core/src/apps/solana/token_account.py
Normal file
@ -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
|
||||
|
||||
|
206
core/tests/test_apps.solana.predefined_transaction.py
Normal file
206
core/tests/test_apps.solana.predefined_transaction.py
Normal file
@ -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…
Reference in New Issue
Block a user