2

I've been building an nft marketplace backend, on top of a manager contract that manages all the permissioned nft collections. The idea is that, manager can create erc721 or erc1155 contracts via this manager contract, and the users can initially mint these tokens via manager contract. Other than that, transfer and other functions are all the same and inside a token contract itself. Now, what i've been building is a Nest.js backend with mongodb to keep track of owners, bids, collections, prices and uri's. So far so good, and i do not have any errors in my stack, however, I'm not sure about my approach to build event listeners. I've created an event listener with provider.on(filter, _) and once the manager contract creates a new collection, this event is triggered. Once this event is triggered, backend emit a custom event to create new event listener for the collection itself :) It's kinda weird at this point, and while it is working, I'm afraid that it will be huge load once there are more than 100's of collections. It means there will be 100's of contracts and event listeners.

Here is the approach I followed, any advices or if this is the right approach, approving this is welcomed. Thanks for reading this mass :)

There are 2 collections in mongo. 1 is to keep track of collections generated by manager contract. other is nfts that keep track of every collections nfts. this part mounts the listener for the manager and fires a custom event once the manager emits a new contract

@Resolver()
export class ManagerListener implements OnApplicationBootstrap {
  provider;
  filter;
  constructor(
    private readonly managerService: ManagerService,
    private eventEmitter: EventEmitter2,
  ) {
    this.provider = getRpcProvider();
    this.filter = {
//manager address
      address: '0x7C705A6fMOCKDATA32BF4507D0',
      topics: [ethers.utils.id('NewCollection(address,uint8)')],
    };
  }

  //manager listener mounts once the server is started
  onApplicationBootstrap() {
    this.provider.on(this.filter, async (log, _) => {
      console.log('new collection deployed at: ', dataToAddress(log.data));
      const address = dataToAddress(log.data);
      const newCollection: ManagerCreateInput = { address };
      //this pushes new collection to mongo just as an address.
      await this.managerService.createManager(newCollection);

      //this part fires a custom event with the new collection address. this will be 
      //listened by the below part
      const collectionCreatedEvent = new CollectionCreatedEvent();
      collectionCreatedEvent.address = address;
      this.eventEmitter.emit('collection.created', collectionCreatedEvent);
    });
  }
}

this part on the other hand, is listening for the custom event and once it is emitted, it mounts a new ethers event listener to the nft contract itself. this could end up in 100's of event listeners, dunno how to scale this one

@Resolver()
export class NFTsListener {
  provider;
  filter;
  constructor(private readonly nftsService: NFTsService) {
    this.provider = getRpcProvider();
  }

  //erc721 listener
  //receives the custom event fired by the above code
  @OnEvent('collection.created')
  handleCollectionCreatedEvent(payload: CollectionCreatedEvent) {
    this.filter = {
      address: payload.address,
      topics: [ethers.utils.id('Transfer(address,address,uint256)')],
    };
    //mounts a new ethers event for the contract that is deployed by the manager
    this.provider.on(this.filter, (log, _) => {
      const newNft: NFTCreateInput = {
        address: log.address,
        owner: logToAddress(log.topics[2]),
        uri: 'MOCKDATA',
        id: parseInt(log.topics[3]),
      };
      //this pushes a new nft to mongodb
      return this.nftsService.createOrUpdateNft(newNft);
    });
  }
}

1 Answer 1

1

I would probably use a single listener for all NFTs. Then by checking log.address you can filter out the bad ones.

For example, first part:

  onApplicationBootstrap() {
    this.provider.on(this.filter, async (log, _) => {
      console.log('new collection deployed at: ', dataToAddress(log.data));
      const address = dataToAddress(log.data);
      const newCollection: ManagerCreateInput = { address };
      //this pushes new collection to mongo just as an address.
      await this.managerService.createManager(newCollection);

      myCollections.push(address);
    });
  }

Second part:

    filter = {
      topics: [ethers.utils.id('Transfer(address,address,uint256)')],
    };

    provider.on(filter, (log, _) => {
      // exit early if it's not our NFT
      if (! myCollections.includes(log.address)) return;

      const newNft: NFTCreateInput = {
        address: log.address,
        owner: logToAddress(log.topics[2]),
        uri: 'MOCKDATA',
        id: parseInt(log.topics[3]),
      };
      //this pushes a new nft to mongodb
      return this.nftsService.createOrUpdateNft(newNft);
    });

All NFTs address are saved in an array myCollections, but you can use whatever method you like.

1
  • wonderful advice. i was just considering this so that server makes way less rpc calls to blockchain. will also write this version and push it. thanks.
    – Rafael
    Oct 3, 2022 at 8:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.