Using the FollowRegister contract deployed by Relation Protocol, we can create a Follow contract for a user. The Follow contract can follow and unfollow someone. Both the FollowRegister and Follow contracts have implemented the contract interface defined by the Contract Standard.
Construct a Contract object
The contract addresses and abi files of the FollowRegister and FollowWithSign contract, and the abi file of the Follow contract, can be accessed via Relation Protocol list of resources. The address of the Follow contract can be accessed via FollowRegister, with each address having its own Follow contract.
Construct a FollowRegisterContract object with "ethers":
We can get the list of followers of a user by querying the token owners of the user's Follow contract.
constaddr='0x000...';constfollowRegisterContract=getFollowRegisterContractInstance()constfollowContractAddress=awaitfollowRegisterContract.ownedFollowContract(addr);constfollowContract=getFollowContractInstance(followContractAddress)constnumOfFollower=awaitfollowContract.totalSupply();var followerList = [];for(var i =0; i < numOfFollower;i++){consttokenId=awaitfollowContract.tokenByIndex(i);constfollower=awaitfollowContract.ownerOf(tokenId);followerList.push(follower);}
Follow(Gas fee can be paid by someone else)
A user signs against the data and constructs it into a parameter to be posted on the blockchain. Any address can initiate a transaction with this parameter, with the gas paid by said address.
import { Bytes } from'@ethersproject/bytes'constaccounts=awaitethereum.request({ method:'eth_requestAccounts' })constfollowRegisterContract=getFollowRegisterContractInstance()constfollowContractAddress=awaitfollowRegisterContract.ownedFollowContract(accounts[0]);constfollowContract=getFollowContractInstance(followContractAddress)constfollowWithsignContract=getFollowWithSignContractInstance(followContractAddress)constname=awaitfollowWithsignContract.name();constnonce=awaitfollowWithsignContract.nonces(accounts[0]);constdeadline=Date.parse(newDate()) /1000+100;constsign=awaitgetSign(awaitbuildFollowParams(name,followWithsignContract.toLowerCase(),followContractAddress.toLowerCase(),parseInt(nonce), deadline), accounts[0]);//The parameter will be passed to the method "followWithSign"var param = {"sig": {"v":sign.v,"r":sign.r,"s":sign.s,"deadline": deadline},"target": followContractAddress,"addr": accounts[0] }//In reality, this method will be called by the address paying the gas fee.awaitfollowWithsignContract.connect(accounts[1]).followWithSign(param);asyncfunctiongetSign(msgParams, signerAddress) {constparams= [signerAddress, msgParams];consttrace=awaithre.network.provider.send("eth_signTypedData_v4", params);returnBytes.splitSignature(trace);}asyncfunctiongetChainId() {returnawaitethereum.request({ method:'eth_chainId', });}asyncfunctionbuildFollowParams(name, contractAddress, followContractAddress, nonce, deadline) {return { domain: { chainId:awaitgetChainId(), name: name, verifyingContract: contractAddress, version:'1', },// Defining the message signing data content. message: { target: followContractAddress, nonce: nonce, deadline: deadline, },// Refers to the keys of the *types* object below. primaryType:'FollowWithSign', types: { EIP712Domain: [ {name:'name', type:'string'}, {name:'version', type:'string'}, {name:'chainId', type:'uint256'}, {name:'verifyingContract', type:'address'}, ], FollowWithSign: [ {name:'target', type:'address'}, {name:'nonce', type:'uint256'}, {name:'deadline', type:'uint256'}, ], }, };}
Unfollow(Gas fee can be paid by someone else)
A user signs against the data and constructs it into a parameter to be posted on the blockchain. Any address can initiate a transaction with this parameter, with the gas paid by said address.
import { Bytes } from'@ethersproject/bytes'constaccounts=awaitethereum.request({ method:'eth_requestAccounts' })constfollowRegisterContract=getFollowRegisterContractInstance()constfollowContractAddress=awaitfollowRegisterContract.ownedFollowContract(accounts[0]);constfollowContract=getFollowContractInstance(followContractAddress)constfollowWithSignContract=getFollowWithSignContractInstance(followContractAddress)constname=awaitfollowWithSignContract.name();constnonce=awaitfollowWithSignContract.nonces(accounts[0]);constdeadline=Date.parse(newDate()) /1000+100;constsign=awaitgetSign(awaitbuildUnFollowParams(name,followWithSignContract.address.toLowerCase(),followContractAddress.toLowerCase(),parseInt(nonce), deadline), accounts[0]);//The parameter will be passed to the method "followWithSign"var param = {"sig": {"v":sign.v,"r":sign.r,"s":sign.s,"deadline": deadline},"target": followContractAddress,"addr": accounts[0] }//In reality, this method will be called by the address paying the gas fee.awaitfollowWithSignContract.connect(accounts[1]).unfollowWithSign(param);asyncfunctiongetSign(msgParams, signerAddress) {constparams= [signerAddress, msgParams];consttrace=awaithre.network.provider.send("eth_signTypedData_v4", params);returnBytes.splitSignature(trace);}asyncfunctiongetChainId() {returnawaitethereum.request({ method:'eth_chainId', });}asyncfunctionbuildUnFollowParams(name, contractAddress, followContractAddress,nonce, deadline) {return { domain: { chainId:awaitgetChainId(), name: name, verifyingContract: contractAddress, version:'1', },// Defining the message signing data content. message: { target: followContractAddress, nonce: nonce, deadline: deadline, },// Refers to the keys of the *types* object below. primaryType:'UnFollowWithSign', types: { EIP712Domain: [ {name:'name', type:'string'}, {name:'version', type:'string'}, {name:'chainId', type:'uint256'}, {name:'verifyingContract', type:'address'}, ], UnFollowWithSign: [ {name:'target', type:'address'}, {name:'nonce', type:'uint256'}, {name:'deadline', type:'uint256'}, ], }, };}