Who is this tutorial for:
- API users that want to interact to an EVM compatible chain that TrustVault does not support natively
- API users that want to send native currency transactions to an EVM compatible chain that TrustVault does not support natively
- API users that want to invoke smart contract methods (including ERC-20 transfers) to an EVM compatible chain that TrustVault does not support natively
Brief Outline
Supported EVM Compatible Chains
Support Chains has the benefit of:
- The balance and transactions of an addresses in the chain can be viewed in the iOS TrustVault app and TrustVault Web
- The ability to construct a transaction for the chain in the iOS TrustVault app and TrustVault Web
- The API supports creating a transaction for a supported asset in the chain by passing just the assetSymbol (e.g. LINK)
- Webhook users can get a webhook when ERC-20s are received in the chain with full ERC-20 data payload including a valuation
Unsupported EVM Compatible Chains
All EVM compatible chains are unsupported by default which means:
- They can only be interacted via TrustVault API or MetaMask x TrustVault chrome extension
In order to interact via TrustVault API with an EVM compatible chain that TrustVault does not support natively there are a few steps that needs to be done manually (these are done automatically for natively supported chains). The steps are:
-
Decide the EVM compatible chain you want to interact with and get the chain information
- see EVM chain info (RPC URL and Chain ID) section
- Decide if you are sending a native currency (i.e. MATIC on Polygon) or invoking a smart contract (including ERC-20 transfers) and follow the correct instructions:
- Construct the raw transaction by calculating the transaction details
-
Submit the raw transaction to TrustVault setting the
sendToNetworkWhenSigned: false
(the signed transaction must be sent to network manually) - Workflow / Sign the transaction as normal using your iOS device
- Poll the transaction request for the correct state
-
Grab the
rawTransactionBytes
and submit to network
Sending native currency transaction (EVM compatible chain)
To send a native currency transaction to an EVM compatible chain that TrustVault does not support natively:
-
Create a transaction using the steps in
Native Currency Transaction Mutation
-
Take note of the
requestId
from the response (required for step 2 and 3)
-
Take note of the
-
Add signatures using the TrustVault IOS app or
Add Signature Mutation
(
requestId
required) for externally held device keys. -
Poll for the
rawTransactionBytes
using the steps in Get rawTransactionBytes of the transaction request -
Broadcast the
rawTransactionBytes
to the EVM compatible chain using the steps in Broadcast the rawTransactionBytes
Invoking a smart contract method (EVM compatible chain)
When sending a smart contract transaction we can invoke any method available in the ABI of a smart contract. For transferring ERC-20 use the transfer
method of the ERC-20 smart contract.
See the steps in Generate data field on how to select the available methods of a smart contract.
To invoke a smart contract method in an EVM compatible chain that TrustVault does not support natively:
-
Create a transaction using the steps in
Smart contract transaction mutation
-
Take note of the
requestId
from the response (required for step 2 and 3)
-
Take note of the
-
Add signatures using the TrustVault IOS app or
Add Signature Mutation
(
requestId
required) for externally held device keys. -
Poll for the
rawTransactionBytes
using the steps in Get rawTransactionBytes of the transaction request -
Broadcast the
rawTransactionBytes
to the EVM compatible chain using the steps in Broadcast the rawTransactionBytes
Native Currency Transaction Mutation
The following fields are needed in order to send a native currency transaction to an EVM compatible chain that TrustVault does not support natively:
- from - the Ethereum TrustVault address where the transaction will come from
- to - the recipient of the transaction
- value - see the steps in Get value section
- gasPrice - see the steps in Get gasPrice section
- gasLimit - see the steps in Get gasLimit section
- nonce - see the steps in Get nonce section
- chainId - see EVM chain info (RPC URL and Chain ID) section
Headers:
x-api-key: <YOUR_API_KEY>
Content-Type: application/json
Request:
mutation (
$from: String!
$to: String!
$value: String!
$gasPrice: String
$gasLimit: String
$nonce: Int
$chainId: Int
) {
createEthereumTransaction(
createTransactionInput: {
ethereumTransaction: {
fromAddress: $from
to: $to
value: $value
gasPrice: $gasPrice
gasLimit: $gasLimit
nonce: $nonce
chainId: $chainId
}
source: "API"
sendToNetworkWhenSigned: false
}
) {
... on CreateEthereumTransactionResponse {
requestId
}
signData {
transaction {
fromAddress
to
value
gasPrice
gasLimit
nonce
chainId
data
}
hdWalletPath {
hdWalletPurpose
hdWalletCoinType
hdWalletAccount
hdWalletUsage
hdWalletAddressIndex
}
unverifiedDigestData {
transactionDigest
signData
shaSignData
}
}
}
}
Variables:
- FROM - the Ethereum TrustVault address where the transaction will come from
- TO - the recipient of the transaction
- VALUE - see the steps in Get value section
- GAS_PRICE - see the steps in Get gasPrice section
- GAS_LIMIT - see the steps in Get gasLimit section
- NONCE - see the steps in Get nonce section
- CHAIN_ID - see EVM chain info (RPC URL and Chain ID) section
{
"from": "<FROM>",
"to":"<TO>",
"value": "<VALUE>",
"gasPrice": "<GAS_PRICE>",
"gasLimit": "<GAS_LIMIT>",
"nonce": <NONCE>,
"chainId": <CHAIN_ID>
}
Response:
{
"data": {
"createEthereumTransaction": {
"requestId": "93b99bf0-22a4-adcf-cef7-06c1b23409c4",
"signData": {
"transaction": {
"fromAddress": "0xB96966D32f4654b823eaa3844EB381932c04C18D",
"to": "0x61Df7eAb4f740AFCeB8e468cb16d323f262e3970",
"value": "100000000000000",
"gasPrice": "30000000000",
"gasLimit": "21000",
"nonce": 0,
"chainId": 137,
"data": null
},
"hdWalletPath": {
"hdWalletPurpose": "0x80000044",
"hdWalletCoinType": "0x80000060",
"hdWalletAccount": "0x80000000",
"hdWalletUsage": "0x0",
"hdWalletAddressIndex": "0x0"
},
"unverifiedDigestData": {
"transactionDigest": "733a5d51394fef258dc2245c928ad92d82c3eee530cb4615e8a71d4ea2eafb28",
"signData": "303f0420733a5d51394fef258dc2245c928ad92d82c3eee530cb4615e8a71d4ea2eafb28301b020500800000440205008000006002050080000000020100020100",
"shaSignData": "d7d20a63d9353f3fd967803ed26ce6774432a852745c7f935ccb75e37f6c7409"
}
}
}
}
}
Smart contract transaction mutation
The following fields are needed in order to invoke a method of a smart contract in an EVM compatible chain that TrustVault does not support natively:
- from - the Ethereum TrustVault address where the transaction will come from
- contractAddress - the address of the smart contract that will be invoked
- gasPrice - see the steps in Get gasPrice section
- gasLimit - see the steps in Get gasLimit section
- nonce - see the steps in Get nonce section
- chainId - see EVM chain info (RPC URL and Chain ID) section
- data - see the steps in Generate data field section
Headers:
x-api-key: <YOUR_API_KEY>
Content-Type: application/json
Request:
mutation (
$from: String!
$contractAddress: String!
$gasPrice: String
$gasLimit: String
$nonce: Int
$chainId: Int
$data: String
) {
createEthereumTransaction(
createTransactionInput: {
ethereumTransaction: {
fromAddress: $from
to: $contractAddress
value: "0"
gasPrice: $gasPrice
gasLimit: $gasLimit
nonce: $nonce
chainId: $chainId
data: $data
}
source: "API"
sendToNetworkWhenSigned: false
}
) {
... on CreateEthereumTransactionResponse {
requestId
}
signData {
transaction {
fromAddress
to
value
gasPrice
gasLimit
nonce
chainId
data
}
hdWalletPath {
hdWalletPurpose
hdWalletCoinType
hdWalletAccount
hdWalletUsage
hdWalletAddressIndex
}
unverifiedDigestData {
transactionDigest
signData
shaSignData
}
}
}
}
Variables:
- FROM - the Ethereum TrustVault address where the transaction will come from
- CONTRACT_ADDRESS - the address of the smart contract that will be invoked
- VALUE - see the steps in Get value section
- GAS_PRICE - see the steps in Get gasPrice section
- GAS_LIMIT - see the steps in Get gasLimit section
- NONCE - see the steps in Get nonce section
- CHAIN_ID - see EVM chain info (RPC URL and Chain ID) section
- DATA - see the steps in Generate data field section
{
"from": "<FROM>",
"contractAddress":"<CONTRACT_ADDRESS>",
"gasPrice": "<GAS_PRICE>",
"gasLimit": "<GAS_LIMIT>",
"nonce": <NONCE>,
"chainId": <CHAIN_ID>,
"data": "<DATA>"
}
Response:
{
"data": {
"createEthereumTransaction": {
"requestId": "be970f58-6fec-f0f6-5880-c68342f5e768",
"signData": {
"transaction": {
"fromAddress": "0xB96966D32f4654b823eaa3844EB381932c04C18D",
"to": "0x61Df7eAb4f740AFCeB8e468cb16d323f262e3970",
"value": "0",
"gasPrice": "30000000000",
"gasLimit": "51473",
"nonce": 0,
"chainId": 137,
"data": "0xa9059cbb000000000000000000000000528880b3eb9b5c6ba9cf5215a777ed3d983c6c9e00000000000000000000000000000000000000000000000000005af3107a4000"
},
"hdWalletPath": {
"hdWalletPurpose": "0x80000044",
"hdWalletCoinType": "0x80000060",
"hdWalletAccount": "0x80000000",
"hdWalletUsage": "0x0",
"hdWalletAddressIndex": "0x0"
},
"unverifiedDigestData": {
"transactionDigest": "e57f9e8b6721571c1b4860a952c26a3a5c63a8016b725117bdf635767c639eb1",
"signData": "303f0420e57f9e8b6721571c1b4860a952c26a3a5c63a8016b725117bdf635767c639eb1301b020500800000440205008000006002050080000000020100020100",
"shaSignData": "0625e966997ca5741d838a4eb3ab59afe94769f094f4e46359a6f07645952872"
}
}
}
}
}
EVM chain info (RPC URL and Chain ID)
RPC URL - this value is needed to get necessary information from the EVM compatible chain. Chain ID - chain identifier where this transaction is going to be sent to
Please refer to rpc.info for the RPC URL
and Chain ID
of a particular EVM compatible chain
Get value
The value of the native currency (i.e. MATIC on Polygon) sent with this transaction (integer string in wei units)
Use this ethereum unit converter to convert the Ether value (i.e native currency) to wei units.
Get gasPrice
The price per unit of gas in wei units (integer string). The eth_gasPrice JSON RPC method will be used to grab the gasLimit for the transaction.
Request:
CHAINRPCURL - EVM chain info (RPC URL and Chain ID)
curl --location --request POST '<CHAIN_RPC_URL>' \
--header 'Content-Type: application/json' \
--data-raw '{
"jsonrpc":"2.0",
"method":"eth_gasPrice",
"params":[],
"id":73
}'
Response:
{"jsonrpc":"2.0","id":73,"result":"0x70295f4f0"}
convert the hex result "0x70295f4f0"
to integer string
const gasPrice = parseInt("0x70295f4f0").toString(); // "30108153072"
Get gasLimit
The maximum units of gas the transaction is allowed to use (integer string). The eth_estimateGas JSON RPC method will be used to grab the gasLimit for the transaction.
Request:
CHAINRPCURL - EVM chain info (RPC URL and Chain ID) TRANSACTIONTOFIELD - for native currency transactions this is the recipient address, for smart contract invocations (including ERC-20 transfers) this is the smart contract address
curl --location --request POST '<CHAIN_RPC_URL>' \
--header 'Content-Type: application/json' \
--data-raw '{
"jsonrpc":"2.0",
"method":"eth_estimateGas",
"params":[{"to": "<TRANSACTION_TO_FIELD>"}],
"id":1
}'
Response:
{"jsonrpc":"2.0","id":1,"result":"0x5208"}
convert the hex result "0x5208"
to integer string
const gasLimit = parseInt("0x5208").toString(); // "21000"
Get nonce
The number of transactions made by the sender prior to this one (integer). The eth_getTransactionCount JSON RPC method will be used to grab the current nonce
value.
Request:
CHAINRPCURL - EVM chain info (RPC URL and Chain ID) TRANSACTIONFROMFIELD - the Ethereum TrustVault address where the transaction will come from
curl --location --request POST '<CHAIN_RPC_URL>' \
--header 'Content-Type: application/json' \
--data-raw '{
"jsonrpc":"2.0",
"method":"eth_getTransactionCount",
"params":[
"<TRANSACTION_FROM_FIELD>",
"latest"
],
"id":1
}'
Response:
{"jsonrpc":"2.0","id":1,"result":"0x0"}
convert the result to an integer
const nonce = parseInt("0x0"); // 0
Generate data field
The data
field is optional and only required if you want to invoke methods of a smart contract (this includes ERC-20 transfers).
Follow this guide to get the ABI of the smart contract you want to invoke. Please check the ABI of the method you want to invoke and the parameters it accepts.
The example below calls the transfer
method which takes in 2 arguments:
-
address
- type address -
value
- type uint256
const web3EthAbi = require("web3-eth-abi");
const encode = (contractAbi, functionName, functionArgs) => {
for (const methodAbi of contractAbi) {
if (methodAbi.name === functionName) {
return web3EthAbi.encodeFunctionCall(methodAbi, functionArgs);
}
}
throw new Error(`function ${functionName} does not exist`);
}
const abi = [...] // contract ABI
const methodName = "transfer";
const methodArgs = [
"0x528880b3Eb9B5C6bA9Cf5215A777ED3D983C6C9e", // recipient address
"100000000000000", // value to be transferred to the recipient address (in wei) - see Get Value section
];
const data = encode(abi, methodName, methodArgs); // 0xa9059cbb000000000000000000000000528880b3eb9b5c6ba9cf5215a777ed3d983c6c9e00000000000000000000000000000000000000000000000000005af3107a4000
This is an example ABI of the WETH contract in Polygon: WETH ABI
Get rawTransactionBytes of the transaction request
The rawTransactionBytes
will be only be populated once the transaction request have enough signature to satisfy the wallet policy, otherwise it will be null.
Headers:
x-api-key: <YOUR_API_KEY>
Content-Type: application/json
Request:
query ($requestId: String!) {
getRequest(requestId: $requestId) {
requestId
status
type
rawTransactionBytes
}
}
Variables:
REQUEST_ID - the requestId of the transaction request from either Native Currency Transaction Mutation or Smart contract transaction mutation response
{
"requestId": "<REQUEST_ID>"
}
Response:
{
"data": {
"getRequest": {
"requestId": "93b99bf0-22a4-adcf-cef7-06c1b23409c4",
"status": "SIGNED",
"type": "EXTERNAL_ETH_TRANSACTION",
"rawTransactionBytes": "0xf8ae82027985400746fe00830493e094c7dad2e953dc7b11474151134737a007049f576e80b844e2bbb158000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000003dff70c63f22be43820217a03ef69f68465b19274f2fa71636b053f7daf7bcdde7b3f9e18662937231f10417a02b0ae881ad04fc9d006c173e86f9cb1b226c95a90c0a2f921a249b25e1574c0b"
}
}
}
Broadcast the rawTransactionBytes
Once the transaction request has collected enough signatures to satisfy the policy the rawTransactionBytes
will be populated and can now be broadcasted to the EVM compatible network.
The eth_sendRawTransaction JSON RPC method will used to broadcast the rawTransactionBytes
.
Request:
CHAINRPCURL - EVM chain info (RPC URL and Chain ID) RAWTRANSACTIONBYTES - Get rawTransactionBytes of the transaction request
curl --location --request POST '<CHAIN_RPC_URL>' \
--header 'Content-Type: application/json' \
--data-raw '{
"jsonrpc":"2.0",
"method":"eth_sendRawTransaction",
"params":["<RAW_TRANSACTION_BYTES>"],
"id":1
}'
Response:
{
"id":1,
"jsonrpc": "2.0",
"result": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"
}
The response.result
is the transactionHash which you can query on the EVM compatible chain blockchain explorer.