Using the Relation PrivacyContent deployed by Relation Protocol, we can store and query users' identity data. The Relation PrivacyContent contract is an implementation of the PrivacyContent contract defined in the Contract Standard.
Construct a Contract object
The contract address and abi file of PrivacyContent and PrivacyContentWithSign contract can be accessed via Relation Protocol list of resources. You can construct a Contract object with "ethers".
A user needs to prepare a token, and passes the tokenId when calling the "post" method. Once the "post" method is called, the prepared token will be consumed.
A user can only publish the content Once a token is prepared. The content is encrypted via Lit Protocol, with the content uploaded to Arweave with the following format:
"encryptionBy": "lit-protocol","accessCondition": [{"contractAddress":"${The contract address of Semantic SBT}","standardContractType":"ERC721","chain":"polygon","method":"isViewerOf","parameters": [":userAddress","${The tokenId}"],"returnValueTest": {"comparator":"=","value":true}}],"encryptedSymmetricKey": "${A hex string that LIT will use to decrypt your content as long as you satisfy the conditions}",// encrypted content"encryptedObject": "${The encrypted content}"}
The subsequent transaction hash will be stored in the contract as the content's record.
Users can share the uploaded privacy content to their followers by specifying the tokenId and the address of the Follow contract sharing the content. A contract can only share with 20 Follow contract addresses to avoid query errors due to too many recursive calls.
Users can share the uploaded privacy content to a certain Dao, and all Dao members can decrypt the privacy content via Lit Protocol. A contract can only share with 20 Dao contract addresses to avoid query errors due to too many recursive calls.
Query the list of Follow contract addresses that a tokenId has shared the content with:
The methods sharedFollowAddressCount and sharedFollowAddressByIndex can get the list of Follow contract addresses that a tokenId has shared the content with.
constaddr='0x000...';constprivacyContract=getContractInstance()consttokenId='1';// Query the number of Follow contract addresses that a tokenId has shared the content with.constcount=awaitcontract.sharedFollowAddressCount(tokenId);var followContractAddressList = [];for (var i =0; i < count; i++) {//Query the Follow contract addressesconstfollowContractAddress=awaitcontract.sharedFollowAddressByIndex(tokenId, i);followContractAddressList.push(followContractAddress);}
Query the list of Dao contract addresses that a tokenId has shared the content with:
The methods sharedDaoAddressCount and sharedFDaoAddressByIndex can get the list of Dao contract addresses that a tokenId has shared the content with.
constaddr='0x000...';constprivacyContract=getContractInstance()consttokenId='1';//Query the number of Dao contract addresses that a tokenId has shared the content withconstcount=awaitcontract.sharedDaoAddressCount(tokenId);var followContractAddressList = [];for (var i =0; i < count; i++) {//Query the list of Dao contract addresses that a tokenId has shared the content with.constdaoContractAddress=awaitcontract.sharedFDaoAddressByIndex(tokenId, i);daoContractAddressList.push(daoContractAddress);}
Query the list of published contents of a user.
We can get the list of published contents of a user through said user's list of tokens.
constaddr='0x000...';constprivacyContract=getContractInstance()//Query the number of tokens held by an addressconstbalance=awaitcontract.balanceOf(addr);var contentList = [];for (var i =0; i < balance; i++) {//Query the tokenId held by a user via the indexconsttokenId=awaitcontract.tokenOfOwnerByIndex(addr, i);//Query the published content via the tokenIdconstcontent=awaitcontract.contentOf(tokenId);contentList.push(content);}
Prepare a token(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'})constprivacyContract=getContractInstance()constprivacyWithSignContract=getPrivacyContentWithSignContractInstance()constname=awaitprivacyWithSignContract.name();constnonce=awaitprivacyWithSignContract.nonces(accounts[0]);//The time when the signature expires(Unit: second). The following example means the signature will expire 100 seconds after the current time.constdeadline=Date.parse(newDate()) /1000+100;constsign=awaitgetSign(awaitbuildPrepareParams(name,privacyWithSignContract.address.toLowerCase(),privacyContract.address,parseInt(nonce), deadline), accounts[0]);var param = {"sig": {"v":sign.v,"r":sign.r,"s":sign.s,"deadline": deadline},"target":privacyContract.address,"addr": accounts[0] }// In reality, this method will be called by the address paying the gas fee.awaitprivacyWithSignContract.connect(accounts[1]).prepareTokenWithSign(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', });}asyncfunctionbuildPrepareParams(name, contractAddress, privacyContentAddress,nonce, deadline) {return { domain: { chainId:awaitgetChainId(), name: name, verifyingContract: contractAddress, version:'1', },// Defining the message signing data content. message: { target: privacyContentAddress, nonce: nonce, deadline: deadline, },// Refers to the keys of the *types* object below. primaryType:'PrepareTokenWithSign', types: { EIP712Domain: [ {name:'name', type:'string'}, {name:'version', type:'string'}, {name:'chainId', type:'uint256'}, {name:'verifyingContract', type:'address'}, ], PrepareTokenWithSign: [ {name:'target', type:'address'}, {name:'nonce', type:'uint256'}, {name:'deadline', type:'uint256'}, ], }, };}
Publish the content(Gas fee can be paid by someone else)
A user uploads the content to Arweave, 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'constcontent='zX_Oa1...';constaccounts=awaitethereum.request({method:'eth_requestAccounts'})constprivacyContract=getContractInstance()constprivacyWithSignContract=getPrivacyContentWithSignContractInstance()let name =awaitprivacyWithSignContract.name();let nonce =awaitprivacyWithSignContract.nonces(accounts[0]);//The time when the signature expires(Unit: second). The following example means the signature will expire 100 seconds after the current time.let deadline =Date.parse(newDate()) /1000+100;let sign =awaitgetSign(awaitbuildPostParams( name,privacyWithSignContract.address.toLowerCase(),privacyContent.address.toLowerCase(), content,parseInt(nonce), deadline), accounts[0]);let param = {"sig": {"v":sign.v,"r":sign.r,"s":sign.s,"deadline": deadline},"target":privacyContract.address,"addr": accounts[0],"content": content}//In reality, this method will be called by the address paying the gas fee.awaitprivacyWithSignContract.connect(accounts[1]).postWithSign(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', });}asyncfunctionbuildPostParams(name, contractAddress, privacyContentAddress, content, nonce, deadline) {return { domain: { chainId:awaitgetChainId(), name: name, verifyingContract: contractAddress, version:'1', },// Defining the message signing data content. message: { target: privacyContentAddress, content: content, nonce: nonce, deadline: deadline, },// Refers to the keys of the *types* object below. primaryType:'PostWithSign', types: { EIP712Domain: [ {name:'name', type:'string'}, {name:'version', type:'string'}, {name:'chainId', type:'uint256'}, {name:'verifyingContract', type:'address'}, ], PostWithSign: [ {name:'target', type:'address'}, {name:'content', type:'string'}, {name:'nonce', type:'uint256'}, {name:'deadline', type:'uint256'}, ], }, };}
Share the content with my followers(Gas fee can be paid by someone else)
A user signs against the tokenId and the Follow contract addresses to be shared with 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'constprivacyContent='';constfollowContractAddress='0x0001...';consttokenId='1'constaccounts=awaitethereum.request({method:'eth_requestAccounts'})constprivacyContent=getContractInstance()constprivacyWithSignContract=getPrivacyContentWithSignContractInstance()let name =awaitprivacyWithSignContract.name();let nonce =awaitprivacyWithSignContract.nonces(accounts[0]);//The time when the signature expires(Unit: second). The following example means the signature will expire 100 seconds after the current time.let deadline =Date.parse(newDate()) /1000+100;let sign =awaitgetSign(awaitbuildShareToFollowerParams( name,privacyWithSignContract.address.toLowerCase(),privacyContent.address.toLowerCase(), tokenId, followContractAddress,parseInt(nonce), deadline), accounts[0]);let param = {"sig": {"v":sign.v,"r":sign.r,"s":sign.s,"deadline": deadline},"target":privacyContent.address,"addr": accounts[0],"tokenId":parseInt(tokenId),"followContractAddress": followContractAddress}//In reality, this method will be called by the address paying the gas fee.awaitprivacyWithSignContract.connect(accounts[1]).shareToFollowerWithSign(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', });}asyncfunctionbuildShareToFollowerParams(name, contractAddress, privacyContentAddress, tokenId, followContractAddress, nonce, deadline) {return { domain: { chainId:awaitgetChainId(), name: name, verifyingContract: contractAddress, version:'1', },// Defining the message signing data content. message: { target: privacyContentAddress, tokenId: tokenId, followContractAddress: followContractAddress, nonce: nonce, deadline: deadline, },// Refers to the keys of the *types* object below. primaryType:'ShareToFollowerWithSign', types: { EIP712Domain: [ {name:'name', type:'string'}, {name:'version', type:'string'}, {name:'chainId', type:'uint256'}, {name:'verifyingContract', type:'address'}, ], ShareToFollowerWithSign: [ {name:'target', type:'address'}, {name:'tokenId', type:'uint256'}, {name:'followContractAddress', type:'address'}, {name:'nonce', type:'uint256'}, {name:'deadline', type:'uint256'}, ], }, };}
Share the content with specified Daos(Gas fee can be paid by someone else)
A user signs against the tokenId and the Dao contract addresses to be shared with 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'constprivacyContent='';constdaoContractAddress='0x0001...';consttokenId='1'constaccounts=awaitethereum.request({method:'eth_requestAccounts'})constprivacyContent=getContractInstance()constprivacyWithSignContract=getPrivacyContentWithSignContractInstance()let name =awaitprivacyWithSignContract.name();let nonce =awaitprivacyWithSignContract.nonces(accounts[0]);//The time when the signature expires(Unit: second). The following example means the signature will expire 100 seconds after the current time.let deadline =Date.parse(newDate()) /1000+100;let sign =awaitgetSign(awaitbuildShareToDaoParams( name,privacyWithSignContract.address.toLowerCase(),privacyContent.address.toLowerCase(), tokenId, daoContractAddress,parseInt(nonce), deadline), accounts[0]);let param = {"sig": {"v":sign.v,"r":sign.r,"s":sign.s,"deadline": deadline},"target":privacyContent.address,"addr": accounts[0],"tokenId":parseInt(tokenId),"daoContractAddress": daoContractAddress}//In reality, this method will be called by the address paying the gas fee.awaitprivacyWithSignContract.connect(accounts[1]).shareToDaoWithSign(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', });}asyncfunctionbuildShareToDaoParams(name, contractAddress, privacyContentAddress, tokenId, daoContractAddress, nonce, deadline) {return { domain: { chainId:awaitgetChainId(), name: name, verifyingContract: contractAddress, version:'1', },// Defining the message signing data content. message: { target: privacyContentAddress, tokenId: tokenId, daoContractAddress: daoContractAddress, nonce: nonce, deadline: deadline, },// Refers to the keys of the *types* object below. primaryType:'ShareToDaoWithSign', types: { EIP712Domain: [ {name:'name', type:'string'}, {name:'version', type:'string'}, {name:'chainId', type:'uint256'}, {name:'verifyingContract', type:'address'}, ], ShareToDaoWithSign: [ {name:'target', type:'address'}, {name:'tokenId', type:'uint256'}, {name:'daoContractAddress', type:'address'}, {name:'nonce', type:'uint256'}, {name:'deadline', type:'uint256'}, ], }, };}