Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Abstractions If Using JS SDK #867

Closed
BenKurrek opened this issue May 26, 2022 · 4 comments · Fixed by #870
Closed

Abstractions If Using JS SDK #867

BenKurrek opened this issue May 26, 2022 · 4 comments · Fixed by #870
Assignees

Comments

@BenKurrek
Copy link
Contributor

BenKurrek commented May 26, 2022

Overview

Currently if you are using the javascript SDK, you're forced into an isolated environment similar to aurora. This isolated environment is called an enclave. The differences between the enclave and WASM (Rust and AS) approaches are outlined in the following table (as outlined in this PR:

| Initial Deployment Cost | **~5x** more than Rust or AssemblyScript | * no difference | | Cross-contract calls | * no difference | * synchronous and can only interact with contracts **within the jsvm**. | | Integrates standards | * no difference | * does not **currently** support any standards such as NFTs, FTs, events, etc.. See [this](https://github.com/near/near-sdk-js/discussions/35) discussion for more details. | | Using near-api-js | * no difference | * you **must** encode your arguments and make calls to the `jsvm` account rather than the JS contract | | GAS | * no difference | * 30% GAS burnt payed out to the contract not **yet** implemented. | | External packages | * supports external npm packages written in javascript | * supports external npm packages written in javascript | | Clearing State | * no difference | * you **must** deploy a contract to your account and call methods to clear state. You cannot delete your account and recreate it. | | Function Call Access Keys | * no difference | * Any user with a function call access key can call any method from any contract. Logic must be implemented in contract to allow/disallow certain calls can proceed |

NOTE: these differences are noted as of right now. Some of them will be changed in future. An example of this is the 30% gas burnt payed out to the contracts will be implemented in the enclave version.

If you'd like to check out my WIP quickstart guide for using the enclave, checkout this draft PR. In addition, I've made a game using the JS SDK (which I'm working on a frontend for). This game can be found here.

Abstractions made with the NEAR-CLI

The current development cycle when creating a javascript is as follows:

  • Create a contract written in JS
  • Compile the contract down to bytecode (i think) and then encode it using base64. This is in contrast with Rust and AS where the contracts are compiled to wasm.

If you want to deploy the contract, you need to call a method on the enclave contract (jsvm.testnet) and pass the base64 as an argument. Using the NEAR-CLI, this looks as such:

near call jsvm.testnet deploy_js_contract --accountId YOUR_ACCOUNT --base64 --args $(cat nim-game/build/nim.base64) --deposit 1

This is contrast with Rust and AS as to deploy a contract, you would simply run:

near deploy --wasmFile YOUR_WASM.wasm --accountId YOUR_ACCOUNT

The differences here are that using the JSVM, the contract is never actually "deployed" and simply lives in the state of the JSVM contract. Your account ID will have no contract deployed to it. When using Rust or AS, the contract is deployed to your account and lives on your account.

This deploy functionality has been abstracted to look more like the AS and Rust deployment command in this PR and there is an ongoing discussion found here. The new command is:

near js deploy --accountId 
 --base64File 
 
 --deposit 0.1
 
 

This makes it easier for developers to interact with their contracts and they don't need to know about the enclave. This sort of abstractions has been made for calling, viewing, and deploying smart contracts to the JSVM enclave.

Using NAJ Right Now

The conventional way to interact with a deployed smart contract on NEAR is to do something as follows:

await contract.account.functionCall({
 contractId: accountName,
 methodName: "newGame",
 args: 

 { playerOne: "benjiman.testnet", playerTwo: "patrick.testnet" } 

,
 gas: "300000000000000",
 });

You're signing a transaction whereby the receiver is the actual contract ID and you're passing in the arguments as key-value pairs. If you'd like to interact with a javascript contract that's deployed to the enclave, the way you use NAJ is different. The first thing you need to do is to introduce an encoding function like such:

function encodeCall(contract, method, args) {
 return Buffer.concat([Buffer.from(contract), Buffer.from([0]), Buffer.from(method), Buffer.from([0]), Buffer.from(args)])
 }

Once you have the encode function, you need to encode your arguments and pass them in like so:

let args = encodeCall(contractId, 'getState', '');
 const result = await wallet.account().functionCall({
 contractId: "jsvm.testnet", 
 methodName: 'call_js_contract', 
 args,
 gas: "300000000000000"
 });

If you want to perform a view call, you would need to do this:

let args = encodeCall(contractId, 'getState', '');
 const stateView = await wallet.account().viewFunction("jsvm.testnet", 'view_js_contract', args, {
 stringify: (val) => val,
 });

The Ask

If we can attempt to mirror what is being done in the NEAR-CLI and abstract some of this encoding and strange behaviour away from the user, it would provide a better UX. We can introduce an optional flag for whether or not users have an enclave JS contract and if they do, the encoding can happen behind the scenes and the receiver ID can be set to the JSVM account behind the scenes.

We could have:

await contract.account.functionCall({
 contractId: "contract.benji.testnet,
 methodName: "newGame",
 args: 

 { playerOne: "benjiman.testnet", playerTwo: "patrick.testnet" } 

,
 gas: "300000000000000",
 jsContract: true
 });

which would do this behind the scenes:

let args = encodeCall("contract.benji.testnet", 'newGame', '["benjiman.testnet", "patrick.testnet"]');
 const result = await wallet.account().functionCall({
 contractId: "jsvm.testnet", 
 methodName: 'call_js_contract', 
 args,
 gas: "300000000000000"
 });

Additional discussions

If you want to read more about some of the discussions going on surrounding the JS SDK, please check this out.

@willemneal
Copy link
Contributor

does not currently support any standards such as NFTs, FTs, events, etc..

What does this mean? Contracts haven't been written yet?

@BenKurrek
Copy link
Contributor Author

does not currently support any standards such as NFTs, FTs, events, etc..

What does this mean? Contracts haven't been written yet?

See this discussion (i'll edit the original post to point to the discussion as well).

@agileurbanite
Copy link
Contributor

As @thisisjoshford put it better dev x to have:

function encodeCall(contract, method, args) {
  return Buffer.concat([Buffer.from(contract), Buffer.from([0]), Buffer.from(method), Buffer.from([0]), Buffer.from(args)])
}

let args = encodeCall(contractId, 'getState', '');
const stateView = await wallet.account().viewFunction("jsvm.testnet", 'view_js_contract', args, {
    stringify: (val) => val,
});

be:

await contract.account.functionCall({
      contractId: "contract.benji.testnet,
      methodName: "newGame",
      args: {
        playerOne: "benjiman.testnet",
        playerTwo: "patrick.testnet"
      },
      gas: "300000000000000",
      jsContract: true
    });

@volovyks
Copy link
Collaborator

volovyks commented Jun 7, 2022

We should give users the ability to set custom JSVM accountId. Some of them will deploy their own JSVMs. It can be an optional parameter that defaults to jsvm.<network>.

Now all the env information is stored in the config file, we can add JSVM parameter to it. The user will ba able to set it like this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants