-
Notifications
You must be signed in to change notification settings - Fork 1
Transaction app types and mapping from json to UI layer
-
Transaction DTO (backend)
-
Data layer mapping: (https://github.com/gnosis/safe/issues/324)
The following was taken directly from the issue #324. As mentioned in the feedback, the clients should not implement as of this writing, the "Backup Plan".
For now, we ignore this type of transaction and not show it in the user interface.
These are external transactions and they are already executed.
For each such transaction, we look into a tx.transfers array.
if transfers is not empty
each transfer is a new UI cell
if transfers is empty or null
if data is not nulll
then it is custom transaction
else data is null,
so it is ETH transfer.
"tx.value" can be null or absent - then treat it as 0.
Each transfer represents a separate user interface row in the transaction list. A transfer may be incoming, or outgoing - you need to see at the transfer.to and transfer.from addresses and see if one of them is a tx.safe address.
transfer.to is a recipient address, and transfer.from is a sender address of that transfer.
These are transactions originating from the user's safe. Each such transaction is represented by a single row in a transactions list user interface.
They include transactions that are already executed (tx.isExecuted is true), transactions that were not executed (isExecuted is false) but superseeded by other executed transactions (we call them cancelled transactions), and transactions that are not yet executed.
Due to the technical details of the working with the blockchain data structures, for now, we must not rely on the transfers array that we get in the transaction object, but rather classify the transaction to one of the user interface types based on the transaction's properties.
That is, we ignore the tx.transfers for the transactions with tx.type MULTISIG_TRANSACTION
All conditions must be met:
tx.data == null
tx.operation == 0
tx.value == any
These will be ether transfers. The tx.to is recipient, the tx.safe is sender.
In all other cases the tx.data is not null, it is present.
The data is a hex-string serialized binary data. If the data is present, we expect backend to also provide the tx.dataDecoded object with the details of the method call that this transaction is invoking.
if tx.dataDecoded == null or empty, then the transaction is Custom transaction
All conditions must be met:
tx.to == safe address
tx.operation == 0
tx.dataDecoded.method is one of:
changeMasterCopy
setFallbackHandler
addOwnerWithThreshold
removeOwner
swapOwner
changeThreshold
enableModule
disableModule
Every interaction with the aforementioned methods of the Safe smart contract requires the smart contract itself to execute it, that is why we check that the tx.to is the tx.safe itself.
All conditions must be met:
tx.operation == 0
tx.contractInfo.type == "ERC20"
tx.dataDecoded.method is one of:
transfer
transferFrom
If the tx.contractInfo is not present, treat the transaction as Custom transaction
Because this is an etherum transaction directed at the token smart contract, the tx.to will be the token contract's address, and not the recipient of tokens.
To get the recipient of tokens, as well as amount transferred, you need to look at the parameters of the methods.
function transfer(address _to, uint256 _value) public returns (bool success)
For this method, the tx.safe is the sender, the _to parameter address is the recipient, and the _value is the amount of tokens.
To get the information about the token's symbol, decimals, name, etc. we look at the tx.contractInfo object.
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)
For this method, the _from parameter address is the sender, the _to parameter address is the recipient, and the value is the amount in the token's basic units.
To get the information about the token's symbol, decimals, name, etc. we look at the tx.contractInfo object.
All conditions must be met:
tx.operation == 0
tx.contractInfo.type == "ERC721"
tx.dataDecoded.method is one of:
safeTransferFrom
transferFrom
If the tx.contractInfo is not present, treat the transaction as Custom transaction
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
There are 3 possible methods. For each of them, the _from parameter is sender, the _to is recipient, the amount is 1, since this transfers only 1 token with specific _tokenId.
Everything else is treated as a Custom transaction.
The tx.to is recipient, the tx.data is the data sent along with the transaction, the tx.value is ETH value transferred with the transaction (can be positive number).
-
UI layer mapping: (each type is grouped by queued/history)
-
Transfer
- transfer direction:
if(Transfer.recipient == currentSafe) Incoming else Outgoing
- transfer direction:
-
SettingsChange
-
methodName = SettingsChange.dataDecoded.method, define human readable assets for each method insettingMethodNames = ["setFallbackHandler", "addOwnerWithThreshold", "removeOwner", "swapOwner", "changeThreshold", "changeMasterCopy", "enableModule", "disableModule"]
-
-
ChangeMasterCopy (specification of SettingsChange)
-
SettingsChange.dataDecoded.method == "changeMasterCopy", get version name string from hardcoded asset (SettingsChange.dataDecoded.params[0].value)
-
-
Custom
- n/a
-
precondition: Safe info needs to be available. Should be fetched on the first page of transaction data and re-triggered on user refresh:
* queued = status is one of {pending, waitingForConfirmation, waitingFroExecution}
sorted by nonce ascending, creation date descending
NOTE: since these are outgoing transactions, they must have nonce.
- Pending: Submitted from device, therefore not supported yet
- WaitingForConfirmation
- condition: transactionDto.isExecuted != true && transactionDto.nonce >= safe.nonce && transactionDto.confirmations.size < safe.threshold
- WaitingForExecution
- condition: transactionDto.isExecuted != true && transactionDto.nonce >= safe.nonce && transactionDto.confirmations.size >= safe.threshold
* history = status is one of {success, failed, cancelled}
sorted by execution date (if null, then created date), descending
- Success
- condition: `transactionDto.isExecuted == true && transactionDto.isSuccessful == true`
- Failed
- condition: `transactionDto.isExecuted == true && transactionDto.isSuccessful != true`
- Cancelled
- condition: `transactionDto.isExecuted != true && transactionDto.nonce < safe.nonce`
-
Pagination No dodgy business
-
Backend Assumptions: transfers - logical transfers inferred from the transaction
-
ETHER_TRANSFER: ETH is transferred (data = null) -
ERC20_TRANSFER: some token -
ERC721_TRANSFER: some other token -
UNKNOWN: might be a sh*t token