PrivacyContent 合约请求示例

我们使用Relation Protocol部署好的Relation PrivacyContent合约,可以实现用户身份数据的存储与查询。Relation PrivacyContent合约是对Contract Standard里PrivacyContent的实现。

构建Contract对象

PrivacyContent和PrivacyContentWithSign的合约地址以及abi文件可以查询Relation Protocol资源列表获得,通过ethers构建Contract对象:

import {ethers, providers} from 'ethers'

const getContractInstance = () => {
    // 合约地址
    const contractAddress = '0x1A4231bedA090c6903c4731518C616F8FAEc5dc7'
    const provider = new providers.Web3Provider(window.ethereum)
    const signer = provider.getSigner()
    const contract = new ethers.Contract(contractAddress, privacyContentAbi, signer)
    return contract
}

const getPrivacyContentWithSignContractInstance = () => {
    // 合约地址
    const contractAddress = ''
    const provider = new providers.Web3Provider(window.ethereum)
    const signer = provider.getSigner()
    const contract = new ethers.Contract(contractAddress, privacyContentWithSignAbi, signer)
    return contract
}

调用合约方法

用户自付gas费

  1. 预生成token

用户需要预生成token,在调用post方法时传入预生成的tokenId。当用户调用post方法,会消耗掉预生成的token。

const privacyContract = getContractInstance()
await (
    await privacyContract.prepareToken()
).wait()
  1. 查询用户预生成的token

const privacyContract = getContractInstance()
const accounts = await ethereum.request({method: 'eth_requestAccounts'})

const tokenId = await privacyContract.ownedPrepareToken(accounts[0]);
  1. 发布内容

预生成token之后,才能发布内容。用户需要通过Lit Protocol进行数据加密,然后将需要发布的内容上传至Arweave,内容格式为:


"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}"
}

上传后得到的交易哈希,作为content记录至合约中

const privacyContract = getContractInstance()
const content = 'zX_Oa1...';
const accounts = await ethereum.request({method: 'eth_requestAccounts'})

await (
    await privacyContract.post(content)
).wait()
  1. 将内容分享给我的follower

用户可以将上传的隐私数据分享给follower,需要指定tokenId以及要分享的Follow合约地址。为了避免出现循环次数过多引发查询异常,合约内限制最多分享20个Follow合约地址

const myFollowContractAddress = '0x000...';
const tokenId = '1';
const privacyContract = getContractInstance()

await (
    await privacyContract.shareToFollower(tokenId, myFollowContractAddress)
).wait()
  1. 将内容分享给指定的Dao

用户可以将上传的隐私数据分享给指定的Dao,那么所有的Dao成员将可以通过Lit Protocol解密出隐私数据。为了避免出现循环次数过多引发查询异常,合约内限制最多分享20个Dao合约地址

const myDaoContractAddress = '0x000...';
const tokenId = '1';
const privacyContract = getContractInstance()

await (
    await privacyContract.shareToDao(tokenId, myDaoContractAddress)
).wait()
  1. 查询tokenId已分享的Follow合约地址列表

通过sharedFollowAddressCount 和 sharedFollowAddressByIndex方法可以遍历出tokenId已分享的Follow合约地址列表

const addr = '0x000...';
const privacyContract = getContractInstance()
const tokenId = '1';

//查询tokenId已分享的Follow合约数量
const count = await contract.sharedFollowAddressCount(tokenId);
var followContractAddressList = [];
for (var i = 0; i < count; i++) {
    //查询Follow合约地址
    const followContractAddress = await contract.sharedFollowAddressByIndex(tokenId, i);
    followContractAddressList.push(followContractAddress);
}
  1. 查询tokenId已分享的Dao合约地址列表

通过sharedDaoAddressCount 和 sharedFDaoAddressByIndex方法可以遍历出tokenId已分享的Follow合约地址列表

const addr = '0x000...';
const privacyContract = getContractInstance()
const tokenId = '1';

//查询tokenId已分享的Dao合约数量
const count = await contract.sharedDaoAddressCount(tokenId);
var followContractAddressList = [];
for (var i = 0; i < count; i++) {
    //查询已分享的Dao合约地址
    const daoContractAddress = await contract.sharedFDaoAddressByIndex(tokenId, i);
    daoContractAddressList.push(daoContractAddress);
}
  1. 查询用户发布的内容列表

遍历出用户的token列表,即可得到其发布的内容列表

const addr = '0x000...';
const privacyContract = getContractInstance()

//查询地址持有的token数量
const balance = await contract.balanceOf(addr);
var contentList = [];
for (var i = 0; i < balance; i++) {
    //根据索引查到我持有的tokenId
    const tokenId = await contract.tokenOfOwnerByIndex(addr, i);
    //查询tokenId对应的发布内容
    const content = await contract.contentOf(tokenId);
    contentList.push(content);
}
  1. 预生成token(代付Gas费)

用户对数据进行签名,构建上链参数。任意地址可携带此上链参数发起交易,Gas费由发起交易的地址支付。

import { Bytes } from '@ethersproject/bytes'

const accounts = await ethereum.request({method: 'eth_requestAccounts'})
const privacyContract = getContractInstance()
const privacyWithSignContract = getPrivacyContentWithSignContractInstance()

const name = await privacyWithSignContract.name();
const nonce = await privacyWithSignContract.nonces(accounts[0]);
//签名过期时间(单位:秒)。此处示例为当前时间100s之后签名失效
const deadline = Date.parse(new Date()) / 1000 + 100;
const sign = await getSign(await buildPrepareParams(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]
    }
//实际场景中,这个方法由实际支付Gas的账户来调用
await privacyWithSignContract.connect(accounts[1]).prepareTokenWithSign(param);

async function getSign(msgParams, signerAddress) {
    const params = [signerAddress, msgParams];
    const trace = await hre.network.provider.send(
        "eth_signTypedData_v4", params);
    return Bytes.splitSignature(trace);
}

async function getChainId() {
    return await ethereum.request({
        method: 'eth_chainId',
    });
}

async function buildPrepareParams(name, contractAddress, privacyContentAddress,nonce, deadline) {
    return {
        domain: {
            chainId: await getChainId(),
            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'},
            ],
        },
    };
}
  1. 发布内容(代付Gas费)

用户上传内容到Arweave,对数据进行签名,构建上链参数。任意地址可携带此上链参数发起交易,Gas费由发起交易的地址支付。

import { Bytes } from '@ethersproject/bytes'

const content = 'zX_Oa1...';
const accounts = await ethereum.request({method: 'eth_requestAccounts'})
const privacyContract = getContractInstance()
const privacyWithSignContract = getPrivacyContentWithSignContractInstance()

let name = await privacyWithSignContract.name();
let nonce = await privacyWithSignContract.nonces(accounts[0]);
//签名过期时间(单位:秒)。此处示例为当前时间100s之后签名失效
let deadline = Date.parse(new Date()) / 1000 + 100;
let sign = await getSign(await buildPostParams(
        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
}
//实际场景中,这个方法由实际支付Gas的账户来调用
await privacyWithSignContract.connect(accounts[1]).postWithSign(param);


async function getSign(msgParams, signerAddress) {
    const params = [signerAddress, msgParams];
    const trace = await hre.network.provider.send(
        "eth_signTypedData_v4", params);
    return Bytes.splitSignature(trace);
}

async function getChainId() {
    return await ethereum.request({
        method: 'eth_chainId',
    });
}

async function buildPostParams(name, contractAddress, privacyContentAddress, content, nonce, deadline) {
    return {
        domain: {
            chainId: await getChainId(),
            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'},
            ],
        },
    };
}
  1. 将内容分享给我的follower(代支付Gas费)

用户对需要分享的tokenId以及Follow合约地址进行签名,构建上链参数。任意地址可携带此上链参数发起交易,Gas费由发起交易的地址支付。

import { Bytes } from '@ethersproject/bytes'

const privacyContent = '';
const followContractAddress = '0x0001...';
const tokenId = '1'
const accounts = await ethereum.request({method: 'eth_requestAccounts'})
const privacyContent = getContractInstance()
const privacyWithSignContract = getPrivacyContentWithSignContractInstance()

let name = await privacyWithSignContract.name();
let nonce = await privacyWithSignContract.nonces(accounts[0]);
//签名过期时间(单位:秒)。此处示例为当前时间100s之后签名失效
let deadline = Date.parse(new Date()) / 1000 + 100;
let sign = await getSign(await buildShareToFollowerParams(
        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
}
//实际场景中,这个方法由实际支付Gas的账户来调用
await privacyWithSignContract.connect(accounts[1]).shareToFollowerWithSign(param);


async function getSign(msgParams, signerAddress) {
    const params = [signerAddress, msgParams];
    const trace = await hre.network.provider.send(
        "eth_signTypedData_v4", params);
    return Bytes.splitSignature(trace);
}

async function getChainId() {
    return await ethereum.request({
        method: 'eth_chainId',
    });
}

async function buildShareToFollowerParams(name, contractAddress, privacyContentAddress, tokenId, followContractAddress, nonce, deadline) {
    return {
        domain: {
            chainId: await getChainId(),
            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'},
            ],
        },
    };
}
  1. 将内容分享给指定的dao(代支付Gas费)

用户对需要分享的tokenId以及dao合约地址进行签名,构建上链参数。任意地址可携带此上链参数发起交易,Gas费由发起交易的地址支付。

import { Bytes } from '@ethersproject/bytes'

const privacyContent = '';
const daoContractAddress = '0x0001...';
const tokenId = '1'
const accounts = await ethereum.request({method: 'eth_requestAccounts'})
const privacyContent = getContractInstance()
const privacyWithSignContract = getPrivacyContentWithSignContractInstance()

let name = await privacyWithSignContract.name();
let nonce = await privacyWithSignContract.nonces(accounts[0]);
//签名过期时间(单位:秒)。此处示例为当前时间100s之后签名失效
let deadline = Date.parse(new Date()) / 1000 + 100;
let sign = await getSign(await buildShareToDaoParams(
        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
}
//实际场景中,这个方法由实际支付Gas的账户来调用
await privacyWithSignContract.connect(accounts[1]).shareToDaoWithSign(param);


async function getSign(msgParams, signerAddress) {
    const params = [signerAddress, msgParams];
    const trace = await hre.network.provider.send(
        "eth_signTypedData_v4", params);
    return Bytes.splitSignature(trace);
}

async function getChainId() {
    return await ethereum.request({
        method: 'eth_chainId',
    });
}

async function buildShareToDaoParams(name, contractAddress, privacyContentAddress, tokenId, daoContractAddress, nonce, deadline) {
    return {
        domain: {
            chainId: await getChainId(),
            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'},
            ],
        },
    };
}

Last updated