mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-05-29 12:18:51 +00:00
refactor(core/solana): restructure programs.json and related code
This commit is contained in:
parent
e3af93e89f
commit
e4e6d60e64
File diff suppressed because it is too large
Load Diff
@ -12,14 +12,14 @@ _This file is generated by `programs.md.mako` via `make solana_templates`, do no
|
|||||||
| Deposit | `lamports` | `lamports` |
|
| Deposit | `lamports` | `lamports` |
|
||||||
| From | `funding_account` | `account` |
|
| From | `funding_account` | `account` |
|
||||||
| _(not shown)_ | `space` | `u64` |
|
| _(not shown)_ | `space` | `u64` |
|
||||||
| _(not shown)_ | `owner` | `authority` |
|
| _(not shown)_ | `owner` | `pubkey` |
|
||||||
|
|
||||||
### (1) Assign
|
### (1) Assign
|
||||||
|
|
||||||
| Label | Value | Type |
|
| Label | Value | Type |
|
||||||
|-------|-------|------|
|
|-------|-------|------|
|
||||||
| Assign account | `assigned_account` | `account` |
|
| Assign account | `assigned_account` | `account` |
|
||||||
| Assign account to program | `owner` | `authority` |
|
| Assign account to program | `owner` | `pubkey` |
|
||||||
|
|
||||||
### (2) Transfer
|
### (2) Transfer
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ _This file is generated by `programs.md.mako` via `make solana_templates`, do no
|
|||||||
| Label | Value | Type |
|
| Label | Value | Type |
|
||||||
|-------|-------|------|
|
|-------|-------|------|
|
||||||
| Initialize nonce account | `nonce_account` | `account` |
|
| Initialize nonce account | `nonce_account` | `account` |
|
||||||
| New authority | `nonce_authority` | `authority` |
|
| New authority | `nonce_authority` | `pubkey` |
|
||||||
| _(not shown)_ | `recent_blockhashes_sysvar` | `account` |
|
| _(not shown)_ | `recent_blockhashes_sysvar` | `account` |
|
||||||
| _(not shown)_ | `rent_sysvar` | `account` |
|
| _(not shown)_ | `rent_sysvar` | `account` |
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ _This file is generated by `programs.md.mako` via `make solana_templates`, do no
|
|||||||
| Label | Value | Type |
|
| Label | Value | Type |
|
||||||
|-------|-------|------|
|
|-------|-------|------|
|
||||||
| Set nonce authority | `nonce_account` | `account` |
|
| Set nonce authority | `nonce_account` | `account` |
|
||||||
| New authority | `nonce_authority` | `authority` |
|
| New authority | `nonce_authority` | `pubkey` |
|
||||||
| Authorized by | `nonce_authority` | `account` |
|
| Authorized by | `nonce_authority` | `account` |
|
||||||
|
|
||||||
### (8) Allocate
|
### (8) Allocate
|
||||||
@ -132,11 +132,11 @@ _This file is generated by `programs.md.mako` via `make solana_templates`, do no
|
|||||||
| Label | Value | Type |
|
| Label | Value | Type |
|
||||||
|-------|-------|------|
|
|-------|-------|------|
|
||||||
| Initialize stake account | `uninitialized_stake_account` | `account` |
|
| Initialize stake account | `uninitialized_stake_account` | `account` |
|
||||||
| New stake authority | `staker` | `authority` |
|
| New stake authority | `staker` | `pubkey` |
|
||||||
| New withdraw authority | `withdrawer` | `authority` |
|
| New withdraw authority | `withdrawer` | `pubkey` |
|
||||||
| Lockup time | `unix_timestamp` | `unix_timestamp` |
|
| Lockup time | `unix_timestamp` | `unix_timestamp` |
|
||||||
| Lockup epoch | `epoch` | `u64` |
|
| Lockup epoch | `epoch` | `u64` |
|
||||||
| Lockup authority | `custodian` | `authority` |
|
| Lockup authority | `custodian` | `pubkey` |
|
||||||
| _(not shown)_ | `rent_sysvar` | `account` |
|
| _(not shown)_ | `rent_sysvar` | `account` |
|
||||||
|
|
||||||
### (1) Authorize
|
### (1) Authorize
|
||||||
@ -340,7 +340,7 @@ _This file is generated by `programs.md.mako` via `make solana_templates`, do no
|
|||||||
| Label | Value | Type |
|
| Label | Value | Type |
|
||||||
|-------|-------|------|
|
|-------|-------|------|
|
||||||
| Set authority for | `mint_account` | `account` |
|
| Set authority for | `mint_account` | `account` |
|
||||||
| New authority | `new_authority` | `authority` |
|
| New authority | `new_authority` | `pubkey` |
|
||||||
| Authority type | `authority_type` | `AuthorityType` |
|
| Authority type | `authority_type` | `AuthorityType` |
|
||||||
| Current authority | `current_authority` | `account` |
|
| Current authority | `current_authority` | `account` |
|
||||||
|
|
||||||
@ -509,7 +509,7 @@ _This file is generated by `programs.md.mako` via `make solana_templates`, do no
|
|||||||
| Label | Value | Type |
|
| Label | Value | Type |
|
||||||
|-------|-------|------|
|
|-------|-------|------|
|
||||||
| Set authority for | `mint_account` | `account` |
|
| Set authority for | `mint_account` | `account` |
|
||||||
| New authority | `new_authority` | `authority` |
|
| New authority | `new_authority` | `pubkey` |
|
||||||
| Authority type | `authority_type` | `AuthorityType` |
|
| Authority type | `authority_type` | `AuthorityType` |
|
||||||
| Current authority | `current_authority` | `account` |
|
| Current authority | `current_authority` | `account` |
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ ${'##'} ${program.name}
|
|||||||
${'###'} (${instruction.id}) ${instruction.name}
|
${'###'} (${instruction.id}) ${instruction.name}
|
||||||
<%
|
<%
|
||||||
all_params = { param.name: param for param in instruction.parameters }
|
all_params = { param.name: param for param in instruction.parameters }
|
||||||
all_accounts = [ref.name for ref in instruction.references]
|
all_accounts = list(instruction.references)
|
||||||
%>
|
%>
|
||||||
| Label | Value | Type |
|
| Label | Value | Type |
|
||||||
|-------|-------|------|
|
|-------|-------|------|
|
||||||
|
@ -168,14 +168,22 @@ The `programs.json` file serves as a structured configuration file in the Solana
|
|||||||
- `name`: The parameter name.
|
- `name`: The parameter name.
|
||||||
- `type`: The data type of the parameter, such as `u64` for 64-bit unsigned integers.
|
- `type`: The data type of the parameter, such as `u64` for 64-bit unsigned integers.
|
||||||
- `optional`: Indicates whether the parameter is optional.
|
- `optional`: Indicates whether the parameter is optional.
|
||||||
- `references`: Defines the references to accounts that this instruction requires.
|
- `args`: An optional dict of arguments for the formatter, see explanation below.
|
||||||
- `name`: The account name.
|
- `references`: An array of account names that are used by the instruction.
|
||||||
- `is_authority`: A boolean specifying whether the account is considered an authority for this instruction.
|
- `references_required`: The number of references required by the instruction. If more `references` are specified than required, the extra ones are optional, and may or may not be present in the transaction.
|
||||||
- `optional`: Indicates whether the account is optional.
|
|
||||||
- `ui_properties`: Contains user interface-related information for this instruction.
|
- `ui_properties`: Contains user interface-related information for this instruction.
|
||||||
- `account`: Reference to one account in the references list identified by its `name`
|
- `account`: Reference to one account in the references list identified by its `name`
|
||||||
- `parameter`: Reference to one parameter in the parameters list identified by its `name`
|
- `parameter`: Reference to one parameter in the parameters list identified by its `name`
|
||||||
- `display_name`: A human-readable label for the parameter or account, suitable for user interfaces.
|
- `display_name`: A human-readable label for the parameter or account, suitable for user interfaces.
|
||||||
|
- `default_value_to_hide`: Optional. If this value is found in the account / parameter, the UI property will not be shown for confirmation. This is useful when the default value is considered safe. In particular, if the value of the property is a public key, and the special word `"signer"` is used for `default_value_to_hide`, the UI property will be hidden if the public key matches the Trezor's account.
|
||||||
|
|
||||||
|
Certain types of parameters, specified in `types` dict of the `programs.json` file, have special formatting capabilities.
|
||||||
|
In particular, the type `token_amount` is a regular `u64` type, but the formatter function accepts additional parameters:
|
||||||
|
* a special parameter `#definitions` that will be pre-set to the loadable definitions manager
|
||||||
|
* a parameter `decimals` that specifies the number of decimals of the token
|
||||||
|
* a parameter `mint` that specifies the mint address of the token
|
||||||
|
|
||||||
|
The corresponding parameter of `token_amount` type must provide the `args` dict, mapping the `decimals` and `mint` arguments to fields of the instruction. E.g.: in a hypothetical Swap instruction, you would have two parameters of `token_amount` type. On the first one, the `args` dict would map `decimals` to the `from_amount_decimals` field and `mint` to the `from_amount_mint` field. On the second one, the mapping would go to the `to_amount_decimals` and `to_amount_mint` fields.
|
||||||
|
|
||||||
|
|
||||||
After the message has been parsed, the Solana app utilizes the Trezor UI engine to present all the necessary information to the user for review and confirmation. If all the programs and instructions contained within the message are recognized and known, the software ensures that all the relevant information is displayed to the user. Each piece of data, including parameters, account references, and instruction details, is presented on the Trezor's user interface for user confirmation.
|
After the message has been parsed, the Solana app utilizes the Trezor UI engine to present all the necessary information to the user for review and confirmation. If all the programs and instructions contained within the message are recognized and known, the software ensures that all the relevant information is displayed to the user. Each piece of data, including parameters, account references, and instruction details, is presented on the Trezor's user interface for user confirmation.
|
||||||
|
@ -3,10 +3,10 @@ from typing import TYPE_CHECKING
|
|||||||
from trezor.strings import format_amount, format_timestamp
|
from trezor.strings import format_amount, format_timestamp
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .transaction.instructions import Instruction
|
from .definitions import Definitions
|
||||||
|
|
||||||
|
|
||||||
def format_pubkey(_: Instruction, value: bytes | None) -> str:
|
def format_pubkey(value: bytes | None) -> str:
|
||||||
from trezor.crypto import base58
|
from trezor.crypto import base58
|
||||||
|
|
||||||
if value is None:
|
if value is None:
|
||||||
@ -15,25 +15,31 @@ def format_pubkey(_: Instruction, value: bytes | None) -> str:
|
|||||||
return base58.encode(value)
|
return base58.encode(value)
|
||||||
|
|
||||||
|
|
||||||
def format_lamports(_: Instruction, value: int) -> str:
|
def format_lamports(value: int) -> str:
|
||||||
formatted = format_amount(value, decimals=9)
|
formatted = format_amount(value, decimals=9)
|
||||||
return f"{formatted} SOL"
|
return f"{formatted} SOL"
|
||||||
|
|
||||||
|
|
||||||
def format_token_amount(instruction: Instruction, value: int) -> str:
|
def format_token_amount(
|
||||||
assert hasattr(instruction, "decimals") # enforced in instructions.py.mako
|
value: int, definitions: Definitions, decimals: int, mint: bytes
|
||||||
|
) -> str:
|
||||||
|
formatted = format_amount(value, decimals=decimals)
|
||||||
|
token = definitions.get_token(mint)
|
||||||
|
if token:
|
||||||
|
symbol = token.symbol
|
||||||
|
else:
|
||||||
|
symbol = "[UNKN]"
|
||||||
|
|
||||||
formatted = format_amount(value, decimals=instruction.decimals)
|
return f"{formatted} {symbol}"
|
||||||
return f"{formatted}"
|
|
||||||
|
|
||||||
|
|
||||||
def format_unix_timestamp(_: Instruction, value: int) -> str:
|
def format_unix_timestamp(value: int) -> str:
|
||||||
return format_timestamp(value)
|
return format_timestamp(value)
|
||||||
|
|
||||||
|
|
||||||
def format_int(_: Instruction, value: int) -> str:
|
def format_int(value: int) -> str:
|
||||||
return str(value)
|
return str(value)
|
||||||
|
|
||||||
|
|
||||||
def format_identity(_: Instruction, value: str) -> str:
|
def format_identity(value: str) -> str:
|
||||||
return value
|
return value
|
||||||
|
@ -84,45 +84,57 @@ async def confirm_instruction(
|
|||||||
property_template = instruction.get_property_template(ui_property.parameter)
|
property_template = instruction.get_property_template(ui_property.parameter)
|
||||||
value = instruction.parsed_data[ui_property.parameter]
|
value = instruction.parsed_data[ui_property.parameter]
|
||||||
|
|
||||||
if property_template.is_authority and signer_public_key == value:
|
if property_template.optional and value is None:
|
||||||
continue
|
|
||||||
|
|
||||||
if property_template.is_optional and value is None:
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if ui_property.default_value_to_hide == value:
|
if ui_property.default_value_to_hide == value:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if (
|
||||||
|
property_template.is_pubkey()
|
||||||
|
and ui_property.default_value_to_hide == "signer"
|
||||||
|
and signer_public_key == value
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
|
||||||
|
args = []
|
||||||
|
for arg in property_template.args:
|
||||||
|
if arg == "#definitions":
|
||||||
|
args.append(definitions)
|
||||||
|
elif arg in instruction.parsed_data:
|
||||||
|
args.append(instruction.parsed_data[arg])
|
||||||
|
elif arg in instruction.parsed_accounts:
|
||||||
|
args.append(instruction.parsed_accounts[arg][0])
|
||||||
|
else:
|
||||||
|
raise ValueError # Invalid property template
|
||||||
|
|
||||||
await confirm_properties(
|
await confirm_properties(
|
||||||
"confirm_instruction",
|
"confirm_instruction",
|
||||||
f"{instruction_index}/{instructions_count}: {instruction.ui_name}",
|
f"{instruction_index}/{instructions_count}: {instruction.ui_name}",
|
||||||
(
|
(
|
||||||
(
|
(
|
||||||
ui_property.display_name,
|
ui_property.display_name,
|
||||||
property_template.format(instruction, value),
|
property_template.format(value, *args),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
elif ui_property.account is not None:
|
elif ui_property.account is not None:
|
||||||
account_template = instruction.get_account_template(ui_property.account)
|
|
||||||
|
|
||||||
# optional account, skip if not present
|
# optional account, skip if not present
|
||||||
if ui_property.account not in instruction.parsed_accounts:
|
if ui_property.account not in instruction.parsed_accounts:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
account_value = instruction.parsed_accounts[ui_property.account]
|
account_value = instruction.parsed_accounts[ui_property.account]
|
||||||
|
|
||||||
if account_template.is_authority:
|
if ui_property.default_value_to_hide == "signer" and signer_public_key == account_value[0]:
|
||||||
if signer_public_key == account_value[0]:
|
continue
|
||||||
continue
|
|
||||||
|
|
||||||
account_data: list[tuple[str, str]] = []
|
account_data: list[tuple[str, str]] = []
|
||||||
# account included in the transaction directly
|
# account included in the transaction directly
|
||||||
if len(account_value) == 2:
|
if len(account_value) == 2:
|
||||||
account_description = f"{base58.encode(account_value[0])}"
|
account_description = f"{base58.encode(account_value[0])}"
|
||||||
if account_template.is_token_mint:
|
token = definitions.get_token(account_value[0])
|
||||||
token = definitions.get_token(account_value[0])
|
if token is not None:
|
||||||
account_description = f"{token.symbol}\n{account_description}"
|
account_description = f"{token.name}\n{account_description}"
|
||||||
elif account_value[0] == signer_public_key:
|
elif account_value[0] == signer_public_key:
|
||||||
account_description = f"{account_description} ({TR.words__signer})"
|
account_description = f"{account_description} ({TR.words__signer})"
|
||||||
|
|
||||||
|
@ -5,25 +5,20 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
from typing_extensions import Self
|
from typing_extensions import Self
|
||||||
|
|
||||||
from ..types import (
|
from ..types import Account, InstructionData, PropertyTemplate, UIProperty
|
||||||
Account,
|
|
||||||
AccountTemplate,
|
|
||||||
InstructionData,
|
|
||||||
PropertyTemplate,
|
|
||||||
UIProperty,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Instruction:
|
class Instruction:
|
||||||
program_id: str
|
program_id: str
|
||||||
instruction_id: int | None
|
instruction_id: int | None
|
||||||
|
|
||||||
property_templates: list[PropertyTemplate]
|
property_templates: tuple[PropertyTemplate, ...]
|
||||||
accounts_template: list[AccountTemplate]
|
accounts_required: int
|
||||||
|
account_templates: tuple[str, ...]
|
||||||
|
|
||||||
ui_name: str
|
ui_name: str
|
||||||
|
|
||||||
ui_properties: list[UIProperty]
|
ui_properties: tuple[UIProperty, ...]
|
||||||
|
|
||||||
parsed_data: dict[str, Any]
|
parsed_data: dict[str, Any]
|
||||||
parsed_accounts: dict[str, Account]
|
parsed_accounts: dict[str, Account]
|
||||||
@ -40,7 +35,8 @@ class Instruction:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_instruction_data(
|
def parse_instruction_data(
|
||||||
instruction_data: InstructionData, property_templates: list[PropertyTemplate]
|
instruction_data: InstructionData,
|
||||||
|
property_templates: tuple[PropertyTemplate, ...],
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
from trezor.utils import BufferReader
|
from trezor.utils import BufferReader
|
||||||
from trezor.wire import DataError
|
from trezor.wire import DataError
|
||||||
@ -50,7 +46,7 @@ class Instruction:
|
|||||||
parsed_data = {}
|
parsed_data = {}
|
||||||
for property_template in property_templates:
|
for property_template in property_templates:
|
||||||
is_included = True
|
is_included = True
|
||||||
if property_template.is_optional:
|
if property_template.optional:
|
||||||
is_included = True if reader.get() == 1 else False
|
is_included = True if reader.get() == 1 else False
|
||||||
|
|
||||||
parsed_data[property_template.name] = (
|
parsed_data[property_template.name] = (
|
||||||
@ -64,18 +60,19 @@ class Instruction:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_instruction_accounts(
|
def parse_instruction_accounts(
|
||||||
accounts: list[Account], accounts_template: list[AccountTemplate]
|
accounts: list[Account],
|
||||||
|
accounts_required: int,
|
||||||
|
account_templates: tuple[str, ...],
|
||||||
) -> dict[str, Account]:
|
) -> dict[str, Account]:
|
||||||
parsed_account = {}
|
parsed_accounts = {}
|
||||||
for i, account_template in enumerate(accounts_template):
|
if len(accounts) < accounts_required:
|
||||||
if i >= len(accounts):
|
raise ValueError # "Account is missing
|
||||||
if account_template.optional:
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
raise ValueError # "Account is missing
|
|
||||||
|
|
||||||
parsed_account[account_template.name] = accounts[i]
|
for i, account_name in enumerate(account_templates):
|
||||||
return parsed_account
|
if i >= len(accounts):
|
||||||
|
break
|
||||||
|
parsed_accounts[account_name] = accounts[i]
|
||||||
|
return parsed_accounts
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -83,9 +80,10 @@ class Instruction:
|
|||||||
program_id: str,
|
program_id: str,
|
||||||
accounts: list[Account],
|
accounts: list[Account],
|
||||||
instruction_id: int | None,
|
instruction_id: int | None,
|
||||||
property_templates: list[PropertyTemplate],
|
property_templates: tuple[PropertyTemplate, ...],
|
||||||
accounts_template: list[AccountTemplate],
|
accounts_required: int,
|
||||||
ui_properties: list[UIProperty],
|
account_templates: tuple[str, ...],
|
||||||
|
ui_properties: tuple[UIProperty, ...],
|
||||||
ui_name: str,
|
ui_name: str,
|
||||||
is_program_supported: bool = True,
|
is_program_supported: bool = True,
|
||||||
is_instruction_supported: bool = True,
|
is_instruction_supported: bool = True,
|
||||||
@ -97,7 +95,8 @@ class Instruction:
|
|||||||
self.instruction_id = instruction_id
|
self.instruction_id = instruction_id
|
||||||
|
|
||||||
self.property_templates = property_templates
|
self.property_templates = property_templates
|
||||||
self.accounts_template = accounts_template
|
self.accounts_required = accounts_required
|
||||||
|
self.account_templates = account_templates
|
||||||
|
|
||||||
self.ui_name = ui_name
|
self.ui_name = ui_name
|
||||||
|
|
||||||
@ -118,10 +117,12 @@ class Instruction:
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.parsed_accounts = self.parse_instruction_accounts(
|
self.parsed_accounts = self.parse_instruction_accounts(
|
||||||
accounts, accounts_template
|
accounts,
|
||||||
|
accounts_required,
|
||||||
|
account_templates,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.multisig_signers = accounts[len(accounts_template) :]
|
self.multisig_signers = accounts[len(account_templates) :]
|
||||||
if self.multisig_signers and not supports_multisig:
|
if self.multisig_signers and not supports_multisig:
|
||||||
raise ValueError # Multisig not supported
|
raise ValueError # Multisig not supported
|
||||||
else:
|
else:
|
||||||
@ -144,13 +145,6 @@ class Instruction:
|
|||||||
|
|
||||||
raise ValueError # Property not found
|
raise ValueError # Property not found
|
||||||
|
|
||||||
def get_account_template(self, account_name: str) -> AccountTemplate:
|
|
||||||
for account_template in self.accounts_template:
|
|
||||||
if account_template.name == account_name:
|
|
||||||
return account_template
|
|
||||||
|
|
||||||
raise ValueError # Account not found
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_type_of(cls, ins: Any) -> TypeGuard[Self]:
|
def is_type_of(cls, ins: Any) -> TypeGuard[Self]:
|
||||||
# gets overridden in `instructions.py` `FakeClass`
|
# gets overridden in `instructions.py` `FakeClass`
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,32 +1,41 @@
|
|||||||
# generated from instructions.py.mako
|
# generated from instructions.py.mako
|
||||||
|
# (by running `make solana_templates` in root)
|
||||||
# do not edit manually!
|
# do not edit manually!
|
||||||
<%def name="getProgramId(program)">${"_" + "_".join(program["name"].upper().split(" ") + ["ID"])}</%def>\
|
|
||||||
<%def name="getInstructionIdText(program, instruction)">${"_".join([getProgramId(program)] + ["INS"] + instruction["name"].upper().split(" "))}</%def>\
|
<%
|
||||||
<%def name="getClassName(program, instruction)">${program["name"].replace(" ", "")}${instruction["name"].replace(" ", "")}Instruction</%def>\
|
def getProgramId(program):
|
||||||
<%def name="getReferenceName(reference)">${"_".join(reference["name"].lower().split(" "))}</%def>\
|
return "_" + "_".join(program["name"].upper().split(" ") + ["ID"])
|
||||||
<%def name="getReferenceOptionalType(reference)">\
|
|
||||||
% if reference["optional"]:
|
def getInstructionIdText(program, instruction):
|
||||||
| None\
|
return "_".join([getProgramId(program)] + ["INS"] + instruction["name"].upper().split(" "))
|
||||||
% endif
|
|
||||||
</%def>\
|
def getClassName(program, instruction):
|
||||||
<%def name="getReferenceOptionalTemplate(reference)">\
|
return program["name"].replace(" ", "") + instruction["name"].replace(" ", "") + "Instruction"
|
||||||
% if reference["optional"]:
|
|
||||||
, True\
|
INT_TYPES = ("u8", "u32", "u64", "i32", "i64", "timestamp", "lamports", "token_amount", "unix_timestamp")
|
||||||
% else:
|
|
||||||
, False\
|
def getPythonType(type):
|
||||||
% endif
|
if type in INT_TYPES:
|
||||||
</%def>\
|
return "int"
|
||||||
<%def name="getPythonType(type)">\
|
elif type in ("pubkey", "authority"):
|
||||||
% if type in ("u32", "u64", "i32", "i64", "timestamp", "lamports", "token_amount"):
|
return "Account"
|
||||||
int\
|
elif type in ("string", "memo"):
|
||||||
% elif type in ("pubKey", "authority"):
|
return "str"
|
||||||
Account\
|
elif type in programs["types"] and programs["types"][type].get("is_enum"):
|
||||||
% elif type in ("string", "memo"):
|
return "int"
|
||||||
str\
|
else:
|
||||||
% else:
|
raise Exception(f"Unknown type: {type}")
|
||||||
int\
|
|
||||||
% endif
|
def args_tuple(required_parameters, args_dict):
|
||||||
</%def>\
|
args = []
|
||||||
|
for required_parameter in required_parameters:
|
||||||
|
if required_parameter.startswith("#"):
|
||||||
|
args.append(required_parameter)
|
||||||
|
else:
|
||||||
|
args.append(args_dict[required_parameter])
|
||||||
|
return repr(tuple(args))
|
||||||
|
|
||||||
|
%>\
|
||||||
from micropython import const
|
from micropython import const
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
@ -34,7 +43,7 @@ from trezor.wire import DataError
|
|||||||
|
|
||||||
from apps.common.readers import read_uint32_le, read_uint64_le
|
from apps.common.readers import read_uint32_le, read_uint64_le
|
||||||
|
|
||||||
from ..types import AccountTemplate, PropertyTemplate, UIProperty
|
from ..types import PropertyTemplate, UIProperty
|
||||||
from ..format import (
|
from ..format import (
|
||||||
format_int,
|
format_int,
|
||||||
format_lamports,
|
format_lamports,
|
||||||
@ -106,8 +115,11 @@ if TYPE_CHECKING:
|
|||||||
% endfor
|
% endfor
|
||||||
|
|
||||||
## generates properties for reference accounts
|
## generates properties for reference accounts
|
||||||
% for reference in instruction["references"]:
|
% for reference in instruction["references"][:instruction["references_required"]]:
|
||||||
${getReferenceName(reference)}: Account${getReferenceOptionalType(reference)}
|
${reference}: Account
|
||||||
|
% endfor
|
||||||
|
% for reference in instruction["references"][instruction["references_required"]:]:
|
||||||
|
${reference}: Account | None
|
||||||
% endfor
|
% endfor
|
||||||
% endfor
|
% endfor
|
||||||
% endfor
|
% endfor
|
||||||
@ -123,7 +135,7 @@ def get_instruction_id_length(program_id: str) -> int:
|
|||||||
|
|
||||||
% for _, type in programs["types"].items():
|
% for _, type in programs["types"].items():
|
||||||
% if "is_enum" in type and type["is_enum"]:
|
% if "is_enum" in type and type["is_enum"]:
|
||||||
def ${type["format"]}(_: Instruction, value: int) -> str:
|
def ${type["format"]}(value: int) -> str:
|
||||||
% for variant in type["fields"]:
|
% for variant in type["fields"]:
|
||||||
if value == ${variant["value"]}:
|
if value == ${variant["value"]}:
|
||||||
return "${variant["name"]}"
|
return "${variant["name"]}"
|
||||||
@ -132,23 +144,24 @@ def ${type["format"]}(_: Instruction, value: int) -> str:
|
|||||||
% endif
|
% endif
|
||||||
% endfor
|
% endfor
|
||||||
|
|
||||||
<%def name="getOptionalString(obj, string)">\
|
|
||||||
% if string in obj:
|
|
||||||
"${obj[string]}"\
|
|
||||||
%else:
|
|
||||||
None\
|
|
||||||
% endif
|
|
||||||
</%def>\
|
|
||||||
<%
|
<%
|
||||||
# Make sure that all required parameters are present in the instruction.
|
# Make sure that all required parameters are present in the instruction.
|
||||||
for program in programs["programs"]:
|
for program in programs["programs"]:
|
||||||
for instruction in program["instructions"]:
|
for instruction in program["instructions"]:
|
||||||
|
param_names = [parameter["name"] for parameter in instruction["parameters"]]
|
||||||
for parameter in instruction["parameters"]:
|
for parameter in instruction["parameters"]:
|
||||||
if "required_parameters" in programs["types"][parameter["type"]]:
|
required_parameters = programs["types"][parameter["type"]].get("required_parameters")
|
||||||
for required_parameter in programs["types"][parameter["type"]]["required_parameters"]:
|
if not required_parameters:
|
||||||
instruction_parameter_names = [parameter["name"] for parameter in instruction["parameters"]]
|
continue
|
||||||
if required_parameter not in instruction_parameter_names:
|
args = parameter.get("args", {})
|
||||||
raise Exception(f"Instruction \"{instruction['name']}\" is missing the required parameter \"{required_parameter}\" from paremeter \"{parameter['name']}\".")
|
for required_parameter in required_parameters:
|
||||||
|
if required_parameter.startswith("#"):
|
||||||
|
continue
|
||||||
|
if required_parameter not in args:
|
||||||
|
raise Exception(f"Parameter \"{parameter['name']}\" is missing the required argument \"{required_parameter}\".")
|
||||||
|
target = args[required_parameter]
|
||||||
|
if target not in param_names and target not in instruction["references"]:
|
||||||
|
raise Exception(f"Instruction \"{instruction['name']}\" is missing the required parameter \"{required_parameter}\" from parameter \"{parameter['name']}\".")
|
||||||
%>
|
%>
|
||||||
|
|
||||||
def get_instruction(
|
def get_instruction(
|
||||||
@ -164,44 +177,35 @@ def get_instruction(
|
|||||||
program_id,
|
program_id,
|
||||||
instruction_accounts,
|
instruction_accounts,
|
||||||
${getInstructionIdText(program, instruction)},
|
${getInstructionIdText(program, instruction)},
|
||||||
[
|
(
|
||||||
% for parameter in instruction["parameters"]:
|
% for parameter in instruction["parameters"]:
|
||||||
PropertyTemplate(
|
PropertyTemplate(
|
||||||
"${parameter["name"]}",
|
"${parameter["name"]}",
|
||||||
${parameter["type"] == "authority"},
|
|
||||||
${parameter["optional"]},
|
${parameter["optional"]},
|
||||||
${programs["types"][parameter["type"]]["parse"]},
|
${programs["types"][parameter["type"]]["parse"]},
|
||||||
${programs["types"][parameter["type"]]["format"]},
|
${programs["types"][parameter["type"]]["format"]},
|
||||||
|
${args_tuple(programs["types"][parameter["type"]].get("required_parameters", []), parameter.get("args", {}))},
|
||||||
),
|
),
|
||||||
% endfor
|
% endfor
|
||||||
],
|
),
|
||||||
[
|
${instruction["references_required"]},
|
||||||
% for reference in instruction["references"]:
|
${repr(tuple(instruction["references"]))},
|
||||||
AccountTemplate(
|
(
|
||||||
"${reference["name"]}",
|
|
||||||
${reference["is_authority"]},
|
|
||||||
${reference["optional"]},
|
|
||||||
${reference.get("is_token_mint", False)},
|
|
||||||
),
|
|
||||||
% endfor
|
|
||||||
],
|
|
||||||
[
|
|
||||||
% for ui_property in instruction["ui_properties"]:
|
% for ui_property in instruction["ui_properties"]:
|
||||||
UIProperty(
|
UIProperty(
|
||||||
${getOptionalString(ui_property, "parameter")},
|
${repr(ui_property.get("parameter"))},
|
||||||
${getOptionalString(ui_property, "account")},
|
${repr(ui_property.get("account"))},
|
||||||
"${ui_property["display_name"]}",
|
"${ui_property["display_name"]}",
|
||||||
${ui_property["is_authority"] if "is_authority" in ui_property else False},
|
${repr(ui_property.get("default_value_to_hide"))},
|
||||||
${ui_property["default_value_to_hide"] if "default_value_to_hide" in ui_property else None},
|
|
||||||
),
|
),
|
||||||
% endfor
|
% endfor
|
||||||
],
|
),
|
||||||
"${program["name"]}: ${instruction["name"]}",
|
"${program["name"]}: ${instruction["name"]}",
|
||||||
True,
|
True,
|
||||||
True,
|
True,
|
||||||
${instruction.get("is_ui_hidden", False)},
|
${instruction.get("is_ui_hidden", False)},
|
||||||
${instruction["is_multisig"]},
|
${instruction["is_multisig"]},
|
||||||
${getOptionalString(instruction, "is_deprecated_warning")},
|
${repr(instruction.get("is_deprecated_warning"))},
|
||||||
)
|
)
|
||||||
% endfor
|
% endfor
|
||||||
return Instruction(
|
return Instruction(
|
||||||
@ -209,9 +213,10 @@ def get_instruction(
|
|||||||
program_id,
|
program_id,
|
||||||
instruction_accounts,
|
instruction_accounts,
|
||||||
instruction_id,
|
instruction_id,
|
||||||
[],
|
(),
|
||||||
[],
|
0,
|
||||||
[],
|
(),
|
||||||
|
(),
|
||||||
"${program["name"]}",
|
"${program["name"]}",
|
||||||
True,
|
True,
|
||||||
False,
|
False,
|
||||||
@ -225,9 +230,10 @@ def get_instruction(
|
|||||||
program_id,
|
program_id,
|
||||||
instruction_accounts,
|
instruction_accounts,
|
||||||
0,
|
0,
|
||||||
[],
|
(),
|
||||||
[],
|
0,
|
||||||
[],
|
(),
|
||||||
|
(),
|
||||||
"Unsupported program",
|
"Unsupported program",
|
||||||
False,
|
False,
|
||||||
False,
|
False,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from .definitions import Definitions
|
from .definitions import Definitions
|
||||||
|
from .transaction.parse import parse_pubkey
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
@ -10,8 +11,6 @@ if TYPE_CHECKING:
|
|||||||
from trezor.utils import BufferReader
|
from trezor.utils import BufferReader
|
||||||
from typing_extensions import Self
|
from typing_extensions import Self
|
||||||
|
|
||||||
from .transaction import Instruction
|
|
||||||
|
|
||||||
Address = tuple[bytes, "AddressType"]
|
Address = tuple[bytes, "AddressType"]
|
||||||
AddressReference = tuple[bytes, int, "AddressType"]
|
AddressReference = tuple[bytes, int, "AddressType"]
|
||||||
Account = Address | AddressReference
|
Account = Address | AddressReference
|
||||||
@ -42,26 +41,19 @@ class PropertyTemplate(Generic[T]):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
is_authority: bool,
|
optional: bool,
|
||||||
is_optional: bool,
|
|
||||||
parse: Callable[[BufferReader], T],
|
parse: Callable[[BufferReader], T],
|
||||||
format: Callable[[Instruction, T], str],
|
format: Callable[..., str],
|
||||||
|
args: tuple[str, ...],
|
||||||
) -> None:
|
) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.is_authority = is_authority
|
self.optional = optional
|
||||||
self.is_optional = is_optional
|
|
||||||
self.parse = parse
|
self.parse = parse
|
||||||
self.format = format
|
self.format = format
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def is_pubkey(self) -> bool:
|
||||||
class AccountTemplate:
|
return self.parse is parse_pubkey
|
||||||
def __init__(
|
|
||||||
self, name: str, is_authority: bool, optional: bool, is_token_mint: bool
|
|
||||||
) -> None:
|
|
||||||
self.name = name
|
|
||||||
self.is_authority = is_authority
|
|
||||||
self.optional = optional
|
|
||||||
self.is_token_mint = is_token_mint
|
|
||||||
|
|
||||||
|
|
||||||
class UIProperty:
|
class UIProperty:
|
||||||
@ -70,13 +62,11 @@ class UIProperty:
|
|||||||
parameter: str | None,
|
parameter: str | None,
|
||||||
account: str | None,
|
account: str | None,
|
||||||
display_name: str,
|
display_name: str,
|
||||||
is_authority: bool,
|
|
||||||
default_value_to_hide: Any | None,
|
default_value_to_hide: Any | None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.parameter = parameter
|
self.parameter = parameter
|
||||||
self.account = account
|
self.account = account
|
||||||
self.display_name = display_name
|
self.display_name = display_name
|
||||||
self.is_authority = is_authority
|
|
||||||
self.default_value_to_hide = default_value_to_hide
|
self.default_value_to_hide = default_value_to_hide
|
||||||
|
|
||||||
|
|
||||||
|
@ -99,7 +99,8 @@ def create_mock_instruction(
|
|||||||
accounts=[],
|
accounts=[],
|
||||||
instruction_id=instruction_id,
|
instruction_id=instruction_id,
|
||||||
property_templates=[],
|
property_templates=[],
|
||||||
accounts_template=[],
|
accounts_required=0,
|
||||||
|
account_templates=[],
|
||||||
ui_properties=[],
|
ui_properties=[],
|
||||||
ui_name="",
|
ui_name="",
|
||||||
is_program_supported=True,
|
is_program_supported=True,
|
||||||
|
@ -28,7 +28,8 @@ def create_mock_instruction(
|
|||||||
accounts=[],
|
accounts=[],
|
||||||
instruction_id=instruction_id,
|
instruction_id=instruction_id,
|
||||||
property_templates=[],
|
property_templates=[],
|
||||||
accounts_template=[],
|
accounts_required=0,
|
||||||
|
account_templates=[],
|
||||||
ui_properties=[],
|
ui_properties=[],
|
||||||
ui_name="",
|
ui_name="",
|
||||||
is_program_supported=True,
|
is_program_supported=True,
|
||||||
|
@ -16,24 +16,31 @@ CONSTRUCT_TYPES = {
|
|||||||
"string": "String",
|
"string": "String",
|
||||||
"memo": "Memo",
|
"memo": "Memo",
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTRUCTION_TYPES = {
|
INSTRUCTION_TYPES = {
|
||||||
0: "Pass",
|
0: "Pass",
|
||||||
1: "Byte",
|
1: "Byte",
|
||||||
4: "Int32ul",
|
4: "Int32ul",
|
||||||
}
|
}
|
||||||
|
|
||||||
def upper_snake_case(name):
|
def upper_snake_case(name):
|
||||||
return "_".join(name.split(" ")).upper()
|
return "_".join(name.split(" ")).upper()
|
||||||
|
|
||||||
def camelcase(name):
|
def camelcase(name):
|
||||||
return "".join([word.capitalize() for word in name.split(" ")])
|
return "".join([word.capitalize() for word in name.split(" ")])
|
||||||
|
|
||||||
def instruction_id(instruction):
|
def instruction_id(instruction):
|
||||||
return "INS_" + upper_snake_case(instruction.name)
|
return "INS_" + upper_snake_case(instruction.name)
|
||||||
|
|
||||||
def instruction_struct_name(program, instruction):
|
def instruction_struct_name(program, instruction):
|
||||||
return camelcase(program.name) + "_" + camelcase(instruction.name) + "_Instruction"
|
return camelcase(program.name) + "_" + camelcase(instruction.name) + "_Instruction"
|
||||||
|
|
||||||
def instruction_subcon(program, instruction):
|
def instruction_subcon(program, instruction):
|
||||||
if instruction.id is None:
|
if instruction.id is None:
|
||||||
return "Pass"
|
return "Pass"
|
||||||
instruction_id_type = INSTRUCTION_TYPES[program.instruction_id_length]
|
instruction_id_type = INSTRUCTION_TYPES[program.instruction_id_length]
|
||||||
return f"Const({instruction.id}, {instruction_id_type})"
|
return f"Const({instruction.id}, {instruction_id_type})"
|
||||||
|
|
||||||
%>\
|
%>\
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from construct import (
|
from construct import (
|
||||||
@ -76,12 +83,11 @@ class ${camelcase(program.name)}Instruction(Enum):
|
|||||||
${camelcase(program.name)}_${camelcase(instruction.name)} = Struct(
|
${camelcase(program.name)}_${camelcase(instruction.name)} = Struct(
|
||||||
"program_index" / Byte,
|
"program_index" / Byte,
|
||||||
"accounts" / CompactStruct(
|
"accounts" / CompactStruct(
|
||||||
% for reference in instruction.references:
|
% for reference in instruction.references[:instruction.references_required]:
|
||||||
% if reference.optional:
|
"${reference}" / Byte,
|
||||||
"${reference.name}" / Optional(Byte),
|
% endfor
|
||||||
% else:
|
% for reference in instruction.references[instruction.references_required:]:
|
||||||
"${reference.name}" / Byte,
|
"${reference}" / Optional(Byte),
|
||||||
% endif
|
|
||||||
% endfor
|
% endfor
|
||||||
% if instruction.is_multisig:
|
% if instruction.is_multisig:
|
||||||
"multisig_signers" / Optional(GreedyRange(Byte))
|
"multisig_signers" / Optional(GreedyRange(Byte))
|
||||||
|
Loading…
Reference in New Issue
Block a user