@proto-kit/module 0.1.1-develop.1086

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/LICENSE.md +201 -0
  2. package/README.md +114 -0
  3. package/dist/factories/MethodIdFactory.d.ts +10 -0
  4. package/dist/factories/MethodIdFactory.d.ts.map +1 -0
  5. package/dist/factories/MethodIdFactory.js +10 -0
  6. package/dist/index.d.ts +11 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +10 -0
  9. package/dist/method/MethodParameterEncoder.d.ts +24 -0
  10. package/dist/method/MethodParameterEncoder.d.ts.map +1 -0
  11. package/dist/method/MethodParameterEncoder.js +164 -0
  12. package/dist/method/runtimeMethod.d.ts +33 -0
  13. package/dist/method/runtimeMethod.d.ts.map +1 -0
  14. package/dist/method/runtimeMethod.js +167 -0
  15. package/dist/module/decorator.d.ts +8 -0
  16. package/dist/module/decorator.d.ts.map +1 -0
  17. package/dist/module/decorator.js +15 -0
  18. package/dist/runtime/MethodIdResolver.d.ts +20 -0
  19. package/dist/runtime/MethodIdResolver.d.ts.map +1 -0
  20. package/dist/runtime/MethodIdResolver.js +90 -0
  21. package/dist/runtime/Runtime.d.ts +71 -0
  22. package/dist/runtime/Runtime.d.ts.map +1 -0
  23. package/dist/runtime/Runtime.js +220 -0
  24. package/dist/runtime/RuntimeEnvironment.d.ts +10 -0
  25. package/dist/runtime/RuntimeEnvironment.d.ts.map +1 -0
  26. package/dist/runtime/RuntimeEnvironment.js +1 -0
  27. package/dist/runtime/RuntimeModule.d.ts +37 -0
  28. package/dist/runtime/RuntimeModule.d.ts.map +1 -0
  29. package/dist/runtime/RuntimeModule.js +79 -0
  30. package/dist/state/InMemoryStateService.d.ts +15 -0
  31. package/dist/state/InMemoryStateService.d.ts.map +1 -0
  32. package/dist/state/InMemoryStateService.js +23 -0
  33. package/dist/state/decorator.d.ts +7 -0
  34. package/dist/state/decorator.d.ts.map +1 -0
  35. package/dist/state/decorator.js +39 -0
  36. package/jest.config.cjs +1 -0
  37. package/package.json +35 -0
  38. package/src/factories/MethodIdFactory.ts +13 -0
  39. package/src/index.ts +10 -0
  40. package/src/method/MethodParameterEncoder.ts +252 -0
  41. package/src/method/runtimeMethod.ts +307 -0
  42. package/src/module/decorator.ts +21 -0
  43. package/src/runtime/MethodIdResolver.ts +108 -0
  44. package/src/runtime/Runtime.ts +379 -0
  45. package/src/runtime/RuntimeEnvironment.ts +16 -0
  46. package/src/runtime/RuntimeModule.ts +112 -0
  47. package/src/state/InMemoryStateService.ts +25 -0
  48. package/src/state/decorator.ts +61 -0
  49. package/test/Runtime.test.ts +70 -0
  50. package/test/TestingRuntime.ts +45 -0
  51. package/test/method/MethodParameterEncoder.test.ts +152 -0
  52. package/test/method/runtimeMethod.test.ts +46 -0
  53. package/test/modules/Admin.ts +19 -0
  54. package/test/modules/Balances.test.ts +337 -0
  55. package/test/modules/Balances.ts +54 -0
  56. package/test/modules/MethodIdResolver.test.ts +73 -0
  57. package/test/modules/State.test.ts +81 -0
  58. package/test/runtimeMethod.test.ts +215 -0
  59. package/test/tsconfig.json +7 -0
  60. package/tsconfig.json +8 -0
@@ -0,0 +1,152 @@
1
+ import {
2
+ Struct,
3
+ Field,
4
+ Bool,
5
+ PublicKey,
6
+ PrivateKey,
7
+ ZkProgram,
8
+ Proof,
9
+ } from "o1js";
10
+ import { NonMethods, noop } from "@proto-kit/common";
11
+
12
+ import {
13
+ MethodParameterEncoder,
14
+ RuntimeModule,
15
+ runtimeModule,
16
+ runtimeMethod,
17
+ } from "../../src";
18
+
19
+ class TestStruct extends Struct({
20
+ a: Field,
21
+ b: Bool,
22
+ }) {}
23
+
24
+ const TestProgram = ZkProgram({
25
+ name: "TestProgram",
26
+ publicInput: PublicKey,
27
+ publicOutput: TestStruct,
28
+ methods: {
29
+ foo: {
30
+ privateInputs: [],
31
+ method: async (input: PublicKey) => {
32
+ return {
33
+ a: Field(input.x),
34
+ b: Bool(input.isOdd),
35
+ };
36
+ },
37
+ },
38
+ },
39
+ });
40
+ class TestProof extends ZkProgram.Proof(TestProgram) {}
41
+
42
+ describe("MethodParameterEncoder", () => {
43
+ it("should en/decode Structs correctly", async () => {
44
+ expect.assertions(7);
45
+
46
+ const encoder = new MethodParameterEncoder([TestStruct]);
47
+ const { fields, auxiliary } = encoder.encode([
48
+ { a: Field(2), b: Bool(true) },
49
+ ]);
50
+
51
+ expect(auxiliary).toHaveLength(0);
52
+ expect(fields).toHaveLength(2);
53
+ expect(fields[0].toString()).toBe("2");
54
+ expect(fields[1].toString()).toStrictEqual(Bool(true).toField().toString());
55
+
56
+ const decoded = await encoder.decode(fields, auxiliary);
57
+ expect(decoded).toHaveLength(1);
58
+ const decoded1 = decoded[0] as unknown as NonMethods<TestStruct>;
59
+ expect(decoded1.a.toString()).toStrictEqual("2");
60
+ expect(decoded1.b.toString()).toStrictEqual("true");
61
+ });
62
+
63
+ it("should en/decode CircuitValues correctly", async () => {
64
+ expect.assertions(6);
65
+
66
+ const encoder = new MethodParameterEncoder([PublicKey]);
67
+ const pk = PrivateKey.random().toPublicKey();
68
+
69
+ const { fields, auxiliary } = encoder.encode([pk]);
70
+
71
+ expect(auxiliary).toHaveLength(0);
72
+ expect(fields).toHaveLength(2);
73
+ expect(fields.map((x) => x.toString())).toStrictEqual(
74
+ pk.toFields().map((x) => x.toString())
75
+ );
76
+
77
+ const decoded = await encoder.decode(fields, auxiliary);
78
+ expect(decoded).toHaveLength(1);
79
+
80
+ const decoded1 = decoded[0] as unknown as PublicKey;
81
+ expect(decoded1.x.toString()).toStrictEqual(pk.x.toString());
82
+ expect(decoded1.isOdd.toString()).toStrictEqual(pk.isOdd.toString());
83
+ });
84
+
85
+ it("should en/decode Proofs correctly", async () => {
86
+ expect.assertions(13);
87
+
88
+ const encoder = new MethodParameterEncoder([TestProof]);
89
+ const input = PrivateKey.random().toPublicKey();
90
+ const output = { a: input.x, b: input.isOdd };
91
+ const dummy = await TestProof.dummy(input, output, 0);
92
+
93
+ const { fields, auxiliary } = encoder.encode([dummy]);
94
+
95
+ expect(auxiliary).toHaveLength(1);
96
+
97
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
98
+ const json = JSON.parse(auxiliary[0]);
99
+
100
+ expect(json).toHaveProperty("maxProofsVerified");
101
+ expect(json).toHaveProperty("proof");
102
+ expect(json.maxProofsVerified).toStrictEqual(0);
103
+ expect(json.proof.length).toBeGreaterThan(20);
104
+
105
+ expect(fields).toHaveLength(4);
106
+ expect(fields.map((x) => x.toString())).toStrictEqual(
107
+ [...input.toFields(), ...TestStruct.toFields(output)].map((x) =>
108
+ x.toString()
109
+ )
110
+ );
111
+
112
+ const decoded = await encoder.decode(fields, auxiliary);
113
+ expect(decoded).toHaveLength(1);
114
+
115
+ const decoded1 = decoded[0] as unknown as Proof<PublicKey, TestStruct>;
116
+ expect(decoded1.maxProofsVerified).toStrictEqual(0);
117
+ expect(decoded1.proof).toBeDefined();
118
+ expect(decoded1.publicInput.equals(input).toBoolean()).toStrictEqual(true);
119
+ expect(decoded1.publicOutput.a.equals(output.a).toBoolean()).toStrictEqual(
120
+ true
121
+ );
122
+ expect(decoded1.publicOutput.b.equals(output.b).toBoolean()).toStrictEqual(
123
+ true
124
+ );
125
+ }, 30000);
126
+ });
127
+
128
+ class TieredStruct extends TestStruct {}
129
+
130
+ @runtimeModule()
131
+ class TestModule extends RuntimeModule {
132
+ @runtimeMethod()
133
+ public async foo(
134
+ a: TieredStruct,
135
+ b: PublicKey,
136
+ c: Field,
137
+ d: TestProof,
138
+ e: string
139
+ ) {
140
+ noop();
141
+ }
142
+ }
143
+
144
+ describe("MethodParameterEncoder construction", () => {
145
+ it("should throw on non-provable method signature", () => {
146
+ const module = new TestModule();
147
+ module.name = "testModule";
148
+ expect(() => MethodParameterEncoder.fromMethod(module, "foo")).toThrowError(
149
+ "'testModule.foo' are provable types or proofs (indizes: [4])"
150
+ );
151
+ });
152
+ });
@@ -0,0 +1,46 @@
1
+ import "reflect-metadata";
2
+
3
+ import { Bool, Field } from "o1js";
4
+ import { Option, StateTransition } from "@proto-kit/protocol";
5
+
6
+ import { toStateTransitionsHash } from "../../src/method/runtimeMethod";
7
+
8
+ describe.skip("toStateTransitionsHash", () => {
9
+ const noneStateTransition = StateTransition.from(
10
+ Field(0),
11
+ new Option(Bool(false), Field(0), Field)
12
+ );
13
+
14
+ const someStateTransition = StateTransition.from(
15
+ Field(0),
16
+ new Option(Bool(true), Field(0), Field)
17
+ );
18
+
19
+ it.each([
20
+ [
21
+ [noneStateTransition],
22
+ "7067243248312463521220230733411703436580237248681301130001246160136823979683",
23
+ ],
24
+ [
25
+ [someStateTransition],
26
+ "12841542804403638489097503092490970035615082088155587790175618374946575398395",
27
+ ],
28
+ [
29
+ [noneStateTransition, someStateTransition],
30
+ "20641278138648130746922286021889771603940136555847557324578879341962747943601",
31
+ ],
32
+ [
33
+ [someStateTransition, noneStateTransition],
34
+ "10362098987098600767020985423446775093761176563902435645494178193997179006954",
35
+ ],
36
+ ])(
37
+ "should calculate a hash of all provided state transitions",
38
+ (stateTransitions, expectedHash) => {
39
+ expect.assertions(1);
40
+
41
+ const hash = toStateTransitionsHash(stateTransitions).toString();
42
+
43
+ expect(hash).toBe(expectedHash);
44
+ }
45
+ );
46
+ });
@@ -0,0 +1,19 @@
1
+ import { PublicKey } from "o1js";
2
+ import { assert } from "@proto-kit/protocol";
3
+
4
+ import { runtimeModule } from "../../src/module/decorator.js";
5
+ import { RuntimeModule } from "../../src/runtime/RuntimeModule.js";
6
+ import { runtimeMethod } from "../../src/method/runtimeMethod.js";
7
+
8
+ interface AdminConfig {
9
+ publicKey: string;
10
+ }
11
+
12
+ @runtimeModule()
13
+ export class Admin extends RuntimeModule<AdminConfig> {
14
+ @runtimeMethod()
15
+ public async isAdmin(publicKey: PublicKey) {
16
+ const admin = PublicKey.empty<typeof PublicKey>().toConstant();
17
+ assert(admin.equals(publicKey));
18
+ }
19
+ }
@@ -0,0 +1,337 @@
1
+ import "reflect-metadata";
2
+ import { Field, Poseidon, PrivateKey, Proof, PublicKey, UInt64 } from "o1js";
3
+ import { container } from "tsyringe";
4
+ import {
5
+ type ProvableStateTransition,
6
+ Path,
7
+ MethodPublicOutput,
8
+ SimpleAsyncStateService,
9
+ RuntimeMethodExecutionContext,
10
+ RuntimeTransaction,
11
+ NetworkState,
12
+ } from "@proto-kit/protocol";
13
+
14
+ import { Runtime } from "../../src";
15
+ import { createTestingRuntime } from "../TestingRuntime";
16
+
17
+ import { Balances } from "./Balances.js";
18
+ import { Admin } from "./Admin.js";
19
+
20
+ describe("balances", () => {
21
+ let balances: Balances;
22
+
23
+ let state: SimpleAsyncStateService;
24
+
25
+ let runtime: Runtime<{
26
+ Admin: typeof Admin;
27
+ Balances: typeof Balances;
28
+ }>;
29
+
30
+ async function getStateValue(path: Field | undefined) {
31
+ if (!path) {
32
+ throw new Error("Path not found");
33
+ }
34
+
35
+ const stateValue = await state.get(path);
36
+
37
+ if (!stateValue) {
38
+ throw new Error("stateValue is undefined");
39
+ }
40
+
41
+ return stateValue;
42
+ }
43
+
44
+ async function createChain() {
45
+ ({ runtime, state } = createTestingRuntime(
46
+ {
47
+ Balances,
48
+ Admin,
49
+ },
50
+ {
51
+ Admin: {
52
+ publicKey: PublicKey.empty<typeof PublicKey>().toBase58(),
53
+ },
54
+
55
+ Balances: {},
56
+ }
57
+ ));
58
+
59
+ balances = runtime.resolve("Balances");
60
+
61
+ await state.set(balances.totalSupply.path!, UInt64.from(10).toFields());
62
+ }
63
+
64
+ describe.skip("compile and prove", () => {
65
+ beforeAll(createChain);
66
+
67
+ // Disabled until we implement a mechanism to enable/disable compiling tests
68
+ it("should compile and prove a method execution", async () => {
69
+ expect.assertions(3);
70
+
71
+ runtime.zkProgrammable.appChain?.setProofsEnabled(true);
72
+
73
+ const executionContext = container.resolve(RuntimeMethodExecutionContext);
74
+ executionContext.setup({
75
+ transaction: RuntimeTransaction.dummyTransaction(),
76
+ networkState: NetworkState.empty(),
77
+ });
78
+
79
+ const expectedStateTransitionsHash =
80
+ "1439144406936083177718146178121957896974210157062549589517697792374542035761";
81
+ const expectedStatus = true;
82
+
83
+ await runtime.zkProgrammable.zkProgram[0].compile();
84
+
85
+ await balances.getTotalSupply();
86
+
87
+ const { result } = executionContext.current();
88
+
89
+ const proof = await result.prove<Proof<undefined, MethodPublicOutput>>();
90
+
91
+ const verified = await runtime.zkProgrammable.zkProgram[0].verify(proof);
92
+
93
+ runtime.zkProgrammable.appChain?.setProofsEnabled(false);
94
+
95
+ expect(verified).toBe(true);
96
+
97
+ expect(proof.publicOutput.stateTransitionsHash.toString()).toStrictEqual(
98
+ expectedStateTransitionsHash
99
+ );
100
+ expect(proof.publicOutput.status.toBoolean()).toBe(expectedStatus);
101
+ }, 180_000);
102
+ });
103
+
104
+ describe("getTotalSupply", () => {
105
+ beforeAll(createChain);
106
+
107
+ describe("state transitions", () => {
108
+ let stateTransitions: ProvableStateTransition[];
109
+
110
+ beforeEach(async () => {
111
+ const executionContext = container.resolve(
112
+ RuntimeMethodExecutionContext
113
+ );
114
+ executionContext.setup({
115
+ transaction: RuntimeTransaction.dummyTransaction(),
116
+ networkState: NetworkState.empty(),
117
+ });
118
+ await balances.getTotalSupply();
119
+
120
+ stateTransitions = executionContext
121
+ .current()
122
+ .result.stateTransitions.map((stateTransition) =>
123
+ stateTransition.toProvable()
124
+ );
125
+ });
126
+
127
+ it("should return a single state transition", () => {
128
+ expect.assertions(1);
129
+ expect(stateTransitions).toHaveLength(1);
130
+ });
131
+
132
+ it("should have a state transition for the correct path", () => {
133
+ expect.assertions(1);
134
+
135
+ const path = Path.fromProperty("Balances", "totalSupply");
136
+
137
+ expect(stateTransitions[0].path.toString()).toStrictEqual(
138
+ path.toString()
139
+ );
140
+ });
141
+
142
+ it("should produce a from-only state transition", async () => {
143
+ expect.assertions(3);
144
+
145
+ const [stateTransition] = stateTransitions;
146
+
147
+ const value = UInt64.fromFields(
148
+ await getStateValue(balances.totalSupply.path)
149
+ );
150
+ const treeValue = Poseidon.hash(value.toFields());
151
+
152
+ expect(stateTransition.from.isSome.toBoolean()).toBe(true);
153
+ expect(stateTransition.from.value.toString()).toBe(
154
+ treeValue.toString()
155
+ );
156
+ expect(stateTransition.to.isSome.toBoolean()).toBe(false);
157
+ });
158
+ });
159
+
160
+ describe("state transitions from empty state", () => {
161
+ let stateTransitions: ProvableStateTransition[];
162
+
163
+ beforeAll(async () => {
164
+ await createChain();
165
+
166
+ await state.set(balances.totalSupply.path!, undefined);
167
+ });
168
+
169
+ beforeEach(async () => {
170
+ const executionContext = container.resolve(
171
+ RuntimeMethodExecutionContext
172
+ );
173
+ executionContext.setup({
174
+ transaction: RuntimeTransaction.dummyTransaction(),
175
+ networkState: NetworkState.empty(),
176
+ });
177
+
178
+ await balances.getTotalSupply();
179
+
180
+ stateTransitions = executionContext
181
+ .current()
182
+ .result.stateTransitions.map((stateTransition) =>
183
+ stateTransition.toProvable()
184
+ );
185
+ });
186
+
187
+ it("should return a single state transition", () => {
188
+ expect.assertions(1);
189
+ expect(stateTransitions).toHaveLength(1);
190
+ });
191
+
192
+ it("should have a state transition for the correct path", () => {
193
+ expect.assertions(1);
194
+
195
+ const path = Path.fromProperty("Balances", "totalSupply");
196
+
197
+ expect(stateTransitions[0].path.toString()).toStrictEqual(
198
+ path.toString()
199
+ );
200
+ });
201
+
202
+ it("should produce a from-only state transition", () => {
203
+ expect.assertions(3);
204
+
205
+ const [stateTransition] = stateTransitions;
206
+
207
+ const treeValue = Field(0);
208
+
209
+ expect(stateTransition.from.isSome.toBoolean()).toBe(true);
210
+ expect(stateTransition.from.value.toString()).toBe(
211
+ treeValue.toString()
212
+ );
213
+ expect(stateTransition.to.isSome.toBoolean()).toBe(false);
214
+ });
215
+ });
216
+ });
217
+
218
+ describe("setTotalSupply", () => {
219
+ beforeAll(createChain);
220
+
221
+ describe("state transitions", () => {
222
+ let stateTransitions: ProvableStateTransition[];
223
+
224
+ beforeEach(async () => {
225
+ const executionContext = container.resolve(
226
+ RuntimeMethodExecutionContext
227
+ );
228
+ executionContext.setup({
229
+ transaction: RuntimeTransaction.dummyTransaction(),
230
+ networkState: NetworkState.empty(),
231
+ });
232
+
233
+ await balances.setTotalSupply();
234
+
235
+ stateTransitions = executionContext
236
+ .current()
237
+ .result.stateTransitions.map((stateTransition) =>
238
+ stateTransition.toProvable()
239
+ );
240
+ });
241
+
242
+ it("should return a single state transition", () => {
243
+ expect.assertions(1);
244
+ expect(stateTransitions).toHaveLength(1);
245
+ });
246
+
247
+ it("should have a state transition for the correct path", () => {
248
+ expect.assertions(1);
249
+
250
+ const path = Path.fromProperty("Balances", "totalSupply");
251
+
252
+ expect(stateTransitions[0].path.toString()).toStrictEqual(
253
+ path.toString()
254
+ );
255
+ });
256
+
257
+ it("should produce a from-to state transition", async () => {
258
+ expect.assertions(4);
259
+
260
+ const [stateTransition] = stateTransitions;
261
+ const fromValue = UInt64.fromFields(
262
+ await getStateValue(balances.totalSupply.path)
263
+ );
264
+ const fromTreeValue = Poseidon.hash(fromValue.toFields());
265
+
266
+ const toValue = UInt64.from(20);
267
+ const toTreeValue = Poseidon.hash(toValue.toFields());
268
+
269
+ expect(stateTransition.from.isSome.toBoolean()).toBe(true);
270
+ expect(stateTransition.from.value.toString()).toBe(
271
+ fromTreeValue.toString()
272
+ );
273
+
274
+ expect(stateTransition.to.isSome.toBoolean()).toBe(true);
275
+ expect(stateTransition.to.value.toString()).toBe(
276
+ toTreeValue.toString()
277
+ );
278
+ });
279
+ });
280
+ });
281
+
282
+ describe("getBalance", () => {
283
+ beforeAll(createChain);
284
+
285
+ describe("state transitions", () => {
286
+ let stateTransitions: ProvableStateTransition[];
287
+ const address = PrivateKey.random().toPublicKey();
288
+
289
+ beforeEach(async () => {
290
+ const executionContext = container.resolve(
291
+ RuntimeMethodExecutionContext
292
+ );
293
+ executionContext.setup({
294
+ transaction: RuntimeTransaction.dummyTransaction(),
295
+ networkState: NetworkState.empty(),
296
+ });
297
+
298
+ await balances.getBalance(address);
299
+
300
+ stateTransitions = executionContext
301
+ .current()
302
+ .result.stateTransitions.map((stateTransition) =>
303
+ stateTransition.toProvable()
304
+ );
305
+ });
306
+
307
+ it("should return a single state transition", () => {
308
+ expect.assertions(1);
309
+ expect(stateTransitions).toHaveLength(1);
310
+ });
311
+
312
+ it("should have a state transition for the correct path", () => {
313
+ expect.assertions(1);
314
+
315
+ const path = Path.fromKey<PublicKey>(
316
+ Path.fromProperty("Balances", "balances"),
317
+ PublicKey,
318
+ address
319
+ );
320
+
321
+ expect(stateTransitions[0].path.toString()).toStrictEqual(
322
+ path.toString()
323
+ );
324
+ });
325
+
326
+ it("should produce a from-only state transition, for non-existing state", () => {
327
+ expect.assertions(3);
328
+
329
+ const [stateTransition] = stateTransitions;
330
+
331
+ expect(stateTransition.from.isSome.toBoolean()).toBe(true);
332
+ expect(stateTransition.from.value.toString()).toBe(Field(0).toString());
333
+ expect(stateTransition.to.isSome.toBoolean()).toBe(false);
334
+ });
335
+ });
336
+ });
337
+ });
@@ -0,0 +1,54 @@
1
+ import { PublicKey, UInt64 } from "o1js";
2
+ import { State, StateMap } from "@proto-kit/protocol";
3
+ import { Presets } from "@proto-kit/common";
4
+
5
+ import { RuntimeModule, runtimeMethod, runtimeModule, state } from "../../src";
6
+
7
+ import { Admin } from "./Admin.js";
8
+
9
+ interface BalancesConfig {}
10
+
11
+ @runtimeModule()
12
+ export class Balances extends RuntimeModule<BalancesConfig> {
13
+ /**
14
+ * We use `satisfies` here in order to be able to access
15
+ * presets by key in a type safe way.
16
+ */
17
+ public static presets = {} satisfies Presets<BalancesConfig>;
18
+
19
+ @state() public totalSupply = State.from<UInt64>(UInt64);
20
+
21
+ @state() public balances = StateMap.from<PublicKey, UInt64>(
22
+ PublicKey,
23
+ UInt64
24
+ );
25
+
26
+ public constructor(public admin: Admin) {
27
+ super();
28
+ }
29
+
30
+ @runtimeMethod()
31
+ public async getTotalSupply() {
32
+ await this.totalSupply.get();
33
+ }
34
+
35
+ @runtimeMethod()
36
+ public async setTotalSupply() {
37
+ await this.totalSupply.set(UInt64.from(20));
38
+ await this.admin.isAdmin(this.transaction.sender.value);
39
+ }
40
+
41
+ @runtimeMethod()
42
+ public async getBalance(address: PublicKey) {
43
+ (await this.balances.get(address)).orElse(UInt64.zero);
44
+ }
45
+
46
+ @runtimeMethod()
47
+ public async transientState() {
48
+ const totalSupply = await this.totalSupply.get();
49
+ await this.totalSupply.set(totalSupply.orElse(UInt64.zero).add(100));
50
+
51
+ const totalSupply2 = await this.totalSupply.get();
52
+ await this.totalSupply.set(totalSupply2.orElse(UInt64.zero).add(100));
53
+ }
54
+ }
@@ -0,0 +1,73 @@
1
+ import "reflect-metadata";
2
+ import { assert } from "@proto-kit/protocol";
3
+ import { Field } from "o1js";
4
+ import { beforeAll } from "@jest/globals";
5
+ import { container } from "tsyringe";
6
+
7
+ import { Runtime } from "../../src/runtime/Runtime";
8
+ import { MethodIdResolver } from "../../src/runtime/MethodIdResolver";
9
+ import { runtimeMethod, RuntimeModule, runtimeModule } from "../../src";
10
+ import { createTestingRuntime } from "../TestingRuntime";
11
+
12
+ import { Balances } from "./Balances";
13
+
14
+ interface AdminConfig {}
15
+
16
+ @runtimeModule()
17
+ class Admin extends RuntimeModule<AdminConfig> {
18
+ @runtimeMethod()
19
+ public async isAdminWithAVeryVeryVeryVeryLongName() {
20
+ assert(Field(1).equals(Field(1)));
21
+ }
22
+ }
23
+
24
+ describe("methodId", () => {
25
+ let runtime: Runtime<{ Admin: typeof Admin; Balance: typeof Balances }>;
26
+ let resolver: MethodIdResolver;
27
+
28
+ beforeAll(() => {
29
+ container.clearInstances();
30
+
31
+ ({ runtime } = createTestingRuntime(
32
+ {
33
+ Admin,
34
+ Balance: Balances,
35
+ },
36
+ {
37
+ Admin: {},
38
+ Balance: {},
39
+ }
40
+ ));
41
+
42
+ resolver =
43
+ runtime.dependencyContainer.resolve<MethodIdResolver>("MethodIdResolver");
44
+ });
45
+
46
+ it.each([
47
+ ["Admin", "isAdminWithAVeryVeryVeryVeryLongName"],
48
+ ["Balance", "getTotalSupply"],
49
+ ["Balance", "getBalance"],
50
+ ])("should pass and encode correctly", (givenModuleName, givenMethodName) => {
51
+ expect.assertions(2);
52
+
53
+ const methodId = resolver.getMethodId(givenModuleName, givenMethodName);
54
+
55
+ const [moduleName, methodName] = resolver.getMethodNameFromId(methodId) ?? [
56
+ undefined,
57
+ undefined,
58
+ ];
59
+
60
+ expect(moduleName).toBe(givenModuleName);
61
+ expect(methodName).toBe(givenMethodName);
62
+ });
63
+
64
+ it("should fail for invalid module name", () => {
65
+ expect.assertions(1);
66
+
67
+ expect(() => {
68
+ resolver.getMethodId("Admin2", "isAdminWithAVeryVeryVeryVeryLongName");
69
+ }).toThrow(
70
+ "Only known module names are allowed, using unknown module name: Admin2"
71
+ );
72
+ });
73
+ });