-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: susbcription 1155 without blocks
- Loading branch information
Showing
3 changed files
with
324 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
131 changes: 131 additions & 0 deletions
131
contracts/token/erc1155/NFT1155SubscriptionWithoutBlocks.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
pragma solidity ^0.8.0; | ||
|
||
import '@openzeppelin/contracts-upgradeable/utils/introspection/ERC165StorageUpgradeable.sol'; | ||
import './NFT1155Upgradeable.sol'; | ||
// Copyright 2022 Nevermined AG. | ||
// SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) | ||
// Code is Apache-2.0 and docs are CC-BY-4.0 | ||
|
||
contract NFT1155SubscriptionWithoutBlocks is NFT1155Upgradeable { | ||
|
||
// struct MintedTokens { | ||
// uint256 amountMinted; // uint64 | ||
// uint256 expirationBlock; | ||
// uint256 mintBlock; | ||
// bool isMintOps; // true means mint, false means burn | ||
// } | ||
// | ||
// mapping(bytes32 => MintedTokens[]) internal _tokens; | ||
|
||
// It represents the NFT type. It is used to identify the NFT type in the Nevermined ecosystem | ||
// solhint-disable-next-line | ||
bytes32 public constant override nftType = keccak256('nft1155-subscription'); | ||
|
||
function initialize( | ||
address owner, | ||
address didRegistryAddress, | ||
string memory name_, | ||
string memory symbol_, | ||
string memory uri_, | ||
address nvmConfig_ | ||
) | ||
public | ||
override | ||
virtual | ||
initializer | ||
{ | ||
__NFT1155Upgradeable_init(owner, didRegistryAddress, name_, symbol_, uri_, nvmConfig_); | ||
} | ||
|
||
|
||
function mint(address to, uint256 tokenId, uint256 amount, bytes memory data) virtual override public { | ||
super.mint(to, tokenId, amount, data); | ||
} | ||
|
||
function burn(uint256 id, uint256 amount) override public { | ||
burn(_msgSender(), id, amount); | ||
} | ||
|
||
// solhint-disable-next-line | ||
function burn(address to, uint256 id, uint256 amount) override public { | ||
address _sender = _msgSender(); | ||
require(balanceOf(to, id) >= amount, 'ERC1155: burn amount exceeds balance'); | ||
require( | ||
isOperator(_sender) || // Or the DIDRegistry is burning the NFT | ||
to == _sender || // Or the NFT owner is _msgSender() | ||
nftRegistry.isDIDProvider(bytes32(id), _sender) || // Or the DID Provider (Node) is burning the NFT | ||
isApprovedForAll(to, _sender), // Or the _msgSender() is approved | ||
'ERC1155: caller is not owner nor approved' | ||
); | ||
|
||
// Update nftSupply | ||
_nftAttributes[id].nftSupply -= amount; | ||
// Register provenance event | ||
|
||
_burn(to, id, amount); | ||
} | ||
|
||
/** | ||
* @dev See {NFT1155Upgradeableable-balanceOf}. | ||
*/ | ||
function balanceOf(address account, uint256 tokenId) public view virtual override returns (uint256) { | ||
return super.balanceOf(account, tokenId); | ||
// bytes32 _key = _getTokenKey(account, tokenId); | ||
// uint256 _amountBurned; | ||
// uint256 _amountMinted; | ||
// for (uint index = 0; index < _tokens[_key].length; index++) { | ||
// if (_tokens[_key][index].mintBlock > 0 && | ||
// (_tokens[_key][index].expirationBlock == 0 || _tokens[_key][index].expirationBlock > block.number)) { | ||
// if (_tokens[_key][index].isMintOps) | ||
// _amountMinted += _tokens[_key][index].amountMinted; | ||
// else | ||
// _amountBurned += _tokens[_key][index].amountMinted; | ||
// } | ||
// } | ||
// | ||
// if (_amountBurned >= _amountMinted) | ||
// return 0; | ||
// else | ||
// return _amountMinted - _amountBurned; | ||
} | ||
|
||
// function whenWasMinted(address owner, uint256 tokenId) public view returns (uint256[] memory) { | ||
// bytes32 _key = _getTokenKey(owner, tokenId); | ||
// uint256[] memory _whenMinted = new uint256[](_tokens[_key].length); | ||
// for (uint index = 0; index < _tokens[_key].length; index++) { | ||
// _whenMinted[index] = _tokens[_key][index].mintBlock; | ||
// } | ||
// return _whenMinted; | ||
// } | ||
// | ||
// function getMintedEntries(address owner, uint256 tokenId) public view returns (MintedTokens[] memory) { | ||
// return _tokens[_getTokenKey(owner, tokenId)]; | ||
// } | ||
// | ||
// function _getTokenKey(address account, uint256 tokenId) internal pure returns (bytes32) { | ||
// return keccak256(abi.encode(account, tokenId)); | ||
// } | ||
|
||
function mintBatch( | ||
address to, | ||
uint256[] memory ids, | ||
uint256[] memory amounts, | ||
bytes memory data | ||
) external { | ||
require(ids.length == amounts.length, 'mintBatch: lengths do not match'); | ||
for (uint i = 0; i < ids.length; i++) { | ||
mint(to, ids[i], amounts[i], data); | ||
} | ||
} | ||
|
||
function burnBatch( | ||
address from, | ||
uint256[] memory ids, | ||
uint256[] memory amounts | ||
) external { | ||
require(ids.length == amounts.length, 'burnBatch: lengths do not match'); | ||
for (uint i = 0; i < ids.length; i++) { | ||
burn(from, ids[i], amounts[i]); | ||
} | ||
} | ||
} |
181 changes: 181 additions & 0 deletions
181
test/unit/token/NFT1155SubscriptionWithoutBlocks.Test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
/* eslint-env mocha */ | ||
/* eslint-disable no-console */ | ||
/* global artifacts, contract, describe, it */ | ||
|
||
const chai = require('chai') | ||
const { assert } = chai | ||
const chaiAsPromised = require('chai-as-promised') | ||
chai.use(chaiAsPromised) | ||
const { ethers } = require('hardhat') | ||
|
||
const DIDRegistry = artifacts.require('DIDRegistry') | ||
const TestERC1155 = artifacts.require('NFT1155SubscriptionWithoutBlocks') | ||
|
||
const testUtils = require('../../helpers/utils.js') | ||
const constants = require('../../helpers/constants.js') | ||
const increaseTime = require('../../helpers/increaseTime.js') | ||
const BigNumber = require('bignumber.js') | ||
|
||
contract('NFT1155 Subscription', (accounts) => { | ||
const web3 = global.web3 | ||
|
||
const didSeedExpiring = testUtils.generateId() | ||
const didSeedNonExpiring = testUtils.generateId() | ||
|
||
let tokenIdExpiring | ||
let tokenIdNonExpiring | ||
|
||
const amount = 1 | ||
const blocksExpiring = 10 | ||
const blocksNonExpiring = 0 | ||
const data = '0x' | ||
|
||
const checksum = testUtils.generateId() | ||
const url = 'https://raw.githubusercontent.com/nevermined-io/assets/main/images/logo/banner_logo.png' | ||
|
||
const [ | ||
owner, | ||
deployer, | ||
minter, | ||
account1, | ||
account2 | ||
] = accounts | ||
|
||
let nft | ||
let didRegistry | ||
|
||
async function setupTest() { | ||
const config = await artifacts.require('NeverminedConfig').new() | ||
await config.initialize(owner, owner, true) | ||
didRegistry = await DIDRegistry.new() | ||
await didRegistry.initialize(owner, constants.address.zero, constants.address.zero, config.address, constants.address.zero) | ||
|
||
nft = await TestERC1155.new({ from: deployer }) | ||
await nft.initialize(owner, didRegistry.address, 'TestERC1155', 'TEST', '', config.address, { from: owner }) | ||
|
||
await nft.setNvmConfigAddress(config.address, { from: owner }) | ||
await config.grantNVMOperatorRole(didRegistry.address, { from: owner }) | ||
await config.grantNVMOperatorRole(owner, { from: owner }) | ||
await config.grantNVMOperatorRole(minter, { from: owner }) | ||
} | ||
|
||
describe('Providers can burn', () => { | ||
const initialAmount = 10 | ||
let tokenId | ||
|
||
it('As a minter can register a DID without providers', async () => { | ||
await setupTest() | ||
const didSeed = testUtils.generateId() | ||
|
||
tokenId = await didRegistry.hashDID(didSeed, minter) | ||
await didRegistry.methods[ | ||
'registerMintableDID(bytes32,address,bytes32,address[],string,uint256,uint256,bool,bytes32,string,string)' | ||
](didSeed, nft.address, checksum, [], url, 0, 0, false, constants.activities.GENERATED, '', '', { from: minter }) | ||
|
||
await nft.methods[ | ||
'mint(address,uint256,uint256,bytes)' | ||
](account1, tokenId, initialAmount, data, { from: minter }) | ||
|
||
const balance = new BigNumber(await nft.balanceOf(account1, tokenId)) | ||
assert.strictEqual(balance.toNumber(), initialAmount) | ||
}) | ||
|
||
it('NFT holder can burn', async () => { | ||
await nft.methods[ | ||
'burn(address,uint256,uint256)' | ||
](account1, tokenId, 1, { from: account1 }) | ||
|
||
const balance = new BigNumber(await nft.balanceOf(account1, tokenId)) | ||
assert.strictEqual(balance.toNumber(), initialAmount - 1) | ||
}) | ||
|
||
it('Account can not burn unless is a provider', async () => { | ||
await assert.isRejected( | ||
nft.methods[ | ||
'burn(address,uint256,uint256)' | ||
](account1, tokenId, 1, { from: account2 }), | ||
'ERC1155: caller is not owner nor approved' | ||
) | ||
|
||
let balance = new BigNumber(await nft.balanceOf(account1, tokenId)) | ||
assert.strictEqual(balance.toNumber(), initialAmount - 1) | ||
|
||
await didRegistry.addDIDProvider(tokenId, account2, { from: minter }) | ||
|
||
await nft.methods[ | ||
'burn(address,uint256,uint256)' | ||
](account1, tokenId, 1, { from: account2 }) | ||
|
||
balance = new BigNumber(await nft.balanceOf(account1, tokenId)) | ||
assert.strictEqual(balance.toNumber(), initialAmount - 2) | ||
}) | ||
}) | ||
|
||
|
||
describe('Mint and burn', () => { | ||
it('New tokens can be minted and burned', async () => { | ||
await setupTest() | ||
|
||
let balance | ||
const didSeed3 = testUtils.generateId() | ||
const tokenId3 = await didRegistry.hashDID(didSeed3, minter) | ||
await didRegistry.methods[ | ||
'registerMintableDID(bytes32,address,bytes32,address[],string,uint256,uint256,bool,bytes32,string,string)' | ||
](didSeed3, nft.address, checksum, [], url, 0, 0, false, constants.activities.GENERATED, '', '', { from: minter }) | ||
|
||
const currentBlockNumber = await ethers.provider.getBlockNumber() | ||
|
||
// MINT 7 tokens | ||
await nft.methods[ | ||
'mint(address,uint256,uint256,bytes)' | ||
](account2, tokenId3, 7, data, { from: minter }) | ||
|
||
// Balance is 7 | ||
balance = new BigNumber(await nft.balanceOf(account2, tokenId3)) | ||
assert.strictEqual(balance.toNumber(), 7) | ||
|
||
// MINT 10 tokens | ||
await nft.methods[ | ||
'mint(address,uint256,uint256,bytes)' | ||
](account2, tokenId3, 10, data, { from: minter }) | ||
|
||
// Balance is 17 | ||
balance = new BigNumber(await nft.balanceOf(account2, tokenId3)) | ||
assert.strictEqual(balance.toNumber(), 17) | ||
|
||
// BURN 4 tokens | ||
await nft.methods[ | ||
'burn(address,uint256,uint256)' | ||
](account2, tokenId3, 4, { from: minter }) | ||
|
||
// Balance is 13 | ||
balance = new BigNumber(await nft.balanceOf(account2, tokenId3)) | ||
assert.strictEqual(balance.toNumber(), 13) | ||
|
||
// Batch mint | ||
const didSeed4 = testUtils.generateId() | ||
const tokenId4 = await didRegistry.hashDID(didSeed4, minter) | ||
await didRegistry.methods[ | ||
'registerMintableDID(bytes32,address,bytes32,address[],string,uint256,uint256,bool,bytes32,string,string)' | ||
](didSeed4, nft.address, checksum, [], url, 0, 0, false, constants.activities.GENERATED, '', '', { from: minter }) | ||
|
||
// Also test balance batch | ||
await nft.mintBatch(account2, [tokenId3, tokenId4], [10, 15], data, { from: minter }) | ||
balance = new BigNumber(await nft.balanceOf(account2, tokenId3)) | ||
let balance2 = new BigNumber(await nft.balanceOf(account2, tokenId4)) | ||
assert.strictEqual(balance.toNumber(), 23) | ||
assert.strictEqual(balance2.toNumber(), 15) | ||
|
||
const balances = (await nft.balanceOfBatch([account2, account2], [tokenId3, tokenId4])).map(a => new BigNumber(a).toNumber()) | ||
assert.strictEqual(balances[0], 23) | ||
assert.strictEqual(balances[1], 15) | ||
|
||
await nft.burnBatch(account2, [tokenId3, tokenId4], [22, 14], { from: minter }) | ||
balance = new BigNumber(await nft.balanceOf(account2, tokenId3)) | ||
balance2 = new BigNumber(await nft.balanceOf(account2, tokenId4)) | ||
assert.strictEqual(balance.toNumber(), 1) | ||
assert.strictEqual(balance2.toNumber(), 1) | ||
}) | ||
|
||
}) | ||
}) |