ADR 0014: Signing Runtime Transactions with Hardware Wallet
Component
Oasis SDK
Changelog
- 2022-07-15: Initial public version
Status
Proposed
Context
This document proposes the APDUSPEC additions and general UI/UX guidelines for signing Runtime transactions on Ledger and other hardware wallets:
- Signing deposit, withdrawal and transfer transactions,
- Signing unencrypted smart contract transactions,
- Signing encrypted transactions,
- Signing EVM transactions.
Test vectors
Test vectors for the Runtime transactions can be generated by using gen_runtime_vectors tool as part of the Oasis SDK.
Runtime transaction format
The structure of the Runtime transaction to be signed by the hardware wallet is the following:
/// Transaction.
#[derive(Clone, Debug, cbor::Encode, cbor::Decode)]
pub struct Transaction {
#[cbor(rename = "v")]
pub version: u16,
pub call: Call,
#[cbor(rename = "ai")]
pub auth_info: AuthInfo,
}
The transaction can be signed with Secp256k1
("Ethereum"), Ed25519
key,
or Sr25519
key! Information on this along with the gas fee is stored inside
ai
field.
call
is defined as follows:
/// Method call.
#[derive(Clone, Debug, cbor::Encode, cbor::Decode)]
pub struct Call {
/// Call format.
#[cbor(optional, default)]
pub format: CallFormat,
/// Method name.
#[cbor(optional, default, skip_serializing_if = "String::is_empty")]
pub method: String,
/// Method body.
pub body: cbor::Value,
/// Read-only flag.
///
/// A read-only call cannot make any changes to runtime state. Any attempt at modifying state
/// will result in the call failing.
#[cbor(optional, default, rename = "ro")]
pub read_only: bool,
}
If format
equals 0
, the transaction is unencrypted
and it is CBOR-encoded inside the body
field.
If format
equals 1
, the transaction is encrypted. In this case method
is
empty and body
contains a CBOR-encoded
CallEnvelopeX25519DeoxysII
which includes the
encrypted transaction body inside the data
field.
Decision
APDUSPEC additions
GET_ADDR_SECP256K1
Command
Field | Type | Content | Expected |
---|---|---|---|
CLA | byte (1) | Application Identifier | 0x05 |
INS | byte (1) | Instruction ID | 0x04 |
P1 | byte (1) | Request User confirmation | No = 0 |
P2 | byte (1) | Parameter 2 | ignored |
L | byte (1) | Bytes in payload | (depends) |
Path[0] | byte (4) | Derivation Path Data | 44 |
Path[1] | byte (4) | Derivation Path Data | 60 |
Path[2] | byte (4) | Derivation Path Data | ? |
Path[3] | byte (4) | Derivation Path Data | ? |
Path[4] | byte (4) | Derivation Path Data | ? |
The first three items in the derivation path are hardened.
Response
Field | Type | Content | Note |
---|---|---|---|
PK | byte (32) | Public Key | |
ADDR | byte (??) | Hex addr | |
SW1-SW2 | byte (2) | Return code | see list of return codes |
GET_ADDR_SR25519
Command
Field | Type | Content | Expected |
---|---|---|---|
CLA | byte (1) | Application Identifier | 0x05 |
INS | byte (1) | Instruction ID | 0x03 |
P1 | byte (1) | Request User confirmation | No = 0 |
P2 | byte (1) | Parameter 2 | ignored |
L | byte (1) | Bytes in payload | (depends) |
Path[0] | byte (4) | Derivation Path Data | 44 |
Path[1] | byte (4) | Derivation Path Data | 474 |
Path[2] | byte (4) | Derivation Path Data | ? |
Path[3] | byte (4) | Derivation Path Data | ? |
Path[4] | byte (4) | Derivation Path Data | ? |
The first three items in the derivation path are hardened.
Response
Field | Type | Content | Note |
---|---|---|---|
PK | byte (32) | Public Key | |
ADDR | byte (??) | Bech 32 addr | |
SW1-SW2 | byte (2) | Return code | see list of return codes |
SIGN_PT_ED25519
Command
Field | Type | Content | Expected |
---|---|---|---|
CLA | byte (1) | Application Identifier | 0x05 |
INS | byte (1) | Instruction ID | 0x05 |
P1 | byte (1) | Payload desc | 0 = init |
1 = add | |||
2 = last | |||
P2 | byte (1) | ---- | not used |
L | byte (1) | Bytes in payload | (depends) |
The first packet/chunk includes only the derivation path.
All other packets/chunks should contain message to sign.
First Packet
Field | Type | Content | Expected |
---|---|---|---|
Path[0] | byte (4) | Derivation Path Data | 44 |
Path[1] | byte (4) | Derivation Path Data | 474 |
Path[2] | byte (4) | Derivation Path Data | ? |
Path[3] | byte (4) | Derivation Path Data | ? |
Path[4] | byte (4) | Derivation Path Data | ? |
Other Chunks/Packets
Field | Type | Content | Expected |
---|---|---|---|
Data | bytes... | Meta+Message |
Data is defined as:
Field | Type | Content | Expected |
---|---|---|---|
Meta | bytes.. | CBOR metadata to verify | |
Message | bytes.. | CBOR data to sign |
Response
Field | Type | Content | Note |
---|---|---|---|
SIG | byte (64) | Signature | |
SW1-SW2 | byte (2) | Return code | see list of return codes |
SIGN_PT_SECP256K1
Command
Field | Type | Content | Expected |
---|---|---|---|
CLA | byte (1) | Application Identifier | 0x05 |
INS | byte (1) | Instruction ID | 0x07 |
P1 | byte (1) | Payload desc | 0 = init |
1 = add | |||
2 = last | |||
P2 | byte (1) | ---- | not used |
L | byte (1) | Bytes in payload | (depends) |
The first packet/chunk includes only the derivation path.
All other packets/chunks should contain message to sign.
First Packet
Field | Type | Content | Expected |
---|---|---|---|
Path[0] | byte (4) | Derivation Path Data | 44 |
Path[1] | byte (4) | Derivation Path Data | 60 |
Path[2] | byte (4) | Derivation Path Data | ? |
Path[3] | byte (4) | Derivation Path Data | ? |
Path[4] | byte (4) | Derivation Path Data | ? |
Other Chunks/Packets
Field | Type | Content | Expected |
---|---|---|---|
Data | bytes... | Meta+Message |
Data is defined as:
Field | Type | Content | Expected |
---|---|---|---|
Meta | bytes.. | CBOR metadata to verify | |
Message | bytes.. | CBOR data to sign |
Response
Field | Type | Content | Note |
---|---|---|---|
SIG | byte (64) | Signature | |
SW1-SW2 | byte (2) | Return code | see list of return codes |
SIGN_PT_SR25519
Command
Field | Type | Content | Expected |
---|---|---|---|
CLA | byte (1) | Application Identifier | 0x05 |
INS | byte (1) | Instruction ID | 0x06 |
P1 | byte (1) | Payload desc | 0 = init |
1 = add | |||
2 = last | |||
P2 | byte (1) | ---- | not used |
L | byte (1) | Bytes in payload | (depends) |
The first packet/chunk includes only the derivation path.
All other packets/chunks should contain message to sign.
First Packet
Field | Type | Content | Expected |
---|---|---|---|
Path[0] | byte (4) | Derivation Path Data | 44 |
Path[1] | byte (4) | Derivation Path Data | 474 |
Path[2] | byte (4) | Derivation Path Data | ? |
Path[3] | byte (4) | Derivation Path Data | ? |
Path[4] | byte (4) | Derivation Path Data | ? |
Other Chunks/Packets
Field | Type | Content | Expected |
---|---|---|---|
Data | bytes... | Meta+Message |
Data is defined as:
Field | Type | Content | Expected |
---|---|---|---|
Meta | bytes.. | CBOR metadata to verify | |
Message | bytes.. | CBOR data to sign |
Response
Field | Type | Content | Note |
---|---|---|---|
SIG | byte (64) | Signature | |
SW1-SW2 | byte (2) | Return code | see list of return codes |
Signing deposit, withdrawal and transfer transactions
Allowance (consensus layer!)
The allowance transaction is part of the consensus layer. In this document we propose an improved UI:
| Type > | < To > | < Amount > | < Fee > | < Gas limit > | < Network > | < > | < |
| Allowance | <TO> | ROSE +-<AMOUNT> | ROSE <FEE> | <GAS LIMIT> | <NETWORK> | APPROVE | REJECT |
| | | | | | | | |
IMPROVEMENT: The hardware wallet renders the
following in place of TO
for specific NETWORK
and addresses:
- Network: Mainnet, To:
oasis1qrnu9yhwzap7rqh6tdcdcpz0zf86hwhycchkhvt8
→Cipher
- Network: Testnet, To:
oasis1qqdn25n5a2jtet2s5amc7gmchsqqgs4j0qcg5k0t
→Cipher
- Network: Mainnet, To:
oasis1qzvlg0grjxwgjj58tx2xvmv26era6t2csqn22pte
→Emerald
- Network: Testnet, To:
oasis1qr629x0tg9gm5fyhedgs9lw5eh3d8ycdnsxf0run
→Emerald
Check the Mainnet network parameters and Testnet network parameters pages to obtain the current hash of the genesis document for Mainnet and Testnet networks respectively.
The mapping above should be hardcoded into the hardware wallet app. If you are interested in how addresses were derived from the Runtime ID check the staking document.
Deposit
We propose the following UI for consensus.Deposit
Runtime transaction:
| Type > | < To (1/1) > | < Amount > | < Fee > | < Gas limit > | < Network > | < ParaTime > | < > | < |
| Deposit | <MIXED_TO> | <SYM> <AMOUNT> | <SYM> <FEE> | <GAS LIMIT> | <NETWORK> | <RUNTIME> | APPROVE | REJECT |
| (ParaTime) | | | | | | | | |
MIXED_TO
can either be oasis1
or the Ethereum's 0x
address. If Meta
does not contain orig_to
field, render the tx.call.body.to
value in
oasis1
format in place of MIXED_TO
. If Meta
contains orig_to
field,
then:
- Check that the
0x
address stored inorig_to
field maps to theoasis1
address oftx.call.body.to
according to this mapping function. - Render
orig_to
value in0x
format in place ofMIXED_TO
.
In addition, if tx.call.body.to
is empty, then the deposit is made to the
signer's account inside the Runtime. In this case Self
literal is rendered in
place of MIXED_TO
.
AMOUNT
and FEE
show the amount of tokens transferred in the transaction and
the transaction fee. The number must be formatted according to the number of
decimal places and showing a corresponding symbol SYM
beside. These are
determined by the following mapping hardcoded in the hardware wallet:
(Network, Runtime ID, Denomination) → (Number of decimals, SYM)
Denomination information is stored in tx.part.body.amount[1]
or
tx.ai.fee.amount[1]
for the tokens transferred in the transaction or the fee
respectively. Empty Denomination is valid and signifies the native token
for the known networks and Runtime IDs (see below).
The hardware wallet should have at least the following mappings hardcoded:
- Network: Mainnet, Runtime ID: Cipher, Denomination: "" → 9,
ROSE
- Network: Testnet, Runtime ID: Cipher, Denomination: "" → 9,
TEST
- Network: Mainnet, Runtime ID: Emerald, Denomination: "" → 18,
ROSE
- Network: Testnet, Runtime ID: Emerald, Denomination: "" → 18,
TEST
If the lookup fails, the following policy should be respected:
SYM
is rendered as empty string.- The number of decimals is 18, if Runtime ID matches any Emerald Runtime on any network.
- Otherwise, the number of decimals is 9.
RUNTIME
shows the 32-byte hex encoded Runtime ID stored in Meta.runtime_id
.
If NETWORK
matches Mainnet or Testnet, then human-readable version of
RUNTIME
is shown:
- Network: Mainnet, Runtime ID:
000000000000000000000000000000000000000000000000e199119c992377cb
→Cipher
- Network: Testnet, Runtime ID:
0000000000000000000000000000000000000000000000000000000000000000
→Cipher
- Network: Mainnet, Runtime ID:
000000000000000000000000000000000000000000000000e2eaa99fc008f87f
→Emerald
- Network: Testnet, Runtime ID:
00000000000000000000000000000000000000000000000072c8215e60d5bca7
→Emerald
SAFETY CHECK: Runtime chain domain separation context Meta.sig_context
must be verified before showing transaction details: The last 64 characters
must match the hex value of the hash derived from Meta.runtime_id
and
Meta.chain_context
on the consensus. See golang implementation for the reference implementation.
Withdraw
The consensus.Withdraw
transaction should have the following UI on the
hardware wallet:
| Type > | < To (1/1) > | < Amount > | < Fee > | < Gas limit > | < Network > | < ParaTime > | < > | < |
| Withdraw | <TO> | <SYM> <AMOUNT> | <SYM> <FEE> | <GAS LIMIT> | <NETWORK> | <RUNTIME> | APPROVE | REJECT |
| (ParaTime) | | | | | | | | |
If tx.call.body.to
is empty, then the withdrawal is made to the signer's
consensus account. In this case Self
literal is rendered in
place of TO
.
Transfer
The accounts.Transfer
transaction should have the following UI on the
hardware wallet:
| Type > | < To (1/1) > | < Amount > | < Fee > | < Gas limit > | < Network > | < ParaTime > | < > | < |
| Transfer | <MIXED_TO> | <SYM> <AMOUNT> | <SYM> <FEE> | <GAS LIMIT> | <NETWORK> | <RUNTIME> | APPROVE | REJECT |
| (ParaTime) | | | | | | | | |
Example
The user wants to deposit 100 ROSE to
0xDce075E1C39b1ae0b75D554558b6451A226ffe00
account on Emerald on the Mainnet.
First they sign the deposit allowance transaction for Emerald.
| Type > | < To > | < Amount > | < Gas limit > | < Fee > | < Network > | < > | < |
| Allowance | Emerald | ROSE +100.0 | 1277 | ROSE 0.0 | Mainnet | APPROVE | REJECT |
| | Mainnet | | | | | | |
Next, they sign the Runtime deposit transaction.
| Type > | < To (1/2) > | < To (2/2) > | < Amount > | < Fee > | < Gas limit > | < Network > | < ParaTime > | < > | < |
| Deposit | 0xDce075E1C39b1 | 451A226ffe00 | ROSE 100.0 | ROSE 0.0 | 11310 | Mainnet | Emerald | APPROVE | REJECT |
| (ParaTime) | ae0b75D554558b6 | | | | | | | | |
Then, they transfer some tokens to another account inside the Runtime:
| Type > | < To (1/2) > | < To (2/2) > | < Amount > | < Fee > | < Gas limit > | < Network > | < ParaTime > | < > | < |
| Transfer | oasis1qpupfu7e2n | m8anj64ytrayne | ROSE 10.0 | ROSE 0.00015 | 11311 | Mainnet | Emerald | APPROVE | REJECT |
| (ParaTime) | 6pkezeaw0yhj8mce | | | | | | | | |
Finally, the user withdraws the remainder of tokens back to the Mainnet.
| Type > | < To (1/2) > | < To (2/2) > | < Amount > | < Fee > | < Gas limit > | < Network > | < ParaTime > | < > | < |
| Withdraw | oasis1qrec770vre | 504k68svq7kzve | ROSE 99.9997 | ROSE 0.00015 | 11311 | Mainnet | Emerald | APPROVE | REJECT |
| (ParaTime) | k0a9a5lcrv0zvt22 | | | | | | | | |
Signing unencrypted smart contract transactions
Uploading smart contract
contracts.Upload
transaction will not be signed by the hardware wallet
because the size of the Wasm byte code to sign may easily exceed the maximum
size of the available encrypted memory.
Instantiating smart contract
contracts.Instantiate
should have the following UI on the hardware wallet:
| Review Contract > | < Code ID > | < Amount (1/1) > | < Data (1/1) > | ... | < Fee > | < Gas limit > | < Network > | < ParaTime > | < > | < |
| Instantiation | <CODE ID> | <AMOUNT...> | <DATA> | ... | <SYM> <FEE> | <GAS LIMIT> | <NETWORK> | <RUNTIME> | APPROVE | REJECT |
| (ParaTime) | | | | ... | | | | | | |
DATA
is a JSON-like representation of tx.call.body.data
, if the latter
is a CBOR-encoded map. If tx.call.body.data
is empty or not present,
then Data screen is hidden. If tx.call.body.data
is in some other format,
require blind signing mode and hide Data screen.
Blind signing means that the user does not see all contract information. In some cases - as is this - not even the amount or the contract address! When signing blindly, it is crucial that the user trusts the client application that it generated a non-malicious transaction!
AMOUNT...
is the amount of tokens sent. Contract SDK supports sending
multiple tokens at once, each with its own denomination symbol. The hardware
wallet should render all of them, one per page. For rendering rules of each
AMOUNT
consult the consensus.Deposit
behavior.
There can be multiple Data screens Data 1, Data 2, ..., Data N for each key in
tx.call.body.data
map. DATA
can be one of the following types:
- string
- number (integer, float)
- array
- map
- boolean
- null
Strings are rendered as UTF-8 strings and the following characters need to be
escaped: :
, ,
, }
, ]
, …
.
Numbers are rendered in standard general base-10 encoding. Floats use decimal period and should be rendered with at least one decimal.
For strings and numbers that cannot fit a single page, a pagination is activated.
Boolean and null values are rendered as true
, false
and null
respectively
on a single page.
Array and map is rendered in form VAL1,VAL2,...
and KEY1:VAL1,KEY1:VAL1,...
respectively. For security, the items of the map must be sorted
lexicographically by KEY. KEY
and VAL
can be of any supported type. If it
is a map or array it is rendered as {DATA}
or [DATA]
respectively
to avoid disambiguation. Otherwise, it is just DATA
.
If the content of an array or a map cannot fit a single page, no pagination
is introduced. Instead, the content is trimmed, ellipsis …
is appended at
the end and the screen becomes confirmable. If the user double-clicks it, a
subscreen for item n
of an array or a map is shown. There is one subscreen
for each item of the array or a map of size N
titled Data n.1,
Data n.2, ..., Data n.N which renders the item n
as
DATA
for an array item or DATA:DATA
for a map item:
| Data 1.1 (1/1) > | < Data 1.2 (1/1) | < Data 1.3 (1/1) | ... | < |
| <DATA> | <DATA> | <DATA> | | BACK |
| | | | | |
The recursive approach described above allows user to browse through a complete tree of data stracture (typically a request name along with the arguments) by using ⬅️ and ➡️ buttons, visit a child by double-clicking and returning to a parent node by confirming the BACK screen.
The maximum string length, the length of the array, the depth of a map must have reasonable limits on the hardware wallet. If that limit is exceeded, the hardware wallet displays an error on the initial screen. Then, if the user still wants to sign such a transaction, they need to enable blind signing.
The following UI is shown when blind-signing a non-encrypted transaction due to too complex function parameters.
| Review Contract > | < BLIND > | < Instance ID (1/1) > | < Amount > | < Fee > | < Network > | < ParaTime > | < > | < |
| Instantiation | SIGNING! | <INSTANCE ID> | <SYM> <AMOUNT> | <SYM> <FEE> | <NETWORK> | <RUNTIME> | APPROVE | REJECT |
| (ParaTime) | | | | | | | | |
Calling smart contract
The hardware wallet should show details of the Runtime transaction to the
user, when this is possible. contracts.Call
should have the following UI on
the hardware wallet:
| Review Contract > | < Instance ID > | < Amount (1/1) > | < Data (1/1) > | ... | < Fee > | < Gas limit > | < Network > | < ParaTime > | < > | < |
| Call | <INSTANCE ID> | <AMOUNT...> | <DATA> | ... | <SYM> <FEE> | <GAS LIMIT> | <NETWORK> | <RUNTIME> | APPROVE | REJECT |
| (ParaTime) | | | | ... | | | | | | |
The Data screen behavior is the same as for
contracts.Instantiate
transaction.
Upgrading smart contracts
Signing contracts.Upgrade
should show the following UI on the hardware wallet:
| Review Contract > | < Instance ID (1/1) > | < Amount (1/1) > | < New Code ID (1/1) > | < Data (1/1) > | ... | < ParaTime > | < Fee > | < Gas limit > | < Network > | < ParaTime > | < > | < |
| Upgrade | <INSTANCE ID> | <AMOUNT...> | <CODE_ID> | <DATA> | | <RUNTIME> | <SYM> <FEE> | <GAS LIMIT> | <NETWORK> | <RUNTIME> | APPROVE | REJECT |
| (ParaTime) | | | | | | | | | | | | |
The Data screen behavior is the same as for
contracts.Instantiate
transaction.
Example
To upload, instantiate and call the hello world example running on Testnet
Cipher the user first signs the contract upload transaction with a file-based
ed25519 keypair. The user obtains the Code ID
3 for the uploaded contract.
Next, the user instantiates the contract and obtains the Instance ID
2.
| Review Contract > | < Code ID > | < Amount > | < Data > | < Fee > | < Gas limit > | < Network > | < ParaTime > | < > | < |
| Instantiation | 3 | ROSE 0.0 | {instantiate:{init | ROSE 0.0 | 1348 | Mainnet | Cipher | APPROVE | REJECT |
| (ParaTime) | | | ial_counter:42}} | | | | | | | |
Finally, they perform a call to say_hello
function on a smart contract
passing the {"who":"me"}
object as a function argument.
| Review Contract > | < Instance ID > | < Amount > | < Data > | < Fee > | < Gas limit > | < Network > | < ParaTime > | < > | < |
| Call | 2 | ROSE 0.0 | {say_hello:{who:me | ROSE 0.0 | 1283 | Mainnet | Cipher | APPROVE | REJECT |
| (ParaTime) | | | }} | | | | | | |
As a complete example, the user can provide a more complex object:
{
"who": {
"username": "alice",
"client_secret": "e5868ebb4445fc2ad9f949956c1cb9ddefa0d421",
"last_logins": [1646835046, 1615299046, 1583763046, 1552140646],
"redirect": null
}
}
In this case the hardware wallet renders the following UI.
| Review Contract > | < Instance ID > | < Amount > | < Data > | < Fee > | < Gas limit > | < Network > | < ParaTime > | < > | < |
| Call | 2 | ROSE 0.0 | {say_hello:{who:{u | ROSE 0.15 | 1283 | Mainnet | Cipher | APPROVE | REJECT |
| (ParaTime) | | | sername:alice,cli… | | | | | | |
V V
| Data 1 > | < |
| say_hello:{who:{us | BACK |
| ername:alice,clie… | |
V V
| Data 1.1 > | < |
| who:{username:alic | BACK |
| e,client_secret:[… | |
V V
| Data 1.1.1 > | < Data 1.1.2 (1/2) > | < Data 1.1.2 (2/2) > | < Data 1.1.3 > | < Data 1.1.4 > | < |
| username:alice | client_secret:e5868e | 1cb9ddefa0d421 | last_logins:[1646835 | redirect:null | BACK |
| | bb4445fc2ad9f949956c | | 046,1615299046,1583… | | |
V V
| Data 1.1.3.1 > | < Data 1.1.3.2 > | < Data 1.1.3.3 > | < Data 1.1.3.4 | < |
| 1646835046 | 1615299046 | 1583763046 | 1552140646 | BACK |
| | | | | |
Signing encrypted transactions
Encrypted transactions (tx.call.format != 0
) contain
the call data encrypted with an ephemeral X25519DeoxysII key. The hardware
wallet is not expected to implement this scheme and decrypt the transaction,
neither it is safe to share the ephemeral key with anyone. Instead, the user
must enable blind signing and the hardware wallet should show the hash of
the encrypted transaction, the X25519DeoxysII public key and the nonce:
| Review Encrypted > | < BLIND > | < Tx hash (1/1) > | < Public key (1/1) > | < Nonce (1/1) > | < Fee > | < Gas limit > | < Network > | < ParaTime > | < > | < |
| Transaction | SIGNING! | <TX_HASH> | <PUBLIC_KEY> | <NONCE> | <SYM> <FEE> | <GAS LIMIT> | <NETWORK> | <RUNTIME> | APPROVE | REJECT |
| (ParaTime) | | | | | | | | | | |
TX_HASH
is a hex representation of sha256 checksum of tx.call.data
field.
PUBLIC_KEY
is a hex representation of the 32-byte tx.call.pk
field.
NONCE
is a hex representation of the 15-byte tx.call.nonce
field.
Signing EVM transactions
Creating smart contract
evm.Create
transaction will not be managed by the hardware wallet because the
size of the EVM byte code may easily exceed the wallet's encrypted memory size.
Calling smart contract
In contrast to contracts.Call
, evm.Call
would require contract ABI in order
to extract argument names which are stored in the RLP-encoded transaction on
the blockchain. We do not support this, so only blind signing is performed
which the user needs to enable first. The UI should be as follows:
| Review EVM > | < BLIND > | < Address (1/1) > | < Amount > | < Fee > | < Network > | < ParaTime > | < > | < |
| Contract Call | SIGNING! | <ADDRESS> | <SYM> <AMOUNT> | <SYM> <FEE> | <NETWORK> | <RUNTIME> | APPROVE | REJECT |
| (ParaTime) | | | | | | | | |
Consequences
Positive
Users will have a similar experience for signing Runtime transactions on any wallet implementing this ADR.
Negative
For some transactions, user will need to trust the client application and use blind signing.
Neutral
Consideration of roothash.SubmitMsg
transactions
This ADR does not propose a UI for generic Runtime calls
(roothash.SubmitMsg
, see ADR 11). The proposed design in this ADR assumes a
new release of the hardware wallet app each time a new Runtime transaction type
is introduced.
Signing contract uploads on hardware wallets
In the future perhaps, if only the merkle root hash of the Wasm contract would be contained in the transaction, signing such a contract could be feasible. See how Ethereum 2.x contract deployment is done using this approach.
Consideration of adding From
screen
None of the proposed UIs and the existing implementation of signing the
consensus transactions on Ledger show who is a signer of the transaction.
The signer's from address can be extracted from
tx.ai.si[0].address_spec.signature.<SIGNATURE TYPE>
for oasis native address and if the signer wants to show the Ethereum address,
Meta.orig_from
should be populated and the hardware wallet should
verify it before showing the tx.