From ca6839a5ae6ae61e2aec93933785a39116601d8d Mon Sep 17 00:00:00 2001 From: Stefan Adolf Date: Mon, 25 May 2020 12:56:31 +0200 Subject: [PATCH] anchoring ipfs data on contract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit incr gas price for görli --- .openzeppelin/goerli.json | 46 +++++++++++++++++++++++ .openzeppelin/project.json | 3 +- contracts/DocExchange.sol | 17 +++++++++ src/IpfsPage.tsx | 69 ++++++++++++++++++++++++++++++++-- src/contracts/DocExchange.json | 1 + 5 files changed, 131 insertions(+), 5 deletions(-) create mode 100644 contracts/DocExchange.sol create mode 120000 src/contracts/DocExchange.json diff --git a/.openzeppelin/goerli.json b/.openzeppelin/goerli.json index a2b52f7..a5f1449 100644 --- a/.openzeppelin/goerli.json +++ b/.openzeppelin/goerli.json @@ -288,6 +288,43 @@ ], "storageDiff": [] } + }, + "DocExchange": { + "address": "0x1E6C6D4c33FfB5763562DDA25F260e84Be69d2C7", + "constructorCode": "608060405234801561001057600080fd5b50610458806100206000396000f3fe", + "bodyBytecodeHash": "167057e1f0e73f4632f0d9c828320dc88f39b7468ad91b99836fc4ebe297285e", + "localBytecodeHash": "e027048d52e4df22ba208f5e508a8bbb2ad41d5496ee54ac8a4266f74c781cdb", + "deployedBytecodeHash": "e027048d52e4df22ba208f5e508a8bbb2ad41d5496ee54ac8a4266f74c781cdb", + "types": { + "t_bytes": { + "id": "t_bytes", + "kind": "elementary", + "label": "bytes" + }, + "t_mapping": { + "id": "t_mapping", + "valueType": "t_bytes", + "label": "mapping(key => bytes)", + "kind": "mapping" + } + }, + "storage": [ + { + "contract": "DocExchange", + "path": "contracts/DocExchange.sol", + "label": "documents", + "astId": 2138, + "type": "t_mapping", + "src": "105:35:13" + } + ], + "warnings": { + "hasConstructor": false, + "hasSelfDestruct": false, + "hasDelegateCall": false, + "hasInitialValuesInDeclarations": false, + "uninitializedBaseContracts": [] + } } }, "solidityLibs": {}, @@ -307,6 +344,15 @@ "admin": "0x5c19BF66CC2c9386Cdf06d346F32F140A4559be9", "kind": "Upgradeable" } + ], + "ledger-academy/DocExchange": [ + { + "address": "0xcB4c372940117184A3fe3eBDe36d118b6f3A454e", + "version": "1.0.0", + "implementation": "0x1E6C6D4c33FfB5763562DDA25F260e84Be69d2C7", + "admin": "0x5c19BF66CC2c9386Cdf06d346F32F140A4559be9", + "kind": "Upgradeable" + } ] }, "manifestVersion": "2.2", diff --git a/.openzeppelin/project.json b/.openzeppelin/project.json index dde69e6..defd591 100644 --- a/.openzeppelin/project.json +++ b/.openzeppelin/project.json @@ -1,7 +1,8 @@ { "manifestVersion": "2.2", "contracts": { - "ADIToken": "ADIToken" + "ADIToken": "ADIToken", + "DocExchange": "DocExchange" }, "dependencies": { "@openzeppelin/contracts-ethereum-package": "^3.0.0" diff --git a/contracts/DocExchange.sol b/contracts/DocExchange.sol new file mode 100644 index 0000000..84025d8 --- /dev/null +++ b/contracts/DocExchange.sol @@ -0,0 +1,17 @@ +pragma solidity ^0.6.0; + + +contract DocExchange { + event DocumentAdded(address owner, bytes cid); + + mapping(address => bytes) documents; + + function addDocument(bytes memory cid) public { + documents[msg.sender] = cid; + emit DocumentAdded(msg.sender, cid); + } + + function getMyDocument() public view returns (bytes memory cid) { + return documents[msg.sender]; + } +} diff --git a/src/IpfsPage.tsx b/src/IpfsPage.tsx index 8b6729f..79389a8 100644 --- a/src/IpfsPage.tsx +++ b/src/IpfsPage.tsx @@ -1,12 +1,60 @@ import React, { useState, useEffect } from "react"; +import { useWeb3React } from "@web3-react/core"; +import Web3 from "web3"; +import DocExchange from "./contracts/DocExchange.json"; -import IPFS from "ipfs"; +import IPFS, { Buffer, CID } from "ipfs"; import _secrets from "../.secrets.json"; const IpfsPage: React.FC = () => { + const { account, library: web3 } = useWeb3React(); const [ipfsNode, setIpfsNode] = useState(); const [files, setFiles] = useState([]); + const [myDocumentContent, setMyDocumentContent] = useState(); + + const contract = new web3.eth.Contract( + DocExchange.abi, + _secrets.docExchangeAddress + ); + + async function anchorOnChain(cid) { + const bytes = [...cid.buffer]; + + const promiEvent = contract.methods.addDocument(bytes).send({ + from: account, + gasPrice: 21 * 1e5, + gas: 30 * 1e5, + }); + + promiEvent.on("transactionHash", console.log); + promiEvent.on("receipt", console.log); + promiEvent.on("confirmation", (number, confirmation) => { + console.debug(confirmation); + }); + } + + async function getMyDocumentFromChainAndResolve() { + const resultBytes = await contract.methods.getMyDocument().call({ + from: account, + }); + console.log("cid stored on chain", resultBytes); + + const bytes = Web3.utils.hexToBytes(resultBytes); + const cid = new CID(Buffer.from(bytes)); + console.log("CID recovered from contract", cid.toString()); + + const contentChunks = await ipfsNode!.cat(cid); + const awaitedChunks: any[] = []; + // eslint-disable-next-line no-restricted-syntax + for await (const chunk of contentChunks) { + awaitedChunks.push(chunk); + } + const result = Buffer.concat(awaitedChunks).toString(); + console.log("content: ", result); + setMyDocumentContent(result); + return result; + } async function addToIpfs(content: string | any[]): Promise { const addResult = ipfsNode!.add(content); @@ -25,6 +73,8 @@ const IpfsPage: React.FC = () => { const ipfsResult = await addToIpfs(_currentContent); console.log(ipfsResult); + anchorOnChain(ipfsResult[0].cid); + setFiles([...files, ipfsResult[0]]); }; @@ -39,12 +89,18 @@ const IpfsPage: React.FC = () => { return (
- + {" "} +
-

Files:

+

History:

    {files.map((f) => (
  • @@ -57,6 +113,11 @@ const IpfsPage: React.FC = () => {
  • ))}
+

My Document:

+ +

{myDocumentContent}

); }; diff --git a/src/contracts/DocExchange.json b/src/contracts/DocExchange.json new file mode 120000 index 0000000..d73d764 --- /dev/null +++ b/src/contracts/DocExchange.json @@ -0,0 +1 @@ +../../build/contracts/DocExchange.json \ No newline at end of file