-
Notifications
You must be signed in to change notification settings - Fork 150
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
🧹 Added unit tests for MyONFT721.sol example #964
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@layerzerolabs/onft721-example": patch | ||
--- | ||
|
||
Added unit tests for `MyONFT721.sol` example |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.20; | ||
|
||
// Mock imports | ||
import { ONFT721Mock } from "../mocks/ONFT721Mock.sol"; | ||
import { ONFT721ComposerMock } from "../mocks/ONFT721ComposerMock.sol"; | ||
|
||
// OApp imports | ||
import { IOAppOptionsType3, EnforcedOptionParam } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol"; | ||
import { OptionsBuilder } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol"; | ||
|
||
// OFT imports | ||
import { IONFT721, SendParam } from "@layerzerolabs/onft-evm/contracts/onft721/interfaces/IONFT721.sol"; | ||
import { MessagingFee, MessagingReceipt } from "@layerzerolabs/onft-evm/contracts/onft721/ONFT721Core.sol"; | ||
import { ONFT721MsgCodec } from "@layerzerolabs/onft-evm/contracts/onft721/libs/ONFT721MsgCodec.sol"; | ||
import { ONFTComposeMsgCodec } from "@layerzerolabs/onft-evm/contracts/libs/ONFTComposeMsgCodec.sol"; | ||
|
||
// OZ imports | ||
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; | ||
|
||
// Forge imports | ||
import "forge-std/console.sol"; | ||
|
||
// DevTools imports | ||
import { TestHelperOz5 } from "@layerzerolabs/test-devtools-evm-foundry/contracts/TestHelperOz5.sol"; | ||
|
||
contract MyONFT721Test is TestHelperOz5 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are these new tests? Why wouldn't you just import the ones from: |
||
using OptionsBuilder for bytes; | ||
|
||
uint32 private aEid = 1; | ||
uint32 private bEid = 2; | ||
|
||
ONFT721Mock private aONFT721; | ||
ONFT721Mock private bONFT721; | ||
|
||
address private userA = address(0x1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use |
||
address private userB = address(0x2); | ||
uint256 private initialBalance = 100 ether; | ||
|
||
function setUp() public virtual override { | ||
vm.deal(userA, 1000 ether); | ||
vm.deal(userB, 1000 ether); | ||
|
||
super.setUp(); | ||
setUpEndpoints(2, LibraryType.UltraLightNode); | ||
|
||
aONFT721 = ONFT721Mock( | ||
_deployOApp( | ||
type(ONFT721Mock).creationCode, | ||
abi.encode("aONFT721", "aONFT721", address(endpoints[aEid]), address(this)) | ||
) | ||
); | ||
|
||
bONFT721 = ONFT721Mock( | ||
_deployOApp( | ||
type(ONFT721Mock).creationCode, | ||
abi.encode("bONFT721", "bONFT721", address(endpoints[bEid]), address(this)) | ||
) | ||
); | ||
|
||
// config and wire the onfts | ||
address[] memory onfts = new address[](2); | ||
onfts[0] = address(aONFT721); | ||
onfts[1] = address(bONFT721); | ||
this.wireOApps(onfts); | ||
|
||
// mint tokens | ||
aONFT721.mint(userA, 0); | ||
} | ||
|
||
function test_constructor() public { | ||
assertEq(aONFT721.owner(), address(this)); | ||
assertEq(bONFT721.owner(), address(this)); | ||
|
||
assertEq(aONFT721.balanceOf(userA), 1); | ||
assertEq(bONFT721.balanceOf(userB), 0); | ||
|
||
assertEq(aONFT721.token(), address(aONFT721)); | ||
assertEq(bONFT721.token(), address(bONFT721)); | ||
} | ||
|
||
function test_send_onft721() public { | ||
uint256 tokenId = 0; | ||
bytes memory options = OptionsBuilder.newOptions().addExecutorLzReceiveOption(200000, 0); | ||
SendParam memory sendParam = SendParam(bEid, addressToBytes32(userB), tokenId, options, "", ""); | ||
MessagingFee memory fee = aONFT721.quoteSend(sendParam, false); | ||
|
||
assertEq(aONFT721.balanceOf(userA), 1); | ||
assertEq(bONFT721.balanceOf(userB), 0); | ||
|
||
vm.prank(userA); | ||
aONFT721.send{ value: fee.nativeFee }(sendParam, fee, payable(address(this))); | ||
verifyPackets(bEid, addressToBytes32(address(bONFT721))); | ||
|
||
assertEq(aONFT721.balanceOf(userA), 0); | ||
assertEq(bONFT721.balanceOf(userB), 1); | ||
} | ||
|
||
function test_send_oft_compose_msg() public { | ||
uint256 tokenIdToSend = 0; | ||
|
||
ONFT721ComposerMock composer = new ONFT721ComposerMock(); | ||
|
||
bytes memory options = OptionsBuilder | ||
.newOptions() | ||
.addExecutorLzReceiveOption(200000, 0) | ||
.addExecutorLzComposeOption(0, 500000, 0); | ||
bytes memory composeMsg = hex"1234"; | ||
SendParam memory sendParam = SendParam( | ||
bEid, | ||
addressToBytes32(address(composer)), | ||
tokenIdToSend, | ||
options, | ||
composeMsg, | ||
"" | ||
); | ||
MessagingFee memory fee = aONFT721.quoteSend(sendParam, false); | ||
|
||
assertEq(aONFT721.balanceOf(userA), 1); | ||
assertEq(bONFT721.balanceOf(address(composer)), 0); | ||
|
||
vm.prank(userA); | ||
MessagingReceipt memory msgReceipt = aONFT721.send{ value: fee.nativeFee }( | ||
sendParam, | ||
fee, | ||
payable(address(this)) | ||
); | ||
verifyPackets(bEid, addressToBytes32(address(bONFT721))); | ||
|
||
// lzCompose params | ||
uint32 dstEid_ = bEid; | ||
address from_ = address(bONFT721); | ||
bytes memory options_ = options; | ||
bytes32 guid_ = msgReceipt.guid; | ||
address to_ = address(composer); | ||
bytes memory composerMsg_ = ONFTComposeMsgCodec.encode( | ||
msgReceipt.nonce, | ||
aEid, | ||
abi.encodePacked(addressToBytes32(userA), composeMsg) | ||
); | ||
this.lzCompose(dstEid_, from_, options_, guid_, to_, composerMsg_); | ||
|
||
assertEq(aONFT721.balanceOf(userA), 0); | ||
assertEq(bONFT721.balanceOf(address(composer)), 1); | ||
|
||
assertEq(composer.from(), from_); | ||
assertEq(composer.guid(), guid_); | ||
assertEq(composer.message(), composerMsg_); | ||
assertEq(composer.executor(), address(this)); | ||
assertEq(composer.extraData(), composerMsg_); // default to setting the extraData to the message as well to test | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' | ||
import { expect } from 'chai' | ||
import { Contract, ContractFactory } from 'ethers' | ||
import { deployments, ethers } from 'hardhat' | ||
|
||
import { Options } from '@layerzerolabs/lz-v2-utilities' | ||
|
||
describe('MyONFT721 Test', function () { | ||
// Constant representing a mock Endpoint ID for testing purposes | ||
const eidA = 1 | ||
const eidB = 2 | ||
// Declaration of variables to be used in the test suite | ||
let MyONFT721: ContractFactory | ||
let EndpointV2Mock: ContractFactory | ||
let ownerA: SignerWithAddress | ||
let ownerB: SignerWithAddress | ||
let endpointOwner: SignerWithAddress | ||
let myONFT721A: Contract | ||
let myONFT721B: Contract | ||
let mockEndpointV2A: Contract | ||
let mockEndpointV2B: Contract | ||
|
||
// Before hook for setup that runs once before all tests in the block | ||
before(async function () { | ||
// Contract factory for our tested contract | ||
// | ||
// We are using a derived contract that exposes a mint() function for testing purposes | ||
MyONFT721 = await ethers.getContractFactory('MyONFT721Mock') | ||
|
||
// Fetching the first three signers (accounts) from Hardhat's local Ethereum network | ||
const signers = await ethers.getSigners() | ||
|
||
ownerA = signers.at(0)! | ||
ownerB = signers.at(1)! | ||
endpointOwner = signers.at(2)! | ||
|
||
// The EndpointV2Mock contract comes from @layerzerolabs/test-devtools-evm-hardhat package | ||
// and its artifacts are connected as external artifacts to this project | ||
// | ||
// Unfortunately, hardhat itself does not yet provide a way of connecting external artifacts, | ||
// so we rely on hardhat-deploy to create a ContractFactory for EndpointV2Mock | ||
// | ||
// See https://github.com/NomicFoundation/hardhat/issues/1040 | ||
const EndpointV2MockArtifact = await deployments.getArtifact('EndpointV2Mock') | ||
EndpointV2Mock = new ContractFactory(EndpointV2MockArtifact.abi, EndpointV2MockArtifact.bytecode, endpointOwner) | ||
}) | ||
|
||
// beforeEach hook for setup that runs before each test in the block | ||
beforeEach(async function () { | ||
// Deploying a mock LZEndpoint with the given Endpoint ID | ||
mockEndpointV2A = await EndpointV2Mock.deploy(eidA) | ||
mockEndpointV2B = await EndpointV2Mock.deploy(eidB) | ||
|
||
// Deploying two instances of MyOFT contract with different identifiers and linking them to the mock LZEndpoint | ||
myONFT721A = await MyONFT721.deploy('aONFT721', 'aONFT721', mockEndpointV2A.address, ownerA.address) | ||
myONFT721B = await MyONFT721.deploy('bONFT721', 'bONFT721', mockEndpointV2B.address, ownerB.address) | ||
|
||
// Setting destination endpoints in the LZEndpoint mock for each MyONFT721 instance | ||
await mockEndpointV2A.setDestLzEndpoint(myONFT721B.address, mockEndpointV2B.address) | ||
await mockEndpointV2B.setDestLzEndpoint(myONFT721A.address, mockEndpointV2A.address) | ||
|
||
// Setting each MyONFT721 instance as a peer of the other in the mock LZEndpoint | ||
await myONFT721A.connect(ownerA).setPeer(eidB, ethers.utils.zeroPad(myONFT721B.address, 32)) | ||
await myONFT721B.connect(ownerB).setPeer(eidA, ethers.utils.zeroPad(myONFT721A.address, 32)) | ||
}) | ||
|
||
// A test case to verify token transfer functionality | ||
it('should send a token from A address to B address', async function () { | ||
// Minting an initial amount of tokens to ownerA's address in the myONFT721A contract | ||
const initialTokenId = 0 | ||
await myONFT721A.mint(ownerA.address, initialTokenId) | ||
|
||
// Defining extra message execution options for the send operation | ||
const options = Options.newOptions().addExecutorLzReceiveOption(200000, 0).toHex().toString() | ||
|
||
const sendParam = [eidB, ethers.utils.zeroPad(ownerB.address, 32), initialTokenId, options, '0x', '0x'] | ||
|
||
// Fetching the native fee for the token send operation | ||
const [nativeFee] = await myONFT721A.quoteSend(sendParam, false) | ||
|
||
// Executing the send operation from myONFT721A contract | ||
await myONFT721A.send(sendParam, [nativeFee, 0], ownerA.address, { value: nativeFee }) | ||
|
||
// Fetching the final token balances of ownerA and ownerB | ||
const finalBalanceA = await myONFT721A.balanceOf(ownerA.address) | ||
const finalBalanceB = await myONFT721B.balanceOf(ownerB.address) | ||
|
||
// Asserting that the final balances are as expected after the send operation | ||
expect(finalBalanceA).eql(ethers.BigNumber.from(0)) | ||
expect(finalBalanceB).eql(ethers.BigNumber.from(1)) | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.0; | ||
|
||
import { IOAppComposer } from "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppComposer.sol"; | ||
|
||
contract ONFT721ComposerMock is IOAppComposer { | ||
// default empty values for testing a lzCompose received message | ||
address public from; | ||
bytes32 public guid; | ||
bytes public message; | ||
address public executor; | ||
bytes public extraData; | ||
|
||
function lzCompose( | ||
address _from, | ||
bytes32 _guid, | ||
bytes calldata _message, | ||
address _executor, | ||
bytes calldata /*_extraData*/ | ||
) external payable { | ||
from = _from; | ||
guid = _guid; | ||
message = _message; | ||
executor = _executor; | ||
extraData = _message; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.0; | ||
|
||
import { ONFT721 } from "@layerzerolabs/onft-evm/contracts/onft721/ONFT721.sol"; | ||
import { SendParam } from "@layerzerolabs/onft-evm/contracts/onft721/interfaces/IONFT721.sol"; | ||
|
||
contract ONFT721Mock is ONFT721 { | ||
constructor( | ||
string memory _name, | ||
string memory _symbol, | ||
address _lzEndpoint, | ||
address _delegate | ||
) ONFT721(_name, _symbol, _lzEndpoint, _delegate) {} | ||
|
||
function mint(address _to, uint256 _tokenId) public { | ||
_mint(_to, _tokenId); | ||
} | ||
|
||
// @dev expose internal functions for testing purposes | ||
function debit(uint256 _tokenId, uint32 _dstEid) public { | ||
_debit(msg.sender, _tokenId, _dstEid); | ||
} | ||
|
||
function credit(address _to, uint256 _tokenId, uint32 _srcEid) public { | ||
_credit(_to, _tokenId, _srcEid); | ||
} | ||
|
||
function buildMsgAndOptions( | ||
SendParam calldata _sendParam | ||
) public view returns (bytes memory message, bytes memory options) { | ||
return _buildMsgAndOptions(_sendParam); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These tests are not run. You need to modify the "test" script in package.json to: