diff --git a/examples/__tests__/test-nested-collections.ava.js b/examples/__tests__/test-nested-collections.ava.js index 62ed835c9..33149a119 100644 --- a/examples/__tests__/test-nested-collections.ava.js +++ b/examples/__tests__/test-nested-collections.ava.js @@ -120,3 +120,19 @@ test("sets then gets nested nested collection", async (t) => { "dog" ); }); + +test("Ali add_lk_sets then get_lk_set", async (t) => { + const { ali, nestedCollections } = t.context.accounts; + await ali.call(nestedCollections, "add_lk_set", { id: "1" }); + await ali.call(nestedCollections, "add_lk_set", { id: "2" }); + + t.is( + await nestedCollections.view("get_lk_set", { id: "1", accountId: ali.accountId }), + true + ); + + t.is( + await nestedCollections.view("get_lk_set", { id: "2", accountId: ali.accountId }), + true + ); +}); diff --git a/examples/src/nested-collections.ts b/examples/src/nested-collections.ts index e8e1cbb8d..194ff7e41 100644 --- a/examples/src/nested-collections.ts +++ b/examples/src/nested-collections.ts @@ -1,22 +1,24 @@ -import { NearBindgen, near, call, view, UnorderedMap } from "near-sdk-js"; +import {NearBindgen, near, call, view, UnorderedMap, LookupMap, LookupSet, UnorderedSet, Vector} from "near-sdk-js"; @NearBindgen({}) export class Contract { outerMap: UnorderedMap>; groups: UnorderedMap>>; + outerLkpSet: UnorderedMap>; + outerSet: UnorderedMap>; + outerVec: UnorderedMap>; constructor() { this.outerMap = new UnorderedMap("o"); this.groups = new UnorderedMap("gs"); + this.outerLkpSet = new UnorderedMap("ols"); + this.outerSet = new UnorderedMap("os"); + this.outerVec = new UnorderedMap("ov"); } - // Using some type-awared format instead of standard JSON.stringify @call({}) add({ id, text }: { id: string; text: string }) { - // But car.run() doesn't work, because SDK only know how to deserialize it as a plain object, not a Car instance. - // This problem is particularly painful when class is nested, for example collection class instance LookupMap containing Car class instance. Currently SDK mitigate this problem by requires user to manually reconstruct the JS object to an instance of the original class. const innerMap = this.outerMap.get(id, { - // reconstructor: UnorderedMap.reconstruct, defaultValue: new UnorderedMap("i_" + id + "_"), }); innerMap.set(near.signerAccountId(), text); @@ -25,9 +27,7 @@ export class Contract { @view({}) get({ id, accountId }: { id: string; accountId: string }) { - const innerMap = this.outerMap.get(id, { - reconstructor: UnorderedMap.reconstruct, - }); + const innerMap = this.outerMap.get(id); if (innerMap === null) { return null; } @@ -45,11 +45,9 @@ export class Contract { text: string; }) { const groupMap = this.groups.get(group, { - // reconstructor: UnorderedMap.reconstruct, defaultValue: new UnorderedMap>("g_" + group + "_"), }); const innerMap = groupMap.get(id, { - // reconstructor: UnorderedMap.reconstruct, defaultValue: new UnorderedMap("gi_" + group + "_" + id + "_"), }); innerMap.set(near.signerAccountId(), text); @@ -67,18 +65,32 @@ export class Contract { id: string; accountId: string; }) { - const groupMap = this.groups.get(group, { - // reconstructor: UnorderedMap.reconstruct, - }); + const groupMap = this.groups.get(group); if (groupMap === null) { return null; } - const innerMap = groupMap.get(id, { - // reconstructor: UnorderedMap.reconstruct, - }); + const innerMap = groupMap.get(id); if (innerMap === null) { return null; } return innerMap.get(accountId); } + + @call({}) + add_lk_set({ id }: { id: string }) { + const innerSet = this.outerLkpSet.get(id, { + defaultValue: new LookupSet("i_" + id + "_"), + }); + innerSet.set(near.signerAccountId()); + this.outerLkpSet.set(id, innerSet); + } + + @view({}) + get_lk_set({ id, accountId }: { id: string; accountId: string }) { + const innerMap = this.outerLkpSet.get(id); + if (innerMap === null) { + return null; + } + return innerMap.contains(accountId); + } } diff --git a/packages/near-sdk-js/lib/collections/lookup-map.js b/packages/near-sdk-js/lib/collections/lookup-map.js index 364049a67..467955fbc 100644 --- a/packages/near-sdk-js/lib/collections/lookup-map.js +++ b/packages/near-sdk-js/lib/collections/lookup-map.js @@ -28,7 +28,7 @@ export class LookupMap { get(key, options) { const storageKey = this.keyPrefix + key; const value = near.storageReadRaw(encode(storageKey)); - return getValueWithOptions(value, options); + return getValueWithOptions(value, options, true); } /** * Removes and retrieves the element with the provided key. diff --git a/packages/near-sdk-js/lib/near-bindgen.js b/packages/near-sdk-js/lib/near-bindgen.js index 5c351db98..a7b4792f8 100644 --- a/packages/near-sdk-js/lib/near-bindgen.js +++ b/packages/near-sdk-js/lib/near-bindgen.js @@ -81,7 +81,6 @@ export function NearBindgen({ requireInit = false, serializer = serialize, deser static _create() { return new target(); } - /// 如何return Class而不是json static _getState() { const rawState = near.storageReadRaw(bytes("STATE")); return rawState ? this._deserialize(rawState) : null; diff --git a/packages/near-sdk-js/lib/utils.js b/packages/near-sdk-js/lib/utils.js index d936be36d..5b1b8b65c 100644 --- a/packages/near-sdk-js/lib/utils.js +++ b/packages/near-sdk-js/lib/utils.js @@ -1,4 +1,5 @@ -import { UnorderedMap } from "./collections"; +import { LookupSet, UnorderedMap } from "./collections"; +import { log } from "./api"; // make PromiseIndex a nominal typing var PromiseIndexBrand; (function (PromiseIndexBrand) { @@ -50,11 +51,20 @@ export function getValueWithOptions(value, options = { return options.reconstructor(deserialized); } else if (check_reconstruct) { - if (deserialized["prefix"] && deserialized["_keys"] && deserialized["values"]) { + log("deserialized=", deserialized); + if (deserialized["prefix"] && + deserialized["_keys"] && + deserialized["values"]) { const f = UnorderedMap.reconstruct; // @ts-ignore return f(deserialized); } + else if (deserialized["keyPrefix"]) { + log("decode LookupSet"); + const f = LookupSet.reconstruct; + // @ts-ignore + return f(deserialized); + } } return deserialized; } diff --git a/packages/near-sdk-js/src/collections/lookup-map.ts b/packages/near-sdk-js/src/collections/lookup-map.ts index 8002449ec..14a552b89 100644 --- a/packages/near-sdk-js/src/collections/lookup-map.ts +++ b/packages/near-sdk-js/src/collections/lookup-map.ts @@ -38,7 +38,7 @@ export class LookupMap { const storageKey = this.keyPrefix + key; const value = near.storageReadRaw(encode(storageKey)); - return getValueWithOptions(value, options); + return getValueWithOptions(value, options, true); } /** diff --git a/packages/near-sdk-js/src/collections/unordered-map.ts b/packages/near-sdk-js/src/collections/unordered-map.ts index 3db215c61..a1f2c4ca4 100644 --- a/packages/near-sdk-js/src/collections/unordered-map.ts +++ b/packages/near-sdk-js/src/collections/unordered-map.ts @@ -12,7 +12,6 @@ import { LookupMap } from "./lookup-map"; import { GetOptions } from "../types/collections"; type ValueAndIndex = [value: string, index: number]; - /** * An unordered map that stores data in NEAR storage. */ diff --git a/packages/near-sdk-js/src/near-bindgen.ts b/packages/near-sdk-js/src/near-bindgen.ts index 932ee7f4c..614f62b90 100644 --- a/packages/near-sdk-js/src/near-bindgen.ts +++ b/packages/near-sdk-js/src/near-bindgen.ts @@ -180,7 +180,6 @@ export function NearBindgen({ return new target(); } - /// 如何return Class而不是json static _getState(): unknown | null { const rawState = near.storageReadRaw(bytes("STATE")); return rawState ? this._deserialize(rawState) : null; diff --git a/packages/near-sdk-js/src/utils.ts b/packages/near-sdk-js/src/utils.ts index 6812a80da..2205ce8b1 100644 --- a/packages/near-sdk-js/src/utils.ts +++ b/packages/near-sdk-js/src/utils.ts @@ -1,5 +1,6 @@ import { GetOptions } from "./types/collections"; -import {UnorderedMap} from "./collections"; +import { LookupSet, UnorderedMap } from "./collections"; +import { log } from "./api"; export interface Env { uint8array_to_latin1_string(a: Uint8Array): string; @@ -75,7 +76,7 @@ export function getValueWithOptions( options: Omit, "serializer"> = { deserializer: deserialize, }, - check_reconstruct?: boolean, + check_reconstruct?: boolean ): DataType | null { if (value === null) { return options?.defaultValue ?? null; @@ -90,10 +91,20 @@ export function getValueWithOptions( if (options?.reconstructor) { return options.reconstructor(deserialized); } else if (check_reconstruct) { - if (deserialized["prefix"] && deserialized["_keys"] && deserialized["values"]) { + log("deserialized=", deserialized); + if ( + deserialized["prefix"] && + deserialized["_keys"] && + deserialized["values"] + ) { const f: unknown = UnorderedMap.reconstruct; // @ts-ignore return f(deserialized) as DataType; + } else if (deserialized["keyPrefix"]) { + log("decode LookupSet"); + const f: unknown = LookupSet.reconstruct; + // @ts-ignore + return f(deserialized) as DataType; } }