Introduction to the EVVM
This preserved section contains legacy technical specifications.
A full architectural overhaul of this documentation is actively being developed.
The Ethereum Virtual Virtual Machine (EVVM) is a concept developed by Roll a Mate made up of a set of smart contracts that abstracts different EVM functionalities and permits the execute of transactions like:
- Pay: Transfer tokens from one account to another using, but not limited to, the addreses or the 'username', 'phone' or 'email' from the MNS (Mate Name Service) on-chain identity as the destination.
- Whitdraw: Transfer funds from the MATE Metaprotocol to the Ethereum network.
- payMultiple: Execute multiple token transfers in a single transaction.
- dispersePayment: Distribute a defined amount of tokens to multiple destination.
- caPay: A smart contract, service or smart account can execute a payment to another user, smart contract, service or smart account.
- disperseCaPayment: An smart contract, service or smart account can distribute a defined amount of funds to multiple addresses.
- recalculateReward: Recalculate the amount of MATE tokens to be rewarded to the fishers and services.
If you notice, we discuss a Mate Name Service (MNS) in the transactions. The MNS is a service that allows users to associate diferent type of identities to their address. An service can be added to the EVVM if the users use a service continuously and it's highly recommended to be added to the EVVM.
EVVM Functions
The EVVM has the following functions:
Pay
A pay function can be divided into two types with two subtypes each:
-
payNoMateStaking: A user or fisher withouth (or even with) sMATE can execute a payment to another user, this is a permissionless functionality.
payNoMateStaking_sync
: The nonce is the corresponding consecutive user nonce.payNoMateStaking_async
: The user can use any nonce as long as it has not been used before.
-
payMateStaking: Only a user or fisher with sMATE can execute a payment to another user, this is a permissioned functionality.
payMateStaking_sync
: The nonce is the corresponding consecutive user nonce.payMateStaking_async
: The user can use any nonce as long as it has not been used before.
Those functions have the following parameters:
- from (
address
): The address of the user who wants to execute the payment. - to_address (
address
): The address of the user or service who will receive the payment, or the follow. - to_identity (
string
): The MNS identity of the user who will receive the payment, only if not the previous. - token (
address
): The address of the token to be transferred. - amount (
uint256
): The amount of tokens to be transferred. - priorityFee (
uint256
): The priority fee to be paid to the fisher, as a optional tip. - executor (
address
): The address of the executor, if this payment is vauched for only some executor address. - signature (
bytes
): The signature of the user who sends the payment.
note: If to_address is not empty (doferent to address(0)
), the to_identity will be ignored.
Important: If the user chooses a sync nonce, the verification of the nonce is done inside the verification of the signature. If the user chooses an async nonce, the verification of the nonce is done outside the verification of the signature.
In every token transfer function, the execution of the transaction has a set of parameter priorities:
- to:
- If
to_address
is not empty, the token transfer will be executed to the address. - If
to_address
is empty (equal toaddress(0)
), the token transfer will be executed to the MNS identity. - If
to_address
andto_identity
are empty, EVVM unsderstands that the user wants to burn the tokens.
- executor:
- If
executor
is empty (equal toaddress(0)
), the execution can be done by the fisher, a Service or user who signed the transaction. - If
executor
is not empty, the execution can be done only by the provided address.
Note:
- For
token
we have a predefined list of tokens that can be used in the EVVM for reference go to the EVVM Tokens documentation section. - The
priorityFee
is paid using the token defined in thetoken
parameter.
Pay Process
There are two forms a user can execute a token transfer:
Sending the payload to the fishing spot
- The user signs the token transfer using the ERC-191 signature.
- The user sends the variables (payload) to the fishing spot to be captured by the fishers.
- The fishers verify the signature, the nonce of the user, the amount of tokens to be transferred, and if there is an executor, they verify if the executor is the same as the fisher. If any of these parameters are invalid, the fishers will not execute the payment.
- The EVVM executes the payment and verifies the signature and the nonce of the user (depending on the type of nonce used). If the signature and the nonce are invalid, the EVVM reverts the payment.
- If there is an executor, the EVVM verifies if the executor is the same as the fisher or the Contract Account (CA). If the executor is not the same as the fisher or CA, the EVVM reverts the payment.
- The fishers verify if the amount of tokens is sufficient to fulfill the payment plus the priority fee (optional, as a tip). If the amount of tokens is insufficient, the fishers will not execute the payment.
- The EVVM processes the payment, and the fishers receive the reward plus the priority fee.
- The nonce is updated in the user's account.
Executing the payment in a permissionless way
- The user signs the token transfer using the ERC-191 signature.
- The user sends the variables to the EVVM to be executed.
- The EVVM verifies the signature, the nonce of the user, the amount of tokens to be transferred, and if there is an executor, it verifies if the executor is the same as the fisher. If any of these parameters are invalid, the EVVM will revert.
- The EVVM executes the token transfer. If the user has sMATE tokens (staked MATE tokens), the EVVM rewards the user. Additionally, if the user has set a priority fee, this fee will be reimbursed. On the other hand, if the user does not have sMATE tokens, the protocol will not reward the user, but if they have defined a priority fee, this fee will be reimburse.
Pay signature structure
The signature of pay is a string signed using ERC-191. The signature is a string that has the following structure:
For the pay function using the to_address
parameter:
string.concat(
_priority_boolean ? "f4e1895b" : "4faa1fa2",
",",
addressToString(_receiverAddress),
",",
addressToString(_token),
",",
Strings.toString(_amount),
",",
Strings.toString(_priorityFee),
",",
Strings.toString(_nonce),
",",
_priority_boolean ? "true" : "false",
",",
addressToString(_executor)
);
For the pay function using the to_identity
parameter:
msg = string.concat(
_priority_boolean ? "f4e1895b" : "4faa1fa2",,
",",
_receiverIdentity,
",",
addressToString(_token),
",",
Strings.toString(_amount),
",",
Strings.toString(_priorityFee),
",",
Strings.toString(_nonce),
",",
_priority_boolean ? "true" : "false",
",",
addressToString(_executor)
)
The string.concat
function is a function that concatenates all the parameters.
All parameters are concatenated using a comma ,
as a separator. The addressToString
and function is a function that converts an address to a string and set all the characters to lowercase. The Strings.toString
function is a function that converts a number to a string. The _priority_boolean
parameter is a boolean value that indicates whether the payment will be executed asynchronously (true
) or synchronously (false
).
As you can see, the expression _priority_boolean ? "f4e1895b" : "4faa1fa2"
uses the boolean value _priority_boolean
to determine the corresponding function selector. The value f4e1895b
is the selector for the asynchronous payment function, while 4faa1fa2
is the selector for the synchronous payment function.
Example of a signature:
f4e1895b,annon,0x0000000000000000000000000000000000000000,100000000000000000,69000000000000,420,true,0x0000000000000000000000000000000000000000
In this example, the username 'annon' refers to an MNS (Mate Name Service) identity, which is a decentralized identity service used in the Mate protocol.
This string to be signed by the user indicates that the user wants to execute a payment to the MNS username annon using 0.1 ETH and paying 0.000069 ETH as a priority fee. The payment will be executed using an asynchronous nonce of 420 and does not have an executor.
Withdraw
A withdraw function can be divided into two types with two subtypes each and a special case:
-
No Mate Staking: A user or fisher withouth (or even with) SMate can execute a withdraw to the ethereum network.
withdrawalNoMateStaking_sync
: The nonce is the corresponding consecutive user nonce.withdrawalNoMateStaking_async
: The user can use any nonce as long as it has not been used before.
-
withdrawalMateStaking: Only a user or fisher with sMATE can execute a withdraw to the ethereum network.
withdrawalMateStaking_sync
: The nonce is the corresponding consecutive user nonce.withdrawalMateStaking_async
: The user can use any nonce as long as it has not been used before.
-
fisherWithdrawal: A user can request a withdraw calling fishers to perform it. This function has a spacial nonce called FisherWithdrawalNonce.
The withdrawalNoMateStaking
/withdrawalMateStaking
function has the following parameters:
-
user (
address
): The address of the user who wants to execute the withdraw. -
addressToReceive (
address
): The address of the user or contract who will receive the withdraw in the Ethereum network. -
token (
address
): The address of the token to be withdrawn. -
amount (
uint256
): The amount of tokens to be withdrawn. -
priorityFee (
uint256
): The priority fee to be paid to the fisher, optionally, as a tip. -
nonce (
uint256
): The nonce of the user who wants to execute the withdraw. -
signature (
bytes
): The signature of the user who wants to execute the withdraw. -
_solutionId (
uint8
): The solution id of the bridging service that the fisher will use to execute the withdraw.Solution Id Protocol 1 Axelar 2 CCIP 3 Hyperlane 4 LayerZero -
_options (
bytes
): The options of the withdraw (for Layer Zero bridge only).
For the fisherWithdrawal
function, the parameters are:
- user (
address
): The address of the user who wants to execute the withdraw. - addressToReceive (
address
): The address of the user or contract who will receive the withdraw in the Ethereum network. - token (
address
): The address of the token to be withdrawn. - amount (
uint256
): The amount of tokens to be withdrawn. - priorityFee (
uint256
): The priority fee to be paid to the fisher, optionally, as a tip. - signature (
bytes
): The signature of the user who wants to execute the withdraw.
Important: If the user chooses a sync nonce, the verification of the nonce is done inside the verification of the signature. If the user chooses an async nonce, the verification of the nonce is done outside the verification of the signature.
Withdraw cross-chain process
The withdraw process is divided into two types:
Sending the payload to the fishing spot
- The user signs the withdraw using the ERC-191 signature.
- The user sends the variables (payload) to the fishing spot to be captured by the fishers.
- The fishers verify the signature, the nonce of the user, the amount of tokens to be transferred, and if there is an executor, they verify if the executor is the same as the fisher. If any of these parameters are invalid, the fishers will not execute the withdraw.
- Verify if the amount of tokens/ETH does not exceed the limit of the withdraw, if the amount exceeds the limit, the fishers will not execute the withdraw.
- The EVVM executes the withdraw and verifies the signature, the nonce of the user (depending on the type of nonce used) and the amount of tokens to be transferred. If the signature, the nonce, or the amount of tokens are invalid, the EVVM reverts the withdraw.
- If there is an executor, the EVVM verifies if the executor is the same as the fisher. If the executor is not the same as the fisher, the EVVM reverts the withdraw.
- The message is sent to the Ethereum network to be executed and the fishers receive the reward plus the priority fee in the MATE Metaprotocol.
- The nonce is updated in the user's account.
Executing the withdraw in a permissionless way
- The user signs the withdraw using the ERC-191 signature.
- The user sends the variables to the EVVM to be executed.
- The EVVM verifies the signature, the nonce of the user, the amount of tokens to be transferred, and if there is an executor, it verifies if the executor is the same as the fisher or contract. If any of these parameters are invalid, the EVVM will not execute the withdraw.
- Verify if the amount of tokens/ETH does not exceed the limit of the withdraw, if the amount exceeds the limit, the EVVM will not execute the withdra
- The EVVM executes the withdraw sending the message to the Ethereum network.
- If the user has sMATE tokens (staked MATE tokens), the EVVM rewards the user. Additionally, if the user has set a priority fee, this fee will be reimbursed. On the other hand, if the user does not have sMATE tokens, the protocol will not reward the user, but if they have defined a priority fee, this fee will be reimbursed.
- The nonce is updated in the user's account.
Because the witdraw
allows all the cross-chain protocol operations, each type of execution has its own function message sender. If you want to see the corresponding function message sender, please check the official documentation:
Withdraw signature structure
The signature of withdraw is a string signed using ERC-191. The signature is a string that has the following structure:
For the withdraw function:
string.concat(
_priority_boolean ? "920f3d76" : "52896a1f",
",",
addressToString(addressToReceive),
",",
addressToString(_token),
",",
Strings.toString(_amount),
",",
Strings.toString(_priorityFee),
",",
Strings.toString(_nonce),
",",
_priority_boolean ? "true" : "false"
);
For the fisher withdraw function:
string.concat(
addressToString(addressToReceive),
",",
Strings.toString(_nonce),
",",
addressToString(tokenAddress),
",",
Strings.toString(_priorityFee),
",",
Strings.toString(_amount)
);
The string.concat
function concatenates all the parameters.
All parameters are concatenated using a comma ,
as a separator. The addressToString
function converts an address to a string and set all the characters to lowercase. The Strings.toString
function converts a number to a string. The _priority_boolean
parameter is a boolean value that indicates whether the withdraw will be executed asynchronously (true
) or synchronously (false
).
As you can see, the expression _priority_boolean ? "920f3d76" : "52896a1f"
uses the boolean value _priority_boolean
to determine the corresponding function selector. The value 920f3d76
is the selector for the asynchronous withdraw function, while 52896a1f
is the selector for the synchronous withdraw function.
Examples of signatures:
Case 1
920f3d76,0x63c3774531EF83631111Fe2Cf01520Fb3F5A68F7,0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48,1000000000,690,420,true
In this example, the user wants to execute a withdraw to the address 0x63c3774531EF83631111Fe2Cf01520Fb3F5A68F7
using 1000 USDC and paying 0.00069 USDC as a priority fee. The withdraw will be executed using an asynchronous nonce of 420.
Case 2
0x63c3774531EF83631111Fe2Cf01520Fb3F5A68F7,77,0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48,888,1000000000
In this example, the user wants to execute a withdraw using a fisher to the address 0x63c3774531EF83631111Fe2Cf01520Fb3F5A68F7
using 1000 USDC and paying 0.000888 USDC as a priority fee. The withdraw will be executed using the nonce 77.
payMultiple
The payMultiple function allows a user to execute multiple token transfers in a single transaction. The function has one only parameter PayData[] memory payData
that is an array of PayData structures. Each PayData structure has the following parameters:
- from (
address
): The address of the user who wants to execute the token transfer. - to_address (
address
): The address of the user who will receive the token transfer. - to_identity (
string
): The identity of the user who will receive the token transfer. - token (
address
): The address of the token to be transferred. - amount (
uint256
): The amount of tokens to be transferred. - priorityFee (
uint256
): The priority fee to be paid to the fisher, optionally, as a tip. - executor (
address
): The address of the executor, optional. - signature (
bytes
): The signature of the user who sends the token transfer.
For each item in the array, each signature must be signed by the user who wants to execute the payment, if the signature is not valid, all the payments will be reverted and the transaction will fail.
payMultiple Process
The payMultiple process is similar to the pay process, but in this case, the function cycles through all the token transfers in the array and executes them one by one, so in this sextion will explain the process externally.
Using the fishing spot
- A fisher recollect a set of token transfers that include the payload and the ERC-191 signatures for each token transfer inside the fishing spot.
- The fishers verify each signature, the nonce of the user, the amount of tokens to be transferred, and if there is an executor, limits the execution to be made by that fisher address or service. If any of these parameters is invalid, the fishers will not execute the token transfer and discard the transaction.
- The fisher executes the token transfer and the EVVM verifies the signature, if one of the signatures is invalid, the EVVM reverts all the transactions. The EVVM also verifies the nonce of the user (depending on the type of nonce used), and the amount of tokens to be transferred. If the nonce, or the amount of tokens are invalid, the EVVM defines the transaction as invalid and continues with the next one.
- If there is an executor, the EVVM verifies if the executor is the same as the fisher. If the executor is not the same as the fisher, the EVVM defines the transaction as invalid and continues with the next one.
- The EVVM verify if the amount of tokens is sufficient to fulfill the token transfer plus the potential priority fee. If the amount of tokens is insufficient, the EVVM defines the transaction as invalid and continues with the next one.
- At the end of the process, the fishers receive the reward plus the optional priority fee for each successfully executed token transfer.
Permissionless way
- A user recollect a set of token transfers with the corresponding payload and ERC-191 signatures for each token transfer.
- The user sends the variables to the EVVM to be executed.
- The EVVM verifies each signature, if one of the signatures is invalid, the EVVM reverts all the transactions.
- The EVVM the nonce of the user, the amount of tokens to be transferred, and if there is an executor, it verifies if the executor is the same as the submitter. If any of these parameters are invalid, the EVVM defines the transaction as invalid and continues with the next one.
- If there is an executor, the EVVM verifies if the executor is the same as the submitter. If the validation fails the EVVM defines the transaction as invalid and continues with the next one.
- The EVVM processes the token transfer and verifies the amount of tokens to be transferred plus the priority fee. If the amount of tokens is insufficient, the EVVM defines the transaction as invalid and continues with the next one.
- At the end if the user has sMATE tokens (staked MATE tokens), the EVVM rewards the user plus the priority fee for each successfully executed token transfer, but if the user does not have sMATE tokens, the protocol will not reward the user, but the priority fee for each successfully executed token transfer will be given.
dispersePayment
The dispersePayment function allows a user to distribute a defined amount of tokens to multiple addresses. The function has the following parameters:
address from, SplitPayMetadata[] memory toData, address token, uint256 amount, uint256 priorityFee, bool priority, uint256 nonce, address executor, bytes memory signature
- from (
address
): The address of the user who wants to execute the token transfer. - toData (
SplitPayMetadata[]
): An array of SplitPayMetadata structures. Each SplitPayMetadata structure has the following parameters:- amount (
uint256
): The amount of tokens to be transferred. - to_address (
address
): The address of the user or contract who will receive the token transfer. - to_identity (
string
): The identity of the user who will receive the token transfer.
- amount (
- token (
address
): The address of the token to be transferred. - amount (
uint256
): The amount of tokens to be transferred. - priorityFee (
uint256
): The priority fee to be paid to the fisher, optionally, as a tip. - priority (
bool
): A boolean value that indicates whether the token transfer will be executed asynchronously (true
) or synchronously (false
). - nonce (
uint256
): The nonce of the user who wants to execute the token transfer. - executor (
address
): The address of the executor. - signature (
bytes
): The signature of the user who requested this token transfer.
Important: If the user chooses a sync nonce, the verification of the nonce is done inside the verification of the signature. If the user chooses an async nonce, the verification of the nonce is done outside the verification of the signature.
dispersePayment Process
The dispersePayment process is similar to the pay process, but in this case, the function verifies the amount of tokens to be transferred versus the total amount of tokens to be transferred to all the addresses in the array.
- A user signs the payment using the ERC-191 signature and send the payload to the fishing spot.
- The fishers verify the signature, the nonce of the user, the amount of tokens to be transferred, and if there is an executor, they verify if the executor is the same as the fisher. If any of these parameters are invalid, the fishers will not execute the token transfer.
- The fishers execute the payment and the EVVM verifies the signature, the nonce of the user (depending on the type of nonce used), if it has sMate, and the amount of tokens to be transferred. If the signature, the nonce, the amount of tokens are invalid, or is not a sMate holder, the EVVM reverts the token transfer.
- If there is an executor, the EVVM verifies if the executor is the same as the fisher. If the executor is not the same as the fisher, the EVVM reverts the token transfer.
- The EVVM processes the token transfer.
- At the end of the process, the EVVM checks if the amount of tokens to be transferred is equal to the sum of the amount of tokens to be transferred to all the addresses in the array. If the amount of tokens is different, the EVVM reverts the token transfer, otherwise, the fishers receive the reward plus the priority fee if any.
dispersePayment signature structure
The signature of dispersePayment is a string signed using ERC-191. The signature is a string that has the following structure:
string.concat(
"f27f71db",
",",
bytes32ToStr(sha256(abi.encode(toData))),
",",
addressToString(_token),
",",
Strings.toString(_amount),
",",
Strings.toString(_priorityFee),
",",
Strings.toString(_nonce),
",",
_priority_boolean ? "true" : "false",
",",
addressToString(_executor)
);
The string.concat
function is a function that concatenates all the parameters.
All parameters are concatenated using a comma ,
as a separator. The addressToString
and function is a function that converts an address to a string and set all the characters to lowercase. The Strings.toString
function is a function that converts a number to a string. The _priority_boolean
parameter is a boolean value that indicates whether the payment will be executed asynchronously (true
) or synchronously (false
). The bytes32ToStr
function is a function that converts a bytes32 to a string, for this case, the function converts the hash of the toData
array to a string. And the expression f27f71db
is the selector for the dispersePayment function.
caPay
The caPay function allows a smart contract/service/smart account to execute a token transfer to another smart contract/service/smart account or to a user. The function has the following parameters:
- to (
address
): The address of the user who will receive the token transfer. - token (
address
): The address of the token to be transferred. - amount (
uint256
): The amount of tokens to be transferred.
caPay Process
The caPay process is similar to the pay process, but in this case, the function verifies if the sender of the token transfer is a smart contract/service/smart account.
- A smart contract/service/smart account sends the variables to the EVVM to be executed.
- The EVVM verifies the sender is a smart contract/service/smart account. If the sender is not a smart contract/service/smart account, the EVVM will not execute the token transfer.
- The EVVM executes the token transfer and verifies the amount of tokens to be transferred. If the amount of tokens is invalid, the EVVM reverts the token transfer.
- The EVVM processes the token transfer.
- The EVVM rewards the smart contract if the smart contract has sMATE tokens (staked MATE tokens).
disperseCaPayment
The disperseCaPayment function allows a smart contract/service/smart account to distribute a defined amount of tokens to multiple addresses. The function has the following parameters:
- toData (
SplitPayMetadata[]
): An array of SplitPayMetadata structures. Each SplitPayMetadata structure has the following parameters:- amount (
uint256
): The amount of tokens to be transferred. - to_address (
address
): The address of the user who will receive the token transfer. - to_identity (
string
): The identity of the user who will receive the token transfer.
- amount (
- token (
address
): The address of the token to be transferred. - amount (
uint256
): The amount of tokens to be transferred.
disperseCaPayment Process
The disperseCaPayment process is similar to the dispersePayment process, but in this case, the function verifies if the sender of the token transfer is a smart contract/service/smart account.
- A smart contract/service/smart account sends the variables to the EVVM to be executed.
- The EVVM verifies the sender is a smart contract/service/smart account. If the sender is not a smart contract/service/smart account, the EVVM will not execute the token transfer.
- The EVVM executes the token transfer and verifies the amount of tokens to be transferred. If the amount of tokens is invalid, the EVVM reverts the token transfer.
- The EVVM processes the token transfer.
- The EVVM rewards the smart contract if it has sMATE tokens (staked MATE tokens).
recalculateReward function
The recalculateReward function allows a fisher or user to recalculate the amount of MATE tokens to be rewarded by the protocol.
When a fisher or user executes in time the recalculateReward function, the executor will receive a reward for the transaction. This reward is calculated in the new MATE reward multiplied by a random number between 1 to 5083. This is to make the governance of the protocol permissionless and contributive, part of the hyperstructure rules set.
Deposit receiver functions
Because every cross-chain protocol has its own receiver message, excluding the Fisher Deposit, please check the oficial documentation to see the corresponding function message receiver:
The only execution added to every cross-chain message receiver is just the update of the balance of the user who receives the deposit balances[user][token] += amount
.
fisherDepositReceiver function
This function allows a fisher to execute a deposit for a user from the Ethereum network to the MATE Metaprotocol. The function has the following parameters:
- user (
address
): The address of the user who wants to execute the Deposit. - addressToReceive (
address
): The address of the user or contract who will receive the deposit in the MATE Metaprotocol. - token (
address
): The address of the token to be transferred. - priorityFee (
uint256
): The priority fee to be paid to the fisher, optionally, as a tip. - amount (
uint256
): The amount of tokens to be received. - signature (
bytes
): The signature of the user who wants to execute the deposit.
Important: The fisher who executes the deposit must be the same as the fisher who signed the deposit.
Because the nextFisherDepositNonce
is syncronous, the version of the nonce is inside the signature verification, so the user must use the getNextFisherDepositNonce
in EVVM
to get the next nonce to use.
The function has the following steps:
- The user call the
fisherDeposit
function in the Treasury contract. - The contract checks if the ERC-191 signature is valid, if it is not, the transaction will revert.
- Using the
withdraw
function allows the fisher to execute the withdraw.
Because the execution of the deposit
function is done in the EVVM
, on this section we will not cover the full process of the withdraw, if you want to see the full process, please check the fisherDepositETH
and fisherDepositERC20
functions in the Treasury
contract.