near / near-api-js

JavaScript library to interact with NEAR Protocol via RPC API
https://near.github.io/near-api-js
MIT License
392 stars 245 forks source link

Unable to use the change methods of the contract on jest with near test-environment. #854

Closed LFSCamargo closed 8 months ago

LFSCamargo commented 2 years ago

Describe the bug Unable to use the change methods of the contract on jest with near test-environment. I get this error when trying to use the change methods

Greeting Contract Tests › should change the greeting from the contract using the `set_greeting` method

 Class PublicKey is missing in schema: publicKey

 at serializeStruct (../../node_modules/borsh/lib/index.js:308:15)
 at serializeField (../../node_modules/borsh/lib/index.js:291:13)
 at ../../node_modules/borsh/lib/index.js:312:13
 at Array.map (
 )
 at serializeStruct (../../node_modules/borsh/lib/index.js:311:29)
 at Object.serialize (../../node_modules/borsh/lib/index.js:334:5)
 at signTransactionObject (node_modules/near-api-js/lib/transaction.js:219:29)
 at Object.signTransaction (node_modules/near-api-js/lib/transaction.js:237:16)
 at Account.signTransaction (node_modules/near-api-js/lib/account.js:99:16)
 at node_modules/near-api-js/lib/account.js:118:34
 

To Reproduce Steps to reproduce the behavior:

  1. Clone https://github.com/hack-a-chain-software/near.monorepo
  2. Paste this into the greeting.spec.ts:
import "jest";
 import 

 { Account, connect, Contract } 

 from "near-api-js";
 import 

 { KeyStore } 

 from "near-api-js/lib/key_stores";
 import 

 { NearConfig } 

 from "near-api-js/lib/near";

/**

*   @name GreetingContract
*   @description - This will handle the interactions
*   with the contract with better TS types
     */
     export class GreetingContract {
     private contract: any;

 /**

*   @constructor
*   @param contract
     */
     constructor(contract: any) { this.contract = contract as any; }

 /**

*   @description - This method gets the message on chain to the user account_id
     */
     public async getGreeting(params: { account_id: string }

 ): Promise

 { return await this.contract.get_greeting(params); } 

 /**

*   @description - This method updates the message for the account on NEAR
     */
     public async updateGreeting(params: { message: string }

 ) 

 { return await this.contract.set_greeting(params); } 

 }

describe("Greeting Contract Tests", () => {
 let contract: GreetingContract;
 let account: Account;
 let config: NearConfig & {
 contractName: string;
 accountId: string;
 deps: 

 { keyStore: KeyStore } 

;
 testAccountId: string;
 };

 /** @description - Runs Before Everything and initializes the near instances */
 beforeAll(async () => {
 // @ts-ignore
 config = nearConfig;

 const near = await connect(config);

 account = await near.account(config.accountId as string);

 contract = await new GreetingContract(
 new Contract(account, config.contractName, 

 { viewMethods: ["get_greeting"], changeMethods: ["set_greeting"], } 

)
 );
 });

 /** @description - Gets the current greeting from the smart contract and checks if it goes okay */
 it("should get the greeting from the contract using the `get_greeting` method", async () => {
 // Gets the current message for that account id on the contract
 const message = await contract.getGreeting(

 { account_id: account.accountId, } 

);

 console.log(message);

 expect(message).toEqual("Hello");
 });

 it("should change the greeting from the contract using the `set_greeting` method", async () => {
 // Gets the current message for that account id on the contract
 await contract.updateGreeting(

 { message: "Whats Up Darling!", } 

);

 const message = await contract.getGreeting(

 { account_id: account.accountId, } 

);

 expect(message).toEqual("Whats Up Darling!");
 });
 });
 

Expected behavior Pass both of the tests using the near config from the near-test-environment

Screenshots If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

Additional context If someone knows a way to bypass this will be cool also 😄

veigajoao commented 2 years ago

I'm having the same issue here.

Important to notice, in order for the repo to run the tests it's needed to add a folder neardev/shared-test in the contracts folder. In this folder, you'll need to place the access keys for test.near (genesis account for the test chain):

{"account_id":"test.near","public_key":"ed25519:22skMptHjFWNyuEWY22ftn2AbLPSYpmYwGJRGwpNHbTV","private_key":"ed25519:2wyRcSwSuHtRVmkMCGjPwnzZmQLeXLzLLyED1NDMt4BjnKgQL6tF85yBx6Jr26D2dUNeC716RBoTxntVHsegogYw"}
lostpebble commented 2 years ago

Running into so many issues with Borsh and serialization and Near API JS. This is becoming by far my biggest frustration with Near at the moment.

Its feeling like a wall at the moment, I've run into it during development- where I had to ensure that when building my transactions, the classes were pulled from the exact same library import in the same way (more difficult than it sounds in complex dev environments). Even though they technically were exactly the same classes (dev tools do strange things sometimes when you have a monorepo and there are multiple projects doing imports of the same thing).

Now I'm running into serialization issues, with almost the exact same error message as the post here, after bundling my code for production deployment:

Content.tsx:201 Error: Class ut is missing in schema: publicKey
    at serializeStruct (index.js:323:15)
    at serializeField (index.js:306:13)
    at index.js:327:13
    at Array.map (<anonymous>)
    at serializeStruct (index.js:326:29)
    at Object.serialize (index.js:349:5)
    at lt (transaction.js:219:29)
    at Object.ft [as signTransaction] (transaction.js:237:16)
    at async ConnectedMeteorWalletAccount.signTransaction (account.js:99:16)
    at async account.js:118:34

Please can there be a better way for this serialization / de-serialization of transactions that doesn't depend on actual exact classes being imported from near-api-js, which is prone to version mismatches and obscure errors. There has to be a better way.


EDIT: As suspected- these kinds of errors are almost always because of duplication- you are creating a transaction using a certain class import, and then trying to serialized it in a place that uses the exact same class import, but for some reason your dev or build tool is pulling that class from a different place. Internally this makes borsch see these two classes as different entities and throws the error.

I am using Vite at the moment. My dev environment was finally working but my builds were failing. To fix this in the build as well (I figured out that near-api-js was being duplicated 3 times in the build output!) I had to force Vite to not duplicate this dependency, using resolve.dedupe: ["near-api-js"] (its a Vite option you can set in your config). I hope this helps someone else running into similar issues!

vikinatora commented 9 months ago

Hey everyone you can try out the newest version of NAJ. The issue should be resolved by the following commit: https://github.com/near/near-api-js/commit/61349aeca3af830f702b24654e0f13cd428192d8. Please let me know if the issue persists. Otherwise I'll close the issue in 7 days. Thanks!

vikinatora commented 8 months ago

Closing as issue should be resolved.