123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370 |
- const { ethers } = require('hardhat');
- const { expect } = require('chai');
- const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
- const {
- shouldBehaveLikeERC20,
- shouldBehaveLikeERC20Transfer,
- shouldBehaveLikeERC20Approve,
- } = require('../ERC20.behavior.js');
- const { shouldSupportInterfaces } = require('../../../utils/introspection/SupportsInterface.behavior');
- const { RevertType } = require('../../../helpers/enums.js');
- const name = 'My Token';
- const symbol = 'MTKN';
- const value = 1000n;
- const data = '0x123456';
- async function fixture() {
- // this.accounts is used by shouldBehaveLikeERC20
- const accounts = await ethers.getSigners();
- const [holder, other] = accounts;
- const receiver = await ethers.deployContract('ERC1363ReceiverMock');
- const spender = await ethers.deployContract('ERC1363SpenderMock');
- const token = await ethers.deployContract('$ERC1363', [name, symbol]);
- await token.$_mint(holder, value);
- return {
- accounts,
- holder,
- other,
- token,
- receiver,
- spender,
- selectors: {
- onTransferReceived: receiver.interface.getFunction('onTransferReceived(address,address,uint256,bytes)').selector,
- onApprovalReceived: spender.interface.getFunction('onApprovalReceived(address,uint256,bytes)').selector,
- },
- };
- }
- describe('ERC1363', function () {
- beforeEach(async function () {
- Object.assign(this, await loadFixture(fixture));
- });
- shouldSupportInterfaces(['ERC165', 'ERC1363']);
- shouldBehaveLikeERC20(value);
- describe('transferAndCall', function () {
- describe('as a transfer', function () {
- beforeEach(async function () {
- this.recipient = this.receiver;
- this.transfer = (holder, ...rest) =>
- this.token.connect(holder).getFunction('transferAndCall(address,uint256)')(...rest);
- });
- shouldBehaveLikeERC20Transfer(value);
- });
- it('reverts transferring to an EOA', async function () {
- await expect(this.token.connect(this.holder).getFunction('transferAndCall(address,uint256)')(this.other, value))
- .to.be.revertedWithCustomError(this.token, 'ERC1363InvalidReceiver')
- .withArgs(this.other.address);
- });
- it('succeeds without data', async function () {
- await expect(
- this.token.connect(this.holder).getFunction('transferAndCall(address,uint256)')(this.receiver, value),
- )
- .to.emit(this.token, 'Transfer')
- .withArgs(this.holder.address, this.receiver.target, value)
- .to.emit(this.receiver, 'Received')
- .withArgs(this.holder.address, this.holder.address, value, '0x');
- });
- it('succeeds with data', async function () {
- await expect(
- this.token.connect(this.holder).getFunction('transferAndCall(address,uint256,bytes)')(
- this.receiver,
- value,
- data,
- ),
- )
- .to.emit(this.token, 'Transfer')
- .withArgs(this.holder.address, this.receiver.target, value)
- .to.emit(this.receiver, 'Received')
- .withArgs(this.holder.address, this.holder.address, value, data);
- });
- it('reverts with reverting hook (without reason)', async function () {
- await this.receiver.setUp(this.selectors.onTransferReceived, RevertType.RevertWithoutMessage);
- await expect(
- this.token.connect(this.holder).getFunction('transferAndCall(address,uint256,bytes)')(
- this.receiver,
- value,
- data,
- ),
- )
- .to.be.revertedWithCustomError(this.token, 'ERC1363InvalidReceiver')
- .withArgs(this.receiver.target);
- });
- it('reverts with reverting hook (with reason)', async function () {
- await this.receiver.setUp(this.selectors.onTransferReceived, RevertType.RevertWithMessage);
- await expect(
- this.token.connect(this.holder).getFunction('transferAndCall(address,uint256,bytes)')(
- this.receiver,
- value,
- data,
- ),
- ).to.be.revertedWith('ERC1363ReceiverMock: reverting');
- });
- it('reverts with reverting hook (with custom error)', async function () {
- const reason = '0x12345678';
- await this.receiver.setUp(reason, RevertType.RevertWithCustomError);
- await expect(
- this.token.connect(this.holder).getFunction('transferAndCall(address,uint256,bytes)')(
- this.receiver,
- value,
- data,
- ),
- )
- .to.be.revertedWithCustomError(this.receiver, 'CustomError')
- .withArgs(reason);
- });
- it('panics with reverting hook (with panic)', async function () {
- await this.receiver.setUp(this.selectors.onTransferReceived, RevertType.Panic);
- await expect(
- this.token.connect(this.holder).getFunction('transferAndCall(address,uint256,bytes)')(
- this.receiver,
- value,
- data,
- ),
- ).to.be.revertedWithPanic();
- });
- it('reverts with bad return value', async function () {
- await this.receiver.setUp('0x12345678', RevertType.None);
- await expect(
- this.token.connect(this.holder).getFunction('transferAndCall(address,uint256,bytes)')(
- this.receiver,
- value,
- data,
- ),
- )
- .to.be.revertedWithCustomError(this.token, 'ERC1363InvalidReceiver')
- .withArgs(this.receiver.target);
- });
- });
- describe('transferFromAndCall', function () {
- beforeEach(async function () {
- await this.token.connect(this.holder).approve(this.other, ethers.MaxUint256);
- });
- describe('as a transfer', function () {
- beforeEach(async function () {
- this.recipient = this.receiver;
- this.transfer = this.token.connect(this.other).getFunction('transferFromAndCall(address,address,uint256)');
- });
- shouldBehaveLikeERC20Transfer(value);
- });
- it('reverts transferring to an EOA', async function () {
- await expect(
- this.token.connect(this.other).getFunction('transferFromAndCall(address,address,uint256)')(
- this.holder,
- this.other,
- value,
- ),
- )
- .to.be.revertedWithCustomError(this.token, 'ERC1363InvalidReceiver')
- .withArgs(this.other.address);
- });
- it('succeeds without data', async function () {
- await expect(
- this.token.connect(this.other).getFunction('transferFromAndCall(address,address,uint256)')(
- this.holder,
- this.receiver,
- value,
- ),
- )
- .to.emit(this.token, 'Transfer')
- .withArgs(this.holder.address, this.receiver.target, value)
- .to.emit(this.receiver, 'Received')
- .withArgs(this.other.address, this.holder.address, value, '0x');
- });
- it('succeeds with data', async function () {
- await expect(
- this.token.connect(this.other).getFunction('transferFromAndCall(address,address,uint256,bytes)')(
- this.holder,
- this.receiver,
- value,
- data,
- ),
- )
- .to.emit(this.token, 'Transfer')
- .withArgs(this.holder.address, this.receiver.target, value)
- .to.emit(this.receiver, 'Received')
- .withArgs(this.other.address, this.holder.address, value, data);
- });
- it('reverts with reverting hook (without reason)', async function () {
- await this.receiver.setUp(this.selectors.onTransferReceived, RevertType.RevertWithoutMessage);
- await expect(
- this.token.connect(this.other).getFunction('transferFromAndCall(address,address,uint256,bytes)')(
- this.holder,
- this.receiver,
- value,
- data,
- ),
- )
- .to.be.revertedWithCustomError(this.token, 'ERC1363InvalidReceiver')
- .withArgs(this.receiver.target);
- });
- it('reverts with reverting hook (with reason)', async function () {
- await this.receiver.setUp(this.selectors.onTransferReceived, RevertType.RevertWithMessage);
- await expect(
- this.token.connect(this.other).getFunction('transferFromAndCall(address,address,uint256,bytes)')(
- this.holder,
- this.receiver,
- value,
- data,
- ),
- ).to.be.revertedWith('ERC1363ReceiverMock: reverting');
- });
- it('reverts with reverting hook (with custom error)', async function () {
- const reason = '0x12345678';
- await this.receiver.setUp(reason, RevertType.RevertWithCustomError);
- await expect(
- this.token.connect(this.other).getFunction('transferFromAndCall(address,address,uint256,bytes)')(
- this.holder,
- this.receiver,
- value,
- data,
- ),
- )
- .to.be.revertedWithCustomError(this.receiver, 'CustomError')
- .withArgs(reason);
- });
- it('panics with reverting hook (with panic)', async function () {
- await this.receiver.setUp(this.selectors.onTransferReceived, RevertType.Panic);
- await expect(
- this.token.connect(this.other).getFunction('transferFromAndCall(address,address,uint256,bytes)')(
- this.holder,
- this.receiver,
- value,
- data,
- ),
- ).to.be.revertedWithPanic();
- });
- it('reverts with bad return value', async function () {
- await this.receiver.setUp('0x12345678', RevertType.None);
- await expect(
- this.token.connect(this.other).getFunction('transferFromAndCall(address,address,uint256,bytes)')(
- this.holder,
- this.receiver,
- value,
- data,
- ),
- )
- .to.be.revertedWithCustomError(this.token, 'ERC1363InvalidReceiver')
- .withArgs(this.receiver.target);
- });
- });
- describe('approveAndCall', function () {
- describe('as an approval', function () {
- beforeEach(async function () {
- this.recipient = this.spender;
- this.approve = (holder, ...rest) =>
- this.token.connect(holder).getFunction('approveAndCall(address,uint256)')(...rest);
- });
- shouldBehaveLikeERC20Approve(value);
- });
- it('reverts approving an EOA', async function () {
- await expect(this.token.connect(this.holder).getFunction('approveAndCall(address,uint256)')(this.other, value))
- .to.be.revertedWithCustomError(this.token, 'ERC1363InvalidSpender')
- .withArgs(this.other.address);
- });
- it('succeeds without data', async function () {
- await expect(this.token.connect(this.holder).getFunction('approveAndCall(address,uint256)')(this.spender, value))
- .to.emit(this.token, 'Approval')
- .withArgs(this.holder.address, this.spender.target, value)
- .to.emit(this.spender, 'Approved')
- .withArgs(this.holder.address, value, '0x');
- });
- it('succeeds with data', async function () {
- await expect(
- this.token.connect(this.holder).getFunction('approveAndCall(address,uint256,bytes)')(this.spender, value, data),
- )
- .to.emit(this.token, 'Approval')
- .withArgs(this.holder.address, this.spender.target, value)
- .to.emit(this.spender, 'Approved')
- .withArgs(this.holder.address, value, data);
- });
- it('with reverting hook (without reason)', async function () {
- await this.spender.setUp(this.selectors.onApprovalReceived, RevertType.RevertWithoutMessage);
- await expect(
- this.token.connect(this.holder).getFunction('approveAndCall(address,uint256,bytes)')(this.spender, value, data),
- )
- .to.be.revertedWithCustomError(this.token, 'ERC1363InvalidSpender')
- .withArgs(this.spender.target);
- });
- it('reverts with reverting hook (with reason)', async function () {
- await this.spender.setUp(this.selectors.onApprovalReceived, RevertType.RevertWithMessage);
- await expect(
- this.token.connect(this.holder).getFunction('approveAndCall(address,uint256,bytes)')(this.spender, value, data),
- ).to.be.revertedWith('ERC1363SpenderMock: reverting');
- });
- it('reverts with reverting hook (with custom error)', async function () {
- const reason = '0x12345678';
- await this.spender.setUp(reason, RevertType.RevertWithCustomError);
- await expect(
- this.token.connect(this.holder).getFunction('approveAndCall(address,uint256,bytes)')(this.spender, value, data),
- )
- .to.be.revertedWithCustomError(this.spender, 'CustomError')
- .withArgs(reason);
- });
- it('panics with reverting hook (with panic)', async function () {
- await this.spender.setUp(this.selectors.onApprovalReceived, RevertType.Panic);
- await expect(
- this.token.connect(this.holder).getFunction('approveAndCall(address,uint256,bytes)')(this.spender, value, data),
- ).to.be.revertedWithPanic();
- });
- it('reverts with bad return value', async function () {
- await this.spender.setUp('0x12345678', RevertType.None);
- await expect(
- this.token.connect(this.holder).getFunction('approveAndCall(address,uint256,bytes)')(this.spender, value, data),
- )
- .to.be.revertedWithCustomError(this.token, 'ERC1363InvalidSpender')
- .withArgs(this.spender.target);
- });
- });
- });
|