Skip to content

Tutorial to getting information about the calculator service and connecting Metamask

This is an example of how to use SingularityNET Web JS SDK to getting information about the calculator service and connecting Metamask.

Development

Install package

Before the beginning you need to install snet-sdk-web package:

sh
npm install snet-sdk-web

And additional packages

sh
npm install google-protobuf @improbable-eng/grpc-web snet-sdk-web web3
npm install --save-dev react-app-rewired buffer process os-browserify url

Make sure that you have Node.js version >= 18 and react-scripts version >= 5.0.1

Create config-overrides.js in the root of your project with the content:

javascript
const webpack = require('webpack');

module.exports = function override(config) {
    const fallback = config.resolve.fallback || {};
    Object.assign(fallback, {
        os: require.resolve('os-browserify'),t
        url: require.resolve('url'),
    });
    config.resolve.fallback = fallback;
    config.plugins = (config.plugins || []).concat([
        new webpack.ProvidePlugin({
            process: 'process/browser',
            Buffer: ['buffer', 'Buffer'],
        }),
    ]);
    config.ignoreWarnings = [/Failed to parse source map/];
    config.module.rules.push({
        test: /\.(js|mjs|jsx)$/,
        enforce: 'pre',
        loader: require.resolve('source-map-loader'),
        resolve: {
            fullySpecified: false,
        },
    });
    return config;
};

Update your package.json scripts to use react-app-rewired instead of react-scripts.

json
{
    "scripts": {
        "start": "react-app-rewired start",
        "build": "react-app-rewired build",
        "test": "react-app-rewired test",
        "eject": "react-scripts eject"
    }
}

Configuration

Firstly, we need to configure sdk and service client. So we create a config dict and then an sdk instance with that config.

js
// sdkConfig.js file
import SnetSDK from 'snet-sdk-web';

const config = {
    web3Provider: window.ethereum,
    networkId: '11155111',
    defaultGasPrice: '4700000',
    defaultGasLimit: '210000',
};

const initSDK = async () => {
    try {
        const web3Provider = window.ethereum;
        sdk = new SnetSDK(config);
        await sdk.setupAccount();
        return sdk;
    } catch (error) {
        throw error;
    }
};
Variable NameDescription
web3ProviderA URL or one of the Web3 provider classes.
networkIdEthereum network ID.
defaultGasPriceThe gas price to be used in case of fetching the gas price form the blockchain fails.
defaultGasLimitThe gas limit to be used in case of fetching the gas estimate from the blockchain fails.
ipfsEndpointA URL for fetching service related metadata.
rpcEndpointRPC endpoints serve as gateways for Web3 applications to connect with blockchain nodes

Note: rpcEndpoint is optional, you should provide this if you are getting block size limit exceeded error. This is usually happens when you are using any web social auth providers.

Getting information about a service

The information about a service that was provided on publishing service contents in service client object:

js
const getServiceInfo = async () => {
    const sdk = await initSDK();
    const client = await sdk.createServiceClient(
        '26072b8b6a0e448180f8c0e702ab6d2f',
        'Exampleservice'
    );
    return client.metadata;
};

The structure of the service data:

json
{
    "version": 1,
    "display_name": "Example service",
    "encoding": "proto",
    "service_type": "grpc",
    "model_ipfs_hash": "QmeyrQkEyba8dd4rc3jrLd5pEwsxHutfH2RvsSaeSMqTtQ",
    "mpe_address": "0x7E0aF8988DF45B824b2E0e0A87c6196897744970",
    "groups": [
        {
            "free_calls": 0,
            "free_call_signer_address": "0x7DF35C98f41F3Af0df1dc4c7F7D4C19a71Dd059F",
            "daemon_addresses": [
                "0x0709e9b78756b740ab0c64427f43f8305fd6d1a7"
            ],
            "pricing": [
                {
                    "default": true,
                    "price_model": "fixed_price",
                    "price_in_cogs": 1
                }
            ],
            "endpoints": [
                "http://node1.naint.tech:62400"
            ],
            "group_id": "/mb90Qs8VktxGQmU0uRu0bSlGgqeDlYrKrs+WbsOvOQ=",
            "group_name": "default_group"
        }
    ],
    "service_description": {
        "url": "https://sepolia-v2-publisher.singularitynet.io/org",
        "short_description": "Example service",
        "description": "Example service"
    },
    "media": [
        {
            "order": 1,
            "url": "https://sepolia-marketplace-service-assets.s3.us-east-1.amazonaws.com/26072b8b6a0e448180f8c0e702ab6d2f/services/d05c62bf9aa84843a195457d98417f4e/assets/20240327124952_asset.jpeg",
            "file_type": "image",
            "asset_type": "hero_image",
            "alt_text": ""
        }
    ],
    "contributors": [
        {
            "name": "test",
            "email_id": ""
        }
    ],
    "tags": [
        "exampleservice"
    ]
}

Connect to metamask

The SDK connects to Metamask at the first initialization. After that, you can exchange data with the user's wallet

For example, to get the wallet address, AGIX token balance or transactions between service and wallet:

js
export const getWalletInfo = async () => {
    const sdk = await initSDK();
    const address = await sdk.account.getAddress();
    const balance = await sdk.account.balance();
    const transactionCount = await sdk.account._transactionCount();
    return { address, balance, transactionCount };
};

From sdk.account you can get next info:

class Account {
  address(): String
  allowance(): BigNumber
  approveTransfer(amountInCogs): Transaction
  balance(): BigNumber
  depositToEscrowAccount(amountInCogs): Transaction
  escrowBalance(): BigNumber
  sendTransaction(to, contractFn, ...contractFnArgs): Transaction
  signData(message): Buffer
  signerAddress(): String
  withdrawFromEscrowAccount(amountInCogs): Transaction
  _waitForTransaction(String hash): Transaction
  _getTokenContract(): Contract
  _generateTokenContract(): Contract
  _baseTransactionObject(operation, to): Transaction
  _getGas(operation) : {gasLimit, gasPrice}
  _transactionCount(): Number
}

For example, the method of creating an organization from a registry contract:

js
const registerOrganization = () => {
    const sdk = await initSDK();
    const address = await sdk.account.getAddress();
    const method = sdk._registryContract
        .createOrganization(orgId, orgMetadataURI, members)
        .send({ from: address })
        .on(blockChainEvents.TRANSACTION_HASH, async (hash) => {
            return hash;
        })
        .once(blockChainEvents.CONFIRMATION, async ({ confirmationNumber, receipt }) => {
            if (!receipt.status) {
                method.off();
                throw new Error(receipt);
            }
            await method.off();
        })
        .on(blockChainEvents.ERROR, (error) => {
            throw new Error(error.message);
        });
}

The entire application code can be viewed at the link to GitHub.