forked from ethereum-optimism/optimism-tutorial
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request ethereum-optimism#137 from ethereum-optimism/20221…
…102-attestations feat(AttestationStation): Simple tutorial
- Loading branch information
Showing
6 changed files
with
8,184 additions
and
0 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 |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# Put the mnemonic for an account on Optimism here | ||
MNEMONIC=test test test test test test test test test test test junk | ||
|
||
# API KEY for Alchemy | ||
ALCHEMY_API_KEY= | ||
|
||
# URL to access Optimism Goerli (if not using Alchemy) | ||
OPTIMISM_GOERLI_URL= |
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 |
---|---|---|
@@ -0,0 +1,112 @@ | ||
# AttestationStation Contract | ||
|
||
[![Discord](https://img.shields.io/discord/667044843901681675.svg?color=768AD4&label=discord&logo=https%3A%2F%2Fdiscordapp.com%2Fassets%2F8c9701b98ad4372b58f13fd9f65f966e.svg)](https://discord-gateway.optimism.io) | ||
[![Twitter Follow](https://img.shields.io/twitter/follow/optimismFND.svg?label=optimismFND&style=social)](https://twitter.com/optimismFND) | ||
|
||
The AttestationStation Contract smart contract contains a public `attestations` mapping that anyone can write to and read from. | ||
In this tutorial you learn how to read, interpret, and write those attestations. | ||
|
||
The contract we'll be using is on the Optimism Goerli network, at address [`0x3Ca8c0B5608AE3E4D3b4d29b2699C5fCc0e67f3d`](https://goerli-optimism.etherscan.io/address/0x3Ca8c0B5608AE3E4D3b4d29b2699C5fCc0e67f3d). | ||
|
||
## Prerequisites | ||
|
||
- You have [Node.js](https://nodejs.org/en/) running on your computer, as well as [yarn](https://classic.yarnpkg.com/lang/en/). | ||
- There is network connectivity to a provider on the Optimism Goerli network, and to the npm package registry. | ||
|
||
|
||
## Setup | ||
|
||
1. Use `yarn` to download the packages you need | ||
|
||
```sh | ||
yarn | ||
``` | ||
|
||
|
||
1. Copy `.env.example` to `.env` and modify the parameters: | ||
|
||
- `MNEMONIC` is the mnemonic to an account that has enough ETH to pay for the transaction. | ||
|
||
- `ALCHEMY_API_KEY` is the API key for an Optimism Goerli app on [Alchemy](https://www.alchemy.com/), our preferred provider. | ||
|
||
- `OPTIMISM_GOERLI_URL` is the URL for Optimism Goerli, if you use [a different node provider](https://community.optimism.io/docs/useful-tools/providers/). | ||
|
||
|
||
1. Enter the hardhat console: | ||
|
||
```sh | ||
yarn hardhat console --network optimism-goerli | ||
``` | ||
|
||
|
||
1. Attach to the contract on the Optimism Goerli network: | ||
|
||
```js | ||
AttestationStation = await ethers.getContractFactory("AttestationStation") | ||
attestationStation = AttestationStation.attach("0x3Ca8c0B5608AE3E4D3b4d29b2699C5fCc0e67f3d") | ||
``` | ||
|
||
|
||
## Write an attestation | ||
|
||
1. Create the attestation. | ||
|
||
```js | ||
goatAddr = '0x00000000000000000000000000000000000060A7' | ||
attendedKey = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("animal-farm.school.attended")) | ||
attestation = { | ||
about: goatAddr, | ||
key: attendedKey, | ||
val: 1 // for true | ||
} | ||
``` | ||
|
||
|
||
1. Send the attestation. | ||
Note that `attestationStation.attest` accepts an array as parameter, so you'll be able to attest to many facts in a single transaction. | ||
|
||
```js | ||
tx = await attestationStation.attest([attestation]) | ||
rcpt = await tx.wait() | ||
``` | ||
|
||
|
||
## Read attestations | ||
|
||
To read an attestation you need to know three things: | ||
|
||
- Creator address (who attested this) | ||
- Subject address (who is being attested about) | ||
- Key | ||
|
||
1. Read the value for the attestation you just created. | ||
|
||
```js | ||
creatorAddr = (await ethers.getSigner()).address | ||
(await attestationStation.attestations(creatorAddr, goatAddr, attendedKey) != '0x') | ||
``` | ||
|
||
1. Check the attestation for a different address to see that the default is false | ||
|
||
```js | ||
notGoatAddr = '0x000000000000000000000000000000000000BEEF' | ||
(await attestationStation.attestations(creatorAddr, notGoatAddr, attendedKey) != '0x') | ||
``` | ||
|
||
1. Read an attestation created by a different user (this one is a grade, so it's text) | ||
|
||
```js | ||
historyKey = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("animal-farm.school.grades.history")) | ||
hex = await attestationStation.attestations('0xBCf86Fd70a0183433763ab0c14E7a760194f3a9F', goatAddr, historyKey) | ||
ethers.utils.toUtf8String(hex) | ||
``` | ||
|
||
Note: To create the attestation with an ascii value I used this data structure: | ||
|
||
```js | ||
attestation = { | ||
about: goatAddr, | ||
key: historyKey, | ||
val: ethers.utils.toUtf8Bytes("A+") | ||
} | ||
``` |
32 changes: 32 additions & 0 deletions
32
ecosystem/attestation-station/contracts/AttestationStation.sol
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 |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.17; | ||
|
||
contract AttestationStation { | ||
mapping(address => mapping(address => mapping(bytes32 => bytes))) public attestations; | ||
|
||
struct AttestationData { | ||
address about; | ||
bytes32 key; | ||
bytes val; | ||
} | ||
|
||
event AttestationCreated( | ||
address indexed creator, | ||
address indexed about, | ||
bytes32 indexed key, | ||
bytes val | ||
); | ||
|
||
function attest(AttestationData[] memory _attestations) public { | ||
for (uint256 i = 0; i < _attestations.length; ++i) { | ||
AttestationData memory attestation = _attestations[i]; | ||
attestations[msg.sender][attestation.about][attestation.key] = attestation.val; | ||
emit AttestationCreated( | ||
msg.sender, | ||
attestation.about, | ||
attestation.key, | ||
attestation.val | ||
); | ||
} | ||
} | ||
} |
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 |
---|---|---|
@@ -0,0 +1,45 @@ | ||
require("@nomiclabs/hardhat-waffle"); | ||
require('dotenv').config() | ||
|
||
// This is a sample Hardhat task. To learn how to create your own go to | ||
// https://hardhat.org/guides/create-task.html | ||
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => { | ||
const accounts = await hre.ethers.getSigners(); | ||
|
||
for (const account of accounts) { | ||
console.log(account.address); | ||
} | ||
}); | ||
|
||
// You need to export an object to set up your config | ||
// Go to https://hardhat.org/config/ to learn more | ||
|
||
/** | ||
* @type import('hardhat/config').HardhatUserConfig | ||
*/ | ||
|
||
const optimismGoerliUrl = | ||
process.env.ALCHEMY_API_KEY ? | ||
`https://opt-goerli.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}` : | ||
process.env.OPTIMISM_GOERLI_URL | ||
|
||
const words = process.env.MNEMONIC.match(/[a-zA-Z]+/g).length | ||
validLength = [12, 15, 18, 24] | ||
if (!validLength.includes(words)) { | ||
console.log(`The mnemonic (${process.env.MNEMONIC}) is the wrong number of words`) | ||
process.exit(-1) | ||
} | ||
|
||
module.exports = { | ||
solidity: "0.8.17", | ||
networks: { | ||
"local-devnode": { | ||
url: "http://localhost:8545", | ||
accounts: { mnemonic: "test test test test test test test test test test test junk" } | ||
}, | ||
"optimism-goerli": { | ||
url: optimismGoerliUrl, | ||
accounts: { mnemonic: process.env.MNEMONIC } | ||
} | ||
} | ||
}; |
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 |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"name": "hardhat-project", | ||
"devDependencies": { | ||
"@nomiclabs/hardhat-ethers": "^2.0.5", | ||
"@nomiclabs/hardhat-waffle": "^2.0.2", | ||
"chai": "^4.3.6", | ||
"ethereum-waffle": "^3.4.0", | ||
"ethers": "^5.5.4", | ||
"hardhat": "^2.8.4" | ||
}, | ||
"dependencies": { | ||
"dotenv": "^16.0.0" | ||
} | ||
} |
Oops, something went wrong.