Typescript SDK
SUAVE-viem is a fork of viem that will eventually be upstreamed but is currently still in a dynamic state.
Sending Confidential Compute Requests works slightly differently, but most other functionality is similar to interacting with any other EVM chain from viem.
This page describes how to work with the SUAVE-viem typescript SDK. The SDK simplifies interaction with the SUAVE Chain and provides easy-to-use functions to send transactions and query data. Below, you'll find steps on how to instantiate the library, symlink, and perform some basic actions.
Installation & Symlinkβ
Since SUAVE-viem has not been upstreamed yet, it is easiest to use via symlink. To do so you'll need to follow these steps:
- Clone the forked repository
- Install its dependencies
- Build the project if necessary
- Create a symbolic link
- Link the symbolic link in your project
Step 1: Clone the forked repositoryβ
First, you need to clone the forked repository suave-viem
from GitHub:
git clone git@github.com:flashbots/suave-viem.git && cd suave-viem
Step 2: Install its dependenciesβ
We'll be using bun for this tutorial, so you need to install that first:
curl -fsSL https://bun.sh/install | bash
Then use bun to install all the dependencies (this can take a while):
bun install
Step 3: Build the projectβ
bun run build
Step 4: Create a symbolic linkβ
After building the project, you can create a symbolic link in the global node_modules
directory:
cd src/ && bun link
This command will create a symlink in the global folder (usually /usr/local/lib/node_modules
on Unix systems) that links to the package you've built.
Step 5: Link the symbolic link in your projectβ
If you'd like to start from a template project, you can follow the tutorial on how to build SUAPPs.
Finally, create a new directory for your SUAPP:
mkdir ~/suapp && cd ~/suapp
Initialize it with:
bun init
You should see something like this logged in your terminal (you can accept the default options):
bun init helps you get started with a minimal project and tries to guess sensible defaults. Press ^C anytime to quit
package name (suapp):
entry point (index.ts):
Done! A package.json file was saved in the current directory.
+ index.ts
+ .gitignore
+ tsconfig.json (for editor auto-complete)
+ README.md
With the project setup, we can now link our specific version of viem
:
bun link viem
This command tells npm to use the symlinked version of viem
(which is actually suave-viem
you've built and linked globally) instead of the one from the npm registry.
Important Notesβ
- When you run
bun link viem
in your project, ensure there is noviem
dependency in yournode_modules
directory. If there is, you should delete it before running the link command to avoid conflicts. - After you've finished working with the symlinked package or if you want to switch back to the official version, you can unlink it by running
npm unlink --no-save viem
within your project directory and then reinstall the package from npm withnpm install viem
. - Remember to occasionally pull updates from the forked repository and rebuild it to ensure you have the latest changes while developing.
The symlink approach allows you to work with a forked library locally, making it easier to develop and test changes without affecting the original package's distribution on npm.
Instantiationβ
The rest of this guide assumes you have SUAVE running locally.
First, you need to import necessary modules and instantiate the client. In your index.ts
file, you can copy and paste the following:
import {http} from 'viem';
import {getSuaveProvider} from 'viem/chains/utils';
// connect to your local SUAVE node
const SUAVE_RPC_URL = 'http://localhost:8545';
const suaveProvider = getSuaveProvider(http(SUAVE_RPC_URL));
Wallet Creationβ
To interact with the SUAVE network, we'll first need a wallet. When running SUAVE locally, there is an account which is set up with funds for you by default. Paste the following the following block to instantiate it in viem
:
// plus other imports from above
import {Hex} from 'viem';
import {getSuaveWallet} from 'viem/chains/utils';
const DEFAULT_PRIVATE_KEY: Hex =
'0x91ab9a7e53c220e6210460b65a7a3bb2ca181412a8a7b43ff336b3df1737ce12';
const defaultWallet = getSuaveWallet({
transport: http(SUAVE_RPC_URL),
privateKey: DEFAULT_PRIVATE_KEY,
});
console.log('Wallet Address:', defaultWallet.account.address);
You can now run this file:
bun run index.ts
And you should see the following printed to your terminal:
Wallet Address: 0xBE69d72ca5f88aCba033a063dF5DBe43a4148De0
Watching Pending Transactionsβ
You can watch for pending transactions and log their details using the following example:
// Watch for pending transactions
suaveProvider.watchPendingTransactions({
async onTransactions(transactions) {
for (const hash of transactions) {
try {
const receipt = await suaveProvider.getTransactionReceipt({hash});
console.log('Transaction Receipt:', receipt);
} catch (error) {
console.error('Error fetching receipt:', error);
}
}
},
});
Send a Confidential Compute Requestβ
Let's walk through how to set up and send a Confidential Compute Request:
1. Get Current Gas Priceβ
First, instantiate a new wallet of your own, and fetch the current gas price from the network.
const PRIVATE_KEY: Hex = '<your_priv_key>';
const gasPrice = await publicClients.suaveLocal.getGasPrice();
const wallet = getSuaveWallet({
transport: http(SUAVE_RPC_URL),
privateKey: DEFAULT_PRIVATE_KEY,
});
2. Prepare the Fund Transactionβ
Create a transaction object to fund your new wallet with the required amount.
import {TransactionRequestSuave} from './node_modules/viem/chains/suave/types';
// ...
const fundTx: TransactionRequestSuave = {
type: '0x0',
value: 100000000000000001n,
gasPrice: gasPrice + 1000000000n,
chainId: suaveRigil.id,
to: wallet.account.address,
gas: 21000n,
};
3. Send the Fund Transaction and wait for confirmationβ
Send the transaction to fund the wallet.
const fund = await defaultWallet.sendTransaction(fundTx);
console.log('sent fund tx', fund);
Use a while loop to periodically check if the transaction has been confirmed.
while (true) {
const fundReceipt = await suaveProvider.getTransactionReceipt({
hash: fund,
});
if (fundReceipt) {
console.log('fund tx landed', fundReceipt);
break;
}
await sleep(4000);
}
If you once again run bun run index.ts
, you should see something like the following logged to your terminal:
sent fund tx 0xe6f7385c6992c91941fe10e43feaf987cb4377f302915945b747fcb0e0a7f40a
formatting tx receipt
fund tx landed {
blockHash: "0x23c08d5a680b75ca10f4e0d737eadade2ec4c6eb66dae6edea88867033bd436e",
blockNumber: 2n,
contractAddress: null,
cumulativeGasUsed: 21000n,
effectiveGasPrice: 3000000001n,
from: "0xbe69d72ca5f88acba033a063df5dbe43a4148de0",
gasUsed: 21000n,
logs: [],
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
status: "success",
to: "0x5f3371793285920351344a1eaaaa48d45e600652",
transactionHash: "0xe6f7385c6992c91941fe10e43feaf987cb4377f302915945b747fcb0e0a7f40a",
transactionIndex: 0,
type: "legacy"
}
4. Create a Confidential Compute Requestβ
Now, let's set up a CCR with the appropriate parameters.
import {
TransactionRequestSuave,
SuaveTxRequestTypes,
} from 'viem/chains/utils';
// ...
const ccr: TransactionRequestSuave = {
confidentialInputs:
'0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000fd7b22626c6f636b4e756d626572223a22307830222c22747873223a5b2230786638363538303064383235323038393461646263653931303332643333396338336463653834316336346566643261393232383165653664383230336538383038343032303131386164613038376337386234353663653762343234386237313565353164326465656236343031363032343832333735663130663037396663666637373934383830653731613035373366336364343133396437323037643165316235623263323365353438623061316361636533373034343739656334653939316362356130623661323930225d2c2270657263656e74223a31307d000000',
kettleAddress: '0xB5fEAfbDD752ad52Afb7e1bD2E40432A485bBB7F', // Address of your local Kettle. Use 0x03493869959C866713C33669cA118E774A30A0E5 if on Rigil.
to: '0x8f21Fdd6B4f4CacD33151777A46c122797c8BF17',
gasPrice: 10000000000n, // Gas price for the transaction
gas: 420000n, // Gas limit for the transaction
type: SuaveTxRequestTypes.ConfidentialRequest, // SUAVE transaction request type ("0x43")
chainId: suaveRigil.id,
data: '0x236eb5a70000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008f21fdd6b4f4cacd33151777a46c122797c8bf170000000000000000000000000000000000000000000000000000000000000000', // Data payload for the transaction
};
confidentialInputs
is a field to store information that should be kept private during computation, and the data field is the typical calldata required to interact with a dapp.
If you prefer typescript, you can see how to craft your own CCRs in the examples directory of suave-viem.
5. Send the Confidential Compute Requestβ
Finally, send the CCR to the network.
const res = await wallet.sendTransaction(ccr);
console.log(`sent ccr! tx hash: ${res}`);
You should see the transaction hash logged to your terminal, like this:
sent ccr! tx hash: 0xad488fd0a2b428bfa30c7ef8f8ce12e2f7f2554643ad1ca94d15ab11ad5dd9dd
Fetching Blockchain Dataβ
To fetch the latest block or transaction receipt, you can use the following functions:
async function fetchBlockchainData() {
// Get the number of the latest block
const latestBlockNumber = await suaveProvider.getBlockNumber();
console.log('Block number: ', latestBlockNumber);
// Fetch the latest block
const latestBlock = await suaveProvider.getBlock({
blockNumber: latestBlockNumber,
includeTransactions: false,
});
console.log('Latest Block:', latestBlock);
}
fetchBlockchainData();