This is a simple example of how Verifiable Credentials can be verified on-chain.
It is based on the EIP-712 standard for typed structured data hashing and signing and Ethereum EIP712 Signature 2021.
The VC used is very basic and minimal, more properties such as id could be added. The basis for this example VC ("DaoVc") is defined in mocks > exampleDocument.json
. The credential subject is simply:
"credentialSubject": {
"name": "MakerDAO",
"category": "DeFi"
}
It provides some information about a DAO, i.e which category the DAO belongs to.
Before running any command, you need to create a .env
file and set a BIP-39 compatible mnemonic or a private key as an environment
variable. Follow the example in .env.example
. If you don't already have a mnemonic, use this website to generate one.
Then, proceed with installing dependencies:
$ yarn install
Then run
npx hardhat present
First, this will deploy the smart contracts locally and generate a serialized Verifiable Credential, which is signed by the private key provided as environment variable and looks like this:
{
"_context": [
"https://www.w3.org/2018/credentials/v1",
"https://schema.org",
"https://w3id.org/security/v2"
],
"_type": [
"VerifiableCredential"
],
"credentialSubject": {
"name": "MakerDAO",
"category": "DeFi"
},
"issuer": "did:pkh:eip155:1:0x0797B98884dE920620DCD9d84C4F106374c6121C",
"issuanceDate": "2022-08-21T18:11:31.863Z",
"proof": {
"created": "2022-08-21T18:11:31.863Z",
"eip712": {
"domain": {
"name": "dao-vc-verifier-test"
},
"primaryType": "DaoVc",
"types": {
"DaoVc": [
{
"name": "_context",
"type": "string[]"
},
{
"name": "_type",
"type": "string[]"
},
{
"name": "issuer",
"type": "string"
},
{
"name": "issuanceDate",
"type": "string"
},
{
"name": "credentialSubject",
"type": "DAO"
}
],
"DAO": [
{
"name": "name",
"type": "string"
},
{
"name": "category",
"type": "string"
}
]
}
},
"proofPurpose": "assertionMethod",
"proofValue": {
"v": 28,
"r": "0xae61c93c59bc36fec9d86904ab55585163578aa52f64bb07b03db3ca683e39fa",
"s": "0x7d4d0b222d81cd783ed6da886db04afa5fce5bc557e5a2e6cd5bf5bd0bffb51d"
},
"type": "EthereumEip712Signature2021",
"verificationMethod": "did:pkh:eip155:1:0x0797B98884dE920620DCD9d84C4F106374c6121C#blockchainAccountId"
}
}
- The proof object is based on Ethereum EIP712 Signature 2021.
- "@" is not a valid character in a variable name in Solidity, hence why the
@context
and@type
properties are renamed_context
and_type
respectively. - The
did:pkh
method is used which allows us to turn the Ethereum address of the VC issuer / signer into a valid DID. The inverse of this "process" is done on-chain by the pseudo-resolver inDIDpkhAdapter.sol
.
Then, the content of the whole VC (minus the proof object) and the signature attached to it is verified on-chain against the Ethereum address in issuer
. This is done by calling verifyDaoVc()
. If the verification is successful you should see:
Verification passed on-chain? true
Gas cost: 83550
As shown above, the verification consumes 83,550 gas. The following table gives an estimate of the cost in dollars on Ethereum Mainnet, based on different gas prices:
Gas price (gwei) | Cost ($) |
---|---|
5 | $0.66 |
10 | $1.32 |
20 | $2.64 |
30 | $3.96 |
- Create an example of a VC as voucher that can be redeemed against something such as ERC20 tokens or 721 tokens
- Generalize the implementation so that an on-chain VC verifier is not tightly coupled to a specific VC schema
Compile the smart contracts with Hardhat:
$ yarn compile
Compile the smart contracts and generate TypeChain artifacts:
$ yarn typechain
Lint the Solidity code:
$ yarn lint:sol
Lint the TypeScript code:
$ yarn lint:ts
Run the Mocha tests:
$ yarn test
Generate the code coverage report:
$ yarn coverage
See the gas usage per unit test and average gas per method call:
$ REPORT_GAS=true yarn test
Delete the smart contract artifacts, the coverage reports and the Hardhat cache:
$ yarn clean
If you use VSCode, you can get Solidity syntax highlighting via the vscode-solidity extension.