Socket
Book a DemoInstallSign in
Socket

@tensor-oss/tcomp-sdk

Package Overview
Dependencies
Maintainers
3
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@tensor-oss/tcomp-sdk

Anchor/JS SDK for interacting with TCOMP.

latest
Source
npmnpm
Version
6.0.0
Version published
Maintainers
3
Created
Source

TComp SDK

  • Getting Started
  • Example Code

Getting Started

# yarn
yarn add @tensor-oss/tcomp-sdk
# npm
npm install @tensor-oss/tcomp-sdk

From source

git clone https://github.com/tensor-hq/tcomp-sdk.git
cd tcomp-sdk/
yarn
# Build JS files
yarn tsc

Example Code

import { AnchorProvider, Wallet } from "@coral-xyz/anchor";
import { Connection, Keypair, PublicKey } from "@solana/web3.js";
import { getLeafAssetId } from "@tensor-hq/tensor-common";
import {
  findListStatePda,
  findTreeAuthorityPda,
  TCompSDK,
} from "@tensor-oss/tcomp-sdk";
import BN from "bn.js";

const conn = new Connection("https://api.mainnet-beta.solana.com");
const provider = new AnchorProvider(
  conn,
  new Wallet(Keypair.generate()),
  AnchorProvider.defaultOptions()
);
const tcompSdk = new TCompSDK({ provider });

// Listing cNFT
const {
  tx: { ixs },
} = await tcompSdk.list({
  // Retrieve these fields from DAS API
  merkleTree,
  root,
  canopyDepth,
  index,
  proof,
  dataHash,
  creatorsHash,
  delegate: delegatePk,

  owner: ownerPk,
  rentPayer: rentPayerPk, // optional: payer and recipient of listing rent
  amount: new BN(priceLamports), // in lamports
  currency: null, // optional: list for SOL or SPL tokens
  expireInSec: expireIn ? new BN(expireIn) : null, // seconds until listing expires
  privateTaker: takerPk, // optional: only this wallet can buy this listing
});


// Fetching the listing
const nonce = new BN(index);
const [treeAuthority] = findTreeAuthorityPda({ merkleTree });
const assetId = getLeafAssetId(merkleTree, nonce);
const listState = findListStatePda({ assetId });
const {
  assetId: listStateAssetId,
  owner,
  amount,
  currency,
  expiry,
  privateTaker,
  makerBroker,
  rentPayer,
} = await tcompSdk.fetchListState(listState);


// Buying cNFT
const {
  tx: { ixs },
} = await tcompSdk.buy({
  // Retrieve these fields from DAS API
  merkleTree,
  root,
  canopyDepth,
  index,
  proof,
  sellerFeeBasisPoints,
  // For constructing metaHash, see example below
  metaHash,
  creators,

  payer: payerPk,
  buyer: buyerPk,
  owner: ownerPk,
  makerBroker,
  rentDest,
  maxAmount: new BN(priceLamports),
  optionalRoyaltyPct: 100, // currently required to be 100% (enforced)
});


// Bidding on a single cNFT
const {
  tx: { ixs },
} = await tcompSdk.bid({
  owner: ownerPk,
  rentPayer: rentPayerPK, // optional: payer and recipient of bid rent
  amount: new BN(priceLamports),
  expireInSec: expireIn ? new BN(expireIn) : null, // seconds until listing expires
  privateTaker: takerPk, // optional: only this wallet can sell into this bid
  bidId: new PublicKey(assetId), // asset ID of nft to bid on
  targetId: new PublicKey(assetId), // asset ID of nft to bid on
  target: Target.AssetId,
  quantity: 1,

  // Ignore these for now (advanced usage)
  margin: null,
  field: null,
  fieldId: null,
});

Constructing metaHash

const axios = require('axios');
const BN = require('bn.js');
const { PublicKey } = require("@solana/web3.js");
const { keccak_256 } = require('js-sha3');
const { computeMetadataArgsHash } = require("@tensor-hq/tensor-common");
const { TokenStandard } = require('@metaplex-foundation/mpl-bubblegum');

const url = `https://mainnet.helius-rpc.com/?api-key=<YOUR_HELIUS_API_KEY>`

const constructMetaHash = async (mint) => {

  // query DAS API for asset info
  const assetRes = await axios.post(url, {
    jsonrpc: '2.0',
    id: '0',
    method: 'getAsset',
    params: {
      id: mint
    }
  });
  const {
    compression,
    content,
    royalty,
    creators,
    uses,
    grouping,
    supply,
    ownership: { owner, delegate },
    mutable,
  } = assetRes.data.result;
  const coll = grouping.find((group) => group.group_key === "collection")?.group_value;
  const tokenStandard = content.metadata.token_standard
  const dataHashBuffer = new PublicKey(compression.data_hash).toBuffer();

  // construct metadataArgs to hash later
  // ordering follows https://docs.metaplex.com/programs/token-metadata/accounts
  var metadataArgs = {
    name: content?.metadata?.name ?? "",
    symbol: content?.metadata?.symbol ?? " ",
    uri: content?.json_uri ?? "",
    sellerFeeBasisPoints: royalty.basis_points,
    creators: creators.map((creator) => ({
      address: new PublicKey(creator.address),
      share: creator.share,
      verified: creator.verified,
    })),
    primarySaleHappened: royalty.primary_sale_happened,
    isMutable: mutable,
    editionNonce: supply?.edition_nonce != null ? supply!.edition_nonce : null,
    tokenStandard: tokenStandard === "Fungible" ? TokenStandard.Fungible :
      tokenStandard === "NonFungibleEdition" ? TokenStandard.NonFungibleEdition :
        tokenStandard === "FungibleAsset" ? TokenStandard.FungibleAsset :
          TokenStandard.NonFungible,

    // if Helius shows a collection in groupings for a cNFT then it's verified
    collection: coll ? { key: new PublicKey(coll), verified: true } : null,
    uses: uses
      ? {
        useMethod: uses.use_method === "Burn" ? 0 : uses.use_method === "Multiple" ? 1 : 2,
        remaining: uses.remaining,
        total: uses.total,
      }
      : null,

    // currently always Original (Token2022 not supported yet)
    tokenProgramVersion: 0,
  };
  const originalMetadata = { ...metadataArgs };
  const sellerFeeBasisPointsBuffer = new BN(royalty.basis_points).toBuffer("le", 2);

  // hash function on top of candidate metaHash to compare against data_hash
  const makeDataHash = (metadataArgs) =>
    Buffer.from(
      keccak_256.digest(
        Buffer.concat([
          new PublicKey(computeMetadataArgsHash(metadataArgs)).toBuffer(),
          sellerFeeBasisPointsBuffer,
        ])
      )
    );

  // try original metadataArgs
  var hash = makeDataHash(metadataArgs);
  if (hash.equals(dataHashBuffer)) return computeMetadataArgsHash(metadataArgs);

  // try tokenStandard = null
  metadataArgs.tokenStandard = null;
  hash = makeDataHash(metadataArgs);
  if (hash.equals(dataHashBuffer)) return computeMetadataArgsHash(metadataArgs);

  // try name + uri = "", tokenStandard = null
  metadataArgs.name = "";
  metadataArgs.uri = "";
  hash = makeDataHash(metadataArgs);
  if (hash.equals(dataHashBuffer)) return computeMetadataArgsHash(metadataArgs);

  // try name + uri = "", tokenStandard = 0
  metadataArgs.tokenStandard = 0;
  hash = makeDataHash(metadataArgs);
  if (hash.equals(dataHashBuffer)) return computeMetadataArgsHash(metadataArgs);

  // try reversing creators
  metadataArgs.creators.reverse();
  metadataArgs.name = originalMetadata.name;
  metadataArgs.uri = originalMetadata.uri;
  metadataArgs.tokenStandard = originalMetadata.tokenStandard;
  hash = makeDataHash(metadataArgs);
  if (hash.equals(dataHashBuffer)) return computeMetadataArgsHash(metadataArgs);

  // can't match - return null
  return null;
};

constructMetaHash("8H6C2tGh5Yu5jGXUbFtZ17FQTDyBAEQ4LfGA527QLXYQ");

FAQs

Package last updated on 07 Jun 2024

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts