Search code examples
nearprotocolnear-api-js

Why is there an error in this contract call?


new to nearprotocol! Trying to do a little hello world using near-api-js, here is my issue ...

const { keyStores, connect } = require("near-api-js");
const fs = require("fs");
const path = require("path");
const homedir = require("os").homedir();

const CREDENTIALS_DIR = ".near-credentials";
const ACCOUNT_ID = "acct.testnet";
const WASM_PATH = "./contract/target/wasm32-unknown-unknown/release/counter.wasm";

const credentialsPath = path.join(homedir, CREDENTIALS_DIR);
const keyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath);

const config = {
    keyStore,
    networkId: "testnet",
    nodeUrl: "https://rpc.testnet.near.org",
};

deployContract(ACCOUNT_ID, WASM_PATH);

async function deployContract(accountId, wasmPath) { 
    const near = await connect(config);
    const account = await near.account(accountId);
    const result = await account.deployContract(fs.readFileSync(wasmPath));
}

I am deploying a wasm contract with this methodology, however, when I try to call the contract using

const nearAPI = require("near-api-js");
const keyStores = nearAPI.keyStores;
const connect = nearAPI.connect;

const homedir = require("os").homedir();
const CREDENTIALS_DIR = ".near-credentials";
const credentialsPath = require("path").join(homedir, CREDENTIALS_DIR);
const keyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath);

const config = {
  networkId: "testnet",
  keyStore, 
  nodeUrl: "https://rpc.testnet.near.org",
  walletUrl: "https://wallet.testnet.near.org",
  helperUrl: "https://helper.testnet.near.org",
  explorerUrl: "https://explorer.testnet.near.org",
};

call();

async function call() {
    // gets the state of the account
    const near = await connect(config);
    const account = await near.account("acct.testnet");

    const contract = new nearAPI.Contract(
        account, // the account object that is connecting
        "acct.testnet",
        {
            contractID : "counter.acct.testnet",
            changeMethods: ["increment", "decrement", "reset"], // view methods do not change state but usually return a value
            viewMethods: ["get_num"], // change methods modify state
            sender: account, // account object to initialize and sign transactions.
        }
    );

    let response = await contract.reset(
        {
          args: {
              //arg_name: "value" // argument name and value - pass empty object if no args required
          },
          gas: 300000000000000 // attached GAS (optional)
        }
    );
    
    console.log(response);
}

Now, the response says that the contract does not exist: ServerTransactionError: Can't complete the action because account counter.acct.testnet doesn't exist.

However, when using acct.testnet, instead of counter.acct.testnet, it works.

Which leaves the question: how can I specify the exact contract that I want to interact with, on the near blockchain, under a specific account?

Thanks!


Solution

  • There are two different ways you can go about using NAJ (near-api-js) and interacting with contracts. The first way, which is the one you're using, is to create a contract object (new nearAPI.Contract()) and connect to the contract using an account object (either from a wallet connection or the native near.account() method):

    const contract = new nearAPI.Contract(
            accountObject, // the account object that is connecting
            ...
    

    This method allows you to pass in the account ID of the contract you wish to interact with:

    const contract = new nearAPI.Contract(
            accountObject,
            "CONTRACT_ACCOUNT_ID_HERE.testnet",
            ...
    

    In your case, simply change that account ID to be whatever you want and that will allow you to interact with different contracts. These account IDs must exist and also have a contract deployed to them otherwise you'll run into errors. Don't forget that the methods you outline when creating the nearAPI contract object must also live on the contract or else when you try to use them, it will throw as well.

    The second way you can interact with contracts, which is my preferred way of doing things, is to create the account object in the same manner as you did previously but instead of creating a contract object and doing contract.methodName, you just take the account object and do account.functionCall or account.viewFunction where you can pass in the contract ID, method name etc...

    This allows you to interact with any contract on-chain without having to create a new contract object for each contract you want to interact with.