Let's take a look on sample function that accepts ethers signer, destination address (address of contract or EOA), raw data field and value to be sent within transaction. This function will request node public key to perform ECDH and derive shared encryption key and then will encrypt provided data using DEOXYS-II algorithm
const { encryptDataField } =require('@swisstronik/utils')// Encrypts data field and sends encrypted transaction// * signer – ethers signer with connected provider// * destination - destination address// * data – raw (unencrypted) transaction `data` field// * value – amount of aswtr to be sent within transactionconstsendShieldedTransaction=async (signer, destination, data, value) => {// Encrypt transaction data. `encryptDataField` function also returns // used encryption key to be able decrypt node response. It will be useful // in case if you're sending some query (for example, request PERC20 balance)const [encryptedData] =awaitencryptDataField(signer.provider.connection.url, data,// encryptionKey – also you can provide encryption key by yourself// if you want to be able to decrypt transaction payload in the future.// Otherwise, it will generate random encryption key using `tweetnacl`// library )// Construct and sign transaction with encrypted data. This transaction will// be sent as a regular Ethereum transactionreturnawaitsigner.sendTransaction({ from:signer.address, to: destination, data: encryptedData, value, })}
Example #2. Send encrypted balanceOf request
Let's take a look on two functions which are used to obtain PERC20 token balance
const { encryptDataField,decryptNodeResponse } =require('@swisstronik/utils')// Obtains PERC20 token balance using ethers wallet and contract instanceconstgetTokenBalance=async (wallet, contract) => {constreq=awaitsendSignedShieldedQuery( wallet,contract.address,contract.interface.encodeFunctionData("balanceOf", [wallet.address]), );constbalance=contract.interface.decodeFunctionResult("balanceOf", req)[0]return balance}// Sends signed encrypted query to the nodeconstsendSignedShieldedQuery=async (wallet, destination, data) => {if (!wallet.provider) {thrownewError("wallet doesn't contain connected provider") }// Encrypt call dataconst [encryptedData,usedEncryptedKey] =awaitencryptDataField(wallet.provider.connection.url, data )// Get chain id for signatureconstnetworkInfo=awaitwallet.provider.getNetwork()constnonce=awaitwallet.getTransactionCount()// We treat signed call as a transaction, but it will be sent using eth_callconstcallData= { nonce:ethers.utils.hexValue(nonce),// We use nonce to create some kind of reuse-protection to: destination, data: encryptedData, chainId:networkInfo.chainId, }// Extract signature valuesconstsignedRawCallData=awaitwallet.signTransaction(callData)constdecoded=ethers.utils.parseTransaction(signedRawCallData)// Construct call with signature valuesconstsignedCallData= { nonce:ethers.utils.hexValue(nonce),// We use nonce to create some kind of reuse-protection to:decoded.to, data:decoded.data, v:ethers.utils.hexValue(decoded.v), r:ethers.utils.hexValue(decoded.r), s:ethers.utils.hexValue(decoded.s), chainId:ethers.utils.hexValue(networkInfo.chainId) }// Do callconstresponse=awaitwallet.provider.send('eth_call', [signedCallData,"latest"])// Decrypt call resultreturnawaitdecryptNodeResponse(wallet.provider.connection.url, response, usedEncryptedKey)}
Function getTokenBalance creates a signature from owner and calls sendSignedShieldedQuery to encrypt function data, sign the query, send the call and decrypt the response