Search code examples
typescriptsmartcontractsnearprotocol

Smart contract panicked: cannot read property 'prefix' of undefined\n


I am trying to interact with a smart contract method and getting the error -

  ExecutionError: "Smart contract panicked: cannot read property 'prefix' of undefined\n" +
      '    at reconstruct (build/microsoft.js:994)\n' +
      '    at _reconstruct (build/microsoft.js:1102)\n' +
      '    at buy (build/microsoft.js:1137)\n'

Below is my smart contract :

import { NearBindgen, call, view, UnorderedMap, initialize } from 'near-sdk-js';

@NearBindgen({})
export class ExchangeContractMicrosoft {
  private stocks: UnorderedMap<number> = new UnorderedMap<number>(
    'unique-id-map1',
  );

  constructor() {
    this.stocks.set('msft', 10000000);
  }

  @call({})
  buy(): void {
    const stock: string = 'msft';
    this.checkStock(stock, 1);
    this.stocks.set('msft', this.stocks.get(stock) - 1);
  }

  private checkStock(stock: string, value: number): void {
    if (this.stocks.get(stock) < value) {
      throw 'Not enough stocks';
    }
  }
}

I am trying to interact using :

near call sipars.testnet buy '{}' --accountId sipars.testnet --gas=300000000000000

Could someone help figure out what I am doing wrong ?


Solution

  • This most likely happens because you have made changes to your smart-contract since the first time you deployed it. You can then end up accessing an object that hasn't been defined yet. This can happen if you try to initialize additional objects after having deployed the contract once already.

    For example

    Deploy the contract for the first time

    private stocks: UnorderedMap<number> = new UnorderedMap<number>('prefix1');
    
    constructor() {
      this.stocks.set('msft', 10);
    }
    
    @call({})
    buy(): number {
      const stock: string = 'msft';
      return this.stocks.get(stock); // will return 10
    }
    

    Deploy the contract again after adding a new stocks object

    private stocks: UnorderedMap<number> = new UnorderedMap<number>('prefix1');
    private superStocks: UnorderedMap<number> = new UnorderedMap<number>('prefix2'); // will not be initialized when we re-deploy the contract  
    
    constructor() {
      this.stocks.set('msft', 10);
      this.superStocks.set('msft', 20); // this will never be called if we re-deploy to the same address
    }
    
    @call({})
    buy(): number {
      const stock: string = 'msft';
      return this.superStocks.get(stock); // Opps. Cannot read property 'prefix' of undefined\n
    }
    

    A smart-contract that is already deployed will not call the constructor again if you re-deploy it (on the same address.) The constructor is only called on the first deploy.

    If I take your current smart-contract code, build and deploy it, I'm able to call the buy function without getting an error.

    How to fix it?

    The only way I was able to fix the issue was to deploy the contract to a new address. According to this GitHub comment, adding a new field to the existing contract require a migration, so this is not a bug.