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

Borsh #75

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions cli/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { hideBin } from 'yargs/helpers'
import { nodeResolve } from '@rollup/plugin-node-resolve';
import sourcemaps from 'rollup-plugin-sourcemaps';
import { babel } from '@rollup/plugin-babel';
import commonjs from '@rollup/plugin-commonjs';

import { rollup } from 'rollup';

Expand Down Expand Up @@ -58,10 +59,10 @@ async function build(argv) {
const bundle = await rollup({
input: SOURCE_FILE_WITH_PATH,
plugins: [
nodeResolve(),
nodeResolve({preferBuiltins: false}),
sourcemaps(),
// commonjs(),
babel({ babelHelpers: 'bundled' })
babel({ babelHelpers: 'bundled' }),
commonjs()
Copy link
Collaborator Author

@volovyks volovyks Jun 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: Not sure why are we doing this change. Let's discuss it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

commonjs is used for import non-es packages, if I didn't remember it wrong, import commonjs module doesn't work without it.

],
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { Worker, BN } from 'near-workspaces';
import {readFile} from 'fs/promises'
import test from 'ava';

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

test.before(async t => {
// Init the worker and start a Sandbox server
const worker = await Worker.init();

// Prepare sandbox for tests, create accounts, deploy contracts, etx.
const root = worker.rootAccount;

// Deploy the jsvm contract.
const jsvm = await root.createAndDeploy(
root.getSubAccount('jsvm').accountId,
'./node_modules/near-sdk-js/res/jsvm.wasm',
);

// Deploy status-message JS contract
const statusMessage = await root.createSubAccount('status-message');
let contract_base64 = (await readFile('build/status-message-borsh.base64')).toString();
await statusMessage.call(jsvm, 'deploy_js_contract', Buffer.from(contract_base64, 'base64'), {attachedDeposit: '4000000000000000000000000', gas: '50 TGas'});
await statusMessage.call(jsvm, 'call_js_contract', encodeCall(statusMessage.accountId, 'init', []), {attachedDeposit: '400000000000000000000000', gas: '150 TGas'});

// Test users
const ali = await root.createSubAccount('ali');
const bob = await root.createSubAccount('bob');
const carl = await root.createSubAccount('carl');

// Save state for test runs
t.context.worker = worker;
t.context.accounts = { root, jsvm, statusMessage, ali, bob, carl };
});

test.after(async t => {
await t.context.worker.tearDown().catch(error => {
console.log('Failed to tear down the worker:', error);
});
});

test('Root gets null status', async t => {
const { root, jsvm, statusMessage } = t.context.accounts;
const result = await jsvm.view('view_js_contract', encodeCall(statusMessage.accountId, 'get_status', [root.accountId]));
t.is(result, null);
});

test('Ali sets then gets status', async t => {
const { ali, jsvm, statusMessage } = t.context.accounts;
await ali.call(jsvm, 'call_js_contract', encodeCall(statusMessage.accountId, 'set_status', ['hello']), {attachedDeposit: '100000000000000000000000', gas: '150 TGas'});

t.is(
await jsvm.view('view_js_contract', encodeCall(statusMessage.accountId, 'get_status', [ali.accountId])),
'hello'
);
});

test('Bob and Carl have different statuses', async t => {
const {jsvm, statusMessage, bob, carl} = t.context.accounts;
await bob.call(jsvm, 'call_js_contract', encodeCall(statusMessage.accountId, 'set_status', ['hello']), {attachedDeposit: '100000000000000000000000', gas: '150 TGas'});
await carl.call(jsvm, 'call_js_contract', encodeCall(statusMessage.accountId, 'set_status', ['world']), {attachedDeposit: '100000000000000000000000', gas: '150 TGas'});

const bobStatus = await jsvm.view('view_js_contract', encodeCall(statusMessage.accountId, 'get_status', [bob.accountId]));
const carlStatus = await jsvm.view('view_js_contract', encodeCall(statusMessage.accountId, 'get_status', [carl.accountId]));
t.is(bobStatus, 'hello');
t.is(carlStatus, 'world');
});
7 changes: 5 additions & 2 deletions examples/status-message/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@
"main": "index.js",
"type": "module",
"scripts": {
"build": "yarn build:simple && yarn build:collections",
"build": "yarn build:simple && yarn build:collections && yarn build:borsh",
"build:simple": "near-sdk build src/status-message.js build/status-message.base64",
"build:collections": "near-sdk build src/status-message-collections.js build/status-message-collections.base64",
"build:borsh": "near-sdk build src/status-message-borsh.js build/status-message-borsh.base64",
"test": "ava"
},
"author": "Near Inc <hello@nearprotocol.com>",
"license": "Apache-2.0",
"dependencies": {
"near-sdk-js": "file:../../"
"borsh": "https://github.com/near/borsh-js/tarball/import-buffer",
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand how this branch is different and why are we using it here. Can we use fix-build branch instead? (or maybe master if we will merge it).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has a import {Buffer} from buffer/` in borsh.ts, and borsh.js built from that. Without this line it doesn't work.

"near-sdk-js": "file:../../",
"yarn": "^1.22.19"
},
"devDependencies": {
"ava": "^4.2.0",
Expand Down
74 changes: 74 additions & 0 deletions examples/status-message/src/status-message-borsh.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { NearContract, NearBindgen, call, view, near } from 'near-sdk-js'
import { serialize, deserialize } from 'borsh';
import { panic } from '../../../src/api';
import { Buffer } from 'buffer/'
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not the best practice. Can we add it as a dependency?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, should do so


@NearBindgen
class StatusMessage extends NearContract {
constructor() {
super()
this.records = new Map()
}

deserialize() {
borshDeserializeStatusMessage(this)
}

serialize() {
borshSerializeStatusMessage(this)
}

@call
set_status(message) {
let account_id = near.signerAccountId()
env.log(`${account_id} set_status with message ${message}`)
this.records.set(account_id, message)
}

@view
get_status(account_id) {
env.log(`get_status for account_id ${account_id}`)
return this.records.get(account_id) || null
}
}

// This doesn't work, maybe due to @Nearbindgen cause this no longer the same class as StatusMessage
// const schema = new Map([[StatusMessage, {
// 'kind': 'struct',
// 'fields': [
// ['records', {kind: 'map', key: 'string', value: 'string'}]
// ]
// }]]);

class Assignable {
constructor(properties) {
Object.keys(properties).map((key) => {
this[key] = properties[key];
});
}
}

class StatusMessageBorsh extends Assignable {}

const schema = new Map([[StatusMessageBorsh, {
'kind': 'struct',
'fields': [
['records', {kind: 'map', key: 'string', value: 'string'}]
]
}]]);

function borshSerializeStatusMessage(statusMessage) {
let temp = new StatusMessageBorsh({records: statusMessage.records})
near.jsvmStorageWrite('STATE', serialize(schema, temp));
}

function borshDeserializeStatusMessage(to) {
let state = near.jsvmStorageRead('STATE');

if (state) {
let temp = deserialize(schema, StatusMessageBorsh, Buffer.from(state))
Object.assign(to, temp);
} else {
throw new Error('Contract state is empty')
}
}
Loading