Usage Examples
This page provides practical examples of how to use the AVA SDK JS to interact with the Ava Protocol's Actively Validated Services (AVS).
This page provides practical examples of how to use the AVA SDK JS to interact with the Ava Protocol's Actively Validated Services (AVS). It demonstrates common use cases including authentication, workflow creation, workflow management, and secret management. For details about the SDK architecture, refer to SDK Architecture.
1. Authentication#
Before interacting with the AVS service, you must authenticate using either a signature or an API key.
1.1 Authentication with Signature#
async function generateSignature(privateKey) {
const wallet = new ethers.Wallet(privateKey);
const keyRequestParams = {
chainId: 11155111,
address: wallet.address,
issuedAt: new Date(),
expiredAt: new Date(new Date().getTime() + 3600),
};
const message = getKeyRequestMessage(keyRequestParams);
const signature = await wallet.signMessage(message);
return { signature, ...keyRequestParams };
}
async function authenticate() {
const signature = await generateSignature(privateKey);
const result = await client.authWithSignature(signature);
client.setAuthKey(result.authKey);
return result;
}
Sources:
1.2 Authentication Flow#
Sources:
2. Smart Wallet Management#
2.1 Creating a Smart Wallet#
// Creates a new smart wallet with provided salt and factory address
const createWallet = async (owner, token, salt, factoryAddress) => {
return await client.getWallet(
{
salt,
factoryAddress,
},
{
authKey: token,
}
);
};
Sources:
2.2 Retrieving Smart Wallets#
// Get all wallets associated with the authenticated user
async function getWallets(owner, token, shouldFetchBalances) {
const walletsResp = await client.getWallets({
authKey: token,
});
// Optionally fetch wallet balances
if (shouldFetchBalances) {
// Fetch and return wallets with balance information
// ...
}
return walletsResp;
}
Sources:
3. Workflow Creation#
Workflows are the core component of the AVA SDK and consist of a trigger, nodes, and edges.
3.1 Workflow Components#
A workflow consists of:
- Trigger: Initiates the workflow (Block, Cron, Event, Fixed Time)
- Nodes: Tasks to be executed (ContractRead, ContractWrite, RestAPI, Branch, etc.)
- Edges: Define the flow between nodes
Sources:
3.2 Creating a Basic Workflow#
// Create a workflow
const workflow = client.createWorkflow({
name: `workflow name`,
smartWalletAddress, // The smart wallet that owns this workflow
nodes: NodeFactory.createNodes([
// Define nodes here
]),
edges: [
// Define connections between nodes
],
trigger: TriggerFactory.create({
// Define trigger type and configuration
}),
startAt: Math.floor(Date.now() / 1000) + 30, // Start 30 seconds from now
expiredAt: Math.floor(Date.now() / 1000 + 3600 * 24 * 30), // Expire in 30 days
maxExecution: 0, // 0 means unlimited executions
});
// Submit the workflow to the AVS service
const workflowId = await client.submitWorkflow(workflow, {
authKey,
});
Sources:
4. Trigger Types#
4.1 Block Trigger#
Executes a workflow every N blocks.
const trigger = TriggerFactory.create({
id: triggerId,
type: TriggerType.Block,
name: "blockTrigger",
data: {
interval: 5, // Execute every 5 blocks
},
});
Sources:
4.2 Cron Trigger#
Executes a workflow based on a cron schedule.
const trigger = TriggerFactory.create({
id: triggerId,
type: TriggerType.Cron,
name: "cronTrigger",
data: {
scheduleList: ["*/2 * * * *"], // Every 2 minutes
},
});
Sources:
4.3 Event Trigger#
Executes a workflow when a specific event (like a contract event) occurs.
const trigger = TriggerFactory.create({
id: triggerId,
type: TriggerType.Event,
name: "eventTrigger",
data: {
expression: "",
matcherList: [
{
type: "topics",
valueList: [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", // Transfer event signature
"",
targetAddress, // Address to monitor
],
},
],
},
});
Sources:
5. Node Types#
5.1 Node Types Overview#
Sources:
5.2 ContractRead Node#
Used to read data from a smart contract.
{
id: nodeId,
name: "checkPrice",
type: NodeType.ContractRead,
data: {
contractAddress: oraclePriceContract,
callData: "0xfeaf968c", // Function selector
contractAbi: `[...]`, // Contract ABI
},
}
Sources:
5.3 RestAPI Node#
Used to interact with external REST APIs.
{
id: nodeId,
name: "notification",
type: NodeType.RestAPI,
data: {
url: "https://api.example.com/endpoint",
method: "POST",
body: `{ "message": "Dynamic content: {{ previousNode.data.toString() }}" }`,
headersMap: [["content-type", "application/json"]],
},
}
Sources:
5.4 Branch Node#
Used for conditional logic within workflows.
{
id: nodeId,
name: "checkAmount",
type: NodeType.Branch,
data: {
conditionsList: [
{
id: branchConditionId,
type: "if",
expression: `someValue > 1`, // JavaScript expression
},
],
},
}
Sources:
6. Practical Use Cases#
6.1 Price Monitoring and Reporting#
This example creates a workflow that periodically checks an Oracle price feed and sends notifications.
async function schedulePriceReport(schedule, smartWalletAddress, { authKey }) {
// Create price monitoring workflow
const workflow = client.createWorkflow({
name: `price report every 5 blocks`,
smartWalletAddress,
nodes: NodeFactory.createNodes([
// Node to read price from Oracle contract
{
id: nodeIdOraclePrice,
name: "checkPrice",
type: NodeType.ContractRead,
data: {
contractAddress: oraclePriceContract,
callData: "0xfeaf968c",
contractAbi: `[...]`,
},
},
// Node to send notification with price data
{
id: nodeIdNotification,
name: "notification",
type: NodeType.RestAPI,
data: {
url: "https://webhook.example.com",
method: "POST",
body: `{ "text": "The price is {{ checkPrice.data.toString() }}." }`,
headersMap: [["content-type", "application/json"]],
},
},
]),
edges: [
// Connect trigger to price check
new Edge({
id: UlidMonotonic.generate().toCanonical(),
source: triggerId,
target: nodeIdOraclePrice,
}),
// Connect price check to notification
new Edge({
id: UlidMonotonic.generate().toCanonical(),
source: nodeIdOraclePrice,
target: nodeIdNotification,
}),
],
trigger: TriggerFactory.create({...}), // Block or Cron trigger
startAt: Math.floor(Date.now() / 1000) + 30,
expiredAt: Math.floor(Date.now() / 1000 + 3600 * 24 * 30),
maxExecution: 0,
});
const workflowId = await client.submitWorkflow(workflow, { authKey });
return workflowId;
}
Sources:
6.2 Token Transfer Monitoring#
This example creates a workflow that monitors token transfers to a specific address.
async function scheduleMonitorTransfer(owner, token, target) {
// Get the first available smart wallet
const wallets = await getWallets(owner, token);
const smartWalletAddress = wallets[0].address;
// Create monitoring workflow
const workflow = client.createWorkflow({
name: `monitor transfer`,
smartWalletAddress,
nodes: NodeFactory.createNodes([
// Check if transfer amount exceeds threshold
{
id: nodeIdCheckAmount,
name: "checkAmount",
type: NodeType.Branch,
data: {
conditionsList: [
{
id: branchIdCheckAmount,
type: "if",
expression: `demoTriggerName.data.address == "0x036cbd53842c5426634e7929541ec2318f3dcf7e" && Number(demoTriggerName.data.value_formatted) > 0.005`,
},
],
},
},
// Send notification about the transfer
{
id: nodeIdNotification,
name: "notification",
type: NodeType.RestAPI,
data: {
url: "https://webhook.example.com",
method: "POST",
body: `{ "text": "Received {{demoTriggerName.data.value_formatted}} tokens" }`,
headersMap: [["content-type", "application/json"]],
},
},
]),
edges: [...], // Connect nodes
trigger: TriggerFactory.create({
id: triggerId,
type: TriggerType.Event,
name: "demoTriggerName",
data: {
expression: "",
matcherList: [
{
type: "topics",
valueList: [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", // Transfer event topic
"",
target, // Address to monitor
],
},
],
},
}),
startAt: Math.floor(Date.now() / 1000) + 30,
expiredAt: Math.floor(Date.now() / 1000 + 3600 * 24 * 30),
maxExecution: 0,
});
const workflowId = await client.submitWorkflow(workflow, { authKey: token });
return workflowId;
}
Sources:
6.3 Auto-Sweep Functionality#
This example creates a workflow that automatically sweeps tokens from a smart wallet to another address when tokens are received.
async function scheduleSweep(owner, token, target) {
// Get the first available smart wallet
const wallets = await getWallets(owner, token);
const smartWalletAddress = wallets[0].address;
// Create auto-sweep workflow
const workflow = client.createWorkflow({
name: `auto-sweep`,
smartWalletAddress,
nodes: NodeFactory.createNodes([
// Check if token is on whitelist
{
id: nodeIdCheckToken,
name: "checktoken",
type: NodeType.Branch,
data: {
conditionsList: [
{
id: branchIdCheckToken,
type: "if",
expression: `["0x036cbd53842c5426634e7929541ec2318f3dcf7e", "0xe4ab69c077896252fafbd49efd26b5d171a32410"].indexOf(demoTriggerName.data.address.toLowerCase()) >= 0`,
},
],
},
},
// Log the transfer
{
id: nodeIdLog,
name: "log",
type: NodeType.RestAPI,
data: {...},
},
// Transfer tokens to destination address
{
id: nodeIdSweep,
name: "sweep",
type: NodeType.ContractWrite,
data: {
contractAddress: "{{demoTriggerName.data.address}}", // Dynamic token address
callData: "0xa9059cbb000000000000000000000000e0f7d11fd714674722d325cd86062a5f1882e13a{{ Number(demoTriggerName.data.value).toString(16).padStart(64, '0') }}", // Transfer function call
contractAbi: "[]",
},
},
]),
edges: [...], // Connect nodes
trigger: TriggerFactory.create({
id: triggerId,
type: TriggerType.Event,
name: "demoTriggerName",
data: {
expression: "",
matcherList: [
{
type: "topics",
valueList: [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", // Transfer event topic
"",
target, // Address to monitor
],
},
],
},
}),
startAt: Math.floor(Date.now() / 1000) + 30,
expiredAt: Math.floor(Date.now() / 1000 + 3600 * 24 * 30),
maxExecution: 0,
});
const workflowId = await client.submitWorkflow(workflow, { authKey: token });
return workflowId;
}
Sources:
7. Workflow Management#
7.1 Retrieving Workflows#
// Get workflows for one or more smart wallet addresses
async function getWorkflows(addresses, options) {
const result = await client.getWorkflows(addresses, {
authKey: options.authKey,
cursor: options.cursor,
limit: options.limit,
});
return result;
}
// Get details for a specific workflow
async function getWorkflow(workflowId, authKey) {
const result = await client.getWorkflow(workflowId, {
authKey,
});
return result;
}
Sources:
7.2 Canceling and Deleting Workflows#
// Cancel a workflow (prevent future executions)
async function cancelWorkflow(workflowId, authKey) {
const result = await client.cancelWorkflow(workflowId, {
authKey,
});
return result;
}
// Delete a workflow completely
async function deleteWorkflow(workflowId, authKey) {
const result = await client.deleteWorkflow(workflowId, {
authKey,
});
return result;
}
Sources:
8. Workflow Execution Management#
8.1 Retrieving Executions#
// Get executions for one or more workflows
async function getExecutions(workflowIds, options) {
const result = await client.getExecutions(workflowIds, {
authKey: options.authKey,
cursor: options.cursor,
limit: options.limit,
});
return result;
}
// Get details for a specific execution
async function getExecution(workflowId, executionId, authKey) {
const result = await client.getExecution(workflowId, executionId, {
authKey,
});
return result;
}
Sources:
8.2 Manually Triggering Workflows#
// Manually trigger a workflow
async function triggerWorkflow(workflowId, triggerReason, authKey) {
const metadata = JSON.parse(triggerReason);
const result = await client.triggerWorkflow(
{
id: workflowId,
reason: {
type: TriggerType.Event,
blockNumber: metadata["block_number"],
logIndex: metadata["log_index"],
txHash: metadata["tx_hash"],
},
isBlocking: true, // Wait for execution to complete
},
{
authKey,
}
);
return result;
}
Sources:
9. Secret Management#
9.1 Creating Secrets#
// Create a new secret
async function createSecret(name, value, authKey) {
const result = await client.createSecret(name, value, {
authKey,
});
return result;
}
Sources:
9.2 Listing Secrets#
// List all secrets
async function listSecrets(authKey) {
const secrets = await client.listSecrets({
authKey,
});
return secrets;
}
Sources: