-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
224 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,128 +1,276 @@ | ||
# Document Store | ||
<h1 align="center"> | ||
<p align="center">Document Store</p> | ||
<img src="https://github.com/Open-Attestation/document-store/blob/beta/docs/images/document-store-banner.png?raw=true" alt="OpenAttestation Document Store" /> | ||
</h1> | ||
|
||
The [Document Store](https://github.com/Open-Attestation/document-store) repository contains the following: | ||
<p align="center"> | ||
Document Store Smart Contracts for the <a href="https://www.openattestation.com">OpenAttestation</a> framework | ||
</p> | ||
|
||
- The smart contract code for document store in the `/contracts` folder | ||
- The node package for using this library in the `/src` folder | ||
<p align="center"> | ||
<a href="https://github.com/Open-Attestation/document-store/actions" alt="Build Status"><img src="https://github.com/Open-Attestation/document-store/actions/workflows/release.yml/badge.svg" /></a> | ||
<a href="https://codecov.io/gh/Open-Attestation/document-store" alt="Code Coverage"><img src="https://codecov.io/gh/Open-Attestation/document-store/branch/beta/graph/badge.svg?token=Y4R9SWXATG" /></a> | ||
<a href="https://github.com/Open-Attestation/document-store/blob/master/LICENSE" alt="License"><img src="https://img.shields.io/badge/License-Apache_2.0-blue.svg" /></a> | ||
</p> | ||
|
||
The Document Store is a set of smart contracts for managing the issuance and revocation of documents. It is designed to be used in conjunction with the [OpenAttestation](https://github.com/Open-Attestation/open-attestation) library to issue and verify documents on the blockchains. | ||
|
||
There are 2 types of document stores, namely, the regular _Document Store_ and the _Transferable Document Store_. | ||
|
||
#### Document Store | ||
|
||
The regular _Document Store_ allows issuers to issue and revoke documents. However, these documents do not have an owner and are, hence, not transferable. Multiple documents issued on the Document Store can be "batched" under a single hash for issuance. | ||
|
||
#### Transferable Document Store | ||
|
||
The _Transferable Document Store_ allows issuers to issue and revoke documents, and these documents have an owner and are transferable. Each document is essentially an ERC-721 NFT (Non-Fungible Token). Thus, documents are issued individually and the document holder's identity can be verified. Although the documents are transferable, they can also be issued as "soulbound" documents which bounds to an owner. This can be particularly useful for certain use cases such as POAP. | ||
|
||
--- | ||
|
||
## Table of Contents | ||
|
||
- [Installation](#installation) | ||
- [Usage](#usage) | ||
- [Document Store](#document-store-1) | ||
- [Transferable Document Store](#transferable-document-store-1) | ||
- [Roles and Access](#roles-and-access) | ||
- [Deployment](#deployment) | ||
- [Document Store](#document-store-2) | ||
- [Transferable Document Store](#transferable-document-store-2) | ||
- [Hardware Wallet](#hardware-wallet) | ||
- [Verification](#verification) | ||
- [Supported Networks](#supported-networks) | ||
- [Configuration](#configuration) | ||
- [Development](#development) | ||
- [Additional Information](#additional-information) | ||
|
||
--- | ||
|
||
## Installation | ||
|
||
To install OpenAttestation document store on your machine, run the command below: | ||
To make integration easier, we have provided the packages containing the Typechain bindings for interacting with the document store. | ||
|
||
#### Using with ethers.js v6 | ||
|
||
```sh | ||
npm i @govtechsg/document-store | ||
npm install @govtechsg/document-store-ethers-v6 | ||
``` | ||
|
||
--- | ||
#### Using with ethers.js v5 | ||
|
||
```sh | ||
npm install @govtechsg/document-store-ethers-v5 | ||
``` | ||
|
||
## Usage | ||
|
||
Provide one of the following depending on your needs: | ||
### Document Store | ||
|
||
For a complete list of functions, refer to [IDocumentStoreBatchable.sol](src/interfaces/IDocumentStoreBatchable.sol). | ||
|
||
- To use the package, provide your own Web3 [provider](https://docs.ethers.io/v5/api/providers/api-providers/). | ||
#### Issuing a document: | ||
|
||
- To write to the blockchain, provide the [signer](https://docs.ethers.io/v5/api/signer/#Wallet) instead. | ||
```typescript | ||
import { DocumentStore__factory } from "@govtechsg/document-store-ethers-v6"; // Or from "@govtechsg/document-store-ethers-v5" | ||
|
||
### Deploying a new document store | ||
const documentStore = DocumentStore__factory.connect(documentStoreAddress, signer); | ||
const tx = await documentStore["issue"]("0x1234"); | ||
await tx.wait(); | ||
``` | ||
|
||
The following shows a code example to deploy a new document store: | ||
#### Checking if a document in a batch is issued: | ||
|
||
```ts | ||
import { deployAndWait } from "@govtechsg/document-store"; | ||
```typescript | ||
const oaDocumentSignature = { | ||
documentRoot: "0xMerkleRoot", | ||
targetHash: "0xTargetHash", | ||
proof: ["0xProof1", "0xProof2"] | ||
}; | ||
|
||
const documentStore = await deployAndWait("My Document Store", signer).then(console.log); | ||
const documentStore = DocumentStore__factory.connect(documentStoreAddress, signer); | ||
const tx = await documentStore["issue"](oaDocumentSignature.documentRoot); | ||
await tx.wait(); | ||
|
||
try { | ||
const isDocIssued = await documentStore.isIssued( | ||
oaDocumentSignature.documentRoot, | ||
oaDocumentSignature.targetHash, | ||
oaDocumentSignature.proof | ||
); | ||
|
||
console.log(isDocIssued); // Returns true or false | ||
} catch (e) { | ||
// Error will be thrown if proof is invalid | ||
console.error(e); | ||
} | ||
``` | ||
|
||
### Connecting to an existing document store | ||
#### Bulk issuing documents: | ||
|
||
The following shows a code example to connect to an existing document store: | ||
Note that this is different from batching. This issues multiple documents or document roots at once. | ||
|
||
```ts | ||
import { connect } from "@govtechsg/document-store"; | ||
```typescript | ||
const documentStore = DocumentStore__factory.connect(documentStoreAddress, signer); | ||
const bulkIssuances = [ | ||
documentStore.interface.encodeFunctionData("issue", ["0xDocRoot1"]), | ||
documentStore.interface.encodeFunctionData("issue", ["0xDocRoot2"]) | ||
]; | ||
const tx = await documentStore.multicall(bulkIssuances); | ||
await tx.wait(); | ||
``` | ||
|
||
const documentStore = await connect("0x4077534e82c97be03a07fb10f5c853d2bc7161fb", providerOrSigner); | ||
#### Revoking a document root: | ||
|
||
```typescript | ||
const documentStore = DocumentStore__factory.connect(documentStoreAddress, signer); | ||
const tx = await documentStore["revoke"]("0x1234"); | ||
await tx.wait(); | ||
``` | ||
|
||
### Interacting with a document store | ||
#### Revoking a document in a batch: | ||
|
||
The following shows a code example to interact with a document store: | ||
```typescript | ||
const documentStore = DocumentStore__factory.connect(documentStoreAddress, signer); | ||
const tx = await documentStore["revoke"]("0xDocumentRoot", "0xTargetHash", ["0xProof1", "0xProof2"]); | ||
await tx.wait(); | ||
``` | ||
|
||
```ts | ||
const issueMerkleRoot = async () => { | ||
const documentStore = connect("0x4077534e82c97be03a07fb10f5c853d2bc7161fb", signer); | ||
### Transferable Document Store | ||
|
||
const tx = await documentStore.issue("0x7fe0b58ed760804eb7118988637693c4351613be327b56527e55bcd0a8d170d7"); | ||
const receipt = await tx.wait(); | ||
console.log(receipt); | ||
For a complete list of functions, refer to [ITransferableDocumentStore.sol](src/interfaces/ITransferableDocumentStore.sol). | ||
|
||
const isIssued = await instance.isIssued("0x7fe0b58ed760804eb7118988637693c4351613be327b56527e55bcd0a8d170d7"); | ||
console.log(isIssued); | ||
}; | ||
#### Issuing a transferable document: | ||
|
||
```typescript | ||
import { TransferableDocumentStore__factory } from "@govtechsg/document-store-ethers-v6"; // Or from "@govtechsg/document-store-ethers-v5" | ||
|
||
const transferableDocumentStore = TransferableDocumentStore__factory.connect(transferableDocumentStoreAddress, signer); | ||
|
||
// Issues a transferable document | ||
const tx = await transferableDocumentStore.issue("0xRecipientAddress", "0xDocTargetHash", false); | ||
await tx.wait(); | ||
|
||
//Issues a soulbound document | ||
const tx = await transferableDocumentStore.issue("0xRecipientAddress", "0xDocTargetHash", true); | ||
await tx.wait(); | ||
``` | ||
|
||
### List of available functions | ||
#### Revoke a transferable document | ||
|
||
```typescript | ||
const transferableDocumentStore = TransferableDocumentStore__factory.connect(transferableDocumentStoreAddress, signer); | ||
const tx = await transferableDocumentStore.revoke("0xDocTargetHash"); | ||
await tx.wait(); | ||
``` | ||
|
||
### Roles and Access | ||
|
||
Roles are useful for granting users to access certain functions only. Here are the designated roles meant for the different key operations. | ||
|
||
The following is a list of available functions to be used with document store: | ||
| Role | Access | | ||
| -------------------- | ------------------------------ | | ||
| `DEFAULT_ADMIN_ROLE` | Able to perform all operations | | ||
| `ISSUER_ROLE` | Able to issue documents | | ||
| `REVOKER_ROLE` | Able to revoke documents | | ||
|
||
- `documentIssued` | ||
- `documentRevoked` | ||
- `isOwner` | ||
- `name` | ||
- `owner` | ||
- `renounceOwnership` | ||
- `transferOwnership` | ||
- `version` | ||
- `initialize` | ||
- `issue` | ||
- `bulkIssue` | ||
- `getIssuedBlock` | ||
- `isIssued` | ||
- `isIssuedBefore` | ||
- `revoke` | ||
- `bulkRevoke` | ||
- `isRevoked` | ||
- `isRevokedBefore` | ||
A trusted user can be granted multiple roles by the admin user to perform different operations. | ||
The following functions can be called on the token contract by the admin user to grant and revoke roles to and from users. | ||
|
||
## Provider & signer | ||
#### Grant a role to a user | ||
|
||
The following code example shows different ways to get the provider or signer: | ||
```ts | ||
const issuerRole = await documentStore.issuerRole(); | ||
await documentStore.grantRole(issuerRole, "0xIssuerStaff"); | ||
``` | ||
|
||
Can only be called by default admin or role admin. | ||
|
||
#### Revoke a role from a user | ||
|
||
```ts | ||
import { Wallet, providers, getDefaultProvider } from "ethers"; | ||
const revokerRole = await documentStore.revokerRole(); | ||
await documentStore.revokeRole(revokerRole, "0xRevokerStaff"); | ||
``` | ||
|
||
Can only be called by default admin or role admin. | ||
|
||
## Deployment | ||
|
||
In all the deployment commands, you can replace `network` argument to any of the [supported networks](#supported-networks). Optionally, you can also supply `--verify=1` if you wish to verify the contracts. | ||
|
||
> [!IMPORTANT] | ||
> The `DEPLOYER_ADDRESS` in `.env` is required to be the address of the deployer. See [Configuration](#configuration) section. | ||
### Document Store | ||
|
||
#### Deploy a Document Store | ||
|
||
```sh | ||
npm run -s deploy:ds --network="mainnet" --name="My Document Store" --admin="0x1234" | ||
``` | ||
|
||
The `name` is the name you want to have for the document store and the `admin` is the address who will be the default admin of the document store. | ||
|
||
#### Deploy an upgradeable Document Store | ||
|
||
```sh | ||
npm run -s deploy:ds:upgradeable --network="mainnet" --name="My Document Store" --admin="0x1234" --verify=1 | ||
``` | ||
|
||
Note that in the example above, the `--verify=1` is optionally passed to verify the contracts. | ||
|
||
// Providers | ||
const mainnetProvider = getDefaultProvider(); | ||
const sepoliaProvider = getDefaultProvider("sepolia"); | ||
const metamaskProvider = new providers.Web3Provider(web3.currentProvider); // Will change network automatically | ||
### Transferable Document Store | ||
|
||
// Signer | ||
const signerFromPrivateKey = new Wallet("YOUR-PRIVATE-KEY-HERE", provider); | ||
const signerFromEncryptedJson = Wallet.fromEncryptedJson(json, password); | ||
signerFromEncryptedJson.connect(provider); | ||
const signerFromMnemonic = Wallet.fromMnemonic("MNEMONIC-HERE"); | ||
signerFromMnemonic.connect(provider); | ||
#### Deploy a Transferable Document Store | ||
|
||
```sh | ||
npm run -s deploy:tds --network="mainnet" --name="My Transferable Document Store" --symbol="XYZ" --admin="0x1234" --verify=1 | ||
``` | ||
|
||
## Setup | ||
The `name` and `symbol` are the standard ERC-721 token name and symbol respectively. The `admin` is the address who will be the default admin of the document store. | ||
|
||
You can install dependencies, check source code, test your project, and use the Truffle development framework with the commands below: | ||
#### Deploy an upgradeable Transferable Document Store | ||
|
||
```sh | ||
npm install | ||
npm lint | ||
npm test | ||
npm truffle <command> | ||
npm run -s deploy:tds:upgradeable --network="mainnet" --name="My Transferable Document Store" --symbol="XYZ" --admin="0x1234" --verify=1 | ||
``` | ||
|
||
## Contract benchmark | ||
### Hardware Wallet | ||
|
||
To deploy using a hardware wallet, you can set the `OA_LEDGER` environment variable to the HD path of your wallet. For example, `OA_LEDGER=m/44'/60'/0'/0/0`. | ||
|
||
### Verification | ||
|
||
You can pass `--verify=1` to the deployment commands if you wish to verify the contracts. You will need to include your Etherscan API keys for the respective networks in your `.env` configuration. See [Configuration](#configuration) section for more info. | ||
|
||
### Supported Networks | ||
|
||
Most EVM-based blockchains should support the document store contracts. For the more common blockchains listed below, we may deploy implementations to help reduce deployment gas fees. | ||
|
||
- Ethereum | ||
- Polygon | ||
- Arbitrum One | ||
- Optimism | ||
|
||
> [!NOTE] | ||
> For a list of pre-configured network names for passing to `--network` during deployment, refer to the [foundry.toml](foundry.toml#L28) file. | ||
If you wish to deploy to a network not configured yet, you can add it to the `foundry.toml` file and pass the name of the network you've added to `--network` during deployment. | ||
|
||
## Configuration | ||
|
||
Create a `.env` file based on [`.env.example`](.env.sample) and provide the information in it. | ||
|
||
The `DEPLOYER_ADDRESS` is required to be the address of the deployer during deployment. The Etherscan API keys are only required if you plan to verify the contracts on their respective chains. | ||
|
||
# Development | ||
|
||
To show the different transaction costs of the different variants of the document store, run the contract benchmark with the command below: | ||
This repository uses [Foundry](https://github.com/foundry-rs/foundry) for its development. To install Foundry, run the following commands: | ||
|
||
```sh | ||
npm run benchmark | ||
curl -L https://foundry.paradigm.xyz | bash | ||
``` | ||
|
||
## Additional information | ||
## Additional Information | ||
|
||
If you are using Visual Studio Code, you may need to link the OpenZeppelin libraries. Refer to [here](https://github.com/juanfranblanco/vscode-solidity#openzeppelin) for more information. | ||
Install lcov for test coverage report. | ||
The contracts have not gone through formal audits yet. Please use them at your own discretion. |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.