diff --git a/src/test/size-limits/cells.spec.ts b/src/test/size-limits/cells.spec.ts new file mode 100644 index 00000000..fb3d0bd4 --- /dev/null +++ b/src/test/size-limits/cells.spec.ts @@ -0,0 +1,53 @@ +import { toNano } from "@ton/core"; +import { Blockchain, SandboxContract, TreasuryContract } from "@ton/sandbox"; +import { MaxCellNumberTester as TestContract } from "./contracts/output/cell-number-limits_MaxCellNumberTester"; +import "@ton/test-utils"; + +// According to config param 43, the absolute max is 2^16 cells by default +// The test below is used to know what's the max for Tact contracts +describe("cell number limits", () => { + let blockchain: Blockchain; + let treasure: SandboxContract; + let contract: SandboxContract; + + beforeEach(async () => { + blockchain = await Blockchain.create(); + blockchain.verbosity.print = false; + treasure = await blockchain.treasury("treasure", { + balance: 1_000_000_000n, + resetBalanceIfZero: true, + }); + contract = blockchain.openContract(await TestContract.fromInit()); + + const deployResult = await contract.send( + treasure.getSender(), + { value: toNano("100000") }, + null, + ); + expect(deployResult.transactions).toHaveTransaction({ + from: treasure.address, + to: contract.address, + success: true, + deploy: true, + }); + }); + + it("should test cell number limits", async () => { + // TODO: a test, that adds more cells to the mix. + const sendResult = await contract.send( + treasure.getSender(), + { value: toNano("1") }, + null, // FIXME: ← placeholder, until issues with Fift decompiler of the contract are resolved + // { + // $$type: "AddCells", + // number: BigInt(16), // NOTE: adjust + // }, + ); + expect(sendResult.transactions).toHaveTransaction({ + from: treasure.address, + to: contract.address, + success: false, + actionResultCode: 50, + }); + }); +}); diff --git a/src/test/size-limits/contracts/cell-number-limits.tact b/src/test/size-limits/contracts/cell-number-limits.tact new file mode 100644 index 00000000..d681b44b --- /dev/null +++ b/src/test/size-limits/contracts/cell-number-limits.tact @@ -0,0 +1,52 @@ +// giantNestedCell +import "./giant-nested-cell.tact"; + +message AddCells { number: Int as uint8 } + +contract MaxCellNumberTester { + c1: Cell; c2: Cell; c3: Cell; c4: Cell; + c5: Cell; c6: Cell; c7: Cell; c8: Cell; + c9: Cell; c10: Cell; c11: Cell; c12: Cell; + c13: Cell; c14: Cell; c15: Cell; c16: Cell; + cExtra: Cell; + + // NOTE: for some reason, Fift's BoC cannot handle neither 2^13 nor 2^12 cells. + // It just breaks 1k cells short of deserializing each. May be an issue of this wasm fift version (we may need to upgrade it to recently released 2024.09 version) + + /// Setup + init() { + self.c1 = giantNestedCell; // 2^{12} - 2 empty cells + self.c2 = giantNestedCell; // 2^{12} - 2 empty cells + self.c3 = giantNestedCell; // 2^{12} - 2 empty cells + self.c4 = giantNestedCell; // 2^{12} - 2 empty cells + self.c5 = giantNestedCell; // 2^{12} - 2 empty cells + self.c6 = giantNestedCell; // 2^{12} - 2 empty cells + self.c7 = giantNestedCell; // 2^{12} - 2 empty cells + self.c8 = giantNestedCell; // 2^{12} - 2 empty cells + self.c9 = giantNestedCell; // 2^{12} - 2 empty cells + self.c10 = giantNestedCell; // 2^{12} - 2 empty cells + self.c11 = giantNestedCell; // 2^{12} - 2 empty cells + self.c12 = giantNestedCell; // 2^{12} - 2 empty cells + self.c13 = giantNestedCell; // 2^{12} - 2 empty cells + self.c14 = giantNestedCell; // 2^{12} - 2 empty cells + self.c15 = giantNestedCell; // 2^{12} - 2 empty cells + self.c16 = giantNestedCell; // 2^{12} - 2 empty cells + self.cExtra = emptyCell(); // 1 empty cell + // Overall: 2^{16} - 2^{4} + 1 = 65520 cells + } + + /// To handle deployment + receive() {} + + /// To add X cells + receive(msg: AddCells) { + let b = beginCell().storeRef(emptyCell()); + + // 2 in b, 1 in cExtra already, and X in msg.number, therefore: X - (2 - 1) + repeat (msg.number - 1) { + b = beginCell().storeRef(b.endCell()); + } + + self.cExtra = b.endCell(); + } +} diff --git a/src/test/size-limits/contracts/giant-nested-cell.tact b/src/test/size-limits/contracts/giant-nested-cell.tact new file mode 100644 index 00000000..5e333a8d --- /dev/null +++ b/src/test/size-limits/contracts/giant-nested-cell.tact @@ -0,0 +1 @@ +const giantNestedCell: Cell = cell("te6cckICD/4AAQAAP/YAAAEAAAEBAAACAQAAAwEAAAQBAAAFAQAABgEAAAcBAAAIAQAACQEAAAoBAAALAQAADAEAAA0BAAAOAQAADwEAABABAAARAQAAEgEAABMBAAAUAQAAFQEAABYBAAAXAQAAGAEAABkBAAAaAQAAGwEAABwBAAAdAQAAHgEAAB8BAAAgAQAAIQEAACIBAAAjAQAAJAEAACUBAAAmAQAAJwEAACgBAAApAQAAKgEAACsBAAAsAQAALQEAAC4BAAAvAQAAMAEAADEBAAAyAQAAMwEAADQBAAA1AQAANgEAADcBAAA4AQAAOQEAADoBAAA7AQAAPAEAAD0BAAA+AQAAPwEAAEABAABBAQAAQgEAAEMBAABEAQAARQEAAEYBAABHAQAASAEAAEkBAABKAQAASwEAAEwBAABNAQAATgEAAE8BAABQAQAAUQEAAFIBAABTAQAAVAEAAFUBAABWAQAAVwEAAFgBAABZAQAAWgEAAFsBAABcAQAAXQEAAF4BAABfAQAAYAEAAGEBAABiAQAAYwEAAGQBAABlAQAAZgEAAGcBAABoAQAAaQEAAGoBAABrAQAAbAEAAG0BAABuAQAAbwEAAHABAABxAQAAcgEAAHMBAAB0AQAAdQEAAHYBAAB3AQAAeAEAAHkBAAB6AQAAewEAAHwBAAB9AQAAfgEAAH8BAACAAQAAgQEAAIIBAACDAQAAhAEAAIUBAACGAQAAhwEAAIgBAACJAQAAigEAAIsBAACMAQAAjQEAAI4BAACPAQAAkAEAAJEBAACSAQAAkwEAAJQBAACVAQAAlgEAAJcBAACYAQAAmQEAAJoBAACbAQAAnAEAAJ0BAACeAQAAnwEAAKABAAChAQAAogEAAKMBAACkAQAApQEAAKYBAACnAQAAqAEAAKkBAACqAQAAqwEAAKwBAACtAQAArgEAAK8BAACwAQAAsQEAALIBAACzAQAAtAEAALUBAAC2AQAAtwEAALgBAAC5AQAAugEAALsBAAC8AQAAvQEAAL4BAAC/AQAAwAEAAMEBAADCAQAAwwEAAMQBAADFAQAAxgEAAMcBAADIAQAAyQEAAMoBAADLAQAAzAEAAM0BAADOAQAAzwEAANABAADRAQAA0gEAANMBAADUAQAA1QEAANYBAADXAQAA2AEAANkBAADaAQAA2wEAANwBAADdAQAA3gEAAN8BAADgAQAA4QEAAOIBAADjAQAA5AEAAOUBAADmAQAA5wEAAOgBAADpAQAA6gEAAOsBAADsAQAA7QEAAO4BAADvAQAA8AEAAPEBAADyAQAA8wEAAPQBAAD1AQAA9gEAAPcBAAD4AQAA+QEAAPoBAAD7AQAA/AEAAP0BAAD+AQAA/wEAAQABAAEBAQABAgEAAQMBAAEEAQABBQEAAQYBAAEHAQABCAEAAQkBAAEKAQABCwEAAQwBAAENAQABDgEAAQ8BAAEQAQABEQEAARIBAAETAQABFAEAARUBAAEWAQABFwEAARgBAAEZAQABGgEAARsBAAEcAQABHQEAAR4BAAEfAQABIAEAASEBAAEiAQABIwEAASQBAAElAQABJgEAAScBAAEoAQABKQEAASoBAAErAQABLAEAAS0BAAEuAQABLwEAATABAAExAQABMgEAATMBAAE0AQABNQEAATYBAAE3AQABOAEAATkBAAE6AQABOwEAATwBAAE9AQABPgEAAT8BAAFAAQABQQEAAUIBAAFDAQABRAEAAUUBAAFGAQABRwEAAUgBAAFJAQABSgEAAUsBAAFMAQABTQEAAU4BAAFPAQABUAEAAVEBAAFSAQABUwEAAVQBAAFVAQABVgEAAVcBAAFYAQABWQEAAVoBAAFbAQABXAEAAV0BAAFeAQABXwEAAWABAAFhAQABYgEAAWMBAAFkAQABZQEAAWYBAAFnAQABaAEAAWkBAAFqAQABawEAAWwBAAFtAQABbgEAAW8BAAFwAQABcQEAAXIBAAFzAQABdAEAAXUBAAF2AQABdwEAAXgBAAF5AQABegEAAXsBAAF8AQABfQEAAX4BAAF/AQABgAEAAYEBAAGCAQABgwEAAYQBAAGFAQABhgEAAYcBAAGIAQABiQEAAYoBAAGLAQABjAEAAY0BAAGOAQABjwEAAZABAAGRAQABkgEAAZMBAAGUAQABlQEAAZYBAAGXAQABmAEAAZkBAAGaAQABmwEAAZwBAAGdAQABngEAAZ8BAAGgAQABoQEAAaIBAAGjAQABpAEAAaUBAAGmAQABpwEAAagBAAGpAQABqgEAAasBAAGsAQABrQEAAa4BAAGvAQABsAEAAbEBAAGyAQABswEAAbQBAAG1AQABtgEAAbcBAAG4AQABuQEAAboBAAG7AQABvAEAAb0BAAG+AQABvwEAAcABAAHBAQABwgEAAcMBAAHEAQABxQEAAcYBAAHHAQAByAEAAckBAAHKAQABywEAAcwBAAHNAQABzgEAAc8BAAHQAQAB0QEAAdIBAAHTAQAB1AEAAdUBAAHWAQAB1wEAAdgBAAHZAQAB2gEAAdsBAAHcAQAB3QEAAd4BAAHfAQAB4AEAAeEBAAHiAQAB4wEAAeQBAAHlAQAB5gEAAecBAAHoAQAB6QEAAeoBAAHrAQAB7AEAAe0BAAHuAQAB7wEAAfABAAHxAQAB8gEAAfMBAAH0AQAB9QEAAfYBAAH3AQAB+AEAAfkBAAH6AQAB+wEAAfwBAAH9AQAB/gEAAf8BAAIAAQACAQEAAgIBAAIDAQACBAEAAgUBAAIGAQACBwEAAggBAAIJAQACCgEAAgsBAAIMAQACDQEAAg4BAAIPAQACEAEAAhEBAAISAQACEwEAAhQBAAIVAQACFgEAAhcBAAIYAQACGQEAAhoBAAIbAQACHAEAAh0BAAIeAQACHwEAAiABAAIhAQACIgEAAiMBAAIkAQACJQEAAiYBAAInAQACKAEAAikBAAIqAQACKwEAAiwBAAItAQACLgEAAi8BAAIwAQACMQEAAjIBAAIzAQACNAEAAjUBAAI2AQACNwEAAjgBAAI5AQACOgEAAjsBAAI8AQACPQEAAj4BAAI/AQACQAEAAkEBAAJCAQACQwEAAkQBAAJFAQACRgEAAkcBAAJIAQACSQEAAkoBAAJLAQACTAEAAk0BAAJOAQACTwEAAlABAAJRAQACUgEAAlMBAAJUAQACVQEAAlYBAAJXAQACWAEAAlkBAAJaAQACWwEAAlwBAAJdAQACXgEAAl8BAAJgAQACYQEAAmIBAAJjAQACZAEAAmUBAAJmAQACZwEAAmgBAAJpAQACagEAAmsBAAJsAQACbQEAAm4BAAJvAQACcAEAAnEBAAJyAQACcwEAAnQBAAJ1AQACdgEAAncBAAJ4AQACeQEAAnoBAAJ7AQACfAEAAn0BAAJ+AQACfwEAAoABAAKBAQACggEAAoMBAAKEAQAChQEAAoYBAAKHAQACiAEAAokBAAKKAQACiwEAAowBAAKNAQACjgEAAo8BAAKQAQACkQEAApIBAAKTAQAClAEAApUBAAKWAQAClwEAApgBAAKZAQACmgEAApsBAAKcAQACnQEAAp4BAAKfAQACoAEAAqEBAAKiAQACowEAAqQBAAKlAQACpgEAAqcBAAKoAQACqQEAAqoBAAKrAQACrAEAAq0BAAKuAQACrwEAArABAAKxAQACsgEAArMBAAK0AQACtQEAArYBAAK3AQACuAEAArkBAAK6AQACuwEAArwBAAK9AQACvgEAAr8BAALAAQACwQEAAsIBAALDAQACxAEAAsUBAALGAQACxwEAAsgBAALJAQACygEAAssBAALMAQACzQEAAs4BAALPAQAC0AEAAtEBAALSAQAC0wEAAtQBAALVAQAC1gEAAtcBAALYAQAC2QEAAtoBAALbAQAC3AEAAt0BAALeAQAC3wEAAuABAALhAQAC4gEAAuMBAALkAQAC5QEAAuYBAALnAQAC6AEAAukBAALqAQAC6wEAAuwBAALtAQAC7gEAAu8BAALwAQAC8QEAAvIBAALzAQAC9AEAAvUBAAL2AQAC9wEAAvgBAAL5AQAC+gEAAvsBAAL8AQAC/QEAAv4BAAL/AQADAAEAAwEBAAMCAQADAwEAAwQBAAMFAQADBgEAAwcBAAMIAQADCQEAAwoBAAMLAQADDAEAAw0BAAMOAQADDwEAAxABAAMRAQADEgEAAxMBAAMUAQADFQEAAxYBAAMXAQADGAEAAxkBAAMaAQADGwEAAxwBAAMdAQADHgEAAx8BAAMgAQADIQEAAyIBAAMjAQADJAEAAyUBAAMmAQADJwEAAygBAAMpAQADKgEAAysBAAMsAQADLQEAAy4BAAMvAQADMAEAAzEBAAMyAQADMwEAAzQBAAM1AQADNgEAAzcBAAM4AQADOQEAAzoBAAM7AQADPAEAAz0BAAM+AQADPwEAA0ABAANBAQADQgEAA0MBAANEAQADRQEAA0YBAANHAQADSAEAA0kBAANKAQADSwEAA0wBAANNAQADTgEAA08BAANQAQADUQEAA1IBAANTAQADVAEAA1UBAANWAQADVwEAA1gBAANZAQADWgEAA1sBAANcAQADXQEAA14BAANfAQADYAEAA2EBAANiAQADYwEAA2QBAANlAQADZgEAA2cBAANoAQADaQEAA2oBAANrAQADbAEAA20BAANuAQADbwEAA3ABAANxAQADcgEAA3MBAAN0AQADdQEAA3YBAAN3AQADeAEAA3kBAAN6AQADewEAA3wBAAN9AQADfgEAA38BAAOAAQADgQEAA4IBAAODAQADhAEAA4UBAAOGAQADhwEAA4gBAAOJAQADigEAA4sBAAOMAQADjQEAA44BAAOPAQADkAEAA5EBAAOSAQADkwEAA5QBAAOVAQADlgEAA5cBAAOYAQADmQEAA5oBAAObAQADnAEAA50BAAOeAQADnwEAA6ABAAOhAQADogEAA6MBAAOkAQADpQEAA6YBAAOnAQADqAEAA6kBAAOqAQADqwEAA6wBAAOtAQADrgEAA68BAAOwAQADsQEAA7IBAAOzAQADtAEAA7UBAAO2AQADtwEAA7gBAAO5AQADugEAA7sBAAO8AQADvQEAA74BAAO/AQADwAEAA8EBAAPCAQADwwEAA8QBAAPFAQADxgEAA8cBAAPIAQADyQEAA8oBAAPLAQADzAEAA80BAAPOAQADzwEAA9ABAAPRAQAD0gEAA9MBAAPUAQAD1QEAA9YBAAPXAQAD2AEAA9kBAAPaAQAD2wEAA9wBAAPdAQAD3gEAA98BAAPgAQAD4QEAA+IBAAPjAQAD5AEAA+UBAAPmAQAD5wEAA+gBAAPpAQAD6gEAA+sBAAPsAQAD7QEAA+4BAAPvAQAD8AEAA/EBAAPyAQAD8wEAA/QBAAP1AQAD9gEAA/cBAAP4AQAD+QEAA/oBAAP7AQAD/AEAA/0BAAP+AQAD/wEABAABAAQBAQAEAgEABAMBAAQEAQAEBQEABAYBAAQHAQAECAEABAkBAAQKAQAECwEABAwBAAQNAQAEDgEABA8BAAQQAQAEEQEABBIBAAQTAQAEFAEABBUBAAQWAQAEFwEABBgBAAQZAQAEGgEABBsBAAQcAQAEHQEABB4BAAQfAQAEIAEABCEBAAQiAQAEIwEABCQBAAQlAQAEJgEABCcBAAQoAQAEKQEABCoBAAQrAQAELAEABC0BAAQuAQAELwEABDABAAQxAQAEMgEABDMBAAQ0AQAENQEABDYBAAQ3AQAEOAEABDkBAAQ6AQAEOwEABDwBAAQ9AQAEPgEABD8BAARAAQAEQQEABEIBAARDAQAERAEABEUBAARGAQAERwEABEgBAARJAQAESgEABEsBAARMAQAETQEABE4BAARPAQAEUAEABFEBAARSAQAEUwEABFQBAARVAQAEVgEABFcBAARYAQAEWQEABFoBAARbAQAEXAEABF0BAAReAQAEXwEABGABAARhAQAEYgEABGMBAARkAQAEZQEABGYBAARnAQAEaAEABGkBAARqAQAEawEABGwBAARtAQAEbgEABG8BAARwAQAEcQEABHIBAARzAQAEdAEABHUBAAR2AQAEdwEABHgBAAR5AQAEegEABHsBAAR8AQAEfQEABH4BAAR/AQAEgAEABIEBAASCAQAEgwEABIQBAASFAQAEhgEABIcBAASIAQAEiQEABIoBAASLAQAEjAEABI0BAASOAQAEjwEABJABAASRAQAEkgEABJMBAASUAQAElQEABJYBAASXAQAEmAEABJkBAASaAQAEmwEABJwBAASdAQAEngEABJ8BAASgAQAEoQEABKIBAASjAQAEpAEABKUBAASmAQAEpwEABKgBAASpAQAEqgEABKsBAASsAQAErQEABK4BAASvAQAEsAEABLEBAASyAQAEswEABLQBAAS1AQAEtgEABLcBAAS4AQAEuQEABLoBAAS7AQAEvAEABL0BAAS+AQAEvwEABMABAATBAQAEwgEABMMBAATEAQAExQEABMYBAATHAQAEyAEABMkBAATKAQAEywEABMwBAATNAQAEzgEABM8BAATQAQAE0QEABNIBAATTAQAE1AEABNUBAATWAQAE1wEABNgBAATZAQAE2gEABNsBAATcAQAE3QEABN4BAATfAQAE4AEABOEBAATiAQAE4wEABOQBAATlAQAE5gEABOcBAAToAQAE6QEABOoBAATrAQAE7AEABO0BAATuAQAE7wEABPABAATxAQAE8gEABPMBAAT0AQAE9QEABPYBAAT3AQAE+AEABPkBAAT6AQAE+wEABPwBAAT9AQAE/gEABP8BAAUAAQAFAQEABQIBAAUDAQAFBAEABQUBAAUGAQAFBwEABQgBAAUJAQAFCgEABQsBAAUMAQAFDQEABQ4BAAUPAQAFEAEABREBAAUSAQAFEwEABRQBAAUVAQAFFgEABRcBAAUYAQAFGQEABRoBAAUbAQAFHAEABR0BAAUeAQAFHwEABSABAAUhAQAFIgEABSMBAAUkAQAFJQEABSYBAAUnAQAFKAEABSkBAAUqAQAFKwEABSwBAAUtAQAFLgEABS8BAAUwAQAFMQEABTIBAAUzAQAFNAEABTUBAAU2AQAFNwEABTgBAAU5AQAFOgEABTsBAAU8AQAFPQEABT4BAAU/AQAFQAEABUEBAAVCAQAFQwEABUQBAAVFAQAFRgEABUcBAAVIAQAFSQEABUoBAAVLAQAFTAEABU0BAAVOAQAFTwEABVABAAVRAQAFUgEABVMBAAVUAQAFVQEABVYBAAVXAQAFWAEABVkBAAVaAQAFWwEABVwBAAVdAQAFXgEABV8BAAVgAQAFYQEABWIBAAVjAQAFZAEABWUBAAVmAQAFZwEABWgBAAVpAQAFagEABWsBAAVsAQAFbQEABW4BAAVvAQAFcAEABXEBAAVyAQAFcwEABXQBAAV1AQAFdgEABXcBAAV4AQAFeQEABXoBAAV7AQAFfAEABX0BAAV+AQAFfwEABYABAAWBAQAFggEABYMBAAWEAQAFhQEABYYBAAWHAQAFiAEABYkBAAWKAQAFiwEABYwBAAWNAQAFjgEABY8BAAWQAQAFkQEABZIBAAWTAQAFlAEABZUBAAWWAQAFlwEABZgBAAWZAQAFmgEABZsBAAWcAQAFnQEABZ4BAAWfAQAFoAEABaEBAAWiAQAFowEABaQBAAWlAQAFpgEABacBAAWoAQAFqQEABaoBAAWrAQAFrAEABa0BAAWuAQAFrwEABbABAAWxAQAFsgEABbMBAAW0AQAFtQEABbYBAAW3AQAFuAEABbkBAAW6AQAFuwEABbwBAAW9AQAFvgEABb8BAAXAAQAFwQEABcIBAAXDAQAFxAEABcUBAAXGAQAFxwEABcgBAAXJAQAFygEABcsBAAXMAQAFzQEABc4BAAXPAQAF0AEABdEBAAXSAQAF0wEABdQBAAXVAQAF1gEABdcBAAXYAQAF2QEABdoBAAXbAQAF3AEABd0BAAXeAQAF3wEABeABAAXhAQAF4gEABeMBAAXkAQAF5QEABeYBAAXnAQAF6AEABekBAAXqAQAF6wEABewBAAXtAQAF7gEABe8BAAXwAQAF8QEABfIBAAXzAQAF9AEABfUBAAX2AQAF9wEABfgBAAX5AQAF+gEABfsBAAX8AQAF/QEABf4BAAX/AQAGAAEABgEBAAYCAQAGAwEABgQBAAYFAQAGBgEABgcBAAYIAQAGCQEABgoBAAYLAQAGDAEABg0BAAYOAQAGDwEABhABAAYRAQAGEgEABhMBAAYUAQAGFQEABhYBAAYXAQAGGAEABhkBAAYaAQAGGwEABhwBAAYdAQAGHgEABh8BAAYgAQAGIQEABiIBAAYjAQAGJAEABiUBAAYmAQAGJwEABigBAAYpAQAGKgEABisBAAYsAQAGLQEABi4BAAYvAQAGMAEABjEBAAYyAQAGMwEABjQBAAY1AQAGNgEABjcBAAY4AQAGOQEABjoBAAY7AQAGPAEABj0BAAY+AQAGPwEABkABAAZBAQAGQgEABkMBAAZEAQAGRQEABkYBAAZHAQAGSAEABkkBAAZKAQAGSwEABkwBAAZNAQAGTgEABk8BAAZQAQAGUQEABlIBAAZTAQAGVAEABlUBAAZWAQAGVwEABlgBAAZZAQAGWgEABlsBAAZcAQAGXQEABl4BAAZfAQAGYAEABmEBAAZiAQAGYwEABmQBAAZlAQAGZgEABmcBAAZoAQAGaQEABmoBAAZrAQAGbAEABm0BAAZuAQAGbwEABnABAAZxAQAGcgEABnMBAAZ0AQAGdQEABnYBAAZ3AQAGeAEABnkBAAZ6AQAGewEABnwBAAZ9AQAGfgEABn8BAAaAAQAGgQEABoIBAAaDAQAGhAEABoUBAAaGAQAGhwEABogBAAaJAQAGigEABosBAAaMAQAGjQEABo4BAAaPAQAGkAEABpEBAAaSAQAGkwEABpQBAAaVAQAGlgEABpcBAAaYAQAGmQEABpoBAAabAQAGnAEABp0BAAaeAQAGnwEABqABAAahAQAGogEABqMBAAakAQAGpQEABqYBAAanAQAGqAEABqkBAAaqAQAGqwEABqwBAAatAQAGrgEABq8BAAawAQAGsQEABrIBAAazAQAGtAEABrUBAAa2AQAGtwEABrgBAAa5AQAGugEABrsBAAa8AQAGvQEABr4BAAa/AQAGwAEABsEBAAbCAQAGwwEABsQBAAbFAQAGxgEABscBAAbIAQAGyQEABsoBAAbLAQAGzAEABs0BAAbOAQAGzwEABtABAAbRAQAG0gEABtMBAAbUAQAG1QEABtYBAAbXAQAG2AEABtkBAAbaAQAG2wEABtwBAAbdAQAG3gEABt8BAAbgAQAG4QEABuIBAAbjAQAG5AEABuUBAAbmAQAG5wEABugBAAbpAQAG6gEABusBAAbsAQAG7QEABu4BAAbvAQAG8AEABvEBAAbyAQAG8wEABvQBAAb1AQAG9gEABvcBAAb4AQAG+QEABvoBAAb7AQAG/AEABv0BAAb+AQAG/wEABwABAAcBAQAHAgEABwMBAAcEAQAHBQEABwYBAAcHAQAHCAEABwkBAAcKAQAHCwEABwwBAAcNAQAHDgEABw8BAAcQAQAHEQEABxIBAAcTAQAHFAEABxUBAAcWAQAHFwEABxgBAAcZAQAHGgEABxsBAAccAQAHHQEABx4BAAcfAQAHIAEAByEBAAciAQAHIwEAByQBAAclAQAHJgEABycBAAcoAQAHKQEAByoBAAcrAQAHLAEABy0BAAcuAQAHLwEABzABAAcxAQAHMgEABzMBAAc0AQAHNQEABzYBAAc3AQAHOAEABzkBAAc6AQAHOwEABzwBAAc9AQAHPgEABz8BAAdAAQAHQQEAB0IBAAdDAQAHRAEAB0UBAAdGAQAHRwEAB0gBAAdJAQAHSgEAB0sBAAdMAQAHTQEAB04BAAdPAQAHUAEAB1EBAAdSAQAHUwEAB1QBAAdVAQAHVgEAB1cBAAdYAQAHWQEAB1oBAAdbAQAHXAEAB10BAAdeAQAHXwEAB2ABAAdhAQAHYgEAB2MBAAdkAQAHZQEAB2YBAAdnAQAHaAEAB2kBAAdqAQAHawEAB2wBAAdtAQAHbgEAB28BAAdwAQAHcQEAB3IBAAdzAQAHdAEAB3UBAAd2AQAHdwEAB3gBAAd5AQAHegEAB3sBAAd8AQAHfQEAB34BAAd/AQAHgAEAB4EBAAeCAQAHgwEAB4QBAAeFAQAHhgEAB4cBAAeIAQAHiQEAB4oBAAeLAQAHjAEAB40BAAeOAQAHjwEAB5ABAAeRAQAHkgEAB5MBAAeUAQAHlQEAB5YBAAeXAQAHmAEAB5kBAAeaAQAHmwEAB5wBAAedAQAHngEAB58BAAegAQAHoQEAB6IBAAejAQAHpAEAB6UBAAemAQAHpwEAB6gBAAepAQAHqgEAB6sBAAesAQAHrQEAB64BAAevAQAHsAEAB7EBAAeyAQAHswEAB7QBAAe1AQAHtgEAB7cBAAe4AQAHuQEAB7oBAAe7AQAHvAEAB70BAAe+AQAHvwEAB8ABAAfBAQAHwgEAB8MBAAfEAQAHxQEAB8YBAAfHAQAHyAEAB8kBAAfKAQAHywEAB8wBAAfNAQAHzgEAB88BAAfQAQAH0QEAB9IBAAfTAQAH1AEAB9UBAAfWAQAH1wEAB9gBAAfZAQAH2gEAB9sBAAfcAQAH3QEAB94BAAffAQAH4AEAB+EBAAfiAQAH4wEAB+QBAAflAQAH5gEAB+cBAAfoAQAH6QEAB+oBAAfrAQAH7AEAB+0BAAfuAQAH7wEAB/ABAAfxAQAH8gEAB/MBAAf0AQAH9QEAB/YBAAf3AQAH+AEAB/kBAAf6AQAH+wEAB/wBAAf9AQAH/gEAB/8BAAgAAQAIAQEACAIBAAgDAQAIBAEACAUBAAgGAQAIBwEACAgBAAgJAQAICgEACAsBAAgMAQAIDQEACA4BAAgPAQAIEAEACBEBAAgSAQAIEwEACBQBAAgVAQAIFgEACBcBAAgYAQAIGQEACBoBAAgbAQAIHAEACB0BAAgeAQAIHwEACCABAAghAQAIIgEACCMBAAgkAQAIJQEACCYBAAgnAQAIKAEACCkBAAgqAQAIKwEACCwBAAgtAQAILgEACC8BAAgwAQAIMQEACDIBAAgzAQAINAEACDUBAAg2AQAINwEACDgBAAg5AQAIOgEACDsBAAg8AQAIPQEACD4BAAg/AQAIQAEACEEBAAhCAQAIQwEACEQBAAhFAQAIRgEACEcBAAhIAQAISQEACEoBAAhLAQAITAEACE0BAAhOAQAITwEACFABAAhRAQAIUgEACFMBAAhUAQAIVQEACFYBAAhXAQAIWAEACFkBAAhaAQAIWwEACFwBAAhdAQAIXgEACF8BAAhgAQAIYQEACGIBAAhjAQAIZAEACGUBAAhmAQAIZwEACGgBAAhpAQAIagEACGsBAAhsAQAIbQEACG4BAAhvAQAIcAEACHEBAAhyAQAIcwEACHQBAAh1AQAIdgEACHcBAAh4AQAIeQEACHoBAAh7AQAIfAEACH0BAAh+AQAIfwEACIABAAiBAQAIggEACIMBAAiEAQAIhQEACIYBAAiHAQAIiAEACIkBAAiKAQAIiwEACIwBAAiNAQAIjgEACI8BAAiQAQAIkQEACJIBAAiTAQAIlAEACJUBAAiWAQAIlwEACJgBAAiZAQAImgEACJsBAAicAQAInQEACJ4BAAifAQAIoAEACKEBAAiiAQAIowEACKQBAAilAQAIpgEACKcBAAioAQAIqQEACKoBAAirAQAIrAEACK0BAAiuAQAIrwEACLABAAixAQAIsgEACLMBAAi0AQAItQEACLYBAAi3AQAIuAEACLkBAAi6AQAIuwEACLwBAAi9AQAIvgEACL8BAAjAAQAIwQEACMIBAAjDAQAIxAEACMUBAAjGAQAIxwEACMgBAAjJAQAIygEACMsBAAjMAQAIzQEACM4BAAjPAQAI0AEACNEBAAjSAQAI0wEACNQBAAjVAQAI1gEACNcBAAjYAQAI2QEACNoBAAjbAQAI3AEACN0BAAjeAQAI3wEACOABAAjhAQAI4gEACOMBAAjkAQAI5QEACOYBAAjnAQAI6AEACOkBAAjqAQAI6wEACOwBAAjtAQAI7gEACO8BAAjwAQAI8QEACPIBAAjzAQAI9AEACPUBAAj2AQAI9wEACPgBAAj5AQAI+gEACPsBAAj8AQAI/QEACP4BAAj/AQAJAAEACQEBAAkCAQAJAwEACQQBAAkFAQAJBgEACQcBAAkIAQAJCQEACQoBAAkLAQAJDAEACQ0BAAkOAQAJDwEACRABAAkRAQAJEgEACRMBAAkUAQAJFQEACRYBAAkXAQAJGAEACRkBAAkaAQAJGwEACRwBAAkdAQAJHgEACR8BAAkgAQAJIQEACSIBAAkjAQAJJAEACSUBAAkmAQAJJwEACSgBAAkpAQAJKgEACSsBAAksAQAJLQEACS4BAAkvAQAJMAEACTEBAAkyAQAJMwEACTQBAAk1AQAJNgEACTcBAAk4AQAJOQEACToBAAk7AQAJPAEACT0BAAk+AQAJPwEACUABAAlBAQAJQgEACUMBAAlEAQAJRQEACUYBAAlHAQAJSAEACUkBAAlKAQAJSwEACUwBAAlNAQAJTgEACU8BAAlQAQAJUQEACVIBAAlTAQAJVAEACVUBAAlWAQAJVwEACVgBAAlZAQAJWgEACVsBAAlcAQAJXQEACV4BAAlfAQAJYAEACWEBAAliAQAJYwEACWQBAAllAQAJZgEACWcBAAloAQAJaQEACWoBAAlrAQAJbAEACW0BAAluAQAJbwEACXABAAlxAQAJcgEACXMBAAl0AQAJdQEACXYBAAl3AQAJeAEACXkBAAl6AQAJewEACXwBAAl9AQAJfgEACX8BAAmAAQAJgQEACYIBAAmDAQAJhAEACYUBAAmGAQAJhwEACYgBAAmJAQAJigEACYsBAAmMAQAJjQEACY4BAAmPAQAJkAEACZEBAAmSAQAJkwEACZQBAAmVAQAJlgEACZcBAAmYAQAJmQEACZoBAAmbAQAJnAEACZ0BAAmeAQAJnwEACaABAAmhAQAJogEACaMBAAmkAQAJpQEACaYBAAmnAQAJqAEACakBAAmqAQAJqwEACawBAAmtAQAJrgEACa8BAAmwAQAJsQEACbIBAAmzAQAJtAEACbUBAAm2AQAJtwEACbgBAAm5AQAJugEACbsBAAm8AQAJvQEACb4BAAm/AQAJwAEACcEBAAnCAQAJwwEACcQBAAnFAQAJxgEACccBAAnIAQAJyQEACcoBAAnLAQAJzAEACc0BAAnOAQAJzwEACdABAAnRAQAJ0gEACdMBAAnUAQAJ1QEACdYBAAnXAQAJ2AEACdkBAAnaAQAJ2wEACdwBAAndAQAJ3gEACd8BAAngAQAJ4QEACeIBAAnjAQAJ5AEACeUBAAnmAQAJ5wEACegBAAnpAQAJ6gEACesBAAnsAQAJ7QEACe4BAAnvAQAJ8AEACfEBAAnyAQAJ8wEACfQBAAn1AQAJ9gEACfcBAAn4AQAJ+QEACfoBAAn7AQAJ/AEACf0BAAn+AQAJ/wEACgABAAoBAQAKAgEACgMBAAoEAQAKBQEACgYBAAoHAQAKCAEACgkBAAoKAQAKCwEACgwBAAoNAQAKDgEACg8BAAoQAQAKEQEAChIBAAoTAQAKFAEAChUBAAoWAQAKFwEAChgBAAoZAQAKGgEAChsBAAocAQAKHQEACh4BAAofAQAKIAEACiEBAAoiAQAKIwEACiQBAAolAQAKJgEACicBAAooAQAKKQEACioBAAorAQAKLAEACi0BAAouAQAKLwEACjABAAoxAQAKMgEACjMBAAo0AQAKNQEACjYBAAo3AQAKOAEACjkBAAo6AQAKOwEACjwBAAo9AQAKPgEACj8BAApAAQAKQQEACkIBAApDAQAKRAEACkUBAApGAQAKRwEACkgBAApJAQAKSgEACksBAApMAQAKTQEACk4BAApPAQAKUAEAClEBAApSAQAKUwEAClQBAApVAQAKVgEAClcBAApYAQAKWQEACloBAApbAQAKXAEACl0BAApeAQAKXwEACmABAAphAQAKYgEACmMBAApkAQAKZQEACmYBAApnAQAKaAEACmkBAApqAQAKawEACmwBAAptAQAKbgEACm8BAApwAQAKcQEACnIBAApzAQAKdAEACnUBAAp2AQAKdwEACngBAAp5AQAKegEACnsBAAp8AQAKfQEACn4BAAp/AQAKgAEACoEBAAqCAQAKgwEACoQBAAqFAQAKhgEACocBAAqIAQAKiQEACooBAAqLAQAKjAEACo0BAAqOAQAKjwEACpABAAqRAQAKkgEACpMBAAqUAQAKlQEACpYBAAqXAQAKmAEACpkBAAqaAQAKmwEACpwBAAqdAQAKngEACp8BAAqgAQAKoQEACqIBAAqjAQAKpAEACqUBAAqmAQAKpwEACqgBAAqpAQAKqgEACqsBAAqsAQAKrQEACq4BAAqvAQAKsAEACrEBAAqyAQAKswEACrQBAAq1AQAKtgEACrcBAAq4AQAKuQEACroBAAq7AQAKvAEACr0BAAq+AQAKvwEACsABAArBAQAKwgEACsMBAArEAQAKxQEACsYBAArHAQAKyAEACskBAArKAQAKywEACswBAArNAQAKzgEACs8BAArQAQAK0QEACtIBAArTAQAK1AEACtUBAArWAQAK1wEACtgBAArZAQAK2gEACtsBAArcAQAK3QEACt4BAArfAQAK4AEACuEBAAriAQAK4wEACuQBAArlAQAK5gEACucBAAroAQAK6QEACuoBAArrAQAK7AEACu0BAAruAQAK7wEACvABAArxAQAK8gEACvMBAAr0AQAK9QEACvYBAAr3AQAK+AEACvkBAAr6AQAK+wEACvwBAAr9AQAK/gEACv8BAAsAAQALAQEACwIBAAsDAQALBAEACwUBAAsGAQALBwEACwgBAAsJAQALCgEACwsBAAsMAQALDQEACw4BAAsPAQALEAEACxEBAAsSAQALEwEACxQBAAsVAQALFgEACxcBAAsYAQALGQEACxoBAAsbAQALHAEACx0BAAseAQALHwEACyABAAshAQALIgEACyMBAAskAQALJQEACyYBAAsnAQALKAEACykBAAsqAQALKwEACywBAAstAQALLgEACy8BAAswAQALMQEACzIBAAszAQALNAEACzUBAAs2AQALNwEACzgBAAs5AQALOgEACzsBAAs8AQALPQEACz4BAAs/AQALQAEAC0EBAAtCAQALQwEAC0QBAAtFAQALRgEAC0cBAAtIAQALSQEAC0oBAAtLAQALTAEAC00BAAtOAQALTwEAC1ABAAtRAQALUgEAC1MBAAtUAQALVQEAC1YBAAtXAQALWAEAC1kBAAtaAQALWwEAC1wBAAtdAQALXgEAC18BAAtgAQALYQEAC2IBAAtjAQALZAEAC2UBAAtmAQALZwEAC2gBAAtpAQALagEAC2sBAAtsAQALbQEAC24BAAtvAQALcAEAC3EBAAtyAQALcwEAC3QBAAt1AQALdgEAC3cBAAt4AQALeQEAC3oBAAt7AQALfAEAC30BAAt+AQALfwEAC4ABAAuBAQALggEAC4MBAAuEAQALhQEAC4YBAAuHAQALiAEAC4kBAAuKAQALiwEAC4wBAAuNAQALjgEAC48BAAuQAQALkQEAC5IBAAuTAQALlAEAC5UBAAuWAQALlwEAC5gBAAuZAQALmgEAC5sBAAucAQALnQEAC54BAAufAQALoAEAC6EBAAuiAQALowEAC6QBAAulAQALpgEAC6cBAAuoAQALqQEAC6oBAAurAQALrAEAC60BAAuuAQALrwEAC7ABAAuxAQALsgEAC7MBAAu0AQALtQEAC7YBAAu3AQALuAEAC7kBAAu6AQALuwEAC7wBAAu9AQALvgEAC78BAAvAAQALwQEAC8IBAAvDAQALxAEAC8UBAAvGAQALxwEAC8gBAAvJAQALygEAC8sBAAvMAQALzQEAC84BAAvPAQAL0AEAC9EBAAvSAQAL0wEAC9QBAAvVAQAL1gEAC9cBAAvYAQAL2QEAC9oBAAvbAQAL3AEAC90BAAveAQAL3wEAC+ABAAvhAQAL4gEAC+MBAAvkAQAL5QEAC+YBAAvnAQAL6AEAC+kBAAvqAQAL6wEAC+wBAAvtAQAL7gEAC+8BAAvwAQAL8QEAC/IBAAvzAQAL9AEAC/UBAAv2AQAL9wEAC/gBAAv5AQAL+gEAC/sBAAv8AQAL/QEAC/4BAAv/AQAMAAEADAEBAAwCAQAMAwEADAQBAAwFAQAMBgEADAcBAAwIAQAMCQEADAoBAAwLAQAMDAEADA0BAAwOAQAMDwEADBABAAwRAQAMEgEADBMBAAwUAQAMFQEADBYBAAwXAQAMGAEADBkBAAwaAQAMGwEADBwBAAwdAQAMHgEADB8BAAwgAQAMIQEADCIBAAwjAQAMJAEADCUBAAwmAQAMJwEADCgBAAwpAQAMKgEADCsBAAwsAQAMLQEADC4BAAwvAQAMMAEADDEBAAwyAQAMMwEADDQBAAw1AQAMNgEADDcBAAw4AQAMOQEADDoBAAw7AQAMPAEADD0BAAw+AQAMPwEADEABAAxBAQAMQgEADEMBAAxEAQAMRQEADEYBAAxHAQAMSAEADEkBAAxKAQAMSwEADEwBAAxNAQAMTgEADE8BAAxQAQAMUQEADFIBAAxTAQAMVAEADFUBAAxWAQAMVwEADFgBAAxZAQAMWgEADFsBAAxcAQAMXQEADF4BAAxfAQAMYAEADGEBAAxiAQAMYwEADGQBAAxlAQAMZgEADGcBAAxoAQAMaQEADGoBAAxrAQAMbAEADG0BAAxuAQAMbwEADHABAAxxAQAMcgEADHMBAAx0AQAMdQEADHYBAAx3AQAMeAEADHkBAAx6AQAMewEADHwBAAx9AQAMfgEADH8BAAyAAQAMgQEADIIBAAyDAQAMhAEADIUBAAyGAQAMhwEADIgBAAyJAQAMigEADIsBAAyMAQAMjQEADI4BAAyPAQAMkAEADJEBAAySAQAMkwEADJQBAAyVAQAMlgEADJcBAAyYAQAMmQEADJoBAAybAQAMnAEADJ0BAAyeAQAMnwEADKABAAyhAQAMogEADKMBAAykAQAMpQEADKYBAAynAQAMqAEADKkBAAyqAQAMqwEADKwBAAytAQAMrgEADK8BAAywAQAMsQEADLIBAAyzAQAMtAEADLUBAAy2AQAMtwEADLgBAAy5AQAMugEADLsBAAy8AQAMvQEADL4BAAy/AQAMwAEADMEBAAzCAQAMwwEADMQBAAzFAQAMxgEADMcBAAzIAQAMyQEADMoBAAzLAQAMzAEADM0BAAzOAQAMzwEADNABAAzRAQAM0gEADNMBAAzUAQAM1QEADNYBAAzXAQAM2AEADNkBAAzaAQAM2wEADNwBAAzdAQAM3gEADN8BAAzgAQAM4QEADOIBAAzjAQAM5AEADOUBAAzmAQAM5wEADOgBAAzpAQAM6gEADOsBAAzsAQAM7QEADO4BAAzvAQAM8AEADPEBAAzyAQAM8wEADPQBAAz1AQAM9gEADPcBAAz4AQAM+QEADPoBAAz7AQAM/AEADP0BAAz+AQAM/wEADQABAA0BAQANAgEADQMBAA0EAQANBQEADQYBAA0HAQANCAEADQkBAA0KAQANCwEADQwBAA0NAQANDgEADQ8BAA0QAQANEQEADRIBAA0TAQANFAEADRUBAA0WAQANFwEADRgBAA0ZAQANGgEADRsBAA0cAQANHQEADR4BAA0fAQANIAEADSEBAA0iAQANIwEADSQBAA0lAQANJgEADScBAA0oAQANKQEADSoBAA0rAQANLAEADS0BAA0uAQANLwEADTABAA0xAQANMgEADTMBAA00AQANNQEADTYBAA03AQANOAEADTkBAA06AQANOwEADTwBAA09AQANPgEADT8BAA1AAQANQQEADUIBAA1DAQANRAEADUUBAA1GAQANRwEADUgBAA1JAQANSgEADUsBAA1MAQANTQEADU4BAA1PAQANUAEADVEBAA1SAQANUwEADVQBAA1VAQANVgEADVcBAA1YAQANWQEADVoBAA1bAQANXAEADV0BAA1eAQANXwEADWABAA1hAQANYgEADWMBAA1kAQANZQEADWYBAA1nAQANaAEADWkBAA1qAQANawEADWwBAA1tAQANbgEADW8BAA1wAQANcQEADXIBAA1zAQANdAEADXUBAA12AQANdwEADXgBAA15AQANegEADXsBAA18AQANfQEADX4BAA1/AQANgAEADYEBAA2CAQANgwEADYQBAA2FAQANhgEADYcBAA2IAQANiQEADYoBAA2LAQANjAEADY0BAA2OAQANjwEADZABAA2RAQANkgEADZMBAA2UAQANlQEADZYBAA2XAQANmAEADZkBAA2aAQANmwEADZwBAA2dAQANngEADZ8BAA2gAQANoQEADaIBAA2jAQANpAEADaUBAA2mAQANpwEADagBAA2pAQANqgEADasBAA2sAQANrQEADa4BAA2vAQANsAEADbEBAA2yAQANswEADbQBAA21AQANtgEADbcBAA24AQANuQEADboBAA27AQANvAEADb0BAA2+AQANvwEADcABAA3BAQANwgEADcMBAA3EAQANxQEADcYBAA3HAQANyAEADckBAA3KAQANywEADcwBAA3NAQANzgEADc8BAA3QAQAN0QEADdIBAA3TAQAN1AEADdUBAA3WAQAN1wEADdgBAA3ZAQAN2gEADdsBAA3cAQAN3QEADd4BAA3fAQAN4AEADeEBAA3iAQAN4wEADeQBAA3lAQAN5gEADecBAA3oAQAN6QEADeoBAA3rAQAN7AEADe0BAA3uAQAN7wEADfABAA3xAQAN8gEADfMBAA30AQAN9QEADfYBAA33AQAN+AEADfkBAA36AQAN+wEADfwBAA39AQAN/gEADf8BAA4AAQAOAQEADgIBAA4DAQAOBAEADgUBAA4GAQAOBwEADggBAA4JAQAOCgEADgsBAA4MAQAODQEADg4BAA4PAQAOEAEADhEBAA4SAQAOEwEADhQBAA4VAQAOFgEADhcBAA4YAQAOGQEADhoBAA4bAQAOHAEADh0BAA4eAQAOHwEADiABAA4hAQAOIgEADiMBAA4kAQAOJQEADiYBAA4nAQAOKAEADikBAA4qAQAOKwEADiwBAA4tAQAOLgEADi8BAA4wAQAOMQEADjIBAA4zAQAONAEADjUBAA42AQAONwEADjgBAA45AQAOOgEADjsBAA48AQAOPQEADj4BAA4/AQAOQAEADkEBAA5CAQAOQwEADkQBAA5FAQAORgEADkcBAA5IAQAOSQEADkoBAA5LAQAOTAEADk0BAA5OAQAOTwEADlABAA5RAQAOUgEADlMBAA5UAQAOVQEADlYBAA5XAQAOWAEADlkBAA5aAQAOWwEADlwBAA5dAQAOXgEADl8BAA5gAQAOYQEADmIBAA5jAQAOZAEADmUBAA5mAQAOZwEADmgBAA5pAQAOagEADmsBAA5sAQAObQEADm4BAA5vAQAOcAEADnEBAA5yAQAOcwEADnQBAA51AQAOdgEADncBAA54AQAOeQEADnoBAA57AQAOfAEADn0BAA5+AQAOfwEADoABAA6BAQAOggEADoMBAA6EAQAOhQEADoYBAA6HAQAOiAEADokBAA6KAQAOiwEADowBAA6NAQAOjgEADo8BAA6QAQAOkQEADpIBAA6TAQAOlAEADpUBAA6WAQAOlwEADpgBAA6ZAQAOmgEADpsBAA6cAQAOnQEADp4BAA6fAQAOoAEADqEBAA6iAQAOowEADqQBAA6lAQAOpgEADqcBAA6oAQAOqQEADqoBAA6rAQAOrAEADq0BAA6uAQAOrwEADrABAA6xAQAOsgEADrMBAA60AQAOtQEADrYBAA63AQAOuAEADrkBAA66AQAOuwEADrwBAA69AQAOvgEADr8BAA7AAQAOwQEADsIBAA7DAQAOxAEADsUBAA7GAQAOxwEADsgBAA7JAQAOygEADssBAA7MAQAOzQEADs4BAA7PAQAO0AEADtEBAA7SAQAO0wEADtQBAA7VAQAO1gEADtcBAA7YAQAO2QEADtoBAA7bAQAO3AEADt0BAA7eAQAO3wEADuABAA7hAQAO4gEADuMBAA7kAQAO5QEADuYBAA7nAQAO6AEADukBAA7qAQAO6wEADuwBAA7tAQAO7gEADu8BAA7wAQAO8QEADvIBAA7zAQAO9AEADvUBAA72AQAO9wEADvgBAA75AQAO+gEADvsBAA78AQAO/QEADv4BAA7/AQAPAAEADwEBAA8CAQAPAwEADwQBAA8FAQAPBgEADwcBAA8IAQAPCQEADwoBAA8LAQAPDAEADw0BAA8OAQAPDwEADxABAA8RAQAPEgEADxMBAA8UAQAPFQEADxYBAA8XAQAPGAEADxkBAA8aAQAPGwEADxwBAA8dAQAPHgEADx8BAA8gAQAPIQEADyIBAA8jAQAPJAEADyUBAA8mAQAPJwEADygBAA8pAQAPKgEADysBAA8sAQAPLQEADy4BAA8vAQAPMAEADzEBAA8yAQAPMwEADzQBAA81AQAPNgEADzcBAA84AQAPOQEADzoBAA87AQAPPAEADz0BAA8+AQAPPwEAD0ABAA9BAQAPQgEAD0MBAA9EAQAPRQEAD0YBAA9HAQAPSAEAD0kBAA9KAQAPSwEAD0wBAA9NAQAPTgEAD08BAA9QAQAPUQEAD1IBAA9TAQAPVAEAD1UBAA9WAQAPVwEAD1gBAA9ZAQAPWgEAD1sBAA9cAQAPXQEAD14BAA9fAQAPYAEAD2EBAA9iAQAPYwEAD2QBAA9lAQAPZgEAD2cBAA9oAQAPaQEAD2oBAA9rAQAPbAEAD20BAA9uAQAPbwEAD3ABAA9xAQAPcgEAD3MBAA90AQAPdQEAD3YBAA93AQAPeAEAD3kBAA96AQAPewEAD3wBAA99AQAPfgEAD38BAA+AAQAPgQEAD4IBAA+DAQAPhAEAD4UBAA+GAQAPhwEAD4gBAA+JAQAPigEAD4sBAA+MAQAPjQEAD44BAA+PAQAPkAEAD5EBAA+SAQAPkwEAD5QBAA+VAQAPlgEAD5cBAA+YAQAPmQEAD5oBAA+bAQAPnAEAD50BAA+eAQAPnwEAD6ABAA+hAQAPogEAD6MBAA+kAQAPpQEAD6YBAA+nAQAPqAEAD6kBAA+qAQAPqwEAD6wBAA+tAQAPrgEAD68BAA+wAQAPsQEAD7IBAA+zAQAPtAEAD7UBAA+2AQAPtwEAD7gBAA+5AQAPugEAD7sBAA+8AQAPvQEAD74BAA+/AQAPwAEAD8EBAA/CAQAPwwEAD8QBAA/FAQAPxgEAD8cBAA/IAQAPyQEAD8oBAA/LAQAPzAEAD80BAA/OAQAPzwEAD9ABAA/RAQAP0gEAD9MBAA/UAQAP1QEAD9YBAA/XAQAP2AEAD9kBAA/aAQAP2wEAD9wBAA/dAQAP3gEAD98BAA/gAQAP4QEAD+IBAA/jAQAP5AEAD+UBAA/mAQAP5wEAD+gBAA/pAQAP6gEAD+sBAA/sAQAP7QEAD+4BAA/vAQAP8AEAD/EBAA/yAQAP8wEAD/QBAA/1AQAP9gEAD/cBAA/4AQAP+QEAD/oBAA/7AQAP/AEAD/0AAN6YSVM="); diff --git a/src/test/size-limits/contracts/map-size-limits.tact b/src/test/size-limits/contracts/map-size-limits.tact new file mode 100644 index 00000000..d912a5f1 --- /dev/null +++ b/src/test/size-limits/contracts/map-size-limits.tact @@ -0,0 +1,165 @@ +// +// map +// + +message AddIntInt { + batchSize: Int as uint8; + startingValue: Int as uint16; +} + +contract MapIntInt { + /// Target map + m: map; + + /// To handle deployment + receive() {} + + /// To add an item + receive(msg: AddIntInt) { + let curVal = msg.startingValue; + repeat (msg.batchSize) { + self.m.set(curVal, curVal); + curVal += 1; + } + } +} + +// +// map +// + +message AddIntBool { + batchSize: Int as uint8; + startingKey: Int; +} + +contract MapIntBool { + /// Target map + m: map; + + /// To handle deployment + receive() {} + + /// To add an item + receive(msg: AddIntBool) { + let curVal = msg.startingKey; + repeat (msg.batchSize) { + self.m.set(curVal, false); + curVal += 1; + } + } +} + +// +// map +// + +message AddIntCell { + batchSize: Int as uint8; + startingKey: Int; +} + +contract MapIntCell { + /// Target map + m: map; + + /// To handle deployment + receive() {} + + /// To add an item + receive(msg: AddIntCell) { + let curVal = msg.startingKey; + repeat (msg.batchSize) { + self.m.set(curVal, emptyCell()); + curVal += 1; + } + } +} + +// +// map +// + +message AddIntAddress { + batchSize: Int as uint8; + startingKey: Int; +} + +contract MapIntAddress { + /// Target map + m: map; + + /// To handle deployment + receive() {} + + /// To add an item + receive(msg: AddIntAddress) { + let curVal = msg.startingKey; + let myAddr = myAddress(); // TODO: different addresses + repeat (msg.batchSize) { + self.m.set(curVal, myAddr); + curVal += 1; + } + } +} + +// +// map +// + +message AddIntStruct { + batchSize: Int as uint8; + startingKey: Int; +} + +struct BoolBool { yes: Bool } + +contract MapIntStruct { + /// Target map + m: map; + + /// To handle deployment + receive() {} + + /// To add an item + receive(msg: AddIntStruct) { + let curVal = msg.startingKey; + let stBool = BoolBool{ yes: true }; + repeat (msg.batchSize) { + self.m.set(curVal, stBool); + curVal += 1; + } + } +} + +// +// map +// + +message AddIntMessage { + batchSize: Int as uint8; + startingKey: Int; +} + +message(0x2A) TheAnswer {} + +contract MapIntMessage { + /// Target map + m: map; + + /// To handle deployment + receive() {} + + /// To add an item + receive(msg: AddIntMessage) { + let curVal = msg.startingKey; + let msgEmpty = TheAnswer{}; + repeat (msg.batchSize) { + self.m.set(curVal, msgEmpty); + curVal += 1; + } + } +} + +// TODO: all the same, but with Addresses as keys. +// NOTE: contractAddress(StateInit{code: emptyCell(), data: beginCell().storeUint(curVal, 16).endCell()}) diff --git a/src/test/size-limits/maps.spec.ts b/src/test/size-limits/maps.spec.ts new file mode 100644 index 00000000..6a292ce4 --- /dev/null +++ b/src/test/size-limits/maps.spec.ts @@ -0,0 +1,297 @@ +import { toNano } from "@ton/core"; +import { + Blockchain, + BlockchainTransaction, + SandboxContract, + TreasuryContract, +} from "@ton/sandbox"; +import "@ton/test-utils"; + +// Number of entries causing exit code 50 is in interval: [32700, 32800) +import { MapIntInt } from "./contracts/output/map-size-limits_MapIntInt"; +const shouldTestIntInt: boolean = false; + +// So far (exit code -14), max number of entries recorded is in interval: [507800, 507900) +import { MapIntBool } from "./contracts/output/map-size-limits_MapIntBool"; +const shouldTestIntBool: boolean = false; + +// So far (exit code -14), max number of entries recorded is in interval: [262000, 262100) +import { MapIntCell } from "./contracts/output/map-size-limits_MapIntCell"; +const shouldTestIntCell: boolean = false; + +// So far (exit code -14), max number of entries recorded is in interval: [507800, 507900) +import { MapIntAddress } from "./contracts/output/map-size-limits_MapIntAddress"; +const shouldTestIntAddress: boolean = false; + +// So far (exit code -14), max number of entries recorded is in interval: [196500, 196600) +import { MapIntStruct } from "./contracts/output/map-size-limits_MapIntStruct"; +const shouldTestIntStruct: boolean = false; + +// So far (exit code -14), max number of entries recorded is in interval: [196500, 196600) +import { MapIntMessage } from "./contracts/output/map-size-limits_MapIntMessage"; +const shouldTestIntMessage: boolean = false; + +// Tests to find the limits of map sizes +describe("map size limits", () => { + let blockchain: Blockchain; + let treasure: SandboxContract; + + beforeEach(async () => { + blockchain = await Blockchain.create(); + blockchain.verbosity.print = false; + treasure = await blockchain.treasury("treasure", { + resetBalanceIfZero: true, // ← doesn't seem to work + }); + }); + + it("should test map", async () => { + const contract = blockchain.openContract(await MapIntInt.fromInit()); + const deployResult = await contract.send( + treasure.getSender(), + { value: toNano("100000") }, + null, + ); + expect(deployResult.transactions).toHaveTransaction({ + from: treasure.address, + to: contract.address, + success: true, + deploy: true, + }); + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (shouldTestIntInt) { + const entries = 60_000; + const batch = 100; + for (let i = 0; i < entries; i += batch) { + const tx = ( + await contract.send( + treasure.getSender(), + { value: toNano("1") }, + { + $$type: "AddIntInt", + batchSize: BigInt(batch), + startingValue: BigInt(i), + }, + ) + ).transactions[1]!; + if (shouldStop(tx)) { + console.log(tx.description); + console.log(i); + break; + } + } + } + }); + + it("should test map", async () => { + const contract = blockchain.openContract(await MapIntBool.fromInit()); + const deployResult = await contract.send( + treasure.getSender(), + { value: toNano("100000") }, + null, + ); + expect(deployResult.transactions).toHaveTransaction({ + from: treasure.address, + to: contract.address, + success: true, + deploy: true, + }); + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (shouldTestIntBool) { + const entries = 10_000_000; + const batch = 100; + for (let i = 0; i < entries; i += batch) { + const tx: BlockchainTransaction = ( + await contract.send( + treasure.getSender(), + { value: toNano("1") }, + { + $$type: "AddIntBool", + batchSize: BigInt(batch), + startingKey: BigInt(i), + }, + ) + ).transactions[1]!; + if (shouldStop(tx)) { + console.log(tx.description); + console.log(i); + break; + } + } + } + }); + + it("should test map", async () => { + const contract = blockchain.openContract(await MapIntCell.fromInit()); + const deployResult = await contract.send( + treasure.getSender(), + { value: toNano("100000") }, + null, + ); + expect(deployResult.transactions).toHaveTransaction({ + from: treasure.address, + to: contract.address, + success: true, + deploy: true, + }); + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (shouldTestIntCell) { + const entries = 10_000_000; + const batch = 100; + for (let i = 0; i < entries; i += batch) { + const tx: BlockchainTransaction = ( + await contract.send( + treasure.getSender(), + { value: toNano("1") }, + { + $$type: "AddIntCell", + batchSize: BigInt(batch), + startingKey: BigInt(i), + }, + ) + ).transactions[1]!; + if (shouldStop(tx)) { + console.log(tx.description); + console.log(i); + break; + } + } + } + }); + + it("should test map", async () => { + const contract = blockchain.openContract( + await MapIntAddress.fromInit(), + ); + const deployResult = await contract.send( + treasure.getSender(), + { value: toNano("100000") }, + null, + ); + expect(deployResult.transactions).toHaveTransaction({ + from: treasure.address, + to: contract.address, + success: true, + deploy: true, + }); + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (shouldTestIntAddress) { + const entries = 10_000_000; + const batch = 100; + for (let i = 0; i < entries; i += batch) { + const tx: BlockchainTransaction = ( + await contract.send( + treasure.getSender(), + { value: toNano("1") }, + { + $$type: "AddIntAddress", + batchSize: BigInt(batch), + startingKey: BigInt(i), + }, + ) + ).transactions[1]!; + if (shouldStop(tx)) { + console.log(tx.description); + console.log(i); + break; + } + } + } + }); + + it("should test map", async () => { + const contract = blockchain.openContract(await MapIntStruct.fromInit()); + const deployResult = await contract.send( + treasure.getSender(), + { value: toNano("100000") }, + null, + ); + expect(deployResult.transactions).toHaveTransaction({ + from: treasure.address, + to: contract.address, + success: true, + deploy: true, + }); + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (shouldTestIntStruct) { + const entries = 10_000_000; + const batch = 100; + for (let i = 0; i < entries; i += batch) { + const tx: BlockchainTransaction = ( + await contract.send( + treasure.getSender(), + { value: toNano("1") }, + { + $$type: "AddIntStruct", + batchSize: BigInt(batch), + startingKey: BigInt(i), + }, + ) + ).transactions[1]!; + if (shouldStop(tx)) { + console.log(tx.description); + console.log(i); + break; + } + } + } + }); + + it("should test map", async () => { + const contract = blockchain.openContract( + await MapIntMessage.fromInit(), + ); + const deployResult = await contract.send( + treasure.getSender(), + { value: toNano("100000") }, + null, + ); + expect(deployResult.transactions).toHaveTransaction({ + from: treasure.address, + to: contract.address, + success: true, + deploy: true, + }); + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (shouldTestIntMessage) { + const entries = 10_000_000; + const batch = 100; + for (let i = 0; i < entries; i += batch) { + const tx: BlockchainTransaction = ( + await contract.send( + treasure.getSender(), + { value: toNano("1") }, + { + $$type: "AddIntMessage", + batchSize: BigInt(batch), + startingKey: BigInt(i), + }, + ) + ).transactions[1]!; + if (shouldStop(tx)) { + console.log(tx.description); + console.log(i); + break; + } + } + } + }); +}); + +/** Helper function for checking whether the test case should be stopped */ +function shouldStop(tx: BlockchainTransaction): boolean { + if ( + tx.description.type === "generic" && + tx.description.computePhase.type === "vm" && + (tx.description.computePhase.exitCode !== 0 || + tx.description.actionPhase?.resultCode !== 0) + ) { + return true; + } + return false; +} diff --git a/tact.config.json b/tact.config.json index 187785ed..b126acac 100644 --- a/tact.config.json +++ b/tact.config.json @@ -410,6 +410,16 @@ "name": "semantics", "path": "./src/test/e2e-emulated/contracts/semantics.tact", "output": "./src/test/e2e-emulated/contracts/output" + }, + { + "name": "map-size-limits", + "path": "./src/test/size-limits/contracts/map-size-limits.tact", + "output": "./src/test/size-limits/contracts/output" + }, + { + "name": "cell-number-limits", + "path": "./src/test/size-limits/contracts/cell-number-limits.tact", + "output": "./src/test/size-limits/contracts/output" } ] }