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

Use uint8array #308

Merged
merged 29 commits into from
Dec 13, 2022
Merged
Changes from 26 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
dd800eb
cherry pick from arraybuffer branch
ailisp Nov 22, 2022
a78156d
reset builder
ailisp Nov 22, 2022
5187700
update builder.c, fix git diff
ailisp Nov 22, 2022
a947408
nit
ailisp Nov 22, 2022
ac487d1
merge develop
ailisp Nov 23, 2022
78ed2a6
resolve conflict
ailisp Nov 23, 2022
c1336a5
strictly different from uint8array and string, fix near-bindgen and u…
ailisp Nov 23, 2022
48a1023
Merge branch 'develop' into use-uint8array
ailisp Nov 29, 2022
c49802a
make collections use uint8array instead of bytes
ailisp Nov 29, 2022
b933287
commit build
ailisp Nov 30, 2022
e927de7
fix build and some tests
ailisp Nov 30, 2022
c09a8db
add text encoding decoding in c side, add TextEncoder and TextDecoder…
ailisp Dec 1, 2022
4adcb8f
refactor utf8 and latin1 api
ailisp Dec 2, 2022
46304fc
fix near bindgen and collections utf 8 char issue
ailisp Dec 2, 2022
18f3b61
fix bytes tests
ailisp Dec 5, 2022
eec8cb4
fix public key tests
ailisp Dec 5, 2022
8197aa6
merge develop
ailisp Dec 5, 2022
2826581
add all test cases of string<>uint8array conversion
ailisp Dec 6, 2022
f6b837c
lint format
ailisp Dec 6, 2022
9263d61
fix clean-state cross contract call loop, ft and programmatic update …
ailisp Dec 7, 2022
65d97ed
Merge branch 'develop' into use-uint8array
ailisp Dec 7, 2022
4e85aba
fixing my-nft.ts build
ailisp Dec 7, 2022
493ce71
fix all examples
ailisp Dec 8, 2022
5d05999
merge develop and resolve conflict
ailisp Dec 8, 2022
0d734ac
remove unused file
ailisp Dec 8, 2022
2d4b3a5
fix test
ailisp Dec 8, 2022
56a7a99
address Serhii comments
ailisp Dec 12, 2022
e8f9c08
keep string APIs mostly backward compatible, make raw apis, fix tests
ailisp Dec 13, 2022
ea494c6
fix tests
ailisp Dec 13, 2022
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
3 changes: 2 additions & 1 deletion examples/__tests__/test-cross-contract-call.ava.js
Original file line number Diff line number Diff line change
@@ -52,12 +52,13 @@ test("Person can be set on-call if AVAILABLE", async (t) => {
// Ali set her status as AVAILABLE
await ali.call(statusMessage, "set_status", { message: "AVAILABLE" });
// Bob sets Ali on-call
await bob.call(
let r = await bob.callRaw(
ailisp marked this conversation as resolved.
Show resolved Hide resolved
onCall,
"set_person_on_call",
{ accountId: ali.accountId },
{ gas: 120000000000000 }
);
console.log(JSON.stringify(r, null, 2));

// Check that Ali is on-call
t.is(await onCall.view("person_on_call", {}), ali.accountId);
12 changes: 8 additions & 4 deletions examples/src/clean-state.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import { NearBindgen, call, view, near } from "near-sdk-js";
import { NearBindgen, call, view, near, encode, decode } from "near-sdk-js";

@NearBindgen({})
export class CleanState {
@call({})
clean({ keys }) {
keys.forEach((key) => near.storageRemove(key));
keys.forEach((key) => near.storageRemove(encode(key)));
volovyks marked this conversation as resolved.
Show resolved Hide resolved
}

@call({})
put({ key, value }) {
near.storageWrite(key, value);
near.storageWrite(encode(key), encode(value));
}

@view({})
get({ key }) {
return near.storageRead(key);
let raw = near.storageRead(encode(key));
if (raw !== null) {
return decode(raw);
}
return null;
}
}
20 changes: 10 additions & 10 deletions examples/src/counter-lowlevel.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
// This contract implements exact same functionality as counter.js, but only use low level APIs
import { near } from "near-sdk-js";
import { near, bytes, str } from "near-sdk-js";

export function init() {
let argsRaw = near.input();
let args = JSON.parse(argsRaw || "{}");
let args = JSON.parse(str(argsRaw) || "{}");
let initial = args.initial || 0;
let count = initial;
let state = JSON.stringify({ count });
near.storageWrite("STATE", state);
near.storageWrite(bytes("STATE"), bytes(state));
}

function deserialize() {
let state = near.storageRead("STATE");
let state = near.storageRead(bytes("STATE"));
if (state) {
return JSON.parse(state);
return JSON.parse(str(state));
} else {
return { count: 0 };
}
@@ -22,25 +22,25 @@ function deserialize() {
export function getCount() {
let state = deserialize();
let count = state.count;
near.valueReturn(JSON.stringify(count));
near.valueReturn(bytes(JSON.stringify(count)));
}

export function increase() {
let argsRaw = near.input();
let args = JSON.parse(argsRaw || "{}");
let args = JSON.parse(str(argsRaw) || "{}");
let n = args.n || 1;
let state = deserialize();
state.count += n;
near.log(`Counter increased to ${state.count}`);
near.storageWrite("STATE", JSON.stringify(state));
near.storageWrite(bytes("STATE"), bytes(JSON.stringify(state)));
}

export function decrease() {
let argsRaw = near.input();
let args = JSON.parse(argsRaw || "{}");
let args = JSON.parse(str(argsRaw) || "{}");
let n = args.n || 1;
let state = deserialize();
state.count -= n;
near.log(`Counter decreased to ${state.count}`);
near.storageWrite("STATE", JSON.stringify(state));
near.storageWrite(bytes("STATE"), bytes(JSON.stringify(state)));
}
13 changes: 10 additions & 3 deletions examples/src/cross-contract-call-loop.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import { call, near, NearBindgen, NearPromise, view } from "near-sdk-js";
import {
call,
near,
NearBindgen,
NearPromise,
view,
decode,
} from "near-sdk-js";

const CONTRACTS = [
"first-contract.test.near",
"second-contract.test.near",
"third-contract.test.near",
];
const NO_ARGS = "";
const NO_ARGS = new Uint8Array();
const THIRTY_TGAS = BigInt("30" + "0".repeat(12));

@NearBindgen({})
@@ -48,7 +55,7 @@ export class LoopXCC {
const callCount = near.promiseResultsCount();
for (let i = 0; i < callCount; i++) {
const promiseResult = near.promiseResult(i);
const result = JSON.parse(promiseResult);
const result = JSON.parse(decode(promiseResult));
this.count += result;
}
return this.count;
12 changes: 10 additions & 2 deletions examples/src/cross-contract-call.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { NearBindgen, call, view, initialize, near, bytes } from "near-sdk-js";
import {
NearBindgen,
call,
view,
initialize,
near,
bytes,
str,
} from "near-sdk-js";

@NearBindgen({ requireInit: true })
export class OnCall {
@@ -37,7 +45,7 @@ export class OnCall {
@call({ privateFunction: true })
_set_person_on_call_private({ accountId }) {
near.log(`_set_person_on_call_private called, accountId ${accountId}`);
const status = JSON.parse(near.promiseResult(0));
const status = JSON.parse(str(near.promiseResult(0)));
near.log(`${accountId} status is ${status}`);
if (status === "AVAILABLE") {
this.personOnCall = accountId;
3 changes: 2 additions & 1 deletion examples/src/fungible-token.ts
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ import {
LookupMap,
assert,
validateAccountId,
encode,
} from "near-sdk-js";

@NearBindgen({ requireInit: true })
@@ -209,7 +210,7 @@ export class FungibleToken {
near.promiseBatchActionFunctionCall(
promise,
"ft_on_transfer",
JSON.stringify(params),
encode(JSON.stringify(params)),
0,
30000000000000
);
3 changes: 2 additions & 1 deletion examples/src/non-fungible-token.js
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ import {
LookupMap,
bytes,
assert,
str,
} from "near-sdk-js";

class Token {
@@ -103,7 +104,7 @@ export class NftContract {
near.log(
`_nftResolveTransfer called, receiver_id ${receiver_id}, token_id ${token_id}`
);
const isTokenTransfered = JSON.parse(near.promiseResult(0));
const isTokenTransfered = JSON.parse(str(near.promiseResult(0)));
near.log(
`${token_id} ${
isTokenTransfered ? "was transfered" : "was NOT transfered"
14 changes: 11 additions & 3 deletions examples/src/programmatic-update-after.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { NearBindgen, near, initialize, assert, view } from "near-sdk-js";
import {
NearBindgen,
near,
initialize,
assert,
view,
bytes,
str,
} from "near-sdk-js";

@NearBindgen({ requireInit: true })
export class ProgrammaticUpdateAfter {
@@ -7,7 +15,7 @@ export class ProgrammaticUpdateAfter {
@initialize({ privateFunction: true })
init({ manager }: { manager: string }) {
near.log(`Setting manager to be ${manager}`);
near.storageWrite("MANAGER", manager);
near.storageWrite(bytes("MANAGER"), bytes(manager));
}

@view({}) // Method renamed and return "Hi" when greeting is "Hello"
@@ -17,7 +25,7 @@ export class ProgrammaticUpdateAfter {
}

export function updateContract() {
const manager = near.storageRead("MANAGER");
const manager = str(near.storageRead(bytes("MANAGER")));
assert(
near.predecessorAccountId() === manager,
"Only the manager can update the code"
14 changes: 11 additions & 3 deletions examples/src/programmatic-update-before.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { NearBindgen, near, initialize, assert, view } from "near-sdk-js";
import {
NearBindgen,
near,
initialize,
assert,
view,
bytes,
str,
} from "near-sdk-js";

@NearBindgen({ requireInit: true })
export class ProgrammaticUpdateBefore {
@@ -7,7 +15,7 @@ export class ProgrammaticUpdateBefore {
@initialize({ privateFunction: true })
init({ manager }: { manager: string }) {
near.log(`Setting manager to be ${manager}`);
near.storageWrite("MANAGER", manager);
near.storageWrite(bytes("MANAGER"), bytes(manager));
}

@view({}) // This method will be renamed after update and will return "Hi" if greeting is "Hello"
@@ -17,7 +25,7 @@ export class ProgrammaticUpdateBefore {
}

export function updateContract() {
const manager = near.storageRead("MANAGER");
const manager = str(near.storageRead(bytes("MANAGER")));
assert(
near.predecessorAccountId() === manager,
"Only the manager can update the code"
18 changes: 9 additions & 9 deletions examples/src/standard-nft/my-nft.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { NonFungibleToken } from "near-contract-standards/lib";
import {
assert,
Bytes,
bytes,
call,
initialize,
near,
@@ -32,26 +32,26 @@ import { NonFungibleTokenEnumeration } from "near-contract-standards/lib/non_fun
class StorageKey {}

class StorageKeyNonFungibleToken extends StorageKey implements IntoStorageKey {
into_storage_key(): Bytes {
return "NFT_";
into_storage_key(): Uint8Array {
return bytes("NFT_");
}
}

class StorageKeyTokenMetadata extends StorageKey implements IntoStorageKey {
into_storage_key(): Bytes {
return "TOKEN_METADATA_";
into_storage_key(): Uint8Array {
return bytes("TOKEN_METADATA_");
}
}

class StorageKeyTokenEnumeration extends StorageKey implements IntoStorageKey {
into_storage_key(): Bytes {
return "TOKEN_ENUMERATION_";
into_storage_key(): Uint8Array {
return bytes("TOKEN_ENUMERATION_");
}
}

class StorageKeyApproval extends StorageKey implements IntoStorageKey {
into_storage_key(): Bytes {
return "APPROVAL1_";
into_storage_key(): Uint8Array {
return bytes("APPROVAL1_");
}
}

3 changes: 1 addition & 2 deletions examples/src/standard-nft/test-approval-receiver.ts
Original file line number Diff line number Diff line change
@@ -6,7 +6,6 @@ import {
PromiseOrValue,
assert,
call,
bytes,
serialize,
} from "near-sdk-js";
import { AccountId } from "near-sdk-js";
@@ -58,7 +57,7 @@ export class ApprovalReceiver
const account_id = near.currentAccountId();
return NearPromise.new(account_id).functionCall(
"ok_go",
bytes(serialize({ msg })),
serialize({ msg }),
0n,
prepaid_gas - GAS_FOR_NFT_ON_APPROVE
);
5 changes: 2 additions & 3 deletions examples/src/standard-nft/test-token-receiver.ts
Original file line number Diff line number Diff line change
@@ -7,7 +7,6 @@ import {
NearBindgen,
NearPromise,
PromiseOrValue,
bytes,
serialize,
} from "near-sdk-js";
import { AccountId } from "near-sdk-js";
@@ -66,7 +65,7 @@ export class TokenReceiver
const account_id = near.currentAccountId();
return NearPromise.new(account_id).functionCall(
"ok_go",
bytes(serialize({ return_it: true })),
serialize({ return_it: true }),
0n,
prepaid_gas - GAS_FOR_NFT_ON_TRANSFER
);
@@ -78,7 +77,7 @@ export class TokenReceiver
const account_id = near.currentAccountId();
return NearPromise.new(account_id).functionCall(
"ok_go",
bytes(serialize({ return_it: false })),
serialize({ return_it: false }),
0n,
prepaid_gas - GAS_FOR_NFT_ON_TRANSFER
);
2,269 changes: 2,269 additions & 0 deletions examples/yarn-error.log

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 13 additions & 13 deletions packages/near-contract-standards/lib/non_fungible_token/impl.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 28 additions & 19 deletions packages/near-contract-standards/src/non_fungible_token/impl.ts
Original file line number Diff line number Diff line change
@@ -2,13 +2,14 @@ import {
AccountId,
UnorderedMap,
LookupMap,
Bytes,
near,
UnorderedSet,
assert,
NearPromise,
bytes,
serialize,
str,
concat,
} from "near-sdk-js";
import { TokenMetadata } from "./metadata";
import {
@@ -214,7 +215,7 @@ export class NonFungibleToken
if (msg) {
return NearPromise.new(account_id).functionCall(
"nft_on_approve",
bytes(serialize({ token_id, owner_id, approval_id, msg })),
serialize({ token_id, owner_id, approval_id, msg }),
0n,
near.prepaidGas() - GAS_FOR_NFT_APPROVE
);
@@ -331,21 +332,23 @@ export class NonFungibleToken
let next_approval_id_by_id: Option<LookupMap<bigint>>;
if (approval_prefix) {
const prefix = approval_prefix.into_storage_key();
approvals_by_id = new LookupMap(prefix);
next_approval_id_by_id = new LookupMap(prefix + "n");
approvals_by_id = new LookupMap(str(prefix));
next_approval_id_by_id = new LookupMap(str(prefix) + "n");
} else {
approvals_by_id = null;
next_approval_id_by_id = null;
}

this.owner_id = owner_id;
this.extra_storage_in_bytes_per_token = 0n;
this.owner_by_id = new UnorderedMap(owner_by_id_prefix.into_storage_key());
this.owner_by_id = new UnorderedMap(
str(owner_by_id_prefix.into_storage_key())
);
this.token_metadata_by_id = token_metadata_prefix
? new LookupMap(token_metadata_prefix.into_storage_key())
? new LookupMap(str(token_metadata_prefix.into_storage_key()))
: null;
this.tokens_per_owner = enumeration_prefix
? new LookupMap(enumeration_prefix.into_storage_key())
? new LookupMap(str(enumeration_prefix.into_storage_key()))
: null;
this.approvals_by_id = approvals_by_id;
this.next_approval_id_by_id = next_approval_id_by_id;
@@ -404,7 +407,11 @@ export class NonFungibleToken
}
if (this.tokens_per_owner) {
const u = new UnorderedSet<AccountId>(
new TokensPerOwner(near.sha256(tmp_owner_id)).into_storage_key()
str(
new TokensPerOwner(
near.sha256(bytes(tmp_owner_id))
).into_storage_key()
)
);
u.set(tmp_token_id);
this.tokens_per_owner.set(tmp_owner_id, u);
@@ -467,7 +474,7 @@ export class NonFungibleToken
});
if (receiver_tokens_set === null) {
receiver_tokens_set = new UnorderedSet<TokenId>(
new TokensPerOwner(near.sha256(to)).into_storage_key()
str(new TokensPerOwner(near.sha256(bytes(to))).into_storage_key())
);
}
receiver_tokens_set.set(token_id);
@@ -577,7 +584,9 @@ export class NonFungibleToken
});
if (token_ids === null) {
token_ids = new UnorderedSet(
new TokensPerOwner(near.sha256(owner_id)).into_storage_key()
str(
new TokensPerOwner(near.sha256(bytes(owner_id))).into_storage_key()
)
);
}
token_ids.set(token_id);
@@ -686,9 +695,9 @@ export class NonFungibleToken
approved_account_ids?: { [approvals: AccountId]: bigint };
}): boolean {
let must_revert = false;
let p: Bytes;
let p: string;
try {
p = near.promiseResult(0);
p = str(near.promiseResult(0));
} catch (e) {
if (e.message.includes("Not Ready")) {
throw new Error();
@@ -698,7 +707,7 @@ export class NonFungibleToken
}
if (!must_revert) {
try {
const yes_or_no = JSON.parse(p as Bytes);
const yes_or_no = JSON.parse(p);
if (typeof yes_or_no == "boolean") {
must_revert = yes_or_no;
} else {
@@ -756,17 +765,17 @@ export class NonFungibleToken
export type StorageKey = TokensPerOwner | TokenPerOwnerInner;

export class TokensPerOwner implements IntoStorageKey {
constructor(public account_hash: Bytes) {}
constructor(public account_hash: Uint8Array) {}

into_storage_key(): Bytes {
return "\x00" + this.account_hash;
into_storage_key(): Uint8Array {
return concat(bytes("\x00"), this.account_hash);
}
}

export class TokenPerOwnerInner implements IntoStorageKey {
constructor(public account_id_hash: Bytes) {}
constructor(public account_id_hash: Uint8Array) {}

into_storage_key(): Bytes {
return "\x01" + this.account_id_hash;
into_storage_key(): Uint8Array {
return concat(bytes("\x01"), this.account_id_hash);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { assert, Bytes } from "near-sdk-js";
import { assert } from "near-sdk-js";
import { Option } from "./utils";

/** This spec can be treated like a version of the standard. */
@@ -12,7 +12,7 @@ export class NFTContractMetadata {
public icon: Option<string>; // Data URL
public base_uri: Option<string>; // Centralized gateway known to have reliable access to decentralized storage assets referenced by `reference` or `media` URLs
public reference: Option<string>; // URL to a JSON file with more info
public reference_hash: Option<Bytes>; // Base64-encoded sha256 hash of JSON from reference field. Required if `reference` is included.
public reference_hash: Option<string>; // Base64-encoded sha256 hash of JSON from reference field. Required if `reference` is included.

constructor() {
this.spec = NFT_METADATA_SPEC;
@@ -31,7 +31,7 @@ export class NFTContractMetadata {
icon: Option<string>,
base_uri: Option<string>,
reference: Option<string>,
reference_hash: Option<Bytes>
reference_hash: Option<string>
) {
this.spec = spec;
this.name = name;
@@ -66,15 +66,15 @@ export class TokenMetadata {
public title: Option<string>, // ex. "Arch Nemesis: Mail Carrier" or "Parcel #5055"
public description: Option<string>, // free-form description
public media: Option<string>, // URL to associated media, preferably to decentralized, content-addressed storage
public media_hash: Option<Bytes>, // Base64-encoded sha256 hash of content referenced by the `media` field. Required if `media` is included.
public media_hash: Option<string>, // Base64-encoded sha256 hash of content referenced by the `media` field. Required if `media` is included.
public copies: Option<bigint>, // number of copies of this set of metadata in existence when token was minted.
public issued_at: Option<string>, // ISO 8601 datetime when token was issued or minted
public expires_at: Option<string>, // ISO 8601 datetime when token expires
public starts_at: Option<string>, // ISO 8601 datetime when token starts being valid
public updated_at: Option<string>, // ISO 8601 datetime when token was last updated
public extra: Option<string>, // anything extra the NFT wants to store on-chain. Can be stringified JSON.
public reference: Option<string>, // URL to an off-chain JSON file with more info.
public reference_hash: Option<Bytes> // Base64-encoded sha256 hash of JSON from reference field. Required if `reference` is included.
public reference_hash: Option<string> // Base64-encoded sha256 hash of JSON from reference field. Required if `reference` is included.
) {}

assert_valid() {
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { near, assert, Bytes, AccountId } from "near-sdk-js";
import { near, assert, AccountId, bytes } from "near-sdk-js";

export function refund_storage_deposit(
account_id: AccountId,
@@ -37,8 +37,8 @@ export function refund_deposit(storage_used: bigint): void {
refund_deposit_to_account(storage_used, near.predecessorAccountId());
}

export function hash_account_id(account_id: AccountId): Bytes {
return near.sha256(account_id);
export function hash_account_id(account_id: AccountId): Uint8Array {
return near.sha256(bytes(account_id));
}

/** Assert that at least 1 yoctoNEAR was attached. */
@@ -60,5 +60,5 @@ export function assert_one_yocto(): void {
export type Option<T> = T | null;

export interface IntoStorageKey {
into_storage_key(): Bytes;
into_storage_key(): Uint8Array;
}
275 changes: 215 additions & 60 deletions packages/near-sdk-js/builder/builder.c

Large diffs are not rendered by default.

76 changes: 38 additions & 38 deletions packages/near-sdk-js/lib/api.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions packages/near-sdk-js/lib/api.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 8 additions & 9 deletions packages/near-sdk-js/lib/collections/lookup-map.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions packages/near-sdk-js/lib/collections/lookup-map.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 3 additions & 4 deletions packages/near-sdk-js/lib/collections/lookup-set.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions packages/near-sdk-js/lib/collections/lookup-set.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 10 additions & 11 deletions packages/near-sdk-js/lib/collections/unordered-map.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 10 additions & 7 deletions packages/near-sdk-js/lib/collections/unordered-map.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 4 additions & 5 deletions packages/near-sdk-js/lib/collections/unordered-set.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 11 additions & 12 deletions packages/near-sdk-js/lib/collections/unordered-set.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 3 additions & 4 deletions packages/near-sdk-js/lib/collections/vector.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions packages/near-sdk-js/lib/collections/vector.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions packages/near-sdk-js/lib/near-bindgen.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions packages/near-sdk-js/lib/near-bindgen.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 10 additions & 10 deletions packages/near-sdk-js/lib/promise.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions packages/near-sdk-js/lib/types/collections.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions packages/near-sdk-js/lib/types/public_key.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions packages/near-sdk-js/lib/types/public_key.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

68 changes: 53 additions & 15 deletions packages/near-sdk-js/lib/utils.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

103 changes: 74 additions & 29 deletions packages/near-sdk-js/lib/utils.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

193 changes: 102 additions & 91 deletions packages/near-sdk-js/src/api.ts

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion packages/near-sdk-js/src/cli/build-tools/include-bytes.ts
Original file line number Diff line number Diff line change
@@ -56,7 +56,15 @@ export default function (): { visitor: Visitor } {
const filePath = join(root, fileRelPath);
const fileSrc = readFileSync(filePath, { encoding }).toString();

path.replaceWith(t.stringLiteral(fileSrc) as Node);
path.replaceWith(
t.callExpression(
t.memberExpression(
t.identifier("env"),
t.identifier("latin1_string_to_uint8array")
),
[t.stringLiteral(fileSrc)]
) as Node
);
}
},
},
24 changes: 12 additions & 12 deletions packages/near-sdk-js/src/collections/lookup-map.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as near from "../api";
import { GetOptions } from "../types/collections";
import {
Bytes,
getValueWithOptions,
serializeValueWithOptions,
encode,
} from "../utils";

/**
@@ -13,16 +13,16 @@ export class LookupMap<DataType> {
/**
* @param keyPrefix - The byte prefix to use when storing elements inside this collection.
*/
constructor(readonly keyPrefix: Bytes) {}
constructor(readonly keyPrefix: string) {}

/**
* Checks whether the collection contains the value.
*
* @param key - The value for which to check the presence.
*/
containsKey(key: Bytes): boolean {
containsKey(key: string): boolean {
const storageKey = this.keyPrefix + key;
return near.storageHasKey(storageKey);
return near.storageHasKey(encode(storageKey));
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm talking about places like this. Can we move this conversion to the API layer?

Copy link
Member Author

Choose a reason for hiding this comment

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

What do you think of this: have two APIs, storageHasKey and storageHasKeyRaw. where storageHasKey(k) = storageHasKeyRaw(encode(k)). The benefit is backward compatible and in most case the string-version it's what user want. And they can still achieve raw bytes manipulation with raw-version.

The disadvantage is this API might hide the fact that state is in the form of binary. When attempt to use the Low level API, it is expected that they already know nomicon. And to me it's unexpected that near.storageHasKey is not env.storage_has_key but near.storageHasKeyRaw is. This makes api naming inconsistent. For example, input still returns Uint8Array, should it be inputRaw and input?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Hm... maybe not raw, maybe inputBytes() ?
(more comments above)

Copy link
Member Author

Choose a reason for hiding this comment

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

inputBytes sounds good to me

}

/**
@@ -32,11 +32,11 @@ export class LookupMap<DataType> {
* @param options - Options for retrieving the data.
*/
get(
key: Bytes,
key: string,
options?: Omit<GetOptions<DataType>, "serializer">
): DataType | null {
const storageKey = this.keyPrefix + key;
const value = near.storageRead(storageKey);
const value = near.storageRead(encode(storageKey));

return getValueWithOptions(value, options);
}
@@ -48,12 +48,12 @@ export class LookupMap<DataType> {
* @param options - Options for retrieving the data.
*/
remove(
key: Bytes,
key: string,
options?: Omit<GetOptions<DataType>, "serializer">
): DataType | null {
const storageKey = this.keyPrefix + key;

if (!near.storageRemove(storageKey)) {
if (!near.storageRemove(encode(storageKey))) {
return options?.defaultValue ?? null;
}

@@ -70,14 +70,14 @@ export class LookupMap<DataType> {
* @param options - Options for retrieving and storing the data.
*/
set(
key: Bytes,
key: string,
newValue: DataType,
options?: GetOptions<DataType>
): DataType | null {
const storageKey = this.keyPrefix + key;
const storageValue = serializeValueWithOptions(newValue, options);

if (!near.storageWrite(storageKey, storageValue)) {
if (!near.storageWrite(encode(storageKey), storageValue)) {
return options?.defaultValue ?? null;
}

@@ -93,7 +93,7 @@ export class LookupMap<DataType> {
* @param options - Options for storing the data.
*/
extend(
keyValuePairs: [Bytes, DataType][],
keyValuePairs: [string, DataType][],
options?: GetOptions<DataType>
): void {
for (const [key, value] of keyValuePairs) {
@@ -106,7 +106,7 @@ export class LookupMap<DataType> {
*
* @param options - Options for storing the data.
*/
serialize(options?: Pick<GetOptions<DataType>, "serializer">): string {
serialize(options?: Pick<GetOptions<DataType>, "serializer">): Uint8Array {
return serializeValueWithOptions(this, options);
}

12 changes: 6 additions & 6 deletions packages/near-sdk-js/src/collections/lookup-set.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as near from "../api";
import { GetOptions } from "../types/collections";
import { Bytes, serializeValueWithOptions } from "../utils";
import { serializeValueWithOptions, encode } from "../utils";

/**
* A lookup set collection that stores entries in NEAR storage.
@@ -9,7 +9,7 @@ export class LookupSet<DataType> {
/**
* @param keyPrefix - The byte prefix to use when storing elements inside this collection.
*/
constructor(readonly keyPrefix: Bytes) {}
constructor(readonly keyPrefix: string) {}

/**
* Checks whether the collection contains the value.
@@ -22,7 +22,7 @@ export class LookupSet<DataType> {
options?: Pick<GetOptions<DataType>, "serializer">
): boolean {
const storageKey = this.keyPrefix + serializeValueWithOptions(key, options);
return near.storageHasKey(storageKey);
return near.storageHasKey(encode(storageKey));
}

/**
@@ -36,7 +36,7 @@ export class LookupSet<DataType> {
options?: Pick<GetOptions<DataType>, "serializer">
): boolean {
const storageKey = this.keyPrefix + serializeValueWithOptions(key, options);
return near.storageRemove(storageKey);
return near.storageRemove(encode(storageKey));
}

/**
@@ -51,7 +51,7 @@ export class LookupSet<DataType> {
options?: Pick<GetOptions<DataType>, "serializer">
): boolean {
const storageKey = this.keyPrefix + serializeValueWithOptions(key, options);
return !near.storageWrite(storageKey, "");
return !near.storageWrite(encode(storageKey), new Uint8Array());
}

/**
@@ -72,7 +72,7 @@ export class LookupSet<DataType> {
*
* @param options - Options for storing the data.
*/
serialize(options?: Pick<GetOptions<DataType>, "serializer">): string {
serialize(options?: Pick<GetOptions<DataType>, "serializer">): Uint8Array {
return serializeValueWithOptions(this, options);
}

40 changes: 22 additions & 18 deletions packages/near-sdk-js/src/collections/unordered-map.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {
assert,
Bytes,
ERR_INCONSISTENT_STATE,
getValueWithOptions,
Mutable,
serializeValueWithOptions,
encode,
decode,
} from "../utils";
import { Vector, VectorIterator } from "./vector";
import { LookupMap } from "./lookup-map";
@@ -16,14 +17,14 @@ type ValueAndIndex = [value: string, index: number];
* An unordered map that stores data in NEAR storage.
*/
export class UnorderedMap<DataType> {
readonly keys: Vector<Bytes>;
readonly keys: Vector<string>;
readonly values: LookupMap<ValueAndIndex>;

/**
* @param prefix - The byte prefix to use when storing elements inside this collection.
*/
constructor(readonly prefix: Bytes) {
this.keys = new Vector<Bytes>(`${prefix}u`); // intentional different prefix with old UnorderedMap
constructor(readonly prefix: string) {
this.keys = new Vector<string>(`${prefix}u`); // intentional different prefix with old UnorderedMap
this.values = new LookupMap<ValueAndIndex>(`${prefix}m`);
}

@@ -48,7 +49,7 @@ export class UnorderedMap<DataType> {
* @param options - Options for retrieving the data.
*/
get(
key: Bytes,
key: string,
options?: Omit<GetOptions<DataType>, "serializer">
): DataType | null {
const valueAndIndex = this.values.get(key);
@@ -59,7 +60,7 @@ export class UnorderedMap<DataType> {

const [value] = valueAndIndex;

return getValueWithOptions(value, options);
return getValueWithOptions(encode(value), options);
}

/**
@@ -70,7 +71,7 @@ export class UnorderedMap<DataType> {
* @param options - Options for retrieving and storing the data.
*/
set(
key: Bytes,
key: string,
value: DataType,
options?: GetOptions<DataType>
): DataType | null {
@@ -81,15 +82,15 @@ export class UnorderedMap<DataType> {
const newElementIndex = this.length;

this.keys.push(key);
this.values.set(key, [serialized, newElementIndex]);
this.values.set(key, [decode(serialized), newElementIndex]);

return null;
}

const [oldValue, oldIndex] = valueAndIndex;
this.values.set(key, [serialized, oldIndex]);
this.values.set(key, [decode(serialized), oldIndex]);

return getValueWithOptions(oldValue, options);
return getValueWithOptions(encode(oldValue), options);
}

/**
@@ -99,7 +100,7 @@ export class UnorderedMap<DataType> {
* @param options - Options for retrieving the data.
*/
remove(
key: Bytes,
key: string,
options?: Omit<GetOptions<DataType>, "serializer">
): DataType | null {
const oldValueAndIndex = this.values.remove(key);
@@ -123,7 +124,7 @@ export class UnorderedMap<DataType> {
this.values.set(swappedKey, [swappedValueAndIndex[0], index]);
}

return getValueWithOptions(value, options);
return getValueWithOptions(encode(value), options);
}

/**
@@ -160,7 +161,7 @@ export class UnorderedMap<DataType> {
*
* @param options - Options for retrieving and storing the data.
*/
toArray(options?: GetOptions<DataType>): [Bytes, DataType][] {
toArray(options?: GetOptions<DataType>): [string, DataType][] {
const array = [];

const iterator = options ? this.createIteratorWithOptions(options) : this;
@@ -177,7 +178,7 @@ export class UnorderedMap<DataType> {
*
* @param keyValuePairs - The key-value pairs to extend the collection with.
*/
extend(keyValuePairs: [Bytes, DataType][]) {
extend(keyValuePairs: [string, DataType][]) {
for (const [key, value] of keyValuePairs) {
this.set(key, value);
}
@@ -188,7 +189,7 @@ export class UnorderedMap<DataType> {
*
* @param options - Options for storing the data.
*/
serialize(options?: Pick<GetOptions<DataType>, "serializer">): string {
serialize(options?: Pick<GetOptions<DataType>, "serializer">): Uint8Array {
return serializeValueWithOptions(this, options);
}

@@ -218,7 +219,7 @@ export class UnorderedMap<DataType> {
* An iterator for the UnorderedMap collection.
*/
class UnorderedMapIterator<DataType> {
private keys: VectorIterator<Bytes>;
private keys: VectorIterator<string>;
private map: LookupMap<ValueAndIndex>;

/**
@@ -233,7 +234,7 @@ class UnorderedMapIterator<DataType> {
this.map = unorderedMap.values;
}

next(): { value: [Bytes | null, DataType | null]; done: boolean } {
next(): { value: [string | null, DataType | null]; done: boolean } {
const key = this.keys.next();

if (key.done) {
@@ -246,7 +247,10 @@ class UnorderedMapIterator<DataType> {

return {
done: key.done,
value: [key.value, getValueWithOptions(valueAndIndex[0], this.options)],
value: [
key.value,
getValueWithOptions(encode(valueAndIndex[0]), this.options),
],
};
}
}
33 changes: 15 additions & 18 deletions packages/near-sdk-js/src/collections/unordered-set.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import * as near from "../api";
import {
u8ArrayToBytes,
bytesToU8Array,
Bytes,
assert,
serializeValueWithOptions,
ERR_INCONSISTENT_STATE,
encode,
} from "../utils";
import { Vector, VectorIterator } from "./vector";
import { Mutable } from "../utils";
@@ -15,12 +13,11 @@ function serializeIndex(index: number) {
const data = new Uint32Array([index]);
const array = new Uint8Array(data.buffer);

return u8ArrayToBytes(array);
return array;
}

function deserializeIndex(rawIndex: Bytes): number {
const array = bytesToU8Array(rawIndex);
const [data] = new Uint32Array(array.buffer);
function deserializeIndex(rawIndex: Uint8Array): number {
const [data] = new Uint32Array(rawIndex.buffer);

return data;
}
@@ -29,13 +26,13 @@ function deserializeIndex(rawIndex: Bytes): number {
* An unordered set that stores data in NEAR storage.
*/
export class UnorderedSet<DataType> {
readonly elementIndexPrefix: Bytes;
readonly elementIndexPrefix: string;
readonly elements: Vector<DataType>;

/**
* @param prefix - The byte prefix to use when storing elements inside this collection.
*/
constructor(readonly prefix: Bytes) {
constructor(readonly prefix: string) {
this.elementIndexPrefix = `${prefix}i`;
this.elements = new Vector(`${prefix}e`);
}
@@ -66,7 +63,7 @@ export class UnorderedSet<DataType> {
): boolean {
const indexLookup =
this.elementIndexPrefix + serializeValueWithOptions(element, options);
return near.storageHasKey(indexLookup);
return near.storageHasKey(encode(indexLookup));
}

/**
@@ -83,13 +80,13 @@ export class UnorderedSet<DataType> {
const indexLookup =
this.elementIndexPrefix + serializeValueWithOptions(element, options);

if (near.storageRead(indexLookup)) {
if (near.storageRead(encode(indexLookup))) {
return false;
}

const nextIndex = this.length;
const nextIndexRaw = serializeIndex(nextIndex);
near.storageWrite(indexLookup, nextIndexRaw);
near.storageWrite(encode(indexLookup), nextIndexRaw);
this.elements.push(element, options);

return true;
@@ -104,7 +101,7 @@ export class UnorderedSet<DataType> {
remove(element: DataType, options?: GetOptions<DataType>): boolean {
const indexLookup =
this.elementIndexPrefix + serializeValueWithOptions(element, options);
const indexRaw = near.storageRead(indexLookup);
const indexRaw = near.storageRead(encode(indexLookup));

if (!indexRaw) {
return false;
@@ -113,7 +110,7 @@ export class UnorderedSet<DataType> {
// If there is only one element then swap remove simply removes it without
// swapping with the last element.
if (this.length === 1) {
near.storageRemove(indexLookup);
near.storageRemove(encode(indexLookup));

const index = deserializeIndex(indexRaw);
this.elements.swapRemove(index);
@@ -127,15 +124,15 @@ export class UnorderedSet<DataType> {

assert(!!lastElement, ERR_INCONSISTENT_STATE);

near.storageRemove(indexLookup);
near.storageRemove(encode(indexLookup));

// If the removed element was the last element from keys, then we don't need to
// reinsert the lookup back.
if (lastElement !== element) {
const lastLookupElement =
this.elementIndexPrefix +
serializeValueWithOptions(lastElement, options);
near.storageWrite(lastLookupElement, indexRaw);
near.storageWrite(encode(lastLookupElement), indexRaw);
}

const index = deserializeIndex(indexRaw);
@@ -151,7 +148,7 @@ export class UnorderedSet<DataType> {
for (const element of this.elements) {
const indexLookup =
this.elementIndexPrefix + serializeValueWithOptions(element, options);
near.storageRemove(indexLookup);
near.storageRemove(encode(indexLookup));
}

this.elements.clear();
@@ -207,7 +204,7 @@ export class UnorderedSet<DataType> {
*
* @param options - Options for storing the data.
*/
serialize(options?: Pick<GetOptions<DataType>, "serializer">): string {
serialize(options?: Pick<GetOptions<DataType>, "serializer">): Uint8Array {
return serializeValueWithOptions(this, options);
}

27 changes: 15 additions & 12 deletions packages/near-sdk-js/src/collections/vector.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import * as near from "../api";
import {
assert,
Bytes,
getValueWithOptions,
u8ArrayToBytes,
serializeValueWithOptions,
ERR_INCONSISTENT_STATE,
ERR_INDEX_OUT_OF_BOUNDS,
str,
bytes,
} from "../utils";
import { GetOptions } from "../types/collections";

function indexToKey(prefix: Bytes, index: number): Bytes {
function indexToKey(prefix: string, index: number): string {
const data = new Uint32Array([index]);
const array = new Uint8Array(data.buffer);
const key = u8ArrayToBytes(array);
const key = str(array);

return prefix + key;
}
@@ -27,7 +27,7 @@ export class Vector<DataType> {
* @param prefix - The byte prefix to use when storing elements inside this collection.
* @param length - The initial length of the collection. By default 0.
*/
constructor(readonly prefix: Bytes, public length = 0) {}
constructor(readonly prefix: string, public length = 0) {}

/**
* Checks whether the collection is empty.
@@ -51,7 +51,7 @@ export class Vector<DataType> {
}

const storageKey = indexToKey(this.prefix, index);
const value = near.storageRead(storageKey);
const value = near.storageRead(bytes(storageKey));

return getValueWithOptions(value, options);
}
@@ -75,7 +75,7 @@ export class Vector<DataType> {
const last = this.pop(options);

assert(
near.storageWrite(key, serializeValueWithOptions(last, options)),
near.storageWrite(bytes(key), serializeValueWithOptions(last, options)),
ERR_INCONSISTENT_STATE
);

@@ -97,7 +97,7 @@ export class Vector<DataType> {
const key = indexToKey(this.prefix, this.length);
this.length += 1;

near.storageWrite(key, serializeValueWithOptions(element, options));
near.storageWrite(bytes(key), serializeValueWithOptions(element, options));
}

/**
@@ -114,7 +114,7 @@ export class Vector<DataType> {
const lastKey = indexToKey(this.prefix, lastIndex);
this.length -= 1;

assert(near.storageRemove(lastKey), ERR_INCONSISTENT_STATE);
assert(near.storageRemove(bytes(lastKey)), ERR_INCONSISTENT_STATE);

const value = near.storageGetEvicted();

@@ -137,7 +137,10 @@ export class Vector<DataType> {
const key = indexToKey(this.prefix, index);

assert(
near.storageWrite(key, serializeValueWithOptions(element, options)),
near.storageWrite(
bytes(key),
serializeValueWithOptions(element, options)
),
ERR_INCONSISTENT_STATE
);

@@ -197,7 +200,7 @@ export class Vector<DataType> {
clear(): void {
for (let index = 0; index < this.length; index++) {
const key = indexToKey(this.prefix, index);
near.storageRemove(key);
near.storageRemove(bytes(key));
}

this.length = 0;
@@ -208,7 +211,7 @@ export class Vector<DataType> {
*
* @param options - Options for storing the data.
*/
serialize(options?: Pick<GetOptions<DataType>, "serializer">): string {
serialize(options?: Pick<GetOptions<DataType>, "serializer">): Uint8Array {
return serializeValueWithOptions(this, options);
}

26 changes: 14 additions & 12 deletions packages/near-sdk-js/src/near-bindgen.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as near from "./api";
import { deserialize, serialize } from "./utils";
import { deserialize, serialize, bytes, encode, decode } from "./utils";

type EmptyParameterObject = Record<never, never>;
type AnyObject = Record<string, unknown>;
@@ -145,8 +145,8 @@ export function middleware<Arguments extends Array<any>>(
*/
export function NearBindgen(options: {
requireInit?: boolean;
serializer?(value: unknown): string;
deserializer?(value: string): unknown;
serializer?(value: unknown): Uint8Array;
deserializer?(value: Uint8Array): unknown;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
}): any;
export function NearBindgen({
@@ -155,8 +155,8 @@ export function NearBindgen({
deserializer = deserialize,
}: {
requireInit?: boolean;
serializer?(value: unknown): string;
deserializer?(value: string): unknown;
serializer?(value: unknown): Uint8Array;
deserializer?(value: Uint8Array): unknown;
}) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return <T extends { new (...args: any[]): any }>(target: T) => {
@@ -166,29 +166,31 @@ export function NearBindgen({
}

static _getState(): unknown | null {
const rawState = near.storageRead("STATE");
const rawState = near.storageRead(bytes("STATE"));
return rawState ? this._deserialize(rawState) : null;
}

static _saveToStorage(objectToSave: unknown): void {
near.storageWrite("STATE", this._serialize(objectToSave));
near.storageWrite(bytes("STATE"), this._serialize(objectToSave));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same comment again. Can be unintuitive for devs. What will happen if we omit bytes() call?

Copy link
Member Author

Choose a reason for hiding this comment

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

In ts, compile error. In js, runtime error, smart contract panicked, expect Uint8Array for key.

}

static _getArgs(): unknown {
return JSON.parse(near.input() || "{}");
return JSON.parse(decode(near.input()) || "{}");
}

static _serialize(value: unknown, forReturn = false): string {
static _serialize(value: unknown, forReturn = false): Uint8Array {
if (forReturn) {
return JSON.stringify(value, (_, value) =>
typeof value === "bigint" ? `${value}` : value
return encode(
JSON.stringify(value, (_, value) =>
typeof value === "bigint" ? `${value}` : value
)
);
}

return serializer(value);
}

static _deserialize(value: string): unknown {
static _deserialize(value: Uint8Array): unknown {
return deserializer(value);
}

14 changes: 7 additions & 7 deletions packages/near-sdk-js/src/promise.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { assert, Bytes, PromiseIndex } from "./utils";
import { assert, PromiseIndex } from "./utils";
import * as near from "./api";
import { Balance, PublicKey, AccountId, Gas, GasWeight } from "./types";
import { Nonce } from "./types/primitives";
@@ -35,7 +35,7 @@ export class DeployContract extends PromiseAction {
/**
* @param code - The code of the contract to be deployed.
*/
constructor(public code: Bytes) {
constructor(public code: Uint8Array) {
super();
}

@@ -58,7 +58,7 @@ export class FunctionCall extends PromiseAction {
*/
constructor(
public functionName: string,
public args: Bytes,
public args: Uint8Array,
public amount: Balance,
public gas: Gas
) {
@@ -91,7 +91,7 @@ export class FunctionCallWeight extends PromiseAction {
*/
constructor(
public functionName: string,
public args: Bytes,
public args: Uint8Array,
public amount: Balance,
public gas: Gas,
public weight: GasWeight
@@ -336,7 +336,7 @@ export class NearPromise {
*
* @param code - The code of the contract to be deployed.
*/
deployContract(code: Bytes): NearPromise {
deployContract(code: Uint8Array): NearPromise {
return this.addAction(new DeployContract(code));
}

@@ -350,7 +350,7 @@ export class NearPromise {
*/
functionCall(
functionName: string,
args: Bytes,
args: Uint8Array,
amount: Balance,
gas: Gas
): NearPromise {
@@ -368,7 +368,7 @@ export class NearPromise {
*/
functionCallWeight(
functionName: string,
args: Bytes,
args: Uint8Array,
amount: Balance,
gas: Gas,
weight: GasWeight
6 changes: 3 additions & 3 deletions packages/near-sdk-js/src/types/collections.ts
Original file line number Diff line number Diff line change
@@ -17,11 +17,11 @@ export interface GetOptions<DataType> {
*
* @param valueToSerialize - The value that will be serialized - either the `DataType` or a unknown value.
*/
serializer?(valueToSerialize: unknown): string;
serializer?(valueToSerialize: unknown): Uint8Array;
/**
* A deserializer function to customize the deserialization of values after reading from NEAR storage for this call.
*
* @param valueToDeserialize - The string retrieved from NEAR storage to deserialize.
* @param valueToDeserialize - The Uint8Array retrieved from NEAR storage to deserialize.
*/
deserializer?(valueToDeserialize: string): unknown;
deserializer?(valueToDeserialize: Uint8Array): unknown;
}
16 changes: 8 additions & 8 deletions packages/near-sdk-js/src/types/public_key.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Bytes, bytes } from "../utils";
import { base58 } from "@scure/base";
import { concat } from "../utils";

export enum CurveType {
ED25519 = 0,
@@ -82,20 +82,20 @@ export class PublicKey {
/**
* The actual value of the public key.
*/
public data: Bytes;
public data: Uint8Array;
private type: CurveType;

/**
* @param data - The string you want to create a PublicKey from.
*/
constructor(data: Bytes) {
const curveLenght = dataLength(data.charCodeAt(0));
constructor(data: Uint8Array) {
const curveLenght = dataLength(data[0]);

if (data.length !== curveLenght + 1) {
throw new InvalidLengthError(data.length, curveLenght + 1);
}

this.type = getCurveType(data.charCodeAt(0));
this.type = getCurveType(data[0]);
this.data = data;
}

@@ -113,14 +113,14 @@ export class PublicKey {
*/
static fromString(publicKeyString: string) {
const [curve, keyData] = splitKeyTypeData(publicKeyString);
let data: Bytes;
let data: Uint8Array;

try {
data = bytes(base58.decode(keyData));
data = base58.decode(keyData);
} catch (error) {
throw new Base58Error(error.message);
}

return new PublicKey(`${String.fromCharCode(curve)}${data}`);
return new PublicKey(concat(new Uint8Array([curve]), data));
}
}
175 changes: 109 additions & 66 deletions packages/near-sdk-js/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { GetOptions } from "./types/collections";

/**
* A string containing byte characters. Can be safely used in NEAR calls.
*/
export type Bytes = string;
export interface Env {
uint8array_to_latin1_string(a: Uint8Array): string;
uint8array_to_utf8_string(a: Uint8Array): string;
latin1_string_to_uint8array(s: string): Uint8Array;
utf8_string_to_uint8array(s: string): Uint8Array;
}

declare const env: Env;

// make PromiseIndex a nominal typing
enum PromiseIndexBrand {
@@ -35,46 +39,17 @@ export const ERR_INDEX_OUT_OF_BOUNDS = "Index out of bounds";
const ACCOUNT_ID_REGEX =
/^(([a-z\d]+[-_])*[a-z\d]+\.)*([a-z\d]+[-_])*[a-z\d]+$/;

export function u8ArrayToBytes(array: Uint8Array): Bytes {
return array.reduce(
(result, value) => `${result}${String.fromCharCode(value)}`,
""
);
}

// TODO this function is a bit broken and the type can't be string
// TODO for more info: https://github.com/near/near-sdk-js/issues/78
export function bytesToU8Array(bytes: Bytes): Uint8Array {
return Uint8Array.from([...bytes].map((byte) => byte.charCodeAt(0)));
}

/**
* Accepts a string or Uint8Array and either checks for the validity of the string or converts the Uint8Array to Bytes.
*
* @param stringOrU8Array - The string or Uint8Array to be checked/transformed
* @returns Safe Bytes to be used in NEAR calls.
* Concat two Uint8Array
* @param array1
* @param array2
* @returns the concatenation of two array
*/
export function bytes(stringOrU8Array: string | Uint8Array): Bytes {
if (typeof stringOrU8Array === "string") {
return checkStringIsBytes(stringOrU8Array);
}

if (stringOrU8Array instanceof Uint8Array) {
return u8ArrayToBytes(stringOrU8Array);
}

throw new Error("bytes: expected string or Uint8Array");
}

function checkStringIsBytes(value: string): string {
[...value].forEach((character, index) => {
assert(
character.charCodeAt(0) <= 255,
`string ${value} at index ${index}: ${character} is not a valid byte`
);
});

return value;
export function concat(array1: Uint8Array, array2: Uint8Array): Uint8Array {
const mergedArray = new Uint8Array(array1.length + array2.length);
mergedArray.set(array1);
mergedArray.set(array2, array1.length);
return mergedArray;
}

/**
@@ -95,11 +70,15 @@ export function assert(
export type Mutable<T> = { -readonly [P in keyof T]: T[P] };

export function getValueWithOptions<DataType>(
value: string,
value: Uint8Array | null,
options: Omit<GetOptions<DataType>, "serializer"> = {
deserializer: deserialize,
}
): DataType | null {
if (value === null) {
return options?.defaultValue ?? null;
}

const deserialized = deserialize(value);

if (deserialized === undefined || deserialized === null) {
@@ -118,36 +97,38 @@ export function serializeValueWithOptions<DataType>(
{ serializer }: Pick<GetOptions<DataType>, "serializer"> = {
serializer: serialize,
}
): string {
): Uint8Array {
return serializer(value);
}

export function serialize(valueToSerialize: unknown): string {
return JSON.stringify(valueToSerialize, function (key, value) {
if (typeof value === "bigint") {
return {
value: value.toString(),
[TYPE_KEY]: TypeBrand.BIGINT,
};
}
export function serialize(valueToSerialize: unknown): Uint8Array {
return encode(
JSON.stringify(valueToSerialize, function (key, value) {
if (typeof value === "bigint") {
return {
value: value.toString(),
[TYPE_KEY]: TypeBrand.BIGINT,
};
}

if (
typeof this[key] === "object" &&
this[key] !== null &&
this[key] instanceof Date
) {
return {
value: this[key].toISOString(),
[TYPE_KEY]: TypeBrand.DATE,
};
}
if (
typeof this[key] === "object" &&
this[key] !== null &&
this[key] instanceof Date
) {
return {
value: this[key].toISOString(),
[TYPE_KEY]: TypeBrand.DATE,
};
}

return value;
});
return value;
})
);
}

export function deserialize(valueToDeserialize: string): unknown {
return JSON.parse(valueToDeserialize, (_, value) => {
export function deserialize(valueToDeserialize: Uint8Array): unknown {
return JSON.parse(decode(valueToDeserialize), (_, value) => {
if (
value !== null &&
typeof value === "object" &&
@@ -179,3 +160,65 @@ export function validateAccountId(accountId: string): boolean {
ACCOUNT_ID_REGEX.test(accountId)
);
}

/**
* A subset of NodeJS TextEncoder API
*/
export class TextEncoder {
encode(s: string): Uint8Array {
return env.utf8_string_to_uint8array(s);
}
}

/**
* A subset of NodeJS TextDecoder API. Only support utf-8 and latin1 encoding.
*/
export class TextDecoder {
constructor(public encoding: string = "utf-8") {}

decode(a: Uint8Array): string {
if (this.encoding == "utf-8") {
return env.uint8array_to_utf8_string(a);
} else if (this.encoding == "latin1") {
return env.uint8array_to_latin1_string(a);
} else {
throw new Error("unsupported encoding: " + this.encoding);
}
}
}

/**
* Convert a string to Uint8Array, each character must have a char code between 0-255.
* @param s - string that with only Latin1 character to convert
* @returns result Uint8Array
*/
export function bytes(s: string): Uint8Array {
return env.latin1_string_to_uint8array(s);
}

/**
* Convert a Uint8Array to string, each uint8 to the single character of that char code
* @param a - Uint8Array to convert
* @returns result string
*/
export function str(a: Uint8Array): string {
return env.uint8array_to_latin1_string(a);
}

/**
* Encode the string to Uint8Array with UTF-8 encoding
* @param s - String to encode
* @returns result Uint8Array
*/
export function encode(s: string): Uint8Array {
return env.utf8_string_to_uint8array(s);
}

/**
* Decode the Uint8Array to string in UTF-8 encoding
* @param a - array to decode
* @returns result string
*/
export function decode(a: Uint8Array): string {
return env.uint8array_to_utf8_string(a);
}
68 changes: 47 additions & 21 deletions tests/__tests__/bytes.ava.js
Original file line number Diff line number Diff line change
@@ -12,10 +12,11 @@ test.beforeEach(async (t) => {
const bytesContract = await root.devDeploy("build/bytes.wasm");
// Test users
const ali = await root.createSubAccount("ali");
const bob = await root.createSubAccount("bob");

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

test.afterEach.always(async (t) => {
@@ -43,9 +44,13 @@ test("Log unexpected types not logging", async (t) => {
const { ali, bytesContract } = t.context.accounts;

let r = await ali.callRaw(bytesContract, "log_unexpected_input_tests", "");
// logUtf8 and logUtf16 only works with bytes, trying to log it with string is unexpected and behavior is undefined
// in this specific case, it logs nothing
t.deepEqual(r.result.receipts_outcome[0].outcome.logs, ["", ""]);
// logUtf8 and logUtf16 only works with bytes, trying to log it with string is error
t.assert(
r.result.status.Failure.ActionError.kind.FunctionCallError.ExecutionError.startsWith(
"Smart contract panicked: Expect Uint8Array for message"
)
);
t.deepEqual(r.result.receipts_outcome[0].outcome.logs, []);
});

test("Log invalid utf-8 sequence panic", async (t) => {
@@ -83,7 +88,7 @@ function encodeStateKey(k) {
return Buffer.from(k).toString("base64");
}

test("storage write bytes tests", async (t) => {
test("storage write bytes tests. Any latin1 string: ascii, valid or invalid utf-8 sequence can be convert to Uint8Array correctly", async (t) => {
const { ali, bytesContract } = t.context.accounts;

await ali.call(bytesContract, "storage_write_bytes", "");
@@ -108,24 +113,12 @@ test("storage write bytes tests", async (t) => {
);
});

test("storage write unexpected types tests", async (t) => {
test("storage write utf8, utf8 string convert to Uint8Array tests", async (t) => {
const { ali, bytesContract } = t.context.accounts;

await ali.call(bytesContract, "storage_write_unexpected_input", "");
let stateMap = new Map();
// viewState doesn't work, because it tries to convert key to utf-8 string, which is not
let state = await bytesContract.viewStateRaw();
for (let { key, value } of state) {
stateMap.set(key, value);
}

t.deepEqual(
stateMap.get(encodeStateKey("123")),
Buffer.from("456").toString("base64")
);
// pass in utf-8 string instead of bytes, key and value become empty
t.deepEqual(stateMap.get(encodeStateKey([0xe6, 0xb0, 0xb4])), undefined);
t.deepEqual(stateMap.get(encodeStateKey([])), "");
await ali.call(bytesContract, "storage_write_utf8", "");
let r = await bytesContract.viewRaw("storage_read_utf8", "");
t.deepEqual(r.result, [...Buffer.from("😂", "utf-8")]);
});

test("Storage read bytes tests", async (t) => {
@@ -207,3 +200,36 @@ test("panic tests", async (t) => {
"String encoding is bad UTF-8 sequence."
);
});

test("utf8 string can be convert to Uint8Array correctly", async (t) => {
const { bob, bytesContract } = t.context.accounts;

let res = await bob.callRaw(
bytesContract,
"utf8_string_to_uint8array_tests",
""
);
t.is(res.result.status.SuccessValue, "");
});

test("valid utf8 sequence can be convert to string correctly", async (t) => {
const { bob, bytesContract } = t.context.accounts;

let res = await bob.callRaw(
bytesContract,
"uint8array_to_utf8_string_tests",
""
);
t.is(res.result.status.SuccessValue, "");
});

test("latin1 sequence can be convert to string correctly", async (t) => {
const { bob, bytesContract } = t.context.accounts;

let res = await bob.callRaw(
bytesContract,
"uint8array_to_latin1_string_tests",
""
);
t.is(res.result.status.SuccessValue, "");
});
22 changes: 22 additions & 0 deletions tests/__tests__/lookup-map.ava.js
Original file line number Diff line number Diff line change
@@ -92,3 +92,25 @@ test("LookupMap allows you to use the same key for the second time", async (t) =

t.is(await lookupMapContract.view("get", { key: "hello" }), "world");
});

test.only("UTF-8 in arguments, store in collections & state, return in returns", async (t) => {
const { ali, lookupMapContract } = t.context.accounts;

let data = {
utf8emoji: "😂",
utf8char: "水",
// this is the byte sequence of above utf8 char, it will be escaped in js contract
// so it won't be mis-recorgnized as above utf8 char.
utf8char_charcode_seq: "\xe6\xb0\xb4",
// this and above shows arbitrary binary data can be put in arguments, state and return
// default serialization and deserialization works
latin1_charcode_seq: "\xc2\x00\x01\xff",
};
let res = await ali.callRaw(lookupMapContract, "set", {
key: "utf8test",
value: data,
});
t.is(res.result.status.SuccessValue, "");

t.deepEqual(await lookupMapContract.view("get", { key: "utf8test" }), data);
});
3 changes: 3 additions & 0 deletions tests/__tests__/test_highlevel_promise.ava.js
Original file line number Diff line number Diff line change
@@ -168,6 +168,9 @@ test("cross contract call success then call a panic method", async (t) => {
}
);
// the last promise fail, cause the transaction fail
console.log(
ailisp marked this conversation as resolved.
Show resolved Hide resolved
r.result.status.Failure.ActionError.kind.FunctionCallError.ExecutionError
);
t.assert(
r.result.status.Failure.ActionError.kind.FunctionCallError.ExecutionError.includes(
"Smart contract panicked: it just panic"
13 changes: 2 additions & 11 deletions tests/__tests__/test_log_panic_api.ava.js
Original file line number Diff line number Diff line change
@@ -33,22 +33,13 @@ test("Log expected types work", async (t) => {
"abc",
"水",
"333",
"\x00\x01\xff",
"\xe6\xb0\xb4",
'{"0":0,"1":1,"2":255}',
'{"0":230,"1":176,"2":180}',
"水",
"水",
]);
});

test("Log unexpected types not logging", async (t) => {
const { ali, testContract } = t.context.accounts;

let r = await ali.callRaw(testContract, "log_unexpected_input_tests", "");
// logUtf8 and logUtf16 only works with bytes, trying to log it with string is unexpected and behavior is undefined
// in this specific case, it logs nothing
t.deepEqual(r.result.receipts_outcome[0].outcome.logs, ["", ""]);
});

test("Log invalid utf-8 sequence panic", async (t) => {
const { ali, testContract } = t.context.accounts;

4 changes: 3 additions & 1 deletion tests/__tests__/test_promise_api.ava.js
Original file line number Diff line number Diff line change
@@ -232,8 +232,10 @@ test("promise batch deploy contract and call", async (t) => {
caller2Contract,
"test_promise_batch_deploy_call",
"",
{ gas: "200 Tgas" }
{ gas: "300 Tgas" }
);
console.log(JSON.stringify(r, null, 2));
ailisp marked this conversation as resolved.
Show resolved Hide resolved

let deployed = caller2Contract.getSubAccount("b");
t.deepEqual(JSON.parse(Buffer.from(r.result.status.SuccessValue, "base64")), {
currentAccountId: deployed.accountId,
80 changes: 74 additions & 6 deletions tests/src/bytes.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { near, bytes } from "near-sdk-js";
import { near, bytes, str, encode, decode, assert } from "near-sdk-js";

export function log_expected_input_tests() {
// log ascii string
@@ -8,9 +8,9 @@ export function log_expected_input_tests() {
// log number
near.log(333);
// log aribrary byte sequence
near.log(bytes("\x00\x01\xff"));
near.log("\x00\x01\xff");
// log valid utf8 seqence
near.log(bytes("\xe6\xb0\xb4"));
near.log("\xe6\xb0\xb4");

// log valid utf8 sequence
near.logUtf8(bytes("\xe6\xb0\xb4"));
@@ -39,9 +39,12 @@ export function storage_write_bytes() {
near.storageWrite(bytes("\xe6\xb0\xb4"), bytes("\x00ab"));
}

export function storage_write_unexpected_input() {
near.storageWrite("水", "水");
near.storageWrite(123, 456);
export function storage_write_utf8() {
near.storageWrite(encode("水"), encode("😂"));
}

export function storage_read_utf8() {
near.valueReturn(near.storageRead(encode("水")));
}

export function storage_read_ascii_bytes() {
@@ -87,3 +90,68 @@ export function panicUtf8_valid_utf8_sequence() {
export function panicUtf8_invalid_utf8_sequence() {
near.panicUtf8(bytes("\x00\x01\xff"));
}

const areEqual = (first, second) =>
first.length === second.length &&
first.every((value, index) => value === second[index]);

export function utf8_string_to_uint8array_tests() {
let utf8chars = "水😂";
let withUtf8CharCodeSeq = "\xe6\xb0\xb4";
let withArbitraryLatinSeq = "\x00\x01\xff";

assert(
areEqual(
encode(utf8chars),
new Uint8Array([230, 176, 180, 240, 159, 152, 130])
)
);
assert(
areEqual(
encode(withUtf8CharCodeSeq),
new Uint8Array([195, 166, 194, 176, 194, 180])
)
);
assert(
areEqual(encode(withArbitraryLatinSeq), new Uint8Array([0, 1, 195, 191]))
);

assert(decode(encode(utf8chars)) == utf8chars);
assert(decode(encode(withUtf8CharCodeSeq)) == withUtf8CharCodeSeq);
assert(decode(encode(withArbitraryLatinSeq)) == withArbitraryLatinSeq);
}

export function uint8array_to_utf8_string_tests() {
let validUtf8SeqArray = new Uint8Array([230, 176, 180, 240, 159, 152, 130]);
let escapedUtf8SeqArray = new Uint8Array([195, 166, 194, 176, 194, 180]);
let invalidUtf8Seq = new Uint8Array([0, 1, 255]);

assert(decode(validUtf8SeqArray) == "水😂");
assert(decode(escapedUtf8SeqArray) == "\xe6\xb0\xb4");
// same behavior as nodejs
assert(decode(invalidUtf8Seq) == "\x00\x01\ufffd");

assert(areEqual(encode(decode(validUtf8SeqArray)), validUtf8SeqArray));
assert(areEqual(encode(decode(escapedUtf8SeqArray)), escapedUtf8SeqArray));
// same behavior as nodejs
assert(
areEqual(
encode(decode(invalidUtf8Seq)),
new Uint8Array([0, 1, 239, 191, 189])
)
);
}

export function uint8array_to_latin1_string_tests() {
let happensToBeUtf8Seq = new Uint8Array([230, 176, 180]);
let validLatin1InvalidUtf8 = new Uint8Array([0, 1, 255]);
let ascii = new Uint8Array([65, 66, 67]);

assert(str(happensToBeUtf8Seq) == "\xe6\xb0\xb4");
assert(str(validLatin1InvalidUtf8) == "\x00\x01\xff");
assert(str(ascii) == "\x41\x42\x43");

assert(areEqual(bytes(str(happensToBeUtf8Seq)), happensToBeUtf8Seq));
assert(areEqual(bytes(str(validLatin1InvalidUtf8)), validLatin1InvalidUtf8));
assert(areEqual(bytes(str(ascii)), ascii));
}
28 changes: 15 additions & 13 deletions tests/src/context_api.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { near } from "near-sdk-js";
import { near, bytes } from "near-sdk-js";

export function get_current_account_id() {
near.valueReturn(near.currentAccountId());
near.valueReturn(bytes(near.currentAccountId()));
}

export function get_signer_account_id() {
near.valueReturn(near.signerAccountId());
near.valueReturn(bytes(near.signerAccountId()));
}

export function get_predecessor_account_id() {
near.valueReturn(near.predecessorAccountId());
near.valueReturn(bytes(near.predecessorAccountId()));
}

export function get_signer_account_pk() {
@@ -21,41 +21,43 @@ export function get_input() {
}

export function get_storage_usage() {
near.valueReturn(near.storageUsage());
near.valueReturn(bytes(near.storageUsage().toString()));
}

export function get_block_height() {
near.valueReturn(near.blockHeight());
near.valueReturn(bytes(near.blockHeight().toString()));
}

export function get_block_timestamp() {
near.valueReturn(near.blockTimestamp());
near.valueReturn(bytes(near.blockTimestamp().toString()));
}

export function get_epoch_height() {
near.valueReturn(near.epochHeight());
near.valueReturn(bytes(near.epochHeight().toString()));
}

export function get_attached_deposit() {
near.valueReturn(JSON.stringify(near.attachedDeposit().toString()));
near.valueReturn(bytes(JSON.stringify(near.attachedDeposit().toString())));
}

export function get_prepaid_gas() {
near.valueReturn(near.prepaidGas());
near.valueReturn(bytes(near.prepaidGas().toString()));
}

export function get_used_gas() {
near.valueReturn(near.usedGas());
near.valueReturn(bytes(near.usedGas().toString()));
}

export function get_random_seed() {
near.valueReturn(near.randomSeed());
}

export function get_validator_stake() {
near.valueReturn(near.validatorStake(near.signerAccountId()));
near.valueReturn(
bytes(near.validatorStake(near.signerAccountId()).toString())
);
}

export function get_total_stake() {
near.valueReturn(near.validatorTotalStake());
near.valueReturn(bytes(near.validatorTotalStake().toString()));
}
14 changes: 7 additions & 7 deletions tests/src/highlevel-promise.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { NearBindgen, call, NearPromise, near, bytes } from "near-sdk-js";
import { NearBindgen, call, NearPromise, near, bytes, str } from "near-sdk-js";
import { PublicKey } from "near-sdk-js";

function callingData() {
return {
currentAccountId: near.currentAccountId(),
signerAccountId: near.signerAccountId(),
predecessorAccountId: near.predecessorAccountId(),
input: near.input(),
input: str(near.input()),
};
}

@@ -120,7 +120,7 @@ export class HighlevelPromiseContract {
return {
...callingData(),
promiseResults: arrayN(near.promiseResultsCount()).map((i) =>
near.promiseResult(i)
str(near.promiseResult(i))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Any use cases we want to represent promiseResult as something else but string?
(this one is about moving conversion to the API level again)

Copy link
Member Author

Choose a reason for hiding this comment

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

One example would be promise return one's public key. And callback function take that public key to do something. Public keys that host function returns and takes are binary format.

Copy link
Collaborator

Choose a reason for hiding this comment

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

So, if they both have string in their API, it will work? But we will do the conversion 2 times (gas issue)?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes. conversion 2 times adds unnecessary gas consumption

),
callbackArg1,
};
@@ -129,9 +129,9 @@ export class HighlevelPromiseContract {
@call({})
cross_contract_callback_write_state() {
// Attempt to write something in state. If this one is successfully executed and not revoked, these should be in state
near.storageWrite("aaa", "bbb");
near.storageWrite("ccc", "ddd");
near.storageWrite("eee", "fff");
near.storageWrite(bytes("aaa"), bytes("bbb"));
near.storageWrite(bytes("ccc"), bytes("ddd"));
near.storageWrite(bytes("eee"), bytes("fff"));
}

@call({})
@@ -213,7 +213,7 @@ export class HighlevelPromiseContract {
2 * Math.pow(10, 13)
)
);
near.storageWrite("aaa", "bbb");
near.storageWrite(bytes("aaa"), bytes("bbb"));
return promise;
}

7 changes: 0 additions & 7 deletions tests/src/log_panic_api.js
Original file line number Diff line number Diff line change
@@ -18,13 +18,6 @@ export function log_expected_input_tests() {
near.logUtf16(bytes("\x34\x6c"));
}

export function log_unexpected_input_tests() {
// log non-bytes with logUtf8
near.logUtf8("水");
// log non-bytes with logUtf16
near.logUtf16("水");
}

export function log_invalid_utf8_sequence_test() {
near.logUtf8(bytes("\x00\x01\xff"));
}
25 changes: 10 additions & 15 deletions tests/src/math_api.js
Original file line number Diff line number Diff line change
@@ -17,21 +17,16 @@ export function test_ripemd160() {
}

export function test_ecrecover() {
let hash = bytes(
new Uint8Array([
206, 6, 119, 187, 48, 186, 168, 207, 6, 124, 136, 219, 152, 17, 244, 51,
61, 19, 27, 248, 188, 241, 47, 231, 6, 93, 33, 29, 206, 151, 16, 8,
])
);
let sign = bytes(
new Uint8Array([
144, 242, 123, 139, 72, 141, 176, 11, 0, 96, 103, 150, 210, 152, 127, 106,
95, 89, 174, 98, 234, 5, 239, 254, 132, 254, 245, 184, 176, 229, 73, 152,
74, 105, 17, 57, 173, 87, 163, 240, 185, 6, 99, 118, 115, 170, 47, 99,
209, 245, 92, 177, 166, 145, 153, 212, 0, 158, 234, 35, 206, 173, 220,
147,
])
);
let hash = new Uint8Array([
206, 6, 119, 187, 48, 186, 168, 207, 6, 124, 136, 219, 152, 17, 244, 51, 61,
19, 27, 248, 188, 241, 47, 231, 6, 93, 33, 29, 206, 151, 16, 8,
]);
let sign = new Uint8Array([
144, 242, 123, 139, 72, 141, 176, 11, 0, 96, 103, 150, 210, 152, 127, 106,
95, 89, 174, 98, 234, 5, 239, 254, 132, 254, 245, 184, 176, 229, 73, 152,
74, 105, 17, 57, 173, 87, 163, 240, 185, 6, 99, 118, 115, 170, 47, 99, 209,
245, 92, 177, 166, 145, 153, 212, 0, 158, 234, 35, 206, 173, 220, 147,
]);
let v = 1;
let malleabilityFlag = 1;
let ret = near.ecrecover(hash, sign, v, malleabilityFlag);
12 changes: 6 additions & 6 deletions tests/src/promise_api.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { near, bytes } from "near-sdk-js";
import { near, str, bytes } from "near-sdk-js";

function arrayN(n) {
return [...Array(Number(n)).keys()];
@@ -10,17 +10,17 @@ export function just_panic() {

export function write_some_state() {
// Attempt to write something in state. If this one is successfully executed and not revoked, these should be in state
near.storageWrite("aaa", "bbb");
near.storageWrite("ccc", "ddd");
near.storageWrite("eee", "fff");
near.storageWrite(bytes("aaa"), bytes("bbb"));
near.storageWrite(bytes("ccc"), bytes("ddd"));
near.storageWrite(bytes("eee"), bytes("fff"));
}

function callingData() {
return {
currentAccountId: near.currentAccountId(),
signerAccountId: near.signerAccountId(),
predecessorAccountId: near.predecessorAccountId(),
input: near.input(),
input: str(near.input()),
};
}

@@ -38,7 +38,7 @@ export function cross_contract_callback() {
JSON.stringify({
...callingData(),
promiseResults: arrayN(near.promiseResultsCount()).map((i) =>
near.promiseResult(i)
str(near.promiseResult(i))
),
})
)
45 changes: 20 additions & 25 deletions tests/src/public-key.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { near, bytes } from "near-sdk-js";
import { near } from "near-sdk-js";
import { CurveType, PublicKey } from "near-sdk-js";
import { assert } from "near-sdk-js";

@@ -16,15 +16,13 @@ export function test_add_signer_key() {

export function test_add_ed25519_key_bytes() {
let pk = new PublicKey(
bytes(
new Uint8Array([
// CurveType.ED25519 = 0
0,
// ED25519 PublicKey data
186, 44, 216, 49, 157, 48, 151, 47, 23, 244, 137, 69, 78, 150, 54, 42,
30, 248, 110, 26, 205, 18, 137, 154, 10, 208, 26, 183, 65, 166, 223, 18,
])
)
new Uint8Array([
// CurveType.ED25519 = 0
0,
// ED25519 PublicKey data
186, 44, 216, 49, 157, 48, 151, 47, 23, 244, 137, 69, 78, 150, 54, 42, 30,
248, 110, 26, 205, 18, 137, 154, 10, 208, 26, 183, 65, 166, 223, 18,
])
);
runtime_validate_public_key("a", pk.data);
}
@@ -37,18 +35,15 @@ export function test_add_ed25519_key_string() {

export function test_add_secp256k1_key_bytes() {
let pk = new PublicKey(
bytes(
new Uint8Array([
// CurveType.SECP256K1 = 1
1,
// SECP256K1 PublicKey data
242, 86, 198, 230, 200, 11, 33, 63, 42, 160, 176, 23, 68, 35, 93, 81,
92, 89, 68, 53, 190, 101, 27, 21, 136, 58, 16, 221, 71, 47, 166, 70,
206, 98, 234, 243, 103, 13, 197, 203, 145, 0, 160, 202, 42, 85, 178,
193, 71, 193, 233, 163, 140, 228, 40, 135, 142, 125, 70, 225, 251, 113,
74, 153,
])
)
new Uint8Array([
// CurveType.SECP256K1 = 1
1,
// SECP256K1 PublicKey data
242, 86, 198, 230, 200, 11, 33, 63, 42, 160, 176, 23, 68, 35, 93, 81, 92,
89, 68, 53, 190, 101, 27, 21, 136, 58, 16, 221, 71, 47, 166, 70, 206, 98,
234, 243, 103, 13, 197, 203, 145, 0, 160, 202, 42, 85, 178, 193, 71, 193,
233, 163, 140, 228, 40, 135, 142, 125, 70, 225, 251, 113, 74, 153,
])
);
runtime_validate_public_key("c", pk.data);
}
@@ -63,7 +58,7 @@ export function test_add_secp256k1_key_string() {
export function add_invalid_public_key() {
runtime_validate_public_key(
"e",
bytes(new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
);
}

@@ -74,11 +69,11 @@ export function curve_type() {
}

export function create_invalid_curve_type() {
new PublicKey(bytes(new Uint8Array([2, 1])));
new PublicKey(new Uint8Array([2, 1]));
}

export function create_invalid_length() {
new PublicKey(bytes(new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])));
new PublicKey(new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]));
}

export function create_from_invalid_base58() {
14 changes: 11 additions & 3 deletions tests/src/storage_api.js
Original file line number Diff line number Diff line change
@@ -2,7 +2,11 @@ import { near, bytes } from "near-sdk-js";

export function test_storage_write() {
near.valueReturn(
near.storageWrite(bytes("\x00tesdsst\xff"), bytes("\x00\x01\xff"))
bytes(
near
.storageWrite(bytes("\x00tesdsst\xff"), bytes("\x00\x01\xff"))
.toString()
)
);
}

@@ -11,11 +15,15 @@ export function test_storage_read() {
}

export function test_storage_remove() {
near.valueReturn(near.storageRemove(bytes("\x00tesdsst\xff")));
near.valueReturn(
bytes(near.storageRemove(bytes("\x00tesdsst\xff")).toString())
);
}

export function test_storage_has_key() {
near.valueReturn(near.storageHasKey(bytes("\x00tesdsst\xff")));
near.valueReturn(
bytes(near.storageHasKey(bytes("\x00tesdsst\xff")).toString())
);
}

export function test_storage_get_evicted() {
2,205 changes: 2,205 additions & 0 deletions tests/yarn-error.log

Large diffs are not rendered by default.