diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8a9378fa4..fb28a44a8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The `VarInt16`, `VarInt32`, `VarUint16`, `VarUint32` integer serialization types: PR [#1186](https://github.com/tact-lang/tact/pull/1186)
- `unboc`: a standalone CLI utility to expose Tact's TVM disassembler: PR [#1259](https://github.com/tact-lang/tact/pull/1259)
- Added alternative parser: PR [#1258](https://github.com/tact-lang/tact/pull/1258)
+- The `fromCell` method for the `Map` type: PR [#1271](https://github.com/tact-lang/tact/pull/1271)
### Changed
diff --git a/docs/src/content/docs/book/maps.mdx b/docs/src/content/docs/book/maps.mdx
index 4183465de..6823b034d 100644
--- a/docs/src/content/docs/book/maps.mdx
+++ b/docs/src/content/docs/book/maps.mdx
@@ -360,6 +360,21 @@ contract Example {
}
```
+### Convert from a `Cell`, `.fromCell()` {#fromcell}
+
+
+
+To convert a [`Cell{:tact}`][cell] type back to a map, use the `.fromCell(){:tact}` [method](/book/functions#extension-function).
+
+```tact
+// Suppose we have a Cell
+let cell: Cell = ...;
+
+// And we want to initialize a map variable from it
+let fizz: map = emptyMap();
+fizz.fromCell(cell);
+```
+
### Traverse over entries {#traverse}
To iterate over map entries there is a [`foreach{:tact}`](/book/statements#foreach-loop) loop statement:
diff --git a/src/abi/map.ts b/src/abi/map.ts
index 2c0332b1f..122f8123c 100644
--- a/src/abi/map.ts
+++ b/src/abi/map.ts
@@ -368,6 +368,70 @@ export const MapFunctions: Map = new Map([
},
},
],
+ [
+ "fromCell",
+ {
+ name: "fromCell",
+ resolve(
+ ctx: CompilerContext,
+ args: (TypeRef | undefined)[],
+ ref: SrcInfo,
+ ) {
+ checkArgumentsLength(
+ args,
+ 2,
+ "fromCell expects one argument",
+ ref,
+ );
+
+ const [self, newCell] = args;
+ checkMapType(self, ref);
+
+ if (
+ !newCell ||
+ newCell.kind !== "ref" ||
+ newCell.name !== "Cell"
+ ) {
+ throwCompilationError(
+ "fromCell expects a Cell as second argument",
+ ref,
+ );
+ }
+
+ return { kind: "void" };
+ },
+ generate(
+ ctx: WriterContext,
+ args: (TypeRef | undefined)[],
+ exprs: AstExpression[],
+ ref: SrcInfo,
+ ) {
+ checkArgumentsLength(
+ args,
+ 2,
+ "fromCell expects one argument",
+ ref,
+ );
+
+ const [self, newCell] = args;
+ checkMapType(self, ref);
+
+ if (
+ !newCell ||
+ newCell.kind !== "ref" ||
+ newCell.name !== "Cell"
+ ) {
+ throwCompilationError(
+ "fromCell expects a Cell as second argument",
+ ref,
+ );
+ }
+
+ const resolved = exprs.map((v) => writeExpression(v, ctx));
+ return `${resolved[0]} = ${resolved[1]}`;
+ },
+ },
+ ],
[
"isEmpty",
{
diff --git a/src/test/e2e-emulated/contracts/maps1.tact b/src/test/e2e-emulated/contracts/maps1.tact
index 69b2b3aee..6e3ced595 100644
--- a/src/test/e2e-emulated/contracts/maps1.tact
+++ b/src/test/e2e-emulated/contracts/maps1.tact
@@ -549,6 +549,88 @@ message ReplaceGetAllMaps {
valueStruct: SomeStruct?;
}
+message FromCellAllMaps {
+ // Integer (`Int`) Key Maps
+ int_varint16: Cell;
+ int_varint32: Cell;
+ int_varuint16: Cell;
+ int_varuint32: Cell;
+ int_bool: Cell;
+ int_cell: Cell;
+ int_address: Cell;
+ int_struct: Cell;
+
+ // Integer (`Int as int8`) Key Maps
+ int8_varint16: Cell;
+ int8_varint32: Cell;
+ int8_varuint16: Cell;
+ int8_varuint32: Cell;
+ int8_bool: Cell;
+ int8_cell: Cell;
+ int8_address: Cell;
+ int8_struct: Cell;
+
+ // Integer (`Int as int42`) Key Maps
+ int42_varint16: Cell;
+ int42_varint32: Cell;
+ int42_varuint16: Cell;
+ int42_varuint32: Cell;
+ int42_bool: Cell;
+ int42_cell: Cell;
+ int42_address: Cell;
+ int42_struct: Cell;
+
+ // Integer (`Int as int256`) Key Maps
+ int256_varint16: Cell;
+ int256_varint32: Cell;
+ int256_varuint16: Cell;
+ int256_varuint32: Cell;
+ int256_bool: Cell;
+ int256_cell: Cell;
+ int256_address: Cell;
+ int256_struct: Cell;
+
+ // Integer (`Int as uint8`) Key Maps
+ uint8_varint16: Cell;
+ uint8_varint32: Cell;
+ uint8_varuint16: Cell;
+ uint8_varuint32: Cell;
+ uint8_bool: Cell;
+ uint8_cell: Cell;
+ uint8_address: Cell;
+ uint8_struct: Cell;
+
+ // Integer (`Int as uint42`) Key Maps
+ uint42_varint16: Cell;
+ uint42_varint32: Cell;
+ uint42_varuint16: Cell;
+ uint42_varuint32: Cell;
+ uint42_bool: Cell;
+ uint42_cell: Cell;
+ uint42_address: Cell;
+ uint42_struct: Cell;
+
+ // Integer (`Int as uint256`) Key Maps
+ uint256_varint16: Cell;
+ uint256_varint32: Cell;
+ uint256_varuint16: Cell;
+ uint256_varuint32: Cell;
+ uint256_bool: Cell;
+ uint256_cell: Cell;
+ uint256_address: Cell;
+ uint256_struct: Cell;
+
+ // Address Key Maps
+ address_varint16: Cell;
+ address_varint32: Cell;
+ address_varuint16: Cell;
+ address_varuint32: Cell;
+ address_bool: Cell;
+ address_cell: Cell;
+ address_address: Cell;
+ address_struct: Cell;
+}
+
message CheckNullReference {
}
@@ -972,6 +1054,80 @@ contract MapTestContract {
self.address_struct.replaceGet(msg.keyAddress, msg.valueStruct);
}
+ receive(msg: FromCellAllMaps) {
+ self.int_varint16.fromCell(msg.int_varint16);
+ self.int_varint32.fromCell(msg.int_varint32);
+ self.int_varuint16.fromCell(msg.int_varuint16);
+ self.int_varuint32.fromCell(msg.int_varuint32);
+ self.int_bool.fromCell(msg.int_bool);
+ self.int_cell.fromCell(msg.int_cell);
+ self.int_address.fromCell(msg.int_address);
+ self.int_struct.fromCell(msg.int_struct);
+
+ self.int8_varint16.fromCell(msg.int8_varint16);
+ self.int8_varint32.fromCell(msg.int8_varint32);
+ self.int8_varuint16.fromCell(msg.int8_varuint16);
+ self.int8_varuint32.fromCell(msg.int8_varuint32);
+ self.int8_bool.fromCell(msg.int8_bool);
+ self.int8_cell.fromCell(msg.int8_cell);
+ self.int8_address.fromCell(msg.int8_address);
+ self.int8_struct.fromCell(msg.int8_struct);
+
+ self.int42_varint16.fromCell(msg.int42_varint16);
+ self.int42_varint32.fromCell(msg.int42_varint32);
+ self.int42_varuint16.fromCell(msg.int42_varuint16);
+ self.int42_varuint32.fromCell(msg.int42_varuint32);
+ self.int42_bool.fromCell(msg.int42_bool);
+ self.int42_cell.fromCell(msg.int42_cell);
+ self.int42_address.fromCell(msg.int42_address);
+ self.int42_struct.fromCell(msg.int42_struct);
+
+ self.int256_varint16.fromCell(msg.int256_varint16);
+ self.int256_varint32.fromCell(msg.int256_varint32);
+ self.int256_varuint16.fromCell(msg.int256_varuint16);
+ self.int256_varuint32.fromCell(msg.int256_varuint32);
+ self.int256_bool.fromCell(msg.int256_bool);
+ self.int256_cell.fromCell(msg.int256_cell);
+ self.int256_address.fromCell(msg.int256_address);
+ self.int256_struct.fromCell(msg.int256_struct);
+
+ self.uint8_varint16.fromCell(msg.uint8_varint16);
+ self.uint8_varint32.fromCell(msg.uint8_varint32);
+ self.uint8_varuint16.fromCell(msg.uint8_varuint16);
+ self.uint8_varuint32.fromCell(msg.uint8_varuint32);
+ self.uint8_bool.fromCell(msg.uint8_bool);
+ self.uint8_cell.fromCell(msg.uint8_cell);
+ self.uint8_address.fromCell(msg.uint8_address);
+ self.uint8_struct.fromCell(msg.uint8_struct);
+
+ self.uint42_varint16.fromCell(msg.uint42_varint16);
+ self.uint42_varint32.fromCell(msg.uint42_varint32);
+ self.uint42_varuint16.fromCell(msg.uint42_varuint16);
+ self.uint42_varuint32.fromCell(msg.uint42_varuint32);
+ self.uint42_bool.fromCell(msg.uint42_bool);
+ self.uint42_cell.fromCell(msg.uint42_cell);
+ self.uint42_address.fromCell(msg.uint42_address);
+ self.uint42_struct.fromCell(msg.uint42_struct);
+
+ self.uint256_varint16.fromCell(msg.uint256_varint16);
+ self.uint256_varint32.fromCell(msg.uint256_varint32);
+ self.uint256_varuint16.fromCell(msg.uint256_varuint16);
+ self.uint256_varuint32.fromCell(msg.uint256_varuint32);
+ self.uint256_bool.fromCell(msg.uint256_bool);
+ self.uint256_cell.fromCell(msg.uint256_cell);
+ self.uint256_address.fromCell(msg.uint256_address);
+ self.uint256_struct.fromCell(msg.uint256_struct);
+
+ self.address_varint16.fromCell(msg.address_varint16);
+ self.address_varint32.fromCell(msg.address_varint32);
+ self.address_varuint16.fromCell(msg.address_varuint16);
+ self.address_varuint32.fromCell(msg.address_varuint32);
+ self.address_bool.fromCell(msg.address_bool);
+ self.address_cell.fromCell(msg.address_cell);
+ self.address_address.fromCell(msg.address_address);
+ self.address_struct.fromCell(msg.address_struct);
+ }
+
// ===============================
// Getters
// ===============================
diff --git a/src/test/e2e-emulated/contracts/maps2.tact b/src/test/e2e-emulated/contracts/maps2.tact
index e14b918e9..e4ea3c8ae 100644
--- a/src/test/e2e-emulated/contracts/maps2.tact
+++ b/src/test/e2e-emulated/contracts/maps2.tact
@@ -549,6 +549,88 @@ message ReplaceGetAllMaps {
valueCoins: Int?;
}
+message FromCellAllMaps {
+ // Integer (`Int`) Key Maps
+ int_int: Cell;
+ int_int8: Cell;
+ int_int42: Cell;
+ int_int256: Cell;
+ int_uint8: Cell;
+ int_uint42: Cell;
+ int_uint256: Cell;
+ int_coins: Cell;
+
+ // Integer (`Int as int8`) Key Maps
+ int8_int: Cell;
+ int8_int8: Cell;
+ int8_int42: Cell;
+ int8_int256: Cell;
+ int8_uint8: Cell;
+ int8_uint42: Cell;
+ int8_uint256: Cell;
+ int8_coins: Cell;
+
+ // Integer (`Int as int42`) Key Maps
+ int42_int: Cell;
+ int42_int8: Cell;
+ int42_int42: Cell;
+ int42_int256: Cell;
+ int42_uint8: Cell;
+ int42_uint42: Cell;
+ int42_uint256: Cell;
+ int42_coins: Cell;
+
+ // Integer (`Int as int256`) Key Maps
+ int256_int: Cell;
+ int256_int8: Cell;
+ int256_int42: Cell;
+ int256_int256: Cell;
+ int256_uint8: Cell;
+ int256_uint42: Cell;
+ int256_uint256: Cell;
+ int256_coins: Cell;
+
+ // Unsigned Integer (`Int as uint8`) Key Maps
+ uint8_int: Cell;
+ uint8_int8: Cell;
+ uint8_int42: Cell;
+ uint8_int256: Cell;
+ uint8_uint8: Cell;
+ uint8_uint42: Cell;
+ uint8_uint256: Cell;
+ uint8_coins: Cell;
+
+ // Unsigned Integer (`Int as uint42`) Key Maps
+ uint42_int: Cell;
+ uint42_int8: Cell;
+ uint42_int42: Cell;
+ uint42_int256: Cell;
+ uint42_uint8: Cell;
+ uint42_uint42: Cell;
+ uint42_uint256: Cell;
+ uint42_coins: Cell;
+
+ // Unsigned Integer (`Int as uint256`) Key Maps
+ uint256_int: Cell;
+ uint256_int8: Cell;
+ uint256_int42: Cell;
+ uint256_int256: Cell;
+ uint256_uint8: Cell;
+ uint256_uint42: Cell;
+ uint256_uint256: Cell;
+ uint256_coins: Cell;
+
+ // Address Key Maps
+ address_int: Cell;
+ address_int8: Cell;
+ address_int42: Cell;
+ address_int256: Cell;
+ address_uint8: Cell;
+ address_uint42: Cell;
+ address_uint256: Cell;
+ address_coins: Cell;
+}
+
message CheckNullReference {
}
@@ -972,6 +1054,80 @@ contract MapTestContract {
self.address_coins.replaceGet(msg.keyAddress, msg.valueCoins);
}
+ receive(msg: FromCellAllMaps) {
+ self.int_int.fromCell(msg.int_int);
+ self.int_int8.fromCell(msg.int_int8);
+ self.int_int42.fromCell(msg.int_int42);
+ self.int_int256.fromCell(msg.int_int256);
+ self.int_uint8.fromCell(msg.int_uint8);
+ self.int_uint42.fromCell(msg.int_uint42);
+ self.int_uint256.fromCell(msg.int_uint256);
+ self.int_coins.fromCell(msg.int_coins);
+
+ self.int8_int.fromCell(msg.int8_int);
+ self.int8_int8.fromCell(msg.int8_int8);
+ self.int8_int42.fromCell(msg.int8_int42);
+ self.int8_int256.fromCell(msg.int8_int256);
+ self.int8_uint8.fromCell(msg.int8_uint8);
+ self.int8_uint42.fromCell(msg.int8_uint42);
+ self.int8_uint256.fromCell(msg.int8_uint256);
+ self.int8_coins.fromCell(msg.int8_coins);
+
+ self.int42_int.fromCell(msg.int42_int);
+ self.int42_int8.fromCell(msg.int42_int8);
+ self.int42_int42.fromCell(msg.int42_int42);
+ self.int42_int256.fromCell(msg.int42_int256);
+ self.int42_uint8.fromCell(msg.int42_uint8);
+ self.int42_uint42.fromCell(msg.int42_uint42);
+ self.int42_uint256.fromCell(msg.int42_uint256);
+ self.int42_coins.fromCell(msg.int42_coins);
+
+ self.int256_int.fromCell(msg.int256_int);
+ self.int256_int8.fromCell(msg.int256_int8);
+ self.int256_int42.fromCell(msg.int256_int42);
+ self.int256_int256.fromCell(msg.int256_int256);
+ self.int256_uint8.fromCell(msg.int256_uint8);
+ self.int256_uint42.fromCell(msg.int256_uint42);
+ self.int256_uint256.fromCell(msg.int256_uint256);
+ self.int256_coins.fromCell(msg.int256_coins);
+
+ self.uint8_int.fromCell(msg.uint8_int);
+ self.uint8_int8.fromCell(msg.uint8_int8);
+ self.uint8_int42.fromCell(msg.uint8_int42);
+ self.uint8_int256.fromCell(msg.uint8_int256);
+ self.uint8_uint8.fromCell(msg.uint8_uint8);
+ self.uint8_uint42.fromCell(msg.uint8_uint42);
+ self.uint8_uint256.fromCell(msg.uint8_uint256);
+ self.uint8_coins.fromCell(msg.uint8_coins);
+
+ self.uint42_int.fromCell(msg.uint42_int);
+ self.uint42_int8.fromCell(msg.uint42_int8);
+ self.uint42_int42.fromCell(msg.uint42_int42);
+ self.uint42_int256.fromCell(msg.uint42_int256);
+ self.uint42_uint8.fromCell(msg.uint42_uint8);
+ self.uint42_uint42.fromCell(msg.uint42_uint42);
+ self.uint42_uint256.fromCell(msg.uint42_uint256);
+ self.uint42_coins.fromCell(msg.uint42_coins);
+
+ self.uint256_int.fromCell(msg.uint256_int);
+ self.uint256_int8.fromCell(msg.uint256_int8);
+ self.uint256_int42.fromCell(msg.uint256_int42);
+ self.uint256_int256.fromCell(msg.uint256_int256);
+ self.uint256_uint8.fromCell(msg.uint256_uint8);
+ self.uint256_uint42.fromCell(msg.uint256_uint42);
+ self.uint256_uint256.fromCell(msg.uint256_uint256);
+ self.uint256_coins.fromCell(msg.uint256_coins);
+
+ self.address_int.fromCell(msg.address_int);
+ self.address_int8.fromCell(msg.address_int8);
+ self.address_int42.fromCell(msg.address_int42);
+ self.address_int256.fromCell(msg.address_int256);
+ self.address_uint8.fromCell(msg.address_uint8);
+ self.address_uint42.fromCell(msg.address_uint42);
+ self.address_uint256.fromCell(msg.address_uint256);
+ self.address_coins.fromCell(msg.address_coins);
+ }
+
// ===============================
// Getters
// ===============================
diff --git a/src/test/e2e-emulated/map1.spec.ts b/src/test/e2e-emulated/map1.spec.ts
index d7a0d600e..f58984127 100644
--- a/src/test/e2e-emulated/map1.spec.ts
+++ b/src/test/e2e-emulated/map1.spec.ts
@@ -9,9 +9,17 @@ import {
SomeStruct,
ReplaceAllMaps,
ReplaceGetAllMaps,
+ storeSomeStruct,
} from "./contracts/output/maps1_MapTestContract";
import { Blockchain, SandboxContract, TreasuryContract } from "@ton/sandbox";
-import { Address, beginCell, Cell, Dictionary, toNano } from "@ton/core";
+import {
+ Address,
+ beginCell,
+ Cell,
+ Dictionary,
+ DictionaryKey,
+ toNano,
+} from "@ton/core";
import "@ton/test-utils";
// Type Guard for SomeStruct
@@ -1465,7 +1473,7 @@ describe("MapTestContract", () => {
}
});
- it.only("asCell: should correctly serialize and deserialize maps", async () => {
+ it("asCell: should correctly serialize and deserialize maps", async () => {
for (const { keys, values } of testCases) {
// Set values for the current test case
const setMessage: SetAllMaps = {
@@ -1973,4 +1981,181 @@ describe("MapTestContract", () => {
exitCode: 128,
});
});
+
+ it("fromCell: should correctly set maps from cells", async () => {
+ function getTestKey(mapName: string): bigint | Address {
+ if (mapName.startsWith("address_")) {
+ return Address.parse(
+ "UQBKgXCNLPexWhs2L79kiARR1phGH1LwXxRbNsCFF9doczSI",
+ );
+ }
+ return 1n;
+ }
+
+ function buildDictionaryCell(mapName: string): Cell {
+ let keyType: DictionaryKey;
+ if (mapName.startsWith("int8_")) {
+ keyType = Dictionary.Keys.BigInt(8);
+ } else if (mapName.startsWith("int42_")) {
+ keyType = Dictionary.Keys.BigInt(42);
+ } else if (mapName.startsWith("int256_")) {
+ keyType = Dictionary.Keys.BigInt(256);
+ } else if (mapName.startsWith("int_")) {
+ keyType = Dictionary.Keys.BigInt(257);
+ } else if (mapName.startsWith("uint8_")) {
+ keyType = Dictionary.Keys.BigUint(8);
+ } else if (mapName.startsWith("uint42_")) {
+ keyType = Dictionary.Keys.BigUint(42);
+ } else if (mapName.startsWith("uint256_")) {
+ keyType = Dictionary.Keys.BigUint(256);
+ } else if (mapName.startsWith("address_")) {
+ keyType = Dictionary.Keys.Address();
+ } else {
+ keyType = Dictionary.Keys.BigInt(257);
+ }
+
+ const [, valuePart] = mapName.split("_", 2) as [string, string];
+ const testKey = getTestKey(mapName);
+
+ let dict: Dictionary;
+
+ switch (valuePart) {
+ case "varint16":
+ dict = Dictionary.empty(
+ keyType,
+ Dictionary.Values.BigVarInt(4),
+ ).set(testKey, 999n);
+ break;
+ case "varint32":
+ dict = Dictionary.empty(
+ keyType,
+ Dictionary.Values.BigVarInt(5),
+ ).set(testKey, -123_456n);
+ break;
+ case "varuint16":
+ dict = Dictionary.empty(
+ keyType,
+ Dictionary.Values.BigVarUint(4),
+ ).set(testKey, 500n);
+ break;
+ case "varuint32":
+ dict = Dictionary.empty(
+ keyType,
+ Dictionary.Values.BigVarUint(5),
+ ).set(testKey, 999_999n);
+ break;
+ case "bool":
+ dict = Dictionary.empty(
+ keyType,
+ Dictionary.Values.Bool(),
+ ).set(testKey, true);
+ break;
+ case "cell":
+ dict = Dictionary.empty(
+ keyType,
+ Dictionary.Values.Cell(),
+ ).set(testKey, beginCell().storeUint(777, 32).endCell());
+ break;
+ case "address":
+ dict = Dictionary.empty(
+ keyType,
+ Dictionary.Values.Address(),
+ ).set(
+ testKey,
+ Address.parse(
+ "UQBKgXCNLPexWhs2L79kiARR1phGH1LwXxRbNsCFF9doczSI",
+ ),
+ );
+ break;
+ case "struct":
+ dict = Dictionary.empty(keyType, Dictionary.Values.Cell());
+ dict.set(
+ testKey,
+ beginCell()
+ .store(
+ storeSomeStruct({
+ $$type: "SomeStruct",
+ int: 123n,
+ bool: false,
+ address: randomAddress(0, "struct_addr"),
+ a: 10n,
+ b: -42n,
+ }),
+ )
+ .endCell(),
+ );
+ break;
+ default:
+ throw new Error(`Unknown value part: ${valuePart}`); // should never happen
+ }
+
+ return beginCell().storeDictDirect(dict).endCell();
+ }
+
+ // Build the message with one dictionary-cell per map in mapConfigs
+ const fromCellMessage: any = { $$type: "FromCellAllMaps" };
+ for (const { mapName } of mapConfigs) {
+ fromCellMessage[mapName] = buildDictionaryCell(mapName);
+ }
+
+ // Send message
+ const result = await contract.send(
+ treasury.getSender(),
+ { value: toNano("1") },
+ fromCellMessage,
+ );
+ expect(result.transactions).toHaveLength(2);
+ expect(result.transactions).toHaveTransaction({
+ on: contract.address,
+ success: true,
+ });
+
+ // Read maps from contract
+ const allMaps = await contract.getAllMaps();
+
+ // Verify each dictionary
+ for (const { mapName } of mapConfigs) {
+ const map = allMaps[mapName] as Dictionary;
+ expect(map.size).toBe(1);
+
+ let testKey: bigint | Address | number = getTestKey(mapName);
+ testKey =
+ testKey instanceof Address ||
+ (!mapName.startsWith("int8_") && !mapName.startsWith("uint8_"))
+ ? testKey
+ : Number(testKey);
+ const val = map.get(testKey);
+
+ // Check the stored value
+ if (mapName.endsWith("_bool")) {
+ expect(val).toBe(true);
+ } else if (mapName.endsWith("_cell")) {
+ expect(val).toEqualCell(
+ beginCell().storeUint(777, 32).endCell(),
+ );
+ } else if (mapName.endsWith("_address")) {
+ expect(val).toEqualAddress(
+ Address.parse(
+ "UQBKgXCNLPexWhs2L79kiARR1phGH1LwXxRbNsCFF9doczSI",
+ ),
+ );
+ } else if (mapName.endsWith("_struct")) {
+ expect(val).toBeTruthy();
+ if (val && typeof val === "object") {
+ expect(val.int).toBe(123n);
+ expect(val.bool).toBe(false);
+ expect(val.a).toBe(10n);
+ expect(val.b).toBe(-42n);
+ }
+ } else if (mapName.endsWith("_varint16")) {
+ expect(val).toBe(999n);
+ } else if (mapName.endsWith("_varint32")) {
+ expect(val).toBe(-123_456n);
+ } else if (mapName.endsWith("_varuint16")) {
+ expect(val).toBe(500n);
+ } else if (mapName.endsWith("_varuint32")) {
+ expect(val).toBe(999_999n);
+ }
+ }
+ });
});
diff --git a/src/test/e2e-emulated/map2.spec.ts b/src/test/e2e-emulated/map2.spec.ts
index c5375a3d6..244446ff2 100644
--- a/src/test/e2e-emulated/map2.spec.ts
+++ b/src/test/e2e-emulated/map2.spec.ts
@@ -10,7 +10,14 @@ import {
ReplaceGetAllMaps,
} from "./contracts/output/maps2_MapTestContract";
import { Blockchain, SandboxContract, TreasuryContract } from "@ton/sandbox";
-import { Address, beginCell, Dictionary, toNano } from "@ton/core";
+import {
+ Address,
+ beginCell,
+ Cell,
+ Dictionary,
+ DictionaryKey,
+ toNano,
+} from "@ton/core";
import "@ton/test-utils";
// Type definitions for keys and values to make them type-safe
@@ -1815,4 +1822,152 @@ describe("MapTestContract", () => {
exitCode: 128,
});
});
+
+ it("fromCell: should correctly set maps from cells", async () => {
+ function getTestKey(mapName: string): bigint | Address {
+ if (mapName.startsWith("address_")) {
+ return Address.parse(
+ "UQBKgXCNLPexWhs2L79kiARR1phGH1LwXxRbNsCFF9doczSI",
+ );
+ }
+ return 1n;
+ }
+
+ function buildDictionaryCell(mapName: string): Cell {
+ let keyType: DictionaryKey;
+ if (mapName.startsWith("int8_")) {
+ keyType = Dictionary.Keys.BigInt(8);
+ } else if (mapName.startsWith("int42_")) {
+ keyType = Dictionary.Keys.BigInt(42);
+ } else if (mapName.startsWith("int256_")) {
+ keyType = Dictionary.Keys.BigInt(256);
+ } else if (mapName.startsWith("int_")) {
+ keyType = Dictionary.Keys.BigInt(257);
+ } else if (mapName.startsWith("uint8_")) {
+ keyType = Dictionary.Keys.BigUint(8);
+ } else if (mapName.startsWith("uint42_")) {
+ keyType = Dictionary.Keys.BigUint(42);
+ } else if (mapName.startsWith("uint256_")) {
+ keyType = Dictionary.Keys.BigUint(256);
+ } else if (mapName.startsWith("address_")) {
+ keyType = Dictionary.Keys.Address();
+ } else {
+ keyType = Dictionary.Keys.BigInt(257);
+ }
+
+ const [, valuePart] = mapName.split("_", 2) as [string, string];
+ const testKey = getTestKey(mapName);
+
+ let dict: Dictionary;
+
+ switch (valuePart) {
+ case "int":
+ dict = Dictionary.empty(
+ keyType,
+ Dictionary.Values.BigInt(257),
+ ).set(testKey, 111n);
+ break;
+ case "int8":
+ dict = Dictionary.empty(
+ keyType,
+ Dictionary.Values.BigInt(8),
+ ).set(testKey, -10n);
+ break;
+ case "int42":
+ dict = Dictionary.empty(
+ keyType,
+ Dictionary.Values.BigInt(42),
+ ).set(testKey, 4242n);
+ break;
+ case "int256":
+ dict = Dictionary.empty(
+ keyType,
+ Dictionary.Values.BigInt(256),
+ ).set(testKey, -99999n);
+ break;
+ case "uint8":
+ dict = Dictionary.empty(
+ keyType,
+ Dictionary.Values.BigUint(8),
+ ).set(testKey, 200n);
+ break;
+ case "uint42":
+ dict = Dictionary.empty(
+ keyType,
+ Dictionary.Values.BigUint(42),
+ ).set(testKey, 424242n);
+ break;
+ case "uint256":
+ dict = Dictionary.empty(
+ keyType,
+ Dictionary.Values.BigUint(256),
+ ).set(testKey, 999999n);
+ break;
+ case "coins":
+ dict = Dictionary.empty(
+ keyType,
+ Dictionary.Values.BigVarUint(4),
+ ).set(testKey, toNano("3"));
+ break;
+ default:
+ throw new Error(`Unknown value part: ${valuePart}`); // should never happen
+ }
+
+ return beginCell().storeDictDirect(dict).endCell();
+ }
+
+ // Build the message with one dictionary-cell per map in mapConfigs
+ const fromCellMessage: any = { $$type: "FromCellAllMaps" };
+ for (const { mapName } of mapConfigs) {
+ fromCellMessage[mapName] = buildDictionaryCell(mapName);
+ }
+
+ // Send message
+ const result = await contract.send(
+ treasury.getSender(),
+ { value: toNano("1") },
+ fromCellMessage,
+ );
+ expect(result.transactions).toHaveLength(2);
+ expect(result.transactions).toHaveTransaction({
+ on: contract.address,
+ success: true,
+ });
+
+ // Read maps from contract
+ const allMaps = await contract.getAllMaps();
+
+ // Verify each dictionary
+ for (const { mapName } of mapConfigs) {
+ const map = allMaps[mapName] as Dictionary;
+ expect(map.size).toBe(1);
+
+ let testKey: bigint | Address | number = getTestKey(mapName);
+ testKey =
+ testKey instanceof Address ||
+ (!mapName.startsWith("int8_") && !mapName.startsWith("uint8_"))
+ ? testKey
+ : Number(testKey);
+ const val = map.get(testKey);
+
+ // Check the stored value
+ if (mapName.endsWith("_int")) {
+ expect(val).toBe(111n);
+ } else if (mapName.endsWith("_int8")) {
+ expect(val).toBe(-10);
+ } else if (mapName.endsWith("_int42")) {
+ expect(val).toBe(4242n);
+ } else if (mapName.endsWith("_int256")) {
+ expect(val).toBe(-99999n);
+ } else if (mapName.endsWith("_uint8")) {
+ expect(val).toBe(200);
+ } else if (mapName.endsWith("_uint42")) {
+ expect(val).toBe(424242n);
+ } else if (mapName.endsWith("_uint256")) {
+ expect(val).toBe(999999n);
+ } else if (mapName.endsWith("_coins")) {
+ expect(val).toBe(toNano("3"));
+ }
+ }
+ });
});