UNPKG

@daostack/arc-experimental

Version:

A platform for building DAOs

674 lines (579 loc) 20.5 kB
const helpers = require("./helpers"); const GenericScheme = artifacts.require("./GenericScheme.sol"); const ERC20Mock = artifacts.require("./ERC20Mock.sol"); const ERC721Mock = artifacts.require("./ERC721Mock.sol"); const IERC721ReceiverMock = artifacts.require("./IERC721ReceiverMock.sol"); const IERC721NonReceiverMock = artifacts.require("./IERC721NonReceiverMock"); const NFTManager = artifacts.require("./NFTManager.sol"); class GenericSchemeParams { constructor() {} } const VotingMachines = { GenesisProtocol: 'genesisProtocol', AbsoluteVote: 'absoluteVote' }; var registration; const setupGenericSchemeParams = async function( accounts, genesisProtocol, token, avatarAddress, contractToCall ) { var genericSchemeParams = new GenericSchemeParams(); if (genesisProtocol === true) { genericSchemeParams.votingMachine = await helpers.setupGenesisProtocol( accounts, token, helpers.NULL_ADDRESS ); genericSchemeParams.initdata = await new web3.eth.Contract( registration.genericScheme.abi ).methods .initialize( avatarAddress, genericSchemeParams.votingMachine.genesisProtocol.address, genericSchemeParams.votingMachine.uintArray, genericSchemeParams.votingMachine.voteOnBehalf, helpers.NULL_HASH, contractToCall ) .encodeABI(); } else { genericSchemeParams.votingMachine = await helpers.setupAbsoluteVote( helpers.NULL_ADDRESS, 50 ); genericSchemeParams.initdata = await new web3.eth.Contract( registration.genericScheme.abi ).methods .initialize( avatarAddress, genericSchemeParams.votingMachine.absoluteVote.address, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], helpers.NULL_ADDRESS, genericSchemeParams.votingMachine.params, contractToCall ) .encodeABI(); } return genericSchemeParams; }; const setup = async function( accounts, contractToCall = 0, genesisProtocol = false, tokenAddress = 0 ) { var testSetup = new helpers.TestSetup(); testSetup.standardTokenMock = await ERC20Mock.new(accounts[1], 100); registration = await helpers.registerImplementation(); testSetup.reputationArray = [20, 10, 70]; testSetup.proxyAdmin = accounts[5]; testSetup.genericSchemeParams = await setupGenericSchemeParams( accounts, genesisProtocol, tokenAddress, helpers.NULL_ADDRESS, contractToCall ); var permissions = "0x0000001f"; [testSetup.org,tx] = await helpers.setupOrganizationWithArraysDAOFactory(testSetup.proxyAdmin, accounts, registration, [accounts[0], accounts[1], accounts[2]], [1000,0,0], testSetup.reputationArray, 0, [web3.utils.fromAscii("GenericScheme")], testSetup.genericSchemeParams.initdata, [helpers.getBytesLength(testSetup.genericSchemeParams.initdata)], [permissions], "metaData"); testSetup.genericScheme = await GenericScheme.at(await helpers.getSchemeAddress(registration.daoFactory.address,tx)); return testSetup; }; const setupAndExecuteProposal = async (accounts, nftManager, callData, votingMachine) => { var standardTokenMock = await ERC20Mock.new(accounts[0], 1000); var testSetup = await setup( accounts, nftManager.address, votingMachine === VotingMachines.AbsoluteVote ? false : true, standardTokenMock.address ); await nftManager.transferOwnership(testSetup.org.avatar.address); var tx = await testSetup.genericScheme.proposeCall( callData, 0, helpers.NULL_HASH ); var proposalId = await helpers.getValueFromLogs(tx, "_proposalId"); // transfer some eth to avatar await web3.eth.sendTransaction({ from: accounts[0], to: testSetup.org.avatar.address, value: web3.utils.toWei("1", "ether"), }); tx = await testSetup.genericSchemeParams.votingMachine[votingMachine].vote( proposalId, 1, 0, helpers.NULL_ADDRESS, { from: accounts[2] } ); if (votingMachine === VotingMachines.GenesisProtocol) { await testSetup.genericScheme .getPastEvents("ProposalExecutedByVotingMachine", { fromBlock: tx.blockNumber, toBlock: "latest", }) .then(function(events) { assert.equal(events[0].event, "ProposalExecutedByVotingMachine"); assert.equal(events[0].args._param, 1); }); } }; const encodeSendNFTCall = async function( _recipient, _nftContract, _tokenId, _data, _actionMock ) { return await new web3.eth.Contract(_actionMock.abi).methods .sendNFT(_recipient, _nftContract, _tokenId, _data) .encodeABI(); // Change tottran }; const encodeSendNFTNoSafeGaurdsCall = async function( _recipient, _nftContract, _tokenId, _actionMock ) { return await new web3.eth.Contract(_actionMock.abi).methods .sendNFTNoSafeguards(_recipient, _nftContract, _tokenId) .encodeABI(); // Change tottran }; const assertEventParams = (event, params, expectedParams) => { for (let i = 0; i < params.length; i++) { expect(event.args[params[i]].toString()).to.be.equal(expectedParams[i].toString()); } }; const setupNFT = async (owner, nftMinter, nftSender) => { const nftMock = await ERC721Mock.new({ from: nftMinter }); await nftMock.initialize(nftMinter ,{ from: nftMinter }); const nftManager = await NFTManager.new({ from: owner }); await nftManager.initialize(owner, { from: owner }); await nftMock.mint(nftManager.address, 0, { from: nftMinter }); await nftMock.mint(nftSender, 1, { from: nftMinter }); const nftReceiverMock = await IERC721ReceiverMock.new({ from: nftMinter }); return { nftManager, nftMock, nftReceiverMock, }; }; const sendNFTData = "0x12345"; const emptyData = []; contract("NFTManager", function(accounts) { before(function() { helpers.etherForEveryone(accounts); }); it("NFT Manager is owned by owner", async function() { const [owner, nftMinter, nftSender] = accounts; const { nftManager } = await setupNFT(owner, nftMinter, nftSender); const actualOwner = await nftManager.owner(); expect(actualOwner).to.be.eq(owner); }); it("Valid NFT transfer succeeds when sent by owner, via raw transaction", async function() { const [owner, nftMinter, nftSender, nftRecipient] = accounts; const { nftManager, nftMock } = await setupNFT(owner, nftMinter, nftSender); const tokenId = 0; const call = await encodeSendNFTNoSafeGaurdsCall( nftRecipient, nftMock.address, tokenId, nftManager ); await NFTManager.web3.eth.sendTransaction({ from: owner, data: call, to: nftManager.address, }); // Verify it's success (the NFT is owned by nftRecipient) const actualOwner = await nftMock.ownerOf(tokenId); expect(actualOwner).to.be.eq(nftRecipient); }); it("Valid NFT transfer succeeds when sent by owner, via abi call", async function() { const [owner, nftMinter, nftSender, nftRecipient] = accounts; const { nftManager, nftMock } = await setupNFT(owner, nftMinter, nftSender); const tokenId = 0; await nftManager.sendNFTNoSafeguards( nftRecipient, nftMock.address, tokenId, { from: owner, } ); // Verify it's success (the NFT is owned by nftRecipient) const actualOwner = await nftMock.ownerOf(tokenId); expect(actualOwner).to.be.eq(nftRecipient); }); it("Valid NFT transfer fails when sent by non-owner", async function() { const [owner, nftMinter, nftSender, nftRecipient] = accounts; const { nftManager, nftMock } = await setupNFT(owner, nftMinter, nftSender); const tokenId = 0; try { await nftManager.sendNFTNoSafeguards( nftRecipient, nftMock.address, tokenId, { from: nftRecipient, } ); } catch (error) { expect(!!error.message, true, "Error message not received"); return; } expect.fail("Transaction should fail when sent by non owner"); }); it("Valid NFT safeTransfer w/o data succeeds when sent by owner", async function() { const [owner, nftMinter, nftSender, nftRecipient] = accounts; const { nftManager, nftMock } = await setupNFT(owner, nftMinter, nftSender); const tokenId = 0; await nftManager.sendNFT(nftRecipient, nftMock.address, tokenId, emptyData, { from: owner, }); // Verify it's success (the NFT is owned by nftRecipient) const actualOwner = await nftMock.ownerOf(tokenId); expect(actualOwner).to.be.eq(nftRecipient); }); it("Valid NFT safeTransfer w/o data fails when sent by non-owner", async function() { const [owner, nftMinter, nftSender, nftRecipient] = accounts; const { nftManager, nftMock } = await setupNFT(owner, nftMinter, nftSender); const tokenId = 0; try { await nftManager.sendNFT(nftRecipient, nftMock.address, tokenId, emptyData, { from: nftRecipient, }); } catch (error) { expect(!!error.message, true, "Error message not received"); return; } expect.fail("Transaction should fail when sent by non owner"); }); it("Valid NFT safeTransfer w/o data to smart contract succeeds when the recipient implements IERC721Reciever", async function() { const [owner, nftMinter, nftSender] = accounts; const { nftManager, nftMock, nftReceiverMock } = await setupNFT( owner, nftMinter, nftSender ); const tokenId = 0; await nftManager.sendNFT( nftReceiverMock.address, nftMock.address, tokenId, emptyData, { from: owner, } ); // Verify it's success (the NFT is owned by nftRecipient) const actualOwner = await nftMock.ownerOf(tokenId); expect(actualOwner).to.be.eq(nftReceiverMock.address); }); it("Valid NFT safeTransfer w/o data to smart contract fails when the recipient does not implement IERC721Reciever", async function() { const [owner, nftMinter, nftSender, nftRecipient] = accounts; const { nftManager, nftMock } = await setupNFT(owner, nftMinter, nftSender); const nftNonReceiverMock = await IERC721NonReceiverMock.new({ from: nftMinter, }); const tokenId = 0; try { await nftManager.sendNFT( nftNonReceiverMock.address, nftMock.address, tokenId, { from: nftRecipient, } ); } catch (error) { expect(!!error.message, true, "Error message not received"); return; } expect.fail( "Transaction should fail when recipient contract does not implement IERC721Reciever" ); }); it("Valid NFT safeTransfer with data succeeds when sent by owner", async function() { const [owner, nftMinter, nftSender, nftRecipient] = accounts; const { nftManager, nftMock } = await setupNFT(owner, nftMinter, nftSender); const tokenId = 0; await nftManager.sendNFT( nftRecipient, nftMock.address, tokenId, sendNFTData, { from: owner, } ); // Expect data in event const events = await nftManager.getPastEvents("SendNFT", {}); assertEventParams( events[0], ["recipient", "nftContract", "tokenId"], [nftRecipient, nftMock.address, tokenId] ); // Verify it's success (the NFT is owned by nftRecipient) const actualOwner = await nftMock.ownerOf(tokenId); expect(actualOwner).to.be.eq(nftRecipient); }); it("Valid NFT safeTransfer with data fails when sent by non-owner", async function() { const [owner, nftMinter, nftSender, nftRecipient] = accounts; const { nftManager, nftMock } = await setupNFT(owner, nftMinter, nftSender); const tokenId = 0; try { await nftManager.sendNFT( nftRecipient, nftMock.address, tokenId, sendNFTData, { from: nftRecipient, } ); } catch (error) { expect(!!error.message, true, "Error message not received"); return; } expect.fail("Transaction should fail when sent by non owner"); }); it("Valid NFT safeTransfer with data to smart contract succeeds when the recipient implements IERC721Reciever", async function() { const [owner, nftMinter, nftSender] = accounts; const { nftManager, nftMock, nftReceiverMock } = await setupNFT( owner, nftMinter, nftSender ); const tokenId = 0; await nftManager.sendNFT( nftReceiverMock.address, nftMock.address, tokenId, sendNFTData, { from: owner, } ); // Expect data in event const events = await nftManager.getPastEvents("SendNFT", {}); assertEventParams( events[0], ["recipient", "nftContract", "tokenId"], [nftReceiverMock.address, nftMock.address, tokenId] ); // Verify it's success (the NFT is owned by nftRecipient) const actualOwner = await nftMock.ownerOf(tokenId); expect(actualOwner).to.be.eq(nftReceiverMock.address); }); it("Valid NFT safeTransfer with data to smart contract fails when the recipient does not implement IERC721Reciever", async function() { const [owner, nftMinter, nftSender, nftRecipient] = accounts; const { nftManager, nftMock, nftNonReceiverMock } = await setupNFT( owner, nftMinter, nftSender ); const tokenId = 0; try { await nftManager.sendNFT( nftNonReceiverMock.address, nftMock.address, tokenId, sendNFTData, { from: nftRecipient, } ); } catch (error) { expect(!!error.message, true, "Error message not received"); return; } expect.fail( "Transaction should fail when recipient contract does not implement IERC721Reciever" ); }); it("NFTManager gracefully accepts NFTs sent using safeTransfer()", async function() { const [owner, nftMinter, nftSender] = accounts; const { nftManager, nftMock } = await setupNFT(owner, nftMinter, nftSender); const tokenId = 1; //Safe Transfer NFT to NFTManager const tx = await nftMock.safeTransferFrom( nftSender, nftManager.address, tokenId, { from: nftSender } ); //Ensure transfer completes as expected const transferLog = tx.logs.find((log) => log.event === "Transfer"); expect(transferLog.args.from).to.be.equal(nftSender); expect(transferLog.args.to).to.be.equal(nftManager.address); expect(transferLog.args.tokenId.toString()).to.be.equal(tokenId.toString()); const actualOwner = await nftMock.ownerOf(tokenId); expect(actualOwner).to.be.eq(nftManager.address); }); it("proposeCall log", async function() { const [owner, nftMinter, nftSender, nftRecipient] = accounts; const { nftManager, nftMock } = await setupNFT(owner, nftMinter, nftSender); var testSetup = await setup(accounts, nftManager.address); await nftManager.transferOwnership(testSetup.org.avatar.address); var callData = await encodeSendNFTNoSafeGaurdsCall( nftRecipient, nftMock.address, 0, nftManager ); var tx = await testSetup.genericScheme.proposeCall( callData, 0, helpers.NULL_HASH ); assert.equal(tx.logs.length, 1); assert.equal(tx.logs[0].event, "NewCallProposal"); }); it("sendNFTNoSafeGuards: execute proposeVote -positive decision", async function() { const [owner, nftMinter, nftSender, nftRecipient] = accounts; const { nftManager, nftMock } = await setupNFT(owner, nftMinter, nftSender); const tokenId = 0; const callData = await encodeSendNFTNoSafeGaurdsCall( nftRecipient, nftMock.address, 0, nftManager ); await setupAndExecuteProposal(accounts, nftManager, callData, VotingMachines.AbsoluteVote); // Expect data in event const events = await nftManager.getPastEvents("SendNFT", {}); assertEventParams( events[0], ["recipient", "nftContract", "tokenId"], [nftRecipient, nftMock.address, tokenId] ); assert.equal(await nftMock.ownerOf(0), nftRecipient); }); it("sendNFTNoSafeGuards: execute proposeVote -positive decision - check action - with GenesisProtocol", async function() { const [owner, nftMinter, nftSender, nftRecipient] = accounts; const { nftManager, nftMock } = await setupNFT(owner, nftMinter, nftSender); assert.equal(await nftMock.ownerOf(0), nftManager.address); const tokenId = 0; const callData = await encodeSendNFTNoSafeGaurdsCall( nftRecipient, nftMock.address, tokenId, nftManager ); await setupAndExecuteProposal(accounts, nftManager, callData, VotingMachines.GenesisProtocol); // Expect data in event const events = await nftManager.getPastEvents("SendNFT", {}); assertEventParams( events[0], ["recipient", "nftContract", "tokenId"], [nftRecipient, nftMock.address, tokenId] ); assert.equal(await nftMock.ownerOf(0), nftRecipient); }); it("sendNFT (no data): execute proposeVote -positive decision", async function() { const [owner, nftMinter, nftSender, nftRecipient] = accounts; const { nftManager, nftMock } = await setupNFT(owner, nftMinter, nftSender); const tokenId = 0; const callData = await encodeSendNFTCall( nftRecipient, nftMock.address, 0, emptyData, nftManager ); await setupAndExecuteProposal(accounts, nftManager, callData, VotingMachines.AbsoluteVote); // Expect data in event const events = await nftManager.getPastEvents("SendNFT", {}); assertEventParams( events[0], ["recipient", "nftContract", "tokenId"], [nftRecipient, nftMock.address, tokenId] ); assert.equal(await nftMock.ownerOf(0), nftRecipient); }); it("sendNFT (no data): execute proposeVote -positive decision - check action - with GenesisProtocol", async function() { const [owner, nftMinter, nftSender, nftRecipient] = accounts; const { nftManager, nftMock } = await setupNFT(owner, nftMinter, nftSender); assert.equal(await nftMock.ownerOf(0), nftManager.address); const tokenId = 0; const callData = await encodeSendNFTCall( nftRecipient, nftMock.address, tokenId, emptyData, nftManager ); await setupAndExecuteProposal(accounts, nftManager, callData, VotingMachines.GenesisProtocol); // Expect data in event const events = await nftManager.getPastEvents("SendNFT", {}); assertEventParams( events[0], ["recipient", "nftContract", "tokenId"], [nftRecipient, nftMock.address, tokenId] ); assert.equal(await nftMock.ownerOf(0), nftRecipient); }); it("sendNFT (with data): execute proposeVote -positive decision", async function() { const [owner, nftMinter, nftSender, nftRecipient] = accounts; const { nftManager, nftMock } = await setupNFT(owner, nftMinter, nftSender); const tokenId = 0; const callData = await encodeSendNFTCall( nftRecipient, nftMock.address, tokenId, '0x12345', nftManager ); await setupAndExecuteProposal(accounts, nftManager, callData, VotingMachines.AbsoluteVote); // Expect data in event const events = await nftManager.getPastEvents("SendNFT", {}); assertEventParams( events[0], ["recipient", "nftContract", "tokenId"], [nftRecipient, nftMock.address, tokenId] ); assert.equal(await nftMock.ownerOf(0), nftRecipient); }); it("sendNFT (with data): execute proposeVote -positive decision - check action - with GenesisProtocol", async function() { const [owner, nftMinter, nftSender, nftRecipient] = accounts; const { nftManager, nftMock } = await setupNFT(owner, nftMinter, nftSender); assert.equal(await nftMock.ownerOf(0), nftManager.address); const tokenId = 0; const callData = await encodeSendNFTCall( nftRecipient, nftMock.address, tokenId, '0x12345', nftManager ); await setupAndExecuteProposal(accounts, nftManager, callData, VotingMachines.GenesisProtocol); // Expect data in event const events = await nftManager.getPastEvents("SendNFT", {}); assertEventParams( events[0], ["recipient", "nftContract", "tokenId"], [nftRecipient, nftMock.address, tokenId] ); assert.equal(await nftMock.ownerOf(0), nftRecipient); }); });