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

@helium/sus

v0.9.12

Published

Transaction simulation sus checker

Downloads

3,032

Readme

Sus

Ever wonder if a given transaction in Sus? Have difficulty parsing the cryptic return of a transaction simulation? This repo is for you!

Solana transactions are notoriously hard to parse, but it is extremely important that users understand what they are approving. This library is an attempt to bridge that gap as best we can.

Features:

  • List human-readable forms of writable accounts
  • Parse all anchor instructions and accounts on programs with published IDLs
  • Create warnings around suspicious activity
  • Human-readable balance changes with token tickers
  • Detect balance changes on cNFTs
  • Explorer link to simulated transaction
  • Parse out base and priority fees
  • Flag writable accounts that did not change in simulation
  • Batch as many fetches as possible into getMultipleAccounts to reduce load on RPC

Usage

import { sus } from "@helium/sus"

const result = await sus({
  connection,
  wallet,
  serializedTransactions: [transaction.serialize({
    verifySignatures: false,
    requireAllSignatures: false,
  })],
  // Check for cNFT changes, this only works if you have an RPC with DAS support
  checkCNfts: true,
  // Necessary for explorer link
  cluster: "devnet"
})
console.log(result)

Human-readable Writable Accounts (and changes)

On Solana, every transaciton must declare which accounts can be changed over the course of a transaction. This is an important way to detect bad behavior. If a transaction is meant to swap SOL for HNT and it labels your USDC account as writable, this is a problem!

Transaction simulation is not a silver bullet, it can be tricked. We cannot rely on simulated balance changes to reflect what can happen in a transaction. As such, we should flag which important accounts are at risk.

This feature also allows us to show a detailed diff on fields that change on anchor accounts. For example, we may see a helium IotHotspotInfoV0.location changed from one value to another in simulation.

const result = await sus({
  connection,
  wallet,
  serializedTransactions: [transaction.serialize({
    verifySignatures: false,
    requireAllSignatures: false,
  })],
  // Check for cNFT changes, this only works if you have an RPC with DAS support
  checkCNfts: true,
  // Necessary for explorer link
  cluster: "devnet"
})
console.log(result[0].writableAccounts)

Consider a transaction that mints helium data credits by burning HNT. This will return something like this. Note the owner field is best-effort but allows a UI to display which accounts may be owned by the user.:

[
  {
    address: sustWW3deA7acADNGJnkYj2EAf65EmqUNLxKekDpu6w,
    name: 'Native SOL Account',
    owner: sustWW3deA7acADNGJnkYj2EAf65EmqUNLxKekDpu6w
    pre: { type: 'NativeAccount', account: [Object], parsed: null },
    post: { type: 'NativeAccount', account: [Object], parsed: null },
    changedInSimulation: true,
    metadata: undefined
  },
  {
    address: 7KjyaNK3qRAsA4WHGzyJGDJQAJKeQmnujKbPmP3sVpzT,
    name: 'DelegatedDataCreditsV0',
    owner: undefined,
    pre: {
      type: 'DelegatedDataCreditsV0',
      account: null,
      parsed: undefined
    },
    post: {
      type: 'DelegatedDataCreditsV0',
      account: [Object],
      parsed: [Object]
    },
    changedInSimulation: true,
    metadata: undefined
  },
  {
    address: dcuc8Amr83Wz27ZkQ2K9NS6r8zRpf1J6cvArEBDZDmm,
    name: 'DC Mint',
    owner: undefined,
    pre: { type: 'Mint', account: [Object], parsed: [Object] },
    post: { type: 'Mint', account: [Object], parsed: [Object] },
    changedInSimulation: true,
    metadata: {
      name: 'Helium Data Credit',
      symbol: 'DC',
      uri: 'https://shdw-drive.genesysgo.net/CsDkETHRRR1EcueeN346MJoqzymkkr7RFjMqGpZMzAib/dc.json',
      decimals: 0,
      mint: dcuc8Amr83Wz27ZkQ2K9NS6r8zRpf1J6cvArEBDZDmm
    }
  },
  {
    address: EP1jYSgzfaMWr8z4gceoEdh3agfTyV2wL8Br1dHhzRVC,,
    name: 'DC Token Account',
    owner: sustWW3deA7acADNGJnkYj2EAf65EmqUNLxKekDpu6w,
    pre: { type: 'TokenAccount', account: null, parsed: undefined },
    post: { type: 'TokenAccount', account: [Object], parsed: [Object] },
    changedInSimulation: true,
    metadata: {
      name: 'Helium Data Credit',
      symbol: 'DC',
      uri: 'https://shdw-drive.genesysgo.net/CsDkETHRRR1EcueeN346MJoqzymkkr7RFjMqGpZMzAib/dc.json',
      decimals: 0,
      mint: dcuc8Amr83Wz27ZkQ2K9NS6r8zRpf1J6cvArEBDZDmm
    }
  },
  {
    address: FzLQKNNe67QgPJnUJyZ2zrNBqmjYaBYgoaYh69Yitn8Q.
    name: 'HNT Token Account',
    owner: sustWW3deA7acADNGJnkYj2EAf65EmqUNLxKekDpu6w,
    pre: { type: 'TokenAccount', account: [Object], parsed: [Object] },
    post: { type: 'TokenAccount', account: [Object], parsed: [Object] },
    changedInSimulation: true,
    metadata: {
      name: 'Helium Network Token',
      symbol: 'HNT',
      uri: 'https://shdw-drive.genesysgo.net/CsDkETHRRR1EcueeN346MJoqzymkkr7RFjMqGpZMzAib/hnt.json',
      decimals: 8,
      mint: hntyVP6YFm1Hg25TN9WGLqM12b8TQmcknKrdu1oxWux
    }
  },
  {
    address: hntyVP6YFm1Hg25TN9WGLqM12b8TQmcknKrdu1oxWux,
    name: 'HNT Mint',
    owner: undefined,
    pre: { type: 'Mint', account: [Object], parsed: [Object] },
    post: { type: 'Mint', account: [Object], parsed: [Object] },
    changedInSimulation: true,
    metadata: {
      name: 'Helium Network Token',
      symbol: 'HNT',
      uri: 'https://shdw-drive.genesysgo.net/CsDkETHRRR1EcueeN346MJoqzymkkr7RFjMqGpZMzAib/hnt.json',
      decimals: 8,
      mint: [PublicKey [PublicKey(hntyVP6YFm1Hg25TN9WGLqM12b8TQmcknKrdu1oxWux)]]
    }
  },
  {
    address: kM5w7CoX6NNZZuV3D17FUfjktMQ8tsU542giQx214QB,
    name: 'DC Token Account',
    owner: 7KjyaNK3qRAsA4WHGzyJGDJQAJKeQmnujKbPmP3sVpzT,
    pre: { type: 'TokenAccount', account: null, parsed: undefined },
    post: { type: 'TokenAccount', account: [Object], parsed: [Object] },
    changedInSimulation: true,
    metadata: {
      name: 'Helium Data Credit',
      symbol: 'DC',
      uri: 'https://shdw-drive.genesysgo.net/CsDkETHRRR1EcueeN346MJoqzymkkr7RFjMqGpZMzAib/dc.json',
      decimals: 0,
      mint: [PublicKey [PublicKey(dcuc8Amr83Wz27ZkQ2K9NS6r8zRpf1J6cvArEBDZDmm)]]
    }
  },
  {
    address: sZgXQVqAv9atfSuwNJnHgSf4tqsos6kRajANmBaBmSx,
    name: 'MintWindowedCircuitBreakerV0',
    owner: undefined,
    pre: {
      type: 'MintWindowedCircuitBreakerV0',
      account: [Object],
      parsed: [Object]
    },
    post: {
      type: 'MintWindowedCircuitBreakerV0',
      account: [Object],
      parsed: [Object]
    },
    changedInSimulation: true,
    metadata: undefined
  }
]

The parsed field in the pre-transaction and post-transaction changes will contain the anchor deserialized accounts as an object.

Parsed Anchor instructions

An important part of understanding what a transaction does is seeing a human-readable form of what actions are being taken and their argument.

Sus attempts to parse the instructions of any program with an anchor-compliant IDL. This allows you to display the actions, their arguments, and the accounts being used:

const result = await sus({
  connection,
  wallet,
  serializedTransaction: [transaction.serialize({
    verifySignatures: false,
    requireAllSignatures: false,
  })],
  // Check for cNFT changes, this only works if you have an RPC with DAS support
  checkCNfts: true,
  // Necessary for explorer link
  cluster: "devnet"
})
console.log(result[0].instructions)

Parsing results in the following data:

[
  {
    parsed: {
      name: 'mintDataCreditsV0',
      data: { args: { hntAmount: null, dcAmount: <BN: a> } },
      accounts: [
        {
          name: 'Data Credits',
          pubkey: [PublicKey [PublicKey(D1LbvrJQ9K2WbGPMbM3Fnrf5PSsDH1TDpjqJdHuvs81n)]],
          isSigner: false,
          isWritable: false
        },
        {
          name: 'Hnt Price Oracle',
          pubkey: [PublicKey [PublicKey(4DdmDswskDxXGpwHrXUfn2CNUm9rt21ac79GHNTN3J33)]],
          isSigner: false,
          isWritable: false
        },
        {
          name: 'Burner',
          pubkey: [PublicKey [PublicKey(FzLQKNNe67QgPJnUJyZ2zrNBqmjYaBYgoaYh69Yitn8Q)]],
          isSigner: false,
          isWritable: true
        },
        {
          name: 'Recipient Token Account',
          pubkey: [PublicKey [PublicKey(EP1jYSgzfaMWr8z4gceoEdh3agfTyV2wL8Br1dHhzRVC)]],
          isSigner: false,
          isWritable: true
        },
        {
          name: 'Recipient',
          pubkey: [PublicKey [PublicKey(sustWW3deA7acADNGJnkYj2EAf65EmqUNLxKekDpu6w)]],
          isSigner: true,
          isWritable: true
        },
        {
          name: 'Owner',
          pubkey: [PublicKey [PublicKey(sustWW3deA7acADNGJnkYj2EAf65EmqUNLxKekDpu6w)]],
          isSigner: true,
          isWritable: true
        },
        {
          name: 'Hnt Mint',
          pubkey: [PublicKey [PublicKey(hntyVP6YFm1Hg25TN9WGLqM12b8TQmcknKrdu1oxWux)]],
          isSigner: false,
          isWritable: true
        },
        {
          name: 'Dc Mint',
          pubkey: [PublicKey [PublicKey(dcuc8Amr83Wz27ZkQ2K9NS6r8zRpf1J6cvArEBDZDmm)]],
          isSigner: false,
          isWritable: true
        },
        {
          name: 'Circuit Breaker',
          pubkey: [PublicKey [PublicKey(sZgXQVqAv9atfSuwNJnHgSf4tqsos6kRajANmBaBmSx)]],
          isSigner: false,
          isWritable: true
        },
        {
          name: 'Circuit Breaker Program',
          pubkey: [PublicKey [PublicKey(circAbx64bbsscPbQzZAUvuXpHqrCe6fLMzc2uKXz9g)]],
          isSigner: false,
          isWritable: false
        },
        {
          name: 'Token Program',
          pubkey: [PublicKey [PublicKey(TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA)]],
          isSigner: false,
          isWritable: false
        },
        {
          name: 'System Program',
          pubkey: [PublicKey [PublicKey(11111111111111111111111111111111)]],
          isSigner: false,
          isWritable: false
        },
        {
          name: 'Associated Token Program',
          pubkey: [PublicKey [PublicKey(ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL)]],
          isSigner: false,
          isWritable: false
        }
      ]
    },
    raw: {
      data: <Buffer 4e 6d a9 84 90 5e dd 39 00 01 0a 00 00 00 00 00 00 00>,
      programId: credMBJhYFzfn7NxBMdU4aUqFggAjgztaCcv2Fo6fPT,
      accounts: [Array]
    }
  },
  {
    parsed: {
      name: 'delegateDataCreditsV0',
      data: [Object],
      accounts: [Array]
    },
    raw: {
      data: <Buffer 9a 38 e2 80 a2 73 e2 05 0a 00 00 00 00 00 00 00 03 00 00 00 46 6f 6f>,
      programId: [PublicKey [PublicKey(credMBJhYFzfn7NxBMdU4aUqFggAjgztaCcv2Fo6fPT)]],
      accounts: [Array]
    }
  }
]

Warnings

Sus attempts to warn for various suspicious transactions. One example is a transaction that sets more than 2 token accounts to writable. A normal swap involves two accounts, so 6 token accounts being writable may be perceived as a rug:

const result = await sus({
  connection,
  wallet,
  serializedTransaction: [transaction.serialize({
    verifySignatures: false,
    requireAllSignatures: false,
  })],
  // Check for cNFT changes, this only works if you have an RPC with DAS support
  checkCNfts: true,
  // Necessary for explorer link
  cluster: "devnet"
})
console.log(result[0].warnings)
[
  {
    severity: 'warning',
    message: "More than 2 accounts with negative balance change. Is this emptying your wallet?",
    shortMessage: '2+ Writable'
  }
]

Human-readable balance changes with token tickers

Balance changes need to be paired with token symbols/tickers to create useful human-redable changes.

import { sus } from "@helium/sus"

const result = await sus({
  connection,
  wallet,
  serializedTransaction: [transaction.serialize({
    verifySignatures: false,
    requireAllSignatures: false,
  })],
  // Check for cNFT changes, this only works if you have an RPC with DAS support
  checkCNfts: true,
  // Necessary for explorer link
  cluster: "devnet"
})
console.log(result[0].balanceChanges)
[
  {
    owner: PublicKey [PublicKey(sustWW3deA7acADNGJnkYj2EAf65EmqUNLxKekDpu6w)] {
      _bn: <BN: d0ad43fd01001ce7196306585b6c7b0d5d9143609a1c2d2da2fbe1094ea4de0>
    },
    address: PublicKey [PublicKey(sustWW3deA7acADNGJnkYj2EAf65EmqUNLxKekDpu6w)] {
      _bn: <BN: d0ad43fd01001ce7196306585b6c7b0d5d9143609a1c2d2da2fbe1094ea4de0>
    },
    amount: -2044280n,
    metadata: {
      mint: [PublicKey [PublicKey(So11111111111111111111111111111111111111112)]],
      decimals: 9,
      name: 'SOL',
      symbol: 'SOL'
    }
  },
  {
    owner: PublicKey [PublicKey(11111111111111111111111111111111)] {
      _bn: <BN: 0>
    },
    address: PublicKey [PublicKey(5pcFdhUfokbCKuQv8id7GV1ZFGWLt2vBEDGX76QxkpxW)] {
      _bn: <BN: 47a0e7d6cdf04ee6fcb69fc1538bcbb28c1419493986e98ed691da963bce74f7>
    },
    amount: 1000000000n,
    metadata: {
      name: 'Helium Network Token',
      symbol: 'HNT',
      uri: 'https://shdw-drive.genesysgo.net/CsDkETHRRR1EcueeN346MJoqzymkkr7RFjMqGpZMzAib/hnt.json',
      decimals: 8,
      mint: [PublicKey [PublicKey(hntyVP6YFm1Hg25TN9WGLqM12b8TQmcknKrdu1oxWux)]]
    }
  },
  {
    owner: PublicKey [PublicKey(sustWW3deA7acADNGJnkYj2EAf65EmqUNLxKekDpu6w)] {
      _bn: <BN: d0ad43fd01001ce7196306585b6c7b0d5d9143609a1c2d2da2fbe1094ea4de0>
    },
    address: PublicKey [PublicKey(FzLQKNNe67QgPJnUJyZ2zrNBqmjYaBYgoaYh69Yitn8Q)] {
      _bn: <BN: deb3a93807100fa36efe96aa2c97b5942a5582f005c99e0d2c2ece93499cf569>
    },
    amount: -1000000000n,
    metadata: {
      name: 'Helium Network Token',
      symbol: 'HNT',
      uri: 'https://shdw-drive.genesysgo.net/CsDkETHRRR1EcueeN346MJoqzymkkr7RFjMqGpZMzAib/hnt.json',
      decimals: 8,
      mint: [PublicKey [PublicKey(hntyVP6YFm1Hg25TN9WGLqM12b8TQmcknKrdu1oxWux)]]
    }
  }
]

Possible cNFT Balance Changes

Traditional transaction simulation shows the changes in balance of typical token accounts. This is, unfortunately, not directly possible with cNFTs. However, we can make a best effort by observing which assets in a wallet belong to a merkle tree that is labeled as writable. This allows us to flag that a cNFT might be transferred, burned, or updated.

To use this feature, you must either (a) use an RPC with DAS support or pass the cNfts arg with an array of cNFTs already in the wallet. You must also enable the checkCNfts param.

import { sus } from "@helium/sus"

const result = await sus({
  connection,
  wallet,
  serializedTransaction: [transaction.serialize({
    verifySignatures: false,
    requireAllSignatures: false,
  })],
  // Check for cNFT changes, this only works if you have an RPC with DAS support
  checkCNfts: true,
  // Necessary for explorer link
  cluster: "devnet"
})
console.log(result[0].possibleCNftChanges)

Unfortunately, this method is limited by how much data can be fetched from DAS. If a user has more than 200 hotspots, for example, Sus will not be able to check all hotspots past that limit for matches to a mutable tree in the transaction.

possibleCNftChanges will contain a list of DAS assets that could be changed in this tx.

Explorer Link

It is sometimes useful to inspect a simulated transaction in the solana explorer. susResult.explorerLink will give you a valid explorer link to inspect the transaction. Note that you will also need to provide the cluster param for explorer.

Base and Priority Fees

Sus will tell you both the base fee and the priority fees on a transaction. This is through susResult.priorityFee and susResult.solFee.