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

@digitalproductpassport/solidity-sdk

v1.0.5

Published

SDK for interacting with Digital Product Passport smart contracts using web3

Downloads

22

Readme

Digital Product Passport Solidity SDK

Introduction

Welcome to the Digital Product Passport SDK repository! This project provides a JavaScript/TypeScript SDK for interacting with Digital Product Passport (DPP) smart contracts deployed on the Ethereum blockchain and integrating with Pinata for IPFS storage.

What is a Digital Product Passport?

A Digital Product Passport (DPP) is a comprehensive digital record that tracks a product's journey from design to disposal. It consolidates data related to material sourcing, manufacturing, and lifecycle events, providing valuable information to manufacturers, consumers, and repair services.

Instead of a physical document, the DPP is represented digitally and can be accessed through various means such as NFC chips, QR codes, or RFID tags. The DPP provides transparency and traceability throughout the product's lifecycle.

SDK Overview

The Digital Product Passport SDK facilitates interactions with the DPP smart contracts and integrates with Pinata for storing and retrieving product-related files on IPFS.

Key Components

  • Pinata Integration: Upload JSON data and files to IPFS via Pinata.
  • ProductPassport: Deploy and interact with Product Passport contracts for managing product details.

Installation

To install the SDK, use npm:

npm install @digitalproductpassport/sdk

Configuration

To securely manage configuration values, such as private keys, RPC URLs, and Pinata API keys, use a .env file in your project root directory.

.env File

Create a .env file with the following content:

# Ethereum Configuration
ETH_PROVIDER_URL=http://localhost:8545
ETH_PRIVATE_KEY=your-private-key

# Pinata Configuration
PINATA_API_KEY=your-pinata-api-key
PINATA_SECRET_API_KEY=your-pinata-secret-api-key

Ensure you replace your-private-key, your-pinata-api-key, and your-pinata-secret-api-key with your actual values.

Loading .env Configuration

The SDK uses the dotenv package to load environment variables from the .env file. Make sure to install it:

npm install dotenv

In your code, load the environment variables at the start of your script:

import dotenv from 'dotenv';
dotenv.config();

Usage

Pinata Integration

To integrate with Pinata, use the provided utility functions to upload JSON data and files to IPFS.

Example: Uploading JSON Data

import { uploadJSONToPinata } from '@digitalproductpassport/sdk';

const jsonData = { key: 'value' };

(async () => {
  try {
    const ipfsHash = await uploadJSONToPinata(jsonData);
    console.log(`Uploaded JSON to Pinata. IPFS Hash: ${ipfsHash}`);
  } catch (error) {
    console.error(`Error: ${error.message}`);
  }
})();

Example: Uploading a File

import { uploadFileToPinata } from '@digitalproductpassport/sdk';

const filePath = 'path/to/file.pdf';

(async () => {
  try {
    const ipfsHash = await uploadFileToPinata(filePath);
    console.log(`Uploaded file to Pinata. IPFS Hash: ${ipfsHash}`);
  } catch (error) {
    console.error(`Error: ${error.message}`);
  }
})();

Product Passport Component

Deploy and interact with Product Passport contracts to manage product details.

Example: Deploying and Using Product Passport Contract

import { ethers } from 'ethers';
import { ProductPassport } from '@digitalproductpassport/sdk';

const provider = new ethers.JsonRpcProvider(process.env.ETH_PROVIDER_URL);
const privateKey = process.env.ETH_PRIVATE_KEY as string;
const gweiBid = 20;

const productPassport = new ProductPassport(provider, privateKey, gweiBid);

(async () => {
  try {
    const passportAddress = await productPassport.deployProductPassport();
    console.log(`Product Passport contract deployed at: ${passportAddress}`);

    const productDetails = {
      description: 'Test Product',
      manuals: ['Manual1', 'Manual2'],
      specifications: ['Spec1', 'Spec2'],
      batchNumber: 'Batch001',
      productionDate: '2022-01-01',
      expiryDate: '2023-01-01',
      certifications: 'Certification1',
      warrantyInfo: 'Warranty1',
      materialComposition: 'Material1',
      complianceInfo: 'Compliance1'
    };

    await productPassport.setProductData(1, productDetails);
    const product = await productPassport.getProductData(1);
    console.log(`Product details: ${JSON.stringify(product)}`);
  } catch (error) {
    console.error(`Error: ${error.message}`);
  }
})();

Example JSON Configuration

Here is an example of the JSON configuration file you might use with the SDK:

{
  "productId": "12345",
  "description": "Example Product",
  "manuals": ["path/to/manual1.pdf", "path/to/manual2.pdf"],
  "specifications": ["path/to/spec1.pdf", "path/to/spec2.pdf"],
  "jsonConfigPath": "path/to/jsonConfig.json"
}

Test Cases

Pinata Integration Tests

The test directory contains tests for the Pinata integration. These tests ensure the correct behavior of the upload functions.

Running Tests

To run the tests, use:

npm test

Example Test Case for Pinata Integration

import { expect } from 'chai';
import sinon from 'sinon';
import fs from 'fs';
import PinataClient from '@pinata/sdk';
import * as pinataModule from '../src/utils/pinata';
import Config from '../src/config';

describe('Pinata', function () {
  let pinataStub: sinon.SinonStubbedInstance<PinataClient>;

  before(function () {
    const pinataInstance = new PinataClient(Config.getPinataApiKey(), Config.getPinataSecretApiKey());
    pinataStub = sinon.stub(pinataInstance) as unknown as sinon.SinonStubbedInstance<PinataClient>;
  });

  afterEach(function () {
    sinon.restore();
  });

  describe('uploadJSONToPinata', function () {
    it('should upload JSON data to Pinata and return the IPFS hash', async function () {
      const jsonData = { key: 'value' };
      const expectedHash = 'QmTestHash';

      pinataStub.pinJSONToIPFS.resolves({ IpfsHash: expectedHash });

      const result = await pinataModule.uploadJSONToPinata(jsonData);
      expect(result).to.equal(expectedHash);
      expect(pinataStub.pinJSONToIPFS.calledOnce).to.be.true;
    });

    it('should throw an error if uploading JSON data fails', async function () {
      const jsonData = { key: 'value' };
      const errorMessage = 'Failed to upload JSON';

      pinataStub.pinJSONToIPFS.rejects(new Error(errorMessage));

      try {
        await pinataModule.uploadJSONToPinata(jsonData);
      } catch (error: any) {
        expect(error.message).to.equal(`Error uploading JSON to Pinata: ${errorMessage}`);
      }
      expect(pinataStub.pinJSONToIPFS.calledOnce).to.be.true;
    });
  });

  describe('uploadFileToPinata', function () {
    it('should upload a file to Pinata and return the IPFS hash', async function () {
      const filePath = 'test.txt';
      const expectedHash = 'QmTestHash';

      sinon.stub(fs, 'createReadStream').returns({} as any);
      pinataStub.pinFileToIPFS.resolves({ IpfsHash: expectedHash });

      const result = await pinataModule.uploadFileToPinata(filePath);
      expect(result).to.equal(expectedHash);
      expect(pinataStub.pinFileToIPFS.calledOnce).to.be.true;
    });

    it('should throw an error if uploading a file fails', async function () {
      const filePath = 'test.txt';
      const errorMessage = 'Failed to upload file';

      sinon.stub(fs, 'createReadStream').returns({} as any);
      pinataStub.pinFileToIPFS.rejects(new Error(errorMessage));

      try {
        await pinataModule.uploadFileToPinata(filePath);
      } catch (error: any) {
        expect(error.message).to.equal(`Error uploading file to Pinata: ${errorMessage}`);
      }
      expect(pinataStub.pinFileToIPFS.calledOnce).to.be.true;
    });
  });
});

Product Passport Tests

The test directory also contains tests for the Product Passport component to ensure contract deployment and data management work as expected.

Example Test Case for Product Passport

import { expect } from 'chai';
import { ethers } from 'ethers';
import ProductPassports from '../src/ProductPassport';
import config from '../src/config';

describe('ProductPassports', function () {
  let productPassports: ProductPassports;
  let provider: ethers.JsonRpcProvider;
  let signer: ethers.Wallet;
  const privateKey = process.env.ETH_PRIVATE_KEY as string;
  const gweiBid = 20;

  before(async function () {
    provider = new ethers.JsonRpcProvider(process.env.ETH_PROVIDER_URL);
    signer = new ethers.Wallet(privateKey, provider);
    productPassports = new ProductPassports(provider, privateKey, gweiBid);
  });

  it('should deploy a product passport contract', async function () {
    const address = await productPassports.deploy

ProductPassport();
    expect(address).to.be.a('string');
    expect(address).to.have.lengthOf(42); // Check if it's a valid Ethereum address
  });

  it('should create a product passport', async function () {
    const productDetails = {
      description: 'Test Product',
      manuals: ['Manual1', 'Manual2'],
      specifications: ['Spec1', 'Spec2'],
      batchNumber: 'Batch001',
      productionDate: '2022-01-01',
      expiryDate: '2023-01-01',
      certifications: 'Certification1',
      warrantyInfo: 'Warranty1',
      materialComposition: 'Material1',
      complianceInfo: 'Compliance1'
    };

    const productPassportAddress = await productPassports.deployProductPassport();
    await productPassports.setProductData(1, productDetails);
    const product = await productPassports.getProductData(1);
    expect(product).to.have.property('description', 'Test Product');
  });

  it('should retrieve a product passport', async function () {
    const product = await productPassports.getProductData(1);
    expect(product).to.have.property('description', 'Test Product');
  });
});