-
Notifications
You must be signed in to change notification settings - Fork 0
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
feat: example usage in nft testing of our foundry library and hardhat plugin (#191) #209
base: main
Are you sure you want to change the base?
Changes from 7 commits
01edc2b
cf9da25
63028de
531d7bd
be1201d
a3aa7d2
ffe7144
768913d
7c592e7
55f3b98
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,33 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity ^0.8.0; | ||
|
||
import {Test} from "forge-std/Test.sol"; | ||
import {htsSetup} from "hedera-forking/contracts/htsSetup.sol"; | ||
import {IERC721} from "hedera-forking/contracts/IERC721.sol"; | ||
|
||
contract NFTExampleTest is Test { | ||
// https://hashscan.io/mainnet/token/0.0.5083205 | ||
address NFT_mainnet = 0x00000000000000000000000000000000004d9045; | ||
|
||
address private user; | ||
|
||
function setUp() external { | ||
htsSetup(); | ||
|
||
user = makeAddr("user"); | ||
dealERC721(NFT_mainnet, user, 3); | ||
} | ||
|
||
function test_get_owner_of_existing_account() view external { | ||
address owner = IERC721(NFT_mainnet).ownerOf(1); | ||
assertNotEq(IERC721(NFT_mainnet).ownerOf(1), address(0)); | ||
|
||
// The assertion below cannot be guaranteed, since we can only query the current owner of the NFT, | ||
// Note that the ownership of the NFT may change over time. | ||
assertEq(owner, 0x000000000000000000000000000000000006889a); | ||
} | ||
|
||
function test_dealt_nft_assigned_to_local_account() view external { | ||
assertEq(IERC721(NFT_mainnet).ownerOf(3), user); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity ^0.8.0; | ||
|
||
import {Test, console} from "forge-std/Test.sol"; | ||
import {htsSetup} from "hedera-forking/contracts/htsSetup.sol"; | ||
import {IERC721} from "hedera-forking/contracts/IERC721.sol"; | ||
|
||
contract NFTConsoleExampleTest is Test { | ||
function setUp() external { | ||
htsSetup(); | ||
} | ||
|
||
function test_using_console_log() view external { | ||
// https://hashscan.io/mainnet/token/0.0.5083205 | ||
address NFT_mainnet = 0x00000000000000000000000000000000004d9045; | ||
|
||
string memory name = IERC721(NFT_mainnet).name(); | ||
string memory symbol = IERC721(NFT_mainnet).symbol(); | ||
string memory tokenURI = IERC721(NFT_mainnet).tokenURI(1); | ||
assertEq(name, "THE BARKANEERS"); | ||
assertEq(symbol, "BARKANEERS"); | ||
assertEq(tokenURI, "ipfs://bafkreif4hpsgflzzvd7c4abx5u5xwrrjl7wkimbjtndvkxodklxdam5upm"); | ||
console.log("name: %s, symbol: %s, tokenURI: %s", name, symbol, tokenURI); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity ^0.8.0; | ||
|
||
import {IERC721} from "./IERC721.sol"; | ||
import {console} from "hardhat/console.sol"; | ||
|
||
contract CallNFT { | ||
function getTokenName(address tokenAddress) external view returns (string memory) { | ||
return IERC721(tokenAddress).name(); | ||
} | ||
|
||
function invokeTransferFrom(address tokenAddress, address to, uint256 serialId) external { | ||
// You can use `console.log` as usual | ||
// https://hardhat.org/tutorial/debugging-with-hardhat-network#solidity--console.log | ||
console.log("Transferring from %s to %s %s tokens", msg.sender, to, serialId); | ||
address owner = IERC721(tokenAddress).ownerOf(serialId); | ||
IERC721(tokenAddress).transferFrom(owner, to, serialId); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity ^0.8.0; | ||
|
||
/** | ||
* See https://ethereum.org/en/developers/docs/standards/tokens/erc-721/#events for more information. | ||
*/ | ||
interface IERC721Events { | ||
event ApprovalForAll(address indexed owner, address indexed operator, bool approved); | ||
} | ||
|
||
/** | ||
* This interface is used to get the selectors and for testing. | ||
* | ||
* https://hips.hedera.com/hip/hip-218 | ||
* https://hips.hedera.com/hip/hip-376 | ||
*/ | ||
interface IERC721 { | ||
/** | ||
* @dev Returns the token collection name. | ||
*/ | ||
function name() external view returns (string memory); | ||
|
||
/** | ||
* @dev Returns the token collection symbol. | ||
*/ | ||
function symbol() external view returns (string memory); | ||
|
||
/** | ||
* @dev Returns the Uniform Resource Identifier (URI) for `serialId` token. | ||
*/ | ||
function tokenURI(uint256 serialId) external view returns (string memory); | ||
|
||
/** | ||
* @dev Returns the total amount of tokens stored by the contract. | ||
*/ | ||
function totalSupply() external view returns (uint256); | ||
|
||
/** | ||
* @dev Returns the number of tokens in `owner`'s account. | ||
*/ | ||
function balanceOf(address owner) external view returns (uint256 balance); | ||
|
||
/** | ||
* @dev Returns the owner of the `serialId` token. | ||
* | ||
* Requirements: | ||
* - `serialId` must exist. | ||
*/ | ||
function ownerOf(uint256 serialId) external view returns (address); | ||
|
||
/** | ||
* @dev Transfers `serialId` token from `sender` to `recipient`. | ||
* | ||
* Requirements: | ||
* - `sender` cannot be the zero address. | ||
* - `recipient` cannot be the zero address. | ||
* - `serialId` token must be owned by `sender`. | ||
* - If the caller is not `sender`, it must be approved to move this token by either {approve} or {setApprovalForAll}. | ||
* | ||
* Emits a {Transfer} event. | ||
*/ | ||
function transferFrom(address sender, address recipient, uint256 serialId) external payable; | ||
|
||
/** | ||
* @dev Gives permission to `spender` to transfer `serialId` token to another account. | ||
* The approval is cleared when the token is transferred. | ||
* | ||
* Only a single account can be approved at a time, so approving the zero address clears previous approvals. | ||
* | ||
* Requirements: | ||
* - The caller must own the token or be an approved operator. | ||
* - `serialId` must exist. | ||
* | ||
* Emits an {Approval} event. | ||
*/ | ||
function approve(address spender, uint256 serialId) external payable; | ||
|
||
/** | ||
* @dev Approve or remove `operator` as an operator for the caller. | ||
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. | ||
* | ||
* Requirements: | ||
* - The `operator` cannot be the address zero. | ||
* | ||
* Emits an {ApprovalForAll} event. | ||
*/ | ||
function setApprovalForAll(address operator, bool approved) external; | ||
|
||
/** | ||
* @dev Returns the account approved for `serialId` token. | ||
* | ||
* Requirements: | ||
* - `serialId` must exist. | ||
*/ | ||
function getApproved(uint256 serialId) external view returns (address operator); | ||
|
||
/** | ||
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. | ||
* | ||
* See {setApprovalForAll} | ||
*/ | ||
function isApprovedForAll(address owner, address operator) external view returns (bool); | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/*- | ||
* Hedera Hardhat Forking Plugin | ||
* | ||
* Copyright (C) 2024 Hedera Hashgraph, LLC | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
const { expect } = require('chai'); | ||
// prettier-ignore | ||
const { ethers: { getContractAt } } = require('hardhat'); | ||
|
||
describe('NFT example -- informational', function () { | ||
it('should get name and symbol', async function () { | ||
// https://hashscan.io/mainnet/token/0.0.4970613 | ||
const nft = await getContractAt('IERC721', '0x00000000000000000000000000000000004bd875'); | ||
expect(await nft['name']()).to.be.equal('Concierge Collectibles'); | ||
expect(await nft['symbol']()).to.be.equal('Concierge Collectibles'); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/*- | ||
* Hedera Hardhat Forking Plugin | ||
* | ||
* Copyright (C) 2024 Hedera Hashgraph, LLC | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
const { expect } = require('chai'); | ||
// prettier-ignore | ||
const { ethers: { getContractAt } } = require('hardhat'); | ||
|
||
describe('NFT example -- ownerOf', function () { | ||
it('should get `ownerOf` account holder', async function () { | ||
// https://hashscan.io/mainnet/token/0.0.4970613 | ||
const nft = await getContractAt('IERC721', '0x00000000000000000000000000000000004bd875'); | ||
expect(await nft['ownerOf'](1)).to.not.be.equal( | ||
'0x0000000000000000000000000000000000000000' | ||
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. can we compare against the current owner? 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. Yes, we can. But we get nft details for the latest block, so all it will take for our tests then is someone who owns this serial id = 1 to transfer it to someone else. (I'll apply your suggestion anyway :) ). |
||
); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/*- | ||
* Hedera Hardhat Forking Plugin | ||
* | ||
* Copyright (C) 2024 Hedera Hashgraph, LLC | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
const { expect } = require('chai'); | ||
// prettier-ignore | ||
const { ethers: { getSigner, getSigners, getContractAt }, network: { provider } } = require('hardhat'); | ||
const { loadFixture } = require('@nomicfoundation/hardhat-toolbox/network-helpers'); | ||
|
||
describe('NFT example -- transferFrom', function () { | ||
async function id() { | ||
return [(await getSigners())[0]]; | ||
} | ||
|
||
it("should `transferFrom` tokens from account holder to one of Hardhat' signers", async function () { | ||
const [receiver] = await loadFixture(id); | ||
|
||
// https://hashscan.io/mainnet/token/0.0.4970613 | ||
const nft = await getContractAt('IERC721', '0x00000000000000000000000000000000004bd875'); | ||
|
||
const holderAddress = await nft['ownerOf'](1n); | ||
|
||
await provider.request({ | ||
method: 'hardhat_impersonateAccount', | ||
params: [holderAddress], | ||
}); | ||
|
||
const holder = await getSigner(holderAddress); | ||
await nft.connect(holder)['transferFrom'](holder, receiver, 1n); | ||
|
||
expect(await nft['ownerOf'](1n)).to.be.equal(receiver.address); | ||
}); | ||
}); |
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.
fallback_fee is nullable in the json response from the mirror node, but abi decode requires it to be set in order to create proper structure. That is why I set the default value here when it is empty.
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.
sure, but also there is a
vm.parseJson(json, ".custom_fees.royalty_fees")
that guards againstnull
, right?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.
@acuarica Only when royalty_fees is null. For the barkaneer I had fallback_fee = null (substructure in the royalty fees object).