diff --git a/packages/lsp8-contracts/constants.ts b/packages/lsp8-contracts/constants.ts index 8754dc916..89c129210 100644 --- a/packages/lsp8-contracts/constants.ts +++ b/packages/lsp8-contracts/constants.ts @@ -23,6 +23,14 @@ export const LSP8_TYPE_IDS = { // keccak256('LSP8Tokens_OperatorNotification') LSP8Tokens_OperatorNotification: '0x8a1c15a8799f71b547e08e2bcb2e85257e81b0a07eee2ce6712549eef1f00970', + + // keccak256('LSP8Tokens_VotesDelegateeNotification') + LSP8Tokens_VotesDelegateeNotification: + '0x4aab908d8f4bc502ab79ddf13d56eb08e6ef25ebab36358bc42e5527adb08b8b', + + // keccak256('LSP8Tokens_VotesDelegatorNotification') + LSP8Tokens_VotesDelegatorNotification: + '0x54a91786b7c126f5ca201cf70323711e2b609e1259aab5cea150cfde4b9a748c', } as const; /** diff --git a/packages/lsp8-contracts/contracts/Mocks/UniversalReceiver.sol b/packages/lsp8-contracts/contracts/Mocks/UniversalReceiver.sol new file mode 100644 index 000000000..603b2b156 --- /dev/null +++ b/packages/lsp8-contracts/contracts/Mocks/UniversalReceiver.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import { + ILSP1UniversalReceiver +} from "@lukso/lsp1-contracts/contracts/ILSP1UniversalReceiver.sol"; +import { + _INTERFACEID_LSP1 +} from "@lukso/lsp1-contracts/contracts/LSP1Constants.sol"; + +contract MockUniversalReceiver is ILSP1UniversalReceiver { + event UniversalReceiverCalled( + address indexed from, + bytes32 indexed typeId, + bytes receivedData + ); + + function universalReceiver( + bytes32 typeId, + bytes calldata receivedData + ) external payable override returns (bytes memory) { + emit UniversalReceiverCalled(msg.sender, typeId, receivedData); + return ""; + } + + function supportsInterface(bytes4 interfaceId) public view returns (bool) { + return interfaceId == _INTERFACEID_LSP1; + } +} diff --git a/packages/lsp8-contracts/tests/LSP8Votes.test.ts b/packages/lsp8-contracts/tests/LSP8Votes.test.ts index a5474fd89..d6958f8cf 100644 --- a/packages/lsp8-contracts/tests/LSP8Votes.test.ts +++ b/packages/lsp8-contracts/tests/LSP8Votes.test.ts @@ -3,6 +3,7 @@ import { ethers } from 'hardhat'; import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; import { MyVotingNFT, MyVotingNFT__factory, MyGovernor, MyGovernor__factory } from '../types'; import { time, mine } from '@nomicfoundation/hardhat-network-helpers'; +import { LSP8_TYPE_IDS } from '../constants'; describe('Comprehensive Governor and NFT Tests', () => { let nft: MyVotingNFT; @@ -173,4 +174,39 @@ describe('Comprehensive Governor and NFT Tests', () => { expect(await nft.getPastTotalSupply(blockNumber2 - 1)).to.equal(initialSupply + BigInt(1)); }); }); + describe('Delegation Notifications', () => { + let mockUniversalReceiver; + + beforeEach(async () => { + const MockUniversalReceiver = await ethers.getContractFactory('MockUniversalReceiver'); + mockUniversalReceiver = await MockUniversalReceiver.deploy(); + }); + + it('should notify delegatee with correct data format', async () => { + const expectedDelegateeData = ethers.AbiCoder.defaultAbiCoder().encode( + ['address', 'address', 'uint256'], + [voter1.address, voter1.address, 1], + ); + + const expectedDelegatorData = ethers.AbiCoder.defaultAbiCoder().encode( + ['address', 'address', 'uint256'], + [voter1.address, voter1.address, 1], + ); + + await expect(nft.connect(voter1).delegate(await mockUniversalReceiver.getAddress())) + .to.emit(mockUniversalReceiver, 'UniversalReceiverCalled') + .withArgs( + await nft.getAddress(), + LSP8_TYPE_IDS.LSP8Tokens_VotesDelegateeNotification, + expectedDelegateeData, + ); + }); + + it('should not notify when delegating to address(0)', async () => { + await expect(nft.connect(voter1).delegate(ethers.ZeroAddress)).to.not.emit( + mockUniversalReceiver, + 'UniversalReceiverCalled', + ); + }); + }); });