Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tests: receiver precedence #1742

Merged
merged 17 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/test/e2e-emulated/contracts/receiver-precedence.tact
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
message Message {
msg: String;
}

contract ReceiverTester {

receiverKind: String = "unknown";

receive() {
self.receiverKind = "empty";
}

receive("message") {
self.receiverKind = "message";
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
self.receiverKind = "message";
self.receiverKind = "comment";

}

receive(msg: String) {
if (msg == "message") {
self.receiverKind = "message_string";
} else {
self.receiverKind = "string";
}
}

receive(msg: Message) {
self.receiverKind = "Message_struct";
}

receive(msg: Slice) {
// Drop the op code
msg.loadUint(32);
let m = msg.asString();
if (m == "message") {
self.receiverKind = "message_slice";
} else {
self.receiverKind = "slice";
}
}

get fun receiverKind(): String {
return self.receiverKind;
}
}

136 changes: 136 additions & 0 deletions src/test/e2e-emulated/receiver-precedence.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { beginCell, Cell, toNano } from "@ton/core";
import { Blockchain, SandboxContract, TreasuryContract } from "@ton/sandbox";
import { ReceiverTester } from "./contracts/output/receiver-precedence_ReceiverTester";
import "@ton/test-utils";

describe("receivers-precedence", () => {
let blockchain: Blockchain;
let treasure: SandboxContract<TreasuryContract>;
let contract: SandboxContract<ReceiverTester>;

beforeEach(async () => {
blockchain = await Blockchain.create();
blockchain.verbosity.print = false;
treasure = await blockchain.treasury("treasure");

contract = blockchain.openContract(await ReceiverTester.fromInit());

const deployResult = await contract.send(
treasure.getSender(),
{ value: toNano("10") },
null,
);
expect(deployResult.transactions).toHaveTransaction({
from: treasure.address,
to: contract.address,
success: true,
deploy: true,
});
});

it("should implement receivers precedence correctly", async () => {
// Initially, since we sent a deploy message with empty message, the empty receiver executed
const receiver1 = await contract.getReceiverKind();
expect(receiver1 === "empty").toBe(true);

// Send now a "message"
await contract.send(
treasure.getSender(),
{ value: toNano("10") },
"message",
);
const receiver2 = await contract.getReceiverKind();
// Note the receiver "message_string" did not execute
expect(receiver2 === "message").toBe(true);

// Send now an arbitrary string different from "message"
await contract.send(
treasure.getSender(),
{ value: toNano("10") },
"msg",
);
const receiver3 = await contract.getReceiverKind();
// Now, the receiver for general strings executed.
// Note that "message_string" still does not execute, nor the "message_slice" receiver.
expect(receiver3 === "string").toBe(true);

// Send now a Message (note the capital letter in Message)
await contract.send(
treasure.getSender(),
{ value: toNano("10") },
{ $$type: "Message", msg: "message" },
);
const receiver4 = await contract.getReceiverKind();
// Now, the receiver for Message executed.
expect(receiver4 === "Message_struct").toBe(true);

// Now, simulate different kinds of messages using slices

// First, an empty message, which can be simulated with an empty slice
await contract.send(
treasure.getSender(),
{ value: toNano("10") },
new Cell().asSlice(),
);
// The empty receiver executed
const receiver5 = await contract.getReceiverKind();
expect(receiver5 === "empty").toBe(true);

// Send now a "message" simulated as slice
await contract.send(
treasure.getSender(),
{ value: toNano("10") },
// String receivers are triggered by passing an operation code 0 at the start of the slice
beginCell()
.storeUint(0, 32)
.storeStringTail("message")
.endCell()
.asSlice(),
);
const receiver6 = await contract.getReceiverKind();
// Note the receiver "message_string" did not execute, nor the receiver "message_slice".
expect(receiver6 === "message").toBe(true);

// Send now an arbitrary string different from "message"
await contract.send(
treasure.getSender(),
{ value: toNano("10") },
// String receivers are triggered by passing an operation code 0 at the start of the slice
beginCell()
.storeUint(0, 32)
.storeStringTail("msg")
.endCell()
.asSlice(),
);
const receiver7 = await contract.getReceiverKind();
// Now, the receiver for general strings executed.
// Note that "message_string" still does not execute.
expect(receiver7 === "string").toBe(true);

// Note that it is possible to trigger the "message_slice" receiver by passing an operation code different from 0, for example 10.
await contract.send(
treasure.getSender(),
{ value: toNano("10") },
beginCell()
.storeUint(10, 32)
.storeStringTail("message")
.endCell()
.asSlice(),
);
const receiver8 = await contract.getReceiverKind();
// Now, the receiver for slices takes the "message" path
expect(receiver8 === "message_slice").toBe(true);

// Send now an arbitrary slice
await contract.send(
treasure.getSender(),
{ value: toNano("10") },
beginCell().storeUint(10, 32).endCell().asSlice(),
);
const receiver9 = await contract.getReceiverKind();
// Now, the receiver for slices executed.
expect(receiver9 === "slice").toBe(true);

// In all the cases, "message_string" did not execute, as it should be.
});
});