npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@ditto-network/core

v0.9.14

Published

A JavaScript SDK for building workflows on the Ditto Network, enabling a Smart Account experience at any level of your project. This SDK provides the necessary tools and adapters for interacting with the blockchain and constructing workflows. You can use

Downloads

61

Readme

@ditto-network/core

A JavaScript SDK for building workflows on the Ditto Network, enabling a Smart Account experience at any level of your project. This SDK provides the necessary tools and adapters for interacting with the blockchain and constructing workflows. You can use the provided adapters or implement your own.

Table of Contents

Installation

npm install @ditto-network/core @ditto-network/web3.js web3

Getting Started

Here’s a quick guide to get you started with the Ditto Network SDK:

Modules

  • Provider
  • SmartWalletFactory
  • WorkflowsFactory
  • Triggers
  • Actions

Provider

The Provider module is responsible for setting up the connection to the blockchain and managing interactions.

Initialization

import { Provider as DittoProvider, BrowserStorage } from '@ditto-network/core';
import { EthersSigner, EthersContractFactory } from '@ditto-network/ethers';
import { ethers } from 'ethers';

const ethersProvider = new ethers.BrowserProvider(window.ethereum!);
const signer = await ethersProvider.getSigner();
const provider = new DittoProvider({
  signer: new EthersSigner(signer),
  storage: new BrowserStorage(),
  contractFactory: new EthersContractFactory(signer),
});

Smart Wallet (Vault)

The Vault is a modular smart contract wallet designed to securely hold and manage assets, execute deferred tasks, and interact with DeFi protocols.

Methods

getLastVaultId

Retrieves the next available Vault ID for the specified blockchain network. This method is useful for generating a new Vault without conflicts.

const chainId = 1; // Ethereum mainnet
const lastVaultId = await swFactory.getLastVaultId(chainId);
console.log(`Last Vault ID: ${lastVaultId}`);
  • chainId: The ID of the blockchain network.
getVaultAddress

Predicts the address of the Vault based on the specified chain ID and Vault ID. This method is useful for knowing the Vault address before actually deploying it.

const chainId = 1; // Ethereum mainnet
const vaultId = 2; // Example Vault ID
const vaultAddress = await swFactory.getVaultAddress(chainId, vaultId);
console.log(`Predicted Vault Address: ${vaultAddress}`);
  • chainId: The ID of the blockchain network.
  • vaultId: The ID of the Vault.
createVault

Creates a new smart wallet (Vault) on the specified blockchain network. This method deploys the Vault contract and returns the deployed Vault instance.

import { SmartWalletFactory } from '@ditto-network/core';

const swFactory = new SmartWalletFactory(provider);
const chainId = 137; // Polygon
const lastVaultId = await swFactory.getLastVaultId(chainId);
const vault = await swFactory.createVault(chainId, nextVaultId + 1);
const vaultAddress = vault.getAddress();
console.log(`New Vault Address: ${vaultAddress}`);
  • chainId: The ID of the blockchain network.
  • vaultId: The ID for the new Vault.
getDefaultOrCreate

Retrieves the default Vault for the specified chain ID and account address. If no default Vault exists, it creates a new one.

const chainId = 1; // Ethereum mainnet
const accountAddress = '0xYourAccountAddress';
const vault = await swFactory.getDefaultOrCreate(chainId, accountAddress);
const vaultAddress = vault.getAddress();
console.log(`Default or New Vault Address: ${vaultAddress}`);
  • chainId: The ID of the blockchain network.
  • accountAddress: The account address for which the default Vault is retrieved or created.

Actions and Triggers

Actions

Actions are the building blocks of a workflow. They are the steps that are executed by triggers. Each action has a configuration that defines how it should be executed. Here are available actions:

Uniswap Swap Action

The UniSwap swap action is an action that swaps tokens on UniSwap. It can be combined with price-based triggers to build a limit order workflow.

Configuration for UniSwap swap action:

type ActionConfig = {
  fromToken: { address: string, decimals: number };
  toToken: { address: string, decimals: number };
  fromAmount: string;
  slippagePercent?: number;
  providerStrategy:
    | { type: 'nodejs'; rpcUrl: string; chainId: number }
    | { type: 'browser'; provider: ethers.providers.ExternalProvider };
};
  • fromToken: Token object that represents the token from which the swap is made.
  • toToken: Token object that represents the token to which the swap is made.
  • fromAmount: Amount of fromToken that should be swapped, represented in weis multiplied by 10^fromToken.decimals.
  • slippagePercent: Optional parameter that determines the slippage percent, default is 0.5%.
  • ProviderStrategy: Configuration for the provider used to execute the swap. It can be one of the following:
    • NodeJS provider: { type: 'nodejs'; rpcUrl: string; chainId: number }
      const providerStrategy = {
        type: 'nodejs',
        rpcUrl: 'https://mainnet.infura.io/v3/your-infura-id',
        chainId: 1, // Ethereum mainnet chain ID
      };
    • Browser provider: { type: 'browser'; provider: ethers.providers.ExternalProvider }
      const providerStrategy = {
        type: 'browser',
        provider: window.ethereum,
      };

Example

new UniswapSwapActionCallDataBuilder(
  {
    fromToken: { address: '0x...', decimals: 18 },
    toToken: { address: '0x...', decimals: 6 },
    fromAmount: '1000000000000000000', // 1 token in wei
    slippagePercent: 0.05,
    providerStrategy: {
      type: 'nodejs',
      chainId: 1,
      rpcUrl: 'https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID',
    },
  },
);

In this example, 1 token of fromToken is swapped to toToken with 0.05% slippage using the NodeJS provider strategy.

MultiSender Action

The MultiSender Action allows you to send tokens to multiple recipients in a single transaction.

Configuration for MultiSender Action:

type MultiSenderActionConfig = {
  items: { to: string, amount: string, asset: { address: string, decimals: number } }[];
};
  • items: Array of objects, each representing a recipient with the address, amount, and asset.

Example

const multiSenderActionConfig = {
  items: [
    { to: '0xRecipientAddress1', amount: '1000000000000000000', asset: { address: '0x...', decimals: 18 } }, // 1 token in wei
    { to: '0xRecipientAddress2', amount: '2000000', asset: { address: '0x...', decimals: 6 } }, // 2 tokens in smallest unit
  ],
};
const multiSenderAction = new MultiSenderAction(multiSenderActionConfig, commonConfig);

Custom Contract Call Action

The CustomContractCall action allows you to interact with any smart contract by specifying the contract address, ABI, function name, and arguments. This action is useful for executing custom logic or interacting with contracts not directly supported by the SDK.

Configuration for Custom Contract Call Action:

type ActionConfig = {
  address: Address;
  functionName: string;
  abi: any;
  args: any[];
  value?: bigint; // native amount to send with the call
};
  • address: The address of the target contract.
  • functionName: The name of the function to be called on the contract.
  • abi: The ABI of the target contract.
  • args: An array of arguments to be passed to the function.
  • value: (Optional) The native amount to send with the call, default is 0.

Example usage

Here is an example of a workflow that uses the CustomContractCall action to disperse Ether to multiple recipients:

import disperseAbi from './path/to/disperseAbi.json';
import { parseUnits } from 'ethers';

const recepients = [
  ['0x...', '0.1'], // 0.1 MATIC
  ['0x...', '0.2'], // 0.2 MATIC
];

new CustomContractCall(
  {
    address: '0xD152f549545093347A162Dce210e7293f1452150',
    abi: disperseAbi,
    functionName: 'disperseEther',
    args: [
      recepients.map(([to]) => to),
      recepients.map(([, amount]) => parseUnits(amount)),
    ],
    value: recepients.reduce(
      (acc, [, amount]) => acc + parseUnits(amount),
      BigInt(0)
    ),
  },
  commonConfig
);

Triggers

Triggers define the conditions under which actions are executed. Here are the available triggers:

Instant Trigger

The Instant Trigger executes an action immediately without any conditions.

Example

const instantTrigger = new InstantTrigger();

Price-Based Trigger

A price-based trigger executes an action when the price of a specified asset meets certain conditions.

Configuration for Price-Based Trigger:

type PriceTriggerConfig = {
  uniswapPoolFeeTier: FeeAmount;
  triggerAtPrice: string; // weis * 10**fromToken.decimals
  priceMustBeHigherThan?: boolean;
  fromToken: { address: string, decimals: number };
  toToken: { address: string, decimals: number };
  providerStrategy:
    | { type: 'nodejs'; rpcUrl: string; chainId: number }
    | { type: 'browser'; provider: ethers.providers.ExternalProvider };
};
  • uniswapPoolFeeTier: The fee tier of the Uniswap pool.
  • triggerAtPrice: The price target that triggers the action.
  • priceMustBeHigherThan: Optional boolean to specify if the price must be higher than the target price.
  • fromToken: Token object representing the asset whose price is being monitored.
  • toToken: Token object representing the target asset.
  • ProviderStrategy: Configuration for the provider used to monitor the price.

Example

const priceTrigger = new PriceTrigger({
  uniswapPoolFeeTier: FeeAmount.LOW,
  triggerAtPrice: '2000000000000000000', // 2 tokens in wei
  priceMustBeHigherThan: true,
  fromToken: { address: '0x...', decimals: 18 },
  toToken: { address: '0x...', decimals: 6 },
  providerStrategy: {
    type: 'nodejs',
    rpcUrl: 'https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID',
    chainId: 1,
  },
});

In this example, the trigger is set to execute an action when the price of the specified token is greater than 2 tokens.

Time-Based Trigger

A time-based trigger executes an action at specified intervals.

Configuration for Time-Based Trigger:

type TimeTriggerConfig = {
  startAtTimestamp: number; // Unix timestamp when the trigger should start
  repeatTimes?: number; // Number of times the trigger should repeat
  cycle: { frequency: number; scale: TimeScale };
  providerStrategy:
    | { type: 'nodejs'; rpcUrl: string; chainId: number }
    | { type: 'browser'; provider: ethers.providers.ExternalProvider };
};
  • startAtTimestamp: Unix timestamp when the trigger should start.
  • repeatTimes: Optional number of times the trigger should repeat.
  • cycle: Object defining the frequency and scale (e.g., minutes, hours, days) of the trigger.
  • ProviderStrategy: Configuration for the provider used to manage the time-based execution.

Example

const timeTrigger = new TimeTrigger({
  startAtTimestamp: Math.floor(Date.now() / 1000) + 3600, // Start in 1 hour
  repeatTimes: 10, // Repeat 10 times
  cycle: { frequency: 1, scale: TimeScale.Hours },
  providerStrategy: {
    type: 'nodejs',
    rpcUrl: 'https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID',
    chainId: 1,
  },
});

In this example, the trigger is set to execute an action every hour, starting in one hour, and will repeat 10 times.

Workflow Creation

Creating workflows involves defining triggers and actions, and deploying them on the blockchain.

Example

const workflowFactory = new WorkflowsFactory(provider);

  const wf = await workflowFactory.create({
    name: 'MultiSender Action Example',
    triggers: [new InstantTrigger()], // or [] for no triggers === instant workflow execution
    actions: [
      new MultiSenderAction(
        {
          items: recepients.map(([to, amount]) => ({
            to,
            amount: parseUnits(amount, tokens.usdt.decimals),
            asset: tokens.usdt,
          })),
        },
        commonConfig
      ),
    ],
    chainId,
  });

  const tx = await wf.buildAndDeploy(swAddress, account as Address);

In this example, tokens are sent to multiple recipients with the specified amounts.

Implementing Custom Actions

Creating a custom action allows you to extend the functionality of your workflow beyond predefined actions like MultiSenderAction. You can define custom actions to interact with smart contracts or execute specific logic required for your application.

Steps to Implement a Custom Action

  1. Define the Configuration: Specify the parameters required for your action.
  2. Implement the Action Class: Create a class that implements the CallDataBuilder interface.
  3. Build Call Data: Encode the function calls to interact with the blockchain.

Example SendTokens Action

The following example demonstrates how to create a custom action that sends tokens to a recipient.

import { CallDataBuilder } from '@ditto-network/core';

// 1. Define the configuration for the SendTokens action
// This type specifies the parameters required to send tokens
type SendTokensConfig = {
  to: string; // The recipient's address
  token: Erc20Token; // The ERC20 token to be sent
  amount: string; // The amount of tokens to send, specified as a string
};

// 2. Implement the CallDataBuilder interface for the SendTokens action
// This class will build the call data necessary to perform the token transfer
export class SendTokens implements CallDataBuilder {
  // Constructor to initialize the SendTokens class with configuration and common builder options
  constructor(
    protected readonly config: SendTokensConfig,
    protected readonly commonCallDataBuilderConfig: CommonBuilderOptions
  ) {}

  // The build method is required by the CallDataBuilder interface
  // It constructs the call data for the token transfer
  public async build(): Promise<CallDataBuilderReturnData> {
    // Initialize a Set to hold the call data
    const callData = new Set<CallData>();

    // Get the contract interface for the Vault contract from the provider
    const vaultInterface = this.commonCallDataBuilderConfig.provider
      .getContractFactory()
      .getContractInterface(JSON.stringify(VaultABI));

    // Extract the chain ID from the common builder options
    const { chainId } = this.commonCallDataBuilderConfig;

    // Add the call data for withdrawing ERC20 tokens
    callData.add({
      to: this.commonCallDataBuilderConfig.vaultAddress, // The address of the Vault contract
      callData: vaultInterface.encodeFunctionData('withdrawERC20', [
        this.config.token.address, // The address of the ERC20 token
        this.config.to, // The recipient's address
        this.config.amount, // The amount of tokens to send
      ]),
    });

    // Return the call data and value (value is set to the token amount converted to BigInt)
    return { callData, value: BigInt(this.config.amount) };
  }
}

// 3. Use the Custom Action in a Workflow
const wf = await workflowFactory.create({
  name: 'Custom Action Example',
  triggers: [new InstantTrigger()],
  actions: [ 
    new SendTokens({
      to: '0x', // recipient address
      token: { address: '0x', decimals: 18 }, // token address and decimals
      amount: '1000000000000000000', // 1 token in wei
    }, commonConfig)
  ],
  chainId,
});

const tx = await wf.buildAndDeploy(swAddress, account as Address);

Implementing custom actions allows you to tailor workflows to your specific requirements, making it possible to automate a wide range of blockchain interactions. By following the steps to define the configuration, implement the action class, and build call data, you can extend the capabilities of your workflows beyond predefined actions.

Examples

Node.js

For Node.js examples, see:

React

For React examples, see the sandbox project in examples/sandbox or examples/react-example.

To run the React examples:

npm run serve

Documentation

For detailed documentation and API reference, visit our documentation site.