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

Turn registration into NFT #76

Merged
merged 10 commits into from
Aug 14, 2021
Merged

Turn registration into NFT #76

merged 10 commits into from
Aug 14, 2021

Conversation

niklr
Copy link
Contributor

@niklr niklr commented Jul 26, 2021

Issue number

Issue #73

Description

Turn Kickback registration into NFT (aka ticket as NFT)

List of features added/changed

  • Each event (Conference) has its own NFT contract
  • Contract supports either ERC721 or 1155
  • Use Conference.participantsIndex as TokenId
  • Metadata uri can be set at Conference constructor
  • Metadata uri is set at Deployer and can be changed by groupAdmin. (Refer to changeClearFee for the similar implementation).
  • The ticketing NFT cannot be transferred to the address which is already registered (if a user want to buy multiple tickets for friends, the user has to transfer to a different user address before purchasing the second ticket)

How Has This Been Tested?

All of the changes have been tested with unit tests.

  • When a user registers, ownerOf(tokenId) returns the registrant address.
  • When the registrant transfers the ticket to a recipient, isRegistered/isAttended/isPaid returns the info about the recipient (if this ended up causing too much attack vector, we may make the token not transferrable ).
  • When the registrant transfer that ticket to a recipient, the user can register again (as the registrant is no longer registered for the event).
  • When a user receives the NFT, the user cannot register as the user is already registered.
  • When the registrant transfers the ticket to a recipient, the new recipient can call withdraw and clearAndSend to receive the payout

Screenshots (if appropriate):

Checklist:

* [x]  My code follows the code style of this project.

* [x]  My code implements all the required features.

contracts/Deployer.sol Outdated Show resolved Hide resolved
contracts/Deployer.sol Outdated Show resolved Hide resolved
contracts/Utils.sol Outdated Show resolved Hide resolved
let tokenId = 123
await ct.tokenURI(tokenId).should.be.rejected
await ct.mint(accounts[1], tokenId).should.be.fulfilled
await ct.tokenURI(tokenId).should.eventually.eq(tokenId.toString())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be {baseUri}/{registrantID}?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the baseTokenURI has not been set it should just return the tokenId.

let tokenId = 123
await ct.setBaseTokenURI('https://kickback.events/test/')
await ct.mint(accounts[1], tokenId).should.be.fulfilled
await ct.tokenURI(tokenId).should.eventually.eq('https://kickback.events/test/123')

@niklr
Copy link
Contributor Author

niklr commented Aug 9, 2021

yarn deploy:polygon

yarn run v1.22.10
$ yarn truffle migrate --network polygon && scripts/local/extractDeployedAddresses.js
$ babel-node node_modules/.bin/truffle migrate --network polygon

Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.



Starting migrations...
======================
> Network name:    'polygon'
> Network id:      137
> Block gas limit: 20000000 (0x1312d00)


2_deploy_contracts.js
=====================
No admin addreses set on ./.admins.js
polygon

   Deploying 'EthDeployer'
   -----------------------
   > transaction hash:    0x9367cbcc7017c3976076bdf4060040627d12f18fefe7ac3701b1dc3d5ebc18b5
   > Blocks: 19           Seconds: 40
   > contract address:    0xD874617a94046499367C162D34c5574E8dbBD1e5
   > block number:        17795967
   > block timestamp:     1628519841
   > account:             0x61d64AfBbD3b5CC2D0554A8a92aa5C7540501E7c
   > balance:             2.951254377197681671
   > gas used:            4018426 (0x3d50fa)
   > gas price:           1 gwei
   > value sent:          0 ETH
   > total cost:          0.004018426 ETH


   Deploying 'ERC20Deployer'
   -------------------------
   > transaction hash:    0xa9c84ad5f273a0e90474dd4fa9f3f6676027f34069c6aa14b83e122082c08ee7
   > Blocks: 5            Seconds: 12
   > contract address:    0x9a153fe3790C5fF96BAF7b4A431024920a7E3621
   > block number:        17795972
   > block timestamp:     1628519855
   > account:             0x61d64AfBbD3b5CC2D0554A8a92aa5C7540501E7c
   > balance:             2.947118846197681671
   > gas used:            4135531 (0x3f1a6b)
   > gas price:           1 gwei
   > value sent:          0 ETH
   > total cost:          0.004135531 ETH


   Deploying 'Deployer'
   --------------------
   > transaction hash:    0x9fdd3e7aa159a1b1e8350cb42bd9d6af5656e4e1fbe34b2f8de3dd2aee06806c
   > Blocks: 3            Seconds: 4
   > contract address:    0xA8d0d8Ac811caE275F2bBBBF5FE0e57fEbfe0999
   > block number:        17795977
   > block timestamp:     1628519865
   > account:             0x61d64AfBbD3b5CC2D0554A8a92aa5C7540501E7c
   > balance:             2.946074962197681671
   > gas used:            1043884 (0xfedac)
   > gas price:           1 gwei
   > value sent:          0 ETH
   > total cost:          0.001043884 ETH

,0,0,604800,10
Admin granted to undefined
   > Saving artifacts
   -------------------------------------
   > Total cost:         0.009197841 ETH


Summary
=======
> Total deployments:   3
> Final cost:          0.009197841 ETH


Done in 77.00s.

@niklr niklr requested a review from makoto August 9, 2021 15:24
*/
constructor (
string memory _name,
uint256 _deposit,
uint256 _limitOfParticipants,
uint256 _coolingPeriod,
address payable _owner,
uint256 _clearFee
uint256 _clearFee,
string memory _baseTokenUri
Copy link
Collaborator

@makoto makoto Aug 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than passing _baseTokenUri at the constructor, can you save the deployer address and reference the baseTokenUri via Deployer.

For example, define deployer variable

Deployer  public deployer;

Then set at the constructor

deployer = msg.sender

then tokenURI becomes like this.

   function tokenURI(uint256 tokenId) public view returns (string memory) { 
        return string(abi.encodePacked(deployer.baseTokenURI(), Utils.addr2str(address(this)), '/', _tokenURI(tokenId)));
    }

Most variables(deposit, cooling period, etc) has to be set at the constructor to make the variable unique to the event.
However, the only time we change baseTokenURI is when we change the metadata server to some other domain so it should be referred to the latest baseTokenURI, rather than passed by the constructor and fixed.

This also has the benefit of reducing changes on frontend/backend code as we are only adding ERC721 related functions, not changing existing interface.

Copy link
Contributor Author

@niklr niklr Aug 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting deployer = msg.sender at the constructor will not work because the sender in this case is either EthDeployer or ERC20Deployer and not Deployer. Thus we can't call deployer.baseTokenURI() as suggested.

What about passing the address of Deployer instead of _baseTokenUri at the constructor?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah good point. Let's do that


Participant memory participant = participants[from];
participantsIndex[participant.index] = to;
participants[from] = Participant(0, address(0), participant.paid);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be participants[from] = Participant(0, address(0), false); if you are nullifying everything


function safeTransferFrom(address from, address to, uint256 tokenId) public {
safeTransferFrom(from, to, tokenId, '');
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be overridden? Seems exactly the same as the parent class implementation

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol#L166

    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        safeTransferFrom(from, to, tokenId, "");
    }

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_tokenURIs[tokenId] = Utils.uint2str(tokenId);
}

function transferFrom(address from, address to, uint256 tokenId) public onlyActive {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you rename transferFrom to _beforeTokenTransfer you don't need to override safeTransferFrom(address from, address to, uint256 tokenId) nor safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, is it because we still use the old version of the solidity? Ok, then let's leave it as is.


function transferFrom(address from, address to, uint256 tokenId) public onlyActive {
require(!isRegistered(to), 'already registered');
super.transferFrom(from, to, tokenId);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we use the _beforeTokenTransfer hook, you don't need to call super.transferFrom as it gets called after _beforeTokenTransfer is called.

@niklr niklr requested a review from makoto August 12, 2021 16:27
@makoto makoto merged commit 743520b into wearekickback:master Aug 14, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants