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

Update legends nft contract #1061

Open
wants to merge 2 commits into
base: v2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
151 changes: 93 additions & 58 deletions contracts/LegendsNft.sol
Original file line number Diff line number Diff line change
@@ -1,66 +1,101 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import '@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol';
import '@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/utils/Strings.sol';

using Strings for address;


contract LegendsNFT is IERC721Metadata, ERC721Enumerable, Ownable {
event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
event MetadataUpdate(uint256 _tokenId);
event PickedCharacter(uint indexed heroType);

string baseURI;

constructor() ERC721("Ambire Legends", "AML") Ownable() {}

function supportsInterface(bytes4 interfaceId) public view override(ERC721Enumerable, IERC165) returns(bool) {
return interfaceId == 0x49064906 || super.supportsInterface(interfaceId);
}

function mint(uint heroType) public {
// single mint allowed
// using address for front-end simplification
_mint(msg.sender, uint256(uint160(msg.sender)));
emit PickedCharacter(heroType);
}

function tokenURI(uint256 tokenId) public view override(ERC721, IERC721Metadata) returns (string memory) {
_requireMinted(tokenId);
return string(abi.encodePacked(baseURI, address(uint160(tokenId)).toHexString()));
}

function setBaseUri(string calldata _baseURI) public onlyOwner {
baseURI = _baseURI;
}

// this is used only for
function updateOpenSeaInfo(uint from, uint to) public {
emit BatchMetadataUpdate(from, to);
}

// Soulbound
function approve(address, uint256) public pure override(ERC721, IERC721) {
revert("Soulbound: cannot approve token transfer");
}

function setApprovalForAll(address, bool) public pure override(ERC721, IERC721) {
revert("Soulbound: cannot set approval for all");
}

function transferFrom(address, address, uint256) public pure override(ERC721, IERC721) {
revert("Soulbound: cannot transfer nft");
}

function safeTransferFrom(address, address, uint256) public pure override(ERC721, IERC721) {
revert("Soulbound: cannot transfer nft");
}

function safeTransferFrom(address, address, uint256, bytes memory ) public pure override(ERC721, IERC721) {
revert("Soulbound: cannot transfer nft");
}
event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
event MetadataUpdate(uint256 _tokenId);
event PickedCharacter(uint indexed heroType);
bool allowTransfers = false;
string baseURI;

constructor() ERC721('Ambire Legends', 'AML') Ownable() {}

function supportsInterface(
bytes4 interfaceId
) public view override(ERC721Enumerable, IERC165) returns (bool) {
return interfaceId == 0x49064906 || super.supportsInterface(interfaceId);
}

function mint(uint heroType) public {
// single mint allowed
// using address for front-end simplification
_mint(msg.sender, uint256(uint160(msg.sender)));
emit PickedCharacter(heroType);
// this line triggers opensea metadata updaate
emit BatchMetadataUpdate(0, type(uint256).max);
}

function tokenURI(
uint256 tokenId
) public view override(ERC721, IERC721Metadata) returns (string memory) {
_requireMinted(tokenId);
return string(abi.encodePacked(baseURI, address(uint160(tokenId)).toHexString()));
}

function setBaseUri(string calldata _baseURI) public onlyOwner {
baseURI = _baseURI;
}

// this is used only for
function updateOpenSeaInfo(uint from, uint to) public {
emit BatchMetadataUpdate(from, to);
}

function setAllowTransfer(bool value) public onlyOwner {
allowTransfers = value;
}

function burn(uint256 tokenId) public {
require(allowTransfers, 'Soulbound: cannot burn');
require(
_msgSender() == _ownerOf(tokenId) || _msgSender() == owner(),
'You cannot burn this NFT.'
);
_burn(tokenId);
}

function approve(address recipient, uint256 tokenId) public override(ERC721, IERC721) {
require(allowTransfers, 'Soulbound: cannot approve token transfer');
_approve(recipient, tokenId);
}

function setApprovalForAll(address recipient, bool isApproved) public override(ERC721, IERC721) {
require(allowTransfers, 'Soulbound: cannot set approval for all');
_setApprovalForAll(_msgSender(), recipient, isApproved);
}

function transferFrom(
address from,
address to,
uint256 tokenId
) public override(ERC721, IERC721) {
require(allowTransfers, 'Soulbound: cannot transfer nft');
_transfer(from, to, tokenId);
}

function safeTransferFrom(
address from,
address to,
uint256 tokenId
) public override(ERC721, IERC721) {
require(allowTransfers, 'Soulbound: cannot transfer nft');
_safeTransfer(from, to, tokenId, '');
}

function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes memory data
) public override(ERC721, IERC721) {
require(allowTransfers, 'Soulbound: cannot transfer nft');
_safeTransfer(from, to, tokenId, data);
}
}
2 changes: 1 addition & 1 deletion hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,6 @@ const config: HardhatUserConfig = {
}
]
}
}
} as any

export default config
30 changes: 24 additions & 6 deletions test/LegendsNft/LegendsNft.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import '@nomicfoundation/hardhat-chai-matchers'

import { expect } from 'chai'
import { hexlify } from 'ethers'
import { ethers } from 'hardhat'
Expand Down Expand Up @@ -32,7 +34,7 @@ describe('Legends nft', () => {
})

it('set base uri not owner', async () => {
await expect(legendsNftContract.connect(signer2).setBaseUri('')).to.be.rejectedWith(
await expect(legendsNftContract.connect(signer2).setBaseUri('')).to.be.revertedWith(
'Ownable: caller is not the owner'
)
})
Expand All @@ -43,25 +45,41 @@ describe('Legends nft', () => {

await expect(
legendsNftContract.transferFrom(signer.address, signer2.address, BigInt(signer.address))
).to.be.rejectedWith('Soulbound: cannot transfer nft')
).to.be.revertedWith('Soulbound: cannot transfer nft')
await expect(
legendsNftContract.safeTransferFrom(signer.address, signer2.address, BigInt(signer.address))
).to.be.rejectedWith('Soulbound: cannot transfer nft')
).to.be.revertedWith('Soulbound: cannot transfer nft')
await expect(
legendsNftContract['safeTransferFrom(address,address,uint256,bytes)'](
signer.address,
signer2.address,
BigInt(signer.address),
ethers.toUtf8Bytes('asd')
)
).to.be.rejectedWith('Soulbound: cannot transfer nft')
).to.be.revertedWith('Soulbound: cannot transfer nft')
await expect(
legendsNftContract.approve(signer2.address, BigInt(signer.address))
).to.be.rejectedWith('Soulbound: cannot approve token transfer')
).to.be.revertedWith('Soulbound: cannot approve token transfer')

await expect(legendsNftContract.setApprovalForAll(signer2.address, true)).to.be.rejectedWith(
await expect(legendsNftContract.setApprovalForAll(signer2.address, true)).to.be.revertedWith(
'Soulbound: cannot set approval for all'
)

await legendsNftContract.setAllowTransfer(true)
await expect(
legendsNftContract.connect(signer2).burn(BigInt(signer.address))
).to.be.revertedWith('You cannot burn this NFT.')

await legendsNftContract.burn(BigInt(signer.address))
await legendsNftContract.burn(BigInt(signer2.address))
await expect(legendsNftContract.ownerOf(BigInt(signer.address))).to.be.revertedWith(
'ERC721: invalid token ID'
)
await expect(legendsNftContract.ownerOf(BigInt(signer2.address))).to.be.revertedWith(
'ERC721: invalid token ID'
)
await legendsNftContract.mint(2)
expect(await legendsNftContract.ownerOf(BigInt(signer.address))).eq(signer.address)
})
it('opensea update', async () => {
const supportsInterface721 = await legendsNftContract.supportsInterface(hexlify('0x80ac58cd'))
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"noEmitOnError": true,
"noEmitHelpers": true,
"lib": ["ESNext", "DOM"],
"types": ["node", "jest"],
"types": ["node", "jest", "mocha"],
"target": "ESNext",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
Expand Down
Loading