forked from DecentralizedClimateFoundation/DCIPs
762 lines
24 KiB
TypeScript
762 lines
24 KiB
TypeScript
|
import { ethers } from "hardhat";
|
||
|
import { expect } from "chai";
|
||
|
import {
|
||
|
ERC721ReceiverMock,
|
||
|
MultiAssetReceiverMock,
|
||
|
MultiAssetTokenMock,
|
||
|
NonReceiverMock,
|
||
|
MultiAssetRenderUtils,
|
||
|
} from "../typechain-types";
|
||
|
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
|
||
|
import { BigNumber } from "ethers";
|
||
|
|
||
|
describe("MultiAsset", async () => {
|
||
|
let token: MultiAssetTokenMock;
|
||
|
let renderUtils: MultiAssetRenderUtils;
|
||
|
let nonReceiver: NonReceiverMock;
|
||
|
let receiver721: ERC721ReceiverMock;
|
||
|
|
||
|
let owner: SignerWithAddress;
|
||
|
let addrs: SignerWithAddress[];
|
||
|
|
||
|
const name = "RmrkTest";
|
||
|
const symbol = "RMRKTST";
|
||
|
|
||
|
const metaURIDefault = "metaURI";
|
||
|
|
||
|
beforeEach(async () => {
|
||
|
const [signersOwner, ...signersAddr] = await ethers.getSigners();
|
||
|
owner = signersOwner;
|
||
|
addrs = signersAddr;
|
||
|
|
||
|
const multiassetFactory = await ethers.getContractFactory(
|
||
|
"MultiAssetTokenMock"
|
||
|
);
|
||
|
token = await multiassetFactory.deploy(name, symbol);
|
||
|
await token.deployed();
|
||
|
|
||
|
const renderFactory = await ethers.getContractFactory(
|
||
|
"MultiAssetRenderUtils"
|
||
|
);
|
||
|
renderUtils = await renderFactory.deploy();
|
||
|
await renderUtils.deployed();
|
||
|
});
|
||
|
|
||
|
describe("Init", async function () {
|
||
|
it("Name", async function () {
|
||
|
expect(await token.name()).to.equal(name);
|
||
|
});
|
||
|
|
||
|
it("Symbol", async function () {
|
||
|
expect(await token.symbol()).to.equal(symbol);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe("ERC165 check", async function () {
|
||
|
it("can support IERC165", async function () {
|
||
|
expect(await token.supportsInterface("0x01ffc9a7")).to.equal(true);
|
||
|
});
|
||
|
|
||
|
it("can support IERC721", async function () {
|
||
|
expect(await token.supportsInterface("0x80ac58cd")).to.equal(true);
|
||
|
});
|
||
|
|
||
|
it("can support IERC5773", async function () {
|
||
|
expect(await token.supportsInterface("0x06b4329a")).to.equal(true);
|
||
|
});
|
||
|
|
||
|
it("cannot support other interfaceId", async function () {
|
||
|
expect(await token.supportsInterface("0xffffffff")).to.equal(false);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe("Check OnReceived ERC721 and Multiasset", async function () {
|
||
|
it("Revert on transfer to non onERC721/onMultiasset implementer", async function () {
|
||
|
const tokenId = 1;
|
||
|
await token.mint(owner.address, tokenId);
|
||
|
|
||
|
const NonReceiver = await ethers.getContractFactory("NonReceiverMock");
|
||
|
nonReceiver = await NonReceiver.deploy();
|
||
|
await nonReceiver.deployed();
|
||
|
|
||
|
await expect(
|
||
|
token
|
||
|
.connect(owner)
|
||
|
["safeTransferFrom(address,address,uint256)"](
|
||
|
owner.address,
|
||
|
nonReceiver.address,
|
||
|
1
|
||
|
)
|
||
|
).to.be.revertedWith(
|
||
|
"MultiAsset: transfer to non ERC721 Receiver implementer"
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it("onERC721Received callback on transfer", async function () {
|
||
|
const tokenId = 1;
|
||
|
await token.mint(owner.address, tokenId);
|
||
|
|
||
|
const ERC721Receiver = await ethers.getContractFactory(
|
||
|
"ERC721ReceiverMock"
|
||
|
);
|
||
|
receiver721 = await ERC721Receiver.deploy();
|
||
|
await receiver721.deployed();
|
||
|
|
||
|
await token
|
||
|
.connect(owner)
|
||
|
["safeTransferFrom(address,address,uint256)"](
|
||
|
owner.address,
|
||
|
receiver721.address,
|
||
|
1
|
||
|
);
|
||
|
expect(await token.ownerOf(1)).to.equal(receiver721.address);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe("Asset storage", async function () {
|
||
|
it("can add asset", async function () {
|
||
|
const id = 10;
|
||
|
|
||
|
await expect(token.addAssetEntry(id, metaURIDefault))
|
||
|
.to.emit(token, "AssetSet")
|
||
|
.withArgs(id);
|
||
|
});
|
||
|
|
||
|
it("cannot get non existing asset", async function () {
|
||
|
const tokenId = 1;
|
||
|
const resId = 10;
|
||
|
await token.mint(owner.address, tokenId);
|
||
|
await expect(token.getAssetMetadata(tokenId, resId)).to.be.revertedWith(
|
||
|
"MultiAsset: Token does not have asset"
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it("cannot add asset entry if not issuer", async function () {
|
||
|
const id = 10;
|
||
|
await expect(
|
||
|
token.connect(addrs[1]).addAssetEntry(id, metaURIDefault)
|
||
|
).to.be.revertedWith("RMRK: Only issuer");
|
||
|
});
|
||
|
|
||
|
it("can set and get issuer", async function () {
|
||
|
const newIssuerAddr = addrs[1].address;
|
||
|
expect(await token.getIssuer()).to.equal(owner.address);
|
||
|
|
||
|
await token.setIssuer(newIssuerAddr);
|
||
|
expect(await token.getIssuer()).to.equal(newIssuerAddr);
|
||
|
});
|
||
|
|
||
|
it("cannot set issuer if not issuer", async function () {
|
||
|
const newIssuer = addrs[1];
|
||
|
await expect(
|
||
|
token.connect(newIssuer).setIssuer(newIssuer.address)
|
||
|
).to.be.revertedWith("RMRK: Only issuer");
|
||
|
});
|
||
|
|
||
|
it("cannot overwrite asset", async function () {
|
||
|
const id = 10;
|
||
|
|
||
|
await token.addAssetEntry(id, metaURIDefault);
|
||
|
await expect(token.addAssetEntry(id, metaURIDefault)).to.be.revertedWith(
|
||
|
"RMRK: asset already exists"
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it("cannot add asset with id 0", async function () {
|
||
|
const id = ethers.utils.hexZeroPad("0x0", 8);
|
||
|
|
||
|
await expect(token.addAssetEntry(id, metaURIDefault)).to.be.revertedWith(
|
||
|
"RMRK: Write to zero"
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it("cannot add same asset twice", async function () {
|
||
|
const id = 10;
|
||
|
|
||
|
await expect(token.addAssetEntry(id, metaURIDefault))
|
||
|
.to.emit(token, "AssetSet")
|
||
|
.withArgs(id);
|
||
|
|
||
|
await expect(token.addAssetEntry(id, metaURIDefault)).to.be.revertedWith(
|
||
|
"RMRK: asset already exists"
|
||
|
);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe("Adding assets", async function () {
|
||
|
it("can add asset to token", async function () {
|
||
|
const resId = 1;
|
||
|
const resId2 = 2;
|
||
|
const tokenId = 1;
|
||
|
|
||
|
await token.mint(owner.address, tokenId);
|
||
|
await addAssets([resId, resId2]);
|
||
|
await expect(token.addAssetToToken(tokenId, resId, 0)).to.emit(
|
||
|
token,
|
||
|
"AssetAddedToTokens"
|
||
|
);
|
||
|
await expect(token.addAssetToToken(tokenId, resId2, 0)).to.emit(
|
||
|
token,
|
||
|
"AssetAddedToTokens"
|
||
|
);
|
||
|
|
||
|
const pendingIds = await token.getPendingAssets(tokenId);
|
||
|
expect(
|
||
|
await renderUtils.getAssetsById(token.address, tokenId, pendingIds)
|
||
|
).to.be.eql([metaURIDefault, metaURIDefault]);
|
||
|
});
|
||
|
|
||
|
it("cannot add non existing asset to token", async function () {
|
||
|
const resId = 1;
|
||
|
const tokenId = 1;
|
||
|
|
||
|
await token.mint(owner.address, tokenId);
|
||
|
await expect(token.addAssetToToken(tokenId, resId, 0)).to.be.revertedWith(
|
||
|
"MultiAsset: Asset not found in storage"
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it("can add asset to non existing token and it is pending when minted", async function () {
|
||
|
const resId = 1;
|
||
|
const tokenId = 1;
|
||
|
await addAssets([resId]);
|
||
|
|
||
|
await token.addAssetToToken(tokenId, resId, 0);
|
||
|
await token.mint(owner.address, tokenId);
|
||
|
expect(await token.getPendingAssets(tokenId)).to.eql([
|
||
|
ethers.BigNumber.from(resId),
|
||
|
]);
|
||
|
});
|
||
|
|
||
|
it("cannot add asset twice to the same token", async function () {
|
||
|
const resId = 1;
|
||
|
const tokenId = 1;
|
||
|
|
||
|
await token.mint(owner.address, tokenId);
|
||
|
await addAssets([resId]);
|
||
|
await token.addAssetToToken(tokenId, resId, 0);
|
||
|
await expect(
|
||
|
token.addAssetToToken(tokenId, ethers.BigNumber.from(resId), 0)
|
||
|
).to.be.revertedWith("MultiAsset: Asset already exists on token");
|
||
|
});
|
||
|
|
||
|
it("cannot add too many assets to the same token", async function () {
|
||
|
const tokenId = 1;
|
||
|
|
||
|
await token.mint(owner.address, tokenId);
|
||
|
for (let i = 1; i <= 128; i++) {
|
||
|
await addAssets([i]);
|
||
|
await token.addAssetToToken(tokenId, i, 0);
|
||
|
}
|
||
|
|
||
|
// Now it's full, next should fail
|
||
|
const resId = 129;
|
||
|
await addAssets([resId]);
|
||
|
await expect(token.addAssetToToken(tokenId, resId, 0)).to.be.revertedWith(
|
||
|
"MultiAsset: Max pending assets reached"
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it("can add same asset to 2 different tokens", async function () {
|
||
|
const resId = 1;
|
||
|
const tokenId1 = 1;
|
||
|
const tokenId2 = 2;
|
||
|
|
||
|
await token.mint(owner.address, tokenId1);
|
||
|
await token.mint(owner.address, tokenId2);
|
||
|
await addAssets([resId]);
|
||
|
await token.addAssetToToken(tokenId1, resId, 0);
|
||
|
await token.addAssetToToken(tokenId2, resId, 0);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe("Accepting assets", async function () {
|
||
|
it("can accept asset if owner", async function () {
|
||
|
const { tokenOwner, tokenId } = await mintSampleToken();
|
||
|
const approved = tokenOwner;
|
||
|
|
||
|
await checkAcceptFromAddress(approved, tokenId);
|
||
|
});
|
||
|
|
||
|
it("can accept asset if approved for assets", async function () {
|
||
|
const { tokenId } = await mintSampleToken();
|
||
|
const approved = addrs[1];
|
||
|
|
||
|
await token.approveForAssets(approved.address, tokenId);
|
||
|
await checkAcceptFromAddress(approved, tokenId);
|
||
|
});
|
||
|
|
||
|
it("can accept asset if approved for assets for all", async function () {
|
||
|
const { tokenId } = await mintSampleToken();
|
||
|
const approved = addrs[2];
|
||
|
|
||
|
await token.setApprovalForAllForAssets(approved.address, true);
|
||
|
await checkAcceptFromAddress(approved, tokenId);
|
||
|
});
|
||
|
|
||
|
it("can accept multiple assets", async function () {
|
||
|
const resId = 1;
|
||
|
const resId2 = 2;
|
||
|
const tokenId = 1;
|
||
|
|
||
|
await token.mint(owner.address, tokenId);
|
||
|
await addAssets([resId, resId2]);
|
||
|
await token.addAssetToToken(tokenId, resId, 0);
|
||
|
await token.addAssetToToken(tokenId, resId2, 0);
|
||
|
await expect(token.acceptAsset(tokenId, 1, resId2))
|
||
|
.to.emit(token, "AssetAccepted")
|
||
|
.withArgs(tokenId, resId2, 0);
|
||
|
await expect(token.acceptAsset(tokenId, 0, resId))
|
||
|
.to.emit(token, "AssetAccepted")
|
||
|
.withArgs(tokenId, resId, 0);
|
||
|
|
||
|
expect(await token.getPendingAssets(tokenId)).to.be.eql([]);
|
||
|
|
||
|
const activeIds = await token.getActiveAssets(tokenId);
|
||
|
expect(
|
||
|
await renderUtils.getAssetsById(token.address, tokenId, activeIds)
|
||
|
).to.eql([metaURIDefault, metaURIDefault]);
|
||
|
});
|
||
|
|
||
|
it("cannot accept asset twice", async function () {
|
||
|
const resId = 1;
|
||
|
const tokenId = 1;
|
||
|
|
||
|
await token.mint(owner.address, tokenId);
|
||
|
await addAssets([resId]);
|
||
|
await token.addAssetToToken(tokenId, resId, 0);
|
||
|
await token.acceptAsset(tokenId, 0, resId);
|
||
|
});
|
||
|
|
||
|
it("cannot accept asset if not owner", async function () {
|
||
|
const resId = 1;
|
||
|
const tokenId = 1;
|
||
|
|
||
|
await token.mint(owner.address, tokenId);
|
||
|
await addAssets([resId]);
|
||
|
await token.addAssetToToken(tokenId, resId, 0);
|
||
|
await expect(
|
||
|
token.connect(addrs[1]).acceptAsset(tokenId, 0, resId)
|
||
|
).to.be.revertedWith("MultiAsset: not owner or approved");
|
||
|
});
|
||
|
|
||
|
it("cannot accept non existing asset", async function () {
|
||
|
const tokenId = 1;
|
||
|
|
||
|
await token.mint(owner.address, tokenId);
|
||
|
await expect(token.acceptAsset(tokenId, 0, 1)).to.be.revertedWith(
|
||
|
"MultiAsset: index out of bounds"
|
||
|
);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe("Overwriting assets", async function () {
|
||
|
it("can add asset to token overwritting an existing one", async function () {
|
||
|
const resId = 1;
|
||
|
const resId2 = 2;
|
||
|
const tokenId = 1;
|
||
|
|
||
|
await token.mint(owner.address, tokenId);
|
||
|
await addAssets([resId, resId2]);
|
||
|
await token.addAssetToToken(tokenId, resId, 0);
|
||
|
await token.acceptAsset(tokenId, 0, resId);
|
||
|
|
||
|
// Add new asset to overwrite the first, and accept
|
||
|
const activeAssets = await token.getActiveAssets(tokenId);
|
||
|
await expect(token.addAssetToToken(tokenId, resId2, activeAssets[0]))
|
||
|
.to.emit(token, "AssetAddedToTokens")
|
||
|
.withArgs([tokenId], resId2, resId);
|
||
|
const pendingAssets = await token.getPendingAssets(tokenId);
|
||
|
|
||
|
expect(
|
||
|
await token.getAssetReplacements(tokenId, pendingAssets[0])
|
||
|
).to.eql(activeAssets[0]);
|
||
|
await expect(token.acceptAsset(tokenId, 0, resId2))
|
||
|
.to.emit(token, "AssetAccepted")
|
||
|
.withArgs(tokenId, resId2, resId);
|
||
|
|
||
|
const activeIds = await token.getActiveAssets(tokenId);
|
||
|
expect(
|
||
|
await renderUtils.getAssetsById(token.address, tokenId, activeIds)
|
||
|
).to.eql([metaURIDefault]);
|
||
|
// Overwrite should be gone
|
||
|
expect(
|
||
|
await token.getAssetReplacements(tokenId, pendingAssets[0])
|
||
|
).to.eql(ethers.BigNumber.from(0));
|
||
|
});
|
||
|
|
||
|
it("can overwrite non existing asset to token, it could have been deleted", async function () {
|
||
|
const resId = 1;
|
||
|
const tokenId = 1;
|
||
|
|
||
|
await token.mint(owner.address, tokenId);
|
||
|
await addAssets([resId]);
|
||
|
await token.addAssetToToken(
|
||
|
tokenId,
|
||
|
resId,
|
||
|
ethers.utils.hexZeroPad("0x1", 8)
|
||
|
);
|
||
|
await token.acceptAsset(tokenId, 0, resId);
|
||
|
|
||
|
const activeIds = await token.getActiveAssets(tokenId);
|
||
|
expect(
|
||
|
await renderUtils.getAssetsById(token.address, tokenId, activeIds)
|
||
|
).to.eql([metaURIDefault]);
|
||
|
});
|
||
|
|
||
|
it("can overwrite an existing asset after 3 have been added and 1 accepted", async function () {
|
||
|
const resId = 1;
|
||
|
const resId2 = 2;
|
||
|
const resId3 = 3;
|
||
|
const tokenId = 1;
|
||
|
|
||
|
await token.mint(owner.address, tokenId);
|
||
|
await addAssets([resId, resId2, resId3]);
|
||
|
await expect(token.addAssetToToken(tokenId, resId, 0)).to.emit(
|
||
|
token,
|
||
|
"AssetAddedToTokens"
|
||
|
);
|
||
|
await expect(token.addAssetToToken(tokenId, resId2, 0)).to.emit(
|
||
|
token,
|
||
|
"AssetAddedToTokens"
|
||
|
);
|
||
|
await expect(token.addAssetToToken(tokenId, resId3, resId2))
|
||
|
.to.emit(token, "AssetAddedToTokens")
|
||
|
.withArgs([tokenId], resId3, resId2);
|
||
|
|
||
|
const pendingIds = await token.getPendingAssets(tokenId);
|
||
|
|
||
|
expect(
|
||
|
await renderUtils.getAssetsById(token.address, tokenId, pendingIds)
|
||
|
).to.be.eql([metaURIDefault, metaURIDefault, metaURIDefault]);
|
||
|
|
||
|
await expect(token.acceptAsset(tokenId, 1, resId2))
|
||
|
.to.emit(token, "AssetAccepted")
|
||
|
.withArgs(tokenId, resId2, 0);
|
||
|
|
||
|
await expect(token.acceptAsset(tokenId, 1, resId3))
|
||
|
.to.emit(token, "AssetAccepted")
|
||
|
.withArgs(tokenId, resId3, 2);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe("Rejecting assets", async function () {
|
||
|
it("can reject asset if owner", async function () {
|
||
|
const { tokenOwner, tokenId } = await mintSampleToken();
|
||
|
const approved = tokenOwner;
|
||
|
|
||
|
await checkRejectFromAddress(approved, tokenId);
|
||
|
});
|
||
|
|
||
|
it("can reject asset if approved for assets", async function () {
|
||
|
const { tokenId } = await mintSampleToken();
|
||
|
const approved = addrs[1];
|
||
|
|
||
|
await token.approveForAssets(approved.address, tokenId);
|
||
|
await checkRejectFromAddress(approved, tokenId);
|
||
|
});
|
||
|
|
||
|
it("can reject asset if approved for assets for all", async function () {
|
||
|
const { tokenId } = await mintSampleToken();
|
||
|
const approved = addrs[2];
|
||
|
|
||
|
await token.setApprovalForAllForAssets(approved.address, true);
|
||
|
await checkRejectFromAddress(approved, tokenId);
|
||
|
});
|
||
|
|
||
|
it("can reject all assets if owner", async function () {
|
||
|
const { tokenOwner, tokenId } = await mintSampleToken();
|
||
|
const approved = tokenOwner;
|
||
|
|
||
|
await checkRejectAllFromAddress(approved, tokenId);
|
||
|
});
|
||
|
|
||
|
it("can reject all assets if approved for assets", async function () {
|
||
|
const { tokenId } = await mintSampleToken();
|
||
|
const approved = addrs[1];
|
||
|
|
||
|
await token.approveForAssets(approved.address, tokenId);
|
||
|
await checkRejectAllFromAddress(approved, tokenId);
|
||
|
});
|
||
|
|
||
|
it("can reject all assets if approved for assets for all", async function () {
|
||
|
const { tokenId } = await mintSampleToken();
|
||
|
const approved = addrs[2];
|
||
|
|
||
|
await token.setApprovalForAllForAssets(approved.address, true);
|
||
|
await checkRejectAllFromAddress(approved, tokenId);
|
||
|
});
|
||
|
|
||
|
it("can reject asset and overwrites are cleared", async function () {
|
||
|
const resId = 1;
|
||
|
const resId2 = 2;
|
||
|
const tokenId = 1;
|
||
|
|
||
|
await token.mint(owner.address, tokenId);
|
||
|
await addAssets([resId, resId2]);
|
||
|
await token.addAssetToToken(tokenId, resId, 0);
|
||
|
await token.acceptAsset(tokenId, 0, resId);
|
||
|
|
||
|
// Will try to overwrite but we reject it
|
||
|
await token.addAssetToToken(tokenId, resId2, resId);
|
||
|
await token.rejectAsset(tokenId, 0, resId2);
|
||
|
|
||
|
expect(await token.getAssetReplacements(tokenId, resId2)).to.eql(
|
||
|
ethers.BigNumber.from(0)
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it("can reject all assets and overwrites are cleared", async function () {
|
||
|
const resId = 1;
|
||
|
const resId2 = 2;
|
||
|
const tokenId = 1;
|
||
|
|
||
|
await token.mint(owner.address, tokenId);
|
||
|
await addAssets([resId, resId2]);
|
||
|
await token.addAssetToToken(tokenId, resId, 0);
|
||
|
await token.acceptAsset(tokenId, 0, resId);
|
||
|
|
||
|
// Will try to overwrite but we reject all
|
||
|
await token.addAssetToToken(tokenId, resId2, resId);
|
||
|
await token.rejectAllAssets(tokenId, 1);
|
||
|
|
||
|
expect(await token.getAssetReplacements(tokenId, resId2)).to.eql(
|
||
|
ethers.BigNumber.from(0)
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it("can reject all pending assets at max capacity", async function () {
|
||
|
const tokenId = 1;
|
||
|
const resArr = [];
|
||
|
|
||
|
for (let i = 1; i < 128; i++) {
|
||
|
resArr.push(i);
|
||
|
}
|
||
|
|
||
|
await token.mint(owner.address, tokenId);
|
||
|
await addAssets(resArr);
|
||
|
|
||
|
for (let i = 1; i < 128; i++) {
|
||
|
await token.addAssetToToken(tokenId, i, 1);
|
||
|
}
|
||
|
await token.rejectAllAssets(tokenId, 128);
|
||
|
|
||
|
expect(await token.getAssetReplacements(1, 2)).to.eql(
|
||
|
ethers.BigNumber.from(0)
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it("cannot reject asset twice", async function () {
|
||
|
const resId = 1;
|
||
|
const tokenId = 1;
|
||
|
|
||
|
await token.mint(owner.address, tokenId);
|
||
|
await addAssets([resId]);
|
||
|
await token.addAssetToToken(tokenId, resId, 0);
|
||
|
await token.rejectAsset(tokenId, 0, resId);
|
||
|
});
|
||
|
|
||
|
it("cannot reject asset nor reject all if not owner", async function () {
|
||
|
const resId = 1;
|
||
|
const tokenId = 1;
|
||
|
|
||
|
await token.mint(owner.address, tokenId);
|
||
|
await addAssets([resId]);
|
||
|
await token.addAssetToToken(tokenId, resId, 0);
|
||
|
|
||
|
await expect(
|
||
|
token.connect(addrs[1]).rejectAsset(tokenId, 0, resId)
|
||
|
).to.be.revertedWith("MultiAsset: not owner or approved");
|
||
|
await expect(
|
||
|
token.connect(addrs[1]).rejectAllAssets(tokenId, 1)
|
||
|
).to.be.revertedWith("MultiAsset: not owner or approved");
|
||
|
});
|
||
|
|
||
|
it("cannot reject non existing asset", async function () {
|
||
|
const tokenId = 1;
|
||
|
|
||
|
await token.mint(owner.address, tokenId);
|
||
|
await expect(token.rejectAsset(tokenId, 0, 1)).to.be.revertedWith(
|
||
|
"MultiAsset: index out of bounds"
|
||
|
);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe("Priorities", async function () {
|
||
|
it("can set and get priorities", async function () {
|
||
|
const tokenId = 1;
|
||
|
await addAssetsToToken(tokenId);
|
||
|
|
||
|
expect(await token.getActiveAssetPriorities(tokenId)).to.be.eql([BigNumber.from(0), BigNumber.from(1)]);
|
||
|
await expect(token.setPriority(tokenId, [2, 1]))
|
||
|
.to.emit(token, "AssetPrioritySet")
|
||
|
.withArgs(tokenId);
|
||
|
expect(await token.getActiveAssetPriorities(tokenId)).to.be.eql([BigNumber.from(2), BigNumber.from(1)]);
|
||
|
});
|
||
|
|
||
|
it("cannot set priorities for non owned token", async function () {
|
||
|
const tokenId = 1;
|
||
|
await addAssetsToToken(tokenId);
|
||
|
await expect(
|
||
|
token.connect(addrs[1]).setPriority(tokenId, [2, 1])
|
||
|
).to.be.revertedWith("MultiAsset: not owner or approved");
|
||
|
});
|
||
|
|
||
|
it("cannot set different number of priorities", async function () {
|
||
|
const tokenId = 1;
|
||
|
await addAssetsToToken(tokenId);
|
||
|
await expect(
|
||
|
token.connect(addrs[1]).setPriority(tokenId, [1])
|
||
|
).to.be.revertedWith("MultiAsset: Bad priority list length");
|
||
|
await expect(
|
||
|
token.connect(addrs[1]).setPriority(tokenId, [2, 1, 3])
|
||
|
).to.be.revertedWith("MultiAsset: Bad priority list length");
|
||
|
});
|
||
|
|
||
|
it("cannot set priorities for non existing token", async function () {
|
||
|
const tokenId = 1;
|
||
|
await expect(
|
||
|
token.connect(addrs[1]).setPriority(tokenId, [])
|
||
|
).to.be.revertedWith("MultiAsset: approved query for nonexistent token");
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe("Approval Cleaning", async function () {
|
||
|
it("cleans token and assets approvals on transfer", async function () {
|
||
|
const tokenId = 1;
|
||
|
const tokenOwner = addrs[1];
|
||
|
const newOwner = addrs[2];
|
||
|
const approved = addrs[3];
|
||
|
await token.mint(tokenOwner.address, tokenId);
|
||
|
await token.connect(tokenOwner).approve(approved.address, tokenId);
|
||
|
await token
|
||
|
.connect(tokenOwner)
|
||
|
.approveForAssets(approved.address, tokenId);
|
||
|
|
||
|
expect(await token.getApproved(tokenId)).to.eql(approved.address);
|
||
|
expect(await token.getApprovedForAssets(tokenId)).to.eql(
|
||
|
approved.address
|
||
|
);
|
||
|
|
||
|
await token.connect(tokenOwner).transfer(newOwner.address, tokenId);
|
||
|
|
||
|
expect(await token.getApproved(tokenId)).to.eql(
|
||
|
ethers.constants.AddressZero
|
||
|
);
|
||
|
expect(await token.getApprovedForAssets(tokenId)).to.eql(
|
||
|
ethers.constants.AddressZero
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it("cleans token and assets approvals on burn", async function () {
|
||
|
const tokenId = 1;
|
||
|
const tokenOwner = addrs[1];
|
||
|
const approved = addrs[3];
|
||
|
await token.mint(tokenOwner.address, tokenId);
|
||
|
await token.connect(tokenOwner).approve(approved.address, tokenId);
|
||
|
await token
|
||
|
.connect(tokenOwner)
|
||
|
.approveForAssets(approved.address, tokenId);
|
||
|
|
||
|
expect(await token.getApproved(tokenId)).to.eql(approved.address);
|
||
|
expect(await token.getApprovedForAssets(tokenId)).to.eql(
|
||
|
approved.address
|
||
|
);
|
||
|
|
||
|
await token.connect(tokenOwner).burn(tokenId);
|
||
|
|
||
|
await expect(token.getApproved(tokenId)).to.be.revertedWith(
|
||
|
"MultiAsset: approved query for nonexistent token"
|
||
|
);
|
||
|
await expect(token.getApprovedForAssets(tokenId)).to.be.revertedWith(
|
||
|
"MultiAsset: approved query for nonexistent token"
|
||
|
);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
async function mintSampleToken(): Promise<{
|
||
|
tokenOwner: SignerWithAddress;
|
||
|
tokenId: number;
|
||
|
}> {
|
||
|
const tokenOwner = owner;
|
||
|
const tokenId = 1;
|
||
|
await token.mint(tokenOwner.address, tokenId);
|
||
|
|
||
|
return { tokenOwner, tokenId };
|
||
|
}
|
||
|
|
||
|
async function addAssets(ids: number[]): Promise<void> {
|
||
|
ids.forEach(async (resId) => {
|
||
|
await token.addAssetEntry(resId, metaURIDefault);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
async function addAssetsToToken(tokenId: number): Promise<void> {
|
||
|
const resId = 1;
|
||
|
const resId2 = 2;
|
||
|
await token.mint(owner.address, tokenId);
|
||
|
await addAssets([resId, resId2]);
|
||
|
await token.addAssetToToken(tokenId, resId, 0);
|
||
|
await token.addAssetToToken(tokenId, resId2, 0);
|
||
|
await token.acceptAsset(tokenId, 0, resId);
|
||
|
await token.acceptAsset(tokenId, 0, resId2);
|
||
|
}
|
||
|
|
||
|
async function checkAcceptFromAddress(
|
||
|
accepter: SignerWithAddress,
|
||
|
tokenId: number
|
||
|
): Promise<void> {
|
||
|
const resId = 1;
|
||
|
|
||
|
await addAssets([resId]);
|
||
|
await token.addAssetToToken(tokenId, resId, 0);
|
||
|
await expect(token.connect(accepter).acceptAsset(tokenId, 0, resId))
|
||
|
.to.emit(token, "AssetAccepted")
|
||
|
.withArgs(tokenId, resId, 0);
|
||
|
|
||
|
expect(await token.getPendingAssets(tokenId)).to.be.eql([]);
|
||
|
|
||
|
const activeIds = await token.getActiveAssets(tokenId);
|
||
|
expect(
|
||
|
await renderUtils.getAssetsById(token.address, tokenId, activeIds)
|
||
|
).to.eql([metaURIDefault]);
|
||
|
}
|
||
|
|
||
|
async function checkRejectFromAddress(
|
||
|
rejecter: SignerWithAddress,
|
||
|
tokenId: number
|
||
|
): Promise<void> {
|
||
|
const resId = 1;
|
||
|
|
||
|
await addAssets([resId]);
|
||
|
await token.addAssetToToken(tokenId, resId, 0);
|
||
|
|
||
|
await expect(
|
||
|
token.connect(rejecter).rejectAsset(tokenId, 0, resId)
|
||
|
).to.emit(token, "AssetRejected");
|
||
|
|
||
|
expect(await token.getPendingAssets(tokenId)).to.be.eql([]);
|
||
|
expect(await token.getActiveAssets(tokenId)).to.be.eql([]);
|
||
|
}
|
||
|
|
||
|
async function checkRejectAllFromAddress(
|
||
|
rejecter: SignerWithAddress,
|
||
|
tokenId: number
|
||
|
): Promise<void> {
|
||
|
const resId = 1;
|
||
|
const resId2 = 2;
|
||
|
|
||
|
await addAssets([resId, resId2]);
|
||
|
await token.addAssetToToken(tokenId, resId, 0);
|
||
|
await token.addAssetToToken(tokenId, resId2, 0);
|
||
|
|
||
|
await expect(token.connect(rejecter).rejectAllAssets(tokenId, 2)).to.emit(
|
||
|
token,
|
||
|
"AssetRejected"
|
||
|
);
|
||
|
|
||
|
expect(await token.getPendingAssets(tokenId)).to.be.eql([]);
|
||
|
expect(await token.getActiveAssets(tokenId)).to.be.eql([]);
|
||
|
}
|
||
|
});
|